Rename checkboxes (too generic) to status (and some bug fixes)
@ -13,7 +13,7 @@ from bricktracker.sql import close
|
||||
from bricktracker.version import __version__
|
||||
from bricktracker.views.add import add_page
|
||||
from bricktracker.views.admin.admin import admin_page
|
||||
from bricktracker.views.admin.checkbox import admin_checkbox_page
|
||||
from bricktracker.views.admin.status import admin_status_page
|
||||
from bricktracker.views.admin.database import admin_database_page
|
||||
from bricktracker.views.admin.image import admin_image_page
|
||||
from bricktracker.views.admin.instructions import admin_instructions_page
|
||||
@ -78,7 +78,7 @@ def setup_app(app: Flask) -> None:
|
||||
|
||||
# Register admin routes
|
||||
app.register_blueprint(admin_page)
|
||||
app.register_blueprint(admin_checkbox_page)
|
||||
app.register_blueprint(admin_status_page)
|
||||
app.register_blueprint(admin_database_page)
|
||||
app.register_blueprint(admin_image_page)
|
||||
app.register_blueprint(admin_instructions_page)
|
||||
|
@ -93,7 +93,7 @@ class BrickMetadata(BrickRecord):
|
||||
metadata_id=self.fields.id
|
||||
)
|
||||
|
||||
# Select a specific checkbox (with an id)
|
||||
# Select a specific metadata (with an id)
|
||||
def select_specific(self, id: str, /) -> Self:
|
||||
# Save the parameters to the fields
|
||||
self.fields.id = id
|
||||
|
@ -7,7 +7,7 @@ if TYPE_CHECKING:
|
||||
# Grab the list of checkboxes to create a list of SQL columns
|
||||
def migration_0007(sql: 'BrickSQL', /) -> dict[str, Any]:
|
||||
# Don't realy on sql files as they could be removed in the future
|
||||
sql.cursor.execute('SELECT "bricktracker_set_checkboxes"."id" FROM "bricktracker_set_checkboxes') # noqa: E501
|
||||
sql.cursor.execute('SELECT "bricktracker_set_checkboxes"."id" FROM "bricktracker_set_checkboxes"') # noqa: E501
|
||||
records = sql.cursor.fetchall()
|
||||
|
||||
return {
|
||||
|
@ -8,13 +8,13 @@ if TYPE_CHECKING:
|
||||
from .part import BrickPart
|
||||
from .rebrickable_set import RebrickableSet
|
||||
from .set import BrickSet
|
||||
from .set_checkbox import BrickSetCheckbox
|
||||
from .set_status import BrickSetStatus
|
||||
from .wish import BrickWish
|
||||
|
||||
T = TypeVar(
|
||||
'T',
|
||||
'BrickSet',
|
||||
'BrickSetCheckbox',
|
||||
'BrickSetStatus',
|
||||
'BrickPart',
|
||||
'BrickMinifigure',
|
||||
'BrickWish',
|
||||
|
@ -1,6 +1,6 @@
|
||||
from .instructions_list import BrickInstructionsList
|
||||
from .retired_list import BrickRetiredList
|
||||
from .set_checkbox_list import BrickSetCheckboxList
|
||||
from .set_status_list import BrickSetStatusList
|
||||
from .theme_list import BrickThemeList
|
||||
|
||||
|
||||
@ -11,8 +11,8 @@ def reload() -> None:
|
||||
# Reload the instructions
|
||||
BrickInstructionsList(force=True)
|
||||
|
||||
# Reload the checkboxes
|
||||
BrickSetCheckboxList(force=True)
|
||||
# Reload the set statuses
|
||||
BrickSetStatusList(force=True)
|
||||
|
||||
# Reload retired sets
|
||||
BrickRetiredList(force=True)
|
||||
|
@ -9,7 +9,7 @@ from .exceptions import NotFoundException
|
||||
from .minifigure_list import BrickMinifigureList
|
||||
from .part_list import BrickPartList
|
||||
from .rebrickable_set import RebrickableSet
|
||||
from .set_checkbox_list import BrickSetCheckboxList
|
||||
from .set_status_list import BrickSetStatusList
|
||||
from .sql import BrickSQL
|
||||
if TYPE_CHECKING:
|
||||
from .socket import BrickSocket
|
||||
@ -161,7 +161,7 @@ class BrickSet(RebrickableSet):
|
||||
|
||||
# Load from database
|
||||
if not self.select(
|
||||
statuses=BrickSetCheckboxList().as_columns(solo=True)
|
||||
statuses=BrickSetStatusList().as_columns(solo=True)
|
||||
):
|
||||
raise NotFoundException(
|
||||
'Set with ID {id} was not found in the database'.format(
|
||||
|
@ -3,7 +3,7 @@ from typing import Self
|
||||
from flask import current_app
|
||||
|
||||
from .record_list import BrickRecordList
|
||||
from .set_checkbox_list import BrickSetCheckboxList
|
||||
from .set_status_list import BrickSetStatusList
|
||||
from .set import BrickSet
|
||||
|
||||
|
||||
@ -37,7 +37,7 @@ class BrickSetList(BrickRecordList[BrickSet]):
|
||||
# Load the sets from the database
|
||||
for record in self.select(
|
||||
order=self.order,
|
||||
statuses=BrickSetCheckboxList().as_columns()
|
||||
statuses=BrickSetStatusList().as_columns()
|
||||
):
|
||||
brickset = BrickSet(record=record)
|
||||
|
||||
@ -73,7 +73,7 @@ class BrickSetList(BrickRecordList[BrickSet]):
|
||||
for record in self.select(
|
||||
order=order,
|
||||
limit=limit,
|
||||
statuses=BrickSetCheckboxList().as_columns()
|
||||
statuses=BrickSetStatusList().as_columns()
|
||||
):
|
||||
brickset = BrickSet(record=record)
|
||||
|
||||
|
@ -4,20 +4,20 @@ from .exceptions import ErrorException
|
||||
from .metadata import BrickMetadata
|
||||
|
||||
|
||||
# Lego set checkbox
|
||||
class BrickSetCheckbox(BrickMetadata):
|
||||
kind: str = 'checkbox'
|
||||
# Lego set status metadata
|
||||
class BrickSetStatus(BrickMetadata):
|
||||
kind: str = 'status'
|
||||
prefix: str = 'status'
|
||||
|
||||
# Set state endpoint
|
||||
set_state_endpoint: str = 'set.update_status'
|
||||
|
||||
# Queries
|
||||
delete_query: str = 'checkbox/delete'
|
||||
insert_query: str = 'checkbox/insert'
|
||||
select_query: str = 'checkbox/select'
|
||||
update_field_query: str = 'checkbox/update/field'
|
||||
update_set_state_query: str = 'set/update/status'
|
||||
delete_query: str = 'set/metadata/status/delete'
|
||||
insert_query: str = 'set/metadata/status/insert'
|
||||
select_query: str = 'set/metadata/status/select'
|
||||
update_field_query: str = 'set/metadata/status/update/field'
|
||||
update_set_state_query: str = 'set/metadata/status/update/state'
|
||||
|
||||
# Grab data from a form
|
||||
def from_form(self, form: dict[str, str], /) -> Self:
|
||||
@ -25,7 +25,7 @@ class BrickSetCheckbox(BrickMetadata):
|
||||
grid = form.get('grid', None)
|
||||
|
||||
if name is None or name == '':
|
||||
raise ErrorException('Checkbox name cannot be empty')
|
||||
raise ErrorException('Status name cannot be empty')
|
||||
|
||||
self.fields.name = name
|
||||
self.fields.displayed_on_grid = grid == 'on'
|
@ -3,39 +3,39 @@ import logging
|
||||
from .exceptions import NotFoundException
|
||||
from .fields import BrickRecordFields
|
||||
from .record_list import BrickRecordList
|
||||
from .set_checkbox import BrickSetCheckbox
|
||||
from .set_status import BrickSetStatus
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# Lego sets checkbox list
|
||||
class BrickSetCheckboxList(BrickRecordList[BrickSetCheckbox]):
|
||||
checkboxes: dict[str, BrickSetCheckbox]
|
||||
# Lego sets status list
|
||||
class BrickSetStatusList(BrickRecordList[BrickSetStatus]):
|
||||
statuses: dict[str, BrickSetStatus]
|
||||
|
||||
# Queries
|
||||
select_query = 'checkbox/list'
|
||||
select_query = 'set/metadata/status/list'
|
||||
|
||||
def __init__(self, /, *, force: bool = False):
|
||||
# Load checkboxes only if there is none already loaded
|
||||
# Load statuses only if there is none already loaded
|
||||
records = getattr(self, 'records', None)
|
||||
|
||||
if records is None or force:
|
||||
# Don't use super()__init__ as it would mask class variables
|
||||
self.fields = BrickRecordFields()
|
||||
|
||||
logger.info('Loading set checkboxes list')
|
||||
logger.info('Loading set statuses list')
|
||||
|
||||
BrickSetCheckboxList.records = []
|
||||
BrickSetCheckboxList.checkboxes = {}
|
||||
BrickSetStatusList.records = []
|
||||
BrickSetStatusList.statuses = {}
|
||||
|
||||
# Load the checkboxes from the database
|
||||
# Load the statuses from the database
|
||||
for record in self.select():
|
||||
checkbox = BrickSetCheckbox(record=record)
|
||||
status = BrickSetStatus(record=record)
|
||||
|
||||
BrickSetCheckboxList.records.append(checkbox)
|
||||
BrickSetCheckboxList.checkboxes[checkbox.fields.id] = checkbox
|
||||
BrickSetStatusList.records.append(status)
|
||||
BrickSetStatusList.statuses[status.fields.id] = status
|
||||
|
||||
# Return the checkboxes as columns for a select
|
||||
# Return the statuses as columns for a select
|
||||
def as_columns(
|
||||
self,
|
||||
/,
|
||||
@ -53,19 +53,19 @@ class BrickSetCheckboxList(BrickRecordList[BrickSetCheckbox]):
|
||||
if solo or record.fields.displayed_on_grid
|
||||
])
|
||||
|
||||
# Grab a specific checkbox
|
||||
def get(self, id: str, /) -> BrickSetCheckbox:
|
||||
if id not in self.checkboxes:
|
||||
# Grab a specific status
|
||||
def get(self, id: str, /) -> BrickSetStatus:
|
||||
if id not in self.statuses:
|
||||
raise NotFoundException(
|
||||
'Checkbox with ID {id} was not found in the database'.format(
|
||||
'Status with ID {id} was not found in the database'.format(
|
||||
id=id,
|
||||
),
|
||||
)
|
||||
|
||||
return self.checkboxes[id]
|
||||
return self.statuses[id]
|
||||
|
||||
# Get the list of checkboxes depending on the context
|
||||
def list(self, /, *, all: bool = False) -> list[BrickSetCheckbox]:
|
||||
# Get the list of statuses depending on the context
|
||||
def list(self, /, *, all: bool = False) -> list[BrickSetStatus]:
|
||||
return [
|
||||
record
|
||||
for record
|
@ -318,13 +318,18 @@ class BrickSQL(object):
|
||||
),
|
||||
package='bricktracker'
|
||||
)
|
||||
except Exception:
|
||||
module = None
|
||||
|
||||
# If a module has been loaded, we need to fail if an error
|
||||
# occured while executing the migration function
|
||||
if module is not None:
|
||||
function = getattr(module, 'migration_{name}'.format(
|
||||
name=pending.name
|
||||
))
|
||||
|
||||
context: dict[str, Any] = function(self)
|
||||
except Exception:
|
||||
else:
|
||||
context: dict[str, Any] = {}
|
||||
|
||||
self.executescript(pending.get_query(), **context)
|
||||
|
@ -1,7 +0,0 @@
|
||||
SELECT
|
||||
"bricktracker_set_checkboxes"."id",
|
||||
"bricktracker_set_checkboxes"."name",
|
||||
"bricktracker_set_checkboxes"."displayed_on_grid"
|
||||
FROM "bricktracker_set_checkboxes"
|
||||
|
||||
{% block where %}{% endblock %}
|
@ -1,9 +0,0 @@
|
||||
BEGIN TRANSACTION;
|
||||
|
||||
ALTER TABLE "bricktracker_set_statuses"
|
||||
DROP COLUMN "status_{{ id }}";
|
||||
|
||||
DELETE FROM "bricktracker_set_checkboxes"
|
||||
WHERE "bricktracker_set_checkboxes"."id" IS NOT DISTINCT FROM '{{ id }}';
|
||||
|
||||
COMMIT;
|
@ -1 +0,0 @@
|
||||
{% extends 'checkbox/base.sql' %}
|
@ -1,5 +0,0 @@
|
||||
{% extends 'checkbox/base.sql' %}
|
||||
|
||||
{% block where %}
|
||||
WHERE "bricktracker_set_checkboxes"."id" IS NOT DISTINCT FROM :id
|
||||
{% endblock %}
|
@ -1,3 +0,0 @@
|
||||
UPDATE "bricktracker_set_checkboxes"
|
||||
SET "{{field}}" = :value
|
||||
WHERE "bricktracker_set_checkboxes"."id" IS NOT DISTINCT FROM :id
|
7
bricktracker/sql/migrations/0012.sql
Normal file
@ -0,0 +1,7 @@
|
||||
-- description: Rename checkboxes to status metadata
|
||||
|
||||
BEGIN TRANSACTION;
|
||||
|
||||
ALTER TABLE "bricktracker_set_checkboxes" RENAME TO "bricktracker_metadata_statuses";
|
||||
|
||||
COMMIT;
|
@ -1,5 +1,6 @@
|
||||
BEGIN transaction;
|
||||
|
||||
DROP TABLE IF EXISTS "bricktracker_metadata_statuses";
|
||||
DROP TABLE IF EXISTS "bricktracker_minifigures";
|
||||
DROP TABLE IF EXISTS "bricktracker_parts";
|
||||
DROP TABLE IF EXISTS "bricktracker_sets";
|
||||
|
7
bricktracker/sql/set/metadata/status/base.sql
Normal file
@ -0,0 +1,7 @@
|
||||
SELECT
|
||||
"bricktracker_metadata_statuses"."id",
|
||||
"bricktracker_metadata_statuses"."name",
|
||||
"bricktracker_metadata_statuses"."displayed_on_grid"
|
||||
FROM "bricktracker_metadata_statuses"
|
||||
|
||||
{% block where %}{% endblock %}
|
9
bricktracker/sql/set/metadata/status/delete.sql
Normal file
@ -0,0 +1,9 @@
|
||||
BEGIN TRANSACTION;
|
||||
|
||||
ALTER TABLE "bricktracker_set_statuses"
|
||||
DROP COLUMN "status_{{ id }}";
|
||||
|
||||
DELETE FROM "bricktracker_metadata_statuses"
|
||||
WHERE "bricktracker_metadata_statuses"."id" IS NOT DISTINCT FROM '{{ id }}';
|
||||
|
||||
COMMIT;
|
@ -3,7 +3,7 @@ BEGIN TRANSACTION;
|
||||
ALTER TABLE "bricktracker_set_statuses"
|
||||
ADD COLUMN "status_{{ id }}" BOOLEAN NOT NULL DEFAULT 0;
|
||||
|
||||
INSERT INTO "bricktracker_set_checkboxes" (
|
||||
INSERT INTO "bricktracker_metadata_statuses" (
|
||||
"id",
|
||||
"name",
|
||||
"displayed_on_grid"
|
1
bricktracker/sql/set/metadata/status/list.sql
Normal file
@ -0,0 +1 @@
|
||||
{% extends 'set/metadata/status/base.sql' %}
|
5
bricktracker/sql/set/metadata/status/select.sql
Normal file
@ -0,0 +1,5 @@
|
||||
{% extends 'set/metadata/status/base.sql' %}
|
||||
|
||||
{% block where %}
|
||||
WHERE "bricktracker_metadata_statuses"."id" IS NOT DISTINCT FROM :id
|
||||
{% endblock %}
|
3
bricktracker/sql/set/metadata/status/update/field.sql
Normal file
@ -0,0 +1,3 @@
|
||||
UPDATE "bricktracker_metadata_statuses"
|
||||
SET "{{field}}" = :value
|
||||
WHERE "bricktracker_metadata_statuses"."id" IS NOT DISTINCT FROM :id
|
@ -2,11 +2,12 @@ from typing import Tuple
|
||||
|
||||
# Some table aliases to make it look cleaner (id: (name, icon))
|
||||
ALIASES: dict[str, Tuple[str, str]] = {
|
||||
'bricktracker_metadata_statuses': ('Bricktracker set status metadata', 'checkbox-line'), # noqa: E501
|
||||
'bricktracker_minifigures': ('Bricktracker minifigures', 'group-line'),
|
||||
'bricktracker_parts': ('Bricktracker parts', 'shapes-line'),
|
||||
'bricktracker_set_checkboxes': ('Bricktracker set checkboxes', 'checkbox-line'), # noqa: E501
|
||||
'bricktracker_set_statuses': ('Bricktracker sets status', 'checkbox-circle-line'), # noqa: E501
|
||||
'bricktracker_set_storages': ('Bricktracker sets storages', 'archive-2-line'), # noqa: E501
|
||||
'bricktracker_set_checkboxes': ('Bricktracker set checkboxes (legacy)', 'checkbox-line'), # noqa: E501
|
||||
'bricktracker_set_statuses': ('Bricktracker set statuses', 'checkbox-line'), # noqa: E501
|
||||
'bricktracker_set_storages': ('Bricktracker set storages', 'archive-2-line'), # noqa: E501
|
||||
'bricktracker_sets': ('Bricktracker sets', 'hashtag'),
|
||||
'bricktracker_wishes': ('Bricktracker wishes', 'gift-line'),
|
||||
'inventory': ('Parts', 'shapes-line'),
|
||||
|
@ -1,4 +1,4 @@
|
||||
from typing import Final
|
||||
|
||||
__version__: Final[str] = '1.2.0'
|
||||
__database_version__: Final[int] = 11
|
||||
__database_version__: Final[int] = 12
|
||||
|
@ -8,8 +8,8 @@ from ..exceptions import exception_handler
|
||||
from ...instructions_list import BrickInstructionsList
|
||||
from ...rebrickable_image import RebrickableImage
|
||||
from ...retired_list import BrickRetiredList
|
||||
from ...set_checkbox import BrickSetCheckbox
|
||||
from ...set_checkbox_list import BrickSetCheckboxList
|
||||
from ...set_status import BrickSetStatus
|
||||
from ...set_status_list import BrickSetStatusList
|
||||
from ...sql_counter import BrickCounter
|
||||
from ...sql import BrickSQL
|
||||
from ...theme_list import BrickThemeList
|
||||
@ -24,11 +24,11 @@ admin_page = Blueprint('admin', __name__, url_prefix='/admin')
|
||||
@login_required
|
||||
@exception_handler(__file__)
|
||||
def admin() -> str:
|
||||
brickset_checkboxes: list[BrickSetCheckbox] = []
|
||||
database_counters: list[BrickCounter] = []
|
||||
database_exception: Exception | None = None
|
||||
database_upgrade_needed: bool = False
|
||||
database_version: int = -1
|
||||
metadata_statuses: list[BrickSetStatus] = []
|
||||
nil_minifigure_name: str = ''
|
||||
nil_minifigure_url: str = ''
|
||||
nil_part_name: str = ''
|
||||
@ -41,7 +41,7 @@ def admin() -> str:
|
||||
database_version = database.version
|
||||
database_counters = BrickSQL().count_records()
|
||||
|
||||
brickset_checkboxes = BrickSetCheckboxList().list(all=True)
|
||||
metadata_statuses = BrickSetStatusList().list(all=True)
|
||||
except Exception as e:
|
||||
database_exception = e
|
||||
|
||||
@ -62,38 +62,38 @@ def admin() -> str:
|
||||
'PARTS_FOLDER'
|
||||
)
|
||||
|
||||
open_checkbox = request.args.get('open_checkbox', None)
|
||||
open_image = request.args.get('open_image', None)
|
||||
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_status = request.args.get('open_status', None)
|
||||
open_theme = request.args.get('open_theme', None)
|
||||
|
||||
open_database = (
|
||||
open_checkbox is None and
|
||||
open_image is None and
|
||||
open_instructions is None and
|
||||
open_logout is None and
|
||||
open_retired is None and
|
||||
open_status is None and
|
||||
open_theme is None
|
||||
)
|
||||
|
||||
return render_template(
|
||||
'admin.html',
|
||||
configuration=BrickConfigurationList.list(),
|
||||
brickset_checkboxes=brickset_checkboxes,
|
||||
checkbox_error=request.args.get('checkbox_error'),
|
||||
status_error=request.args.get('status_error'),
|
||||
database_counters=database_counters,
|
||||
database_error=request.args.get('database_error'),
|
||||
database_exception=database_exception,
|
||||
database_upgrade_needed=database_upgrade_needed,
|
||||
database_version=database_version,
|
||||
instructions=BrickInstructionsList(),
|
||||
metadata_statuses=metadata_statuses,
|
||||
nil_minifigure_name=nil_minifigure_name,
|
||||
nil_minifigure_url=nil_minifigure_url,
|
||||
nil_part_name=nil_part_name,
|
||||
nil_part_url=nil_part_url,
|
||||
open_checkbox=open_checkbox,
|
||||
open_status=open_status,
|
||||
open_database=open_database,
|
||||
open_image=open_image,
|
||||
open_instructions=open_instructions,
|
||||
|
@ -1,98 +0,0 @@
|
||||
from flask import (
|
||||
Blueprint,
|
||||
jsonify,
|
||||
redirect,
|
||||
request,
|
||||
render_template,
|
||||
url_for,
|
||||
)
|
||||
from flask_login import login_required
|
||||
from werkzeug.wrappers.response import Response
|
||||
|
||||
from ..exceptions import exception_handler
|
||||
from ...reload import reload
|
||||
from ...set_checkbox import BrickSetCheckbox
|
||||
|
||||
admin_checkbox_page = Blueprint(
|
||||
'admin_checkbox',
|
||||
__name__,
|
||||
url_prefix='/admin/checkbox'
|
||||
)
|
||||
|
||||
|
||||
# Add a checkbox
|
||||
@admin_checkbox_page.route('/add', methods=['POST'])
|
||||
@login_required
|
||||
@exception_handler(
|
||||
__file__,
|
||||
post_redirect='admin.admin',
|
||||
error_name='checkbox_error',
|
||||
open_checkbox=True
|
||||
)
|
||||
def add() -> Response:
|
||||
BrickSetCheckbox().from_form(request.form).insert()
|
||||
|
||||
reload()
|
||||
|
||||
return redirect(url_for('admin.admin', open_checkbox=True))
|
||||
|
||||
|
||||
# Delete the checkbox
|
||||
@admin_checkbox_page.route('<id>/delete', methods=['GET'])
|
||||
@login_required
|
||||
@exception_handler(__file__)
|
||||
def delete(*, id: str) -> str:
|
||||
return render_template(
|
||||
'admin.html',
|
||||
delete_checkbox=True,
|
||||
checkbox=BrickSetCheckbox().select_specific(id),
|
||||
error=request.args.get('checkbox_error')
|
||||
)
|
||||
|
||||
|
||||
# Actually delete the checkbox
|
||||
@admin_checkbox_page.route('<id>/delete', methods=['POST'])
|
||||
@login_required
|
||||
@exception_handler(
|
||||
__file__,
|
||||
post_redirect='admin_checkbox.delete',
|
||||
error_name='checkbox_error'
|
||||
)
|
||||
def do_delete(*, id: str) -> Response:
|
||||
checkbox = BrickSetCheckbox().select_specific(id)
|
||||
checkbox.delete()
|
||||
|
||||
reload()
|
||||
|
||||
return redirect(url_for('admin.admin', open_checkbox=True))
|
||||
|
||||
|
||||
# Change the field of a checkbox
|
||||
@admin_checkbox_page.route('/<id>/field/<name>', methods=['POST'])
|
||||
@login_required
|
||||
@exception_handler(__file__, json=True)
|
||||
def update_field(*, id: str, name: str) -> Response:
|
||||
checkbox = BrickSetCheckbox().select_specific(id)
|
||||
value = checkbox.update_field(name, json=request.json)
|
||||
|
||||
reload()
|
||||
|
||||
return jsonify({'value': value})
|
||||
|
||||
|
||||
# Rename the checkbox
|
||||
@admin_checkbox_page.route('<id>/rename', methods=['POST'])
|
||||
@login_required
|
||||
@exception_handler(
|
||||
__file__,
|
||||
post_redirect='admin.admin',
|
||||
error_name='checkbox_error',
|
||||
open_checkbox=True
|
||||
)
|
||||
def rename(*, id: str) -> Response:
|
||||
checkbox = BrickSetCheckbox().select_specific(id)
|
||||
checkbox.from_form(request.form).rename()
|
||||
|
||||
reload()
|
||||
|
||||
return redirect(url_for('admin.admin', open_checkbox=True))
|
98
bricktracker/views/admin/status.py
Normal file
@ -0,0 +1,98 @@
|
||||
from flask import (
|
||||
Blueprint,
|
||||
jsonify,
|
||||
redirect,
|
||||
request,
|
||||
render_template,
|
||||
url_for,
|
||||
)
|
||||
from flask_login import login_required
|
||||
from werkzeug.wrappers.response import Response
|
||||
|
||||
from ..exceptions import exception_handler
|
||||
from ...reload import reload
|
||||
from ...set_status import BrickSetStatus
|
||||
|
||||
admin_status_page = Blueprint(
|
||||
'admin_status',
|
||||
__name__,
|
||||
url_prefix='/admin/status'
|
||||
)
|
||||
|
||||
|
||||
# Add a metadata status
|
||||
@admin_status_page.route('/add', methods=['POST'])
|
||||
@login_required
|
||||
@exception_handler(
|
||||
__file__,
|
||||
post_redirect='admin.admin',
|
||||
error_name='status_error',
|
||||
open_status=True
|
||||
)
|
||||
def add() -> Response:
|
||||
BrickSetStatus().from_form(request.form).insert()
|
||||
|
||||
reload()
|
||||
|
||||
return redirect(url_for('admin.admin', open_status=True))
|
||||
|
||||
|
||||
# Delete the metadata status
|
||||
@admin_status_page.route('<id>/delete', methods=['GET'])
|
||||
@login_required
|
||||
@exception_handler(__file__)
|
||||
def delete(*, id: str) -> str:
|
||||
return render_template(
|
||||
'admin.html',
|
||||
delete_status=True,
|
||||
status=BrickSetStatus().select_specific(id),
|
||||
error=request.args.get('status_error')
|
||||
)
|
||||
|
||||
|
||||
# Actually delete the metadata status
|
||||
@admin_status_page.route('<id>/delete', methods=['POST'])
|
||||
@login_required
|
||||
@exception_handler(
|
||||
__file__,
|
||||
post_redirect='admin_status.delete',
|
||||
error_name='status_error'
|
||||
)
|
||||
def do_delete(*, id: str) -> Response:
|
||||
status = BrickSetStatus().select_specific(id)
|
||||
status.delete()
|
||||
|
||||
reload()
|
||||
|
||||
return redirect(url_for('admin.admin', open_status=True))
|
||||
|
||||
|
||||
# Change the field of a metadata status
|
||||
@admin_status_page.route('/<id>/field/<name>', methods=['POST'])
|
||||
@login_required
|
||||
@exception_handler(__file__, json=True)
|
||||
def update_field(*, id: str, name: str) -> Response:
|
||||
status = BrickSetStatus().select_specific(id)
|
||||
value = status.update_field(name, json=request.json)
|
||||
|
||||
reload()
|
||||
|
||||
return jsonify({'value': value})
|
||||
|
||||
|
||||
# Rename the metadata status
|
||||
@admin_status_page.route('<id>/rename', methods=['POST'])
|
||||
@login_required
|
||||
@exception_handler(
|
||||
__file__,
|
||||
post_redirect='admin.admin',
|
||||
error_name='status_error',
|
||||
open_status=True
|
||||
)
|
||||
def rename(*, id: str) -> Response:
|
||||
status = BrickSetStatus().select_specific(id)
|
||||
status.from_form(request.form).rename()
|
||||
|
||||
reload()
|
||||
|
||||
return redirect(url_for('admin.admin', open_status=True))
|
@ -2,7 +2,7 @@ from flask import Blueprint, render_template
|
||||
|
||||
from .exceptions import exception_handler
|
||||
from ..minifigure_list import BrickMinifigureList
|
||||
from ..set_checkbox_list import BrickSetCheckboxList
|
||||
from ..set_status_list import BrickSetStatusList
|
||||
from ..set_list import BrickSetList
|
||||
|
||||
index_page = Blueprint('index', __name__)
|
||||
@ -16,5 +16,5 @@ def index() -> str:
|
||||
'index.html',
|
||||
brickset_collection=BrickSetList().last(),
|
||||
minifigure_collection=BrickMinifigureList().last(),
|
||||
brickset_checkboxes=BrickSetCheckboxList().list(),
|
||||
brickset_statuses=BrickSetStatusList().list(),
|
||||
)
|
||||
|
@ -16,7 +16,7 @@ from .exceptions import exception_handler
|
||||
from ..minifigure import BrickMinifigure
|
||||
from ..part import BrickPart
|
||||
from ..set import BrickSet
|
||||
from ..set_checkbox_list import BrickSetCheckboxList
|
||||
from ..set_status_list import BrickSetStatusList
|
||||
from ..set_list import BrickSetList
|
||||
from ..socket import MESSAGES
|
||||
|
||||
@ -32,19 +32,19 @@ def list() -> str:
|
||||
return render_template(
|
||||
'sets.html',
|
||||
collection=BrickSetList().all(),
|
||||
brickset_checkboxes=BrickSetCheckboxList().list(),
|
||||
brickset_statuses=BrickSetStatusList().list(),
|
||||
)
|
||||
|
||||
|
||||
# Change the status of a checkbox
|
||||
# Change the status of a status
|
||||
@set_page.route('/<id>/status/<metadata_id>', methods=['POST'])
|
||||
@login_required
|
||||
@exception_handler(__file__, json=True)
|
||||
def update_status(*, id: str, metadata_id: str) -> Response:
|
||||
brickset = BrickSet().select_light(id)
|
||||
checkbox = BrickSetCheckboxList().get(metadata_id)
|
||||
status = BrickSetStatusList().get(metadata_id)
|
||||
|
||||
state = checkbox.update_set_state(brickset, request.json)
|
||||
state = status.update_set_state(brickset, request.json)
|
||||
|
||||
return jsonify({'value': state})
|
||||
|
||||
@ -97,7 +97,7 @@ def details(*, id: str) -> str:
|
||||
'set.html',
|
||||
item=BrickSet().select_specific(id),
|
||||
open_instructions=request.args.get('open_instructions'),
|
||||
brickset_checkboxes=BrickSetCheckboxList().list(all=True),
|
||||
brickset_statuses=BrickSetStatusList().list(all=True),
|
||||
)
|
||||
|
||||
|
||||
|
@ -14,7 +14,7 @@ This page helps you navigate the documentation of BrickTracker.
|
||||
|
||||
- [First steps](first-steps.md)
|
||||
- [Managing your sets](set.md)
|
||||
- [Managing your set checkboxes](checkbox.md)
|
||||
- [Managing your set statuses](set-statuses.md)
|
||||
|
||||
## Specific procedures
|
||||
|
||||
|
@ -1,58 +0,0 @@
|
||||
# Manage your set chechboxes
|
||||
|
||||
> **Note**
|
||||
> The following page is based on version `1.1.0` of BrickTracker.
|
||||
|
||||
They are useful to store "yes/no" info about a set and quickly set it. Once clicked the change is immediatly stored in the database. A visual indicator tells you the change was succesful.
|
||||
|
||||
![](images/checkbox-01.png)
|
||||
|
||||
## Default checkboxes
|
||||
|
||||
The original version of BrickTracker defined the following checkboxes
|
||||
|
||||
- Minifigures are collected
|
||||
- Set is checked
|
||||
- Set is collected and boxed
|
||||
|
||||
## Visibility
|
||||
|
||||
The checkboxes are **never visible** on the front page. The display here tries to be as minimalistic as possible.
|
||||
|
||||
Prior to version `1.1.0`, the checkboxes were visible both on the Grid view (**Sets**) and the details of a set.
|
||||
|
||||
![](images/checkbox-02.png)
|
||||
![](images/checkbox-03.png)
|
||||
|
||||
From version `1.1.0`, it is possible to decide if a checkbox is visible from the Grid or not. It will always be visible in a set details.
|
||||
|
||||
### Change the visibility of a checkbox
|
||||
|
||||
To change the visibility of a checkbox, head to the **Admin page** and open the **Checkboxes** section.
|
||||
|
||||
![](images/checkbox-04.png)
|
||||
|
||||
Simply click on the **Displayed on the Set Grid** checkbox to select whether it is displayed or not. The change is immediately saved to the database.
|
||||
|
||||
![](images/checkbox-05.png)
|
||||
|
||||
In this example, we have decided to have no checkbox visible on the Grid view.
|
||||
|
||||
![](images/checkbox-06.png)
|
||||
|
||||
## Management
|
||||
|
||||
Starting version `1.1.0`, you can manage the checkboxes for the **Checkboxes** section of the **Admin page**.
|
||||
|
||||
![](images/checkbox-04.png)
|
||||
|
||||
From there you can do the following:
|
||||
|
||||
- Add a new checkbox: use the last line of the list and press the **Add** button
|
||||
- Rename an existing checkbox: use the **Name** field to change the name and press the **Rename** button
|
||||
- Change the Grid display of an existing checkbox: tick or untick the **Displayed on the Set Grid** checkbox
|
||||
- Delete an existing checkbox: use the **Delete** button and confirm on the following screen
|
||||
|
||||
It is possible to delete all the checkboxes, they are an optional component of a set.
|
||||
|
||||
![](images/checkbox-07.png)
|
Before Width: | Height: | Size: 8.8 KiB After Width: | Height: | Size: 8.8 KiB |
Before Width: | Height: | Size: 773 KiB After Width: | Height: | Size: 773 KiB |
Before Width: | Height: | Size: 389 KiB After Width: | Height: | Size: 389 KiB |
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 52 KiB |
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 54 KiB |
Before Width: | Height: | Size: 948 KiB After Width: | Height: | Size: 948 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
67
docs/set-statuses.md
Normal file
@ -0,0 +1,67 @@
|
||||
# Manage your set statuses
|
||||
|
||||
> **Note**
|
||||
> The following page is based on version `1.1.0` of BrickTracker.
|
||||
|
||||
> **Note**
|
||||
> On version `1.2.0`, this feature has been renommed from `Checkboxes` to `Set statuses`. It works exactly the same.
|
||||
|
||||
They are useful to store "yes/no" info about a set and quickly set it. Once clicked the change is immediatly stored in the database. A visual indicator tells you the change was succesful.
|
||||
|
||||
![](images/status-01.png)
|
||||
|
||||
## Default statuses
|
||||
|
||||
The original version of BrickTracker defined the following statuses
|
||||
|
||||
- Minifigures are collected
|
||||
- Set is checked
|
||||
- Set is collected and boxed
|
||||
|
||||
## Visibility
|
||||
|
||||
The statuses are **never visible** on the front page. The display here tries to be as minimalistic as possible.
|
||||
|
||||
Prior to version `1.1.0`, the statuses were visible both on the Grid view (**Sets**) and the details of a set.
|
||||
|
||||
![](images/status-02.png)
|
||||
![](images/status-03.png)
|
||||
|
||||
From version `1.1.0`, it is possible to decide if a status is visible from the Grid or not. It will always be visible in a set details.
|
||||
|
||||
### Change the visibility of a status
|
||||
|
||||
> **Note**
|
||||
> On version `1.2.0`, the Admin page section has been renamed from `Checkboxes` to `Set statuses`. It works exactly the same.
|
||||
|
||||
To change the visibility of a status, head to the **Admin page** and open the **Set statuses** section.
|
||||
|
||||
![](images/status-04.png)
|
||||
|
||||
Simply click on the **Displayed on the Set Grid** status to select whether it is displayed or not. The change is immediately saved to the database.
|
||||
|
||||
![](images/status-05.png)
|
||||
|
||||
In this example, we have decided to have no status visible on the Grid view.
|
||||
|
||||
![](images/status-06.png)
|
||||
|
||||
## Management
|
||||
|
||||
> **Note**
|
||||
> On version `1.2.0`, the Admin page section has been renamed from `Checkboxes` to `Set statuses`. It works exactly the same.
|
||||
|
||||
Starting version `1.1.0`, you can manage the set statuses for the **Set statuses** section of the **Admin page**.
|
||||
|
||||
![](images/status-04.png)
|
||||
|
||||
From there you can do the following:
|
||||
|
||||
- Add a new set status: use the last line of the list and press the **Add** button
|
||||
- Rename an existing set status: use the **Name** field to change the name and press the **Rename** button
|
||||
- Change the Grid display of an existing status: tick or untick the **Displayed on the Set Grid** checkbox
|
||||
- Delete an existing set status: use the **Delete** button and confirm on the following screen
|
||||
|
||||
It is possible to delete all the set statuses, they are an optional component of a set.
|
||||
|
||||
![](images/status-07.png)
|
@ -12,8 +12,8 @@
|
||||
<h5 class="mb-0"><i class="ri-settings-4-line"></i> Administration</h5>
|
||||
</div>
|
||||
<div class="accordion accordion-flush" id="admin">
|
||||
{% if delete_checkbox %}
|
||||
{% include 'admin/checkbox/delete.html' %}
|
||||
{% if delete_status %}
|
||||
{% include 'admin/status/delete.html' %}
|
||||
{% elif delete_database %}
|
||||
{% include 'admin/database/delete.html' %}
|
||||
{% elif drop_database %}
|
||||
@ -30,7 +30,7 @@
|
||||
{% endif %}
|
||||
{% include 'admin/theme.html' %}
|
||||
{% include 'admin/retired.html' %}
|
||||
{% include 'admin/checkbox.html' %}
|
||||
{% include 'admin/status.html' %}
|
||||
{% include 'admin/database.html' %}
|
||||
{% include 'admin/configuration.html' %}
|
||||
{% endif %}
|
||||
|
@ -1,42 +1,42 @@
|
||||
{% import 'macro/accordion.html' as accordion %}
|
||||
|
||||
{{ accordion.header('Checkboxes', 'checkbox', 'admin', expanded=open_checkbox, icon='checkbox-line', class='p-0') }}
|
||||
{% if checkbox_error %}<div class="alert alert-danger m-2" role="alert"><strong>Error:</strong> {{ checkbox_error }}.</div>{% endif %}
|
||||
{{ accordion.header('Set statuses', 'status', 'admin', expanded=open_status, icon='checkbox-line', class='p-0') }}
|
||||
{% if status_error %}<div class="alert alert-danger m-2" role="alert"><strong>Error:</strong> {{ status_error }}.</div>{% endif %}
|
||||
<ul class="list-group list-group-flush">
|
||||
{% if brickset_checkboxes | length %}
|
||||
{% for checkbox in brickset_checkboxes %}
|
||||
{% if metadata_statuses | length %}
|
||||
{% for status in metadata_statuses %}
|
||||
<li class="list-group-item">
|
||||
<form action="{{ url_for('admin_checkbox.rename', id=checkbox.fields.id) }}" method="post" class="row row-cols-lg-auto g-3 align-items-center">
|
||||
<form action="{{ url_for('admin_status.rename', id=status.fields.id) }}" method="post" class="row row-cols-lg-auto g-3 align-items-center">
|
||||
<div class="col-12 flex-grow-1">
|
||||
<label class="visually-hidden" for="name-{{ checkbox.fields.id }}">Name</label>
|
||||
<label class="visually-hidden" for="name-{{ status.fields.id }}">Name</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-text">Name</div>
|
||||
<input type="text" class="form-control" id="name-{{ checkbox.fields.id }}" name="name" value="{{ checkbox.fields.name }}">
|
||||
<input type="text" class="form-control" id="name-{{ status.fields.id }}" name="name" value="{{ status.fields.name }}">
|
||||
<button type="submit" class="btn btn-primary"><i class="ri-edit-line"></i> Rename</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="grid-{{ checkbox.fields.id }}"
|
||||
data-changer-id="{{ checkbox.fields.id }}" data-changer-prefix="grid" data-changer-url="{{ url_for('admin_checkbox.update_field', id=checkbox.fields.id, name='displayed_on_grid')}}"
|
||||
{% if checkbox.fields.displayed_on_grid %}checked{% endif %} autocomplete="off">
|
||||
<label class="form-check-label" for="grid-{{ checkbox.fields.id }}">
|
||||
<input class="form-check-input" type="checkbox" id="grid-{{ status.fields.id }}"
|
||||
data-changer-id="{{ status.fields.id }}" data-changer-prefix="grid" data-changer-url="{{ url_for('admin_status.update_field', id=status.fields.id, name='displayed_on_grid')}}"
|
||||
{% if status.fields.displayed_on_grid %}checked{% endif %} autocomplete="off">
|
||||
<label class="form-check-label" for="grid-{{ status.fields.id }}">
|
||||
<i class="ri-grid-line"></i> Displayed on the Set Grid
|
||||
<i id="status-grid-{{ checkbox.fields.id }}" class="mb-1"></i>
|
||||
<i id="status-grid-{{ status.fields.id }}" class="mb-1"></i>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<a href="{{ url_for('admin_checkbox.delete', id=checkbox.fields.id) }}" class="btn btn-danger" role="button"><i class="ri-delete-bin-2-line"></i> Delete</a>
|
||||
<a href="{{ url_for('admin_status.delete', id=status.fields.id) }}" class="btn btn-danger" role="button"><i class="ri-delete-bin-2-line"></i> Delete</a>
|
||||
</div>
|
||||
</form>
|
||||
</li>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<li class="list-group-item"><i class="ri-error-warning-line"></i> No checkbox found.</li>
|
||||
<li class="list-group-item"><i class="ri-error-warning-line"></i> No status found.</li>
|
||||
{% endif %}
|
||||
<li class="list-group-item">
|
||||
<form action="{{ url_for('admin_checkbox.add') }}" method="post" class="row row-cols-lg-auto g-3 align-items-center">
|
||||
<form action="{{ url_for('admin_status.add') }}" method="post" class="row row-cols-lg-auto g-3 align-items-center">
|
||||
<div class="col-12 flex-grow-1">
|
||||
<label class="visually-hidden" for="name">Name</label>
|
||||
<div class="input-group">
|
@ -1,25 +1,25 @@
|
||||
{% import 'macro/accordion.html' as accordion %}
|
||||
|
||||
{{ accordion.header('Checkbox danger zone', 'checkbox-danger', 'admin', expanded=true, danger=true, class='text-end') }}
|
||||
<form action="{{ url_for('admin_checkbox.do_delete', id=checkbox.fields.id) }}" method="post">
|
||||
{{ accordion.header('Set statuses danger zone', 'status-danger', 'admin', expanded=true, danger=true, class='text-end') }}
|
||||
<form action="{{ url_for('admin_status.do_delete', id=status.fields.id) }}" method="post">
|
||||
{% if error %}<div class="alert alert-danger text-start" role="alert"><strong>Error:</strong> {{ error }}.</div>{% endif %}
|
||||
<div class="alert alert-danger text-center" role="alert">You are about to <strong>delete a checkbox</strong>. This action is irreversible.</div>
|
||||
<div class="alert alert-danger text-center" role="alert">You are about to <strong>delete a set status</strong>. This action is irreversible.</div>
|
||||
<div class="row row-cols-lg-auto g-3 align-items-center">
|
||||
<div class="col-12 flex-grow-1">
|
||||
<div class="input-group">
|
||||
<div class="input-group-text">Name</div>
|
||||
<input type="text" class="form-control" value="{{ checkbox.fields.name }}" disabled>
|
||||
<input type="text" class="form-control" value="{{ status.fields.name }}" disabled>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" {% if checkbox.fields.displayed_on_grid %}checked{% endif %} disabled>
|
||||
<input class="form-check-input" type="checkbox" {% if status.fields.displayed_on_grid %}checked{% endif %} disabled>
|
||||
<span class="form-check-label">Displayed on the Set Grid</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr class="border-bottom">
|
||||
<a class="btn btn-danger" href="{{ url_for('admin.admin', open_checkbox=true) }}" role="button"><i class="ri-arrow-left-long-line"></i> Back to the admin</a>
|
||||
<button type="submit" class="btn btn-danger"><i class="ri-delete-bin-2-line"></i> Delete <strong>the checkbox</strong></button>
|
||||
<a class="btn btn-danger" href="{{ url_for('admin.admin', open_status=true) }}" role="button"><i class="ri-arrow-left-long-line"></i> Back to the admin</a>
|
||||
<button type="submit" class="btn btn-danger"><i class="ri-delete-bin-2-line"></i> Delete <strong>the set status</strong></button>
|
||||
</form>
|
||||
{{ accordion.footer() }}
|
@ -8,7 +8,7 @@
|
||||
data-index="{{ index }}" data-number="{{ item.fields.set }}" data-name="{{ item.fields.name | lower }}" data-parts="{{ item.fields.number_of_parts }}"
|
||||
data-year="{{ item.fields.year }}" data-theme="{{ item.theme.name | lower }}" data-minifigures="{{ item.fields.total_minifigures }}" data-has-minifigures="{{ (item.fields.total_minifigures > 0) | int }}"
|
||||
data-has-missing="{{ (item.fields.total_missing > 0) | int }}" data-has-missing-instructions="{{ (not (item.instructions | length)) | int }}" data-missing="{{ item.fields.total_missing }}"
|
||||
{% for checkbox in brickset_checkboxes %}data-{{ checkbox.as_dataset() }}="{{ item.fields[checkbox.as_column()] }}" {% endfor %}
|
||||
{% for status in brickset_statuses %}data-{{ status.as_dataset() }}="{{ item.fields[status.as_column()] }}" {% endfor %}
|
||||
{% endif %}
|
||||
>
|
||||
{{ card.header(item, item.fields.name, solo=solo, identifier=item.fields.set) }}
|
||||
@ -26,11 +26,11 @@
|
||||
{{ badge.rebrickable(item, solo=solo, last=last) }}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if not tiny and brickset_checkboxes | length %}
|
||||
{% if not tiny and brickset_statuses | length %}
|
||||
<ul class="list-group list-group-flush card-check border-bottom-0">
|
||||
{% for checkbox in brickset_checkboxes %}
|
||||
{% for status in brickset_statuses %}
|
||||
<li class="list-group-item {% if not solo %}p-1{% endif %}">
|
||||
{{ form.checkbox(checkbox.as_dataset(), item.fields.id, checkbox.fields.name, checkbox.url_for_set_state(item.fields.id), item.fields[checkbox.as_column()], delete=delete) }}
|
||||
{{ form.checkbox(status.as_dataset(), item.fields.id, status.fields.name, status.url_for_set_state(item.fields.id), item.fields[status.as_column()], delete=delete) }}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
@ -22,9 +22,9 @@
|
||||
<option value="-has-missing">Set is complete</option>
|
||||
<option value="has-missing">Set has missing pieces</option>
|
||||
<option value="has-missing-instructions">Set has missing instructions</option>
|
||||
{% for checkbox in brickset_checkboxes %}
|
||||
<option value="{{ checkbox.as_dataset() }}">{{ checkbox.fields.name }}</option>
|
||||
<option value="-{{ checkbox.as_dataset() }}">NOT: {{ checkbox.fields.name }}</option>
|
||||
{% for status in brickset_statuses %}
|
||||
<option value="{{ status.as_dataset() }}">{{ status.fields.name }}</option>
|
||||
<option value="-{{ status.as_dataset() }}">NOT: {{ status.fields.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<select id="grid-theme" class="form-select form-select-sm" autocomplete="off">
|
||||
|