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:
parent
b978e0da56
commit
4459000d4f
@ -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
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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')
|
||||
|
||||
@ -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
|
||||
@ -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)
|
||||
@ -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))
|
||||
|
||||
@ -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 }}">
|
||||
|
||||
@ -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)))
|
||||
|
||||
@ -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
12
boot.sh
@ -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
15
configs/config.py
Normal 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
|
||||
@ -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
|
||||
@ -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'], ),
|
||||
|
||||
BIN
requirements.txt
BIN
requirements.txt
Binary file not shown.
7
run.py
7
run.py
@ -1,14 +1,9 @@
|
||||
from app import create_app, db
|
||||
from app.models import *
|
||||
from gevent.pywsgi import WSGIServer
|
||||
|
||||
app = create_app()
|
||||
|
||||
@app.shell_context_processor
|
||||
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()
|
||||
"LoginToken": LoginToken, "Establishment": Establishment, "Receipt": Receipt}
|
||||
Loading…
x
Reference in New Issue
Block a user