Beispiel #1
0
class Question(models.Model):
    """FAQ Questions."""
    question = TranslatedField(
                            models.CharField(
                                max_length=200,
                                verbose_name=_("str_question")),
                            {settings.LANGUAGES[0][0]: {"blank": False}},
                            attrgetter=fallback_to_default,)
    answer = TranslatedField(
                            HTMLField(
                                verbose_name=_("str_answer")),
                            {settings.LANGUAGES[0][0]: {"blank": False}},
                            attrgetter=fallback_to_default,)
    topic = models.ForeignKey(Topic, related_name='question',
                              on_delete=models.CASCADE)
    number = models.PositiveIntegerField()

    class Meta:
        unique_together = ("number", "topic")
        verbose_name = _("str_Question")
        verbose_name_plural = _("str_Questions")
        ordering = ['number']

    def __unicode__(self):
        return u'(%s) %s' % (self.number, self.question, )

    def __str__(self):
        return self.question
class ListDisplayModel(models.Model):
    name = TranslatedField(models.CharField(_("name"), max_length=200))
    choice = TranslatedField(
        models.CharField(_("choice"),
                         max_length=3,
                         choices=[("a", "Andrew"), ("b", "Betty")]))
    ordering = models.IntegerField(_("ordering"), default=0)
class Question(models.Model):
    question = TranslatedField(
        models.CharField(verbose_name="Question Verbose Name",
                         max_length=200), )
    answer = TranslatedField(
        models.CharField(verbose_name="Answer Verbose Name", max_length=200), )

    def __str__(self):
        return self.question
Beispiel #4
0
class Question(models.Model):
    question = TranslatedField(
        models.CharField(_("soru"), max_length=200),
    )
    answer = TranslatedField(
        models.CharField(_("cevap"), max_length=200),
    )

    def __str__(self):
        return self.question
class TestModel(models.Model):
    name = TranslatedField(models.CharField(_("name"), max_length=200))
    other = TranslatedField(
        models.CharField(_("other field"), max_length=200, blank=True))

    def __str__(self):
        return self.name

    stuff_en = "eng"
    stuff_de = "ger"
class ListDisplayModel(models.Model):
    name = TranslatedField(models.CharField(_("name"), max_length=200))
    choice = TranslatedField(
        models.CharField(_("choice"),
                         max_length=3,
                         choices=[("a", "Andrew"), ("b", "Betty")]))
    is_active = TranslatedField(
        models.BooleanField(_("is active"), default=True))
    file = TranslatedField(models.FileField(_("file"), blank=True))
    ordering = models.IntegerField(_("ordering"), default=0)
Beispiel #7
0
class TestModel(models.Model):
    name = TranslatedField(
        models.CharField(_('name'), max_length=200),
    )
    other = TranslatedField(
        models.CharField(_('other field'), max_length=200, blank=True),
    )

    def __str__(self):
        return self.name

    stuff_en = 'eng'
    stuff_de = 'ger'
Beispiel #8
0
class Chapter(models.Model):
    number = models.PositiveIntegerField()
    name = TranslatedField(
        models.CharField(max_length=128)
    )
    description = TranslatedField(
        models.CharField(max_length=256)
    )

    def __str__(self):
        return f'{self.name}'

    class Meta:
        ordering = ['number']
class SpecificModel(models.Model):
    name = TranslatedField(
        models.CharField(_("name"), max_length=200, blank=True),
        {"en": {
            "blank": False
        }},
    )
Beispiel #10
0
class Chunk(models.Model):
    """
    A Chunk is a piece of content associated
    with a unique key that can be inserted into
    any template with the use of a special template
    tag.
    """
    page = models.ForeignKey(Page,
                             on_delete=models.CASCADE,
                             related_name='chunks')
    key = models.CharField(_('str_Key'),
                           help_text=_("str_Chunk_Key_help_text"),
                           blank=False,
                           max_length=255,
                           unique=True)
    display = models.BooleanField(default=True,
                                  verbose_name=_('str_Display'),
                                  help_text=_('str_chunk_display_help_text'))
    content = TranslatedField(
        HTMLField(_('str_Content'), blank=True),
        {settings.LANGUAGES[0][0]: {
             "blank": True
         }},
        attrgetter=fallback_to_default,
    )
    description = TranslatedField(
        models.CharField(_('str_Description'),
                         blank=True,
                         max_length=64,
                         help_text=_("str_Short_description")),
        {settings.LANGUAGES[0][0]: {
             "blank": True
         }},
        attrgetter=fallback_to_default,
    )

    objects = ChunkManager()

    class Meta:
        verbose_name = _('str_chunk')
        verbose_name_plural = _('str_chunks')

    def __unicode__(self):
        return u"%s" % (self.key, )

    def __str__(self):
        return self.key
Beispiel #11
0
class Article(models.Model):
    number = models.PositiveIntegerField(unique=True)
    content = TranslatedField(
        models.CharField(max_length=1024)
    )
    video_link = models.URLField(max_length=128)
    description = TranslatedField(
        models.CharField(max_length=256)
    )
    date = models.DateField()
    section = models.ForeignKey('Section', related_name='articles',
                                on_delete=models.CASCADE)

    def __str__(self):
        return f'{self.number}'

    class Meta:
        ordering = ['number']
Beispiel #12
0
class Section(models.Model):
    number = models.PositiveIntegerField()
    name = TranslatedField(
        models.CharField(max_length=128)
    )
    chapter = models.ForeignKey('Chapter', related_name='sections',
                                on_delete=models.CASCADE)

    def __str__(self):
        return f'{self.name}'

    class Meta:
        ordering = ['number']
Beispiel #13
0
class Article(models.Model):
	title = TranslatedField( models.CharField(_('title'), max_length=200) )
	image = models.ImageField(_('image'), upload_to="images")
	publish = models.DateTimeField(_('publish'), default=timezone.now)
	created = models.DateTimeField(auto_now_add=True)
	updated = models.DateTimeField(auto_now=True)
	status = models.BooleanField(_('status'), default=False)

	class Meta:
		ordering = ['-publish']
		verbose_name = _("Article")
		verbose_name_plural = _("Articles")

	def __str__(self):
		return self.title
Beispiel #14
0
class Topic(models.Model):
    """FAQ Topics."""
    topic_name = TranslatedField(
                        models.CharField(
                                max_length=200,
                                verbose_name=_("str_Topic")),
                        {settings.LANGUAGES[0][0]: {"blank": False}},
                        attrgetter=fallback_to_default,)
    number = models.PositiveIntegerField(unique=True)

    class Meta:
        verbose_name = _("str_Topic")
        verbose_name_plural = _("str_Topics")
        ordering = ['number']

    def __unicode__(self):
        return u'(%s) %s' % (self.number, self.topic_name, )

    def __str__(self):
        return self.topic_name
Beispiel #15
0
class MySlide(DirtyFieldsMixin, MultilingualMixin, RulesModel):
    REQUIRED_TRANSLATED_FIELDS = ('description',)

    location = models.CharField(verbose_name=_("Location"), max_length=12,
            choices=AVAILABLE_SLIDES, db_index=True)
    position = models.PositiveIntegerField(verbose_name=_("Position"),
            db_index=True, default=0)

    description = TranslatedField(
            models.TextField(verbose_name=_("Description"), null=True,
                blank=True),
            attrgetter=attrgetter)

    image = models.ForeignKey(MyFile, verbose_name=_("Image"),
            on_delete=models.CASCADE)

    created_by = models.ForeignKey(settings.AUTH_USER_MODEL,
            on_delete=models.PROTECT, db_index=True,
            related_name='created_slides')
    created_at = models.DateTimeField(auto_now_add=True, editable=False)
    modified_at = models.DateTimeField(auto_now=True, editable=False)


    class Meta:

        get_latest_by = 'created_at'
        ordering = ['-created_at']
        rules_permissions = {
            'add': rules.is_authenticated,
            'read': rules.always_allow,
            'change': rules.is_staff,
            'delete': rules.is_staff,
        }


    def __str__(self): # pylint:disable=invalid-str-returned
        return self.location
class TranslatedFieldsMovie(models.Model):
    year = models.IntegerField()
    title = TranslatedField(models.CharField("Title", max_length=190))
Beispiel #17
0
class User(PermissionsMixin, Entity, AbstractBaseUser):
    LOCALIZABLE_FIELDS = ('first_name', 'last_name', 'city')
    NAME_LOCALIZABLE_FIELDS = LOCALIZABLE_FIELDS[:2]

    GENDER_UNKNOWN = 0
    GENDER_FEMALE = 1
    GENDER_MALE = 2
    GENDER_OTHER = 3
    GENDER_MAX_VALUE_PLUS_ONE = 4

    GENDER_FEMALE_STRING = 'female'
    GENDER_MALE_STRING = 'male'
    GENDER_OTHER_STRING = 'other'

    GENDER_CHOICES = (
        (GENDER_FEMALE, _("Female")),
        (GENDER_MALE, _("Male")),
        (GENDER_OTHER, _("Other")),
    )
    GENDER_VALID_VALUES = [choice[0] for choice in GENDER_CHOICES]
    GENDERS_DICT = {
        GENDER_FEMALE: GENDER_FEMALE_STRING,
        GENDER_MALE: GENDER_MALE_STRING,
        GENDER_OTHER: GENDER_OTHER_STRING
    }

    DIET_UNKNOWN = 0
    DIET_VEGAN = 1
    DIET_VEGETARIAN = 2
    DIET_CARNIST = 3
    DIET_MAX_VALUE_PLUS_ONE = 4

    DIET_CHOICES_WITH_DEFAULT = (
        (DIET_UNKNOWN, _("Unknown")),
        (DIET_VEGAN, _("Vegan (eats only plants and fungi)")),
        (DIET_VEGETARIAN, _("Vegetarian (doesn't eat fish and meat)")),
        (DIET_CARNIST, _("Carnist (eats animals)")),
    )
    DIET_VALID_CHOICES = DIET_CHOICES_WITH_DEFAULT[1:]
    DIET_VALID_VALUES = [choice[0] for choice in DIET_VALID_CHOICES]

    SMOKING_STATUS_UNKNOWN = 0
    SMOKING_STATUS_NOT_SMOKING = 1
    SMOKING_STATUS_SMOKING_OCCASIONALLY = 2
    SMOKING_STATUS_SMOKING = 3
    SMOKING_STATUS_MAX_VALUE_PLUS_ONE = 4

    SMOKING_STATUS_CHOICES_WITH_DEFAULT = (
        (SMOKING_STATUS_UNKNOWN, _("Unknown")),
        (SMOKING_STATUS_NOT_SMOKING, _("Not smoking")),
        (SMOKING_STATUS_SMOKING_OCCASIONALLY, _("Smoking occasionally")),
        (SMOKING_STATUS_SMOKING, _("Smoking")),
    )
    SMOKING_STATUS_VALID_CHOICES = SMOKING_STATUS_CHOICES_WITH_DEFAULT[1:]
    SMOKING_STATUS_VALID_VALUES = [
        choice[0] for choice in SMOKING_STATUS_VALID_CHOICES
    ]

    RELATIONSHIP_STATUS_UNKNOWN = 0
    RELATIONSHIP_STATUS_SINGLE = 1
    RELATIONSHIP_STATUS_DIVORCED = 2
    RELATIONSHIP_STATUS_WIDOWED = 3
    RELATIONSHIP_STATUS_IN_RELATIONSHIP = 4
    RELATIONSHIP_STATUS_IN_OPEN_RELATIONSHIP = 5
    RELATIONSHIP_STATUS_COMPLICATED = 6
    RELATIONSHIP_STATUS_SEPARATED = 7
    RELATIONSHIP_STATUS_ENGAGED = 8
    RELATIONSHIP_STATUS_MARRIED = 9
    RELATIONSHIP_STATUS_MAX_VALUE_PLUS_ONE = 10

    RELATIONSHIP_STATUS_CHOICES_WITH_DEFAULT = (
        (RELATIONSHIP_STATUS_UNKNOWN, _("Unknown")),
        (RELATIONSHIP_STATUS_SINGLE, _("Single")),
        (RELATIONSHIP_STATUS_DIVORCED, _("Divorced")),
        (RELATIONSHIP_STATUS_WIDOWED, _("Widowed")),
        (RELATIONSHIP_STATUS_IN_RELATIONSHIP, _("In a relationship")),
        (RELATIONSHIP_STATUS_IN_OPEN_RELATIONSHIP,
         _("In an open relationship")),
        (RELATIONSHIP_STATUS_COMPLICATED, _("It's complicated")),
        (RELATIONSHIP_STATUS_SEPARATED, _("Separated")),
        (RELATIONSHIP_STATUS_ENGAGED, _("Engaged")),
        (RELATIONSHIP_STATUS_MARRIED, _("Married")),
    )
    RELATIONSHIP_STATUS_VALID_CHOICES = RELATIONSHIP_STATUS_CHOICES_WITH_DEFAULT[
        1:]
    RELATIONSHIP_STATUS_VALID_VALUES = [
        choice[0] for choice in RELATIONSHIP_STATUS_VALID_CHOICES
    ]

    NOTIFICATIONS_OFF = 0
    NOTIFICATIONS_ON = 1

    NOTIFICATIONS_CHOICES = (
        (NOTIFICATIONS_ON, _("Notify me")),
        (NOTIFICATIONS_OFF, _("Don't notify me")),
    )

    USERNAME_FIELD = 'username'
    REQUIRED_FIELDS = [
        'first_name', 'last_name', 'date_of_birth', 'gender', 'diet', 'slug'
    ]

    @staticmethod
    def diet_choices_with_description(gender):
        return (
            (__class__.DIET_VEGAN,
             pgettext_lazy(context=gender,
                           message="Vegan (eats only plants and fungi)")),
            (__class__.DIET_VEGETARIAN,
             pgettext_lazy(context=gender,
                           message="Vegetarian (doesn't eat fish and meat)")),
            (__class__.DIET_CARNIST,
             pgettext_lazy(context=gender, message="Carnist (eats animals)")),
        )

    @staticmethod
    def diet_choices(gender):
        return (
            (__class__.DIET_VEGAN,
             pgettext_lazy(context=gender, message="Vegan")),
            (__class__.DIET_VEGETARIAN,
             pgettext_lazy(context=gender, message="Vegetarian")),
            (__class__.DIET_CARNIST,
             pgettext_lazy(context=gender, message="Carnist")),
        )

    @staticmethod
    def smoking_status_choices(gender):
        return (
            (__class__.SMOKING_STATUS_NOT_SMOKING,
             pgettext_lazy(context=gender, message="Not smoking")),
            (__class__.SMOKING_STATUS_SMOKING_OCCASIONALLY,
             pgettext_lazy(context=gender, message="Smoking occasionally")),
            (__class__.SMOKING_STATUS_SMOKING,
             pgettext_lazy(context=gender, message="Smoking")),
        )

    @staticmethod
    def relationship_status_choices(gender):
        return (
            (__class__.RELATIONSHIP_STATUS_SINGLE,
             pgettext_lazy(context=gender, message="Single")),
            (__class__.RELATIONSHIP_STATUS_DIVORCED,
             pgettext_lazy(context=gender, message="Divorced")),
            (__class__.RELATIONSHIP_STATUS_WIDOWED,
             pgettext_lazy(context=gender, message="Widowed")),
            (__class__.RELATIONSHIP_STATUS_IN_RELATIONSHIP,
             pgettext_lazy(context=gender, message="In a relationship")),
            (__class__.RELATIONSHIP_STATUS_IN_OPEN_RELATIONSHIP,
             pgettext_lazy(context=gender, message="In an open relationship")),
            (__class__.RELATIONSHIP_STATUS_COMPLICATED,
             pgettext_lazy(context=gender, message="It's complicated")),
            (__class__.RELATIONSHIP_STATUS_SEPARATED,
             pgettext_lazy(context=gender, message="Separated")),
            (__class__.RELATIONSHIP_STATUS_ENGAGED,
             pgettext_lazy(context=gender, message="Engaged")),
            (__class__.RELATIONSHIP_STATUS_MARRIED,
             pgettext_lazy(context=gender, message="Married")),
        )

    first_name = TranslatedField(field=models.CharField(
        verbose_name=_('first name'), max_length=75), )
    last_name = TranslatedField(field=models.CharField(
        verbose_name=_('last name'), max_length=150), )
    gender = models.SmallIntegerField(verbose_name=_('I am'),
                                      choices=GENDER_CHOICES)
    date_of_birth = models.DateField(verbose_name=_('date of birth'))
    diet = models.SmallIntegerField(verbose_name=_('diet'),
                                    choices=DIET_CHOICES_WITH_DEFAULT,
                                    default=DIET_UNKNOWN)
    smoking_status = models.SmallIntegerField(
        verbose_name=_('smoking status'),
        choices=SMOKING_STATUS_CHOICES_WITH_DEFAULT,
        default=SMOKING_STATUS_UNKNOWN)
    relationship_status = models.SmallIntegerField(
        verbose_name=_('relationship status'),
        choices=RELATIONSHIP_STATUS_CHOICES_WITH_DEFAULT,
        default=RELATIONSHIP_STATUS_UNKNOWN)
    city = TranslatedField(field=models.CharField(
        verbose_name=_('city or locality'),
        max_length=120,
        blank=True,
        null=True), )
    is_active = models.BooleanField(default=True)
    is_staff = models.BooleanField(default=False)
    has_confirmed_email = models.BooleanField(default=False)
    access_dob_day_month = UserAccessField(
        verbose_name=_('Who can view my birth month and day'),
        default=UserAccessField.ACCESS_ME)
    access_dob_year = UserAccessField(
        verbose_name=_('Who can view my birth year'),
        default=UserAccessField.ACCESS_ME)
    notify_on_message = models.SmallIntegerField(
        verbose_name=_('On new messages'),
        choices=NOTIFICATIONS_CHOICES,
        default=NOTIFICATIONS_ON)

    objects = UserManager()

    @classproperty
    def settings(cls):
        return django_settings.USER_SETTINGS

    @classproperty
    def AGE_VALID_VALUES_IN_MODEL(cls):
        return range(cls.settings.MIN_AGE_ALLOWED_IN_MODEL,
                     cls.settings.MAX_AGE_ALLOWED_IN_MODEL)

    @classproperty
    def AGE_VALID_VALUES_IN_FORMS(cls):
        return range(cls.settings.MIN_AGE_ALLOWED_IN_FORMS,
                     cls.settings.MAX_AGE_ALLOWED_IN_FORMS)

    @classproperty
    def validators(cls):
        validators = {
            'username':
            get_username_validators(
                min_username_length=cls.settings.MIN_USERNAME_LENGTH,
                max_username_length=cls.settings.MAX_USERNAME_LENGTH,
                allow_letters_after_digits=False),
            'slug':
            get_slug_validators(
                min_username_length=cls.settings.MIN_USERNAME_LENGTH,
                max_username_length=cls.settings.MAX_USERNAME_LENGTH,
                min_slug_length=cls.settings.MIN_SLUG_LENGTH,
                max_slug_length=cls.settings.MAX_SLUG_LENGTH,
                allow_letters_after_digits=False) + ["validate_slug"],
            'date_of_birth': [validate_date_of_birth_in_model],
        }
        return validators

    @property
    def name(self):
        return self.profile.get_name()

    @property
    def email(self):
        try:
            return self.email_addresses.get(is_primary=True).email
        except UserEmailAddress.DoesNotExist:
            return None

    @property
    def profile(self):
        if (not (hasattr(self, '_profile'))):
            self.refresh_all_profiles()
        return self._profile

    @property
    def speedy_net_profile(self):
        if (django_settings.LOGIN_ENABLED):
            if (not (hasattr(self, '_speedy_net_profile'))):
                self.refresh_all_profiles()
            return self._speedy_net_profile

    @property
    def speedy_match_profile(self):
        if (django_settings.LOGIN_ENABLED):
            if (not (hasattr(self, '_speedy_match_profile'))):
                self.refresh_all_profiles()
            return self._speedy_match_profile

    @property
    def received_friendship_requests(self):
        if (django_settings.LOGIN_ENABLED):
            if (not (hasattr(self, '_received_friendship_requests'))):
                self.refresh_all_friends_lists()
            return self._received_friendship_requests

    @property
    def received_friendship_requests_count(self):
        return len(self.received_friendship_requests)

    @property
    def sent_friendship_requests(self):
        if (django_settings.LOGIN_ENABLED):
            if (not (hasattr(self, '_sent_friendship_requests'))):
                self.refresh_all_friends_lists()
            return self._sent_friendship_requests

    @property
    def sent_friendship_requests_count(self):
        return len(self.sent_friendship_requests)

    @property
    def all_friends(self):
        if (django_settings.LOGIN_ENABLED):
            if (not (hasattr(self, '_friends'))):
                self.refresh_all_friends_lists()
            return self._friends

    @property
    def friends_count(self):
        return len(self.all_friends)

    @property
    def all_speedy_net_friends(self):
        if (django_settings.LOGIN_ENABLED):
            if (not (hasattr(self, '_speedy_net_friends'))):
                self.refresh_all_friends_lists()
            return self._speedy_net_friends

    @property
    def speedy_net_friends_count(self):
        return len(self.all_speedy_net_friends)

    @property
    def friends_trans(self):
        if (django_settings.SITE_ID == django_settings.SPEEDY_MATCH_SITE_ID):
            return pgettext_lazy(
                context=self.speedy_match_profile.get_match_gender(),
                message='Friends')
        else:
            return _('Friends')

    class Meta:
        verbose_name = _('user')
        verbose_name_plural = _('users')
        ordering = ('-last_login', 'id')
        swappable = 'AUTH_USER_MODEL'

    def __str__(self):
        # Depends on site: full name in Speedy Net, first name in Speedy Match.
        return '<User {} - {}/{}>'.format(self.id, self.name, self.slug)
        # return '<User {} - name={}, username={}, slug={}>'.format(self.id, self.name, self.username, self.slug)

    def _update_has_confirmed_email_field(self):
        speedy_net_site = Site.objects.get(
            pk=django_settings.SPEEDY_NET_SITE_ID)
        previous_has_confirmed_email = self.has_confirmed_email
        self.has_confirmed_email = (
            self.email_addresses.filter(is_confirmed=True).count() > 0)
        self.save_user_and_profile()
        if (not (self.has_confirmed_email == previous_has_confirmed_email)):
            logger.info(
                'User::_update_has_confirmed_email_field::User {user} has_confirmed_email is {has_confirmed_email} on {site_name}.'
                .format(site_name=_(speedy_net_site.name),
                        user=self,
                        has_confirmed_email=self.has_confirmed_email))

    def save(self, *args, **kwargs):
        # Superuser must be equal to staff.
        if (not (self.is_superuser == self.is_staff)):
            raise ValidationError(_("Superuser must be equal to staff."))
        return super().save(*args, **kwargs)

    def set_password(self, raw_password):
        password_validation.validate_password(password=raw_password)
        return super().set_password(raw_password=raw_password)

    def delete(self, *args, **kwargs):
        if ((self.is_staff) or (self.is_superuser)):
            warnings.warn('Can’t delete staff user.')
            return False
        else:
            self.email_addresses.all().delete()
            return super().delete(*args, **kwargs)

    def clean_fields(self, exclude=None):
        """
        Allows to have different slug and username validators for Entity and User.
        """
        if exclude is None:
            exclude = []

        # If special username is true, don't validate username.
        if (self.special_username):
            # ~~~~ TODO: fix models! Exceptions should be 'slug' and not '__all__'.
            self.normalize_slug_and_username()
            self.validate_username_for_slug()
            self.validate_username_required()
            self.validate_username_unique()
            exclude += ['username', 'slug']

        return super().clean_fields(exclude=exclude)

    def clean_all_fields(self, exclude=None):
        super().clean_all_fields(exclude=exclude)

        for base_field_name in __class__.NAME_LOCALIZABLE_FIELDS:
            self.clean_localizable_field(base_field_name=base_field_name)

    def clean_localizable_field(self, base_field_name):
        field_names = get_all_field_names(base_field_name=base_field_name)
        for field_name in field_names:
            if (not (string_is_not_empty(getattr(self, field_name)))):
                for _field_name in field_names:
                    # Check again because maybe this field is already not empty.
                    if (not (string_is_not_empty(getattr(self, field_name)))):
                        if (string_is_not_empty(getattr(self, _field_name))):
                            setattr(self, field_name,
                                    getattr(self, _field_name))

    def get_absolute_url(self):
        return reverse('profiles:user', kwargs={'slug': self.slug})

    def mail_user(self,
                  template_name_prefix,
                  context=None,
                  send_to_unconfirmed=False):
        site = Site.objects.get_current()
        context = context or {}
        addresses = self.email_addresses.filter(is_primary=True)
        if (not (send_to_unconfirmed)):
            addresses = addresses.filter(is_confirmed=True)
        addresses = list(addresses)
        context.update({
            'site_name': _(site.name),
            'user': self,
        })
        if (addresses):
            return addresses[0].mail(template_name_prefix=template_name_prefix,
                                     context=context)
        return False

    def get_full_name(self):
        return '{} {}'.format(self.first_name,
                              self.last_name).strip() or self.slug

    def get_first_name(self):
        return '{}'.format(self.first_name).strip() or self.slug

    def get_short_name(self):
        return self.get_first_name()

    @cached_property
    def has_confirmed_email_or_registered_now(self):
        return ((self.has_confirmed_email)
                or (self.date_created > now() - timedelta(hours=2)))

    def activate(self):
        self.is_active = True
        self.save_user_and_profile()

    def get_profile(self, model=None, profile_model=None) -> 'SiteProfileBase':
        if (model is None):
            model = get_site_profile_model(profile_model=profile_model)
        profile = getattr(self, model.RELATED_NAME, None)
        if (profile is None):
            profile = model.objects.get_or_create(user=self)[0]
        return profile

    def refresh_all_profiles(self):
        self._profile = self.get_profile()
        if (django_settings.LOGIN_ENABLED):
            from speedy.net.accounts.models import SiteProfile as SpeedyNetSiteProfile
            from speedy.match.accounts.models import SiteProfile as SpeedyMatchSiteProfile
            self._speedy_net_profile = self.get_profile(
                model=SpeedyNetSiteProfile)
            self._speedy_match_profile = self.get_profile(
                model=SpeedyMatchSiteProfile)

    def refresh_all_friends_lists(self):
        self._received_friendship_requests = self.get_received_friendship_requests(
        )
        self._sent_friendship_requests = self.get_sent_friendship_requests()
        self._friends = self.get_friends()
        self._speedy_net_friends = self.get_speedy_net_friends()

    def get_received_friendship_requests(self):
        from speedy.net.accounts.models import SiteProfile as SpeedyNetSiteProfile
        from speedy.match.accounts.models import SiteProfile as SpeedyMatchSiteProfile

        SiteProfile = get_site_profile_model()
        qs = self.friendship_requests_received.all().prefetch_related(
            "from_user",
            "from_user__{}".format(SpeedyNetSiteProfile.RELATED_NAME),
            "from_user__{}".format(
                SpeedyMatchSiteProfile.RELATED_NAME)).distinct().order_by(
                    '-from_user__{}__last_visit'.format(
                        SiteProfile.RELATED_NAME))
        received_friendship_requests = [
            friendship_request for friendship_request in qs
            if (friendship_request.from_user.profile.is_active)
        ]
        if (django_settings.SITE_ID == django_settings.SPEEDY_NET_SITE_ID):
            return received_friendship_requests
        elif (django_settings.SITE_ID == django_settings.SPEEDY_MATCH_SITE_ID):
            from speedy.match.accounts.models import SiteProfile as SpeedyMatchSiteProfile
            received_friendship_requests = [
                friendship_request
                for friendship_request in received_friendship_requests
                if (self.speedy_match_profile.get_matching_rank(
                    other_profile=friendship_request.from_user.profile) >
                    SpeedyMatchSiteProfile.RANK_0)
            ]
            return received_friendship_requests
        else:
            raise NotImplementedError()

    def get_sent_friendship_requests(self):
        from speedy.net.accounts.models import SiteProfile as SpeedyNetSiteProfile
        from speedy.match.accounts.models import SiteProfile as SpeedyMatchSiteProfile

        SiteProfile = get_site_profile_model()
        qs = self.friendship_requests_sent.all().prefetch_related(
            "to_user", "to_user__{}".format(SpeedyNetSiteProfile.RELATED_NAME),
            "to_user__{}".format(
                SpeedyMatchSiteProfile.RELATED_NAME)).distinct().order_by(
                    '-to_user__{}__last_visit'.format(
                        SiteProfile.RELATED_NAME))
        sent_friendship_requests = [
            friendship_request for friendship_request in qs
            if (friendship_request.to_user.profile.is_active)
        ]
        if (django_settings.SITE_ID == django_settings.SPEEDY_NET_SITE_ID):
            return sent_friendship_requests
        elif (django_settings.SITE_ID == django_settings.SPEEDY_MATCH_SITE_ID):
            from speedy.match.accounts.models import SiteProfile as SpeedyMatchSiteProfile
            sent_friendship_requests = [
                friendship_request
                for friendship_request in sent_friendship_requests
                if (self.speedy_match_profile.get_matching_rank(
                    other_profile=friendship_request.to_user.profile) >
                    SpeedyMatchSiteProfile.RANK_0)
            ]
            return sent_friendship_requests
        else:
            raise NotImplementedError()

    def get_speedy_net_friends(self):
        from speedy.net.accounts.models import SiteProfile as SpeedyNetSiteProfile
        from speedy.match.accounts.models import SiteProfile as SpeedyMatchSiteProfile

        SiteProfile = get_site_profile_model()
        qs = self.friends.all().prefetch_related(
            "from_user",
            "from_user__{}".format(SpeedyNetSiteProfile.RELATED_NAME),
            "from_user__{}".format(
                SpeedyMatchSiteProfile.RELATED_NAME)).distinct().order_by(
                    '-from_user__{}__last_visit'.format(
                        SiteProfile.RELATED_NAME))
        friends = [
            friendship for friendship in qs
            if (friendship.from_user.speedy_net_profile.is_active)
        ]
        return friends

    def get_friends(self):
        from speedy.net.accounts.models import SiteProfile as SpeedyNetSiteProfile
        from speedy.match.accounts.models import SiteProfile as SpeedyMatchSiteProfile

        SiteProfile = get_site_profile_model()
        qs = self.friends.all().prefetch_related(
            "from_user",
            "from_user__{}".format(SpeedyNetSiteProfile.RELATED_NAME),
            "from_user__{}".format(
                SpeedyMatchSiteProfile.RELATED_NAME)).distinct().order_by(
                    '-from_user__{}__last_visit'.format(
                        SiteProfile.RELATED_NAME))
        friends = [
            friendship for friendship in qs
            if (friendship.from_user.profile.is_active)
        ]
        if (django_settings.SITE_ID == django_settings.SPEEDY_NET_SITE_ID):
            return friends
        elif (django_settings.SITE_ID == django_settings.SPEEDY_MATCH_SITE_ID):
            friends = [
                friendship for friendship in friends
                if (self.speedy_match_profile.get_matching_rank(
                    other_profile=friendship.from_user.profile) >
                    SiteProfile.RANK_0)
            ]
            return friends
        else:
            raise NotImplementedError()

    def save_user_and_profile(self):
        with transaction.atomic():
            self.save()
            self.profile.save()
            if (django_settings.LOGIN_ENABLED):
                self.speedy_net_profile.save()
                self.speedy_match_profile.save()

    def get_gender(self):
        return self.__class__.GENDERS_DICT.get(self.gender)

    def get_diet(self):
        diets = {
            self.__class__.DIET_VEGAN:
            pgettext_lazy(context=self.get_gender(), message="Vegan"),
            self.__class__.DIET_VEGETARIAN:
            pgettext_lazy(context=self.get_gender(), message="Vegetarian"),
            self.__class__.DIET_CARNIST:
            pgettext_lazy(context=self.get_gender(), message="Carnist"),
        }
        return diets.get(self.diet, "")

    def get_smoking_status(self):
        smoking_statuses = {
            self.__class__.SMOKING_STATUS_NOT_SMOKING:
            pgettext_lazy(context=self.get_gender(), message="Not smoking"),
            self.__class__.SMOKING_STATUS_SMOKING_OCCASIONALLY:
            pgettext_lazy(context=self.get_gender(),
                          message="Smoking occasionally"),
            self.__class__.SMOKING_STATUS_SMOKING:
            pgettext_lazy(context=self.get_gender(), message="Smoking"),
        }
        return smoking_statuses.get(self.smoking_status, "")

    def get_relationship_status(self):
        relationship_statuses = {
            self.__class__.RELATIONSHIP_STATUS_SINGLE:
            pgettext_lazy(context=self.get_gender(), message="Single"),
            self.__class__.RELATIONSHIP_STATUS_DIVORCED:
            pgettext_lazy(context=self.get_gender(), message="Divorced"),
            self.__class__.RELATIONSHIP_STATUS_WIDOWED:
            pgettext_lazy(context=self.get_gender(), message="Widowed"),
            self.__class__.RELATIONSHIP_STATUS_IN_RELATIONSHIP:
            pgettext_lazy(context=self.get_gender(),
                          message="In a relationship"),
            self.__class__.RELATIONSHIP_STATUS_IN_OPEN_RELATIONSHIP:
            pgettext_lazy(context=self.get_gender(),
                          message="In an open relationship"),
            self.__class__.RELATIONSHIP_STATUS_COMPLICATED:
            pgettext_lazy(context=self.get_gender(),
                          message="It's complicated"),
            self.__class__.RELATIONSHIP_STATUS_SEPARATED:
            pgettext_lazy(context=self.get_gender(), message="Separated"),
            self.__class__.RELATIONSHIP_STATUS_ENGAGED:
            pgettext_lazy(context=self.get_gender(), message="Engaged"),
            self.__class__.RELATIONSHIP_STATUS_MARRIED:
            pgettext_lazy(context=self.get_gender(), message="Married"),
        }
        return relationship_statuses.get(self.relationship_status, "")

    def get_age(self):
        return get_age(date_of_birth=self.date_of_birth)

    def get_diet_choices_with_description(self):
        return self.__class__.diet_choices_with_description(
            gender=self.get_gender())

    def get_smoking_status_choices(self):
        return self.__class__.smoking_status_choices(gender=self.get_gender())

    def get_relationship_status_choices(self):
        return self.__class__.relationship_status_choices(
            gender=self.get_gender())
Beispiel #18
0
class SiteProfile(SiteProfileBase):
    settings = django_settings.SPEEDY_MATCH_SITE_PROFILE_SETTINGS

    LOCALIZABLE_FIELDS = ('profile_description', 'city', 'children',
                          'more_children', 'match_description')

    RELATED_NAME = 'speedy_match_site_profile'

    HEIGHT_VALID_VALUES = range(settings.MIN_HEIGHT_ALLOWED,
                                settings.MAX_HEIGHT_ALLOWED + 1)
    AGE_MATCH_VALID_VALUES = range(settings.MIN_AGE_MATCH_ALLOWED,
                                   settings.MAX_AGE_MATCH_ALLOWED + 1)

    SMOKING_STATUS_UNKNOWN = 0
    SMOKING_STATUS_NO = 1
    SMOKING_STATUS_SOMETIMES = 2
    SMOKING_STATUS_YES = 3
    SMOKING_STATUS_MAX_VALUE_PLUS_ONE = 4

    SMOKING_STATUS_CHOICES_WITH_DEFAULT = (
        (SMOKING_STATUS_UNKNOWN, _("Unknown")),
        (SMOKING_STATUS_NO, _("No")),
        (SMOKING_STATUS_SOMETIMES, _("Sometimes")),
        (SMOKING_STATUS_YES, _("Yes")),
    )
    SMOKING_STATUS_VALID_CHOICES = SMOKING_STATUS_CHOICES_WITH_DEFAULT[1:]
    SMOKING_STATUS_VALID_VALUES = [
        choice[0] for choice in SMOKING_STATUS_VALID_CHOICES
    ]

    MARITAL_STATUS_UNKNOWN = 0
    MARITAL_STATUS_SINGLE = 1
    MARITAL_STATUS_DIVORCED = 2
    MARITAL_STATUS_WIDOWED = 3
    MARITAL_STATUS_IN_RELATIONSHIP = 4
    MARITAL_STATUS_IN_OPEN_RELATIONSHIP = 5
    MARITAL_STATUS_COMPLICATED = 6
    MARITAL_STATUS_SEPARATED = 7
    MARITAL_STATUS_MARRIED = 8
    MARITAL_STATUS_MAX_VALUE_PLUS_ONE = 9

    MARITAL_STATUS_CHOICES_WITH_DEFAULT = (
        (MARITAL_STATUS_UNKNOWN, _("Unknown")),
        (MARITAL_STATUS_SINGLE, _("Single")),
        (MARITAL_STATUS_DIVORCED, _("Divorced")),
        (MARITAL_STATUS_WIDOWED, _("Widowed")),
        (MARITAL_STATUS_IN_RELATIONSHIP, _("In a relatioship")),
        (MARITAL_STATUS_IN_OPEN_RELATIONSHIP, _("In an open relationship")),
        (MARITAL_STATUS_COMPLICATED, _("It's complicated")),
        (MARITAL_STATUS_SEPARATED, _("Separated")),
        (MARITAL_STATUS_MARRIED, _("Married")),
    )
    MARITAL_STATUS_VALID_CHOICES = MARITAL_STATUS_CHOICES_WITH_DEFAULT[1:]
    MARITAL_STATUS_VALID_VALUES = [
        choice[0] for choice in MARITAL_STATUS_VALID_CHOICES
    ]

    RANK_0 = 0
    RANK_1 = 1
    RANK_2 = 2
    RANK_3 = 3
    RANK_4 = 4
    RANK_5 = 5

    RANK_CHOICES = (
        (RANK_0, _("0 hearts")),
        (RANK_1, _("1 hearts")),
        (RANK_2, _("2 hearts")),
        (RANK_3, _("3 hearts")),
        (RANK_4, _("4 hearts")),
        (RANK_5, _("5 hearts")),
    )
    RANK_VALID_VALUES = [choice[0] for choice in RANK_CHOICES]

    @staticmethod
    def gender_to_match_default():
        return list()

    @staticmethod
    def diet_match_default():
        return dict(
            {str(diet): __class__.RANK_5
             for diet in User.DIET_VALID_VALUES})

    @staticmethod
    def smoking_status_match_default():
        return dict({
            str(smoking_status): __class__.RANK_5
            for smoking_status in __class__.SMOKING_STATUS_VALID_VALUES
        })

    @staticmethod
    def marital_status_match_default():
        return dict({
            str(marital_status): __class__.RANK_5
            for marital_status in __class__.MARITAL_STATUS_VALID_VALUES
        })

    @staticmethod
    def smoking_status_choices(gender):
        return (
            # (__class__.SMOKING_STATUS_UNKNOWN, _("Unknown")), # ~~~~ TODO: remove this line!
            (__class__.SMOKING_STATUS_NO,
             pgettext_lazy(context=gender, message="No")),
            (__class__.SMOKING_STATUS_SOMETIMES,
             pgettext_lazy(context=gender, message="Sometimes")),
            (__class__.SMOKING_STATUS_YES,
             pgettext_lazy(context=gender, message="Yes")),
        )

    @staticmethod
    def marital_status_choices(gender):
        return (
            # (__class__.MARITAL_STATUS_UNKNOWN, _("Unknown")), # ~~~~ TODO: remove this line!
            (__class__.MARITAL_STATUS_SINGLE,
             pgettext_lazy(context=gender, message="Single")),
            (__class__.MARITAL_STATUS_DIVORCED,
             pgettext_lazy(context=gender, message="Divorced")),
            (__class__.MARITAL_STATUS_WIDOWED,
             pgettext_lazy(context=gender, message="Widowed")),
            (__class__.MARITAL_STATUS_IN_RELATIONSHIP,
             pgettext_lazy(context=gender, message="In a relatioship")),
            (__class__.MARITAL_STATUS_IN_OPEN_RELATIONSHIP,
             pgettext_lazy(context=gender, message="In an open relationship")),
            (__class__.MARITAL_STATUS_COMPLICATED,
             pgettext_lazy(context=gender, message="It's complicated")),
            (__class__.MARITAL_STATUS_SEPARATED,
             pgettext_lazy(context=gender, message="Separated")),
            (__class__.MARITAL_STATUS_MARRIED,
             pgettext_lazy(context=gender, message="Married")),
        )

    user = models.OneToOneField(to=User,
                                verbose_name=_('user'),
                                primary_key=True,
                                on_delete=models.CASCADE,
                                related_name=RELATED_NAME)
    notify_on_like = models.PositiveIntegerField(
        verbose_name=_('on new likes'),
        choices=User.NOTIFICATIONS_CHOICES,
        default=User.NOTIFICATIONS_ON)
    active_languages = models.TextField(verbose_name=_('active languages'),
                                        blank=True)
    height = models.SmallIntegerField(verbose_name=_('height'),
                                      help_text=_('cm'),
                                      blank=True,
                                      null=True)
    # ~~~~ TODO: diet, smoking_status and marital_status - decide which model should contain them - are they relevant also to Speedy Net or only to Speedy Match?
    smoking_status = models.SmallIntegerField(
        verbose_name=_('smoking status'),
        choices=SMOKING_STATUS_CHOICES_WITH_DEFAULT,
        default=SMOKING_STATUS_UNKNOWN)
    marital_status = models.SmallIntegerField(
        verbose_name=_('marital status'),
        choices=MARITAL_STATUS_CHOICES_WITH_DEFAULT,
        default=MARITAL_STATUS_UNKNOWN)
    profile_description = TranslatedField(
        models.TextField(verbose_name=_('Few words about me'),
                         blank=True,
                         null=True))
    city = TranslatedField(
        models.CharField(verbose_name=_('city or locality'),
                         max_length=255,
                         blank=True,
                         null=True))
    children = TranslatedField(
        models.TextField(verbose_name=_('Do you have children? How many?'),
                         blank=True,
                         null=True))
    more_children = TranslatedField(
        models.TextField(verbose_name=_('Do you want (more) children?'),
                         blank=True,
                         null=True))
    match_description = TranslatedField(
        models.TextField(verbose_name=_('My ideal match'),
                         blank=True,
                         null=True))
    gender_to_match = ArrayField(models.SmallIntegerField(),
                                 verbose_name=_('Gender'),
                                 size=len(User.GENDER_VALID_VALUES),
                                 default=gender_to_match_default.__func__,
                                 blank=True,
                                 null=True)
    min_age_match = models.SmallIntegerField(
        verbose_name=_('minimal age to match'),
        default=settings.MIN_AGE_MATCH_ALLOWED)
    max_age_match = models.SmallIntegerField(
        verbose_name=_('maximal age to match'),
        default=settings.MAX_AGE_MATCH_ALLOWED)
    diet_match = JSONField(verbose_name=_('diet match'),
                           default=diet_match_default.__func__)
    smoking_status_match = JSONField(
        verbose_name=_('smoking status match'),
        default=smoking_status_match_default.__func__)
    marital_status_match = JSONField(
        verbose_name=_('marital status match'),
        default=marital_status_match_default.__func__)
    activation_step = models.PositiveSmallIntegerField(default=2)

    objects = SiteProfileManager()

    @property
    def is_active(self):
        return ((self.user.is_active)
                and (get_language() in self.get_active_languages()))

    class Meta:
        verbose_name = _('Speedy Match Profile')
        verbose_name_plural = _('Speedy Match Profiles')

    def __str__(self):
        return '{} @ Speedy Match'.format(self.user)

    def _set_active_languages(self, languages):
        languages = sorted(list(set(languages)))
        self.active_languages = ','.join(set(languages))

    def _deactivate_language(self, step):
        # Profile is invalid. Deactivate in this language.
        language_code = get_language()
        self._set_active_languages(
            set(self.get_active_languages()) - {language_code})
        self.activation_step = step
        self.user.save_user_and_profile()

    def get_active_languages(self):
        return list(
            filter(None,
                   (l.strip() for l in self.active_languages.split(','))))

    def validate_profile_and_activate(self):
        # ~~~~ TODO: all the error messages in this function may depend on the current user's (or other user's) gender.
        from speedy.match.accounts import utils
        language_code = get_language()
        error_messages = []
        for step in utils.get_steps_range():
            fields = utils.get_step_fields_to_validate(step=step)
            for field_name in fields:
                try:
                    utils.validate_field(field_name=field_name, user=self.user)
                except ValidationError as e:
                    error_messages.append(str(e))
            if (len(error_messages) > 0):
                self._deactivate_language(step=step)
                return step, error_messages
        # Registration form is complete. Check if the user has a confirmed email address.
        step = len(__class__.settings.SPEEDY_MATCH_SITE_PROFILE_FORM_FIELDS)
        if ((self.user.has_confirmed_email())
                and (step >= self.activation_step)):
            # Profile is valid. Activate in this language.
            languages = self.get_active_languages()
            if (not (language_code in languages)):
                languages.append(language_code)
                self._set_active_languages(languages=languages)
                self.user.save_user_and_profile()
        else:
            self._deactivate_language(step=self.activation_step)
            error_messages.append(_("Please confirm your email address."))
        return step, error_messages

    def call_after_verify_email_address(self):
        old_step = self.activation_step
        self.activation_step = len(
            __class__.settings.SPEEDY_MATCH_SITE_PROFILE_FORM_FIELDS)
        self.validate_profile_and_activate()
        self.activation_step = old_step
        self.user.save_user_and_profile()

    def get_matching_rank(self, other_profile, second_call=True) -> int:
        self.validate_profile_and_activate()
        try:
            other_profile.validate_profile_and_activate()
        except ValidationError as e:
            logger.error(
                "get_matching_rank::other_profile.validate_profile_and_activate() failed, other_profile.user.pk={other_user_pk}, other_profile.user.username={other_user_username}, other_profile.user.slug={other_user_slug}, e={e}"
                .format(
                    other_user_pk=other_profile.user.pk,
                    other_user_username=other_profile.user.username,
                    other_user_slug=other_profile.user.slug,
                    e=e,
                ))
            return self.__class__.RANK_0
        if (self.user.pk == other_profile.user.pk):
            return self.__class__.RANK_0
        if ((self.is_active) and (other_profile.is_active)):
            if (Block.objects.there_is_block(user_1=self.user,
                                             user_2=other_profile.user)):
                return self.__class__.RANK_0
            if (other_profile.user.gender not in self.gender_to_match):
                return self.__class__.RANK_0
            if (not (self.min_age_match <= other_profile.user.get_age() <=
                     self.max_age_match)):
                return self.__class__.RANK_0
            if (other_profile.user.diet == User.DIET_UNKNOWN):
                return self.__class__.RANK_0
            if (other_profile.smoking_status ==
                    self.__class__.SMOKING_STATUS_UNKNOWN):
                return self.__class__.RANK_0
            if (other_profile.marital_status ==
                    self.__class__.MARITAL_STATUS_UNKNOWN):
                return self.__class__.RANK_0
            diet_rank = self.diet_match.get(str(other_profile.user.diet),
                                            self.__class__.RANK_5)
            smoking_status_rank = self.smoking_status_match.get(
                str(other_profile.smoking_status), self.__class__.RANK_5)
            marital_status_rank = self.marital_status_match.get(
                str(other_profile.marital_status), self.__class__.RANK_5)
            rank = min([diet_rank, smoking_status_rank, marital_status_rank])
            if (rank > self.__class__.RANK_0) and (second_call):
                other_user_rank = other_profile.get_matching_rank(
                    other_profile=self, second_call=False)
                if (other_user_rank == self.__class__.RANK_0):
                    rank = self.__class__.RANK_0
            other_profile.rank = rank
            return rank
        else:
            return self.__class__.RANK_0

    def deactivate(self):
        self._set_active_languages([])
        self.activation_step = 0
        self.user.save_user_and_profile()

    def get_name(self):
        # Speedy Match name is user's first name.
        return self.user.get_first_name()

    def get_match_gender(self):
        if (len(self.gender_to_match) == 1):
            return User.GENDERS_DICT.get(self.gender_to_match[0])
        else:
            return User.GENDERS_DICT.get(User.GENDER_OTHER)

    def get_smoking_status_choices(self):
        return self.__class__.smoking_status_choices(
            gender=self.user.get_gender())

    def get_marital_status_choices(self):
        return self.__class__.marital_status_choices(
            gender=self.user.get_gender())

    def get_diet_match_choices(self):
        return User.diet_choices(gender=self.get_match_gender())

    def get_smoking_status_match_choices(self):
        return self.__class__.smoking_status_choices(
            gender=self.get_match_gender())

    def get_marital_status_match_choices(self):
        return self.__class__.marital_status_choices(
            gender=self.get_match_gender())
Beispiel #19
0
class FAQ(models.Model):
    question = TranslatedField(
        models.CharField(_("question"), max_length=200, null=True), )
    answer = TranslatedField(
        models.CharField(_("answer"), max_length=200, null=True), )
Beispiel #20
0
class BlogPosting(DirtyFieldsMixin, MultilingualMixin, RulesModel):
    REQUIRED_TRANSLATED_FIELDS = ('title', 'body', 'summary', 'slug')

    title = TranslatedField(models.CharField(verbose_name=_("Title"),
                                             max_length=65,
                                             blank=True),
                            {settings.LANGUAGE_CODE: {
                                'blank': False
                            }},
                            attrgetter=attrgetter)
    body = TranslatedField(models.TextField(verbose_name=_("Body"),
                                            blank=True),
                           {settings.LANGUAGE_CODE: {
                               'blank': False
                           }},
                           attrgetter=attrgetter)
    summary = TranslatedField(models.TextField(verbose_name=_("Summary"),
                                               blank=True),
                              {settings.LANGUAGE_CODE: {
                                  'blank': False
                              }},
                              attrgetter=attrgetter)
    slug = TranslatedField(models.SlugField(
        verbose_name=_("URL Name"),
        max_length=64,
        unique=True,
        db_index=True,
        blank=True,
        null=True,
        help_text=_("Human friendly unique url to identify the "
                    "content, will automatically be filled if left empty.")),
                           {settings.LANGUAGE_CODE: {
                               'null': False
                           }},
                           attrgetter=attrgetter)

    image = models.ImageField(verbose_name=_("Image"),
                              upload_to='blog_posting/original/',
                              width_field='image_width',
                              height_field='image_height',
                              null=True,
                              blank=True)
    image_height = models.SmallIntegerField(null=True, editable=False)
    image_width = models.SmallIntegerField(null=True, editable=False)

    published_at = models.DateTimeField(verbose_name=_("Publish Date"),
                                        db_index=True,
                                        null=True,
                                        blank=True)
    published_by = models.ForeignKey(settings.AUTH_USER_MODEL,
                                     on_delete=models.PROTECT,
                                     null=True,
                                     blank=True,
                                     related_name='published_blogpostings')

    created_by = models.ForeignKey(settings.AUTH_USER_MODEL,
                                   on_delete=models.PROTECT,
                                   db_index=True,
                                   related_name='created_blogpostings')
    created_at = models.DateTimeField(auto_now_add=True, editable=False)
    modified_at = models.DateTimeField(auto_now=True, editable=False)
    deleted_at = models.DateTimeField(db_index=True, null=True, blank=True)

    image_xs = models.ImageField(upload_to='blog_posting/image_xs/',
                                 null=True,
                                 editable=False)
    image_sm = models.ImageField(upload_to='blog_posting/image_sm/',
                                 null=True,
                                 editable=False)
    image_md = models.ImageField(upload_to='blog_posting/image_md/',
                                 null=True,
                                 editable=False)
    image_lg = models.ImageField(upload_to='blog_posting/image_lg/',
                                 null=True,
                                 editable=False)

    objects = BlogPostingManager()
    keywords = GenericRelation(ThingKeywordField)

    class Meta:

        get_latest_by = 'published_at'
        ordering = ['-modified_at']
        rules_permissions = {
            'add': rules.is_authenticated,
            'read': rules.always_allow,
            'change': rules.is_staff,
            'delete': rules.is_staff,
        }

    def __str__(self):  # pylint:disable=invalid-str-returned
        return self.title

    def get_absolute_url(self):
        with translation.override(self.valid_language()):
            return reverse('BlogPostingLang:Display', args=(self.slug, 'html'))

    def get_natural_key(self):
        return (self.slug, )

    def get_image_url(self):
        return reverse('BlogPosting:Image', args=(self.pk, 'lg'))

    def is_published(self):
        return self.published_at and not self.deleted_at

    def save(self, update_fields=None, **kwargs):
        dirty = self.get_dirty_fields()
        if not update_fields is None:
            dirty = {key: val for (key, val) in dirty.items()\
                    if key in update_fields}

        for langcode, _ in settings.LANGUAGES:
            title_field = to_attribute('title', langcode)
            slug_field = to_attribute('slug', langcode)
            if title_field in dirty and getattr(self, title_field) and\
                    (slug_field not in dirty or not getattr(self, slug_field)):
                setattr(self, slug_field, slugify(getattr(self, title_field)))
                if update_fields and slug_field not in update_fields:
                    update_fields.append(slug_field)

        return super().save(update_fields=update_fields, **kwargs)

    def get_html_attr_srcset(self):
        attribute_value = []
        for name, size, _ in settings.IMAGE_SIZES:
            imgfield = getattr(self, f'image_{name}')
            if not imgfield:
                continue
            attribute_value.append(
                '%s %sw' %
                (reverse('BlogPosting:Image', args=(self.pk, name)), size[0]))
        return ', '.join(attribute_value)

    def get_html_attr_sizes(self):
        attribute_value = []
        for name, size, viewport_width in settings.IMAGE_SIZES:
            imgfield = getattr(self, f'image_{name}')
            if not imgfield:
                continue
            if viewport_width:
                attribute_value.append('(max-width: %spx) %spx' %
                                       (viewport_width, size[0]))
            else:
                attribute_value.append('%spx' % size[0])
        return ', '.join(attribute_value)

    @staticmethod
    def get_microdata_type():
        return 'http://schema.org/BlogPosting'
Beispiel #21
0
class Event(DirtyFieldsMixin, MultilingualMixin, RulesModel):
    REQUIRED_TRANSLATED_FIELDS = ('title', 'body', 'summary', 'slug')

    started_at = models.DateTimeField(verbose_name=_("Started At"),
                                      db_index=True)
    stopped_at = models.DateTimeField(verbose_name=_("Stopped At"),
                                      db_index=True,
                                      null=True,
                                      blank=True)

    title = TranslatedField(models.CharField(verbose_name=_("Title"),
                                             max_length=65,
                                             blank=True),
                            {settings.LANGUAGE_CODE: {
                                'blank': False
                            }},
                            attrgetter=attrgetter)
    body = TranslatedField(models.TextField(verbose_name=_("Body"),
                                            blank=True),
                           {settings.LANGUAGE_CODE: {
                               'blank': False
                           }},
                           attrgetter=attrgetter)
    summary = TranslatedField(models.TextField(verbose_name=_("Summary"),
                                               blank=True),
                              {settings.LANGUAGE_CODE: {
                                  'blank': False
                              }},
                              attrgetter=attrgetter)
    slug = TranslatedField(models.SlugField(
        verbose_name=_("URL Name"),
        max_length=64,
        unique=True,
        db_index=True,
        blank=True,
        help_text=_("Human friendly unique url to identify the "
                    "content, will automatically be filled if left empty.")),
                           attrgetter=attrgetter)

    published_at = models.DateTimeField(verbose_name=_("Publish Date"),
                                        db_index=True,
                                        null=True,
                                        blank=True)
    published_by = models.ForeignKey(settings.AUTH_USER_MODEL,
                                     on_delete=models.PROTECT,
                                     null=True,
                                     blank=True,
                                     related_name='published_events')

    created_by = models.ForeignKey(settings.AUTH_USER_MODEL,
                                   on_delete=models.PROTECT,
                                   db_index=True,
                                   related_name='created_events')
    created_at = models.DateTimeField(auto_now_add=True, editable=False)
    modified_at = models.DateTimeField(auto_now=True, editable=False)
    deleted_at = models.DateTimeField(db_index=True, null=True, blank=True)

    objects = EventManager()

    class Meta:

        get_latest_by = 'created_at'
        ordering = ['-created_at']
        rules_permissions = {
            'add': rules.is_staff,
            'read': rules.always_allow,
            'change': rules.is_staff,
            'delete': rules.is_staff,
        }

    def __str__(self):  # pylint:disable=invalid-str-returned
        return self.title

    def get_absolute_url(self):
        with translation.override(self.valid_language()):
            return reverse('EventLang:Display', args=(self.slug, 'html'))

    def get_natural_key(self):
        return (self.slug, )

    def is_published(self):
        return self.published_at and not self.deleted_at

    def save(self, update_fields=None, **kwargs):
        dirty = self.get_dirty_fields()
        if not update_fields is None:
            dirty = {key: val for (key, val) in dirty.items()\
                    if key in update_fields}

        for langcode, _ in settings.LANGUAGES:
            title_field = to_attribute('title', langcode)
            slug_field = to_attribute('slug', langcode)
            if title_field in dirty and getattr(self, title_field) and\
                    (slug_field not in dirty or not getattr(self, slug_field)):
                setattr(self, slug_field, slugify(getattr(self, title_field)))
                if update_fields and slug_field not in update_fields:
                    update_fields.append(slug_field)

        return super().save(update_fields=update_fields, **kwargs)
class ModelWithAnyFallback(models.Model):
    optional = TranslatedField(
        models.CharField(_("optional"), max_length=20, blank=True),
        attrgetter=fallback_to_any,
    )
Beispiel #23
0
class SiteProfile(SiteProfileBase):
    RELATED_NAME = 'speedy_net_site_profile'

    user = models.OneToOneField(to=User,
                                verbose_name=_('User'),
                                primary_key=True,
                                on_delete=models.CASCADE,
                                related_name=RELATED_NAME)
    is_active = models.BooleanField(default=True)
    number_of_friends = TranslatedField(field=models.PositiveSmallIntegerField(
        verbose_name=_("Number of friends on last user's visit"),
        default=None,
        blank=True,
        null=True), )

    @property
    def is_active_and_valid(self):
        return (self.is_active)

    class Meta:
        verbose_name = _('Speedy Net Profile')
        verbose_name_plural = _('Speedy Net Profiles')

    def __str__(self):
        return '{} @ Speedy Net'.format(super().__str__())

    def save(self, *args, **kwargs):
        self._update_number_of_friends()
        return super().save(*args, **kwargs)

    def _update_number_of_friends(self):
        speedy_net_site = Site.objects.get(
            pk=django_settings.SPEEDY_NET_SITE_ID)
        previous_number_of_friends = self.number_of_friends
        self.number_of_friends = self.user.speedy_net_friends_count
        if (not (self.number_of_friends == previous_number_of_friends)):
            logger.info(
                'SpeedyNetSiteProfile::_update_number_of_friends::User {user} has {number_of_friends} friends on {site_name}.'
                .format(site_name=_(speedy_net_site.name),
                        user=self.user,
                        number_of_friends=self.number_of_friends))
            if (self.number_of_friends >
                    User.settings.MAX_NUMBER_OF_FRIENDS_ALLOWED - 20):
                logger.warning(
                    'SpeedyNetSiteProfile::_update_number_of_friends::User {user} has more than {number_of_friends} friends on {site_name}.'
                    .format(site_name=_(speedy_net_site.name),
                            user=self.user,
                            number_of_friends=User.settings.
                            MAX_NUMBER_OF_FRIENDS_ALLOWED - 20))
            if (self.number_of_friends >
                    User.settings.MAX_NUMBER_OF_FRIENDS_ALLOWED):
                logger.error(
                    'SpeedyNetSiteProfile::_update_number_of_friends::User {user} has more than {MAX_NUMBER_OF_FRIENDS_ALLOWED} friends on {site_name}.'
                    .format(site_name=_(speedy_net_site.name),
                            user=self.user,
                            MAX_NUMBER_OF_FRIENDS_ALLOWED=User.settings.
                            MAX_NUMBER_OF_FRIENDS_ALLOWED))

    def activate(self):
        self.is_active = True
        self.user.is_active = True
        self.user.save_user_and_profile()

    def deactivate(self):
        self.is_active = False
        if (not (self.user.is_superuser)):
            self.user.is_active = False
        self.user.save_user_and_profile()

    def get_name(self):
        return self.user.get_full_name()

    def call_after_verify_email_address(self):
        pass
Beispiel #24
0
class MyFile(DirtyFieldsMixin, MultilingualMixin, RulesModel):
    REQUIRED_TRANSLATED_FIELDS = ('description',)

    filehash = models.CharField(verbose_name=_("Title"), max_length=65,
            editable=False)
    mimetype = models.CharField(verbose_name=_("Mime Type"), max_length=50,
            editable=False)

    description = TranslatedField(
            models.TextField(verbose_name=_("Description"), blank=True),
            {settings.LANGUAGE_CODE: {'blank': False}},
            attrgetter=attrgetter)
    alt_text = TranslatedField(
            models.TextField(verbose_name=_("Alternate Text (alt text)"),
                null=True, blank=True,
                help_text=_("Only need to fill this if you were uploading "
                "an image.")),
            attrgetter=attrgetter)
    databits = models.FileField(verbose_name=_("Content"),
            upload_to='files/original/')

    created_by = models.ForeignKey(settings.AUTH_USER_MODEL,
            on_delete=models.PROTECT, db_index=True,
            related_name='created_files')
    created_at = models.DateTimeField(auto_now_add=True, editable=False)
    modified_at = models.DateTimeField(auto_now=True, editable=False)
    deleted_at = models.DateTimeField(db_index=True, null=True, blank=True)

    image_xs = models.ImageField(upload_to='files/image_xs/', null=True,
            editable=False)
    image_sm = models.ImageField(upload_to='files/image_sm/', null=True,
            editable=False)
    image_md = models.ImageField(upload_to='files/image_md/', null=True,
            editable=False)
    image_lg = models.ImageField(upload_to='files/image_lg/', null=True,
            editable=False)

    objects = MyFileManager()


    class Meta:

        get_latest_by = 'created_at'
        ordering = ['-created_at']
        rules_permissions = {
            'add': rules.is_authenticated,
            'read': rules.always_allow,
            'change': rules.is_staff,
            'delete': rules.is_staff,
        }


    def __str__(self): # pylint:disable=invalid-str-returned
        return self.filehash


    def get_absolute_url(self):
        if self.is_image():
            return reverse('MyFile:Display', args=(self.pk, 'xs'))
        return reverse('MyFile:Display', args=(self.pk,))


    def get_natural_key(self):
        return (self.filehash,)


    def is_image(self):
        return self.mimetype.startswith('image/')


    def get_filename(self):
        return os.path.basename(self.databits.name)


    def get_html_attr_srcset(self):
        attribute_value = []
        for name, size, _ in settings.IMAGE_SIZES:
            imgfield = getattr(self, f'image_{name}')
            if not imgfield:
                continue
            attribute_value.append('%s %sw' % (
                    reverse('MyFile:Display', args=(self.pk, name)), size[0]))
        return ', '.join(attribute_value)


    def get_html_attr_sizes(self):
        attribute_value = []
        for name, size, viewport_width in settings.IMAGE_SIZES:
            imgfield = getattr(self, f'image_{name}')
            if not imgfield:
                continue
            if viewport_width:
                attribute_value.append('(max-width: %spx) %spx' % (
                        viewport_width,
                        size[0]))
            else:
                attribute_value.append('%spx' % size[0])
        return ', '.join(attribute_value)
class CustomLanguagesModel(models.Model):
    name = TranslatedField(
        models.CharField(_("name"), max_length=200),
        languages=("fr", "it"),
        attrgetter=custom_attrgetter,
    )
Beispiel #26
0
class CustomLanguagesModel(models.Model):
    name = TranslatedField(
        models.CharField(_('name'), max_length=200),
        languages=('fr', 'it'),
        attrgetter=custom_attrgetter,
    )
Beispiel #27
0
class SpecificModel(models.Model):
    name = TranslatedField(
        models.CharField(_('name'), max_length=200, blank=True),
        {'en': {'blank': False}},
    )
Beispiel #28
0
class SiteProfile(SiteProfileBase):
    LOCALIZABLE_FIELDS = ('profile_description', 'children', 'more_children',
                          'match_description')

    RELATED_NAME = 'speedy_match_site_profile'

    RANK_0 = 0
    RANK_1 = 1
    RANK_2 = 2
    RANK_3 = 3
    RANK_4 = 4
    RANK_5 = 5

    RANK_CHOICES = (
        (RANK_0, _("No match")),
        (RANK_1, _("One heart")),
        (RANK_2, _("Two hearts")),
        (RANK_3, _("Three hearts")),
        (RANK_4, _("Four hearts")),
        (RANK_5, _("Five hearts")),
    )
    RANK_VALID_VALUES = [choice[0] for choice in RANK_CHOICES]

    @staticmethod
    def active_languages_default():
        return list()

    @staticmethod
    def gender_to_match_default():
        return list()

    @staticmethod
    def min_age_to_match_default():
        return __class__.settings.MIN_AGE_TO_MATCH_ALLOWED

    @staticmethod
    def max_age_to_match_default():
        return __class__.settings.MAX_AGE_TO_MATCH_ALLOWED

    @staticmethod
    def diet_match_default():
        return dict(
            {str(diet): __class__.RANK_5
             for diet in User.DIET_VALID_VALUES})

    @staticmethod
    def smoking_status_match_default():
        return dict({
            str(smoking_status): __class__.RANK_5
            for smoking_status in User.SMOKING_STATUS_VALID_VALUES
        })

    @staticmethod
    def relationship_status_match_default():
        return dict({
            str(relationship_status): __class__.RANK_5
            for relationship_status in User.RELATIONSHIP_STATUS_VALID_VALUES
        })

    @staticmethod
    def diet_to_match_default():
        return list()

    @staticmethod
    def smoking_status_to_match_default():
        return list()

    @staticmethod
    def relationship_status_to_match_default():
        return list()

    @staticmethod
    def get_rank_description(rank):
        rank_descriptions = {
            __class__.RANK_0: _("No match"),
            __class__.RANK_1: _("One heart"),
            __class__.RANK_2: _("Two hearts"),
            __class__.RANK_3: _("Three hearts"),
            __class__.RANK_4: _("Four hearts"),
            __class__.RANK_5: _("Five hearts"),
        }
        return rank_descriptions.get(rank, "")

    user = models.OneToOneField(to=User,
                                verbose_name=_('User'),
                                primary_key=True,
                                on_delete=models.CASCADE,
                                related_name=RELATED_NAME)
    notify_on_like = models.SmallIntegerField(
        verbose_name=_('On new likes'),
        choices=User.NOTIFICATIONS_CHOICES,
        default=User.NOTIFICATIONS_ON)
    active_languages = ArrayField(
        base_field=models.CharField(max_length=2,
                                    choices=django_settings.LANGUAGES),
        verbose_name=_('Active languages'),
        size=len(django_settings.LANGUAGES),
        default=active_languages_default.__func__,
        blank=True,
        null=True,
    )
    height = models.SmallIntegerField(verbose_name=_('Height'),
                                      help_text=_('cm'),
                                      blank=True,
                                      null=True)
    profile_description = TranslatedField(field=models.TextField(
        verbose_name=_('Few words about me'),
        max_length=50000,
        validators=[MaxLengthValidator(limit_value=50000)],
        blank=True,
        null=True), )
    children = TranslatedField(field=models.TextField(
        verbose_name=_('Do you have children? How many?'),
        max_length=50000,
        validators=[MaxLengthValidator(limit_value=50000)],
        blank=True,
        null=True), )
    more_children = TranslatedField(field=models.TextField(
        verbose_name=_('Do you want (more) children?'),
        max_length=50000,
        validators=[MaxLengthValidator(limit_value=50000)],
        blank=True,
        null=True), )
    match_description = TranslatedField(field=models.TextField(
        verbose_name=_('My ideal match'),
        max_length=50000,
        validators=[MaxLengthValidator(limit_value=50000)],
        blank=True,
        null=True), )
    gender_to_match = ArrayField(
        base_field=models.SmallIntegerField(choices=User.GENDER_CHOICES),
        verbose_name=_('Gender to match'),
        size=len(User.GENDER_VALID_VALUES),
        default=gender_to_match_default.__func__,
        blank=True,
        null=True,
    )
    min_age_to_match = models.SmallIntegerField(
        verbose_name=_('Minimal age to match'),
        default=min_age_to_match_default.__func__)
    max_age_to_match = models.SmallIntegerField(
        verbose_name=_('Maximal age to match'),
        default=max_age_to_match_default.__func__)
    diet_match = JSONField(verbose_name=_('Diet match'),
                           default=diet_match_default.__func__)
    smoking_status_match = JSONField(
        verbose_name=_('Smoking status match'),
        default=smoking_status_match_default.__func__)
    relationship_status_match = JSONField(
        verbose_name=_('Relationship status match'),
        default=relationship_status_match_default.__func__)
    diet_to_match = ArrayField(
        base_field=models.SmallIntegerField(choices=User.DIET_VALID_CHOICES),
        verbose_name=_('Diet to match'),
        size=len(User.DIET_VALID_VALUES),
        default=diet_to_match_default.__func__,
        blank=True,
        null=True,
    )
    smoking_status_to_match = ArrayField(
        base_field=models.SmallIntegerField(
            choices=User.SMOKING_STATUS_VALID_CHOICES),
        verbose_name=_('Smoking status to match'),
        size=len(User.SMOKING_STATUS_VALID_VALUES),
        default=smoking_status_to_match_default.__func__,
        blank=True,
        null=True,
    )
    relationship_status_to_match = ArrayField(
        base_field=models.SmallIntegerField(
            choices=User.RELATIONSHIP_STATUS_VALID_CHOICES),
        verbose_name=_('Relationship status to match'),
        size=len(User.RELATIONSHIP_STATUS_VALID_VALUES),
        default=relationship_status_to_match_default.__func__,
        blank=True,
        null=True,
    )
    activation_step = TranslatedField(field=models.PositiveSmallIntegerField(
        verbose_name=_('Activation step'), default=2), )
    number_of_matches = TranslatedField(field=models.PositiveSmallIntegerField(
        verbose_name=_("Number of matches on last user's search"),
        default=None,
        blank=True,
        null=True), )
    profile_picture_months_offset = models.PositiveSmallIntegerField(
        default=5
    )  # If a face is detected, will be 0. Otherwise, will be 5 months.
    not_allowed_to_use_speedy_match = models.BooleanField(
        default=False)  # If set to True, user will have no matches.
    likes_to_user_count = models.PositiveSmallIntegerField(default=0,
                                                           blank=True,
                                                           null=True)

    objects = SiteProfileManager()

    @classproperty
    def settings(cls):
        return django_settings.SPEEDY_MATCH_SITE_PROFILE_SETTINGS

    @classproperty
    def HEIGHT_VALID_VALUES(cls):
        return range(cls.settings.MIN_HEIGHT_ALLOWED,
                     cls.settings.MAX_HEIGHT_ALLOWED + 1)

    @classproperty
    def AGE_TO_MATCH_VALID_VALUES(cls):
        return range(cls.settings.MIN_AGE_TO_MATCH_ALLOWED,
                     cls.settings.MAX_AGE_TO_MATCH_ALLOWED + 1)

    @cached_property
    def is_active(self):
        return ((self.user.is_active)
                and (get_language() in self.active_languages))

    @cached_property
    def is_active_and_valid(self):
        if (self.is_active):
            step, error_messages = self.validate_profile_and_activate(
                commit=False)
            error = False
            if (len(error_messages) > 0):
                error = True
            if (not (step == len(
                    __class__.settings.SPEEDY_MATCH_SITE_PROFILE_FORM_FIELDS))
                ):
                error = True
            if (error):
                logger.error(
                    "is_active_and_valid::user is active but not valid, step={step}, error_messages={error_messages}, self.user.pk={self_user_pk}, self.user.username={self_user_username}, self.user.slug={self_user_slug} (registered {registered_days_ago} days ago)"
                    .format(
                        step=step,
                        error_messages=error_messages,
                        self_user_pk=self.user.pk,
                        self_user_username=self.user.username,
                        self_user_slug=self.user.slug,
                        registered_days_ago=(now() -
                                             self.user.date_created).days,
                    ))
                return False
        return (self.is_active)

    class Meta:
        verbose_name = _('Speedy Match Profile')
        verbose_name_plural = _('Speedy Match Profiles')
        ordering = ('-last_visit', 'user_id')

    def __str__(self):
        return '{} @ Speedy Match'.format(super().__str__())

    def save(self, *args, **kwargs):
        if (hasattr(self, "_rank_dict")):
            delattr(self, "_rank_dict")
        self._set_values_to_match()
        if (self.activation_step < 2):
            self.activation_step = 2
        if (self.activation_step > len(
                __class__.settings.SPEEDY_MATCH_SITE_PROFILE_FORM_FIELDS)):
            self.activation_step = len(
                __class__.settings.SPEEDY_MATCH_SITE_PROFILE_FORM_FIELDS)
        if ((self.is_active) and (self.activation_step < len(
                __class__.settings.SPEEDY_MATCH_SITE_PROFILE_FORM_FIELDS))):
            self._deactivate_language(step=self.activation_step, commit=False)
        if ((len(self.active_languages) > 0)
                and (not (self.user.has_confirmed_email))):
            self._set_active_languages(languages=[])
        return super().save(*args, **kwargs)

    def _set_values_to_match(self):
        from speedy.match.accounts import utils
        self._set_active_languages(languages=self.active_languages)
        self.gender_to_match = sorted(list(set(self.gender_to_match)))
        errors = 0
        for field_name in [
                'diet_match', 'smoking_status_match',
                'relationship_status_match'
        ]:
            try:
                utils.validate_field(field_name=field_name, user=self.user)
            except (ValidationError, AttributeError):
                errors += 1
        if (errors == 0):
            self.diet_to_match = [
                diet for diet in User.DIET_VALID_VALUES
                if (self.diet_match[str(diet)] > self.__class__.RANK_0)
            ]
            self.smoking_status_to_match = [
                smoking_status
                for smoking_status in User.SMOKING_STATUS_VALID_VALUES
                if (self.smoking_status_match[str(smoking_status)] >
                    self.__class__.RANK_0)
            ]
            self.relationship_status_to_match = [
                relationship_status for relationship_status in
                User.RELATIONSHIP_STATUS_VALID_VALUES
                if (self.relationship_status_match[str(relationship_status)] >
                    self.__class__.RANK_0)
            ]
        else:
            self.diet_to_match = list()
            self.smoking_status_to_match = list()
            self.relationship_status_to_match = list()

    def _set_active_languages(self, languages):
        self.active_languages = sorted(list(set(languages)))
        if (hasattr(self, "is_active")):
            delattr(self, "is_active")
        if (hasattr(self, "is_active_and_valid")):
            delattr(self, "is_active_and_valid")

    def _deactivate_language(self, step, commit=True):
        # Profile is invalid. Deactivate in this language.
        language_code = get_language()
        self._set_active_languages(languages=set(self.active_languages) -
                                   {language_code})
        self.activation_step = step
        if (commit):
            self.user.save_user_and_profile()

    def validate_profile_and_activate(self, commit=True):
        from speedy.match.accounts import utils
        language_code = get_language()
        error_messages = []
        for step in utils.get_steps_range():
            fields = utils.get_step_fields_to_validate(step=step)
            for field_name in fields:
                try:
                    utils.validate_field(field_name=field_name, user=self.user)
                except ValidationError as e:
                    error_messages.append(str(e))
            if (len(error_messages) > 0):
                if (commit):
                    self._deactivate_language(step=step)
                return step, error_messages
        # Check if the user is not allowed to use Speedy Match.
        if (self.not_allowed_to_use_speedy_match):
            step = len(
                __class__.settings.SPEEDY_MATCH_SITE_PROFILE_FORM_FIELDS) - 1
            return step, error_messages
        # Registration form is complete. Check if the user has a confirmed email address.
        step = len(__class__.settings.SPEEDY_MATCH_SITE_PROFILE_FORM_FIELDS)
        if ((self.user.has_confirmed_email)
                and (step >= self.activation_step)):
            if (commit):
                # Profile is valid. Activate in this language.
                self.activation_step = step
                languages = self.active_languages
                if (not (language_code in languages)):
                    languages.append(language_code)
                    self._set_active_languages(languages=languages)
                self.user.save_user_and_profile()
        else:
            if (commit):
                self._deactivate_language(step=self.activation_step)
            error_messages.append(_("Please confirm your email address."))
        return step, error_messages

    def call_after_verify_email_address(self):
        pass

    def _get_matching_rank(self, other_profile, second_call=True) -> int:
        self._get_matching_rank_calls = getattr(
            self, "_get_matching_rank_calls", 0) + 1
        if (self._get_matching_rank_calls >= 5):
            logger.debug(
                '_get_matching_rank::_get_matching_rank_calls={_get_matching_rank_calls}, self={self}, other_profile={other_profile}'
                .format(_get_matching_rank_calls=self._get_matching_rank_calls,
                        self=self,
                        other_profile=other_profile))
        if (self.user.pk == other_profile.user.pk):
            return self.__class__.RANK_0
        if ((self.is_active_and_valid)
                and (other_profile.is_active_and_valid)):
            if (Block.objects.there_is_block(user_1=self.user,
                                             user_2=other_profile.user)):
                return self.__class__.RANK_0
            if (not (
                (__class__.settings.MIN_HEIGHT_TO_MATCH <= self.height <=
                 __class__.settings.MAX_HEIGHT_TO_MATCH) and
                (__class__.settings.MIN_HEIGHT_TO_MATCH <= other_profile.height
                 <= __class__.settings.MAX_HEIGHT_TO_MATCH))):
                return self.__class__.RANK_0
            if (self.not_allowed_to_use_speedy_match
                    or other_profile.not_allowed_to_use_speedy_match):
                return self.__class__.RANK_0
            if (other_profile.user.gender not in self.gender_to_match):
                return self.__class__.RANK_0
            if (not (self.min_age_to_match <= other_profile.user.get_age() <=
                     self.max_age_to_match)):
                return self.__class__.RANK_0
            if (other_profile.user.diet == User.DIET_UNKNOWN):
                return self.__class__.RANK_0
            if (other_profile.user.smoking_status ==
                    User.SMOKING_STATUS_UNKNOWN):
                return self.__class__.RANK_0
            if (other_profile.user.relationship_status ==
                    User.RELATIONSHIP_STATUS_UNKNOWN):
                return self.__class__.RANK_0
            diet_rank = self.diet_match.get(str(other_profile.user.diet),
                                            self.__class__.RANK_0)
            smoking_status_rank = self.smoking_status_match.get(
                str(other_profile.user.smoking_status), self.__class__.RANK_0)
            relationship_status_rank = self.relationship_status_match.get(
                str(other_profile.user.relationship_status),
                self.__class__.RANK_0)
            rank = min(
                [diet_rank, smoking_status_rank, relationship_status_rank])
            if (rank > self.__class__.RANK_0) and (second_call):
                other_user_rank = other_profile._get_matching_rank(
                    other_profile=self, second_call=False)
                if (other_user_rank == self.__class__.RANK_0):
                    rank = self.__class__.RANK_0
            return rank
        else:
            if (self.is_active):
                if (not (self.is_active_and_valid)):
                    logger.error(
                        '_get_matching_rank::get inside "if (not (self.is_active_and_valid)):", self={self}, other_profile={other_profile}'
                        .format(self=self, other_profile=other_profile))
            if (other_profile.is_active):
                if (not (other_profile.is_active_and_valid)):
                    logger.error(
                        '_get_matching_rank::get inside "if (not (other_profile.is_active_and_valid)):", self={self}, other_profile={other_profile}'
                        .format(self=self, other_profile=other_profile))
            return self.__class__.RANK_0

    def get_matching_rank(self, other_profile) -> int:
        if (self.user.pk == other_profile.user.pk):
            return self.__class__.RANK_0
        rank_dict = getattr(self, "_rank_dict", {})
        if (other_profile.user.pk in rank_dict):
            rank = rank_dict[other_profile.user.pk]
        else:
            rank = self._get_matching_rank(other_profile=other_profile)
            rank_dict[other_profile.user.pk] = rank
            self._rank_dict = rank_dict
        other_profile.rank = rank
        self.rank = self.__class__.RANK_0
        return rank

    def deactivate(self):
        self._set_active_languages(languages=[])
        self.activation_step = 2
        for language_code, language_name in django_settings.LANGUAGES:
            setattr(
                self,
                to_attribute(name='activation_step',
                             language_code=language_code), 2)
        self.user.save_user_and_profile()

    def get_name(self):
        # Speedy Match name is the user's first name.
        return self.user.get_first_name()

    def get_match_gender(self):
        if (len(self.gender_to_match) == 1):
            match_gender = User.GENDERS_DICT.get(self.gender_to_match[0])
        else:
            match_gender = User.GENDERS_DICT.get(User.GENDER_OTHER)
        return match_gender

    def get_like_gender(self):
        # No need to query the database if len(self.gender_to_match) is not 1.
        if ((len(self.gender_to_match) == 1)
                and ({self.get_match_gender()} == {
                    like.to_user.get_gender()
                    for like in UserLike.objects.filter(from_user=self.user).
                    prefetch_related("to_user").distinct()
                } | {
                    like.from_user.get_gender()
                    for like in UserLike.objects.filter(to_user=self.user).
                    prefetch_related("from_user").distinct()
                } | {self.get_match_gender()})):
            like_gender = self.get_match_gender()
        else:
            like_gender = User.GENDERS_DICT.get(User.GENDER_OTHER)
        return like_gender

    def get_diet_match_choices(self):
        return User.diet_choices(gender=self.get_match_gender())

    def get_smoking_status_match_choices(self):
        return User.smoking_status_choices(gender=self.get_match_gender())

    def get_relationship_status_match_choices(self):
        return User.relationship_status_choices(gender=self.get_match_gender())
class Country(models.Model):
    name = TranslatedField(
        models.CharField(max_length=255, default=None, blank=True))

    def __str__(self):
        return self.name
Beispiel #30
0
class User(PermissionsMixin, Entity, AbstractBaseUser):
    settings = django_settings.USER_SETTINGS
    # settings = speedy_net_global_settings.UserSettings # ~~~~ TODO: remove this line!

    LOCALIZABLE_FIELDS = ('first_name', 'last_name')

    AGE_VALID_VALUES_IN_MODEL = range(settings.MIN_AGE_ALLOWED_IN_MODEL,
                                      settings.MAX_AGE_ALLOWED_IN_MODEL)
    AGE_VALID_VALUES_IN_FORMS = range(settings.MIN_AGE_ALLOWED_IN_FORMS,
                                      settings.MAX_AGE_ALLOWED_IN_FORMS)

    GENDER_UNKNOWN = 0
    GENDER_FEMALE = 1
    GENDER_MALE = 2
    GENDER_OTHER = 3
    GENDER_MAX_VALUE_PLUS_ONE = 4

    GENDER_FEMALE_STRING = 'female'
    GENDER_MALE_STRING = 'male'
    GENDER_OTHER_STRING = 'other'

    GENDER_CHOICES = (
        (GENDER_FEMALE, _("Female")),
        (GENDER_MALE, _("Male")),
        (GENDER_OTHER, _("Other")),
    )
    GENDER_VALID_VALUES = [choice[0] for choice in GENDER_CHOICES]
    GENDERS_DICT = {
        GENDER_FEMALE: GENDER_FEMALE_STRING,
        GENDER_MALE: GENDER_MALE_STRING,
        GENDER_OTHER: GENDER_OTHER_STRING
    }
    # print(GENDERS_DICT) # for debugging. # ~~~~ TODO: remove this line!
    # ALL_GENDERS = [GENDERS_DICT[gender] for gender in GENDER_VALID_VALUES] # ~~~~ TODO: remove this line!
    # ALL_GENDERS = GENDERS_DICT # ~~~~ TODO: remove this line!
    # ALL_GENDERS = [__class__.GENDERS_DICT[gender] for gender in __class__.GENDER_VALID_VALUES] # ~~~~ TODO: maybe rename to ALL_GENDERS_STRINGS? # ~~~~ TODO: remove this line!
    # ALL_GENDERS = [__class__.GENDERS_DICT[gender] for gender in __class__.GENDER_VALID_VALUES] # ~~~~ TODO: maybe rename to ALL_GENDERS_STRINGS? # ~~~~ TODO: remove this line!

    DIET_UNKNOWN = 0
    DIET_VEGAN = 1
    DIET_VEGETARIAN = 2
    DIET_CARNIST = 3
    DIET_MAX_VALUE_PLUS_ONE = 4

    DIET_CHOICES_WITH_DEFAULT = (
        (DIET_UNKNOWN, _("Unknown")),
        (DIET_VEGAN, _("Vegan (eats only plants and fungi)")),
        (DIET_VEGETARIAN, _("Vegetarian (doesn't eat fish and meat)")),
        (DIET_CARNIST, _("Carnist (eats animals)")),
    )
    DIET_VALID_CHOICES = DIET_CHOICES_WITH_DEFAULT[1:]
    DIET_VALID_VALUES = [choice[0] for choice in DIET_VALID_CHOICES]

    NOTIFICATIONS_OFF = 0
    NOTIFICATIONS_ON = 1

    NOTIFICATIONS_CHOICES = (
        (NOTIFICATIONS_ON, _("Notify me")),
        (NOTIFICATIONS_OFF, _("Don't notify me")),
    )

    USERNAME_FIELD = 'username'
    REQUIRED_FIELDS = [
        'first_name', 'last_name', 'date_of_birth', 'gender', 'diet', 'slug'
    ]

    @staticmethod
    def diet_choices(gender):
        return (
            # (__class__.DIET_UNKNOWN, _("Unknown")), # ~~~~ TODO: remove this line!
            (__class__.DIET_VEGAN,
             pgettext_lazy(context=gender,
                           message="Vegan (eats only plants and fungi)")),
            (__class__.DIET_VEGETARIAN,
             pgettext_lazy(context=gender,
                           message="Vegetarian (doesn't eat fish and meat)")),
            (__class__.DIET_CARNIST,
             pgettext_lazy(context=gender, message="Carnist (eats animals)")),
        )

    first_name = TranslatedField(
        models.CharField(verbose_name=_('first name'), max_length=75))
    last_name = TranslatedField(
        models.CharField(verbose_name=_('last name'), max_length=75))
    gender = models.SmallIntegerField(verbose_name=_('I am'),
                                      choices=GENDER_CHOICES)
    date_of_birth = models.DateField(verbose_name=_('date of birth'))
    # ~~~~ TODO: diet, smoking_status and marital_status - decide which model should contain them - are they relevant also to Speedy Net or only to Speedy Match?
    diet = models.SmallIntegerField(verbose_name=_('diet'),
                                    choices=DIET_CHOICES_WITH_DEFAULT,
                                    default=DIET_UNKNOWN)
    is_active = models.BooleanField(default=True)
    is_staff = models.BooleanField(default=False)
    access_dob_day_month = UserAccessField(
        verbose_name=_('who can view my birth month and day'),
        default=UserAccessField.ACCESS_ME)
    access_dob_year = UserAccessField(
        verbose_name=_('who can view my birth year'),
        default=UserAccessField.ACCESS_ME)
    notify_on_message = models.PositiveIntegerField(
        verbose_name=_('on new messages'),
        choices=NOTIFICATIONS_CHOICES,
        default=NOTIFICATIONS_ON)

    objects = UserManager()

    @property
    def validators(self):
        validators = {
            'username':
            get_username_validators(
                min_username_length=self.settings.MIN_USERNAME_LENGTH,
                max_username_length=self.settings.MAX_USERNAME_LENGTH,
                allow_letters_after_digits=False),
            'slug':
            get_slug_validators(
                min_username_length=self.settings.MIN_USERNAME_LENGTH,
                max_username_length=self.settings.MAX_USERNAME_LENGTH,
                min_slug_length=self.settings.MIN_SLUG_LENGTH,
                max_slug_length=self.settings.MAX_SLUG_LENGTH,
                allow_letters_after_digits=False) + ["validate_slug"],
            'date_of_birth': [validate_date_of_birth_in_model],
        }
        return validators

    @property
    def email(self):
        try:
            return self.email_addresses.get(is_primary=True).email
        except UserEmailAddress.DoesNotExist:
            return None

    @property
    def profile(self):
        if (not (hasattr(self, '_profile'))):
            self.refresh_all_profiles()
        return self._profile

    @property
    def speedy_net_profile(self):
        if (django_settings.LOGIN_ENABLED):
            if (not (hasattr(self, '_speedy_net_profile'))):
                self.refresh_all_profiles()
            return self._speedy_net_profile

    @property
    def speedy_match_profile(self):
        if (django_settings.LOGIN_ENABLED):
            if (not (hasattr(self, '_speedy_match_profile'))):
                self.refresh_all_profiles()
            return self._speedy_match_profile

    class Meta:
        verbose_name = _('user')
        verbose_name_plural = _('users')
        ordering = ('-last_login', 'id')
        swappable = 'AUTH_USER_MODEL'

    def __str__(self):
        # Depends on site: full name in Speedy Net, first name in Speedy Match.
        return self.profile.get_name()

    def set_password(self, raw_password):
        password_validation.validate_password(password=raw_password)
        return super().set_password(raw_password=raw_password)

    def delete(self, *args, **kwargs):
        if ((self.is_staff) or (self.is_superuser)):
            warnings.warn('Can’t delete staff user.')
            return False
        else:
            return super().delete(*args, **kwargs)

    def clean_fields(self, exclude=None):
        """
        Allows to have different slug and username validators for Entity and User.
        """
        if exclude is None:
            exclude = []

        if (self.is_superuser):
            exclude = ['username', 'slug']

        return super().clean_fields(exclude=exclude)

    def clean_all_fields(self, exclude=None):
        super().clean_all_fields(exclude=exclude)

        for base_field_name in __class__.LOCALIZABLE_FIELDS:
            self.clean_localizable_field(base_field_name=base_field_name)

    def clean_localizable_field(self, base_field_name):
        # raise Exception(base_field_name)############ # ~~~~ TODO: remove this line!
        field_names = get_all_field_names(base_field_name=base_field_name)
        for field_name in field_names:
            if (not (string_is_not_empty(getattr(self, field_name)))):
                for _field_name in field_names:
                    # Check again because maybe this field is already not empty.
                    if (not (string_is_not_empty(getattr(self, field_name)))):
                        if (string_is_not_empty(getattr(self, _field_name))):
                            setattr(self, field_name,
                                    getattr(self, _field_name))

    def get_absolute_url(self):
        return reverse('profiles:user', kwargs={'slug': self.slug})

    def mail_user(self,
                  template_name_prefix,
                  context=None,
                  send_to_unconfirmed=False):
        addresses = self.email_addresses.filter(is_primary=True)
        if (not (send_to_unconfirmed)):
            addresses = addresses.filter(is_confirmed=True)
        addresses = list(addresses)
        if (addresses):
            return addresses[0].mail(template_name_prefix=template_name_prefix,
                                     context=context)
        return False

    def get_full_name(self):
        return '{} {}'.format(self.first_name,
                              self.last_name).strip() or self.slug

    def get_first_name(self):
        return '{}'.format(self.first_name).strip() or self.slug

    def get_short_name(self):
        return self.get_first_name()

    def has_confirmed_email(self):
        return (self.email_addresses.filter(is_confirmed=True).exists())

    @cached_property
    def has_confirmed_email_or_registered_now(self):
        return ((self.has_confirmed_email())
                or (self.date_created > now() - timedelta(hours=2)))

    def activate(self):
        self.is_active = True
        self.save_user_and_profile()

    def get_profile(self, model=None, profile_model=None) -> 'SiteProfileBase':
        if (model is None):
            model = get_site_profile_model(profile_model=profile_model)
        profile = getattr(self, model.RELATED_NAME, None)
        if (profile is None):
            profile = model.objects.get_or_create(user=self)[0]
        return profile

    def refresh_all_profiles(self):
        self._profile = self.get_profile()
        if (django_settings.LOGIN_ENABLED):
            from speedy.net.accounts.models import SiteProfile as SpeedyNetSiteProfile
            from speedy.match.accounts.models import SiteProfile as SpeedyMatchSiteProfile
            self._speedy_net_profile = self.get_profile(
                model=SpeedyNetSiteProfile)
            self._speedy_match_profile = self.get_profile(
                model=SpeedyMatchSiteProfile)

    def save_user_and_profile(self):
        with transaction.atomic():
            self.save()
            self.profile.save()
            if (django_settings.LOGIN_ENABLED):
                self.speedy_net_profile.save()  # ~~~~ TODO: is this necessary?
                self.speedy_match_profile.save(
                )  # ~~~~ TODO: is this necessary?

    def get_gender(self):
        return self.__class__.GENDERS_DICT.get(self.gender)

    def get_diet(self):
        diets = {
            self.__class__.DIET_VEGAN:
            pgettext_lazy(context=self.get_gender(), message='Vegan'),
            self.__class__.DIET_VEGETARIAN:
            pgettext_lazy(context=self.get_gender(), message='Vegetarian'),
            self.__class__.DIET_CARNIST:
            pgettext_lazy(context=self.get_gender(), message='Carnist'),
        }
        return diets.get(self.diet)

    def get_age(self):
        return get_age(date_of_birth=self.date_of_birth)

    def get_diet_choices(self):
        return self.__class__.diet_choices(gender=self.get_gender())