initial support for lodur
This commit is contained in:
parent
1ae30cdece
commit
5ce9b2b3b2
76
README.md
76
README.md
|
@ -12,9 +12,77 @@
|
|||
* Connect to Lodur and create a new Einsatzrapport with
|
||||
as much information as possible
|
||||
|
||||
## Todo
|
||||
## TODO
|
||||
|
||||
* Parse PDF
|
||||
* Lodur Connect (Create Einsatzrapport)
|
||||
* IMAP idle
|
||||
### Version 1
|
||||
|
||||
* Lodur Connect
|
||||
* Fill in Einsatzprotokoll data
|
||||
* Store tuple F_ID <-> LODUR_ID persistently
|
||||
* Error Handling
|
||||
* Parse PDF
|
||||
* Store parsed data in Lodur text fields for copy/paste
|
||||
* Cleanup code into proper functions and classes
|
||||
* Lodur "API" class
|
||||
|
||||
### Future versions
|
||||
|
||||
* Generalize
|
||||
* Documentation
|
||||
* IMAP idle
|
||||
* Display PDF on Dashboard
|
||||
* Webapp to see what's going on
|
||||
* Get as many data out of the PDFs as possible
|
||||
* Simple webform to fill-in missing data (skipping Lodur completely)
|
||||
* Webapp for chosing who was there during the Einsatz
|
||||
|
||||
## Lodur Information Gathering
|
||||
|
||||
### eins_stat_kantone
|
||||
|
||||
_02. Einsatzart FKS_
|
||||
|
||||
```
|
||||
<option value="1">Brandbekämpfung</option>
|
||||
<option value="2">Elementarereignisse</option>
|
||||
<option value="3">Strassenrettung</option>
|
||||
<option value="4">Technische Hilfeleistungen</option>
|
||||
<option value="5">Ölwehr</option>
|
||||
<option value="6">Chemierwehr inkl. B-Einsätze</option>
|
||||
<option value="7">Strahlenwehr</option>
|
||||
<option value="8">Einsätze auf Bahnanlagen</option>
|
||||
<option value="9">BMA Unechte Alarme</option>
|
||||
<option value="10">Verschiedene Einsätze</option>
|
||||
<option value="11">Keine alarmmässigen Einsätze</option>
|
||||
```
|
||||
|
||||
### emergency_concept_id / ver_sart
|
||||
|
||||
_03. Verrechnungsart_
|
||||
|
||||
* `emergency_concept_id` = value
|
||||
* `ver_sart`= rc-id
|
||||
|
||||
```
|
||||
<option value="2" rc-id="ab">ABC-Einsatz inkl. Oel (Ortsfeuerwehr)</option>
|
||||
<option value="3" rc-id="ab">ABC-Einsätze inkl. Oel (Stützpunkte)</option>
|
||||
<option value="4" rc-id="ab">ABC-Messwagen (Stützpunkte)</option>
|
||||
<option value="5" rc-id="ab">Gaseinsätze (Ortsfeuerwehr)</option>
|
||||
<option value="6" rc-id="ab">Verkehrsunfälle (ohne Strassenrettung)</option>
|
||||
<option value="7" rc-id="ab">Strassenrettung (Ortsfeuerwehr)</option>
|
||||
<option value="8" rc-id="ab">Strassenrettung (Stützpunkt)</option>
|
||||
<option value="9" rc-id="ab">Fahrzeugbrände (ohne Brandstiftung)</option>
|
||||
|
||||
<option value="10" rc-id="th">BMA-Alarm</option>
|
||||
<option value="18" rc-id="th">Hilfeleistungs-Einsätze, verrechenbar durch OFW</option>
|
||||
<option value="19" rc-id="th">Unterstützung Rettungsdienst (ADL/Hilfskräfte)</option>
|
||||
|
||||
<option value="11" rc-id="uh">Dienstleistungen, Verrechenbar durch OFW</option>
|
||||
<option value="12" rc-id="uh">Stützpunkteinsatz (Grossereignisse)</option>
|
||||
<option value="14" rc-id="uh">Nachbarschaftshilfe Ortsfeuerwehr</option>
|
||||
<option value="20" rc-id="uh">Kernaufgaben (Brand, Explosion, Elementar, Erdbeben)</option>
|
||||
<option value="21" rc-id="uh">ADL-/HRF-Einsatz BRAND (ADL = Stüpt-Fahrzeug)</option>
|
||||
|
||||
<option value="13" rc-id="ak">ADL-/HRF-Einsatz BRAND (ADL = OFW-Fahrzeug)</option>
|
||||
<option value="15" rc-id="tt">Grosstierrettung Stützpunkt (PIF mit Kran)</option>
|
||||
```
|
|
@ -0,0 +1,108 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import re
|
||||
from datetime import datetime
|
||||
import requests
|
||||
|
||||
import logging
|
||||
import http.client
|
||||
|
||||
def create_einsatzrapport(username, password, base_url, f_id):
|
||||
|
||||
session = requests.session()
|
||||
login_data = {
|
||||
'login_member_name': username,
|
||||
'login_member_pwd': password,
|
||||
}
|
||||
|
||||
# Authenticate
|
||||
session.post(base_url, data=login_data)
|
||||
|
||||
params = (
|
||||
('modul', '36'),
|
||||
('what', '144'),
|
||||
('sp', '1'),
|
||||
('event', ''),
|
||||
('edit', ''),
|
||||
('is_herznotfall', ''),
|
||||
)
|
||||
|
||||
data = {
|
||||
'e_r_num': (None, '1'), # 01. Einsatzrapportnummer
|
||||
'eins_stat_kantone': (None, '1'), # 02. Einsatzart FKS
|
||||
'emergency_concept_id': (None, '2'), # 03. Verrechnungsart
|
||||
'ver_sart': (None, 'ab'), # 03. Verrechnungsart internal: ab, th, uh, ak, tt
|
||||
'dtv_d': (None, str(datetime.now().day)), # 04. Datum von
|
||||
'dtv_m': (None, str(datetime.now().month)), # 04. Datum von
|
||||
'dtv_y': (None, str(datetime.now().year)), # 04. Datum von
|
||||
'dtb_d': (None, str(datetime.now().day)), # 04. Datum bis
|
||||
'dtb_m': (None, str(datetime.now().month)), # 04. Datum bis
|
||||
'dtb_y': (None, str(datetime.now().year)), # 04. Datum bis
|
||||
'ztv_h': (None, '11'), # 05. Zeit von
|
||||
'ztv_m': (None, '11'), # 05. Zeit von
|
||||
'ztb_h': (None, '12'), # 05. Zeit bis
|
||||
'ztb_m': (None, '12'), # 05. Zeit bis
|
||||
'e_ort_1': (None, '306'), # 06. Einsatzort: Urdorf 306, Birmensdorf 298
|
||||
'eins_ereig': (None, f_id), # 07. Ereignis
|
||||
'adr': (None, 'TBD'), # 08. Adresse
|
||||
#'zh_alarmierung_h': (None, 'UNKNOWN'), # 12. Alarmierung
|
||||
#'zh_alarmierung_m': (None, 'UNKNOWN'), # 12. Alarmierung
|
||||
#'zh_fw_ausg_h': (None, 'UNKNOWN'), # 13. FW ausgerückt
|
||||
#'zh_fw_ausg_m': (None, 'UNKNOWN'), # 13. FW ausgerückt
|
||||
#'zh_am_schad_h': (None, 'UNKNOWN'), # 14. Am Schadenplatz
|
||||
#'zh_am_schad_m': (None, 'UNKNOWN'), # 14. Am Schadenplatz
|
||||
#'zh_fw_einge_h': (None, 'UNKNOWN'), # 15. FW eingerückt
|
||||
#'zh_fw_einge_m': (None, 'UNKNOWN'), # 15. FW eingerückt
|
||||
#'eins_erst_h': (None, 'UNKNOWN'), # 16. Einsatzbereitschaft erstellt
|
||||
#'eins_erst_m': (None, 'UNKNOWN'), # 16. Einsatzbereitschaft erstellt
|
||||
'ang_sit': (None, 'TBD1'), # 17. Angetroffene Situation
|
||||
'mn': (None, 'TBD2'), # 19. Massnahmen
|
||||
'bk': (None, 'TBD3'), # 20. Bemerkungen
|
||||
'en_kr_feuwehr': (None, '1'), # 21. Einsatzkräfte
|
||||
'ali_io': (None, '1'), # 24. Alarmierung
|
||||
'kopie_gvz': (None, '1'), # 31. Kopie innert 10 Tagen an
|
||||
'mannschaftd_einsa': (None, '70'), # 32. Einsatzleiter|in
|
||||
}
|
||||
|
||||
# post data to create new einsatzrapport
|
||||
answer = session.post(
|
||||
'https://lodur-zh.ch/urdorf/index.php',
|
||||
params=params,
|
||||
files=data,
|
||||
)
|
||||
# very ugly way to find the assigned event id by lodur
|
||||
# lodur really adds a script element at the bottom of the returned html
|
||||
# with the location to reload the page - containing the assigned event id
|
||||
lodur_id = re.search('modul=36&event=([0-9].*)&edit=1&what=144', answer.text).group(1)
|
||||
return lodur_id
|
||||
|
||||
def upload_alarmdepesche(username, password, base_url, lodur_id, file_name, file_path):
|
||||
http.client.HTTPConnection.debuglevel = 1
|
||||
|
||||
logging.basicConfig()
|
||||
logging.getLogger().setLevel(logging.DEBUG)
|
||||
requests_log = logging.getLogger("requests.packages.urllib3")
|
||||
requests_log.setLevel(logging.DEBUG)
|
||||
requests_log.propagate = True
|
||||
session = requests.session()
|
||||
login_data = {
|
||||
'login_member_name': username,
|
||||
'login_member_pwd': password,
|
||||
}
|
||||
|
||||
# Authenticate
|
||||
session.post(base_url, data=login_data)
|
||||
|
||||
params = (
|
||||
('modul', '36'),
|
||||
('what', '828'),
|
||||
('event', lodur_id),
|
||||
)
|
||||
|
||||
data = {'alarmdepesche': open(file_path, 'rb')}
|
||||
|
||||
session.post(
|
||||
'https://lodur-zh.ch/urdorf/index.php',
|
||||
params=params,
|
||||
files=data,
|
||||
)
|
59
main.py
59
main.py
|
@ -4,17 +4,17 @@
|
|||
|
||||
import os
|
||||
import re
|
||||
import datetime
|
||||
from datetime import datetime
|
||||
import asyncio
|
||||
import logging
|
||||
import time
|
||||
import email
|
||||
import email.parser
|
||||
import imaplib
|
||||
from datetime import datetime
|
||||
import aioeasywebdav
|
||||
from dotenv import load_dotenv, find_dotenv
|
||||
import paho.mqtt.client as mqtt
|
||||
from lodur_connect import create_einsatzrapport, upload_alarmdepesche
|
||||
|
||||
_EMAIL_SUBJECTS = '(OR SUBJECT "Einsatzausdruck_FW" SUBJECT "Einsatzprotokoll" UNSEEN)'
|
||||
|
||||
|
@ -32,6 +32,9 @@ tmp_dir = os.getenv("TMP_DIR", "/tmp")
|
|||
mqtt_server = os.getenv("MQTT_SERVER")
|
||||
mqtt_user = os.getenv("MQTT_USER")
|
||||
mqtt_password = os.getenv("MQTT_PASSWORD")
|
||||
lodur_user = os.getenv("LODUR_USER")
|
||||
lodur_password = os.getenv("LODUR_PASSWORD")
|
||||
lodur_base_url = os.getenv("LODUR_BASE_URL")
|
||||
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
|
@ -53,7 +56,7 @@ def get_attachments(mqtt_client):
|
|||
_EMAIL_SUBJECTS,
|
||||
)
|
||||
if typ != 'OK':
|
||||
print('Error searching for matching messages')
|
||||
logger.error('Error searching for matching messages')
|
||||
raise
|
||||
|
||||
logger.info('Found ' + str(len(msg_ids[0].split())) + ' matching messages')
|
||||
|
@ -61,27 +64,48 @@ def get_attachments(mqtt_client):
|
|||
for msg_id in msg_ids[0].split():
|
||||
subject = str()
|
||||
f_id = str()
|
||||
f_type = str()
|
||||
|
||||
# download message
|
||||
typ, msg_data = imap.fetch(msg_id, '(RFC822)')
|
||||
|
||||
for response_part in msg_data:
|
||||
if isinstance(response_part, tuple):
|
||||
msg = email.message_from_string(str(response_part[1],'utf-8'))
|
||||
msg = email.message_from_string(str(response_part[1], 'utf-8'))
|
||||
subject = msg["subject"]
|
||||
# extract F id from subject
|
||||
f_id = re.search('.*: (F[0-9].*)',subject).group(1)
|
||||
parse_subject = re.search('(.*): (F[0-9].*)', subject)
|
||||
f_type = parse_subject.group(1)
|
||||
f_id = parse_subject.group(2)
|
||||
|
||||
logger.info('Processing message: ' + subject)
|
||||
logger.info('Detected F ID: ' + f_id)
|
||||
|
||||
mqtt_client.publish("pylokid/einsatz/" + f_id, subject)
|
||||
|
||||
# Mark as seen
|
||||
# mark as seen
|
||||
imap.store(msg_id, '+FLAGS', '(\\Seen)')
|
||||
|
||||
logger.info('Processing message: ' + subject)
|
||||
logger.info('Detected type: ' + f_type)
|
||||
logger.info('Detected F ID: ' + f_id)
|
||||
|
||||
# publish over MQTT
|
||||
mqtt_client.publish("pylokid/einsatz/" + f_id, f_type)
|
||||
|
||||
# Talk to Lodur
|
||||
if f_type == 'Einsatzausdruck_FW':
|
||||
# create new Einsatzrapport in Lodur
|
||||
logger.info('Sending data to Lodur')
|
||||
lodur_id = create_einsatzrapport(
|
||||
lodur_user,
|
||||
lodur_password,
|
||||
lodur_base_url,
|
||||
f_id,
|
||||
)
|
||||
logger.info('Sent data to Lodur. Assigned Lodur ID: ' + lodur_id)
|
||||
elif f_type == 'Einsatzprotokoll':
|
||||
logger.info('Updating data in Lodur')
|
||||
else:
|
||||
logger.error('Unknown type: ' + f_type)
|
||||
|
||||
# extract attachment from body
|
||||
mail = email.message_from_string(str(msg_data[0][1],'utf-8'))
|
||||
mail = email.message_from_string(str(msg_data[0][1], 'utf-8'))
|
||||
|
||||
for part in mail.walk():
|
||||
if part.get_content_maintype() == 'multipart':
|
||||
|
@ -98,13 +122,22 @@ def get_attachments(mqtt_client):
|
|||
|
||||
logger.info('Saving attachment to ' + file_path)
|
||||
if not os.path.isfile(file_path):
|
||||
print(file_name)
|
||||
file = open(file_path, 'wb')
|
||||
file.write(part.get_payload(decode=True))
|
||||
file.close()
|
||||
|
||||
upload_attachment(file_path, file_name, f_id)
|
||||
|
||||
logger.info('Uploading PDF to Lodur')
|
||||
upload_alarmdepesche(
|
||||
lodur_user,
|
||||
lodur_password,
|
||||
lodur_base_url,
|
||||
lodur_id,
|
||||
file_name,
|
||||
file_path,
|
||||
)
|
||||
|
||||
|
||||
def upload_attachment(file, file_name, f_id):
|
||||
# webdav connection
|
||||
|
|
Loading…
Reference in New Issue