Skip to content

Commit

Permalink
Differenciate notifications for unsupervised beacons
Browse files Browse the repository at this point in the history
  • Loading branch information
VincentAntoine committed Dec 11, 2023
1 parent b809bcc commit 221e7ed
Show file tree
Hide file tree
Showing 19 changed files with 402 additions and 52 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
ALTER TYPE public.beacon_malfunction_notification_type
ADD VALUE 'MALFUNCTION_AT_SEA_INITIAL_NOTIFICATION_UNSUPERVISED_BEACON'
AFTER 'MALFUNCTION_NOTIFICATION_TO_FOREIGN_FMC';

ALTER TYPE public.beacon_malfunction_notification_type
ADD VALUE 'MALFUNCTION_AT_PORT_INITIAL_NOTIFICATION_UNSUPERVISED_BEACON'
AFTER 'MALFUNCTION_AT_SEA_INITIAL_NOTIFICATION_UNSUPERVISED_BEACON';
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{% extends "base_template.jinja" %}
{% block message %}
{% block beginning %}
<p>Nous vous informons que <strong>le CNSP ne reçoit pas les positions de votre navire par le
système de surveillance satellitaire des navires (VMS).
{% if last_position_latitude and last_position_longitude and last_position_datetime_utc %}
</strong>La dernière position reçue date du {{ last_position_datetime_utc }},
latitude {{ last_position_latitude }}, longitude {{ last_position_longitude }}.
{%endif%}
</p>
{% endblock %}

<p>Si l'absence d'émission est liée à un problème technique, veuillez contacter votre installateur ou votre
opérateur satellitaire pour régler le problème.</p>

<p>Pour rappel, <strong>votre navire n'est pas autorisé à reprendre la
mer tant que votre installation VMS n'est pas en état de fonctionnement*.</strong></p>

<p>Le non-respect de ces obligations peut être sanctionné par une amende de 22 500€**.</p>
{% endblock %}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{% extends "base_template.jinja" %}
{% block message %}
{% block beginning %}
<p>Nous vous informons que <strong>le CNSP ne reçoit pas les positions de votre navire par le
système de surveillance satellitaire des navires (VMS).</strong>
{% if last_position_latitude and last_position_longitude and last_position_datetime_utc %}
La dernière position reçue date du {{ last_position_datetime_utc }},
latitude {{ last_position_latitude }}, longitude {{ last_position_longitude }}.
{%endif%}
</p>

<p>Veuillez contacter votre installateur ou votre opérateur satellitaire pour régler le problème.</p>
{% endblock %}

<p>Le CNSP vous informera de la reprise de réception des positions de votre navire.</p>

<p>Pour rappel, au terme de votre marée, <strong>votre navire ne sera pas autorisé à reprendre la
mer tant que votre installation VMS ne sera pas en état de fonctionnement*.</strong></p>
<p>Le non-respect de ces obligations peut être sanctionné par une amende de 22 500€**.</p>
{% endblock %}
15 changes: 8 additions & 7 deletions datascience/src/pipeline/entities/beacon_malfunctions.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,19 +58,27 @@ class EndOfMalfunctionReason(Enum):

class BeaconMalfunctionNotificationType(Enum):
MALFUNCTION_AT_SEA_INITIAL_NOTIFICATION = "MALFUNCTION_AT_SEA_INITIAL_NOTIFICATION"
MALFUNCTION_AT_SEA_INITIAL_NOTIFICATION_UNSUPERVISED_BEACON = (
"MALFUNCTION_AT_SEA_INITIAL_NOTIFICATION_UNSUPERVISED_BEACON"
)
MALFUNCTION_AT_SEA_REMINDER = "MALFUNCTION_AT_SEA_REMINDER"
MALFUNCTION_AT_PORT_INITIAL_NOTIFICATION = (
"MALFUNCTION_AT_PORT_INITIAL_NOTIFICATION"
)
MALFUNCTION_AT_PORT_INITIAL_NOTIFICATION_UNSUPERVISED_BEACON = (
"MALFUNCTION_AT_PORT_INITIAL_NOTIFICATION_UNSUPERVISED_BEACON"
)
MALFUNCTION_AT_PORT_REMINDER = "MALFUNCTION_AT_PORT_REMINDER"
END_OF_MALFUNCTION = "END_OF_MALFUNCTION"
MALFUNCTION_NOTIFICATION_TO_FOREIGN_FMC = "MALFUNCTION_NOTIFICATION_TO_FOREIGN_FMC"

def to_notification_subject_template(self):
type_subject_mapping = {
"MALFUNCTION_AT_SEA_INITIAL_NOTIFICATION": "{vessel_name} ({immat}) : interruption en mer des émissions VMS",
"MALFUNCTION_AT_SEA_INITIAL_NOTIFICATION_UNSUPERVISED_BEACON": "{vessel_name} ({immat}) : interruption en mer des émissions VMS",
"MALFUNCTION_AT_SEA_REMINDER": "{vessel_name} ({immat}) : RAPPEL : interruption en mer des émissions VMS",
"MALFUNCTION_AT_PORT_INITIAL_NOTIFICATION": "{vessel_name} ({immat}) : interruption à quai des émissions VMS",
"MALFUNCTION_AT_PORT_INITIAL_NOTIFICATION_UNSUPERVISED_BEACON": "{vessel_name} ({immat}) : interruption à quai des émissions VMS",
"MALFUNCTION_AT_PORT_REMINDER": "{vessel_name} ({immat}) : RAPPEL : interruption à quai des émissions VMS",
"END_OF_MALFUNCTION": "{vessel_name} ({immat}) : reprise des émissions VMS",
"MALFUNCTION_NOTIFICATION_TO_FOREIGN_FMC": "Interruption of VMS transmissions from fishing vessel {vessel_name} ({immat})",
Expand Down Expand Up @@ -129,9 +137,7 @@ def __post_init__(self):
)

def get_sms_addressees(self) -> List[BeaconMalfunctionNotificationAddressee]:

if not self.test_mode:

addressees = []

if self.vessel_mobile_phone:
Expand Down Expand Up @@ -167,9 +173,7 @@ def get_sms_addressees(self) -> List[BeaconMalfunctionNotificationAddressee]:
return addressees

def get_fax_addressees(self) -> List[BeaconMalfunctionNotificationAddressee]:

if not self.test_mode:

addressees = []

if self.vessel_fax:
Expand Down Expand Up @@ -205,9 +209,7 @@ def get_fax_addressees(self) -> List[BeaconMalfunctionNotificationAddressee]:
return addressees

def get_email_addressees(self) -> List[BeaconMalfunctionNotificationAddressee]:

if not self.test_mode:

vessel_emails = self.vessel_emails if self.vessel_emails else []
satellite_operator_emails = (
self.satellite_operator_emails if self.satellite_operator_emails else []
Expand Down Expand Up @@ -307,7 +309,6 @@ class BeaconMalfunctionMessageToSend:
communication_means: CommunicationMeans

def get_addressees(self) -> List[BeaconMalfunctionNotificationAddressee]:

if self.communication_means is CommunicationMeans.EMAIL:
return self.beacon_malfunction_to_notify.get_email_addressees()
elif self.communication_means is CommunicationMeans.SMS:
Expand Down
31 changes: 20 additions & 11 deletions datascience/src/pipeline/flows/notify_beacon_malfunctions.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,6 @@ def to_malfunctions_to_notify_list(

@task(checkpoint=False)
def get_templates() -> dict:

templates_locations = [
EMAIL_TEMPLATES_LOCATION / "beacon_malfunctions",
EMAIL_STYLESHEETS_LOCATION,
Expand All @@ -87,9 +86,19 @@ def get_templates() -> dict:
BeaconMalfunctionNotificationType.MALFUNCTION_AT_SEA_INITIAL_NOTIFICATION: (
env.get_template("malfunction_at_sea_initial_notification.jinja")
),
BeaconMalfunctionNotificationType.MALFUNCTION_AT_SEA_INITIAL_NOTIFICATION_UNSUPERVISED_BEACON: (
env.get_template(
"malfunction_at_sea_initial_notification_unsupervised_beacon.jinja"
)
),
BeaconMalfunctionNotificationType.MALFUNCTION_AT_PORT_INITIAL_NOTIFICATION: (
env.get_template("malfunction_at_port_initial_notification.jinja")
),
BeaconMalfunctionNotificationType.MALFUNCTION_AT_PORT_INITIAL_NOTIFICATION_UNSUPERVISED_BEACON: (
env.get_template(
"malfunction_at_port_initial_notification_unsupervised_beacon.jinja"
)
),
BeaconMalfunctionNotificationType.MALFUNCTION_AT_SEA_REMINDER: (
env.get_template("malfunction_at_sea_reminder.jinja")
),
Expand All @@ -109,7 +118,6 @@ def get_templates() -> dict:

@task(checkpoint=False)
def get_sms_templates() -> dict:

env = Environment(
loader=FileSystemLoader(SMS_TEMPLATES_LOCATION), autoescape=select_autoescape()
)
Expand All @@ -118,9 +126,19 @@ def get_sms_templates() -> dict:
BeaconMalfunctionNotificationType.MALFUNCTION_AT_SEA_INITIAL_NOTIFICATION: (
env.get_template("malfunction_at_sea_initial_notification.jinja")
),
BeaconMalfunctionNotificationType.MALFUNCTION_AT_SEA_INITIAL_NOTIFICATION_UNSUPERVISED_BEACON: (
env.get_template(
"malfunction_initial_notification_unsupervised_beacon.jinja"
)
),
BeaconMalfunctionNotificationType.MALFUNCTION_AT_PORT_INITIAL_NOTIFICATION: (
env.get_template("malfunction_at_port_initial_notification.jinja")
),
BeaconMalfunctionNotificationType.MALFUNCTION_AT_PORT_INITIAL_NOTIFICATION_UNSUPERVISED_BEACON: (
env.get_template(
"malfunction_initial_notification_unsupervised_beacon.jinja"
)
),
BeaconMalfunctionNotificationType.MALFUNCTION_AT_SEA_REMINDER: (
env.get_template("malfunction_at_sea_reminder.jinja")
),
Expand All @@ -139,7 +157,6 @@ def get_sms_templates() -> dict:
def render(
m: BeaconMalfunctionToNotify, templates: dict, output_format: str = "html"
) -> Union[str, bytes]:

try:
assert output_format in ("html", "pdf")
except AssertionError:
Expand Down Expand Up @@ -211,7 +228,6 @@ def render(

@task(checkpoint=False)
def render_sms(m: BeaconMalfunctionToNotify, templates: dict) -> str:

if (
m.notification_type
is BeaconMalfunctionNotificationType.MALFUNCTION_NOTIFICATION_TO_FOREIGN_FMC
Expand Down Expand Up @@ -253,7 +269,6 @@ def render_sms(m: BeaconMalfunctionToNotify, templates: dict) -> str:
def create_email(
html: str, pdf: bytes, m: BeaconMalfunctionToNotify
) -> BeaconMalfunctionMessageToSend:

to = [
email_addressee.address_or_number
for email_addressee in m.get_email_addressees()
Expand Down Expand Up @@ -285,7 +300,6 @@ def create_email(
def create_sms(
text: str, m: BeaconMalfunctionToNotify
) -> BeaconMalfunctionMessageToSend:

to = [sms_addressee.address_or_number for sms_addressee in m.get_sms_addressees()]

if to:
Expand All @@ -302,7 +316,6 @@ def create_sms(
def create_fax(
pdf: bytes, m: BeaconMalfunctionToNotify
) -> BeaconMalfunctionMessageToSend:

to = [fax_addressee.address_or_number for fax_addressee in m.get_fax_addressees()]

if to:
Expand Down Expand Up @@ -438,7 +451,6 @@ def send_beacon_malfunction_message(
notifications = []

for addressee in addressees:

if addressee.address_or_number in send_errors:
success = False
error_message = send_errors[addressee.address_or_number][1]
Expand Down Expand Up @@ -485,7 +497,6 @@ def make_reset_requested_notifications_statement(
beacon_malfunctions_table: Table,
notified_malfunctions: List[BeaconMalfunctionToNotify],
) -> sqlalchemy.sql.dml.Update:

beacon_malfunction_ids_to_reset = [
n.beacon_malfunction_id for n in notified_malfunctions
]
Expand All @@ -509,10 +520,8 @@ def execute_statement(reset_requested_notifications_statement):


with Flow("Notify malfunctions", executor=LocalDaskExecutor()) as flow:

flow_not_running = check_flow_not_running()
with case(flow_not_running, True):

test_mode = Parameter("test_mode")
is_integration = Parameter("is_integration")

Expand Down
49 changes: 35 additions & 14 deletions datascience/src/pipeline/flows/update_beacon_malfunctions.py
Original file line number Diff line number Diff line change
Expand Up @@ -268,18 +268,28 @@ def prepare_new_beacon_malfunctions(new_malfunctions: pd.DataFrame) -> pd.DataFr
new_malfunctions["vessel_status_last_modification_date_utc"] = datetime.utcnow()

notification_to_send = {
BeaconMalfunctionVesselStatus.AT_SEA.value: (
(BeaconMalfunctionVesselStatus.AT_SEA.value, BeaconStatus.ACTIVATED.value): (
BeaconMalfunctionNotificationType.MALFUNCTION_AT_SEA_INITIAL_NOTIFICATION.value
),
BeaconMalfunctionVesselStatus.AT_PORT.value: (
(BeaconMalfunctionVesselStatus.AT_PORT.value, BeaconStatus.ACTIVATED.value): (
BeaconMalfunctionNotificationType.MALFUNCTION_AT_PORT_INITIAL_NOTIFICATION.value
),
(BeaconMalfunctionVesselStatus.AT_SEA.value, BeaconStatus.UNSUPERVISED.value): (
BeaconMalfunctionNotificationType.MALFUNCTION_AT_SEA_INITIAL_NOTIFICATION_UNSUPERVISED_BEACON.value
),
(
BeaconMalfunctionVesselStatus.AT_PORT.value,
BeaconStatus.UNSUPERVISED.value,
): (
BeaconMalfunctionNotificationType.MALFUNCTION_AT_PORT_INITIAL_NOTIFICATION_UNSUPERVISED_BEACON.value
),
}

new_malfunctions["notification_requested"] = new_malfunctions.vessel_status.map(
lambda x: notification_to_send[x]
new_malfunctions["notification_requested"] = (
new_malfunctions[["vessel_status", "beacon_status"]]
.apply(lambda row: tuple(row), axis=1)
.map(notification_to_send)
)

new_malfunctions = new_malfunctions.rename(
columns={
"cfr": "internal_reference_number",
Expand Down Expand Up @@ -540,8 +550,9 @@ def request_notification(
),
)

# Malfunctions "at port" (or supposed to be, according to the latest vessel_status
# of the malfunction) are moved to ARCHIVED and automatically notified.
# Malfunctions "at port" (or supposed to be, according to the latest
# vessel_status of the malfunction) and malfunctions of unsupervised beacons
# are moved to ARCHIVED and automatically notified.
ids_at_port_restarted_emitting_updated = update_beacon_malfunction.map(
ids_at_port_restarted_emitting,
new_stage=unmapped(BeaconMalfunctionStage.ARCHIVED),
Expand All @@ -559,22 +570,32 @@ def request_notification(
unmapped(BeaconMalfunctionNotificationType.END_OF_MALFUNCTION),
)

# Malfunctions for which the beacon is unsupervised, has been deactivated or
# completely unequipped are just archived.
update_beacon_malfunction.map(
ids_not_required_to_emit,
ids_unsupervised_restarted_emitting_updated = update_beacon_malfunction.map(
ids_unsupervised_restarted_emitting,
new_stage=unmapped(BeaconMalfunctionStage.ARCHIVED),
end_of_malfunction_reason=unmapped(
EndOfMalfunctionReason.BEACON_DEACTIVATED_OR_UNEQUIPPED
EndOfMalfunctionReason.RESUMED_TRANSMISSION
),
)

ids_unsupervised_restarted_emitting_updated = filter_results(
ids_unsupervised_restarted_emitting_updated
)

request_notification.map(
ids_unsupervised_restarted_emitting_updated,
unmapped(BeaconMalfunctionNotificationType.END_OF_MALFUNCTION),
)

# Malfunctions for which the beacon has been deactivated or completely
# unequipped are just archived.
update_beacon_malfunction.map(
ids_unsupervised_restarted_emitting,
ids_not_required_to_emit,
new_stage=unmapped(BeaconMalfunctionStage.ARCHIVED),
end_of_malfunction_reason=unmapped(
EndOfMalfunctionReason.RESUMED_TRANSMISSION
EndOfMalfunctionReason.BEACON_DEACTIVATED_OR_UNEQUIPPED
),
)


flow.file_name = Path(__file__).name
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{% extends "base_template.jinja" %}
{% block message %}Le CNSP ne reçoit pas les positions VMS de votre navire.
{% if last_position_latitude and last_position_longitude and last_position_datetime_utc %}
La dernière position reçue date du {{ last_position_datetime_utc }}, latitude {{ last_position_latitude }}, longitude {{ last_position_longitude }}.
{%endif%}
{% endblock %}
Binary file modified datascience/tests/test_data/emails/END_OF_MALFUNCTION.pdf
Binary file not shown.
Binary file not shown.
Loading

0 comments on commit 221e7ed

Please sign in to comment.