2017-12-21 21:12:44 +00:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
|
|
|
|
""" Thy pylokid main program """
|
|
|
|
|
|
|
|
import os
|
|
|
|
import re
|
2017-12-24 14:35:16 +00:00
|
|
|
from datetime import datetime
|
2017-12-21 21:12:44 +00:00
|
|
|
import asyncio
|
|
|
|
import logging
|
|
|
|
import time
|
|
|
|
import email
|
|
|
|
import email.parser
|
|
|
|
import imaplib
|
|
|
|
import aioeasywebdav
|
|
|
|
from dotenv import load_dotenv, find_dotenv
|
2017-12-23 16:55:02 +00:00
|
|
|
import paho.mqtt.client as mqtt
|
2017-12-24 14:35:16 +00:00
|
|
|
from lodur_connect import create_einsatzrapport, upload_alarmdepesche
|
2017-12-21 21:12:44 +00:00
|
|
|
|
2017-12-25 12:52:43 +00:00
|
|
|
_EMAIL_SUBJECTS = '(OR SUBJECT "Einsatzausdruck_FW" SUBJECT "Einsatzprotokoll" UNSEEN)'
|
|
|
|
_INTERVAL = 10
|
2017-12-21 21:12:44 +00:00
|
|
|
|
|
|
|
load_dotenv(find_dotenv())
|
|
|
|
imap_server = os.getenv("IMAP_SERVER")
|
|
|
|
imap_username = os.getenv("IMAP_USERNAME")
|
|
|
|
imap_password = os.getenv("IMAP_PASSWORD")
|
|
|
|
imap_mailbox = os.getenv("IMAP_MAILBOX", "INBOX")
|
2017-12-23 17:31:40 +00:00
|
|
|
imap_mailbox_archive = os.getenv("IMAP_MAILBOX_ARCHIVE", "Archive")
|
2017-12-21 21:12:44 +00:00
|
|
|
webdav_url = os.getenv("WEBDAV_URL")
|
|
|
|
webdav_username = os.getenv("WEBDAV_USERNAME")
|
|
|
|
webdav_password = os.getenv("WEBDAV_PASSWORD")
|
|
|
|
webdav_basedir = os.getenv("WEBDAV_BASEDIR")
|
|
|
|
tmp_dir = os.getenv("TMP_DIR", "/tmp")
|
2017-12-23 16:55:02 +00:00
|
|
|
mqtt_server = os.getenv("MQTT_SERVER")
|
|
|
|
mqtt_user = os.getenv("MQTT_USER")
|
|
|
|
mqtt_password = os.getenv("MQTT_PASSWORD")
|
2017-12-24 14:35:16 +00:00
|
|
|
lodur_user = os.getenv("LODUR_USER")
|
|
|
|
lodur_password = os.getenv("LODUR_PASSWORD")
|
|
|
|
lodur_base_url = os.getenv("LODUR_BASE_URL")
|
2017-12-21 21:12:44 +00:00
|
|
|
|
|
|
|
logging.basicConfig(
|
|
|
|
level=logging.INFO,
|
|
|
|
format='%(asctime)s - %(levelname)s - %(message)s'
|
|
|
|
)
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
2017-12-25 11:39:37 +00:00
|
|
|
def search_emails(imap):
|
|
|
|
""" searches for emails matching the configured subject """
|
2017-12-21 21:12:44 +00:00
|
|
|
# search for matching messages
|
|
|
|
logger.info('Searching for messages matching the subject')
|
|
|
|
typ, msg_ids = imap.search(
|
|
|
|
None,
|
|
|
|
_EMAIL_SUBJECTS,
|
|
|
|
)
|
|
|
|
if typ != 'OK':
|
2017-12-24 14:35:16 +00:00
|
|
|
logger.error('Error searching for matching messages')
|
2017-12-21 21:12:44 +00:00
|
|
|
raise
|
|
|
|
|
2017-12-25 11:39:37 +00:00
|
|
|
num_messages = len(msg_ids[0].split())
|
|
|
|
logger.info('Found ' + str(num_messages) + ' matching messages')
|
|
|
|
return num_messages, msg_ids
|
2017-12-23 17:31:40 +00:00
|
|
|
|
2017-12-25 11:39:37 +00:00
|
|
|
def store_attachments(imap, msg_ids):
|
|
|
|
""" stores the attachments to filesystem and marks message as read """
|
|
|
|
data = {}
|
2017-12-21 21:12:44 +00:00
|
|
|
for msg_id in msg_ids[0].split():
|
2017-12-25 11:39:37 +00:00
|
|
|
# download message from imap
|
2017-12-21 21:12:44 +00:00
|
|
|
typ, msg_data = imap.fetch(msg_id, '(RFC822)')
|
|
|
|
|
2017-12-25 12:52:43 +00:00
|
|
|
if typ != 'OK':
|
|
|
|
logger.error('Error fetching message')
|
|
|
|
raise
|
|
|
|
|
|
|
|
# extract attachment
|
2017-12-21 21:12:44 +00:00
|
|
|
for response_part in msg_data:
|
|
|
|
if isinstance(response_part, tuple):
|
2017-12-25 12:52:43 +00:00
|
|
|
mail = email.message_from_string(str(response_part[1], 'utf-8'))
|
|
|
|
subject = mail['subject']
|
|
|
|
logger.info('Getting attachment from: ' + subject)
|
|
|
|
for part in mail.walk():
|
|
|
|
if part.get_content_maintype() == 'multipart':
|
|
|
|
continue
|
|
|
|
if part.get('Content-Disposition') is None:
|
|
|
|
continue
|
|
|
|
file_name = part.get_filename()
|
|
|
|
|
|
|
|
logger.info('Extracting attachment: ' + file_name)
|
|
|
|
|
|
|
|
if bool(file_name):
|
|
|
|
# save attachment to filesystem
|
|
|
|
file_path = os.path.join(tmp_dir, file_name)
|
|
|
|
|
|
|
|
logger.info('Saving attachment to ' + file_path)
|
|
|
|
if not os.path.isfile(file_path):
|
|
|
|
file = open(file_path, 'wb')
|
|
|
|
file.write(part.get_payload(decode=True))
|
|
|
|
file.close()
|
|
|
|
|
|
|
|
data[subject] = file_name
|
2017-12-21 21:12:44 +00:00
|
|
|
|
2017-12-25 11:39:37 +00:00
|
|
|
# mark as seen
|
|
|
|
imap.store(msg_id, '+FLAGS', '(\\Seen)')
|
2017-12-24 14:35:16 +00:00
|
|
|
|
2017-12-25 11:39:37 +00:00
|
|
|
return data
|
2017-12-21 21:12:44 +00:00
|
|
|
|
2017-12-25 11:39:37 +00:00
|
|
|
def upload_webdav(loop, webdav, file_name, f_id):
|
|
|
|
""" uploads a file to webdav - checks for existence before doing so """
|
2017-12-21 21:12:44 +00:00
|
|
|
# upload with webdav
|
2017-12-25 11:39:37 +00:00
|
|
|
remote_upload_dir = webdav_basedir + "/" + str(datetime.now().year) + "/" + f_id
|
2017-12-25 12:52:43 +00:00
|
|
|
logger.info('Uploading file to WebDAV:' + remote_upload_dir)
|
2017-12-23 16:16:14 +00:00
|
|
|
|
|
|
|
# create directory if not yet there
|
2017-12-25 11:39:37 +00:00
|
|
|
if not loop.run_until_complete(webdav.exists(remote_upload_dir)):
|
|
|
|
logger.info('Creating directory ' + remote_upload_dir)
|
|
|
|
loop.run_until_complete(webdav.mkdir(remote_upload_dir))
|
2017-12-21 21:12:44 +00:00
|
|
|
|
2017-12-25 11:39:37 +00:00
|
|
|
remote_file_path = remote_upload_dir + "/" + file_name
|
2017-12-23 16:16:14 +00:00
|
|
|
if loop.run_until_complete(webdav.exists(remote_file_path)):
|
2017-12-25 11:39:37 +00:00
|
|
|
logger.info('File ' + file_name + ' already exists on webdav')
|
2017-12-23 16:16:14 +00:00
|
|
|
else:
|
|
|
|
loop.run_until_complete(
|
2017-12-25 11:39:37 +00:00
|
|
|
webdav.upload(os.path.join(tmp_dir, file_name), remote_file_path)
|
2017-12-23 16:16:14 +00:00
|
|
|
)
|
|
|
|
logger.info('File ' + file_name + ' uploaded')
|
2017-12-21 21:12:44 +00:00
|
|
|
|
2017-12-25 11:39:37 +00:00
|
|
|
def einsatz_exists(loop, webdav, f_id):
|
|
|
|
""" check if an einsatz is already created """
|
|
|
|
remote_upload_dir = webdav_basedir + "/" + str(datetime.now().year) + "/" + f_id
|
|
|
|
if loop.run_until_complete(webdav.exists(remote_upload_dir)):
|
|
|
|
logger.info('Einsatz exists ' + f_id)
|
|
|
|
return True
|
|
|
|
else:
|
|
|
|
return False
|
|
|
|
|
|
|
|
def parse_subject(subject):
|
|
|
|
""" extract f id and type from subject """
|
|
|
|
parsed = re.search('(.*): (F[0-9].*)', subject)
|
|
|
|
f_type = parsed.group(1)
|
|
|
|
f_id = parsed.group(2)
|
|
|
|
return f_type, f_id
|
|
|
|
|
2017-12-25 12:52:43 +00:00
|
|
|
def store_lodur_id(loop, webdav, lodur_id, f_id):
|
|
|
|
""" stores assigned lodur_id on webdav """
|
|
|
|
file_name = f_id + '_lodurid.txt'
|
|
|
|
file_path = os.path.join(tmp_dir, file_name)
|
|
|
|
if not os.path.isfile(file_path):
|
|
|
|
file = open(file_path, 'w')
|
|
|
|
file.write(str(lodur_id))
|
|
|
|
file.close()
|
|
|
|
logger.info('Stored Lodur ID locally in: ' + file_path)
|
|
|
|
upload_webdav(loop, webdav, file_name, f_id)
|
|
|
|
else:
|
|
|
|
logger.info('Lodur ID already available locally in: ' + file_path)
|
|
|
|
|
|
|
|
def get_lodur_id(loop, webdav, f_id):
|
|
|
|
""" gets lodur_id if it exists """
|
|
|
|
file_name = f_id + '_lodurid.txt'
|
|
|
|
file_path = os.path.join(tmp_dir, file_name)
|
|
|
|
|
|
|
|
# first check if we already have it locally - then check on webdav
|
|
|
|
if os.path.isfile(file_path):
|
|
|
|
with open(file_path, 'r') as content:
|
|
|
|
lodur_id = content.read()
|
|
|
|
logger.info('Found Lodur ID for ' + f_id + ' locally: ' + lodur_id)
|
|
|
|
return lodur_id
|
|
|
|
else:
|
|
|
|
remote_upload_dir = webdav_basedir + "/" + str(datetime.now().year) + "/" + f_id
|
|
|
|
remote_file_path = remote_upload_dir + '/' + file_name
|
|
|
|
if loop.run_until_complete(webdav.exists(remote_file_path)):
|
|
|
|
loop.run_until_complete(webdav.download(remote_file_path, file_path))
|
|
|
|
with open(file_path, 'r') as content:
|
|
|
|
lodur_id = content.read()
|
|
|
|
logger.info('Found Lodur ID for ' + f_id + ' on WebDAV: ' + lodur_id)
|
|
|
|
return lodur_id
|
|
|
|
else:
|
|
|
|
logger.info('No Lodur ID found for ' + f_id)
|
|
|
|
return False
|
2017-12-23 16:55:02 +00:00
|
|
|
|
2017-12-21 21:12:44 +00:00
|
|
|
def main():
|
|
|
|
""" main """
|
|
|
|
|
2017-12-25 11:39:37 +00:00
|
|
|
# MQTT connection
|
2017-12-25 12:52:43 +00:00
|
|
|
logger.info('Connecting to MQTT broker ' + mqtt_server)
|
|
|
|
mqtt_client = mqtt.Client('pylokid')
|
|
|
|
mqtt_client.username_pw_set(mqtt_user, password=mqtt_password)
|
|
|
|
mqtt_client.tls_set()
|
|
|
|
mqtt_client.connect(mqtt_server, 8883, 60)
|
|
|
|
mqtt_client.loop_start()
|
2017-12-25 11:39:37 +00:00
|
|
|
|
|
|
|
# imap connection
|
|
|
|
logger.info('Connecting to IMAP server ' + imap_server)
|
|
|
|
imap = imaplib.IMAP4_SSL(imap_server)
|
|
|
|
imap.login(imap_username, imap_password)
|
|
|
|
imap.select(imap_mailbox, readonly=False)
|
2017-12-23 16:55:02 +00:00
|
|
|
|
2017-12-25 11:39:37 +00:00
|
|
|
# webdav connection
|
|
|
|
logger.info('Connecting to WebDAV server ' + webdav_url)
|
|
|
|
loop = asyncio.get_event_loop()
|
|
|
|
webdav = aioeasywebdav.connect(
|
|
|
|
webdav_url,
|
|
|
|
username=webdav_username,
|
|
|
|
password=webdav_password,
|
|
|
|
)
|
2017-12-23 16:55:02 +00:00
|
|
|
|
2017-12-21 21:12:44 +00:00
|
|
|
while True:
|
2017-12-25 11:39:37 +00:00
|
|
|
attachments = {}
|
|
|
|
num_messages, msg_ids = search_emails(imap)
|
|
|
|
if num_messages > 0:
|
|
|
|
attachments = store_attachments(imap, msg_ids)
|
|
|
|
|
|
|
|
if len(attachments) > 0:
|
|
|
|
for subject in attachments:
|
|
|
|
f_type, f_id = parse_subject(subject)
|
|
|
|
file_name = attachments[subject]
|
|
|
|
upload_webdav(loop, webdav, file_name, f_id)
|
|
|
|
|
|
|
|
# Take actions - depending on the type
|
|
|
|
if f_type == 'Einsatzausdruck_FW':
|
2017-12-25 12:52:43 +00:00
|
|
|
lodur_id = get_lodur_id(loop, webdav, f_id)
|
|
|
|
if lodur_id:
|
|
|
|
logger.info(
|
|
|
|
'Einsatzrapport ' + f_id + ' already created in Lodur: ' + lodur_id
|
|
|
|
)
|
|
|
|
else:
|
|
|
|
# this is real - publish Einsatz on MQTT
|
|
|
|
mqtt_client.publish('pylokid/' + f_type, f_id)
|
|
|
|
|
|
|
|
# create new Einsatzrapport in Lodur
|
|
|
|
logger.info('Creating Einsatzrapport in Lodur for ' + f_id)
|
|
|
|
lodur_id = create_einsatzrapport(
|
|
|
|
lodur_user,
|
|
|
|
lodur_password,
|
|
|
|
lodur_base_url,
|
|
|
|
f_id,
|
|
|
|
)
|
|
|
|
logger.info('Sent data to Lodur. Assigned Lodur ID: ' + lodur_id)
|
|
|
|
# store lodur id in webdav
|
|
|
|
store_lodur_id(loop, webdav, lodur_id, f_id)
|
|
|
|
|
|
|
|
logger.info(
|
|
|
|
'Uploading PDF for ' + f_id + ' to Lodur Einsatzrapport ' + lodur_id
|
|
|
|
)
|
|
|
|
upload_alarmdepesche(
|
|
|
|
lodur_user,
|
|
|
|
lodur_password,
|
|
|
|
lodur_base_url,
|
|
|
|
lodur_id,
|
|
|
|
file_name,
|
|
|
|
os.path.join(tmp_dir, file_name),
|
|
|
|
)
|
2017-12-25 11:39:37 +00:00
|
|
|
elif f_type == 'Einsatzprotokoll':
|
2017-12-25 12:52:43 +00:00
|
|
|
# Einsatz finished - publish on MQTT
|
|
|
|
mqtt_client.publish('pylokid/' + f_type, f_id)
|
|
|
|
|
|
|
|
lodur_id = get_lodur_id(loop, webdav, f_id)
|
|
|
|
if lodur_id:
|
|
|
|
logger.info('Uploading Einsatzprotokoll to Lodur')
|
|
|
|
upload_alarmdepesche(
|
|
|
|
lodur_user,
|
|
|
|
lodur_password,
|
|
|
|
lodur_base_url,
|
|
|
|
lodur_id,
|
|
|
|
file_name,
|
|
|
|
os.path.join(tmp_dir, file_name),
|
|
|
|
)
|
|
|
|
else:
|
|
|
|
logger.error('Cannot process Einsatzprotokoll as there is no Lodur ID')
|
2017-12-25 11:39:37 +00:00
|
|
|
else:
|
|
|
|
logger.error('Unknown type: ' + f_type)
|
|
|
|
|
|
|
|
# repeat every
|
|
|
|
time.sleep(_INTERVAL)
|
2017-12-21 21:12:44 +00:00
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
main()
|