готовность к созданию API и базы контекста для ИИ.

This commit is contained in:
2026-08-05 16:29:17 +03:00
parent 0fd145df88
commit 8f611ec41a
12 changed files with 1093 additions and 354 deletions
+27 -17
View File
@@ -15,7 +15,7 @@ import pyodbc
from openpyxl.utils import get_column_letter
from config import SCUD_DIR, clean_scud_fio_light, load_exceptions
from core.database import save_scud_to_db
from core.database import save_scud_to_db, has_scud_logs_for_date
warnings.filterwarnings("ignore", message="pandas only supports SQLAlchemy connectable")
@@ -85,7 +85,7 @@ WITH DailyLogs AS (
Passages AS (
SELECT
EmployeeID,
MIN(TimeVal) AS FirstRawEvent, -- ⭐️ Первое событие ДНЯ ВООБЩЕ (любая дверь/туалет/выход)
MIN(TimeVal) AS FirstRawEvent,
MIN(CASE WHEN Direction = 'IN' THEN TimeVal END) AS FirstIn,
MAX(CASE WHEN Direction = 'OUT' THEN TimeVal END) AS LastOut,
MAX(CASE WHEN RowNumDesc = 1 THEN Direction END) AS LastEventType
@@ -106,14 +106,11 @@ SELECT
ISNULL(CAST(p.TabNumber AS NVARCHAR(50)), N'—') AS [Таб_№],
CONVERT(VARCHAR(10), @TargetDate, 104) AS [Дата],
ISNULL(CAST(CONVERT(VARCHAR(8), pass.FirstIn, 108) AS NVARCHAR(20)), N'Нет входа') AS [Начало_дня],
-- ⭐️ НОВАЯ КОЛОНКА: "Первая активность" (если нет входа, но есть события в здании)
CASE
WHEN pass.FirstIn IS NULL AND pass.FirstRawEvent IS NOT NULL
THEN CAST(CONVERT(VARCHAR(8), pass.FirstRawEvent, 108) AS NVARCHAR(20))
ELSE N'—'
END AS [Первая_активность],
CASE
WHEN @TargetDate = CAST(GETDATE() AS DATE) AND (pass.LastEventType = 'IN' OR pass.LastOut IS NULL OR pass.LastOut <= pass.FirstIn)
THEN N'Нет выхода'
@@ -228,9 +225,15 @@ def run_export(input_date: str | None = None, save_xlsx: bool = True, debug: boo
processing_date = target["date"]
processing_date_str = processing_date.strftime("%d.%m.%Y")
period_label = target["name"]
is_yesterday = (period_label == "Вчера")
# ⚡️ ПРОВЕРКА: Если вчерашние данные уже есть в SQLite, повторную выгрузку из MS SQL пропускаем
if is_yesterday and has_scud_logs_for_date(processing_date_str):
log(f"--- Обработка периода: {period_label} ({processing_date_str}) ---")
log(f"[ℹ️] Вчерашние логи СКУД за {processing_date_str} уже присутствуют в SQLite. Повторная выгрузка пропущена.", "INFO")
continue
log(f"--- Обработка периода: {period_label} ({processing_date_str}) ---")
sql_query = SQL_QUERY_TEMPLATE.format(target_date=processing_date.strftime("%Y-%m-%d"))
connection = None
@@ -241,22 +244,17 @@ def run_export(input_date: str | None = None, save_xlsx: bool = True, debug: boo
log(f"База вернула {len(df)} строк за {processing_date_str}")
if len(df) > 0:
# 1. Готовим колонки для локальной БД
df['fio_clean'] = df['Сотрудник'].apply(clean_scud_fio_light)
# Валидация прихода с учетом аномалии неявки на вход
df['anomaly_flag'] = 'NONE'
mask_anomaly = (df['Начало_дня'] == 'Нет входа') & (df['Первая_активность'] != '—')
df.loc[mask_anomaly, 'anomaly_flag'] = 'ANOMALY_NO_IN_HAS_ACTIVITY'
# Флаг 'Пришел' ставится в True только если был вход
df['Пришел'] = df['Статус'].str.contains('Присутствовал', case=False, na=False) & (~mask_anomaly)
# 2. ПРЯМОЕ СОХРАНЕНИЕ В SQLite (scud_orion_ai.db) С METКОЙ СРЕЗА snapshot_time
save_scud_to_db(df, processing_date_str, snapshot_time=snapshot_time)
log(f"[✓] Записи за {processing_date_str} успешно сохранены в SQLite с меткой {snapshot_time}!", "SUCCESS")
save_scud_to_db(df, processing_date_str, snapshot_time=snapshot_time, is_yesterday=is_yesterday)
log(f"[✓] Записи за {processing_date_str} успешно сохранены в SQLite!", "SUCCESS")
# 3. Сохранение в Excel (если включен флаг save_xlsx)
if save_xlsx:
file_name = f"Сотрудники_{processing_date_str}.xlsx"
file_path = os.path.join(SCUD_DIR, file_name)
@@ -285,10 +283,22 @@ def run_export(input_date: str | None = None, save_xlsx: bool = True, debug: boo
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Экспорт данных СКУД в SQLite и Excel")
parser.add_argument("--date", dest="input_date", default=None)
parser.add_argument("-d", "--debug", action="store_true")
parser.add_argument("--no-xlsx", dest="save_xlsx", action="store_false", default=True, help="Отключить сохранение в XLSX")
help_text = """
Модуль прямого экспорта данных СКУД Орион Pro (MS SQL Server) в SQLite и Excel.
ПРИМЕРЫ ЗАПУСКА:
python services/scud_export.py -- Автоматическая выгрузка за Сегодня и Вчера
python services/scud_export.py --date 05.08.2026 -- Точечная выгрузка СКУД за конкретную дату
python services/scud_export.py --no-xlsx -- Сохранение логов СКУД ТОЛЬКО в SQLite без дублирования в XLSX
python services/scud_export.py -d -- Запуск в режиме отладки MS SQL запросов (DEBUG)
"""
parser = argparse.ArgumentParser(
description=help_text,
formatter_class=argparse.RawDescriptionHelpFormatter
)
parser.add_argument("--date", dest="input_date", default=None, help="Дата выгрузки в формате ДД.ММ.ГГГГ (по умолчанию выгружаются Вчера и Сегодня)")
parser.add_argument("-d", "--debug", action="store_true", help="Включить подробные логи отладки подключения и запросов к MS SQL")
parser.add_argument("--no-xlsx", dest="save_xlsx", action="store_false", default=True, help="Отключить генерирование промежуточных XLSX-файлов в папке data/scud/")
args = parser.parse_args()
run_export(args.input_date, save_xlsx=args.save_xlsx, debug=args.debug)