mirror of
https://github.com/nelsbrock/NateMan.git
synced 2026-08-14 21:36:49 +02:00
187 lines
6.0 KiB
Python
187 lines
6.0 KiB
Python
# NateMan – Nachschreibtermin-Manager
|
||
# __init__.py
|
||
# Copyright © 2020 Niklas Elsbrock
|
||
#
|
||
# This program is free software: you can redistribute it and/or modify
|
||
# it under the terms of the GNU General Public License as published by
|
||
# the Free Software Foundation, either version 3 of the License, or
|
||
# (at your option) any later version.
|
||
#
|
||
# This program is distributed in the hope that it will be useful,
|
||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
# GNU General Public License for more details.
|
||
#
|
||
# You should have received a copy of the GNU General Public License
|
||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||
|
||
"""
|
||
Enthält die Initialisierungsfunktion der NateMan-Webapp.
|
||
"""
|
||
|
||
import locale
|
||
import logging.handlers
|
||
import os
|
||
import sys
|
||
from datetime import datetime
|
||
|
||
import sqlalchemy
|
||
from flask import Flask
|
||
from flask.logging import default_handler
|
||
|
||
from . import util
|
||
from .config_manager import config_file_exists, create_config_file, config
|
||
from .config_manager import read_config_file
|
||
from .models import Klausur, Klausurteilnahme, Lehrer, Schueler, Stufe, db
|
||
|
||
|
||
def create_app() -> Flask:
|
||
"""Initialisiert die NateMan-Webapp"""
|
||
|
||
app: Flask = Flask(__name__, instance_relative_config=True)
|
||
|
||
# Sprache festlegen
|
||
locale.setlocale(locale.LC_ALL, "")
|
||
|
||
setup_logging(app)
|
||
|
||
# falls nötig, instance-Ordner anlegen
|
||
os.makedirs(app.instance_path, exist_ok=True)
|
||
|
||
load_config(app)
|
||
|
||
secret_key = read_or_generate_secret_key(app)
|
||
|
||
configure_jinja(app)
|
||
|
||
# Falls in der Konfiguration aktiviert, Datei-Logging einrichten
|
||
if config["logging"].get("syslog", False):
|
||
setup_syslog(app)
|
||
|
||
# Logging-Level ändern
|
||
app.logger.setLevel(config["logging"]["level"])
|
||
|
||
# Flask und Flask-SQLAlchemy konfigurieren
|
||
app.config.from_mapping(
|
||
SERVER_NAME=config["server-name"],
|
||
SECRET_KEY=secret_key,
|
||
PREFERRED_URL_SCHEME="https" if config["uses-ssl"] else "http",
|
||
SESSION_COOKIE_SECURE=config["uses-ssl"],
|
||
MAX_CONTENT_LENGTH=16 * 1024 * 1024, # 16 MiB upload limit
|
||
SQLALCHEMY_DATABASE_URI="sqlite:///" + os.path.join(app.instance_path, "nateman.sqlite3"),
|
||
SQLALCHEMY_TRACK_MODIFICATIONS=False
|
||
)
|
||
|
||
db.init_app(app)
|
||
db.create_all(app=app)
|
||
|
||
with app.app_context():
|
||
init_db(app)
|
||
|
||
# Blueprints registrieren
|
||
register_blueprints(app)
|
||
|
||
return app
|
||
|
||
|
||
def setup_logging(app: Flask):
|
||
stdout_handler = logging.StreamHandler(sys.stdout)
|
||
formatter = logging.Formatter(
|
||
fmt="[%(asctime)s | %(levelname)s]: %(message)s"
|
||
)
|
||
stdout_handler.setFormatter(formatter)
|
||
app.logger.removeHandler(default_handler)
|
||
app.logger.addHandler(stdout_handler)
|
||
|
||
|
||
def load_config(app: Flask):
|
||
"""
|
||
Liest die Konfigurationsdatei.
|
||
Falls keine vorhanden ist, erstellt eine und beendet anschließend NateMan.
|
||
"""
|
||
if not config_file_exists(app):
|
||
config_path = create_config_file(app)
|
||
app.logger.info(f"Da die Konfigurationsdatei nicht gefunden werden konnte, "
|
||
f"wurde eine neue erstellt ({config_path}).")
|
||
|
||
# Konfigurationsdatei laden
|
||
read_config_file(app)
|
||
|
||
|
||
def read_or_generate_secret_key(app: Flask) -> bytes:
|
||
""" Liest den Secret Key aus bzw. generiert ihn, falls noch keiner vorhanden ist. """
|
||
secret_key_path = os.path.join(app.instance_path, "SECRET_KEY")
|
||
|
||
if not os.path.isfile(secret_key_path):
|
||
with open(os.open(secret_key_path, os.O_WRONLY | os.O_CREAT, mode=0o600), "wb") as file:
|
||
secret_key = os.urandom(128)
|
||
file.write(secret_key)
|
||
else:
|
||
with open(secret_key_path, "rb") as file:
|
||
secret_key = file.read()
|
||
|
||
return secret_key
|
||
|
||
|
||
def configure_jinja(app: Flask):
|
||
app.jinja_env.globals.update(
|
||
nateman_config=config,
|
||
util=util,
|
||
sqlalchemy=sqlalchemy,
|
||
datetime=datetime,
|
||
db=db,
|
||
Klausurteilnahme=Klausurteilnahme,
|
||
Stufe=Stufe,
|
||
Schueler=Schueler,
|
||
Lehrer=Lehrer,
|
||
Klausur=Klausur
|
||
)
|
||
app.jinja_env.trim_blocks = True
|
||
app.jinja_env.lstrip_blocks = True
|
||
app.jinja_env.strip_trailing_newlines = False
|
||
|
||
|
||
def setup_syslog(app: Flask):
|
||
""" Erstellt Logging-Handler zum loggen in Syslog. """
|
||
file_handler = logging.handlers.SysLogHandler(address="/dev/log")
|
||
file_formatter = logging.Formatter(fmt="nateman: [%(levelname)s] %(message)s")
|
||
file_handler.setFormatter(file_formatter)
|
||
app.logger.addHandler(file_handler)
|
||
|
||
|
||
def init_db(app: Flask):
|
||
""" Initialisiert Stufen und Administratorkonto, falls nicht vorhanden """
|
||
if Stufe.query.count() == 0:
|
||
new_stufen_namen = ["EF", "Q1", "Q2"]
|
||
for stufe_name in new_stufen_namen:
|
||
stufe = Stufe(name=stufe_name)
|
||
db.session.add(stufe)
|
||
app.logger.info(f"Da keine Stufen vorhanden waren, wurden neue erstellt ({', '.join(new_stufen_namen)}).")
|
||
|
||
if Lehrer.query.count() == 0:
|
||
new_lehrer_kuerzel = "admin"
|
||
new_lehrer_password = "admin"
|
||
lehrer = Lehrer(kuerzel="admin", is_admin=True, pwd_changed=False)
|
||
lehrer.set_password("admin", set_pwd_changed=False)
|
||
db.session.add(lehrer)
|
||
app.logger.info(f"Da keine Lehrerkonten vorhanden waren, wurde ein neues Administratorkonto erstellt. "
|
||
f"(Kürzel: {new_lehrer_kuerzel}; Passwort: {new_lehrer_password})")
|
||
|
||
db.session.commit()
|
||
|
||
|
||
def register_blueprints(app: Flask):
|
||
""" Registriert die Flask-Blueprints """
|
||
from . import blueprints
|
||
from .blueprints import admin, auth, error, fileio, info, klausuren, schueler
|
||
from .blueprints.admin import lehrer as admin_lehrer
|
||
app.register_blueprint(blueprints.bp)
|
||
app.register_blueprint(admin.bp)
|
||
app.register_blueprint(admin_lehrer.bp, name="admin.lehrer")
|
||
app.register_blueprint(auth.bp)
|
||
app.register_blueprint(error.bp)
|
||
app.register_blueprint(fileio.bp)
|
||
app.register_blueprint(info.bp)
|
||
app.register_blueprint(klausuren.bp)
|
||
app.register_blueprint(schueler.bp)
|