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

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

@ -38,7 +38,7 @@ class UserConsumer(WebsocketConsumer):
# Receive message from room group # Receive message from room group
def sync_update(self, event): def sync_update(self, event):
message = event["message"] message = event["message"]
print(f'event = {event}') # print(f'event = {event}')
# Send message to WebSocket # Send message to WebSocket
self.send(text_data=message) 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.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 django.dispatch import receiver
from .models import DataAccess, ModelLog, ModelOperation, BaseModel, SideStoreModel from .models import DataAccess, ModelLog, ModelOperation, BaseModel, SideStoreModel
@ -10,8 +10,20 @@ from .ws_sender import websocket_sender
User = get_user_model() User = get_user_model()
@receiver([pre_save, pre_delete]) @receiver(pre_save)
def synchronization_prepare(sender, instance, created=False, **kwargs): 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') signal = kwargs.get('signal')
@ -69,19 +81,15 @@ def notify_impacted_users(instance):
if hasattr(instance, '_device_id'): if hasattr(instance, '_device_id'):
device_id = instance._device_id device_id = instance._device_id
print(f'notify: {user_ids}') # print(f'notify: {user_ids}')
for user_id in user_ids: for user_id in user_ids:
websocket_sender.send_user_message(user_id, device_id) websocket_sender.send_user_message(user_id, device_id)
# send_user_message(user_id) # send_user_message(user_id)
def save_model_log_if_possible(instance, signal, created, device_id): def save_model_log_if_possible(instance, signal, created, device_id):
if isinstance(instance, User): users = related_users(instance)
users = {instance} # print(f'users = {len(users)}, instance = {instance}')
else:
users = related_users(instance)
# print(f'users = {users}')
if users: if users:
if signal == post_save or signal == pre_save: if signal == post_save or signal == pre_save:
if created: 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] 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 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) save_model_log(users, operation, model_name, instance.id, store_id, device_id)
else: 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): def save_model_log(users, model_operation, model_name, model_id, store_id, device_id):
now = timezone.now() 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() existing_log = ModelLog.objects.filter(users__in=users, model_id=model_id, operation=model_operation).first()
if existing_log: if existing_log:
# print(f'update existing log {existing_log.users} ') # print(f'update existing log {existing_log.users} ')

@ -91,10 +91,12 @@ class SynchronizationApi(HierarchyApiView):
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
device_id = request.data.get('device_id') device_id = request.data.get('device_id')
print(f"DataApi post > device: {device_id}")
operations = request.data['operations'] operations = request.data['operations']
results = [] results = []
print(f"DataApi post, {len(operations)} operations / device: {device_id}")
models = set()
for op in operations: for op in operations:
result = None result = None
@ -104,6 +106,8 @@ class SynchronizationApi(HierarchyApiView):
model_name = op.get('model_name') model_name = op.get('model_name')
data = op.get('data') data = op.get('data')
models.add(model_name)
serializer_class = build_serializer_class(model_name) serializer_class = build_serializer_class(model_name)
data['last_updated_by'] = request.user.id # always refresh the user performing the operation data['last_updated_by'] = request.user.id # always refresh the user performing the operation
@ -162,6 +166,8 @@ class SynchronizationApi(HierarchyApiView):
'message': message 'message': message
}) })
print(f"sync POST completed for models: {models}")
return Response({ return Response({
'results': results 'results': results
}, status=207) # Multi-Status }, status=207) # Multi-Status
@ -252,7 +258,8 @@ class SynchronizationApi(HierarchyApiView):
"date": last_log_date "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) return Response(response_data, status=status.HTTP_200_OK)
def query_model_logs(self, last_update, user, device_id): def query_model_logs(self, last_update, user, device_id):
@ -265,56 +272,66 @@ class UserDataAccessApi(HierarchyApiView):
permission_classes = [IsAuthenticated] permission_classes = [IsAuthenticated]
def get(self, request, *args, **kwargs): 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())
# Prepare response data structure
data_by_model = defaultdict(dict)
print(f'>>> grants = {len(active_grants)}') 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
for log in active_grants: data_by_model = defaultdict(dict)
try:
model = sync_registry.get_model(log.model_name)
instance = model.objects.get(id=log.model_id)
# Get the base data print(f'>>> grants = {len(data_access_objects)}')
serializer = get_serializer(instance, log.model_name)
data_by_model[log.model_name][log.model_id] = serializer.data
# Add parents & children recursively for data_access in data_access_objects:
self.add_children_recursively(instance, data_by_model) try:
self.add_parents_recursively(instance, data_by_model) model = sync_registry.get_model(data_access.model_name)
instance = model.objects.get(id=data_access.model_id)
except ObjectDoesNotExist: # Get the base data
continue serializer = get_serializer(instance, data_access.model_name)
data_by_model[data_access.model_name][data_access.model_id] = serializer.data
# Convert dictionary values to lists # Add parents & children recursively
response_data = { self.add_children_recursively(instance, data_by_model)
model_name: list(model_data.values()) self.add_parents_recursively(instance, data_by_model)
for model_name, model_data in data_by_model.items()
}
print(f'response_data = {response_data}') except ObjectDoesNotExist:
continue
return Response(response_data, status=status.HTTP_200_OK) 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): # def _add_children_recursively(self, instance, data_dict):
# """ # """

@ -14,7 +14,7 @@ class WebSocketSender:
""" """
Schedules a notification for a specific user with debouncing. 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 # Cancel existing timer for this user if any
if user_id in self._user_timers and self._user_timers[user_id]: if user_id in self._user_timers and self._user_timers[user_id]:
self._user_timers[user_id].cancel() self._user_timers[user_id].cancel()

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

Loading…
Cancel
Save