class Progress(_Model): from django.db.models.fields import related as _related from django.db.models import deletion as _deletion user = _related.ForeignKey( 'entry.User', on_delete=_deletion.CASCADE, verbose_name="Пользователь", null=False, blank=False, ) feature = _related.ForeignKey( 'entry.Feature', on_delete=_deletion.CASCADE, verbose_name="Фича", null=False, blank=False, ) def __str__(self): return "Прогресс по %s для пользователя %s" % (self.feature, self.user) class Meta: verbose_name = 'Прогресс' verbose_name_plural = 'Прогресс'
class PaymentDetails(_Model): from django.db.models.fields import related as _related from django.db.models import deletion as _deletion from django.db.models import fields as _field from datetime import datetime as _dt company = _related.ForeignKey( 'holding.Company', on_delete=_deletion.CASCADE, null=False, blank=False, ) rate = _related.ForeignKey( 'pay.Rate', on_delete=_deletion.CASCADE, verbose_name="Выбранный тариф", null=False, blank=False, ) discount = _related.ForeignKey( 'pay.Discount', on_delete=_deletion.CASCADE, null=True, blank=False, ) start = _field.DateTimeField( "Дата начала действия тарифа", default=_dt.now, ) discount_percent = _field.PositiveSmallIntegerField( "Процент скидки на момент платежа", default=None, null=True, blank=True, ) def save(self, force_insert=False, force_update=False, using=None, update_fields=None): if not self.discount: self.discount = self.company.discount if self.discount: self.discount_percent = self.discount.percent super().save(force_insert=force_insert, force_update=force_update, using=using, update_fields=update_fields) class Meta: verbose_name = "Дополнительные детали" verbose_name_plural = "Детали оплат"
class FieldValue(models.Model): answer = related.ForeignKey(QuestionnaireAnswer, on_delete=models.CASCADE, related_name='field_values') field = related.ForeignKey(QuestionnaireField, on_delete=models.CASCADE) value = fields.CharField(max_length=255) class Meta: unique_together = ('answer', 'field')
class Face(_Model): from django.contrib.contenttypes.fields import GenericRelation as _Generic from django.db.models.fields import related as _related from django.db.models import deletion as _deletion from django.db.models import fields as _field employee = _related.ForeignKey( 'holding.Employee', on_delete=_deletion.CASCADE, null=False, blank=False, ) face_id = _field.CharField( verbose_name="Face ID", max_length=64, ) photo = _Generic( 'common.Image', verbose_name="Фотография", null=True, ) class Meta: verbose_name = "Лицо" verbose_name_plural = "Лица"
class Teacher(models.Model): name = models.CharField(max_length=200) first_name = models.CharField(max_length=200) user = related.ForeignKey(CustomUser, on_delete=models.CASCADE) def __str__(self): return self.name
class Video(_Model): from django.db.models.fields import related as _related from django.db.models import deletion as _deletion from django.db.models import fields as _field start = _field.DateTimeField("Начало") duration = _field.DurationField("Продолжительность") filename = _field.CharField( verbose_name="Название файла", max_length=128, null=False, blank=False, ) # There is need to be link to file camera = _related.ForeignKey( 'cam.Camera', on_delete=_deletion.CASCADE, null=False, blank=False, ) @property def timezone(self): return self.camera.timezone if self.camera.timezone else self.camera.company.timezone class Meta: verbose_name = "Видео" verbose_name_plural = "Видео"
class PositionToDepartment(_Model): position = _related.ForeignKey( Position, on_delete=_deletion.CASCADE, null=False, blank=False, related_name="departments", ) department = _related.ForeignKey( 'holding.Department', on_delete=_deletion.CASCADE, null=False, blank=False, related_name="positions", ) def __str__(self): return "Должность %s из отдела %s (%s)" % (self.position, self.department, self.position.company) class Meta: verbose_name = "Должность к отделу" verbose_name_plural = "Должности к отделам"
class Violation(_Model): employee = _related.ForeignKey( 'holding.Employee', on_delete=_deletion.SET_NULL, null=True, blank=False, # For admin panel? related_name="violations", ) when = _field.DateTimeField("Когда") title = _field.CharField( verbose_name="Название нарушения", null=False, blank=False, max_length=128, ) description = _field.TextField( verbose_name="Описание нарушения", null=True, blank=True, ) timezone = _timezone.TimeZoneField( verbose_name="Временная зона", null=True, blank=True, ) @property def employee_fullname(self): return "%s %s" % (self.employee.first_name, self.employee.last_name) employee_fullname.fget.short_description = "Сотрудник" def save(self, force_insert=False, force_update=False, using=None, update_fields=None): if self.timezone is None: self.timezone = self.employee.company.timezone return super(Violation, self).save(force_insert, force_update, using, update_fields) def __str__(self): return self.title class Meta: verbose_name = "Нарушение" verbose_name_plural = "Нарушения"
class Position(_Model): name = _field.CharField( verbose_name="Название должности", max_length=255, null=False, blank=False, ) company = _related.ForeignKey( 'holding.Company', on_delete=_deletion.SET_NULL, null=True, blank=True, related_name="positions", ) is_protected = _field.BooleanField( verbose_name='Неудаляемая должность', default=False, ) @property def department_id(self): department = self.departments.first() if department: return department.id return department def __str__(self): return self.name def delete(self, **kwargs): if self.is_protected: raise _APIException({ 'detail': 'Protected', 'protected': True, }, status_code=403) super(Position, self).delete(**kwargs) class Meta: verbose_name = "Должность" verbose_name_plural = "Должности"
class QuestionnaireField(models.Model): FIELD_TYPES = ( ('TXT', 'text'), ('RAD', 'radiobutton'), ('CHK', 'checkbox'), ('NUM', 'number_choice') ) questionnaire = related.ForeignKey(Questionnaire, on_delete=models.CASCADE, related_name='fields') position = fields.IntegerField() field_type = fields.CharField(max_length=3, choices=FIELD_TYPES) text_before = fields.CharField(max_length=255) text_after = fields.CharField(max_length=255, blank=True) default_val = fields.CharField(max_length=255, blank=True) min_val = fields.IntegerField(null=True) max_val = fields.IntegerField(null=True) class Meta: unique_together = ('questionnaire', 'position') ordering = ['position']
class Zone(_Model): from django.db.models.fields import related as _related from django.db.models import deletion as _deletion from django.db.models import fields as _field company = _related.ForeignKey( 'holding.Company', on_delete=_deletion.CASCADE, related_name="zones", ) name = _field.CharField( verbose_name="Название", max_length=128, ) def __str__(self): return self.name class Meta: verbose_name = "Зона" verbose_name_plural = "Зоны"
class Image(_Model): from django.contrib.contenttypes.fields import GenericForeignKey as _GenericForeign from django.contrib.contenttypes.models import ContentType as _Type from django.db.models.fields.files import ImageField as _image from django.db.models.fields import related as _related from django.db.models import deletion as _deletion from django.db.models import fields as _field content_type = _related.ForeignKey(_Type, on_delete=_deletion.CASCADE, verbose_name="Модель") object_id = _field.PositiveIntegerField(verbose_name="Ключ") content_object = _GenericForeign() def computed_folder(self, file_name): import uuid from app.base.helpers import snake extension = file_name.split('.')[-1] return "%s/%d/%s" % (snake(self.content_type.model_class().__name__), self.object_id, "%s.%s" % (uuid.uuid4(), extension)) image = _image("Изображение", null=True, upload_to=computed_folder) # Can be empty... description = _field.CharField(verbose_name="Описание", null=True, blank=True, max_length=512) def __str__(self): return "Изображение для %s [№ %d]" % (self.content_type, self.object_id) class Meta: verbose_name = "Изображение" verbose_name_plural = "Изображения"
class CompanyCreator(_Model): creator = _related.OneToOneField( to='holding.Employee', on_delete=_deletion.DO_NOTHING, verbose_name='Создатель', related_name='creators', ) company = _related.OneToOneField( to='holding.Company', on_delete=_deletion.CASCADE, verbose_name='Компания', related_name='companies', ) physical = _related.ForeignKey( to='entry.User', on_delete=_deletion.DO_NOTHING, verbose_name='Физический пользователь', related_name='users', blank=True, null=True, ) def save(self, force_insert=False, force_update=False, using=None, update_fields=None): self.physical = self.creator.user super(CompanyCreator, self).save(force_insert, force_update, using, update_fields) class Meta: verbose_name = 'Основатель' verbose_name_plural = 'Основатели'
class QuestionnaireAnswer(models.Model): questionnaire = related.ForeignKey(Questionnaire, on_delete=models.CASCADE) respondent = related.ForeignKey(User, on_delete=models.CASCADE) class Meta: unique_together = ('questionnaire', 'respondent')
class Schedule(_Model): from django.db.models.fields import related as _related from django.db.models import deletion as _deletion from django.db.models import fields as _field from app.fields import timezone as _timezone employee = _related.ForeignKey( 'holding.Employee', on_delete=_deletion.SET_NULL, null=True, blank=False, # For admin panel? related_name="schedules", ) start = _field.DateTimeField("Начало") end = _field.DateTimeField("Конец") is_wanted = _field.NullBooleanField( verbose_name="Хочет работать", default=None, ) timezone = _timezone.TimeZoneField( verbose_name="Временная зона", null=True, blank=True, ) def save(self, force_insert=False, force_update=False, using=None, update_fields=None): if self.timezone is None: self.timezone = self.company.timezone return super(Schedule, self).save(force_insert, force_update, using, update_fields) @property def employee_fullname(self): return "%s %s" % (self.employee.first_name, self.employee.last_name) employee_fullname.fget.short_description = "Сотрудник" @property def company(self): return self.employee.company company.fget.short_description = "Компания" @property def company_name(self): return self.company.name company_name.fget.short_description = "Компания" def __str__(self): return "%s %s [%s]:(%s - %s)" % ( self.employee.first_name, self.employee.last_name, self.start.date(), self.start.time(), self.end.time()) class Meta: verbose_name = "График сотрудника" verbose_name_plural = "Графики сотрудников"
class Company(_Model): from app.fields import timezone as _timezone name = _field.CharField( verbose_name='Название компании', max_length=255, null=False, ) description = _field.TextField( verbose_name='Описание компании', max_length=4000, null=True, blank=True, ) address = _field.CharField( verbose_name='Адрес компании', max_length=512, null=True, blank=True, ) phone = _field.CharField( verbose_name='Телефон', max_length=32, null=True, blank=True, ) email = _field.EmailField( verbose_name='Почтовый ящик', max_length=255, null=False, ) owner = _related.ForeignKey( 'holding.Employee', # Changed company owner from user to employee because of the system flow verbose_name='Владелец', on_delete=_deletion.DO_NOTHING, null=False, related_name='owner', ) discount = _related.ForeignKey( to='pay.Discount', on_delete=_deletion.SET_NULL, null=True, blank=True, ) timezone = _timezone.TimeZoneField( verbose_name='Локальное время компании', default='UTC', null=False, blank=True, ) def __str__(self): return self.name @property def last_payment(self): from pay.models import Payment return Payment.objects.filter(details__company=self).last() @property def rate(self): return self.last_payment.details.rate if self.last_payment else None rate.fget.short_description = u'Тариф' @property def time_left(self): return self.last_payment.time_left if self.last_payment else -1 time_left.fget.short_description = u"Оплаченное время" @property def employee_amount(self): return self.employees.count() employee_amount.fget.short_description = u"Сотрудников" @property def departments_amount(self): return self.departments.count() departments_amount.fget.short_description = u"Отделов" def save(self, force_insert=False, force_update=False, using=None, update_fields=None): creation = True if self.id: creation = False super(Company, self).save(force_insert, force_update, using, update_fields) self.owner.company = self self.owner.save() if creation: CompanyCreator(creator=self.owner, company=self).save() class Meta: verbose_name = "Компания" verbose_name_plural = "Компании"
class Questionnaire(models.Model): author = related.ForeignKey(User, on_delete=models.CASCADE) description = fields.TextField(max_length=1024) created_at = fields.DateTimeField(auto_now_add=True)
class Employee(_Model): from django.db.models import fields as _field from app.fields import timezone as _timezone from django.db.models.fields import related as _related from django.db.models import deletion as _deletion first_name = _field.CharField( max_length=200, verbose_name="Имя", ) last_name = _field.CharField( max_length=200, verbose_name="Фамилия", ) phone = _field.CharField( max_length=200, verbose_name="Телефон", null=True, blank=True, ) company = _related.ForeignKey( 'holding.Company', on_delete=_deletion.SET_NULL, verbose_name="Компания", null=True, blank=True, related_name="employees", ) is_manager = _field.BooleanField( verbose_name="Менеджер", default=False, ) is_fired = _field.BooleanField( verbose_name="Уволен", default=False, ) import uuid as _uuid auth_key = _field.UUIDField( verbose_name="Уникальный авторизационный ключ", editable=False, unique=True, null=True, blank=False, default=_uuid.uuid4, ) email = _field.EmailField( verbose_name="E-mail", null=True, blank=True, ) timezone = _timezone.TimeZoneField( verbose_name="Пояс", default="UTC", null=False, blank=True, ) invitation = _field.DateTimeField( verbose_name="Последнее приглашение", null=True, blank=True, ) is_invited = _field.BooleanField( verbose_name="Приглашён", null=False, default=False, ) is_active = _field.BooleanField( verbose_name="Активен", null=False, default=False, ) face_id = _field.UUIDField( verbose_name="Face ID", null=True, blank=True, ) user = _related.ForeignKey( 'entry.User', on_delete=_deletion.CASCADE, verbose_name="Физический пользователь", null=True, blank=True, ) department = _related.ForeignKey( 'holding.Department', on_delete=_deletion.SET_NULL, default=None, null=True, blank=True, related_name="employees", ) position = _related.ForeignKey( 'holding.Position', on_delete=_deletion.SET_NULL, default=None, null=True, blank=True, related_name="employees", ) def clear_face_id(self): self.face_id = None def new_face_id(self): self.face_id = self._uuid.uuid4() def new_invitation(self): from datetime import datetime old = self.invitation self.invitation = datetime.now() return old def activate(self): self.is_invited = True self.is_active = True self.auth_key = None def __str__(self): user = self.user ttl = {} if self.last_name and self.first_name: ttl['first_name'] = self.first_name ttl['last_name'] = self.last_name elif user: if user.last_name and user.first_name: ttl['first_name'] = user.first_name ttl['last_name'] = user.last_name ttl['username'] = user.username if ttl.get('first_name'): if ttl.get('username'): return "%(first_name)s %(last_name)s [%(username)s]" % ttl else: return "%(first_name)s %(last_name)s" % ttl elif ttl.get('username'): return "%(username)s" % ttl else: return "Employee №[%d]" % self.id @classmethod def create_from_user(cls, user, **kwargs): """ :type user: entry.models.User """ from factory.faker import faker fake = faker.Faker(locale='ru_RU') employee = cls( user=user, first_name=user.first_name if user.first_name else fake.first_name(), last_name=user.last_name if user.last_name else fake.last_name(), timezone=user.timezone, phone=user.phone, email=user.email, is_active=True, **kwargs ) employee.save() return employee def set_position(self, position, department=None): self.position = position if not department and not self.department: related = position.departments.first() if related: self.department = related.department elif department: self.department = department class Meta: verbose_name = "Сотрудник" verbose_name_plural = "Сотрудники" unique_together = (('user', 'company'),) def create_user(self, username=None): """ Creates user from employee, save it and send email if application not in debug mode :type username: str """ from django.conf import settings from entry.models import User if not self.auth_key and not username: raise AttributeError('No username provided or can\'t be get from auth') user = User( username=username if username else self.auth_key, first_name=self.first_name, last_name=self.last_name, email=self.email, phone=self.phone, timezone=self.timezone, ) user.save() self.user = user if user.email and not settings.DEBUG: user.mail_activation() if settings.DEBUG: user.activate() self.activate() return user
class Payment(_Model): from django.db.models import fields as _field from django.db.models.fields import related as _related from django.db.models import deletion as _deletion user = _related.ForeignKey( 'entry.User', on_delete=_deletion.DO_NOTHING, verbose_name="Платильщик", null=True, blank=True, ) discount = _related.ForeignKey( 'pay.Discount', on_delete=_deletion.SET_NULL, null=True, blank=True, ) info = _field.TextField( "Информация о платеже", null=False, blank=False, default="{}", ) from pay.models.detail.model import PaymentDetails as _Details details = _related.OneToOneField( _Details, on_delete=_deletion.PROTECT, verbose_name="Детали", related_name="payment", ) @property def company(self): return self.details.company if self.details else None @property def rate(self): return self.details.rate if self.details else None @property def time_left(self): from datetime import datetime if self.details and self.rate: # Cast details start to native datetime return self.details.start.replace( tzinfo=None) + self.rate.lifetime - datetime.now() return -0 time_left.fget.short_description = u"Остаток времени по текущему тарифу" def __str__(self): return "Оплата от пользователя [%s] за компанию [%s] по тарифу [%s]." % ( str(self.user), str(self.company), str(self.rate)) class Meta: verbose_name = "Оплата" verbose_name_plural = "Оплаты"
class Department(_Model): from django.db.models import fields as _field from django.db.models.fields import related as _related from django.db.models import deletion as _deletion name = _field.CharField( verbose_name='Название отдела', max_length=255, null=False, blank=False, ) company = _related.ForeignKey( 'holding.Company', verbose_name='Компания', on_delete=_deletion.SET_NULL, null=True, blank=True, related_name="departments", ) is_protected = _field.BooleanField( verbose_name='Неудаляемый отдел', default=False, ) @property def employee_amount(self): return self.employees.count() employee_amount.fget.short_description = "Кол-во сотрудников" def __str__(self): return self.name def delete(self, **kwargs): if self.is_protected: raise _APIException({ 'detail': 'Protected', 'protected': True, }, status_code=403) super(Department, self).delete(**kwargs) class Meta: verbose_name = "Отдел" verbose_name_plural = "Отделы" def attach_position(self, position): from holding.models import PositionToDepartment position.save() old_connection = PositionToDepartment.objects.filter(position_id=position.id, department_id=self.id).first() if old_connection: old_connection.save(department=self) else: PositionToDepartment(department=self, position=position).save() return self def create_position(self, name, **kwargs): from holding.models import Position if not self.id: self.save() position = Position( name=name, company=kwargs.get('company') if kwargs.get('company') else self.company, is_protected=kwargs.get('is_protected', False), ) self.attach_position(position) return position
class Logbook(_Model): from django.db.models.fields import related as _related from django.db.models import deletion as _deletion from django.db.models import fields as _field from app.fields import timezone as _timezone employee = _related.ForeignKey( 'holding.Employee', on_delete=_deletion.SET_NULL, null=True, blank=False, # For admin panel? related_name="logbooks", ) start = _field.DateTimeField("Начало отрезка") end = _field.DateTimeField("Конец отрезка") activity = _field.PositiveSmallIntegerField( verbose_name="Активность", default=0, ) mood = _field.PositiveSmallIntegerField( verbose_name="Настроение", default=0, ) fatigue = _field.PositiveSmallIntegerField( verbose_name="Усталость", default=0, ) timezone = _timezone.TimeZoneField( verbose_name="Временная зона", null=True, blank=True, ) def save(self, force_insert=False, force_update=False, using=None, update_fields=None): if self.timezone is None: self.timezone = self.company.timezone return super(Logbook, self).save(force_insert, force_update, using, update_fields) @property def employee_fullname(self): return "%s %s" % (self.employee.first_name, self.employee.last_name) employee_fullname.fget.short_description = "Сотрудник" @property def company(self): return self.employee.company company.fget.short_description = "Компания" @property def company_name(self): return self.company.name company_name.fget.short_description = "Компания" def __str__(self): return "%s %s [%s]:(%s - %s)" % ( self.employee.first_name, self.employee.last_name, self.start.date(), self.start.time(), self.end.time(), ) class Meta: verbose_name = "Показатель сотрудника" verbose_name_plural = "Показатели сотрудников"
class Camera(_Model): from django.db.models.fields import related as _related from django.db.models import deletion as _deletion from django.db.models import fields as _field from app.fields import timezone as _timezone name = _field.CharField( verbose_name="Название", max_length=128, null=False, blank=False, ) ip_address = _field.GenericIPAddressField( verbose_name="IP адрес", max_length=64, ) login = _field.CharField( verbose_name="Логин", max_length=256, null=False, blank=False, ) password = _field.CharField( verbose_name="Пароль", max_length=256, null=False, blank=True, default="", ) is_active = _field.BooleanField( verbose_name="Активна", null=False, blank=False, default=True, ) company = _related.ForeignKey( 'holding.Company', on_delete=_deletion.CASCADE, null=False, blank=False, related_name="cameras", ) zone = _related.ForeignKey( 'cam.Zone', on_delete=_deletion.SET_NULL, null=True, blank=True, related_name="cameras", ) timezone = _timezone.TimeZoneField( "Временная зона", null=True, blank=True, ) def __str__(self): return self.name class Meta: verbose_name = "Камера" verbose_name_plural = "Камеры"