|
1 | 1 | import logging |
| 2 | +from zoneinfo import ZoneInfo, ZoneInfoNotFoundError |
2 | 3 |
|
3 | 4 | from django.contrib.auth.models import Group |
4 | 5 | from django.db.models.signals import m2m_changed, post_save, pre_delete, pre_save |
5 | 6 | from django.dispatch import receiver |
| 7 | +from django.conf import settings |
| 8 | +from django.utils import timezone |
6 | 9 | from guardian.shortcuts import assign_perm |
7 | 10 |
|
8 | 11 | from ami.users.roles import BasicMember, ProjectManager, create_roles_for_project |
9 | 12 |
|
10 | | -from .models import Project, User |
| 13 | +from .models import Classification, Detection, Event, Project, SourceImage, User |
11 | 14 |
|
12 | 15 | logger = logging.getLogger(__name__) |
13 | 16 |
|
@@ -110,3 +113,52 @@ def delete_project_groups(sender, instance, **kwargs): |
110 | 113 | prefix = f"{instance.pk}_" |
111 | 114 | # Find and delete all groups that start with {project_id}_ |
112 | 115 | Group.objects.filter(name__startswith=prefix).delete() |
| 116 | + |
| 117 | + |
| 118 | +_TZ_FIELDS = { |
| 119 | + Event: ("start", "end", "calculated_fields_updated_at"), |
| 120 | + SourceImage: ("timestamp", "last_modified"), |
| 121 | + Detection: ("timestamp", "detection_time"), |
| 122 | + Classification: ("timestamp",), |
| 123 | +} |
| 124 | + |
| 125 | + |
| 126 | +def _resolve_deployment(instance): |
| 127 | + deployment = getattr(instance, "deployment", None) |
| 128 | + if deployment: |
| 129 | + return deployment |
| 130 | + |
| 131 | + source_image = getattr(instance, "source_image", None) |
| 132 | + if source_image: |
| 133 | + return getattr(source_image, "deployment", None) |
| 134 | + |
| 135 | + detection = getattr(instance, "detection", None) |
| 136 | + if detection: |
| 137 | + src = getattr(detection, "source_image", None) |
| 138 | + return getattr(src, "deployment", None) if src else None |
| 139 | + |
| 140 | + return None |
| 141 | + |
| 142 | + |
| 143 | +def _normalize_datetimes(sender, instance, **_kwargs): |
| 144 | + fields = _TZ_FIELDS.get(sender) |
| 145 | + if not fields or not settings.USE_TZ: |
| 146 | + return |
| 147 | + |
| 148 | + deployment = _resolve_deployment(instance) |
| 149 | + try: |
| 150 | + tz = ZoneInfo(getattr(deployment, "time_zone", None) or timezone.get_default_timezone_name()) |
| 151 | + except ZoneInfoNotFoundError: |
| 152 | + tz = timezone.get_default_timezone() |
| 153 | + |
| 154 | + for field in fields: |
| 155 | + value = getattr(instance, field, None) |
| 156 | + if value is None: |
| 157 | + continue |
| 158 | + if timezone.is_naive(value): |
| 159 | + value = timezone.make_aware(value, tz) |
| 160 | + setattr(instance, field, timezone.localtime(value, timezone.utc)) |
| 161 | + |
| 162 | + |
| 163 | +for model in _TZ_FIELDS: |
| 164 | + pre_save.connect(_normalize_datetimes, sender=model, dispatch_uid=f"tznorm-{model.__name__}", weak=False) |
0 commit comments