F: Implement editors change for OSS -> RSForm

This commit is contained in:
Ivan 2024-08-06 22:27:51 +03:00
parent edc728fe00
commit 91642816b1
13 changed files with 177 additions and 56 deletions

View File

@ -0,0 +1,21 @@
# Generated by Django 5.0.7 on 2024-08-06 19:31
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('library', '0001_initial'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.AlterField(
model_name='editor',
name='editor',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Редактор'),
),
]

View File

@ -0,0 +1,19 @@
# Generated by Django 5.0.7 on 2024-08-06 19:35
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('library', '0002_alter_editor_editor'),
]
operations = [
migrations.AlterField(
model_name='librarytemplate',
name='lib_source',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='library.libraryitem', verbose_name='Источник'),
),
]

View File

@ -1,14 +1,11 @@
''' Models: Editor. '''
from typing import TYPE_CHECKING
from typing import Iterable
from django.db import transaction
from django.db.models import CASCADE, DateTimeField, ForeignKey, Model
from apps.users.models import User
if TYPE_CHECKING:
from .LibraryItem import LibraryItem
class Editor(Model):
''' Editor list. '''
@ -20,8 +17,7 @@ class Editor(Model):
editor: ForeignKey = ForeignKey(
verbose_name='Редактор',
to=User,
on_delete=CASCADE,
null=True
on_delete=CASCADE
)
time_create: DateTimeField = DateTimeField(
verbose_name='Дата добавления',
@ -38,17 +34,17 @@ class Editor(Model):
return f'{self.item}: {self.editor}'
@staticmethod
def add(item: 'LibraryItem', user: User) -> bool:
def add(item: int, user: int) -> bool:
''' Add Editor for item. '''
if Editor.objects.filter(item=item, editor=user).exists():
if Editor.objects.filter(item_id=item, editor_id=user).exists():
return False
Editor.objects.create(item=item, editor=user)
Editor.objects.create(item_id=item, editor_id=user)
return True
@staticmethod
def remove(item: 'LibraryItem', user: User) -> bool:
def remove(item: int, user: int) -> bool:
''' Remove Editor. '''
editor = Editor.objects.filter(item=item, editor=user)
editor = Editor.objects.filter(item_id=item, editor_id=user).only('pk')
if not editor.exists():
return False
editor.delete()
@ -56,16 +52,40 @@ class Editor(Model):
@staticmethod
@transaction.atomic
def set(item: 'LibraryItem', users: list[User]):
def set(item: int, users: Iterable[int]):
''' Set editors for item. '''
processed: list[User] = []
for editor_item in Editor.objects.filter(item=item):
if editor_item.editor not in users:
processed: set[int] = set()
for editor_item in Editor.objects.filter(item_id=item).only('pk', 'editor_id'):
editor_id = editor_item.editor_id
if editor_id not in users:
editor_item.delete()
else:
processed.append(editor_item.editor)
processed.add(editor_id)
for user in users:
if user not in processed:
processed.add(user)
Editor.objects.create(item_id=item, editor_id=user)
@staticmethod
@transaction.atomic
def set_and_return_diff(item: int, users: Iterable[int]) -> tuple[list[int], list[int]]:
''' Set editors for item and return diff. '''
processed: list[int] = []
deleted: list[int] = []
added: list[int] = []
for editor_item in Editor.objects.filter(item_id=item).only('pk', 'editor_id'):
editor_id = editor_item.editor_id
if editor_id not in users:
deleted.append(editor_id)
editor_item.delete()
else:
processed.append(editor_id)
for user in users:
if user not in processed:
processed.append(user)
Editor.objects.create(item=item, editor=user)
added.append(user)
Editor.objects.create(item_id=item, editor_id=user)
return (added, deleted)

View File

@ -16,7 +16,6 @@ from django.db.models import (
from apps.users.models import User
from .Editor import Editor
from .Subscription import Subscription
from .Version import Version
@ -119,9 +118,9 @@ class LibraryItem(Model):
''' Get all subscribers for this item. '''
return [subscription.user for subscription in Subscription.objects.filter(item=self.pk).only('user')]
def editors(self) -> list[User]:
def editors(self) -> QuerySet[User]:
''' Get all Editors of this item. '''
return [item.editor for item in Editor.objects.filter(item=self.pk).only('editor')]
return User.objects.filter(editor__item=self.pk)
def versions(self) -> QuerySet[Version]:
''' Get all Versions of this item. '''

View File

@ -7,8 +7,7 @@ class LibraryTemplate(Model):
lib_source: ForeignKey = ForeignKey(
verbose_name='Источник',
to='library.LibraryItem',
on_delete=CASCADE,
null=True
on_delete=CASCADE
)
class Meta:

View File

@ -86,7 +86,7 @@ class LibraryItemDetailsSerializer(serializers.ModelSerializer):
return [item.pk for item in instance.subscribers()]
def get_editors(self, instance: LibraryItem) -> list[int]:
return [item.pk for item in instance.editors()]
return list(instance.editors().values_list('pk', flat=True))
def get_versions(self, instance: LibraryItem) -> list:
return [VersionInnerSerializer(item).data for item in instance.versions()]

View File

@ -34,44 +34,65 @@ class TestEditor(TestCase):
def test_add_editor(self):
self.assertTrue(Editor.add(self.item, self.user1))
self.assertEqual(len(self.item.editors()), 1)
self.assertTrue(self.user1 in self.item.editors())
self.assertTrue(Editor.add(self.item.pk, self.user1.pk))
self.assertEqual(self.item.editors().count(), 1)
self.assertTrue(self.user1 in list(self.item.editors()))
self.assertFalse(Editor.add(self.item, self.user1))
self.assertEqual(len(self.item.editors()), 1)
self.assertFalse(Editor.add(self.item.pk, self.user1.pk))
self.assertEqual(self.item.editors().count(), 1)
self.assertTrue(Editor.add(self.item, self.user2))
self.assertEqual(len(self.item.editors()), 2)
self.assertTrue(Editor.add(self.item.pk, self.user2.pk))
self.assertEqual(self.item.editors().count(), 2)
self.assertTrue(self.user1 in self.item.editors())
self.assertTrue(self.user2 in self.item.editors())
self.user1.delete()
self.assertEqual(len(self.item.editors()), 1)
self.assertEqual(self.item.editors().count(), 1)
def test_remove_editor(self):
self.assertFalse(Editor.remove(self.item, self.user1))
Editor.add(self.item, self.user1)
Editor.add(self.item, self.user2)
self.assertEqual(len(self.item.editors()), 2)
self.assertFalse(Editor.remove(self.item.pk, self.user1.pk))
Editor.add(self.item.pk, self.user1.pk)
Editor.add(self.item.pk, self.user2.pk)
self.assertEqual(self.item.editors().count(), 2)
self.assertTrue(Editor.remove(self.item, self.user1))
self.assertEqual(len(self.item.editors()), 1)
self.assertTrue(Editor.remove(self.item.pk, self.user1.pk))
self.assertEqual(self.item.editors().count(), 1)
self.assertTrue(self.user2 in self.item.editors())
self.assertFalse(Editor.remove(self.item, self.user1))
self.assertFalse(Editor.remove(self.item.pk, self.user1.pk))
def test_set_editors(self):
Editor.set(self.item, [self.user1])
self.assertEqual(self.item.editors(), [self.user1])
Editor.set(self.item.pk, [self.user1.pk])
self.assertEqual(list(self.item.editors()), [self.user1])
Editor.set(self.item, [self.user1, self.user1])
self.assertEqual(self.item.editors(), [self.user1])
Editor.set(self.item.pk, [self.user1.pk, self.user1.pk])
self.assertEqual(list(self.item.editors()), [self.user1])
Editor.set(self.item, [])
self.assertEqual(self.item.editors(), [])
Editor.set(self.item.pk, [])
self.assertEqual(list(self.item.editors()), [])
Editor.set(self.item, [self.user1, self.user2])
Editor.set(self.item.pk, [self.user1.pk, self.user2.pk])
self.assertEqual(set(self.item.editors()), set([self.user1, self.user2]))
def test_set_editors_return_diff(self):
added, deleted = Editor.set_and_return_diff(self.item.pk, [self.user1.pk])
self.assertEqual(added, [self.user1.pk])
self.assertEqual(deleted, [])
self.assertEqual(list(self.item.editors()), [self.user1])
added, deleted = Editor.set_and_return_diff(self.item.pk, [self.user1.pk, self.user1.pk])
self.assertEqual(added, [])
self.assertEqual(deleted, [])
self.assertEqual(list(self.item.editors()), [self.user1])
added, deleted = Editor.set_and_return_diff(self.item.pk, [])
self.assertEqual(added, [])
self.assertEqual(deleted, [self.user1.pk])
self.assertEqual(list(self.item.editors()), [])
added, deleted = Editor.set_and_return_diff(self.item.pk, [self.user1.pk, self.user2.pk])
self.assertEqual(added, [self.user1.pk, self.user2.pk])
self.assertEqual(deleted, [])
self.assertEqual(set(self.item.editors()), set([self.user1, self.user2]))

View File

@ -197,18 +197,18 @@ class TestLibraryViewset(EndpointTester):
self.executeOK(data=data, item=self.owned.pk)
self.owned.refresh_from_db()
self.assertEqual(self.owned.time_update, time_update)
self.assertEqual(self.owned.editors(), [self.user])
self.assertEqual(list(self.owned.editors()), [self.user])
self.executeOK(data=data)
self.assertEqual(self.owned.editors(), [self.user])
self.assertEqual(list(self.owned.editors()), [self.user])
data = {'users': [self.user2.pk]}
self.executeOK(data=data)
self.assertEqual(self.owned.editors(), [self.user2])
self.assertEqual(list(self.owned.editors()), [self.user2])
data = {'users': []}
self.executeOK(data=data)
self.assertEqual(self.owned.editors(), [])
self.assertEqual(list(self.owned.editors()), [])
data = {'users': [self.user2.pk, self.user.pk]}
self.executeOK(data=data)

View File

@ -261,8 +261,32 @@ class LibraryViewSet(viewsets.ModelViewSet):
item = self._get_item()
serializer = s.UsersListSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
editors = serializer.validated_data['users']
m.Editor.set(item=item, users=editors)
editors: list[int] = request.data['users']
with transaction.atomic():
added, deleted = m.Editor.set_and_return_diff(item.pk, editors)
if len(added) >= 0 or len(deleted) >= 0:
owned_schemas = OperationSchema(item).owned_schemas().only('pk')
if owned_schemas.exists():
m.Editor.objects.filter(
item__in=owned_schemas,
editor_id__in=deleted
).delete()
existing_editors = m.Editor.objects.filter(
item__in=owned_schemas,
editor__in=added
).values_list('item_id', 'editor_id')
existing_editor_set = set(existing_editors)
new_editors = [
m.Editor(item=schema, editor_id=user)
for schema in owned_schemas
for user in added
if (item.id, user) not in existing_editor_set
]
m.Editor.objects.bulk_create(new_editors)
return Response(status=c.HTTP_200_OK)

View File

@ -169,7 +169,7 @@ class OperationSchema:
access_policy=self.model.access_policy,
location=self.model.location
)
Editor.set(schema.model, self.model.editors())
Editor.set(schema.model.pk, self.model.editors().values_list('pk', flat=True))
operation.result = schema.model
operation.save()
self.save()

View File

@ -2,7 +2,7 @@
from rest_framework import status
from apps.library.models import AccessPolicy, LocationHead
from apps.library.models import AccessPolicy, Editor, LocationHead
from apps.oss.models import Operation, OperationSchema, OperationType
from apps.rsform.models import RSForm
from apps.users.models import User
@ -105,3 +105,21 @@ class TestChangeAttributes(EndpointTester):
self.assertNotEqual(self.ks1.model.access_policy, data['access_policy'])
self.assertNotEqual(self.ks2.model.access_policy, data['access_policy'])
self.assertEqual(self.ks3.access_policy, data['access_policy'])
@decl_endpoint('/api/library/{item}/set-editors', method='patch')
def test_set_editors(self):
Editor.set(self.owned.model.pk, [self.user2.pk])
Editor.set(self.ks1.model.pk, [self.user2.pk, self.user.pk])
Editor.set(self.ks3.pk, [self.user2.pk, self.user.pk])
data = {'users': [self.user3.pk]}
self.executeOK(data=data, item=self.owned_id)
self.owned.refresh_from_db()
self.ks1.refresh_from_db()
self.ks2.refresh_from_db()
self.ks3.refresh_from_db()
self.assertEqual(list(self.owned.model.editors()), [self.user3])
self.assertEqual(list(self.ks1.model.editors()), [self.user, self.user2])
self.assertEqual(list(self.ks2.model.editors()), [])
self.assertEqual(set(self.ks3.editors()), set([self.user, self.user3]))

View File

@ -220,7 +220,7 @@ class TestOssViewset(EndpointTester):
@decl_endpoint('/api/oss/{item}/create-operation', method='post')
def test_create_operation_schema(self):
self.populateData()
Editor.add(self.owned.model, self.user2)
Editor.add(self.owned.model.pk, self.user2.pk)
data = {
'item_data': {
'alias': 'Test4',

View File

@ -67,9 +67,9 @@ class EndpointTester(APITestCase):
def toggle_editor(self, item: LibraryItem, value: bool = True):
if value:
Editor.add(item, self.user)
Editor.add(item.pk, self.user.pk)
else:
Editor.remove(item, self.user)
Editor.remove(item.pk, self.user.pk)
def login(self):
self.client.force_authenticate(user=self.user)