From 4ff6d307f814cdd5d4c1bbfe495d1dd3c2631c08 Mon Sep 17 00:00:00 2001 From: busya Date: Thu, 26 Feb 2026 17:54:23 +0300 Subject: [PATCH] +ai update --- .ai/PERSONA.md | 42 ++++ .ai/ROOT.md | 8 +- .../superset_openapi.json} | 0 .ai/{ => structure}/MODULE_MAP.md | 0 .ai/{ => structure}/PROJECT_MAP.md | 0 backend/src/plugins/llm_analysis/service.py | 26 ++- check_test_data.py | 27 +++ .../charts/FI-0083_4019.yaml | 55 ----- .../databases/Prod_Clickhouse_10.yaml | 13 -- .../Prod_Clickhouse_10/FI-0080-06_875.yaml | 119 ----------- .../Prod_Clickhouse_10/FI-0090_859.yaml | 190 ------------------ .../metadata.yaml | 3 - frontend/src/routes/dashboards/+page.svelte | 21 +- frontend/src/routes/datasets/+page.svelte | 2 +- 14 files changed, 97 insertions(+), 409 deletions(-) create mode 100644 .ai/PERSONA.md rename .ai/{openapi.json => openapi/superset_openapi.json} (100%) rename .ai/{ => structure}/MODULE_MAP.md (100%) rename .ai/{ => structure}/PROJECT_MAP.md (100%) create mode 100644 check_test_data.py delete mode 100644 dashboards_example/dashboard_export_20260114T112108/charts/FI-0083_4019.yaml delete mode 100644 dashboards_example/dashboard_export_20260114T112108/databases/Prod_Clickhouse_10.yaml delete mode 100644 dashboards_example/dashboard_export_20260114T112108/datasets/Prod_Clickhouse_10/FI-0080-06_875.yaml delete mode 100644 dashboards_example/dashboard_export_20260114T112108/datasets/Prod_Clickhouse_10/FI-0090_859.yaml delete mode 100644 dashboards_example/dashboard_export_20260114T112108/metadata.yaml diff --git a/.ai/PERSONA.md b/.ai/PERSONA.md new file mode 100644 index 0000000..78cb86f --- /dev/null +++ b/.ai/PERSONA.md @@ -0,0 +1,42 @@ +# [DEF:Std:UserPersona:Standard] +# @TIER: CRITICAL +# @SEMANTICS: persona, tone_of_voice, interaction_rules, architect +# @PURPOSE: Defines how the AI Agent MUST interact with the user and the codebase. + +@ROLE: Chief Semantic Architect & AI-Engineering Lead. +@PHILOSOPHY: "Смысл первичен. Код вторичен. ИИ — это семантический процессор, а не собеседник." +@METHODOLOGY: Создатель и строгий приверженец стандарта GRACE-Poly. + +## ОЖИДАНИЯ ОТ AI-АГЕНТА (КАК СО МНОЙ РАБОТАТЬ) + +1. **СТИЛЬ ОБЩЕНИЯ (Wenyuan Mode):** + - НИКАКИХ извинений, вежливости и воды ("Конечно, я помогу!", "Извините за ошибку"). + - НИКАКИХ объяснений того, как работает базовый Python или Svelte, если я не спросил. + - Отвечай предельно сухо, структурно и строго по делу. Максимум технической плотности. + +2. **ОТНОШЕНИЕ К КОДУ:** + - Я не принимаю "голый код". Любой код без Контракта (DbC) и Якорей `[DEF]...[/DEF]` считается мусором. + - Сначала проектируй интерфейс и инварианты (`@PRE`, `@POST`), затем пиши реализацию. + - Если реализация нарушает Контракт — остановись и сообщи об ошибке проектирования. Не пытайся "подогнать" логику в обход правил. + +3. **БОРЬБА С "СЕМАНТИЧЕСКИМ КАЗИНО":** + - Не угадывай. Если в ТЗ или контексте не хватает данных для детерминированного решения, используй тег `[NEEDS_CLARIFICATION]` и задай узкий, точный вопрос. + - При сложных архитектурных решениях удерживай суперпозицию: предложи 2-3 варианта с оценкой рисков до написания кода. + +4. **ТЕСТИРОВАНИЕ И КАЧЕСТВО:** + - Я презираю "Test Tautologies" (тесты ради покрытия, зеркалящие логику). + - Тесты должны быть Contract-Driven. Если есть `@PRE`, я ожидаю тест на его нарушение. + - Тесты обязаны использовать `@TEST_DATA` из контрактов. + +5. **ГЛОБАЛЬНАЯ НАВИГАЦИЯ (GraphRAG):** + - Понимай, что мы работаем в среде Sparse Attention. + - Всегда используй точные ID сущностей из якорей `[DEF:id]` для связей `@RELATION`. Не ломай семантические каналы опечатками. + +## ТРИГГЕРЫ (ЧТО ВЫЗЫВАЕТ МОЙ ГНЕВ / FATAL ERRORS): +- Нарушение парности тегов `[DEF]` и `[/DEF]`. +- Написание тестов, которые "мокают" саму проверяемую систему. +- Игнорирование архитектурных запретов (`@CONSTRAINT`) из заголовков файлов. + +**Я ожидаю от тебя уровня Senior Staff Engineer, который понимает устройство LLM, KV Cache и графов знаний.** + +# [/DEF:Std:UserPersona:Standard] \ No newline at end of file diff --git a/.ai/ROOT.md b/.ai/ROOT.md index b57e07c..06b42e0 100644 --- a/.ai/ROOT.md +++ b/.ai/ROOT.md @@ -5,6 +5,8 @@ ## 1. SYSTEM STANDARDS (Rules of the Game) Strict policies and formatting rules. +* **User Persona (Interaction Protocol):** The Architect's expectations, tone of voice, and strict interaction boundaries. + * Ref: `.ai/standards/persona.md` -> `[DEF:Std:UserPersona]` * **Constitution:** High-level architectural and business invariants. * Ref: `.ai/standards/constitution.md` -> `[DEF:Std:Constitution]` * **Architecture:** Service boundaries and tech stack decisions. @@ -30,9 +32,9 @@ Use these for code generation (Style Transfer). * Ref: `.ai/shots/critical_module.py` -> `[DEF:Shot:Critical_Module]` ## 3. DOMAIN MAP (Modules) -* **Module Map:** `.ai/MODULE_MAP.md` -> `[DEF:Module_Map]` -* **Project Map:** `.ai/PROJECT_MAP.md` -> `[DEF:Project_Map]` -* **Apache Superset OpenAPI:** `.ai/openapi.json` -> `[DEF:Doc:Superset_OpenAPI]` +* **High-level Module Map:** `.ai/structure/MODULE_MAP.md` -> `[DEF:Module_Map]` +* **Low-level Project Map:** `.ai/structure/PROJECT_MAP.md` -> `[DEF:Project_Map]` +* **Apache Superset OpenAPI:** `.ai/openapi/superset_openapi.json` -> `[DEF:Doc:Superset_OpenAPI]` * **Backend Core:** `backend/src/core` -> `[DEF:Module:Backend_Core]` * **Backend API:** `backend/src/api` -> `[DEF:Module:Backend_API]` * **Frontend Lib:** `frontend/src/lib` -> `[DEF:Module:Frontend_Lib]` diff --git a/.ai/openapi.json b/.ai/openapi/superset_openapi.json similarity index 100% rename from .ai/openapi.json rename to .ai/openapi/superset_openapi.json diff --git a/.ai/MODULE_MAP.md b/.ai/structure/MODULE_MAP.md similarity index 100% rename from .ai/MODULE_MAP.md rename to .ai/structure/MODULE_MAP.md diff --git a/.ai/PROJECT_MAP.md b/.ai/structure/PROJECT_MAP.md similarity index 100% rename from .ai/PROJECT_MAP.md rename to .ai/structure/PROJECT_MAP.md diff --git a/backend/src/plugins/llm_analysis/service.py b/backend/src/plugins/llm_analysis/service.py index 4ef46af..6acc395 100644 --- a/backend/src/plugins/llm_analysis/service.py +++ b/backend/src/plugins/llm_analysis/service.py @@ -13,6 +13,7 @@ import base64 import json import io from typing import List, Dict, Any +import httpx from PIL import Image from playwright.async_api import async_playwright from openai import AsyncOpenAI, RateLimitError, AuthenticationError as OpenAIAuthenticationError @@ -422,7 +423,10 @@ class LLMClient: # @PRE: api_key, base_url, and default_model are non-empty strings. def __init__(self, provider_type: LLMProviderType, api_key: str, base_url: str, default_model: str): self.provider_type = provider_type - self.api_key = api_key + normalized_key = (api_key or "").strip() + if normalized_key.lower().startswith("bearer "): + normalized_key = normalized_key[7:].strip() + self.api_key = normalized_key self.base_url = base_url self.default_model = default_model @@ -431,10 +435,22 @@ class LLMClient: logger.info(f"[LLMClient.__init__] Provider Type: {provider_type}") logger.info(f"[LLMClient.__init__] Base URL: {base_url}") logger.info(f"[LLMClient.__init__] Default Model: {default_model}") - logger.info(f"[LLMClient.__init__] API Key (first 8 chars): {api_key[:8] if api_key and len(api_key) > 8 else 'EMPTY_OR_NONE'}...") - logger.info(f"[LLMClient.__init__] API Key Length: {len(api_key) if api_key else 0}") - - self.client = AsyncOpenAI(api_key=api_key, base_url=base_url) + logger.info(f"[LLMClient.__init__] API Key (first 8 chars): {self.api_key[:8] if self.api_key and len(self.api_key) > 8 else 'EMPTY_OR_NONE'}...") + logger.info(f"[LLMClient.__init__] API Key Length: {len(self.api_key) if self.api_key else 0}") + + # Some OpenAI-compatible gateways are strict about auth header naming. + default_headers = {"Authorization": f"Bearer {self.api_key}"} + if self.provider_type == LLMProviderType.KILO: + default_headers["Authentication"] = f"Bearer {self.api_key}" + default_headers["X-API-Key"] = self.api_key + + http_client = httpx.AsyncClient(headers=default_headers, timeout=120.0) + self.client = AsyncOpenAI( + api_key=self.api_key, + base_url=base_url, + default_headers=default_headers, + http_client=http_client, + ) # [/DEF:LLMClient.__init__:Function] # [DEF:LLMClient._supports_json_response_format:Function] diff --git a/check_test_data.py b/check_test_data.py new file mode 100644 index 0000000..c41aa2f --- /dev/null +++ b/check_test_data.py @@ -0,0 +1,27 @@ +import os + +def check_file(filepath): + try: + with open(filepath, 'r', encoding='utf-8') as f: + content = f.read() + if '@TIER: CRITICAL' in content: + if '@TEST_DATA' not in content: + return filepath + except Exception as e: + print(f"Error reading {filepath}: {e}") + return None + +missing_files = [] +for root_dir in ['backend/src', 'frontend/src']: + for dirpath, _, filenames in os.walk(root_dir): + for name in filenames: + ext = os.path.splitext(name)[1] + if ext in ['.py', '.js', '.ts', '.svelte']: + full_path = os.path.join(dirpath, name) + res = check_file(full_path) + if res: + missing_files.append(res) + +print("Files missing @TEST_DATA:") +for f in missing_files: + print(f) diff --git a/dashboards_example/dashboard_export_20260114T112108/charts/FI-0083_4019.yaml b/dashboards_example/dashboard_export_20260114T112108/charts/FI-0083_4019.yaml deleted file mode 100644 index 2fe3ed3..0000000 --- a/dashboards_example/dashboard_export_20260114T112108/charts/FI-0083_4019.yaml +++ /dev/null @@ -1,55 +0,0 @@ -slice_name: "FI-0083 \u0421\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u043A\u0430\ - \ \u043F\u043E \u0414\u0417/\u041F\u0414\u0417" -description: null -certified_by: null -certification_details: null -viz_type: pivot_table_v2 -params: - datasource: 859__table - viz_type: pivot_table_v2 - slice_id: 4019 - groupbyColumns: - - dt - groupbyRows: - - counterparty_search_name - - attribute - time_grain_sqla: P1M - temporal_columns_lookup: - dt: true - metrics: - - m_debt_amount - - m_overdue_amount - metricsLayout: COLUMNS - adhoc_filters: - - clause: WHERE - comparator: No filter - expressionType: SIMPLE - operator: TEMPORAL_RANGE - subject: dt - row_limit: '90000' - order_desc: false - aggregateFunction: Sum - combineMetric: true - valueFormat: SMART_NUMBER - date_format: smart_date - rowOrder: key_a_to_z - colOrder: key_a_to_z - value_font_size: 12 - header_font_size: 12 - label_align: left - column_config: - m_debt_amount: - d3NumberFormat: ',d' - m_overdue_amount: - d3NumberFormat: ',d' - conditional_formatting: [] - extra_form_data: {} - dashboards: - - 184 -query_context: '{"datasource":{"id":859,"type":"table"},"force":false,"queries":[{"filters":[{"col":"dt","op":"TEMPORAL_RANGE","val":"No - filter"}],"extras":{"having":"","where":""},"applied_time_extras":{},"columns":[{"timeGrain":"P1M","columnType":"BASE_AXIS","sqlExpression":"dt","label":"dt","expressionType":"SQL"},"counterparty_search_name","attribute"],"metrics":["m_debt_amount","m_overdue_amount"],"orderby":[["m_debt_amount",true]],"annotation_layers":[],"row_limit":90000,"series_limit":0,"order_desc":false,"url_params":{},"custom_params":{},"custom_form_data":{}}],"form_data":{"datasource":"859__table","viz_type":"pivot_table_v2","slice_id":4019,"groupbyColumns":["dt"],"groupbyRows":["counterparty_search_name","attribute"],"time_grain_sqla":"P1M","temporal_columns_lookup":{"dt":true},"metrics":["m_debt_amount","m_overdue_amount"],"metricsLayout":"COLUMNS","adhoc_filters":[{"clause":"WHERE","comparator":"No - filter","expressionType":"SIMPLE","operator":"TEMPORAL_RANGE","subject":"dt"}],"row_limit":"90000","order_desc":false,"aggregateFunction":"Sum","combineMetric":true,"valueFormat":"SMART_NUMBER","date_format":"smart_date","rowOrder":"key_a_to_z","colOrder":"key_a_to_z","value_font_size":12,"header_font_size":12,"label_align":"left","column_config":{"m_debt_amount":{"d3NumberFormat":",d"},"m_overdue_amount":{"d3NumberFormat":",d"}},"conditional_formatting":[],"extra_form_data":{},"dashboards":[184],"force":false,"result_format":"json","result_type":"full"},"result_format":"json","result_type":"full"}' -cache_timeout: null -uuid: 9c293065-73e2-4d9b-a175-d188ff8ef575 -version: 1.0.0 -dataset_uuid: 9e645dc0-da25-4f61-9465-6e649b0bc4b1 diff --git a/dashboards_example/dashboard_export_20260114T112108/databases/Prod_Clickhouse_10.yaml b/dashboards_example/dashboard_export_20260114T112108/databases/Prod_Clickhouse_10.yaml deleted file mode 100644 index f4ed072..0000000 --- a/dashboards_example/dashboard_export_20260114T112108/databases/Prod_Clickhouse_10.yaml +++ /dev/null @@ -1,13 +0,0 @@ -database_name: Prod Clickhouse -sqlalchemy_uri: clickhousedb+connect://viz_superset_click_prod:XXXXXXXXXX@rgm-s-khclk.hq.root.ad:443/dm -cache_timeout: null -expose_in_sqllab: true -allow_run_async: false -allow_ctas: false -allow_cvas: false -allow_dml: true -allow_file_upload: false -extra: - allows_virtual_table_explore: true -uuid: 97aced68-326a-4094-b381-27980560efa9 -version: 1.0.0 diff --git a/dashboards_example/dashboard_export_20260114T112108/datasets/Prod_Clickhouse_10/FI-0080-06_875.yaml b/dashboards_example/dashboard_export_20260114T112108/datasets/Prod_Clickhouse_10/FI-0080-06_875.yaml deleted file mode 100644 index c6672dd..0000000 --- a/dashboards_example/dashboard_export_20260114T112108/datasets/Prod_Clickhouse_10/FI-0080-06_875.yaml +++ /dev/null @@ -1,119 +0,0 @@ -table_name: "FI-0080-06 \u041A\u0430\u043B\u0435\u043D\u0434\u0430\u0440\u044C (\u041E\ - \u0431\u0449\u0438\u0439 \u0441\u043F\u0440\u0430\u0432\u043E\u0447\u043D\u0438\u043A\ - )" -main_dttm_col: null -description: null -default_endpoint: null -offset: 0 -cache_timeout: null -schema: dm_view -sql: "-- [HEADER]\r\n-- [\u041D\u0410\u0417\u041D\u0410\u0427\u0415\u041D\u0418\u0415\ - ]: \u041F\u043E\u043B\u0443\u0447\u0435\u043D\u0438\u0435 \u0434\u0438\u0430\u043F\ - \u0430\u0437\u043E\u043D\u0430 \u0434\u0430\u0442 \u0434\u043B\u044F \u043E\u0442\ - \u0447\u0435\u0442\u0430 \u043E \u0437\u0430\u0434\u043E\u043B\u0436\u0435\u043D\ - \u043D\u043E\u0441\u0442\u044F\u0445 \u043F\u043E \u043E\u0431\u043E\u0440\u043E\ - \u0442\u043D\u044B\u043C \u0441\u0440\u0435\u0434\u0441\u0442\u0432\u0430\u043C\r\ - \n-- [\u041A\u041B\u042E\u0427\u0415\u0412\u042B\u0415 \u041A\u041E\u041B\u041E\u041D\ - \u041A\u0418]:\r\n-- - from_dt_txt: \u041D\u0430\u0447\u0430\u043B\u044C\u043D\ - \u0430\u044F \u0434\u0430\u0442\u0430 \u0432 \u0444\u043E\u0440\u043C\u0430\u0442\ - \u0435 DD.MM.YYYY\r\n-- - to_dt_txt: \u041A\u043E\u043D\u0435\u0447\u043D\u0430\ - \u044F \u0434\u0430\u0442\u0430 \u0432 \u0444\u043E\u0440\u043C\u0430\u0442\u0435\ - \ DD.MM.YYYY\r\n-- [JINJA \u041F\u0410\u0420\u0410\u041C\u0415\u0422\u0420\u042B\ - ]:\r\n-- - {{ filter_values(\"yes_no_check\") }}: \u0424\u0438\u043B\u044C\u0442\ - \u0440 \"\u0414\u0430/\u041D\u0435\u0442\" \u0434\u043B\u044F \u043E\u0433\u0440\ - \u0430\u043D\u0438\u0447\u0435\u043D\u0438\u044F \u0432\u044B\u0431\u043E\u0440\u043A\ - \u0438 \u043F\u043E \u0434\u0430\u0442\u0435\r\n-- [\u041B\u041E\u0413\u0418\u041A\ - \u0410]: \u041E\u043F\u0440\u0435\u0434\u0435\u043B\u044F\u0435\u0442 \u043F\u043E\ - \u0440\u043E\u0433\u043E\u0432\u0443\u044E \u0434\u0430\u0442\u0443 \u0432 \u0437\ - \u0430\u0432\u0438\u0441\u0438\u043C\u043E\u0441\u0442\u0438 \u043E\u0442 \u0442\ - \u0435\u043A\u0443\u0449\u0435\u0433\u043E \u0434\u043D\u044F \u043C\u0435\u0441\ - \u044F\u0446\u0430 \u0438 \u0444\u0438\u043B\u044C\u0442\u0440\u0443\u0435\u0442\ - \ \u0434\u0430\u043D\u043D\u044B\u0435\r\n\r\nWITH date_threshold AS (\r\n SELECT\ - \ \r\n -- \u041E\u043F\u0440\u0435\u0434\u0435\u043B\u044F\u0435\u043C \u043F\ - \u043E\u0440\u043E\u0433\u043E\u0432\u0443\u044E \u0434\u0430\u0442\u0443 \u0432\ - \ \u0437\u0430\u0432\u0438\u0441\u0438\u043C\u043E\u0441\u0442\u0438 \u043E\u0442\ - \ \u0442\u0435\u043A\u0443\u0449\u0435\u0433\u043E \u0434\u043D\u044F \r\n \ - \ CASE \r\n WHEN toDayOfMonth(now()) <= 10 THEN \r\n \ - \ toStartOfMonth(dateSub(MONTH, 1, now())) \r\n ELSE \r\n \ - \ toStartOfMonth(now()) \r\n END AS cutoff_date \r\n),\r\nfiltered_dates\ - \ AS (\r\n SELECT \r\n dt,\r\n formatDateTime(dt, '%d.%m.%Y') AS\ - \ from_dt_txt,\r\n formatDateTime(dt, '%d.%m.%Y') AS to_dt_txt\r\n \ - \ --dt as from_dt_txt,\r\n -- dt as to_dt_txt\r\n FROM dm_view.account_debt_for_working_capital_final\r\ - \n WHERE 1=1\r\n -- \u0411\u0435\u0437\u043E\u043F\u0430\u0441\u043D\u0430\ - \u044F \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0430 \u0444\u0438\u043B\u044C\ - \u0442\u0440\u0430\r\n {% if filter_values(\"yes_no_check\") | length !=\ - \ 0 %}\r\n {% if filter_values(\"yes_no_check\")[0] == \"\u0414\u0430\ - \" %}\r\n AND dt < (SELECT cutoff_date FROM date_threshold)\r\n \ - \ {% endif %}\r\n {% endif %}\r\n)\r\nSELECT \r\ndt,\r\n from_dt_txt,\r\ - \n to_dt_txt,\r\n formatDateTime(toLastDayOfMonth(dt), '%d.%m.%Y') as last_day_of_month_dt_txt\r\ - \nFROM \r\n filtered_dates\r\nGROUP BY \r\n dt, from_dt_txt, to_dt_txt\r\n\ - ORDER BY \r\n dt DESC" -params: null -template_params: null -filter_select_enabled: true -fetch_values_predicate: null -extra: null -normalize_columns: false -uuid: fca62707-6947-4440-a16b-70cb6a5cea5b -metrics: -- metric_name: max_date - verbose_name: max_date - metric_type: count - expression: max(dt) - description: null - d3format: null - currency: null - extra: - warning_markdown: '' - warning_text: null -columns: -- column_name: from_dt_txt - verbose_name: null - is_dttm: true - is_active: true - type: String - advanced_data_type: null - groupby: true - filterable: true - expression: null - description: null - python_date_format: '%Y' - extra: {} -- column_name: dt - verbose_name: null - is_dttm: true - is_active: true - type: Date - advanced_data_type: null - groupby: true - filterable: true - expression: null - description: null - python_date_format: null - extra: {} -- column_name: last_day_of_month_dt_txt - verbose_name: null - is_dttm: false - is_active: true - type: String - advanced_data_type: null - groupby: true - filterable: true - expression: null - description: null - python_date_format: null - extra: {} -- column_name: to_dt_txt - verbose_name: null - is_dttm: true - is_active: true - type: String - advanced_data_type: null - groupby: true - filterable: true - expression: null - description: null - python_date_format: null - extra: {} -version: 1.0.0 -database_uuid: 97aced68-326a-4094-b381-27980560efa9 diff --git a/dashboards_example/dashboard_export_20260114T112108/datasets/Prod_Clickhouse_10/FI-0090_859.yaml b/dashboards_example/dashboard_export_20260114T112108/datasets/Prod_Clickhouse_10/FI-0090_859.yaml deleted file mode 100644 index 15dd8b4..0000000 --- a/dashboards_example/dashboard_export_20260114T112108/datasets/Prod_Clickhouse_10/FI-0090_859.yaml +++ /dev/null @@ -1,190 +0,0 @@ -table_name: "FI-0090 \u0421\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u043A\u0430\ - \ \u043F\u043E \u0414\u0417/\u041F\u0414\u0417" -main_dttm_col: dt -description: null -default_endpoint: null -offset: 0 -cache_timeout: null -schema: dm_view -sql: "-- [JINJA_BLOCK] \u0426\u0435\u043D\u0442\u0440\u0430\u043B\u0438\u0437\u043E\ - \u0432\u0430\u043D\u043D\u043E\u0435 \u043E\u043F\u0440\u0435\u0434\u0435\u043B\u0435\ - \u043D\u0438\u0435 \u0432\u0441\u0435\u0445 Jinja \u043F\u0435\u0440\u0435\u043C\ - \u0435\u043D\u043D\u044B\u0445\r\n{% set raw_to = filter_values('last_day_of_month_dt_txt')[0]\ - \ \r\n if filter_values('last_day_of_month_dt_txt') else '01.05.2025'\ - \ %}\r\n\r\n{# \u0440\u0430\u0437\u0431\u0438\u0432\u0430\u0435\u043C \xABDD.MM.YYYY\xBB\ - \ \u043D\u0430 \u0447\u0430\u0441\u0442\u0438 #}\r\n{% set to_parts = raw_to.split('.')\ - \ %}\r\n\r\n{# \u0441\u043E\u0431\u0438\u0440\u0430\u0435\u043C ISO\u2011\u0441\u0442\ - \u0440\u043E\u043A\u0443 \xABYYYY-MM-DD\xBB #}\r\n{% set to_dt = to_parts[2] \ - \ ~ '-' ~ to_parts[1] ~ '-' ~ to_parts[0] %}\r\n\r\nwith \r\ncp_relations_type\ - \ AS (\r\n select * from ( SELECT \r\n ctd.counterparty_code AS counterparty_code,\r\ - \n min(dt_from) as dt_from,\r\n max(dt_to) as dt_to,\r\n crt.relation_type_code\ - \ || ' ' || crt.relation_type_name AS relation_type_code_name\r\n FROM\r\n \ - \ dm_view.counterparty_td ctd\r\n JOIN dm_view.counterparty_relation_type_texts\ - \ crt \r\n ON ctd.relation_type_code = crt.relation_type_code\r\n GROUP\ - \ BY\r\n ctd.counterparty_code, ctd.counterparty_full_name,\r\n crt.relation_type_code,crt.relation_type_name)\r\ - \n WHERE \r\n dt_from <= toDate('{{to_dt }}') AND \r\n \ - \ dt_to >= toDate('{{to_dt }}')\r\n ),\r\nt_debt as \r\n(SELECT dt, \r\n\ - counterparty_search_name,\r\ncp_relations_type.relation_type_code_name as relation_type_code_name,\r\ - \nunit_balance_code || ' ' || unit_balance_name as unit_balance_code_name,\r\n'1.\ - \ \u0421\u0443\u043C\u043C\u0430' as attribute,\r\nsum(debt_balance_subposition_no_revaluation_usd_amount)\ - \ as debt_amount,\r\nsumIf(debt_balance_subposition_no_revaluation_usd_amount,dt_overdue\ - \ < dt) as overdue_amount\r\nfrom dm_view.account_debt_for_working_capital t_debt\r\ - \njoin cp_relations_type ON\r\ncp_relations_type.counterparty_code = t_debt.counterparty_code\r\ - \nwhere dt = toLastDayOfMonth(dt)\r\nand match(general_ledger_account_code,'((62)|(60)|(76))')\r\ - \nand debit_or_credit = 'S'\r\nand account_type = 'D'\r\nand dt between addMonths(toDate('{{to_dt\ - \ }}'),-12) and toDate('{{to_dt }}')\r\ngroup by dt, counterparty_search_name,unit_balance_code_name,relation_type_code_name\r\ - \n),\r\n\r\nt_transaction_count_base as \r\n(\r\nselect *,\r\ncp_relations_type.relation_type_code_name\ - \ as relation_type_code_name,\r\nunit_balance_code || ' ' || unit_balance_name as\ - \ unit_balance_code_name,\r\n case when dt_overdue 0\r\ngroup by toLastDayOfMonth(dt_clearing), \r\ncounterparty_search_name,\r\ - \nrelation_type_code_name,\r\nunit_balance_code_name,attribute\r\n\r\nunion all\ - \ \r\n\r\nselect toLastDayOfMonth(dt_clearing) as dt, \r\ncounterparty_search_name,\r\ - \nrelation_type_code_name,\r\nunit_balance_code_name,\r\nmultiIf(\r\noverdue_days\ - \ < 30,'3. \u0434\u043E 30',\r\noverdue_days between 30 and 60, '4. \u043E\u0442\ - \ 30 \u0434\u043E 60',\r\noverdue_days between 61 and 90, '5. \u043E\u0442 61 \u0434\ - \u043E 90',\r\noverdue_days>90,'6. \u0431\u043E\u043B\u0435\u0435 90 \u0434\u043D\ - ',\r\nnull\r\n)\r\n as attribute,\r\nnull as debt_amount,\r\ncount(1) as overdue_amount\r\ - \nfrom t_transaction_count_base\r\nwhere dt_clearing between addMonths(toDate('{{to_dt\ - \ }}'),-12) and toDate('{{to_dt }}')\r\nand overdue_days > 0\r\ngroup by toLastDayOfMonth(dt_clearing),\ - \ \r\ncounterparty_search_name,\r\nrelation_type_code_name,\r\nattribute,unit_balance_code_name,attribute\r\ - \n" -params: null -template_params: null -filter_select_enabled: true -fetch_values_predicate: null -extra: null -normalize_columns: false -uuid: 9e645dc0-da25-4f61-9465-6e649b0bc4b1 -metrics: -- metric_name: m_debt_amount - verbose_name: "\u0414\u0417, $" - metric_type: count - expression: sum(debt_amount) - description: null - d3format: null - currency: null - extra: - warning_markdown: '' - warning_text: null -- metric_name: m_overdue_amount - verbose_name: "\u041F\u0414\u0417, $" - metric_type: null - expression: sum(overdue_amount) - description: null - d3format: null - currency: null - extra: - warning_markdown: '' - warning_text: null -columns: -- column_name: debt_amount - verbose_name: null - is_dttm: false - is_active: true - type: Nullable(Decimal(38, 2)) - advanced_data_type: null - groupby: true - filterable: true - expression: null - description: null - python_date_format: null - extra: - warning_markdown: null -- column_name: overdue_amount - verbose_name: null - is_dttm: false - is_active: true - type: Nullable(Decimal(38, 2)) - advanced_data_type: null - groupby: true - filterable: true - expression: null - description: null - python_date_format: null - extra: - warning_markdown: null -- column_name: dt - verbose_name: null - is_dttm: true - is_active: true - type: Nullable(Date) - advanced_data_type: null - groupby: true - filterable: true - expression: null - description: null - python_date_format: null - extra: - warning_markdown: null -- column_name: unit_balance_code_name - verbose_name: null - is_dttm: false - is_active: true - type: Nullable(String) - advanced_data_type: null - groupby: true - filterable: true - expression: null - description: null - python_date_format: null - extra: - warning_markdown: null -- column_name: relation_type_code_name - verbose_name: null - is_dttm: false - is_active: true - type: Nullable(String) - advanced_data_type: null - groupby: true - filterable: true - expression: null - description: null - python_date_format: null - extra: - warning_markdown: null -- column_name: counterparty_search_name - verbose_name: null - is_dttm: false - is_active: true - type: Nullable(String) - advanced_data_type: null - groupby: true - filterable: true - expression: null - description: null - python_date_format: null - extra: - warning_markdown: null -- column_name: attribute - verbose_name: null - is_dttm: false - is_active: true - type: Nullable(String) - advanced_data_type: null - groupby: true - filterable: true - expression: null - description: null - python_date_format: null - extra: - warning_markdown: null -version: 1.0.0 -database_uuid: 97aced68-326a-4094-b381-27980560efa9 diff --git a/dashboards_example/dashboard_export_20260114T112108/metadata.yaml b/dashboards_example/dashboard_export_20260114T112108/metadata.yaml deleted file mode 100644 index bc361de..0000000 --- a/dashboards_example/dashboard_export_20260114T112108/metadata.yaml +++ /dev/null @@ -1,3 +0,0 @@ -version: 1.0.0 -type: Dashboard -timestamp: '2026-01-14T11:21:08.078620+00:00' diff --git a/frontend/src/routes/dashboards/+page.svelte b/frontend/src/routes/dashboards/+page.svelte index 4ff611e..4dc77f6 100644 --- a/frontend/src/routes/dashboards/+page.svelte +++ b/frontend/src/routes/dashboards/+page.svelte @@ -5,7 +5,7 @@ * @PURPOSE: Dashboard Hub - Central hub for managing dashboards with Git status and task actions * @LAYER: UI * @RELATION: BINDS_TO -> sidebarStore, taskDrawerStore - * @INVARIANT: Always shows environment selector and dashboard grid + * @INVARIANT: Always shows dashboards for the active environment from context store * * @UX_STATE: Loading -> Shows skeleton loader * @UX_STATE: Loaded -> Shows dashboard grid with status badges @@ -29,7 +29,6 @@ import { environmentContextStore, initializeEnvironmentContext, - setSelectedEnvironment, } from "$lib/stores/environmentContext.js"; // State @@ -160,15 +159,6 @@ } } - // Handle environment change - function handleEnvChange(event) { - selectedEnv = event.target.value; - setSelectedEnvironment(selectedEnv); - currentPage = 1; - selectedIds.clear(); - loadDashboards(); - } - // Handle search input function handleSearch(event) { debouncedSearch(event.target.value); @@ -724,15 +714,6 @@ {$t.nav?.dashboards }
-