feat(etl): stable pipeline, exception registry in SQLite, multi-pass aggregation and db_cli
This commit is contained in:
+132
-49
@@ -7,7 +7,7 @@ from datetime import datetime
|
||||
|
||||
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
from config import DATA_DIR, DATE_TODAY, OUTPUT_DIR
|
||||
from config import DATA_DIR, DATE_TODAY, OUTPUT_DIR, EXCEPTIONS_PATH, normalize_fio
|
||||
from core.database import (
|
||||
get_connection,
|
||||
get_available_snapshots,
|
||||
@@ -15,9 +15,16 @@ from core.database import (
|
||||
load_scud_from_db_by_snapshot,
|
||||
get_latest_snapshot_time
|
||||
)
|
||||
from services.exceptions_repo import (
|
||||
get_all_exceptions_from_db,
|
||||
add_exception_to_db,
|
||||
remove_exception_from_db,
|
||||
sync_json_to_db
|
||||
)
|
||||
|
||||
DB_PATH = os.path.join(DATA_DIR, "scud_orion_ai.db")
|
||||
|
||||
|
||||
def print_tool_actions():
|
||||
"""Выводит реестр декларативных действий инструментов и шаблоны кнопок."""
|
||||
print("\n" + "=" * 110)
|
||||
@@ -25,25 +32,29 @@ def print_tool_actions():
|
||||
print("=" * 110)
|
||||
with get_connection() as conn:
|
||||
cursor = conn.cursor()
|
||||
cursor.execute("""
|
||||
SELECT id, tool_name, category, bypass_llm, success_template, follow_up_question, buttons_json
|
||||
FROM tool_action_registry
|
||||
WHERE is_active = 1
|
||||
ORDER BY id ASC
|
||||
""")
|
||||
rows = cursor.fetchall()
|
||||
if not rows:
|
||||
print("Таблица tool_action_registry пуста.")
|
||||
else:
|
||||
for r in rows:
|
||||
print(f"ID: {r[0]} | Tool: [{r[1]}] | Категория: {r[2]} | Bypass LLM: {'ДА (0.05с)' if r[3] else 'НЕТ'}")
|
||||
print(f" • Сообщение: {r[4]}")
|
||||
if r[5]:
|
||||
print(f" • Вопрос: {r[5]}")
|
||||
print(f" • Кнопки: {r[6]}")
|
||||
print("-" * 110)
|
||||
try:
|
||||
cursor.execute("""
|
||||
SELECT id, tool_name, category, bypass_llm, success_template, follow_up_question, buttons_json
|
||||
FROM tool_action_registry
|
||||
WHERE is_active = 1
|
||||
ORDER BY id ASC
|
||||
""")
|
||||
rows = cursor.fetchall()
|
||||
if not rows:
|
||||
print("Таблица tool_action_registry пуста.")
|
||||
else:
|
||||
for r in rows:
|
||||
print(f"ID: {r[0]} | Tool: [{r[1]}] | Категория: {r[2]} | Bypass LLM: {'ДА (0.05с)' if r[3] else 'НЕТ'}")
|
||||
print(f" • Сообщение: {r[4]}")
|
||||
if r[5]:
|
||||
print(f" • Вопрос: {r[5]}")
|
||||
print(f" • Кнопки: {r[6]}")
|
||||
print("-" * 110)
|
||||
except Exception as e:
|
||||
print(f"Таблица tool_action_registry недоступна: {e}")
|
||||
print("=" * 110 + "\n")
|
||||
|
||||
|
||||
def print_stats():
|
||||
"""Выводит общую статистику по записям в таблицах БД."""
|
||||
print("\n" + "=" * 60)
|
||||
@@ -51,12 +62,16 @@ def print_stats():
|
||||
print("=" * 60)
|
||||
with get_connection() as conn:
|
||||
cursor = conn.cursor()
|
||||
tables = ['scud_logs', 'zup_staff', 'zup_absences', 'anomalies_history', 'ai_knowledge_base', 'chat_messages', 'session_states', 'system_prompt_nodes', 'tasks']
|
||||
tables = [
|
||||
'scud_logs', 'zup_staff', 'zup_absences', 'anomalies_history',
|
||||
'ai_knowledge_base', 'chat_messages', 'session_states',
|
||||
'system_prompt_nodes', 'tasks', 'exceptions_registry'
|
||||
]
|
||||
for t in tables:
|
||||
try:
|
||||
cursor.execute(f"SELECT COUNT(*) FROM {t}")
|
||||
cnt = cursor.fetchone()[0]
|
||||
print(f" • Таблица [{t:<20}]: {cnt:>6} записей")
|
||||
print(f" • Таблица [{t:<22}]: {cnt:>6} записей")
|
||||
except Exception:
|
||||
pass
|
||||
print("=" * 60 + "\n")
|
||||
@@ -82,13 +97,11 @@ def print_snapshots_list(date_str=None):
|
||||
def snapshot_sort_key(row):
|
||||
snap_id = row[0] or ""
|
||||
snap_time = row[2] or ""
|
||||
|
||||
seq_num = 0
|
||||
if "-" in snap_id:
|
||||
parts = snap_id.replace("Y", "").split("-")
|
||||
if len(parts) > 1 and parts[1].isdigit():
|
||||
seq_num = int(parts[1])
|
||||
|
||||
return (snap_time, seq_num)
|
||||
|
||||
sorted_rows = sorted(rows, key=snapshot_sort_key, reverse=True)
|
||||
@@ -104,11 +117,7 @@ def print_snapshots_list(date_str=None):
|
||||
time_part = snap_time.split(" ")[1]
|
||||
|
||||
slice_datetime_str = f"{log_date} {time_part}" if time_part != "—" else log_date
|
||||
|
||||
if not snap_id.startswith("Y"):
|
||||
formatted_snap_id = f" {snap_id}"
|
||||
else:
|
||||
formatted_snap_id = snap_id
|
||||
formatted_snap_id = f" {snap_id}" if not snap_id.startswith("Y") else snap_id
|
||||
|
||||
print(f"{formatted_snap_id:<16} | {snap_time:<24} | {slice_datetime_str:<20} | {count:<8}")
|
||||
|
||||
@@ -141,31 +150,44 @@ def inspect_scud(snapshot_id=None, date_str=None, export_xlsx=None):
|
||||
"""Инспектирует логи СКУД за выбранный снапшот или дату и опционально сохраняет XLSX."""
|
||||
target_date = date_str if date_str else DATE_TODAY
|
||||
|
||||
print("\n" + "=" * 90)
|
||||
print("\n" + "=" * 115)
|
||||
if snapshot_id:
|
||||
print(f"🔍 ИНСПЕКЦИЯ СКУД ПО СНАПШОТУ [{snapshot_id}] (Дата: {target_date}):")
|
||||
print(f"🔍 ИНСПЕКЦИЯ СКУД ПО СНАПШОТУ [{snapshot_id}] (Дата среза: {target_date}):")
|
||||
else:
|
||||
print(f"🔍 ИНСПЕКЦИЯ СКУД ЗА ТЕКУЩУЮ ДАТУ [{target_date}] (ПОСЛЕДНИЙ СРЕЗ):")
|
||||
print("=" * 90)
|
||||
print("=" * 115)
|
||||
|
||||
df = load_scud_from_db_by_snapshot(target_date, snapshot_param=snapshot_id)
|
||||
|
||||
if df.empty:
|
||||
if df is None or df.empty:
|
||||
print("Записи СКУД не найдены.")
|
||||
print("=" * 90 + "\n")
|
||||
print("=" * 115 + "\n")
|
||||
return
|
||||
|
||||
fio_col = next((c for c in ['Сотрудник', 'fio', 'fio_clean'] if c in df.columns), None)
|
||||
dept_col = next((c for c in ['Подразделение', 'department_scud', 'department'] if c in df.columns), None)
|
||||
pos_col = next((c for c in ['Должность', 'position'] if c in df.columns), None)
|
||||
in_col = next((c for c in ['Начало_дня', 'time_in'] if c in df.columns), None)
|
||||
first_act_col = next((c for c in ['Первая_активность', 'first_activity'] if c in df.columns), None)
|
||||
out_col = next((c for c in ['Конец_дня', 'time_out'] if c in df.columns), None)
|
||||
dur_col = next((c for c in ['Находился_в_здании', 'time_in_building', 'duration'] if c in df.columns), None)
|
||||
present_col = next((c for c in ['Пришел', 'is_present'] if c in df.columns), None)
|
||||
anom_col = 'anomaly_flag' if 'anomaly_flag' in df.columns else None
|
||||
snap_col = 'snapshot_id' if 'snapshot_id' in df.columns else None
|
||||
|
||||
total = len(df)
|
||||
present_cnt = len(df[df['Пришел'] == True]) if 'Пришел' in df.columns else 0
|
||||
if present_col:
|
||||
present_cnt = len(df[df[present_col].astype(str).str.lower().isin(['true', '1'])])
|
||||
else:
|
||||
present_cnt = 0
|
||||
absent_cnt = total - present_cnt
|
||||
|
||||
print(f"Всего записей: {total} | Пришли: {present_cnt} | Не пришли: {absent_cnt}")
|
||||
print("-" * 90)
|
||||
print(f"Всего записей: {total} | Присутствовали: {present_cnt} | Отсутствовали: {absent_cnt}")
|
||||
print("-" * 115)
|
||||
|
||||
cols_to_show = ['fio', 'department', 'position', 'time_in', 'first_activity', 'time_out', 'is_present', 'anomaly_flag', 'snapshot_id']
|
||||
existing_cols = [c for c in cols_to_show if c in df.columns]
|
||||
display_cols = [c for c in [fio_col, dept_col, in_col, first_act_col, out_col, dur_col, present_col, anom_col, snap_col] if c]
|
||||
print(df[display_cols].head(30).to_string(index=False))
|
||||
|
||||
print(df[existing_cols].head(30).to_string(index=False))
|
||||
if len(df) > 30:
|
||||
print(f"\n... и ещё {len(df) - 30} строк.")
|
||||
|
||||
@@ -175,11 +197,11 @@ def inspect_scud(snapshot_id=None, date_str=None, export_xlsx=None):
|
||||
out_path = os.path.join(OUTPUT_DIR, out_path)
|
||||
|
||||
df.to_excel(out_path, index=False)
|
||||
print("\n" + "*" * 90)
|
||||
print("\n" + "*" * 115)
|
||||
print(f"[✓] УСПЕШНЫЙ ЭКСПОРТ ДЕБАГ-ФАЙЛА В EXCEL: {out_path}")
|
||||
print("*" * 90)
|
||||
print("*" * 115)
|
||||
|
||||
print("=" * 90 + "\n")
|
||||
print("=" * 115 + "\n")
|
||||
|
||||
|
||||
def print_absences(date_str=None):
|
||||
@@ -239,7 +261,7 @@ def dump_all_to_excel(out_filename="db_dump_full.xlsx"):
|
||||
out_path = os.path.join(OUTPUT_DIR, out_filename)
|
||||
print(f"\n[🔄] Создание полного дампа БД в файл: {out_path} ...")
|
||||
with get_connection() as conn, pd.ExcelWriter(out_path, engine='openpyxl') as writer:
|
||||
for table in ['scud_logs', 'zup_staff', 'zup_absences', 'anomalies_history', 'ai_knowledge_base', 'chat_messages', 'session_states', 'system_prompt_nodes', 'tasks']:
|
||||
for table in ['scud_logs', 'zup_staff', 'zup_absences', 'anomalies_history', 'ai_knowledge_base', 'chat_messages', 'session_states', 'system_prompt_nodes', 'tasks', 'exceptions_registry']:
|
||||
try:
|
||||
df = pd.read_sql_query(f"SELECT * FROM {table}", conn)
|
||||
df.to_excel(writer, sheet_name=table[:31], index=False)
|
||||
@@ -373,6 +395,24 @@ def purge_chat_context(session_id=None, purge_all=False):
|
||||
print(f"\n[✓] Умная зачистка контекста выполнена! Удалено сообщений: {deleted_msgs}\n")
|
||||
|
||||
|
||||
# ⭐️ Новые функции управления исключениями (Exceptions & Whitelist)
|
||||
def print_exceptions():
|
||||
"""Выводит реестр исключений и белый список сотрудников из базы SQLite."""
|
||||
exc = get_all_exceptions_from_db()
|
||||
print("\n" + "=" * 80)
|
||||
print("📋 РЕЕСТР ИСКЛЮЧЕНИЙ И БЕЛЫЙ СПИСОК (exceptions_registry):")
|
||||
print("=" * 80)
|
||||
for cat, items in exc.items():
|
||||
print(f"[{cat.upper()}] ({len(items)} шт.):")
|
||||
if items:
|
||||
for it in items:
|
||||
print(f" • {it}")
|
||||
else:
|
||||
print(" — пусто")
|
||||
print("-" * 80)
|
||||
print("=" * 80 + "\n")
|
||||
|
||||
|
||||
HELP_TEXT = """
|
||||
CLI-утилита инспекции и управления SQLite базой данных СКУД (scud_orion_ai.db)
|
||||
|
||||
@@ -391,6 +431,11 @@ CLI-утилита инспекции и управления SQLite базой
|
||||
dump [output.xlsx] -- Полный дамп всех таблиц БД в многостраничный Excel
|
||||
snapshot del [ID] или [--day ДД.ММ.ГГГГ] -- Удаление снапшота по ID или всех за выбранный день
|
||||
|
||||
exceptions [list] -- Посмотреть реестр исключений и белый список (SQLite)
|
||||
exceptions add -c CATEGORY -v VALUE [-m COMMENT] -- Добавить исключение (fio, include_fio, departments, positions, position_keywords)
|
||||
exceptions del -c CATEGORY -v VALUE -- Удалить исключение из БД
|
||||
exceptions sync -- Синхронизировать exceptions.json -> SQLite
|
||||
|
||||
ПРИМЕРЫ ЗАПУСКА:
|
||||
python scripts/db_cli.py stats
|
||||
python scripts/db_cli.py snapshots 06.08.2026
|
||||
@@ -405,6 +450,11 @@ CLI-утилита инспекции и управления SQLite базой
|
||||
python scripts/db_cli.py context purge web_session_main --all
|
||||
python scripts/db_cli.py snapshot del Y20260805-007
|
||||
python scripts/db_cli.py dump my_dump.xlsx
|
||||
python scripts/db_cli.py exceptions
|
||||
python scripts/db_cli.py exceptions add -c include_fio -v "Тарасенко Александр Александрович"
|
||||
python scripts/db_cli.py exceptions add -c fio -v "Михалев Сергей Геннадьевич" -m "Уборщик"
|
||||
python scripts/db_cli.py exceptions del -c fio -v "Михалев Сергей Геннадьевич"
|
||||
python scripts/db_cli.py exceptions sync
|
||||
"""
|
||||
|
||||
|
||||
@@ -418,9 +468,16 @@ def main():
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
add_help=False
|
||||
)
|
||||
parser.add_argument('command', nargs='?', default=None, choices=['stats', 'snapshots', 'scud', 'absences', 'anomalies', 'rules', 'prompts', 'sessions', 'dump', 'snapshot', 'tools', 'context'], help="Основная команда")
|
||||
parser.add_argument('action', nargs='?', default=None, help="Дополнительное действие (например, 'del', 'purge') или session_id для контекста")
|
||||
parser.add_argument('param', nargs='?', default=None, help="Параметр (дата, ID снапшота, session_id, '--all' или имя файла)")
|
||||
parser.add_argument('command', nargs='?', default=None, choices=[
|
||||
'stats', 'snapshots', 'scud', 'absences', 'anomalies',
|
||||
'rules', 'prompts', 'sessions', 'dump', 'snapshot',
|
||||
'tools', 'context', 'exceptions'
|
||||
], help="Основная команда")
|
||||
parser.add_argument('action', nargs='?', default=None, help="Действие ('del', 'purge', 'add', 'sync') или дата/сессия")
|
||||
parser.add_argument('param', nargs='?', default=None, help="Параметр (дата, ID снапшота, session_id, имя файла)")
|
||||
parser.add_argument('-c', '--category', type=str, default=None, choices=['departments', 'positions', 'fio', 'position_keywords', 'include_fio'], help="Категория исключения")
|
||||
parser.add_argument('-v', '--value', type=str, default=None, help="Значение исключения (ФИО, отдел, должность)")
|
||||
parser.add_argument('-m', '--comment', type=str, default="", help="Комментарий к исключению")
|
||||
parser.add_argument('--snapshot', type=str, default=None, help="ID конкретного снапшота для инспекции")
|
||||
parser.add_argument('--export-xlsx', type=str, default=None, help="Экспорт среза СКУД в Excel-файл")
|
||||
parser.add_argument('--day', type=str, default=None, help="Удалить снапшоты за конкретный день (ДД.ММ.ГГГГ)")
|
||||
@@ -436,12 +493,13 @@ def main():
|
||||
if args.command == 'stats':
|
||||
print_stats()
|
||||
elif args.command == 'snapshots':
|
||||
date_val = args.param or args.action
|
||||
date_val = args.action or args.param
|
||||
print_snapshots_list(date_str=date_val)
|
||||
elif args.command == 'scud':
|
||||
inspect_scud(snapshot_id=args.snapshot, date_str=args.param, export_xlsx=args.export_xlsx)
|
||||
date_val = args.action or args.param
|
||||
inspect_scud(snapshot_id=args.snapshot, date_str=date_val, export_xlsx=args.export_xlsx)
|
||||
elif args.command == 'absences':
|
||||
date_val = args.param or args.action
|
||||
date_val = args.action or args.param
|
||||
print_absences(date_str=date_val)
|
||||
elif args.command == 'anomalies':
|
||||
print_anomalies()
|
||||
@@ -462,7 +520,7 @@ def main():
|
||||
sess_id = args.action if args.action else None
|
||||
print_chat_messages(session_id=sess_id, limit=args.limit)
|
||||
elif args.command == 'dump':
|
||||
filename = args.param if args.param else "db_dump_full.xlsx"
|
||||
filename = args.action or args.param or "db_dump_full.xlsx"
|
||||
dump_all_to_excel(filename)
|
||||
elif args.command == 'snapshot':
|
||||
if args.action == 'del':
|
||||
@@ -476,9 +534,34 @@ def main():
|
||||
else:
|
||||
print(f"\n[❌] Ошибка: Неизвестное действие '{args.action}' для команды snapshot.")
|
||||
print("Используйте: python scripts/db_cli.py snapshot del [ID или --day 'ДД.ММ.ГГГГ']\n")
|
||||
elif args.command == 'exceptions':
|
||||
if args.action == 'add':
|
||||
if not args.category or not args.value:
|
||||
print("\n[❌] Ошибка: Для добавления исключения укажите флаги -c/--category и -v/--value")
|
||||
print("Пример: python scripts/db_cli.py exceptions add -c include_fio -v \"Тарасенко Александр Александрович\"\n")
|
||||
return
|
||||
if add_exception_to_db(args.category, args.value, args.comment):
|
||||
print(f"\n[✓] Успешно добавлено исключение: [{args.category}] {args.value}\n")
|
||||
else:
|
||||
print(f"\n[❌] Ошибка добавления исключения [{args.category}] {args.value}\n")
|
||||
elif args.action in ['del', 'delete', 'remove']:
|
||||
if not args.category or not args.value:
|
||||
print("\n[❌] Ошибка: Для удаления исключения укажите флаги -c/--category и -v/--value")
|
||||
print("Пример: python scripts/db_cli.py exceptions del -c fio -v \"Михалев Сергей Геннадьевич\"\n")
|
||||
return
|
||||
if remove_exception_from_db(args.category, args.value):
|
||||
print(f"\n[✓] Успешно удалено исключение: [{args.category}] {args.value}\n")
|
||||
else:
|
||||
print(f"\n[⚠️] Запись не найдена в базе: [{args.category}] {args.value}\n")
|
||||
elif args.action == 'sync':
|
||||
sync_json_to_db()
|
||||
print("\n[✓] Синхронизация exceptions.json -> SQLite успешно завершена.\n")
|
||||
else:
|
||||
print_exceptions()
|
||||
else:
|
||||
print("\n[❌] Ошибка: Неизвестная команда.")
|
||||
print(HELP_TEXT)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,46 +0,0 @@
|
||||
import os
|
||||
|
||||
OUTPUT_SNAPSHOT = "project_code_snapshot.md"
|
||||
|
||||
# Расширения файлов для включения в снимок (только исходный код)
|
||||
ALLOWED_EXTENSIONS = {'.py', '.json', '.sh', '.ini', '.js', '.html', '.css', '.sql'}
|
||||
|
||||
# Исключаемые каталоги (убираем docs, кэши, архивы и окружения)
|
||||
EXCLUDE_DIRS = {
|
||||
'.git', '__pycache__', 'venv', '.venv', 'output', 'logs',
|
||||
'extracted_project', 'docs', 'data'
|
||||
}
|
||||
|
||||
# Исключаемые файлы
|
||||
EXCLUDE_FILES = {
|
||||
OUTPUT_SNAPSHOT,
|
||||
'api_code_snapshot.md',
|
||||
'project_code_snapshot.md',
|
||||
'scud_orion_ai_v2.tar.gz',
|
||||
'scud_context_api.tar.gz',
|
||||
'context_memory.db'
|
||||
}
|
||||
|
||||
print(f"🔄 Сборка оптимизированного слепка кода в {OUTPUT_SNAPSHOT}...")
|
||||
|
||||
with open(OUTPUT_SNAPSHOT, 'w', encoding='utf-8') as out:
|
||||
out.write("# 📦 ОПТИМИЗИРОВАННЫЙ СЛЕПОК ИСХОДНОГО КОДА scud_orion_ai\n\n")
|
||||
|
||||
for root, dirs, files in os.walk('.'):
|
||||
dirs[:] = [d for d in dirs if d not in EXCLUDE_DIRS]
|
||||
|
||||
for file in sorted(files):
|
||||
ext = os.path.splitext(file)[1].lower()
|
||||
if ext in ALLOWED_EXTENSIONS and file not in EXCLUDE_FILES:
|
||||
filepath = os.path.join(root, file)
|
||||
out.write(f"## File: `{filepath}`\n")
|
||||
out.write("```" + ext.replace('.', '') + "\n")
|
||||
try:
|
||||
with open(filepath, 'r', encoding='utf-8', errors='replace') as f:
|
||||
out.write(f.read())
|
||||
except Exception as e:
|
||||
out.write(f"// Ошибка чтения файла: {e}\n")
|
||||
out.write("\n```\n\n")
|
||||
|
||||
size_kb = os.path.getsize(OUTPUT_SNAPSHOT) / 1024
|
||||
print(f"✓ Слепок успешно создан: {OUTPUT_SNAPSHOT} ({size_kb:.1f} KB)")
|
||||
@@ -0,0 +1,73 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
===============================================================================
|
||||
FILE: scripts/diagnostics/make_etl_snapshot.py
|
||||
ROLE: Генерация компактного слепка ETL-конвейера, БД и сервисов СКУД.
|
||||
===============================================================================
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
ROOT_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", ".."))
|
||||
OUTPUT_FILE = os.path.join(ROOT_DIR, "etl_code_snapshot.md")
|
||||
|
||||
# Строгий целевой список файлов для ETL-слепка
|
||||
TARGET_FILES = [
|
||||
"config.py",
|
||||
"exceptions.json",
|
||||
"main_etl.py",
|
||||
"run_cron_etl.sh",
|
||||
"scripts/db_cli.py", # ⭐️ Гарантированно включен
|
||||
"core/connection.py",
|
||||
"core/database.py",
|
||||
"core/schema.py",
|
||||
"core/repositories/scud_repo.py",
|
||||
"core/repositories/zup_repo.py",
|
||||
"services/data_loader.py",
|
||||
"services/scud_export.py",
|
||||
"services/share_copier.py",
|
||||
"services/excel_exporter.py",
|
||||
"services/text_reporter.py",
|
||||
"services/exceptions_repo.py",
|
||||
"services/zup_extractor.py",
|
||||
"services/ai_verifier.py",
|
||||
"services/knowledge_base.py",
|
||||
"services/knowledge/service.py",
|
||||
"services/scud_etl/pipeline.py",
|
||||
"services/scud_etl/merger.py",
|
||||
"services/scud_etl/anomaly_detector.py",
|
||||
"services/snapshots/service.py",
|
||||
"services/tasks/repository.py",
|
||||
"services/tasks/service.py"
|
||||
]
|
||||
|
||||
|
||||
def create_etl_snapshot():
|
||||
content = ["# 📦 ETL-СЛЕПОК ИСХОДНОГО КОДА (СКУД ⟷ 1С & DB CORE)\n"]
|
||||
included_count = 0
|
||||
|
||||
for rel_path in TARGET_FILES:
|
||||
full_path = os.path.join(ROOT_DIR, rel_path)
|
||||
if os.path.exists(full_path):
|
||||
ext = os.path.splitext(rel_path)[1].replace(".", "")
|
||||
lang = "py" if ext == "py" else ("json" if ext == "json" else "bash")
|
||||
try:
|
||||
with open(full_path, "r", encoding="utf-8") as f:
|
||||
file_text = f.read()
|
||||
content.append(f"## File: `./{rel_path}`\n```{lang}\n{file_text}\n```\n")
|
||||
included_count += 1
|
||||
except Exception as e:
|
||||
print(f"[⚠️] Ошибка чтения {rel_path}: {e}")
|
||||
else:
|
||||
print(f"[ℹ️] Пропущен отсутствующий файл: {rel_path}")
|
||||
|
||||
with open(OUTPUT_FILE, "w", encoding="utf-8") as f:
|
||||
f.write("\n".join(content))
|
||||
|
||||
size_kb = os.path.getsize(OUTPUT_FILE) / 1024
|
||||
print(f"\n[✓] ETL-слепок создан: {OUTPUT_FILE}")
|
||||
print(f" Включено файлов: {included_count} | Размер: {size_kb:.1f} KB\n")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
create_etl_snapshot()
|
||||
@@ -0,0 +1,63 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
===============================================================================
|
||||
FILE: scripts/diagnostics/make_web_snapshot.py
|
||||
ROLE: Генерация слепка Web API, LLM-движка и клиентских скриптов.
|
||||
===============================================================================
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
ROOT_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", ".."))
|
||||
OUTPUT_FILE = os.path.join(ROOT_DIR, "web_api_code_snapshot.md")
|
||||
|
||||
WEB_TARGET_FILES = [
|
||||
"modules/web_api/main.py",
|
||||
"modules/web_api/routers/chat.py",
|
||||
"modules/web_api/routers/auth.py",
|
||||
"modules/web_api/routers/tasks.py",
|
||||
"modules/web_api/routers/exceptions.py",
|
||||
"modules/web_api/routers/admin.py",
|
||||
"modules/web_api/routers/files.py",
|
||||
"modules/web_api/llm/agent.py",
|
||||
"modules/web_api/llm/db_tools.py",
|
||||
"modules/web_api/llm/schemas.py",
|
||||
"modules/web_api/llm/core/context_manager.py",
|
||||
"modules/web_api/llm/core/tool_injector.py",
|
||||
"modules/web_api/llm/core/ollama_client.py",
|
||||
"modules/web_api/llm/core/fast_path.py",
|
||||
"modules/web_api/static/js/app.js",
|
||||
"modules/web_api/static/js/auth.js",
|
||||
"modules/web_api/static/js/tasks.js",
|
||||
"modules/web_api/static/js/chat/core.js",
|
||||
"modules/web_api/static/js/chat/task_widget.js"
|
||||
]
|
||||
|
||||
|
||||
def create_web_snapshot():
|
||||
content = ["# 🌐 WEB API & LLM AGENT CODE SNAPSHOT\n"]
|
||||
included_count = 0
|
||||
|
||||
for rel_path in WEB_TARGET_FILES:
|
||||
full_path = os.path.join(ROOT_DIR, rel_path)
|
||||
if os.path.exists(full_path):
|
||||
ext = os.path.splitext(rel_path)[1].replace(".", "")
|
||||
lang = "js" if ext == "js" else ("py" if ext == "py" else "text")
|
||||
try:
|
||||
with open(full_path, "r", encoding="utf-8") as f:
|
||||
file_text = f.read()
|
||||
content.append(f"## File: `./{rel_path}`\n```{lang}\n{file_text}\n```\n")
|
||||
included_count += 1
|
||||
except Exception as e:
|
||||
print(f"[⚠️] Ошибка чтения {rel_path}: {e}")
|
||||
|
||||
with open(OUTPUT_FILE, "w", encoding="utf-8") as f:
|
||||
f.write("\n".join(content))
|
||||
|
||||
size_kb = os.path.getsize(OUTPUT_FILE) / 1024
|
||||
print(f"\n[✓] Web API слепок создан: {OUTPUT_FILE}")
|
||||
print(f" Включено файлов: {included_count} | Размер: {size_kb:.1f} KB\n")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
create_web_snapshot()
|
||||
@@ -0,0 +1,22 @@
|
||||
import os
|
||||
|
||||
OUTPUT_SNAPSHOT = "web_api_code_snapshot.md"
|
||||
ALLOWED_EXTENSIONS = {'.py', '.html', '.css', '.js'}
|
||||
EXCLUDE_DIRS = {'.git', '__pycache__', 'venv', '.venv', 'output', 'logs', 'extracted_project', 'docs', 'data'}
|
||||
|
||||
print(f"🔄 Сборка Web API слепка кода в {OUTPUT_SNAPSHOT}...")
|
||||
with open(OUTPUT_SNAPSHOT, 'w', encoding='utf-8') as out:
|
||||
out.write("# 📦 WEB API & AI СЛЕПОК ИСХОДНОГО КОДА\n\n")
|
||||
if os.path.exists('modules'):
|
||||
for r, d, files in os.walk('modules'):
|
||||
d[:] = [sub for sub in d if sub not in EXCLUDE_DIRS]
|
||||
for file in sorted(files):
|
||||
ext = os.path.splitext(file)[1].lower()
|
||||
if ext in ALLOWED_EXTENSIONS:
|
||||
filepath = os.path.join(r, file)
|
||||
out.write(f"## File: `./{filepath}`\n```" + ext.replace('.', '') + "\n")
|
||||
with open(filepath, 'r', encoding='utf-8', errors='replace') as f:
|
||||
out.write(f.read())
|
||||
out.write("\n```\n\n")
|
||||
|
||||
print(f"✓ Web API слепок готов: {OUTPUT_SNAPSHOT} ({os.path.getsize(OUTPUT_SNAPSHOT) / 1024:.1f} KB)")
|
||||
Reference in New Issue
Block a user