Add flask-caching and kurjerzy shipment creation

master
informatic 2020-04-19 10:34:05 +02:00
parent c0df7cd026
commit e3c74b48cb
8 changed files with 189 additions and 9 deletions

View File

@ -8,6 +8,9 @@ services:
environment:
- POSTGRES_PASSWORD=secret
redis:
image: redis:5.0.8-alpine@sha256:cda5d02e4ea900a8d52e834bc3158e83b8a87a5b44ae081885aecf9b156dcff1
backend:
build: .
image: registry.k0.hswaw.net/informatic/covid-formity
@ -18,6 +21,8 @@ services:
environment:
- SPACEAUTH_DISABLE=true
- TEMPLATES_AUTO_RELOAD=true
- SHIPPING_KURJERZY_EMAIL
- SHIPPING_KURJERZY_PASSWORD
volumes:
pgdata:

View File

@ -1,7 +1,7 @@
import flask
from formity.admin import IndexView
from formity.external_auth import ExternalSpaceAuth
from formity.extensions import db, migrate, admin, babel, metrics
from formity.extensions import db, migrate, admin, babel, metrics, cache
def create_app():
app = flask.Flask(
@ -18,6 +18,7 @@ def create_app():
babel.init_app(app)
ExternalSpaceAuth().init_app(app)
metrics.init_app(app)
cache.init_app(app)
import formity.views
import formity.models

View File

@ -1,10 +1,13 @@
from flask import redirect, flash, request, url_for
import enum
import io
import datetime
import csv
from flask import redirect, flash, request, url_for, make_response, current_app
import flask_admin
from formity.extensions import admin, db, ModelView, ModelViewHighSecurity, AdminSecurityMixin
from wtforms import TextAreaField, validators
from formity.models import FaceshieldRequest, RequestChange, Status, PostalCode, ExternalUser
from spaceauth import current_user
import enum
from flask_weasyprint import HTML, render_pdf
from sqlalchemy import func
@ -178,6 +181,155 @@ class FaceshieldRequestAdmin(ModelView):
models = self.get_query().filter(FaceshieldRequest.id.in_(ids)).all()
return render_pdf(HTML(string=self.render('label.html', models=models)))
@flask_admin.actions.action('csv_kurjerzy', 'Export Kurjerzy.pl CSV')
def action_csv_kurjerzy(self, ids):
models = self.get_query().filter(FaceshieldRequest.id.in_(ids)).all()
fields = [
'kurier',
'nadawca_nazwa', 'nadawca_email', 'nadawca_ulica',
'nadawca_nr_lok', 'nadawca_kod', 'nadawca_miasto',
'nadawca_telefon', 'nadawca_kraj', 'nadawca_region',
'nadawca_punkt',
'odbiorca_nazwa', 'odbiorca_email', 'odbiorca_ulica',
'odbiorca_nr_lok', 'odbiorca_kod', 'odbiorca_miasto',
'odbiorca_telefon', 'odbiorca_kraj', 'odbiorca_region',
'odbiorca_punkt',
'przesylka_rodzaj', 'przesylka_opakowanie', 'przesylka_waga',
'przesylka_szerokosc', 'przesylka_wysokosc', 'przesylka_dlugosc',
'przesylka_wartosc', 'przesylka_zawartosc',
'pobranie_wartosc', 'pobranie_nr_bank', 'pobranie_1_dzien',
'sms_nadawca', 'sms_odbiorca', 'dostawa_nast_dnia',
'dokumenty_zwrotne', 'bez_odbioru', 'numer_ref', 'kod_promo'
]
fname = 'kurjerzy-export-%s.csv' % (datetime.datetime.now().strftime(r'%Y%m%d-%H%M%S'),)
si = io.StringIO()
writer = csv.DictWriter(si, fields, delimiter=';')
writer.writeheader()
config = current_app.config
for row in models:
writer.writerow({
'kurier': 'ups',
'nadawca_nazwa': config['SHIPPING_SENDER_NAME'],
'nadawca_email': config['SHIPPING_SENDER_EMAIL'],
'nadawca_ulica': config['SHIPPING_SENDER_STREET'],
'nadawca_nr_lok': config['SHIPPING_SENDER_NUMBER'],
'nadawca_kod': config['SHIPPING_SENDER_POSTALCODE'],
'nadawca_miasto': config['SHIPPING_SENDER_CITY'],
'nadawca_telefon': config['SHIPPING_SENDER_PHONE_NUMBER'],
'nadawca_kraj': 'PL',
'odbiorca_nazwa': row.shipping_name,
'odbiorca_email': row.email,
'odbiorca_ulica': row.shipping_street,
'odbiorca_nr_lok': '1/1', # FIXME
'odbiorca_kod': row.shipping_postalcode,
'odbiorca_miasto': row.shipping_city,
'odbiorca_telefon': row.phone_number,
'odbiorca_kraj': 'PL',
# TODO
'przesylka_rodzaj': 1,
'przesylka_opakowanie': 1,
'przesylka_waga': 4,
'przesylka_szerokosc': 40,
'przesylka_wysokosc': 30,
'przesylka_dlugosc': 20,
'przesylka_wartosc': 100,
'przesylka_zawartosc': 'przyłbice dla medyków %d' % (row.id,),
'sms_odbiorca': 1,
'dostawa_nast_dnia': 1,
})
row.changelog.append(RequestChange(
remarks='included on Kurjerzy export (%s)' % (fname,),
))
db.session.commit()
output = make_response(si.getvalue())
output.headers["Content-Disposition"] = "attachment; filename=%s" % (fname,)
output.headers["Content-type"] = "text/csv"
return output
@flask_admin.actions.action('csv_xbs', 'Export XBS Group/DPD CSV')
def action_csv_xbs(self, ids):
models = self.get_query().filter(FaceshieldRequest.id.in_(ids)).all()
fields = [
'NAZWA NADAWCY', 'OSOBA KONTAKTOWA NADAWCA',
'TELEFON KONTAKTOWY NADAWCA ', 'ULICA NADAWCA',
'KOD POCZTOWY NADAWCA', 'MIASTO NADAWCA',
'NAZWA ODBIORCA', 'OSOBA KONTAKTOWA ODBIORCA',
'TELEFON KONTAKTOWYODBIORCA', 'ULICA ODBIORCA',
'KOD POCZTOWY ODBIORCA', 'MIASTO ODBIORCA',
'WAGA', 'ILOŚĆ PACZEK', 'NUMKAT', 'NEXT DAY', 'SOBOTA',
'GWARANT 9:30', 'GWARANT 12:00', 'DZ', 'COD', 'WARTOŚĆ',
'NR REFERENCYJNY 1', 'NR REFERENCYJNY 2',
'ZAWARTOŚĆ', 'UWAGII',
]
fname = 'xbs-dpd-export-%s.csv' % (datetime.datetime.now().strftime(r'%Y%m%d-%H%M%S'),)
si = io.StringIO()
writer = csv.DictWriter(si, fields)
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'],
'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',
})
row.changelog.append(RequestChange(
remarks='included on XBS export (%s)' % (fname,),
))
db.session.commit()
output = make_response(si.getvalue())
output.headers["Content-Disposition"] = "attachment; filename=%s" % (fname,)
output.headers["Content-type"] = "text/csv"
return output
@flask_admin.actions.action('create_shipment', 'Create Kurjerzy shipment')
def action_create_shipment(self, ids):
from shipping.kurjerzy import Kurjerzy
models = self.get_query().filter(FaceshieldRequest.id.in_(ids)).all()
k = Kurjerzy(current_app)
k.authenticate()
for model in models:
k.create_shipment(model)
db.session.commit()
flash('Shipments created')
class FilteredFaceshieldRequestAdmin(FaceshieldRequestAdmin):
def get_query(self):
return super(FilteredFaceshieldRequestAdmin, self).get_query().filter(~FaceshieldRequest.status.in_([Status.rejected, Status.spam, Status.fulfilled, Status.delegated]))

View File

@ -4,6 +4,7 @@ import flask_sqlalchemy
import flask_migrate
import flask_admin
import flask_babel
import flask_caching
import prometheus_flask_exporter
@ -41,3 +42,4 @@ migrate = flask_migrate.Migrate()
admin = flask_admin.Admin(name='Covid-Formity CRM™', template_mode='bootstrap3')
babel = flask_babel.Babel()
metrics = prometheus_flask_exporter.PrometheusMetrics(None, group_by='url_rule')
cache = flask_caching.Cache()

View File

@ -92,13 +92,17 @@ class FaceshieldRequest(db.Model):
return self.faceshield_full_delivered // 150
return 1
def changelog_default_user_id():
from flask_login import current_user
return current_user.get_id() if current_user and current_user.get_id() else 'nobody'
class RequestChange(db.Model):
id = db.Column(db.Integer, primary_key=True)
request_id = db.Column(db.Integer, db.ForeignKey(FaceshieldRequest.id))
request = db.relationship(FaceshieldRequest, backref='changelog')
user_id = db.Column(db.String, nullable=False)
user_id = db.Column(db.String, nullable=False, default=changelog_default_user_id)
state_before = db.Column(db.JSON)
state_after = db.Column(db.JSON)
@ -140,12 +144,9 @@ def on_request_change(mapper, connection, target):
before, after = {'diff': 'failed'}, {'diff': 'failed'}
if before or after:
from flask_login import current_user
user_id = current_user.get_id() if current_user and current_user.get_id() else 'nobody'
@event.listens_for(db.session, "after_flush", once=True)
def receive_after_flush(session, context):
db.session.add(RequestChange(
user_id=user_id,
request_id=target.id,
state_before=before,
state_after=after,

View File

@ -21,3 +21,19 @@ PROXYFIX_NUM_PROXIES = env.int('PROXYFIX_NUM_PROXIES', default=1)
BABEL_DEFAULT_LOCALE = 'pl'
TEMPLATES_AUTO_RELOAD = env.bool('TEMPLATES_AUTO_RELOAD', default=False)
SHIPPING_KURJERZY_EMAIL = env.str('SHIPPING_KURJERZY_EMAIL', default='')
SHIPPING_KURJERZY_PASSWORD = env.str('SHIPPING_KURJERZY_PASSWORD', default='')
SHIPPING_SENDER_NAME = env.str('SHIPPING_SENDER_NAME', default='Warszawski Hackerspace')
SHIPPING_SENDER_NAME2 = env.str('SHIPPING_SENDER_NAME2', default='')
SHIPPING_SENDER_EMAIL = env.str('SHIPPING_SENDER_EMAIL', default='covid-logistics@hackerspace.pl')
SHIPPING_SENDER_STREET = env.str('SHIPPING_SENDER_STREET', default='Wolność')
SHIPPING_SENDER_NUMBER = env.str('SHIPPING_SENDER_NUMBER', default='2A')
SHIPPING_SENDER_POSTALCODE = env.str('SHIPPING_SENDER_POSTALCODE', default='01-018')
SHIPPING_SENDER_CITY = env.str('SHIPPING_SENDER_CITY', default='Warszawa')
SHIPPING_SENDER_PHONE_NUMBER = env.str('SHIPPING_SENDER_PHONE_NUMBER', default='')
CACHE_TYPE = env.str('CACHE_TYPE', default='redis')
CACHE_REDIS_URL = env.str('CACHE_REDIS_URL', default='redis://redis')

View File

@ -13,6 +13,7 @@ environs==7.3.1
Flask==1.1.1
Flask-Admin==1.5.5
Flask-Babel==1.0.0
Flask-Caching==1.8.0
Flask-Login==0.5.0
Flask-Migrate==2.5.3
Flask-OAuthlib==0.9.5
@ -37,6 +38,7 @@ python-dateutil==2.8.1
python-dotenv==0.12.0
python-editor==1.0.4
pytz==2019.3
redis==3.4.1
requests==2.23.0
requests-oauthlib==1.3.0
six==1.14.0

View File

@ -1,7 +1,7 @@
{% macro render_changelog(model) %}
{% for entry in model.changelog %}
<p>
<span class="text-muted">{{ entry.created.strftime('%Y/%m/%d %H:%M') }}</span> <b>{{ entry.user_id }}</b> changed
<span class="text-muted">{{ entry.created.strftime('%Y/%m/%d %H:%M') }}</span> <b>{{ entry.user_id }}</b> {% if entry.state_after %}changed
{% for key, value in entry.state_after.items() %}
{%- if not loop.first -%}
{%- if loop.last -%}
@ -9,7 +9,8 @@
{%- else -%}
,
{%- endif -%}
{%- endif %} <b>{{ key }}</b> to <code title="{{ value }}">{{ value|string()|truncate(40) }}</code> {% endfor %}
{%- endif %} <b>{{ key }}</b> to <code title="{{ value }}">{{ value|string()|truncate(40) }}</code> {% endfor %}{% endif %}
{% if entry.remarks %}<i>{{ entry.remarks }}</i>{% endif %}
</p>
{% endfor %}
{% endmacro %}