docker image and changes in models

Made some convenience changes in models and created a Dockerfile for
deployment.
Also changed the configs to be compatible for custom variables in Docker
This commit is contained in:
Lunaresk 2022-07-21 11:55:46 +02:00
parent b978e0da56
commit 4459000d4f
15 changed files with 57 additions and 37 deletions

View File

@ -1,15 +1,20 @@
FROM python:3.10-slim
FROM python@sha256:38000b248a186dcae150fe2f64d23bd44a0730347d1e5e4d1faedd449a9a4913
RUN useradd scan2kasse
WORKDIR /home/scan2kasse
RUN apt update && apt upgrade
RUN apt install -y libpq-dev gcc g++
COPY requirements.txt requirements.txt
RUN python -m venv venv
RUN venv/bin/pip install -r requirements.txt
RUN venv/bin/pip install gunicorn
COPY app app
COPY migrations migrations
COPY configs configs
COPY run.py boot.sh ./
RUN chmod +x boot.sh

View File

@ -1,3 +1,4 @@
from configs.config import Config
from flask import Flask
from flask_bootstrap import Bootstrap
from flask_login import LoginManager
@ -7,7 +8,6 @@ from logging import getLogger
from logging.config import fileConfig
from os import makedirs
from os.path import dirname, exists
from yaml import safe_load
try:
dir_name = dirname(__file__)
@ -18,10 +18,10 @@ try:
except NameError:
DIR = "./"
if not exists(DIR + "logs"):
makedirs(DIR + "logs")
if not exists(DIR + "../logs"):
makedirs(DIR + "../logs")
fileConfig(DIR + "configs/log.conf")
fileConfig(DIR + "../configs/log.conf")
LOGGER = getLogger("root")
bootstrap = Bootstrap()
@ -31,9 +31,9 @@ login.login_view = 'web_login'
migrate = Migrate()
def create_app(config_file="configs/config.yaml"):
def create_app(config_class=Config):
app = Flask(__name__)
app.config.from_file(config_file, safe_load)
app.config.from_object(config_class)
bootstrap.init_app(app)
db.init_app(app)
login.init_app(app)

View File

@ -13,7 +13,7 @@ def web_register():
return redirect(url_for('main.index'))
form = RegistrationForm()
if form.validate_on_submit():
user = User(username=form.username.data, email=form.email.data)
user = User(email=form.email.data)
user.set_password(form.password.data)
db.session.add(user)
db.session.commit()
@ -27,9 +27,9 @@ def web_login():
return redirect(url_for('main.index'))
form = LoginForm()
if form.validate_on_submit():
user = User.query.filter_by(username=form.username.data).first()
user = User.query.filter_by(email=form.email.data).first()
if user is None or not user.check_password(form.password.data):
flash('Invalid username or password')
flash('Invalid email or password')
return redirect(url_for('auth.web_login'))
login_user(user, remember=form.remember_me.data)
next_page = request.args.get('next')

View File

@ -1,3 +0,0 @@
SECRET_KEY: "MY_5€cr37_K€Y"
SQLALCHEMY_DATABASE_URI: "dialect+driver://username:password@host:port/database"
SQLALCHEMY_TRACK_MODIFICATIONS: False

View File

@ -44,9 +44,9 @@ def get_report_from_user():
return jsonify(result_list)
else:
if "establishment" in request.args:
return render_template("overview.html", results=result_list, establishment = Establishment.query.get(int(request.args['establishment'])))
return render_template("main/overview.html", results=result_list, establishment = Establishment.query.get(int(request.args['establishment'])))
else:
return render_template("overview.html", results=result_list)
return render_template("main/overview.html", results=result_list)
@bp.route('/token_authorization')
def token_authorization():
@ -95,7 +95,7 @@ def new_item():
db.session.add(new_item)
db.session.commit()
return redirect(url_for('index'))
return render_template('admin/new_item.html', form=form)
return render_template('main/new_item.html', form=form)
@bp.route('/overview/register_boughts', methods=['GET'])
@login_required
@ -113,4 +113,4 @@ def check_unregistered_items():
if request.content_type == "application/json":
return jsonify(result_list)
else:
return render_template("overview.html", results=result_list)
return render_template("main/overview.html", results=result_list)

View File

@ -24,7 +24,7 @@ class User(UserMixin, db.Model):
return check_password_hash(self.password_hash, password)
def __repr__(self) -> str:
return f"<User {self.id} ({self.username})>"
return f"<User {self.id} ({self.email})>"
class Establishment(db.Model):
id = db.Column(db.BigInteger, primary_key=True)
@ -64,7 +64,7 @@ class Category(db.Model):
return f"<Category {self.id} ({self.name})>"
class Item(db.Model):
id = db.Column(db.BigInteger, primary_key=True)
id = db.Column(db.BigInteger, primary_key=True, autoincrement=False)
name = db.Column(db.String(64), nullable=False)
brand = db.Column(db.ForeignKey('brand.id'), nullable=False, server_onupdate=db.FetchedValue())
description = db.Column(db.Text, nullable=False)
@ -104,7 +104,7 @@ class AmountChange(db.Model):
return f"<Amount_Change {self.item} ({self.date})>"
class Receipt(db.Model):
id = db.Column(db.Numeric(precision=22, scale=0), primary_key=True)
id = db.Column(db.Numeric(precision=24, scale=0), primary_key=True, autoincrement=False)
date = db.Column(db.Date, nullable=False)
from_user = db.Column(db.ForeignKey("login_token.token"), server_onupdate=db.FetchedValue())
registered = db.Column(db.Boolean, nullable=False, server_default=str(False))

View File

@ -3,7 +3,7 @@
{% block app_content %}
{% if establishment %}
{% if current_user.id == establishment.owner %}
<button type="button" class="btn btn-outline-dark px-2" data-bs-toggle="button" autocomplete="off" onclick="window.location.href='{{ url_for('check_unregistered_items', establishment=establishment.id) }}'">
<button type="button" class="btn btn-outline-dark px-2" data-bs-toggle="button" autocomplete="off" onclick="window.location.href='{{ url_for('main.check_unregistered_items', establishment=establishment.id) }}'">
Abrechnung
</button>
{% endif %}
@ -12,7 +12,7 @@
<div class="card">
<button class="btn btn-primary" data-bs-toggle="collapse" data-bs-target="#b{{ user.id }}" aria-expanded="true">
<div class="card-header">
<h3>{{ user.username }}: {{ user.sum/100 }} €</h3>
<h3>{{ user.email }}: {{ user.sum/100 }} €</h3>
</div>
</button>
<div class="collapse" id="b{{ user.id }}">

View File

@ -29,7 +29,7 @@ def insert_bought_items(token: str, items: dict, date: str = None):
return {'user':token, 'date': date, 'items': items} if items else {}
def get_report(**kwargs):
query_select = db.session.query(bwp.c.token, User.username, bwp.c.date, bwp.c.item, Item.name, bwp.c.amount, bwp.c.price)
query_select = db.session.query(bwp.c.token, User.email, bwp.c.date, bwp.c.item, Item.name, bwp.c.amount, bwp.c.price)
query_select = query_select.select_from(bwp).join(LoginToken, LoginToken.token==bwp.c.token).join(User, LoginToken.user==User.id).join(Item, Item.id==bwp.c.item)
match kwargs:
case {"token": token}:
@ -62,7 +62,7 @@ def get_unregistered_and_register(intEstablishment: int):
if current_user.id != establishment.owner:
LOGGER.debug("!!!Wrong User!!!")
return False
query_select = db.session.query(bwp.c.token, User.username, bwp.c.date, bwp.c.item, Item.name, bwp.c.amount, bwp.c.price)
query_select = db.session.query(bwp.c.token, User.email, bwp.c.date, bwp.c.item, Item.name, bwp.c.amount, bwp.c.price)
query_select = query_select.select_from(bwp).join(LoginToken, LoginToken.token==bwp.c.token).join(User, LoginToken.user==User.id)
query_select = query_select.join(Item, Item.id==bwp.c.item).join(Bought, and_(Bought.token==bwp.c.token, Bought.item==bwp.c.item, Bought.date==bwp.c.date))
query_select = query_select.filter(bwp.c.token.in_(db.session.query(LoginToken.token).filter_by(establishment = intEstablishment)))

View File

@ -9,7 +9,7 @@ def group_results(results: tuple) -> list:
try:
result_user_index = [result[0] == result_item['id'] for result_item in result_list].index(True)
except ValueError as e:
result_list.append({"id": result[0], "username": result[1], "sum": 0, "item_infos": []})
result_list.append({"id": result[0], "email": result[1], "sum": 0, "item_infos": []})
result_user_index = -1
result_user = result_list[result_user_index]
try:

12
boot.sh
View File

@ -1,5 +1,13 @@
#!/bin/bash
source venv/bin/activate
while true; do
flask db upgrade 05fce74b56cb
if [[ "$?" == "0" ]]; then
break
fi
echo Deploy command failed, retrying in 5 secs...
sleep 5
done
flask db upgrade 2be4d1ae5493
flask db upgrade
flask translate compile
python run.py
exec gunicorn -b :5000 --access-logfile - --error-logfile - run:app

15
configs/config.py Normal file
View File

@ -0,0 +1,15 @@
import os
from dotenv import load_dotenv
basedir = os.path.abspath(os.path.dirname(__file__))
load_dotenv(os.path.join(basedir, '.env'))
class Config(object):
SECRET_KEY = os.environ.get('SECRET_KEY') or "MY_5€cr37_K€Y"
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL', '').replace(
'postgres://', 'postgresql://') or \
(f"postgresql://{os.environ.get('DATABASE_USER', 'scan2kasse')}:{os.environ.get('DATABASE_PASS', 'asdf1337')}"
f"@{os.environ.get('DATABASE_HOST', 'localhost')}:{os.environ.get('DATABASE_PORT', '5432')}"
f"/{os.environ.get('DATABASE_DB', '') or os.environ.get('DATABASE_USER', 'scan2kasse')}")
SQLALCHEMY_TRACK_MODIFICATIONS = False

View File

@ -25,7 +25,7 @@ formatter = stdout
class = logging.FileHandler
level = INFO
formatter = stdout
kwargs = {"filename": "app/logs/infos.log"}
kwargs = {"filename": "logs/infos.log"}
[formatter_stdout]
format = %(asctime)s [%(threadName)s] [%(levelname)s]: %(message)s

View File

@ -54,7 +54,7 @@ def upgrade():
sa.UniqueConstraint('token')
)
op.create_table('receipt',
sa.Column('id', sa.Numeric(precision=22, scale=0), nullable=False),
sa.Column('id', sa.Numeric(precision=24, scale=0), autoincrement=False, nullable=False),
sa.Column('date', sa.Date(), nullable=False),
sa.Column('from_user', sa.String(length=15), nullable=True),
sa.Column('registered', sa.Boolean(), server_default='False', nullable=False),
@ -62,7 +62,7 @@ def upgrade():
sa.PrimaryKeyConstraint('id')
)
op.create_table('item',
sa.Column('id', sa.BigInteger(), nullable=False),
sa.Column('id', sa.BigInteger(), autoincrement=False, nullable=False),
sa.Column('name', sa.String(length=64), nullable=False),
sa.Column('brand', sa.Integer(), nullable=False),
sa.Column('description', sa.Text(), nullable=False),
@ -94,7 +94,7 @@ def upgrade():
sa.PrimaryKeyConstraint('item', 'category')
)
op.create_table('item_receipt',
sa.Column('receipt', sa.Numeric(precision=22, scale=0), nullable=False),
sa.Column('receipt', sa.Numeric(precision=24, scale=0), nullable=False),
sa.Column('item', sa.BigInteger(), nullable=False),
sa.Column('amount', sa.SmallInteger(), nullable=False),
sa.ForeignKeyConstraint(['item'], ['item.id'], ),

Binary file not shown.

5
run.py
View File

@ -1,6 +1,5 @@
from app import create_app, db
from app.models import *
from gevent.pywsgi import WSGIServer
app = create_app()
@ -8,7 +7,3 @@ app = create_app()
def make_shell_context():
return {'db': db, 'User': User, 'Bought': Bought, 'Item': Item,
"LoginToken": LoginToken, "Establishment": Establishment, "Receipt": Receipt}
if __name__ == '__main__':
http_server = WSGIServer(('', 5000), app)
http_server.serve_forever()