sync improvements and custom children sharing

sync_v2
Laurent 6 months ago
parent a2a4916c2f
commit 94580cdf73
  1. 5
      padelclub_backend/settings_app.py
  2. 2
      sync/models/data_access.py
  3. 4
      sync/models/model_log.py
  4. 4
      sync/signals.py
  5. 89
      sync/views.py
  6. 2
      tournaments/models/__init__.py
  7. 12
      tournaments/models/enums.py

@ -41,6 +41,11 @@ SYNC_APPS = {
'tournaments': { 'exclude': ['Log', 'FailedApiCall', 'DeviceToken'] }
}
SYNC_MODEL_CHILDREN_SHARING = {
'Match': {'team_scores', 'team_registration', 'player_registrations'}
}
STRIPE_CURRENCY = 'eur'
# Add managers who should receive internal emails
SHOP_MANAGERS = [

@ -22,7 +22,7 @@ class DataAccess(BaseModel):
pass
def create_revoke_access_log(self):
self.create_access_log(self.shared_with.all(), 'REVOKE_ACCESS')
self.create_access_log(self.shared_with.all(), 'REVOKED_ACCESS')
def concerned_users(self):
users = list(self.shared_with.all())

@ -5,8 +5,8 @@ class ModelOperation(models.TextChoices):
POST = 'POST', 'POST'
PUT = 'PUT', 'PUT'
DELETE = 'DELETE', 'DELETE'
GRANT_ACCESS = 'GRANT_ACCESS', 'GRANT_ACCESS'
REVOKE_ACCESS = 'REVOKE_ACCESS', 'REVOKE_ACCESS'
SHARED_ACCESS = 'SHARED_ACCESS', 'SHARED_ACCESS'
REVOKED_ACCESS = 'REVOKED_ACCESS', 'REVOKED_ACCESS'
SHARED_RELATIONSHIP_SET = 'SHARED_RELATIONSHIP_SET', 'SHARED_RELATIONSHIP_SET'
SHARED_RELATIONSHIP_REMOVED = 'SHARED_RELATIONSHIP_REMOVED', 'SHARED_RELATIONSHIP_REMOVED'

@ -259,9 +259,9 @@ def handle_shared_with_changes(sender, instance, action, pk_set, **kwargs):
users = User.objects.filter(id__in=pk_set)
if action == "post_add":
instance.create_access_log(users, 'GRANT_ACCESS')
instance.create_access_log(users, 'SHARED_ACCESS')
elif action == "post_remove":
instance.create_access_log(users, 'REVOKE_ACCESS')
instance.create_access_log(users, 'REVOKED_ACCESS')
device_id = device_registry.get_device_id(instance.id)
for user_id in pk_set:

@ -1,7 +1,6 @@
# from django.shortcuts import render
import re
import json
from .serializers import DataAccessSerializer
from rest_framework import viewsets
from rest_framework.views import APIView
@ -9,6 +8,7 @@ from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework import status
from django.conf import settings
from django.utils import timezone
from django.core.exceptions import ObjectDoesNotExist
from django.db.models import Q
@ -16,6 +16,7 @@ from django.db.models import Q
from collections import defaultdict
from urllib.parse import unquote
from .serializers import DataAccessSerializer
from .utils import get_serializer, build_serializer_class, get_data, get_serialized_data, HierarchyOrganizer
from .models import ModelLog, BaseModel, SideStoreModel, DataAccess
@ -24,7 +25,42 @@ from .registry import model_registry, device_registry
# class HierarchyApiView(APIView):
def add_children_recursively(instance, updates):
def add_children_hierarchy(instance, models_dict):
sync_models = getattr(settings, 'SYNC_MODEL_CHILDREN_SHARING', {})
if instance.__class__.__name__ in sync_models:
relationships = sync_models[instance.__class__.__name__]
# 'Match': {'team_scores', 'team_registration', 'player_registrations'}
# print(f'relationships = {relationships}')
current = [instance]
for relationship in relationships:
# print(f'relationship = {relationship}')
values = []
for item in current:
value = getattr(item, relationship)
if hasattr(value, 'all') and callable(value.all):
# This is a queryset from a reverse relationship
for related_obj in value.all():
child_model_name = related_obj.__class__.__name__
serializer = get_serializer(related_obj, child_model_name)
models_dict[child_model_name][related_obj.id] = serializer.data
# print(f'>>> 1 Added child for {relationship}: {child_model_name}')
values.extend(value.all())
else:
# This is a single object
child_model_name = value.__class__.__name__
serializer = get_serializer(value, child_model_name)
models_dict[child_model_name][value.id] = serializer.data
# print(f'>>> 2 Added child for {relationship}: {child_model_name}')
values.append(value)
current = values
else:
add_children_recursively(instance, models_dict)
def add_children_recursively(instance, models_dict):
"""
Recursively add all children of an instance to the updates dictionary.
"""
@ -33,10 +69,10 @@ def add_children_recursively(instance, updates):
for child_model_name, children in child_models.items():
for child in children:
if isinstance(child, BaseModel):
print(f'add_children_recursively: {child_model_name}')
# print(f'add_children_recursively: {child_model_name}')
serializer = get_serializer(child, child_model_name)
updates[child_model_name][child.id] = serializer.data
add_children_recursively(child, updates)
models_dict[child_model_name][child.id] = serializer.data
add_children_recursively(child, models_dict)
def add_parents_with_hierarchy_organizer(instance, hierarchy_organizer, current_level=0):
"""
@ -63,28 +99,17 @@ def add_parents_with_hierarchy_organizer(instance, hierarchy_organizer, current_
# Recursively process parent's parents
add_parents_with_hierarchy_organizer(parent, hierarchy_organizer, current_level + 1)
def add_parents_recursively(instance, dictionary, minimal=False):
def add_parents_recursively(instance, dictionary):
"""
Recursively add all parents of an instance to the updates dictionary.
If minimal=True, only add id and store_id.
"""
parent_models = instance.get_parents_by_model()
for parent_model_name, parent in parent_models.items():
if isinstance(parent, BaseModel):
if minimal:
store_id = None
if isinstance(parent, SideStoreModel):
store_id = parent.store_id
dictionary[parent_model_name][parent.id] = {
'model_id': parent.id,
'store_id': store_id
}
else:
serializer = get_serializer(parent, parent_model_name)
dictionary[parent_model_name][parent.id] = serializer.data
add_parents_recursively(parent, dictionary, minimal)
serializer = get_serializer(parent, parent_model_name)
dictionary[parent_model_name][parent.id] = serializer.data
add_parents_recursively(parent, dictionary)
class SynchronizationApi(APIView):
permission_classes = [IsAuthenticated]
@ -224,6 +249,7 @@ class LogProcessingResult:
# Initialize all the collections
self.updates = defaultdict(dict)
self.deletions = defaultdict(list)
self.shared_instances = defaultdict(dict) # {model_name: {model_id: instance}}
self.grant_instances = defaultdict(dict) # {model_name: {model_id: instance}}
self.revoke_info = defaultdict(list) # {model_name: [{model_id, store_id}]}
self.shared_relationship_sets = defaultdict(dict)
@ -240,7 +266,7 @@ class LogProcessingResult:
self.updates[log.model_name][log.model_id] = data
elif log.operation == 'DELETE':
self.deletions[log.model_name].append(log.data_identifier_dict())
elif log.operation == 'GRANT_ACCESS':
elif log.operation == 'SHARED_ACCESS':
# Remove any existing revocations for this model_id
self._remove_revocation(log.model_name, log.model_id)
@ -249,10 +275,10 @@ class LogProcessingResult:
model = model_registry.get_model(log.model_name)
try:
instance = model.objects.get(id=log.model_id)
self.grant_instances[log.model_name][log.model_id] = instance
self.shared_instances[log.model_name][log.model_id] = instance
except model.DoesNotExist:
pass
elif log.operation == 'REVOKE_ACCESS':
elif log.operation == 'REVOKED_ACCESS':
print(f'revoke access {log.model_id} - {log.store_id}')
# Remove any existing grants for this model_id
@ -300,25 +326,27 @@ class LogProcessingResult:
if not self.grant_instances[model_name]:
del self.grant_instances[model_name]
def process_grants(self):
def process_shared(self):
"""Process grants and their hierarchies."""
shared = defaultdict(dict)
grants = defaultdict(dict)
# Process each grant instance
for model_name, instances in self.grant_instances.items():
for model_name, instances in self.shared_instances.items():
for model_id, instance in instances.items():
serializer = get_serializer(instance, model_name)
grants[model_name][model_id] = serializer.data
shared[model_name][model_id] = serializer.data
# Add hierarchies only once per instance
add_children_recursively(instance, grants)
add_children_hierarchy(instance, grants)
add_parents_recursively(instance, grants)
# Convert to lists
for model_name in shared:
shared[model_name] = list(shared[model_name].values())
for model_name in grants:
grants[model_name] = list(grants[model_name].values())
return grants
return shared, grants
def process_revocations(self):
"""Process revocations and their hierarchies."""
@ -347,7 +375,7 @@ class LogProcessingResult:
def get_response_data(self):
"""Construct the complete response data structure."""
grants = self.process_grants()
shared, grants = self.process_shared()
revocations, revocations_parents_organizer = self.process_revocations()
# print(f'self.deletions = {dict(self.deletions)}')
@ -357,6 +385,7 @@ class LogProcessingResult:
return {
"updates": dict(self.updates),
"deletions": dict(self.deletions),
"shared": dict(shared),
"grants": dict(grants),
"revocations": dict(revocations),
"revocation_parents": revocations_parents_organizer.get_organized_data(),

@ -5,7 +5,7 @@ from .custom_user import CustomUser
from .club import Club
from .court import Court
from .date_interval import DateInterval
from .enums import UserOrigin, TournamentPayment, FederalCategory, FederalLevelCategory, FederalAgeCategory, FederalMatchCategory, OnlineRegistrationStatus, RegistrationStatus, ModelOperation
from .enums import UserOrigin, TournamentPayment, FederalCategory, FederalLevelCategory, FederalAgeCategory, FederalMatchCategory, OnlineRegistrationStatus, RegistrationStatus
from .player_enums import PlayerSexType, PlayerDataSource, PlayerPaymentType
from .event import Event
from .tournament import Tournament, TeamSummon, TeamSortingType, TeamItem

@ -168,12 +168,12 @@ class FederalMatchCategory(models.IntegerChoices):
else:
return 3
class ModelOperation(models.TextChoices):
POST = 'POST', 'POST'
PUT = 'PUT', 'PUT'
DELETE = 'DELETE', 'DELETE'
GRANT_ACCESS = 'GRANT_ACCESS', 'GRANT_ACCESS'
REVOKE_ACCESS = 'REVOKE_ACCESS', 'REVOKE_ACCESS'
# class ModelOperation(models.TextChoices):
# POST = 'POST', 'POST'
# PUT = 'PUT', 'PUT'
# DELETE = 'DELETE', 'DELETE'
# GRANT_ACCESS = 'GRANT_ACCESS', 'GRANT_ACCESS'
# REVOKE_ACCESS = 'REVOKE_ACCESS', 'REVOKE_ACCESS'
class OnlineRegistrationStatus(models.IntegerChoices):
OPEN = 1, 'Open'

Loading…
Cancel
Save