"""Utils app models."""
import logging
from os.path import exists
from django.contrib.auth.tokens import PasswordResetTokenGenerator
from django.contrib.gis.db import models
from django.contrib.postgres.fields import JSONField
from django.contrib.postgres.fields.jsonb import KeyTextTransform
from django.utils import timezone
from django.utils.html import mark_safe
from django.utils.translation import ugettext_lazy as _, get_language
from easy_thumbnails.fields import ThumbnailerImageField
from utils.methods import image_path, svg_image_path
from utils.validators import svg_image_validator
from sorl.thumbnail.fields import ImageField as SORLImageField
from sorl.thumbnail import get_thumbnail
from django.conf import settings
from utils.methods import image_url_valid
logger = logging.getLogger(__name__)
class ProjectBaseMixin(models.Model):
"""Base mixin model."""
created = models.DateTimeField(default=timezone.now, editable=False,
verbose_name=_('Date created'))
modified = models.DateTimeField(auto_now=True,
verbose_name=_('Date updated'))
class Meta:
"""Meta class."""
abstract = True
def valid(value):
print("Run")
class TJSONField(JSONField):
"""Overrided JsonField."""
def to_locale(language):
"""Turn a language name (en-us) into a locale name (en_US)."""
language, _, country = language.lower().partition('-')
if not country:
return language
# A language with > 2 characters after the dash only has its first
# character after the dash capitalized; e.g. sr-latn becomes sr-Latn.
# A language with 2 characters after the dash has both characters
# capitalized; e.g. en-us becomes en-US.
country, _, tail = country.partition('-')
country = country.title() if len(country) > 2 else country.upper()
if tail:
country += '-' + tail
return language + '-' + country
def translate_field(self, field_name):
def translate(self):
field = getattr(self, field_name)
if isinstance(field, dict):
return field.get(to_locale(get_language()))
return None
return translate
# todo: refactor this
class IndexJSON:
def __getattr__(self, item):
return None
def index_field(self, field_name):
def index(self):
field = getattr(self, field_name)
obj = IndexJSON()
if isinstance(field, dict):
for key, value in field.items():
setattr(obj, key, value)
return obj
return index
class TranslatedFieldsMixin:
"""Translated Fields mixin"""
STR_FIELD_NAME = ''
def __init__(self, *args, **kwargs):
"""Overrided __init__ method."""
super(TranslatedFieldsMixin, self).__init__(*args, **kwargs)
cls = self.__class__
for field in cls._meta.fields:
field_name = field.name
if isinstance(field, TJSONField):
setattr(cls, f'{field.name}_translated',
property(translate_field(self, field_name)))
setattr(cls, f'{field_name}_indexing',
property(index_field(self, field_name)))
def __str__(self):
"""Overrided __str__ method."""
value = None
if self.STR_FIELD_NAME:
field = getattr(self, getattr(self, 'STR_FIELD_NAME'))
if isinstance(field, dict):
value = field.get(get_current_language())
return value if value else super(TranslatedFieldsMixin, self).__str__()
def get_current_language():
"""Get current language."""
return to_locale(get_language())
class OAuthProjectMixin:
"""OAuth2 mixin for project GM"""
def get_source(self):
"""Method to get of platform"""
return NotImplemented
class BaseAttributes(ProjectBaseMixin):
created_by = models.ForeignKey(
'account.User', on_delete=models.SET_NULL, verbose_name=_('created by'),
null=True, related_name='%(class)s_records_created'
)
modified_by = models.ForeignKey(
'account.User', on_delete=models.SET_NULL, verbose_name=_('modified by'),
null=True, related_name='%(class)s_records_modified'
)
class Meta:
"""Meta class."""
abstract = True
class ImageMixin(models.Model):
"""Avatar model."""
THUMBNAIL_KEY = 'default'
image = ThumbnailerImageField(upload_to=image_path,
blank=True, null=True, default=None,
verbose_name=_('Image'))
class Meta:
"""Meta class."""
abstract = True
def get_image(self, key=None):
"""Get thumbnailed image file."""
return self.image[key or self.THUMBNAIL_KEY] if self.image else None
def get_image_url(self, key=None):
"""Get image thumbnail url."""
return self.get_image(key).url if self.image else None
def image_tag(self):
"""Admin preview tag."""
if self.image:
return mark_safe('
' % self.get_image_url())
else:
return None
def get_full_image_url(self, request, thumbnail_key=None):
"""Get full image url"""
if self.image and exists(self.image.path):
return request.build_absolute_uri(self.get_image(thumbnail_key).url)
else:
return None
image_tag.short_description = _('Image')
image_tag.allow_tags = True
class SORLImageMixin(models.Model):
"""Abstract model for SORL ImageField"""
image = SORLImageField(upload_to=image_path,
blank=True, null=True, default=None,
verbose_name=_('Image'))
class Meta:
"""Meta class."""
abstract = True
def get_image(self, thumbnail_key=None):
"""Get thumbnail image file."""
if thumbnail_key in settings.SORL_THUMBNAIL_ALIASES:
return get_thumbnail(file_=self.image,
**settings.SORL_THUMBNAIL_ALIASES[thumbnail_key])
def get_image_url(self, thumbnail_key=None):
"""Get image thumbnail url."""
return self.get_image(thumbnail_key).url
def image_tag(self):
"""Admin preview tag."""
if self.image:
return mark_safe('
' % self.image.url)
else:
return None
image_tag.short_description = _('Image')
image_tag.allow_tags = True
class SVGImageMixin(models.Model):
"""SVG image model."""
svg_image = models.FileField(upload_to=svg_image_path,
blank=True, null=True, default=None,
validators=[svg_image_validator, ],
verbose_name=_('SVG image'))
class Meta:
abstract = True
class URLImageMixin(models.Model):
"""URl for image and special methods."""
image_url = models.URLField(verbose_name=_('Image URL path'),
blank=True, null=True, default=None)
class Meta:
abstract = True
def image_tag(self):
if self.image_url:
return mark_safe(
f'
')
else:
return None
image_tag.short_description = _('Image')
image_tag.allow_tags = True
class PlatformMixin(models.Model):
"""Platforms"""
MOBILE = 0
WEB = 1
ALL = 2
SOURCES = (
(MOBILE, _('Mobile')),
(WEB, _('Web')),
(ALL, _('All'))
)
source = models.PositiveSmallIntegerField(choices=SOURCES, default=MOBILE,
verbose_name=_('Source'))
class Meta:
"""Meta class"""
abstract = True
class LocaleManagerMixin(models.Manager):
"""Manager for locale"""
def annotate_localized_fields(self, locale):
"""Return queryset by locale"""
prefix = 'trans'
# Prepare fields to localization (only JSONField can be localized)
fields = [field.name for field in self.model._meta.fields if isinstance(field, JSONField)]
# Check filters to check if field has localization
filters = {f'{field}__has_key': locale for field in fields}
# Filter QuerySet by prepared filters
queryset = self.filter(**filters)
# Prepare field for annotator
localized_fields = {f'{field}_{prefix}': KeyTextTransform(f'{locale}', field) for field in
fields}
# Annotate them
for _ in fields:
queryset = queryset.annotate(**localized_fields)
return queryset
class GMTokenGenerator(PasswordResetTokenGenerator):
CHANGE_EMAIL = 0
RESET_PASSWORD = 1
CHANGE_PASSWORD = 2
CONFIRM_EMAIL = 3
TOKEN_CHOICES = (
CHANGE_EMAIL,
RESET_PASSWORD,
CHANGE_PASSWORD,
CONFIRM_EMAIL
)
def __init__(self, purpose: int):
if purpose in self.TOKEN_CHOICES:
self.purpose = purpose
def get_fields(self, user, timestamp):
"""
Get user fields for hash value.
"""
fields = [str(timestamp), str(user.is_active), str(user.pk)]
if self.purpose == self.CHANGE_EMAIL or \
self.purpose == self.CONFIRM_EMAIL:
fields.extend([str(user.email_confirmed), str(user.email)])
elif self.purpose == self.RESET_PASSWORD or \
self.purpose == self.CHANGE_PASSWORD:
fields.append(str(user.password))
return fields
def _make_hash_value(self, user, timestamp):
"""
Hash the user's primary key and some user state that's sure to change
after a password reset to produce a token that invalidated when it's
used.
"""
return self.get_fields(user, timestamp)