Skip to content

Commit

Permalink
Merge pull request #64 from wmo-raf/dev
Browse files Browse the repository at this point in the history
Updates and Bug Fixes
  • Loading branch information
erick-otenyo authored May 31, 2024
2 parents f9ab119 + 7386803 commit af1c620
Show file tree
Hide file tree
Showing 10 changed files with 50 additions and 84 deletions.
12 changes: 4 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,9 @@ http://docs.oasis-open.org/emergency/cap/v1.2/CAP-v1.2-os.html
- Inbuilt CAP validation. The page will not save if you have not input the required data according to CAP standard
- User-friendly alert area map tool that allows multiple ways of constructing alert geographic areas, while keeping the
interface simple
- Select country official administrative boundaries for different levels (Admin 1, Admin 2, Admin 3), with in-built
simplification of complex boundaries
- Draw a polygon
- Draw a circle
- Selecting predefined areas that you create beforehand for common alert areas
- Use Geocode key values
- Inbuilt publishing workflow using Wagtail's powerful page model, with automated emails to composers and approvers
- Collaborate with team members using inbuilt comments (similar to how you could do in Word) with automated
Expand Down Expand Up @@ -188,12 +187,7 @@ area contains the following elements:
- Altitude (altitude),
- Ceiling (ceiling)

The Alert area input has 4 selector options:

- Admin Boundary (area is picked from predefined boundaries). To use this option, ensure that admin boundaries are
initially loaded. Refer to [Setting up boundaries](#setting-up-boundaries) section.

![Alert Area boundary](images/alert_sections/alert_area_boundary.png "Alert Area Boundary")
The Alert area input has multiple selector options:

- Polygon (drawing a polygon)

Expand All @@ -203,6 +197,8 @@ The Alert area input has 4 selector options:

![Alert Area circle](images/alert_sections/alert_area_circle.png "Alert Area Circle")

- Predefined Area (selecting a predefined area that you create beforehand for common alert areas)

- Geocode (specifying area geocode name and value). Using this option presumes knowledge of the coding system

![Alert Area geocode](images/alert_sections/alert_area_geocode.png "Alert Area Geocode")
Expand Down
66 changes: 1 addition & 65 deletions capeditor/blocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,69 +160,6 @@ class AlertResponseType(blocks.StructBlock):
"target audience"))


class AlertAreaBoundaryStructValue(StructValue):
@cached_property
def area(self):
geom_geojson_str = self.get("boundary")
geom_geojson_dict = json.loads(geom_geojson_str)
geom_shape = shape(geom_geojson_dict)

polygons = []

if isinstance(geom_shape, Polygon):
polygons.append(geom_shape)
else:
polygons = list(geom_shape.geoms)

polygons_data = []
for polygon in polygons:
coords = " ".join(["{},{}".format(y, x) for x, y in list(polygon.exterior.reverse().coords)])
polygons_data.append(coords)

area_data = {
"areaDesc": self.get("areaDesc"),
"polygons": polygons_data
}

if self.get("altitude"):
area_data.update({"altitude": self.get("altitude")})
if self.get("ceiling"):
area_data.update({"ceiling": self.get("ceiling")})

return area_data

@cached_property
def geojson(self):
polygon = self.get("boundary")
return json.loads(polygon)


class AlertAreaBoundaryBlock(blocks.StructBlock):
class Meta:
value_class = AlertAreaBoundaryStructValue

ADMIN_LEVEL_CHOICES = (
(0, _("Level 0")),
(1, _("Level 1")),
(2, _("Level 2")),
(3, _("Level 3"))
)

areaDesc = blocks.TextBlock(label=_("Affected areas / Regions"),
help_text=_("The text describing the affected area of the alert message"))
admin_level = blocks.ChoiceBlock(choices=ADMIN_LEVEL_CHOICES, default=1, label=_("Administrative Level"))
boundary = BoundaryFieldBlock(label=_("Boundary"),
help_text=_("The paired values of points defining a polygon that delineates "
"the affected area of the alert message"))

altitude = blocks.CharBlock(max_length=100, required=False, label=_("Altitude"),
help_text=_("The specific or minimum altitude of the affected "
"area of the alert message"))
ceiling = blocks.CharBlock(max_length=100, required=False, label=_("Ceiling"),
help_text=_("The maximum altitude of the affected area of the alert message."
"MUST NOT be used except in combination with the altitude element. "))


class AlertAreaPolygonStructValue(StructValue):
@cached_property
def area(self):
Expand Down Expand Up @@ -679,12 +616,11 @@ class AlertInfo(blocks.StructBlock):
audience = blocks.CharBlock(max_length=255, required=False, label=_("Audience"),
help_text=AUDIENCE_HELP_TEXT)
area = blocks.StreamBlock([
("boundary_block", AlertAreaBoundaryBlock(label=_("Admin Boundary"))),
("polygon_block", AlertAreaPolygonBlock(label=_("Draw Polygon"))),
("circle_block", AlertAreaCircleBlock(label=_("Circle"))),
("geocode_block", AlertAreaGeocodeBlock(label=_("Geocode"))),
("predefined_block", AlertAreaPredefined(label=_("Predefined Area"))),
], label=_("Alert Area"), help_text=_("Admin Boundary, Polygon, Circle or Geocode"))
], label=_("Alert Area"), help_text=_("Polygon, Circle, Predefined area or Geocode"))

resource = blocks.StreamBlock([
("file_resource", FileResource()),
Expand Down
7 changes: 7 additions & 0 deletions capeditor/cap_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ class CapSetting(BaseSiteSetting, ClusterableModel):
help_text=_("Email of the sending institution"))
sender_name = models.CharField(max_length=255, blank=True, null=True, verbose_name=_("CAP Sender Name"),
help_text=_("Name of the sending institution"))
wmo_oid = models.CharField(max_length=255, blank=True, null=True,
verbose_name=_("WMO Register of Alerting Authorities OID"),
help_text=_("WMO Register of Alerting Authorities "
"Object Identifier (OID) of the sending institution. "
"This will be used to generate CAP messages identifiers"))

logo = models.ForeignKey("wagtailimages.Image", null=True, blank=True, on_delete=models.SET_NULL, related_name="+",
verbose_name=_("Logo of the sending institution"))

Expand All @@ -45,6 +51,7 @@ class Meta:
ObjectList([
FieldPanel("sender_name"),
FieldPanel("sender"),
FieldPanel("wmo_oid"),
FieldPanel("logo"),
FieldPanel("contacts"),
], heading=_("Sender Details")),
Expand Down
18 changes: 18 additions & 0 deletions capeditor/migrations/0006_capsetting_wmo_oid.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 4.2.11 on 2024-05-30 13:21

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('capeditor', '0005_alter_capsetting_sender'),
]

operations = [
migrations.AddField(
model_name='capsetting',
name='wmo_oid',
field=models.CharField(blank=True, max_length=255, null=True, verbose_name='WMO Register of Alerting Authorities OID'),
),
]
15 changes: 9 additions & 6 deletions capeditor/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,10 @@ def clean(self):
for reference in references:
ref_alert_page = reference.value.get("ref_alert").specific
if ref_alert_page:
alerts_ids.append(ref_alert_page.identifier)
if hasattr(ref_alert_page, 'identifier'):
alerts_ids.append(ref_alert_page.identifier)
else:
alerts_ids.append(ref_alert_page.id)

# check if the same alert is selected more than once
if len(alerts_ids) != len(set(alerts_ids)):
Expand All @@ -118,7 +121,7 @@ def clean(self):
class AbstractCapAlertPage(Page):
base_form_class = CapAlertPageForm

exclude_fields_in_copy = ["identifier"]
exclude_fields_in_copy = ["guid"]

STATUS_CHOICES = (
("Draft", _("Draft - A preliminary template or draft, not actionable in its current form")),
Expand Down Expand Up @@ -146,9 +149,8 @@ class AbstractCapAlertPage(Page):
('Private', _("Private - For dissemination only to specified addresses"
" as in the addresses field in the alert")),
)

identifier = models.UUIDField(default=uuid.uuid4, editable=False, verbose_name=_("Identifier"),
help_text=_("Unique ID. Auto generated on creation."), unique=True)
guid = models.UUIDField(default=uuid.uuid4, editable=False, verbose_name=_("Id"),
help_text=_("Unique ID. Auto generated on creation."), unique=True)
sender = models.CharField(max_length=255, verbose_name=_("Sender"), default=get_default_sender,
help_text=_("Identifies the originator of an alert. "
"For example the website address of the institution"))
Expand Down Expand Up @@ -292,7 +294,8 @@ def infos(self):
"expires": expires,
"expired": expired,
"properties": {
"id": self.identifier,
"id": self.guid,
"identifier": self.identifier if hasattr(self, "identifier") else self.guid,
"event": event,
"event_type": info.value.get('event'),
"headline": info.value.get("headline"),
Expand Down
6 changes: 6 additions & 0 deletions capeditor/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ class AlertSerializer(serializers.ModelSerializer):
references = serializers.SerializerMethodField()
info = serializers.SerializerMethodField()
incidents = serializers.SerializerMethodField()
identifier = serializers.SerializerMethodField()

class Meta:
fields = [
Expand All @@ -46,6 +47,11 @@ class Meta:
"info",
]

def get_identifier(self, obj):
if hasattr(obj, 'identifier'):
return obj.identifier
return obj.guid

def get_info(self, obj):
request = self.context.get("request")
info_values = []
Expand Down
4 changes: 2 additions & 2 deletions sandbox/home/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ class CapAlertPage(AbstractCapAlertPage):

parent_page_type = ["home.HomePage"]
subpage_types = []
exclude_fields_in_copy = ["identifier", ]
exclude_fields_in_copy = ["guid", ]

"""An implementation of MetadataMixin for Wagtail pages."""
search_image = models.ForeignKey(
Expand Down Expand Up @@ -92,7 +92,7 @@ class Meta:

@cached_property
def xml_link(self):
return reverse("cap_alert_detail", args=(self.identifier,))
return reverse("cap_alert_detail", args=(self.guid,))


def on_publish_cap_alert(sender, **kwargs):
Expand Down
2 changes: 1 addition & 1 deletion sandbox/home/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@

urlpatterns = [
path('api/cap/feed.xml', AlertList.as_view(), name="cap_alert_feed"),
path("api/cap/<uuid:identifier>.xml", AlertDetail.as_view(), name="cap_alert_detail"),
path("api/cap/<uuid:guid>.xml", AlertDetail.as_view(), name="cap_alert_detail"),
]
2 changes: 1 addition & 1 deletion sandbox/home/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,4 @@ class AlertDetail(generics.RetrieveAPIView):
renderer_classes = (CapXMLRenderer,)
queryset = CapAlertPage.objects.live()

lookup_field = "identifier"
lookup_field = "guid"
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[metadata]
name = capeditor
version = 0.5.7
version = 0.5.8
description = Wagtail based CAP composer
long_description = file:README.md
long_description_content_type = text/markdown
Expand Down

0 comments on commit af1c620

Please sign in to comment.