2024-06-07 20:17:03 +03:00
|
|
|
''' Models: LibraryItem. '''
|
|
|
|
import re
|
|
|
|
|
|
|
|
from django.db import transaction
|
|
|
|
from django.db.models import (
|
|
|
|
SET_NULL,
|
|
|
|
BooleanField,
|
|
|
|
CharField,
|
|
|
|
DateTimeField,
|
|
|
|
ForeignKey,
|
|
|
|
Model,
|
2024-07-17 12:56:01 +03:00
|
|
|
QuerySet,
|
2024-06-07 20:17:03 +03:00
|
|
|
TextChoices,
|
|
|
|
TextField
|
|
|
|
)
|
|
|
|
|
|
|
|
from apps.users.models import User
|
|
|
|
|
|
|
|
from .Subscription import Subscription
|
|
|
|
from .Version import Version
|
|
|
|
|
|
|
|
|
|
|
|
class LibraryItemType(TextChoices):
|
|
|
|
''' Type of library items '''
|
|
|
|
RSFORM = 'rsform'
|
2024-07-16 12:17:40 +03:00
|
|
|
OPERATION_SCHEMA = 'oss'
|
2024-06-07 20:17:03 +03:00
|
|
|
|
|
|
|
|
|
|
|
class AccessPolicy(TextChoices):
|
|
|
|
''' Type of item access policy. '''
|
|
|
|
PUBLIC = 'public'
|
|
|
|
PROTECTED = 'protected'
|
|
|
|
PRIVATE = 'private'
|
|
|
|
|
|
|
|
|
|
|
|
class LocationHead(TextChoices):
|
|
|
|
''' Location prefixes. '''
|
|
|
|
PROJECTS = '/P'
|
|
|
|
LIBRARY = '/L'
|
|
|
|
USER = '/U'
|
|
|
|
COMMON = '/S'
|
|
|
|
|
|
|
|
|
|
|
|
_RE_LOCATION = r'^/[PLUS]((/[!\d\w]([!\d\w\- ]*[!\d\w])?)*)?$' # cspell:disable-line
|
|
|
|
|
|
|
|
|
|
|
|
def validate_location(target: str) -> bool:
|
|
|
|
return bool(re.search(_RE_LOCATION, target))
|
|
|
|
|
|
|
|
|
|
|
|
class LibraryItem(Model):
|
|
|
|
''' Abstract library item.'''
|
|
|
|
item_type: CharField = CharField(
|
|
|
|
verbose_name='Тип',
|
|
|
|
max_length=50,
|
2024-07-25 19:12:31 +03:00
|
|
|
choices=LibraryItemType.choices,
|
|
|
|
default=LibraryItemType.RSFORM
|
2024-06-07 20:17:03 +03:00
|
|
|
)
|
|
|
|
owner: ForeignKey = ForeignKey(
|
|
|
|
verbose_name='Владелец',
|
|
|
|
to=User,
|
|
|
|
on_delete=SET_NULL,
|
|
|
|
null=True
|
|
|
|
)
|
|
|
|
title: TextField = TextField(
|
|
|
|
verbose_name='Название'
|
|
|
|
)
|
|
|
|
alias: CharField = CharField(
|
|
|
|
verbose_name='Шифр',
|
|
|
|
max_length=255,
|
|
|
|
blank=True
|
|
|
|
)
|
|
|
|
comment: TextField = TextField(
|
|
|
|
verbose_name='Комментарий',
|
|
|
|
blank=True
|
|
|
|
)
|
|
|
|
visible: BooleanField = BooleanField(
|
|
|
|
verbose_name='Отображаемая',
|
|
|
|
default=True
|
|
|
|
)
|
|
|
|
read_only: BooleanField = BooleanField(
|
|
|
|
verbose_name='Запретить редактирование',
|
|
|
|
default=False
|
|
|
|
)
|
|
|
|
access_policy: CharField = CharField(
|
|
|
|
verbose_name='Политика доступа',
|
|
|
|
max_length=500,
|
|
|
|
choices=AccessPolicy.choices,
|
|
|
|
default=AccessPolicy.PUBLIC
|
|
|
|
)
|
|
|
|
location: TextField = TextField(
|
|
|
|
verbose_name='Расположение',
|
|
|
|
max_length=500,
|
|
|
|
default=LocationHead.USER
|
|
|
|
)
|
|
|
|
|
|
|
|
time_create: DateTimeField = DateTimeField(
|
|
|
|
verbose_name='Дата создания',
|
|
|
|
auto_now_add=True
|
|
|
|
)
|
|
|
|
time_update: DateTimeField = DateTimeField(
|
|
|
|
verbose_name='Дата изменения',
|
|
|
|
auto_now=True
|
|
|
|
)
|
|
|
|
|
|
|
|
class Meta:
|
|
|
|
''' Model metadata. '''
|
|
|
|
verbose_name = 'Схема'
|
|
|
|
verbose_name_plural = 'Схемы'
|
|
|
|
|
|
|
|
def __str__(self) -> str:
|
|
|
|
return f'{self.alias}'
|
|
|
|
|
|
|
|
def get_absolute_url(self):
|
|
|
|
return f'/api/library/{self.pk}'
|
|
|
|
|
2024-08-06 23:13:57 +03:00
|
|
|
def subscribers(self) -> QuerySet[User]:
|
2024-06-07 20:17:03 +03:00
|
|
|
''' Get all subscribers for this item. '''
|
2024-08-06 23:13:57 +03:00
|
|
|
return User.objects.filter(subscription__item=self.pk)
|
2024-06-07 20:17:03 +03:00
|
|
|
|
2024-08-06 22:27:51 +03:00
|
|
|
def editors(self) -> QuerySet[User]:
|
2024-06-07 20:17:03 +03:00
|
|
|
''' Get all Editors of this item. '''
|
2024-08-06 22:27:51 +03:00
|
|
|
return User.objects.filter(editor__item=self.pk)
|
2024-06-07 20:17:03 +03:00
|
|
|
|
2024-07-17 12:56:01 +03:00
|
|
|
def versions(self) -> QuerySet[Version]:
|
|
|
|
''' Get all Versions of this item. '''
|
|
|
|
return Version.objects.filter(item=self.pk).order_by('-time_create')
|
|
|
|
|
2024-08-06 23:13:57 +03:00
|
|
|
# TODO: move to View layer
|
2024-06-07 20:17:03 +03:00
|
|
|
@transaction.atomic
|
|
|
|
def save(self, *args, **kwargs):
|
2024-07-24 22:23:05 +03:00
|
|
|
''' Save updating subscriptions and connected operations. '''
|
|
|
|
if not self._state.adding:
|
|
|
|
self._update_connected_operations()
|
|
|
|
subscribe = self._state.adding and self.owner
|
2024-06-07 20:17:03 +03:00
|
|
|
super().save(*args, **kwargs)
|
|
|
|
if subscribe:
|
2024-08-06 23:13:57 +03:00
|
|
|
Subscription.subscribe(user=self.owner_id, item=self.pk)
|
2024-07-24 22:23:05 +03:00
|
|
|
|
|
|
|
def _update_connected_operations(self):
|
|
|
|
# using method level import to prevent circular dependency
|
|
|
|
from apps.oss.models import Operation # pylint: disable=import-outside-toplevel
|
2024-07-30 15:59:37 +03:00
|
|
|
operations = Operation.objects.filter(result__pk=self.pk)
|
2024-07-24 22:23:05 +03:00
|
|
|
if not operations.exists():
|
|
|
|
return
|
|
|
|
for operation in operations:
|
|
|
|
changed = False
|
|
|
|
if operation.alias != self.alias:
|
|
|
|
operation.alias = self.alias
|
|
|
|
changed = True
|
|
|
|
if operation.title != self.title:
|
|
|
|
operation.title = self.title
|
|
|
|
changed = True
|
|
|
|
if operation.comment != self.comment:
|
|
|
|
operation.comment = self.comment
|
|
|
|
changed = True
|
|
|
|
if changed:
|
|
|
|
operation.save()
|