298 lines
10 KiB
Python
298 lines
10 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
Проверка подключения к SQL Server и загрузки данных табеля
|
||
за последние 5 месяцев для ds.krivochenko@sibcem.ru.
|
||
|
||
Цепочка поиска сотрудника (как в LK модуле бота):
|
||
1. AD: search_by_user_id(email) → extensionAttribute2 = CARD_ID
|
||
2. SQL: SELECT ID FROM UOV_SELFSERVICE_PR_EMP WHERE CARD_ID = %s → EMP_ID
|
||
3. SQL: SELECT ... FROM UOV_SELFSERVICE_TB_TABEL WHERE EMP_ID = %s → табель
|
||
|
||
Запуск:
|
||
cd /opt/trueconf_bot
|
||
python3 test_tabel_check.py
|
||
"""
|
||
import sys
|
||
import os
|
||
import calendar
|
||
from datetime import datetime
|
||
|
||
# ============================================================
|
||
# 1. Загрузка .env
|
||
# ============================================================
|
||
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
|
||
ENV_PATH = os.path.join(BASE_DIR, "config", ".env")
|
||
|
||
def load_env(filepath):
|
||
try:
|
||
with open(filepath, "r", encoding="utf-8") as f:
|
||
for line in f:
|
||
line = line.strip()
|
||
if line and not line.startswith("#"):
|
||
if "=" in line:
|
||
key, value = line.split("=", 1)
|
||
os.environ[key.strip()] = value.strip(' "\'\r\n')
|
||
except FileNotFoundError:
|
||
print(f"⚠️ Файл {filepath} не найден.")
|
||
|
||
load_env(ENV_PATH)
|
||
|
||
# Карта соответствия: Имя переменной в боте -> Название карточки в сейфе
|
||
CREDENTIALS_MAP = {
|
||
"DB_PASSWORD": os.getenv("PW_ID_SQL", "").strip(' "\'\r\n'),
|
||
"AD_PASSWORD": os.getenv("PW_ID_AD", "").strip(' "\'\r\n'),
|
||
}
|
||
|
||
# ============================================================
|
||
# 2. Загрузка паролей из Passwork
|
||
# ============================================================
|
||
DB_USER = ""
|
||
DB_PASSWORD = ""
|
||
AD_USER = ""
|
||
AD_PASSWORD = ""
|
||
|
||
for pw_path in ["/opt/passwork", os.path.join(BASE_DIR, "passwork"), os.path.join(BASE_DIR, "config", "passwork")]:
|
||
if os.path.isdir(pw_path) and pw_path not in sys.path:
|
||
sys.path.insert(0, pw_path)
|
||
if BASE_DIR not in sys.path:
|
||
sys.path.insert(0, BASE_DIR)
|
||
|
||
try:
|
||
from passwork import get_passwork_secrets
|
||
print("🔐 Загрузка секретов из Passwork...")
|
||
required_cards = [name for name in CREDENTIALS_MAP.values() if name]
|
||
passwork_pool = get_passwork_secrets(required_cards=required_cards)
|
||
for var_name, card_name in CREDENTIALS_MAP.items():
|
||
if not card_name:
|
||
continue
|
||
clean_card = card_name.strip(' "\'\r\n')
|
||
card_data = passwork_pool.get(clean_card)
|
||
if card_data is None:
|
||
print(f" ⚠️ Карточка '{clean_card}' не найдена")
|
||
continue
|
||
globals()[var_name] = card_data["password"].strip(' "\'\r\n')
|
||
if var_name == "AD_PASSWORD":
|
||
globals()["AD_USER"] = card_data.get("login", "").strip(' "\'\r\n')
|
||
elif var_name == "DB_PASSWORD":
|
||
globals()["DB_USER"] = card_data.get("login", "").strip(' "\'\r\n')
|
||
print(f" ✓ {var_name} из карточки '{clean_card}'")
|
||
except ImportError:
|
||
print("⚠️ Модуль passwork не найден")
|
||
except Exception as e:
|
||
print(f"⚠️ Ошибка Passwork: {e}")
|
||
|
||
if not DB_PASSWORD:
|
||
DB_PASSWORD = os.getenv("DB_PASSWORD", "").strip()
|
||
DB_USER = os.getenv("DB_USER", DB_USER).strip()
|
||
if not AD_PASSWORD:
|
||
AD_PASSWORD = os.getenv("AD_PASSWORD", "").strip()
|
||
AD_USER = os.getenv("AD_USER", AD_USER).strip()
|
||
|
||
# ============================================================
|
||
# 3. Настройки подключения
|
||
# ============================================================
|
||
DB_SERVER = os.getenv("SQL_SERVER", "SRVKEM-MOBILEIN.sibcem.ru")
|
||
DB_NAME = os.getenv("SQL_DB_NAME", "BossCopy")
|
||
AD_SERVER = os.getenv("AD_SERVER", "ldap://172.16.20.20")
|
||
AD_BASES = (
|
||
"OU=-Пользователи,DC=sibcem,DC=ru",
|
||
"OU=Планшеты,OU=enabled,OU=БезКомпьютеров,DC=sibcem,DC=ru",
|
||
)
|
||
|
||
print()
|
||
print("=" * 60)
|
||
print(" Проверка подключения к SQL Server")
|
||
print("=" * 60)
|
||
print(f" Сервер : {DB_SERVER}")
|
||
print(f" База : {DB_NAME}")
|
||
print(f" Логин : {DB_USER}")
|
||
print(f" Пароль : {'*' * len(DB_PASSWORD) if DB_PASSWORD else '(не задан)'}")
|
||
print()
|
||
|
||
# ============================================================
|
||
# 4. Проверка pymssql
|
||
# ============================================================
|
||
try:
|
||
import pymssql
|
||
print(f"✅ pymssql загружен (версия {pymssql.__version__})")
|
||
except ImportError:
|
||
print("❌ pymssql не установлен!")
|
||
sys.exit(1)
|
||
|
||
# ============================================================
|
||
# 5. Подключение к SQL Server
|
||
# ============================================================
|
||
try:
|
||
conn = pymssql.connect(
|
||
server=DB_SERVER,
|
||
user=DB_USER,
|
||
password=DB_PASSWORD,
|
||
database=DB_NAME,
|
||
charset='cp1251',
|
||
login_timeout=10
|
||
)
|
||
print("✅ Подключение к SQL Server успешно!")
|
||
cursor = conn.cursor()
|
||
except Exception as e:
|
||
print(f"❌ Ошибка подключения: {e}")
|
||
sys.exit(1)
|
||
|
||
# ============================================================
|
||
# 6. Поиск сотрудника в AD по email → CARD_ID (как в LK модуле)
|
||
# ============================================================
|
||
print()
|
||
print("=" * 60)
|
||
print(" Поиск сотрудника в Active Directory")
|
||
print("=" * 60)
|
||
|
||
TARGET_EMAIL = "ds.krivochenko@sibcem.ru"
|
||
print(f" Ищем: {TARGET_EMAIL}")
|
||
|
||
try:
|
||
from ldap3 import Server, Connection, ALL
|
||
|
||
server = Server(AD_SERVER, get_info=ALL)
|
||
ldap_conn = Connection(server, user=AD_USER, password=AD_PASSWORD, auto_bind=True)
|
||
|
||
# Как в ad_search.py: ищем по mail, userPrincipalName, sAMAccountName
|
||
short_username = TARGET_EMAIL.split("@")[0] if "@" in TARGET_EMAIL else TARGET_EMAIL
|
||
|
||
or_parts = [
|
||
"(mail={})".format(TARGET_EMAIL),
|
||
"(userPrincipalName={})".format(TARGET_EMAIL),
|
||
"(userPrincipalName={})".format(short_username + "@sibcem.ru"),
|
||
"(sAMAccountName={})".format(short_username),
|
||
]
|
||
or_filter = "(|{})".format("".join(or_parts))
|
||
search_filter = "(&{}{})".format("(objectClass=user)", or_filter)
|
||
|
||
print(f" LDAP-фильтр: {search_filter}")
|
||
|
||
card_id_from_ad = None
|
||
|
||
for base in AD_BASES:
|
||
ldap_conn.search(
|
||
search_base=base,
|
||
search_filter=search_filter,
|
||
attributes=["cn", "sAMAccountName", "mail", "userPrincipalName", "extensionAttribute2"],
|
||
)
|
||
if ldap_conn.entries:
|
||
entry = ldap_conn.entries[0]
|
||
print(f" ✅ Найден в OU: {base}")
|
||
print(f" CN : {entry.cn}")
|
||
print(f" sAMAccountName : {entry.sAMAccountName}")
|
||
print(f" mail : {entry.mail}")
|
||
ext2 = str(entry.extensionAttribute2) if hasattr(entry, 'extensionAttribute2') and entry.extensionAttribute2 else "(не задан)"
|
||
print(f" extensionAttribute2: {ext2}")
|
||
card_id_from_ad = ext2
|
||
break
|
||
|
||
if not ldap_conn.entries:
|
||
print(f" ❌ Сотрудник не найден в AD по email {TARGET_EMAIL}")
|
||
|
||
except ImportError:
|
||
print("❌ ldap3 не установлен")
|
||
sys.exit(1)
|
||
except Exception as e:
|
||
print(f"❌ Ошибка LDAP: {e}")
|
||
sys.exit(1)
|
||
|
||
# ============================================================
|
||
# 7. Поиск EMP_ID по CARD_ID в SQL (как в LK модуле)
|
||
# ============================================================
|
||
print()
|
||
print("=" * 60)
|
||
print(" Поиск EMP_ID по CARD_ID в SQL")
|
||
print("=" * 60)
|
||
|
||
emp_id = None
|
||
card_id = None
|
||
|
||
if card_id_from_ad:
|
||
card_id = card_id_from_ad
|
||
print(f" CARD_ID из AD: {card_id}")
|
||
|
||
# Как в info_service.py: SELECT e.ID FROM UOV_SELFSERVICE_PR_EMP e WHERE e.CARD_ID = %s
|
||
cursor.execute("SELECT e.ID FROM UOV_SELFSERVICE_PR_EMP e WHERE e.CARD_ID = %s", (card_id,))
|
||
row = cursor.fetchone()
|
||
if row:
|
||
emp_id = row[0]
|
||
print(f" ✅ Найден: emp_id={emp_id}")
|
||
else:
|
||
print(f" ❌ Сотрудник с CARD_ID={card_id} не найден в UOV_SELFSERVICE_PR_EMP")
|
||
|
||
# ============================================================
|
||
# 8. Загрузка табеля за последние 5 месяцев
|
||
# ============================================================
|
||
print()
|
||
print("=" * 60)
|
||
print(" Загрузка табеля за последние 5 месяцев")
|
||
print("=" * 60)
|
||
|
||
if not emp_id:
|
||
print(" ❌ Не удалось найти сотрудника — завершение")
|
||
conn.close()
|
||
sys.exit(1)
|
||
|
||
now = datetime.now()
|
||
months_back = 5
|
||
|
||
for i in range(months_back):
|
||
month = now.month - i
|
||
year = now.year
|
||
while month <= 0:
|
||
month += 12
|
||
year -= 1
|
||
|
||
month_name = calendar.month_name[month]
|
||
print(f"\n📅 {month_name} {year}")
|
||
print("-" * 40)
|
||
|
||
query = """
|
||
SELECT e.D, e.TDAY_ID, e.H
|
||
FROM UOV_SELFSERVICE_TB_TABEL e
|
||
WHERE e.EMP_ID = %s AND YEAR(e.D) = %s AND MONTH(e.D) = %s
|
||
ORDER BY e.D ASC
|
||
"""
|
||
try:
|
||
cursor.execute(query, (emp_id, year, month))
|
||
records = cursor.fetchall()
|
||
except Exception as e:
|
||
print(f" ❌ Ошибка запроса: {e}")
|
||
continue
|
||
|
||
if not records:
|
||
print(f" ⚠️ Данные табеля отсутствуют")
|
||
continue
|
||
|
||
print(f" Записей: {len(records)}")
|
||
|
||
total_days = 0
|
||
total_hours = 0.0
|
||
codes = {}
|
||
|
||
for r in records:
|
||
tday_id = str(r[1]).strip() if r[1] else "?"
|
||
hours = float(r[2]) if r[2] is not None else 0.0
|
||
codes[tday_id] = codes.get(tday_id, 0) + 1
|
||
if tday_id == 'Я':
|
||
total_days += 1
|
||
total_hours += hours
|
||
|
||
print(f" Явки (Я): {codes.get('Я', 0)} дн., часов: {total_hours:.1f}")
|
||
print(f" Коды: {', '.join(f'{k}={v}' for k, v in codes.items())}")
|
||
|
||
print(f" Первые 5 записей:")
|
||
for r in records[:5]:
|
||
d = r[0].strftime('%d.%m.%Y') if hasattr(r[0], 'strftime') else str(r[0])
|
||
print(f" {d} | {str(r[1]).strip():>3} | {float(r[2]) if r[2] else 0:.1f} ч.")
|
||
|
||
# ============================================================
|
||
# Закрытие
|
||
# ============================================================
|
||
conn.close()
|
||
print()
|
||
print("=" * 60)
|
||
print(" ✅ Проверка завершена")
|
||
print("=" * 60)
|