|
|
|
|
@ -13,46 +13,26 @@ User = get_user_model() |
|
|
|
|
@receiver([pre_save, pre_delete]) |
|
|
|
|
def synchronization_prepare(sender, instance, created=False, **kwargs): |
|
|
|
|
|
|
|
|
|
# some other classes are excluded in settings_app.py: SYNC_APPS |
|
|
|
|
if not isinstance(instance, BaseModel) and not isinstance(instance, User): |
|
|
|
|
return |
|
|
|
|
|
|
|
|
|
save_model_log_if_possible(instance, kwargs.get('signal'), created) |
|
|
|
|
signal = kwargs.get('signal') |
|
|
|
|
|
|
|
|
|
if kwargs.get('signal') == pre_save: |
|
|
|
|
detect_foreign_key_changes(sender, instance) |
|
|
|
|
|
|
|
|
|
def detect_foreign_key_changes(sender, instance, **kwargs): |
|
|
|
|
if not hasattr(instance, 'pk') or not instance.pk: |
|
|
|
|
return |
|
|
|
|
if not isinstance(instance, BaseModel): |
|
|
|
|
# avoid crash in manage.py createsuperuser + delete user in the admin |
|
|
|
|
if isinstance(instance, User) and (instance._state.db is None or signal == pre_delete): |
|
|
|
|
return |
|
|
|
|
|
|
|
|
|
data_access_list = related_data_access(instance) |
|
|
|
|
if data_access_list: |
|
|
|
|
try: |
|
|
|
|
old_instance = sender.objects.get(pk=instance.pk) |
|
|
|
|
except sender.DoesNotExist: |
|
|
|
|
# some other classes are excluded in settings_app.py: SYNC_APPS |
|
|
|
|
if not isinstance(instance, BaseModel) and not isinstance(instance, User): |
|
|
|
|
return |
|
|
|
|
|
|
|
|
|
# Check foreign key fields |
|
|
|
|
for field in sender._meta.get_fields(): |
|
|
|
|
if isinstance(field, models.ForeignKey) and not field.related_model == User: |
|
|
|
|
print(f'field.related_model = {field.related_model}') |
|
|
|
|
old_value = getattr(old_instance, field.name, None) |
|
|
|
|
new_value = getattr(instance, field.name, None) |
|
|
|
|
if old_value != new_value: |
|
|
|
|
for data_access in data_access_list: |
|
|
|
|
if old_value: |
|
|
|
|
model_name = old_value.__class__.__name__ |
|
|
|
|
save_model_log(data_access.shared_with.all(), 'REVOKE_ACCESS', model_name, old_value.id, old_value.get_store_id()) |
|
|
|
|
if new_value: |
|
|
|
|
model_name = new_value.__class__.__name__ |
|
|
|
|
save_model_log(data_access.shared_with.all(), 'GRANT_ACCESS', model_name, new_value.id, new_value.get_store_id()) |
|
|
|
|
device_id = None |
|
|
|
|
if hasattr(instance, '_device_id'): |
|
|
|
|
device_id = instance._device_id |
|
|
|
|
|
|
|
|
|
# REVOKE access for old_value and GRANT new_value |
|
|
|
|
print(f"Foreign key changed in {sender.__name__}: " |
|
|
|
|
f"{field.name} from {old_value} to {new_value}") |
|
|
|
|
# print(f'kwargs = {kwargs}') |
|
|
|
|
save_model_log_if_possible(instance, signal, created, device_id) |
|
|
|
|
|
|
|
|
|
if signal == pre_save: |
|
|
|
|
# print('yes') |
|
|
|
|
detect_foreign_key_changes(sender, instance, device_id) |
|
|
|
|
|
|
|
|
|
@receiver([post_save, post_delete]) |
|
|
|
|
def synchronization_notifications(sender, instance, created=False, **kwargs): |
|
|
|
|
@ -66,10 +46,10 @@ def synchronization_notifications(sender, instance, created=False, **kwargs): |
|
|
|
|
if not isinstance(instance, BaseModel) and not isinstance(instance, User): |
|
|
|
|
return |
|
|
|
|
|
|
|
|
|
# print(f'*** signals {sender}') |
|
|
|
|
notify_impacted_users(instance, kwargs.get('signal')) |
|
|
|
|
# print(f'*** instance._state.db: {instance._state.db}') |
|
|
|
|
notify_impacted_users(instance) |
|
|
|
|
|
|
|
|
|
def notify_impacted_users(instance, signal): |
|
|
|
|
def notify_impacted_users(instance): |
|
|
|
|
user_ids = set() |
|
|
|
|
# add impacted users |
|
|
|
|
if isinstance(instance, User): |
|
|
|
|
@ -80,23 +60,28 @@ def notify_impacted_users(instance, signal): |
|
|
|
|
user_ids.add(owner.id) |
|
|
|
|
|
|
|
|
|
if isinstance(instance, BaseModel): |
|
|
|
|
if instance._users_to_notify is not None: |
|
|
|
|
if hasattr(instance, '_users_to_notify'): |
|
|
|
|
user_ids.update(instance._users_to_notify) |
|
|
|
|
else: |
|
|
|
|
print('no users to notify') |
|
|
|
|
|
|
|
|
|
device_id = None |
|
|
|
|
if hasattr(instance, '_device_id'): |
|
|
|
|
device_id = instance._device_id |
|
|
|
|
|
|
|
|
|
print(f'notify: {user_ids}') |
|
|
|
|
for user_id in user_ids: |
|
|
|
|
websocket_sender.send_user_message(user_id) |
|
|
|
|
websocket_sender.send_user_message(user_id, device_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): |
|
|
|
|
if isinstance(instance, User): |
|
|
|
|
users = {instance} |
|
|
|
|
else: |
|
|
|
|
users = related_users(instance) |
|
|
|
|
|
|
|
|
|
# print(f'users = {users}') |
|
|
|
|
if users: |
|
|
|
|
if signal == post_save or signal == pre_save: |
|
|
|
|
if created: |
|
|
|
|
@ -118,17 +103,18 @@ def save_model_log_if_possible(instance, signal, created): |
|
|
|
|
|
|
|
|
|
print(f'users to notify: {user_ids}') |
|
|
|
|
instance._users_to_notify = user_ids # save this for the post_save signal |
|
|
|
|
save_model_log(users, operation, model_name, instance.id, store_id) |
|
|
|
|
save_model_log(users, operation, model_name, instance.id, store_id, device_id) |
|
|
|
|
|
|
|
|
|
else: |
|
|
|
|
print('>>> Model Log could not be created because instance.last_updated_by is None') |
|
|
|
|
|
|
|
|
|
def save_model_log(users, model_operation, model_name, model_id, store_id): |
|
|
|
|
def save_model_log(users, model_operation, model_name, model_id, store_id, device_id): |
|
|
|
|
now = timezone.now() |
|
|
|
|
existing_log = ModelLog.objects.filter(users__in=users, model_id=model_id, operation=model_operation).first() |
|
|
|
|
if existing_log: |
|
|
|
|
# print(f'update existing log {existing_log.users} ') |
|
|
|
|
existing_log.date = now |
|
|
|
|
existing_log.device_id = device_id |
|
|
|
|
# existing_log.operation = model_operation |
|
|
|
|
existing_log.save() |
|
|
|
|
existing_log.users.set(users) |
|
|
|
|
@ -139,9 +125,42 @@ def save_model_log(users, model_operation, model_name, model_id, store_id): |
|
|
|
|
model_log.model_name = model_name |
|
|
|
|
model_log.model_id = model_id |
|
|
|
|
model_log.store_id = store_id |
|
|
|
|
model_log.device_id = device_id |
|
|
|
|
model_log.save() |
|
|
|
|
model_log.users.set(users) |
|
|
|
|
|
|
|
|
|
def detect_foreign_key_changes(sender, instance, device_id): |
|
|
|
|
if not hasattr(instance, 'pk') or not instance.pk: |
|
|
|
|
return |
|
|
|
|
if not isinstance(instance, BaseModel): |
|
|
|
|
return |
|
|
|
|
|
|
|
|
|
data_access_list = related_data_access(instance) |
|
|
|
|
if data_access_list: |
|
|
|
|
try: |
|
|
|
|
old_instance = sender.objects.get(pk=instance.pk) |
|
|
|
|
except sender.DoesNotExist: |
|
|
|
|
return |
|
|
|
|
|
|
|
|
|
# Check foreign key fields |
|
|
|
|
for field in sender._meta.get_fields(): |
|
|
|
|
if isinstance(field, models.ForeignKey) and not field.related_model == User: |
|
|
|
|
# print(f'field.related_model = {field.related_model}') |
|
|
|
|
old_value = getattr(old_instance, field.name, None) |
|
|
|
|
new_value = getattr(instance, field.name, None) |
|
|
|
|
if old_value != new_value: |
|
|
|
|
for data_access in data_access_list: |
|
|
|
|
if old_value: |
|
|
|
|
model_name = old_value.__class__.__name__ |
|
|
|
|
save_model_log(data_access.shared_with.all(), 'REVOKE_ACCESS', model_name, old_value.id, old_value.get_store_id(), device_id) |
|
|
|
|
if new_value: |
|
|
|
|
model_name = new_value.__class__.__name__ |
|
|
|
|
save_model_log(data_access.shared_with.all(), 'GRANT_ACCESS', model_name, new_value.id, new_value.get_store_id(), device_id) |
|
|
|
|
|
|
|
|
|
# REVOKE access for old_value and GRANT new_value |
|
|
|
|
print(f"Foreign key changed in {sender.__name__}: " |
|
|
|
|
f"{field.name} from {old_value} to {new_value}") |
|
|
|
|
|
|
|
|
|
def delete_data_access_if_necessary(model_id): |
|
|
|
|
DataAccess.objects.filter(model_id=model_id).delete() |
|
|
|
|
|
|
|
|
|
@ -155,9 +174,12 @@ def handle_shared_with_changes(sender, instance, action, pk_set, **kwargs): |
|
|
|
|
elif action == "post_remove": |
|
|
|
|
instance.create_access_log(users, 'REVOKE_ACCESS') |
|
|
|
|
|
|
|
|
|
device_id = None |
|
|
|
|
if hasattr(instance, '_device_id'): |
|
|
|
|
device_id = instance._device_id |
|
|
|
|
|
|
|
|
|
for user_id in pk_set: |
|
|
|
|
websocket_sender.send_user_message(user_id) |
|
|
|
|
# send_user_message(user_id) |
|
|
|
|
websocket_sender.send_user_message(user_id, device_id) |
|
|
|
|
|
|
|
|
|
@receiver(pre_delete, sender=DataAccess) |
|
|
|
|
def revoke_access_after_delete(sender, instance, **kwargs): |
|
|
|
|
@ -171,10 +193,12 @@ def related_users(instance): |
|
|
|
|
users.add(instance.related_user) |
|
|
|
|
users.add(instance.last_updated_by) |
|
|
|
|
|
|
|
|
|
# look in hierarchy |
|
|
|
|
related_instances = instance.related_instances() |
|
|
|
|
related_users = [ri.related_user for ri in related_instances if isinstance(ri, BaseModel)] |
|
|
|
|
users.update(related_users) |
|
|
|
|
|
|
|
|
|
# look in related DataAccess |
|
|
|
|
data_access_list = instances_related_data_access(instance, related_instances) |
|
|
|
|
for data_access in data_access_list: |
|
|
|
|
users.add(data_access.related_user) |
|
|
|
|
|