From 481838a825c442cc9838b4bfc5d9d1798677392d Mon Sep 17 00:00:00 2001 From: thenav56 Date: Mon, 24 Jun 2024 20:30:26 +0545 Subject: [PATCH 01/11] Upgrade sentry-sdk --- main/sentry.py | 2 +- poetry.lock | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/main/sentry.py b/main/sentry.py index b916414..ee532cc 100644 --- a/main/sentry.py +++ b/main/sentry.py @@ -22,10 +22,10 @@ def init_sentry(app_type, tags={}, **config): integrations = [ + StrawberryIntegration(async_execution=True), DjangoIntegration(), CeleryIntegration(), RedisIntegration(), - StrawberryIntegration(async_execution=True), ] sentry_sdk.init( **config, diff --git a/poetry.lock b/poetry.lock index 4b7c7ff..66d9a1f 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1215,13 +1215,13 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] [[package]] name = "sentry-sdk" -version = "2.3.1" +version = "2.6.0" description = "Python client for Sentry (https://sentry.io)" optional = false python-versions = ">=3.6" files = [ - {file = "sentry_sdk-2.3.1-py2.py3-none-any.whl", hash = "sha256:c5aeb095ba226391d337dd42a6f9470d86c9fc236ecc71cfc7cd1942b45010c6"}, - {file = "sentry_sdk-2.3.1.tar.gz", hash = "sha256:139a71a19f5e9eb5d3623942491ce03cf8ebc14ea2e39ba3e6fe79560d8a5b1f"}, + {file = "sentry_sdk-2.6.0-py2.py3-none-any.whl", hash = "sha256:422b91cb49378b97e7e8d0e8d5a1069df23689d45262b86f54988a7db264e874"}, + {file = "sentry_sdk-2.6.0.tar.gz", hash = "sha256:65cc07e9c6995c5e316109f138570b32da3bd7ff8d0d0ee4aaf2628c3dd8127d"}, ] [package.dependencies] From 3a39182c59006edd4487eb2e24cd0a386ad560c4 Mon Sep 17 00:00:00 2001 From: thenav56 Date: Mon, 24 Jun 2024 21:17:36 +0545 Subject: [PATCH 02/11] Use middlware for Custom Sentry transaction name --- main/middlewares.py | 31 +++++++++++++++++++++++++++++++ main/settings.py | 1 + main/urls.py | 1 + 3 files changed, 33 insertions(+) create mode 100644 main/middlewares.py diff --git a/main/middlewares.py b/main/middlewares.py new file mode 100644 index 0000000..1c57ef1 --- /dev/null +++ b/main/middlewares.py @@ -0,0 +1,31 @@ +import json + +from django.urls import reverse +from sentry_sdk import Scope + + +class SentryTransactionMiddleware: + graphql_url = reverse('graphql') + + def __init__(self, get_response): + self.get_response = get_response + + def __call__(self, request): + if request.path == self.graphql_url: + operation_type = "Query" + operation_name = "Unknown" + try: + body = request.body.decode('utf-8') + if body: + # XXX: This will be repeated by Strawberry as well. + data = json.loads(body) + operation_name = data.get("operationName", operation_name) + if data.get("query", "").startswith("mutation"): + operation_type = "Mutation" + except Exception: + ... + + scope = Scope.get_current_scope() + scope.set_transaction_name(f"GraphQL/{operation_type}/{operation_name}") + + return self.get_response(request) diff --git a/main/settings.py b/main/settings.py index 6b46dc2..f207864 100644 --- a/main/settings.py +++ b/main/settings.py @@ -127,6 +127,7 @@ 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', + 'main.middlewares.SentryTransactionMiddleware', ] AUTHENTICATION_BACKENDS = [ diff --git a/main/urls.py b/main/urls.py index 3fb385c..3601844 100644 --- a/main/urls.py +++ b/main/urls.py @@ -31,6 +31,7 @@ schema=graphql_schema, graphiql=False, ), + name='graphql', ), path('', include('apps.cap_feed.urls')), ] From c68efb53bbc69ef888d974a89d87f1e58b2856c8 Mon Sep 17 00:00:00 2001 From: thenav56 Date: Mon, 24 Jun 2024 22:27:17 +0545 Subject: [PATCH 03/11] Add transaction support for Backend --- Dockerfile | 2 + README.md | 5 + apps/cap_feed/locale/ar/LC_MESSAGES/django.po | 288 ++++++++++++++++++ apps/cap_feed/locale/es/LC_MESSAGES/django.po | 287 +++++++++++++++++ apps/cap_feed/locale/fr/LC_MESSAGES/django.po | 287 +++++++++++++++++ apps/cap_feed/models.py | 139 ++++----- apps/user/locale/ar/LC_MESSAGES/django.po | 63 ++++ apps/user/locale/es/LC_MESSAGES/django.po | 62 ++++ apps/user/locale/fr/LC_MESSAGES/django.po | 62 ++++ apps/user/models.py | 8 +- deploy/run_web.sh | 1 + locale/ar/LC_MESSAGES/django.po | 24 ++ locale/es/LC_MESSAGES/django.po | 23 ++ locale/fr/LC_MESSAGES/django.po | 23 ++ main/settings.py | 1 + templates/admin/base_site.html | 2 +- 16 files changed, 1203 insertions(+), 74 deletions(-) create mode 100644 apps/cap_feed/locale/ar/LC_MESSAGES/django.po create mode 100644 apps/cap_feed/locale/es/LC_MESSAGES/django.po create mode 100644 apps/cap_feed/locale/fr/LC_MESSAGES/django.po create mode 100644 apps/user/locale/ar/LC_MESSAGES/django.po create mode 100644 apps/user/locale/es/LC_MESSAGES/django.po create mode 100644 apps/user/locale/fr/LC_MESSAGES/django.po create mode 100644 locale/ar/LC_MESSAGES/django.po create mode 100644 locale/es/LC_MESSAGES/django.po create mode 100644 locale/fr/LC_MESSAGES/django.po diff --git a/Dockerfile b/Dockerfile index eb618f9..b312f8b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,6 +12,8 @@ RUN apt-get update -y \ && apt-get install -y --no-install-recommends \ # Build required packages gcc libc-dev gdal-bin libproj-dev \ + # Django translation + gettext \ # Helper packages wait-for-it \ # Upgrade pip and install python packages for code diff --git a/README.md b/README.md index a53800a..81992ce 100644 --- a/README.md +++ b/README.md @@ -242,3 +242,8 @@ Start celery worker and scheduler on deployment: celery multi start w1 -A main -l info celery -A main beat --detach -l info ``` + +Translation +``` +docker compose exec web ./manage.py makemessages -l es -l ar -l fr --ignore legacy --ignore main +``` diff --git a/apps/cap_feed/locale/ar/LC_MESSAGES/django.po b/apps/cap_feed/locale/ar/LC_MESSAGES/django.po new file mode 100644 index 0000000..455b99e --- /dev/null +++ b/apps/cap_feed/locale/ar/LC_MESSAGES/django.po @@ -0,0 +1,288 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-06-24 16:20+0000\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 " +"&& n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n" +#: apps/cap_feed/models.py:109 +msgid "5 seconds" +msgstr "" + +#: apps/cap_feed/models.py:110 +msgid "10 seconds" +msgstr "" + +#: apps/cap_feed/models.py:111 +msgid "15 seconds" +msgstr "" + +#: apps/cap_feed/models.py:112 +msgid "20 seconds" +msgstr "" + +#: apps/cap_feed/models.py:113 +msgid "25 seconds" +msgstr "" + +#: apps/cap_feed/models.py:114 +msgid "30 seconds" +msgstr "" + +#: apps/cap_feed/models.py:115 +msgid "35 seconds" +msgstr "" + +#: apps/cap_feed/models.py:116 +msgid "40 seconds" +msgstr "" + +#: apps/cap_feed/models.py:117 +msgid "45 seconds" +msgstr "" + +#: apps/cap_feed/models.py:118 +msgid "50 seconds" +msgstr "" + +#: apps/cap_feed/models.py:119 +msgid "55 seconds" +msgstr "" + +#: apps/cap_feed/models.py:120 +msgid "60 seconds" +msgstr "" + +#: apps/cap_feed/models.py:121 +msgid "10 minutes" +msgstr "" + +#: apps/cap_feed/models.py:124 +msgid "ATOM" +msgstr "" + +#: apps/cap_feed/models.py:125 +msgid "RSS" +msgstr "" + +#: apps/cap_feed/models.py:126 +msgid "NWS_US" +msgstr "" + +#: apps/cap_feed/models.py:129 +msgid "Active" +msgstr "" + +#: apps/cap_feed/models.py:130 +msgid "Testing" +msgstr "" + +#: apps/cap_feed/models.py:131 +msgid "Inactive" +msgstr "" + +#: apps/cap_feed/models.py:132 +msgid "Unusable" +msgstr "" + +#: apps/cap_feed/models.py:167 +msgid "Actual" +msgstr "" + +#: apps/cap_feed/models.py:168 +msgid "Exercise" +msgstr "" + +#: apps/cap_feed/models.py:169 +msgid "System" +msgstr "" + +#: apps/cap_feed/models.py:170 +msgid "Test" +msgstr "" + +#: apps/cap_feed/models.py:171 +msgid "Draft" +msgstr "" + +#: apps/cap_feed/models.py:174 +msgid "Alert" +msgstr "" + +#: apps/cap_feed/models.py:175 +msgid "Update" +msgstr "" + +#: apps/cap_feed/models.py:176 +msgid "Cancel" +msgstr "" + +#: apps/cap_feed/models.py:177 +msgid "Ack" +msgstr "" + +#: apps/cap_feed/models.py:178 +msgid "Error" +msgstr "" + +#: apps/cap_feed/models.py:181 +msgid "Public" +msgstr "" + +#: apps/cap_feed/models.py:182 +msgid "Restricted" +msgstr "" + +#: apps/cap_feed/models.py:183 +msgid "Private" +msgstr "" + +#: apps/cap_feed/models.py:244 +msgid "Geo" +msgstr "" + +#: apps/cap_feed/models.py:245 +msgid "Met" +msgstr "" + +#: apps/cap_feed/models.py:246 +msgid "Safety" +msgstr "" + +#: apps/cap_feed/models.py:247 +msgid "Security" +msgstr "" + +#: apps/cap_feed/models.py:248 +msgid "Rescue" +msgstr "" + +#: apps/cap_feed/models.py:249 +msgid "Fire" +msgstr "" + +#: apps/cap_feed/models.py:250 +msgid "Health" +msgstr "" + +#: apps/cap_feed/models.py:251 +msgid "Env" +msgstr "" + +#: apps/cap_feed/models.py:252 +msgid "Transport" +msgstr "" + +#: apps/cap_feed/models.py:253 +msgid "Infra" +msgstr "" + +#: apps/cap_feed/models.py:254 +msgid "CBRNE" +msgstr "" + +#: apps/cap_feed/models.py:255 +msgid "Other" +msgstr "" + +#: apps/cap_feed/models.py:258 +msgid "Shelter" +msgstr "" + +#: apps/cap_feed/models.py:259 +msgid "Evacuate" +msgstr "" + +#: apps/cap_feed/models.py:260 +msgid "Prepare" +msgstr "" + +#: apps/cap_feed/models.py:261 +msgid "Execute" +msgstr "" + +#: apps/cap_feed/models.py:262 +msgid "Avoid" +msgstr "" + +#: apps/cap_feed/models.py:263 +msgid "Monitor" +msgstr "" + +#: apps/cap_feed/models.py:264 +msgid "Assess" +msgstr "" + +#: apps/cap_feed/models.py:265 +msgid "AllClear" +msgstr "" + +#: apps/cap_feed/models.py:266 +msgid "None" +msgstr "" + +#: apps/cap_feed/models.py:269 +msgid "Immediate" +msgstr "" + +#: apps/cap_feed/models.py:270 +msgid "Expected" +msgstr "" + +#: apps/cap_feed/models.py:271 +msgid "Future" +msgstr "" + +#: apps/cap_feed/models.py:272 +msgid "Past" +msgstr "" + +#: apps/cap_feed/models.py:273 apps/cap_feed/models.py:280 +#: apps/cap_feed/models.py:287 +msgid "Unknown" +msgstr "" + +#: apps/cap_feed/models.py:276 +msgid "Extreme" +msgstr "" + +#: apps/cap_feed/models.py:277 +msgid "Severe" +msgstr "" + +#: apps/cap_feed/models.py:278 +msgid "Moderate" +msgstr "" + +#: apps/cap_feed/models.py:279 +msgid "Minor" +msgstr "" + +#: apps/cap_feed/models.py:283 +msgid "Observed" +msgstr "" + +#: apps/cap_feed/models.py:284 +msgid "Likely" +msgstr "" + +#: apps/cap_feed/models.py:285 +msgid "Possible" +msgstr "" + +#: apps/cap_feed/models.py:286 +msgid "Unlikely" +msgstr "" diff --git a/apps/cap_feed/locale/es/LC_MESSAGES/django.po b/apps/cap_feed/locale/es/LC_MESSAGES/django.po new file mode 100644 index 0000000..b189712 --- /dev/null +++ b/apps/cap_feed/locale/es/LC_MESSAGES/django.po @@ -0,0 +1,287 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-06-24 16:20+0000\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +#: apps/cap_feed/models.py:109 +msgid "5 seconds" +msgstr "" + +#: apps/cap_feed/models.py:110 +msgid "10 seconds" +msgstr "" + +#: apps/cap_feed/models.py:111 +msgid "15 seconds" +msgstr "" + +#: apps/cap_feed/models.py:112 +msgid "20 seconds" +msgstr "" + +#: apps/cap_feed/models.py:113 +msgid "25 seconds" +msgstr "" + +#: apps/cap_feed/models.py:114 +msgid "30 seconds" +msgstr "" + +#: apps/cap_feed/models.py:115 +msgid "35 seconds" +msgstr "" + +#: apps/cap_feed/models.py:116 +msgid "40 seconds" +msgstr "" + +#: apps/cap_feed/models.py:117 +msgid "45 seconds" +msgstr "" + +#: apps/cap_feed/models.py:118 +msgid "50 seconds" +msgstr "" + +#: apps/cap_feed/models.py:119 +msgid "55 seconds" +msgstr "" + +#: apps/cap_feed/models.py:120 +msgid "60 seconds" +msgstr "" + +#: apps/cap_feed/models.py:121 +msgid "10 minutes" +msgstr "" + +#: apps/cap_feed/models.py:124 +msgid "ATOM" +msgstr "" + +#: apps/cap_feed/models.py:125 +msgid "RSS" +msgstr "" + +#: apps/cap_feed/models.py:126 +msgid "NWS_US" +msgstr "" + +#: apps/cap_feed/models.py:129 +msgid "Active" +msgstr "" + +#: apps/cap_feed/models.py:130 +msgid "Testing" +msgstr "" + +#: apps/cap_feed/models.py:131 +msgid "Inactive" +msgstr "" + +#: apps/cap_feed/models.py:132 +msgid "Unusable" +msgstr "" + +#: apps/cap_feed/models.py:167 +msgid "Actual" +msgstr "" + +#: apps/cap_feed/models.py:168 +msgid "Exercise" +msgstr "" + +#: apps/cap_feed/models.py:169 +msgid "System" +msgstr "" + +#: apps/cap_feed/models.py:170 +msgid "Test" +msgstr "" + +#: apps/cap_feed/models.py:171 +msgid "Draft" +msgstr "" + +#: apps/cap_feed/models.py:174 +msgid "Alert" +msgstr "" + +#: apps/cap_feed/models.py:175 +msgid "Update" +msgstr "" + +#: apps/cap_feed/models.py:176 +msgid "Cancel" +msgstr "" + +#: apps/cap_feed/models.py:177 +msgid "Ack" +msgstr "" + +#: apps/cap_feed/models.py:178 +msgid "Error" +msgstr "" + +#: apps/cap_feed/models.py:181 +msgid "Public" +msgstr "" + +#: apps/cap_feed/models.py:182 +msgid "Restricted" +msgstr "" + +#: apps/cap_feed/models.py:183 +msgid "Private" +msgstr "" + +#: apps/cap_feed/models.py:244 +msgid "Geo" +msgstr "" + +#: apps/cap_feed/models.py:245 +msgid "Met" +msgstr "" + +#: apps/cap_feed/models.py:246 +msgid "Safety" +msgstr "" + +#: apps/cap_feed/models.py:247 +msgid "Security" +msgstr "" + +#: apps/cap_feed/models.py:248 +msgid "Rescue" +msgstr "" + +#: apps/cap_feed/models.py:249 +msgid "Fire" +msgstr "" + +#: apps/cap_feed/models.py:250 +msgid "Health" +msgstr "" + +#: apps/cap_feed/models.py:251 +msgid "Env" +msgstr "" + +#: apps/cap_feed/models.py:252 +msgid "Transport" +msgstr "" + +#: apps/cap_feed/models.py:253 +msgid "Infra" +msgstr "" + +#: apps/cap_feed/models.py:254 +msgid "CBRNE" +msgstr "" + +#: apps/cap_feed/models.py:255 +msgid "Other" +msgstr "" + +#: apps/cap_feed/models.py:258 +msgid "Shelter" +msgstr "" + +#: apps/cap_feed/models.py:259 +msgid "Evacuate" +msgstr "" + +#: apps/cap_feed/models.py:260 +msgid "Prepare" +msgstr "" + +#: apps/cap_feed/models.py:261 +msgid "Execute" +msgstr "" + +#: apps/cap_feed/models.py:262 +msgid "Avoid" +msgstr "" + +#: apps/cap_feed/models.py:263 +msgid "Monitor" +msgstr "" + +#: apps/cap_feed/models.py:264 +msgid "Assess" +msgstr "" + +#: apps/cap_feed/models.py:265 +msgid "AllClear" +msgstr "" + +#: apps/cap_feed/models.py:266 +msgid "None" +msgstr "" + +#: apps/cap_feed/models.py:269 +msgid "Immediate" +msgstr "" + +#: apps/cap_feed/models.py:270 +msgid "Expected" +msgstr "" + +#: apps/cap_feed/models.py:271 +msgid "Future" +msgstr "" + +#: apps/cap_feed/models.py:272 +msgid "Past" +msgstr "" + +#: apps/cap_feed/models.py:273 apps/cap_feed/models.py:280 +#: apps/cap_feed/models.py:287 +msgid "Unknown" +msgstr "" + +#: apps/cap_feed/models.py:276 +msgid "Extreme" +msgstr "" + +#: apps/cap_feed/models.py:277 +msgid "Severe" +msgstr "" + +#: apps/cap_feed/models.py:278 +msgid "Moderate" +msgstr "" + +#: apps/cap_feed/models.py:279 +msgid "Minor" +msgstr "" + +#: apps/cap_feed/models.py:283 +msgid "Observed" +msgstr "" + +#: apps/cap_feed/models.py:284 +msgid "Likely" +msgstr "" + +#: apps/cap_feed/models.py:285 +msgid "Possible" +msgstr "" + +#: apps/cap_feed/models.py:286 +msgid "Unlikely" +msgstr "" diff --git a/apps/cap_feed/locale/fr/LC_MESSAGES/django.po b/apps/cap_feed/locale/fr/LC_MESSAGES/django.po new file mode 100644 index 0000000..10caddf --- /dev/null +++ b/apps/cap_feed/locale/fr/LC_MESSAGES/django.po @@ -0,0 +1,287 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-06-24 16:20+0000\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" +#: apps/cap_feed/models.py:109 +msgid "5 seconds" +msgstr "5 secondes" + +#: apps/cap_feed/models.py:110 +msgid "10 seconds" +msgstr "10 secondes" + +#: apps/cap_feed/models.py:111 +msgid "15 seconds" +msgstr "15 secondes" + +#: apps/cap_feed/models.py:112 +msgid "20 seconds" +msgstr "20 secondes" + +#: apps/cap_feed/models.py:113 +msgid "25 seconds" +msgstr "25 secondes" + +#: apps/cap_feed/models.py:114 +msgid "30 seconds" +msgstr "30 secondes" + +#: apps/cap_feed/models.py:115 +msgid "35 seconds" +msgstr "35 secondes" + +#: apps/cap_feed/models.py:116 +msgid "40 seconds" +msgstr "40 secondes" + +#: apps/cap_feed/models.py:117 +msgid "45 seconds" +msgstr "45 secondes" + +#: apps/cap_feed/models.py:118 +msgid "50 seconds" +msgstr "50 secondes" + +#: apps/cap_feed/models.py:119 +msgid "55 seconds" +msgstr "55 secondes" + +#: apps/cap_feed/models.py:120 +msgid "60 seconds" +msgstr "60 secondes" + +#: apps/cap_feed/models.py:121 +msgid "10 minutes" +msgstr "" + +#: apps/cap_feed/models.py:124 +msgid "ATOM" +msgstr "" + +#: apps/cap_feed/models.py:125 +msgid "RSS" +msgstr "" + +#: apps/cap_feed/models.py:126 +msgid "NWS_US" +msgstr "" + +#: apps/cap_feed/models.py:129 +msgid "Active" +msgstr "" + +#: apps/cap_feed/models.py:130 +msgid "Testing" +msgstr "" + +#: apps/cap_feed/models.py:131 +msgid "Inactive" +msgstr "" + +#: apps/cap_feed/models.py:132 +msgid "Unusable" +msgstr "" + +#: apps/cap_feed/models.py:167 +msgid "Actual" +msgstr "" + +#: apps/cap_feed/models.py:168 +msgid "Exercise" +msgstr "" + +#: apps/cap_feed/models.py:169 +msgid "System" +msgstr "" + +#: apps/cap_feed/models.py:170 +msgid "Test" +msgstr "" + +#: apps/cap_feed/models.py:171 +msgid "Draft" +msgstr "" + +#: apps/cap_feed/models.py:174 +msgid "Alert" +msgstr "" + +#: apps/cap_feed/models.py:175 +msgid "Update" +msgstr "" + +#: apps/cap_feed/models.py:176 +msgid "Cancel" +msgstr "" + +#: apps/cap_feed/models.py:177 +msgid "Ack" +msgstr "" + +#: apps/cap_feed/models.py:178 +msgid "Error" +msgstr "" + +#: apps/cap_feed/models.py:181 +msgid "Public" +msgstr "" + +#: apps/cap_feed/models.py:182 +msgid "Restricted" +msgstr "" + +#: apps/cap_feed/models.py:183 +msgid "Private" +msgstr "" + +#: apps/cap_feed/models.py:244 +msgid "Geo" +msgstr "" + +#: apps/cap_feed/models.py:245 +msgid "Met" +msgstr "" + +#: apps/cap_feed/models.py:246 +msgid "Safety" +msgstr "" + +#: apps/cap_feed/models.py:247 +msgid "Security" +msgstr "" + +#: apps/cap_feed/models.py:248 +msgid "Rescue" +msgstr "" + +#: apps/cap_feed/models.py:249 +msgid "Fire" +msgstr "" + +#: apps/cap_feed/models.py:250 +msgid "Health" +msgstr "" + +#: apps/cap_feed/models.py:251 +msgid "Env" +msgstr "" + +#: apps/cap_feed/models.py:252 +msgid "Transport" +msgstr "" + +#: apps/cap_feed/models.py:253 +msgid "Infra" +msgstr "" + +#: apps/cap_feed/models.py:254 +msgid "CBRNE" +msgstr "" + +#: apps/cap_feed/models.py:255 +msgid "Other" +msgstr "" + +#: apps/cap_feed/models.py:258 +msgid "Shelter" +msgstr "" + +#: apps/cap_feed/models.py:259 +msgid "Evacuate" +msgstr "" + +#: apps/cap_feed/models.py:260 +msgid "Prepare" +msgstr "" + +#: apps/cap_feed/models.py:261 +msgid "Execute" +msgstr "" + +#: apps/cap_feed/models.py:262 +msgid "Avoid" +msgstr "" + +#: apps/cap_feed/models.py:263 +msgid "Monitor" +msgstr "" + +#: apps/cap_feed/models.py:264 +msgid "Assess" +msgstr "" + +#: apps/cap_feed/models.py:265 +msgid "AllClear" +msgstr "" + +#: apps/cap_feed/models.py:266 +msgid "None" +msgstr "" + +#: apps/cap_feed/models.py:269 +msgid "Immediate" +msgstr "" + +#: apps/cap_feed/models.py:270 +msgid "Expected" +msgstr "" + +#: apps/cap_feed/models.py:271 +msgid "Future" +msgstr "" + +#: apps/cap_feed/models.py:272 +msgid "Past" +msgstr "" + +#: apps/cap_feed/models.py:273 apps/cap_feed/models.py:280 +#: apps/cap_feed/models.py:287 +msgid "Unknown" +msgstr "" + +#: apps/cap_feed/models.py:276 +msgid "Extreme" +msgstr "" + +#: apps/cap_feed/models.py:277 +msgid "Severe" +msgstr "" + +#: apps/cap_feed/models.py:278 +msgid "Moderate" +msgstr "" + +#: apps/cap_feed/models.py:279 +msgid "Minor" +msgstr "" + +#: apps/cap_feed/models.py:283 +msgid "Observed" +msgstr "" + +#: apps/cap_feed/models.py:284 +msgid "Likely" +msgstr "" + +#: apps/cap_feed/models.py:285 +msgid "Possible" +msgstr "" + +#: apps/cap_feed/models.py:286 +msgid "Unlikely" +msgstr "" diff --git a/apps/cap_feed/models.py b/apps/cap_feed/models.py index 3ca6b84..bbd90ad 100644 --- a/apps/cap_feed/models.py +++ b/apps/cap_feed/models.py @@ -8,6 +8,7 @@ from django.db.models.signals import post_save from django.dispatch import receiver from django.utils import timezone +from django.utils.translation import gettext_lazy as _ from iso639 import iter_langs if TYPE_CHECKING: @@ -105,30 +106,30 @@ class PoolingInterval(models.IntegerChoices): Generated using: range(5, 65, 5): """ - I_05 = 5, '5 seconds' - I_10 = 10, '10 seconds' - I_15 = 15, '15 seconds' - I_20 = 20, '20 seconds' - I_25 = 25, '25 seconds' - I_30 = 30, '30 seconds' - I_35 = 35, '35 seconds' - I_40 = 40, '40 seconds' - I_45 = 45, '45 seconds' - I_50 = 50, '50 seconds' - I_55 = 55, '55 seconds' - I_60 = 60, '60 seconds' - I_10m = 600, '10 minutes' + I_05 = 5, _('5 seconds') + I_10 = 10, _('10 seconds') + I_15 = 15, _('15 seconds') + I_20 = 20, _('20 seconds') + I_25 = 25, _('25 seconds') + I_30 = 30, _('30 seconds') + I_35 = 35, _('35 seconds') + I_40 = 40, _('40 seconds') + I_45 = 45, _('45 seconds') + I_50 = 50, _('50 seconds') + I_55 = 55, _('55 seconds') + I_60 = 60, _('60 seconds') + I_10m = 600, _('10 minutes') class Format(models.TextChoices): - ATOM = 'atom', 'ATOM' - RSS = 'rss', 'RSS' - NWS_US = 'nws_us', 'NWS_US' + ATOM = 'atom', _('ATOM') + RSS = 'rss', _('RSS') + NWS_US = 'nws_us', _('NWS_US') class Status(models.TextChoices): - ACTIVE = 'active', 'Active' - TESTING = 'testing', 'Testing' - INACTIVE = 'inactive', 'Inactive' - UNUSABLE = 'unusable', 'Unusable' + ACTIVE = 'active', _('Active') + TESTING = 'testing', _('Testing') + INACTIVE = 'inactive', _('Inactive') + UNUSABLE = 'unusable', _('Unusable') url = models.CharField(unique=True) country = models.ForeignKey(Country, on_delete=models.CASCADE) @@ -163,23 +164,23 @@ def __str__(self): class Alert(models.Model): class Status(models.TextChoices): - ACTUAL = 'Actual', 'Actual' - EXERCISE = 'Exercise', 'Exercise' - SYSTEM = 'System', 'System' - TEST = 'Test', 'Test' - DRAFT = 'Draft', 'Draft' + ACTUAL = 'Actual', _('Actual') + EXERCISE = 'Exercise', _('Exercise') + SYSTEM = 'System', _('System') + TEST = 'Test', _('Test') + DRAFT = 'Draft', _('Draft') class MsgType(models.TextChoices): - ALERT = 'Alert', 'Alert' - UPDATE = 'Update', 'Update' - CANCEL = 'Cancel', 'Cancel' - ACK = 'Ack', 'Ack' - ERROR = 'Error', 'Error' + ALERT = 'Alert', _('Alert') + UPDATE = 'Update', _('Update') + CANCEL = 'Cancel', _('Cancel') + ACK = 'Ack', _('Ack') + ERROR = 'Error', _('Error') class Scope(models.TextChoices): # XXX: Not used, maybe we need to use this in scope field? - PUBLIC = 'Public', 'Public' - RESTRICTED = 'Restricted', 'Restricted' - PRIVATE = 'Private', 'Private' + PUBLIC = 'Public', _('Public') + RESTRICTED = 'Restricted', _('Restricted') + PRIVATE = 'Private', _('Private') country = models.ForeignKey(Country, on_delete=models.CASCADE) admin1s = models.ManyToManyField(Admin1, through='AlertAdmin1') @@ -240,50 +241,50 @@ class AlertAdmin1(models.Model): class AlertInfo(models.Model): class Category(models.TextChoices): - GEO = 'Geo', 'Geo' - MET = 'Met', 'Met' - SAFETY = 'Safety', 'Safety' - SECURITY = 'Security', 'Security' - RESCUE = 'Rescue', 'Rescue' - FIRE = 'Fire', 'Fire' - HEALTH = 'Health', 'Health' - ENV = 'Env', 'Env' - TRANSPORT = 'Transport', 'Transport' - INFRA = 'Infra', 'Infra' - CBRNE = 'CBRNE', 'CBRNE' - OTHER = 'Other', 'Other' + GEO = 'Geo', _('Geo') + MET = 'Met', _('Met') + SAFETY = 'Safety', _('Safety') + SECURITY = 'Security', _('Security') + RESCUE = 'Rescue', _('Rescue') + FIRE = 'Fire', _('Fire') + HEALTH = 'Health', _('Health') + ENV = 'Env', _('Env') + TRANSPORT = 'Transport', _('Transport') + INFRA = 'Infra', _('Infra') + CBRNE = 'CBRNE', _('CBRNE') + OTHER = 'Other', _('Other') class ResponseType(models.TextChoices): - SHELTER = 'Shelter', 'Shelter' - EVACUATE = 'Evacuate', 'Evacuate' - PREPARE = 'Prepare', 'Prepare' - EXECUTE = 'Execute', 'Execute' - AVOID = 'Avoid', 'Avoid' - MONITOR = 'Monitor', 'Monitor' - ASSESS = 'Assess', 'Assess' - ALLCLEAR = 'AllClear', 'AllClear' - NONE = 'None', 'None' + SHELTER = 'Shelter', _('Shelter') + EVACUATE = 'Evacuate', _('Evacuate') + PREPARE = 'Prepare', _('Prepare') + EXECUTE = 'Execute', _('Execute') + AVOID = 'Avoid', _('Avoid') + MONITOR = 'Monitor', _('Monitor') + ASSESS = 'Assess', _('Assess') + ALLCLEAR = 'AllClear', _('AllClear') + NONE = 'None', _('None') class Urgency(models.TextChoices): - IMMEDIATE = 'Immediate', 'Immediate' - EXPECTED = 'Expected', 'Expected' - FUTURE = 'Future', 'Future' - PAST = 'Past', 'Past' - UNKNOWN = 'Unknown', 'Unknown' + IMMEDIATE = 'Immediate', _('Immediate') + EXPECTED = 'Expected', _('Expected') + FUTURE = 'Future', _('Future') + PAST = 'Past', _('Past') + UNKNOWN = 'Unknown', _('Unknown') class Severity(models.TextChoices): - EXTREME = 'Extreme', 'Extreme' - SEVERE = 'Severe', 'Severe' - MODERATE = 'Moderate', 'Moderate' - MINOR = 'Minor', 'Minor' - UNKNOWN = 'Unknown', 'Unknown' + EXTREME = 'Extreme', _('Extreme') + SEVERE = 'Severe', _('Severe') + MODERATE = 'Moderate', _('Moderate') + MINOR = 'Minor', _('Minor') + UNKNOWN = 'Unknown', _('Unknown') class Certainty(models.TextChoices): - OBSERVED = 'Observed', 'Observed' - LIKELY = 'Likely', 'Likely' - POSSIBLE = 'Possible', 'Possible' - UNLIKELY = 'Unlikely', 'Unlikely' - UNKNOWN = 'Unknown', 'Unknown' + OBSERVED = 'Observed', _('Observed') + LIKELY = 'Likely', _('Likely') + POSSIBLE = 'Possible', _('Possible') + UNLIKELY = 'Unlikely', _('Unlikely') + UNKNOWN = 'Unknown', _('Unknown') alert = models.ForeignKey(Alert, on_delete=models.CASCADE, related_name='infos') diff --git a/apps/user/locale/ar/LC_MESSAGES/django.po b/apps/user/locale/ar/LC_MESSAGES/django.po new file mode 100644 index 0000000..ece0562 --- /dev/null +++ b/apps/user/locale/ar/LC_MESSAGES/django.po @@ -0,0 +1,63 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-06-24 16:20+0000\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 " +"&& n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n" +#: apps/user/admin.py:27 +msgid "Personal info" +msgstr "Informations personnelles" + +#: apps/user/admin.py:39 +msgid "Permissions" +msgstr "Autorisations" + +#: apps/user/admin.py:50 +msgid "Important dates" +msgstr "Rendez-vous importants" + +#: apps/user/models.py:18 +msgid "The email must be set" +msgstr "L'email doit être paramétré" + +#: apps/user/models.py:45 +msgid "Account Activation" +msgstr "Activation de compte" + +#: apps/user/models.py:46 +msgid "Password Reset" +msgstr "Réinitialisation du mot de passe" + +#: apps/user/models.py:47 +msgid "Password Changed" +msgstr "Mot de passe changé" + +#: apps/user/models.py:48 +msgid "News And Offers" +msgstr "Nouvelles et offres" + +#: apps/user/models.py:67 +msgid "email" +msgstr "e-mail" + +#: apps/user/models.py:81 +msgid "system generated user display name" +msgstr "nom d'affichage de l'utilisateur généré par le système" + +#: apps/user/models.py:85 +msgid "phone" +msgstr "téléphone" diff --git a/apps/user/locale/es/LC_MESSAGES/django.po b/apps/user/locale/es/LC_MESSAGES/django.po new file mode 100644 index 0000000..d29cc64 --- /dev/null +++ b/apps/user/locale/es/LC_MESSAGES/django.po @@ -0,0 +1,62 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-06-24 16:20+0000\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +#: apps/user/admin.py:27 +msgid "Personal info" +msgstr "معلومات شخصية" + +#: apps/user/admin.py:39 +msgid "Permissions" +msgstr "الأذونات" + +#: apps/user/admin.py:50 +msgid "Important dates" +msgstr "تواريخ مهمة" + +#: apps/user/models.py:18 +msgid "The email must be set" +msgstr "يجب ضبط البريد الإلكتروني" + +#: apps/user/models.py:45 +msgid "Account Activation" +msgstr "تفعيل الحساب" + +#: apps/user/models.py:46 +msgid "Password Reset" +msgstr "إعادة تعيين كلمة المرور" + +#: apps/user/models.py:47 +msgid "Password Changed" +msgstr "تم تغيير كلمة السر" + +#: apps/user/models.py:48 +msgid "News And Offers" +msgstr "الأخبار والعروض" + +#: apps/user/models.py:67 +msgid "email" +msgstr "بريد إلكتروني" + +#: apps/user/models.py:81 +msgid "system generated user display name" +msgstr "اسم عرض المستخدم الذي أنشأه النظام" + +#: apps/user/models.py:85 +msgid "phone" +msgstr "هاتف" diff --git a/apps/user/locale/fr/LC_MESSAGES/django.po b/apps/user/locale/fr/LC_MESSAGES/django.po new file mode 100644 index 0000000..ed61d73 --- /dev/null +++ b/apps/user/locale/fr/LC_MESSAGES/django.po @@ -0,0 +1,62 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-06-24 16:20+0000\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" +#: apps/user/admin.py:27 +msgid "Personal info" +msgstr "Informations personnelles" + +#: apps/user/admin.py:39 +msgid "Permissions" +msgstr "Autorisations" + +#: apps/user/admin.py:50 +msgid "Important dates" +msgstr "Rendez-vous importants" + +#: apps/user/models.py:18 +msgid "The email must be set" +msgstr "L'email doit être paramétré" + +#: apps/user/models.py:45 +msgid "Account Activation" +msgstr "Activation de compte" + +#: apps/user/models.py:46 +msgid "Password Reset" +msgstr "Réinitialisation du mot de passe" + +#: apps/user/models.py:47 +msgid "Password Changed" +msgstr "Mot de passe changé" + +#: apps/user/models.py:48 +msgid "News And Offers" +msgstr "Nouvelles et offres" + +#: apps/user/models.py:67 +msgid "email" +msgstr "e-mail" + +#: apps/user/models.py:81 +msgid "system generated user display name" +msgstr "nom d'affichage de l'utilisateur généré par le système" + +#: apps/user/models.py:85 +msgid "phone" +msgstr "téléphone" diff --git a/apps/user/models.py b/apps/user/models.py index 9d1f2f0..b5df951 100644 --- a/apps/user/models.py +++ b/apps/user/models.py @@ -42,10 +42,10 @@ def create_superuser(self, email=None, password=None, **extra_fields): class EmailNotificationType(models.IntegerChoices): # Generic email types - ACCOUNT_ACTIVATION = 1, 'Account Activation' - PASSWORD_RESET = 2, 'Password Reset' - PASSWORD_CHANGED = 3, 'Password Changed' - NEWS_AND_OFFERS = 4, 'News And Offers' + ACCOUNT_ACTIVATION = 1, _('Account Activation') + PASSWORD_RESET = 2, _('Password Reset') + PASSWORD_CHANGED = 3, _('Password Changed') + NEWS_AND_OFFERS = 4, _('News And Offers') # Other emails are configured using subscriptions @classmethod diff --git a/deploy/run_web.sh b/deploy/run_web.sh index e2af362..aba007c 100755 --- a/deploy/run_web.sh +++ b/deploy/run_web.sh @@ -8,4 +8,5 @@ cd $ROOT_DIR wait-for-it $DB_HOST:$DB_PORT +./manage.py compilemessages --ignore ".venv" uwsgi --ini ./deploy/uwsgi.ini # Start uwsgi server diff --git a/locale/ar/LC_MESSAGES/django.po b/locale/ar/LC_MESSAGES/django.po new file mode 100644 index 0000000..30cb394 --- /dev/null +++ b/locale/ar/LC_MESSAGES/django.po @@ -0,0 +1,24 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-06-24 16:22+0000\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 " +"&& n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n" + +#: templates/admin/base_site.html:3 +msgid "Alert-Hub site admin" +msgstr "تنبيه-محور الموقع المشرف" diff --git a/locale/es/LC_MESSAGES/django.po b/locale/es/LC_MESSAGES/django.po new file mode 100644 index 0000000..c0dbcc2 --- /dev/null +++ b/locale/es/LC_MESSAGES/django.po @@ -0,0 +1,23 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-06-24 16:22+0000\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: templates/admin/base_site.html:3 +msgid "Alert-Hub site admin" +msgstr "Administrador del sitio Alert-Hub" diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po new file mode 100644 index 0000000..c424ccd --- /dev/null +++ b/locale/fr/LC_MESSAGES/django.po @@ -0,0 +1,23 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-06-24 16:22+0000\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" + +#: templates/admin/base_site.html:3 +msgid "Alert-Hub site admin" +msgstr "Administrateur du site Alert-Hub" diff --git a/main/settings.py b/main/settings.py index f207864..fb2a8c2 100644 --- a/main/settings.py +++ b/main/settings.py @@ -122,6 +122,7 @@ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'corsheaders.middleware.CorsMiddleware', + 'django.middleware.locale.LocaleMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', diff --git a/templates/admin/base_site.html b/templates/admin/base_site.html index 6a3a37a..cafabfa 100644 --- a/templates/admin/base_site.html +++ b/templates/admin/base_site.html @@ -1,6 +1,6 @@ {% extends "admin/base.html" %} -{% block title %}{% if subtitle %}{{ subtitle }} | {% endif %}{{ title }} | {{ site_title|default:_('Django site admin') }}{% endblock %} +{% block title %}{% if subtitle %}{{ subtitle }} | {% endif %}{{ title }} | {{ site_title|default:_('Alert-Hub site admin') }}{% endblock %} {% block branding %}

Alert-Hub Administration From 21397ab026b5b0b7ccb115a8b50feea89a3f8f91 Mon Sep 17 00:00:00 2001 From: thenav56 Date: Tue, 25 Jun 2024 10:07:53 +0545 Subject: [PATCH 04/11] Add translation for spanish --- apps/cap_feed/locale/es/LC_MESSAGES/django.po | 50 +++++++++---------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/apps/cap_feed/locale/es/LC_MESSAGES/django.po b/apps/cap_feed/locale/es/LC_MESSAGES/django.po index b189712..bd8559b 100644 --- a/apps/cap_feed/locale/es/LC_MESSAGES/django.po +++ b/apps/cap_feed/locale/es/LC_MESSAGES/django.po @@ -151,51 +151,51 @@ msgstr "" #: apps/cap_feed/models.py:244 msgid "Geo" -msgstr "" +msgstr "Geofísico" #: apps/cap_feed/models.py:245 msgid "Met" -msgstr "" +msgstr "Meteorológico" #: apps/cap_feed/models.py:246 msgid "Safety" -msgstr "" +msgstr "Protección" #: apps/cap_feed/models.py:247 msgid "Security" -msgstr "" +msgstr "Seguridad" #: apps/cap_feed/models.py:248 msgid "Rescue" -msgstr "" +msgstr "Rescate" #: apps/cap_feed/models.py:249 msgid "Fire" -msgstr "" +msgstr "Incendio" #: apps/cap_feed/models.py:250 msgid "Health" -msgstr "" +msgstr "Salud" #: apps/cap_feed/models.py:251 msgid "Env" -msgstr "" +msgstr "Medioambiental" #: apps/cap_feed/models.py:252 msgid "Transport" -msgstr "" +msgstr "Transporte" #: apps/cap_feed/models.py:253 msgid "Infra" -msgstr "" +msgstr "Infraestructura" #: apps/cap_feed/models.py:254 msgid "CBRNE" -msgstr "" +msgstr "CBRNE" #: apps/cap_feed/models.py:255 msgid "Other" -msgstr "" +msgstr "Otro" #: apps/cap_feed/models.py:258 msgid "Shelter" @@ -235,53 +235,53 @@ msgstr "" #: apps/cap_feed/models.py:269 msgid "Immediate" -msgstr "" +msgstr "Inmediata" #: apps/cap_feed/models.py:270 msgid "Expected" -msgstr "" +msgstr "Prevista" #: apps/cap_feed/models.py:271 msgid "Future" -msgstr "" +msgstr "Futura" #: apps/cap_feed/models.py:272 msgid "Past" -msgstr "" +msgstr "Pasada" #: apps/cap_feed/models.py:273 apps/cap_feed/models.py:280 #: apps/cap_feed/models.py:287 msgid "Unknown" -msgstr "" +msgstr "Desconocida" #: apps/cap_feed/models.py:276 msgid "Extreme" -msgstr "" +msgstr "Extrema" #: apps/cap_feed/models.py:277 msgid "Severe" -msgstr "" +msgstr "Severa" #: apps/cap_feed/models.py:278 msgid "Moderate" -msgstr "" +msgstr "Moderada" #: apps/cap_feed/models.py:279 msgid "Minor" -msgstr "" +msgstr "Menor" #: apps/cap_feed/models.py:283 msgid "Observed" -msgstr "" +msgstr "Observada" #: apps/cap_feed/models.py:284 msgid "Likely" -msgstr "" +msgstr "Probable" #: apps/cap_feed/models.py:285 msgid "Possible" -msgstr "" +msgstr "Posible" #: apps/cap_feed/models.py:286 msgid "Unlikely" -msgstr "" +msgstr "Improbable" From cb7f6923cdca1fe73fb7427fd7094706e6bf6f59 Mon Sep 17 00:00:00 2001 From: thenav56 Date: Sun, 21 Jul 2024 20:41:35 +0545 Subject: [PATCH 05/11] Add timeout for feed fetch --- apps/cap_feed/formats/atom.py | 8 ++++++-- apps/cap_feed/formats/nws_us.py | 11 +++++++++-- apps/cap_feed/formats/rss.py | 8 ++++++-- apps/cap_feed/models.py | 2 ++ pyproject.toml | 9 +++++++++ utils/strawberry/mutations.py | 4 ++++ 6 files changed, 36 insertions(+), 6 deletions(-) diff --git a/apps/cap_feed/formats/atom.py b/apps/cap_feed/formats/atom.py index f18ad61..ab8cc44 100644 --- a/apps/cap_feed/formats/atom.py +++ b/apps/cap_feed/formats/atom.py @@ -4,7 +4,7 @@ import requests import validators -from apps.cap_feed.models import Alert, ProcessedAlert +from apps.cap_feed.models import Alert, Feed, ProcessedAlert from utils.common import logger_log_extra from .cap_xml import get_alert @@ -21,7 +21,11 @@ def get_alerts_atom(feed, ns): # navigate list of alerts try: - response = requests.get(feed.url, headers=COMMON_REQUESTS_HEADERS) + response = requests.get( + feed.url, + headers=COMMON_REQUESTS_HEADERS, + timeout=Feed.MAX_REQUEST_TIMEOUT, + ) response.raise_for_status() except requests.exceptions.RequestException: logger.error( diff --git a/apps/cap_feed/formats/nws_us.py b/apps/cap_feed/formats/nws_us.py index 39fda2a..3a37f5f 100644 --- a/apps/cap_feed/formats/nws_us.py +++ b/apps/cap_feed/formats/nws_us.py @@ -5,7 +5,7 @@ import validators from apps.cap_feed.formats.cap_xml import get_alert -from apps.cap_feed.models import Alert, ProcessedAlert +from apps.cap_feed.models import Alert, Feed, ProcessedAlert from utils.common import logger_log_extra from .utils import COMMON_REQUESTS_HEADERS, fetch_alert_using_url @@ -21,7 +21,14 @@ def get_alerts_nws_us(feed, ns): # navigate list of alerts try: - response = requests.get(feed.url, headers={**COMMON_REQUESTS_HEADERS, 'Accept': 'application/atom+xml'}) + response = requests.get( + feed.url, + headers={ + **COMMON_REQUESTS_HEADERS, + 'Accept': 'application/atom+xml', + }, + timeout=Feed.MAX_REQUEST_TIMEOUT, + ) response.raise_for_status() except requests.exceptions.RequestException: logger.error( diff --git a/apps/cap_feed/formats/rss.py b/apps/cap_feed/formats/rss.py index 09758a7..a84ac72 100644 --- a/apps/cap_feed/formats/rss.py +++ b/apps/cap_feed/formats/rss.py @@ -5,7 +5,7 @@ import validators from apps.cap_feed.formats.cap_xml import get_alert -from apps.cap_feed.models import Alert, ProcessedAlert +from apps.cap_feed.models import Alert, Feed, ProcessedAlert from utils.common import logger_log_extra from .utils import COMMON_REQUESTS_HEADERS, fetch_alert_using_url @@ -21,7 +21,11 @@ def get_alerts_rss(feed, ns): # navigate list of alerts try: - response = requests.get(feed.url, headers=COMMON_REQUESTS_HEADERS) + response = requests.get( + feed.url, + headers=COMMON_REQUESTS_HEADERS, + timeout=Feed.MAX_REQUEST_TIMEOUT, + ) response.raise_for_status() except requests.exceptions.RequestException: logger.error( diff --git a/apps/cap_feed/models.py b/apps/cap_feed/models.py index bbd90ad..d2515c5 100644 --- a/apps/cap_feed/models.py +++ b/apps/cap_feed/models.py @@ -101,6 +101,8 @@ class LanguageInfo(models.Model): class Feed(models.Model): + MAX_REQUEST_TIMEOUT = 60 * 4 + class PoolingInterval(models.IntegerChoices): """ Generated using: range(5, 65, 5): diff --git a/pyproject.toml b/pyproject.toml index 5e6baf3..8df1a65 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -41,6 +41,15 @@ pytest-django = "*" django-stubs = "==4.2.6" # NOTE: 4.2.7 is messing with TextChoices/IntergerChoices celery-types = "*" +[tool.ruff] +exclude = [ + "**/__pycache__", + "**/snap_test_*.py", + ".venv/", + "legacy/", + "**/migrations/*.py", +] + [tool.black] line-length = 125 skip-string-normalization = true diff --git a/utils/strawberry/mutations.py b/utils/strawberry/mutations.py index f553757..6d8e04e 100644 --- a/utils/strawberry/mutations.py +++ b/utils/strawberry/mutations.py @@ -24,9 +24,13 @@ ) +# TODO: Add tests def process_input_data(data) -> dict | list: """ Return dict from Strawberry Input Object + NOTE: strawberry.asdict doesn't handle nested and strawberry.UNSET + Related issue: https://github.com/strawberry-graphql/strawberry/issues/3265 + https://github.com/strawberry-graphql/strawberry/blob/d2c0fb4d2d363929c9ac10161884d004ab9cf555/strawberry/object_type.py#L395 """ # TODO: Write test if type(data) in [tuple, list]: From cddcfa6b8b2d85554f2032dd81e61cbf243acbca Mon Sep 17 00:00:00 2001 From: thenav56 Date: Sun, 21 Jul 2024 22:16:15 +0545 Subject: [PATCH 06/11] Add translation for Region & Country name - Using django-modeltranslation --- apps/cap_feed/admin.py | 24 +++-- apps/cap_feed/data_injector/geo.py | 87 +++++++++++++++---- .../cap_feed/management/commands/add_feeds.py | 10 +++ .../management/commands/update_geo_data.py | 14 ++- ...ountry_name_en_country_name_es_and_more.py | 53 +++++++++++ apps/cap_feed/translation.py | 13 +++ main/settings.py | 15 ++++ poetry.lock | 16 +++- pyproject.toml | 1 + utils/strawberry/types.py | 3 + 10 files changed, 209 insertions(+), 27 deletions(-) create mode 100644 apps/cap_feed/management/commands/add_feeds.py create mode 100644 apps/cap_feed/migrations/0007_country_name_ar_country_name_en_country_name_es_and_more.py create mode 100644 apps/cap_feed/translation.py diff --git a/apps/cap_feed/admin.py b/apps/cap_feed/admin.py index 5d06184..c2f7bab 100644 --- a/apps/cap_feed/admin.py +++ b/apps/cap_feed/admin.py @@ -1,5 +1,6 @@ from admin_auto_filters.filters import AutocompleteFilterFactory from django.contrib import admin +from modeltranslation.admin import TranslationAdmin from .models import ( Admin1, @@ -78,6 +79,7 @@ class AlertInfoInline(admin.StackedInline): extra = 0 +@admin.register(Alert) class AlertAdmin(admin.ModelAdmin): list_display = ['url', 'country', 'feed', 'sent', 'status', 'msg_type', 'scope'] list_filter = ( @@ -112,7 +114,14 @@ class AlertAdmin(admin.ModelAdmin): inlines = [AlertInfoInline] -class CountryAdmin(admin.ModelAdmin): +@admin.register(Region) +class RegionAdmin(TranslationAdmin): + list_display = ['name'] + search_fields = ['name'] + + +@admin.register(Country) +class CountryAdmin(TranslationAdmin): list_display = ['name', 'iso3', 'region', 'continent'] list_filter = ( 'region', @@ -121,6 +130,7 @@ class CountryAdmin(admin.ModelAdmin): search_fields = ['name', 'iso3'] +@admin.register(Admin1) class Admin1Admin(admin.ModelAdmin): list_display = ['name', 'country'] list_filter = (AutocompleteFilterFactory('Country', 'country'),) @@ -137,6 +147,7 @@ def get_formset(self, *args, **kwargs): return super().get_formset(validate_min=self.validate_min, *args, **kwargs) +@admin.register(Feed) class FeedAdmin(admin.ModelAdmin): list_display = ['name', 'country', 'url', 'format', 'polling_interval'] list_filter = ( @@ -159,6 +170,7 @@ def name(self, obj): return feed_name +@admin.register(FeedLog) class FeedLogAdmin(admin.ModelAdmin): list_display = ['exception', 'feed', 'description', 'alert_url', 'timestamp'] list_filter = ( @@ -172,6 +184,7 @@ class FeedLogAdmin(admin.ModelAdmin): ] +@admin.register(AlertAdmin1) class AlertAdmin1Admin(admin.ModelAdmin): list_display = ['alert', 'admin1'] list_filter = ( @@ -181,18 +194,11 @@ class AlertAdmin1Admin(admin.ModelAdmin): search_fields = ['alert__url', 'admin1__name'] +@admin.register(ProcessedAlert) class ProcessedAlertAdmin(admin.ModelAdmin): list_display = ['url', 'feed'] list_filter = (AutocompleteFilterFactory('Feed', 'feed'),) search_fields = ['url'] -admin.site.register(ProcessedAlert, ProcessedAlertAdmin) -admin.site.register(Alert, AlertAdmin) admin.site.register(Continent) -admin.site.register(Region) -admin.site.register(Country, CountryAdmin) -admin.site.register(Admin1, Admin1Admin) -admin.site.register(AlertAdmin1, AlertAdmin1Admin) -admin.site.register(Feed, FeedAdmin) -admin.site.register(FeedLog, FeedLogAdmin) diff --git a/apps/cap_feed/data_injector/geo.py b/apps/cap_feed/data_injector/geo.py index ef5e4f7..1a7e3a1 100644 --- a/apps/cap_feed/data_injector/geo.py +++ b/apps/cap_feed/data_injector/geo.py @@ -2,6 +2,7 @@ import json import os import re +from collections import defaultdict import requests from django.conf import settings @@ -9,6 +10,7 @@ from django.contrib.gis.geos import GEOSGeometry from django.core.management.base import BaseCommand from django.db import models, transaction +from modeltranslation.utils import build_localized_fieldname from apps.cap_feed.models import Admin1, Country, Region from main.managers import BulkUpdateManager @@ -116,6 +118,9 @@ def log_error(self, *messages: str): @staticmethod def clean_name(name: str | None) -> str | None: + """ + Removes excessive spaces and newlines + """ if name is None: return @@ -126,8 +131,9 @@ def clean_name(name: str | None) -> str | None: ).strip() def handle_pagination(self, url, **requests_kwargs): - _url = f'{self.GO_DOMAIN}{url}?limit=50' - self.log_info('Fetching data from IFRC-GO:', _url) + _url = f'{self.GO_DOMAIN}{url}' + _lang = requests_kwargs.get('headers', {}).get('Accept-Language', 'en') + self.log_info(f'Fetching data from IFRC-GO: ({_lang})', _url) # TODO: Add some check to avoid infinite run while True: resp = requests.get(_url, **requests_kwargs).json() @@ -145,6 +151,19 @@ def inject_continents(self): def inject_regions(self): go_data = self.handle_pagination('/api/v2/region/') + lang_map = defaultdict(dict) + for lang, _ in settings.LANGUAGES: + if lang == 'en': + continue + lang_go_data = self.handle_pagination( + '/api/v2/region/', + headers={"Accept-Language": lang}, + ) + for region_data in lang_go_data: + ifrc_go_id = region_data['id'] + if translated_name := self.clean_name(region_data['region_name']): + lang_map[ifrc_go_id][lang] = translated_name + for region_data in go_data: ifrc_go_id = region_data['id'] region_name = self.clean_name(region_data['region_name']) @@ -153,12 +172,19 @@ def inject_regions(self): models.Q(ifrc_go_id=ifrc_go_id) | models.Q(name__iexact=region_name), ).get_or_create( defaults={ - # 'name': region_name, + 'name': region_name, }, ) region.ifrc_go_id = ifrc_go_id + region.name = region_name + for lang, _ in settings.LANGUAGES: + translated_name = lang_map[ifrc_go_id].get(lang) + if lang == 'en' or translated_name == region.name: + continue + setattr(region, build_localized_fieldname('name', lang), translated_name) + region.bbox = GEOSGeometry(str(region_data['bbox'])) region.save() self.region_map[region.ifrc_go_id] = region @@ -168,14 +194,38 @@ def inject_regions(self): self.log_success(f'Update region: {region}') def inject_countries(self): + fetch_params = { + 'is_independent': True, + 'is_deprecated': False, + 'limit': 300, + } go_data = self.handle_pagination( '/api/v2/country/', - params={ - 'is_independent': True, - 'is_deprecated': False, - }, + params=fetch_params, + ) + + lang_map = defaultdict(dict) + for lang, _ in settings.LANGUAGES: + if lang == 'en': + continue + lang_go_data = self.handle_pagination( + '/api/v2/country/', + params=fetch_params, + headers={"Accept-Language": lang}, + ) + for country_data in lang_go_data: + ifrc_go_id = country_data['id'] + if translated_name := self.clean_name(country_data['name']): + lang_map[ifrc_go_id][lang] = translated_name + + mgr = BulkUpdateManager( + update_fields=[ + 'ifrc_go_id', + 'region', + 'bbox', + *[build_localized_fieldname('name', lang) for lang, _ in settings.LANGUAGES], + ] ) - mgr = BulkUpdateManager(update_fields=['ifrc_go_id', 'name', 'region', 'bbox']) CUSTOM_COUNTRY_BBOX = get_custom_country_bbox() @@ -215,7 +265,14 @@ def inject_countries(self): ) country.ifrc_go_id = ifrc_go_id + country.name = country_name + for lang, _ in settings.LANGUAGES: + translated_name = lang_map[ifrc_go_id].get(lang) + if lang == 'en' or translated_name == country.name: + continue + setattr(country, build_localized_fieldname('name', lang), translated_name) + country.region = region if iso3 in CUSTOM_COUNTRY_BBOX: country.bbox = GEOSGeometry(json.dumps(CUSTOM_COUNTRY_BBOX[iso3])) @@ -294,15 +351,15 @@ def get_geojson_data(): self.log_success(str(mgr.summary())) @transaction.atomic - def sync(self): + def sync( + self, + # NOTE: Admin1s sync takes a lot of time + skip_admin1s_sync: bool = False, + ): # NOTE: Inject order matters here self.inject_continents() self.inject_regions() self.inject_countries() - self.inject_admin1s() + if not skip_admin1s_sync: + self.inject_admin1s() # TODO: Show change summary - - -# inject region and country data if not already present -def inject_geographical_data(): - IfrcGoGeoInjector().sync() diff --git a/apps/cap_feed/management/commands/add_feeds.py b/apps/cap_feed/management/commands/add_feeds.py new file mode 100644 index 0000000..bab39e3 --- /dev/null +++ b/apps/cap_feed/management/commands/add_feeds.py @@ -0,0 +1,10 @@ +from django.core.management.base import BaseCommand + +from apps.cap_feed.data_injector.feed import inject_feeds + + +class Command(BaseCommand): + + def handle(self, *_, **options): + self.stdout.write('Initiating feeds...') + inject_feeds() diff --git a/apps/cap_feed/management/commands/update_geo_data.py b/apps/cap_feed/management/commands/update_geo_data.py index 5a8a925..c3874d3 100644 --- a/apps/cap_feed/management/commands/update_geo_data.py +++ b/apps/cap_feed/management/commands/update_geo_data.py @@ -5,6 +5,16 @@ class Command(BaseCommand): + def add_arguments(self, parser): + parser.add_argument( + "--skip-admin1s-sync", + action="store_true", + help="Skip Admin1s update", + ) + def handle(self, *_, **options): - self.stdout.write('Initiating geo data...') - IfrcGoGeoInjector(django_cmd=self).sync() + self.stdout.write("Initiating geo data...") + skip_admin1s_sync = options.get("skip_admin1s_sync", False) + IfrcGoGeoInjector(django_cmd=self).sync( + skip_admin1s_sync=skip_admin1s_sync, + ) diff --git a/apps/cap_feed/migrations/0007_country_name_ar_country_name_en_country_name_es_and_more.py b/apps/cap_feed/migrations/0007_country_name_ar_country_name_en_country_name_es_and_more.py new file mode 100644 index 0000000..647b636 --- /dev/null +++ b/apps/cap_feed/migrations/0007_country_name_ar_country_name_en_country_name_es_and_more.py @@ -0,0 +1,53 @@ +# Generated by Django 4.2.13 on 2024-07-21 16:14 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('cap_feed', '0006_alert_is_expired'), + ] + + operations = [ + migrations.AddField( + model_name='country', + name='name_ar', + field=models.CharField(null=True), + ), + migrations.AddField( + model_name='country', + name='name_en', + field=models.CharField(null=True), + ), + migrations.AddField( + model_name='country', + name='name_es', + field=models.CharField(null=True), + ), + migrations.AddField( + model_name='country', + name='name_fr', + field=models.CharField(null=True), + ), + migrations.AddField( + model_name='region', + name='name_ar', + field=models.CharField(null=True), + ), + migrations.AddField( + model_name='region', + name='name_en', + field=models.CharField(null=True), + ), + migrations.AddField( + model_name='region', + name='name_es', + field=models.CharField(null=True), + ), + migrations.AddField( + model_name='region', + name='name_fr', + field=models.CharField(null=True), + ), + ] diff --git a/apps/cap_feed/translation.py b/apps/cap_feed/translation.py new file mode 100644 index 0000000..cd9e50b --- /dev/null +++ b/apps/cap_feed/translation.py @@ -0,0 +1,13 @@ +from modeltranslation.translator import TranslationOptions, register + +from .models import Country, Region + + +@register(Region) +class RegionTO(TranslationOptions): + fields = ("name",) + + +@register(Country) +class CountryTO(TranslationOptions): + fields = ("name",) diff --git a/main/settings.py b/main/settings.py index fb2a8c2..37f3b9a 100644 --- a/main/settings.py +++ b/main/settings.py @@ -14,6 +14,7 @@ from pathlib import Path import environ +from django.utils.translation import gettext_lazy as _ from main import sentry @@ -86,6 +87,7 @@ # Application definition INSTALLED_APPS = [ + "modeltranslation", # https://django-modeltranslation.readthedocs.io/en/latest/installation.html#installed-apps # Native 'django.contrib.admin', 'django.contrib.auth', @@ -396,3 +398,16 @@ def log_render_extra_context(record): }, } sentry.init_sentry(**SENTRY_CONFIG) + + +# Translation +LANGUAGES = ( + ("en", _("English")), + ("es", _("Spanish")), + ("fr", _("French")), + ("ar", _("Arabic")), +) + +# modeltranslation configs +# -- NOTE: "en" is used as default languages in the codebase, changing this will break logics +MODELTRANSLATION_DEFAULT_LANGUAGE = "en" # Also the fallback diff --git a/poetry.lock b/poetry.lock index 66d9a1f..a8770a0 100644 --- a/poetry.lock +++ b/poetry.lock @@ -493,6 +493,20 @@ django = ">=2.2" docs = ["sphinx"] test = ["boto3", "celery", "django-storages", "pytest", "pytest-cov", "pytest-django", "redis"] +[[package]] +name = "django-modeltranslation" +version = "0.19.5" +description = "Translates Django models using a registration approach." +optional = false +python-versions = "*" +files = [ + {file = "django_modeltranslation-0.19.5-py3-none-any.whl", hash = "sha256:3b976d8a9ed44d14a2ace6f2ee66394ce5aef04aa80ff28e67078c133c46d5f2"}, + {file = "django_modeltranslation-0.19.5.tar.gz", hash = "sha256:0d5b65c1a1a9a1142ece6ab79aa1525eec70ccb9977bae945f04620e43313820"}, +] + +[package.dependencies] +Django = ">=4.2" + [[package]] name = "django-redis" version = "5.4.0" @@ -1496,4 +1510,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "ec116a8b574c8c5e7604038c0e93d35cc66ab5e3e5ce739f855fb8a8af554d11" +content-hash = "d8a651e3845a71a84160ce78f73374a3cf3a4aad6694ea534cee95af03448999" diff --git a/pyproject.toml b/pyproject.toml index 8df1a65..2220196 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,6 +17,7 @@ django-extensions = "^3.2.3" django-redis = "^5.3.0" django-storages = "^1.13.2" django-admin-autocomplete-filter = "*" +django-modeltranslation = "==0.19.5" iso639-lang = "^2.1.0" graphene-django = "*" psycopg2-binary = "^2.9.9" diff --git a/utils/strawberry/types.py b/utils/strawberry/types.py index 7cd02e3..75d6ed9 100644 --- a/utils/strawberry/types.py +++ b/utils/strawberry/types.py @@ -5,6 +5,7 @@ from django.contrib.gis.geos import GEOSGeometry from django.db import models from django.db.models.fields import Field as DjangoBaseField +from modeltranslation.fields import TranslationFieldDescriptor if typing.TYPE_CHECKING: from django.db.models.fields import _FieldDescriptor @@ -57,6 +58,8 @@ def string_field( _field = field if isinstance(field, models.query_utils.DeferredAttribute): _field = field.field + elif isinstance(field, TranslationFieldDescriptor): + _field = field.field def _get_value(root) -> None | str: return getattr(root, _field.attname) # type: ignore[reportGeneralTypeIssues] FIXME From 69450f82253f675cf923779396f46cb2e809dcf8 Mon Sep 17 00:00:00 2001 From: thenav56 Date: Mon, 22 Jul 2024 13:10:12 +0545 Subject: [PATCH 07/11] Add static translation for French & Arabic --- README.md | 6 + apps/cap_feed/locale/ar/LC_MESSAGES/django.po | 134 +++++++++--------- apps/cap_feed/locale/fr/LC_MESSAGES/django.po | 110 +++++++------- 3 files changed, 128 insertions(+), 122 deletions(-) diff --git a/README.md b/README.md index 81992ce..72feb08 100644 --- a/README.md +++ b/README.md @@ -245,5 +245,11 @@ celery -A main beat --detach -l info Translation ``` +# Static translation docker compose exec web ./manage.py makemessages -l es -l ar -l fr --ignore legacy --ignore main + +# Dynamic translation +# -- Run after adding new field to translations +:wa +docker compose exec web ./manage.py update_translation_fields ``` diff --git a/apps/cap_feed/locale/ar/LC_MESSAGES/django.po b/apps/cap_feed/locale/ar/LC_MESSAGES/django.po index 455b99e..409055a 100644 --- a/apps/cap_feed/locale/ar/LC_MESSAGES/django.po +++ b/apps/cap_feed/locale/ar/LC_MESSAGES/django.po @@ -20,269 +20,269 @@ msgstr "" "&& n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n" #: apps/cap_feed/models.py:109 msgid "5 seconds" -msgstr "" +msgstr "5 ثوانٍ" #: apps/cap_feed/models.py:110 msgid "10 seconds" -msgstr "" +msgstr "10 ثوانٍ" #: apps/cap_feed/models.py:111 msgid "15 seconds" -msgstr "" +msgstr "15 ثانية" #: apps/cap_feed/models.py:112 msgid "20 seconds" -msgstr "" +msgstr "20 ثانية" #: apps/cap_feed/models.py:113 msgid "25 seconds" -msgstr "" +msgstr "25 ثانية" #: apps/cap_feed/models.py:114 msgid "30 seconds" -msgstr "" +msgstr "30 ثانية" #: apps/cap_feed/models.py:115 msgid "35 seconds" -msgstr "" +msgstr "35 ثانية" #: apps/cap_feed/models.py:116 msgid "40 seconds" -msgstr "" +msgstr "40 ثانية" #: apps/cap_feed/models.py:117 msgid "45 seconds" -msgstr "" +msgstr "45 ثانية" #: apps/cap_feed/models.py:118 msgid "50 seconds" -msgstr "" +msgstr "50 ثانية" #: apps/cap_feed/models.py:119 msgid "55 seconds" -msgstr "" +msgstr "55 ثانية" #: apps/cap_feed/models.py:120 msgid "60 seconds" -msgstr "" +msgstr "60 ثانية" #: apps/cap_feed/models.py:121 msgid "10 minutes" -msgstr "" +msgstr "10 دقائق" #: apps/cap_feed/models.py:124 msgid "ATOM" -msgstr "" +msgstr "ATOM" #: apps/cap_feed/models.py:125 msgid "RSS" -msgstr "" +msgstr "RSS" #: apps/cap_feed/models.py:126 msgid "NWS_US" -msgstr "" +msgstr "NWS_US" #: apps/cap_feed/models.py:129 msgid "Active" -msgstr "" +msgstr "نشط" #: apps/cap_feed/models.py:130 msgid "Testing" -msgstr "" +msgstr "اختبار" #: apps/cap_feed/models.py:131 msgid "Inactive" -msgstr "" +msgstr "غير نشط" #: apps/cap_feed/models.py:132 msgid "Unusable" -msgstr "" +msgstr "غير قابل للاستخدام" #: apps/cap_feed/models.py:167 msgid "Actual" -msgstr "" +msgstr "فعلي" #: apps/cap_feed/models.py:168 msgid "Exercise" -msgstr "" +msgstr "تمرين" #: apps/cap_feed/models.py:169 msgid "System" -msgstr "" +msgstr "نظام" #: apps/cap_feed/models.py:170 msgid "Test" -msgstr "" +msgstr "اختبار" #: apps/cap_feed/models.py:171 msgid "Draft" -msgstr "" +msgstr "مسودة" #: apps/cap_feed/models.py:174 msgid "Alert" -msgstr "" +msgstr "تنبيه" #: apps/cap_feed/models.py:175 msgid "Update" -msgstr "" +msgstr "تحديث" #: apps/cap_feed/models.py:176 msgid "Cancel" -msgstr "" +msgstr "إلغاء" #: apps/cap_feed/models.py:177 msgid "Ack" -msgstr "" +msgstr "إقرار" #: apps/cap_feed/models.py:178 msgid "Error" -msgstr "" +msgstr "خطأ" #: apps/cap_feed/models.py:181 msgid "Public" -msgstr "" +msgstr "عام" #: apps/cap_feed/models.py:182 msgid "Restricted" -msgstr "" +msgstr "مقيد" #: apps/cap_feed/models.py:183 msgid "Private" -msgstr "" +msgstr "خاص" #: apps/cap_feed/models.py:244 msgid "Geo" -msgstr "" +msgstr "جيو (جيوفيزيائي)" #: apps/cap_feed/models.py:245 msgid "Met" -msgstr "" +msgstr "جوي (الأرصاد الجوية)" #: apps/cap_feed/models.py:246 msgid "Safety" -msgstr "" +msgstr "السلامة" #: apps/cap_feed/models.py:247 msgid "Security" -msgstr "" +msgstr "أمن" #: apps/cap_feed/models.py:248 msgid "Rescue" -msgstr "" +msgstr "إنقاذ" #: apps/cap_feed/models.py:249 msgid "Fire" -msgstr "" +msgstr "حريق أو اشتعال" #: apps/cap_feed/models.py:250 msgid "Health" -msgstr "" +msgstr "الصحة" #: apps/cap_feed/models.py:251 msgid "Env" -msgstr "" +msgstr "بيئية" #: apps/cap_feed/models.py:252 msgid "Transport" -msgstr "" +msgstr "نقل" #: apps/cap_feed/models.py:253 msgid "Infra" -msgstr "" +msgstr "البنية التحتية" #: apps/cap_feed/models.py:254 msgid "CBRNE" -msgstr "" +msgstr "المتفجرات الكيميائية، والحيوية، والإشعاعية، والنووية (CBRNE)" #: apps/cap_feed/models.py:255 msgid "Other" -msgstr "" +msgstr "أخرى" #: apps/cap_feed/models.py:258 msgid "Shelter" -msgstr "" +msgstr "مأوى" #: apps/cap_feed/models.py:259 msgid "Evacuate" -msgstr "" +msgstr "إخلاء" #: apps/cap_feed/models.py:260 msgid "Prepare" -msgstr "" +msgstr "تحضير" #: apps/cap_feed/models.py:261 msgid "Execute" -msgstr "" +msgstr "تنفيذ" #: apps/cap_feed/models.py:262 msgid "Avoid" -msgstr "" +msgstr "تجنب" #: apps/cap_feed/models.py:263 msgid "Monitor" -msgstr "" +msgstr "مراقبة" #: apps/cap_feed/models.py:264 msgid "Assess" -msgstr "" +msgstr "تقييم" #: apps/cap_feed/models.py:265 msgid "AllClear" -msgstr "" +msgstr "الكل واضح" #: apps/cap_feed/models.py:266 msgid "None" -msgstr "" +msgstr "لا شيء" #: apps/cap_feed/models.py:269 msgid "Immediate" -msgstr "" +msgstr "فوري" #: apps/cap_feed/models.py:270 msgid "Expected" -msgstr "" +msgstr "متوقع" #: apps/cap_feed/models.py:271 msgid "Future" -msgstr "" +msgstr "مستقبلي" #: apps/cap_feed/models.py:272 msgid "Past" -msgstr "" +msgstr "ماضي" #: apps/cap_feed/models.py:273 apps/cap_feed/models.py:280 #: apps/cap_feed/models.py:287 msgid "Unknown" -msgstr "" +msgstr "غير معروف" #: apps/cap_feed/models.py:276 msgid "Extreme" -msgstr "" +msgstr "شديد" #: apps/cap_feed/models.py:277 msgid "Severe" -msgstr "" +msgstr "شديد" #: apps/cap_feed/models.py:278 msgid "Moderate" -msgstr "" +msgstr "معتدل" #: apps/cap_feed/models.py:279 msgid "Minor" -msgstr "" +msgstr "طفيف" #: apps/cap_feed/models.py:283 msgid "Observed" -msgstr "" +msgstr "ملاحظ" #: apps/cap_feed/models.py:284 msgid "Likely" -msgstr "" +msgstr "محتمل" #: apps/cap_feed/models.py:285 msgid "Possible" -msgstr "" +msgstr "ممكن" #: apps/cap_feed/models.py:286 msgid "Unlikely" -msgstr "" +msgstr "غير محتمل" diff --git a/apps/cap_feed/locale/fr/LC_MESSAGES/django.po b/apps/cap_feed/locale/fr/LC_MESSAGES/django.po index 10caddf..990d845 100644 --- a/apps/cap_feed/locale/fr/LC_MESSAGES/django.po +++ b/apps/cap_feed/locale/fr/LC_MESSAGES/django.po @@ -67,221 +67,221 @@ msgstr "60 secondes" #: apps/cap_feed/models.py:121 msgid "10 minutes" -msgstr "" +msgstr "10 minutes" #: apps/cap_feed/models.py:124 msgid "ATOM" -msgstr "" +msgstr "ATOM" #: apps/cap_feed/models.py:125 msgid "RSS" -msgstr "" +msgstr "RSS" #: apps/cap_feed/models.py:126 msgid "NWS_US" -msgstr "" +msgstr "NWS_US" #: apps/cap_feed/models.py:129 msgid "Active" -msgstr "" +msgstr "Actif" #: apps/cap_feed/models.py:130 msgid "Testing" -msgstr "" +msgstr "Test" #: apps/cap_feed/models.py:131 msgid "Inactive" -msgstr "" +msgstr "Inactif" #: apps/cap_feed/models.py:132 msgid "Unusable" -msgstr "" +msgstr "Inutilisable" #: apps/cap_feed/models.py:167 msgid "Actual" -msgstr "" +msgstr "Réel" #: apps/cap_feed/models.py:168 msgid "Exercise" -msgstr "" +msgstr "Exercice" #: apps/cap_feed/models.py:169 msgid "System" -msgstr "" +msgstr "Système" #: apps/cap_feed/models.py:170 msgid "Test" -msgstr "" +msgstr "Test" #: apps/cap_feed/models.py:171 msgid "Draft" -msgstr "" +msgstr "Brouillon" #: apps/cap_feed/models.py:174 msgid "Alert" -msgstr "" +msgstr "Alerte" #: apps/cap_feed/models.py:175 msgid "Update" -msgstr "" +msgstr "Mise à jour" #: apps/cap_feed/models.py:176 msgid "Cancel" -msgstr "" +msgstr "Annuler" #: apps/cap_feed/models.py:177 msgid "Ack" -msgstr "" +msgstr "Accusé" #: apps/cap_feed/models.py:178 msgid "Error" -msgstr "" +msgstr "Erreur" #: apps/cap_feed/models.py:181 msgid "Public" -msgstr "" +msgstr "Public" #: apps/cap_feed/models.py:182 msgid "Restricted" -msgstr "" +msgstr "Restreint" #: apps/cap_feed/models.py:183 msgid "Private" -msgstr "" +msgstr "Privé" #: apps/cap_feed/models.py:244 msgid "Geo" -msgstr "" +msgstr "Géo (géophysique)" #: apps/cap_feed/models.py:245 msgid "Met" -msgstr "" +msgstr "Met (météorologique)" #: apps/cap_feed/models.py:246 msgid "Safety" -msgstr "" +msgstr "Sécurité" #: apps/cap_feed/models.py:247 msgid "Security" -msgstr "" +msgstr "Sécurité" #: apps/cap_feed/models.py:248 msgid "Rescue" -msgstr "" +msgstr "Sauvetage" #: apps/cap_feed/models.py:249 msgid "Fire" -msgstr "" +msgstr "Incendie" #: apps/cap_feed/models.py:250 msgid "Health" -msgstr "" +msgstr "Santé" #: apps/cap_feed/models.py:251 msgid "Env" -msgstr "" +msgstr "Env (environnemental)" #: apps/cap_feed/models.py:252 msgid "Transport" -msgstr "" +msgstr "Transport" #: apps/cap_feed/models.py:253 msgid "Infra" -msgstr "" +msgstr "Infra (infrastructure)" #: apps/cap_feed/models.py:254 msgid "CBRNE" -msgstr "" +msgstr "CBRNE (Chimique, biologique, radiologique, nucléaire et explosif)" #: apps/cap_feed/models.py:255 msgid "Other" -msgstr "" +msgstr "Autres" #: apps/cap_feed/models.py:258 msgid "Shelter" -msgstr "" +msgstr "Abri" #: apps/cap_feed/models.py:259 msgid "Evacuate" -msgstr "" +msgstr "Évacuer" #: apps/cap_feed/models.py:260 msgid "Prepare" -msgstr "" +msgstr "Préparer" #: apps/cap_feed/models.py:261 msgid "Execute" -msgstr "" +msgstr "Exécuter" #: apps/cap_feed/models.py:262 msgid "Avoid" -msgstr "" +msgstr "Éviter" #: apps/cap_feed/models.py:263 msgid "Monitor" -msgstr "" +msgstr "Surveiller" #: apps/cap_feed/models.py:264 msgid "Assess" -msgstr "" +msgstr "Évaluer" #: apps/cap_feed/models.py:265 msgid "AllClear" -msgstr "" +msgstr "Tout est clair" #: apps/cap_feed/models.py:266 msgid "None" -msgstr "" +msgstr "Aucun" #: apps/cap_feed/models.py:269 msgid "Immediate" -msgstr "" +msgstr "Immédiat" #: apps/cap_feed/models.py:270 msgid "Expected" -msgstr "" +msgstr "Attendu" #: apps/cap_feed/models.py:271 msgid "Future" -msgstr "" +msgstr "Futur" #: apps/cap_feed/models.py:272 msgid "Past" -msgstr "" +msgstr "Passé" #: apps/cap_feed/models.py:273 apps/cap_feed/models.py:280 #: apps/cap_feed/models.py:287 msgid "Unknown" -msgstr "" +msgstr "Inconnu" #: apps/cap_feed/models.py:276 msgid "Extreme" -msgstr "" +msgstr "Extrême" #: apps/cap_feed/models.py:277 msgid "Severe" -msgstr "" +msgstr "Sévère" #: apps/cap_feed/models.py:278 msgid "Moderate" -msgstr "" +msgstr "Modéré" #: apps/cap_feed/models.py:279 msgid "Minor" -msgstr "" +msgstr "Mineur" #: apps/cap_feed/models.py:283 msgid "Observed" -msgstr "" +msgstr "Observé" #: apps/cap_feed/models.py:284 msgid "Likely" -msgstr "" +msgstr "Probable" #: apps/cap_feed/models.py:285 msgid "Possible" -msgstr "" +msgstr "Possible" #: apps/cap_feed/models.py:286 msgid "Unlikely" -msgstr "" +msgstr "Improbable" From 85992b95e72c19918e78429dd659ff1fa3c23e19 Mon Sep 17 00:00:00 2001 From: thenav56 Date: Wed, 28 Aug 2024 14:25:14 +0545 Subject: [PATCH 08/11] Add DB Index for active alerts --- ...0008_alert_cap_feed_alert_not_expired_idx.py | 17 +++++++++++++++++ apps/cap_feed/models.py | 9 +++++++++ 2 files changed, 26 insertions(+) create mode 100644 apps/cap_feed/migrations/0008_alert_cap_feed_alert_not_expired_idx.py diff --git a/apps/cap_feed/migrations/0008_alert_cap_feed_alert_not_expired_idx.py b/apps/cap_feed/migrations/0008_alert_cap_feed_alert_not_expired_idx.py new file mode 100644 index 0000000..c7b57cf --- /dev/null +++ b/apps/cap_feed/migrations/0008_alert_cap_feed_alert_not_expired_idx.py @@ -0,0 +1,17 @@ +# Generated by Django 4.2.13 on 2024-08-28 08:39 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('cap_feed', '0007_country_name_ar_country_name_en_country_name_es_and_more'), + ] + + operations = [ + migrations.AddIndex( + model_name='alert', + index=models.Index(condition=models.Q(('is_expired', False)), fields=['is_expired'], name='cap_feed_alert_not_expired_idx'), + ), + ] diff --git a/apps/cap_feed/models.py b/apps/cap_feed/models.py index d2515c5..4acbe7d 100644 --- a/apps/cap_feed/models.py +++ b/apps/cap_feed/models.py @@ -213,6 +213,15 @@ class Scope(models.TextChoices): # XXX: Not used, maybe we need to use this in alertinfo_set: ManyRelatedManager['AlertInfo'] __all_info_added = None + class Meta: # type: ignore [reportIncompatibleVariableOverride] + indexes = [ + models.Index( + fields=['is_expired'], + name='%(app_label)s_%(class)s_not_expired_idx', + condition=models.Q(is_expired=False), + ) + ] + def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.__all_info_added = False From 67a73689fbb371c6fe40452a9d51b1979261fcc3 Mon Sep 17 00:00:00 2001 From: thenav56 Date: Wed, 28 Aug 2024 14:32:20 +0545 Subject: [PATCH 09/11] Replace deprecated docker-compose command --- .github/workflows/ci.yaml | 6 +++--- main/tests/test_fake.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 025b6ce..4c2fae6 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -66,7 +66,7 @@ jobs: env: DOCKER_IMAGE_BACKEND: ${{ steps.prep.outputs.tagged_image }} run: | - docker-compose -f ./gh-docker-compose.yml run --rm web bash -c 'wait-for-it db:5432 && ./manage.py graphql_schema --out /ci-share/schema-latest.graphql' && + docker compose -f ./gh-docker-compose.yml run --rm web bash -c 'wait-for-it db:5432 && ./manage.py graphql_schema --out /ci-share/schema-latest.graphql' && cmp --silent schema.graphql ./ci-share/schema-latest.graphql || { echo 'The schema.graphql is not up to date with the latest changes. Please update and push latest'; diff schema.graphql ./ci-share/schema-latest.graphql; @@ -77,7 +77,7 @@ jobs: env: DOCKER_IMAGE_BACKEND: ${{ steps.prep.outputs.tagged_image }} run: | - docker-compose -f ./gh-docker-compose.yml run --rm web bash -c 'wait-for-it db:5432 && ./manage.py makemigrations --check --dry-run' || { + docker compose -f ./gh-docker-compose.yml run --rm web bash -c 'wait-for-it db:5432 && ./manage.py makemigrations --check --dry-run' || { echo 'There are some changes to be reflected in the migration. Make sure to run makemigrations'; exit 1; } @@ -85,7 +85,7 @@ jobs: - name: 🤞 Run Test 🧪 & Publish coverage to code climate env: DOCKER_IMAGE_BACKEND: ${{ steps.prep.outputs.tagged_image }} - run: docker-compose -f gh-docker-compose.yml run --rm web /code/scripts/run_tests.sh + run: docker compose -f gh-docker-compose.yml run --rm web /code/scripts/run_tests.sh # Temp fix # https://github.com/docker/build-push-action/blob/master/docs/advanced/cache.md#github-cache diff --git a/main/tests/test_fake.py b/main/tests/test_fake.py index 8a21827..b382754 100644 --- a/main/tests/test_fake.py +++ b/main/tests/test_fake.py @@ -4,7 +4,7 @@ class FakeTest(TestCase): """ This test is for running migrations only - docker-compose run --rm server ./manage.py test -v 2 --pattern="deep/tests/test_fake.py" + docker compose run --rm server ./manage.py test -v 2 --pattern="deep/tests/test_fake.py" """ def test_fake(self): From 73975b7de9a6178935e9fe90380975a45bb78aab Mon Sep 17 00:00:00 2001 From: thenav56 Date: Wed, 18 Sep 2024 17:11:19 +0545 Subject: [PATCH 10/11] Add new feed sources --- apps/cap_feed/feeds.json | 288 +++++++++++++++++++++++++++++++++++++++ apps/cap_feed/views.py | 24 ++-- 2 files changed, 302 insertions(+), 10 deletions(-) diff --git a/apps/cap_feed/feeds.json b/apps/cap_feed/feeds.json index e86cb26..5b6da4f 100644 --- a/apps/cap_feed/feeds.json +++ b/apps/cap_feed/feeds.json @@ -1546,5 +1546,293 @@ "picUrl": "https://alert-hub.s3.amazonaws.com/images/gn-anm-en.png", "capAlertFeed": "https://cap-sources.s3.amazonaws.com/gn-anm-en/rss.xml", "format": "rss" + }, + { + "sourceId": "bh-meteo-ar", + "name": "Bahrain: Bahrain Meteorological Service", + "iso3": "BHR", + "language": "Arabic", + "picUrl": "https://alert-hub.s3.amazonaws.com/images/bh-meteo-ar.png", + "capAlertFeed": "https://cap-sources.s3.amazonaws.com/bh-meteo-ar/rss.xml", + "format": "rss" + }, + { + "sourceId": "bh-meteo-en", + "name": "Bahrain: Bahrain Meteorological Service", + "iso3": "BHR", + "language": "English", + "picUrl": "https://alert-hub.s3.amazonaws.com/images/bh-meteo-en.png", + "capAlertFeed": "https://cap-sources.s3.amazonaws.com/bh-meteo-en/rss.xml", + "format": "rss" + }, + { + "sourceId": "by-belhydromet-en", + "name": "Belarus: State institution \"Republican center for hydrometeorology, control of radioactive contamination and environmental monitoring\" (Belhydromet)", + "iso3": "BLR", + "language": "English", + "picUrl": "https://alert-hub.s3.amazonaws.com/images/by-belhydromet-en.png", + "capAlertFeed": "https://meteoalert.meteoinfo.ru/belarus/cap-feed/en/atom.xml", + "format": "atom" + }, + { + "sourceId": "by-belhydromet-ru", + "name": "Belarus: State institution \"Republican center for hydrometeorology, control of radioactive contamination and environmental monitoring\" (Belhydromet)", + "iso3": "BLR", + "language": "Russian", + "picUrl": "https://alert-hub.s3.amazonaws.com/images/by-belhydromet-ru.png", + "capAlertFeed": "https://meteoalert.meteoinfo.ru/belarus/cap-feed/ru/atom.xml", + "format": "atom" + }, + { + "sourceId": "bz-nms-en", + "name": "Belize: National Meteorological Service", + "iso3": "BLZ", + "language": "English", + "picUrl": "https://alert-hub.s3.amazonaws.com/images/bz-nms-en.png", + "capAlertFeed": "https://cap-sources.s3.amazonaws.com/bz-nms-en/rss.xml", + "format": "rss" + }, + { + "sourceId": "bz-nms-es", + "name": "Belize: National Meteorological Service", + "iso3": "BLZ", + "language": "Spanish", + "picUrl": "https://alert-hub.s3.amazonaws.com/images/bz-nms-es.png", + "capAlertFeed": "https://cap-sources.s3.amazonaws.com/bz-nms-es/rss.xml", + "format": "rss" + }, + { + "sourceId": "bj-smn-fr", + "name": "Benin: Service Météorologique National", + "iso3": "BEN", + "language": "French", + "picUrl": "https://alert-hub.s3.amazonaws.com/images/bj-meteo-fr.png", + "capAlertFeed": "https://www.meteobenin.bj/api/cap/rss.xml", + "format": "rss" + }, + { + "sourceId": "td-anam-fr", + "name": "Chad: Agence Nationale de la Météorologie", + "iso3": "TCD", + "language": "French", + "picUrl": "https://alert-hub.s3.amazonaws.com/images/td-anam-fr.png", + "capAlertFeed": "https://cap-sources.s3.amazonaws.com/td-anam-fr/rss.xml", + "format": "rss" + }, + { + "sourceId": "td-anam-en", + "name": "Chad: Agence Nationale de la Météorologie", + "iso3": "TCD", + "language": "English", + "picUrl": "https://alert-hub.s3.amazonaws.com/images/td-anam-en.png", + "capAlertFeed": "https://cap-sources.s3.amazonaws.com/td-anam-en/rss.xml", + "format": "rss" + }, + { + "sourceId": "td-anam-ha", + "name": "Chad: Agence Nationale de la Météorologie", + "iso3": "TCD", + "language": "Hausa", + "picUrl": "https://alert-hub.s3.amazonaws.com/images/td-anam-ha.png", + "capAlertFeed": "https://cap-sources.s3.amazonaws.com/td-anam-ha/rss.xml", + "format": "rss" + }, + { + "sourceId": "cl-dmc-es", + "name": "Chile: Direccion Meteorologica de Chile", + "iso3": "CHL", + "language": "Spanish", + "picUrl": "https://alert-hub.s3.amazonaws.com/images/cl-meteo-es.png", + "capAlertFeed": "https://archivos.meteochile.gob.cl/portaldmc/rss/rss.php", + "format": "rss" + }, + { + "sourceId": "do-indomet-es", + "name": "Dominican Republic: Instituto Dominicano de Meteorología", + "iso3": "DOM", + "language": "Spanish", + "picUrl": "https://alert-hub.s3.amazonaws.com/images/do-indomet-es.png", + "capAlertFeed": "https://cap-sources.s3.amazonaws.com/do-indomet-es/rss.xml", + "format": "rss" + }, + { + "sourceId": "et-emi-en", + "name": "Ethiopia: National Meteorological Agency", + "iso3": "ETH", + "language": "English", + "picUrl": "https://alert-hub.s3.amazonaws.com/images/et-emi-en.png", + "capAlertFeed": "https://cap-sources.s3.amazonaws.com/et-emi-en/rss.xml", + "format": "rss" + }, + { + "sourceId": "et-emi-om", + "name": "Ethiopia: National Meteorological Agency", + "iso3": "ETH", + "language": "Oromo", + "picUrl": "https://alert-hub.s3.amazonaws.com/images/et-emi-om.png", + "capAlertFeed": "https://cap-sources.s3.amazonaws.com/et-emi-om/rss.xml", + "format": "rss" + }, + { + "sourceId": "et-emi-am", + "name": "Ethiopia: National Meteorological Agency", + "iso3": "ETH", + "language": "Amharic", + "picUrl": "https://alert-hub.s3.amazonaws.com/images/et-emi-am.png", + "capAlertFeed": "https://cap-sources.s3.amazonaws.com/et-emi-am/rss.xml", + "format": "rss" + }, + { + "sourceId": "et-emi-so", + "name": "Ethiopia: National Meteorological Agency", + "iso3": "ETH", + "language": "Somali", + "picUrl": "https://alert-hub.s3.amazonaws.com/images/et-emi-so.png", + "capAlertFeed": "https://cap-sources.s3.amazonaws.com/et-emi-so/rss.xml", + "format": "rss" + }, + { + "sourceId": "gy-hsg-en", + "name": "Guyana: Hydrometeorological Service of Guyana", + "iso3": "GUY", + "language": "English", + "picUrl": "https://alert-hub.s3.amazonaws.com/images/gy-hms-en.png", + "capAlertFeed": "https://hydromet.gov.gy/cap/en/alerts/rss.xml", + "format": "rss" + }, + { + "sourceId": "kz-kazhydromet-ru", + "name": "Kazakhstan: Kazhydromet", + "iso3": "KAZ", + "language": "Russian", + "picUrl": "https://alert-hub.s3.amazonaws.com/images/kz-kazhydromet-ru.png", + "capAlertFeed": "https://meteoalert.meteoinfo.ru/kazakhstan/cap-feed/ru/atom.xml", + "format": "atom" + }, + { + "sourceId": "kz-kazhydromet-en", + "name": "Kazakhstan: Kazhydromet", + "iso3": "KAZ", + "language": "English", + "picUrl": "https://alert-hub.s3.amazonaws.com/images/kz-kazhydromet-en.png", + "capAlertFeed": "https://meteoalert.meteoinfo.ru/kazakhstan/cap-feed/en/atom.xml", + "format": "atom" + }, + { + "sourceId": "mv-mms-en", + "name": "Maldives: Maldives Meteorological Service", + "iso3": "MDV", + "language": "English", + "picUrl": "https://alert-hub.s3.amazonaws.com/images/mv-met-en.png", + "capAlertFeed": "https://cap.meteorology.gov.mv/rss/alerts/", + "format": "rss" + }, + { + "sourceId": "pw-wso-en", + "name": "Palau: Palau Weather Service Office", + "iso3": "PLW", + "language": "English", + "picUrl": "https://alert-hub.s3.amazonaws.com/images/pw-wso-en.png", + "capAlertFeed": "https://api.weather.gov/alerts/active.atom?zone=PMZ153", + "format": "atom" + }, + { + "sourceId": "qa-caa-ar", + "name": "Qatar: Civil Aviation Authority", + "iso3": "QAT", + "language": "Arabic", + "picUrl": "https://alert-hub.s3.amazonaws.com/images/qa-caa-ar.png", + "capAlertFeed": "https://cap-sources.s3.amazonaws.com/qa-caa-ar/rss.xml", + "format": "rss" + }, + { + "sourceId": "qa-caa-en", + "name": "Qatar: Civil Aviation Authority", + "iso3": "QAT", + "language": "English", + "picUrl": "https://alert-hub.s3.amazonaws.com/images/qa-caa-en.png", + "capAlertFeed": "https://cap-sources.s3.amazonaws.com/qa-caa-en/rss.xml", + "format": "rss" + }, + { + "sourceId": "kr-kma-ko-eqk", + "name": "Republic of Korea: Korea Meteorological Administration", + "iso3": "KOR", + "language": "Korean", + "picUrl": "https://alert-hub.s3.amazonaws.com/images/kr-kma-en.png", + "capAlertFeed": "https://www.weather.go.kr/w/rss/cap/eqk.do", + "format": "rss" + }, + { + "sourceId": "kr-kma-ko-warning", + "name": "Republic of Korea: Korea Meteorological Administration", + "iso3": "KOR", + "language": "Korean", + "picUrl": "https://alert-hub.s3.amazonaws.com/images/kr-kma-en.png", + "capAlertFeed": "https://www.weather.go.kr/w/rss/cap/warning.do", + "format": "rss" + }, + { + "sourceId": "sl-slmet-en", + "name": "Sierra Leone: Sierra Leone Meteorological Agency", + "iso3": "SLE", + "language": "English", + "picUrl": "https://alert-hub.s3.amazonaws.com/images/sl-slmet-en.png", + "capAlertFeed": "https://cap-sources.s3.amazonaws.com/sl-slmet-en/rss.xml", + "format": "rss" + }, + { + "sourceId": "sg-mss-en", + "name": "Singapore: Meteorological Services Singapore", + "iso3": "SGP", + "language": "English", + "picUrl": "https://alert-hub.s3.amazonaws.com/images/sg-mss-en.png", + "capAlertFeed": "http://www.weather.gov.sg/files/rss/rsscapalert/rsscapalert.xml", + "format": "rss" + }, + { + "sourceId": "sd-sma-ar", + "name": "Sudan: Sudan Meteorological Authority", + "iso3": "SDN", + "language": "Arabic", + "picUrl": "https://alert-hub.s3.amazonaws.com/images/sd-sma-ar.png", + "capAlertFeed": "https://meteosudan.sd/api/cap/rss.xml", + "format": "rss" + }, + { + "sourceId": "th-tmd-en", + "name": "Thailand: Thai Meteorological Department (TMD)", + "iso3": "THA", + "language": "English", + "picUrl": "https://alert-hub.s3.amazonaws.com/images/th-tmd-en.png", + "capAlertFeed": "https://www.tmd.go.th/en/api/xml/CAP", + "format": "atom" + }, + { + "sourceId": "th-tmd-th", + "name": "Thailand: Thai Meteorological Department (TMD)", + "iso3": "THA", + "language": "Thai", + "picUrl": "https://alert-hub.s3.amazonaws.com/images/th-tmd-th.png", + "capAlertFeed": "https://www.tmd.go.th/api/xml/CAP", + "format": "atom" + }, + { + "sourceId": "ua-uhmc-uk", + "name": "Ukraine: Ukrainian Hydrometeorological Center", + "iso3": "UKR", + "language": "Ukrainian", + "picUrl": "https://alert-hub.s3.amazonaws.com/images/ua-meteo-en.png", + "capAlertFeed": "https://feeds.meteoalarm.org/feeds/meteoalarm-legacy-atom-ukraine", + "format": "atom" + }, + { + "sourceId": "ye-yms-ar", + "name": "Yemen: Yemen Meteorological Service", + "iso3": "YEM", + "language": "Arabic", + "picUrl": "https://alert-hub.s3.amazonaws.com/images/ye-yms-ar.png", + "capAlertFeed": "https://cap-sources.s3.amazonaws.com/ye-yms-ar/rss.xml", + "format": "rss" } ] diff --git a/apps/cap_feed/views.py b/apps/cap_feed/views.py index 6d3da97..0fbe7b4 100644 --- a/apps/cap_feed/views.py +++ b/apps/cap_feed/views.py @@ -6,16 +6,20 @@ def index(request): - latest_alert_list = Alert.objects.order_by("-sent").values( - 'identifier', - 'sender', - 'sent', - 'status', - 'msg_type', - country_name=models.F('country__name'), - country_iso3=models.F('country__iso3'), - feed_url=models.F('feed__url'), - )[:10] + latest_alert_list = ( + Alert.get_queryset() + .order_by("-sent") + .values( + 'identifier', + 'sender', + 'sent', + 'status', + 'msg_type', + country_name=models.F('country__name'), + country_iso3=models.F('country__iso3'), + feed_url=models.F('feed__url'), + )[:10] + ) template = loader.get_template("cap_feed/index.html") context = { "latest_alert_list": latest_alert_list, From 09d6ef4a670a0ddb6f119f71df60e03210a3017e Mon Sep 17 00:00:00 2001 From: thenav56 Date: Tue, 5 Nov 2024 20:00:38 +0545 Subject: [PATCH 11/11] Remove feed --- apps/cap_feed/feeds.json | 9 --------- apps/cap_feed/queries.py | 2 +- apps/cap_feed/types.py | 6 +++--- 3 files changed, 4 insertions(+), 13 deletions(-) diff --git a/apps/cap_feed/feeds.json b/apps/cap_feed/feeds.json index 5b6da4f..59145e0 100644 --- a/apps/cap_feed/feeds.json +++ b/apps/cap_feed/feeds.json @@ -89,15 +89,6 @@ "capAlertFeed": "https://cap-sources.s3.amazonaws.com/bj-meteo-en/rss.xml", "format": "rss" }, - { - "sourceId": "bj-meteo-fr", - "name": "Benin: Agence Nationale de la Météorologie", - "iso3": "BEN", - "language": "French", - "picUrl": "https://alert-hub.s3.amazonaws.com/images/bj-meteo-fr.png", - "capAlertFeed": "https://cap-sources.s3.amazonaws.com/bj-meteo-fr/rss.xml", - "format": "rss" - }, { "sourceId": "ba-fhmzbih-bs", "name": "Bosnia and Herzegovina: Federalni hidrometeorološki zavod BiH", diff --git a/apps/cap_feed/queries.py b/apps/cap_feed/queries.py index 50fff4b..fb1ca2a 100644 --- a/apps/cap_feed/queries.py +++ b/apps/cap_feed/queries.py @@ -132,7 +132,7 @@ async def feed(self, info: Info, pk: strawberry.ID) -> FeedType | None: @strawberry_django.field async def alert(self, info: Info, pk: strawberry.ID) -> AlertType | None: - return await get_alert_queryset(None, is_list=False).filter(pk=pk).afirst() + return await get_alert_queryset(None, is_active=False).filter(pk=pk).afirst() @strawberry_django.field async def alert_info(self, info: Info, pk: strawberry.ID) -> AlertInfoType | None: diff --git a/apps/cap_feed/types.py b/apps/cap_feed/types.py index e0871e6..8938d5c 100644 --- a/apps/cap_feed/types.py +++ b/apps/cap_feed/types.py @@ -28,9 +28,9 @@ ) -def get_alert_queryset(queryset: models.QuerySet | None, is_list: bool): +def get_alert_queryset(queryset: models.QuerySet | None, is_active: bool): qs = get_queryset_for_model(Alert, queryset) - if is_list: + if is_active: return qs.filter(is_expired=False) return qs @@ -369,7 +369,7 @@ class AlertType: @staticmethod def get_queryset(_, queryset: models.QuerySet | None, info: Info): - return get_alert_queryset(queryset, is_list=True) + return get_alert_queryset(queryset, is_active=True) # TODO: Create a separate country_name @strawberry.field