forked from rc/aircox
rewrite a bit how views and sections work; section's prepare function; website interfaces; section.as_view returns a view containing it and usable as it
This commit is contained in:
parent
32a30004d6
commit
4e5d90fb1d
|
@ -34,21 +34,21 @@ class Route:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_queryset(cl, model, request, **kwargs):
|
def get_queryset(cl, website, request, **kwargs):
|
||||||
"""
|
"""
|
||||||
Called by the view to get the queryset when it is needed
|
Called by the view to get the queryset when it is needed
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_object(cl, model, request, **kwargs):
|
def get_object(cl, website, request, **kwargs):
|
||||||
"""
|
"""
|
||||||
Called by the view to get the object when it is needed
|
Called by the view to get the object when it is needed
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_title(cl, model, request, **kwargs):
|
def get_title(cl, website, request, **kwargs):
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -56,8 +56,12 @@ class Route:
|
||||||
return name + '.' + cl.name
|
return name + '.' + cl.name
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def as_url(cl, name, view, view_kwargs = None):
|
def make_pattern(cl, prefix = ''):
|
||||||
pattern = '^{}/{}'.format(name, cl.name)
|
"""
|
||||||
|
Make a url pattern using prefix as prefix and cl.params as
|
||||||
|
parameters.
|
||||||
|
"""
|
||||||
|
pattern = prefix
|
||||||
if cl.params:
|
if cl.params:
|
||||||
pattern += ''.join([
|
pattern += ''.join([
|
||||||
'{pre}/(?P<{name}>{regexp}){post}'.format(
|
'{pre}/(?P<{name}>{regexp}){post}'.format(
|
||||||
|
@ -68,13 +72,13 @@ class Route:
|
||||||
for name, regexp, *optional in cl.params
|
for name, regexp, *optional in cl.params
|
||||||
])
|
])
|
||||||
pattern += '/?$'
|
pattern += '/?$'
|
||||||
|
return pattern
|
||||||
|
|
||||||
kwargs = {
|
@classmethod
|
||||||
'route': cl,
|
def as_url(cl, name, view, kwargs = None):
|
||||||
}
|
pattern = cl.make_pattern('^{}/{}'.format(name, cl.name))
|
||||||
if view_kwargs:
|
kwargs = kwargs.copy() if kwargs else {}
|
||||||
kwargs.update(view_kwargs)
|
kwargs['route'] = cl
|
||||||
|
|
||||||
return url(pattern, view, kwargs = kwargs,
|
return url(pattern, view, kwargs = kwargs,
|
||||||
name = cl.make_view_name(name))
|
name = cl.make_view_name(name))
|
||||||
|
|
||||||
|
|
108
cms/sections.py
108
cms/sections.py
|
@ -29,20 +29,19 @@ class Viewable:
|
||||||
@classmethod
|
@classmethod
|
||||||
def as_view (cl, *args, **kwargs):
|
def as_view (cl, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Similar to View.as_view, but instead, wrap a constructor of the
|
Create a view containing the current viewable, using a subclass
|
||||||
given class that is used as is.
|
of aircox.cms.views.BaseView.
|
||||||
|
All the arguments are passed to the view directly.
|
||||||
"""
|
"""
|
||||||
def func(**kwargs_):
|
from aircox.cms.views import PageView
|
||||||
if kwargs_:
|
kwargs['sections'] = cl
|
||||||
kwargs.update(kwargs_)
|
return PageView.as_view(*args, **kwargs)
|
||||||
instance = cl(*args, **kwargs)
|
|
||||||
return instance
|
|
||||||
return func
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def extends (cl, **kwargs):
|
def extends (cl, **kwargs):
|
||||||
"""
|
"""
|
||||||
Return a sub class where the given attribute have been updated
|
Return a sub class where the given attribute have been updated
|
||||||
|
using kwargs.
|
||||||
"""
|
"""
|
||||||
class Sub(cl):
|
class Sub(cl):
|
||||||
pass
|
pass
|
||||||
|
@ -60,13 +59,20 @@ class Sections(Viewable, list):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def prepare(self, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
prepare all children sections
|
||||||
|
"""
|
||||||
for i, section in enumerate(self):
|
for i, section in enumerate(self):
|
||||||
if callable(section) or type(section) == type:
|
if callable(section) or type(section) == type:
|
||||||
self[i] = section()
|
self[i] = section()
|
||||||
|
self[i].prepare(*args, **kwargs)
|
||||||
|
|
||||||
def render(self, *args, **kwargs):
|
def render(self, *args, **kwargs):
|
||||||
|
if args:
|
||||||
|
self.prepare(*args, **kwargs)
|
||||||
return ''.join([
|
return ''.join([
|
||||||
section.render(*args, **kwargs)
|
section.render()
|
||||||
for section in self
|
for section in self
|
||||||
])
|
])
|
||||||
|
|
||||||
|
@ -127,6 +133,7 @@ class Section(Viewable, View):
|
||||||
its value is an empty string (prints an empty string).
|
its value is an empty string (prints an empty string).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
view = None
|
||||||
request = None
|
request = None
|
||||||
object = None
|
object = None
|
||||||
kwargs = None
|
kwargs = None
|
||||||
|
@ -138,8 +145,8 @@ class Section(Viewable, View):
|
||||||
else:
|
else:
|
||||||
self.css_class = css_class
|
self.css_class = css_class
|
||||||
|
|
||||||
def __init__ (self, *args, **kwargs):
|
def __init__ (self, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
self.add_css_class('section')
|
self.add_css_class('section')
|
||||||
if type(self) != Section:
|
if type(self) != Section:
|
||||||
|
@ -159,11 +166,7 @@ class Section(Viewable, View):
|
||||||
"""
|
"""
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def get_context_data(self, request = None, object = None, **kwargs):
|
def get_context_data(self):
|
||||||
if request: self.request = request
|
|
||||||
if object: self.object = object
|
|
||||||
if kwargs: self.kwargs = kwargs
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'view': self,
|
'view': self,
|
||||||
'exp': (hasattr(self, '_exposure') and self._exposure) or None,
|
'exp': (hasattr(self, '_exposure') and self._exposure) or None,
|
||||||
|
@ -178,8 +181,24 @@ class Section(Viewable, View):
|
||||||
'embed': True,
|
'embed': True,
|
||||||
}
|
}
|
||||||
|
|
||||||
def render(self, request, object=None, **kwargs):
|
def prepare(self, view, **kwargs):
|
||||||
context = self.get_context_data(request=request, object=object, **kwargs)
|
"""
|
||||||
|
initialize the object with valuable informations.
|
||||||
|
"""
|
||||||
|
self.view = view
|
||||||
|
self.request = view.request
|
||||||
|
self.kwargs = view.kwargs
|
||||||
|
if hasattr(view, 'object'):
|
||||||
|
self.object = view.object
|
||||||
|
|
||||||
|
def render(self, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Render the section as a string. Use *args and **kwargs to prepare
|
||||||
|
the section, then get_context_data and render.
|
||||||
|
"""
|
||||||
|
if args and not self.view:
|
||||||
|
self.prepare(*args, **kwargs)
|
||||||
|
context = self.get_context_data()
|
||||||
|
|
||||||
is_empty = self.is_empty()
|
is_empty = self.is_empty()
|
||||||
if not context or (is_empty and not self.message_empty):
|
if not context or (is_empty and not self.message_empty):
|
||||||
|
@ -189,7 +208,9 @@ class Section(Viewable, View):
|
||||||
context['content'] = self.message_empty
|
context['content'] = self.message_empty
|
||||||
|
|
||||||
context['embed'] = True
|
context['embed'] = True
|
||||||
return render_to_string(self.template_name, context, request=request)
|
return render_to_string(
|
||||||
|
self.template_name, context, request=self.request
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class Image(Section):
|
class Image(Section):
|
||||||
|
@ -336,11 +357,17 @@ class List(Section):
|
||||||
ListItem(item) for item in items
|
ListItem(item) for item in items
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def as_view(cl, *args, **kwargs):
|
||||||
|
from aircox.cms.views import PostListView
|
||||||
|
kwargs['sections'] = cl
|
||||||
|
return PostListView.as_view(*args, **kwargs)
|
||||||
|
|
||||||
def is_empty(self):
|
def is_empty(self):
|
||||||
return not self.object_list
|
return not self.object_list
|
||||||
|
|
||||||
def get_object_list(self):
|
def get_object_list(self):
|
||||||
return self.object_list
|
return self.object_list or []
|
||||||
|
|
||||||
def prepare_list(self, object_list):
|
def prepare_list(self, object_list):
|
||||||
"""
|
"""
|
||||||
|
@ -349,8 +376,7 @@ class List(Section):
|
||||||
"""
|
"""
|
||||||
return object_list
|
return object_list
|
||||||
|
|
||||||
def get_context_data(self, request, object=None, object_list=None,
|
def get_context_data(self, *args, object_list=None, **kwargs):
|
||||||
*args, **kwargs):
|
|
||||||
"""
|
"""
|
||||||
Return a context that is passed to the template at rendering, with
|
Return a context that is passed to the template at rendering, with
|
||||||
the following values:
|
the following values:
|
||||||
|
@ -364,21 +390,20 @@ class List(Section):
|
||||||
|
|
||||||
Set `request`, `object`, `object_list` and `kwargs` in self.
|
Set `request`, `object`, `object_list` and `kwargs` in self.
|
||||||
"""
|
"""
|
||||||
if request: self.request = request
|
if args:
|
||||||
if object: self.object = object
|
self.prepare(*args, **kwargs)
|
||||||
if kwargs: self.kwargs = kwargs
|
|
||||||
|
|
||||||
if object_list is None:
|
if object_list is None:
|
||||||
object_list = self.object_list or self.get_object_list()
|
object_list = self.object_list or self.get_object_list()
|
||||||
if not object_list and not self.message_empty:
|
if not object_list and not self.message_empty:
|
||||||
return
|
return {}
|
||||||
|
|
||||||
self.object_list = object_list
|
self.object_list = object_list
|
||||||
if object_list:
|
if object_list:
|
||||||
object_list = self.prepare_list(object_list)
|
object_list = self.prepare_list(object_list)
|
||||||
Actions.make(request, object_list = object_list)
|
Actions.make(self.request, object_list = object_list)
|
||||||
|
|
||||||
context = super().get_context_data(request, object, *args, **kwargs)
|
context = super().get_context_data()
|
||||||
context.update({
|
context.update({
|
||||||
'list': self,
|
'list': self,
|
||||||
'object_list': object_list[:self.paginate_by]
|
'object_list': object_list[:self.paginate_by]
|
||||||
|
@ -510,13 +535,16 @@ class Menu(Section):
|
||||||
if not self.attrs:
|
if not self.attrs:
|
||||||
self.attrs = {}
|
self.attrs = {}
|
||||||
|
|
||||||
def get_context_data(self, *args, **kwargs):
|
def prepare(self, *args, **kwargs):
|
||||||
super().get_context_data(*args, **kwargs)
|
super().prepare(*args, **kwargs)
|
||||||
|
self.sections.prepare(*args, **kwargs)
|
||||||
|
|
||||||
|
def get_context_data(self):
|
||||||
return {
|
return {
|
||||||
'tag': self.tag,
|
'tag': self.tag,
|
||||||
'css_class': self.css_class,
|
'css_class': self.css_class,
|
||||||
'attrs': self.attrs,
|
'attrs': self.attrs,
|
||||||
'content': self.sections.render(*args, **kwargs)
|
'content': self.sections.render()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -579,16 +607,18 @@ class Calendar(Section):
|
||||||
date = date.replace(day = 1)
|
date = date.replace(day = 1)
|
||||||
|
|
||||||
first, count = calendar.monthrange(date.year, date.month)
|
first, count = calendar.monthrange(date.year, date.month)
|
||||||
|
def make_date(date, day):
|
||||||
|
date += tz.timedelta(days=day)
|
||||||
|
return (
|
||||||
|
date, self.model.reverse(
|
||||||
|
routes.DateRoute, year = date.year,
|
||||||
|
month = date.month, day = date.day
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
context.update({
|
context.update({
|
||||||
'first_weekday': first,
|
'first_weekday': first,
|
||||||
'days': [
|
'days': [ make_date(date, day) for day in range(0, count) ],
|
||||||
(date + tz.timedelta(days=day), self.model.reverse(
|
|
||||||
routes.DateRoute, year = date.year, month = date.month,
|
|
||||||
day = day
|
|
||||||
)
|
|
||||||
) for day in range(0, count)
|
|
||||||
],
|
|
||||||
|
|
||||||
'today': datetime.date.today(),
|
'today': datetime.date.today(),
|
||||||
'this_month': date,
|
'this_month': date,
|
||||||
'prev_month': date - tz.timedelta(days=10),
|
'prev_month': date - tz.timedelta(days=10),
|
||||||
|
|
52
cms/views.py
52
cms/views.py
|
@ -21,7 +21,7 @@ class BaseView:
|
||||||
# Request GET params:
|
# Request GET params:
|
||||||
* embed: view is embedded, render only the content of the view
|
* embed: view is embedded, render only the content of the view
|
||||||
"""
|
"""
|
||||||
template_name = ''
|
template_name = 'aircox/cms/website.html'
|
||||||
"""it is set to "aircox/cms/detail.html" to render multiple sections"""
|
"""it is set to "aircox/cms/detail.html" to render multiple sections"""
|
||||||
sections = None
|
sections = None
|
||||||
"""sections used to render the page"""
|
"""sections used to render the page"""
|
||||||
|
@ -43,9 +43,6 @@ class BaseView:
|
||||||
self.sections = sections
|
self.sections = sections
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
def __section_is_single(self):
|
|
||||||
return not issubclass(type(self.sections), list)
|
|
||||||
|
|
||||||
def add_css_class(self, css_class):
|
def add_css_class(self, css_class):
|
||||||
"""
|
"""
|
||||||
Add the given class to the current class list if not yet present.
|
Add the given class to the current class list if not yet present.
|
||||||
|
@ -65,19 +62,15 @@ class BaseView:
|
||||||
|
|
||||||
# update from sections
|
# update from sections
|
||||||
if self.sections:
|
if self.sections:
|
||||||
if self.__section_is_single():
|
if issubclass(type(self.sections), sections.Section):
|
||||||
self.template_name = self.sections.template_name
|
self.template_name = self.sections.template_name
|
||||||
context.update(self.sections.get_context_data(
|
self.sections.prepare(self)
|
||||||
self.request,
|
context.update(self.sections.get_context_data())
|
||||||
object_list = hasattr(self, 'object_list') and \
|
|
||||||
self.object_list,
|
|
||||||
**self.kwargs
|
|
||||||
) or {})
|
|
||||||
else:
|
else:
|
||||||
if not self.template_name:
|
if not self.template_name:
|
||||||
self.template_name = 'aircox/cms/detail.html'
|
self.template_name = 'aircox/cms/detail.html'
|
||||||
context.update({
|
context.update({
|
||||||
'content': self.sections.render(self.request, **kwargs)
|
'content': self.sections.render(self)
|
||||||
})
|
})
|
||||||
|
|
||||||
context.update(super().get_context_data(**kwargs))
|
context.update(super().get_context_data(**kwargs))
|
||||||
|
@ -98,7 +91,7 @@ class BaseView:
|
||||||
else None
|
else None
|
||||||
if self.menus:
|
if self.menus:
|
||||||
context['menus'] = {
|
context['menus'] = {
|
||||||
k: v.render(self.request, **kwargs)
|
k: v.render(self)
|
||||||
for k, v in self.menus.items()
|
for k, v in self.menus.items()
|
||||||
if v is not self
|
if v is not self
|
||||||
}
|
}
|
||||||
|
@ -148,6 +141,12 @@ class PostListView(BaseView, ListView):
|
||||||
return super().dispatch(request, *args, **kwargs)
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
|
default = self.prepare_list()
|
||||||
|
if default:
|
||||||
|
qs = self.list.get_object_list()
|
||||||
|
if qs:
|
||||||
|
return qs
|
||||||
|
|
||||||
if self.route:
|
if self.route:
|
||||||
qs = self.route.get_queryset(self.model, self.request,
|
qs = self.route.get_queryset(self.model, self.request,
|
||||||
**self.kwargs)
|
**self.kwargs)
|
||||||
|
@ -166,15 +165,23 @@ class PostListView(BaseView, ListView):
|
||||||
return qs
|
return qs
|
||||||
|
|
||||||
def prepare_list(self):
|
def prepare_list(self):
|
||||||
|
"""
|
||||||
|
Prepare the list and return True if the list has been created using
|
||||||
|
defaults.
|
||||||
|
"""
|
||||||
if not self.list:
|
if not self.list:
|
||||||
self.list = sections.List(
|
self.list = sections.List(
|
||||||
truncate = 32,
|
truncate = 32,
|
||||||
paginate_by = 0,
|
paginate_by = 0,
|
||||||
)
|
)
|
||||||
else:
|
default = True
|
||||||
|
elif type(self.list) == type:
|
||||||
self.list = self.list(paginate_by = 0)
|
self.list = self.list(paginate_by = 0)
|
||||||
self.template_name = self.list.template_name
|
self.template_name = self.list.template_name
|
||||||
self.css_class = self.list.css_class
|
self.css_class = self.list.css_class
|
||||||
|
default = False
|
||||||
|
|
||||||
|
self.list.prepare(self)
|
||||||
|
|
||||||
if self.request.GET.get('fields'):
|
if self.request.GET.get('fields'):
|
||||||
self.list.fields = [
|
self.list.fields = [
|
||||||
|
@ -184,12 +191,14 @@ class PostListView(BaseView, ListView):
|
||||||
|
|
||||||
# done in list
|
# done in list
|
||||||
# Actions.make(self.request, object_list = self.object_list)
|
# Actions.make(self.request, object_list = self.object_list)
|
||||||
|
return default
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
self.prepare_list()
|
|
||||||
self.add_css_class('list')
|
self.add_css_class('list')
|
||||||
|
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
|
if not context.get('object_list'):
|
||||||
|
context['object_list'] = self.list.object_list
|
||||||
|
|
||||||
if self.route and not context.get('title'):
|
if self.route and not context.get('title'):
|
||||||
context['title'] = self.route.get_title(
|
context['title'] = self.route.get_title(
|
||||||
|
@ -232,12 +241,14 @@ class PostDetailView(BaseView, DetailView):
|
||||||
"""
|
"""
|
||||||
Handle new comments
|
Handle new comments
|
||||||
"""
|
"""
|
||||||
|
self.sections.prepare(self)
|
||||||
if not self.comments:
|
if not self.comments:
|
||||||
for section in self.sections:
|
for section in self.sections:
|
||||||
if issubclass(type(section), sections.Comments):
|
if issubclass(type(section), sections.Comments):
|
||||||
self.comments = section
|
self.comments = section
|
||||||
|
|
||||||
self.object = self.get_object()
|
self.object = self.get_object()
|
||||||
|
self.comments.prepare(self)
|
||||||
self.comments.post(self, request, self.object)
|
self.comments.post(self, request, self.object)
|
||||||
return self.get(request, *args, **kwargs)
|
return self.get(request, *args, **kwargs)
|
||||||
|
|
||||||
|
@ -249,8 +260,5 @@ class PageView(BaseView, TemplateView):
|
||||||
If sections is a list of sections, then render like a detail view;
|
If sections is a list of sections, then render like a detail view;
|
||||||
If it is a single section, render it as website.html view;
|
If it is a single section, render it as website.html view;
|
||||||
"""
|
"""
|
||||||
# dirty hack in order to accept a "model" kwargs, to allow "model=None"
|
|
||||||
# in routes. Cf. website.register (at if model / else)
|
|
||||||
model = None
|
|
||||||
|
|
||||||
|
|
||||||
|
|
146
cms/website.py
146
cms/website.py
|
@ -9,6 +9,7 @@ import aircox.cms.routes as routes_
|
||||||
import aircox.cms.views as views
|
import aircox.cms.views as views
|
||||||
import aircox.cms.models as models
|
import aircox.cms.models as models
|
||||||
import aircox.cms.sections as sections
|
import aircox.cms.sections as sections
|
||||||
|
import aircox.cms.sections as sections_
|
||||||
|
|
||||||
|
|
||||||
class Website:
|
class Website:
|
||||||
|
@ -37,7 +38,7 @@ class Website:
|
||||||
|
|
||||||
## components
|
## components
|
||||||
Registration = namedtuple('Registration',
|
Registration = namedtuple('Registration',
|
||||||
'name model routes as_default'
|
'name model routes default'
|
||||||
)
|
)
|
||||||
|
|
||||||
urls = []
|
urls = []
|
||||||
|
@ -64,7 +65,7 @@ class Website:
|
||||||
if self.comments_routes:
|
if self.comments_routes:
|
||||||
self.register_comments()
|
self.register_comments()
|
||||||
|
|
||||||
def register_model(self, name, model, as_default):
|
def register_model(self, name, model, default):
|
||||||
"""
|
"""
|
||||||
Register a model and update model's fields with few data:
|
Register a model and update model's fields with few data:
|
||||||
- _website: back ref to self
|
- _website: back ref to self
|
||||||
|
@ -77,9 +78,9 @@ class Website:
|
||||||
if reg.model is model:
|
if reg.model is model:
|
||||||
return reg
|
return reg
|
||||||
raise ValueError('A model has yet been registered under "{}"'
|
raise ValueError('A model has yet been registered under "{}"'
|
||||||
.format(name))
|
.format(reg.model, name))
|
||||||
|
|
||||||
reg = self.Registration(name, model, [], as_default)
|
reg = self.Registration(name, model, [], default)
|
||||||
self.registry[name] = reg
|
self.registry[name] = reg
|
||||||
model._registration = reg
|
model._registration = reg
|
||||||
model._website = self
|
model._website = self
|
||||||
|
@ -97,74 +98,89 @@ class Website:
|
||||||
continue
|
continue
|
||||||
self.exposures += section._exposure.gather(section)
|
self.exposures += section._exposure.gather(section)
|
||||||
|
|
||||||
def register(self, name, routes = [], view = views.PageView,
|
|
||||||
model = None, sections = None,
|
|
||||||
as_default = False, **view_kwargs):
|
|
||||||
"""
|
|
||||||
Register a view using given name and routes. If model is given,
|
|
||||||
register the views for it.
|
|
||||||
|
|
||||||
* name is used to register the routes as urls and the model if given
|
def __route_to_url(self, name, route, view, sections, kwargs):
|
||||||
* routes: can be a path or a route used to generate urls for the view.
|
# route can be a tuple
|
||||||
Can be a one item or a list of items.
|
if type(route) in (tuple,list):
|
||||||
* view: route that is registered for the given routes
|
route, view = route
|
||||||
* model: model being registrated. If given, register it in the website
|
view = view.as_view(
|
||||||
under the given name, and make it available to the view.
|
website = self, **kwargs
|
||||||
* as_default: make the view available as a default view.
|
)
|
||||||
"""
|
|
||||||
if type(routes) not in (tuple, list):
|
|
||||||
routes = [ routes ]
|
|
||||||
|
|
||||||
# model registration
|
# route can be a route or a string
|
||||||
if model:
|
if type(route) == type and issubclass(route, routes_.Route):
|
||||||
reg = self.register_model(name, model, as_default)
|
return route.as_url(name, view)
|
||||||
reg.routes.extend(routes)
|
|
||||||
view_kwargs['model'] = model
|
|
||||||
else:
|
|
||||||
view_kwargs['model'] = None
|
|
||||||
|
|
||||||
# init view
|
return url(
|
||||||
if not view_kwargs.get('menus'):
|
slugify(name) if not route else str(route),
|
||||||
view_kwargs['menus'] = self.menus
|
view = view, name = name, kwargs = kwargs
|
||||||
|
|
||||||
if sections:
|
|
||||||
self.register_exposures(sections)
|
|
||||||
view_kwargs['sections'] = sections
|
|
||||||
|
|
||||||
view = view.as_view(
|
|
||||||
website = self,
|
|
||||||
**view_kwargs
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# url gen
|
def add_page(self, name, routes = [], view = views.PageView,
|
||||||
|
sections = None, default = False, **kwargs):
|
||||||
|
"""
|
||||||
|
Add a view and declare it on the given routes.
|
||||||
|
|
||||||
|
* routes: list of routes or patterns, or tuple (route/pattern, view)
|
||||||
|
to force a view to be used;
|
||||||
|
* view: view to use by default to render the page;
|
||||||
|
* sections: sections to display on the view;
|
||||||
|
* default: use this as a default view;
|
||||||
|
* kwargs: extra kwargs to pass to the view;
|
||||||
|
|
||||||
|
If view is a section, generate a PageView with this section as
|
||||||
|
child. Note: the kwargs are passed to the PageView constructor.
|
||||||
|
"""
|
||||||
|
if view and issubclass(type(view), sections_.Section):
|
||||||
|
sections, view = view, views.PageView
|
||||||
|
|
||||||
|
if not kwargs.get('menus'):
|
||||||
|
kwargs['menus'] = self.menus
|
||||||
|
if sections:
|
||||||
|
self.register_exposures(sections)
|
||||||
|
|
||||||
|
view = view.as_view(website = self, sections = sections, **kwargs)
|
||||||
|
|
||||||
|
if not hasattr(routes, '__iter__'):
|
||||||
|
routes = [routes]
|
||||||
|
|
||||||
self.urls += [
|
self.urls += [
|
||||||
route.as_url(name, view)
|
self.__route_to_url(name, route, view, sections, kwargs)
|
||||||
if type(route) == type and issubclass(route, routes_.Route)
|
|
||||||
else url(slugify(name) if not route else route,
|
|
||||||
view = view, name = name)
|
|
||||||
for route in routes
|
for route in routes
|
||||||
]
|
]
|
||||||
|
|
||||||
def register_dl(self, name, model, sections = None, routes = None,
|
def add_model(self, name, model, sections = None, routes = None,
|
||||||
list_view = views.PostListView,
|
default = False,
|
||||||
detail_view = views.PostDetailView,
|
list_view = views.PostListView,
|
||||||
list_kwargs = {}, detail_kwargs = {},
|
detail_view = views.PostDetailView,
|
||||||
as_default = False):
|
**kwargs):
|
||||||
"""
|
"""
|
||||||
Register a detail and list view for a given model, using
|
Add a model to the Website, register it and declare its routes.
|
||||||
routes.
|
|
||||||
|
|
||||||
Just a wrapper around `register`.
|
* model: model to register
|
||||||
|
* sections: sections to display in the *detail* view;
|
||||||
|
* routes: routes to use for the *list* view -- cf. add_page.routes;
|
||||||
|
* default: use as default route;
|
||||||
|
* list_view: use it as view for lists;
|
||||||
|
* detail_view: use it as view for details;
|
||||||
|
* kwargs: extra kwargs arguments to pass to the view;
|
||||||
"""
|
"""
|
||||||
|
# register the model and the routes
|
||||||
|
reg = self.register_model(name, model, default)
|
||||||
|
reg.routes.extend([
|
||||||
|
route[0] if type(route) in (list,tuple) else route
|
||||||
|
for route in routes
|
||||||
|
])
|
||||||
|
reg.routes.append(routes_.DetailRoute)
|
||||||
|
|
||||||
|
kwargs['model'] = model
|
||||||
if sections:
|
if sections:
|
||||||
self.register(name, [ routes_.DetailRoute ], view = detail_view,
|
self.add_page(name, view = detail_view, sections = sections,
|
||||||
model = model, sections = sections,
|
routes = routes_.DetailRoute, default = default,
|
||||||
as_default = as_default,
|
**kwargs)
|
||||||
**detail_kwargs)
|
|
||||||
if routes:
|
if routes:
|
||||||
self.register(name, routes, view = list_view,
|
self.add_page(name, view = list_view, routes = routes,
|
||||||
model = model, as_default = as_default,
|
default = default, **kwargs)
|
||||||
**list_kwargs)
|
|
||||||
|
|
||||||
def register_comments(self):
|
def register_comments(self):
|
||||||
"""
|
"""
|
||||||
|
@ -173,16 +189,12 @@ class Website:
|
||||||
|
|
||||||
Just a wrapper around `register`.
|
Just a wrapper around `register`.
|
||||||
"""
|
"""
|
||||||
self.register(
|
self.add_model(
|
||||||
'comment',
|
'comment',
|
||||||
view = views.PostListView,
|
|
||||||
routes = [routes.ThreadRoute],
|
|
||||||
model = models.Comment,
|
model = models.Comment,
|
||||||
|
routes = [routes.ThreadRoute],
|
||||||
css_class = 'comments',
|
css_class = 'comments',
|
||||||
list = sections.Comments(
|
list = sections.Comments
|
||||||
truncate = 30,
|
|
||||||
fields = ['content','author','date','time'],
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def set_menu(self, menu):
|
def set_menu(self, menu):
|
||||||
|
@ -204,7 +216,7 @@ class Website:
|
||||||
given route.
|
given route.
|
||||||
"""
|
"""
|
||||||
for r in self.registry.values():
|
for r in self.registry.values():
|
||||||
if r.as_default and route in r.routes:
|
if r.default and route in r.routes:
|
||||||
return r
|
return r
|
||||||
|
|
||||||
def reverse(self, model, route, use_default = True, **kwargs):
|
def reverse(self, model, route, use_default = True, **kwargs):
|
||||||
|
@ -226,7 +238,7 @@ class Website:
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
for r in self.registry.values():
|
for r in self.registry.values():
|
||||||
if r.as_default and route in r.routes:
|
if r.default and route in r.routes:
|
||||||
try:
|
try:
|
||||||
name = route.make_view_name(r.name)
|
name = route.make_view_name(r.name)
|
||||||
return reverse(name, kwargs = kwargs)
|
return reverse(name, kwargs = kwargs)
|
||||||
|
|
|
@ -755,7 +755,7 @@ class Track(Related):
|
||||||
blank=True,
|
blank=True,
|
||||||
)
|
)
|
||||||
pos_in_secs = models.BooleanField(
|
pos_in_secs = models.BooleanField(
|
||||||
_('use seconds'),
|
_('seconds'),
|
||||||
default = False,
|
default = False,
|
||||||
help_text=_('position in the playlist is expressed in seconds')
|
help_text=_('position in the playlist is expressed in seconds')
|
||||||
)
|
)
|
||||||
|
|
|
@ -95,7 +95,6 @@ class Player(sections.Section):
|
||||||
|
|
||||||
def get_context_data(self, *args, **kwargs):
|
def get_context_data(self, *args, **kwargs):
|
||||||
context = super().get_context_data(*args, **kwargs)
|
context = super().get_context_data(*args, **kwargs)
|
||||||
|
|
||||||
context.update({
|
context.update({
|
||||||
'base_template': 'aircox/cms/section.html',
|
'base_template': 'aircox/cms/section.html',
|
||||||
'live_streams': self.live_streams,
|
'live_streams': self.live_streams,
|
||||||
|
@ -220,7 +219,7 @@ class Diffusions(sections.List):
|
||||||
|
|
||||||
return ' / \n'.join([str_sched(sched)
|
return ' / \n'.join([str_sched(sched)
|
||||||
for sched in programs.Schedule.objects \
|
for sched in programs.Schedule.objects \
|
||||||
.filter(program = self.object.related.pk)
|
.filter(program = self.object and self.object.related.pk)
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|
||||||
|
@ -256,27 +255,42 @@ class Sounds(sections.List):
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class Schedule(Diffusions):
|
|
||||||
|
class ListByDate(sections.List):
|
||||||
"""
|
"""
|
||||||
Render a list of diffusions in the form of a schedule
|
List that add a navigation by date in its header.
|
||||||
"""
|
"""
|
||||||
template_name = 'aircox/website/schedule.html'
|
template_name = 'aircox/website/list_by_date.html'
|
||||||
date = None
|
|
||||||
nav_date_format = '%a. %d'
|
|
||||||
fields = [ 'time', 'image', 'title']
|
|
||||||
message_empty = ''
|
message_empty = ''
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
model = None
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
self.add_css_class('schedule')
|
|
||||||
|
|
||||||
@staticmethod
|
date = None
|
||||||
def get_week_dates(date):
|
"""
|
||||||
|
date of the items to print
|
||||||
|
"""
|
||||||
|
nav_days = 7
|
||||||
|
"""
|
||||||
|
number of days to display in the header
|
||||||
|
"""
|
||||||
|
nav_date_format = '%a. %d'
|
||||||
|
"""
|
||||||
|
format of dates to display in the header
|
||||||
|
"""
|
||||||
|
nav_per_week = True
|
||||||
|
"""
|
||||||
|
if true, print days in header by week
|
||||||
|
"""
|
||||||
|
|
||||||
|
def nav_dates(self, date):
|
||||||
"""
|
"""
|
||||||
Return a list of dates of the week of the given date.
|
Return a list of dates of the week of the given date.
|
||||||
"""
|
"""
|
||||||
first = date - tz.timedelta(days=date.weekday())
|
first = int((self.nav_days - 1) / 2)
|
||||||
return [ first + tz.timedelta(days=i) for i in range(0, 7) ]
|
first = date - tz.timedelta(days=date.weekday()) \
|
||||||
|
if self.nav_per_week else \
|
||||||
|
date - tz.timedelta(days=first)
|
||||||
|
return [ first + tz.timedelta(days=i) for i in range(0, self.nav_days) ]
|
||||||
|
|
||||||
def date_or_default(self):
|
def date_or_default(self):
|
||||||
if self.date:
|
if self.date:
|
||||||
|
@ -287,40 +301,20 @@ class Schedule(Diffusions):
|
||||||
day = int(self.kwargs['day']),
|
day = int(self.kwargs['day']),
|
||||||
hour = 0, minute = 0, second = 0,
|
hour = 0, minute = 0, second = 0,
|
||||||
microsecond = 0)
|
microsecond = 0)
|
||||||
return tz.datetime.now()
|
return tz.now()
|
||||||
|
|
||||||
def get_object_list(self):
|
|
||||||
date = self.date_or_default()
|
|
||||||
return routes.DateRoute.get_queryset(
|
|
||||||
models.Diffusion, self.request, date.year, date.month,
|
|
||||||
date.day
|
|
||||||
).order_by('date')
|
|
||||||
|
|
||||||
def get_context_data(self, *args, **kwargs):
|
def get_context_data(self, *args, **kwargs):
|
||||||
context = super().get_context_data(*args, **kwargs)
|
context = super().get_context_data(*args, **kwargs)
|
||||||
|
|
||||||
date = self.date_or_default()
|
date = self.date_or_default()
|
||||||
dates = [
|
dates = [ (date, self.get_date_url(date))
|
||||||
(date, models.Diffusion.reverse(
|
for date in self.nav_dates(date) ]
|
||||||
routes.DateRoute,
|
|
||||||
year = date.year, month = date.month, day = date.day
|
|
||||||
))
|
|
||||||
for date in self.get_week_dates(date)
|
|
||||||
]
|
|
||||||
|
|
||||||
next_week = dates[-1][0] + tz.timedelta(days=1)
|
next_week = dates[-1][0] + tz.timedelta(days=1)
|
||||||
next_week = models.Diffusion.reverse(
|
next_week = self.get_date_url(next_week)
|
||||||
routes.DateRoute,
|
|
||||||
year = next_week.year, month = next_week.month,
|
|
||||||
day = next_week.day
|
|
||||||
)
|
|
||||||
|
|
||||||
prev_week = dates[0][0] - tz.timedelta(days=1)
|
prev_week = dates[0][0] - tz.timedelta(days=1)
|
||||||
prev_week = models.Diffusion.reverse(
|
prev_week = self.get_date_url(prev_week)
|
||||||
routes.DateRoute,
|
|
||||||
year = prev_week.year, month = prev_week.month,
|
|
||||||
day = prev_week.day
|
|
||||||
)
|
|
||||||
|
|
||||||
context.update({
|
context.update({
|
||||||
'date': date,
|
'date': date,
|
||||||
|
@ -330,15 +324,90 @@ class Schedule(Diffusions):
|
||||||
})
|
})
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_date_url(date):
|
||||||
|
"""
|
||||||
|
return a url to the list for the given date
|
||||||
|
"""
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def url(self):
|
def url(self):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
class Logs(Schedule):
|
class Schedule(Diffusions,ListByDate):
|
||||||
|
"""
|
||||||
|
Render a list of diffusions in the form of a schedule
|
||||||
|
"""
|
||||||
|
fields = [ 'time', 'image', 'title', 'content', 'info', 'actions' ]
|
||||||
|
truncate = 30
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.add_css_class('schedule')
|
||||||
|
|
||||||
|
def get_object_list(self):
|
||||||
|
date = self.date_or_default()
|
||||||
|
return routes.DateRoute.get_queryset(
|
||||||
|
models.Diffusion, self.request, date.year, date.month,
|
||||||
|
date.day
|
||||||
|
).order_by('date')
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_date_url(date):
|
||||||
|
"""
|
||||||
|
return an url for the given date
|
||||||
|
"""
|
||||||
|
return models.Diffusion.reverse(
|
||||||
|
routes.DateRoute,
|
||||||
|
year = date.year, month = date.month, day = date.day,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Logs(ListByDate):
|
||||||
"""
|
"""
|
||||||
Return a list of played stream sounds and diffusions.
|
Return a list of played stream sounds and diffusions.
|
||||||
"""
|
"""
|
||||||
template_name = 'aircox/website/schedule.html'
|
|
||||||
# HERE -- + rename aircox/website/schedule to dated_list
|
@staticmethod
|
||||||
|
def make_item(log):
|
||||||
|
"""
|
||||||
|
Return a list of items to add to the playlist.
|
||||||
|
"""
|
||||||
|
if issubclass(type(log.related), programs.Diffusion):
|
||||||
|
diff = log.related
|
||||||
|
post = models.Diffusion.objects.filter(related = diff).first() \
|
||||||
|
or models.Program.objects.filter(related = diff.program).first() \
|
||||||
|
or ListItem(title = diff.program.name)
|
||||||
|
post.date = diff.start
|
||||||
|
return post
|
||||||
|
|
||||||
|
if issubclass(type(log.related), programs.Track):
|
||||||
|
track = log.related
|
||||||
|
post = ListItem(
|
||||||
|
title = '{artist} — {name}'.format(
|
||||||
|
artist = track.artist,
|
||||||
|
name = track.name,
|
||||||
|
),
|
||||||
|
date = log.date,
|
||||||
|
content = track.info,
|
||||||
|
info = '♫',
|
||||||
|
)
|
||||||
|
return post
|
||||||
|
|
||||||
|
def get_object_list(self):
|
||||||
|
station = self.view._website.station
|
||||||
|
qs = station.get_played(
|
||||||
|
models = [ programs.Diffusion, programs.Track ],
|
||||||
|
).filter(
|
||||||
|
date__year = int(year), date__month = int(month),
|
||||||
|
date__day = int(day)
|
||||||
|
)
|
||||||
|
# TODO for each, exclude if there is a diffusion (that has not been logged)
|
||||||
|
|
||||||
|
return [ cl.make_item(log) for log in qs ]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_date_url(date):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
|
@ -1,19 +0,0 @@
|
||||||
{% extends 'aircox/cms/list.html' %}
|
|
||||||
|
|
||||||
{% block header %}
|
|
||||||
<header>
|
|
||||||
<script>
|
|
||||||
sched_fields = [ {% for field in list.fields %}"fields={{ field }}",{% endfor %} ];
|
|
||||||
sched_fields = sched_fields.join('&');
|
|
||||||
</script>
|
|
||||||
<a href="{{ prev_week }}" onclick="return Section.load_event(event, sched_fields);"><</a>
|
|
||||||
{% for curr, url in dates %}
|
|
||||||
<a href="{{ url }}" {% if curr == date %}class="selected" {% endif %}
|
|
||||||
onclick="return Section.load_event(event);">
|
|
||||||
{{ curr|date:'D. d' }}
|
|
||||||
</a>
|
|
||||||
{% endfor %}
|
|
||||||
<a href="{{ next_week }}" onclick="return Section.load_event(event, sched_fields);">></a>
|
|
||||||
</header>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user