Compare commits

...
4 Commits
Author SHA1 Message Date
Simon Zeyer 69da949aa8 add basic auth 2024-10-16 12:07:22 +00:00
Simon Zeyer fa67553299 rename flask app and add upload function 2024-10-16 11:59:02 +00:00
Simon Zeyer b5c9a2761f fix requirements flask import 2024-10-15 20:01:33 +00:00
Simon Zeyer 1748f754ca http for jfw bf tag 2024-10-15 19:08:21 +00:00
6 changed files with 224 additions and 26 deletions
+1
View File
@@ -12,3 +12,4 @@ alarminator_token=""
alarminator_zvies_use_PEALGRP="False" alarminator_zvies_use_PEALGRP="False"
printer=DEFAULT printer=DEFAULT
print_num=0 print_num=0
BASIC_AUTH_PASSWORD=""
+3 -1
View File
@@ -13,6 +13,8 @@ ENV alarminator_zvies_use_PEALGRP="False"
ENV print_num=0 ENV print_num=0
ENV printer="DEFAULT" ENV printer="DEFAULT"
ENV REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt ENV REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt
ENV MAPS_API_KEY=""
ENV BASIC_AUTH_PASSWORD=""
COPY *.deb / COPY *.deb /
@@ -61,4 +63,4 @@ COPY ./app .
COPY *.crt /usr/local/share/ca-certificates/ COPY *.crt /usr/local/share/ca-certificates/
RUN update-ca-certificates RUN update-ca-certificates
CMD [ "sh","-c","/etc/init.d/cups start && python3 /usr/src/app/exchange_connect.py" ] CMD [ "sh","-c","/etc/init.d/cups start && python3 /usr/src/app/app.py" ]
+136
View File
@@ -0,0 +1,136 @@
from flask import Flask, flash, request, redirect, send_from_directory, get_flashed_messages
import os
import glob
import securecad_parser
import datetime
from hooks import webhook, alarminator_api, cups_print
from pathlib import Path
from flask_basicauth import BasicAuth
app = Flask(__name__)
app.secret_key = 'super secret key'
app.config['SESSION_TYPE'] = 'filesystem'
app.config['BASIC_AUTH_USERNAME'] = 'admin'
app.config['BASIC_AUTH_PASSWORD'] = os.environ.get('BASIC_AUTH_PASSWORD','password')
basic_auth = BasicAuth(app)
def wrapHtml(innerHtml):
return '''<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>JFW Alarm</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
</head>
<body><div class="container text-center">
''' + flashMessages() +'''
''' + innerHtml + '''
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
</body>
</html>'''
def flashMessages():
messages = get_flashed_messages(with_categories=True)
html = ""
if messages:
for category, message in messages:
html = html + category + ": "+message+"<br>"
return html
def alarmsPath():
dir_path = os.path.dirname(os.path.realpath(__file__))
return dir_path + "/alarms" + os.sep
def getAlarmFiles():
a_list = glob.glob(alarmsPath()+"*.html")
a_list.sort()
return [os.path.basename(f) for f in a_list]
def parseAlarm(f):
f = alarmsPath() + os.sep + f
with open(f, "r") as o:
_r: str = o.read()
now = datetime.datetime.now()
_r = _r.replace('%DATUM%',now.strftime("%d.%m.%Y"))
_r = _r.replace('%UHRZEIT%',now.strftime("%H:%M"))
if _r != '':
return [securecad_parser.parse_securecad_message(_r),_r]
def allowed_file(filename:str):
print(filename.rsplit('.', 1)[1].lower())
return '.' in filename and \
filename.rsplit('.', 1)[1].lower() in ["html"]
@app.route("/")
@basic_auth.required
def root():
html = ""
file = request.args.get('sendalarm')
if file is not None:
parsed_body, raw = parseAlarm(file)
if parsed_body != None:
if 'ALARMDEPESCHE' in parsed_body:
webhook(parsed_body)
alarminator_api(parsed_body)
cups_print(parsed_body,raw)
flash("Alarm gesendet!")
return redirect('/')
html = html + '<div class="row align-items-start p-3 ">'
i: str
files = getAlarmFiles()
for f in files:
html = html + "<div class=\"col-6\" style='padding: 5px;'><a style='display:block; padding:20px' class=\"btn btn-block btn-danger\" href=\"/?sendalarm="+f+"\">" + f + "</a></div>"
html = html + "</div>"
return wrapHtml(html)
@app.route('/files', methods=['POST'])
@basic_auth.required
def upload_file():
# check if the post request has the file part
print(request.files)
if 'file' not in request.files:
flash('No file part')
return redirect(request.url)
file = request.files['file']
# If the user does not select a file, the browser submits an
# empty file without a filename.
print(file.filename)
if file.filename == '':
flash('No selected file')
return redirect(request.url)
if file and allowed_file(file.filename):
file.save(os.path.join(alarmsPath(), file.filename))
return redirect('/files')
@app.route('/files/<path:filename>', methods=['GET'])
@basic_auth.required
def download(filename):
uploads = alarmsPath()
return send_from_directory(uploads, filename, as_attachment=True)
@app.route("/files", methods=['GET'])
@basic_auth.required
def files():
Path(alarmsPath()).mkdir(parents=True, exist_ok=True)
file = request.args.get('del')
if file is not None:
try:
os.remove(alarmsPath()+os.sep+file)
except:
pass
flash("Datei gelöscht!")
return redirect('/files')
files = getAlarmFiles()
html = '<ul class="list-group">'
for f in files:
html = html + '<li class="list-group-item"><a href="/files/'+f+'">'+f+'</a> <a href="/files?del='+f+'">löschen</a></li>'
html = html + '</ul><form method="post" enctype="multipart/form-data"><input type="file" name="file"><input type=submit value=Upload></form>'
return html
if __name__ == "__main__":
app.run(port=5000)
+54 -16
View File
@@ -2,7 +2,8 @@ import os
import logging import logging
import requests import requests
import cups import cups
from weasyprint import HTML from weasyprint import HTML, CSS
from weasyprint.text.fonts import FontConfiguration
from requests.adapters import Retry from requests.adapters import Retry
import uuid import uuid
@@ -26,21 +27,21 @@ def alarminator_api(parsed_body: dict):
alarminator_api = os.environ.get('alarminator_api') if os.environ.get('alarminator_api') else "" alarminator_api = os.environ.get('alarminator_api') if os.environ.get('alarminator_api') else ""
alarminator_token = os.environ.get('alarminator_token') if os.environ.get('alarminator_token') else "" alarminator_token = os.environ.get('alarminator_token') if os.environ.get('alarminator_token') else ""
alarminator_zvies_use_PEALGRP = True if os.environ.get('alarminator_zvies_use_PEALGRP') == 'True' else False alarminator_zvies_use_PEALGRP = True if os.environ.get('alarminator_zvies_use_PEALGRP') == 'True' else False
maps_api_key = os.environ.get('MAPS_API_KEY',"")
if alarminator_api != "" and alarminator_token != "": if alarminator_api != "" and alarminator_token != "":
if 'ALARMDEPESCHE' in parsed_body: # sendAlarm triggern if 'ALARMDEPESCHE' in parsed_body: # sendAlarm triggern
logging.info("GET zu {}/operations/sendAlarm".format(alarminator_api)) logging.info("GET zu {}/operations/sendAlarm".format(alarminator_api))
s = requests.Session() s = requests.Session()
s.mount('https://', requests.adapters.HTTPAdapter(max_retries=retries)) s.mount('https://', requests.adapters.HTTPAdapter(max_retries=retries))
#&object=Kirmesplatz &district=Oberlinxweilerstrasse &subject=THK (TH klein &street=Oberlinxweilerstrasse &ils=\"secur.CAD\" <leitstelle@zrf-saar.de>&connector=mailParser&token=ea2110e1-11b9-421f-a53d-96cc0fc82c31
req_string = "" req_string = ""
req_string +="?token={}".format(alarminator_token) req_string +="?token={}".format(alarminator_token)
if 'Einsatzbeginn(Soll)' in parsed_body: if 'Einsatzbeginn(Soll)' in parsed_body:
req_string +="&alarmdate={}".format(parsed_body['Einsatzbeginn(Soll)'].split(" ")[0]) req_string +="&alarmdate={}".format(parsed_body['Einsatzbeginn(Soll)'].split(" ")[0])
req_string +="&alarmtime={}".format(parsed_body['Einsatzbeginn(Soll)'].split(" ")[1]) req_string +="&alarmtime={}".format(parsed_body['Einsatzbeginn(Soll)'].split(" ")[1])
if 'Auftragsnummer' in parsed_body: if 'Auftragsnummer' in parsed_body and not (os.environ.get('IS_DEV') and os.environ.get('IS_DEV') == "True"):
req_string +="&operationnumber={}".format(parsed_body['Auftragsnummer']) req_string +="&operationnumber={}".format(parsed_body['Auftragsnummer'])
if 'Sachverhalt' in parsed_body: if 'Notfallgeschehen' in parsed_body:
req_string +="&message={}".format(parsed_body['Sachverhalt']) req_string +="&message={}".format(parsed_body['Notfallgeschehen'])
if 'Einsatzziel' in parsed_body: if 'Einsatzziel' in parsed_body:
if 'Stadt' in parsed_body['Einsatzziel']: if 'Stadt' in parsed_body['Einsatzziel']:
req_string +="&community={}".format(parsed_body['Einsatzziel']['Stadt']) req_string +="&community={}".format(parsed_body['Einsatzziel']['Stadt'])
@@ -48,21 +49,29 @@ def alarminator_api(parsed_body: dict):
req_string +="&location={}".format(parsed_body['Einsatzziel']['PLZ / Ort']) req_string +="&location={}".format(parsed_body['Einsatzziel']['PLZ / Ort'])
if 'Objekt' in parsed_body['Einsatzziel']: if 'Objekt' in parsed_body['Einsatzziel']:
req_string +="&object={}".format(parsed_body['Einsatzziel']['Objekt']) req_string +="&object={}".format(parsed_body['Einsatzziel']['Objekt'])
street = []
if 'Strasse' in parsed_body['Einsatzziel']: if 'Strasse' in parsed_body['Einsatzziel']:
req_string +="&street={}".format(parsed_body['Einsatzziel']['Strasse']) + (("\n"+parsed_body['Einsatzziel']['Info ']) if 'Info ' in parsed_body['Einsatzziel'] else "" ) street.append(parsed_body['Einsatzziel']['Strasse'])
if 'Zusatz Strasse' in parsed_body['Einsatzziel']:
street.append(parsed_body['Einsatzziel']['Zusatz Strasse'])
if 'Strasse / Hs.-Nr.' in parsed_body['Einsatzziel']: if 'Strasse / Hs.-Nr.' in parsed_body['Einsatzziel']:
req_string +="&street={}".format(parsed_body['Einsatzziel']['Strasse / Hs.-Nr.'] + (("\n"+parsed_body['Einsatzziel']['Info ']) if 'Info ' in parsed_body['Einsatzziel'] else "" )) street.append(parsed_body['Einsatzziel']['Strasse / Hs.-Nr.'])
if 'Info' in parsed_body['Einsatzziel']:
street.append(parsed_body['Einsatzziel']['Info'])
if street.__len__() > 0:
req_string +="&street={}".format("\n".join(street))
if 'Einsatzmittelliste' in parsed_body: if 'Einsatzmittelliste' in parsed_body:
gear = [] gear = []
for r in parsed_body['Einsatzmittelliste']: for r in parsed_body['Einsatzmittelliste']:
if r['Typ'] != 'PEALGRP': #if r['Typ'] != 'PEALGRP':
if r['Ressourcen'] not in gear:
gear.append(r['Ressourcen']) gear.append(r['Ressourcen'])
req_string +="&gear={}".format(';'.join(gear)) req_string +="&gear={}".format(';'.join(gear))
#req_string +="&district={}".format('district') req_string +="&district={}".format('district')
#req_string +="&floor={}".format('floor') req_string +="&floor={}".format('floor')
#req_string +="&section={}".format('section') req_string +="&section={}".format('section')
req_string +="&keywordRaw={}".format(parsed_body['Einsatzstichwort']) req_string +="&keywordRaw={}".format(parsed_body['Einsatzstichwort'])
#req_string +="&keywordId={}".format('keywordId') #req_string +="&keywordId={}".format('keywordId')
req_string +="&keywordCategory={}".format(parsed_body['Einsatzstichwort'].split("(")[0]) req_string +="&keywordCategory={}".format(parsed_body['Einsatzstichwort'].split("(")[0])
@@ -76,31 +85,60 @@ def alarminator_api(parsed_body: dict):
req_string +="&zveis={}".format(';'.join(zveis)) req_string +="&zveis={}".format(';'.join(zveis))
else: else:
req_string +="&zveis={}".format(parsed_body['ALARMDEPESCHE']) req_string +="&zveis={}".format(parsed_body['ALARMDEPESCHE'])
if maps_api_key != "":
try:
maps_address_param = []
if 'Objekt' in parsed_body['Einsatzziel']:
maps_address_param.append("{}".format(parsed_body['Einsatzziel']['Objekt']))
if street.__len__() > 0:
maps_address_param.append('{}'.format(",".join(street)))
if 'Stadt' in parsed_body['Einsatzziel']:
maps_address_param.append("{}".format(parsed_body['Einsatzziel']['Stadt']))
if 'PLZ / Ort' in parsed_body['Einsatzziel']:
maps_address_param.append("{}".format(parsed_body['Einsatzziel']['PLZ / Ort']))
maps_request = requests.get('https://maps.google.com/maps/api/geocode/json?address={}&key={}'.format(','.join(maps_address_param),maps_api_key))
if maps_request.json()['results'].__len__() == 1:
req_string +="&lat={}".format(maps_request.json()['results'][0]['geometry']['location']['lat'])
req_string +="&lon={}".format(maps_request.json()['results'][0]['geometry']['location']['lng'])
except Exception as maps_e:
logging.error('error getting maps',maps_e)
# req_string +="&gkx={}".format() if False # req_string +="&gkx={}".format() if False
# req_string +="&gky={}".format() if False # req_string +="&gky={}".format() if False
# req_string +="&lat={}".format() if False # if False
# req_string +="&lon={}".format() if False # req_string +="&lon={}".format() if False
subject = "" subject = ""
if 'Notfallgeschehen' in parsed_body:
subject = parsed_body['Notfallgeschehen'] + "\n"
if 'Notfallgeschehen' in parsed_body: if 'Notfallgeschehen' in parsed_body:
subject = parsed_body['Notfallgeschehen'] + "\n" subject = parsed_body['Notfallgeschehen'] + "\n"
req_string +="&subject={}".format(subject) req_string +="&subject={}".format(subject)
req_string +="&ils={}".format("ILS Saar") req_string +="&ils={}".format("ILS Saar")
req_string +="&connector={}".format("MailParser") req_string +="&connector={}".format("MailParser")
if os.environ.get('IS_DEV') and os.environ.get('IS_DEV') == "True":
req_string +="&isTest=1"
s.get(alarminator_api+"/operations/sendAlarm/"+req_string) s.get(alarminator_api+"/operations/sendAlarm/"+req_string)
except Exception as e: except Exception as e:
logging.error("alarminator_api", e) logging.error("alarminator_api", e)
def cups_print(parsed_body: dict, body: str): def cups_print(parsed_body: dict, body: str):
html = HTML(string='<html><head></head><body>'+body+"</body></html>", base_url="")
font_config = FontConfiguration()
pdf = html.write_pdf(font_config=font_config,stylesheets=[CSS(string='body { font-family: serif !important }')])
fname = "/tmp/{}.pdf".format(uuid.uuid4()) fname = "/tmp/{}.pdf".format(uuid.uuid4())
if os.environ.get('IS_DEV') and os.environ.get('IS_DEV') == "True":
with open("{}.pdf".format(uuid.uuid4()),"wb") as f:
f.write(pdf)
try: try:
conn = cups.Connection () conn = cups.Connection ()
printer = os.environ.get('printer',"DEFAULT") printer_arr = os.environ.get('printer',"DEFAULT").split(";")
print_num = int(os.environ.get('print_num',0)) print_num = int(os.environ.get('print_num',0))
for printer in printer_arr:
if 'ALARMDEPESCHE' in parsed_body: if 'ALARMDEPESCHE' in parsed_body:
with open(fname,"wb") as f: with open(fname,"wb") as f:
f.write(HTML(string=body, base_url="").write_pdf()) f.write(pdf)
for i in range(0, print_num): for i in range(0, print_num):
conn.printFile (printer, fname, "Alarmfax", {}) conn.printFile (printer, fname, "Alarmfax", {})
os.remove(fname) os.remove(fname)
+5 -1
View File
@@ -2,9 +2,12 @@ version: "2"
services: services:
app: app:
build: ./ build: ./
image: gitea.simonzeyer.de/simon/wnd_ils_alarmfax_parser:latest #image: gitea.simonzeyer.de/simon/wnd_ils_alarmfax_parser:latest
restart: always restart: always
privileged: true privileged: true
# ports:
# - 631:631
# - 5000:5000
environment: environment:
- username=${username} - username=${username}
- password=${password} - password=${password}
@@ -20,6 +23,7 @@ services:
- alarminator_zvies_use_PEALGRP=${alarminator_zvies_use_PEALGRP} - alarminator_zvies_use_PEALGRP=${alarminator_zvies_use_PEALGRP}
- printer=${printer} - printer=${printer}
- print_num=${print_num} - print_num=${print_num}
- MAPS_API_KEY=${MAPS_API_KEY}
volumes: volumes:
- ./cups:/etc/cups - ./cups:/etc/cups
- ./cupsd.conf.txt:/etc/cups/cupsd.conf - ./cupsd.conf.txt:/etc/cups/cupsd.conf
+19 -2
View File
@@ -1,20 +1,34 @@
blinker==1.8.2
Brotli==1.1.0
cached-property==1.5.2 cached-property==1.5.2
certifi==2022.12.7 certifi==2022.12.7
cffi==1.15.1 cffi==1.15.1
charset-normalizer==3.0.1 charset-normalizer==3.0.1
click==8.1.7
cryptography==39.0.0 cryptography==39.0.0
cssselect2==0.7.0
defusedxml==0.7.1 defusedxml==0.7.1
dnspython==2.3.0 dnspython==2.3.0
exchangelib==4.9.0 exchangelib==4.9.0
Flask==3.0.3
fonttools==4.54.1
html5lib==1.1
idna==3.4 idna==3.4
isodate==0.6.1 isodate==0.6.1
itsdangerous==2.2.0
Jinja2==3.1.4
lxml==4.9.2 lxml==4.9.2
MarkupSafe==3.0.1
ntlm-auth==1.5.0 ntlm-auth==1.5.0
numpy==1.24.1 numpy==1.24.1
oauthlib==3.2.2 oauthlib==3.2.2
pandas==1.5.2 pandas==1.5.2
pillow==10.4.0
pycparser==2.21 pycparser==2.21
pycups==2.0.1
pydyf==0.11.0
Pygments==2.14.0 Pygments==2.14.0
pyphen==0.16.0
python-dateutil==2.8.2 python-dateutil==2.8.2
pytz==2022.7.1 pytz==2022.7.1
pytz-deprecation-shim==0.1.0.post0 pytz-deprecation-shim==0.1.0.post0
@@ -22,10 +36,13 @@ requests==2.28.2
requests-ntlm==1.1.0 requests-ntlm==1.1.0
requests-oauthlib==1.3.1 requests-oauthlib==1.3.1
six==1.16.0 six==1.16.0
tinycss2==1.3.0
tzdata==2022.7 tzdata==2022.7
tzlocal==4.2 tzlocal==4.2
urllib3==1.26.14 urllib3==1.26.14
weasyprint==62.3
webencodings==0.5.1
Werkzeug==3.0.4
xmltodict==0.12.0 xmltodict==0.12.0
xmltojson==2.0.1 xmltojson==2.0.1
pycups==2.0.1 zopfli==0.2.3
weasyprint