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
|
RUN useradd scan2kasse
|
||||||
|
|
||||||
WORKDIR /home/scan2kasse
|
WORKDIR /home/scan2kasse
|
||||||
|
|
||||||
|
RUN apt update && apt upgrade
|
||||||
|
RUN apt install -y libpq-dev gcc g++
|
||||||
|
|
||||||
COPY requirements.txt requirements.txt
|
COPY requirements.txt requirements.txt
|
||||||
RUN python -m venv venv
|
RUN python -m venv venv
|
||||||
RUN venv/bin/pip install -r requirements.txt
|
RUN venv/bin/pip install -r requirements.txt
|
||||||
|
RUN venv/bin/pip install gunicorn
|
||||||
|
|
||||||
COPY app app
|
COPY app app
|
||||||
COPY migrations migrations
|
COPY migrations migrations
|
||||||
|
COPY configs configs
|
||||||
COPY run.py boot.sh ./
|
COPY run.py boot.sh ./
|
||||||
RUN chmod +x boot.sh
|
RUN chmod +x boot.sh
|
||||||
|
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
from configs.config import Config
|
||||||
from flask import Flask
|
from flask import Flask
|
||||||
from flask_bootstrap import Bootstrap
|
from flask_bootstrap import Bootstrap
|
||||||
from flask_login import LoginManager
|
from flask_login import LoginManager
|
||||||
@ -7,7 +8,6 @@ from logging import getLogger
|
|||||||
from logging.config import fileConfig
|
from logging.config import fileConfig
|
||||||
from os import makedirs
|
from os import makedirs
|
||||||
from os.path import dirname, exists
|
from os.path import dirname, exists
|
||||||
from yaml import safe_load
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
dir_name = dirname(__file__)
|
dir_name = dirname(__file__)
|
||||||
@ -18,10 +18,10 @@ try:
|
|||||||
except NameError:
|
except NameError:
|
||||||
DIR = "./"
|
DIR = "./"
|
||||||
|
|
||||||
if not exists(DIR + "logs"):
|
if not exists(DIR + "../logs"):
|
||||||
makedirs(DIR + "logs")
|
makedirs(DIR + "../logs")
|
||||||
|
|
||||||
fileConfig(DIR + "configs/log.conf")
|
fileConfig(DIR + "../configs/log.conf")
|
||||||
LOGGER = getLogger("root")
|
LOGGER = getLogger("root")
|
||||||
|
|
||||||
bootstrap = Bootstrap()
|
bootstrap = Bootstrap()
|
||||||
@ -31,9 +31,9 @@ login.login_view = 'web_login'
|
|||||||
migrate = Migrate()
|
migrate = Migrate()
|
||||||
|
|
||||||
|
|
||||||
def create_app(config_file="configs/config.yaml"):
|
def create_app(config_class=Config):
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
app.config.from_file(config_file, safe_load)
|
app.config.from_object(config_class)
|
||||||
bootstrap.init_app(app)
|
bootstrap.init_app(app)
|
||||||
db.init_app(app)
|
db.init_app(app)
|
||||||
login.init_app(app)
|
login.init_app(app)
|
||||||
|
|||||||
@ -13,7 +13,7 @@ def web_register():
|
|||||||
return redirect(url_for('main.index'))
|
return redirect(url_for('main.index'))
|
||||||
form = RegistrationForm()
|
form = RegistrationForm()
|
||||||
if form.validate_on_submit():
|
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)
|
user.set_password(form.password.data)
|
||||||
db.session.add(user)
|
db.session.add(user)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
@ -27,9 +27,9 @@ def web_login():
|
|||||||
return redirect(url_for('main.index'))
|
return redirect(url_for('main.index'))
|
||||||
form = LoginForm()
|
form = LoginForm()
|
||||||
if form.validate_on_submit():
|
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):
|
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'))
|
return redirect(url_for('auth.web_login'))
|
||||||
login_user(user, remember=form.remember_me.data)
|
login_user(user, remember=form.remember_me.data)
|
||||||
next_page = request.args.get('next')
|
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)
|
return jsonify(result_list)
|
||||||
else:
|
else:
|
||||||
if "establishment" in request.args:
|
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:
|
else:
|
||||||
return render_template("overview.html", results=result_list)
|
return render_template("main/overview.html", results=result_list)
|
||||||
|
|
||||||
@bp.route('/token_authorization')
|
@bp.route('/token_authorization')
|
||||||
def token_authorization():
|
def token_authorization():
|
||||||
@ -95,7 +95,7 @@ def new_item():
|
|||||||
db.session.add(new_item)
|
db.session.add(new_item)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
return redirect(url_for('index'))
|
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'])
|
@bp.route('/overview/register_boughts', methods=['GET'])
|
||||||
@login_required
|
@login_required
|
||||||
@ -113,4 +113,4 @@ def check_unregistered_items():
|
|||||||
if request.content_type == "application/json":
|
if request.content_type == "application/json":
|
||||||
return jsonify(result_list)
|
return jsonify(result_list)
|
||||||
else:
|
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)
|
return check_password_hash(self.password_hash, password)
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return f"<User {self.id} ({self.username})>"
|
return f"<User {self.id} ({self.email})>"
|
||||||
|
|
||||||
class Establishment(db.Model):
|
class Establishment(db.Model):
|
||||||
id = db.Column(db.BigInteger, primary_key=True)
|
id = db.Column(db.BigInteger, primary_key=True)
|
||||||
@ -64,7 +64,7 @@ class Category(db.Model):
|
|||||||
return f"<Category {self.id} ({self.name})>"
|
return f"<Category {self.id} ({self.name})>"
|
||||||
|
|
||||||
class Item(db.Model):
|
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)
|
name = db.Column(db.String(64), nullable=False)
|
||||||
brand = db.Column(db.ForeignKey('brand.id'), nullable=False, server_onupdate=db.FetchedValue())
|
brand = db.Column(db.ForeignKey('brand.id'), nullable=False, server_onupdate=db.FetchedValue())
|
||||||
description = db.Column(db.Text, nullable=False)
|
description = db.Column(db.Text, nullable=False)
|
||||||
@ -104,7 +104,7 @@ class AmountChange(db.Model):
|
|||||||
return f"<Amount_Change {self.item} ({self.date})>"
|
return f"<Amount_Change {self.item} ({self.date})>"
|
||||||
|
|
||||||
class Receipt(db.Model):
|
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)
|
date = db.Column(db.Date, nullable=False)
|
||||||
from_user = db.Column(db.ForeignKey("login_token.token"), server_onupdate=db.FetchedValue())
|
from_user = db.Column(db.ForeignKey("login_token.token"), server_onupdate=db.FetchedValue())
|
||||||
registered = db.Column(db.Boolean, nullable=False, server_default=str(False))
|
registered = db.Column(db.Boolean, nullable=False, server_default=str(False))
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
{% block app_content %}
|
{% block app_content %}
|
||||||
{% if establishment %}
|
{% if establishment %}
|
||||||
{% if current_user.id == establishment.owner %}
|
{% 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
|
Abrechnung
|
||||||
</button>
|
</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -12,7 +12,7 @@
|
|||||||
<div class="card">
|
<div class="card">
|
||||||
<button class="btn btn-primary" data-bs-toggle="collapse" data-bs-target="#b{{ user.id }}" aria-expanded="true">
|
<button class="btn btn-primary" data-bs-toggle="collapse" data-bs-target="#b{{ user.id }}" aria-expanded="true">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<h3>{{ user.username }}: {{ user.sum/100 }} €</h3>
|
<h3>{{ user.email }}: {{ user.sum/100 }} €</h3>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
<div class="collapse" id="b{{ user.id }}">
|
<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 {}
|
return {'user':token, 'date': date, 'items': items} if items else {}
|
||||||
|
|
||||||
def get_report(**kwargs):
|
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)
|
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:
|
match kwargs:
|
||||||
case {"token": token}:
|
case {"token": token}:
|
||||||
@ -62,7 +62,7 @@ def get_unregistered_and_register(intEstablishment: int):
|
|||||||
if current_user.id != establishment.owner:
|
if current_user.id != establishment.owner:
|
||||||
LOGGER.debug("!!!Wrong User!!!")
|
LOGGER.debug("!!!Wrong User!!!")
|
||||||
return False
|
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.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.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)))
|
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:
|
try:
|
||||||
result_user_index = [result[0] == result_item['id'] for result_item in result_list].index(True)
|
result_user_index = [result[0] == result_item['id'] for result_item in result_list].index(True)
|
||||||
except ValueError as e:
|
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_index = -1
|
||||||
result_user = result_list[result_user_index]
|
result_user = result_list[result_user_index]
|
||||||
try:
|
try:
|
||||||
|
|||||||
12
boot.sh
12
boot.sh
@ -1,5 +1,13 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
source venv/bin/activate
|
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 db upgrade
|
||||||
flask translate compile
|
exec gunicorn -b :5000 --access-logfile - --error-logfile - run:app
|
||||||
python run.py
|
|
||||||
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
|
class = logging.FileHandler
|
||||||
level = INFO
|
level = INFO
|
||||||
formatter = stdout
|
formatter = stdout
|
||||||
kwargs = {"filename": "app/logs/infos.log"}
|
kwargs = {"filename": "logs/infos.log"}
|
||||||
|
|
||||||
[formatter_stdout]
|
[formatter_stdout]
|
||||||
format = %(asctime)s [%(threadName)s] [%(levelname)s]: %(message)s
|
format = %(asctime)s [%(threadName)s] [%(levelname)s]: %(message)s
|
||||||
@ -54,7 +54,7 @@ def upgrade():
|
|||||||
sa.UniqueConstraint('token')
|
sa.UniqueConstraint('token')
|
||||||
)
|
)
|
||||||
op.create_table('receipt',
|
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('date', sa.Date(), nullable=False),
|
||||||
sa.Column('from_user', sa.String(length=15), nullable=True),
|
sa.Column('from_user', sa.String(length=15), nullable=True),
|
||||||
sa.Column('registered', sa.Boolean(), server_default='False', nullable=False),
|
sa.Column('registered', sa.Boolean(), server_default='False', nullable=False),
|
||||||
@ -62,7 +62,7 @@ def upgrade():
|
|||||||
sa.PrimaryKeyConstraint('id')
|
sa.PrimaryKeyConstraint('id')
|
||||||
)
|
)
|
||||||
op.create_table('item',
|
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('name', sa.String(length=64), nullable=False),
|
||||||
sa.Column('brand', sa.Integer(), nullable=False),
|
sa.Column('brand', sa.Integer(), nullable=False),
|
||||||
sa.Column('description', sa.Text(), nullable=False),
|
sa.Column('description', sa.Text(), nullable=False),
|
||||||
@ -94,7 +94,7 @@ def upgrade():
|
|||||||
sa.PrimaryKeyConstraint('item', 'category')
|
sa.PrimaryKeyConstraint('item', 'category')
|
||||||
)
|
)
|
||||||
op.create_table('item_receipt',
|
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('item', sa.BigInteger(), nullable=False),
|
||||||
sa.Column('amount', sa.SmallInteger(), nullable=False),
|
sa.Column('amount', sa.SmallInteger(), nullable=False),
|
||||||
sa.ForeignKeyConstraint(['item'], ['item.id'], ),
|
sa.ForeignKeyConstraint(['item'], ['item.id'], ),
|
||||||
|
|||||||
BIN
requirements.txt
BIN
requirements.txt
Binary file not shown.
5
run.py
5
run.py
@ -1,6 +1,5 @@
|
|||||||
from app import create_app, db
|
from app import create_app, db
|
||||||
from app.models import *
|
from app.models import *
|
||||||
from gevent.pywsgi import WSGIServer
|
|
||||||
|
|
||||||
app = create_app()
|
app = create_app()
|
||||||
|
|
||||||
@ -8,7 +7,3 @@ app = create_app()
|
|||||||
def make_shell_context():
|
def make_shell_context():
|
||||||
return {'db': db, 'User': User, 'Bought': Bought, 'Item': Item,
|
return {'db': db, 'User': User, 'Bought': Bought, 'Item': Item,
|
||||||
"LoginToken": LoginToken, "Establishment": Establishment, "Receipt": Receipt}
|
"LoginToken": LoginToken, "Establishment": Establishment, "Receipt": Receipt}
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
http_server = WSGIServer(('', 5000), app)
|
|
||||||
http_server.serve_forever()
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user