multiolap fixed

This commit is contained in:
2025-07-26 05:53:45 +03:00
parent f5cf4c32da
commit 36a8548562

View File

@@ -328,32 +328,103 @@ def render_olap():
if req_module.login(): if req_module.login():
result = req_module.take_olap(json_body) result = req_module.take_olap(json_body)
# --- НАЧАЛО НОВОЙ УЛУЧШЕННОЙ ЛОГИКИ ОБРАБОТКИ ДАННЫХ ---
if 'data' not in result or not isinstance(result['data'], list): if 'data' not in result or not isinstance(result['data'], list):
flash(_('Error: Unexpected response format from RMS for report "%(name)s".', name=preset.get('name', report_id)), 'error') flash(_('Error: Unexpected response format from RMS for report "%(name)s".', name=preset.get('name', report_id)), 'error')
current_app.logger.error(f"Unexpected API response for report {report_id} ('{preset.get('name')}'). Response: {result}")
return redirect(url_for('.index')) return redirect(url_for('.index'))
data_to_insert = [] report_data = result['data']
if result['data']:
headers = list(result['data'][0].keys()) # Если отчет пуст, очищаем лист и уведомляем пользователя.
data_to_insert.append(headers) if not report_data:
for item in result['data']: gs_client.clear_and_write_data(sheet_title, [])
data_to_insert.append([item.get(h) for h in headers]) flash(_('Report "%(name)s" returned no data for the selected period. Sheet "%(sheet)s" has been cleared.', name=preset.get('name', report_id), sheet=sheet_title), 'warning')
return redirect(url_for('.index'))
# Здесь будет храниться наш итоговый "плоский" список словарей
processed_data = []
# Проверяем структуру отчета: сводный (pivoted) или простой (flat)
first_item = report_data[0]
is_pivoted = isinstance(first_item, dict) and 'row' in first_item and 'cells' in first_item
if is_pivoted:
current_app.logger.info(f"Processing a pivoted report: {preset.get('name', report_id)}")
# "Разворачиваем" (unpivot) данные в плоский список словарей
for row_item in report_data:
row_values = row_item.get('row', {})
cells = row_item.get('cells', [])
if not cells:
# Обрабатываем строки, у которых может не быть данных в ячейках
processed_data.append(row_values.copy())
else:
for cell in cells:
new_flat_row = row_values.copy()
new_flat_row.update(cell.get('col', {}))
new_flat_row.update(cell.get('values', {}))
processed_data.append(new_flat_row)
else:
current_app.logger.info(f"Processing a simple flat report: {preset.get('name', report_id)}")
# Данные уже в виде плоского списка, просто присваиваем
processed_data = [item for item in report_data if isinstance(item, dict)]
# --- Универсальное формирование заголовков и данных ---
# 1. Собираем все уникальные ключи из всех строк для гарантии целостности.
all_keys = set()
for row in processed_data:
all_keys.update(row.keys())
# 2. Создаем упорядоченный список заголовков для лучшей читаемости.
# Используем поля из пресета для определения логического порядка.
row_group_fields = preset.get('groupByRowFields', [])
col_group_fields = preset.get('groupByColFields', [])
agg_fields = preset.get('aggregateFields', [])
ordered_headers = []
# Сначала добавляем известные поля из пресета в логической последовательности.
for field in row_group_fields + col_group_fields + agg_fields:
if field in all_keys:
ordered_headers.append(field)
all_keys.remove(field)
# Добавляем любые другие (неожиданные) поля, отсортировав их по алфавиту.
ordered_headers.extend(sorted(list(all_keys)))
# 3. Собираем итоговый список списков для Google Sheets, приводя все значения к строкам.
data_to_insert = [ordered_headers]
for row in processed_data:
row_data = []
for header in ordered_headers:
value_str = str(row.get(header, ''))
if value_str.startswith(('=', '+', '-', '@')):
row_data.append("'" + value_str)
else:
row_data.append(value_str)
# Преобразуем None в пустую строку, а все остальное в строковое представление.
# Это предотвращает потенциальные ошибки типов со стороны Google Sheets API.
data_to_insert.append(row_data)
gs_client.clear_and_write_data(sheet_title, data_to_insert) gs_client.clear_and_write_data(sheet_title, data_to_insert)
if len(data_to_insert) > 1: rows_count = len(data_to_insert) - 1
flash(_('Report "%(name)s" data successfully written to sheet "%(sheet)s".', name=preset.get('name', report_id), sheet=sheet_title), 'success') flash(_('Report "%(name)s" data (%(rows)s rows) successfully written to sheet "%(sheet)s".',
else: name=preset.get('name', report_id),
flash(_('Report "%(name)s" returned no data for the selected period. Sheet "%(sheet)s" has been cleared.', name=preset.get('name', report_id), sheet=sheet_title), 'warning') rows=rows_count,
sheet=sheet_title), 'success')
else: else:
flash(_('Error authorizing on RMS server when trying to get a report.'), 'error') flash(_('Error authorizing on RMS server when trying to get a report.'), 'error')
except ValueError as ve: except ValueError as ve:
flash(_('Data Error: %(error)s', error=str(ve)), 'error') flash(_('Data Error: %(error)s', error=str(ve)), 'error')
except gspread.exceptions.APIError as api_err: except gspread.exceptions.APIError as api_err:
flash(_('Google API Error accessing sheet "%(sheet)s". Check service account permissions.', sheet=sheet_title), 'error') flash(_('Google API Error accessing sheet "%(sheet)s". Check service account permissions.', sheet=sheet_title or _('Unknown')), 'error')
current_app.logger.error(f"Google API Error for sheet '{sheet_title}': {api_err}", exc_info=True)
except Exception as e: except Exception as e:
flash(_('An unexpected error occurred: %(error)s', error=str(e)), 'error') flash(_('An unexpected error occurred: %(error)s', error=str(e)), 'error')
current_app.logger.error(f"Unexpected error in render_olap: {e}", exc_info=True)
finally: finally:
if req_module and req_module.token: if req_module and req_module.token:
req_module.logout() req_module.logout()