fix(individual-parts): bulk add image download and storage deletion validation
This commit is contained in:
@@ -803,6 +803,19 @@ class IndividualPart(BrickRecord):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Download part image if available
|
||||||
|
image_url = color_info.get('part_img_url', '')
|
||||||
|
if image_url:
|
||||||
|
try:
|
||||||
|
self.download_image(image_url)
|
||||||
|
except Exception as e:
|
||||||
|
# Don't fail the whole operation if image download fails
|
||||||
|
logger.warning('Could not download image for part {part_num} color {color_id}: {error}'.format(
|
||||||
|
part_num=part_num,
|
||||||
|
color_id=color_id,
|
||||||
|
error=e
|
||||||
|
))
|
||||||
|
|
||||||
parts_added += 1
|
parts_added += 1
|
||||||
|
|
||||||
# Commit all changes
|
# Commit all changes
|
||||||
@@ -816,7 +829,7 @@ class IndividualPart(BrickRecord):
|
|||||||
|
|
||||||
# Generate link to individual parts list
|
# Generate link to individual parts list
|
||||||
from flask import url_for
|
from flask import url_for
|
||||||
parts_url = url_for('individual_part.list_all')
|
parts_url = url_for('individual_part.list')
|
||||||
|
|
||||||
# Send completion with message and link
|
# Send completion with message and link
|
||||||
socket.complete(
|
socket.complete(
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
from .metadata import BrickMetadata
|
from .metadata import BrickMetadata
|
||||||
|
from .exceptions import ErrorException
|
||||||
|
from .sql import BrickSQL
|
||||||
|
|
||||||
from flask import url_for
|
from flask import url_for
|
||||||
|
|
||||||
@@ -13,6 +15,7 @@ class BrickSetStorage(BrickMetadata):
|
|||||||
select_query: str = 'set/metadata/storage/select'
|
select_query: str = 'set/metadata/storage/select'
|
||||||
update_field_query: str = 'set/metadata/storage/update/field'
|
update_field_query: str = 'set/metadata/storage/update/field'
|
||||||
update_set_value_query: str = 'set/metadata/storage/update/value'
|
update_set_value_query: str = 'set/metadata/storage/update/value'
|
||||||
|
count_usage_query: str = 'set/metadata/storage/count_usage'
|
||||||
|
|
||||||
# Self url
|
# Self url
|
||||||
def url(self, /) -> str:
|
def url(self, /) -> str:
|
||||||
@@ -20,3 +23,52 @@ class BrickSetStorage(BrickMetadata):
|
|||||||
'storage.details',
|
'storage.details',
|
||||||
id=self.fields.id,
|
id=self.fields.id,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Delete from database - check if storage is in use first
|
||||||
|
def delete(self, /) -> None:
|
||||||
|
# Check if storage is being used
|
||||||
|
sql = BrickSQL()
|
||||||
|
result = sql.fetchone(self.count_usage_query, parameters={'id': self.fields.id})
|
||||||
|
|
||||||
|
if result:
|
||||||
|
sets_count = result[0]
|
||||||
|
minifigures_count = result[1]
|
||||||
|
parts_count = result[2]
|
||||||
|
lots_count = result[3]
|
||||||
|
|
||||||
|
total_count = sets_count + minifigures_count + parts_count + lots_count
|
||||||
|
|
||||||
|
if total_count > 0:
|
||||||
|
# Build error message with counts and link
|
||||||
|
error_parts = []
|
||||||
|
if sets_count > 0:
|
||||||
|
error_parts.append('{count} set{plural}'.format(
|
||||||
|
count=sets_count,
|
||||||
|
plural='s' if sets_count != 1 else ''
|
||||||
|
))
|
||||||
|
if minifigures_count > 0:
|
||||||
|
error_parts.append('{count} individual minifigure{plural}'.format(
|
||||||
|
count=minifigures_count,
|
||||||
|
plural='s' if minifigures_count != 1 else ''
|
||||||
|
))
|
||||||
|
if parts_count > 0:
|
||||||
|
error_parts.append('{count} individual part{plural}'.format(
|
||||||
|
count=parts_count,
|
||||||
|
plural='s' if parts_count != 1 else ''
|
||||||
|
))
|
||||||
|
if lots_count > 0:
|
||||||
|
error_parts.append('{count} part lot{plural}'.format(
|
||||||
|
count=lots_count,
|
||||||
|
plural='s' if lots_count != 1 else ''
|
||||||
|
))
|
||||||
|
|
||||||
|
error_message = 'Cannot delete storage location "{name}". You need to remove {items} from this storage before it can be deleted. <a href="{url}">View storage details</a>'.format(
|
||||||
|
name=self.fields.name,
|
||||||
|
items=', '.join(error_parts),
|
||||||
|
url=self.url()
|
||||||
|
)
|
||||||
|
|
||||||
|
raise ErrorException(error_message)
|
||||||
|
|
||||||
|
# If not in use, proceed with deletion
|
||||||
|
super().delete()
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
-- Count how many items are using this storage location
|
||||||
|
SELECT
|
||||||
|
(SELECT COUNT(*) FROM bricktracker_sets WHERE storage = :id) as sets_count,
|
||||||
|
(SELECT COUNT(*) FROM bricktracker_individual_minifigures WHERE storage = :id) as minifigures_count,
|
||||||
|
(SELECT COUNT(*) FROM bricktracker_individual_parts WHERE storage = :id) as parts_count,
|
||||||
|
(SELECT COUNT(*) FROM bricktracker_individual_part_lots WHERE storage = :id) as lots_count
|
||||||
@@ -45,7 +45,7 @@ def delete(*, id: str) -> str:
|
|||||||
'admin.html',
|
'admin.html',
|
||||||
delete_storage=True,
|
delete_storage=True,
|
||||||
storage=BrickSetStorage().select_specific(id),
|
storage=BrickSetStorage().select_specific(id),
|
||||||
error=request.args.get('storage_error')
|
storage_error=request.args.get('storage_error')
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
{{ accordion.header('Set storages danger zone', 'storage-danger', 'admin', expanded=true, danger=true, class='text-end') }}
|
{{ accordion.header('Set storages danger zone', 'storage-danger', 'admin', expanded=true, danger=true, class='text-end') }}
|
||||||
<form action="{{ url_for('admin_storage.do_delete', id=storage.fields.id) }}" method="post">
|
<form action="{{ url_for('admin_storage.do_delete', id=storage.fields.id) }}" method="post">
|
||||||
{% if storage_error %}<div class="alert alert-danger text-start" role="alert"><strong>Error:</strong> {{ storage_error }}.</div>{% endif %}
|
{% if storage_error %}<div class="alert alert-danger text-start" role="alert"><strong>Error:</strong> {{ storage_error|safe }}</div>{% endif %}
|
||||||
<div class="alert alert-danger text-center" role="alert">You are about to <strong>delete a set storage</strong>. This action is irreversible.</div>
|
<div class="alert alert-danger text-center" role="alert">You are about to <strong>delete a set storage</strong>. This action is irreversible.</div>
|
||||||
<div class="row row-cols-lg-auto g-3 align-items-center">
|
<div class="row row-cols-lg-auto g-3 align-items-center">
|
||||||
<div class="col-12 flex-grow-1">
|
<div class="col-12 flex-grow-1">
|
||||||
|
|||||||
Reference in New Issue
Block a user