You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
648 lines
28 KiB
648 lines
28 KiB
# from django.shortcuts import render
|
|
import re
|
|
import json
|
|
from .serializers import DataAccessSerializer
|
|
|
|
from rest_framework import viewsets
|
|
from rest_framework.views import APIView
|
|
from rest_framework.permissions import IsAuthenticated
|
|
from rest_framework.response import Response
|
|
from rest_framework import status
|
|
|
|
from django.utils import timezone
|
|
from django.core.exceptions import ObjectDoesNotExist
|
|
from django.db.models import Q
|
|
|
|
from collections import defaultdict
|
|
from urllib.parse import unquote
|
|
|
|
from .utils import get_serializer, build_serializer_class, get_data, get_serialized_data, HierarchyOrganizer
|
|
|
|
from .models import ModelLog, BaseModel, SideStoreModel, DataAccess
|
|
|
|
from .registry import model_registry, device_registry
|
|
|
|
# class HierarchyApiView(APIView):
|
|
|
|
def add_children_recursively(instance, updates):
|
|
"""
|
|
Recursively add all children of an instance to the updates 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)
|
|
updates[child_model_name][child.id] = serializer.data
|
|
add_children_recursively(child, updates)
|
|
|
|
def add_parents_with_hierarchy_organizer(instance, hierarchy_organizer, current_level=0):
|
|
"""
|
|
Recursively add all parents of an instance to the hierarchy organizer.
|
|
Parents are added at a higher level than their children.
|
|
"""
|
|
parent_models = instance.get_parents_by_model()
|
|
|
|
for parent_model_name, parent in parent_models.items():
|
|
if isinstance(parent, BaseModel):
|
|
store_id = None
|
|
if isinstance(parent, SideStoreModel):
|
|
store_id = parent.store_id
|
|
|
|
parent_data = {
|
|
'model_id': parent.id,
|
|
'store_id': store_id
|
|
}
|
|
|
|
# Add parent at the next level
|
|
hierarchy_organizer.add_item(parent_model_name, parent_data, current_level + 1)
|
|
|
|
# Recursively process parent's parents
|
|
add_parents_with_hierarchy_organizer(parent, hierarchy_organizer, current_level + 1)
|
|
|
|
def add_parents_recursively(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)
|
|
dictionary[parent_model_name][parent.id] = serializer.data
|
|
|
|
add_parents_recursively(parent, dictionary, minimal)
|
|
|
|
class SynchronizationApi(APIView):
|
|
permission_classes = [IsAuthenticated]
|
|
|
|
def post(self, request, *args, **kwargs):
|
|
|
|
device_id = request.data.get('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
|
|
message = None
|
|
model_operation = op.get('operation')
|
|
model_name = op.get('model_name')
|
|
data = op.get('data')
|
|
data_id = data.get('id')
|
|
device_registry.register(data_id, device_id)
|
|
# print(f'*** YEAH: {model_operation} : {model_name}')
|
|
|
|
try:
|
|
# print(f'{model_operation} : {model_name}, id = {data['id']}')
|
|
|
|
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
|
|
|
|
# model = apps.get_model(app_label='tournaments', model_name=model_name)
|
|
model = model_registry.get_model(model_name)
|
|
|
|
if model_operation == 'POST':
|
|
|
|
if data['related_user'] is None: # affect related_user is necessary
|
|
data['related_user'] = request.user.id
|
|
|
|
serializer = serializer_class(data=data, context={'request': request})
|
|
|
|
if serializer.is_valid():
|
|
instance = serializer.save()
|
|
result = serializer.data
|
|
response_status = status.HTTP_201_CREATED
|
|
else:
|
|
print(f'Data invalid ! {serializer.errors}')
|
|
message = json.dumps(serializer.errors)
|
|
response_status = status.HTTP_400_BAD_REQUEST
|
|
elif model_operation == 'PUT':
|
|
instance = get_data(model_name, data_id)
|
|
serializer = serializer_class(instance, data=data, context={'request': request})
|
|
if serializer.is_valid():
|
|
if instance.last_update <= serializer.validated_data.get('last_update'):
|
|
serializer.save()
|
|
result = serializer.data
|
|
response_status = status.HTTP_200_OK
|
|
else:
|
|
result = serializer.data
|
|
response_status = status.HTTP_203_NON_AUTHORITATIVE_INFORMATION
|
|
else:
|
|
print(f'Data invalid ! {serializer.errors}')
|
|
response_status = status.HTTP_400_BAD_REQUEST
|
|
elif model_operation == 'DELETE':
|
|
try:
|
|
instance = get_data(model_name, data_id)
|
|
try:
|
|
instance.delete()
|
|
response_status = status.HTTP_204_NO_CONTENT
|
|
except model.DoesNotExist: # a relationship was not found, not good
|
|
response_status = status.HTTP_400_BAD_REQUEST
|
|
except model.DoesNotExist: # the data was not found, it's ok
|
|
response_status = status.HTTP_208_ALREADY_REPORTED
|
|
print(f'{model_name} DoesNotExist, id: {data_id}')
|
|
|
|
except Exception as e:
|
|
response_status = status.HTTP_400_BAD_REQUEST
|
|
import traceback
|
|
tb = traceback.extract_tb(e.__traceback__)
|
|
file_name = tb[-1].filename
|
|
line_number = tb[-1].lineno
|
|
print(f'OTHER exception on {model_operation} {model_name} : {str(e)}, type: {type(e)}, id: {data.get('id')}, file: {file_name}, line: {line_number}')
|
|
message = str(e)
|
|
|
|
results.append({
|
|
'api_call_id': op.get('api_call_id'),
|
|
'status': response_status,
|
|
'data': result,
|
|
'message': message
|
|
})
|
|
|
|
# print(f"sync POST completed for models: {models}")
|
|
|
|
return Response({
|
|
'results': results
|
|
}, status=207) # Multi-Status
|
|
|
|
# def get(self, request, *args, **kwargs):
|
|
# last_update_str = request.query_params.get('last_update')
|
|
# device_id = request.query_params.get('device_id')
|
|
|
|
# # print(f'last_update_str = {last_update_str}')
|
|
# decoded_last_update = unquote(last_update_str) # Decodes %2B into +
|
|
|
|
# # print(f'last_update_str = {last_update_str}')
|
|
# # print(f'decoded_last_update = {decoded_last_update}')
|
|
# 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)
|
|
|
|
# # print(f'/data GET: {last_update}')
|
|
|
|
# logs = self.query_model_logs(last_update, request.user, device_id)
|
|
# print(f'>>> log count = {len(logs)}')
|
|
|
|
# updates = defaultdict(dict)
|
|
# deletions = defaultdict(list)
|
|
# grants = defaultdict(dict)
|
|
# revocations = defaultdict(list)
|
|
# revocations_parents_organizer = HierarchyOrganizer()
|
|
|
|
# # revocated_parents = defaultdict(dict)
|
|
|
|
# last_log_date = None
|
|
# for log in logs:
|
|
|
|
# # log.retrieved()
|
|
# # log.save()
|
|
|
|
# # print(f'log date = {log.date}')
|
|
# last_log_date = log.date
|
|
# try:
|
|
# if log.operation in ['POST', 'PUT']:
|
|
# data = get_serialized_data(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 = model_registry.get_model(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':
|
|
# print(f'revoke access {log.model_id} - {log.store_id}')
|
|
# revocations[log.model_name].append({
|
|
# 'model_id': log.model_id,
|
|
# 'store_id': log.store_id
|
|
# })
|
|
|
|
# # Get the model instance and add its parents to hierarchy
|
|
# model = model_registry.get_model(log.model_name)
|
|
# try:
|
|
# instance = model.objects.get(id=log.model_id)
|
|
# self.add_parents_with_hierarchy_organizer(instance, revocations_parents_organizer)
|
|
# except model.DoesNotExist:
|
|
# pass
|
|
# except ObjectDoesNotExist:
|
|
# pass
|
|
|
|
# # Convert updates dict to list for each model
|
|
# for model_name in updates:
|
|
# updates[model_name] = list(updates[model_name].values())
|
|
|
|
# # Convert deletions set to list for each model
|
|
# for model_name in deletions:
|
|
# deletions[model_name] = deletions[model_name]
|
|
|
|
# for model_name in grants:
|
|
# grants[model_name] = list(grants[model_name].values())
|
|
|
|
# # for model_name in revocation_parents:
|
|
# # revocation_parents[model_name] = list(revocation_parents[model_name].values())
|
|
|
|
# response_data = {
|
|
# "updates": dict(updates),
|
|
# "deletions": dict(deletions),
|
|
# "grants": dict(grants),
|
|
# "revocations": dict(revocations),
|
|
# "revocation_parents": revocations_parents_organizer.get_organized_data(),
|
|
# "date": last_log_date
|
|
# }
|
|
|
|
# # 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)
|
|
|
|
## GET
|
|
|
|
def get(self, request, *args, **kwargs):
|
|
last_update_str = request.query_params.get('last_update')
|
|
device_id = request.query_params.get('device_id')
|
|
|
|
decoded_last_update = unquote(last_update_str) # Decodes %2B into +
|
|
|
|
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, device_id)
|
|
print(f'>>> log count = {len(logs)}')
|
|
|
|
# Process all logs and get response data
|
|
result = LogProcessingResult()
|
|
result.process_logs(logs)
|
|
response_data = result.get_response_data()
|
|
|
|
return Response(response_data, status=status.HTTP_200_OK)
|
|
|
|
# def process_logs(self, logs):
|
|
# """Process logs to collect basic operations and handle grant/revoke efficiently."""
|
|
# # Create an instance of the LogProcessingResult class
|
|
# result = LogProcessingResult()
|
|
# last_log_date = None
|
|
|
|
# for log in logs:
|
|
# last_log_date = log.date
|
|
# try:
|
|
# if log.operation in ['POST', 'PUT']:
|
|
# data = get_serialized_data(log.model_name, log.model_id)
|
|
# result.updates[log.model_name][log.model_id] = data
|
|
# elif log.operation == 'DELETE':
|
|
# result.deletions[log.model_name].append({'model_id': log.model_id, 'store_id': log.store_id})
|
|
# elif log.operation == 'GRANT_ACCESS':
|
|
# # Remove any existing revocations for this model_id
|
|
# self._remove_revocation(result.revoke_info, log.model_name, log.model_id)
|
|
|
|
# # Add to grant instances if not already there
|
|
# if log.model_id not in result.grant_instances[log.model_name]:
|
|
# model = model_registry.get_model(log.model_name)
|
|
# try:
|
|
# instance = model.objects.get(id=log.model_id)
|
|
# result.grant_instances[log.model_name][log.model_id] = instance
|
|
# except model.DoesNotExist:
|
|
# pass
|
|
# elif log.operation == 'REVOKE_ACCESS':
|
|
# print(f'revoke access {log.model_id} - {log.store_id}')
|
|
|
|
# # Remove any existing grants for this model_id
|
|
# self._remove_grant(result.grant_instances, log.model_name, log.model_id)
|
|
|
|
# # Add to revocations
|
|
# result.revoke_info[log.model_name].append({
|
|
# 'model_id': log.model_id,
|
|
# 'store_id': log.store_id
|
|
# })
|
|
# elif log.operation == 'RELATIONSHIP_SET':
|
|
# data = get_serialized_data(log.model_name, log.model_id)
|
|
# result.relationship_sets[log.model_name][log.model_id] = data
|
|
# elif log.operation == 'RELATIONSHIP_REMOVED':
|
|
# result.relationship_removals[log.model_name].append({
|
|
# 'model_id': log.model_id,
|
|
# 'store_id': log.store_id
|
|
# })
|
|
# elif log.operation == 'SHARED_RELATIONSHIP_SET':
|
|
# data = get_serialized_data(log.model_name, log.model_id)
|
|
# result.shared_relationship_sets[log.model_name][log.model_id] = data
|
|
# elif log.operation == 'SHARED_RELATIONSHIP_REMOVED':
|
|
# result.shared_relationship_removals[log.model_name].append({
|
|
# 'model_id': log.model_id,
|
|
# 'store_id': log.store_id
|
|
# })
|
|
# except ObjectDoesNotExist:
|
|
# pass
|
|
|
|
# # Convert updates dict to list for each model
|
|
# for model_name in result.updates:
|
|
# result.updates[model_name] = list(result.updates[model_name].values())
|
|
|
|
# return result, last_log_date
|
|
|
|
# def _remove_revocation(self, revoke_info, model_name, model_id):
|
|
# """Remove any revocation entries for the specified model and ID."""
|
|
# if model_name in revoke_info:
|
|
# revoke_info[model_name] = [
|
|
# r for r in revoke_info[model_name]
|
|
# if r['model_id'] != model_id
|
|
# ]
|
|
# # Clean up empty lists
|
|
# if not revoke_info[model_name]:
|
|
# del revoke_info[model_name]
|
|
|
|
# def _remove_grant(self, grant_instances, model_name, model_id):
|
|
# """Remove any grant entries for the specified model and ID."""
|
|
# if model_name in grant_instances and model_id in grant_instances[model_name]:
|
|
# del grant_instances[model_name][model_id]
|
|
# # Clean up empty dictionaries
|
|
# if not grant_instances[model_name]:
|
|
# del grant_instances[model_name]
|
|
|
|
# def process_grants(self, grant_instances):
|
|
# """Process grants and their hierarchies."""
|
|
# grants = defaultdict(dict)
|
|
|
|
# # Process each grant instance
|
|
# for model_name, instances in grant_instances.items():
|
|
# for model_id, instance in instances.items():
|
|
# serializer = get_serializer(instance, model_name)
|
|
# grants[model_name][model_id] = serializer.data
|
|
|
|
# # Add hierarchies only once per instance
|
|
# self.add_children_recursively(instance, grants)
|
|
# self.add_parents_recursively(instance, grants)
|
|
|
|
# # Convert to lists
|
|
# for model_name in grants:
|
|
# grants[model_name] = list(grants[model_name].values())
|
|
|
|
# return grants
|
|
|
|
# def process_revocations(self, revoke_info):
|
|
# """Process revocations and their hierarchies."""
|
|
# revocations = defaultdict(list)
|
|
# revocations_parents_organizer = HierarchyOrganizer()
|
|
|
|
# # First, collect all revocations
|
|
# for model_name, items in revoke_info.items():
|
|
# revocations[model_name].extend(items)
|
|
|
|
# # Process parent hierarchies for each revoked item
|
|
# model = model_registry.get_model(model_name)
|
|
# for item in items:
|
|
# try:
|
|
# instance = model.objects.get(id=item['model_id'])
|
|
# self.add_parents_with_hierarchy_organizer(instance, revocations_parents_organizer)
|
|
# except model.DoesNotExist:
|
|
# pass
|
|
|
|
# return revocations, revocations_parents_organizer
|
|
|
|
def query_model_logs(self, last_update, user, device_id):
|
|
log_query = Q(date__gt=last_update, user=user)
|
|
if device_id:
|
|
log_query &= ~Q(device_id=device_id) # exclude query
|
|
return ModelLog.objects.filter(log_query).order_by('date')
|
|
|
|
# class LogProcessingResult:
|
|
# """Class to hold all the results from log processing."""
|
|
|
|
# def __init__(self):
|
|
# # Initialize all the collections
|
|
# self.updates = defaultdict(dict)
|
|
# self.deletions = defaultdict(list)
|
|
# self.grant_instances = defaultdict(dict) # {model_name: {model_id: instance}}
|
|
# self.revoke_info = defaultdict(list) # {model_name: [{model_id, store_id}]}
|
|
# self.relationship_sets = defaultdict(dict)
|
|
# self.relationship_removals = defaultdict(list)
|
|
# self.shared_relationship_sets = defaultdict(dict)
|
|
# self.shared_relationship_removals = defaultdict(list)
|
|
|
|
class LogProcessingResult:
|
|
"""Class to handle all log processing and organize the results."""
|
|
|
|
def __init__(self):
|
|
# Initialize all the collections
|
|
self.updates = defaultdict(dict)
|
|
self.deletions = defaultdict(list)
|
|
self.grant_instances = defaultdict(dict) # {model_name: {model_id: instance}}
|
|
self.revoke_info = defaultdict(list) # {model_name: [{model_id, store_id}]}
|
|
self.relationship_sets = defaultdict(dict)
|
|
self.relationship_removals = defaultdict(list)
|
|
self.shared_relationship_sets = defaultdict(dict)
|
|
self.shared_relationship_removals = defaultdict(list)
|
|
self.last_log_date = None
|
|
|
|
def process_logs(self, logs):
|
|
"""Process logs to collect basic operations and handle grant/revoke efficiently."""
|
|
for log in logs:
|
|
self.last_log_date = log.date
|
|
try:
|
|
if log.operation in ['POST', 'PUT']:
|
|
data = get_serialized_data(log.model_name, log.model_id)
|
|
self.updates[log.model_name][log.model_id] = data
|
|
elif log.operation == 'DELETE':
|
|
self.deletions[log.model_name].append({'model_id': log.model_id, 'store_id': log.store_id})
|
|
elif log.operation == 'GRANT_ACCESS':
|
|
# Remove any existing revocations for this model_id
|
|
self._remove_revocation(log.model_name, log.model_id)
|
|
|
|
# Add to grant instances if not already there
|
|
if log.model_id not in self.grant_instances[log.model_name]:
|
|
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
|
|
except model.DoesNotExist:
|
|
pass
|
|
elif log.operation == 'REVOKE_ACCESS':
|
|
print(f'revoke access {log.model_id} - {log.store_id}')
|
|
|
|
# Remove any existing grants for this model_id
|
|
self._remove_grant(log.model_name, log.model_id)
|
|
|
|
# Add to revocations
|
|
self.revoke_info[log.model_name].append({
|
|
'model_id': log.model_id,
|
|
'store_id': log.store_id
|
|
})
|
|
elif log.operation == 'RELATIONSHIP_SET':
|
|
data = get_serialized_data(log.model_name, log.model_id)
|
|
self.relationship_sets[log.model_name][log.model_id] = data
|
|
elif log.operation == 'RELATIONSHIP_REMOVED':
|
|
self.relationship_removals[log.model_name].append({
|
|
'model_id': log.model_id,
|
|
'store_id': log.store_id
|
|
})
|
|
elif log.operation == 'SHARED_RELATIONSHIP_SET':
|
|
data = get_serialized_data(log.model_name, log.model_id)
|
|
self.shared_relationship_sets[log.model_name][log.model_id] = data
|
|
elif log.operation == 'SHARED_RELATIONSHIP_REMOVED':
|
|
self.shared_relationship_removals[log.model_name].append({
|
|
'model_id': log.model_id,
|
|
'store_id': log.store_id
|
|
})
|
|
except ObjectDoesNotExist:
|
|
pass
|
|
|
|
# Convert updates dict to list for each model
|
|
for model_name in self.updates:
|
|
self.updates[model_name] = list(self.updates[model_name].values())
|
|
|
|
# return self
|
|
|
|
def _remove_revocation(self, model_name, model_id):
|
|
"""Remove any revocation entries for the specified model and ID."""
|
|
if model_name in self.revoke_info:
|
|
self.revoke_info[model_name] = [
|
|
r for r in self.revoke_info[model_name]
|
|
if r['model_id'] != model_id
|
|
]
|
|
# Clean up empty lists
|
|
if not self.revoke_info[model_name]:
|
|
del self.revoke_info[model_name]
|
|
|
|
def _remove_grant(self, model_name, model_id):
|
|
"""Remove any grant entries for the specified model and ID."""
|
|
if model_name in self.grant_instances and model_id in self.grant_instances[model_name]:
|
|
del self.grant_instances[model_name][model_id]
|
|
# Clean up empty dictionaries
|
|
if not self.grant_instances[model_name]:
|
|
del self.grant_instances[model_name]
|
|
|
|
def process_grants(self):
|
|
"""Process grants and their hierarchies."""
|
|
grants = defaultdict(dict)
|
|
|
|
# Process each grant instance
|
|
for model_name, instances in self.grant_instances.items():
|
|
for model_id, instance in instances.items():
|
|
serializer = get_serializer(instance, model_name)
|
|
grants[model_name][model_id] = serializer.data
|
|
|
|
# Add hierarchies only once per instance
|
|
add_children_recursively(instance, grants)
|
|
add_parents_recursively(instance, grants)
|
|
|
|
# Convert to lists
|
|
for model_name in grants:
|
|
grants[model_name] = list(grants[model_name].values())
|
|
|
|
return grants
|
|
|
|
def process_revocations(self):
|
|
"""Process revocations and their hierarchies."""
|
|
revocations = defaultdict(list)
|
|
revocations_parents_organizer = HierarchyOrganizer()
|
|
|
|
# First, collect all revocations
|
|
for model_name, items in self.revoke_info.items():
|
|
revocations[model_name].extend(items)
|
|
|
|
# Process parent hierarchies for each revoked item
|
|
model = model_registry.get_model(model_name)
|
|
for item in items:
|
|
try:
|
|
instance = model.objects.get(id=item['model_id'])
|
|
add_parents_with_hierarchy_organizer(instance, revocations_parents_organizer)
|
|
except model.DoesNotExist:
|
|
pass
|
|
|
|
return revocations, revocations_parents_organizer
|
|
|
|
def get_response_data(self):
|
|
"""Construct the complete response data structure."""
|
|
grants = self.process_grants()
|
|
revocations, revocations_parents_organizer = self.process_revocations()
|
|
|
|
return {
|
|
"updates": dict(self.updates),
|
|
"deletions": dict(self.deletions),
|
|
"grants": dict(grants),
|
|
"revocations": dict(revocations),
|
|
"revocation_parents": revocations_parents_organizer.get_organized_data(),
|
|
"relationship_sets": dict(self.relationship_sets),
|
|
"relationship_removals": dict(self.relationship_removals),
|
|
"shared_relationship_sets": dict(self.shared_relationship_sets),
|
|
"shared_relationship_removals": dict(self.shared_relationship_removals),
|
|
"date": self.last_log_date
|
|
}
|
|
|
|
class UserDataAccessApi(APIView):
|
|
permission_classes = [IsAuthenticated]
|
|
|
|
def get(self, request, *args, **kwargs):
|
|
|
|
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
|
|
|
|
data_by_model = defaultdict(dict)
|
|
|
|
print(f'>>> grants = {len(data_access_objects)}')
|
|
|
|
for data_access in data_access_objects:
|
|
try:
|
|
model = model_registry.get_model(data_access.model_name)
|
|
instance = model.objects.get(id=data_access.model_id)
|
|
|
|
# Get the base 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
|
|
add_children_recursively(instance, data_by_model)
|
|
add_parents_recursively(instance, data_by_model)
|
|
|
|
except ObjectDoesNotExist:
|
|
continue
|
|
|
|
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)
|
|
|
|
class DataAccessViewSet(viewsets.ModelViewSet):
|
|
queryset = DataAccess.objects.all()
|
|
serializer_class = DataAccessSerializer
|
|
|
|
def get_queryset(self):
|
|
if self.request.user:
|
|
return self.queryset.filter(Q(related_user=self.request.user) | Q(shared_with__in=[self.request.user]))
|
|
return []
|
|
|