ConceptPortal-public/rsconcept/backend/apps/rsform/models.py
2023-07-15 17:46:19 +03:00

215 lines
6.5 KiB
Python

from django.db import models, transaction
from django.core.validators import MinValueValidator
from django.core.exceptions import ValidationError
from apps.users.models import User
class CstType(models.TextChoices):
''' Type of constituenta '''
BASE = 'basic'
CONSTANT = 'constant'
STRUCTURED = 'structure'
AXIOM = 'axiom'
TERM = 'term'
FUNCTION = 'function'
PREDICATE = 'predicate'
THEOREM = 'theorem'
class Syntax(models.TextChoices):
''' Syntax types '''
UNDEF = 'undefined'
ASCII = 'ascii'
MATH = 'math'
def _empty_term():
return {'raw': '', 'resolved': '', 'forms': []}
def _empty_definition():
return {'raw': '', 'resolved': ''}
class RSForm(models.Model):
''' RSForm is a math form of capturing conceptual schema '''
owner = models.ForeignKey(
verbose_name='Владелец',
to=User,
on_delete=models.SET_NULL,
null=True
)
title = models.TextField(
verbose_name='Название'
)
alias = models.CharField(
verbose_name='Шифр',
max_length=255,
blank=True
)
comment = models.TextField(
verbose_name='Комментарий',
blank=True
)
is_common = models.BooleanField(
verbose_name='Общая',
default=False
)
time_create = models.DateTimeField(
verbose_name='Дата создания',
auto_now_add=True
)
time_update = models.DateTimeField(
verbose_name='Дата изменения',
auto_now=True
)
class Meta:
verbose_name = 'Схема'
verbose_name_plural = 'Схемы'
def constituents(self) -> models.QuerySet:
''' Get QuerySet containing all constituents of current RSForm '''
return Constituenta.objects.filter(schema=self)
@transaction.atomic
def insert_at(self, position: int, alias: str, type: CstType) -> 'Constituenta':
''' Insert new constituenta at given position. All following constituents order is shifted by 1 position '''
if position <= 0:
raise ValidationError('Invalid position: should be positive integer')
update_list = Constituenta.objects.filter(schema=self, order__gte=position)
for cst in update_list:
cst.order += 1
cst.save()
return Constituenta.objects.create(
schema=self,
order=position,
alias=alias,
csttype=type
)
@transaction.atomic
def insert_last(self, alias: str, type: CstType) -> 'Constituenta':
''' Insert new constituenta at last position '''
position = 1
if self.constituents().exists():
position += self.constituents().aggregate(models.Max('order'))['order__max']
return Constituenta.objects.create(
schema=self,
order=position,
alias=alias,
csttype=type
)
@staticmethod
@transaction.atomic
def import_json(owner: User, data: dict, is_common: bool = True) -> 'RSForm':
schema = RSForm.objects.create(
title=data.get('title', 'Без названия'),
owner=owner,
alias=data.get('alias', ''),
comment=data.get('comment', ''),
is_common=is_common
)
order = 1
for cst in data['items']:
# TODO: get rid of empty_term etc. Use None instead
Constituenta.objects.create(
alias=cst['alias'],
schema=schema,
order=order,
csttype=cst['cstType'],
convention=cst.get('convention', 'Без названия'),
definition_formal=cst['definition'].get('formal', '') if 'definition' in cst else '',
term=cst.get('term', _empty_term()),
definition_text=cst['definition']['text'] \
if 'definition' in cst and 'text' in cst['definition'] else _empty_definition() # noqa: E502
)
order += 1
return schema
def to_json(self) -> str:
''' Generate JSON string containing all data from RSForm '''
result = self._prepare_json_rsform()
items = self.constituents().order_by('order')
for cst in items:
result['items'].append(self._prepare_json_cst(cst))
return result
def __str__(self):
return self.title
def _prepare_json_rsform(self: 'Constituenta') -> dict:
return {
'type': 'rsform',
'title': self.title,
'alias': self.alias,
'comment': self.comment,
'items': []
}
@staticmethod
def _prepare_json_cst(cst: 'Constituenta') -> dict:
return {
'entityUID': cst.id,
'type': 'constituenta',
'cstType': cst.csttype,
'alias': cst.alias,
'convention': cst.convention,
'term': cst.term,
'definition': {
'formal': cst.definition_formal,
'text': cst.definition_text
}
}
class Constituenta(models.Model):
''' Constituenta is the base unit for every conceptual schema '''
schema = models.ForeignKey(
verbose_name='Концептуальная схема',
to=RSForm,
on_delete=models.CASCADE
)
order = models.PositiveIntegerField(
verbose_name='Позиция',
validators=[MinValueValidator(1)]
)
alias = models.CharField(
verbose_name='Имя',
max_length=8
)
csttype = models.CharField(
verbose_name='Тип',
max_length=10,
choices=CstType.choices,
default=CstType.BASE
)
convention = models.TextField(
verbose_name='Комментарий/Конвенция',
default='',
blank=True
)
term = models.JSONField(
verbose_name='Термин',
default=_empty_term
)
definition_formal = models.TextField(
verbose_name='Родоструктурное определение',
default='',
blank=True
)
definition_text = models.JSONField(
verbose_name='Текстовое определние',
default=_empty_definition,
blank=True
)
class Meta:
verbose_name = 'Конституета'
verbose_name_plural = 'Конституенты'
unique_together = (('schema', 'alias'), ('schema', 'order'))
def __str__(self):
return self.alias