BrickTracker/bricktracker/views/admin.py

318 lines
8.3 KiB
Python
Raw Normal View History

2025-01-17 11:03:00 +01:00
from datetime import datetime
import logging
import os
from flask import (
Blueprint,
current_app,
g,
redirect,
request,
render_template,
send_file,
url_for,
)
from flask_login import login_required
from werkzeug.wrappers.response import Response
from ..configuration_list import BrickConfigurationList
from .exceptions import exception_handler
from ..instructions_list import BrickInstructionsList
from ..minifigure import BrickMinifigure
from ..part import BrickPart
from ..rebrickable_image import RebrickableImage
2025-01-17 11:03:00 +01:00
from ..retired_list import BrickRetiredList
from ..set import BrickSet
2025-01-17 11:03:00 +01:00
from ..sql import BrickSQL
from ..theme_list import BrickThemeList
from .upload import upload_helper
logger = logging.getLogger(__name__)
admin_page = Blueprint('admin', __name__, url_prefix='/admin')
# Admin
@admin_page.route('/', methods=['GET'])
@login_required
@exception_handler(__file__)
def admin() -> str:
counters: dict[str, int] = {}
count_none: int = 0
2025-01-17 11:03:00 +01:00
exception: Exception | None = None
is_init: bool = False
nil_minifigure_name: str = ''
nil_minifigure_url: str = ''
nil_part_name: str = ''
nil_part_url: str = ''
2025-01-17 11:03:00 +01:00
# This view needs to be protected against SQL errors
try:
is_init = BrickSQL.is_init()
if is_init:
counters = BrickSQL.count_records()
record = BrickSQL().fetchone('missing/count_none')
if record is not None:
count_none = record['count']
nil_minifigure_name = RebrickableImage.nil_minifigure_name()
nil_minifigure_url = RebrickableImage.static_url(
nil_minifigure_name,
'MINIFIGURES_FOLDER'
)
nil_part_name = RebrickableImage.nil_name()
nil_part_url = RebrickableImage.static_url(
nil_part_name,
'PARTS_FOLDER'
)
2025-01-17 11:03:00 +01:00
except Exception as e:
exception = e
# Warning
logger.warning('An exception occured while loading the admin page: {exception}'.format( # noqa: E501
exception=str(e),
))
open_image = request.args.get('open_image', None)
2025-01-17 11:03:00 +01:00
open_instructions = request.args.get('open_instructions', None)
open_logout = request.args.get('open_logout', None)
open_retired = request.args.get('open_retired', None)
open_theme = request.args.get('open_theme', None)
open_database = (
open_image is None and
open_instructions is None and
open_logout is None and
open_retired is None and
open_theme is None
2025-01-17 11:03:00 +01:00
)
return render_template(
'admin.html',
configuration=BrickConfigurationList.list(),
counters=counters,
count_none=count_none,
2025-01-17 11:03:00 +01:00
error=request.args.get('error'),
exception=exception,
instructions=BrickInstructionsList(),
is_init=is_init,
nil_minifigure_name=nil_minifigure_name,
nil_minifigure_url=nil_minifigure_url,
nil_part_name=nil_part_name,
nil_part_url=nil_part_url,
2025-01-17 11:03:00 +01:00
open_database=open_database,
open_image=open_image,
2025-01-17 11:03:00 +01:00
open_instructions=open_instructions,
open_logout=open_logout,
open_retired=open_retired,
open_theme=open_theme,
retired=BrickRetiredList(),
theme=BrickThemeList(),
)
# Initialize the database
@admin_page.route('/init-database', methods=['POST'])
@login_required
@exception_handler(__file__, post_redirect='admin.admin')
def init_database() -> Response:
BrickSQL.initialize()
# Reload the instructions
BrickInstructionsList(force=True)
return redirect(url_for('admin.admin'))
# Delete the database
@admin_page.route('/delete-database', methods=['GET'])
@login_required
@exception_handler(__file__)
def delete_database() -> str:
return render_template(
'admin.html',
delete_database=True,
error=request.args.get('error')
)
# Actually delete the database
@admin_page.route('/delete-database', methods=['POST'])
@login_required
@exception_handler(__file__, post_redirect='admin.delete_database')
def do_delete_database() -> Response:
BrickSQL.delete()
# Reload the instructions
BrickInstructionsList(force=True)
return redirect(url_for('admin.admin'))
# Download the database
@admin_page.route('/download-database', methods=['GET'])
@login_required
@exception_handler(__file__)
def download_database() -> Response:
# Create a file name with a timestamp embedded
name, extension = os.path.splitext(
os.path.basename(current_app.config['DATABASE_PATH'].value)
)
# Info
logger.info('The database has been downloaded')
return send_file(
current_app.config['DATABASE_PATH'].value,
as_attachment=True,
download_name='{name}-{timestamp}{extension}'.format(
name=name,
timestamp=datetime.now().astimezone(g.timezone).strftime(
current_app.config['DATABASE_TIMESTAMP_FORMAT'].value
),
extension=extension
)
)
# Drop the database
@admin_page.route('/drop-database', methods=['GET'])
@login_required
@exception_handler(__file__)
def drop_database() -> str:
return render_template(
'admin.html',
drop_database=True,
error=request.args.get('error')
)
# Actually drop the database
@admin_page.route('/drop-database', methods=['POST'])
@login_required
@exception_handler(__file__, post_redirect='admin.drop_database')
def do_drop_database() -> Response:
BrickSQL.drop()
# Reload the instructions
BrickInstructionsList(force=True)
return redirect(url_for('admin.admin'))
# Import a database
@admin_page.route('/import-database', methods=['GET'])
@login_required
@exception_handler(__file__)
def import_database() -> str:
return render_template(
'admin.html',
import_database=True,
error=request.args.get('error')
)
# Actually import a database
@admin_page.route('/import-database', methods=['POST'])
@login_required
@exception_handler(__file__, post_redirect='admin.import_database')
def do_import_database() -> Response:
file = upload_helper(
'database',
'admin.import_database',
extensions=['.db'],
)
if isinstance(file, Response):
return file
BrickSQL.upload(file)
# Reload the instructions
BrickInstructionsList(force=True)
return redirect(url_for('admin.admin'))
# Refresh the instructions cache
@admin_page.route('/refresh-instructions', methods=['GET'])
@login_required
@exception_handler(__file__)
def refresh_instructions() -> Response:
BrickInstructionsList(force=True)
return redirect(url_for('admin.admin', open_instructions=True))
# Refresh the retired sets cache
@admin_page.route('/refresh-retired', methods=['GET'])
@login_required
@exception_handler(__file__)
def refresh_retired() -> Response:
BrickRetiredList(force=True)
return redirect(url_for('admin.admin', open_retired=True))
# Refresh the themes cache
@admin_page.route('/refresh-themes', methods=['GET'])
@login_required
@exception_handler(__file__)
def refresh_themes() -> Response:
BrickThemeList(force=True)
return redirect(url_for('admin.admin', open_theme=True))
# Update the default images
@admin_page.route('/update-image', methods=['GET'])
@login_required
@exception_handler(__file__)
def update_image() -> Response:
# Abusing the object to create a 'nil' minifigure
RebrickableImage(
BrickSet(),
minifigure=BrickMinifigure(record={
'set_img_url': None,
})
).download()
# Abusing the object to create a 'nil' part
RebrickableImage(
BrickSet(),
part=BrickPart(record={
'part_img_url': None,
'part_img_url_id': None
})
).download()
return redirect(url_for('admin.admin', open_image=True))
2025-01-17 11:03:00 +01:00
# Update the themes file
@admin_page.route('/update-retired', methods=['GET'])
@login_required
@exception_handler(__file__)
def update_retired() -> Response:
BrickRetiredList().update()
BrickRetiredList(force=True)
return redirect(url_for('admin.admin', open_retired=True))
# Update the themes file
@admin_page.route('/update-themes', methods=['GET'])
@login_required
@exception_handler(__file__)
def update_themes() -> Response:
BrickThemeList().update()
BrickThemeList(force=True)
return redirect(url_for('admin.admin', open_theme=True))