Adds control over when to save ModelLogs

sync
Laurent 9 months ago
parent d9a5e98ad3
commit 3d6aa8282a
  1. 3
      api/views.py
  2. 29
      sync/models/data_access.py
  3. 4
      sync/models/model_log.py
  4. 85
      sync/signals.py
  5. 2
      sync/views.py
  6. 2
      tournaments/admin.py
  7. 18
      tournaments/migrations/0111_customuser_should_synchronize.py
  8. 3
      tournaments/models/custom_user.py

@ -46,6 +46,7 @@ class CustomAuthToken(APIView):
if user:
# user.device_id = device_id
# user.save()
# self.create_or_update_device(user, device_id)
# token, created = Token.objects.get_or_create(user=user)
# return Response({'token': token.key})
@ -90,6 +91,8 @@ class Logout(APIView):
request.user.device_id = None
request.user.save()
Device.objects.filter(id=device_id).delete()
return Response(status=status.HTTP_200_OK)
@api_view(['GET'])

@ -37,20 +37,21 @@ class DataAccess(BaseModel):
if isinstance(obj, SideStoreModel):
store_id = obj.store_id
existing_log = ModelLog.objects.filter(users__in=users, model_id=self.model_id, operation=operation).first()
if existing_log:
existing_log.date = timezone.now()
existing_log.model_operation = operation
existing_log.save()
else:
model_log = ModelLog.objects.create(
model_id=self.model_id,
model_name=self.model_name,
operation=operation,
date=timezone.now(),
store_id=store_id
)
model_log.users.set(users)
for user in users:
existing_log = ModelLog.objects.filter(user=user, model_id=self.model_id, operation=operation).first()
if existing_log:
existing_log.date = timezone.now()
existing_log.model_operation = operation
existing_log.save()
else:
ModelLog.objects.create(
user=user,
model_id=self.model_id,
model_name=self.model_name,
operation=operation,
date=timezone.now(),
store_id=store_id
)
except ObjectDoesNotExist:
pass
else:

@ -1,10 +1,6 @@
from django.db import models
from django.conf import settings
import uuid
from tkinter.constants import CASCADE
class ModelOperation(models.TextChoices):
POST = 'POST', 'POST'
PUT = 'PUT', 'PUT'

@ -2,7 +2,7 @@ from django.db.models.signals import pre_save, post_save, pre_delete, post_delet
from django.db import models, transaction
from django.dispatch import receiver
from .models import DataAccess, ModelLog, ModelOperation, BaseModel, SideStoreModel
from .models import DataAccess, ModelLog, ModelOperation, BaseModel, SideStoreModel, Device
from django.contrib.auth import get_user_model
from django.utils import timezone
@ -10,14 +10,26 @@ from .ws_sender import websocket_sender
User = get_user_model()
@receiver(post_save, sender=Device)
def device_created(sender, instance, **kwargs):
if not instance.user:
return
evaluate_if_user_should_sync(instance.user)
@receiver(pre_delete, sender=Device)
def device_pre_delete(sender, instance, **kwargs):
instance._user = instance.user if instance.user else None
@receiver(post_delete, sender=Device)
def device_post_delete(sender, instance, **kwargs):
if not hasattr(instance, '_user') or not instance._user:
return
evaluate_if_user_should_sync(instance._user)
@receiver(pre_save)
def presave_handler(sender, instance, **kwargs):
synchronization_prepare(sender, instance, **kwargs)
# @receiver(pre_delete)
# def predelete_handler(sender, instance, **kwargs):
# synchronization_prepare(sender, instance, False, **kwargs)
def synchronization_prepare(sender, instance, **kwargs):
signal = kwargs.get('signal')
@ -122,14 +134,15 @@ def save_model_log(users, model_operation, model_name, model_id, store_id, devic
with transaction.atomic():
for user in users:
model_log = ModelLog()
model_log.user = user
model_log.operation = model_operation
model_log.model_name = model_name
model_log.model_id = model_id
model_log.store_id = store_id
model_log.device_id = device_id
model_log.save()
if user.should_synchronize:
model_log = ModelLog()
model_log.user = user
model_log.operation = model_operation
model_log.model_name = model_name
model_log.model_id = model_id
model_log.store_id = store_id
model_log.device_id = device_id
model_log.save()
# print(f'ML users = {len(users)}')
# existing_log = ModelLog.objects.filter(users__in=users, model_id=model_id, operation=model_operation).first()
@ -177,18 +190,6 @@ def detect_foreign_key_changes(sender, instance, device_id):
'new_value': new_value
})
# for data_access in data_access_list:
# if old_value:
# model_name = old_value.__class__.__name__
# save_model_log(data_access.concerned_users(), 'REVOKE_ACCESS', model_name, old_value.id, old_value.get_store_id(), device_id)
# if new_value:
# model_name = new_value.__class__.__name__
# save_model_log(data_access.concerned_users(), 'GRANT_ACCESS', model_name, new_value.id, new_value.get_store_id(), device_id)
# # REVOKE access for old_value and GRANT new_value
# print(f"Foreign key changed in {sender.__name__}: "
# f"{field.name} from {old_value} to {new_value}")
def process_foreign_key_changes(sender, instance, device_id, **kwargs):
if hasattr(instance, '_fk_changes'):
for change in instance._fk_changes:
@ -206,6 +207,8 @@ def process_foreign_key_changes(sender, instance, device_id, **kwargs):
@receiver(post_delete)
def delete_data_access_if_necessary(sender, instance, **kwargs):
if not isinstance(instance, BaseModel):
return
if hasattr(instance, 'id'):
DataAccess.objects.filter(model_id=instance.id).delete()
@ -225,10 +228,24 @@ def handle_shared_with_changes(sender, instance, action, pk_set, **kwargs):
for user_id in pk_set:
websocket_sender.send_user_message(user_id, device_id)
for user in users:
evaluate_if_user_should_sync(user)
@receiver(post_save, sender=DataAccess)
def data_access_post_save(sender, instance, **kwargs):
if instance.related_user:
evaluate_if_user_should_sync(instance.related_user)
@receiver(pre_delete, sender=DataAccess)
def revoke_access_after_delete(sender, instance, **kwargs):
instance.create_revoke_access_log()
instance._user = instance.related_user
@receiver(post_delete, sender=DataAccess)
def data_access_post_delete(sender, instance, **kwargs):
if not hasattr(instance, '_user') or not instance._user:
return
evaluate_if_user_should_sync(instance._user)
def related_users(instance):
users = set()
@ -259,7 +276,23 @@ def related_data_access(instance):
return instances_related_data_access(instance, related_instances)
def instances_related_data_access(instance, related_instances):
# related_instances = instance.related_instances()
related_ids = [ri.id for ri in related_instances]
related_ids.append(instance.id)
return DataAccess.objects.filter(model_id__in=related_ids)
def evaluate_if_user_should_sync(user):
should_synchronize = False
if user.devices.count() > 1:
should_synchronize = True
elif DataAccess.objects.filter(
models.Q(shared_with=user) |
models.Q(related_user=user)
).count() > 0:
should_synchronize = True
print(f'should_synchronize = {should_synchronize}')
with transaction.atomic():
user.should_synchronize = should_synchronize
# if we go from True to False we might want to delete ModelLog once the last device has synchronized
user.save()

@ -160,7 +160,7 @@ class SynchronizationApi(HierarchyApiView):
except Exception as e:
response_status = status.HTTP_400_BAD_REQUEST
print(f'other exception: {str(e)}')
print(f'other exception: {str(e)}, type: {type(e)}, args: {e.args}, traceback: {e.__traceback__}')
message = str(e)
results.append({

@ -22,7 +22,7 @@ class CustomUserAdmin(UserAdmin):
'summons_message_body', 'summons_message_signature', 'summons_available_payment_methods',
'summons_display_format', 'summons_display_entry_fee', 'summons_use_full_custom_message',
'match_formats_default_duration', 'bracket_match_format_preference', 'group_stage_match_format_preference',
'loser_bracket_match_format_preference', 'device_id', 'loser_bracket_mode', 'groups', 'origin', 'agents'
'loser_bracket_match_format_preference', 'device_id', 'loser_bracket_mode', 'groups', 'origin', 'agents', 'should_synchronize'
]}),
]
add_fieldsets = [

@ -0,0 +1,18 @@
# Generated by Django 5.1 on 2025-02-14 16:06
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('tournaments', '0110_remove_failedapicall_creation_date_and_more'),
]
operations = [
migrations.AddField(
model_name='customuser',
name='should_synchronize',
field=models.BooleanField(default=False),
),
]

@ -37,6 +37,7 @@ class CustomUser(AbstractUser):
loser_bracket_mode = models.IntegerField(default=0)
origin = models.IntegerField(default=enums.UserOrigin.ADMIN, choices=enums.UserOrigin.choices, null=True, blank=True)
should_synchronize = models.BooleanField(default=False)
### ### ### ### ### ### ### ### ### ### ### WARNING ### ### ### ### ### ### ### ### ### ###
### WARNING : Any added field MUST be inserted in the method below: fields_for_update() ###
@ -49,7 +50,7 @@ class CustomUser(AbstractUser):
'summons_message_body', 'summons_message_signature', 'summons_available_payment_methods',
'summons_display_format', 'summons_display_entry_fee',
'summons_use_full_custom_message', 'match_formats_default_duration', 'bracket_match_format_preference',
'group_stage_match_format_preference', 'loser_bracket_match_format_preference', 'device_id', 'loser_bracket_mode', 'origin', 'agents']
'group_stage_match_format_preference', 'loser_bracket_match_format_preference', 'device_id', 'loser_bracket_mode', 'origin', 'agents', 'should_synchronize']
def __str__(self):
return self.username

Loading…
Cancel
Save