Skip to content

Commit d963fa0

Browse files
Merge pull request #2547 from IFRCGo/refactor/local-unit-status
Refactor local unit status
2 parents 6015083 + 9a08b3d commit d963fa0

File tree

8 files changed

+168
-63
lines changed

8 files changed

+168
-63
lines changed

local_units/admin.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,13 +55,9 @@ class LocalUnitAdmin(CompareVersionAdmin, admin.OSMGeoAdmin):
5555
"health",
5656
"bulk_upload",
5757
)
58-
readonly_fields = (
59-
"validated",
60-
"status",
61-
"is_locked",
62-
"is_new_local_unit",
63-
)
58+
readonly_fields = ("status",)
6459
list_filter = (
60+
"status",
6561
AutocompleteFilterFactory("Country", "country"),
6662
AutocompleteFilterFactory("Type", "type"),
6763
AutocompleteFilterFactory("Level", "level"),

local_units/bulk_upload.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,10 @@ def delete_existing_local_unit(self):
119119
def _validate_csv(self, fieldnames) -> None:
120120
pass
121121

122+
def _is_csv_empty(self, csv_reader: csv.DictReader) -> tuple[bool, list[dict]]:
123+
rows = list(csv_reader)
124+
return len(rows) == 0, rows
125+
122126
def process_row(self, data: Dict[str, Any]) -> bool:
123127
serializer = self.serializer_class(data=data)
124128
if serializer.is_valid():
@@ -133,12 +137,18 @@ def run(self) -> None:
133137
csv_reader = csv.DictReader(file)
134138
fieldnames = csv_reader.fieldnames or []
135139
try:
140+
is_empty, rows = self._is_csv_empty(csv_reader)
141+
if is_empty:
142+
raise BulkUploadError("The uploaded CSV file is empty or contains only blank rows.")
143+
144+
csv_reader = iter(rows)
145+
136146
self._validate_csv(fieldnames)
137147
except BulkUploadError as e:
138148
self.bulk_upload.status = LocalUnitBulkUpload.Status.FAILED
139149
self.bulk_upload.error_message = str(e)
140150
self.bulk_upload.save(update_fields=["status", "error_message"])
141-
logger.error(f"[BulkUpload:{self.bulk_upload.pk}] Validation error: {str(e)}")
151+
logger.warning(f"[BulkUpload:{self.bulk_upload.pk}] Validation error: {str(e)}")
142152
return
143153

144154
context = self.get_context().__dict__
@@ -231,7 +241,7 @@ def _validate_csv(self, fieldnames) -> None:
231241
raise ValueError("Invalid local unit type")
232242

233243
if present_health_fields and local_unit_type.name.strip().lower() != "health care":
234-
raise BulkUploadError(f"Health data are not allowed for this type: {local_unit_type.name}.")
244+
raise BulkUploadError(f"You cannot upload Healthcare data when the Local Unit type is set to {local_unit_type.name}.")
235245

236246

237247
class BulkUploadHealthData(BaseBulkUpload[LocalUnitUploadContext]):

local_units/filterset.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,6 @@ class Meta:
2222
"country__iso",
2323
"type__code",
2424
"draft",
25-
"validated",
26-
"is_locked",
2725
"status",
2826
)
2927

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# Generated by Django 4.2.19 on 2025-09-02 17:20
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
("local_units", "0022_localunit_status_migrate"),
10+
]
11+
12+
operations = [
13+
migrations.RemoveField(
14+
model_name="localunit",
15+
name="is_locked",
16+
),
17+
migrations.RemoveField(
18+
model_name="localunit",
19+
name="is_new_local_unit",
20+
),
21+
migrations.RemoveField(
22+
model_name="localunit",
23+
name="validated",
24+
),
25+
migrations.AlterField(
26+
model_name="localunit",
27+
name="status",
28+
field=models.IntegerField(
29+
choices=[(1, "Validated"), (2, "Unvalidated"), (3, "Pending Edit Validation"), (4, "Externally Managed")],
30+
default=2,
31+
verbose_name="Validation Status",
32+
),
33+
),
34+
]

local_units/models.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,7 @@ class Status(models.IntegerChoices):
355355
VALIDATED = 1, "Validated"
356356
UNVALIDATED = 2, "Unvalidated"
357357
PENDING_EDIT_VALIDATION = 3, "Pending Edit Validation"
358+
EXTERNALLY_MANAGED = 4, "Externally Managed"
358359

359360
# added to track health local unit data (Table B)
360361
health = models.ForeignKey(
@@ -394,7 +395,6 @@ class Status(models.IntegerChoices):
394395
auto_now=False,
395396
)
396397
draft = models.BooleanField(default=False, verbose_name=_("Draft"))
397-
validated = models.BooleanField(default=False, verbose_name=_("Validated")) # NOTE: This field might be deprecated soon.
398398
status = models.IntegerField(
399399
choices=Status.choices, default=Status.UNVALIDATED, verbose_name=_("Validation Status")
400400
) # NOTE: Replacement of validated field for better status tracking
@@ -422,7 +422,6 @@ class Status(models.IntegerChoices):
422422
email = models.EmailField(max_length=255, blank=True, null=True, verbose_name=_("Email"))
423423
link = models.URLField(max_length=255, blank=True, null=True, verbose_name=_("Social link"))
424424
location = models.PointField(srid=4326, help_text="Local Unit Location")
425-
is_locked = models.BooleanField(default=False, verbose_name=_("Is locked?"))
426425
is_deprecated = models.BooleanField(default=False, verbose_name=_("Is deprecated?"))
427426
deprecated_reason = models.IntegerField(
428427
choices=DeprecateReason.choices, verbose_name=_("deprecated reason"), blank=True, null=True
@@ -446,8 +445,6 @@ class Status(models.IntegerChoices):
446445
related_name="bulk_upload_local_unit",
447446
)
448447

449-
is_new_local_unit = models.BooleanField(default=False, verbose_name=("Is New Local Unit?"))
450-
451448
def __str__(self):
452449
branch_name = self.local_branch_name or self.english_branch_name
453450
return f"{branch_name} ({self.country.name})"

local_units/serializers.py

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,6 @@ class PrivateLocalUnitDetailSerializer(NestedCreateMixin, NestedUpdateMixin):
263263
modified_by_details = LocalUnitMiniUserSerializer(source="modified_by", read_only=True)
264264
created_by_details = LocalUnitMiniUserSerializer(source="created_by", read_only=True)
265265
version_id = serializers.SerializerMethodField()
266-
is_locked = serializers.BooleanField(read_only=True)
267266

268267
class Meta:
269268
model = LocalUnit
@@ -306,9 +305,7 @@ class Meta:
306305
"modified_by_details",
307306
"created_by_details",
308307
"version_id",
309-
"is_locked",
310308
"update_reason_overview",
311-
"is_new_local_unit",
312309
"bulk_upload",
313310
)
314311

@@ -374,8 +371,7 @@ def create(self, validated_data):
374371
)
375372
validated_data["location"] = GEOSGeometry("POINT(%f %f)" % (lng, lat))
376373
validated_data["created_by"] = self.context["request"].user
377-
validated_data["is_locked"] = True
378-
validated_data["is_new_local_unit"] = True
374+
validated_data["status"] = LocalUnit.Status.UNVALIDATED
379375
return super().create(validated_data)
380376

381377
def update(self, instance, validated_data):
@@ -445,7 +441,6 @@ class PrivateLocalUnitSerializer(serializers.ModelSerializer):
445441
health_details = MiniHealthDataSerializer(read_only=True, source="health")
446442
status_details = serializers.CharField(source="get_status_display", read_only=True)
447443
modified_by_details = LocalUnitMiniUserSerializer(source="modified_by", read_only=True)
448-
is_locked = serializers.BooleanField(read_only=True)
449444
update_reason_overview = serializers.CharField(read_only=True)
450445

451446
class Meta:
@@ -471,8 +466,6 @@ class Meta:
471466
"phone",
472467
"modified_at",
473468
"modified_by_details",
474-
"is_locked",
475-
"is_new_local_unit",
476469
"bulk_upload",
477470
"update_reason_overview",
478471
)
@@ -944,9 +937,7 @@ def validate(self, validated_data):
944937
)
945938

946939
validated_data["location"] = GEOSGeometry("POINT(%f %f)" % (longitude, latitude))
947-
validated_data["validated"] = True # This might deprecate soon
948-
validated_data["is_locked"] = True
949-
validated_data["status"] = LocalUnit.Status.VALIDATED
940+
validated_data["status"] = LocalUnit.Status.EXTERNALLY_MANAGED
950941

951942
# NOTE: Bulk upload doesn't call create() method
952943
health_data = validated_data.pop("health", None)

0 commit comments

Comments
 (0)