Пример #1
0
class Page(Model):
    """One page of teaching text
    """
    init_arg_names = {'parent', 'slug'}
    pk_name = 'slug'
    parent_attrs = 'lesson', 'course'

    title = Field(str, doc='Human-readable title')

    attribution = Field(ListConverter(HTMLFragmentConverter()),
                        doc='Lines of attribution, as HTML fragments')
    license = Field(
        LicenseConverter(),
        doc='License slugs. Only approved licenses are allowed.')
    license_code = Field(
        LicenseConverter(), optional=True,
        doc='Slug of licence for code snippets.')

    source_file = source_file_field

    css = Field(
        PageCSSConverter(), optional=True,
        doc="CSS specific to this page. (Subject to restrictions which " +
            "aren't yet finalized.)")

    solutions = Field(
        ListConverter(Solution, index_arg='index'),
        factory=list,
        doc="Solutions to problems that appear on the page.")

    modules = Field(
        DictConverter(str), factory=dict,
        doc='Additional modules as a dict with `slug` key and version values')

    content = Field(
        HTMLFragmentConverter(sanitizer=_sanitize_page_content),
        output=False,
        doc='Content, as HTML')
Пример #2
0
class Session(Model):
    """A smaller collection of teaching materials

    Usually used for one meeting of an in-preson course or
    a self-contained section of a longer workshop.
    """
    init_arg_names = {'parent', 'index'}
    pk_name = 'slug'
    parent_attrs = ('course', )

    slug = Field(str)
    title = Field(str, doc="A human-readable session title")
    date = Field(
        DateConverter(),
        optional=True,
        doc="The date when this session occurs (if it has a set time)",
    )
    serial = VersionField({
        (0, 1):
        Field(str,
              optional=True,
              doc="""
                Human-readable string identifying the session's position
                in the course.
                The serial is usually numeric: `1`, `2`, `3`, ...,
                but, for example, i, ii, iii... can be used for appendices.
                Some courses start numbering sessions from 0.
            """),
        # For API version 0.0, serial is generated in
        # Course._sessions_after_load.
    })

    description = Field(HTMLFragmentConverter(),
                        optional=True,
                        doc="Short description of the session.")

    source_file = source_file_field

    materials = Field(
        ListConverter(Material),
        factory=list,
        doc="The session's materials",
    )

    @materials.after_load()
    def _index_materials(self, context):
        set_prev_next(m for m in self.materials if m.lesson_slug)

    pages = Field(DictConverter(SessionPage, key_arg='slug'),
                  optional=True,
                  doc="The session's cover pages")

    @pages.after_load()
    def _set_pages(self, context):
        if not self.pages:
            self.pages = {}
        for slug in 'front', 'back':
            if slug not in self.pages:
                page = load(
                    SessionPage,
                    {
                        'api_version': [0, 0],
                        'session-page': {}
                    },
                    slug=slug,
                    parent=self,
                )
                self.pages[slug] = page

    time = Field(DictConverter(SessionTimeConverter(),
                               required=['start', 'end']),
                 optional=True,
                 doc="Time when this session takes place.")

    @time.after_load()
    def _fix_time(self, context):
        self.time = fix_session_time(
            self.time,
            self.date,
            self.course.default_time,
            self.course.timezone,
            self.slug,
        )
Пример #3
0
class Page(Model):
    """One page of teaching text
    """
    init_arg_names = {'parent', 'slug'}
    pk_name = 'slug'
    parent_attrs = 'lesson', 'course'

    subtitle = VersionField({
        (0, 2):
        Field(str,
              optional=True,
              doc="""Human-readable subpage title.
                Required for index subpages other than "index" (unless "title"
                is given).
                """),
    })
    title = VersionField({
        (0, 2):
        Field(str,
              optional=True,
              doc="""Human-readable page title.

                Deprecated since API version 0.2: use lesson.title
                (and, for subpages other than index, page.subtitle)
                """),
        (0, 0):
        Field(str, doc='Human-readable title'),
    })

    @title.after_load()
    def _generate_title(self, context):
        if self.title is None:
            if self.slug == 'index':
                self.title = self.lesson.title
            else:
                if self.subtitle is None:
                    raise ValueError('Either title or subtitle is required')
                self.title = f'{self.lesson.title} – {self.subtitle}'

    attribution = Field(ListConverter(HTMLFragmentConverter()),
                        doc='Lines of attribution, as HTML fragments')
    license = Field(LicenseConverter(),
                    doc='License slugs. Only approved licenses are allowed.')
    license_code = Field(LicenseConverter(),
                         optional=True,
                         doc='Slug of licence for code snippets.')

    source_file = source_file_field

    css = Field(
        PageCSSConverter(),
        optional=True,
        doc="CSS specific to this page. (Subject to restrictions which " +
        "aren't yet finalized.)")

    solutions = Field(ListConverter(Solution, index_arg='index'),
                      factory=list,
                      doc="Solutions to problems that appear on the page.")

    modules = Field(
        DictConverter(str),
        factory=dict,
        doc='Additional modules as a dict with `slug` key and version values')

    content = Field(CourseHTMLFragmentConverter(),
                    output=False,
                    doc='Content, as HTML')

    def freeze(self):
        if self.content:
            self.content.freeze()
Пример #4
0
class Session(Model):
    """A smaller collection of teaching materials

    Usually used for one meeting of an in-preson course or
    a self-contained section of a longer workshop.
    """
    init_arg_names = {'parent', 'index'}
    pk_name = 'slug'
    parent_attrs = ('course', )

    slug = Field(str)
    title = Field(str, doc="A human-readable session title")
    date = Field(
        DateConverter(),
        optional=True,
        doc="The date when this session occurs (if it has a set time)",
    )

    description = Field(HTMLFragmentConverter(),
                        optional=True,
                        doc="Short description of the session.")

    source_file = source_file_field

    materials = Field(
        ListConverter(Material),
        factory=list,
        doc="The session's materials",
    )

    @materials.after_load()
    def _index_materials(self):
        set_prev_next(m for m in self.materials if m.lesson_slug)

    pages = Field(DictConverter(SessionPage, key_arg='slug'),
                  optional=True,
                  doc="The session's cover pages")

    @pages.after_load()
    def _set_pages(self):
        if not self.pages:
            self.pages = {}
        for slug in 'front', 'back':
            if slug not in self.pages:
                page = get_converter(SessionPage).load(
                    {},
                    slug=slug,
                    parent=self,
                )
                self.pages[slug] = page

    time = Field(DictConverter(SessionTimeConverter(),
                               required=['start', 'end']),
                 optional=True,
                 doc="Time when this session takes place.")

    @time.after_load()
    def _fix_time(self):
        if self.time is None:
            self.time = {}
        else:
            if set(self.time) != {'start', 'end'}:
                raise ValueError('Session time may must have start and end')
        result = {}
        for kind in 'start', 'end':
            time = self.time.get(kind, None)
            if isinstance(time, datetime.datetime):
                result[kind] = time
            elif isinstance(time, datetime.time):
                if self.date:
                    result[kind] = datetime.datetime.combine(self.date, time)
                else:
                    self.time = None
                    return
            elif time is None:
                if self.date and self.course.default_time:
                    result[kind] = datetime.datetime.combine(
                        self.date,
                        self.course.default_time[kind],
                    )
                else:
                    self.time = None
                    return
            else:
                raise TypeError(time)
        self.time = result