Пример #1
0
class Snippet(FileResource):
    source = FileField(upload_to='snippets')
Пример #2
0
class Topic(BasePage):
    resource_type = "topic"
    parent_page_types = ["Topics"]
    subpage_types = ["Topic"]
    template = "topic.html"

    # Content fields
    description = RichTextField(
        blank=True,
        default="",
        features=RICH_TEXT_FEATURES_SIMPLE,
        help_text="Optional short text description, max. 400 characters",
        max_length=400,
    )
    featured = StreamField(
        StreamBlock(
            [
                (
                    "article",
                    PageChooserBlock(
                        target_model=(
                            "articles.Article",
                            "externalcontent.ExternalArticle",
                        )
                    ),
                ),
                ("external_page", FeaturedExternalBlock()),
            ],
            max_num=4,
            required=False,
        ),
        null=True,
        blank=True,
        help_text="Optional space for featured articles, max. 4",
    )
    tabbed_panels_title = CharField(max_length=250, blank=True, default="")
    tabbed_panels = StreamField(
        StreamBlock([("panel", TabbedPanelBlock())], max_num=3, required=False),
        null=True,
        blank=True,
        help_text="Optional tabbed panels for linking out to other resources, max. 3",
        verbose_name="Tabbed panels",
    )
    latest_articles_count = IntegerField(
        choices=RESOURCE_COUNT_CHOICES,
        default=3,
        help_text="The number of articles to display for this topic.",
    )

    # Card fields
    card_title = CharField("Title", max_length=140, blank=True, default="")
    card_description = TextField("Description", max_length=400, blank=True, default="")
    card_image = ForeignKey(
        "mozimages.MozImage",
        null=True,
        blank=True,
        on_delete=SET_NULL,
        related_name="+",
        verbose_name="Image",
    )

    # Meta
    icon = FileField(upload_to="topics/icons", blank=True, default="")
    color = CharField(max_length=14, choices=COLOR_CHOICES, default="blue-40")
    keywords = ClusterTaggableManager(through=TopicTag, blank=True)

    # Content panels
    content_panels = BasePage.content_panels + [
        FieldPanel("description"),
        StreamFieldPanel("featured"),
        FieldPanel("tabbed_panels_title"),
        StreamFieldPanel("tabbed_panels"),
        FieldPanel("latest_articles_count"),
        MultiFieldPanel(
            [InlinePanel("people")],
            heading="People",
            help_text="Optional list of people associated with this topic as experts",
        ),
    ]

    # Card panels
    card_panels = [
        FieldPanel("card_title"),
        FieldPanel("card_description"),
        ImageChooserPanel("card_image"),
    ]

    # Meta panels
    meta_panels = [
        MultiFieldPanel(
            [
                InlinePanel("parent_topics", label="Parent topic(s)"),
                InlinePanel("child_topics", label="Child topic(s)"),
            ],
            heading="Parent/child topic(s)",
            classname="collapsible collapsed",
            help_text=(
                "Topics with no parent (i.e. top-level topics) will be "
                "listed on the home page. Child topics are listed "
                "on the parent topic’s page."
            ),
        ),
        MultiFieldPanel(
            [FieldPanel("icon"), FieldPanel("color")],
            heading="Theme",
            help_text=(
                "Theme settings used on topic page and any tagged content. "
                "For example, an article tagged with this topic "
                "will use the color specified here as its accent color."
            ),
        ),
        MultiFieldPanel(
            [
                FieldPanel("seo_title"),
                FieldPanel("search_description"),
                ImageChooserPanel("social_image"),
                FieldPanel("keywords"),
            ],
            heading="SEO",
            help_text=(
                "Optional fields to override the default "
                "title and description for SEO purposes"
            ),
        ),
    ]

    # Settings panels
    settings_panels = [FieldPanel("slug"), FieldPanel("show_in_menus")]

    # Tabs
    edit_handler = TabbedInterface(
        [
            ObjectList(content_panels, heading="Content"),
            ObjectList(card_panels, heading="Card"),
            ObjectList(meta_panels, heading="Meta"),
            ObjectList(settings_panels, heading="Settings", classname="settings"),
        ]
    )

    @property
    def articles(self):
        return get_combined_articles(self, topics__topic__pk=self.pk)

    @property
    def events(self):
        """Return upcoming events for this topic,
        ignoring events in the past, ordered by start date"""
        return get_combined_events(
            self, topics__topic__pk=self.pk, start_date__gte=datetime.datetime.now()
        )

    @property
    def videos(self):
        """Return the latest videos and external videos for this topic. """
        return get_combined_videos(self, topics__topic__pk=self.pk)

    @property
    def color_value(self):
        return dict(COLOR_VALUES)[self.color]

    @property
    def subtopics(self):
        return [topic.child for topic in self.child_topics.all()]
class File(Model):
    attachment = FileField(blank=True,null=True,
        upload_to='attachment')

    def __str__(self):
        return self.attachment.name
Пример #4
0
class ScorePage(RoutablePageMixin, Page):
    cover_image = ForeignKey('wagtailimages.Image',
                             null=True,
                             blank=True,
                             on_delete=PROTECT,
                             related_name='cover_image')
    description = StreamField([('rich_text', RichTextBlock()),
                               ('image', ImageChooserBlock())])
    duration = DurationField(null=True,
                             blank=True,
                             help_text='Expects data in the format "HH:MM:SS"')
    file = FileField(
        upload_to='scores/',
        validators=[FileExtensionValidator(allowed_extensions=['pdf', 'zip'])])
    preview_score = FileField(
        upload_to='preview_scores/',
        validators=[FileExtensionValidator(allowed_extensions=['pdf'])])
    preview_score_checksum = CharField(editable=False,
                                       max_length=256,
                                       blank=True)
    preview_score_checked = False
    preview_score_updated = False
    genre = ParentalManyToManyField(Genre, blank=True, related_name='scores')
    date = CharField(max_length=256, blank=True)
    instrumentation = ParentalManyToManyField(
        'Instrument',
        blank=True,
        help_text='The instrumentation of the compostition.')
    price = DecimalField(max_digits=6, decimal_places=2)
    materials = RichTextField(
        blank=True,
        features=['bold', 'italic', 'link', 'document-link'],
        help_text='The materials sent in the PDF file.')

    def save(self, *args, **kwargs):
        if self.preview_score_checked:
            # This was the cause of a subtle bug. Because this method can be
            # called multiple times during model creation, leaving this flag
            # set would cause the post save hook to fire multiple times.
            self.preview_score_updated = False
            return super().save(*args, **kwargs)

        h = hashlib.md5()
        for chunk in iter(lambda: self.preview_score.read(8192), b''):
            h.update(chunk)

        self.preview_score.seek(0)
        checksum = h.hexdigest()
        if not self.preview_score_checksum == checksum:
            self.preview_score_checksum = checksum
            self.preview_score_updated = True

        self.preview_score_checked = True
        return super().save(*args, **kwargs)

    @route(r'^([\w-]+)/$')
    def get_score_file(self, request, score_link_slug):
        if request.method == 'GET':
            item_link = get_object_or_404(OrderItemLink, slug=score_link_slug)

            if item_link.is_expired():
                raise Http404()

            item_link.access_ip = request.META.get('REMOTE_ADDR', '0.0.0.0')
            item_link.save()

            return render(request, "main/score_page_download.html", {
                'self': self,
                'page': self,
            })
        else:
            raise Http404()

    @route(r'^$')
    def score(self, request):
        cart_page = ShoppingCartPage.objects.first()
        if request.method == 'POST':
            in_cart = toggle_score_in_cart(request, self.pk)
            return render(
                request, "main/score_page.html", {
                    'self': self,
                    'page': self,
                    'in_cart': in_cart,
                    'cart_page': cart_page
                })
        else:
            return render(
                request, "main/score_page.html", {
                    'self': self,
                    'page': self,
                    'in_cart': score_in_cart(request, self.pk),
                    'cart_page': cart_page
                })

    class Meta:
        verbose_name = "Score Page"

    content_panels = Page.content_panels + [
        FieldPanel('date'),
        FieldPanel('duration'),
        FieldPanel('genre'),
        FieldPanel('instrumentation'),
        FieldPanel('price'),
        StreamFieldPanel('description'),
        FieldPanel('materials'),
        FieldPanel('file'),
        FieldPanel('preview_score'),
        ImageChooserPanel('cover_image')
    ]
Пример #5
0
class User(AbstractUser):
    username = CharField(_('username'), max_length=150, primary_key=True)
    cpf = CharField(_('cpf'), max_length=255, null=True, blank=True)

    is_active = BooleanField(_('is active'), default=True)
    is_staff = BooleanField(_('staff status'), default=False)
    is_superuser = BooleanField(_('superuser status'), default=False)
    active = CharField(_('active'), max_length=255, null=True, blank=True)

    presentation_name = CharField(_('presentation name'), max_length=255, null=True, blank=True)
    civil_name = CharField(_('civil name'), max_length=255, null=True, blank=True)
    social_name = CharField(_('social name'), max_length=255, null=True, blank=True)
    first_name = CharField(_('first name'), max_length=255, null=True, blank=True)
    last_name = CharField(_('last name'), max_length=255, null=True, blank=True)

    campus = CharField(_('campus'), max_length=255, null=True, blank=True)
    campus_code = CharField(_('campus code'), max_length=255, null=True, blank=True)
    department = CharField(_('department'), max_length=255, null=True, blank=True)
    title = CharField(_('title'), max_length=255, null=True, blank=True)
    carrer = CharField(_('carrer'), max_length=255, null=True, blank=True)
    job = CharField(_('job'), max_length=255, null=True, blank=True)
    polo = CharField(_('polo'), max_length=255, null=True, blank=True, default=_('unknown'))
    polo_code = CharField(_('polo code'), max_length=255, null=True, blank=True)

    course = CharField(_('course'), max_length=255, null=True, blank=True)
    course_code = CharField(_('course code'), max_length=255, null=True, blank=True)

    email = CharField(_('personal mail'), max_length=250, null=True, blank=True)
    enterprise_email = CharField(_('enterprise email'), max_length=250, null=True, blank=True)
    academic_email = CharField(_('academic email'), max_length=250, null=True, blank=True)
    scholar_email = CharField(_('scholar email'), max_length=250, null=True, blank=True)
    eduroam = CharField(_('eduroam'), max_length=250, null=True, blank=True)

    first_access = DateTimeField(_('date joined'), auto_now_add=True)
    last_access = DateTimeField(_('last access'), auto_now=True)
    deleted = DateTimeField(_('deleted at'), null=True, blank=True)

    created_at = DateTimeField(_('created at'), null=True, blank=True)
    changed_at = DateTimeField(_('changed at'), null=True, blank=True)
    password_set_at = DateTimeField(_('password set at'), null=True, blank=True)
    last_access_at = DateTimeField(_('last ad access'), null=True, blank=True)
    member_of = ArrayField(CharField(_('member_of'), max_length=1000), null=True, blank=True)

    photo_url = CharField(_('photo'), max_length=250, null=True, blank=True)

    biografy = TextField(_('biografy'), blank=True, null=True)
    is_biografy_public = TextField(_('show to all'), blank=True, null=True)

    valid_photo = FileField(_('valid photo'), null=True, blank=True)
    pending_photo = FileField(_('pending photo'), null=True, blank=True)
    photo_solicitation_at = DateTimeField(_('photo_solicitation_at'), blank=True, null=True)
    photo_approved_at = DateTimeField(_('photo_approved at'), blank=True, null=True)
    photo_approved_by = CharField(_('photo_approved by'), max_length=250, blank=True, null=True)

    font_size = SmallIntegerField(_('font size'), blank=True, null=True)
    theme_skin = CharField(_('theme skin'), choices=suap_ead.skins, max_length=250, blank=True, null=True)
    legends = NullBooleanField(_('legends'), blank=True, null=True)
    sign_language = NullBooleanField(_('sign language'), blank=True, null=True)
    screen_reader = NullBooleanField(_('screen reader'), blank=True, null=True)

    special_needs = ManyToManyField(SpecialNeed, verbose_name=_('special needs'), blank=True)
    is_special_needs_public = NullBooleanField(_('show to all'))

    REQUIRED_FIELDS = []

    class Meta:
        verbose_name = _('User')
        verbose_name_plural = _('Users')
        ordering = ['first_name']

    def __str__(self):
        return self.printing_name

    def save(self, *args, **kwargs):
        self.is_active = settings.LDAP_ACTIVE_VALUE == self.active if settings.USE_LDAP else True

        self.created_at = _cast_timestamp(self.created_at)
        self.changed_at = _cast_timestamp(self.changed_at)
        self.password_set_at = _cast_timestamp(self.password_set_at)
        self.last_access_at = _cast_timestamp(self.last_access_at)

        self.email = self.email or self.enterprise_email or self.academic_email or self.scholar_email

        self.civil_name = "%s %s" % (self.first_name, self.last_name)
        self.presentation_name = self.social_name or self.civil_name

        super().save(*args, **kwargs)

    @property
    def printing_name(self):
        if self.social_name and self.social_name != self.civil_name:
            return "%s (%s)" % (self.social_name, self.civil_name)
        return self.civil_name

    @property
    def sigla(self):
        parts = self.presentation_name.split(" ")
        if len(parts) > 1:
            return ("%s%s" % (parts[0][0], parts[-1][0])).upper()
        else:
            return parts[0][0].upper()

    @property
    def status(self):
        result = ""
        result += "%s " % (_("active") if self.is_active else _("inactive"))
        if self.is_superuser:
            result += "(%s" % _("superuser")
            if not self.is_staff:
                result += " %s" % _("but not a staff")
            result += ")"
        elif self.is_staff:
            result += "(%s)" % _("staff")
        else:
            result += "(%s)" % _("user")
        return result
Пример #6
0
class AudioVideoAbstract(Source):
    fichier_ogg = FileField(
        _('fichier (OGG)'), upload_to='files/ogg/', blank=True, editable=False,
    )
    fichier_mpeg = FileField(
        _('fichier (MPEG)'), upload_to='files/mpeg/', blank=True,
        editable=False,
    )
    extrait = FileField(_('extrait'), upload_to='files/extraits/', blank=True)
    extrait_ogg = FileField(
        _('extrait (OGG)'), upload_to='files/extraits/ogg/', blank=True,
        editable=False,
    )
    extrait_mpeg = FileField(
        _('extrait (MPEG)'), upload_to='files/extraits/mpeg/', blank=True,
        editable=False,
    )
    format = CharField(_('format'), max_length=10, blank=True, editable=False)
    format_extrait = CharField(
        _('format de l’extrait'), max_length=10, blank=True, editable=False,
    )
    duree = PositiveSmallIntegerField(
        _('durée (en secondes)'), null=True, blank=True,
        editable=False,
    )
    duree_extrait = PositiveSmallIntegerField(
        _('durée de l’extrait (en secondes)'), null=True, blank=True,
        editable=False,
    )

    type_fichier_attendu = None
    format_ogg_attendu = 'ogg'
    format_mpeg_attendu = 'mpeg'

    class Meta:
        abstract = True

    def update_media_info(self):
        if (
            (self.fichier_ogg and self.fichier_mpeg)
            and (self.extrait_ogg and self.extrait_mpeg)
        ):
            return
        elif not self.fichier and not self.extrait:
            message = _(
                'Vous devez remplir au moins « fichier » ou « extrait ».'
            )
            raise ValidationError({'fichier': message, 'extrait': message})

        if self.fichier:
            file_analyzer = FileAnalyzer(self, 'fichier')
            file_analyzer.validate(expected_type=self.type_fichier_attendu)

            self.format = file_analyzer.format_name
            self.duree = file_analyzer.avprobe_info.duration
            if isinstance(self, Video):
                self.largeur = file_analyzer.avprobe_info.width
                self.hauteur = file_analyzer.avprobe_info.height

            if self.fichier_ogg:
                file_analyzer = FileAnalyzer(self, 'fichier_ogg')
                file_analyzer.validate(
                    expected_type=self.type_fichier_attendu,
                    expected_format=self.format_ogg_attendu,
                )
            if self.fichier_mpeg:
                file_analyzer = FileAnalyzer(self, 'fichier_mpeg')
                file_analyzer.validate(
                    expected_type=self.type_fichier_attendu,
                    expected_format=self.format_mpeg_attendu,
                )

        if self.extrait:
            file_analyzer = FileAnalyzer(self, 'extrait')
            file_analyzer.validate(expected_type=self.type_fichier_attendu)

            self.format_extrait = file_analyzer.format_name
            self.duree_extrait = file_analyzer.avprobe_info.duration
            if isinstance(self, Video):
                self.largeur_extrait = file_analyzer.avprobe_info.width
                self.hauteur_extrait = file_analyzer.avprobe_info.height

            if self.extrait_ogg:
                file_analyzer = FileAnalyzer(self, 'extrait_ogg')
                file_analyzer.validate(
                    expected_type=self.type_fichier_attendu,
                    expected_format=self.format_ogg_attendu,
                )
            if self.extrait_mpeg:
                file_analyzer = FileAnalyzer(self, 'extrait_mpeg')
                file_analyzer.validate(
                    expected_type=self.type_fichier_attendu,
                    expected_format=self.format_mpeg_attendu,
                )

        self.type_fichier = self.type_fichier_attendu
Пример #7
0
class Request(models.Model):
    id = CharField(max_length=36, default=uuid4, primary_key=True)
    path = CharField(max_length=190, db_index=True)
    query_params = TextField(blank=True, default='')
    raw_body = TextField(blank=True, default='')
    body = TextField(blank=True, default='')
    method = CharField(max_length=10)
    start_time = DateTimeField(default=timezone.now, db_index=True)
    view_name = CharField(max_length=190,
                          db_index=True,
                          blank=True,
                          default='',
                          null=True)
    end_time = DateTimeField(null=True, blank=True)
    time_taken = FloatField(blank=True, null=True)
    encoded_headers = TextField(blank=True, default='')  # stores json
    meta_time = FloatField(null=True, blank=True)
    meta_num_queries = IntegerField(null=True, blank=True)
    meta_time_spent_queries = FloatField(null=True, blank=True)
    pyprofile = TextField(blank=True, default='')
    prof_file = FileField(max_length=300,
                          blank=True,
                          storage=silk_storage,
                          null=True)

    # Useful method to create shortened copies of strings without losing start and end context
    # Used to ensure path and view_name don't exceed 190 characters
    def _shorten(self, string):
        return '%s...%s' % (string[:94], string[len(string) - 93:])

    @property
    def total_meta_time(self):
        return (self.meta_time or 0) + (self.meta_time_spent_queries or 0)

    @property
    def profile_table(self):
        for n, columns in enumerate(parse_profile(self.pyprofile)):
            location = columns[-1]
            if n and '{' not in location and '<' not in location:
                r = re.compile('(?P<src>.*\.py)\:(?P<num>[0-9]+).*')
                m = r.search(location)
                group = m.groupdict()
                src = group['src']
                num = group['num']
                name = 'c%d' % n
                fmt = '<a name={name} href="?pos={n}&file_path={src}&line_num={num}#{name}">{location}</a>'
                rep = fmt.format(**dict(group, **locals()))
                yield columns[:-1] + [mark_safe(rep)]
            else:
                yield columns

    # defined in atomic transaction within SQLQuery save()/delete() as well
    # as in bulk_create of SQLQueryManager
    # TODO: This is probably a bad way to do this, .count() will prob do?
    num_sql_queries = IntegerField(default=0)  # TODO replace with count()

    @property
    def time_spent_on_sql_queries(self):
        """
        TODO: Perhaps there is a nicer way to do this with Django aggregates?
        My initial thought was to perform:
        SQLQuery.objects.filter.aggregate(Sum(F('end_time')) - Sum(F('start_time')))
        However this feature isnt available yet, however there has been talk
        for use of F objects within aggregates for four years
        here: https://code.djangoproject.com/ticket/14030. It looks
        like this will go in soon at which point this should be changed.
        """
        return sum(x.time_taken for x in SQLQuery.objects.filter(request=self))

    @property
    def headers(self):
        if self.encoded_headers:
            raw = json.loads(self.encoded_headers)
        else:
            raw = {}

        return CaseInsensitiveDictionary(raw)

    @property
    def content_type(self):
        return self.headers.get('content-type', None)

    @classmethod
    def garbage_collect(cls, force=False):
        """ Remove Request/Responses when we are at the SILKY_MAX_RECORDED_REQUESTS limit
        Note that multiple in-flight requests may call this at once causing a
        double collection """
        check_percent = SilkyConfig().SILKY_MAX_RECORDED_REQUESTS_CHECK_PERCENT
        check_percent /= 100.0
        if check_percent < random.random() and not force:
            return
        target_count = SilkyConfig().SILKY_MAX_RECORDED_REQUESTS

        # Since garbage collection is probabilistic, the target count should
        # be lowered to account for requests before the next garbage collection
        if check_percent != 0:
            target_count -= int(1 / check_percent)

        # Make sure we can delete everything if needed by settings
        if target_count <= 0:
            cls.objects.all().delete()
            return
        requests = cls.objects.order_by('-start_time')
        if not requests or len(requests) - 1 < target_count:
            return
        time_cutoff = requests[target_count].start_time
        cls.objects.filter(start_time__lte=time_cutoff).delete()

    def save(self, *args, **kwargs):
        # sometimes django requests return the body as 'None'
        if self.raw_body is None:
            self.raw_body = ''

        if self.body is None:
            self.body = ''

        if self.end_time and self.start_time:
            interval = self.end_time - self.start_time
            self.time_taken = interval.total_seconds() * 1000

        # We can't save if either path or view_name exceed 190 characters
        if self.path and len(self.path) > 190:
            self.path = self._shorten(self.path)

        if self.view_name and len(self.view_name) > 190:
            self.view_name = self._shorten(self.view_name)

        super(Request, self).save(*args, **kwargs)
        Request.garbage_collect(force=False)
Пример #8
0
    def test_callable_class_storage_file_field(self):
        class GetStorage(FileSystemStorage):
            pass

        obj = FileField(storage=GetStorage)
        self.assertIsInstance(obj.storage, BaseStorage)
Пример #9
0
class ContentMedia(Model):
    data = FileField(    blank=False, editable=True,
                        upload_to="content_media", max_length=256)

    def __unicode__(self):
        return basename(self.data.url)
Пример #10
0
class Person(BaseModelMixin):
    first_name = CharField('Имя', max_length=50)
    last_name = CharField('Фамилия', max_length=50)
    middle_name = CharField('Отчество', max_length=50, default='', blank=True)

    birth_date = DateField('Дата рождения', null=True, blank=True)

    user = OneToOneField(User,
                         verbose_name='Пользователь',
                         on_delete=PROTECT,
                         related_name='person',
                         null=True,
                         blank=True)

    passport_number = CharField('Паспорт',
                                max_length=10,
                                null=True,
                                blank=True,
                                unique=True)
    passport_date = DateField('Дата выдачи паспорта', null=True, blank=True)

    phone = CharField('Телефон', max_length=15, null=True, blank=True)

    WORKED = 'worked'
    FIRED = 'fired'
    CANDIDATE = 'candidate'
    REFUSED = 'refused'
    APPROVED = 'approved'
    RESERVE = 'reserve'

    STATUSES = (
        (WORKED, 'Работает'),
        (FIRED, 'Уволен'),
        (CANDIDATE, 'Кандидат'),
        (REFUSED, 'Отклонен'),
        (APPROVED, 'Проверен'),
        (RESERVE, 'В резерве'),
    )

    status = CharField('Статус',
                       choices=STATUSES,
                       max_length=10,
                       null=True,
                       blank=True)

    station = ForeignKey('Station',
                         verbose_name='Станция',
                         on_delete=PROTECT,
                         related_name='persons',
                         null=True,
                         blank=True)

    def save_to_path(self, filename):
        valid_extensions = [
            'pdf',
        ]
        ext = filename.rsplit('.', 1)[1]
        if ext.lower() in valid_extensions:
            raise ValidationError('Unsupported file extension.')
        return f'documents/{self.passport_number.lower()}.{ext}'

    document_file = FileField(upload_to=save_to_path,
                              verbose_name='Файл документа',
                              null=True,
                              blank=True)

    @property
    def full_name(self):
        return f'{self.first_name} {self.last_name} {self.middle_name}'.strip()

    @property
    def short_name(self):
        return f'{self.first_name} {self.last_name[0]}.'

    def __str__(self):
        return f"{self.full_name} ({self.passport_number})"

    class Meta:
        verbose_name = _('Персона')
        verbose_name_plural = _('Персоны')
Пример #11
0
 def test_file_field_storage_none_uses_default_storage(self):
     self.assertEqual(FileField().storage, default_storage)
Пример #12
0
class Youtube(models.Model):
    title = models.TextField(null=False, blank=False, default="")
    content = models.TextField(null=False, blank=False, default="")
    video = FileField(upload_to='Youtube')
    thumbnail = ImageField(upload_to="Youtube_thumnail")
Пример #13
0
class Stylesheet(FileResource):
    source = FileField(upload_to='stylesheets')
Пример #14
0
class Script(FileResource):
    source = FileField(upload_to='scripts')
Пример #15
0
 def test_filefield_dangerous_filename_dir(self):
     f = FileField(upload_to='some/folder/')
     msg = "File name '/tmp/path' includes path elements"
     with self.assertRaisesMessage(SuspiciousFileOperation, msg):
         f.generate_filename(None, '/tmp/path')
Пример #16
0
class ContentPage(BasePage):
    parent_page_types = ["home.HomePage", "content.ContentPage", "topics.Topic"]
    subpage_types = ["people.People", "content.ContentPage"]
    template = "content.html"

    # Content fields
    description = RichTextField(
        blank=True,
        default="",
        features=RICH_TEXT_FEATURES_SIMPLE,
        help_text=(
            "Optional short text description, "
            f"max. {DESCRIPTION_MAX_LENGTH} characters"
        ),
        max_length=DESCRIPTION_MAX_LENGTH,
    )
    body = CustomStreamField(
        help_text=(
            "Main page body content. Supports rich text, images, embed via URL, "
            "embed via HTML, and inline code snippets"
        )
    )

    sidebar = CustomStreamField(
        null=True,
        blank=True,
        help_text=(
            "Sidebar page body content (narrower than main body). Rendered to the "
            "right of the main body content in desktop and below it in mobile."
            "Supports rich text, images, embed via URL, "
            "embed via HTML, and inline code snippets"
        ),
    )

    # Card fields
    card_title = CharField("Title", max_length=140, blank=True, default="")
    card_description = TextField("Description", max_length=140, blank=True, default="")
    card_image = ForeignKey(
        "mozimages.MozImage",
        null=True,
        blank=True,
        on_delete=SET_NULL,
        related_name="+",
        verbose_name="Image",
        help_text="An image in 16:9 aspect ratio",
    )
    card_image = ForeignKey(
        "mozimages.MozImage",
        null=True,
        blank=True,
        on_delete=SET_NULL,
        related_name="+",
        verbose_name="Image",
        help_text="An image in 16:9 aspect ratio",
    )
    card_image_3_2 = ForeignKey(
        "mozimages.MozImage",
        null=True,
        blank=True,
        on_delete=SET_NULL,
        related_name="+",
        verbose_name="Image",
        help_text="An image in 3:2 aspect ratio",
    )

    # Meta fields
    nav_description = TextField(
        "Navigation description",
        max_length=DESCRIPTION_MAX_LENGTH,
        blank=True,
        default="",
    )
    icon = FileField(
        upload_to="contentpage/icons",
        blank=True,
        default="",
        help_text=(
            "MUST be a black-on-transparent SVG icon ONLY, "
            "with no bitmap embedded in it."
        ),
        validators=[check_for_svg_file],
    )
    keywords = ClusterTaggableManager(through=ContentPageTag, blank=True)

    # Editor panel configuration
    content_panels = BasePage.content_panels + [
        FieldPanel("description"),
        StreamFieldPanel("body"),
        StreamFieldPanel("sidebar"),
    ]

    # Card panels
    card_panels = [
        FieldPanel(
            "card_title",
            help_text=(
                "Title displayed when this page is "
                "represented by a card in a list of items. "
                "If blank, the page's title is used."
            ),
        ),
        FieldPanel(
            "card_description",
            help_text=(
                "Summary text displayed when this page is "
                "represented by a card in a list of items. "
                "If blank, the page's description is used."
            ),
        ),
        MultiFieldPanel(
            [ImageChooserPanel("card_image")],
            heading="16:9 Image",
            help_text=(
                "Image used for representing this page as a Card. "
                "Should be 16:9 aspect ratio. "
                "If not specified a fallback will be used. "
                "This image is also shown when sharing this page via social "
                "media unless a social image is specified."
            ),
        ),
        MultiFieldPanel(
            [ImageChooserPanel("card_image_3_2")],
            heading="3:2 Image",
            help_text=(
                "Image used for representing this page as a Card. "
                "Should be 3:2 aspect ratio. "
                "If not specified a fallback will be used. "
            ),
        ),
    ]

    # Meta panels
    meta_panels = [
        FieldPanel(
            "nav_description",
            help_text="Text to display in the navigation with the title for this page.",
        ),
        MultiFieldPanel(
            [FieldPanel("icon")],
            heading="Theme",
            help_text=(
                "This icon will be used if, for example, this page is shown in a Menu"
            ),
        ),
        MultiFieldPanel(
            [
                FieldPanel("seo_title"),
                FieldPanel("search_description"),
                ImageChooserPanel("social_image"),
                FieldPanel("keywords"),
            ],
            heading="SEO",
            help_text=(
                "Optional fields to override the default title and "
                "description for SEO purposes"
            ),
        ),
    ]

    # Settings panels
    settings_panels = BasePage.settings_panels + [
        FieldPanel("slug"),
        FieldPanel("show_in_menus"),
    ]

    # Tabs
    edit_handler = TabbedInterface(
        [
            ObjectList(content_panels, heading="Content"),
            ObjectList(card_panels, heading="Card"),
            ObjectList(meta_panels, heading="Meta"),
            ObjectList(settings_panels, heading="Settings", classname="settings"),
        ]
    )

    # Search config
    search_fields = BasePage.search_fields + [  # Inherit search_fields from Page
        # "title" is already specced in BasePage
        index.SearchField("description"),
        index.SearchField("body"),
        index.SearchField("sidebar"),
        # Add FilterFields for things we may be filtering on (eg topics)
        index.FilterField("slug"),
    ]
Пример #17
0
class Source(AutoriteModel):
    parent = ForeignKey(
        'self', related_name='children', verbose_name=_('parent'),
        null=True, blank=True, on_delete=CASCADE,
        help_text=_(
            'À remplir par exemple si la source est une page d’un recueil '
            'déjà existant ou un tome d’une série.'
        ),
    )
    position = PositiveIntegerField(
        _('position'), null=True, blank=True,
        help_text=_('Position au sein de son parent.')
    )
    est_promu = BooleanField(_('est dans la bibliothèque'), default=False)

    type = ForeignKey('TypeDeSource', related_name='sources',
                      help_text=ex(_('compte rendu')), verbose_name=_('type'),
                      on_delete=PROTECT)
    titre = CharField(_('titre'), max_length=200, blank=True, db_index=True,
                      help_text=ex(_('Journal de Rouen')))
    legende = CharField(_('légende'), max_length=600, blank=True,
                        help_text=_('Recommandée pour les images.'))

    ancrage = AncrageSpatioTemporel(has_heure=False, has_lieu=False)
    numero = NumberCharField(
        _('numéro'), max_length=50, blank=True, db_index=True,
        help_text=_('Sans « № ». Exemple : « 52 »'),
    )
    folio = CharField(_('folio'), max_length=15, blank=True,
                      help_text=_('Sans « f. ». Exemple : « 3 ».'))
    page = CharField(_('page'), max_length=15, blank=True, db_index=True,
                     help_text=_('Sans « p. ». Exemple : « 3 »'))
    lieu_conservation = CharField(_('lieu de conservation'), max_length=75,
                                  blank=True, db_index=True)
    cote = CharField(_('cote'), max_length=60, blank=True, db_index=True)
    url = URLField(_('URL'), blank=True,
                   help_text=_('Uniquement un permalien extérieur à Dezède.'))

    transcription = HTMLField(
        _('transcription'), blank=True,
        help_text=_('Recopier la source ou un extrait en suivant les règles '
                    'définies dans '  # FIXME: Don’t hardcode the URL.
                    '<a href="/examens/source">le didacticiel.</a>'),
    )

    fichier = FileField(_('fichier'), upload_to='files/', blank=True)
    TYPES = (
        (FileAnalyzer.OTHER, _('autre')),
        (FileAnalyzer.IMAGE, _('image')),
        (FileAnalyzer.AUDIO, _('audio')),
        (FileAnalyzer.VIDEO, _('vidéo')),
    )
    type_fichier = PositiveSmallIntegerField(
        choices=TYPES, null=True, blank=True, editable=False, db_index=True,
    )
    telechargement_autorise = BooleanField(
        _('téléchargement autorisé'), default=True,
    )

    evenements = ManyToManyField(
        'Evenement', through='SourceEvenement', related_name='sources',
        verbose_name=_('événements'))
    oeuvres = ManyToManyField('Oeuvre', through='SourceOeuvre',
                              related_name='sources', verbose_name=_('œuvres'))
    individus = ManyToManyField(
        'Individu', through='SourceIndividu', related_name='sources',
        verbose_name=_('individus'))
    ensembles = ManyToManyField(
        'Ensemble', through='SourceEnsemble', related_name='sources',
        verbose_name=_('ensembles'))
    lieux = ManyToManyField('Lieu', through='SourceLieu',
                            related_name='sources', verbose_name=_('lieux'))
    parties = ManyToManyField(
        'Partie', through='SourcePartie', related_name='sources',
        verbose_name=_('sources'))

    #
    # Dossier
    #

    # Métadonnées
    editeurs_scientifiques = ManyToManyField(
        'accounts.HierarchicUser', related_name='sources_editees',
        verbose_name=_('éditeurs scientifiques'), blank=True,
    )
    date_publication = DateField(_('date de publication'),
                                 default=datetime.datetime.now)
    publications = TextField(_('publication(s) associée(s)'), blank=True)
    developpements = TextField(_('développements envisagés'), blank=True)

    # Article
    presentation = TextField(_('présentation'), blank=True)
    contexte = TextField(_('contexte historique'), blank=True)
    sources_et_protocole = TextField(_('sources et protocole'), blank=True)
    bibliographie = TextField(_('bibliographie indicative'), blank=True)

    objects = SourceManager()

    class Meta:
        verbose_name = _('source')
        verbose_name_plural = _('sources')
        ordering = (
            'date', 'titre', 'numero',
            'parent__date', 'parent__titre', 'parent__numero',
            'position', 'page',
            'lieu_conservation', 'cote',
        )
        permissions = (('can_change_status', _('Peut changer l’état')),)

    def __str__(self):
        return strip_tags(self.html(False))

    def has_presentation_tab(self):
        def iterator():
            yield self.editeurs_scientifiques.exists()
            yield self.publications
            yield self.developpements
            yield self.presentation
            yield self.contexte
            yield self.sources_et_protocole
            yield self.bibliographie

        return any(iterator())

    def has_index_tab(self):
        def iterator():
            yield self.parent
            yield self.auteurs_html()
            yield self.nested_individus()
            yield self.nested_oeuvres()
            yield self.nested_parties()
            yield self.nested_evenements()
            yield self.nested_ensembles()
            yield self.notes_publiques

        return any(iterator())

    @cached_property
    def specific(self):
        if self.type_fichier == FileAnalyzer.AUDIO:
            return Audio.objects.get(pk=self.pk)
        if self.type_fichier == FileAnalyzer.VIDEO:
            return Video.objects.get(pk=self.pk)
        return self

    @permalink
    def get_absolute_url(self):
        return 'source_permanent_detail', (self.pk,)

    @permalink
    def get_change_url(self):
        meta = self.specific._meta
        return f'admin:{meta.app_label}_{meta.model_name}_change', (self.pk,)

    def permalien(self):
        return self.get_absolute_url()

    def link(self):
        return self.html()
    link.short_description = _('Lien')
    link.allow_tags = True

    def auteurs_html(self, tags=True):
        return self.auteurs.html(tags)

    def no(self):
        return ugettext('n° %s') % self.numero

    def f(self):
        return ugettext('f. %s') % self.folio

    def p(self):
        return ugettext('p. %s') % self.page

    def html(self, tags=True, pretty_title=False, link=True):
        url = None if not tags else self.get_absolute_url()
        conservation = hlp(self.lieu_conservation,
                           ugettext('Lieu de conservation'), tags)
        if self.ancrage.date or self.ancrage.date_approx:
            ancrage = hlp(self.ancrage.html(tags, caps=False), ugettext('date'))
        else:
            ancrage = None
        if self.cote:
            conservation += f", {hlp(self.cote, 'cote', tags)}"
        if self.titre:
            l = [cite(self.titre, tags)]
            if self.numero:
                l.append(self.no())
            if ancrage is not None:
                l.append(ancrage)
            if self.lieu_conservation:
                l[-1] += f' ({conservation})'
        else:
            l = [conservation]
            if ancrage is not None:
                l.append(ancrage)
        if self.folio:
            l.append(hlp(self.f(), ugettext('folio'), tags))
        if self.page:
            l.append(hlp(self.p(), ugettext('page'), tags))
        if self.parent is not None:
            l.insert(
                0, self.parent.html(tags=tags, pretty_title=pretty_title,
                                    link=pretty_title)
            )
        l = (l[0], small(str_list(l[1:]), tags=tags)) if pretty_title else l
        out = str_list(l)
        if link:
            return mark_safe(href(url, out, tags))
        return out
    html.short_description = _('rendu HTML')
    html.allow_tags = True

    def pretty_title(self):
        return self.html(pretty_title=True, link=False)

    def has_events(self):
        if hasattr(self, '_has_events'):
            return self._has_events
        return self.evenements.exists()
    has_events.short_description = _('événements')
    has_events.boolean = True
    has_events.admin_order_field = 'evenements'

    def has_program(self):
        if hasattr(self, '_has_program'):
            return self._has_program
        return self.evenements.with_program().exists()
    has_program.short_description = _('programme')
    has_program.boolean = True

    def is_other(self):
        return self.type_fichier == FileAnalyzer.OTHER

    def is_pdf(self):
        return self.is_other() and self.fichier.name.endswith('.pdf')

    def is_image(self):
        return self.type_fichier == FileAnalyzer.IMAGE

    def is_audio(self):
        return self.type_fichier == FileAnalyzer.AUDIO

    def is_video(self):
        return self.type_fichier == FileAnalyzer.VIDEO

    def has_children_images(self):
        return self.children.filter(type_fichier=FileAnalyzer.IMAGE).exists()

    def has_images(self):
        return (
            self.type_fichier == FileAnalyzer.IMAGE
            or self.has_children_images()
        )

    def has_fichiers(self):
        return (
            self.is_other() or self.is_audio() or self.is_video()
            or self.has_images()
        )

    def images_iterator(self):
        if self.is_image():
            yield self
        for child in Source.objects.filter(
            Q(parent=self) | Q(parent__parent=self),
            type_fichier=FileAnalyzer.IMAGE,
        ).order_by('position', 'page'):
            yield child

    @cached_property
    def images(self):
        return list(self.images_iterator())

    @cached_property
    def preview_image(self):
        return next(self.images_iterator())

    @cached_property
    def is_collection(self):
        return Source.objects.filter(parent__parent=self).exists()

    def is_empty(self):
        return not (self.transcription or self.url or self.has_fichiers())

    DATA_TYPES = ('video', 'audio', 'image', 'other', 'text', 'link')
    VIDEO, AUDIO, IMAGE, OTHER, TEXT, LINK = DATA_TYPES

    @property
    def data_types(self):
        data_types = []
        if self.is_video():
            data_types.append(self.VIDEO)
        if self.is_audio():
            data_types.append(self.AUDIO)
        if self.has_images():
            data_types.append(self.IMAGE)
        if self.is_other():
            data_types.append(self.OTHER)
        if self.transcription:
            data_types.append(self.TEXT)
        if self.url:
            data_types.append(self.LINK)
        return data_types

    ICONS = {
        VIDEO: '<i class="fa fa-fw fa-video-camera"></i>',
        AUDIO: '<i class="fa fa-fw fa-volume-up"></i>',
        IMAGE: '<i class="fa fa-fw fa-photo"></i>',
        OTHER: '<i class="fa fa-fw fa-paperclip"></i>',
        TEXT: '<i class="fa fa-fw fa-file-text-o"></i>',
        LINK: '<i class="fa fa-fw fa-external-link"></i>',
    }

    DATA_TYPES_WITH_ICONS = (
        (VIDEO, _(f'{ICONS[VIDEO]} Vidéo')),
        (AUDIO, _(f'{ICONS[AUDIO]} Audio')),
        (IMAGE, _(f'{ICONS[IMAGE]} Image')),
        (OTHER, _(f'{ICONS[OTHER]} Autre')),
        (TEXT, _(f'{ICONS[TEXT]} Texte')),
        (LINK, _(f'{ICONS[LINK]} Lien')),
    )

    @property
    def icons(self):
        return ''.join([self.ICONS[data_type]
                        for data_type in self.data_types])

    def update_media_info(self):
        if self.fichier:
            file_analyzer = FileAnalyzer(self, 'fichier')
            self.type_fichier = file_analyzer.type
        else:
            self.type_fichier = None

    def clean(self):
        super().clean()
        if not getattr(self, 'updated_media_info', False):
            self.update_media_info()
            self.updated_media_info = True

    @cached_property
    def first_page(self):
        return self.children.order_by('position').first()

    @cached_property
    def prev_page(self):
        if self.parent is not None:
            return self.parent.children.exclude(pk=self.pk).filter(
                position__lte=self.position,
            ).order_by('-position').first()

    @cached_property
    def next_page(self):
        if self.parent is not None:
            return self.parent.children.exclude(pk=self.pk).filter(
                position__gte=self.position,
            ).order_by('position').first()

    @property
    def filename(self):
        return Path(self.fichier.path).name

    @cached_property
    def linked_individus(self):
        return self.individus.distinct()

    @cached_property
    def linked_evenements(self):
        return self.evenements.distinct()

    @cached_property
    def linked_oeuvres(self):
        return self.oeuvres.distinct()

    @cached_property
    def linked_ensembles(self):
        return self.ensembles.distinct()

    @cached_property
    def linked_lieux(self):
        return self.lieux.distinct()

    @cached_property
    def linked_parties(self):
        return self.parties.distinct()

    def get_linked_objects(self):
        return [
            *self.auteurs.all(),
            *self.linked_individus,
            *self.linked_evenements,
            *self.linked_oeuvres,
            *self.linked_ensembles,
            *self.linked_lieux,
            *self.linked_parties,
        ]

    def get_linked_objects_json(self):
        return json.dumps([
            {
                'url': obj.get_absolute_url(), 'label': str(obj),
                'model': obj.class_name().lower(),
            }
            for obj in self.get_linked_objects()
        ])

    def nested_evenements(self):
        return apps.get_model('libretto.Evenement').objects.filter(
            sources__in=self.children.all() | Source.objects.filter(pk=self.pk)
        ).distinct()

    def nested_oeuvres(self):
        return apps.get_model('libretto.Oeuvre').objects.filter(
            sources__in=self.children.all() | Source.objects.filter(pk=self.pk)
        ).distinct()

    def nested_individus(self):
        return apps.get_model('libretto.Individu').objects.filter(
            sources__in=self.children.all() | Source.objects.filter(pk=self.pk)
        ).distinct()

    def nested_ensembles(self):
        return apps.get_model('libretto.Ensemble').objects.filter(
            sources__in=self.children.all() | Source.objects.filter(pk=self.pk)
        ).distinct()

    def nested_lieux(self):
        return apps.get_model('libretto.Lieu').objects.filter(
            sources__in=self.children.all() | Source.objects.filter(pk=self.pk)
        ).distinct()

    def nested_parties(self):
        return apps.get_model('libretto.Partie').objects.filter(
            sources__in=self.children.all() | Source.objects.filter(pk=self.pk)
        ).distinct()

    @property
    def small_thumbnail(self):
        if self.is_image():
            thumbnailer = get_thumbnailer(self.fichier)
            return thumbnailer.get_thumbnail(aliases.get('small')).url

    @property
    def medium_thumbnail(self):
        if self.is_image():
            thumbnailer = get_thumbnailer(self.fichier)
            return thumbnailer.get_thumbnail(aliases.get('medium')).url

    @staticmethod
    def autocomplete_search_fields():
        return (
            'type__nom__unaccent__icontains', 'titre__unaccent__icontains',
            'date__icontains', 'date_approx__unaccent__icontains',
            'numero__unaccent__icontains',
            'lieu_conservation__unaccent__icontains',
            'cote__unaccent__icontains',
        )
Пример #18
0
class FileFlag(BaseFlag):
    """ A more advanced flag which allows for file submission """
    file = FileField()
    check_function = CharField(
        max_length=255,
        help_text="Name of function that will be called to check the file")
Пример #19
0
class TemporaryStorage(DjangoModel):
    # This is the storage and reading of the initial cellml file
    file = FileField(blank=False)
    tree = TextField(blank=True)
    owner = ForeignKey(Person, related_name="stored_files", on_delete=CASCADE, null=True, blank=True)
Пример #20
0
class CreativeVideo(Creative):
    '''
        Creative video data model.
        Extends basic model with field to store video files (uploaded to
        LiveRail).
    '''

    file = FileField(upload_to='creatives')

    allowed_extensions = ('api')

    # Fields connected to LiveRail integration.
    liverail_id = IntegerField(max_length=20, null=True, db_index=True)
    liverail_status = CharField(max_length=20,
                                choices=LIVERAIL_STATUS_CHOICES,
                                default=LiverailStatus.pending,
                                db_index=True)
    liverail_error = TextField(blank=True, default='')
    media_variants = JSONField(blank=True)

    # Duration of content, stored in seconds.
    duration = IntegerField(null=True, default=0)

    @property
    def media_variants_secure(self):
        """Provide a list of media_variants that can be served over https."""
        secure_variants = []
        for media in self.media_variants:
            secure_media = media.copy()
            secure_media['url'] = secure_media['url'].replace(
                LiveRailAPI.LIVERAIL_CDN, LiveRailAPI.LIVERAIL_CDN_SECURE)
            secure_variants.append(secure_media)
        return secure_variants

    @property
    def file_path_abs(self):
        return settings.MEDIA_ROOT / self.file.name

    @property
    def is_auditable(self):
        is_auditable = super(CreativeVideo, self).is_auditable
        return is_auditable and self.liverail_status == LiverailStatus.active

    @property
    def appnexus_media_url(self):
        return appnexus_vast_url(
            self.public_id,
            # is_secure set to False will create a vast url with http:// scheme, which is required by appnexus.
            is_secure=False)

    @property
    def vast_url(self):
        """
        Return vast url without any url schema.

        :returns: schema-less url to vast
        :rtype: str
        """
        return appnexus_vast_url(
            self.public_id,
            # is_secure None will render url without any scheme, leaving it out to browser.
            is_secure=None,
        )

    @property
    def appnexus_template_id(self):
        if self.duration == 15:
            return AppCreativeTemplate.video_vast_15s
        if self.duration == 30:
            return AppCreativeTemplate.video_vast_30s
        return AppCreativeTemplate.video_vast_other

    @property
    def api_data(self):
        if self.file:
            return self.file.url

    @api_data.setter
    def api_data(self, videoname):
        video = ''

        if videoname:
            video = videoname.replace(settings.MEDIA_URL, '')

        if video == self.file:
            return

        file_path_abs = settings.MEDIA_ROOT / video

        if not magic.from_file(file_path_abs, mime=True).startswith('video'):
            raise ValidationError("Uploaded video file isn't recognizable "
                                  "as a video filetype.")

        self.file = video

    def upload_to_cdn(self, contents):
        """
        Upload file to liverail cdn.

        :param str contents: read video file content.
        """
        with liverail.liverail_connection() as api:
            if not self.liverail_id:
                self.liverail_id = api.add_creative(self.pk, self.name)

            api.upload_video_asset(self.liverail_id, contents)
            self.liverail_status = LiverailStatus.uploaded
            self.save()

    def liverail_update_media(self, liverail_conn):
        """
        Update creative's LiveRail status.

        If the real updated status is 'active', save updated media types
        and set duration, width and height to their maximum of returned videos.

        :param apis.liverail.LiveRailAPI liverail_conn: LiveRail API wrapper in connected state
        """
        try:
            creative_status = liverail_conn.creative_status(self.liverail_id)
            if creative_status != liverail_conn.CreativeStatus.active:
                return
            video_assets = liverail_conn.list_video_assets(self.liverail_id)
        except liverail.LiveRailApiResponseError as e:
            self.liverail_error = str(e)
            self.liverail_status = LiverailStatus.error
            self.save(update_fields=['liverail_error', 'liverail_status'])
            return

        # Update the creative:
        self.liverail_status = LiverailStatus.active
        self.media_variants = video_assets
        self.width = max(v['width'] for v in video_assets)
        self.height = max(v['height'] for v in video_assets)
        # Duration should be constant but who knows (different encodings, different framerates, rounding...).
        self.duration = max(v['duration'] for v in video_assets)
        self.save(update_fields=[
            'width', 'height', 'duration', 'liverail_status', 'media_variants'
        ])

    @classmethod
    def liverail_update_media_all(cls):
        """
        Update media files list of all not active creatives with liverail_id.
        """
        with liverail.liverail_connection() as api:
            for creative in cls.objects_visible.filter(
                    liverail_id__isnull=False).exclude(
                        liverail_status=LiverailStatus.active):
                creative.liverail_update_media(liverail_conn=api)

    def upload_to_cdn_async(self):
        """
        Overrides base method. Sets liverail_status to pending when creative
        is being uploaded to service.
        """
        # We can't execute save, because this method is called during
        # post_save signal, so we use update to avoid triggering signals at all.
        CreativeVideo.objects.filter(pk=self.pk).update(
            liverail_status=LiverailStatus.pending)

        return super(CreativeVideo, self).upload_to_cdn_async()
Пример #21
0
class CompositionPage(Page):
    composition_title = RichTextField(features=['bold', 'italic'])
    description = StreamField([('rich_text', RichTextBlock()),
                               ('image', ImageChooserBlock())],
                              blank=True)
    location = RichTextField(
        blank=True,
        features=['bold', 'italic', 'link', 'document-link'],
    )
    genre = ParentalManyToManyField(Genre,
                                    blank=True,
                                    related_name='compositions')
    instrumentation = ParentalManyToManyField(
        'Instrument',
        blank=True,
    )
    orchestration = RichTextField(
        blank=True,
        features=['bold', 'italic'],
        help_text=(
            'If the composition is for an ensemble, use this field to enter '
            'the orchestration of the work.'))
    duration = DurationField(null=True,
                             blank=True,
                             help_text='Expects data in the format "HH:MM:SS"')
    dedicatee = RichTextField(
        blank=True,
        features=['bold', 'italic', 'link', 'document-link'],
    )
    text_source = RichTextField(
        blank=True,
        features=['bold', 'italic', 'link', 'document-link'],
        help_text='The source of the text used in the compostition.')
    collaborator = RichTextField(
        blank=True,
        features=['bold', 'italic', 'link', 'document-link'],
        help_text='Others that Decruck collaborated with.')
    manuscript_status = RichTextField(
        blank=True,
        features=['bold', 'italic', 'link', 'document-link'],
        help_text='Notes about the location and condition of the manuscript.')
    recording = StreamField([('rich_text', RichTextBlock()),
                             ('image', ImageChooserBlock())],
                            blank=True)
    information_up_to_date = BooleanField(default=False)
    scanned = BooleanField(default=False)
    premiere = RichTextField(
        blank=True,
        features=['bold', 'italic', 'link', 'document-link'],
    )

    # For preview score
    preview_score = FileField(
        upload_to='composition_preview_scores/',
        blank=True,
        null=True,
        validators=[FileExtensionValidator(allowed_extensions=['pdf'])])
    preview_score_checksum = CharField(editable=False,
                                       max_length=256,
                                       blank=True)
    preview_score_checked = False
    preview_score_updated = False

    # Extended Date Time Format
    nat_lang_edtf_string = CharField(
        verbose_name='Natural Language Date',
        help_text=('The EDTF date in natural language. This field is help '
                   'users who aren\'t familiar with the EDTF. It does not '
                   'change how the date is represented.'),
        blank=True,
        max_length=256)
    edtf_string = CharField(
        verbose_name='EDTF Date',
        help_text=mark_safe(
            'A date in the <a href="https://www.loc.gov/standards/datetime/" '
            'target="_blank"><strong>Extended Date Time Format</strong></a>'),
        blank=True,
        max_length=256)
    lower_fuzzy = DateField(editable=False, null=True, blank=True)
    upper_fuzzy = DateField(editable=False, null=True, blank=True)
    lower_strict = DateField(editable=False, null=True, blank=True)
    upper_strict = DateField(editable=False, null=True, blank=True)
    nat_lang_year = CharField(editable=False, max_length=9, blank=True)

    def instrumentation_list(self):
        return ', '.join([str(i) for i in self.instrumentation.all()])

    class Meta:
        verbose_name = "Composition"

    def get_context(self, request, *args, **kwargs):
        ctx = super().get_context(request, *args, **kwargs)

        try:
            search_idx = request.session['comp_search_index']
            if search_idx:
                idx = search_idx.index(self.pk)
                prev_url = None
                next_url = None
                if idx > 0:
                    pk = search_idx[idx - 1]
                    prev_url = CompositionPage.objects.get(pk=pk).url

                if idx < len(search_idx) - 1:
                    pk = search_idx[idx + 1]
                    next_url = CompositionPage.objects.get(pk=pk).url

                ctx['prev_url'] = prev_url
                ctx['next_url'] = next_url
                ctx['comp_search_qs'] = request.\
                    session.get('comp_search_qs', '')
        except (KeyError, ValueError):
            pass

        return ctx

    def clean(self):
        super().clean()
        # Per Django docs: validate and modify values in Model.clean()
        # https://docs.djangoproject.com/en/3.1/ref/models/instances/#django.db.models.Model.clean

        # Check that nat_lang_edtf_string and edtf_string are either both set, or both unset
        if (self.nat_lang_edtf_string
                and not self.edtf_string) or (not self.nat_lang_edtf_string
                                              and self.edtf_string):
            raise ValidationError(
                'If setting a date on a composition, an EDTF string and a natural language EDTF string must be provided.'
            )

        # Validate edtf_string
        if self.edtf_string and self.nat_lang_edtf_string:
            try:
                e = parse_edtf(self.edtf_string)
            except EDTFParseException:
                raise ValidationError({
                    'edtf_string':
                    '{} is not a valid EDTF string'.format(self.edtf_string)
                })

            self.lower_fuzzy = struct_time_to_date(e.lower_fuzzy())
            self.upper_fuzzy = struct_time_to_date(e.upper_fuzzy())
            self.lower_strict = struct_time_to_date(e.lower_strict())
            self.upper_strict = struct_time_to_date(e.upper_strict())

            if self.lower_strict.year != self.upper_strict.year:
                self.nat_lang_year = '{}-{}'.format(self.lower_strict.year,
                                                    self.upper_strict.year)
            else:
                self.nat_lang_year = str(self.lower_strict.year)

    def save(self, *args, **kwargs):
        # If there's no preview score file, then just save the model
        if not self.preview_score:
            return super().save(*args, **kwargs)

        if self.preview_score_checked:
            # This was the cause of a subtle bug. Because this method can be
            # called multiple times during model creation, leaving this flag
            # set would cause the post save hook to fire multiple times.
            self.preview_score_updated = False
            return super().save(*args, **kwargs)

        h = hashlib.md5()
        for chunk in iter(lambda: self.preview_score.read(8192), b''):
            h.update(chunk)

        self.preview_score.seek(0)
        checksum = h.hexdigest()
        if not self.preview_score_checksum == checksum:
            self.preview_score_checksum = checksum
            self.preview_score_updated = True

        self.preview_score_checked = True
        return super().save(*args, **kwargs)

    content_panels = Page.content_panels + [
        FieldPanel('composition_title'),
        StreamFieldPanel('description'),
        MultiFieldPanel(
            [FieldPanel('edtf_string'),
             FieldPanel('nat_lang_edtf_string')],
            help_text='Enter a date in the LOC Extended Date Time Format',
            heading='Date'),
        FieldPanel('location'),
        FieldPanel('instrumentation'),
        FieldPanel('orchestration'),
        FieldPanel('duration'),
        FieldPanel('dedicatee'),
        FieldPanel('premiere'),
        FieldPanel('genre'),
        FieldPanel('text_source'),
        FieldPanel('collaborator'),
        FieldPanel('manuscript_status'),
        FieldPanel('information_up_to_date'),
        FieldPanel('scanned'),
        FieldPanel('preview_score'),
        StreamFieldPanel('recording'),
    ]

    search_fields = Page.search_fields + [
        index.SearchField('description', partial_match=True),
        index.SearchField('location', partial_match=True),
        index.SearchField('dedicatee', partial_match=True),
        index.SearchField('premiere', partial_match=True),
        index.SearchField('text_source', partial_match=True),
        index.SearchField('collaborator', partial_match=True),
        index.SearchField('manuscript_status', partial_match=True),
        index.SearchField('recording', partial_match=True),
        index.RelatedFields('genre', [
            index.SearchField('genre_en', partial_match=True),
            index.SearchField('genre_fr', partial_match=True),
        ]),
        index.RelatedFields('instrumentation', [
            index.SearchField('instrument_en', partial_match=True),
            index.SearchField('instrument_fr', partial_match=True),
        ]),
    ]

    parent_page_types = ['CompositionListingPage']
Пример #22
0
class User(AbstractBaseUser, PermissionsMixin):
    # Account info
    business_email = EmailField(unique=True)
    is_staff = BooleanField(
        verbose_name='staff status',
        default=False,
        help_text=('Designates whether the user can log into this admin '
                   'site.'))
    is_active = BooleanField(
        verbose_name='active',
        default=True,
        help_text=('Designates whether this user should be treated as '
                   'active. Unselect this instead of deleting accounts.'))
    date_joined = DateTimeField(default=timezone.now)

    # general contact information
    title = CharField(max_length=32)
    first_name = CharField(max_length=50)
    last_name = CharField(max_length=50)
    gender = CharField(max_length=6, choices=GENDER_CHOICES)
    contact_type = CharField(max_length=32, verbose_name="Type of Contact")
    # Address
    home_address = TextField(blank=True)
    business_address = TextField(blank=True)
    country = CharField(max_length=64,
                        blank=True,
                        choices=COUNTRIES,
                        help_text=('The country in which the contact is '
                                   'currently working in'))
    nationality = CharField(max_length=64, blank=True, choices=NATIONALITIES)

    # Work
    job_title = CharField(max_length=64, blank=True)
    area_of_specialisation = CharField(max_length=128, blank=True)

    # Email
    personal_email = EmailField(blank=True)

    # IM
    skype_id = CharField(max_length=32, blank=True)
    yahoo_messenger = CharField(max_length=32,
                                blank=True,
                                verbose_name='Yahoo Messenger')
    msn_id = CharField(max_length=32, blank=True, verbose_name='MSN ID')

    # Phones & fax
    home_tel = CharField(max_length=20,
                         blank=True,
                         verbose_name="Home telephone")
    business_tel = CharField(max_length=20,
                             blank=True,
                             verbose_name="Business telephone")
    mobile = CharField(max_length=20, blank=True)
    fax = CharField(max_length=20, blank=True, verbose_name="Fax no")

    # Misc
    notes = TextField(blank=True)
    picture = ImageField(null=True,
                         blank=True,
                         upload_to=PictureUploadHandler('pictures'))
    cv = FileField(upload_to=UploadToHandler('pi_cvs', get_user_fields),
                   blank=True,
                   null=True)

    # Managers and book-keeping

    USERNAME_FIELD = 'business_email'
    REQUIRED_FIELDS = ['first_name', 'last_name']

    objects = UserManager()

    def get_full_name(self):
        return u"{0} {1}".format(self.first_name, self.last_name)

    def get_short_name(self):
        return self.first_name

    def __unicode__(self):
        return self.get_full_name()

    @property
    def email(self):
        return self.business_email

    def save(self, *args, **kwargs):
        super(User, self).save(*args, **kwargs)
Пример #23
0
class Application(Model):
    owner = ForeignKey(verbose_name=_('owner'), to=User, on_delete=CASCADE)
    name = CharField(_('name'), max_length=150)
    description = TextField(_('description'), null=True, blank=True)
    client_id = CharField(_('client_id'), max_length=150)
    secret = CharField(_('client_secret'), max_length=150)
    logo = FileField(_('application_logo'), null=True, blank=True)
    allowed_callback_urls = TextField(_('allowed_callback_urls'), null=True, blank=True)
    allowed_web_origins = TextField(_('allowed_web_origins'), null=True, blank=True)
    allowed_logout_urls = TextField(_('allowed_logout_urls'), null=True, blank=True)
    expiration = PositiveIntegerField(_('expiration_in_seconds'), default=300)
    created_at = DateTimeField(_('when_created'), auto_now_add=True)
    deleted_at = DateTimeField(_('deleted_at'), null=True, blank=True)

    class Meta:
        verbose_name = _('Aplicação')
        verbose_name_plural = _('Aplicações')

    def __str__(self):
        return "%s [%s]" % (self.name, self.owner)

    def save(self, *args, **kwargs):
        if self.client_id is None or self.client_id == '' or self.secret is None or self.secret == '':
            self.client_id = hashlib.sha1(("%s" % uuid.uuid1()).encode('utf-8')).hexdigest()
            self.secret = hashlib.sha3_512(("%s" % uuid.uuid1()).encode('utf-8')).hexdigest()
        super().save(*args, **kwargs)

    @staticmethod
    def authorize(user, client_id, state, redirect_uri, referer):
        app = Application.validate_client_id(client_id)

        try:
            state = jwt.decode(state, app.secret, algorithm='HS512')
        except Exception as e:
            raise Exception("Invalid 'state'")
        assert 'client_id' in state, "state invalid encoded, client_id not present"
        assert 'uuid' in state, "state invalid encoded, uuid not present"

        # if referer is not None:
        #     referer = referer
        #     assert app.allowed_web_origins is None and validate_url(referer.split("?")[0], app.allowed_web_origins), \
        #         "'referer (%s)' not present on '%s (%s)'" % (referer.split("?")[0],
        #                                                     _('allowed_web_origins'),
        #                                                      app.allowed_web_origins)

        redirect_uri = unquote_plus(redirect_uri)

        assert validate_url(redirect_uri, app.allowed_callback_urls), \
            "'redirect_uri' not present on '%s' - %s" % (_('allowed_callback_urls'), url_only(redirect_uri))

        expire_at = make_aware(datetime.datetime.now() + datetime.timedelta(minutes=10))

        hashcode = "%s" % uuid.uuid1()
        TransactionToken.objects.create(application=app,
                                        user=user,
                                        hashcode=hashcode,
                                        state=state,
                                        redirect_uri=redirect_uri,
                                        referer=referer,
                                        expire_at=expire_at)
        return hashcode

    @staticmethod
    def validate_client_id(client_id):
        try:
            return Application.objects.get(client_id=client_id, deleted_at__isnull=True)
        except Exception as e:
            raise Exception("Invalid 'client_id'")
Пример #24
0
class KidSite(Model):
    enabled = BooleanField()
    name = CharField(max_length=30, blank=True, null=True)
    url = URLField(null=True)
    icon = FileField(null=True)
Пример #25
0
class Topic(Page):
    resource_type = 'topic'
    parent_page_types = ['Topics']
    subpage_types = ['Topic']
    template = 'topic.html'

    # Content fields
    description = TextField(max_length=250, blank=True, default='')
    featured = StreamField(
        StreamBlock([
            ('article',
             PageChooserBlock(required=False,
                              target_model=(
                                  'articles.Article',
                                  'externalcontent.ExternalArticle',
                              ))),
            ('external_page', FeaturedExternalBlock()),
        ],
                    min_num=0,
                    max_num=4,
                    required=False),
        null=True,
        blank=True,
    )
    tabbed_panels_title = CharField(max_length=250, blank=True, default='')
    tabbed_panels = StreamField(
        StreamBlock([('panel', TabbedPanelBlock())],
                    min_num=0,
                    max_num=3,
                    required=False),
        null=True,
        blank=True,
        verbose_name='Tabbed panels',
    )
    latest_articles_count = IntegerField(
        default=3,
        choices=((3, '3'), (6, '6'), (9, '9')),
        help_text='The number of articles to display for this topic.')

    # Card fields
    card_title = CharField('Title', max_length=140, blank=True, default='')
    card_description = TextField('Description',
                                 max_length=140,
                                 blank=True,
                                 default='')
    card_image = ForeignKey(
        'mozimages.MozImage',
        null=True,
        blank=True,
        on_delete=SET_NULL,
        related_name='+',
        verbose_name='Image',
    )

    # Meta
    icon = FileField(upload_to='topics/icons', blank=True, default='')
    color = CharField(max_length=14, choices=COLOR_CHOICES, default='blue-40')
    keywords = ClusterTaggableManager(through=TopicTag, blank=True)

    # Content panels
    content_panels = Page.content_panels + [
        FieldPanel('description'),
        StreamFieldPanel('featured'),
        FieldPanel('tabbed_panels_title'),
        StreamFieldPanel('tabbed_panels'),
        FieldPanel('latest_articles_count'),
        MultiFieldPanel([
            InlinePanel('people'),
        ], heading='People'),
    ]

    # Card panels
    card_panels = [
        FieldPanel('card_title'),
        FieldPanel('card_description'),
        ImageChooserPanel('card_image'),
    ]

    # Meta panels
    meta_panels = [
        MultiFieldPanel(
            [
                InlinePanel('parent_topics', label='Parent topic(s)'),
                InlinePanel('child_topics', label='Child topic(s)'),
            ],
            heading='Parent/child topic(s)',
            classname='collapsible collapsed',
            help_text=(
                'Topics with no parent (i.e. top-level topics) will be listed '
                'on the home page. Child topics are listed on the parent '
                'topic’s page.')),
        MultiFieldPanel([
            FieldPanel('icon'),
            FieldPanel('color'),
        ],
                        heading='Theme'),
        MultiFieldPanel([
            FieldPanel('seo_title'),
            FieldPanel('search_description'),
            FieldPanel('keywords'),
        ],
                        heading='SEO'),
    ]

    # Settings panels
    settings_panels = [
        FieldPanel('slug'),
        FieldPanel('show_in_menus'),
    ]

    # Tabs
    edit_handler = TabbedInterface([
        ObjectList(content_panels, heading='Content'),
        ObjectList(card_panels, heading='Card'),
        ObjectList(meta_panels, heading='Meta'),
        ObjectList(settings_panels, heading='Settings', classname='settings'),
    ])

    @property
    def articles(self):
        return get_combined_articles(self, topics__topic__pk=self.pk)

    @property
    def events(self):
        """Return upcoming events for this topic,
        ignoring events in the past, ordered by start date"""
        return get_combined_events(self,
                                   topics__topic__pk=self.pk,
                                   start_date__gte=datetime.datetime.now())

    @property
    def videos(self):
        """Return the latest videos and external videos for this topic. """
        return get_combined_videos(self, topics__topic__pk=self.pk)

    @property
    def color_value(self):
        return dict(COLOR_VALUES)[self.color]

    @property
    def subtopics(self):
        return [topic.child for topic in self.child_topics.all()]
Пример #26
0
class Topic(BasePage):
    resource_type = "topic"
    parent_page_types = ["Topics"]
    subpage_types = ["Topic", "content.ContentPage"]
    template = "topic.html"

    # Content fields
    description = RichTextField(
        blank=True,
        default="",
        features=RICH_TEXT_FEATURES_SIMPLE,
        help_text="Optional short text description, max. 400 characters",
        max_length=400,
    )
    featured = StreamField(
        StreamBlock(
            [
                (
                    "post",
                    PageChooserBlock(target_model=(
                        "articles.Article",
                        "externalcontent.ExternalArticle",
                    )),
                ),
                (
                    "content_page",
                    PageChooserBlock(target_model=("content.ContentPage", )),
                ),
                ("external_page", FeaturedExternalBlock()),
            ],
            min_num=2,
            max_num=7,
            required=True,
        ),
        null=True,
        blank=True,
        help_text="Optional space for featured items, max. 7",
    )
    #  "What We've Been Working On" panel
    recent_work = StreamField(
        StreamBlock(
            [
                (
                    "post",
                    PageChooserBlock(target_model=(
                        "articles.Article",
                        "externalcontent.ExternalArticle",
                    )),
                ),
                ("external_page", FeaturedExternalBlock()),
                (
                    "video",
                    PageChooserBlock(
                        target_model=("videos.Video",
                                      "externalcontent.ExternalVideo")),
                ),
            ],
            max_num=4,
            required=False,
        ),
        null=True,
        blank=True,
        help_text=
        ("Optional space for featured posts, videos or links, min. 1, max. 4."
         ),
    )

    # "Relevant Events" panel
    relevant_events = StreamField(
        StreamBlock(
            [(
                "event",
                PageChooserBlock(
                    target_model=("events.Event",
                                  "externalcontent.ExternalEvent")),
            )],
            max_num=4,
            required=False,
        ),
        null=True,
        blank=True,
        help_text=("Optional space for featured Events, max. 4."),
    )

    # Card fields
    card_title = CharField("Title", max_length=140, blank=True, default="")
    card_description = TextField("Description",
                                 max_length=400,
                                 blank=True,
                                 default="")
    card_image = ForeignKey(
        "mozimages.MozImage",
        null=True,
        blank=True,
        on_delete=SET_NULL,
        related_name="+",
        verbose_name="Image",
        help_text="An image in 16:9 aspect ratio",
    )

    # Meta
    nav_description = TextField("Navigation description",
                                max_length=400,
                                blank=True,
                                default="")
    icon = FileField(
        upload_to="topics/icons",
        blank=True,
        default="",
        help_text=("MUST be a black-on-transparent SVG icon ONLY, "
                   "with no bitmap embedded in it."),
        validators=[check_for_svg_file],
    )
    keywords = ClusterTaggableManager(through=TopicTag, blank=True)

    # Content panels
    content_panels = BasePage.content_panels + [
        FieldPanel("description"),
        StreamFieldPanel("featured"),
        StreamFieldPanel("recent_work"),
        StreamFieldPanel("relevant_events"),
        MultiFieldPanel(
            [InlinePanel("people")],
            heading="Content by",
            help_text=
            "Optional list of people who create content on this topic",
        ),
    ]

    # Card panels
    card_panels = [
        FieldPanel(
            "card_title",
            help_text=("Title displayed when this page is "
                       "represented by a card in a list of items. "
                       "If blank, the page's title is used."),
        ),
        FieldPanel(
            "card_description",
            help_text=("Summary text displayed when this page is "
                       "represented by a card in a list of items. "
                       "If blank, the page's description is used."),
        ),
        MultiFieldPanel(
            [ImageChooserPanel("card_image")],
            heading="16:9 Image",
            help_text=
            ("Image used for representing this page as a Card. "
             "Must be 16:9 aspect ratio. "
             "If not specified a fallback will be used. "
             "This image is also used for social media posts, unless overriden"
             ),
        ),
    ]

    # Meta panels
    meta_panels = [
        FieldPanel(
            "nav_description",
            help_text=
            "Text to display in the navigation with the title for this page.",
        ),
        MultiFieldPanel(
            [
                InlinePanel("parent_topics", label="Parent topic(s)"),
                InlinePanel("child_topics", label="Child topic(s)"),
            ],
            heading="Parent/child topic(s)",
            classname="collapsible collapsed",
            help_text=("Topics with no parent (i.e. top-level topics) will be "
                       "listed on the home page. Child topics are listed "
                       "on the parent topic’s page."),
        ),
        MultiFieldPanel([FieldPanel("icon")], heading="Theme"),
        MultiFieldPanel(
            [
                FieldPanel("seo_title"),
                FieldPanel("search_description"),
                ImageChooserPanel("social_image"),
                FieldPanel("keywords"),
            ],
            heading="SEO",
            help_text=("Optional fields to override the default "
                       "title and description for SEO purposes"),
        ),
    ]

    # Settings panels
    settings_panels = BasePage.settings_panels + [
        FieldPanel("slug"),
        FieldPanel("show_in_menus"),
    ]

    # Tabs
    edit_handler = TabbedInterface([
        ObjectList(content_panels, heading="Content"),
        ObjectList(card_panels, heading="Card"),
        ObjectList(meta_panels, heading="Meta"),
        ObjectList(settings_panels, heading="Settings", classname="settings"),
    ])

    @property
    def articles(self):
        return get_combined_articles(self, topics__topic__pk=self.pk)

    def experts(self):
        """Return Person instances for topic experts"""
        return [person.person for person in self.people.all()]

    @property
    def videos(self):
        """Return the latest videos and external videos for this topic. """
        return get_combined_videos(self, topics__topic__pk=self.pk)

    @property
    def subtopics(self):
        return [topic.child for topic in self.child_topics.all()]

    def get_section_background_panel_hints(self) -> dict:
        """When we have multiple optional sections on a Topic page, currently comprising
        'recent_work', 'relevant_events' and 'experts', we want to ensure they are
        rendered on contrasting panels.

        This method (which is opinionated based on state, hence a method, not a
        function) returns a dictionary referencing the name of each panel and whether
        it should be on a tinted panel.
        """
        output = {
            # Default state
            "recent_work": False,
            "relevant_events": False,
            "experts": False,
        }

        if self.recent_work:
            # If present, always takes a tint panel
            output["recent_work"] = True

        if self.relevant_events:
            output["relevant_events"] = not self.recent_work

        if self.experts:
            if not self.recent_work and not self.relevant_events:
                output["experts"] = True  # Only panel, so tint it
            elif self.recent_work and self.relevant_events:
                output["experts"] = True  # Third panel, so tint it
            else:
                output["experts"] = False
        return output
Пример #27
0
 def test_filefield_generate_filename(self):
     f = FileField(upload_to='some/folder/')
     self.assertEqual(f.generate_filename(None, 'test with space.txt'),
                      os.path.normpath('some/folder/test_with_space.txt'))
Пример #28
0
 def test_FileField(self):
     lazy_func = lazy(lambda: 'filename.ext', str)
     self.assertIsInstance(FileField().get_prep_value(lazy_func()), str)
     lazy_func = lazy(lambda: 0, int)
     self.assertIsInstance(FileField().get_prep_value(lazy_func()), str)
Пример #29
0
class CommentHistoryExport(models.Model):
    request_user = ForeignKey(User, null=False, blank=False)
    mime_type = CharField(max_length=127,
                          null=False, blank=False, db_index=True)
    file = FileField(upload_to='media/comment_history_dump/')
Пример #30
0
class Post(TimeStampedModel):
    author = ForeignKey(User, on_delete=SET_NULL, null=True)
    title = CharField(_("Post Title"), blank=False, null=True, max_length=255)
    slug = SlugField(unique=True, null=True, blank=True, max_length=500)
    videos = FileField(
        _("Upload Video"),
        upload_to=blog_file_path,
        null=True,
        blank=True,
        validators=[file_validator],
    )
    url = URLField(blank=True, max_length=500, null=True, unique=True)
    pub_date = DateField(
        _("Post Published Date"),
        auto_now=False,
        auto_now_add=False,
        null=True,
        blank=False,
    )
    draft = BooleanField(default=False)
    featured = BooleanField(default=False)
    content = RichTextUploadingField()
    comments = GenericRelation(Comment)
    added_by = CharField(_("added_by"), max_length=555, null=True, blank=True)
    categories = ManyToManyField("category.Category",
                                 help_text="Categorize this item.")
    tags = ManyToManyField("category.Tag", help_text="Tag this item.")
    objects = PostManager()

    def __str__(self):
        return self.title

    @staticmethod
    def autocomplete_search_fields():
        return "title", "author"

    @property
    def readtime(self):
        return str(readtime.of_test(self.content))

    @property
    def get_image_url(self):
        img = self.image_set.first()
        if img:
            return img.image.url
        return img  #None

    class Meta:
        managed = True
        verbose_name = "Post"
        verbose_name_plural = "Posts"
        ordering = ["title", "-pub_date"]

    @property
    def get_related_posts_by_tags(self):
        return Post.objects.filter(tags__in=self.tags.all())[:4]

    # @property
    # def comments(self):
    #     instance = self
    #     return Comment.objects.filter_by_instance(instance)

    @property
    def get_content_type(self):
        instance = self
        content_type = ContentType.objects.get_for_model(instance.__class__)
        return content_type

    def get_absolute_url(self):
        return f"/blogs/{self.slug}"

    def get_update_url(self):
        return f"{self.get_absolute_url}/update"

    def get_delete_url(self):
        return f"{self.get_absolute_url}/delete"