work on list

This commit is contained in:
bkfox 2016-07-22 16:20:04 +02:00
parent ee91ff76e7
commit eae1b9dca0
3 changed files with 218 additions and 137 deletions

View File

@ -145,6 +145,173 @@ class BaseRelatedLink(Orderable):
] ]
class BaseList(models.Model):
"""
Generic list
"""
class DateFilter(IntEnum):
none = 0x00
previous = 0x01
next = 0x02
before_related = 0x03,
after_related = 0x04,
filter_date = models.SmallIntegerField(
verbose_name = _('filter by date'),
choices = [ (int(y), _(x.replace('_', ' ')))
for x,y in DateFilter.__members__.items() ],
blank = True, null = True,
)
filter_model = models.ForeignKey(
ContentType,
verbose_name = _('filter by type'),
blank = True, null = True,
help_text = _('if set, select only elements that are of this type'),
limit_choices_to = {
'publication__isnull': False,
}
)
filter_related = models.ForeignKey(
'Publication',
verbose_name = _('filter by a related publication'),
blank = True, null = True,
help_text = _('if set, select children or siblings of this publication'),
)
related_sibling = models.BooleanField(
verbose_name = _('related is a sibling'),
default = False,
help_text = _('if selected select related publications that are '
'siblings of this one'),
)
sort_asc = models.BooleanField(
verbose_name = _('order ascending'),
default = True,
help_text = _('if selected sort list in the ascending order by date')
)
@classmethod
def get_base_queryset(cl, filter_date = None, filter_model = None,
filter_related = None, related_siblings = None,
sort_asc = True):
"""
Get queryset based on the arguments. This class is intended to be
reusable by other classes if needed.
"""
# model
if filter_model:
qs = filter_model.objects.all()
else:
qs = Publication.objects.all()
qs = qs.live().not_in_menu()
# related
if filter_related:
if related_siblings:
qs = qs.sibling_of(filter_related)
else:
qs = qs.descendant_of(filter_related)
date = filter_related.date
if filter_date == cl.DateFilter.before_related:
qs = qs.filter(date__lt = date)
elif filter_date == cl.DateFilter.after_related:
qs = qs.filter(date__gte = date)
# date
else:
date = tz.now()
if filter_date == cl.DateFilter.previous:
qs = qs.filter(date__lt = date)
elif filter_date == cl.DateFilter.next:
qs = qs.filter(date__gte = date)
# sort
if sort_asc:
return qs.order_by('date', 'pk')
return qs.order_by('-date', '-pk')
class BaseDatedList(models.Model):
"""
List that display items per days. Renders a navigation menu on the
top.
"""
nav_days = models.SmallIntegerField(
_('navigation days count'),
default = 7,
help_text = _('number of days to display in the navigation header '
'when we use dates')
)
nav_per_week = models.BooleanField(
_('navigation per week'),
default = False,
help_text = _('if selected, show dates navigation per weeks instead '
'of show days equally around the current date')
)
class Meta:
abstract = True
panels = [
MultiFieldPanel([
FieldPanel('nav_days'),
FieldPanel('nav_per_week'),
], heading=_('Navigation')),
]
@staticmethod
def str_to_date(date):
"""
Parse a string and return a regular date or None.
Format is either "YYYY/MM/DD" or "YYYY-MM-DD" or "YYYYMMDD"
"""
try:
exp = r'(?P<year>[0-9]{4})(-|\/)?(?P<month>[0-9]{1,2})(-|\/)?' \
r'(?P<day>[0-9]{1,2})'
date = re.match(exp, date).groupdict()
return datetime.date(
year = int(date['year']), month = int(date['month']),
day = int(date['day'])
)
except:
return None
def get_nav_dates(self, date):
"""
Return a list of dates availables for the navigation
"""
if self.nav_per_week:
first = date.weekday()
else:
first = int((self.nav_days - 1) / 2)
first = date - tz.timedelta(days = first)
return [ first + tz.timedelta(days=i)
for i in range(0, self.nav_days) ]
def get_date_context(self, date):
"""
Return a dict that can be added to the context to be used by
a date_list.
"""
today = tz.now().today()
if not date:
date = today
# next/prev weeks/date bunch
dates = self.get_nav_dates(date)
next = date + tz.timedelta(days=self.nav_days)
prev = date - tz.timedelta(days=self.nav_days)
# context dict
return {
'nav_dates': {
'date': date,
'next': next,
'prev': prev,
'dates': dates,
}
}
# #
# Publications # Publications
# #
@ -205,8 +372,13 @@ class PublicationTag(TaggedItemBase):
class Publication(Page): class Publication(Page):
order_field = 'first_published_at' order_field = 'date'
date = models.DateTimeField(
_('date'),
blank = True, null = True,
auto_now_add = True,
)
publish_as = models.ForeignKey( publish_as = models.ForeignKey(
'ProgramPage', 'ProgramPage',
verbose_name = _('publish as program'), verbose_name = _('publish as program'),
@ -254,7 +426,7 @@ class Publication(Page):
FieldPanel('summary'), FieldPanel('summary'),
FieldPanel('tags'), FieldPanel('tags'),
FieldPanel('focus'), FieldPanel('focus'),
]), ], heading=_('Content')),
InlinePanel('related_links', label=_('Links')) InlinePanel('related_links', label=_('Links'))
] + Page.promote_panels ] + Page.promote_panels
settings_panels = Page.settings_panels + [ settings_panels = Page.settings_panels + [
@ -262,14 +434,10 @@ class Publication(Page):
FieldPanel('allow_comments'), FieldPanel('allow_comments'),
] ]
@property
def date(self):
return self.first_published_at
@property @property
def recents(self): def recents(self):
return self.get_children().not_in_menu() \ return self.get_children().type(Publication).not_in_menu().live() \
.order_by('-first_published_at') .order_by('-publication__date')
@property @property
def comments(self): def comments(self):
@ -278,6 +446,11 @@ class Publication(Page):
published = True, published = True,
).order_by('-date') ).order_by('-date')
def save(self, *args, **kwargs):
if not self.date and self.first_published_at:
self.date = self.first_published_at
super().save(*args, **kwargs)
def get_context(self, request, *args, **kwargs): def get_context(self, request, *args, **kwargs):
from aircox.cms.forms import CommentForm from aircox.cms.forms import CommentForm
context = super().get_context(request, *args, **kwargs) context = super().get_context(request, *args, **kwargs)
@ -314,7 +487,6 @@ class Publication(Page):
messages.error( messages.error(
request, settings.comment_error_message, fail_silently=True request, settings.comment_error_message, fail_silently=True
) )
return super().serve(request) return super().serve(request)
@ -458,7 +630,7 @@ class DiffusionPage(Publication):
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
if self.diffusion: if self.diffusion:
self.first_published_at = self.diffusion.start self.date = self.diffusion.start
super().save(*args, **kwargs) super().save(*args, **kwargs)
class EventPageQuerySet(PageQuerySet): class EventPageQuerySet(PageQuerySet):
@ -509,7 +681,7 @@ class EventPage(Publication):
] ]
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
self.first_published_at = self.start self.date = self.start
super().save(*args, **kwargs) super().save(*args, **kwargs)
@ -529,7 +701,7 @@ class ListPage(Page):
@classmethod @classmethod
def get_queryset(cl, request, *args, def get_queryset(cl, request, *args,
thread = None, context = {}, related = None, context = {},
**kwargs): **kwargs):
""" """
Return a queryset from the request's GET parameters. Context Return a queryset from the request's GET parameters. Context
@ -538,60 +710,46 @@ class ListPage(Page):
This function can be used by other views if needed This function can be used by other views if needed
Parameters: Parameters:
* type: ['program','diffusion','event'] type of the publication * model: ['program','diffusion','event'] type of the publication
* asc: if present, sort ascending instead of descending
* related: children of the thread passed in arguments only
* siblings: sibling of the related instead of children
* tag: tag to search for * tag: tag to search for
* search: query to search in the publications * search: query to search in the publications
* thread: children of the thread passed in arguments only
* order: ['asc','desc'] sort ordering
* page: page number * page: page number
Context's fields: Context's fields:
* object_list: the final queryset * object_list: the final queryset
* list_type: type of the items in the list (as Page subclass) * list_selector: dict of { 'tag_query', 'search_query' } plus
* tag_query: tag searched for arguments passed to BaseList.get_base_queryset
* search_query: search terms
* thread_query: thread
* paginator: paginator object * paginator: paginator object
""" """
if 'thread' in request.GET and thread: model = request.GET.get('model')
qs = thread.get_children().not_in_menu()
context['thread_query'] = thread
else:
qs = Publication.objects.all()
qs = qs.live()
# ordering kwargs = {
order = request.GET.get('order') 'filter_model': ProgramPage if model == 'program' else \
if order not in ('asc','desc'): DiffusionPage if model == 'diffusion' else \
order = 'desc' EventPage if model == 'event' else None,
qs = qs.order_by( 'filter_related': 'related' in request.GET and related,
('-' if order == 'desc' else '') + 'first_published_at' 'related_siblings': 'siblings' in request.GET,
) 'sort_asc': 'asc' in request.GET,
}
# type qs = BaseList.get_base_queryset(**kwargs)
type = request.GET.get('type')
if type == 'program':
qs = qs.type(ProgramPage)
context['list_type'] = ProgramPage
elif type == 'diffusion':
qs = qs.type(DiffusionPage)
context['list_type'] = DiffusionPage
elif type == 'event':
qs = qs.type(EventPage)
context['list_type'] = EventPage
# filter by tag # filter by tag
tag = request.GET.get('tag') tag = request.GET.get('tag')
if tag: if tag:
context['tag_query'] = tag kwargs['tag_query'] = tag
qs = qs.filter(tags__name = tag) qs = qs.filter(tags__name = tag)
# search # search
search = request.GET.get('search') search = request.GET.get('search')
if search: if search:
context['search_query'] = search kwargs['search_query'] = search
qs = qs.search(search) qs = qs.search(search)
context['list_selector'] = kwargs
# paginator # paginator
if qs: if qs:
paginator = Paginator(qs, 30) paginator = Paginator(qs, 30)
@ -612,84 +770,6 @@ class ListPage(Page):
return context return context
class BaseDatedList(models.Model):
nav_days = models.SmallIntegerField(
_('navigation days count'),
default = 7,
help_text = _('number of days to display in the navigation header '
'when we use dates')
)
nav_per_week = models.BooleanField(
_('navigation per week'),
default = False,
help_text = _('if selected, show dates navigation per weeks instead '
'of show days equally around the current date')
)
class Meta:
abstract = True
panels = [
MultiFieldPanel([
FieldPanel('nav_days'),
FieldPanel('nav_per_week'),
], heading=_('Navigation')),
]
@staticmethod
def str_to_date(date):
"""
Parse a string and return a regular date or None.
Format is either "YYYY/MM/DD" or "YYYY-MM-DD" or "YYYYMMDD"
"""
try:
exp = r'(?P<year>[0-9]{4})(-|\/)?(?P<month>[0-9]{1,2})(-|\/)?' \
r'(?P<day>[0-9]{1,2})'
date = re.match(exp, date).groupdict()
return datetime.date(
year = int(date['year']), month = int(date['month']),
day = int(date['day'])
)
except:
return None
def get_nav_dates(self, date):
"""
Return a list of dates availables for the navigation
"""
if self.nav_per_week:
first = date.weekday()
else:
first = int((self.nav_days - 1) / 2)
first = date - tz.timedelta(days = first)
return [ first + tz.timedelta(days=i)
for i in range(0, self.nav_days) ]
def get_date_context(self, date):
"""
Return a dict that can be added to the context to be used by
a date_list.
"""
today = tz.now().today()
if not date:
date = today
# next/prev weeks/date bunch
dates = self.get_nav_dates(date)
next = date + tz.timedelta(days=self.nav_days)
prev = date - tz.timedelta(days=self.nav_days)
# context dict
return {
'nav_dates': {
'date': date,
'next': next,
'prev': prev,
'dates': dates,
}
}
class DatedListPage(BaseDatedList,Page): class DatedListPage(BaseDatedList,Page):
body = RichTextField( body = RichTextField(
_('body'), _('body'),
@ -863,6 +943,7 @@ class Menu(ClusterableModel):
@register_snippet @register_snippet
class MenuItem(models.Model): class MenuItem(models.Model):
menu = ParentalKey(Menu, related_name='menu_items')
real_type = models.CharField( real_type = models.CharField(
max_length=32, max_length=32,
blank = True, null = True, blank = True, null = True,
@ -878,7 +959,6 @@ class MenuItem(models.Model):
blank = True, null = True, blank = True, null = True,
help_text=_('menu container\'s "class" attribute') help_text=_('menu container\'s "class" attribute')
) )
menu = ParentalKey(Menu, related_name='menu_items')
panels = [ panels = [
MultiFieldPanel([ MultiFieldPanel([

View File

@ -8,15 +8,15 @@
{% block title %} {% block title %}
<h1> <h1>
{% if search_query %} {% if list_selector.search_query %}
{% blocktrans %}Search in publications for <i>{{ search_query }}</i>{% endblocktrans %} {% blocktrans %}Search in publications for <i>{{ search_query }}</i>{% endblocktrans %}
{% elif tag_query %} {% elif list_selector.tag_query %}
{% blocktrans %}Search in publications for <i>{{ search_query }}</i>{% endblocktrans %} {% blocktrans %}Search in publications for <i>{{ search_query }}</i>{% endblocktrans %}
{% elif thread_query %} {% elif list_selector.filter_related %}
{# should never happen #} {# should never happen #}
{% with title=thread_query.title url=thread_query.url %} {% with title=list_selector.filter_related.title url=list_selector.filter_related.url %}
{% blocktrans %} {% blocktrans %}
Publications in <a href="{{ url }}">{{ title }}</a>{% endblocktrans %} Related to <a href="{{ url }}">{{ title }}</a>{% endblocktrans %}
{% endwith %} {% endwith %}
{% else %} {% else %}
{% blocktrans %}All the publications{% endblocktrans %} {% blocktrans %}All the publications{% endblocktrans %}
@ -26,18 +26,19 @@
{% block content %} {% block content %}
{% if thread_query %} {% with related=list_selector.filter_related %}
{% if related %}
<div class="body summary"> <div class="body summary">
{% image thread_query.cover fill-128x128 class="cover item_cover" %} {% image related.cover fill-128x128 class="cover item_cover" %}
{{ thread_query.summary }} {{ related.summary }}
<a href="{{ thread_query.url }}">{% trans "More about it" %}</a> <a href="{{ related.url }}">{% trans "More about it" %}</a>
</div> </div>
{% elif page.body %} {% elif page.body %}
<div class="body"> <div class="body">
{{ page.body|richtext }} {{ page.body|richtext }}
</div> </div>
{% endif %} {% endif %}
{% endwith %}
{% with list_paginator=paginator %} {% with list_paginator=paginator %}
{% include "cms/snippets/list.html" %} {% include "cms/snippets/list.html" %}

View File

@ -56,9 +56,9 @@
{{ page.owner }} {{ page.owner }}
{% endif %} {% endif %}
</div> </div>
<time datetime="{{ page.first_published_at }}"> <time datetime="{{ page.specific.date }}">
{% trans "Published on " %} {% trans "Published on " %}
{{ page.first_published_at|date:'l d F, H:i' }} {{ page.specific.date|date:'l d F, H:i' }}
</time> </time>
<div class="tags"> <div class="tags">