Add DPD protocol import
parent
0cb51988a1
commit
ec7705761c
|
@ -10,7 +10,7 @@ RUN apk add --no-cache \
|
|||
# psycopg2 needs some extra build tools and headers. Install them and build in a
|
||||
# single step in order not to pollute Docker layers
|
||||
RUN apk add --no-cache --virtual .build-deps gcc python3-dev musl-dev postgresql-dev libffi-dev && \
|
||||
pip3 install --no-cache-dir psycopg2==2.8.4 pycparser==2.20 cffi==1.14.0 bcrypt==3.1.7 && \
|
||||
pip3 install --no-cache-dir psycopg2==2.8.4 pycparser==2.20 cffi==1.14.0 bcrypt==3.1.7 pycryptodome==3.9.7 && \
|
||||
apk del --no-cache .build-deps
|
||||
|
||||
RUN apk add --no-cache py3-pillow cairo pango glib font-noto gdk-pixbuf-dev
|
||||
|
|
|
@ -2,8 +2,9 @@ import enum
|
|||
import io
|
||||
import datetime
|
||||
import csv
|
||||
from flask import redirect, flash, request, url_for, make_response, current_app
|
||||
from flask import redirect, flash, request, url_for, make_response, current_app, session
|
||||
import flask_admin
|
||||
import pdfplumber
|
||||
from formity.extensions import admin, db, ModelView, ModelViewHighSecurity, AdminSecurityMixin
|
||||
from wtforms import TextAreaField, validators
|
||||
from formity.models import FaceshieldRequest, RequestChange, Status, PostalCode, ExternalUser
|
||||
|
@ -138,6 +139,58 @@ class FaceshieldRequestAdmin(ModelView):
|
|||
model = self.get_one(request.args.get('id'))
|
||||
return self.render('label.html', models=[model])
|
||||
|
||||
@flask_admin.expose('/import-dpd', methods=['POST', 'GET'])
|
||||
def dpd_import(self):
|
||||
if request.method == 'POST':
|
||||
# check if the post request has the file part
|
||||
if request.form.get('accept') == '1':
|
||||
changed = 0
|
||||
for k, v in session.get('dpd-import', {}).items():
|
||||
m = FaceshieldRequest.query.filter(FaceshieldRequest.id == k).first()
|
||||
if not m:
|
||||
flash('Request #{} not found'.format(k), 'warning')
|
||||
continue
|
||||
m.shipping_id = ','.join(v)
|
||||
m.shipping_provider = 'xbs'
|
||||
changed += 1
|
||||
db.session.commit()
|
||||
flash('{} requests changed'.format(changed), 'info')
|
||||
session.pop('dpd-import')
|
||||
return redirect(request.url)
|
||||
|
||||
if 'file' not in request.files:
|
||||
flash('No file uploaded', 'error')
|
||||
return redirect(request.url)
|
||||
file = request.files['file']
|
||||
|
||||
if file.filename == '':
|
||||
flash('No selected file', 'error')
|
||||
return redirect(request.url)
|
||||
|
||||
p = pdfplumber.load(file.stream)
|
||||
data = self.parse_dpd(p)
|
||||
session['dpd-import'] = {k: list(v) for k, v in data.items()}
|
||||
return self.render('admin/dpd_pdf_import.html', data=session['dpd-import'])
|
||||
|
||||
return self.render('admin/dpd_pdf_import.html')
|
||||
|
||||
def parse_dpd(self, pdf):
|
||||
data = {}
|
||||
for page in pdf.pages:
|
||||
table = page.extract_table()
|
||||
if table[0] != [None, None, None, None, 'MIEJSCE DORĘCZENIA PACZKI', None, None, None, None, None]:
|
||||
raise Exception('Invalid header (1/2)')
|
||||
if table[1] != ['LP', 'Numkat', 'NUMER PACZKI', 'NUMER REFERENCYJNY\n( np. numer faktury, zamówienia itp.)', 'OPIS', 'Odbiorca', 'Wartość\nprzesyłki', 'KWOTA\nCOD PLN', 'Waga dekl.\n(kg)', 'Zawartość']:
|
||||
raise Exception('Invalid header (2/2)')
|
||||
|
||||
for row in table[2:]:
|
||||
ref = row[3].split('/')[0]
|
||||
tracking = row[2]
|
||||
if ref not in data:
|
||||
data[ref] = set()
|
||||
data[ref].add(tracking)
|
||||
return data
|
||||
|
||||
def bulk_status_change(self, ids, status):
|
||||
try:
|
||||
query = self.get_query().filter(FaceshieldRequest.id.in_(ids))
|
||||
|
@ -283,27 +336,28 @@ class FaceshieldRequestAdmin(ModelView):
|
|||
writer.writeheader()
|
||||
config = current_app.config
|
||||
for row in models:
|
||||
writer.writerow({
|
||||
'NAZWA NADAWCY': config['SHIPPING_SENDER_NAME'],
|
||||
'OSOBA KONTAKTOWA NADAWCA': config['SHIPPING_SENDER_NAME2'],
|
||||
'ULICA NADAWCA': '%s %s' % (config['SHIPPING_SENDER_STREET'], config['SHIPPING_SENDER_NUMBER']),
|
||||
'KOD POCZTOWY NADAWCA': config['SHIPPING_SENDER_POSTALCODE'],
|
||||
'MIASTO NADAWCA': config['SHIPPING_SENDER_CITY'],
|
||||
'TELEFON KONTAKTOWY NADAWCA ': config['SHIPPING_SENDER_PHONE_NUMBER'],
|
||||
for label_id in range(row.label_count):
|
||||
writer.writerow({
|
||||
'NAZWA NADAWCY': config['SHIPPING_SENDER_NAME'],
|
||||
'OSOBA KONTAKTOWA NADAWCA': config['SHIPPING_SENDER_NAME2'],
|
||||
'ULICA NADAWCA': '%s %s' % (config['SHIPPING_SENDER_STREET'], config['SHIPPING_SENDER_NUMBER']),
|
||||
'KOD POCZTOWY NADAWCA': config['SHIPPING_SENDER_POSTALCODE'],
|
||||
'MIASTO NADAWCA': config['SHIPPING_SENDER_CITY'],
|
||||
'TELEFON KONTAKTOWY NADAWCA ': config['SHIPPING_SENDER_PHONE_NUMBER'],
|
||||
|
||||
'NAZWA ODBIORCA': row.shipping_name,
|
||||
'OSOBA KONTAKTOWA ODBIORCA': row.full_name,
|
||||
'TELEFON KONTAKTOWYODBIORCA': row.phone_number,
|
||||
'ULICA ODBIORCA': row.shipping_street,
|
||||
'KOD POCZTOWY ODBIORCA': row.shipping_postalcode,
|
||||
'MIASTO ODBIORCA': row.shipping_city,
|
||||
'NAZWA ODBIORCA': row.shipping_name,
|
||||
'OSOBA KONTAKTOWA ODBIORCA': row.full_name,
|
||||
'TELEFON KONTAKTOWYODBIORCA': row.phone_number,
|
||||
'ULICA ODBIORCA': row.shipping_street,
|
||||
'KOD POCZTOWY ODBIORCA': row.shipping_postalcode,
|
||||
'MIASTO ODBIORCA': row.shipping_city,
|
||||
|
||||
# TODO kartony?
|
||||
'WAGA': 12,
|
||||
'ILOŚĆ PACZEK': 1,
|
||||
'NR REFERENCYJNY 1': row.id,
|
||||
'ZAWARTOŚĆ': 'przyłbice dla medyków',
|
||||
})
|
||||
# TODO kartony?
|
||||
'WAGA': 20,
|
||||
'ILOŚĆ PACZEK': 1,
|
||||
'NR REFERENCYJNY 1': '%s/%s/%s' % (row.id, label_id + 1, row.label_count),
|
||||
'ZAWARTOŚĆ': 'przyłbice dla medyków',
|
||||
})
|
||||
|
||||
row.changelog.append(RequestChange(
|
||||
remarks='included on XBS export (%s)' % (fname,),
|
||||
|
|
|
@ -29,10 +29,13 @@ Mako==1.1.2
|
|||
MarkupSafe==1.1.1
|
||||
marshmallow==3.5.1
|
||||
oauthlib==2.1.0
|
||||
pdfminer.six==20200104
|
||||
pdfplumber==0.5.19
|
||||
Pillow==6.2.1
|
||||
prometheus-client==0.7.1
|
||||
prometheus-flask-exporter==0.13.0
|
||||
pycparser==2.20
|
||||
pycryptodome==3.9.7
|
||||
Pyphen==0.9.5
|
||||
python-dateutil==2.8.1
|
||||
python-dotenv==0.12.0
|
||||
|
@ -42,9 +45,12 @@ redis==3.4.1
|
|||
requests==2.23.0
|
||||
requests-oauthlib==1.3.0
|
||||
six==1.14.0
|
||||
sortedcontainers==2.1.0
|
||||
SQLAlchemy==1.3.15
|
||||
tinycss2==1.0.2
|
||||
unicodecsv==0.14.1
|
||||
urllib3==1.25.8
|
||||
Wand==0.5.9
|
||||
WeasyPrint==51
|
||||
webencodings==0.5.1
|
||||
Werkzeug==0.16.1
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
{% extends "admin/master.html" %}
|
||||
{% block body %}
|
||||
<h2 class="page-header">DPD Protocol Import</h2>
|
||||
<form method=post enctype=multipart/form-data>
|
||||
<input type=file name=file>
|
||||
<input type=submit value=Upload>
|
||||
</form>
|
||||
{% if data is defined %}
|
||||
<h3>Imported data:</h3>
|
||||
<form method="post">
|
||||
<table class="table">
|
||||
{% for k, v in data.items() %}
|
||||
<tr><td>{{ k }}</td><td>{{ v }}</td></tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
<input type="hidden" name="accept" value="1" />
|
||||
<button class="btn btn-primary">Accept</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
{% endblock %}
|
Loading…
Reference in New Issue