From acb16c46c10f29c2eae9e3ad6cbcf486e590f911 Mon Sep 17 00:00:00 2001 From: Vincent Date: Mon, 4 Nov 2024 17:37:31 +0100 Subject: [PATCH 1/6] Add RTC fishing alert --- datascience/src/pipeline/entities/alerts.py | 1 + datascience/src/pipeline/flows/position_alerts.py | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/datascience/src/pipeline/entities/alerts.py b/datascience/src/pipeline/entities/alerts.py index b22eec7b1e..4a9ed54e36 100644 --- a/datascience/src/pipeline/entities/alerts.py +++ b/datascience/src/pipeline/entities/alerts.py @@ -9,3 +9,4 @@ class AlertType(Enum): MISSING_FAR_ALERT = "MISSING_FAR_ALERT" MISSING_FAR_48_HOURS_ALERT = "MISSING_FAR_48_HOURS_ALERT" SUSPICION_OF_UNDER_DECLARATION = "SUSPICION_OF_UNDER_DECLARATION" + RTC_FISHING_ALERT = "RTC_FISHING_ALERT" diff --git a/datascience/src/pipeline/flows/position_alerts.py b/datascience/src/pipeline/flows/position_alerts.py index 3b0578c211..d02f12743d 100644 --- a/datascience/src/pipeline/flows/position_alerts.py +++ b/datascience/src/pipeline/flows/position_alerts.py @@ -130,6 +130,11 @@ def get_alert_type_zones_table(alert_type: str) -> ZonesTable: "filter_column": "iso_sov1", "geometry_column": "wkb_geometry", }, + "RTC_FISHING_ALERT": { + "table": "regulations", + "filter_column": "law_type", + "geometry_column": "geometry", + }, } logger = prefect.context.get("logger") From c4c02f4a94663d06a7b8f307aa234d7c9d82f63d Mon Sep 17 00:00:00 2001 From: Vincent Date: Mon, 4 Nov 2024 17:58:16 +0100 Subject: [PATCH 2/6] Add eez_areas parameter --- .../src/pipeline/flows/position_alerts.py | 44 +++++++++++++------ .../test_flows/test_position_alerts.py | 25 ++++++++--- 2 files changed, 51 insertions(+), 18 deletions(-) diff --git a/datascience/src/pipeline/flows/position_alerts.py b/datascience/src/pipeline/flows/position_alerts.py index d02f12743d..a9ec775415 100644 --- a/datascience/src/pipeline/flows/position_alerts.py +++ b/datascience/src/pipeline/flows/position_alerts.py @@ -167,14 +167,17 @@ def get_alert_type_zones_table(alert_type: str) -> ZonesTable: @task(checkpoint=False) def make_positions_in_alert_query( + *, positions_table: Table, facades_table: Table, zones_table: ZonesTable, + eez_areas_table: Table, only_fishing_positions: bool, zones: List = None, hours_from_now: int = 8, flag_states: List = None, except_flag_states: List = None, + eez_areas: List = None, ) -> Select: """ Creates select statement for the query to execute to compute positions in alert. @@ -183,6 +186,7 @@ def make_positions_in_alert_query( positions_table (Table): `SQLAlchemy.Table` of positions. facades_table (Table): `SQLAlchemy.Table` of façades. zones_table (ZonesTable): `ZonesTable` of zones. + eez_areas_table (Table): `SQLAlchemy.Table` of Exclusive Economic Zones. only_fishing_positions (bool): If `True`, filters positions to keep only positions tagged as `is_fishing`. zones (List, optional): If provided, adds a @@ -201,6 +205,25 @@ def make_positions_in_alert_query( now = datetime.utcnow() start_date = now - timedelta(hours=hours_from_now) + from_tables = positions_table.join( + zones_table.table, + ST_Intersects( + positions_table.c.geometry, + zones_table.table.c[zones_table.geometry_column], + ), + ).join( + facades_table, + ST_Intersects(positions_table.c.geometry, facades_table.c.geometry), + isouter=True, + ) + + if eez_areas: + from_tables = from_tables.join( + eez_areas_table, + ST_Intersects(positions_table.c.geometry, eez_areas_table.c.wkb_geometry), + isouter=True, + ) + q = ( select( positions_table.c.id, @@ -216,19 +239,7 @@ def make_positions_in_alert_query( positions_table.c.longitude, facades_table.c.facade, ) - .select_from( - positions_table.join( - zones_table.table, - ST_Intersects( - positions_table.c.geometry, - zones_table.table.c[zones_table.geometry_column], - ), - ).join( - facades_table, - ST_Intersects(positions_table.c.geometry, facades_table.c.geometry), - isouter=True, - ) - ) + .select_from(from_tables) .where( and_( positions_table.c.date_time > start_date, @@ -254,6 +265,9 @@ def make_positions_in_alert_query( if except_flag_states: q = q.where(positions_table.c.flag_state.notin_(except_flag_states)) + if eez_areas: + q = q.where(eez_areas_table.c["iso_sov1"].in_(eez_areas)) + return q @@ -448,6 +462,7 @@ def get_vessels_in_alert(positions_in_alert: pd.DataFrame) -> pd.DataFrame: include_vessels_unknown_gear = Parameter( "include_vessels_unknown_gear", default=True ) + eez_areas = Parameter("eez_areas", default=None) must_filter_on_gears = alert_has_gear_parameters( fishing_gears, fishing_gear_categories @@ -456,6 +471,7 @@ def get_vessels_in_alert(positions_in_alert: pd.DataFrame) -> pd.DataFrame: positions_table = get_table("positions") vessels_table = get_table("vessels") districts_table = get_table("districts") + eez_areas_table = get_table("eez_areas") zones_table = get_alert_type_zones_table(alert_type) facades_table = get_table("facade_areas_subdivided") @@ -463,11 +479,13 @@ def get_vessels_in_alert(positions_in_alert: pd.DataFrame) -> pd.DataFrame: positions_table=positions_table, facades_table=facades_table, zones_table=zones_table, + eez_areas_table=eez_areas_table, only_fishing_positions=only_fishing_positions, zones=zones, hours_from_now=hours_from_now, flag_states=flag_states, except_flag_states=except_flag_states, + eez_areas=eez_areas, ) positions_in_alert = read_query_task("monitorfish_remote", positions_query) diff --git a/datascience/tests/test_pipeline/test_flows/test_position_alerts.py b/datascience/tests/test_pipeline/test_flows/test_position_alerts.py index 766753f8a0..de5fe00eb4 100644 --- a/datascience/tests/test_pipeline/test_flows/test_position_alerts.py +++ b/datascience/tests/test_pipeline/test_flows/test_position_alerts.py @@ -118,6 +118,15 @@ def test_make_positions_in_alert_query(): Column("geometry", Geometry), ) + eez_areas_table = Table( + "eez_areas", + meta, + Column("id", Integer), + Column("iso_sov1", VARCHAR), + Column("some_other_data", VARCHAR), + Column("wkb_geometry", Geometry), + ) + facades_table = Table( "facades", meta, Column("facade", VARCHAR), Column("geometry", Geometry) ) @@ -145,11 +154,13 @@ def test_make_positions_in_alert_query(): positions_table=positions_table, facades_table=facades_table, zones_table=zones_table, + eez_areas_table=eez_areas_table, only_fishing_positions=only_fishing_positions, zones=zones, hours_from_now=hours_from_now, flag_states=flag_states, except_flag_states=except_flag_states, + eez_areas=["FRA", "BEL"], ) query = str(select_statement.compile(compile_kwargs={"literal_binds": True})) @@ -171,6 +182,8 @@ def test_make_positions_in_alert_query(): "ON ST_Intersects(positions.geometry, zones.geometry_col) " "LEFT OUTER JOIN facades " "ON ST_Intersects(positions.geometry, facades.geometry) " + "LEFT OUTER JOIN eez_areas " + "ON ST_Intersects(positions.geometry, eez_areas.wkb_geometry) " "\nWHERE positions.date_time > '2021-01-01 10:10:00' " "AND positions.date_time < '2021-01-01 16:10:00' " "AND (" @@ -180,7 +193,8 @@ def test_make_positions_in_alert_query(): "AND positions.is_fishing " "AND zones.zone_name IN ('Zone A') " "AND positions.flag_state IN ('NL, DE') " - "AND (positions.flag_state NOT IN ('VE'))" + "AND (positions.flag_state NOT IN ('VE')) " + "AND eez_areas.iso_sov1 IN ('FRA', 'BEL')" ) assert query == expected_query @@ -190,10 +204,11 @@ def test_make_positions_in_alert_query(): only_fishing_positions = False select_statement = make_positions_in_alert_query.run( - positions_table, - facades_table, - zones_table, - only_fishing_positions, + positions_table=positions_table, + facades_table=facades_table, + zones_table=zones_table, + eez_areas_table=eez_areas_table, + only_fishing_positions=only_fishing_positions, hours_from_now=hours_from_now, ) From 95f74b6a249253efbf6b10c25d00b7b9d0046fd4 Mon Sep 17 00:00:00 2001 From: Vincent Date: Tue, 5 Nov 2024 09:39:04 +0100 Subject: [PATCH 3/6] Add RTC alert flow test --- .../test_data/emails/REGULATIONS_CHECKUP.html | 7 +++- .../V666.24__Reset_test_regulations.sql | 11 ++--- .../test_flows/test_position_alerts.py | 42 ++++++++++++++++++- .../test_flows/test_regulations_checkup.py | 20 ++++++--- .../test_flows/test_regulations_open_data.py | 18 +++++++- 5 files changed, 84 insertions(+), 14 deletions(-) diff --git a/datascience/tests/test_data/emails/REGULATIONS_CHECKUP.html b/datascience/tests/test_data/emails/REGULATIONS_CHECKUP.html index f71dbddc04..b12da9902c 100644 --- a/datascience/tests/test_data/emails/REGULATIONS_CHECKUP.html +++ b/datascience/tests/test_data/emails/REGULATIONS_CHECKUP.html @@ -174,7 +174,7 @@

Références manquantes dans Monitorfish

-

Les 1 réglementations suivantes ne comportent pas de référence réglementaire ¯\_(ツ)_/¯

+

Les 2 réglementations suivantes ne comportent pas de référence réglementaire ¯\_(ツ)_/¯

@@ -189,6 +189,11 @@

Références manquantes dans Monitorfish

+ + + + +
Mediterranée - filets Zone A
Reg. RTCZone RTC DNKZone RTC

Pensez à ajouter des références réglementaires dans le Back'O'Fish `·.¸¸ ><((((º>.·´¯`·><((((º>

diff --git a/datascience/tests/test_data/remote_database/V666.24__Reset_test_regulations.sql b/datascience/tests/test_data/remote_database/V666.24__Reset_test_regulations.sql index 521ff0d3c7..7ec5a1aa9b 100644 --- a/datascience/tests/test_data/remote_database/V666.24__Reset_test_regulations.sql +++ b/datascience/tests/test_data/remote_database/V666.24__Reset_test_regulations.sql @@ -1,10 +1,11 @@ DELETE FROM public.regulations; INSERT INTO public.regulations ( - id, law_type, topic, zone, regulatory_references, geometry + id, law_type, topic, zone, regulatory_references, geometry ) VALUES - (1, 'Reg. Facade 1', 'Morbihan - bivalves', 'Secteur 1', '[{"url": "http://external.site.regulation", "reference": "External regulation", "endDate": "2017-07-14T02:40:00.000Z"}]', '0106000020E610000001000000010300000001000000050000000000000000000000000000000000000000000000000024400000000000000000000000000000244000000000000024400000000000000000000000000000244000000000000000000000000000000000'), + (1, 'Reg. Facade 1', 'Morbihan - bivalves', 'Secteur 1', '[{"url": "http://external.site.regulation", "reference": "External regulation", "endDate": "2017-07-14T02:40:00.000Z"}]', '0106000020E610000001000000010300000001000000050000000000000000000000000000000000000000000000000024400000000000000000000000000000244000000000000024400000000000000000000000000000244000000000000000000000000000000000'), (2, 'Reg. Facade 1', 'Morbihan - bivalves', 'Secteur 2', '[{"url": "http://legipeche.metier.e2.rie.gouv.fr/some-regulation-a666.html?var=12", "reference": "some regulation", "endDate": 123456789}, {"url": "http://legipeche.metier.e2.rie.gouv.fr/modified-regulation-a668.html", "reference": "some other regulation", "endDate": "infinite"}]', '0106000020E610000001000000010300000001000000050000000000000000005E4000000000000034C00000000000E0604000000000000034C00000000000E0604000000000000024C00000000000005E4000000000000024C00000000000005E4000000000000034C0'), - (3, 'Reg. Facade 2', 'Mediterranée - filets', 'Zone A', 'null', '0106000020E610000001000000010300000001000000050000000000000000004EC000000000000024400000000000804BC000000000000024400000000000804BC000000000000034400000000000004EC000000000000034400000000000004EC00000000000002440'), - (4, 'Reg. Facade 2', 'Mediterranée - filets', 'Zone B', '[{"url": "http://legipeche.metier.e2.rie.gouv.fr/regulation-a689.html", "reference": "Med regulation", "endDate": "2030-03-17T17:46:40.000Z"}]', '0106000020E6100000010000000103000000010000000500000000000000000024C00000000000804640000000000040654000000000008046400000000000406540000000000000494000000000000024C0000000000000494000000000000024C00000000000804640'), - (5, 'Reg. Facade 2', 'Mediterranée - filets', 'Zone C', '[{"url": "http://legipeche.metier.e2.rie.gouv.fr/deleted-regulation-a671.html", "reference": "Dead link regulation"}]', '0106000020E610000001000000010300000001000000050000000000000000805BC00000000000004E4000000000000059C00000000000004E4000000000000059C000000000008051400000000000805BC000000000008051400000000000805BC00000000000004E40'); + (3, 'Reg. Facade 2', 'Mediterranée - filets', 'Zone A', 'null', '0106000020E610000001000000010300000001000000050000000000000000004EC000000000000024400000000000804BC000000000000024400000000000804BC000000000000034400000000000004EC000000000000034400000000000004EC00000000000002440'), + (4, 'Reg. Facade 2', 'Mediterranée - filets', 'Zone B', '[{"url": "http://legipeche.metier.e2.rie.gouv.fr/regulation-a689.html", "reference": "Med regulation", "endDate": "2030-03-17T17:46:40.000Z"}]', '0106000020E6100000010000000103000000010000000500000000000000000024C00000000000804640000000000040654000000000008046400000000000406540000000000000494000000000000024C0000000000000494000000000000024C00000000000804640'), + (5, 'Reg. Facade 2', 'Mediterranée - filets', 'Zone C', '[{"url": "http://legipeche.metier.e2.rie.gouv.fr/deleted-regulation-a671.html", "reference": "Dead link regulation"}]', '0106000020E610000001000000010300000001000000050000000000000000805BC00000000000004E4000000000000059C00000000000004E4000000000000059C000000000008051400000000000805BC000000000008051400000000000805BC00000000000004E40'), + (6, 'Reg. RTC', 'Zone RTC DNK', 'Zone RTC', 'null', ST_Multi(ST_Polygon('LINESTRING(-1.0 49.0, 0.0 49.0, 0.0 50.0, -1.0 50.0, -1.0 49.0)'::geometry, 4326))); diff --git a/datascience/tests/test_pipeline/test_flows/test_position_alerts.py b/datascience/tests/test_pipeline/test_flows/test_position_alerts.py index de5fe00eb4..9df8e2f5f1 100644 --- a/datascience/tests/test_pipeline/test_flows/test_position_alerts.py +++ b/datascience/tests/test_pipeline/test_flows/test_position_alerts.py @@ -1104,7 +1104,6 @@ def test_flow_french_eez_fishing_alert(reset_test_data): # With these parameters, 2 french vessels should be in alert. alert_type = "FRENCH_EEZ_FISHING_ALERT" alert_config_name = "ALERTE_1" - alert_config_name = "ALERTE_1" zones = ["FRA"] hours_from_now = 8 only_fishing_positions = False @@ -1181,3 +1180,44 @@ def test_flow_french_eez_fishing_alert(reset_test_data): .drop(columns=["creation_date", "id"]), expected_pending_alerts.sort_values("vessel_id").reset_index(drop=True), ) + + +def test_flow_rtc_fishing_alert(reset_test_data): + # With these parameters, 2 french vessels should be in alert. + alert_type = "RTC_FISHING_ALERT" + alert_config_name = "RTC_FISHING_ALERT" + zones = ["Reg. RTC"] + hours_from_now = 8 + only_fishing_positions = True + eez_areas = ["EST"] + flag_states = ["FR"] + + flow.schedule = None + state = flow.run( + alert_type=alert_type, + alert_config_name=alert_config_name, + zones=zones, + hours_from_now=hours_from_now, + only_fishing_positions=only_fishing_positions, + flag_states=flag_states, + eez_areas=eez_areas, + ) + + assert state.is_successful() + + pending_alerts = read_query( + """ + SELECT * + FROM pending_alerts + WHERE alert_config_name = 'RTC_FISHING_ALERT' + """, + db="monitorfish_remote", + ) + assert len(pending_alerts) == 1 + assert ( + pending_alerts.loc[ + pending_alerts.alert_config_name == "RTC_FISHING_ALERT", + "internal_reference_number", + ].values[0] + == "ABC000306959" + ) diff --git a/datascience/tests/test_pipeline/test_flows/test_regulations_checkup.py b/datascience/tests/test_pipeline/test_flows/test_regulations_checkup.py index 4fbac3deac..02ec04a8ae 100644 --- a/datascience/tests/test_pipeline/test_flows/test_regulations_checkup.py +++ b/datascience/tests/test_pipeline/test_flows/test_regulations_checkup.py @@ -74,6 +74,7 @@ def monitorfish_regulations() -> pd.DataFrame: "Reg. Facade 2", "Reg. Facade 2", "Reg. Facade 2", + "Reg. RTC", ], "topic": [ "Morbihan - bivalves", @@ -82,6 +83,7 @@ def monitorfish_regulations() -> pd.DataFrame: "Mediterranée - filets", "Mediterranée - filets", "Mediterranée - filets", + "Zone RTC DNK", ], "zone": [ "Secteur 1", @@ -90,6 +92,7 @@ def monitorfish_regulations() -> pd.DataFrame: "Zone A", "Zone B", "Zone C", + "Zone RTC", ], "url": [ "http://external.site.regulation", @@ -107,6 +110,7 @@ def monitorfish_regulations() -> pd.DataFrame: "http://legipeche.metier.e2" ".rie.gouv.fr/deleted-regulation-a671.html" ), + None, ], "reference": [ "External regulation", @@ -115,6 +119,7 @@ def monitorfish_regulations() -> pd.DataFrame: None, "Med regulation", "Dead link regulation", + None, ], "end_date": [ datetime.datetime(2017, 7, 14, 2, 40, 0), @@ -123,6 +128,7 @@ def monitorfish_regulations() -> pd.DataFrame: None, datetime.datetime(2030, 3, 17, 17, 46, 40), None, + None, ], } ) @@ -216,7 +222,7 @@ def legipeche_regulations() -> pd.DataFrame: @pytest.fixture def monitorfish_regulations_with_id(monitorfish_regulations) -> pd.DataFrame: regulations = monitorfish_regulations.assign( - article_id=[None, "666", "668", None, "689", "671"] + article_id=[None, "666", "668", None, "689", "671", None] ) return regulations @@ -281,9 +287,9 @@ def transformed_regulations() -> pd.DataFrame: def missing_references() -> pd.DataFrame: references = pd.DataFrame( { - "Type de réglementation": ["Reg. Facade 2"], - "Thématique": ["Mediterranée - filets"], - "Zone": ["Zone A"], + "Type de réglementation": ["Reg. Facade 2", "Reg. RTC"], + "Thématique": ["Mediterranée - filets", "Zone RTC DNK"], + "Zone": ["Zone A", "Zone RTC"], } ) return references @@ -298,8 +304,10 @@ def unknown_links() -> set: @pytest.fixture -def dead_links(monitorfish_regulations_with_id) -> pd.DataFrame: - return monitorfish_regulations_with_id.iloc[[0, -1]].reset_index(drop=True) +def dead_links(monitorfish_regulations_with_id, unknown_links) -> pd.DataFrame: + return monitorfish_regulations_with_id[ + monitorfish_regulations_with_id.url.isin(unknown_links) + ].reset_index(drop=True) @pytest.fixture diff --git a/datascience/tests/test_pipeline/test_flows/test_regulations_open_data.py b/datascience/tests/test_pipeline/test_flows/test_regulations_open_data.py index 6e34f92eed..f63a9d567d 100644 --- a/datascience/tests/test_pipeline/test_flows/test_regulations_open_data.py +++ b/datascience/tests/test_pipeline/test_flows/test_regulations_open_data.py @@ -26,6 +26,7 @@ def regulations_open_data() -> gpd.GeoDataFrame: "Reg. Facade 2", "Reg. Facade 2", "Reg. Facade 2", + "Reg. RTC", ], "thematique": [ "Morbihan - bivalves", @@ -33,6 +34,7 @@ def regulations_open_data() -> gpd.GeoDataFrame: "Mediterranée - filets", "Mediterranée - filets", "Mediterranée - filets", + "Zone RTC DNK", ], "zone": [ "Secteur 1", @@ -40,6 +42,7 @@ def regulations_open_data() -> gpd.GeoDataFrame: "Zone A", "Zone B", "Zone C", + "Zone RTC", ], "reglementations": [ "External regulation", @@ -47,6 +50,7 @@ def regulations_open_data() -> gpd.GeoDataFrame: None, "Med regulation", "Dead link regulation", + None, ], "geometry": [ make_square_multipolygon(0, 0, 10, 10), @@ -54,6 +58,7 @@ def regulations_open_data() -> gpd.GeoDataFrame: make_square_multipolygon(-60, 10, 5, 10), make_square_multipolygon(-10, 45, 180, 5), make_square_multipolygon(-110, 60, 10, 10), + make_square_multipolygon(-1, 49, 1, 1), ], "wkt": [ "MULTIPOLYGON(((0 0,10 0,10 10,0 10,0 0)))", @@ -61,6 +66,7 @@ def regulations_open_data() -> gpd.GeoDataFrame: "MULTIPOLYGON(((-60 10,-55 10,-55 20,-60 20,-60 10)))", "MULTIPOLYGON(((-10 45,170 45,170 50,-10 50,-10 45)))", "MULTIPOLYGON(((-110 60,-100 60,-100 70,-110 70,-110 60)))", + "MULTIPOLYGON(((-1 49,0 49,0 50,-1 50,-1 49)))", ], } ) @@ -76,6 +82,7 @@ def regulations_for_csv() -> pd.DataFrame: "Reg. Facade 2", "Reg. Facade 2", "Reg. Facade 2", + "Reg. RTC", ], "thematique": [ "Morbihan - bivalves", @@ -83,6 +90,7 @@ def regulations_for_csv() -> pd.DataFrame: "Mediterranée - filets", "Mediterranée - filets", "Mediterranée - filets", + "Zone RTC DNK", ], "zone": [ "Secteur 1", @@ -90,6 +98,7 @@ def regulations_for_csv() -> pd.DataFrame: "Zone A", "Zone B", "Zone C", + "Zone RTC", ], "reglementations": [ "External regulation", @@ -97,6 +106,7 @@ def regulations_for_csv() -> pd.DataFrame: None, "Med regulation", "Dead link regulation", + None, ], "wkt": [ "MULTIPOLYGON(((0 0,10 0,10 10,0 10,0 0)))", @@ -104,6 +114,7 @@ def regulations_for_csv() -> pd.DataFrame: "MULTIPOLYGON(((-60 10,-55 10,-55 20,-60 20,-60 10)))", "MULTIPOLYGON(((-10 45,170 45,170 50,-10 50,-10 45)))", "MULTIPOLYGON(((-110 60,-100 60,-100 70,-110 70,-110 60)))", + "MULTIPOLYGON(((-1 49,0 49,0 50,-1 50,-1 49)))", ], } ) @@ -119,6 +130,7 @@ def regulations_for_geopackage() -> gpd.GeoDataFrame: "Reg. Facade 2", "Reg. Facade 2", "Reg. Facade 2", + "Reg. RTC", ], "thematique": [ "Morbihan - bivalves", @@ -126,6 +138,7 @@ def regulations_for_geopackage() -> gpd.GeoDataFrame: "Mediterranée - filets", "Mediterranée - filets", "Mediterranée - filets", + "Zone RTC DNK", ], "zone": [ "Secteur 1", @@ -133,6 +146,7 @@ def regulations_for_geopackage() -> gpd.GeoDataFrame: "Zone A", "Zone B", "Zone C", + "Zone RTC", ], "reglementations": [ "External regulation", @@ -140,6 +154,7 @@ def regulations_for_geopackage() -> gpd.GeoDataFrame: None, "Med regulation", "Dead link regulation", + None, ], "geometry": [ make_square_multipolygon(0, 0, 10, 10), @@ -147,6 +162,7 @@ def regulations_for_geopackage() -> gpd.GeoDataFrame: make_square_multipolygon(-60, 10, 5, 10), make_square_multipolygon(-10, 45, 180, 5), make_square_multipolygon(-110, 60, 10, 10), + make_square_multipolygon(-1, 49, 1, 1), ], } ) @@ -191,7 +207,7 @@ def test_flow(reset_test_data, regulations_for_csv, regulations_for_geopackage): ].result assert isinstance(geopackage_file_object, BytesIO) - layers = ["Reg. Facade 1", "Reg. Facade 2"] + layers = ["Reg. Facade 1", "Reg. Facade 2", "Reg. RTC"] gdfs = [] for layer in layers: geopackage_file_object.seek(0) From 49b3b0b62fccb6c9e61426a7eba2c89015365b4e Mon Sep 17 00:00:00 2001 From: Vincent Date: Tue, 5 Nov 2024 09:40:28 +0100 Subject: [PATCH 4/6] Schedule RTC alert --- datascience/src/pipeline/flows_config.py | 25 ++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/datascience/src/pipeline/flows_config.py b/datascience/src/pipeline/flows_config.py index 60f73f5ea6..498e41a02c 100644 --- a/datascience/src/pipeline/flows_config.py +++ b/datascience/src/pipeline/flows_config.py @@ -212,8 +212,33 @@ "include_vessels_unknown_gear": True, }, ), + # RTC alert for vessels flying a FR flag : applies everywhere in the world + clocks.CronClock( + "2,12,22,32,42,52 * * * *", + parameter_defaults={ + "alert_type": "RTC_FISHING_ALERT", + "alert_config_name": "RTC_FISHING_ALERT", + "zones": ["Reg. RTC"], + "hours_from_now": 8, + "flag_states": ["FR"], + "only_fishing_positions": True, + }, + ), + # RTC alert for vessels flying a non FR flag : applies only in the FRA EEZ clocks.CronClock( "3,13,23,33,43,53 * * * *", + parameter_defaults={ + "alert_type": "RTC_FISHING_ALERT", + "alert_config_name": "RTC_FISHING_ALERT", + "zones": ["Reg. RTC"], + "hours_from_now": 8, + "except_flag_states": ["FR"], + "eez_areas": ["FRA"], + "only_fishing_positions": True, + }, + ), + clocks.CronClock( + "4,14,24,34,44,54 * * * *", parameter_defaults={ "alert_type": "FRENCH_EEZ_FISHING_ALERT", "alert_config_name": "FRENCH_EEZ_FISHING_ALERT", From 779cc311f8330f44962cfcda7d43132d25ab5a89 Mon Sep 17 00:00:00 2001 From: Vincent Date: Tue, 5 Nov 2024 10:08:55 +0100 Subject: [PATCH 5/6] Add RTC fishing alerts in backend --- .../domain/entities/alerts/type/AlertTypeMapping.kt | 4 ++++ .../domain/entities/alerts/type/RTCFishingAlert.kt | 7 +++++++ .../monitorfish/domain/use_cases/alert/GetPendingAlerts.kt | 1 + .../monitorfish/domain/use_cases/alert/SilenceAlert.kt | 1 + .../monitorfish/domain/use_cases/GetPendingAlertsUTests.kt | 1 + .../fr/gouv/cnsp/monitorfish/domain/use_cases/TestUtils.kt | 1 + 6 files changed, 15 insertions(+) create mode 100644 backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/alerts/type/RTCFishingAlert.kt diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/alerts/type/AlertTypeMapping.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/alerts/type/AlertTypeMapping.kt index d6f5aaca68..07adf82af6 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/alerts/type/AlertTypeMapping.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/alerts/type/AlertTypeMapping.kt @@ -20,6 +20,10 @@ enum class AlertTypeMapping( clazz = TwelveMilesFishingAlert::class.java, alertName = "12 milles - Pêche sans droits historiques", ), + RTC_FISHING_ALERT( + clazz = RTCFishingAlert::class.java, + alertName = "Pêche en zone RTC", + ), MISSING_DEP_ALERT( clazz = MissingDEPAlert::class.java, alertName = "Sortie en mer sans émission de message \"DEP\"", diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/alerts/type/RTCFishingAlert.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/alerts/type/RTCFishingAlert.kt new file mode 100644 index 0000000000..f9b92e623c --- /dev/null +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/alerts/type/RTCFishingAlert.kt @@ -0,0 +1,7 @@ +package fr.gouv.cnsp.monitorfish.domain.entities.alerts.type + +class RTCFishingAlert( + override var seaFront: String? = null, + override var dml: String? = null, + var riskFactor: Double? = null, +) : AlertType(AlertTypeMapping.RTC_FISHING_ALERT, seaFront, dml, 2596) diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/alert/GetPendingAlerts.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/alert/GetPendingAlerts.kt index eaca16376b..5bd37fbec8 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/alert/GetPendingAlerts.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/alert/GetPendingAlerts.kt @@ -21,6 +21,7 @@ class GetPendingAlerts( AlertTypeMapping.THREE_MILES_TRAWLING_ALERT, AlertTypeMapping.FRENCH_EEZ_FISHING_ALERT, AlertTypeMapping.TWELVE_MILES_FISHING_ALERT, + AlertTypeMapping.RTC_FISHING_ALERT, AlertTypeMapping.MISSING_DEP_ALERT, AlertTypeMapping.MISSING_FAR_48_HOURS_ALERT, AlertTypeMapping.SUSPICION_OF_UNDER_DECLARATION_ALERT, diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/alert/SilenceAlert.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/alert/SilenceAlert.kt index 4e872d2639..4b8d134e80 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/alert/SilenceAlert.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/alert/SilenceAlert.kt @@ -47,6 +47,7 @@ class SilenceAlert( AlertTypeMapping.THREE_MILES_TRAWLING_ALERT -> ThreeMilesTrawlingAlert() AlertTypeMapping.FRENCH_EEZ_FISHING_ALERT -> FrenchEEZFishingAlert() AlertTypeMapping.TWELVE_MILES_FISHING_ALERT -> TwelveMilesFishingAlert() + AlertTypeMapping.RTC_FISHING_ALERT -> RTCFishingAlert() AlertTypeMapping.MISSING_DEP_ALERT -> MissingDEPAlert() AlertTypeMapping.MISSING_FAR_ALERT -> MissingFARAlert() AlertTypeMapping.MISSING_FAR_48_HOURS_ALERT -> MissingFAR48HoursAlert() diff --git a/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/GetPendingAlertsUTests.kt b/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/GetPendingAlertsUTests.kt index 558810fc7e..30e494b17f 100644 --- a/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/GetPendingAlertsUTests.kt +++ b/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/GetPendingAlertsUTests.kt @@ -65,6 +65,7 @@ class GetPendingAlertsUTests { AlertTypeMapping.THREE_MILES_TRAWLING_ALERT, AlertTypeMapping.FRENCH_EEZ_FISHING_ALERT, AlertTypeMapping.TWELVE_MILES_FISHING_ALERT, + AlertTypeMapping.RTC_FISHING_ALERT, AlertTypeMapping.MISSING_DEP_ALERT, AlertTypeMapping.MISSING_FAR_48_HOURS_ALERT, AlertTypeMapping.SUSPICION_OF_UNDER_DECLARATION_ALERT, diff --git a/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/TestUtils.kt b/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/TestUtils.kt index aeb376e428..d1a9d7299d 100644 --- a/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/TestUtils.kt +++ b/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/TestUtils.kt @@ -51,6 +51,7 @@ object TestUtils { AlertTypeMapping.THREE_MILES_TRAWLING_ALERT -> ThreeMilesTrawlingAlert(seaFront = NAMO.toString()) AlertTypeMapping.FRENCH_EEZ_FISHING_ALERT -> FrenchEEZFishingAlert(seaFront = NAMO.toString()) AlertTypeMapping.TWELVE_MILES_FISHING_ALERT -> TwelveMilesFishingAlert(seaFront = NAMO.toString()) + AlertTypeMapping.RTC_FISHING_ALERT -> RTCFishingAlert(seaFront = NAMO.toString()) AlertTypeMapping.MISSING_FAR_ALERT -> MissingFARAlert(seaFront = NAMO.toString()) AlertTypeMapping.MISSING_FAR_48_HOURS_ALERT -> MissingFAR48HoursAlert(seaFront = NAMO.toString()) else -> From c0858c2013e3bab7976ac4ad7d17397b628f2bc2 Mon Sep 17 00:00:00 2001 From: Vincent Date: Tue, 5 Nov 2024 10:10:45 +0100 Subject: [PATCH 6/6] Add RTC fishing alerts in front end --- frontend/src/domain/entities/alerts/constants.ts | 5 +++++ frontend/src/domain/entities/alerts/types.ts | 1 + 2 files changed, 6 insertions(+) diff --git a/frontend/src/domain/entities/alerts/constants.ts b/frontend/src/domain/entities/alerts/constants.ts index b96792dd75..50816d1c85 100644 --- a/frontend/src/domain/entities/alerts/constants.ts +++ b/frontend/src/domain/entities/alerts/constants.ts @@ -46,6 +46,11 @@ export const COMMON_ALERT_TYPE_OPTION: Record< nameWithAlertDetails: (percentOfTolerance, minimumWeightThreshold) => `Tolérance de ${percentOfTolerance}% non respectée, appliquée pour un poids minimum de ${minimumWeightThreshold}kg.` }, + RTC_FISHING_ALERT: { + code: PendingAlertValueType.RTC_FISHING_ALERT, + isOperationalAlert: true, + name: 'Pêche en zone RTC' + }, SUSPICION_OF_UNDER_DECLARATION_ALERT: { code: PendingAlertValueType.SUSPICION_OF_UNDER_DECLARATION_ALERT, isOperationalAlert: true, diff --git a/frontend/src/domain/entities/alerts/types.ts b/frontend/src/domain/entities/alerts/types.ts index 84afd98ebd..15b21e23f5 100644 --- a/frontend/src/domain/entities/alerts/types.ts +++ b/frontend/src/domain/entities/alerts/types.ts @@ -9,6 +9,7 @@ export enum PendingAlertValueType { MISSING_DEP_ALERT = 'MISSING_DEP_ALERT', MISSING_FAR_48_HOURS_ALERT = 'MISSING_FAR_48_HOURS_ALERT', MISSING_FAR_ALERT = 'MISSING_FAR_ALERT', + RTC_FISHING_ALERT = 'RTC_FISHING_ALERT', SUSPICION_OF_UNDER_DECLARATION_ALERT = 'SUSPICION_OF_UNDER_DECLARATION_ALERT', THREE_MILES_TRAWLING_ALERT = 'THREE_MILES_TRAWLING_ALERT', TWELVE_MILES_FISHING_ALERT = 'TWELVE_MILES_FISHING_ALERT'