+ai update
This commit is contained in:
42
.ai/PERSONA.md
Normal file
42
.ai/PERSONA.md
Normal file
@@ -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]
|
||||||
@@ -5,6 +5,8 @@
|
|||||||
|
|
||||||
## 1. SYSTEM STANDARDS (Rules of the Game)
|
## 1. SYSTEM STANDARDS (Rules of the Game)
|
||||||
Strict policies and formatting rules.
|
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.
|
* **Constitution:** High-level architectural and business invariants.
|
||||||
* Ref: `.ai/standards/constitution.md` -> `[DEF:Std:Constitution]`
|
* Ref: `.ai/standards/constitution.md` -> `[DEF:Std:Constitution]`
|
||||||
* **Architecture:** Service boundaries and tech stack decisions.
|
* **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]`
|
* Ref: `.ai/shots/critical_module.py` -> `[DEF:Shot:Critical_Module]`
|
||||||
|
|
||||||
## 3. DOMAIN MAP (Modules)
|
## 3. DOMAIN MAP (Modules)
|
||||||
* **Module Map:** `.ai/MODULE_MAP.md` -> `[DEF:Module_Map]`
|
* **High-level Module Map:** `.ai/structure/MODULE_MAP.md` -> `[DEF:Module_Map]`
|
||||||
* **Project Map:** `.ai/PROJECT_MAP.md` -> `[DEF:Project_Map]`
|
* **Low-level Project Map:** `.ai/structure/PROJECT_MAP.md` -> `[DEF:Project_Map]`
|
||||||
* **Apache Superset OpenAPI:** `.ai/openapi.json` -> `[DEF:Doc:Superset_OpenAPI]`
|
* **Apache Superset OpenAPI:** `.ai/openapi/superset_openapi.json` -> `[DEF:Doc:Superset_OpenAPI]`
|
||||||
* **Backend Core:** `backend/src/core` -> `[DEF:Module:Backend_Core]`
|
* **Backend Core:** `backend/src/core` -> `[DEF:Module:Backend_Core]`
|
||||||
* **Backend API:** `backend/src/api` -> `[DEF:Module:Backend_API]`
|
* **Backend API:** `backend/src/api` -> `[DEF:Module:Backend_API]`
|
||||||
* **Frontend Lib:** `frontend/src/lib` -> `[DEF:Module:Frontend_Lib]`
|
* **Frontend Lib:** `frontend/src/lib` -> `[DEF:Module:Frontend_Lib]`
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import base64
|
|||||||
import json
|
import json
|
||||||
import io
|
import io
|
||||||
from typing import List, Dict, Any
|
from typing import List, Dict, Any
|
||||||
|
import httpx
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
from playwright.async_api import async_playwright
|
from playwright.async_api import async_playwright
|
||||||
from openai import AsyncOpenAI, RateLimitError, AuthenticationError as OpenAIAuthenticationError
|
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.
|
# @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):
|
def __init__(self, provider_type: LLMProviderType, api_key: str, base_url: str, default_model: str):
|
||||||
self.provider_type = provider_type
|
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.base_url = base_url
|
||||||
self.default_model = default_model
|
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__] Provider Type: {provider_type}")
|
||||||
logger.info(f"[LLMClient.__init__] Base URL: {base_url}")
|
logger.info(f"[LLMClient.__init__] Base URL: {base_url}")
|
||||||
logger.info(f"[LLMClient.__init__] Default Model: {default_model}")
|
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 (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(api_key) if api_key else 0}")
|
logger.info(f"[LLMClient.__init__] API Key Length: {len(self.api_key) if self.api_key else 0}")
|
||||||
|
|
||||||
self.client = AsyncOpenAI(api_key=api_key, base_url=base_url)
|
# 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.__init__:Function]
|
||||||
|
|
||||||
# [DEF:LLMClient._supports_json_response_format:Function]
|
# [DEF:LLMClient._supports_json_response_format:Function]
|
||||||
|
|||||||
27
check_test_data.py
Normal file
27
check_test_data.py
Normal file
@@ -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)
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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<dt_clearing then\r\n \
|
|
||||||
\ dateDiff(day, dt_overdue, dt_clearing) \r\n else 0\r\n end\
|
|
||||||
\ as overdue_days\r\nfrom dm_view.accounting_documents_leading_to_debt t_docs\r\n\
|
|
||||||
join cp_relations_type ON\r\ncp_relations_type.counterparty_code = t_docs.counterparty_code\r\
|
|
||||||
\nwhere 1=1\r\n\r\nand match(general_ledger_account_code,'((62)|(60)|(76))')\r\n\
|
|
||||||
and debit_or_credit = 'S'\r\nand account_type = 'D'\r\n)\r\n\r\nselect * from t_debt\r\
|
|
||||||
\n\r\nunion all \r\n\r\nselect toLastDayOfMonth(dt_debt) as dt, \r\ncounterparty_search_name,\r\
|
|
||||||
\nrelation_type_code_name,\r\nunit_balance_code_name,\r\n'2. \u043A\u043E\u043B\u0438\
|
|
||||||
\u0447\u0435\u0441\u0442\u0432\u043E \u0442\u0440\u0430\u043D\u0437\u0430\u043A\u0446\
|
|
||||||
\u0438\u0439 \u0432 \u043C\u0435\u0441\u044F\u0446' as attribute,\r\ncount(1) as\
|
|
||||||
\ debt_amount,\r\nnull as overdue_amount\r\nfrom t_transaction_count_base\r\nwhere\
|
|
||||||
\ dt_debt between addMonths(toDate('{{to_dt }}'),-12) and toDate('{{to_dt }}')\r\
|
|
||||||
\ngroup by toLastDayOfMonth(dt_debt), \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\
|
|
||||||
\n'2. \u043A\u043E\u043B\u0438\u0447\u0435\u0441\u0442\u0432\u043E \u0442\u0440\u0430\
|
|
||||||
\u043D\u0437\u0430\u043A\u0446\u0438\u0439 \u0432 \u043C\u0435\u0441\u044F\u0446\
|
|
||||||
' 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\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
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
version: 1.0.0
|
|
||||||
type: Dashboard
|
|
||||||
timestamp: '2026-01-14T11:21:08.078620+00:00'
|
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
* @PURPOSE: Dashboard Hub - Central hub for managing dashboards with Git status and task actions
|
* @PURPOSE: Dashboard Hub - Central hub for managing dashboards with Git status and task actions
|
||||||
* @LAYER: UI
|
* @LAYER: UI
|
||||||
* @RELATION: BINDS_TO -> sidebarStore, taskDrawerStore
|
* @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: Loading -> Shows skeleton loader
|
||||||
* @UX_STATE: Loaded -> Shows dashboard grid with status badges
|
* @UX_STATE: Loaded -> Shows dashboard grid with status badges
|
||||||
@@ -29,7 +29,6 @@
|
|||||||
import {
|
import {
|
||||||
environmentContextStore,
|
environmentContextStore,
|
||||||
initializeEnvironmentContext,
|
initializeEnvironmentContext,
|
||||||
setSelectedEnvironment,
|
|
||||||
} from "$lib/stores/environmentContext.js";
|
} from "$lib/stores/environmentContext.js";
|
||||||
|
|
||||||
// State
|
// 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
|
// Handle search input
|
||||||
function handleSearch(event) {
|
function handleSearch(event) {
|
||||||
debouncedSearch(event.target.value);
|
debouncedSearch(event.target.value);
|
||||||
@@ -724,15 +714,6 @@
|
|||||||
{$t.nav?.dashboards }
|
{$t.nav?.dashboards }
|
||||||
</h1>
|
</h1>
|
||||||
<div class="flex items-center space-x-4">
|
<div class="flex items-center space-x-4">
|
||||||
<select
|
|
||||||
class="px-4 py-2 border border-gray-300 rounded-lg bg-white focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
||||||
bind:value={selectedEnv}
|
|
||||||
on:change={handleEnvChange}
|
|
||||||
>
|
|
||||||
{#each environments as env}
|
|
||||||
<option value={env.id}>{env.name}</option>
|
|
||||||
{/each}
|
|
||||||
</select>
|
|
||||||
<button
|
<button
|
||||||
class="inline-flex items-center justify-center rounded-lg bg-primary px-4 py-2 text-sm font-medium text-white transition-colors hover:bg-primary-hover"
|
class="inline-flex items-center justify-center rounded-lg bg-primary px-4 py-2 text-sm font-medium text-white transition-colors hover:bg-primary-hover"
|
||||||
on:click={loadDashboards}
|
on:click={loadDashboards}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
* @PURPOSE: Dataset Hub - Dedicated hub for datasets with mapping progress
|
* @PURPOSE: Dataset Hub - Dedicated hub for datasets with mapping progress
|
||||||
* @LAYER: UI
|
* @LAYER: UI
|
||||||
* @RELATION: BINDS_TO -> sidebarStore, taskDrawerStore
|
* @RELATION: BINDS_TO -> sidebarStore, taskDrawerStore
|
||||||
* @INVARIANT: Always shows environment selector and dataset grid
|
* @INVARIANT: Always shows datasets for the active environment from context store
|
||||||
*
|
*
|
||||||
* @UX_STATE: Loading -> Shows skeleton loader
|
* @UX_STATE: Loading -> Shows skeleton loader
|
||||||
* @UX_STATE: Loaded -> Shows dataset grid with mapping progress
|
* @UX_STATE: Loaded -> Shows dataset grid with mapping progress
|
||||||
|
|||||||
Reference in New Issue
Block a user