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:
		@ -34,21 +34,21 @@ class Route:
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    @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
 | 
			
		||||
        """
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    @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
 | 
			
		||||
        """
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def get_title(cl, model, request, **kwargs):
 | 
			
		||||
    def get_title(cl, website, request, **kwargs):
 | 
			
		||||
        return ''
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
@ -56,8 +56,12 @@ class Route:
 | 
			
		||||
        return name + '.' + cl.name
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def as_url(cl, name, view, view_kwargs = None):
 | 
			
		||||
        pattern = '^{}/{}'.format(name, cl.name)
 | 
			
		||||
    def make_pattern(cl, prefix = ''):
 | 
			
		||||
        """
 | 
			
		||||
        Make a url pattern using prefix as prefix and cl.params as
 | 
			
		||||
        parameters.
 | 
			
		||||
        """
 | 
			
		||||
        pattern = prefix
 | 
			
		||||
        if cl.params:
 | 
			
		||||
            pattern += ''.join([
 | 
			
		||||
                '{pre}/(?P<{name}>{regexp}){post}'.format(
 | 
			
		||||
@ -68,13 +72,13 @@ class Route:
 | 
			
		||||
                for name, regexp, *optional in cl.params
 | 
			
		||||
            ])
 | 
			
		||||
        pattern += '/?$'
 | 
			
		||||
        return pattern
 | 
			
		||||
 | 
			
		||||
        kwargs = {
 | 
			
		||||
            'route': cl,
 | 
			
		||||
        }
 | 
			
		||||
        if view_kwargs:
 | 
			
		||||
            kwargs.update(view_kwargs)
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def as_url(cl, name, view, kwargs = None):
 | 
			
		||||
        pattern = cl.make_pattern('^{}/{}'.format(name, cl.name))
 | 
			
		||||
        kwargs = kwargs.copy() if kwargs else {}
 | 
			
		||||
        kwargs['route'] = cl
 | 
			
		||||
        return url(pattern, view, kwargs = kwargs,
 | 
			
		||||
                   name = cl.make_view_name(name))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										108
									
								
								cms/sections.py
									
									
									
									
									
								
							
							
						
						
									
										108
									
								
								cms/sections.py
									
									
									
									
									
								
							@ -29,20 +29,19 @@ class Viewable:
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def as_view (cl, *args, **kwargs):
 | 
			
		||||
        """
 | 
			
		||||
        Similar to View.as_view, but instead, wrap a constructor of the
 | 
			
		||||
        given class that is used as is.
 | 
			
		||||
        Create a view containing the current viewable, using a subclass
 | 
			
		||||
        of aircox.cms.views.BaseView.
 | 
			
		||||
        All the arguments are passed to the view directly.
 | 
			
		||||
        """
 | 
			
		||||
        def func(**kwargs_):
 | 
			
		||||
            if kwargs_:
 | 
			
		||||
                kwargs.update(kwargs_)
 | 
			
		||||
            instance = cl(*args, **kwargs)
 | 
			
		||||
            return instance
 | 
			
		||||
        return func
 | 
			
		||||
        from aircox.cms.views import PageView
 | 
			
		||||
        kwargs['sections'] = cl
 | 
			
		||||
        return PageView.as_view(*args, **kwargs)
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def extends (cl, **kwargs):
 | 
			
		||||
        """
 | 
			
		||||
        Return a sub class where the given attribute have been updated
 | 
			
		||||
        using kwargs.
 | 
			
		||||
        """
 | 
			
		||||
        class Sub(cl):
 | 
			
		||||
            pass
 | 
			
		||||
@ -60,13 +59,20 @@ class Sections(Viewable, list):
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        super().__init__(*args, **kwargs)
 | 
			
		||||
 | 
			
		||||
    def prepare(self, *args, **kwargs):
 | 
			
		||||
        """
 | 
			
		||||
        prepare all children sections
 | 
			
		||||
        """
 | 
			
		||||
        for i, section in enumerate(self):
 | 
			
		||||
            if callable(section) or type(section) == type:
 | 
			
		||||
                self[i] = section()
 | 
			
		||||
            self[i].prepare(*args, **kwargs)
 | 
			
		||||
 | 
			
		||||
    def render(self, *args, **kwargs):
 | 
			
		||||
        if args:
 | 
			
		||||
            self.prepare(*args, **kwargs)
 | 
			
		||||
        return ''.join([
 | 
			
		||||
            section.render(*args, **kwargs)
 | 
			
		||||
            section.render()
 | 
			
		||||
            for section in self
 | 
			
		||||
        ])
 | 
			
		||||
 | 
			
		||||
@ -127,6 +133,7 @@ class Section(Viewable, View):
 | 
			
		||||
    its value is an empty string (prints an empty string).
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    view = None
 | 
			
		||||
    request = None
 | 
			
		||||
    object = None
 | 
			
		||||
    kwargs = None
 | 
			
		||||
@ -138,8 +145,8 @@ class Section(Viewable, View):
 | 
			
		||||
        else:
 | 
			
		||||
            self.css_class = css_class
 | 
			
		||||
 | 
			
		||||
    def __init__ (self, *args, **kwargs):
 | 
			
		||||
        super().__init__(*args, **kwargs)
 | 
			
		||||
    def __init__ (self, **kwargs):
 | 
			
		||||
        super().__init__(**kwargs)
 | 
			
		||||
 | 
			
		||||
        self.add_css_class('section')
 | 
			
		||||
        if type(self) != Section:
 | 
			
		||||
@ -159,11 +166,7 @@ class Section(Viewable, View):
 | 
			
		||||
        """
 | 
			
		||||
        return False
 | 
			
		||||
 | 
			
		||||
    def get_context_data(self, request = None, object = None, **kwargs):
 | 
			
		||||
        if request: self.request = request
 | 
			
		||||
        if object: self.object = object
 | 
			
		||||
        if kwargs: self.kwargs = kwargs
 | 
			
		||||
 | 
			
		||||
    def get_context_data(self):
 | 
			
		||||
        return {
 | 
			
		||||
            'view': self,
 | 
			
		||||
            'exp': (hasattr(self, '_exposure') and self._exposure) or None,
 | 
			
		||||
@ -178,8 +181,24 @@ class Section(Viewable, View):
 | 
			
		||||
            'embed': True,
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    def render(self, request, object=None, **kwargs):
 | 
			
		||||
        context = self.get_context_data(request=request, object=object, **kwargs)
 | 
			
		||||
    def prepare(self, view, **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()
 | 
			
		||||
        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['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):
 | 
			
		||||
@ -336,11 +357,17 @@ class List(Section):
 | 
			
		||||
                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):
 | 
			
		||||
        return not self.object_list
 | 
			
		||||
 | 
			
		||||
    def get_object_list(self):
 | 
			
		||||
        return self.object_list
 | 
			
		||||
        return self.object_list or []
 | 
			
		||||
 | 
			
		||||
    def prepare_list(self, object_list):
 | 
			
		||||
        """
 | 
			
		||||
@ -349,8 +376,7 @@ class List(Section):
 | 
			
		||||
        """
 | 
			
		||||
        return object_list
 | 
			
		||||
 | 
			
		||||
    def get_context_data(self, request, object=None, object_list=None,
 | 
			
		||||
                            *args, **kwargs):
 | 
			
		||||
    def get_context_data(self, *args, object_list=None, **kwargs):
 | 
			
		||||
        """
 | 
			
		||||
        Return a context that is passed to the template at rendering, with
 | 
			
		||||
        the following values:
 | 
			
		||||
@ -364,21 +390,20 @@ class List(Section):
 | 
			
		||||
 | 
			
		||||
        Set `request`, `object`, `object_list` and `kwargs` in self.
 | 
			
		||||
        """
 | 
			
		||||
        if request: self.request = request
 | 
			
		||||
        if object: self.object = object
 | 
			
		||||
        if kwargs: self.kwargs = kwargs
 | 
			
		||||
        if args:
 | 
			
		||||
            self.prepare(*args, **kwargs)
 | 
			
		||||
 | 
			
		||||
        if object_list is None:
 | 
			
		||||
            object_list = self.object_list or self.get_object_list()
 | 
			
		||||
            if not object_list and not self.message_empty:
 | 
			
		||||
                return
 | 
			
		||||
                return {}
 | 
			
		||||
 | 
			
		||||
        self.object_list = object_list
 | 
			
		||||
        if 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({
 | 
			
		||||
            'list': self,
 | 
			
		||||
            'object_list': object_list[:self.paginate_by]
 | 
			
		||||
@ -510,13 +535,16 @@ class Menu(Section):
 | 
			
		||||
        if not self.attrs:
 | 
			
		||||
            self.attrs = {}
 | 
			
		||||
 | 
			
		||||
    def get_context_data(self, *args, **kwargs):
 | 
			
		||||
        super().get_context_data(*args, **kwargs)
 | 
			
		||||
    def prepare(self, *args, **kwargs):
 | 
			
		||||
        super().prepare(*args, **kwargs)
 | 
			
		||||
        self.sections.prepare(*args, **kwargs)
 | 
			
		||||
 | 
			
		||||
    def get_context_data(self):
 | 
			
		||||
        return {
 | 
			
		||||
            'tag': self.tag,
 | 
			
		||||
            'css_class': self.css_class,
 | 
			
		||||
            '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)
 | 
			
		||||
 | 
			
		||||
        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({
 | 
			
		||||
            'first_weekday': first,
 | 
			
		||||
            'days': [
 | 
			
		||||
                (date + tz.timedelta(days=day), self.model.reverse(
 | 
			
		||||
                        routes.DateRoute, year = date.year, month = date.month,
 | 
			
		||||
                        day = day
 | 
			
		||||
                    )
 | 
			
		||||
                ) for day in range(0, count)
 | 
			
		||||
            ],
 | 
			
		||||
 | 
			
		||||
            'days': [ make_date(date, day) for day in range(0, count) ],
 | 
			
		||||
            'today': datetime.date.today(),
 | 
			
		||||
            'this_month': date,
 | 
			
		||||
            'prev_month': date - tz.timedelta(days=10),
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										52
									
								
								cms/views.py
									
									
									
									
									
								
							
							
						
						
									
										52
									
								
								cms/views.py
									
									
									
									
									
								
							@ -21,7 +21,7 @@ class BaseView:
 | 
			
		||||
    # Request GET params:
 | 
			
		||||
    * 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"""
 | 
			
		||||
    sections = None
 | 
			
		||||
    """sections used to render the page"""
 | 
			
		||||
@ -43,9 +43,6 @@ class BaseView:
 | 
			
		||||
            self.sections = sections
 | 
			
		||||
        super().__init__(*args, **kwargs)
 | 
			
		||||
 | 
			
		||||
    def __section_is_single(self):
 | 
			
		||||
        return not issubclass(type(self.sections), list)
 | 
			
		||||
 | 
			
		||||
    def add_css_class(self, css_class):
 | 
			
		||||
        """
 | 
			
		||||
        Add the given class to the current class list if not yet present.
 | 
			
		||||
@ -65,19 +62,15 @@ class BaseView:
 | 
			
		||||
 | 
			
		||||
        # update from sections
 | 
			
		||||
        if self.sections:
 | 
			
		||||
            if self.__section_is_single():
 | 
			
		||||
            if issubclass(type(self.sections), sections.Section):
 | 
			
		||||
                self.template_name = self.sections.template_name
 | 
			
		||||
                context.update(self.sections.get_context_data(
 | 
			
		||||
                    self.request,
 | 
			
		||||
                    object_list = hasattr(self, 'object_list') and \
 | 
			
		||||
                                    self.object_list,
 | 
			
		||||
                    **self.kwargs
 | 
			
		||||
                ) or {})
 | 
			
		||||
                self.sections.prepare(self)
 | 
			
		||||
                context.update(self.sections.get_context_data())
 | 
			
		||||
            else:
 | 
			
		||||
                if not self.template_name:
 | 
			
		||||
                    self.template_name = 'aircox/cms/detail.html'
 | 
			
		||||
                context.update({
 | 
			
		||||
                    'content': self.sections.render(self.request, **kwargs)
 | 
			
		||||
                    'content': self.sections.render(self)
 | 
			
		||||
                })
 | 
			
		||||
 | 
			
		||||
        context.update(super().get_context_data(**kwargs))
 | 
			
		||||
@ -98,7 +91,7 @@ class BaseView:
 | 
			
		||||
                                    else None
 | 
			
		||||
            if self.menus:
 | 
			
		||||
                context['menus'] = {
 | 
			
		||||
                    k: v.render(self.request, **kwargs)
 | 
			
		||||
                    k: v.render(self)
 | 
			
		||||
                    for k, v in self.menus.items()
 | 
			
		||||
                    if v is not self
 | 
			
		||||
                }
 | 
			
		||||
@ -148,6 +141,12 @@ class PostListView(BaseView, ListView):
 | 
			
		||||
        return super().dispatch(request, *args, **kwargs)
 | 
			
		||||
 | 
			
		||||
    def get_queryset(self):
 | 
			
		||||
        default = self.prepare_list()
 | 
			
		||||
        if default:
 | 
			
		||||
            qs = self.list.get_object_list()
 | 
			
		||||
            if qs:
 | 
			
		||||
                return qs
 | 
			
		||||
 | 
			
		||||
        if self.route:
 | 
			
		||||
            qs = self.route.get_queryset(self.model, self.request,
 | 
			
		||||
                                         **self.kwargs)
 | 
			
		||||
@ -166,15 +165,23 @@ class PostListView(BaseView, ListView):
 | 
			
		||||
        return qs
 | 
			
		||||
 | 
			
		||||
    def prepare_list(self):
 | 
			
		||||
        """
 | 
			
		||||
        Prepare the list and return True if the list has been created using
 | 
			
		||||
        defaults.
 | 
			
		||||
        """
 | 
			
		||||
        if not self.list:
 | 
			
		||||
           self.list = sections.List(
 | 
			
		||||
               truncate = 32,
 | 
			
		||||
               paginate_by = 0,
 | 
			
		||||
           )
 | 
			
		||||
        else:
 | 
			
		||||
            self.list = sections.List(
 | 
			
		||||
                truncate = 32,
 | 
			
		||||
                paginate_by = 0,
 | 
			
		||||
            )
 | 
			
		||||
            default = True
 | 
			
		||||
        elif type(self.list) == type:
 | 
			
		||||
            self.list = self.list(paginate_by = 0)
 | 
			
		||||
            self.template_name = self.list.template_name
 | 
			
		||||
            self.css_class = self.list.css_class
 | 
			
		||||
            default = False
 | 
			
		||||
 | 
			
		||||
        self.list.prepare(self)
 | 
			
		||||
 | 
			
		||||
        if self.request.GET.get('fields'):
 | 
			
		||||
            self.list.fields = [
 | 
			
		||||
@ -184,12 +191,14 @@ class PostListView(BaseView, ListView):
 | 
			
		||||
 | 
			
		||||
        # done in list
 | 
			
		||||
        # Actions.make(self.request, object_list = self.object_list)
 | 
			
		||||
        return default
 | 
			
		||||
 | 
			
		||||
    def get_context_data(self, **kwargs):
 | 
			
		||||
        self.prepare_list()
 | 
			
		||||
        self.add_css_class('list')
 | 
			
		||||
 | 
			
		||||
        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'):
 | 
			
		||||
            context['title'] = self.route.get_title(
 | 
			
		||||
@ -232,12 +241,14 @@ class PostDetailView(BaseView, DetailView):
 | 
			
		||||
        """
 | 
			
		||||
        Handle new comments
 | 
			
		||||
        """
 | 
			
		||||
        self.sections.prepare(self)
 | 
			
		||||
        if not self.comments:
 | 
			
		||||
            for section in self.sections:
 | 
			
		||||
                if issubclass(type(section), sections.Comments):
 | 
			
		||||
                    self.comments = section
 | 
			
		||||
 | 
			
		||||
        self.object = self.get_object()
 | 
			
		||||
        self.comments.prepare(self)
 | 
			
		||||
        self.comments.post(self, request, self.object)
 | 
			
		||||
        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 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.models as models
 | 
			
		||||
import aircox.cms.sections as sections
 | 
			
		||||
import aircox.cms.sections as sections_
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Website:
 | 
			
		||||
@ -37,7 +38,7 @@ class Website:
 | 
			
		||||
 | 
			
		||||
    ## components
 | 
			
		||||
    Registration = namedtuple('Registration',
 | 
			
		||||
        'name model routes as_default'
 | 
			
		||||
        'name model routes default'
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    urls = []
 | 
			
		||||
@ -64,7 +65,7 @@ class Website:
 | 
			
		||||
        if self.comments_routes:
 | 
			
		||||
            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:
 | 
			
		||||
        - _website: back ref to self
 | 
			
		||||
@ -77,9 +78,9 @@ class Website:
 | 
			
		||||
            if reg.model is model:
 | 
			
		||||
                return reg
 | 
			
		||||
            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
 | 
			
		||||
        model._registration = reg
 | 
			
		||||
        model._website = self
 | 
			
		||||
@ -97,74 +98,89 @@ class Website:
 | 
			
		||||
                continue
 | 
			
		||||
            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
 | 
			
		||||
        * routes: can be a path or a route used to generate urls for the view.
 | 
			
		||||
            Can be a one item or a list of items.
 | 
			
		||||
        * view: route that is registered for the given routes
 | 
			
		||||
        * model: model being registrated. If given, register it in the website
 | 
			
		||||
            under the given name, and make it available to the view.
 | 
			
		||||
        * as_default: make the view available as a default view.
 | 
			
		||||
        """
 | 
			
		||||
        if type(routes) not in (tuple, list):
 | 
			
		||||
            routes = [ routes ]
 | 
			
		||||
    def __route_to_url(self, name, route, view, sections, kwargs):
 | 
			
		||||
        # route can be a tuple
 | 
			
		||||
        if type(route) in (tuple,list):
 | 
			
		||||
            route, view = route
 | 
			
		||||
            view = view.as_view(
 | 
			
		||||
                website = self, **kwargs
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
        # model registration
 | 
			
		||||
        if model:
 | 
			
		||||
            reg = self.register_model(name, model, as_default)
 | 
			
		||||
            reg.routes.extend(routes)
 | 
			
		||||
            view_kwargs['model'] = model
 | 
			
		||||
        else:
 | 
			
		||||
            view_kwargs['model'] = None
 | 
			
		||||
        # route can be a route or a string
 | 
			
		||||
        if type(route) == type and issubclass(route, routes_.Route):
 | 
			
		||||
            return route.as_url(name, view)
 | 
			
		||||
 | 
			
		||||
        # init view
 | 
			
		||||
        if not view_kwargs.get('menus'):
 | 
			
		||||
            view_kwargs['menus'] = self.menus
 | 
			
		||||
 | 
			
		||||
        if sections:
 | 
			
		||||
            self.register_exposures(sections)
 | 
			
		||||
            view_kwargs['sections'] = sections
 | 
			
		||||
 | 
			
		||||
        view = view.as_view(
 | 
			
		||||
            website = self,
 | 
			
		||||
            **view_kwargs
 | 
			
		||||
        return url(
 | 
			
		||||
            slugify(name) if not route else str(route),
 | 
			
		||||
            view = view, name = name, kwargs = 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 += [
 | 
			
		||||
            route.as_url(name, view)
 | 
			
		||||
                if type(route) == type and issubclass(route, routes_.Route)
 | 
			
		||||
                else url(slugify(name) if not route else route,
 | 
			
		||||
                         view = view, name = name)
 | 
			
		||||
            self.__route_to_url(name, route, view, sections, kwargs)
 | 
			
		||||
            for route in routes
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
    def register_dl(self, name, model, sections = None, routes = None,
 | 
			
		||||
                    list_view = views.PostListView,
 | 
			
		||||
                    detail_view = views.PostDetailView,
 | 
			
		||||
                    list_kwargs = {}, detail_kwargs = {},
 | 
			
		||||
                    as_default = False):
 | 
			
		||||
    def add_model(self, name, model, sections = None, routes = None,
 | 
			
		||||
                  default = False,
 | 
			
		||||
                  list_view = views.PostListView,
 | 
			
		||||
                  detail_view = views.PostDetailView,
 | 
			
		||||
                  **kwargs):
 | 
			
		||||
        """
 | 
			
		||||
        Register a detail and list view for a given model, using
 | 
			
		||||
        routes.
 | 
			
		||||
        Add a model to the Website, register it and declare its 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:
 | 
			
		||||
            self.register(name, [ routes_.DetailRoute ], view = detail_view,
 | 
			
		||||
                          model = model, sections = sections,
 | 
			
		||||
                          as_default = as_default,
 | 
			
		||||
                          **detail_kwargs)
 | 
			
		||||
            self.add_page(name, view = detail_view, sections = sections,
 | 
			
		||||
                          routes = routes_.DetailRoute, default = default,
 | 
			
		||||
                          **kwargs)
 | 
			
		||||
        if routes:
 | 
			
		||||
            self.register(name, routes, view = list_view,
 | 
			
		||||
                          model = model, as_default = as_default,
 | 
			
		||||
                          **list_kwargs)
 | 
			
		||||
            self.add_page(name, view = list_view, routes = routes,
 | 
			
		||||
                          default = default, **kwargs)
 | 
			
		||||
 | 
			
		||||
    def register_comments(self):
 | 
			
		||||
        """
 | 
			
		||||
@ -173,16 +189,12 @@ class Website:
 | 
			
		||||
 | 
			
		||||
        Just a wrapper around `register`.
 | 
			
		||||
        """
 | 
			
		||||
        self.register(
 | 
			
		||||
        self.add_model(
 | 
			
		||||
            'comment',
 | 
			
		||||
            view = views.PostListView,
 | 
			
		||||
            routes = [routes.ThreadRoute],
 | 
			
		||||
            model = models.Comment,
 | 
			
		||||
            routes = [routes.ThreadRoute],
 | 
			
		||||
            css_class = 'comments',
 | 
			
		||||
            list = sections.Comments(
 | 
			
		||||
                truncate = 30,
 | 
			
		||||
                fields = ['content','author','date','time'],
 | 
			
		||||
            )
 | 
			
		||||
            list = sections.Comments
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def set_menu(self, menu):
 | 
			
		||||
@ -204,7 +216,7 @@ class Website:
 | 
			
		||||
        given route.
 | 
			
		||||
        """
 | 
			
		||||
        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
 | 
			
		||||
 | 
			
		||||
    def reverse(self, model, route, use_default = True, **kwargs):
 | 
			
		||||
@ -226,7 +238,7 @@ class Website:
 | 
			
		||||
            return ''
 | 
			
		||||
 | 
			
		||||
        for r in self.registry.values():
 | 
			
		||||
            if r.as_default and route in r.routes:
 | 
			
		||||
            if r.default and route in r.routes:
 | 
			
		||||
                try:
 | 
			
		||||
                    name = route.make_view_name(r.name)
 | 
			
		||||
                    return reverse(name, kwargs = kwargs)
 | 
			
		||||
 | 
			
		||||
@ -755,7 +755,7 @@ class Track(Related):
 | 
			
		||||
        blank=True,
 | 
			
		||||
    )
 | 
			
		||||
    pos_in_secs = models.BooleanField(
 | 
			
		||||
        _('use seconds'),
 | 
			
		||||
        _('seconds'),
 | 
			
		||||
        default = False,
 | 
			
		||||
        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):
 | 
			
		||||
        context = super().get_context_data(*args, **kwargs)
 | 
			
		||||
 | 
			
		||||
        context.update({
 | 
			
		||||
            'base_template': 'aircox/cms/section.html',
 | 
			
		||||
            'live_streams': self.live_streams,
 | 
			
		||||
@ -220,7 +219,7 @@ class Diffusions(sections.List):
 | 
			
		||||
 | 
			
		||||
        return ' / \n'.join([str_sched(sched)
 | 
			
		||||
                 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'
 | 
			
		||||
    date = None
 | 
			
		||||
    nav_date_format = '%a. %d'
 | 
			
		||||
    fields = [ 'time', 'image', 'title']
 | 
			
		||||
    template_name = 'aircox/website/list_by_date.html'
 | 
			
		||||
    message_empty = ''
 | 
			
		||||
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        super().__init__(*args, **kwargs)
 | 
			
		||||
        self.add_css_class('schedule')
 | 
			
		||||
    model = None
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def get_week_dates(date):
 | 
			
		||||
    date = None
 | 
			
		||||
    """
 | 
			
		||||
    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.
 | 
			
		||||
        """
 | 
			
		||||
        first = date - tz.timedelta(days=date.weekday())
 | 
			
		||||
        return [ first + tz.timedelta(days=i) for i in range(0, 7) ]
 | 
			
		||||
        first = int((self.nav_days - 1) / 2)
 | 
			
		||||
        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):
 | 
			
		||||
        if self.date:
 | 
			
		||||
@ -287,40 +301,20 @@ class Schedule(Diffusions):
 | 
			
		||||
                               day = int(self.kwargs['day']),
 | 
			
		||||
                               hour = 0, minute = 0, second = 0,
 | 
			
		||||
                               microsecond = 0)
 | 
			
		||||
        return tz.datetime.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')
 | 
			
		||||
        return tz.now()
 | 
			
		||||
 | 
			
		||||
    def get_context_data(self, *args, **kwargs):
 | 
			
		||||
        context = super().get_context_data(*args, **kwargs)
 | 
			
		||||
 | 
			
		||||
        date = self.date_or_default()
 | 
			
		||||
        dates = [
 | 
			
		||||
            (date, models.Diffusion.reverse(
 | 
			
		||||
                routes.DateRoute,
 | 
			
		||||
                year = date.year, month = date.month, day = date.day
 | 
			
		||||
            ))
 | 
			
		||||
            for date in self.get_week_dates(date)
 | 
			
		||||
        ]
 | 
			
		||||
        dates = [ (date, self.get_date_url(date))
 | 
			
		||||
                    for date in self.nav_dates(date) ]
 | 
			
		||||
 | 
			
		||||
        next_week = dates[-1][0] + tz.timedelta(days=1)
 | 
			
		||||
        next_week = models.Diffusion.reverse(
 | 
			
		||||
                routes.DateRoute,
 | 
			
		||||
                year = next_week.year, month = next_week.month,
 | 
			
		||||
                day = next_week.day
 | 
			
		||||
        )
 | 
			
		||||
        next_week = self.get_date_url(next_week)
 | 
			
		||||
 | 
			
		||||
        prev_week = dates[0][0] - tz.timedelta(days=1)
 | 
			
		||||
        prev_week = models.Diffusion.reverse(
 | 
			
		||||
                routes.DateRoute,
 | 
			
		||||
                year = prev_week.year, month = prev_week.month,
 | 
			
		||||
                day = prev_week.day
 | 
			
		||||
        )
 | 
			
		||||
        prev_week = self.get_date_url(prev_week)
 | 
			
		||||
 | 
			
		||||
        context.update({
 | 
			
		||||
            'date': date,
 | 
			
		||||
@ -330,15 +324,90 @@ class Schedule(Diffusions):
 | 
			
		||||
        })
 | 
			
		||||
        return context
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def get_date_url(date):
 | 
			
		||||
        """
 | 
			
		||||
        return a url to the list for the given date
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def url(self):
 | 
			
		||||
        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.
 | 
			
		||||
    """
 | 
			
		||||
    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 %}
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user