Веб-помощник для домашнего склада: помнит, в какой коробке и в каком месте лежит вещь, и находит её по названию, тегам и маркировке. Открывается с телефона, интеллект — локальная Ollama. Основное: - структурный ввод «место → ёмкость → предмет» и кнопки Положить / Забрать / Переместить / Удалить; - иерархия мест (Гараж → Стеллаж А → Полка 2), ёмкости с кодом «буква+цифра», состояние «на руках» для взятых вещей; - поиск по словам с учётом русских окончаний, теги от модели расширяют словарь предмета, смысловой поиск — вспомогательный (замеры в search.py); - фото к вещам и коробкам: съёмка и выбор из галереи; - правка и удаление всех сущностей, удаление коробки не теряет её содержимое; - работает и без Ollama: отключаются только разбор фраз, теги и поиск по смыслу. Данные (data/, static/photos/) и локальный конфиг (.env) в репозиторий не попадают. Тесты работают только в песочнице: путь к данным берётся из окружения, tests/guard.py отказывается запускаться на живых данных. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
60 lines
2.4 KiB
Python
60 lines
2.4 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""Защита живых данных. Импортировать ПЕРВЫМ в любом тесте.
|
|
|
|
История: тест начинался со `shutil.rmtree(DATA_DIR)`, DATA_DIR указывал на
|
|
рабочую папку, и один прогон уничтожил реальную базу с вещами. Восстановить
|
|
её не удалось. Поэтому теперь тест обязан работать в песочнице, а проверка
|
|
этого — не пожелание, а условие запуска.
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
import atexit
|
|
import shutil
|
|
import tempfile
|
|
from pathlib import Path
|
|
|
|
|
|
def sandbox():
|
|
"""Переводит приложение на временную папку и возвращает её.
|
|
|
|
Вызывать ДО импорта hlamingo.* — переменная окружения читается в config
|
|
на этапе импорта. Если живые данные всё же оказались подключены, тест
|
|
падает, а не удаляет их.
|
|
"""
|
|
root = Path(tempfile.mkdtemp(prefix="hlamingo-test-"))
|
|
os.environ["HLAMINGO_DATA_DIR"] = str(root)
|
|
|
|
for name in list(sys.modules):
|
|
if name == "hlamingo" or name.startswith("hlamingo."):
|
|
del sys.modules[name] # чтобы config перечитал окружение
|
|
|
|
from hlamingo import config
|
|
if config.IS_LIVE_DATA:
|
|
raise SystemExit("ОТКАЗ: приложение смотрит на живые данные. Тест не запускается.")
|
|
if root not in config.DATA_DIR.parents and config.DATA_DIR != root / "data":
|
|
raise SystemExit("ОТКАЗ: база вне песочницы (%s)." % config.DATA_DIR)
|
|
|
|
atexit.register(lambda: shutil.rmtree(str(root), ignore_errors=True))
|
|
return root
|
|
|
|
|
|
class Report(object):
|
|
"""Счётчик проверок с человеческим выводом."""
|
|
|
|
def __init__(self):
|
|
self.ok = 0
|
|
self.fail = 0
|
|
|
|
def check(self, label, cond, extra=""):
|
|
if cond:
|
|
self.ok += 1
|
|
print(" OK %s" % label)
|
|
else:
|
|
self.fail += 1
|
|
print(" FAIL %s %s" % (label, extra))
|
|
|
|
def finish(self):
|
|
print("\n%s успешно: %d, провалено: %d\n" % ("=" * 40, self.ok, self.fail))
|
|
return 1 if self.fail else 0
|