Сводка CA работает корректно
This commit is contained in:
@@ -25,20 +25,28 @@ class SvodkaCAParser(ParserPort):
|
||||
|
||||
def _get_data_wrapper(self, params: dict):
|
||||
"""Получение данных по режимам и таблицам"""
|
||||
print(f"🔍 DEBUG: _get_data_wrapper вызван с параметрами: {params}")
|
||||
|
||||
# Валидируем параметры с помощью схемы Pydantic
|
||||
validated_params = validate_params_with_schema(params, SvodkaCARequest)
|
||||
|
||||
modes = validated_params["modes"]
|
||||
tables = validated_params["tables"]
|
||||
|
||||
print(f"🔍 DEBUG: Запрошенные режимы: {modes}")
|
||||
print(f"🔍 DEBUG: Запрошенные таблицы: {tables}")
|
||||
|
||||
# Проверяем, есть ли данные в data_dict (из парсинга) или в df (из загрузки)
|
||||
if hasattr(self, 'data_dict') and self.data_dict is not None:
|
||||
# Данные из парсинга
|
||||
data_source = self.data_dict
|
||||
print(f"🔍 DEBUG: Используем data_dict с режимами: {list(data_source.keys())}")
|
||||
elif hasattr(self, 'df') and self.df is not None and not self.df.empty:
|
||||
# Данные из загрузки - преобразуем DataFrame обратно в словарь
|
||||
data_source = self._df_to_data_dict()
|
||||
print(f"🔍 DEBUG: Используем df, преобразованный в data_dict с режимами: {list(data_source.keys())}")
|
||||
else:
|
||||
print(f"🔍 DEBUG: Нет данных! data_dict={getattr(self, 'data_dict', 'None')}, df={getattr(self, 'df', 'None')}")
|
||||
return {}
|
||||
|
||||
# Фильтруем данные по запрошенным режимам и таблицам
|
||||
@@ -46,10 +54,19 @@ class SvodkaCAParser(ParserPort):
|
||||
for mode in modes:
|
||||
if mode in data_source:
|
||||
result_data[mode] = {}
|
||||
available_tables = list(data_source[mode].keys())
|
||||
print(f"🔍 DEBUG: Режим '{mode}' содержит таблицы: {available_tables}")
|
||||
for table_name, table_data in data_source[mode].items():
|
||||
if table_name in tables:
|
||||
result_data[mode][table_name] = table_data
|
||||
# Ищем таблицы по частичному совпадению
|
||||
for requested_table in tables:
|
||||
if requested_table in table_name:
|
||||
result_data[mode][table_name] = table_data
|
||||
print(f"🔍 DEBUG: Добавлена таблица '{table_name}' (совпадение с '{requested_table}') с {len(table_data)} записями")
|
||||
break # Найдено совпадение, переходим к следующей таблице
|
||||
else:
|
||||
print(f"🔍 DEBUG: Режим '{mode}' не найден в data_source")
|
||||
|
||||
print(f"🔍 DEBUG: Итоговый результат содержит режимы: {list(result_data.keys())}")
|
||||
return result_data
|
||||
|
||||
def _df_to_data_dict(self):
|
||||
@@ -74,6 +91,8 @@ class SvodkaCAParser(ParserPort):
|
||||
|
||||
def parse(self, file_path: str, params: dict) -> pd.DataFrame:
|
||||
"""Парсинг файла и возврат DataFrame"""
|
||||
print(f"🔍 DEBUG: SvodkaCAParser.parse вызван с файлом: {file_path}")
|
||||
|
||||
# Парсим данные и сохраняем словарь для использования в геттерах
|
||||
self.data_dict = self.parse_svodka_ca(file_path, params)
|
||||
|
||||
@@ -95,132 +114,108 @@ class SvodkaCAParser(ParserPort):
|
||||
if data_rows:
|
||||
df = pd.DataFrame(data_rows)
|
||||
self.df = df
|
||||
print(f"🔍 DEBUG: Создан DataFrame с {len(data_rows)} записями")
|
||||
return df
|
||||
|
||||
# Если данных нет, возвращаем пустой DataFrame
|
||||
self.df = pd.DataFrame()
|
||||
print(f"🔍 DEBUG: Возвращаем пустой DataFrame")
|
||||
return self.df
|
||||
|
||||
def parse_svodka_ca(self, file_path: str, params: dict) -> dict:
|
||||
"""Парсинг сводки СА"""
|
||||
# Получаем параметры из params
|
||||
sheet_name = params.get('sheet_name', 0) # По умолчанию первый лист
|
||||
inclusion_list = params.get('inclusion_list', {'ТиП', 'Топливо', 'Потери'})
|
||||
"""Парсинг сводки СА - работает с тремя листами: План, Факт, Норматив"""
|
||||
print(f"🔍 DEBUG: Начинаем парсинг сводки СА из файла: {file_path}")
|
||||
|
||||
# === Извлечение и фильтрация ===
|
||||
tables = self.extract_all_tables(file_path, sheet_name)
|
||||
# === Точка входа. Нужно выгрузить три таблицы: План, Факт и Норматив ===
|
||||
|
||||
# Выгружаем План
|
||||
inclusion_list_plan = {
|
||||
"ТиП, %",
|
||||
"Топливо итого, тонн",
|
||||
"Топливо итого, %",
|
||||
"Топливо на технологию, тонн",
|
||||
"Топливо на технологию, %",
|
||||
"Топливо на энергетику, тонн",
|
||||
"Топливо на энергетику, %",
|
||||
"Потери итого, тонн",
|
||||
"Потери итого, %",
|
||||
"в т.ч. Идентифицированные безвозвратные потери, тонн**",
|
||||
"в т.ч. Идентифицированные безвозвратные потери, %**",
|
||||
"в т.ч. Неидентифицированные потери, тонн**",
|
||||
"в т.ч. Неидентифицированные потери, %**"
|
||||
}
|
||||
|
||||
# Фильтруем таблицы: оставляем только те, где первая строка содержит нужные заголовки
|
||||
filtered_tables = []
|
||||
for table in tables:
|
||||
if table.empty:
|
||||
continue
|
||||
first_row_values = table.iloc[0].astype(str).str.strip().tolist()
|
||||
if any(val in inclusion_list for val in first_row_values):
|
||||
filtered_tables.append(table)
|
||||
df_ca_plan = self.parse_sheet(file_path, 'План', inclusion_list_plan)
|
||||
print(f"🔍 DEBUG: Объединённый и отсортированный План: {df_ca_plan.shape if df_ca_plan is not None else 'None'}")
|
||||
|
||||
tables = filtered_tables
|
||||
# Выгружаем Факт
|
||||
inclusion_list_fact = {
|
||||
"ТиП, %",
|
||||
"Топливо итого, тонн",
|
||||
"Топливо итого, %",
|
||||
"Топливо на технологию, тонн",
|
||||
"Топливо на технологию, %",
|
||||
"Топливо на энергетику, тонн",
|
||||
"Топливо на энергетику, %",
|
||||
"Потери итого, тонн",
|
||||
"Потери итого, %",
|
||||
"в т.ч. Идентифицированные безвозвратные потери, тонн",
|
||||
"в т.ч. Идентифицированные безвозвратные потери, %",
|
||||
"в т.ч. Неидентифицированные потери, тонн",
|
||||
"в т.ч. Неидентифицированные потери, %"
|
||||
}
|
||||
|
||||
# === Итоговый список таблиц датафреймов ===
|
||||
result_list = []
|
||||
df_ca_fact = self.parse_sheet(file_path, 'Факт', inclusion_list_fact)
|
||||
print(f"🔍 DEBUG: Объединённый и отсортированный Факт: {df_ca_fact.shape if df_ca_fact is not None else 'None'}")
|
||||
|
||||
for table in tables:
|
||||
if table.empty:
|
||||
continue
|
||||
# Выгружаем Норматив
|
||||
inclusion_list_normativ = {
|
||||
"Топливо итого, тонн",
|
||||
"Топливо итого, %",
|
||||
"Топливо на технологию, тонн",
|
||||
"Топливо на технологию, %",
|
||||
"Топливо на энергетику, тонн",
|
||||
"Топливо на энергетику, %",
|
||||
"Потери итого, тонн",
|
||||
"Потери итого, %",
|
||||
"в т.ч. Идентифицированные безвозвратные потери, тонн**",
|
||||
"в т.ч. Идентифицированные безвозвратные потери, %**",
|
||||
"в т.ч. Неидентифицированные потери, тонн**",
|
||||
"в т.ч. Неидентифицированные потери, %**"
|
||||
}
|
||||
|
||||
# Получаем первую строку (до удаления)
|
||||
first_row_values = table.iloc[0].astype(str).str.strip().tolist()
|
||||
df_ca_normativ = self.parse_sheet(file_path, 'Норматив', inclusion_list_normativ)
|
||||
print(f"🔍 DEBUG: Объединённый и отсортированный Норматив: {df_ca_normativ.shape if df_ca_normativ is not None else 'None'}")
|
||||
|
||||
# Находим, какой элемент из inclusion_list присутствует
|
||||
matched_key = None
|
||||
for val in first_row_values:
|
||||
if val in inclusion_list:
|
||||
matched_key = val
|
||||
break # берём первый совпадающий заголовок
|
||||
|
||||
if matched_key is None:
|
||||
continue # на всякий случай (хотя уже отфильтровано)
|
||||
|
||||
# Удаляем первую строку (заголовок) и сбрасываем индекс
|
||||
df_cleaned = table.iloc[1:].copy().reset_index(drop=True)
|
||||
|
||||
# Пропускаем, если таблица пустая
|
||||
if df_cleaned.empty:
|
||||
continue
|
||||
|
||||
# Первая строка становится заголовком
|
||||
new_header = df_cleaned.iloc[0] # извлекаем первую строку как потенциальные названия столбцов
|
||||
|
||||
# Преобразуем заголовок: только первый столбец может быть заменён на "name"
|
||||
cleaned_header = []
|
||||
|
||||
# Обрабатываем первый столбец отдельно
|
||||
first_item = new_header.iloc[0] if isinstance(new_header, pd.Series) else new_header[0]
|
||||
first_item_str = str(first_item).strip() if pd.notna(first_item) else ""
|
||||
if first_item_str == "" or first_item_str == "nan":
|
||||
cleaned_header.append("name")
|
||||
else:
|
||||
cleaned_header.append(first_item_str)
|
||||
|
||||
# Остальные столбцы добавляем без изменений (или с минимальной очисткой)
|
||||
for item in new_header[1:]:
|
||||
# Опционально: приводим к строке и убираем лишние пробелы, но не заменяем на "name"
|
||||
item_str = str(item).strip() if pd.notna(item) else ""
|
||||
cleaned_header.append(item_str)
|
||||
|
||||
# Применяем очищенные названия столбцов
|
||||
df_cleaned = df_cleaned[1:] # удаляем строку с заголовком
|
||||
df_cleaned.columns = cleaned_header
|
||||
df_cleaned = df_cleaned.reset_index(drop=True)
|
||||
|
||||
if matched_key.endswith('**'):
|
||||
cleaned_key = matched_key[:-2] # удаляем последние **
|
||||
else:
|
||||
cleaned_key = matched_key
|
||||
|
||||
# Добавляем новую колонку с именем параметра
|
||||
df_cleaned["table"] = cleaned_key
|
||||
|
||||
# Проверяем, что колонка 'name' существует
|
||||
if 'name' not in df_cleaned.columns:
|
||||
print(
|
||||
f"Внимание: колонка 'name' отсутствует в таблице для '{matched_key}'. Пропускаем добавление 'id'.")
|
||||
continue # или обработать по-другому
|
||||
else:
|
||||
# Применяем функцию get_id_by_name к каждой строке в колонке 'name'
|
||||
df_cleaned['id'] = df_cleaned['name'].apply(get_og_by_name)
|
||||
|
||||
# Удаляем строки, где id — None, NaN или пустой
|
||||
df_cleaned = df_cleaned.dropna(subset=['id']) # dropna удаляет NaN
|
||||
# Дополнительно: удаляем None (если не поймал dropna)
|
||||
df_cleaned = df_cleaned[df_cleaned['id'].notna() & (df_cleaned['id'].astype(str) != 'None')]
|
||||
|
||||
# Добавляем в словарь
|
||||
result_list.append(df_cleaned)
|
||||
|
||||
# === Объединение и сортировка по id (индекс) и table ===
|
||||
if result_list:
|
||||
combined_df = pd.concat(result_list, axis=0)
|
||||
|
||||
# Сортируем по индексу (id) и по столбцу 'table'
|
||||
combined_df = combined_df.sort_values(by=['id', 'table'], axis=0)
|
||||
|
||||
# Преобразуем DataFrame в словарь по режимам и таблицам
|
||||
# Для сводки СА у нас есть только один режим - 'fact' (по умолчанию)
|
||||
# Но нужно определить режим из данных или параметров
|
||||
mode = params.get('mode', 'fact') # По умолчанию 'fact'
|
||||
|
||||
data_dict = {mode: {}}
|
||||
|
||||
# Группируем данные по таблицам
|
||||
for table_name, group_df in combined_df.groupby('table'):
|
||||
# Удаляем колонку 'table' из результата
|
||||
# Преобразуем DataFrame в словарь по режимам и таблицам
|
||||
data_dict = {}
|
||||
|
||||
# Обрабатываем План
|
||||
if df_ca_plan is not None and not df_ca_plan.empty:
|
||||
data_dict['plan'] = {}
|
||||
for table_name, group_df in df_ca_plan.groupby('table'):
|
||||
table_data = group_df.drop('table', axis=1)
|
||||
data_dict[mode][table_name] = table_data.to_dict('records')
|
||||
|
||||
return data_dict
|
||||
else:
|
||||
return {}
|
||||
data_dict['plan'][table_name] = table_data.to_dict('records')
|
||||
|
||||
# Обрабатываем Факт
|
||||
if df_ca_fact is not None and not df_ca_fact.empty:
|
||||
data_dict['fact'] = {}
|
||||
for table_name, group_df in df_ca_fact.groupby('table'):
|
||||
table_data = group_df.drop('table', axis=1)
|
||||
data_dict['fact'][table_name] = table_data.to_dict('records')
|
||||
|
||||
# Обрабатываем Норматив
|
||||
if df_ca_normativ is not None and not df_ca_normativ.empty:
|
||||
data_dict['normativ'] = {}
|
||||
for table_name, group_df in df_ca_normativ.groupby('table'):
|
||||
table_data = group_df.drop('table', axis=1)
|
||||
data_dict['normativ'][table_name] = table_data.to_dict('records')
|
||||
|
||||
print(f"🔍 DEBUG: Итоговый data_dict содержит режимы: {list(data_dict.keys())}")
|
||||
for mode, tables in data_dict.items():
|
||||
print(f"🔍 DEBUG: Режим '{mode}' содержит таблицы: {list(tables.keys())}")
|
||||
|
||||
return data_dict
|
||||
|
||||
def extract_all_tables(self, file_path, sheet_name=0):
|
||||
"""Извлечение всех таблиц из Excel файла"""
|
||||
|
||||
@@ -102,6 +102,9 @@ class ReportService:
|
||||
|
||||
# Устанавливаем DataFrame в парсер для использования в геттерах
|
||||
parser.df = df
|
||||
print(f"🔍 DEBUG: ReportService.get_data - установлен df в парсер {request.report_type}")
|
||||
print(f"🔍 DEBUG: DataFrame shape: {df.shape if df is not None else 'None'}")
|
||||
print(f"🔍 DEBUG: DataFrame columns: {list(df.columns) if df is not None and not df.empty else 'Empty'}")
|
||||
|
||||
# Получаем параметры запроса
|
||||
get_params = request.get_params or {}
|
||||
|
||||
Reference in New Issue
Block a user