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.