Esempio n. 1
0
class Video(ThumbnailAbstract):
    """A video"""
    subscription = models.ForeignKey(Subscription)
    user = models.ForeignKey(settings.AUTH_USER_MODEL)
    viewed = models.BooleanField(default=False)
    buckets = RelatedSetField(Bucket)

    # from video endpoint
    youtube_id = models.CharField(max_length=200)  # id
    published_at = models.DateTimeField()

    # calculate id based on user ID + video ID so we can get by keys later
    id = ComputedCharField(
        lambda self: create_composite_key(str(self.user_id), self.youtube_id),
        primary_key=True,
        max_length=200)
    ordering_key = ComputedCharField(lambda self: create_composite_key(
        self.published_at.isoformat(" "), self.youtube_id),
                                     max_length=200)

    objects = VideoQuerySet.as_manager()

    class Meta:
        ordering = ["ordering_key"]

    @property
    def html_snippet(self):
        tmpl = get_template("subscribae/includes/videos.html")
        return tmpl.render({"video": self})

    def add_titles(self):
        """Fetches titles and descriptions for Video"""
        from subscribae.utils import video_add_titles
        return list(video_add_titles([self]))[0]
Esempio n. 2
0
class Subscription(ThumbnailAbstract):
    """A subscription that belongs to a user"""
    user = models.ForeignKey(settings.AUTH_USER_MODEL)
    last_update = models.DateTimeField()
    last_viewed = models.DateTimeField(null=True)
    last_watched_video = models.CharField(max_length=200)

    # from subscription endpoint
    channel_id = models.CharField(
        max_length=200)  # snippet.resourceId.channelId

    # from channel endpoint
    upload_playlist = models.CharField(
        max_length=200)  # contentDetails.relatedPlaylists.uploads

    # calculate id based on user ID + channel ID so we can get by keys later
    id = ComputedCharField(
        lambda self: create_composite_key(str(self.user_id), self.channel_id),
        primary_key=True,
        max_length=200)

    objects = SubscriptionQuerySet.as_manager()

    def __unicode__(self):
        tmpl = get_template("subscribae/models/subscription.html")
        return tmpl.render({"object": self})

    def __repr__(self):
        return "<Subscription {}>".format(
            self.channel_id.encode("utf-8", "ignore"))

    def add_titles(self):
        """Fetches titles and descriptions for Subscription"""
        from subscribae.utils import subscription_add_titles
        return list(subscription_add_titles([self]))[0]
Esempio n. 3
0
class ComputedFieldModel(models.Model):
    def computer(self):
        return "%s_%s" % (self.int_field, self.char_field)

    int_field = models.IntegerField()
    char_field = models.CharField(max_length=50)
    test_field = ComputedCharField(computer, max_length=50)

    class Meta:
        app_label = "djangae"
Esempio n. 4
0
    def __call__(self, cls):
        """
            Dynamically adds pagination fields to a model depending on
            the orderings you specify
        """
        for ordering in self.orderings:
            new_field_name = _field_name_for_ordering(ordering)
            ComputedCharField(partial(generator, ordering),
                              max_length=500,
                              editable=False).contribute_to_class(
                                  cls, new_field_name)

        return cls
Esempio n. 5
0
class GaeAbstractBaseUser(AbstractBaseUser):
    """ Absract base class for creating a User model which works with the App
    Engine users API. """

    username = CharOrNoneField(
        # This stores the Google user_id, or custom username for non-Google-based users.
        # We allow it to be null so that Google-based users can be pre-created before they log in.
        _('User ID'),
        max_length=21,
        unique=True,
        blank=True,
        null=True,
        default=None,
        validators=[validate_google_user_id])
    first_name = models.CharField(_('first name'), max_length=30, blank=True)
    last_name = models.CharField(_('last name'), max_length=30, blank=True)

    # Email addresses are case sensitive, but many email systems and many people treat them as if
    # they're not.  We must store the case-preserved email address to ensure that sending emails
    # always works, but we must be able to query for them case insensitively and therefore we must
    # enforce uniqueness on a case insensitive basis, hence these 2 fields
    email = models.EmailField(_('email address'))
    # The null-able-ness of the email_lower is only to deal with when an email address moves between
    # Google Accounts and therefore we need to wipe it without breaking the unique constraint.
    email_lower = ComputedCharField(_get_email_lower,
                                    max_length=email.max_length,
                                    unique=True,
                                    null=True)

    is_staff = models.BooleanField(
        _('staff status'),
        default=False,
        help_text=_(
            'Designates whether the user can log into this admin site.'))
    is_active = models.BooleanField(
        _('active'),
        default=True,
        help_text=_('Designates whether this user should be treated as '
                    'active. Unselect this instead of deleting accounts.'))
    date_joined = models.DateTimeField(_('date joined'), default=timezone.now)

    USERNAME_FIELD = 'username'
    REQUIRED_FIELDS = ['email']

    objects = GaeUserManager()

    class Meta:
        abstract = True

    def clean(self):
        # Only call up if username is not none. Parent's clean() stringifies
        # username blindly
        if self.get_username() is not None:
            super(GaeAbstractBaseUser, self).clean()

    def get_absolute_url(self):
        return "/users/%s/" % urlquote(self.username)

    def get_full_name(self):
        """
        Returns the first_name plus the last_name, with a space in between.
        """
        full_name = '%s %s' % (self.first_name, self.last_name)
        return full_name.strip()

    def get_short_name(self):
        "Returns the short name for the user."
        return self.first_name

    def email_user(self, subject, message, from_email=None):
        """
        Sends an email to this User.
        """
        send_mail(subject, message, from_email, [self.email])

    def __str__(self):
        """
            We have to override this as username is nullable. We either return the email
            address, or if there is a username, we return "email (username)".
        """
        username = self.get_username()
        if username:
            return "{} ({})".format(six.text_type(self.email),
                                    six.text_type(username))
        return six.text_type(self.email)

    def validate_unique(self, exclude=None):
        """ Check that the email address does not already exist by querying on email_lower. """
        exclude = exclude or []
        if "email_lower" not in exclude:
            # We do our own check using the email_lower field, so we don't need Django to query
            # on it as well
            exclude.append("email_lower")

        try:
            super(GaeAbstractBaseUser, self).validate_unique(exclude=exclude)
        except ValidationError as super_error:
            pass
        else:
            super_error = None

        if self.email and "email" not in exclude:
            existing = self.__class__.objects.filter(
                email_lower=self.email.lower())
            if not self._state.adding:
                existing = existing.exclude(pk=self.pk)
            if existing.exists():
                model_name = self._meta.verbose_name
                field_name = self._meta.get_field("email").verbose_name
                message = "%s with this %s already exists" % (model_name,
                                                              field_name)
                error_dict = {"email": [message]}
                if super_error:
                    super_error.update_error_dict(error_dict)
                    raise super_error
                else:
                    raise ValidationError(error_dict)
        elif super_error:
            raise
Esempio n. 6
0
class MasterTranslation(models.Model):
    id = models.CharField(max_length=64, primary_key=True)

    text = models.TextField()
    text_for_ordering = ComputedCharField(lambda instance: instance.text[:500], max_length=500)

    plural_text = models.TextField(blank=True)
    hint = models.CharField(max_length=500, default="", blank=True)

    language_code = models.CharField(
        max_length=8,
        choices=settings.LANGUAGES,
        default=settings.LANGUAGE_CODE
    )

    translations_by_language_code = JSONField()
    translations = RelatedSetField(Translation)
    translated_into_languages = SetField(models.CharField(max_length=8), editable=False)

    # Was this master translation updated or created by make messages?
    used_in_code_or_templates = models.BooleanField(default=False, blank=True, editable=False)

    # Were any groups specified in the trans tags?
    used_by_groups_in_code_or_templates = SetField(models.CharField(max_length=64), blank=True)

    # Record the ID of the last scan which updated this instance (if any)
    last_updated_by_scan_uuid = models.CharField(max_length=64, blank=True, default="")

    first_letter = models.CharField(max_length=1, editable=False)

    @property
    def is_plural(self):
        return bool(self.plural_text)

    def __unicode__(self):
        return u"{} ({}{})".format(self.text, self.language_code, ' plural' if self.is_plural else '')

    def __repr__(self):
        """
        Define an ASCII string safe representation of the master translation.
        """
        return str("{}".format(self.id))

    def get_display(self):
        from fluent.trans import _get_trans
        result = _get_trans(self.text, self.hint)
        return result

    def text_for_language_code(self, lang_code):
        new_code = find_closest_supported_language(lang_code)
        if new_code not in self.translations_by_language_code.keys():
            # we don't have a translation for this language
            return self.text

        translation_id = self.translations_by_language_code[new_code]
        translation = Translation.objects.get(id=translation_id)

        return translation.text

    @classmethod
    def find_by_groups(cls, groups):
        from .fields import find_installed_translatable_fields
        translatable_fields_by_model = find_installed_translatable_fields(with_groups=groups)

        # Go through all Translatable(Char|Text)Fields or TextFields marked with the specified group and get
        # all the master translation IDs which are set to them
        master_translation_ids = []
        for model, fields in translatable_fields_by_model.items():
            master_translation_ids.extend(chain(*model.objects.values_list(*[field.attname for field in fields])))
            master_translation_ids = list(set(master_translation_ids))

        # Now get all the master translations with a group specified in the templates
        master_translation_ids.extend(
            list(MasterTranslation.objects.filter(used_by_groups_in_code_or_templates__overlap=groups)
                 .values_list("pk", flat=True))
        )

        # Make sure master translation ids don't include None values or duplicates
        master_translation_ids = set(master_translation_ids)
        master_translation_ids = master_translation_ids - {None}
        # Return them all!
        return MasterTranslation.objects.filter(pk__in=master_translation_ids)

    @classmethod
    def find_by_group(cls, group_name):
        return cls.find_by_groups([group_name])

    @staticmethod
    def generate_key(text, hint, language_code):
        assert text
        assert hint is not None
        assert language_code

        result = md5()
        for x in (text.encode("utf-8"), hint.encode("utf-8"), language_code):
            result.update(x)
        return result.hexdigest()

    def save(self, *args, **kwargs):
        assert self.text
        assert self.language_code

        # Always store the first letter for querying
        self.first_letter = self.text.strip()[:1]

        # Generate the appropriate key on creation
        if self._state.adding:
            self.pk = MasterTranslation.generate_key(
                self.text, self.hint, self.language_code
            )

        # If we are adding for the first time, then create a counterpart
        # translation for the master language.

        # Note that this Translation will be complete and correct only for the languages that
        # only require 2 plural forms - for others this language needs to be explicitly translated
        # or updated later.
        if self._state.adding:
            with transaction.atomic(xg=True):

                singular_form = get_plural_index(self.language_code, 1)
                plural_form = get_plural_index(self.language_code, 2)

                plurals = {singular_form: self.text}
                if self.is_plural:
                    plurals[plural_form] = self.plural_text

                # if len(LANGUAGE_LOOKUPS[self.language_code].plurals_needed) > len(plurals):
                # FIXME: We can detect that we're dealing with a language that needs more plurals
                # What should we do? mark the translation as incomplete?
                # Don't create the translation object at all?
                new_trans = Translation.objects.create(
                    master_translation=self,
                    language_code=self.language_code,
                    plural_texts=plurals,
                    denorm_master_text=self.text,
                    denorm_master_hint=self.hint
                )
                self.translations_by_language_code[self.language_code] = new_trans.pk
                self.translations.add(new_trans)

                self.translated_into_languages = set(self.translations_by_language_code.keys())
                return super(MasterTranslation, self).save(*args, **kwargs)
        else:
            # Otherwise just do a normal save
            self.translated_into_languages = set(self.translations_by_language_code.keys())
            return super(MasterTranslation, self).save(*args, **kwargs)

    def create_or_update_translation(self, language_code, singular_text=None, plural_texts=None, validate=False):

        if language_code not in dict(settings.LANGUAGES).keys():
            return ["'{}' is not included as a language in your settings file".format(language_code)]

        with transaction.atomic(xg=True):
            trans = None
            if language_code in self.translations_by_language_code:
                # We already have a translation for this language, update it!
                try:
                    trans = Translation.objects.get(pk=self.translations_by_language_code[language_code])
                    created = False
                except Translation.DoesNotExist:
                    trans = None

            if not trans:
                # OK, create the translation object and add it to the respective fields
                trans = Translation(
                    master_translation_id=self.pk,
                    language_code=language_code,
                    denorm_master_hint=self.hint,
                    denorm_master_text=self.text
                )
                created = True

            if plural_texts:
                trans.plural_texts = plural_texts
            else:
                trans.text = singular_text

            if validate:
                errors = validate_translation_texts(trans, self)
                if errors:
                    return errors

            trans.master_translation = self
            trans.save()

            if created:
                self.refresh_from_db()
                self.translations_by_language_code[language_code] = trans.pk
                self.translations.add(trans)
                self.save()

    class Meta:
        app_label = "fluent"