Add flask-caching and kurjerzy shipment creation
parent
c0df7cd026
commit
e3c74b48cb
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
156
formity/admin.py
156
formity/admin.py
|
@ -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]))
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 %}
|
||||
|
|
Loading…
Reference in New Issue