app.memos.signals
1from django.db.models.signals import post_save 2from django.dispatch import receiver 3from app.infrastructure.models import ApproverHierarchy, ApproverLevel 4from app.infrastructure.serializers import ApproverLevelSerializer 5from .models import MemoEvents, MemoSnapshot 6from accounts.fcm_service import * 7 8@receiver(post_save, sender=MemoEvents) 9def create_memo_snapshot(sender, instance: MemoEvents, created: bool, **kwargs): 10 """ 11 Signal handler to create or update MemoSnapshot whenever a MemoEvents instance is saved. 12 13 Handles different event types: 14 - "worker_status_delete": Marks the relevant worker status as deleted in the latest snapshot. 15 - "updated": Updates the latest snapshot's info with the event payload. 16 - "approved"/"rejected": Updates approval status in the latest snapshot's hierarchy and approval_history. 17 - Worker status events ("attended", "completed", "incomplete", "tagged"): Appends worker status to the latest snapshot and updates status fields. 18 - Other events: Creates a new MemoSnapshot capturing user context, complaint info, hierarchy, and approval history. 19 20 Also triggers FCM notifications for memo creation and tagging. 21 """ 22 if instance.event_type == "worker_status_delete": 23 # Mark worker status as deleted in the latest snapshot. 24 latest_snapshot = ( 25 MemoSnapshot.objects.filter(memo=instance.memo).order_by('-timestamp').first() 26 ) 27 comments = latest_snapshot.worker_status or [] 28 for comment in comments: 29 if comment.get('event_id') == instance.payload.get('event_id'): 30 comment.update({'deleted': True}) 31 break 32 latest_snapshot.worker_status = comments 33 latest_snapshot.save() 34 return 35 36 if not created: 37 # Only snapshot on new events – updates to the same event record should be ignored. 38 return 39 40 # For 'updated' events, update the latest snapshot's info. 41 if instance.event_type == 'updated': 42 latest_snapshot = ( 43 MemoSnapshot.objects.filter(memo=instance.memo).order_by('-timestamp').first() 44 ) 45 if latest_snapshot: 46 updated_info = {**latest_snapshot.info, **instance.payload} 47 latest_snapshot.info = updated_info 48 latest_snapshot.save() 49 return 50 51 # For approval events, update hierarchy and approval history in the latest snapshot. 52 if instance.event_type in ['approved', 'rejected']: 53 latest_snapshot = ( 54 MemoSnapshot.objects.filter(memo=instance.memo).order_by('-timestamp').first() 55 ) 56 if latest_snapshot: 57 approval_history = latest_snapshot.hierarchy or [] 58 role_id = instance.event_by.role.id 59 event_type = instance.event_type 60 event_by = instance.event_by 61 event_timestamp = instance.event_timestamp.isoformat() 62 63 for level in approval_history: 64 if level.get('role') == role_id: 65 is_approved = event_type == 'approved' 66 is_rejected = event_type == 'rejected' 67 hist = latest_snapshot.approval_history 68 if is_approved: 69 hist.update({ 70 f"{role_id}_approved": True, 71 }) 72 else: 73 hist.update({ 74 f"{role_id}_approved": False, 75 }) 76 level.update({ 77 'approved': is_approved, 78 'rejected': is_rejected, 79 'by': event_by.id, 80 'by_phone': event_by.phone_number, 81 'approved_by': event_by.institution_id if is_approved else None, 82 'rejected_by': event_by.institution_id if is_rejected else None, 83 'approved_at': event_timestamp if is_approved else None, 84 'rejected_at': event_timestamp if is_rejected else None, 85 }) 86 break 87 latest_snapshot.hierarchy = approval_history 88 latest_snapshot.save() 89 return 90 91 # For worker status events, append status and update info in the latest snapshot. 92 if instance.event_type in ['attended','completed','incomplete','tagged']: 93 memo_snapshot = MemoSnapshot.objects.filter(memo=instance.memo).order_by('-timestamp').first() 94 if memo_snapshot: 95 comments = memo_snapshot.worker_status or [] 96 comments.append({**instance.payload,"event_id":instance.id,"deleted":False,"timestamp":instance.event_timestamp.isoformat()}) 97 memo_snapshot.worker_status = comments 98 if instance.event_type == 'completed': 99 memo_snapshot.info.update({"status": "completed"}) 100 memo_snapshot.info.update({"display_status":instance.event_type}) 101 memo_snapshot.save() 102 if instance.event_type == 'tagged': 103 memo_tagged_notification(memo_snapshot.info,instance.payload.get("tagged_role_name"),instance.payload.get("tagged_role_id") ,memo_snapshot.memo.memoId) 104 return 105 106 # For all other event types, create a fresh snapshot. 107 metadata = instance.metadata or {} 108 user_meta = metadata.get("user", metadata) 109 info = { 110 "status": "ongoing", 111 "user_role_id": user_meta.get("role_id"), 112 "user_role_name": user_meta.get("role"), 113 "current_block_id": user_meta.get("current_block_id",'null'), 114 "current_block_name": user_meta.get("current_block_name",'null'), 115 "current_ward_id": user_meta.get("current_ward_id",'null'), 116 "current_ward_name": user_meta.get("current_ward_name",'null'), 117 "current_floor": user_meta.get("floor",'null'), 118 "user_id": instance.event_by.id, 119 "institution_id": getattr(instance.event_by, "institution_id", None), 120 "phone_number": instance.event_by.phone_number, 121 } 122 payload = instance.payload or {} 123 info.update({ 124 "complaint": payload.get("complaint"), 125 "department": payload.get("department"), 126 "tagged_role_id": payload.get("tagged_role_id"), 127 "tagged_role_name": payload.get("tagged_role_name"), 128 }) 129 130 # Apply active hierarchy to the snapshot 131 hierarchy = ApproverHierarchy.objects.filter(is_active=True, levels__role__hospital=instance.event_by.hospital).order_by('-created_at').first() 132 levels = [] 133 hist = {} 134 if hierarchy: 135 temp = hierarchy.levels.all().order_by('priority') 136 levels = ApproverLevelSerializer(temp, many=True).data 137 for level in levels: 138 level['approved'] = False 139 hist[f"{level['role']}_approved"] = False 140 141 # Persist snapshot 142 snapshot = MemoSnapshot.objects.create( 143 memo=instance.memo, 144 info=info, 145 hierarchy=levels, 146 approval_history=hist or {}, 147 worker_status=payload.get("worker_status", []), 148 ) 149 150 # Send notification for memo creation 151 memo_created_notification(snapshot.info,snapshot.memo.memoId)
@receiver(post_save, sender=MemoEvents)
def
create_memo_snapshot( sender, instance: app.memos.models.MemoEvents, created: bool, **kwargs):
9@receiver(post_save, sender=MemoEvents) 10def create_memo_snapshot(sender, instance: MemoEvents, created: bool, **kwargs): 11 """ 12 Signal handler to create or update MemoSnapshot whenever a MemoEvents instance is saved. 13 14 Handles different event types: 15 - "worker_status_delete": Marks the relevant worker status as deleted in the latest snapshot. 16 - "updated": Updates the latest snapshot's info with the event payload. 17 - "approved"/"rejected": Updates approval status in the latest snapshot's hierarchy and approval_history. 18 - Worker status events ("attended", "completed", "incomplete", "tagged"): Appends worker status to the latest snapshot and updates status fields. 19 - Other events: Creates a new MemoSnapshot capturing user context, complaint info, hierarchy, and approval history. 20 21 Also triggers FCM notifications for memo creation and tagging. 22 """ 23 if instance.event_type == "worker_status_delete": 24 # Mark worker status as deleted in the latest snapshot. 25 latest_snapshot = ( 26 MemoSnapshot.objects.filter(memo=instance.memo).order_by('-timestamp').first() 27 ) 28 comments = latest_snapshot.worker_status or [] 29 for comment in comments: 30 if comment.get('event_id') == instance.payload.get('event_id'): 31 comment.update({'deleted': True}) 32 break 33 latest_snapshot.worker_status = comments 34 latest_snapshot.save() 35 return 36 37 if not created: 38 # Only snapshot on new events – updates to the same event record should be ignored. 39 return 40 41 # For 'updated' events, update the latest snapshot's info. 42 if instance.event_type == 'updated': 43 latest_snapshot = ( 44 MemoSnapshot.objects.filter(memo=instance.memo).order_by('-timestamp').first() 45 ) 46 if latest_snapshot: 47 updated_info = {**latest_snapshot.info, **instance.payload} 48 latest_snapshot.info = updated_info 49 latest_snapshot.save() 50 return 51 52 # For approval events, update hierarchy and approval history in the latest snapshot. 53 if instance.event_type in ['approved', 'rejected']: 54 latest_snapshot = ( 55 MemoSnapshot.objects.filter(memo=instance.memo).order_by('-timestamp').first() 56 ) 57 if latest_snapshot: 58 approval_history = latest_snapshot.hierarchy or [] 59 role_id = instance.event_by.role.id 60 event_type = instance.event_type 61 event_by = instance.event_by 62 event_timestamp = instance.event_timestamp.isoformat() 63 64 for level in approval_history: 65 if level.get('role') == role_id: 66 is_approved = event_type == 'approved' 67 is_rejected = event_type == 'rejected' 68 hist = latest_snapshot.approval_history 69 if is_approved: 70 hist.update({ 71 f"{role_id}_approved": True, 72 }) 73 else: 74 hist.update({ 75 f"{role_id}_approved": False, 76 }) 77 level.update({ 78 'approved': is_approved, 79 'rejected': is_rejected, 80 'by': event_by.id, 81 'by_phone': event_by.phone_number, 82 'approved_by': event_by.institution_id if is_approved else None, 83 'rejected_by': event_by.institution_id if is_rejected else None, 84 'approved_at': event_timestamp if is_approved else None, 85 'rejected_at': event_timestamp if is_rejected else None, 86 }) 87 break 88 latest_snapshot.hierarchy = approval_history 89 latest_snapshot.save() 90 return 91 92 # For worker status events, append status and update info in the latest snapshot. 93 if instance.event_type in ['attended','completed','incomplete','tagged']: 94 memo_snapshot = MemoSnapshot.objects.filter(memo=instance.memo).order_by('-timestamp').first() 95 if memo_snapshot: 96 comments = memo_snapshot.worker_status or [] 97 comments.append({**instance.payload,"event_id":instance.id,"deleted":False,"timestamp":instance.event_timestamp.isoformat()}) 98 memo_snapshot.worker_status = comments 99 if instance.event_type == 'completed': 100 memo_snapshot.info.update({"status": "completed"}) 101 memo_snapshot.info.update({"display_status":instance.event_type}) 102 memo_snapshot.save() 103 if instance.event_type == 'tagged': 104 memo_tagged_notification(memo_snapshot.info,instance.payload.get("tagged_role_name"),instance.payload.get("tagged_role_id") ,memo_snapshot.memo.memoId) 105 return 106 107 # For all other event types, create a fresh snapshot. 108 metadata = instance.metadata or {} 109 user_meta = metadata.get("user", metadata) 110 info = { 111 "status": "ongoing", 112 "user_role_id": user_meta.get("role_id"), 113 "user_role_name": user_meta.get("role"), 114 "current_block_id": user_meta.get("current_block_id",'null'), 115 "current_block_name": user_meta.get("current_block_name",'null'), 116 "current_ward_id": user_meta.get("current_ward_id",'null'), 117 "current_ward_name": user_meta.get("current_ward_name",'null'), 118 "current_floor": user_meta.get("floor",'null'), 119 "user_id": instance.event_by.id, 120 "institution_id": getattr(instance.event_by, "institution_id", None), 121 "phone_number": instance.event_by.phone_number, 122 } 123 payload = instance.payload or {} 124 info.update({ 125 "complaint": payload.get("complaint"), 126 "department": payload.get("department"), 127 "tagged_role_id": payload.get("tagged_role_id"), 128 "tagged_role_name": payload.get("tagged_role_name"), 129 }) 130 131 # Apply active hierarchy to the snapshot 132 hierarchy = ApproverHierarchy.objects.filter(is_active=True, levels__role__hospital=instance.event_by.hospital).order_by('-created_at').first() 133 levels = [] 134 hist = {} 135 if hierarchy: 136 temp = hierarchy.levels.all().order_by('priority') 137 levels = ApproverLevelSerializer(temp, many=True).data 138 for level in levels: 139 level['approved'] = False 140 hist[f"{level['role']}_approved"] = False 141 142 # Persist snapshot 143 snapshot = MemoSnapshot.objects.create( 144 memo=instance.memo, 145 info=info, 146 hierarchy=levels, 147 approval_history=hist or {}, 148 worker_status=payload.get("worker_status", []), 149 ) 150 151 # Send notification for memo creation 152 memo_created_notification(snapshot.info,snapshot.memo.memoId)
Signal handler to create or update MemoSnapshot whenever a MemoEvents instance is saved.
Handles different event types:
- "worker_status_delete": Marks the relevant worker status as deleted in the latest snapshot.
- "updated": Updates the latest snapshot's info with the event payload.
- "approved"/"rejected": Updates approval status in the latest snapshot's hierarchy and approval_history.
- Worker status events ("attended", "completed", "incomplete", "tagged"): Appends worker status to the latest snapshot and updates status fields.
- Other events: Creates a new MemoSnapshot capturing user context, complaint info, hierarchy, and approval history.
Also triggers FCM notifications for memo creation and tagging.