1
0
Fork 0
mirror of https://codeberg.org/Mo8it/AdvLabDB.git synced 2024-12-24 23:51:03 +00:00

Use app factory pattern

This commit is contained in:
Mo 2022-08-09 14:46:48 +02:00
parent e1e9c0e42e
commit bdabc9f32a
15 changed files with 179 additions and 132 deletions

View file

@ -1,47 +1,51 @@
from os import environ
from flask import Flask
from flask_admin import Admin
from flask_security import Security, SQLAlchemyUserDatastore
from flask_security.models import fsqla_v2 as fsqla
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate from flask_migrate import Migrate
from flask_security import SQLAlchemyUserDatastore
from .config import set_config from .config import get_settings
from .models import db, User, Role
app = Flask(__name__) migrate = Migrate()
# Config settings = get_settings()
settings = set_config(app)
# Setup Flask-SQLAlchemy user_datastore = SQLAlchemyUserDatastore(db, User, Role)
db = SQLAlchemy(app)
fsqla.FsModels.set_db_info(db)
# Setup Flask-Migrate
migrate = Migrate(app, db)
# Setup Flask-Admin def create_app(create_for_server=True):
from .custom_classes import SecureAdminIndexView, SecureAssistantIndexView from flask import Flask
adminSpace = Admin( app = Flask(__name__)
app,
name="Admin@AdvLabDB",
url="/admin",
template_mode="bootstrap3",
index_view=SecureAdminIndexView(name="Home", url="/admin", endpoint="admin"),
)
assistantSpace = Admin(
app,
name="Assistant@AdvLabDB",
url="/assistant",
template_mode="bootstrap3",
index_view=SecureAssistantIndexView(name="Home", url="/assistant", endpoint="assistant"),
)
from . import models # Config
from .config import set_config
user_datastore = SQLAlchemyUserDatastore(db, models.User, models.Role) set_config(app)
Security(app, user_datastore)
from . import routes, adminModelViews, assistantModelViews # Setup Flask-SQLAlchemy
db.init_app(app)
# Setup Flask-Migrate
migrate.init_app(app, db)
# Setup Flask-Security-Too
from flask_security import Security
Security(app, user_datastore)
if create_for_server:
# Setup views
from .adminModelViews import init_admin_model_views
init_admin_model_views(app)
from .assistantModelViews import init_assistant_model_views
init_assistant_model_views(app)
# Register blueprints
from .routes import bp as routes_bp
app.register_blueprint(routes_bp)
return app

View file

@ -4,6 +4,7 @@ from pathlib import Path
import numpy as np import numpy as np
from flask import flash, has_request_context, redirect, url_for from flask import flash, has_request_context, redirect, url_for
from flask_admin import Admin as FlaskAdmin
from flask_admin import expose from flask_admin import expose
from flask_admin.contrib.sqla.fields import QuerySelectField, QuerySelectMultipleField from flask_admin.contrib.sqla.fields import QuerySelectField, QuerySelectMultipleField
from flask_admin.contrib.sqla.filters import BooleanEqualFilter, FilterEqual from flask_admin.contrib.sqla.filters import BooleanEqualFilter, FilterEqual
@ -28,7 +29,7 @@ from wtforms.fields import (
from wtforms.validators import URL, DataRequired, Email, NumberRange, Optional from wtforms.validators import URL, DataRequired, Email, NumberRange, Optional
from wtforms.widgets import NumberInput from wtforms.widgets import NumberInput
from . import adminSpace, assistantSpace, db, user_datastore from . import user_datastore
from .admin_link_formatters import ( from .admin_link_formatters import (
admin_formatter, admin_formatter,
appointment_date_formatter, appointment_date_formatter,
@ -56,7 +57,12 @@ from .advlabdb_independent_funs import (
flashRandomPassword, flashRandomPassword,
str_without_semester_formatter, str_without_semester_formatter,
) )
from .custom_classes import SecureAdminBaseView, SecureAdminModelView from .assistantModelViews import assistantSpace
from .custom_classes import (
SecureAdminBaseView,
SecureAdminIndexView,
SecureAdminModelView,
)
from .database_import import importFromFile from .database_import import importFromFile
from .exceptions import ModelViewException from .exceptions import ModelViewException
from .model_dependent_funs import ( from .model_dependent_funs import (
@ -86,6 +92,15 @@ from .models import (
SemesterExperiment, SemesterExperiment,
Student, Student,
User, User,
db,
)
adminSpace = FlaskAdmin(
name="Admin@AdvLabDB",
url="/admin",
template_mode="bootstrap4",
static_url_path="/static/a",
index_view=SecureAdminIndexView(name="Home", url="/admin", endpoint="admin"),
) )
@ -356,7 +371,7 @@ class SemesterView(SecureAdminModelView):
categoryText = "Active semester" categoryText = "Active semester"
link = MenuLink( link = MenuLink(
name=str(newSemester), name=str(newSemester),
url=url_for("set_semester") + "?semester_id=" + str(newSemester.id), url=url_for("main.set_semester") + "?semester_id=" + str(newSemester.id),
category=categoryText, category=categoryText,
) )
@ -1282,7 +1297,7 @@ class ImportView(SecureAdminBaseView):
except Exception as ex: except Exception as ex:
flash(str(ex), "error") flash(str(ex), "error")
return redirect(url_for("index")) return redirect(url_for("main.index"))
return self.render("import.html", form=form) return self.render("import.html", form=form)
@ -1304,7 +1319,7 @@ class ActionsView(SecureAdminBaseView):
flash("Manually updated all final experiment and part marks", "success") flash("Manually updated all final experiment and part marks", "success")
return redirect(url_for("index")) return redirect(url_for("main.index"))
return self.render("actions.html", form=form) return self.render("actions.html", form=form)
@ -1469,23 +1484,26 @@ class DocsView(SecureAdminBaseView):
return self.render("docs/docs.html", role="admin") return self.render("docs/docs.html", role="admin")
adminSpace.add_view(StudentView(Student, url="student")) def init_admin_model_views(app):
adminSpace.add_view(PartStudentView(PartStudent, url="part_student")) adminSpace.init_app(app)
adminSpace.add_view(GroupView(Group, url="group"))
adminSpace.add_view(GroupExperimentView(GroupExperiment, url="group_experiment"))
adminSpace.add_view(AppointmentView(Appointment, url="appointment"))
adminSpace.add_view(ExperimentMarkView(ExperimentMark, url="experiment_mark"))
adminSpace.add_view(ExperimentView(Experiment, url="experiment"))
adminSpace.add_view(SemesterExperimentView(SemesterExperiment, url="semester_experiment"))
adminSpace.add_view(SemesterView(Semester, url="semester"))
adminSpace.add_view(PartView(Part, url="part"))
adminSpace.add_view(AssistantView(Assistant, url="assistant"))
adminSpace.add_view(AdminView(Admin, url="admin"))
adminSpace.add_view(UserView(User, url="user"))
adminSpace.add_view(ProgramView(Program, url="program"))
adminSpace.add_view(ImportView(name="Import", url="import"))
adminSpace.add_view(ActionsView(name="Actions", url="actions"))
adminSpace.add_view(AnalysisView(name="Analysis", url="analysis"))
adminSpace.add_view(DocsView(name="Docs", url="docs"))
initActiveSemesterMenuLinks(adminSpace) adminSpace.add_view(StudentView(Student, url="student"))
adminSpace.add_view(PartStudentView(PartStudent, url="part_student"))
adminSpace.add_view(GroupView(Group, url="group"))
adminSpace.add_view(GroupExperimentView(GroupExperiment, url="group_experiment"))
adminSpace.add_view(AppointmentView(Appointment, url="appointment"))
adminSpace.add_view(ExperimentMarkView(ExperimentMark, url="experiment_mark"))
adminSpace.add_view(ExperimentView(Experiment, url="experiment"))
adminSpace.add_view(SemesterExperimentView(SemesterExperiment, url="semester_experiment"))
adminSpace.add_view(SemesterView(Semester, url="semester"))
adminSpace.add_view(PartView(Part, url="part"))
adminSpace.add_view(AssistantView(Assistant, url="assistant"))
adminSpace.add_view(AdminView(Admin, url="admin"))
adminSpace.add_view(UserView(User, url="user"))
adminSpace.add_view(ProgramView(Program, url="program"))
adminSpace.add_view(ImportView(name="Import", url="import"))
adminSpace.add_view(ActionsView(name="Actions", url="actions"))
adminSpace.add_view(AnalysisView(name="Analysis", url="analysis"))
adminSpace.add_view(DocsView(name="Docs", url="docs"))
initActiveSemesterMenuLinks(adminSpace, app)

View file

@ -1,17 +1,21 @@
from flask import flash, redirect, request, url_for from flask import flash, redirect, request, url_for
from flask_admin import Admin as FlaskAdmin
from flask_admin import expose from flask_admin import expose
from flask_admin.model.template import EndpointLinkRowAction from flask_admin.model.template import EndpointLinkRowAction
from flask_security import admin_change_password, current_user from flask_security import admin_change_password, current_user
from flask_wtf import FlaskForm from flask_wtf import FlaskForm
from . import assistantSpace, db
from .advlabdb_independent_funs import ( from .advlabdb_independent_funs import (
deep_getattr, deep_getattr,
experiment_marks_missing_formatter, experiment_marks_missing_formatter,
flashRandomPassword, flashRandomPassword,
str_formatter, str_formatter,
) )
from .custom_classes import SecureAssistantBaseView, SecureAssistantModelView from .custom_classes import (
SecureAssistantBaseView,
SecureAssistantIndexView,
SecureAssistantModelView,
)
from .exceptions import ModelViewException from .exceptions import ModelViewException
from .forms import assistant_group_experiment_form_factory from .forms import assistant_group_experiment_form_factory
from .model_dependent_funs import ( from .model_dependent_funs import (
@ -21,7 +25,15 @@ from .model_dependent_funs import (
user_info_fields, user_info_fields,
) )
from .model_independent_funs import randomPassword, reportBadAttempt from .model_independent_funs import randomPassword, reportBadAttempt
from .models import Assistant, GroupExperiment, SemesterExperiment, User from .models import Assistant, GroupExperiment, SemesterExperiment, User, db
assistantSpace = FlaskAdmin(
name="Assistant@AdvLabDB",
url="/assistant",
template_mode="bootstrap4",
static_url_path="/static/a",
index_view=SecureAssistantIndexView(name="Home", url="/assistant", endpoint="assistant"),
)
class AssistantGroupExperimentView(SecureAssistantModelView): class AssistantGroupExperimentView(SecureAssistantModelView):
@ -89,7 +101,7 @@ class AssistantGroupExperimentView(SecureAssistantModelView):
try: try:
group_experiment = db.session.get(GroupExperiment, int(group_experiment_id_str)) group_experiment = db.session.get(GroupExperiment, int(group_experiment_id_str))
except Exception: except Exception:
red = url_for("index") + "assistant/group_experiment" red = url_for("main.index") + "assistant/group_experiment"
flash("No valid group experiment id") flash("No valid group experiment id")
return redirect(red) return redirect(red)
@ -146,7 +158,7 @@ class AssistantGroupExperimentView(SecureAssistantModelView):
db.session.commit() db.session.commit()
red = url_for("index") + "assistant/group_experiment" red = url_for("main.index") + "assistant/group_experiment"
return redirect(red) return redirect(red)
except Exception as ex: except Exception as ex:
flash(str(ex), "error") flash(str(ex), "error")
@ -211,8 +223,11 @@ class AssistantDocsView(SecureAssistantBaseView):
return self.render("docs/docs.html", role="assistant") return self.render("docs/docs.html", role="assistant")
assistantSpace.add_view(AssistantGroupExperimentView(GroupExperiment, url="group_experiment")) def init_assistant_model_views(app):
assistantSpace.add_view(AssistantUserView(User, url="user")) assistantSpace.init_app(app)
assistantSpace.add_view(AssistantDocsView(name="Docs", url="docs"))
initActiveSemesterMenuLinks(assistantSpace) assistantSpace.add_view(AssistantGroupExperimentView(GroupExperiment, url="group_experiment"))
assistantSpace.add_view(AssistantUserView(User, url="user"))
assistantSpace.add_view(AssistantDocsView(name="Docs", url="docs"))
initActiveSemesterMenuLinks(assistantSpace, app)

View file

@ -3,14 +3,14 @@ from configparser import ConfigParser
from pathlib import Path from pathlib import Path
def load_config(app, *files): def load_config(*files):
config = ConfigParser() config = ConfigParser()
for file in files: for file in files:
file = Path(file) file = Path(file)
if not file.is_file(): if not file.is_file():
app.logger.critical(str(file) + " is missing") print(f"{file} is missing!")
sys.exit(1) sys.exit(1)
config.read(file) config.read(file)
@ -18,8 +18,15 @@ def load_config(app, *files):
return config return config
def get_settings():
config = load_config("settings.ini")
settings = config["Settings"]
return settings
def set_config(app): def set_config(app):
config = load_config(app, "secrets.ini", "settings.ini") config = load_config("secrets.ini", "settings.ini")
secrets = config["Secrets"] secrets = config["Secrets"]
settings = config["Settings"] settings = config["Settings"]
@ -55,5 +62,3 @@ def set_config(app):
app.config["SECURITY_PASSWORD_SALT"] = secrets["SECURITY_PASSWORD_SALT"] app.config["SECURITY_PASSWORD_SALT"] = secrets["SECURITY_PASSWORD_SALT"]
app.config["SECURITY_PASSWORD_LENGTH_MIN"] = settings.getint("SECURITY_PASSWORD_LENGTH_MIN", 15) app.config["SECURITY_PASSWORD_LENGTH_MIN"] = settings.getint("SECURITY_PASSWORD_LENGTH_MIN", 15)
# TODO: app.config["SECURITY_LOGIN_USER_TEMPLATE"] = # TODO: app.config["SECURITY_LOGIN_USER_TEMPLATE"] =
return settings

View file

@ -6,9 +6,8 @@ from flask_admin.model.helpers import get_mdict_item_or_list
from flask_security import current_user from flask_security import current_user
from sqlalchemy import select from sqlalchemy import select
from . import db
from .exceptions import DataBaseException, ModelViewException from .exceptions import DataBaseException, ModelViewException
from .model_independent_funs import get_count, reportBadAttempt from .model_independent_funs import reportBadAttempt
from .models import ( from .models import (
Assistant, Assistant,
ExperimentMark, ExperimentMark,
@ -16,6 +15,8 @@ from .models import (
Part, Part,
PartStudent, PartStudent,
SemesterExperiment, SemesterExperiment,
db,
get_count,
) )

View file

@ -5,9 +5,8 @@ from shutil import copy2
from flask import flash, has_request_context from flask import flash, has_request_context
from sqlalchemy import select from sqlalchemy import select
from . import db, settings from . import settings
from .exceptions import DataBaseImportException from .exceptions import DataBaseImportException
from .model_independent_funs import get_first
from .models import ( from .models import (
Appointment, Appointment,
Assistant, Assistant,
@ -21,6 +20,8 @@ from .models import (
SemesterExperiment, SemesterExperiment,
Student, Student,
User, User,
db,
get_first,
) )

View file

@ -10,11 +10,11 @@ from flask_security import current_user
from wtforms.fields import BooleanField, IntegerField, SelectField, StringField from wtforms.fields import BooleanField, IntegerField, SelectField, StringField
from wtforms.validators import DataRequired, NumberRange, Optional from wtforms.validators import DataRequired, NumberRange, Optional
from . import app, settings from . import settings
from .models import MAX_MARK, MIN_MARK, Semester from .models import MAX_MARK, MIN_MARK, Semester
def initActiveSemesterMenuLinks(space): def initActiveSemesterMenuLinks(space, app):
with app.app_context(): with app.app_context():
try: try:
semesters = Semester.sortedSemestersStartingWithNewest() semesters = Semester.sortedSemestersStartingWithNewest()

View file

@ -5,25 +5,15 @@ Functions not dependent on advlabdb.models.
import secrets import secrets
from string import ascii_letters, digits from string import ascii_letters, digits
from sqlalchemy import func, select from flask import current_app
from . import app, db
PASSWORD_CHARS: str = ascii_letters + digits + "!%*+=?" PASSWORD_CHARS: str = ascii_letters + digits + "!%*+=?"
def randomPassword() -> str: def randomPassword() -> str:
password_length = app.config["SECURITY_PASSWORD_LENGTH_MIN"] password_length = current_app.config["SECURITY_PASSWORD_LENGTH_MIN"]
return "".join(secrets.choice(PASSWORD_CHARS) for i in range(password_length)) return "".join(secrets.choice(PASSWORD_CHARS) for i in range(password_length))
def reportBadAttempt(message: str) -> None: def reportBadAttempt(message: str) -> None:
print("BAD ATTEMPT:", message) # TODO: Log print("BAD ATTEMPT:", message) # TODO: Log
def get_count(table):
return db.session.scalar(select(func.count()).select_from(table))
def get_first(table):
return db.session.execute(table.limit(1)).scalars().first()

View file

@ -10,12 +10,12 @@ from decimal import ROUND_HALF_UP, Decimal
from flask import flash from flask import flash
from flask_security import current_user from flask_security import current_user
from flask_security.models import fsqla_v2 as fsqla
from flask_security.models.fsqla_v2 import FsRoleMixin, FsUserMixin from flask_security.models.fsqla_v2 import FsRoleMixin, FsUserMixin
from sqlalchemy import select from flask_sqlalchemy import SQLAlchemy
from sqlalchemy import func, select
from . import db
from .exceptions import DataBaseException from .exceptions import DataBaseException
from .model_independent_funs import get_first
MIN_MARK = 0 MIN_MARK = 0
MAX_MARK = 15 MAX_MARK = 15
@ -27,6 +27,19 @@ MIN_GROUP_NUMBER = 1
MIN_DURATION_IN_DAYS = 1 MIN_DURATION_IN_DAYS = 1
MIN_PART_NUMBER = 1 MIN_PART_NUMBER = 1
db = SQLAlchemy()
# For Flask-Security-Too
fsqla.FsModels.set_db_info(db)
def get_count(table):
return db.session.scalar(select(func.count()).select_from(table))
def get_first(table):
return db.session.execute(table.limit(1)).scalars().first()
def roundHalfUpToInt(number): def roundHalfUpToInt(number):
return int(Decimal(number).quantize(Decimal(0), rounding=ROUND_HALF_UP)) return int(Decimal(number).quantize(Decimal(0), rounding=ROUND_HALF_UP))

View file

@ -1,19 +1,21 @@
from flask import flash, redirect, request, url_for from flask import Blueprint, flash, redirect, request, url_for
from flask_security import auth_required, current_user from flask_security import auth_required, current_user
from . import app, db
from .model_dependent_funs import active_semester_str from .model_dependent_funs import active_semester_str
from .models import Semester from .models import Semester, db
bp = Blueprint("main", __name__, root_path="/", template_folder="templates")
@app.context_processor @bp.app_context_processor
def util_processor(): def util_processor():
author_email = "mobitar@students.uni-mainz.de" author_email = "mobitar@students.uni-mainz.de"
footer = f"<hr><p style='font-size:14px;'>This website is still under development (beta release)! If you have any questions, find any bugs or want some feature, please write a formless email (german/english) to Mo Bitar: <a href='mailto:{author_email}'>{author_email}</a>. Feedback is also welcome :)</p><br>" footer = f"<hr><p style='font-size:14px;'>This website is still under development (beta release)! If you have any questions, find any bugs or want some feature, please write a formless email (german/english) to Mo Bitar: <a href='mailto:{author_email}'>{author_email}</a>. Feedback is also welcome :)</p><br>"
return dict(active_semester_str=active_semester_str, current_user=current_user, footer=footer) return dict(active_semester_str=active_semester_str, current_user=current_user, footer=footer)
@app.route("/") @bp.route("/")
def index(): def index():
if current_user.has_role("admin"): if current_user.has_role("admin"):
endpoint_base = "admin" endpoint_base = "admin"
@ -30,7 +32,7 @@ def index():
return redirect(url) return redirect(url)
@app.route("/set_semester") @bp.route("/set_semester")
@auth_required() @auth_required()
def set_semester(): def set_semester():
try: try:
@ -41,5 +43,5 @@ def set_semester():
semester = db.session.get(Semester, semesterId) semester = db.session.get(Semester, semesterId)
current_user.setActiveSemester(semester) current_user.setActiveSemester(semester)
red = request.referrer or url_for("index") red = request.referrer or url_for("main.index")
return redirect(red) return redirect(red)

View file

@ -1,18 +1,20 @@
{% macro information(current_user, active_semester_str, role) %} {% macro information(current_user, active_semester_str, role) %} User:
User: <a href="{{ url_for('index') }}{{ role }}/user/details/?id={{ current_user.id }}">{{ current_user }}</a> <a
href="{{ url_for('main.index') }}{{ role }}/user/details/?id={{ current_user.id }}"
>{{ current_user }}</a
>
| Active semester: {{ active_semester_str() }} | Active semester: {{ active_semester_str() }} {% if (role == "admin") and
(current_user.has_role("assistant")) %} |
{% if (role == "admin") and (current_user.has_role("assistant")) %} <a href="{{ url_for('main.index') }}assistant">Assistant space</a>. {% elif
| <a href="{{ url_for('index') }}assistant">Assistant space</a>. (role == "assistant") and (current_user.has_role("admin")) %} |
{% elif (role == "assistant") and (current_user.has_role("admin")) %} <a href="{{ url_for('main.index') }}admin">Admin space</a>. {% endif %} {%
| <a href="{{ url_for('index') }}admin">Admin space</a>. endmacro %} {% macro
{% endif %} missing_final_experiment_marks(number_of_missing_final_experiment_marks,
{% endmacro %} number_of_all_experiment_marks) %}
<p>
{% macro missing_final_experiment_marks(number_of_missing_final_experiment_marks, number_of_all_experiment_marks) %} Number of <strong>missing</strong> final experiment marks: {{
<p> number_of_missing_final_experiment_marks }} / {{
Number of <strong>missing</strong> final experiment marks: number_of_all_experiment_marks }}
{{ number_of_missing_final_experiment_marks }} / {{ number_of_all_experiment_marks }} </p>
</p>
{% endmacro %} {% endmacro %}

View file

@ -117,7 +117,7 @@ cd ~/advlabdb
+ +
[source,bash] [source,bash]
---- ----
poetry run python3 manage.py setup initialize-database poetry run python3 manage.py setup init-db
---- ----
.. *Done!* Now go to your SERVER_NAME using a browser to verify that everything is working. .. *Done!* Now go to your SERVER_NAME using a browser to verify that everything is working.

View file

@ -12,9 +12,9 @@ from flask_admin import __file__ as flask_admin_path
from flask_security import admin_change_password, hash_password from flask_security import admin_change_password, hash_password
from sqlalchemy import select from sqlalchemy import select
from advlabdb import app, db, settings, user_datastore from advlabdb import create_app, settings, user_datastore
from advlabdb.model_independent_funs import randomPassword from advlabdb.model_independent_funs import randomPassword
from advlabdb.models import MAX_YEAR, MIN_YEAR, Admin, Semester, User from advlabdb.models import MAX_YEAR, MIN_YEAR, Admin, Semester, User, db
def run(command: str, **kwargs): def run(command: str, **kwargs):
@ -76,23 +76,16 @@ def generate_secrets():
short_help="Initialize the database.", short_help="Initialize the database.",
help="Initialize the database if it does not already exist.", help="Initialize the database if it does not already exist.",
) )
def initialize_database(): def init_db():
db_file = Path(settings["SQLITE_DB_PATH"]) db_file = Path(settings["SQLITE_DB_PATH"])
if db_file.is_file(): if db_file.is_file():
click.echo(f"Skipping database initialization because the database does already exist at {db_file}.") click.echo(f"Skipping database initialization because the database does already exist at {db_file}.")
return return
click.echo("\nThis script should only be used to initialize the database after setting up a server") app = create_app(create_for_server=False)
click.echo(click.style("The old database will be DELETED and a new database will be created!", bg="red"))
if not click.confirm(click.style("Are you sure that you want to continue?", fg="red"), default=False):
click.echo(click.style("Aborted!", fg="yellow"))
return
with app.app_context(): with app.app_context():
with db.session.begin(): with db.session.begin():
# Delete old database
db.drop_all()
# Create new database # Create new database
db.create_all() db.create_all()
@ -170,6 +163,8 @@ def maintain():
def reset_admin_password(): def reset_admin_password():
click.echo("This script will generate a new random password for a chosen admin.\n") click.echo("This script will generate a new random password for a chosen admin.\n")
app = create_app(create_for_server=False)
with app.app_context(): with app.app_context():
with db.session.begin(): with db.session.begin():
admins = db.session.execute(select(Admin).join(User).where(User.active == True)).scalars().all() admins = db.session.execute(select(Admin).join(User).where(User.active == True)).scalars().all()

View file

@ -167,7 +167,7 @@ commands = [
# Install Python requirements in the container # Install Python requirements in the container
"buildah run builder -- pip3 install -r requirements.txt", "buildah run builder -- pip3 install -r requirements.txt",
"buildah run builder -- python3 manage.py setup generate-secrets", "buildah run builder -- python3 manage.py setup generate-secrets",
"buildah run builder -- python3 manage.py setup initialize-database", "buildah run builder -- python3 manage.py setup init-db",
"buildah config --cmd 'gunicorn --bind 0.0.0.0:80 --workers 5 --log-file /volumes/logs/gunicorn.log run:app' builder", "buildah config --cmd 'gunicorn --bind 0.0.0.0:80 --workers 5 --log-file /volumes/logs/gunicorn.log run:app' builder",
] ]

3
run.py
View file

@ -1,4 +1,5 @@
from advlabdb import app from advlabdb import create_app
if __name__ == "__main__": if __name__ == "__main__":
app = create_app()
app.run(debug=True) app.run(debug=True)