You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
portal-oddajanje-solar/app.py

330 lines
9.9 KiB

import os
import re
import hashlib
import time
import ssl
import configparser
from pathlib import Path
from flask import Flask, render_template, request
from flask_dropzone import Dropzone
import imaplib
from smtplib import SMTP_SSL
import email
from email import encoders
from email.mime.base import MIMEBase
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.application import MIMEApplication
import pdfkit
from jinja2 import Environment, FileSystemLoader
class ContractCreator:
def __init__(self):
template_loader = FileSystemLoader(searchpath="./")
template_env = Environment(loader=template_loader)
self.template = template_env.get_template('contract/template.html')
self.pdfkit_options = {
'page-size': 'A4',
'margin-top': '0.75in',
'margin-right': '0.75in',
'margin-bottom': '0.75in',
'margin-left': '0.75in',
'encoding': "UTF-8",
'custom-header' : [
('Accept-Encoding', 'gzip')
]
}
def fill_template(self, **kwargs):
return self.template.render(**kwargs)
def create_pdf(self, out_f, fields_dict):
html_str = self.fill_template(**fields_dict)
pdfkit.from_string(html_str, out_f, options=self.pdfkit_options)
ENABLED_FILETYPES = ['txt', 'csv', 'pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx']
REGEX_EMAIL = re.compile('^[a-z0-9]+[\._]?[a-z0-9]+[@]\w+[.]\w{2,3}$')
######################
# Load configuration #
######################
config = configparser.ConfigParser()
config.read('config.ini')
config = config['DEFAULT']
MAIL_HOST = config['MAIL_HOST']
MAIL_LOGIN = config['MAIL_LOGIN']
MAIL_PASS = config['MAIL_PASS']
SMTP_PORT = int(config['SMTP_PORT'])
IMAP_PORT = int(config['IMAP_PORT'])
MAX_UPLOAD_SIZE = int(config['MAX_UPLOAD_SIZE']) # Bytes
CONTRACT_CLIENT_CONTACT = config['CONTRACT_CLIENT_CONTACT']
if 'BASE_DIR' in config:
BASE_DIR = Path(config['BASE_DIR'])
else:
BASE_DIR = Path(__file__).resolve().parent
# Override configs with environment variables, if set
if 'PORTALDS4DS1_MAIL_HOST' in os.environ:
MAIL_HOST = os.environ('PORTALDS4DS1_MAIL_HOST')
if 'PORTALDS4DS1_MAIL_LOGIN' in os.environ:
MAIL_LOGIN = os.environ('PORTALDS4DS1_MAIL_LOGIN')
if 'PORTALDS4DS1_MAIL_PASS' in os.environ:
MAIL_PASS = os.environ('PORTALDS4DS1_MAIL_PASS')
if 'PORTALDS4DS1_SMTP_PORT' in os.environ:
SMTP_PORT = int(os.environ('PORTALDS4DS1_SMTP_PORT'))
if 'PORTALDS4DS1_IMAP_PORT' in os.environ:
IMAP_PORT = int(os.environ('PORTALDS4DS1_IMAP_PORT'))
if 'MAX_UPLOAD_SIZE' in os.environ:
MAX_UPLOAD_SIZE = int(os.environ('PORTALDS4DS1_MAX_UPLOAD_SIZE'))
if 'CONTRACT_CLIENT_CONTACT' in os.environ:
CONTRACT_CLIENT_CONTACT = os.environ('PORTALDS4DS1_CONTRACT_CLIENT_CONTACT')
UPLOAD_DIR = BASE_DIR / 'uploads'
if not UPLOAD_DIR.exists:
UPLOAD_DIR.mkdir(parents=True)
######################
app = Flask(__name__)
app.config.update(
UPLOADED_PATH = UPLOAD_DIR,
MAX_CONTENT_LENGTH = MAX_UPLOAD_SIZE,
TEMPLATES_AUTO_RELOAD = True
)
dropzone = Dropzone(app)
contract_creator = ContractCreator()
@app.route('/')
def index():
return render_template('index.html')
@app.route('/upload', methods=['POST'])
def handle_upload():
files = request.files
if len(files) > 20:
return 'Naložite lahko do 20 datotek hkrati.', 400
elif len(files) < 1:
return 'Priložena ni bila nobena datoteka.', 400
err = check_suffixes(files)
if err:
return err, 400
err = check_form(request.form)
if err:
return err, 400
upload_metadata = get_upload_metadata(request)
contract_file_name = generate_contract_pdf(upload_metadata)
# Add contract_file_name to metadata TODO: move somewhere else
upload_metadata['contract'] = contract_file_name
store_datafiles(files, upload_metadata)
store_metadata(upload_metadata)
send_confirm_mail(upload_metadata)
return 'Uspešno ste oddali datotek(e). Št. datotek: {}'.format(len(files))
def get_upload_metadata(request):
upload_metadata = dict()
file_hashes = create_file_hashes(request.files)
form_data = request.form.copy()
upload_timestamp = int(time.time())
upload_id = create_upload_id(form_data, upload_timestamp, file_hashes)
upload_metadata['form_data'] = form_data
upload_metadata['upload_id'] = upload_id
upload_metadata['timestamp'] = upload_timestamp
upload_metadata['file_hashes'] = file_hashes
return upload_metadata
def check_suffixes(files):
for key, f in files.items():
if key.startswith('file'):
suffix = f.filename.split('.')[-1]
if suffix not in ENABLED_FILETYPES:
return 'Datoteka "{}" ni pravilnega formata.'.format(f.filename)
return None
def get_subdir(dir_name):
subdir = app.config['UPLOADED_PATH'] / dir_name
if not subdir.exists():
subdir.mkdir()
return subdir
def create_upload_id(form_data, upload_timestamp, file_hashes):
tip = form_data.get('tip')
ime = form_data.get('ime')
podjetje = form_data.get('podjetje')
naslov = form_data.get('naslov')
posta = form_data.get('posta')
email = form_data.get('email')
telefon = form_data.get('telefon')
# This hash serves as an unique identifier for the whole upload.
metahash = hashlib.md5((tip+ime+podjetje+naslov+posta+email+telefon).encode())
# Include file hashes to avoid metafile name collisions if they have the same form values,
# but different data files. Sort hashes first so upload order doesn't matter.
sorted_f_hashes = list(file_hashes.values())
sorted_f_hashes.sort()
metahash.update(''.join(sorted_f_hashes).encode())
metahash = metahash.hexdigest()
return metahash
def check_form(form):
tip = form.get('tip')
ime = form.get('ime')
podjetje = form.get('podjetje')
naslov = form.get('naslov')
posta = form.get('posta')
email = form.get('email')
telefon = form.get('telefon')
if tip not in ['enojez', 'prevodi']:
return 'Napačen tip besedila.'
if len(ime) > 100:
return 'Predolgo ime.'
if len(podjetje) > 100:
return 'Predolgo ime institucije.'
if len(email) > 100:
return 'Predolgi email naslov'
elif not re.search(REGEX_EMAIL, email):
return 'Email napačnega formata.'
if len(telefon) > 100:
return 'Predolga telefonska št.'
if len(naslov) > 100:
return 'Predolg naslov.'
if len(posta) > 100:
return 'Predolga pošta'
return None
def create_file_hashes(files):
res = dict()
for key, f in files.items():
if key.startswith('file'):
h = hashlib.md5(f.filename.encode())
h.update(f.stream.read())
res[key] = h.hexdigest()
f.seek(0)
return res
def store_metadata(upload_metadata):
base = get_subdir('meta')
timestamp = upload_metadata['timestamp']
upload_id = upload_metadata['upload_id']
form_data = upload_metadata['form_data']
email = form_data['email']
file_hashes = upload_metadata['file_hashes']
contract = upload_metadata['contract']
filename = str(timestamp) + '-' + email + '-' + upload_id + '.meta'
sorted_f_hashes = list(file_hashes.values())
sorted_f_hashes.sort()
path = base / filename
with path.open('w') as f:
f.write('tip=' + form_data['tip'])
f.write('\nime=' + form_data['ime'])
f.write('\npodjetje=' + form_data['podjetje'])
f.write('\nnaslov=' + form_data['naslov'])
f.write('\nposta=' + form_data['posta'])
f.write('\nemail=' + form_data['email'])
f.write('\ndatoteke=' + str(sorted_f_hashes))
f.write('\npogodba=' + contract)
def store_datafiles(files, upload_metadata):
base = get_subdir('files')
file_hashes = upload_metadata['file_hashes']
for key, f in files.items():
if key.startswith('file'):
path = base / file_hashes[key]
if not path.exists():
path.mkdir()
f.save(path / f.filename)
def generate_contract_pdf(upload_metadata):
base = get_subdir('contracts')
contract_file_name = upload_metadata['upload_id'] + '.pdf'
form_data = upload_metadata['form_data']
data = {
'ime_priimek': form_data['ime'],
'naslov': form_data['naslov'],
'posta': form_data['posta'],
'kontakt_narocnik': CONTRACT_CLIENT_CONTACT,
'kontakt_imetnikpravic': form_data['ime']
}
contract_creator.create_pdf(base / contract_file_name, data)
return contract_file_name
def send_confirm_mail(upload_metadata):
body = 'Usprešno ste oddali besedila. V prilogi vam pošiljamo pogodbo.'
message = MIMEMultipart()
message['From'] = MAIL_LOGIN
message['To'] = upload_metadata['form_data']['email']
message['Subject'] = 'Pogodba za oddana besedila ' + upload_metadata['upload_id']
message.attach(MIMEText(body, "plain"))
contracts_dir = get_subdir('contracts')
base_name = upload_metadata['contract']
contract_file = contracts_dir / base_name
with open(contract_file, "rb") as f:
part = MIMEApplication(
f.read(),
Name = base_name
)
part['Content-Disposition'] = 'attachment; filename="%s"' % base_name
message.attach(part)
text = message.as_string()
# Create a secure SSL context
context = ssl.create_default_context()
with SMTP_SSL(MAIL_HOST, SMTP_PORT, context=context) as server:
server.login(MAIL_LOGIN, MAIL_PASS)
server.sendmail(message['From'], message['To'], text)
# Save copy of sent mail in Sent mailbox
imap = imaplib.IMAP4_SSL(MAIL_HOST, IMAP_PORT)
imap.login(MAIL_LOGIN, MAIL_PASS)
imap.append('Sent', '\\Seen', imaplib.Time2Internaldate(time.time()), text.encode('utf8'))
imap.logout()
if __name__ == '__main__':
app.run(debug=True)