"""
return render_template_string(edit_point_html, point=point, panorama_url=panorama_url,
all_points=all_points, all_categories=all_categories,
all_standalone_products=all_standalone_products)
@app.route('/admin/points//hotspots/save', methods=['POST'])
def admin_save_hotspot(point_id):
point = find_point(point_id)
if not point:
return redirect(url_for('admin_panel'))
try:
pitch_str = request.form.get('pitch')
yaw_str = request.form.get('yaw')
text = request.form.get('text')
type = request.form.get('type')
hotspot_id = request.form.get('hotspot_id')
if not pitch_str or not yaw_str:
logging.warning("Координаты Pitch/Yaw отсутствуют. Кликните на панораму.")
return redirect(url_for('admin_edit_point', point_id=point_id))
pitch = float(pitch_str)
yaw = float(yaw_str)
target = None
if type == 'point':
target = request.form.get('target_point_id')
elif type == 'link':
target = request.form.get('target_link_url')
elif type == 'category':
target = request.form.get('target_category_id')
elif type == 'product':
target = request.form.get('target_product_id')
if not all([pitch is not None, yaw is not None, text, type, target]):
logging.warning("Отсутствуют данные для сохранения хотспота.")
return redirect(url_for('admin_edit_point', point_id=point_id))
new_hotspot = {
"id": generate_id(),
"pitch": pitch,
"yaw": yaw,
"type": type,
"target": target,
"text": text
}
point.setdefault('hotspots', []).append(new_hotspot)
save_data()
logging.info(f"Добавлен хотспот к поинту {point_id}: Тип={type}, Цель={target}")
except ValueError:
logging.error("Неверный формат числа для pitch или yaw.")
except Exception as e:
logging.error(f"Ошибка сохранения хотспота для поинта {point_id}: {e}")
return redirect(url_for('admin_edit_point', point_id=point_id))
@app.route('/admin/points//hotspots/delete/', methods=['POST'])
def admin_delete_hotspot(point_id, hotspot_id):
point = find_point(point_id)
if point and 'hotspots' in point:
initial_length = len(point['hotspots'])
point['hotspots'] = [hs for hs in point['hotspots'] if hs.get('id') != hotspot_id]
if len(point['hotspots']) < initial_length:
save_data()
logging.info(f"Удален хотспот {hotspot_id} из поинта {point_id}")
else:
logging.warning(f"Хотспот {hotspot_id} не найден в поинте {point_id} для удаления.")
else:
logging.warning(f"Поинт {point_id} не найден или не имеет хотспотов для удаления.")
return redirect(url_for('admin_edit_point', point_id=point_id))
@app.route('/admin/categories/add', methods=['POST'])
def admin_add_category():
category_name = request.form.get('category_name')
if category_name:
if any(c.get('name', '').lower() == category_name.lower() for c in app_data.get('categories', [])):
logging.warning(f"Попытка добавить дубликат имени категории: {category_name}")
else:
new_category = {
"id": generate_id(),
"name": category_name,
"products": []
}
app_data.setdefault('categories', []).append(new_category)
save_data()
logging.info(f"Добавлена категория: {category_name}")
else:
logging.warning("Попытка добавить категорию с пустым именем.")
return redirect(url_for('admin_panel'))
@app.route('/admin/categories/delete/', methods=['POST'])
def admin_delete_category(category_id):
category_index = next((index for (index, c) in enumerate(app_data.get('categories', [])) if c.get('id') == category_id), None)
if category_index is not None:
category = app_data['categories'][category_index]
category_name = category.get('name', category_id)
products_to_delete = category.get('products', [])
hf_delete_failed_overall = False
for product in products_to_delete:
if delete_product_files_from_hf(product):
hf_delete_failed_overall = True
deleted_category = app_data['categories'].pop(category_index)
save_data()
logging.info(f"Удалена категория: {category_name} и ее {len(products_to_delete)} товаров.")
for product in products_to_delete:
cleanup_local_product_files(product)
if hf_delete_failed_overall:
pass
else:
pass
else:
logging.warning(f"Попытка удалить несуществующую категорию с ID: {category_id}")
return redirect(url_for('admin_panel'))
@app.route('/admin/category//products', methods=['GET'])
def admin_manage_category_products(category_id):
category = find_category(category_id)
if not category:
return redirect(url_for('admin_panel'))
products_with_urls = []
for product in category.get('products', []):
photo_urls = [get_hf_image_url(REPO_ID, p) for p in product.get('photo_paths', []) if p]
products_with_urls.append({**product, 'photo_urls': photo_urls})
manage_products_html = """
Управление Товарами: {{ category.name }}
Управление Товарами в Категории: {{ category.name }}
{% if product.photo_urls %}
{% for url in product.photo_urls %}
{% endfor %}
{% else %}
Нет фото
{% endif %}
Редактировать: {{ product.name }}
{% else %}
В этой категории пока нет товаров.
{% endfor %}
"""
return render_template_string(manage_products_html, category=category, products=products_with_urls)
@app.route('/admin/category//products/add', methods=['POST'])
def admin_add_product_to_category(category_id):
category = find_category(category_id)
if not category:
return redirect(url_for('admin_panel'))
name = request.form.get('name')
price = request.form.get('price')
description = request.form.get('description')
photos = request.files.getlist('photos')
if not name or price is None or description is None:
return redirect(url_for('admin_manage_category_products', category_id=category_id))
try:
price_float = float(price)
if price_float < 0: raise ValueError("Цена не может быть отрицательной")
except ValueError:
return redirect(url_for('admin_manage_category_products', category_id=category_id))
photo_hf_paths = []
upload_failed = False
uploaded_local_paths = []
for photo in photos[:10]:
if photo and photo.filename:
filename = secure_filename(photo.filename)
unique_filename = f"{generate_id()}_{filename}"
local_path = os.path.join(PRODUCT_PHOTO_UPLOADS, unique_filename)
hf_path = f"product_photos/{unique_filename}"
uploaded_local_paths.append(local_path)
try:
photo.save(local_path)
commit_msg = f"Добавить фото для товара: {name} в категорию {category.get('name')}"
if upload_file_to_hf(local_path, hf_path, REPO_ID, HF_TOKEN_WRITE, commit_msg):
photo_hf_paths.append(hf_path)
else:
upload_failed = True
logging.error(f"Не удалось загрузить фото товара {filename} в HF.")
except Exception as e:
upload_failed = True
logging.error(f"Ошибка обработки фото товара {filename}: {e}")
new_product = {
"id": generate_id(),
"name": name,
"price": price_float,
"description": description,
"photo_paths": photo_hf_paths
}
category.setdefault('products', []).append(new_product)
save_data()
logging.info(f"Добавлен товар '{name}' в категорию '{category.get('name')}'. Фото: {len(photo_hf_paths)}.")
if upload_failed:
pass
else:
pass
for local_path in uploaded_local_paths:
if os.path.exists(local_path):
try:
os.remove(local_path)
except OSError as e:
logging.warning(f"Не удалось удалить временный локальный файл фото товара {local_path}: {e}")
return redirect(url_for('admin_manage_category_products', category_id=category_id))
@app.route('/admin/category//products/edit/', methods=['POST'])
def admin_edit_product_in_category(category_id, product_id):
category = find_category(category_id)
product = find_product_in_category(category_id, product_id)
if not category or not product:
return redirect(url_for('admin_panel'))
name = request.form.get('name')
price = request.form.get('price')
description = request.form.get('description')
new_photos = request.files.getlist('photos')
if not name or price is None or description is None:
return redirect(url_for('admin_manage_category_products', category_id=category_id))
try:
price_float = float(price)
if price_float < 0: raise ValueError("Цена не может быть отрицательной")
except ValueError:
return redirect(url_for('admin_manage_category_products', category_id=category_id))
product['name'] = name
product['price'] = price_float
product['description'] = description
if new_photos and any(p.filename for p in new_photos):
logging.info(f"Замена фото для товара {product_id} в категории {category_id}...")
old_photo_paths = product.get('photo_paths', [])
new_photo_hf_paths = []
upload_failed = False
uploaded_local_paths = []
for photo in new_photos[:10]:
if photo and photo.filename:
filename = secure_filename(photo.filename)
unique_filename = f"{generate_id()}_{filename}"
local_path = os.path.join(PRODUCT_PHOTO_UPLOADS, unique_filename)
hf_path = f"product_photos/{unique_filename}"
uploaded_local_paths.append(local_path)
try:
photo.save(local_path)
commit_msg = f"Обновить фото для товара: {name}"
if upload_file_to_hf(local_path, hf_path, REPO_ID, HF_TOKEN_WRITE, commit_msg):
new_photo_hf_paths.append(hf_path)
else:
upload_failed = True
logging.error(f"Не удалось загрузить новое фото {filename} в HF.")
except Exception as e:
upload_failed = True
logging.error(f"Ошибка обработки нового фото {filename}: {e}")
if len(new_photo_hf_paths) > 0:
logging.info(f"Удаление {len(old_photo_paths)} старых фото для товара {product_id} из HF...")
hf_delete_failed = False
for old_hf_path in old_photo_paths:
commit_msg = f"Удалить старое фото {os.path.basename(old_hf_path)} для товара {name} при обновлении"
if not delete_file_from_hf(old_hf_path, REPO_ID, HF_TOKEN_WRITE, commit_msg):
logging.warning(f"Не удалось удалить старое фото {old_hf_path} из HF при обновлении.")
hf_delete_failed = True
product['photo_paths'] = new_photo_hf_paths
if hf_delete_failed:
pass
elif upload_failed:
logging.error(f"Замена фото не удалась для товара {product_id}. Старые фото сохранены.")
for local_path in uploaded_local_paths:
if os.path.exists(local_path):
try:
os.remove(local_path)
except OSError as e:
logging.warning(f"Не удалось удалить временный локальный файл нового фото {local_path}: {e}")
save_data()
logging.info(f"Обновлен товар '{name}' (ID: {product_id}) в категории '{category.get('name')}'.")
return redirect(url_for('admin_manage_category_products', category_id=category_id))
@app.route('/admin/category//products/delete/', methods=['POST'])
def admin_delete_product_from_category(category_id, product_id):
category = find_category(category_id)
if not category or 'products' not in category:
return redirect(url_for('admin_panel'))
product_index = next((index for (index, p) in enumerate(category['products']) if p.get('id') == product_id), None)
if product_index is not None:
product = category['products'][product_index]
product_name = product.get('name', product_id)
hf_delete_failed = delete_product_files_from_hf(product)
deleted_product = category['products'].pop(product_index)
save_data()
logging.info(f"Удален товар: {product_name} из категории {category.get('name')}")
cleanup_local_product_files(product)
if hf_delete_failed:
pass
else:
pass
else:
logging.warning(f"Товар {product_id} не найден в категории {category_id} для удаления.")
return redirect(url_for('admin_manage_category_products', category_id=category_id))
@app.route('/admin/products/standalone', methods=['GET'])
def admin_manage_standalone_products():
products_with_urls = []
for product in app_data.get('standalone_products', []):
photo_urls = [get_hf_image_url(REPO_ID, p) for p in product.get('photo_paths', []) if p]
products_with_urls.append({**product, 'photo_urls': photo_urls})
manage_standalone_html = """
Управление Отдельными Товарами
Управление Отдельными Товарами
Эти товары можно выбирать напрямую при создании хотспота типа "Товар". Они не принадлежат к какой-либо категории.