проект теперь работает с использованием локальной базы данных SQLite. Все данные аккумулируются и хранятся в ней. Впереди - разработка веб интерфейса.
This commit is contained in:
+21
-19
@@ -128,22 +128,26 @@ def generate_summary_excel(merged_df, date_str="27.07.2026", filename=None):
|
||||
|
||||
current_row = 4
|
||||
|
||||
# 3. НЕИЗВЕСТНО (По умолчанию РАСКРЫТО, нумерация 1. ФИО)
|
||||
# 3. НЕИЗВЕСТНО (По умолчанию РАСКРЫТО, чистые ФИО без цифр)
|
||||
unexplained = merged_df[(merged_df['Пришел'] == False) & (merged_df['Вид_отсутствия'].isna())]
|
||||
ws.cell(row=current_row, column=1, value="неизвестно")
|
||||
ws.cell(row=current_row, column=2, value=len(unexplained))
|
||||
format_row_cells(ws, current_row, FILL_UNEXPLAINED, is_bold=True, bold_font=bold_font)
|
||||
current_row += 1
|
||||
|
||||
for idx, fio in enumerate(sorted(unexplained['Сотрудник'].dropna().unique()), 1):
|
||||
ws.cell(row=current_row, column=1, value=f"{idx}. {fio}")
|
||||
for fio in sorted(unexplained['Сотрудник'].dropna().unique()):
|
||||
ws.cell(row=current_row, column=1, value=fio)
|
||||
format_row_cells(ws, current_row, FILL_UNEXPLAINED, is_bold=False)
|
||||
ws.row_dimensions[current_row].outlineLevel = 1
|
||||
ws.row_dimensions[current_row].hidden = False
|
||||
current_row += 1
|
||||
|
||||
# 4. КАТЕГОРИИ ОТСУТСТВИЙ (Свернуты hidden=True)
|
||||
absent_only = merged_df[(merged_df['Пришел'] == False) & (merged_df['Вид_отсутствия'].notna())]
|
||||
# 4. КАТЕГОРИИ ОТСУТСТВИЙ (Свернуты hidden=True, ИСКЛЮЧЕНИЯ ИСКЛЮЧЕНЫ)
|
||||
absent_only = merged_df[
|
||||
(merged_df['Пришел'] == False) &
|
||||
(merged_df['Вид_отсутствия'].notna()) &
|
||||
(~merged_df['Вид_отсутствия'].astype(str).str.startswith('Исключение'))
|
||||
]
|
||||
absent_groups = absent_only.groupby('Вид_отсутствия')
|
||||
|
||||
for idx_cat, (cat_name, group) in enumerate(absent_groups):
|
||||
@@ -155,14 +159,14 @@ def generate_summary_excel(merged_df, date_str="27.07.2026", filename=None):
|
||||
format_row_cells(ws, current_row, cat_fill, is_bold=True, bold_font=bold_font)
|
||||
current_row += 1
|
||||
|
||||
for idx, fio in enumerate(sorted(group['Сотрудник'].dropna().unique()), 1):
|
||||
ws.cell(row=current_row, column=1, value=f"{idx}. {fio}")
|
||||
for fio in sorted(group['Сотрудник'].dropna().unique()):
|
||||
ws.cell(row=current_row, column=1, value=fio)
|
||||
format_row_cells(ws, current_row, cat_fill, is_bold=False)
|
||||
ws.row_dimensions[current_row].outlineLevel = 1
|
||||
ws.row_dimensions[current_row].hidden = True
|
||||
current_row += 1
|
||||
|
||||
# 5. ИТОГО НА РАБОТЕ (Раскрывающийся список, свернут hidden=True)
|
||||
# 5. ИТОГО НА РАБОТЕ (Свернут hidden=True)
|
||||
is_working_mask = (merged_df['Пришел'] == True) | (
|
||||
merged_df['Вид_отсутствия'].astype(str).str.lower().str.contains('командировк|удален|дистанцион|разъездн', regex=True, na=False)
|
||||
)
|
||||
@@ -174,14 +178,14 @@ def generate_summary_excel(merged_df, date_str="27.07.2026", filename=None):
|
||||
current_row += 1
|
||||
|
||||
if not present.empty:
|
||||
for idx, fio in enumerate(sorted(present['Сотрудник'].dropna().unique()), 1):
|
||||
ws.cell(row=current_row, column=1, value=f"{idx}. {fio}")
|
||||
for fio in sorted(present['Сотрудник'].dropna().unique()):
|
||||
ws.cell(row=current_row, column=1, value=fio)
|
||||
format_row_cells(ws, current_row, FILL_PRESENT, is_bold=False)
|
||||
ws.row_dimensions[current_row].outlineLevel = 1
|
||||
ws.row_dimensions[current_row].hidden = True
|
||||
current_row += 1
|
||||
|
||||
# 6. АНОМАЛИИ (В самом низу таблицы, раскрывающийся список)
|
||||
# 6. АНОМАЛИИ (Свернут hidden=True)
|
||||
anomalies = merged_df[(merged_df['Пришел'] == True) & (merged_df['Вид_отсутствия'].notna())]
|
||||
|
||||
ws.cell(row=current_row, column=1, value="Аномалии")
|
||||
@@ -189,15 +193,15 @@ def generate_summary_excel(merged_df, date_str="27.07.2026", filename=None):
|
||||
format_row_cells(ws, current_row, FILL_ANOMALY, is_bold=True, bold_font=bold_font)
|
||||
current_row += 1
|
||||
|
||||
chars_per_line_b = 30 # Влезает символов в столбец B шириной 230px (32.0 units)
|
||||
chars_per_line_b = 30
|
||||
|
||||
if not anomalies.empty:
|
||||
for idx, (_, row) in enumerate(anomalies.iterrows(), 1):
|
||||
for _, row in anomalies.iterrows():
|
||||
fio = row.get('Сотрудник', '')
|
||||
reason = row.get('Вид_отсутствия', 'Неизвестная причина')
|
||||
reason_text = f"В 1С: {reason}"
|
||||
|
||||
cell_a = ws.cell(row=current_row, column=1, value=f"{idx}. {fio} (На работе)")
|
||||
cell_a = ws.cell(row=current_row, column=1, value=f"{fio} (На работе)")
|
||||
cell_b = ws.cell(row=current_row, column=2, value=reason_text)
|
||||
|
||||
cell_a.fill = FILL_ANOMALY
|
||||
@@ -219,7 +223,7 @@ def generate_summary_excel(merged_df, date_str="27.07.2026", filename=None):
|
||||
current_row += 1
|
||||
|
||||
ws.column_dimensions['A'].width = 45.0
|
||||
ws.column_dimensions['B'].width = 32.0 # Ровно 230px в Excel
|
||||
ws.column_dimensions['B'].width = 32.0
|
||||
|
||||
try:
|
||||
wb.save(output_path)
|
||||
@@ -274,7 +278,6 @@ def generate_detailed_excel(merged_df, date_str="26.07.2026", filename=None):
|
||||
|
||||
in_building_str = str(row.get(hours_col, '00:00'))
|
||||
|
||||
# Расчет отклонения с учетом вычета 30 мин обеда и причины в 1С
|
||||
deviation_val = calculate_deviation(in_building_str, reason=absence_reason if has_reason else "", norm_hours=8, lunch_minutes=30)
|
||||
|
||||
ws.append([
|
||||
@@ -291,11 +294,10 @@ def generate_detailed_excel(merged_df, date_str="26.07.2026", filename=None):
|
||||
|
||||
row_num = 5 + idx
|
||||
|
||||
# Разграничение заливки для Детального отчета:
|
||||
if is_present and has_reason:
|
||||
row_fill = GREEN_FILL # Аномалия (Пришел в отпуске/на больничном) -> Бледно-зеленый
|
||||
row_fill = GREEN_FILL
|
||||
elif not is_present:
|
||||
row_fill = YELLOW_FILL if has_reason else LIGHT_RED_FILL # Обычные отсутствия / Неизвестные
|
||||
row_fill = YELLOW_FILL if has_reason else LIGHT_RED_FILL
|
||||
else:
|
||||
row_fill = None
|
||||
|
||||
|
||||
Reference in New Issue
Block a user