Laurent 10 months ago
parent ea77f2db83
commit 1f4fad4302
  1. 1
      padelclub_backend/settings_local.py.dist
  2. 2
      sync/consumers.py
  3. 31
      sync/signals.py
  4. 77
      sync/views.py
  5. 2
      sync/ws_sender.py
  6. 1
      tournaments/admin.py

@ -19,6 +19,7 @@ DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
'ATOMIC_REQUESTS': True,
}
}

@ -38,7 +38,7 @@ class UserConsumer(WebsocketConsumer):
# Receive message from room group
def sync_update(self, event):
message = event["message"]
print(f'event = {event}')
# print(f'event = {event}')
# Send message to WebSocket
self.send(text_data=message)

@ -1,5 +1,5 @@
from django.db.models.signals import pre_save, post_save, pre_delete, post_delete, m2m_changed
from django.db import models
from django.db import models, transaction
from django.dispatch import receiver
from .models import DataAccess, ModelLog, ModelOperation, BaseModel, SideStoreModel
@ -10,8 +10,20 @@ from .ws_sender import websocket_sender
User = get_user_model()
@receiver([pre_save, pre_delete])
def synchronization_prepare(sender, instance, created=False, **kwargs):
@receiver(pre_save)
def presave_handler(sender, instance, **kwargs):
try:
sender.objects.get(pk=instance.pk)
created = False
except sender.DoesNotExist:
created = True
synchronization_prepare(sender, instance, created, **kwargs)
@receiver(pre_delete)
def predelete_handler(sender, instance, **kwargs):
synchronization_prepare(sender, instance, False, **kwargs)
def synchronization_prepare(sender, instance, created, **kwargs):
signal = kwargs.get('signal')
@ -69,19 +81,15 @@ def notify_impacted_users(instance):
if hasattr(instance, '_device_id'):
device_id = instance._device_id
print(f'notify: {user_ids}')
# print(f'notify: {user_ids}')
for user_id in user_ids:
websocket_sender.send_user_message(user_id, device_id)
# send_user_message(user_id)
def save_model_log_if_possible(instance, signal, created, device_id):
if isinstance(instance, User):
users = {instance}
else:
users = related_users(instance)
# print(f'users = {users}')
# print(f'users = {len(users)}, instance = {instance}')
if users:
if signal == post_save or signal == pre_save:
if created:
@ -101,15 +109,16 @@ def save_model_log_if_possible(instance, signal, created, device_id):
user_ids = [user.id for user in users]
print(f'users to notify: {user_ids}')
# print(f'users to notify: {user_ids}')
instance._users_to_notify = user_ids # save this for the post_save signal
save_model_log(users, operation, model_name, instance.id, store_id, device_id)
else:
print('>>> Model Log could not be created because instance.last_updated_by is None')
print(f'>>> Model Log could not be created because no linked user could be found: {instance}, {signal}')
def save_model_log(users, model_operation, model_name, model_id, store_id, device_id):
now = timezone.now()
# print(f'ML users = {len(users)}')
existing_log = ModelLog.objects.filter(users__in=users, model_id=model_id, operation=model_operation).first()
if existing_log:
# print(f'update existing log {existing_log.users} ')

@ -91,10 +91,12 @@ class SynchronizationApi(HierarchyApiView):
def post(self, request, *args, **kwargs):
device_id = request.data.get('device_id')
print(f"DataApi post > device: {device_id}")
operations = request.data['operations']
results = []
print(f"DataApi post, {len(operations)} operations / device: {device_id}")
models = set()
for op in operations:
result = None
@ -104,6 +106,8 @@ class SynchronizationApi(HierarchyApiView):
model_name = op.get('model_name')
data = op.get('data')
models.add(model_name)
serializer_class = build_serializer_class(model_name)
data['last_updated_by'] = request.user.id # always refresh the user performing the operation
@ -162,6 +166,8 @@ class SynchronizationApi(HierarchyApiView):
'message': message
})
print(f"sync POST completed for models: {models}")
return Response({
'results': results
}, status=207) # Multi-Status
@ -252,7 +258,8 @@ class SynchronizationApi(HierarchyApiView):
"date": last_log_date
}
print(f'response_data = {response_data}')
print(f'sync GET response. UP = {len(updates)} / DEL = {len(deletions)} / G = {len(grants)} / R = {len(revocations)}')
# print(f'sync GET response. response = {response_data}')
return Response(response_data, status=status.HTTP_200_OK)
def query_model_logs(self, last_update, user, device_id):
@ -265,39 +272,25 @@ class UserDataAccessApi(HierarchyApiView):
permission_classes = [IsAuthenticated]
def get(self, request, *args, **kwargs):
# Get all GRANT_ACCESS and REVOKE_ACCESS logs for the user, ordered by date
all_logs = ModelLog.objects.filter(
Q(users=request.user) &
(Q(operation='GRANT_ACCESS') | Q(operation='REVOKE_ACCESS'))
).order_by('date')
# Track latest status for each (model_name, model_id)
active_grants = {}
# Process logs chronologically to determine current access state
for log in all_logs:
if log.operation == 'GRANT_ACCESS':
active_grants[log.model_id] = log
elif log.operation == 'REVOKE_ACCESS':
if log.model_id in active_grants and active_grants[log.model_id].date < log.date:
del active_grants[log.model_id]
# Convert active_grants dict to list of grant logs
active_grants = list(active_grants.values())
try:
# Get all DataAccess objects where the requesting user is in shared_with
data_access_objects = DataAccess.objects.filter(
shared_with=request.user
).prefetch_related('shared_with') # Use prefetch_related for better performance
# Prepare response data structure
data_by_model = defaultdict(dict)
print(f'>>> grants = {len(active_grants)}')
print(f'>>> grants = {len(data_access_objects)}')
for log in active_grants:
for data_access in data_access_objects:
try:
model = sync_registry.get_model(log.model_name)
instance = model.objects.get(id=log.model_id)
model = sync_registry.get_model(data_access.model_name)
instance = model.objects.get(id=data_access.model_id)
# Get the base data
serializer = get_serializer(instance, log.model_name)
data_by_model[log.model_name][log.model_id] = serializer.data
serializer = get_serializer(instance, data_access.model_name)
data_by_model[data_access.model_name][data_access.model_id] = serializer.data
# Add parents & children recursively
self.add_children_recursively(instance, data_by_model)
@ -306,16 +299,40 @@ class UserDataAccessApi(HierarchyApiView):
except ObjectDoesNotExist:
continue
# Convert dictionary values to lists
response_data = {
model_name: list(model_data.values())
for model_name, model_data in data_by_model.items()
}
print(f'response_data = {response_data}')
return Response(response_data, status=status.HTTP_200_OK)
except Exception as e:
return Response({
'status': 'error',
'message': str(e)
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
# # Get all GRANT_ACCESS and REVOKE_ACCESS logs for the user, ordered by date
# all_logs = ModelLog.objects.filter(
# Q(users=request.user) &
# (Q(operation='GRANT_ACCESS') | Q(operation='REVOKE_ACCESS'))
# ).order_by('date')
# # Track latest status for each (model_name, model_id)
# active_grants = {}
# # Process logs chronologically to determine current access state
# for log in all_logs:
# if log.operation == 'GRANT_ACCESS':
# active_grants[log.model_id] = log
# elif log.operation == 'REVOKE_ACCESS':
# if log.model_id in active_grants and active_grants[log.model_id].date < log.date:
# del active_grants[log.model_id]
# # Convert active_grants dict to list of grant logs
# active_grants = list(active_grants.values())
# def _add_children_recursively(self, instance, data_dict):
# """
# Recursively add all children of an instance to the data dictionary.

@ -14,7 +14,7 @@ class WebSocketSender:
"""
Schedules a notification for a specific user with debouncing.
"""
print(f'>>> send message: {device_id}')
# print(f'>>> send message: {device_id}')
# Cancel existing timer for this user if any
if user_id in self._user_timers and self._user_timers[user_id]:
self._user_timers[user_id].cancel()

@ -51,6 +51,7 @@ class TournamentAdmin(admin.ModelAdmin):
class TeamRegistrationAdmin(AutoUpdateAdmin):
list_display = ['player_names', 'group_stage_position', 'name', 'tournament']
list_filter = [SimpleTournamentListFilter]
search_fields = ['id']
class TeamScoreAdmin(AutoUpdateAdmin):
list_display = ['team_registration', 'score', 'walk_out', 'match']

Loading…
Cancel
Save