Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 36 additions & 1 deletion api/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,9 @@
DrefFactory,
DrefFinalReportFactory,
DrefOperationalUpdateFactory,
DrefSummaryFactory,
)
from dref.models import Dref, DrefFile
from dref.models import Dref, DrefFile, DrefSummary
from main.test_case import APITestCase
from per.factories import OpsLearningFactory

Expand Down Expand Up @@ -1535,3 +1536,37 @@ def test_dref_operational_update_with_timeline_ops_updates(self):
self.assertIsNotNone(dref_data)
self.assertIsNotNone(dref_data["final_report_details"])
self.assertEqual(len(dref_data["timeline_operational_updates"]), 3)

def test_dref_summary_fields_present_when_summary_exists(self):
event = EventFactory.create(dtype=self.disaster_type)
dref = self._approved_dref(event)
DrefSummaryFactory.create(
dref=dref,
status=DrefSummary.SummaryStatus.SUCCESS,
situational_overview="overview text",
operational_strategy="strategy text",
people_centered_approach="approach text",
challenges_identified="challenges text",
lessons_learned="lessons text",
)

data = self._get(event).data

self.assertEqual(data["stage"], EventStage.DREF_APPLICATION)
summary = data["dref"]["summary"]
self.assertIsNotNone(summary)
self.assertEqual(summary["status"], DrefSummary.SummaryStatus.SUCCESS)
self.assertEqual(summary["situational_overview"], "overview text")
self.assertEqual(summary["operational_strategy"], "strategy text")
self.assertEqual(summary["people_centered_approach"], "approach text")
self.assertEqual(summary["challenges_identified"], "challenges text")
self.assertEqual(summary["lessons_learned"], "lessons text")

def test_dref_summary_is_none_when_no_summary_exists(self):
event = EventFactory.create(dtype=self.disaster_type)
self._approved_dref(event)

data = self._get(event).data

self.assertEqual(data["stage"], EventStage.DREF_APPLICATION)
self.assertIsNone(data["dref"]["summary"])
2 changes: 1 addition & 1 deletion assets
Submodule assets updated 1 files
+43 −0 openapi-schema.yaml
47 changes: 46 additions & 1 deletion dref/admin.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
from django.contrib import admin
from reversion_compare.admin import CompareVersionAdmin

from lang.admin import TranslationAdmin
from lang.admin import TranslationAdmin, TranslationInlineModelAdmin

from .models import (
Dref,
DrefFile,
DrefFinalReport,
DrefOperationalUpdate,
DrefSummary,
IdentifiedNeed,
NationalSocietyAction,
PlannedIntervention,
Expand Down Expand Up @@ -125,8 +126,31 @@ class SourceInformationAdmin(admin.ModelAdmin):
search_fields = ("source_name",)


class DrefSummaryInline(admin.StackedInline, TranslationInlineModelAdmin):
model = DrefSummary
extra = 0
readonly_fields = (
"status",
"hash",
"created_at",
"updated_at",
)
fields = (
"status",
"hash",
"situational_overview",
"operational_strategy",
"people_centered_approach",
"challenges_identified",
"lessons_learned",
"created_at",
"updated_at",
)


@admin.register(Dref)
class DrefAdmin(CompareVersionAdmin, TranslationAdmin, admin.ModelAdmin):
inlines = [DrefSummaryInline]
search_fields = ("title", "appeal_code")
list_display = (
"title",
Expand Down Expand Up @@ -301,3 +325,24 @@ def save_model(self, request, obj, form, change):
@admin.register(ProposedAction)
class ProposedActionAdmin(ReadOnlyMixin, admin.ModelAdmin):
search_fields = ["action"]


@admin.register(DrefSummary)
class DrefSummaryAdmin(TranslationAdmin, admin.ModelAdmin):
list_display = ("dref", "status", "created_at", "updated_at")
list_filter = ("status",)
search_fields = ("dref__title", "dref__appeal_code")
readonly_fields = ("hash", "created_at", "updated_at")
autocomplete_fields = ("dref",)
fields = (
"dref",
"status",
"hash",
"situational_overview",
"operational_strategy",
"people_centered_approach",
"challenges_identified",
"lessons_learned",
"created_at",
"updated_at",
)
15 changes: 15 additions & 0 deletions dref/factories/dref.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
DrefFile,
DrefFinalReport,
DrefOperationalUpdate,
DrefSummary,
IdentifiedNeed,
NationalSocietyAction,
PlannedIntervention,
Expand Down Expand Up @@ -198,3 +199,17 @@ class Meta:
model = ProposedAction

proposed_type = fuzzy.FuzzyChoice(ProposedAction.Action)


class DrefSummaryFactory(factory.django.DjangoModelFactory):
class Meta:
model = DrefSummary

dref = factory.SubFactory(DrefFactory)
hash = factory.Sequence(lambda n: f"{n:064d}")
status = DrefSummary.SummaryStatus.SUCCESS
situational_overview = fuzzy.FuzzyText(length=100)
operational_strategy = fuzzy.FuzzyText(length=100)
people_centered_approach = fuzzy.FuzzyText(length=100)
challenges_identified = fuzzy.FuzzyText(length=100)
lessons_learned = fuzzy.FuzzyText(length=100)
30 changes: 30 additions & 0 deletions dref/migrations/0090_drefsummary.py

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's merge this Migrations!

Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Generated by Django 4.2.30 on 2026-06-22 05:01

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
('dref', '0089_remove_dref_field_report_dref_event'),
]

operations = [
migrations.CreateModel(
name='DrefSummary',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('hash', models.CharField(max_length=64, unique=True)),
('situational_overview', models.TextField(blank=True, null=True)),
('operational_strategy', models.TextField(blank=True, null=True)),
('people_centered_approach', models.TextField(blank=True, null=True)),
('challenges_identified', models.TextField(blank=True, null=True)),
('lessons_learned', models.TextField(blank=True, null=True)),
('status', models.IntegerField(choices=[(100, 'Pending'), (200, 'Success'), (300, 'Failed')], default=100)),
('dref', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='summary', to='dref.dref')),
],
),
]
123 changes: 123 additions & 0 deletions dref/migrations/0091_drefsummary_challenges_identified_ar_and_more.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
# Generated by Django 4.2.30 on 2026-06-22 08:40

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('dref', '0090_drefsummary'),
]

operations = [
migrations.AddField(
model_name='drefsummary',
name='challenges_identified_ar',
field=models.TextField(blank=True, null=True),
),
migrations.AddField(
model_name='drefsummary',
name='challenges_identified_en',
field=models.TextField(blank=True, null=True),
),
migrations.AddField(
model_name='drefsummary',
name='challenges_identified_es',
field=models.TextField(blank=True, null=True),
),
migrations.AddField(
model_name='drefsummary',
name='challenges_identified_fr',
field=models.TextField(blank=True, null=True),
),
migrations.AddField(
model_name='drefsummary',
name='lessons_learned_ar',
field=models.TextField(blank=True, null=True),
),
migrations.AddField(
model_name='drefsummary',
name='lessons_learned_en',
field=models.TextField(blank=True, null=True),
),
migrations.AddField(
model_name='drefsummary',
name='lessons_learned_es',
field=models.TextField(blank=True, null=True),
),
migrations.AddField(
model_name='drefsummary',
name='lessons_learned_fr',
field=models.TextField(blank=True, null=True),
),
migrations.AddField(
model_name='drefsummary',
name='operational_strategy_ar',
field=models.TextField(blank=True, null=True),
),
migrations.AddField(
model_name='drefsummary',
name='operational_strategy_en',
field=models.TextField(blank=True, null=True),
),
migrations.AddField(
model_name='drefsummary',
name='operational_strategy_es',
field=models.TextField(blank=True, null=True),
),
migrations.AddField(
model_name='drefsummary',
name='operational_strategy_fr',
field=models.TextField(blank=True, null=True),
),
migrations.AddField(
model_name='drefsummary',
name='people_centered_approach_ar',
field=models.TextField(blank=True, null=True),
),
migrations.AddField(
model_name='drefsummary',
name='people_centered_approach_en',
field=models.TextField(blank=True, null=True),
),
migrations.AddField(
model_name='drefsummary',
name='people_centered_approach_es',
field=models.TextField(blank=True, null=True),
),
migrations.AddField(
model_name='drefsummary',
name='people_centered_approach_fr',
field=models.TextField(blank=True, null=True),
),
migrations.AddField(
model_name='drefsummary',
name='situational_overview_ar',
field=models.TextField(blank=True, null=True),
),
migrations.AddField(
model_name='drefsummary',
name='situational_overview_en',
field=models.TextField(blank=True, null=True),
),
migrations.AddField(
model_name='drefsummary',
name='situational_overview_es',
field=models.TextField(blank=True, null=True),
),
migrations.AddField(
model_name='drefsummary',
name='situational_overview_fr',
field=models.TextField(blank=True, null=True),
),
migrations.AddField(
model_name='drefsummary',
name='translation_module_original_language',
field=models.CharField(choices=[('en', 'English'), ('es', 'Spanish'), ('fr', 'French'), ('ar', 'Arabic')], default='en', help_text='Language used to create this entity', max_length=2, verbose_name='Entity Original language'),
),
migrations.AddField(
model_name='drefsummary',
name='translation_module_skip_auto_translation',
field=models.BooleanField(default=False, help_text='Skip auto translation operation for this entity?', verbose_name='Skip auto translation'),
),
]
52 changes: 52 additions & 0 deletions dref/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -1700,3 +1700,55 @@ def get_for(user, status=None):
if status == Dref.Status.APPROVED:
return queryset.filter(status=Dref.Status.APPROVED)
return queryset


class DrefSummary(models.Model):

class SummaryStatus(models.IntegerChoices):
PENDING = 100, _("Pending")
SUCCESS = 200, _("Success")
FAILED = 300, _("Failed")
Comment on lines +1707 to +1710

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's also add Processing status too. Here!

Suggested change
class SummaryStatus(models.IntegerChoices):
PENDING = 100, _("Pending")
SUCCESS = 200, _("Success")
FAILED = 300, _("Failed")
class SummaryStatus(models.IntegerChoices):
PENDING = 100, _("Pending")
PROCESSING = 200, _("Processing")
SUCCESS = 300, _("Success")
FAILED = 400, _("Failed")


dref = models.OneToOneField(
Dref,
on_delete=models.CASCADE,
related_name="summary",
)

created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need this?


hash = models.CharField(
max_length=64,
unique=True,
)
Comment on lines +1721 to +1724

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
hash = models.CharField(
max_length=64,
unique=True,
)
prompt_hash = models.CharField(
max_length=64,
unique=True,
)


situational_overview = models.TextField(
blank=True,
null=True,
)

operational_strategy = models.TextField(
blank=True,
null=True,
)

people_centered_approach = models.TextField(
blank=True,
null=True,
)

challenges_identified = models.TextField(
blank=True,
null=True,
)

lessons_learned = models.TextField(
blank=True,
null=True,
)

status = models.IntegerField(
choices=SummaryStatus.choices,
default=SummaryStatus.PENDING,
)
Loading
Loading