|
|
|
|
@ -21,7 +21,62 @@ from .models import ModelLog, BaseModel, SideStoreModel, DataAccess |
|
|
|
|
|
|
|
|
|
from .registry import sync_registry |
|
|
|
|
|
|
|
|
|
class SynchronizationApi(APIView): |
|
|
|
|
class HierarchyApiView(APIView): |
|
|
|
|
|
|
|
|
|
def add_children_recursively(self, instance, updates): |
|
|
|
|
""" |
|
|
|
|
Recursively add all children of an instance to the updates dictionary. |
|
|
|
|
""" |
|
|
|
|
# print(f"Instance class: {instance.__class__}") |
|
|
|
|
child_models = instance.get_children_by_model() |
|
|
|
|
|
|
|
|
|
for child_model_name, children in child_models.items(): |
|
|
|
|
for child in children: |
|
|
|
|
if isinstance(child, BaseModel): |
|
|
|
|
serializer = get_serializer(child, child_model_name) |
|
|
|
|
# serializer_class = build_serializer_class(child_model_name) |
|
|
|
|
# serializer = serializer_class(child) |
|
|
|
|
updates[child_model_name][child.id] = serializer.data |
|
|
|
|
self.add_children_recursively(child, updates) |
|
|
|
|
|
|
|
|
|
# def add_parents_recursively(self, instance, updates): |
|
|
|
|
# parent_models = instance.get_parents_by_model() |
|
|
|
|
|
|
|
|
|
# for parent_model_name, parent in parent_models.items(): |
|
|
|
|
# # print(f'parent = {parent_model_name}') |
|
|
|
|
# if isinstance(parent, BaseModel): |
|
|
|
|
# serializer_class = build_serializer_class(parent_model_name) |
|
|
|
|
# serializer = serializer_class(parent) |
|
|
|
|
# updates[parent_model_name][parent.id] = serializer.data |
|
|
|
|
# self.add_parents_recursively(parent, updates) |
|
|
|
|
|
|
|
|
|
def add_parents_recursively(self, instance, dictionary, minimal=False): |
|
|
|
|
""" |
|
|
|
|
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) |
|
|
|
|
# Add full serialized data |
|
|
|
|
# serializer_class = build_serializer_class(parent_model_name) |
|
|
|
|
# serializer = serializer_class(parent) |
|
|
|
|
dictionary[parent_model_name][parent.id] = serializer.data |
|
|
|
|
|
|
|
|
|
self.add_parents_recursively(parent, dictionary, minimal) |
|
|
|
|
|
|
|
|
|
class SynchronizationApi(HierarchyApiView): |
|
|
|
|
permission_classes = [IsAuthenticated] |
|
|
|
|
|
|
|
|
|
def post(self, request, *args, **kwargs): |
|
|
|
|
@ -74,70 +129,6 @@ class SynchronizationApi(APIView): |
|
|
|
|
else: |
|
|
|
|
return Response(status=status.HTTP_404_NOT_FOUND) |
|
|
|
|
|
|
|
|
|
# def get(self, request, *args, **kwargs): |
|
|
|
|
# last_update_str = request.query_params.get('last_update') |
|
|
|
|
# decoded_last_update = unquote(last_update_str) |
|
|
|
|
|
|
|
|
|
# if not decoded_last_update: |
|
|
|
|
# return Response({"error": "last_update parameter is required"}, status=status.HTTP_400_BAD_REQUEST) |
|
|
|
|
|
|
|
|
|
# try: |
|
|
|
|
# last_update = timezone.datetime.fromisoformat(decoded_last_update) |
|
|
|
|
# except ValueError: |
|
|
|
|
# return Response({"error": f"Invalid date format for last_update: {decoded_last_update}"}, status=status.HTTP_400_BAD_REQUEST) |
|
|
|
|
|
|
|
|
|
# logs = self.query_model_logs(last_update, request.user) |
|
|
|
|
|
|
|
|
|
# updates = defaultdict(dict) |
|
|
|
|
# deletions = defaultdict(list) |
|
|
|
|
# grants = defaultdict(dict) |
|
|
|
|
# revocations = defaultdict(list) |
|
|
|
|
# revocation_parents = defaultdict(dict) |
|
|
|
|
|
|
|
|
|
# last_log_date = None |
|
|
|
|
# for log in logs: |
|
|
|
|
# last_log_date = log.date |
|
|
|
|
# self._process_log(log, updates, deletions, grants, revocations, revocation_parents) |
|
|
|
|
|
|
|
|
|
# response_data = { |
|
|
|
|
# "updates": {k: list(v.values()) for k, v in updates.items()}, |
|
|
|
|
# "deletions": dict(deletions), |
|
|
|
|
# "grants": {k: list(v.values()) for k, v in grants.items()}, |
|
|
|
|
# "revocations": dict(revocations), |
|
|
|
|
# "revocation_parents": {k: list(v.values()) for k, v in revocation_parents.items()}, |
|
|
|
|
# "date": last_log_date |
|
|
|
|
# } |
|
|
|
|
|
|
|
|
|
# return Response(response_data, status=status.HTTP_200_OK) |
|
|
|
|
|
|
|
|
|
# def _process_log(self, log, updates, deletions, grants, revocations, revocation_parents): |
|
|
|
|
# try: |
|
|
|
|
# if log.operation in ['POST', 'PUT']: |
|
|
|
|
# data = get_serialized_data('tournaments', log.model_name, log.model_id) |
|
|
|
|
# updates[log.model_name][log.model_id] = data |
|
|
|
|
# elif log.operation == 'DELETE': |
|
|
|
|
# deletions[log.model_name].append({'model_id': log.model_id, 'store_id': log.store_id}) |
|
|
|
|
# elif log.operation == 'GRANT_ACCESS': |
|
|
|
|
# model = apps.get_model('tournaments', model_name=log.model_name) |
|
|
|
|
# instance = model.objects.get(id=log.model_id) |
|
|
|
|
# serializer = get_serializer(instance, log.model_name) |
|
|
|
|
# grants[log.model_name][log.model_id] = serializer.data |
|
|
|
|
# self.add_children_recursively(instance, grants) |
|
|
|
|
# self.add_parents_recursively(instance, grants) |
|
|
|
|
# elif log.operation == 'REVOKE_ACCESS': |
|
|
|
|
# revocations[log.model_name].append({ |
|
|
|
|
# 'model_id': log.model_id, |
|
|
|
|
# 'store_id': log.store_id |
|
|
|
|
# }) |
|
|
|
|
# model = apps.get_model('tournaments', model_name=log.model_name) |
|
|
|
|
# try: |
|
|
|
|
# instance = model.objects.get(id=log.model_id) |
|
|
|
|
# self.add_parents_recursively(instance, revocation_parents, minimal=True) |
|
|
|
|
# except model.DoesNotExist: |
|
|
|
|
# pass |
|
|
|
|
# except ObjectDoesNotExist: |
|
|
|
|
# pass |
|
|
|
|
|
|
|
|
|
def get(self, request, *args, **kwargs): |
|
|
|
|
last_update_str = request.query_params.get('last_update') |
|
|
|
|
decoded_last_update = unquote(last_update_str) # Decodes %2B into + |
|
|
|
|
@ -235,58 +226,86 @@ class SynchronizationApi(APIView): |
|
|
|
|
log_query = Q(date__gt=last_update) & Q(users=user) |
|
|
|
|
return ModelLog.objects.filter(log_query).order_by('date') |
|
|
|
|
|
|
|
|
|
def add_children_recursively(self, instance, updates): |
|
|
|
|
""" |
|
|
|
|
Recursively add all children of an instance to the updates dictionary. |
|
|
|
|
""" |
|
|
|
|
# print(f"Instance class: {instance.__class__}") |
|
|
|
|
child_models = instance.get_children_by_model() |
|
|
|
|
class UserDataAccessApi(HierarchyApiView): |
|
|
|
|
permission_classes = [IsAuthenticated] |
|
|
|
|
|
|
|
|
|
for child_model_name, children in child_models.items(): |
|
|
|
|
for child in children: |
|
|
|
|
if isinstance(child, BaseModel): |
|
|
|
|
serializer = get_serializer(child, child_model_name) |
|
|
|
|
# serializer_class = build_serializer_class(child_model_name) |
|
|
|
|
# serializer = serializer_class(child) |
|
|
|
|
updates[child_model_name][child.id] = serializer.data |
|
|
|
|
self.add_children_recursively(child, updates) |
|
|
|
|
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] |
|
|
|
|
|
|
|
|
|
# def add_parents_recursively(self, instance, updates): |
|
|
|
|
# parent_models = instance.get_parents_by_model() |
|
|
|
|
# Convert active_grants dict to list of grant logs |
|
|
|
|
active_grants = list(active_grants.values()) |
|
|
|
|
|
|
|
|
|
# for parent_model_name, parent in parent_models.items(): |
|
|
|
|
# # print(f'parent = {parent_model_name}') |
|
|
|
|
# if isinstance(parent, BaseModel): |
|
|
|
|
# serializer_class = build_serializer_class(parent_model_name) |
|
|
|
|
# serializer = serializer_class(parent) |
|
|
|
|
# updates[parent_model_name][parent.id] = serializer.data |
|
|
|
|
# self.add_parents_recursively(parent, updates) |
|
|
|
|
# Prepare response data structure |
|
|
|
|
data_by_model = defaultdict(dict) |
|
|
|
|
|
|
|
|
|
def add_parents_recursively(self, instance, dictionary, minimal=False): |
|
|
|
|
""" |
|
|
|
|
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() |
|
|
|
|
print(f'>>> grants = {len(active_grants)}') |
|
|
|
|
|
|
|
|
|
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 |
|
|
|
|
for log in active_grants: |
|
|
|
|
try: |
|
|
|
|
model = sync_registry.get_model(log.model_name) |
|
|
|
|
instance = model.objects.get(id=log.model_id) |
|
|
|
|
|
|
|
|
|
# Get the base data |
|
|
|
|
serializer = get_serializer(instance, log.model_name) |
|
|
|
|
data_by_model[log.model_name][log.model_id] = serializer.data |
|
|
|
|
|
|
|
|
|
# Add parents & children recursively |
|
|
|
|
self.add_children_recursively(instance, data_by_model) |
|
|
|
|
self.add_parents_recursively(instance, data_by_model) |
|
|
|
|
|
|
|
|
|
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() |
|
|
|
|
} |
|
|
|
|
else: |
|
|
|
|
serializer = get_serializer(parent, parent_model_name) |
|
|
|
|
# Add full serialized data |
|
|
|
|
# serializer_class = build_serializer_class(parent_model_name) |
|
|
|
|
# serializer = serializer_class(parent) |
|
|
|
|
dictionary[parent_model_name][parent.id] = serializer.data |
|
|
|
|
|
|
|
|
|
self.add_parents_recursively(parent, dictionary, minimal) |
|
|
|
|
print(f'response_data = {response_data}') |
|
|
|
|
|
|
|
|
|
return Response(response_data, status=status.HTTP_200_OK) |
|
|
|
|
|
|
|
|
|
# def _add_children_recursively(self, instance, data_dict): |
|
|
|
|
# """ |
|
|
|
|
# Recursively add all children of an instance to the data dictionary. |
|
|
|
|
# """ |
|
|
|
|
# child_models = instance.get_children_by_model() |
|
|
|
|
|
|
|
|
|
# for child_model_name, children in child_models.items(): |
|
|
|
|
# for child in children: |
|
|
|
|
# if isinstance(child, BaseModel): |
|
|
|
|
# serializer = get_serializer(child, child_model_name) |
|
|
|
|
# data_dict[child_model_name][child.id] = serializer.data |
|
|
|
|
# self._add_children_recursively(child, data_dict) |
|
|
|
|
|
|
|
|
|
# def _add_parents_recursively(self, instance, data_dict): |
|
|
|
|
# """ |
|
|
|
|
# Recursively add all parents of an instance to the data dictionary. |
|
|
|
|
# """ |
|
|
|
|
# parent_models = instance.get_parents_by_model() |
|
|
|
|
|
|
|
|
|
# for parent_model_name, parent in parent_models.items(): |
|
|
|
|
# if isinstance(parent, BaseModel): |
|
|
|
|
# serializer = get_serializer(parent, parent_model_name) |
|
|
|
|
# data_dict[parent_model_name][parent.id] = serializer.data |
|
|
|
|
# self._add_parents_recursively(parent, data_dict) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class DataAccessViewSet(viewsets.ModelViewSet): |
|
|
|
|
queryset = DataAccess.objects.all() |
|
|
|
|
|