Skip to content

Commit 2a44fb7

Browse files
committed
refactor(dref): refactor dref finalize logic
1 parent aa8b32c commit 2a44fb7

11 files changed

+276
-214
lines changed

dref/migrations/0084_dref_original_language_and_more.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,18 @@ def update_status(apps, schema_editor):
2020
Model.objects.filter(is_published=True).update(status=4) # Published → APPROVED
2121
Model.objects.filter(is_published=False).update(status=1) # Not published → DRAFT
2222

23+
def update_original_language(apps, schema_editor):
24+
"""
25+
Populate the original_language field with 'en' for all existing records.
26+
"""
27+
models = [
28+
"Dref",
29+
"DrefOperationalUpdate",
30+
"DrefFinalReport",
31+
]
32+
for model_name in models:
33+
Model = apps.get_model("dref", model_name)
34+
Model.objects.update(original_language="en")
2335

2436
class Migration(migrations.Migration):
2537

@@ -80,4 +92,5 @@ class Migration(migrations.Migration):
8092
),
8193
),
8294
migrations.RunPython(update_status, reverse_code=migrations.RunPython.noop),
95+
migrations.RunPython(update_original_language,reverse_code=migrations.RunPython.noop),
8396
]
Lines changed: 40 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,55 @@
1-
# Generated by Django 4.2.19 on 2025-09-16 09:13
1+
# Generated by Django 4.2.19 on 2025-11-05 04:07
2+
3+
from django.db import migrations, models
24

3-
from django.db import migrations
45

5-
def migrate_approved_status_to_new_value(apps, schema_editor):
6-
"""
7-
Migrates the 'APPROVED' status from old value (4) to new value (3)
8-
"""
9-
models = [
10-
"Dref",
11-
"DrefOperationalUpdate",
12-
"DrefFinalReport",
13-
]
14-
for model_name in models:
15-
Model = apps.get_model("dref", model_name)
16-
Model.objects.filter(status=4).update(status=3)
176
class Migration(migrations.Migration):
187

198
dependencies = [
20-
("dref", "0084_dref_original_language_and_more"),
9+
('dref', '0084_dref_original_language_and_more'),
2110
]
2211

2312
operations = [
2413
migrations.RemoveField(
25-
model_name="dref",
26-
name="is_published",
14+
model_name='dref',
15+
name='is_published',
16+
),
17+
migrations.RenameField(
18+
model_name='dref',
19+
old_name='original_language',
20+
new_name='starting_language',
2721
),
2822
migrations.RemoveField(
29-
model_name="dreffinalreport",
30-
name="is_published",
23+
model_name='dreffinalreport',
24+
name='is_published',
25+
),
26+
migrations.RenameField(
27+
model_name='dreffinalreport',
28+
old_name='original_language',
29+
new_name='starting_language',
3130
),
3231
migrations.RemoveField(
33-
model_name="drefoperationalupdate",
34-
name="is_published",
32+
model_name='drefoperationalupdate',
33+
name='is_published',
34+
),
35+
migrations.RenameField(
36+
model_name='drefoperationalupdate',
37+
old_name='original_language',
38+
new_name='starting_language',
39+
),
40+
migrations.AlterField(
41+
model_name='dref',
42+
name='starting_language',
43+
field=models.CharField(blank=True, help_text='The language in which this record was first created.', null=True, verbose_name='Starting language'),
44+
),
45+
migrations.AlterField(
46+
model_name='dreffinalreport',
47+
name='starting_language',
48+
field=models.CharField(blank=True, help_text='The language in which this record was first created.', null=True, verbose_name='Starting language'),
49+
),
50+
migrations.AlterField(
51+
model_name='drefoperationalupdate',
52+
name='starting_language',
53+
field=models.CharField(blank=True, help_text='The language in which this record was first created.', null=True, verbose_name='Starting language'),
3554
),
36-
migrations.RunPython(migrate_approved_status_to_new_value, reverse_code=migrations.RunPython.noop),
3755
]

dref/migrations/0086_alter_dref_status_alter_dreffinalreport_status_and_more.py

Lines changed: 0 additions & 43 deletions
This file was deleted.

dref/migrations/0087_remove_dref_original_language_and_more.py

Lines changed: 0 additions & 43 deletions
This file was deleted.

dref/models.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -266,9 +266,11 @@ class DisasterCategory(models.IntegerChoices):
266266
class Status(models.IntegerChoices):
267267
DRAFT = 1, _("Draft")
268268
"""Draft: Initial stage content is being created and is not ready for review."""
269-
FINALIZED = 2, _("Finalized")
269+
FINALIZING = 2, _("Finalizing")
270+
"""Finalizing: Content is in the translation process from the original language into English."""
271+
FINALIZED = 3, _("Finalized")
270272
"""Finalized: Translation is completed, content is ready for review, and updates to the original language are locked."""
271-
APPROVED = 3, _("Approved")
273+
APPROVED = 4, _("Approved")
272274
"""Approved: The content has been reviewed, accepted, and is ready for use."""
273275

274276
created_at = models.DateTimeField(verbose_name=_("created at"), auto_now_add=True)

dref/serializers.py

Lines changed: 16 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,8 @@ def get_dref_access_user_list(self, obj) -> List[int] | None:
472472
def validate(self, data):
473473
event_date = data.get("event_date")
474474
operation_timeframe = data.get("operation_timeframe")
475+
if self.instance and self.instance.status == Dref.Status.FINALIZING:
476+
raise serializers.ValidationError(gettext("Cannot be updated while the translation is in progress"))
475477
is_assessment_report = data.get("is_assessment_report")
476478
if event_date and data["type_of_onset"] not in [Dref.OnsetType.SLOW, Dref.OnsetType.SUDDEN]:
477479
raise serializers.ValidationError(
@@ -746,6 +748,8 @@ class Meta:
746748

747749
def validate(self, data):
748750
dref = data.get("dref")
751+
if self.instance and self.instance.status == Dref.Status.FINALIZING:
752+
raise serializers.ValidationError(gettext("Cannot be updated while the translation is in progress"))
749753
if not self.instance and dref:
750754
if dref.status != Dref.Status.APPROVED:
751755
raise serializers.ValidationError(gettext("Can't create Operational Update for not approved %s dref." % dref.id))
@@ -789,17 +793,13 @@ def validate_images_file(self, images):
789793
def create(self, validated_data):
790794
dref = validated_data["dref"]
791795
current_language = get_language()
796+
stating_langauge = validated_data.get("starting_language")
792797
valid_languages = [dref.starting_language, dref.translation_module_original_language]
793-
# Check request language
794-
if current_language not in valid_languages:
798+
if current_language != stating_langauge:
799+
raise serializers.ValidationError(gettext("Starting language does not match the expected language."))
800+
if stating_langauge not in valid_languages:
795801
raise serializers.ValidationError(
796-
gettext(f"Language must be either '{valid_languages[0]}' or '{valid_languages[1]}'.")
797-
)
798-
# Check payload language
799-
language = validated_data.get("starting_language")
800-
if language not in valid_languages:
801-
raise serializers.ValidationError(
802-
gettext(f"Language must be either '{valid_languages[0]}' or '{valid_languages[1]}'.")
802+
gettext(f"Invalid starting language. Supported options are '{valid_languages[0]}' and '{valid_languages[1]}'.")
803803
)
804804
dref_operational_update = DrefOperationalUpdate.objects.filter(dref=dref).order_by("-operational_update_number").first()
805805
validated_data["created_by"] = self.context["request"].user
@@ -1147,6 +1147,8 @@ class Meta:
11471147

11481148
def validate(self, data):
11491149
dref = data.get("dref")
1150+
if self.instance and self.instance.status == Dref.Status.FINALIZING:
1151+
raise serializers.ValidationError(gettext("Cannot be updated while the translation is in progress"))
11501152
# Check if dref is published and operational_update associated with it is also published
11511153
if not self.instance and dref:
11521154
if dref.status != Dref.Status.APPROVED:
@@ -1248,18 +1250,13 @@ def create(self, validated_data):
12481250
# else copy from dref
12491251
dref = validated_data["dref"]
12501252
current_language = get_language()
1253+
stating_langauge = validated_data.get("starting_language")
12511254
valid_languages = [dref.starting_language, dref.translation_module_original_language]
1252-
# Check request language
1253-
if current_language not in valid_languages:
1254-
raise serializers.ValidationError(
1255-
gettext(f"Language must be either '{valid_languages[0]}' or '{valid_languages[1]}'.")
1256-
)
1257-
1258-
# Check payload language
1259-
language = validated_data.get("starting_language")
1260-
if language not in valid_languages:
1255+
if current_language != stating_langauge:
1256+
raise serializers.ValidationError(gettext("Starting language does not match the expected language."))
1257+
if stating_langauge not in valid_languages:
12611258
raise serializers.ValidationError(
1262-
gettext(f"Language must be either '{valid_languages[0]}' or '{valid_languages[1]}'.")
1259+
gettext(f"Invalid starting language. Supported options are '{valid_languages[0]}' and '{valid_languages[1]}'.")
12631260
)
12641261
dref_operational_update = (
12651262
DrefOperationalUpdate.objects.filter(dref=dref, status=Dref.Status.APPROVED)

dref/tasks.py

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
1+
import logging
2+
13
from celery import shared_task
4+
from django.apps import apps
25
from django.template.loader import render_to_string
36

7+
from api.utils import get_model_name
8+
from lang.tasks import translate_model_fields
49
from notifications.notification import send_notification
510

611
from .models import Dref
712
from .utils import get_email_context
813

14+
logger = logging.getLogger(__name__)
15+
916

1017
@shared_task
1118
def send_dref_email(dref_id, users_emails, new_or_updated=""):
@@ -20,3 +27,64 @@ def send_dref_email(dref_id, users_emails, new_or_updated=""):
2027

2128
send_notification(email_subject, users_emails, email_body, email_type)
2229
return email_context
30+
31+
32+
@shared_task
33+
def process_translation(model_name, instance_pk):
34+
"""
35+
Task to translate model instance and its related objects
36+
"""
37+
instance = None
38+
try:
39+
model = apps.get_model(model_name)
40+
instance = model.objects.get(pk=instance_pk)
41+
translate_model_fields(model_name, instance_pk)
42+
_translate_related_objects(instance)
43+
instance.status = Dref.Status.FINALIZED
44+
instance.translation_module_original_language = "en"
45+
instance.save(update_fields=["status", "translation_module_original_language"])
46+
logger.info(f"Successfully finalized {model_name} ID: {instance_pk}")
47+
except Exception as exc:
48+
if instance is not None:
49+
instance.status = Dref.Status.DRAFT
50+
instance.save(update_fields=["status"])
51+
logger.error(f"Translation failed for {model_name} ID {instance_pk}: {str(exc)}")
52+
raise exc
53+
54+
55+
def _translate_related_objects(instance, visited=None):
56+
if visited is None:
57+
visited = set()
58+
59+
instance_id = id(instance)
60+
if instance_id in visited:
61+
return
62+
visited.add(instance_id)
63+
64+
for field in instance._meta.get_fields():
65+
if not field.is_relation or field.auto_created:
66+
continue
67+
68+
try:
69+
related_value = getattr(instance, field.name, None)
70+
if related_value is None:
71+
continue
72+
73+
# Handle related objects
74+
if not field.many_to_many:
75+
if hasattr(related_value, "translation_module_original_language"):
76+
model_name = get_model_name(type(related_value))
77+
translate_model_fields(model_name, related_value.pk)
78+
_translate_related_objects(related_value, visited)
79+
80+
# Handle multiple related objects
81+
else:
82+
for related_obj in related_value.all():
83+
if hasattr(related_obj, "translation_module_original_language"):
84+
model_name = get_model_name(type(related_obj))
85+
translate_model_fields(model_name, related_obj.pk)
86+
_translate_related_objects(related_obj, visited)
87+
88+
except Exception as e:
89+
logger.warning(f"Error processing field {field.name}: {e}")
90+
continue

0 commit comments

Comments
 (0)