Refactoring: enable formatting with autopep8

This commit is contained in:
IRBorisov 2024-05-24 19:06:39 +03:00
parent 9e4f7ca2f2
commit 18c09ecd93
22 changed files with 116 additions and 78 deletions

11
.vscode/settings.json vendored
View File

@ -4,6 +4,7 @@
".pytest_cache/": true
},
"typescript.tsdk": "rsconcept/frontend/node_modules/typescript/lib",
"eslint.workingDirectories": ["rsconcept/frontend"],
"isort.args": [
"--line-length",
"100",
@ -12,10 +13,16 @@
"--project",
"apps"
],
"eslint.workingDirectories": ["rsconcept/frontend"],
"autopep8.args": [
"--max-line-length",
"120",
"--aggressive",
"--ignore",
"E303"
],
"[python]": {
"editor.formatOnSave": false,
"editor.defaultFormatter": "ms-python.autopep8",
"editor.formatOnSave": true,
"editor.tabSize": 4,
"editor.insertSpaces": true,
"editor.codeActionsOnSave": {

View File

@ -7,13 +7,14 @@ ItemType = TypeVar("ItemType")
class Graph(Generic[ItemType]):
''' Directed graph. '''
def __init__(self, graph: Optional[dict[ItemType, list[ItemType]]]=None):
def __init__(self, graph: Optional[dict[ItemType, list[ItemType]]] = None):
if graph is None:
self.outputs: dict[ItemType, list[ItemType]] = {}
self.inputs: dict[ItemType, list[ItemType]] = {}
else:
self.outputs = graph
self.inputs: dict[ItemType, list[ItemType]] = {id : [] for id in graph.keys()} #type: ignore[no-redef]
self.inputs: dict[ItemType, list[ItemType]] = {id: [] for id in graph.keys()} # type: ignore[no-redef]
for parent in graph.keys():
for child in graph[parent]:
self.inputs[child].append(parent)

View File

@ -1,41 +1,54 @@
''' Utility: Text messages. '''
# pylint: skip-file
def constituentaNotOwned(title: str):
return f'Конституента не принадлежит схеме: {title}'
def substitutionNotInList():
return 'Отождествляемая конституента отсутствует в списке'
def schemaNotOwned():
return 'Нет доступа к схеме'
def renameTrivial(name: str):
return f'Имя должно отличаться от текущего: {name}'
def substituteTrivial(name: str):
return f'Отождествление конституенты с собой не корректно: {name}'
def substituteDouble(name: str):
return f'Повторное отождествление: {name}'
def aliasTaken(name: str):
return f'Имя уже используется: {name}'
def pyconceptFailure():
return 'Invalid data response from pyconcept'
def typificationInvalidStr():
return 'Invalid typification string'
def libraryTypeUnexpected():
return 'Attempting to use invalid adaptor for non-RSForm item'
def exteorFileVersionNotSupported():
return 'Некорректный формат файла Экстеор. Сохраните файл в новой версии'
def invalidPosition():
return 'Invalid position: should be positive integer'
def constituentaNoStructure():
return 'Указанная конституента не обладает теоретико-множественной типизацией'

View File

@ -17,7 +17,7 @@ from django.urls import reverse
from ..utils import apply_pattern
_REF_ENTITY_PATTERN = re.compile(r'@{([^0-9\-].*?)\|.*?}')
_GLOBAL_ID_PATTERN = re.compile(r'([XCSADFPT][0-9]+)') # cspell:disable-line
_GLOBAL_ID_PATTERN = re.compile(r'([XCSADFPT][0-9]+)') # cspell:disable-line
def _empty_forms():

View File

@ -29,6 +29,7 @@ _INSERT_LAST: int = -1
class RSForm:
''' RSForm is math form of conceptual schema. '''
def __init__(self, item: LibraryItem):
if item.item_type != LibraryItemType.RSFORM:
raise ValueError(msg.libraryTypeUnexpected())
@ -144,7 +145,7 @@ class RSForm:
for (value, _) in CstType.choices:
indices[value] = self.get_max_index(cast(CstType, value))
mapping: dict[str, str] = {}
mapping: dict[str, str] = {}
for cst in items:
indices[cst.cst_type] = indices[cst.cst_type] + 1
newAlias = f'{get_type_prefix(cst.cst_type)}{indices[cst.cst_type]}'
@ -195,7 +196,7 @@ class RSForm:
self.item.save()
@transaction.atomic
def create_cst(self, data: dict, insert_after: Optional[str]=None) -> Constituenta:
def create_cst(self, data: dict, insert_after: Optional[str] = None) -> Constituenta:
''' Create new cst from data. '''
resolver = self.resolver()
cst = self._insert_new(data, insert_after)
@ -224,7 +225,7 @@ class RSForm:
):
''' Execute constituenta substitution. '''
assert original.pk != substitution.pk
mapping = { original.alias: substitution.alias }
mapping = {original.alias: substitution.alias}
self.apply_mapping(mapping)
if transfer_term:
substitution.term_raw = original.term_raw
@ -331,8 +332,8 @@ class RSForm:
return
update_list = \
Constituenta.objects \
.only('id', 'order', 'schema') \
.filter(schema=self.item, order__gte=start)
.only('id', 'order', 'schema') \
.filter(schema=self.item, order__gte=start)
for cst in update_list:
cst.order += shift
Constituenta.objects.bulk_update(update_list, ['order'])
@ -350,7 +351,7 @@ class RSForm:
if position == _INSERT_LAST:
position = lastPosition + 1
else:
position = max(1, min(position, lastPosition + 1))
position = max(1, min(position, lastPosition + 1))
return position
@transaction.atomic
@ -362,7 +363,7 @@ class RSForm:
cst.save()
order += 1
def _insert_new(self, data: dict, insert_after: Optional[str]=None) -> Constituenta:
def _insert_new(self, data: dict, insert_after: Optional[str] = None) -> Constituenta:
if insert_after is not None:
cst_after = Constituenta.objects.get(pk=insert_after)
return self.insert_new(data['alias'], data['cst_type'], cst_after.order + 1)
@ -426,21 +427,22 @@ class RSForm:
class SemanticInfo:
''' Semantic information derived from constituents. '''
def __init__(self, schema: RSForm):
self._graph = schema._graph_formal()
self._items = list(
schema.constituents() \
.only('id', 'alias', 'cst_type', 'definition_formal') \
.order_by('order')
schema.constituents()
.only('id', 'alias', 'cst_type', 'definition_formal')
.order_by('order')
)
self._cst_by_alias = { cst.alias : cst for cst in self._items }
self._cst_by_ID = { cst.id : cst for cst in self._items }
self._cst_by_alias = {cst.alias: cst for cst in self._items}
self._cst_by_ID = {cst.id: cst for cst in self._items}
self.info = {
cst.id: {
'is_simple' : False, \
'is_template' : False, \
'parent' : cst.id, \
'children' : []
'is_simple': False,
'is_template': False,
'parent': cst.id,
'children': []
}
for cst in self._items
}
@ -483,8 +485,8 @@ class SemanticInfo:
dependencies = self._graph.inputs[target.id]
has_complex_dependency = any(
self.is_template(cst_id) and \
not self.is_simple_expression(cst_id) for cst_id in dependencies
self.is_template(cst_id) and
not self.is_simple_expression(cst_id) for cst_id in dependencies
)
if has_complex_dependency:
return False
@ -534,11 +536,11 @@ class SemanticInfo:
parent_info = self[parent.id]
if not is_base_set(parent.cst_type) and \
(not parent_info['is_template'] or not parent_info['is_simple']):
(not parent_info['is_template'] or not parent_info['is_simple']):
sources.add(parent_info['parent'])
return sources
def _need_check_head(self, sources: set[int], head: str)-> bool:
def _need_check_head(self, sources: set[int], head: str) -> bool:
if len(sources) == 0:
return True
elif len(sources) != 1:
@ -551,15 +553,16 @@ class SemanticInfo:
class _OrderManager:
''' Ordering helper class '''
def __init__(self, schema: RSForm):
self._semantic = schema.semantic()
self._graph = schema._graph_formal()
self._items = list(
schema.constituents() \
.only('id', 'order', 'alias', 'cst_type', 'definition_formal') \
.order_by('order')
schema.constituents()
.only('id', 'order', 'alias', 'cst_type', 'definition_formal')
.order_by('order')
)
self._cst_by_ID = { cst.id : cst for cst in self._items }
self._cst_by_ID = {cst.id: cst for cst in self._items}
def restore_order(self) -> None:
''' Implement order restoration process. '''
@ -579,9 +582,9 @@ class _OrderManager:
result = [cst for cst in self._items if cst.cst_type == CstType.BASE]
result = result + [cst for cst in self._items if cst.cst_type == CstType.CONSTANT]
kernel = [
cst.id for cst in self._items if \
cst.cst_type in [CstType.STRUCTURED, CstType.AXIOM] or \
self._cst_by_ID[self._semantic.parent(cst.id)].cst_type == CstType.STRUCTURED
cst.id for cst in self._items if
cst.cst_type in [CstType.STRUCTURED, CstType.AXIOM] or
self._cst_by_ID[self._semantic.parent(cst.id)].cst_type == CstType.STRUCTURED
]
kernel = kernel + self._graph.expand_inputs(kernel)
result = result + [cst for cst in self._items if result.count(cst) == 0 and cst.id in kernel]
@ -604,7 +607,6 @@ class _OrderManager:
result.append(child)
self._items = result
@transaction.atomic
def _save_order(self) -> None:
order = 1

View File

@ -9,7 +9,7 @@ import pyconcept
from .. import messages as msg
from .Constituenta import CstType
_RE_GLOBALS = r'[XCSADFPT]\d+' # cspell:disable-line
_RE_GLOBALS = r'[XCSADFPT]\d+' # cspell:disable-line
_RE_TEMPLATE = r'R\d+'
_RE_COMPLEX_SYMBOLS = r'[∀∃×ℬ;|:]'
@ -34,7 +34,7 @@ def get_type_prefix(cst_type: CstType) -> str:
case CstType.STRUCTURED: return 'S'
case CstType.AXIOM: return 'A'
case CstType.TERM: return 'D'
case CstType.FUNCTION: return 'F'
case CstType.FUNCTION: return 'F'
case CstType.PREDICATE: return 'P'
case CstType.THEOREM: return 'T'
return 'X'
@ -140,9 +140,9 @@ def generate_structure(alias: str, expression: str, parse: dict) -> list:
for (n, item) in enumerate(ast):
if n == 0:
generated.append({
'text': link, # generated text
'operation': None, # applied operation. None if text should be skipped
'is_boolean': False # is the result of operation has an additional boolean
'text': link, # generated text
'operation': None, # applied operation. None if text should be skipped
'is_boolean': False # is the result of operation has an additional boolean
})
continue
@ -150,7 +150,7 @@ def generate_structure(alias: str, expression: str, parse: dict) -> list:
parent_type = ast[parent_index]['typeID']
parent_text = generated[parent_index]['text']
parent_is_boolean = generated[parent_index]['is_boolean']
assert(parent_type in [TokenType.BOOLEAN, TokenType.DECART])
assert parent_type in [TokenType.BOOLEAN, TokenType.DECART]
if parent_is_boolean:
if parent_type == TokenType.BOOLEAN:

View File

@ -65,6 +65,7 @@ class ErrorDescriptionSerializer(serializers.Serializer):
child=serializers.CharField()
)
class NodeDataSerializer(serializers.Serializer):
''' Serializer: Node data. '''
dataType = serializers.CharField()
@ -74,11 +75,11 @@ class NodeDataSerializer(serializers.Serializer):
class ASTNodeSerializer(serializers.Serializer):
''' Serializer: Syntax tree node. '''
uid = serializers.IntegerField()
parent = serializers.IntegerField() # type: ignore
parent = serializers.IntegerField() # type: ignore
typeID = serializers.IntegerField()
start = serializers.IntegerField()
finish = serializers.IntegerField()
data = NodeDataSerializer() # type: ignore
data = NodeDataSerializer() # type: ignore
class ExpressionParseSerializer(serializers.Serializer):
@ -91,7 +92,7 @@ class ExpressionParseSerializer(serializers.Serializer):
ast = serializers.ListField(
child=ASTNodeSerializer()
)
errors = serializers.ListField( # type: ignore
errors = serializers.ListField( # type: ignore
child=ErrorDescriptionSerializer()
)
args = serializers.ListField(
@ -116,7 +117,7 @@ class ReferenceDataSerializer(serializers.Serializer):
class ReferenceSerializer(serializers.Serializer):
''' Serializer: Language reference. '''
type = serializers.CharField()
data = ReferenceDataSerializer() # type: ignore
data = ReferenceDataSerializer() # type: ignore
pos_input = TextPositionSerializer()
pos_output = TextPositionSerializer()

View File

@ -88,12 +88,12 @@ class CstSerializer(serializers.ModelSerializer):
read_only_fields = ('id', 'order', 'alias', 'cst_type', 'definition_resolved', 'term_resolved')
def update(self, instance: Constituenta, validated_data) -> Constituenta:
data = validated_data # Note: use alias for better code readability
data = validated_data # Note: use alias for better code readability
schema = RSForm(instance.schema)
definition: Optional[str] = data['definition_raw'] if 'definition_raw' in data else None
term: Optional[str] = data['term_raw'] if 'term_raw' in data else None
term_changed = 'term_forms' in data
if definition is not None and definition != instance.definition_raw :
if definition is not None and definition != instance.definition_raw:
data['definition_resolved'] = schema.resolver().resolve(definition)
if term is not None and term != instance.term_raw:
data['term_resolved'] = schema.resolver().resolve(term)
@ -368,7 +368,7 @@ class CstSubstituteSerializer(serializers.Serializer):
class InlineSynthesisSerializer(serializers.Serializer):
''' Serializer: Inline synthesis operation input. '''
receiver = PKField(many=False, queryset=LibraryItem.objects.all())
source = PKField(many=False, queryset=LibraryItem.objects.all()) # type: ignore
source = PKField(many=False, queryset=LibraryItem.objects.all()) # type: ignore
items = PKField(many=True, queryset=Constituenta.objects.all())
substitutions = serializers.ListField(
child=CstSubstituteSerializerBase()

View File

@ -12,6 +12,7 @@ _TRS_VERSION_MIN = 16
_TRS_VERSION = 16
_TRS_HEADER = 'Exteor 4.8.13.1000 - 30/05/2022'
class FileSerializer(serializers.Serializer):
''' Serializer: File input. '''
file = serializers.FileField(allow_empty_file=False)
@ -25,6 +26,7 @@ class RSFormUploadSerializer(serializers.Serializer):
class RSFormTRSSerializer(serializers.Serializer):
''' Serializer: TRS file production and loading for RSForm. '''
def to_representation(self, instance: RSForm) -> dict:
result = self._prepare_json_rsform(instance)
items = instance.constituents().order_by('order')
@ -115,7 +117,7 @@ class RSFormTRSSerializer(serializers.Serializer):
if self.context['load_meta']:
result['title'] = data.get('title', 'Без названия')
result['alias'] = data.get('alias', '')
result['comment']= data.get('comment', '')
result['comment'] = data.get('comment', '')
if 'id' in data:
result['id'] = data['id']
self.instance = RSForm(LibraryItem.objects.get(pk=result['id']))

View File

@ -10,6 +10,7 @@ from ..models import RSForm
class PyConceptAdapter:
''' RSForm adapter for interacting with pyconcept module. '''
def __init__(self, data: Union[RSForm, dict]):
try:
if 'items' in cast(dict, data):

View File

@ -14,6 +14,7 @@ class NewCstResponse(serializers.Serializer):
new_cst = serializers.IntegerField()
schema = RSFormParseSerializer()
class NewMultiCstResponse(serializers.Serializer):
''' Serializer: Create multiple cst response. '''
cst_list = serializers.ListField(
@ -21,6 +22,7 @@ class NewMultiCstResponse(serializers.Serializer):
)
schema = RSFormParseSerializer()
class NewVersionResponse(serializers.Serializer):
''' Serializer: Create cst response. '''
version = serializers.IntegerField()

View File

@ -8,6 +8,7 @@ from apps.rsform.models import Constituenta, CstType, LibraryItem, LibraryItemTy
class TestConstituenta(TestCase):
''' Testing Constituenta model. '''
def setUp(self):
self.schema1 = LibraryItem.objects.create(item_type=LibraryItemType.RSFORM, title='Test1')
self.schema2 = LibraryItem.objects.create(item_type=LibraryItemType.RSFORM, title='Test2')

View File

@ -6,6 +6,7 @@ from apps.rsform.models import LibraryItem, LibraryItemType, Subscription, User
class TestLibraryItem(TestCase):
''' Testing LibraryItem model. '''
def setUp(self):
self.user1 = User.objects.create(username='User1')
self.user2 = User.objects.create(username='User2')

View File

@ -7,6 +7,7 @@ from apps.rsform.models import Constituenta, CstType, RSForm, User
class TestRSForm(TestCase):
''' Testing RSForm wrapper. '''
def setUp(self):
self.user1 = User.objects.create(username='User1')
self.user2 = User.objects.create(username='User2')
@ -81,7 +82,7 @@ class TestRSForm(TestCase):
def test_insert_at_reorder(self):
self.schema.insert_new('X1')
d1 = self.schema.insert_new('D1')
d2 = self.schema.insert_new('D2',position=1)
d2 = self.schema.insert_new('D2', position=1)
d1.refresh_from_db()
self.assertEqual(d1.order, 3)
self.assertEqual(d2.order, 1)
@ -108,7 +109,7 @@ class TestRSForm(TestCase):
definition_raw='@{X1|datv} @{X2|datv}'
)
x2 = self.schema.create_cst({
'alias': 'X2',
'alias': 'X2',
'cst_type': CstType.BASE,
'term_raw': 'слон',
'definition_raw': '@{X1|plur} @{X2|plur}'
@ -161,7 +162,7 @@ class TestRSForm(TestCase):
convention='X1',
term_raw='@{X1|plur}'
)
self.schema.apply_mapping({x1.alias: 'X3', x2.alias: 'X4'})
d1.refresh_from_db()
self.assertEqual(d1.definition_formal, 'X3 = X4 = X2', msg='Map IDs in expression')
@ -260,7 +261,7 @@ class TestRSForm(TestCase):
alias='F2',
definition_formal=r'[α∈ℬ(X1)] X1\α',
)
self.schema.restore_order()
x1.refresh_from_db()
x2.refresh_from_db()
@ -345,8 +346,8 @@ class TestRSForm(TestCase):
definition_resolved='очень сильный человек'
)
x1.term_raw='слон'
x1.term_resolved='слон'
x1.term_raw = 'слон'
x1.term_resolved = 'слон'
x1.save()
self.schema.on_term_change([x1.id])

View File

@ -6,7 +6,7 @@ from apps.rsform.graph import Graph
class TestGraph(unittest.TestCase):
''' Test class for graph. '''
def test_construction(self):
graph = Graph()
self.assertFalse(graph.contains(1))

View File

@ -8,7 +8,6 @@ from apps.rsform.utils import apply_pattern, fix_old_references
class TestUtils(unittest.TestCase):
''' Test various utility functions. '''
def test_apply_mapping_patter(self):
mapping = {'X101': 'X20'}
pattern = re.compile(r'(X[0-9]+)')

View File

@ -15,16 +15,18 @@ _REF_OLD_PATTERN = re.compile(r'@{([^0-9\-][^\}\|\{]*?)\|([^\}\|\{]*?)\|([^\}\|\
class ObjectOwnerOrAdmin(BasePermission):
''' Permission for object ownership restriction '''
def has_object_permission(self, request, view, obj):
if request.user == obj.owner:
return True
if not hasattr(request.user, 'is_staff'):
return False
return request.user.is_staff # type: ignore
return request.user.is_staff # type: ignore
class IsClaimable(IsAuthenticated):
''' Permission for object ownership restriction '''
def has_object_permission(self, request, view, obj):
if not super().has_permission(request, view):
return False
@ -33,22 +35,24 @@ class IsClaimable(IsAuthenticated):
class SchemaOwnerOrAdmin(BasePermission):
''' Permission for object ownership restriction '''
def has_object_permission(self, request, view, obj):
if request.user == obj.schema.owner:
return True
if not hasattr(request.user, 'is_staff'):
return False
return request.user.is_staff # type: ignore
return request.user.is_staff # type: ignore
class ItemOwnerOrAdmin(BasePermission):
''' Permission for object ownership restriction '''
def has_object_permission(self, request, view, obj):
if request.user == obj.item.owner:
return True
if not hasattr(request.user, 'is_staff'):
return False
return request.user.is_staff # type: ignore
return request.user.is_staff # type: ignore
def read_zipped_json(data, json_filename: str) -> dict:
@ -77,11 +81,11 @@ def apply_pattern(text: str, mapping: dict[str, str], pattern: re.Pattern[str])
for segment in re.finditer(pattern, text):
entity = segment.group(1)
if entity in mapping:
output += text[pos_input : segment.start(1)]
output += text[pos_input: segment.start(1)]
output += mapping[entity]
output += text[segment.end(1) : segment.end(0)]
output += text[segment.end(1): segment.end(0)]
pos_input = segment.end(0)
output += text[pos_input : len(text)]
output += text[pos_input: len(text)]
return output
@ -92,10 +96,10 @@ def fix_old_references(text: str) -> str:
pos_input: int = 0
output: str = ''
for segment in re.finditer(_REF_OLD_PATTERN, text):
output += text[pos_input : segment.start(0)]
output += text[pos_input: segment.start(0)]
output += f'@{{{segment.group(1)}|{segment.group(2)},{segment.group(3)}}}'
pos_input = segment.end(0)
output += text[pos_input : len(text)]
output += text[pos_input: len(text)]
return output

View File

@ -90,12 +90,12 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr
cst = cast(m.Constituenta, serializer.validated_data['target'])
schema_details = s.RSFormParseSerializer(schema.item).data['items']
cst_parse = next(item for item in schema_details if item['id']==cst.id)['parse']
cst_parse = next(item for item in schema_details if item['id'] == cst.id)['parse']
if not cst_parse['typification']:
return Response(
status=c.HTTP_400_BAD_REQUEST,
data={f'{cst.id}': msg.constituentaNoStructure()}
)
status=c.HTTP_400_BAD_REQUEST,
data={f'{cst.id}': msg.constituentaNoStructure()}
)
result = schema.produce_structure(cst, cst_parse)
return Response(
@ -131,7 +131,7 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr
cst.cst_type = serializer.validated_data['cst_type']
cst.save()
mapping = { old_alias: cst.alias }
mapping = {old_alias: cst.alias}
schema.apply_mapping(mapping, change_aliases=False)
schema.item.refresh_from_db()
cst.refresh_from_db()
@ -350,7 +350,7 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr
serializer = s.ExpressionSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
expression = serializer.validated_data['expression']
schema = s.PyConceptAdapter(self._get_schema())
schema = s.PyConceptAdapter(self._get_schema())
result = pyconcept.check_expression(json.dumps(schema.data), expression)
return Response(
status=c.HTTP_200_OK,

View File

@ -121,14 +121,14 @@ def retrieve_version(request: Request, pk_item: int, pk_version: int):
@extend_schema(
summary='export versioned data as file',
tags=['Version'],
request=None,
responses={
(c.HTTP_200_OK, 'application/zip'): bytes,
c.HTTP_404_NOT_FOUND: None
}
)
summary='export versioned data as file',
tags=['Version'],
request=None,
responses={
(c.HTTP_200_OK, 'application/zip'): bytes,
c.HTTP_404_NOT_FOUND: None
}
)
@api_view(['GET'])
def export_file(request: Request, pk: int):
''' Endpoint: Download Exteor compatible file for versioned data. '''

View File

@ -1,8 +1,10 @@
''' Utility: Text messages. '''
# pylint: skip-file
def passwordAuthFailed():
return 'Неизвестное сочетание имени пользователя и пароля'
def passwordsNotMatch():
return 'Введенные пароли не совпадают'

View File

@ -1,4 +1,4 @@
''' Models: User profile and Authentification. '''
''' Models: User profile and Authorization. '''
# Note: using User import to isolate original
# pylint: disable=unused-import,ungrouped-imports

View File

@ -23,6 +23,7 @@ def _get_secret(key: str, default):
return f.read()
return value
_TRUE_VARIANTS = [True, 'True', '1']
# Build paths inside the project like this: BASE_DIR / 'subdir'.