Add PDF generation and printing functionality using Google Chrome

This commit is contained in:
Simon Zeyer 2025-06-09 11:52:44 +00:00
parent 2b331fbd1f
commit 8b4bc74b1d
2 changed files with 81 additions and 19 deletions

View File

@ -55,6 +55,24 @@ RUN useradd \
print \ print \
&& sed -i '/%sudo[[:space:]]/ s/ALL[[:space:]]*$/NOPASSWD:ALL/' /etc/sudoers && sed -i '/%sudo[[:space:]]/ s/ALL[[:space:]]*$/NOPASSWD:ALL/' /etc/sudoers
# Print PDF
RUN apt-get update && apt-get install -y \
apt-transport-https \
ca-certificates \
curl \
gnupg \
--no-install-recommends \
&& curl -sSL https://dl.google.com/linux/linux_signing_key.pub | apt-key add - \
&& echo "deb [arch=amd64] https://dl.google.com/linux/chrome/deb/ stable main" > /etc/apt/sources.list.d/google-chrome.list \
&& apt-get update && apt-get install -y \
google-chrome-stable \
--no-install-recommends
# It won't run from the root user.
RUN groupadd chrome && useradd -g chrome -s /bin/bash -G audio,video chrome \
&& mkdir -p /home/chrome && chown -R chrome:chrome /home/chrome
COPY requirements.txt ./ COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt RUN pip install --no-cache-dir -r requirements.txt

View File

@ -5,6 +5,7 @@ import cups
from weasyprint import HTML from weasyprint import HTML
from requests.adapters import Retry from requests.adapters import Retry
import uuid import uuid
import subprocess
retries = Retry(total=5, retries = Retry(total=5,
backoff_factor=0.1, backoff_factor=0.1,
@ -26,18 +27,18 @@ 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 'Sachverhalt' in parsed_body:
req_string +="&message={}".format(parsed_body['Sachverhalt']) req_string +="&message={}".format(parsed_body['Sachverhalt'])
@ -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,9 +85,30 @@ 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: if 'Notfallgeschehen' in parsed_body:
@ -88,23 +118,37 @@ def alarminator_api(parsed_body: dict):
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 generate_pdf(html_body, filename):
f = "/tmp/{}.html".format(uuid.uuid4())
with open(f,"w") as _f:
_f.write(html_body)
subprocess.run(["/usr/bin/google-chrome-stable", "--headless", "--no-sandbox", "--disable-gpu", "--print-to-pdf="+filename, "--no-pdf-header-footer", "--print-to-pdf-no-header", "--no-margins", f])
if os.path.exists(f):
os.remove(f)
def cups_print(parsed_body: dict, body: str): def cups_print(parsed_body: dict, body: str):
fname = "/tmp/{}.pdf".format(uuid.uuid4()) # if os.environ.get('IS_DEV') and os.environ.get('IS_DEV') == "True":
# generate_pdf(body, "{}.pdf".format(uuid.uuid4()))
fname = "{}.pdf".format(uuid.uuid4())
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))
if 'ALARMDEPESCHE' in parsed_body: #if printer_arr.__len__() > 0:
with open(fname,"wb") as f: generate_pdf(body, fname)
f.write(HTML(string=body, base_url="").write_pdf()) for printer in printer_arr:
for i in range(0, print_num): print(printer)
conn.printFile (printer, fname, "Alarmfax", {}) if 'ALARMDEPESCHE' in parsed_body:
os.remove(fname) for i in range(0, print_num):
conn.printFile (printer, fname, "Alarmfax", {})
except Exception as e: except Exception as e:
logging.error("cups_print", e)
finally:
if os.path.exists(fname): if os.path.exists(fname):
os.remove(fname) os.remove(fname)
logging.error("cups_print", e)