|
|
|
@ -10,6 +10,7 @@ from django.db.models import Q |
|
|
|
from django.core.exceptions import ObjectDoesNotExist |
|
|
|
from django.core.exceptions import ObjectDoesNotExist |
|
|
|
|
|
|
|
|
|
|
|
from collections import defaultdict |
|
|
|
from collections import defaultdict |
|
|
|
|
|
|
|
from urllib.parse import unquote |
|
|
|
|
|
|
|
|
|
|
|
from .utils import build_serializer_class, get_data, get_serialized_data |
|
|
|
from .utils import build_serializer_class, get_data, get_serialized_data |
|
|
|
|
|
|
|
|
|
|
|
@ -38,14 +39,19 @@ class DataApi(APIView): |
|
|
|
instance = get_data('tournaments', model_name, data_id) |
|
|
|
instance = get_data('tournaments', model_name, data_id) |
|
|
|
|
|
|
|
|
|
|
|
if model_operation == 'DELETE': |
|
|
|
if model_operation == 'DELETE': |
|
|
|
parent_model, parent_id = instance.get_parent_reference() |
|
|
|
# parent_model, parent_id = instance.get_parent_reference() |
|
|
|
return self.delete_and_save_log(request, data_id, model_operation, model_name, store_id, now) |
|
|
|
instance = get_data('tournaments', model_name, data_id) |
|
|
|
|
|
|
|
instance.delete() |
|
|
|
|
|
|
|
return Response(status=status.HTTP_204_NO_CONTENT) |
|
|
|
|
|
|
|
# return self.delete_and_save_log(request, data_id, model_operation, model_name, store_id, now) |
|
|
|
else: # PUT |
|
|
|
else: # PUT |
|
|
|
serializer = serializer_class(instance, data=data, context={'request': request}) |
|
|
|
serializer = serializer_class(instance, data=data, context={'request': request}) |
|
|
|
if serializer.is_valid(): |
|
|
|
if serializer.is_valid(): |
|
|
|
if instance.last_update <= serializer.validated_data.get('last_update'): |
|
|
|
if instance.last_update <= serializer.validated_data.get('last_update'): |
|
|
|
print('>>> update') |
|
|
|
print('>>> update') |
|
|
|
return self.save_and_create_log(request, serializer, model_operation, model_name, store_id, now) |
|
|
|
serializer.save() |
|
|
|
|
|
|
|
return Response(serializer.data, status=status.HTTP_200_OK) |
|
|
|
|
|
|
|
# return self.save_and_create_log(request, serializer, model_operation, model_name, store_id, now) |
|
|
|
else: |
|
|
|
else: |
|
|
|
print('>>> return 203') |
|
|
|
print('>>> return 203') |
|
|
|
return Response(serializer.data, status=status.HTTP_203_NON_AUTHORITATIVE_INFORMATION) |
|
|
|
return Response(serializer.data, status=status.HTTP_203_NON_AUTHORITATIVE_INFORMATION) |
|
|
|
@ -55,89 +61,36 @@ class DataApi(APIView): |
|
|
|
print('>>> insert') |
|
|
|
print('>>> insert') |
|
|
|
serializer = serializer_class(data=data, context={'request': request}) |
|
|
|
serializer = serializer_class(data=data, context={'request': request}) |
|
|
|
if serializer.is_valid(): |
|
|
|
if serializer.is_valid(): |
|
|
|
return self.save_and_create_log(request, serializer, model_operation, model_name, store_id, now) |
|
|
|
serializer.save() |
|
|
|
else: |
|
|
|
|
|
|
|
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def save_and_create_log(self, request, serializer, model_operation, model_name, store_id, date): |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
instance = serializer.save() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
self.save_model_log( |
|
|
|
|
|
|
|
user=request.user, |
|
|
|
|
|
|
|
model_operation=model_operation, |
|
|
|
|
|
|
|
model_name=model_name, |
|
|
|
|
|
|
|
model_id=instance.id, |
|
|
|
|
|
|
|
store_id=store_id, |
|
|
|
|
|
|
|
date=date |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
self.update_linked_data_access(instance, date) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return Response(serializer.data, status=status.HTTP_201_CREATED) |
|
|
|
return Response(serializer.data, status=status.HTTP_201_CREATED) |
|
|
|
|
|
|
|
# return self.save_and_create_log(request, serializer, model_operation, model_name, store_id, now) |
|
|
|
def delete_and_save_log(self, request, data_id, model_operation, model_name, store_id, date): |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
instance = get_data('tournaments', model_name, data_id) |
|
|
|
|
|
|
|
self.update_linked_data_access(instance, date) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
instance.delete() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# We delete all previous logs linked to the instance because they won't be needed anymore |
|
|
|
|
|
|
|
ModelLog.objects.filter(model_id=data_id).delete() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
self.save_model_log( |
|
|
|
|
|
|
|
user=request.user, |
|
|
|
|
|
|
|
model_operation=model_operation, |
|
|
|
|
|
|
|
model_name=model_name, |
|
|
|
|
|
|
|
model_id=data_id, |
|
|
|
|
|
|
|
store_id=store_id, |
|
|
|
|
|
|
|
date=date |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return Response(status=status.HTTP_204_NO_CONTENT) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def update_linked_data_access(self, instance, date): |
|
|
|
|
|
|
|
related_instances = instance.related_instances() |
|
|
|
|
|
|
|
related_ids = [ri.id for ri in instance.related_instances()] |
|
|
|
|
|
|
|
related_ids.append(instance.id) |
|
|
|
|
|
|
|
data_access_list = DataAccess.objects.filter(model_id__in=related_ids) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for data_access in data_access_list: |
|
|
|
|
|
|
|
data_access.last_hierarchy_update = date |
|
|
|
|
|
|
|
data_access.save() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def save_model_log(self, user, model_operation, model_name, model_id, store_id, date): |
|
|
|
|
|
|
|
existing_log = ModelLog.objects.filter(user=user, model_id=model_id, operation=model_operation).first() |
|
|
|
|
|
|
|
if existing_log: |
|
|
|
|
|
|
|
existing_log.date = date |
|
|
|
|
|
|
|
existing_log.model_operation = model_operation |
|
|
|
|
|
|
|
existing_log.save() |
|
|
|
|
|
|
|
else: |
|
|
|
else: |
|
|
|
model_log = ModelLog() |
|
|
|
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) |
|
|
|
model_log.user = user |
|
|
|
|
|
|
|
model_log.operation = model_operation |
|
|
|
|
|
|
|
model_log.date = date |
|
|
|
|
|
|
|
model_log.model_name = model_name |
|
|
|
|
|
|
|
model_log.model_id = model_id |
|
|
|
|
|
|
|
model_log.store_id = store_id |
|
|
|
|
|
|
|
model_log.save() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get(self, request, *args, **kwargs): |
|
|
|
def get(self, request, *args, **kwargs): |
|
|
|
last_update = request.query_params.get('last_update') |
|
|
|
last_update_str = request.query_params.get('last_update') |
|
|
|
if not last_update: |
|
|
|
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) |
|
|
|
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}') |
|
|
|
print(f'/data GET: {last_update}') |
|
|
|
|
|
|
|
|
|
|
|
logs = self.query_model_logs(last_update, request.user) |
|
|
|
logs = self.query_model_logs(last_update, request.user) |
|
|
|
|
|
|
|
print(f'>>> log count = {len(logs)}') |
|
|
|
|
|
|
|
|
|
|
|
updates = defaultdict(dict) |
|
|
|
updates = defaultdict(dict) |
|
|
|
deletions = defaultdict(list) |
|
|
|
deletions = defaultdict(list) |
|
|
|
|
|
|
|
|
|
|
|
# print(f'>>> log count = {len(logs)}') |
|
|
|
last_log_date = None |
|
|
|
|
|
|
|
|
|
|
|
for log in logs: |
|
|
|
for log in logs: |
|
|
|
|
|
|
|
last_log_date = log.date |
|
|
|
try: |
|
|
|
try: |
|
|
|
if log.operation in ['POST', 'PUT']: |
|
|
|
if log.operation in ['POST', 'PUT']: |
|
|
|
data = get_serialized_data('tournaments', log.model_name, log.model_id) |
|
|
|
data = get_serialized_data('tournaments', log.model_name, log.model_id) |
|
|
|
@ -156,7 +109,7 @@ class DataApi(APIView): |
|
|
|
# instance = model.objects.get(id=log.model_id) |
|
|
|
# instance = model.objects.get(id=log.model_id) |
|
|
|
self.add_children_recursively(instance, updates) |
|
|
|
self.add_children_recursively(instance, updates) |
|
|
|
self.add_parents_recursively(instance, updates) |
|
|
|
self.add_parents_recursively(instance, updates) |
|
|
|
elif log.operation == 'delete data access signal': |
|
|
|
elif log.operation == 'REVOKE_ACCESS': |
|
|
|
print(f'revoke access {log.model_id} - {log.store_id}') |
|
|
|
print(f'revoke access {log.model_id} - {log.store_id}') |
|
|
|
deletions[log.model_name].append({'model_id': log.model_id, 'store_id': log.store_id}) |
|
|
|
deletions[log.model_name].append({'model_id': log.model_id, 'store_id': log.store_id}) |
|
|
|
except ObjectDoesNotExist: |
|
|
|
except ObjectDoesNotExist: |
|
|
|
@ -172,38 +125,37 @@ class DataApi(APIView): |
|
|
|
|
|
|
|
|
|
|
|
response_data = { |
|
|
|
response_data = { |
|
|
|
"updates": dict(updates), |
|
|
|
"updates": dict(updates), |
|
|
|
"deletions": dict(deletions) |
|
|
|
"deletions": dict(deletions), |
|
|
|
|
|
|
|
"date": last_log_date |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
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): |
|
|
|
def query_model_logs(self, last_update, user): |
|
|
|
try: |
|
|
|
# print(f'last_update = {last_update}') |
|
|
|
last_update = timezone.datetime.fromisoformat(last_update) |
|
|
|
log_query = Q(date__gt=last_update) & Q(users=user) |
|
|
|
except ValueError: |
|
|
|
return ModelLog.objects.filter(log_query).order_by('date') |
|
|
|
return Response({"error": f"Invalid date format for last_update: {last_update}"}, status=status.HTTP_400_BAD_REQUEST) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# get recently modified DataAccess |
|
|
|
# get recently modified DataAccess |
|
|
|
data_access_query = Q(last_hierarchy_update__gt=last_update) & (Q(shared_with__in=[user]) | Q(owner=user)) |
|
|
|
# data_access_query = Q(last_hierarchy_update__gt=last_update) & (Q(shared_with__in=[user]) | Q(owner=user)) |
|
|
|
data_access_list = DataAccess.objects.filter(data_access_query) #.values_list('model_id', flat=True) |
|
|
|
# data_access_list = DataAccess.objects.filter(data_access_query) #.values_list('model_id', flat=True) |
|
|
|
|
|
|
|
|
|
|
|
# print(f'>> da count = {len(data_access_list)}') |
|
|
|
# print(f'>> da count = {len(data_access_list)}') |
|
|
|
|
|
|
|
|
|
|
|
# get ids of all recently updated related instances of each shared data |
|
|
|
# # get ids of all recently updated related instances of each shared data |
|
|
|
model_ids = [] |
|
|
|
# model_ids = [] |
|
|
|
for data_access in data_access_list: |
|
|
|
# for data_access in data_access_list: |
|
|
|
model_ids.append(data_access.model_id) |
|
|
|
# model_ids.append(data_access.model_id) |
|
|
|
try: |
|
|
|
# try: |
|
|
|
instance = get_data('tournaments', data_access.model_name, data_access.model_id) |
|
|
|
# instance = get_data('tournaments', data_access.model_name, data_access.model_id) |
|
|
|
related_instances = instance.related_instances() |
|
|
|
# related_instances = instance.related_instances() |
|
|
|
related_ids = [ri.id for ri in instance.related_instances() if ri.last_update > last_update] |
|
|
|
# related_ids = [ri.id for ri in instance.related_instances() if ri.last_update > last_update] |
|
|
|
model_ids.extend(related_ids) |
|
|
|
# model_ids.extend(related_ids) |
|
|
|
except ObjectDoesNotExist: |
|
|
|
# except ObjectDoesNotExist: |
|
|
|
pass |
|
|
|
# pass |
|
|
|
|
|
|
|
|
|
|
|
# get all ModelLog list since the last_update, from the user and from the data he has access to |
|
|
|
# get all ModelLog list since the last_update, from the user and from the data he has access to |
|
|
|
log_query = Q(date__gt=last_update) & (Q(user=user) | Q(model_id__in=model_ids)) |
|
|
|
|
|
|
|
return ModelLog.objects.filter(log_query).order_by('date') |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def add_children_recursively(self, instance, updates): |
|
|
|
def add_children_recursively(self, instance, updates): |
|
|
|
""" |
|
|
|
""" |
|
|
|
|