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

4 years ago
import os
import re
import hashlib
import time
import ssl
import configparser
4 years ago
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)
######################
4 years ago
app = Flask(__name__)
app.config.update(
UPLOADED_PATH = UPLOAD_DIR,
MAX_CONTENT_LENGTH = MAX_UPLOAD_SIZE,
3 years ago
TEMPLATES_AUTO_RELOAD = True
4 years ago
)
dropzone = Dropzone(app)
contract_creator = ContractCreator()
4 years ago
@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)
3 years ago
4 years ago
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
4 years ago
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:
4 years ago
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
4 years ago
def check_form(form):
tip = form.get('tip')
ime = form.get('ime')
podjetje = form.get('podjetje')
naslov = form.get('naslov')
posta = form.get('posta')
4 years ago
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.'
4 years ago
if len(email) > 100:
return 'Predolgi email naslov'
elif not re.search(REGEX_EMAIL, email):
4 years ago
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'
4 years ago
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')
4 years ago
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'
4 years ago
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'])
4 years ago
f.write('\ndatoteke=' + str(sorted_f_hashes))
f.write('\npogodba=' + contract)
4 years ago
def store_datafiles(files, upload_metadata):
base = get_subdir('files')
file_hashes = upload_metadata['file_hashes']
4 years ago
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()
3 years ago
4 years ago
if __name__ == '__main__':
app.run(debug=True)