@dataif/cli 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -0
- package/bin/dataif.js +623 -0
- package/package.json +26 -0
- package/scripts/build-template.mjs +72 -0
- package/templates/dataif/README.md +157 -0
- package/templates/dataif/infra/.env.example +119 -0
- package/templates/dataif/infra/.env.stg.example +119 -0
- package/templates/dataif/infra/airflow/Dockerfile +11 -0
- package/templates/dataif/infra/airflow/Dockerfile.release +17 -0
- package/templates/dataif/infra/airflow/requirements.txt +3 -0
- package/templates/dataif/infra/docker-compose.yml +306 -0
- package/templates/dataif/infra/init-db/01-init-dataif.sh +129 -0
- package/templates/dataif/infra/init-db/pnp-curated-views.sqlinc +444 -0
- package/templates/dataif/infra/init-db/pnp-raw-staging-curated.sqlinc +701 -0
- package/templates/dataif/infra/keycloak/Dockerfile +4 -0
- package/templates/dataif/infra/keycloak/realm-dataif.json +73 -0
- package/templates/dataif/infra/ollama/Dockerfile +9 -0
- package/templates/dataif/infra/ollama/bootstrap-model.sh +100 -0
- package/templates/dataif/infra/ollama/sabia-7b.Modelfile +14 -0
- package/templates/dataif/infra/postgres/Dockerfile +4 -0
- package/templates/dataif/pipelines/airflow/dags/generated/.gitkeep +1 -0
- package/templates/dataif/pipelines/airflow/dags/generated/2020_financeiro_fcc6f1f3_sync.py +9 -0
- package/templates/dataif/pipelines/dataif_pipelines/__init__.py +1 -0
- package/templates/dataif/pipelines/dataif_pipelines/airflow/__init__.py +1 -0
- package/templates/dataif/pipelines/dataif_pipelines/airflow/pnp_pipeline_factory.py +167 -0
- package/templates/dataif/pipelines/dataif_pipelines/connectors/__init__.py +1 -0
- package/templates/dataif/pipelines/dataif_pipelines/connectors/base/__init__.py +1 -0
- package/templates/dataif/pipelines/dataif_pipelines/connectors/base/connector.py +28 -0
- package/templates/dataif/pipelines/dataif_pipelines/connectors/base/types.py +14 -0
- package/templates/dataif/pipelines/dataif_pipelines/connectors/nilo_pecanha/__init__.py +1 -0
- package/templates/dataif/pipelines/dataif_pipelines/connectors/nilo_pecanha/config.py +19 -0
- package/templates/dataif/pipelines/dataif_pipelines/connectors/nilo_pecanha/connector.py +558 -0
- package/templates/dataif/pipelines/dataif_pipelines/connectors/nilo_pecanha/powerbi_microdados.py +728 -0
- package/templates/dataif/pipelines/dataif_pipelines/connectors/nilo_pecanha/transform.py +296 -0
- package/templates/dataif/pipelines/dataif_pipelines/jobs/__init__.py +1 -0
- package/templates/dataif/pipelines/dataif_pipelines/jobs/nilo_pipeline.py +112 -0
- package/templates/dataif/pipelines/dataif_pipelines/orchestration/__init__.py +21 -0
- package/templates/dataif/pipelines/dataif_pipelines/orchestration/pnp_workflow.py +783 -0
- package/templates/dataif/pipelines/dataif_pipelines/repositories/__init__.py +1 -0
- package/templates/dataif/pipelines/dataif_pipelines/repositories/pnp_raw_repository.py +860 -0
- package/templates/dataif/pipelines/dataif_pipelines/services/__init__.py +19 -0
- package/templates/dataif/pipelines/dataif_pipelines/services/pnp_curated_service.py +66 -0
- package/templates/dataif/pipelines/dataif_pipelines/services/pnp_download_service.py +534 -0
- package/templates/dataif/pipelines/dataif_pipelines/services/pnp_quality_service.py +9 -0
- package/templates/dataif/pipelines/dataif_pipelines/services/pnp_raw_ingestion_service.py +124 -0
- package/templates/dataif/pipelines/dataif_pipelines/services/pnp_staging_service.py +271 -0
- package/templates/dataif/pipelines/dataif_pipelines/services/powerbi_catalog_service.py +159 -0
- package/templates/dataif/pipelines/sql/staging/020_pnp_matriculas.sql +112 -0
- package/templates/dataif/pipelines/sql/staging/030_pnp_eficiencia_academica.sql +83 -0
- package/templates/dataif/pipelines/sql/staging/040_pnp_servidores.sql +90 -0
- package/templates/dataif/pipelines/sql/staging/050_pnp_financeiro.sql +72 -0
- package/templates/dataif/pipelines/sql/views_curated/004_mv_pnp_dashboard_fast.sql +204 -0
- package/templates/dataif/pipelines/sql/views_curated/010_vw_pnp_admin_ingestao.sql +51 -0
- package/templates/dataif/pipelines/sql/views_curated/020_vw_pnp_qualidade_dados.sql +114 -0
- package/templates/dataif/pipelines/sql/views_curated/030_vw_pnp_matriculas.sql +67 -0
- package/templates/dataif/pipelines/sql/views_curated/040_vw_pnp_eficiencia.sql +33 -0
- package/templates/dataif/pipelines/sql/views_curated/050_vw_pnp_servidores.sql +30 -0
- package/templates/dataif/pipelines/sql/views_curated/060_vw_pnp_financeiro.sql +22 -0
- package/templates/dataif/pipelines/sql/views_curated/070_vw_pnp_vanna.sql +115 -0
- package/templates/dataif/scripts/configure-env.sh +149 -0
- package/templates/dataif/scripts/create_metabase_pnp_dashboard.py +943 -0
- package/templates/dataif/scripts/create_metabase_pnp_matriculas_dashboard.py +580 -0
- package/templates/dataif/scripts/deploy.sh +79 -0
- package/templates/dataif/scripts/fix_metabase_template_tag_ids.py +91 -0
- package/templates/dataif/scripts/pnp_powerbi_microdados_probe.py +14 -0
- package/templates/dataif/scripts/pnp_validate_raw_run.py +330 -0
- package/templates/dataif/scripts/publish-images.sh +31 -0
- package/templates/dataif/scripts/sync_metabase_dashboard_field_filters.py +241 -0
- package/templates/dataif/scripts/use-vanna-ollama.sh +139 -0
- package/templates/dataif/services/api/.dockerignore +18 -0
- package/templates/dataif/services/api/Dockerfile +12 -0
- package/templates/dataif/services/api/app/__init__.py +1 -0
- package/templates/dataif/services/api/app/auth.py +48 -0
- package/templates/dataif/services/api/app/config.py +59 -0
- package/templates/dataif/services/api/app/keycloak_admin.py +215 -0
- package/templates/dataif/services/api/app/main.py +2432 -0
- package/templates/dataif/services/api/app/metabase_admin.py +191 -0
- package/templates/dataif/services/api/app/metabase_bootstrap.py +44 -0
- package/templates/dataif/services/api/app/metabase_embed.py +15 -0
- package/templates/dataif/services/api/app/pnp_dag_provisioner.py +113 -0
- package/templates/dataif/services/api/app/pnp_instance_repository.py +951 -0
- package/templates/dataif/services/api/app/pnp_powerbi.py +438 -0
- package/templates/dataif/services/api/app/vanna_client.py +32 -0
- package/templates/dataif/services/api/requirements.txt +9 -0
- package/templates/dataif/services/vanna/.dockerignore +18 -0
- package/templates/dataif/services/vanna/Dockerfile +12 -0
- package/templates/dataif/services/vanna/app/config.py +57 -0
- package/templates/dataif/services/vanna/app/main.py +108 -0
- package/templates/dataif/services/vanna/app/runtime_config.py +114 -0
- package/templates/dataif/services/vanna/app/sql_guard.py +123 -0
- package/templates/dataif/services/vanna/app/vanna_engine.py +382 -0
- package/templates/dataif/services/vanna/requirements.txt +8 -0
- package/templates/dataif/services/web/.dockerignore +13 -0
- package/templates/dataif/services/web/Dockerfile +16 -0
- package/templates/dataif/services/web/index.html +12 -0
- package/templates/dataif/services/web/nginx.conf +74 -0
- package/templates/dataif/services/web/package-lock.json +4397 -0
- package/templates/dataif/services/web/package.json +32 -0
- package/templates/dataif/services/web/postcss.config.mjs +5 -0
- package/templates/dataif/services/web/src/App.jsx +2817 -0
- package/templates/dataif/services/web/src/adminAuth.js +245 -0
- package/templates/dataif/services/web/src/assets/avatar_placeholder.png +0 -0
- package/templates/dataif/services/web/src/assets/github_logo_icon_229278.svg +1 -0
- package/templates/dataif/services/web/src/assets/if-logo.png +0 -0
- package/templates/dataif/services/web/src/assets/if.svg +0 -0
- package/templates/dataif/services/web/src/assets/pnp-horizontal.svg +1 -0
- package/templates/dataif/services/web/src/components/AppHeader.jsx +233 -0
- package/templates/dataif/services/web/src/components/application/app-navigation/base-components/mobile-header.tsx +56 -0
- package/templates/dataif/services/web/src/components/application/app-navigation/base-components/nav-account-card.tsx +209 -0
- package/templates/dataif/services/web/src/components/application/app-navigation/base-components/nav-item-button.tsx +67 -0
- package/templates/dataif/services/web/src/components/application/app-navigation/base-components/nav-item.tsx +108 -0
- package/templates/dataif/services/web/src/components/application/app-navigation/base-components/nav-list.tsx +83 -0
- package/templates/dataif/services/web/src/components/application/app-navigation/config.ts +23 -0
- package/templates/dataif/services/web/src/components/application/app-navigation/header-navigation.tsx +240 -0
- package/templates/dataif/services/web/src/components/application/pagination/pagination-base.tsx +376 -0
- package/templates/dataif/services/web/src/components/application/pagination/pagination-dot.tsx +52 -0
- package/templates/dataif/services/web/src/components/application/pagination/pagination-line.tsx +48 -0
- package/templates/dataif/services/web/src/components/application/pagination/pagination.tsx +328 -0
- package/templates/dataif/services/web/src/components/application/tabs/tabs.tsx +223 -0
- package/templates/dataif/services/web/src/components/base/avatar/avatar-label-group.tsx +28 -0
- package/templates/dataif/services/web/src/components/base/avatar/avatar.tsx +129 -0
- package/templates/dataif/services/web/src/components/base/avatar/base-components/avatar-add-button.tsx +32 -0
- package/templates/dataif/services/web/src/components/base/avatar/base-components/avatar-company-icon.tsx +24 -0
- package/templates/dataif/services/web/src/components/base/avatar/base-components/avatar-online-indicator.tsx +29 -0
- package/templates/dataif/services/web/src/components/base/avatar/base-components/index.tsx +4 -0
- package/templates/dataif/services/web/src/components/base/avatar/base-components/verified-tick.tsx +32 -0
- package/templates/dataif/services/web/src/components/base/badges/badge-types.ts +264 -0
- package/templates/dataif/services/web/src/components/base/badges/badges.tsx +415 -0
- package/templates/dataif/services/web/src/components/base/button-group/button-group.tsx +104 -0
- package/templates/dataif/services/web/src/components/base/buttons/button.tsx +267 -0
- package/templates/dataif/services/web/src/components/base/input/hint-text.tsx +31 -0
- package/templates/dataif/services/web/src/components/base/input/input.tsx +269 -0
- package/templates/dataif/services/web/src/components/base/input/label.tsx +48 -0
- package/templates/dataif/services/web/src/components/base/radio-buttons/radio-buttons.tsx +127 -0
- package/templates/dataif/services/web/src/components/base/select/combobox.tsx +150 -0
- package/templates/dataif/services/web/src/components/base/select/multi-select.tsx +361 -0
- package/templates/dataif/services/web/src/components/base/select/popover.tsx +32 -0
- package/templates/dataif/services/web/src/components/base/select/select-item.tsx +95 -0
- package/templates/dataif/services/web/src/components/base/select/select-native.tsx +67 -0
- package/templates/dataif/services/web/src/components/base/select/select.tsx +144 -0
- package/templates/dataif/services/web/src/components/base/tags/base-components/tag-close-x.tsx +32 -0
- package/templates/dataif/services/web/src/components/base/tooltip/tooltip.tsx +107 -0
- package/templates/dataif/services/web/src/components/foundations/dot-icon.tsx +22 -0
- package/templates/dataif/services/web/src/components/foundations/logo/untitledui-logo-minimal.tsx +170 -0
- package/templates/dataif/services/web/src/components/foundations/logo/untitledui-logo.tsx +58 -0
- package/templates/dataif/services/web/src/hooks/use-breakpoint.ts +34 -0
- package/templates/dataif/services/web/src/hooks/use-resize-observer.ts +67 -0
- package/templates/dataif/services/web/src/main.jsx +14 -0
- package/templates/dataif/services/web/src/providers/theme-provider.jsx +62 -0
- package/templates/dataif/services/web/src/styles/globals.css +60 -0
- package/templates/dataif/services/web/src/styles/theme.css +1326 -0
- package/templates/dataif/services/web/src/styles/typography.css +430 -0
- package/templates/dataif/services/web/src/styles.css +1287 -0
- package/templates/dataif/services/web/src/utils/cx.ts +24 -0
- package/templates/dataif/services/web/src/utils/is-react-component.ts +33 -0
- package/templates/dataif/services/web/vite.config.js +14 -0
- package/templates/dataif/sql/ddl/001_schemas.sql +6 -0
- package/templates/dataif/sql/ddl/003_pnp_raw_staging_curated.sql +699 -0
- package/templates/dataif/sql/migrations/001_pnp_phase1_backfill.sql +3 -0
- package/templates/dataif/sql/migrations/002_pnp_phase2_admin_config_backfill.sql +184 -0
- package/templates/dataif/sql/migrations/003_pnp_phase3_raw_tabular_backfill.sql +3 -0
- package/templates/dataif/sql/migrations/004_pnp_phase3_raw_backfill_support_index.sql +3 -0
- package/templates/dataif/sql/migrations/005_pnp_phase7_staging_support_indexes.sql +2 -0
- package/templates/dataif/sql/migrations/006_pnp_phase7_staging_autovacuum_tuning.sql +2 -0
- package/templates/dataif/sql/migrations/007_pnp_phase7b_run_packages.sql +20 -0
- package/templates/dataif/sql/migrations/008_pnp_phase7a_pipeline_endpoints.sql +169 -0
- package/templates/dataif/sql/migrations/009_pnp_phase8_curated.sql +35 -0
- package/templates/dataif/sql/migrations/010_pnp_phase10_staging_incremental_upsert.sql +3 -0
- package/templates/dataif/sql/migrations/010_pnp_pipeline_uuid.sql +51 -0
- package/templates/dataif/sql/migrations/011_app_settings.sql +7 -0
- package/templates/dataif/sql/staging/020_pnp_matriculas.sql +112 -0
- package/templates/dataif/sql/staging/030_pnp_eficiencia_academica.sql +83 -0
- package/templates/dataif/sql/staging/040_pnp_servidores.sql +90 -0
- package/templates/dataif/sql/staging/050_pnp_financeiro.sql +72 -0
- package/templates/dataif/sql/views_curated/003_vw_pnp_microdados_admin.sql +160 -0
- package/templates/dataif/sql/views_curated/004_mv_pnp_dashboard_fast.sql +204 -0
- package/templates/dataif/sql/views_curated/010_vw_pnp_admin_ingestao.sql +51 -0
- package/templates/dataif/sql/views_curated/020_vw_pnp_qualidade_dados.sql +114 -0
- package/templates/dataif/sql/views_curated/030_vw_pnp_matriculas.sql +67 -0
- package/templates/dataif/sql/views_curated/040_vw_pnp_eficiencia.sql +33 -0
- package/templates/dataif/sql/views_curated/050_vw_pnp_servidores.sql +30 -0
- package/templates/dataif/sql/views_curated/060_vw_pnp_financeiro.sql +22 -0
- package/templates/dataif/sql/views_curated/070_vw_pnp_vanna.sql +115 -0
|
@@ -0,0 +1,943 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
import sys
|
|
6
|
+
import urllib.error
|
|
7
|
+
import urllib.request
|
|
8
|
+
from dataclasses import dataclass
|
|
9
|
+
from hashlib import md5
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
API_BASE = os.getenv("METABASE_API_URL", "http://localhost:3001/api").rstrip("/")
|
|
13
|
+
API_KEY = os.getenv("METABASE_API_KEY")
|
|
14
|
+
DATABASE_ID = int(os.getenv("METABASE_DATABASE_ID", "2"))
|
|
15
|
+
DASHBOARD_NAME = os.getenv(
|
|
16
|
+
"METABASE_DASHBOARD_NAME",
|
|
17
|
+
"PNP 2024 - Painel Integrado de Gestao Administrativa",
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
if not API_KEY:
|
|
21
|
+
raise SystemExit("METABASE_API_KEY is required")
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def api(method: str, path: str, payload: dict | list | None = None) -> dict | list:
|
|
25
|
+
data = None
|
|
26
|
+
headers = {"x-api-key": API_KEY, "Accept": "application/json"}
|
|
27
|
+
if payload is not None:
|
|
28
|
+
data = json.dumps(payload).encode("utf-8")
|
|
29
|
+
headers["Content-Type"] = "application/json"
|
|
30
|
+
|
|
31
|
+
request = urllib.request.Request(f"{API_BASE}{path}", data=data, headers=headers, method=method)
|
|
32
|
+
try:
|
|
33
|
+
with urllib.request.urlopen(request) as response:
|
|
34
|
+
body = response.read().decode("utf-8")
|
|
35
|
+
return json.loads(body) if body else {}
|
|
36
|
+
except urllib.error.HTTPError as exc:
|
|
37
|
+
body = exc.read().decode("utf-8", errors="replace")
|
|
38
|
+
raise RuntimeError(f"{method} {path} failed: {exc.code} {body}") from exc
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def find_dashboard_by_name(name: str) -> dict | None:
|
|
42
|
+
for dashboard in api("GET", "/dashboard"):
|
|
43
|
+
if dashboard.get("name") == name and not dashboard.get("archived"):
|
|
44
|
+
return dashboard
|
|
45
|
+
return None
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def list_cards() -> list[dict]:
|
|
49
|
+
return list(api("GET", "/card"))
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def archive_card(card_id: int) -> None:
|
|
53
|
+
api("PUT", f"/card/{card_id}", {"archived": True})
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def cleanup_previous_pnp_cards() -> None:
|
|
57
|
+
prefix = "PNP 2024 - "
|
|
58
|
+
for card in list_cards():
|
|
59
|
+
if card.get("archived"):
|
|
60
|
+
continue
|
|
61
|
+
name = card.get("name") or ""
|
|
62
|
+
if name.startswith(prefix):
|
|
63
|
+
archive_card(int(card["id"]))
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def ensure_dashboard() -> int:
|
|
67
|
+
existing = find_dashboard_by_name(DASHBOARD_NAME)
|
|
68
|
+
if existing:
|
|
69
|
+
dashboard_id = int(existing["id"])
|
|
70
|
+
api(
|
|
71
|
+
"PUT",
|
|
72
|
+
f"/dashboard/{dashboard_id}",
|
|
73
|
+
{
|
|
74
|
+
"name": DASHBOARD_NAME,
|
|
75
|
+
"description": DASHBOARD_DESCRIPTION,
|
|
76
|
+
"width": "fixed",
|
|
77
|
+
"auto_apply_filters": True,
|
|
78
|
+
"parameters": DASHBOARD_PARAMETERS,
|
|
79
|
+
"tabs": [],
|
|
80
|
+
"dashcards": [],
|
|
81
|
+
},
|
|
82
|
+
)
|
|
83
|
+
return dashboard_id
|
|
84
|
+
|
|
85
|
+
response = api(
|
|
86
|
+
"POST",
|
|
87
|
+
"/dashboard",
|
|
88
|
+
{
|
|
89
|
+
"name": DASHBOARD_NAME,
|
|
90
|
+
"description": DASHBOARD_DESCRIPTION,
|
|
91
|
+
"width": "fixed",
|
|
92
|
+
"auto_apply_filters": True,
|
|
93
|
+
"collection_id": None,
|
|
94
|
+
"parameters": DASHBOARD_PARAMETERS,
|
|
95
|
+
},
|
|
96
|
+
)
|
|
97
|
+
return int(response["id"])
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def create_card(card: "CardDef") -> int:
|
|
101
|
+
response = api(
|
|
102
|
+
"POST",
|
|
103
|
+
"/card",
|
|
104
|
+
{
|
|
105
|
+
"name": card.name,
|
|
106
|
+
"description": card.description,
|
|
107
|
+
"display": card.display,
|
|
108
|
+
"database_id": DATABASE_ID,
|
|
109
|
+
"collection_id": None,
|
|
110
|
+
"dataset_query": {
|
|
111
|
+
"type": "native",
|
|
112
|
+
"database": DATABASE_ID,
|
|
113
|
+
"native": {
|
|
114
|
+
"query": card.sql,
|
|
115
|
+
"template-tags": card.template_tags(),
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
"visualization_settings": card.visualization_settings,
|
|
119
|
+
},
|
|
120
|
+
)
|
|
121
|
+
return int(response["id"])
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def update_dashboard(dashboard_id: int, tabs: list[dict], dashcards: list[dict]) -> None:
|
|
125
|
+
api(
|
|
126
|
+
"PUT",
|
|
127
|
+
f"/dashboard/{dashboard_id}",
|
|
128
|
+
{
|
|
129
|
+
"name": DASHBOARD_NAME,
|
|
130
|
+
"description": DASHBOARD_DESCRIPTION,
|
|
131
|
+
"width": "fixed",
|
|
132
|
+
"auto_apply_filters": True,
|
|
133
|
+
"parameters": DASHBOARD_PARAMETERS,
|
|
134
|
+
"tabs": tabs,
|
|
135
|
+
"dashcards": dashcards,
|
|
136
|
+
},
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
@dataclass(frozen=True)
|
|
141
|
+
class CardDef:
|
|
142
|
+
name: str
|
|
143
|
+
description: str
|
|
144
|
+
tab: str
|
|
145
|
+
row: int
|
|
146
|
+
col: int
|
|
147
|
+
size_x: int
|
|
148
|
+
size_y: int
|
|
149
|
+
sql: str
|
|
150
|
+
display: str
|
|
151
|
+
filters: tuple[str, ...]
|
|
152
|
+
visualization_settings: dict
|
|
153
|
+
|
|
154
|
+
def template_tags(self) -> dict:
|
|
155
|
+
return {
|
|
156
|
+
slug: {
|
|
157
|
+
**TEMPLATE_TAGS[slug],
|
|
158
|
+
# Metabase requires template tag ids so the SQL editor can persist
|
|
159
|
+
# card parameter definitions on subsequent edits.
|
|
160
|
+
"id": md5(f"{self.name}:{slug}".encode("utf-8")).hexdigest()[:12],
|
|
161
|
+
}
|
|
162
|
+
for slug in self.filters
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
def parameter_mappings(self) -> list[dict]:
|
|
166
|
+
return [
|
|
167
|
+
{
|
|
168
|
+
"parameter_id": PARAM_ID_BY_SLUG[slug],
|
|
169
|
+
"target": ["variable", ["template-tag", slug]],
|
|
170
|
+
}
|
|
171
|
+
for slug in self.filters
|
|
172
|
+
]
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
@dataclass(frozen=True)
|
|
176
|
+
class VirtualCardDef:
|
|
177
|
+
tab: str
|
|
178
|
+
text: str
|
|
179
|
+
row: int
|
|
180
|
+
col: int
|
|
181
|
+
size_x: int
|
|
182
|
+
size_y: int
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
DASHBOARD_DESCRIPTION = (
|
|
186
|
+
"Painel da PNP organizado em guias tematicas leves, com filtros do cabecalho "
|
|
187
|
+
"ligados apenas aos conjuntos de perguntas correspondentes."
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
TAB_LAYOUT = ["Matriculas", "Eficiencia Academica", "Servidores", "Financeiro", "Qualidade e Ingestao"]
|
|
191
|
+
|
|
192
|
+
DASHBOARD_PARAMETERS = [
|
|
193
|
+
{"id": "p_ano", "name": "Ano", "slug": "ano", "type": "number/=", "sectionId": "number"},
|
|
194
|
+
{"id": "p_instituicao", "name": "Instituicao", "slug": "instituicao", "type": "string/=", "sectionId": "string"},
|
|
195
|
+
{"id": "p_regiao", "name": "Regiao", "slug": "regiao", "type": "string/=", "sectionId": "string"},
|
|
196
|
+
{"id": "p_uf", "name": "UF", "slug": "uf", "type": "string/=", "sectionId": "string"},
|
|
197
|
+
{"id": "p_municipio", "name": "Municipio", "slug": "municipio", "type": "string/=", "sectionId": "string"},
|
|
198
|
+
{"id": "p_sexo", "name": "Sexo", "slug": "sexo", "type": "string/=", "sectionId": "string"},
|
|
199
|
+
{"id": "p_cor_raca", "name": "Cor / Raca", "slug": "cor_raca", "type": "string/=", "sectionId": "string"},
|
|
200
|
+
{"id": "p_renda_familiar", "name": "Renda Familiar", "slug": "renda_familiar", "type": "string/=", "sectionId": "string"},
|
|
201
|
+
{"id": "p_faixa_etaria", "name": "Faixa Etaria", "slug": "faixa_etaria", "type": "string/=", "sectionId": "string"},
|
|
202
|
+
{"id": "p_situacao_matricula", "name": "Situacao de Matricula", "slug": "situacao_matricula", "type": "string/=", "sectionId": "string"},
|
|
203
|
+
{"id": "p_modalidade_ensino", "name": "Modalidade de Ensino", "slug": "modalidade_ensino", "type": "string/=", "sectionId": "string"},
|
|
204
|
+
{"id": "p_tipo_curso", "name": "Tipo de Curso", "slug": "tipo_curso", "type": "string/=", "sectionId": "string"},
|
|
205
|
+
{"id": "p_tipo_oferta", "name": "Tipo de Oferta", "slug": "tipo_oferta", "type": "string/=", "sectionId": "string"},
|
|
206
|
+
{"id": "p_turno", "name": "Turno", "slug": "turno", "type": "string/=", "sectionId": "string"},
|
|
207
|
+
{"id": "p_nome_curso", "name": "Nome do Curso", "slug": "nome_curso", "type": "string/=", "sectionId": "string"},
|
|
208
|
+
{"id": "p_matricula_atendida", "name": "Matricula Atendida", "slug": "matricula_atendida", "type": "string/=", "sectionId": "string"},
|
|
209
|
+
{"id": "p_classe", "name": "Classe", "slug": "classe", "type": "string/=", "sectionId": "string"},
|
|
210
|
+
{"id": "p_jornada_trabalho", "name": "Jornada de Trabalho", "slug": "jornada_trabalho", "type": "string/=", "sectionId": "string"},
|
|
211
|
+
{"id": "p_titulacao", "name": "Titulacao", "slug": "titulacao", "type": "string/=", "sectionId": "string"},
|
|
212
|
+
{"id": "p_vinculo_carreira", "name": "Vinculo de Carreira", "slug": "vinculo_carreira", "type": "string/=", "sectionId": "string"},
|
|
213
|
+
{"id": "p_vinculo_contrato", "name": "Vinculo de Contrato", "slug": "vinculo_contrato", "type": "string/=", "sectionId": "string"},
|
|
214
|
+
{"id": "p_vinculo_professor", "name": "Vinculo Professor", "slug": "vinculo_professor", "type": "string/=", "sectionId": "string"},
|
|
215
|
+
{"id": "p_nome_uo", "name": "Nome UO", "slug": "nome_uo", "type": "string/=", "sectionId": "string"},
|
|
216
|
+
{"id": "p_grupo_despesa", "name": "Grupo de Despesa", "slug": "grupo_despesa", "type": "string/=", "sectionId": "string"},
|
|
217
|
+
{"id": "p_cod_acao", "name": "Codigo da Acao", "slug": "cod_acao", "type": "string/=", "sectionId": "string"},
|
|
218
|
+
{"id": "p_nome_acao", "name": "Nome da Acao", "slug": "nome_acao", "type": "string/=", "sectionId": "string"},
|
|
219
|
+
]
|
|
220
|
+
|
|
221
|
+
PARAM_ID_BY_SLUG = {item["slug"]: item["id"] for item in DASHBOARD_PARAMETERS}
|
|
222
|
+
|
|
223
|
+
TEMPLATE_TAGS = {
|
|
224
|
+
slug: {
|
|
225
|
+
"name": slug,
|
|
226
|
+
"display-name": next(item["name"] for item in DASHBOARD_PARAMETERS if item["slug"] == slug),
|
|
227
|
+
"type": "number" if slug == "ano" else "text",
|
|
228
|
+
"required": False,
|
|
229
|
+
}
|
|
230
|
+
for slug in PARAM_ID_BY_SLUG
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def where_clause(filters: tuple[str, ...]) -> str:
|
|
235
|
+
clauses = {
|
|
236
|
+
"ano": "ano = {{ano}}",
|
|
237
|
+
"instituicao": "instituicao = {{instituicao}}",
|
|
238
|
+
"regiao": "regiao = {{regiao}}",
|
|
239
|
+
"uf": "uf = {{uf}}",
|
|
240
|
+
"municipio": "municipio = {{municipio}}",
|
|
241
|
+
"sexo": "sexo = {{sexo}}",
|
|
242
|
+
"cor_raca": "cor_raca = {{cor_raca}}",
|
|
243
|
+
"renda_familiar": "renda_familiar = {{renda_familiar}}",
|
|
244
|
+
"faixa_etaria": "faixa_etaria = {{faixa_etaria}}",
|
|
245
|
+
"situacao_matricula": "situacao_matricula = {{situacao_matricula}}",
|
|
246
|
+
"modalidade_ensino": "modalidade_ensino = {{modalidade_ensino}}",
|
|
247
|
+
"tipo_curso": "tipo_curso = {{tipo_curso}}",
|
|
248
|
+
"tipo_oferta": "tipo_oferta = {{tipo_oferta}}",
|
|
249
|
+
"turno": "turno = {{turno}}",
|
|
250
|
+
"nome_curso": "nome_curso = {{nome_curso}}",
|
|
251
|
+
"matricula_atendida": "matricula_atendida = {{matricula_atendida}}",
|
|
252
|
+
"classe": "classe = {{classe}}",
|
|
253
|
+
"jornada_trabalho": "jornada_trabalho = {{jornada_trabalho}}",
|
|
254
|
+
"titulacao": "titulacao = {{titulacao}}",
|
|
255
|
+
"vinculo_carreira": "vinculo_carreira = {{vinculo_carreira}}",
|
|
256
|
+
"vinculo_contrato": "vinculo_contrato = {{vinculo_contrato}}",
|
|
257
|
+
"vinculo_professor": "vinculo_professor = {{vinculo_professor}}",
|
|
258
|
+
"nome_uo": "nome_uo = {{nome_uo}}",
|
|
259
|
+
"grupo_despesa": "grupo_despesa = {{grupo_despesa}}",
|
|
260
|
+
"cod_acao": "cod_acao = {{cod_acao}}",
|
|
261
|
+
"nome_acao": "nome_acao = {{nome_acao}}",
|
|
262
|
+
}
|
|
263
|
+
parts = ["WHERE 1=1"]
|
|
264
|
+
for slug in filters:
|
|
265
|
+
parts.append(f"[[ AND {clauses[slug]} ]]")
|
|
266
|
+
return "\n".join(parts)
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
MATRICULAS_FILTERS = (
|
|
270
|
+
"ano",
|
|
271
|
+
"instituicao",
|
|
272
|
+
"regiao",
|
|
273
|
+
"uf",
|
|
274
|
+
"municipio",
|
|
275
|
+
"sexo",
|
|
276
|
+
"cor_raca",
|
|
277
|
+
"renda_familiar",
|
|
278
|
+
"faixa_etaria",
|
|
279
|
+
"situacao_matricula",
|
|
280
|
+
"modalidade_ensino",
|
|
281
|
+
"tipo_curso",
|
|
282
|
+
"tipo_oferta",
|
|
283
|
+
"turno",
|
|
284
|
+
"nome_curso",
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
EFICIENCIA_FILTERS = (
|
|
288
|
+
"ano",
|
|
289
|
+
"instituicao",
|
|
290
|
+
"regiao",
|
|
291
|
+
"uf",
|
|
292
|
+
"municipio",
|
|
293
|
+
"sexo",
|
|
294
|
+
"cor_raca",
|
|
295
|
+
"renda_familiar",
|
|
296
|
+
"faixa_etaria",
|
|
297
|
+
"situacao_matricula",
|
|
298
|
+
"matricula_atendida",
|
|
299
|
+
)
|
|
300
|
+
|
|
301
|
+
SERVIDORES_FILTERS = (
|
|
302
|
+
"ano",
|
|
303
|
+
"instituicao",
|
|
304
|
+
"regiao",
|
|
305
|
+
"classe",
|
|
306
|
+
"jornada_trabalho",
|
|
307
|
+
"titulacao",
|
|
308
|
+
"vinculo_carreira",
|
|
309
|
+
"vinculo_contrato",
|
|
310
|
+
"vinculo_professor",
|
|
311
|
+
)
|
|
312
|
+
|
|
313
|
+
FINANCEIRO_FILTERS = ("ano", "nome_uo", "grupo_despesa", "cod_acao", "nome_acao")
|
|
314
|
+
|
|
315
|
+
MATRICULAS_SOURCE = "curated.mv_pnp_dashboard_matriculas"
|
|
316
|
+
EFICIENCIA_SOURCE = "curated.mv_pnp_dashboard_eficiencia"
|
|
317
|
+
SERVIDORES_SOURCE = "curated.mv_pnp_dashboard_servidores"
|
|
318
|
+
FINANCEIRO_SOURCE = "curated.mv_pnp_dashboard_financeiro"
|
|
319
|
+
QUALIDADE_SOURCE = "curated.mv_pnp_dashboard_qualidade"
|
|
320
|
+
INGESTAO_SOURCE = "curated.mv_pnp_dashboard_ingestao"
|
|
321
|
+
|
|
322
|
+
VIRTUALS = [
|
|
323
|
+
VirtualCardDef("Matriculas", "Oferta, procura, perfil discente e situacao das matriculas.", 0, 0, 24, 1),
|
|
324
|
+
VirtualCardDef("Eficiencia Academica", "Permanencia, conclusao e evasao por perfil e territorio.", 0, 0, 24, 1),
|
|
325
|
+
VirtualCardDef("Servidores", "Composicao do quadro por instituicao, titulacao, jornada e vinculos.", 0, 0, 24, 1),
|
|
326
|
+
VirtualCardDef("Financeiro", "Execucao financeira por UO, acao e grupo de despesa.", 0, 0, 24, 1),
|
|
327
|
+
VirtualCardDef("Qualidade e Ingestao", "Qualidade basica dos microdados e trilha operacional da carga.", 0, 0, 24, 1),
|
|
328
|
+
]
|
|
329
|
+
|
|
330
|
+
CARDS = [
|
|
331
|
+
CardDef(
|
|
332
|
+
name="PNP 2024 - KPI Matriculas",
|
|
333
|
+
description="Total de matriculas no recorte.",
|
|
334
|
+
tab="Matriculas",
|
|
335
|
+
row=1,
|
|
336
|
+
col=0,
|
|
337
|
+
size_x=6,
|
|
338
|
+
size_y=4,
|
|
339
|
+
sql=f"SELECT COALESCE(SUM(matriculas), 0) AS matriculas FROM {MATRICULAS_SOURCE}\n{where_clause(MATRICULAS_FILTERS)}",
|
|
340
|
+
display="scalar",
|
|
341
|
+
filters=MATRICULAS_FILTERS,
|
|
342
|
+
visualization_settings={},
|
|
343
|
+
),
|
|
344
|
+
CardDef(
|
|
345
|
+
name="PNP 2024 - KPI Inscritos",
|
|
346
|
+
description="Total de inscritos no recorte.",
|
|
347
|
+
tab="Matriculas",
|
|
348
|
+
row=1,
|
|
349
|
+
col=6,
|
|
350
|
+
size_x=6,
|
|
351
|
+
size_y=4,
|
|
352
|
+
sql=f"SELECT COALESCE(SUM(inscritos), 0) AS inscritos FROM {MATRICULAS_SOURCE}\n{where_clause(MATRICULAS_FILTERS)}",
|
|
353
|
+
display="scalar",
|
|
354
|
+
filters=MATRICULAS_FILTERS,
|
|
355
|
+
visualization_settings={},
|
|
356
|
+
),
|
|
357
|
+
CardDef(
|
|
358
|
+
name="PNP 2024 - KPI Vagas Ofertadas",
|
|
359
|
+
description="Total de vagas ofertadas no recorte.",
|
|
360
|
+
tab="Matriculas",
|
|
361
|
+
row=1,
|
|
362
|
+
col=12,
|
|
363
|
+
size_x=6,
|
|
364
|
+
size_y=4,
|
|
365
|
+
sql=f"SELECT COALESCE(SUM(vagas_ofertadas), 0) AS vagas_ofertadas FROM {MATRICULAS_SOURCE}\n{where_clause(MATRICULAS_FILTERS)}",
|
|
366
|
+
display="scalar",
|
|
367
|
+
filters=MATRICULAS_FILTERS,
|
|
368
|
+
visualization_settings={},
|
|
369
|
+
),
|
|
370
|
+
CardDef(
|
|
371
|
+
name="PNP 2024 - KPI Relacao Inscritos por Vaga",
|
|
372
|
+
description="Razao entre inscritos e vagas ofertadas.",
|
|
373
|
+
tab="Matriculas",
|
|
374
|
+
row=1,
|
|
375
|
+
col=18,
|
|
376
|
+
size_x=6,
|
|
377
|
+
size_y=4,
|
|
378
|
+
sql=f"""SELECT
|
|
379
|
+
ROUND(COALESCE(SUM(inscritos), 0) / NULLIF(COALESCE(SUM(vagas_ofertadas), 0), 0), 2) AS inscritos_por_vaga
|
|
380
|
+
FROM {MATRICULAS_SOURCE}
|
|
381
|
+
{where_clause(MATRICULAS_FILTERS)}""",
|
|
382
|
+
display="scalar",
|
|
383
|
+
filters=MATRICULAS_FILTERS,
|
|
384
|
+
visualization_settings={},
|
|
385
|
+
),
|
|
386
|
+
CardDef(
|
|
387
|
+
name="PNP 2024 - Matriculas por Situacao",
|
|
388
|
+
description="Distribuicao das matriculas por situacao.",
|
|
389
|
+
tab="Matriculas",
|
|
390
|
+
row=5,
|
|
391
|
+
col=0,
|
|
392
|
+
size_x=8,
|
|
393
|
+
size_y=6,
|
|
394
|
+
sql=f"""SELECT situacao_matricula, SUM(matriculas) AS matriculas
|
|
395
|
+
FROM {MATRICULAS_SOURCE}
|
|
396
|
+
{where_clause(MATRICULAS_FILTERS)}
|
|
397
|
+
GROUP BY situacao_matricula
|
|
398
|
+
ORDER BY matriculas DESC NULLS LAST""",
|
|
399
|
+
display="bar",
|
|
400
|
+
filters=MATRICULAS_FILTERS,
|
|
401
|
+
visualization_settings={"graph.dimensions": ["situacao_matricula"], "graph.metrics": ["matriculas"]},
|
|
402
|
+
),
|
|
403
|
+
CardDef(
|
|
404
|
+
name="PNP 2024 - Matriculas por Sexo",
|
|
405
|
+
description="Perfil por sexo.",
|
|
406
|
+
tab="Matriculas",
|
|
407
|
+
row=5,
|
|
408
|
+
col=8,
|
|
409
|
+
size_x=8,
|
|
410
|
+
size_y=6,
|
|
411
|
+
sql=f"""SELECT sexo, SUM(matriculas) AS matriculas
|
|
412
|
+
FROM {MATRICULAS_SOURCE}
|
|
413
|
+
{where_clause(MATRICULAS_FILTERS)}
|
|
414
|
+
GROUP BY sexo
|
|
415
|
+
ORDER BY matriculas DESC NULLS LAST""",
|
|
416
|
+
display="bar",
|
|
417
|
+
filters=MATRICULAS_FILTERS,
|
|
418
|
+
visualization_settings={"graph.dimensions": ["sexo"], "graph.metrics": ["matriculas"]},
|
|
419
|
+
),
|
|
420
|
+
CardDef(
|
|
421
|
+
name="PNP 2024 - Matriculas por Tipo de Curso",
|
|
422
|
+
description="Distribuicao por tipo de curso.",
|
|
423
|
+
tab="Matriculas",
|
|
424
|
+
row=5,
|
|
425
|
+
col=16,
|
|
426
|
+
size_x=8,
|
|
427
|
+
size_y=6,
|
|
428
|
+
sql=f"""SELECT tipo_curso, SUM(matriculas) AS matriculas
|
|
429
|
+
FROM {MATRICULAS_SOURCE}
|
|
430
|
+
{where_clause(MATRICULAS_FILTERS)}
|
|
431
|
+
GROUP BY tipo_curso
|
|
432
|
+
ORDER BY matriculas DESC NULLS LAST""",
|
|
433
|
+
display="bar",
|
|
434
|
+
filters=MATRICULAS_FILTERS,
|
|
435
|
+
visualization_settings={"graph.dimensions": ["tipo_curso"], "graph.metrics": ["matriculas"]},
|
|
436
|
+
),
|
|
437
|
+
CardDef(
|
|
438
|
+
name="PNP 2024 - Oferta por Curso",
|
|
439
|
+
description="Oferta, inscritos e matriculas por curso.",
|
|
440
|
+
tab="Matriculas",
|
|
441
|
+
row=11,
|
|
442
|
+
col=0,
|
|
443
|
+
size_x=24,
|
|
444
|
+
size_y=7,
|
|
445
|
+
sql=f"""SELECT
|
|
446
|
+
nome_curso,
|
|
447
|
+
tipo_curso,
|
|
448
|
+
modalidade_ensino,
|
|
449
|
+
tipo_oferta,
|
|
450
|
+
turno,
|
|
451
|
+
SUM(matriculas) AS matriculas,
|
|
452
|
+
SUM(vagas_ofertadas) AS vagas_ofertadas,
|
|
453
|
+
SUM(inscritos) AS inscritos
|
|
454
|
+
FROM {MATRICULAS_SOURCE}
|
|
455
|
+
{where_clause(MATRICULAS_FILTERS)}
|
|
456
|
+
GROUP BY nome_curso, tipo_curso, modalidade_ensino, tipo_oferta, turno
|
|
457
|
+
ORDER BY matriculas DESC NULLS LAST
|
|
458
|
+
LIMIT 20""",
|
|
459
|
+
display="table",
|
|
460
|
+
filters=MATRICULAS_FILTERS,
|
|
461
|
+
visualization_settings={},
|
|
462
|
+
),
|
|
463
|
+
CardDef(
|
|
464
|
+
name="PNP 2024 - KPI Registros de Eficiencia",
|
|
465
|
+
description="Total de registros de eficiencia academica.",
|
|
466
|
+
tab="Eficiencia Academica",
|
|
467
|
+
row=1,
|
|
468
|
+
col=0,
|
|
469
|
+
size_x=6,
|
|
470
|
+
size_y=4,
|
|
471
|
+
sql=f"SELECT COALESCE(SUM(registros), 0) AS registros_eficiencia FROM {EFICIENCIA_SOURCE}\n{where_clause(EFICIENCIA_FILTERS)}",
|
|
472
|
+
display="scalar",
|
|
473
|
+
filters=EFICIENCIA_FILTERS,
|
|
474
|
+
visualization_settings={},
|
|
475
|
+
),
|
|
476
|
+
CardDef(
|
|
477
|
+
name="PNP 2024 - KPI Concluintes",
|
|
478
|
+
description="Total de concluintes no recorte.",
|
|
479
|
+
tab="Eficiencia Academica",
|
|
480
|
+
row=1,
|
|
481
|
+
col=6,
|
|
482
|
+
size_x=6,
|
|
483
|
+
size_y=4,
|
|
484
|
+
sql=f"""SELECT COALESCE(SUM(registros), 0) AS concluintes
|
|
485
|
+
FROM {EFICIENCIA_SOURCE}
|
|
486
|
+
{where_clause(EFICIENCIA_FILTERS)}
|
|
487
|
+
AND categoria_situacao = 'Concluintes'""",
|
|
488
|
+
display="scalar",
|
|
489
|
+
filters=EFICIENCIA_FILTERS,
|
|
490
|
+
visualization_settings={},
|
|
491
|
+
),
|
|
492
|
+
CardDef(
|
|
493
|
+
name="PNP 2024 - KPI Evadidos",
|
|
494
|
+
description="Total de evadidos no recorte.",
|
|
495
|
+
tab="Eficiencia Academica",
|
|
496
|
+
row=1,
|
|
497
|
+
col=12,
|
|
498
|
+
size_x=6,
|
|
499
|
+
size_y=4,
|
|
500
|
+
sql=f"""SELECT COALESCE(SUM(registros), 0) AS evadidos
|
|
501
|
+
FROM {EFICIENCIA_SOURCE}
|
|
502
|
+
{where_clause(EFICIENCIA_FILTERS)}
|
|
503
|
+
AND categoria_situacao = 'Evadidos'""",
|
|
504
|
+
display="scalar",
|
|
505
|
+
filters=EFICIENCIA_FILTERS,
|
|
506
|
+
visualization_settings={},
|
|
507
|
+
),
|
|
508
|
+
CardDef(
|
|
509
|
+
name="PNP 2024 - KPI Em Curso",
|
|
510
|
+
description="Total de registros em curso.",
|
|
511
|
+
tab="Eficiencia Academica",
|
|
512
|
+
row=1,
|
|
513
|
+
col=18,
|
|
514
|
+
size_x=6,
|
|
515
|
+
size_y=4,
|
|
516
|
+
sql=f"""SELECT COALESCE(SUM(registros), 0) AS em_curso
|
|
517
|
+
FROM {EFICIENCIA_SOURCE}
|
|
518
|
+
{where_clause(EFICIENCIA_FILTERS)}
|
|
519
|
+
AND categoria_situacao = 'Em curso'""",
|
|
520
|
+
display="scalar",
|
|
521
|
+
filters=EFICIENCIA_FILTERS,
|
|
522
|
+
visualization_settings={},
|
|
523
|
+
),
|
|
524
|
+
CardDef(
|
|
525
|
+
name="PNP 2024 - Eficiencia por Categoria",
|
|
526
|
+
description="Distribuicao por categoria.",
|
|
527
|
+
tab="Eficiencia Academica",
|
|
528
|
+
row=5,
|
|
529
|
+
col=0,
|
|
530
|
+
size_x=12,
|
|
531
|
+
size_y=6,
|
|
532
|
+
sql=f"""SELECT categoria_situacao, SUM(registros) AS registros
|
|
533
|
+
FROM {EFICIENCIA_SOURCE}
|
|
534
|
+
{where_clause(EFICIENCIA_FILTERS)}
|
|
535
|
+
GROUP BY categoria_situacao
|
|
536
|
+
ORDER BY registros DESC NULLS LAST""",
|
|
537
|
+
display="bar",
|
|
538
|
+
filters=EFICIENCIA_FILTERS,
|
|
539
|
+
visualization_settings={"graph.dimensions": ["categoria_situacao"], "graph.metrics": ["registros"]},
|
|
540
|
+
),
|
|
541
|
+
CardDef(
|
|
542
|
+
name="PNP 2024 - Eficiencia por UF",
|
|
543
|
+
description="Distribuicao territorial.",
|
|
544
|
+
tab="Eficiencia Academica",
|
|
545
|
+
row=5,
|
|
546
|
+
col=12,
|
|
547
|
+
size_x=12,
|
|
548
|
+
size_y=6,
|
|
549
|
+
sql=f"""SELECT uf, SUM(registros) AS registros
|
|
550
|
+
FROM {EFICIENCIA_SOURCE}
|
|
551
|
+
{where_clause(EFICIENCIA_FILTERS)}
|
|
552
|
+
GROUP BY uf
|
|
553
|
+
ORDER BY registros DESC NULLS LAST""",
|
|
554
|
+
display="bar",
|
|
555
|
+
filters=EFICIENCIA_FILTERS,
|
|
556
|
+
visualization_settings={"graph.dimensions": ["uf"], "graph.metrics": ["registros"]},
|
|
557
|
+
),
|
|
558
|
+
CardDef(
|
|
559
|
+
name="PNP 2024 - Situacao Academica por Instituicao",
|
|
560
|
+
description="Detalhamento por instituicao e categoria.",
|
|
561
|
+
tab="Eficiencia Academica",
|
|
562
|
+
row=11,
|
|
563
|
+
col=0,
|
|
564
|
+
size_x=24,
|
|
565
|
+
size_y=7,
|
|
566
|
+
sql=f"""SELECT instituicao, categoria_situacao, SUM(registros) AS registros
|
|
567
|
+
FROM {EFICIENCIA_SOURCE}
|
|
568
|
+
{where_clause(EFICIENCIA_FILTERS)}
|
|
569
|
+
GROUP BY instituicao, categoria_situacao
|
|
570
|
+
ORDER BY registros DESC NULLS LAST
|
|
571
|
+
LIMIT 25""",
|
|
572
|
+
display="table",
|
|
573
|
+
filters=EFICIENCIA_FILTERS,
|
|
574
|
+
visualization_settings={},
|
|
575
|
+
),
|
|
576
|
+
CardDef(
|
|
577
|
+
name="PNP 2024 - KPI Total de Servidores",
|
|
578
|
+
description="Total de servidores no recorte.",
|
|
579
|
+
tab="Servidores",
|
|
580
|
+
row=1,
|
|
581
|
+
col=0,
|
|
582
|
+
size_x=6,
|
|
583
|
+
size_y=4,
|
|
584
|
+
sql=f"SELECT COALESCE(SUM(servidores), 0) AS servidores FROM {SERVIDORES_SOURCE}\n{where_clause(SERVIDORES_FILTERS)}",
|
|
585
|
+
display="scalar",
|
|
586
|
+
filters=SERVIDORES_FILTERS,
|
|
587
|
+
visualization_settings={},
|
|
588
|
+
),
|
|
589
|
+
CardDef(
|
|
590
|
+
name="PNP 2024 - KPI Instituicoes com Servidores",
|
|
591
|
+
description="Quantidade de instituicoes no recorte.",
|
|
592
|
+
tab="Servidores",
|
|
593
|
+
row=1,
|
|
594
|
+
col=6,
|
|
595
|
+
size_x=6,
|
|
596
|
+
size_y=4,
|
|
597
|
+
sql=f"SELECT COUNT(DISTINCT instituicao) AS instituicoes FROM {SERVIDORES_SOURCE}\n{where_clause(SERVIDORES_FILTERS)}",
|
|
598
|
+
display="scalar",
|
|
599
|
+
filters=SERVIDORES_FILTERS,
|
|
600
|
+
visualization_settings={},
|
|
601
|
+
),
|
|
602
|
+
CardDef(
|
|
603
|
+
name="PNP 2024 - KPI Titulacoes Distintas",
|
|
604
|
+
description="Quantidade de titulacoes distintas.",
|
|
605
|
+
tab="Servidores",
|
|
606
|
+
row=1,
|
|
607
|
+
col=12,
|
|
608
|
+
size_x=6,
|
|
609
|
+
size_y=4,
|
|
610
|
+
sql=f"SELECT COUNT(DISTINCT titulacao) AS titulacoes_distintas FROM {SERVIDORES_SOURCE}\n{where_clause(SERVIDORES_FILTERS)}",
|
|
611
|
+
display="scalar",
|
|
612
|
+
filters=SERVIDORES_FILTERS,
|
|
613
|
+
visualization_settings={},
|
|
614
|
+
),
|
|
615
|
+
CardDef(
|
|
616
|
+
name="PNP 2024 - KPI Vinculos Distintos",
|
|
617
|
+
description="Quantidade de vinculos distintos.",
|
|
618
|
+
tab="Servidores",
|
|
619
|
+
row=1,
|
|
620
|
+
col=18,
|
|
621
|
+
size_x=6,
|
|
622
|
+
size_y=4,
|
|
623
|
+
sql=f"SELECT COUNT(DISTINCT vinculo_carreira) AS vinculos_distintos FROM {SERVIDORES_SOURCE}\n{where_clause(SERVIDORES_FILTERS)}",
|
|
624
|
+
display="scalar",
|
|
625
|
+
filters=SERVIDORES_FILTERS,
|
|
626
|
+
visualization_settings={},
|
|
627
|
+
),
|
|
628
|
+
CardDef(
|
|
629
|
+
name="PNP 2024 - Servidores por Titulacao",
|
|
630
|
+
description="Distribuicao por titulacao.",
|
|
631
|
+
tab="Servidores",
|
|
632
|
+
row=5,
|
|
633
|
+
col=0,
|
|
634
|
+
size_x=8,
|
|
635
|
+
size_y=6,
|
|
636
|
+
sql=f"""SELECT titulacao, SUM(servidores) AS servidores
|
|
637
|
+
FROM {SERVIDORES_SOURCE}
|
|
638
|
+
{where_clause(SERVIDORES_FILTERS)}
|
|
639
|
+
GROUP BY titulacao
|
|
640
|
+
ORDER BY servidores DESC NULLS LAST""",
|
|
641
|
+
display="bar",
|
|
642
|
+
filters=SERVIDORES_FILTERS,
|
|
643
|
+
visualization_settings={"graph.dimensions": ["titulacao"], "graph.metrics": ["servidores"]},
|
|
644
|
+
),
|
|
645
|
+
CardDef(
|
|
646
|
+
name="PNP 2024 - Servidores por Jornada",
|
|
647
|
+
description="Distribuicao por jornada.",
|
|
648
|
+
tab="Servidores",
|
|
649
|
+
row=5,
|
|
650
|
+
col=8,
|
|
651
|
+
size_x=8,
|
|
652
|
+
size_y=6,
|
|
653
|
+
sql=f"""SELECT jornada_trabalho, SUM(servidores) AS servidores
|
|
654
|
+
FROM {SERVIDORES_SOURCE}
|
|
655
|
+
{where_clause(SERVIDORES_FILTERS)}
|
|
656
|
+
GROUP BY jornada_trabalho
|
|
657
|
+
ORDER BY servidores DESC NULLS LAST""",
|
|
658
|
+
display="bar",
|
|
659
|
+
filters=SERVIDORES_FILTERS,
|
|
660
|
+
visualization_settings={"graph.dimensions": ["jornada_trabalho"], "graph.metrics": ["servidores"]},
|
|
661
|
+
),
|
|
662
|
+
CardDef(
|
|
663
|
+
name="PNP 2024 - Servidores por Vinculo",
|
|
664
|
+
description="Distribuicao por vinculo de carreira.",
|
|
665
|
+
tab="Servidores",
|
|
666
|
+
row=5,
|
|
667
|
+
col=16,
|
|
668
|
+
size_x=8,
|
|
669
|
+
size_y=6,
|
|
670
|
+
sql=f"""SELECT vinculo_carreira, SUM(servidores) AS servidores
|
|
671
|
+
FROM {SERVIDORES_SOURCE}
|
|
672
|
+
{where_clause(SERVIDORES_FILTERS)}
|
|
673
|
+
GROUP BY vinculo_carreira
|
|
674
|
+
ORDER BY servidores DESC NULLS LAST""",
|
|
675
|
+
display="bar",
|
|
676
|
+
filters=SERVIDORES_FILTERS,
|
|
677
|
+
visualization_settings={"graph.dimensions": ["vinculo_carreira"], "graph.metrics": ["servidores"]},
|
|
678
|
+
),
|
|
679
|
+
CardDef(
|
|
680
|
+
name="PNP 2024 - Servidores por Instituicao",
|
|
681
|
+
description="Ranking institucional.",
|
|
682
|
+
tab="Servidores",
|
|
683
|
+
row=11,
|
|
684
|
+
col=0,
|
|
685
|
+
size_x=24,
|
|
686
|
+
size_y=7,
|
|
687
|
+
sql=f"""SELECT instituicao, SUM(servidores) AS servidores
|
|
688
|
+
FROM {SERVIDORES_SOURCE}
|
|
689
|
+
{where_clause(SERVIDORES_FILTERS)}
|
|
690
|
+
GROUP BY instituicao
|
|
691
|
+
ORDER BY servidores DESC NULLS LAST
|
|
692
|
+
LIMIT 20""",
|
|
693
|
+
display="table",
|
|
694
|
+
filters=SERVIDORES_FILTERS,
|
|
695
|
+
visualization_settings={},
|
|
696
|
+
),
|
|
697
|
+
CardDef(
|
|
698
|
+
name="PNP 2024 - KPI Liquidacoes Totais",
|
|
699
|
+
description="Total liquidado no recorte.",
|
|
700
|
+
tab="Financeiro",
|
|
701
|
+
row=1,
|
|
702
|
+
col=0,
|
|
703
|
+
size_x=6,
|
|
704
|
+
size_y=4,
|
|
705
|
+
sql=f"SELECT COALESCE(SUM(liquidacoes_totais), 0) AS liquidacoes_totais FROM {FINANCEIRO_SOURCE}\n{where_clause(FINANCEIRO_FILTERS)}",
|
|
706
|
+
display="scalar",
|
|
707
|
+
filters=FINANCEIRO_FILTERS,
|
|
708
|
+
visualization_settings={},
|
|
709
|
+
),
|
|
710
|
+
CardDef(
|
|
711
|
+
name="PNP 2024 - KPI UOs Distintas",
|
|
712
|
+
description="Quantidade de UOs distintas.",
|
|
713
|
+
tab="Financeiro",
|
|
714
|
+
row=1,
|
|
715
|
+
col=6,
|
|
716
|
+
size_x=6,
|
|
717
|
+
size_y=4,
|
|
718
|
+
sql=f"SELECT COUNT(DISTINCT nome_uo) AS uos_distintas FROM {FINANCEIRO_SOURCE}\n{where_clause(FINANCEIRO_FILTERS)}",
|
|
719
|
+
display="scalar",
|
|
720
|
+
filters=FINANCEIRO_FILTERS,
|
|
721
|
+
visualization_settings={},
|
|
722
|
+
),
|
|
723
|
+
CardDef(
|
|
724
|
+
name="PNP 2024 - KPI Acoes Distintas",
|
|
725
|
+
description="Quantidade de acoes distintas.",
|
|
726
|
+
tab="Financeiro",
|
|
727
|
+
row=1,
|
|
728
|
+
col=12,
|
|
729
|
+
size_x=6,
|
|
730
|
+
size_y=4,
|
|
731
|
+
sql=f"SELECT COUNT(DISTINCT cod_acao) AS acoes_distintas FROM {FINANCEIRO_SOURCE}\n{where_clause(FINANCEIRO_FILTERS)}",
|
|
732
|
+
display="scalar",
|
|
733
|
+
filters=FINANCEIRO_FILTERS,
|
|
734
|
+
visualization_settings={},
|
|
735
|
+
),
|
|
736
|
+
CardDef(
|
|
737
|
+
name="PNP 2024 - KPI Grupos de Despesa Distintos",
|
|
738
|
+
description="Quantidade de grupos de despesa distintos.",
|
|
739
|
+
tab="Financeiro",
|
|
740
|
+
row=1,
|
|
741
|
+
col=18,
|
|
742
|
+
size_x=6,
|
|
743
|
+
size_y=4,
|
|
744
|
+
sql=f"SELECT COUNT(DISTINCT grupo_despesa) AS grupos_despesa_distintos FROM {FINANCEIRO_SOURCE}\n{where_clause(FINANCEIRO_FILTERS)}",
|
|
745
|
+
display="scalar",
|
|
746
|
+
filters=FINANCEIRO_FILTERS,
|
|
747
|
+
visualization_settings={},
|
|
748
|
+
),
|
|
749
|
+
CardDef(
|
|
750
|
+
name="PNP 2024 - Liquidacoes por Grupo de Despesa",
|
|
751
|
+
description="Distribuicao por grupo de despesa.",
|
|
752
|
+
tab="Financeiro",
|
|
753
|
+
row=5,
|
|
754
|
+
col=0,
|
|
755
|
+
size_x=10,
|
|
756
|
+
size_y=6,
|
|
757
|
+
sql=f"""SELECT grupo_despesa, SUM(liquidacoes_totais) AS liquidacoes_totais
|
|
758
|
+
FROM {FINANCEIRO_SOURCE}
|
|
759
|
+
{where_clause(FINANCEIRO_FILTERS)}
|
|
760
|
+
GROUP BY grupo_despesa
|
|
761
|
+
ORDER BY liquidacoes_totais DESC NULLS LAST""",
|
|
762
|
+
display="bar",
|
|
763
|
+
filters=FINANCEIRO_FILTERS,
|
|
764
|
+
visualization_settings={"graph.dimensions": ["grupo_despesa"], "graph.metrics": ["liquidacoes_totais"]},
|
|
765
|
+
),
|
|
766
|
+
CardDef(
|
|
767
|
+
name="PNP 2024 - Top UOs por Liquidacoes",
|
|
768
|
+
description="Ranking de UOs por liquidacoes.",
|
|
769
|
+
tab="Financeiro",
|
|
770
|
+
row=5,
|
|
771
|
+
col=10,
|
|
772
|
+
size_x=14,
|
|
773
|
+
size_y=6,
|
|
774
|
+
sql=f"""SELECT nome_uo, SUM(liquidacoes_totais) AS liquidacoes_totais
|
|
775
|
+
FROM {FINANCEIRO_SOURCE}
|
|
776
|
+
{where_clause(FINANCEIRO_FILTERS)}
|
|
777
|
+
GROUP BY nome_uo
|
|
778
|
+
ORDER BY liquidacoes_totais DESC NULLS LAST
|
|
779
|
+
LIMIT 15""",
|
|
780
|
+
display="bar",
|
|
781
|
+
filters=FINANCEIRO_FILTERS,
|
|
782
|
+
visualization_settings={"graph.dimensions": ["nome_uo"], "graph.metrics": ["liquidacoes_totais"]},
|
|
783
|
+
),
|
|
784
|
+
CardDef(
|
|
785
|
+
name="PNP 2024 - Execucao por Acao",
|
|
786
|
+
description="Detalhamento financeiro por acao.",
|
|
787
|
+
tab="Financeiro",
|
|
788
|
+
row=11,
|
|
789
|
+
col=0,
|
|
790
|
+
size_x=24,
|
|
791
|
+
size_y=7,
|
|
792
|
+
sql=f"""SELECT
|
|
793
|
+
nome_uo,
|
|
794
|
+
cod_acao,
|
|
795
|
+
nome_acao,
|
|
796
|
+
grupo_despesa,
|
|
797
|
+
SUM(liquidacoes_totais) AS liquidacoes_totais
|
|
798
|
+
FROM {FINANCEIRO_SOURCE}
|
|
799
|
+
{where_clause(FINANCEIRO_FILTERS)}
|
|
800
|
+
GROUP BY nome_uo, cod_acao, nome_acao, grupo_despesa
|
|
801
|
+
ORDER BY liquidacoes_totais DESC NULLS LAST
|
|
802
|
+
LIMIT 25""",
|
|
803
|
+
display="table",
|
|
804
|
+
filters=FINANCEIRO_FILTERS,
|
|
805
|
+
visualization_settings={},
|
|
806
|
+
),
|
|
807
|
+
CardDef(
|
|
808
|
+
name="PNP 2024 - Qualidade por Tipo de Microdado",
|
|
809
|
+
description="Cobertura basica dos microdados.",
|
|
810
|
+
tab="Qualidade e Ingestao",
|
|
811
|
+
row=1,
|
|
812
|
+
col=0,
|
|
813
|
+
size_x=14,
|
|
814
|
+
size_y=8,
|
|
815
|
+
sql=f"""SELECT
|
|
816
|
+
tipo_microdados,
|
|
817
|
+
registros,
|
|
818
|
+
registros_sem_instituicao,
|
|
819
|
+
registros_sem_uf,
|
|
820
|
+
registros_sem_sexo,
|
|
821
|
+
registros_sem_cor_raca,
|
|
822
|
+
registros_sem_renda_familiar,
|
|
823
|
+
registros_sem_faixa_etaria,
|
|
824
|
+
registros_financeiros_sem_valor,
|
|
825
|
+
registros_servidores_sem_quantidade,
|
|
826
|
+
pct_sem_instituicao,
|
|
827
|
+
pct_sem_uf
|
|
828
|
+
FROM {QUALIDADE_SOURCE}
|
|
829
|
+
ORDER BY tipo_microdados""",
|
|
830
|
+
display="table",
|
|
831
|
+
filters=(),
|
|
832
|
+
visualization_settings={},
|
|
833
|
+
),
|
|
834
|
+
CardDef(
|
|
835
|
+
name="PNP 2024 - Execucoes de Ingestao",
|
|
836
|
+
description="Trilha operacional das execucoes da carga.",
|
|
837
|
+
tab="Qualidade e Ingestao",
|
|
838
|
+
row=1,
|
|
839
|
+
col=14,
|
|
840
|
+
size_x=10,
|
|
841
|
+
size_y=8,
|
|
842
|
+
sql=f"""SELECT
|
|
843
|
+
run_id,
|
|
844
|
+
status,
|
|
845
|
+
endpoint_key,
|
|
846
|
+
loaded_count,
|
|
847
|
+
registros_raw,
|
|
848
|
+
downloads_total,
|
|
849
|
+
manifests_total,
|
|
850
|
+
started_at,
|
|
851
|
+
finished_at
|
|
852
|
+
FROM {INGESTAO_SOURCE}
|
|
853
|
+
ORDER BY started_at DESC NULLS LAST
|
|
854
|
+
LIMIT 20""",
|
|
855
|
+
display="table",
|
|
856
|
+
filters=(),
|
|
857
|
+
visualization_settings={},
|
|
858
|
+
),
|
|
859
|
+
]
|
|
860
|
+
|
|
861
|
+
|
|
862
|
+
def build_virtual_dashcard(card: VirtualCardDef, tab_id: int, dashcard_id: int) -> dict:
|
|
863
|
+
return {
|
|
864
|
+
"id": dashcard_id,
|
|
865
|
+
"card_id": None,
|
|
866
|
+
"dashboard_tab_id": tab_id,
|
|
867
|
+
"row": card.row,
|
|
868
|
+
"col": card.col,
|
|
869
|
+
"size_x": card.size_x,
|
|
870
|
+
"size_y": card.size_y,
|
|
871
|
+
"parameter_mappings": [],
|
|
872
|
+
"visualization_settings": {
|
|
873
|
+
"dashcard.background": False,
|
|
874
|
+
"text": card.text,
|
|
875
|
+
"virtual_card": {
|
|
876
|
+
"archived": False,
|
|
877
|
+
"dataset_query": {},
|
|
878
|
+
"display": "text",
|
|
879
|
+
"visualization_settings": {},
|
|
880
|
+
},
|
|
881
|
+
},
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
|
|
885
|
+
def build_real_dashcard(card: CardDef, tab_id: int, dashcard_id: int, card_id: int) -> dict:
|
|
886
|
+
return {
|
|
887
|
+
"id": dashcard_id,
|
|
888
|
+
"card_id": card_id,
|
|
889
|
+
"dashboard_tab_id": tab_id,
|
|
890
|
+
"row": card.row,
|
|
891
|
+
"col": card.col,
|
|
892
|
+
"size_x": card.size_x,
|
|
893
|
+
"size_y": card.size_y,
|
|
894
|
+
"parameter_mappings": card.parameter_mappings(),
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
|
|
898
|
+
def main() -> int:
|
|
899
|
+
cleanup_previous_pnp_cards()
|
|
900
|
+
dashboard_id = ensure_dashboard()
|
|
901
|
+
|
|
902
|
+
next_tab_id = -1
|
|
903
|
+
tabs_payload = []
|
|
904
|
+
tab_ids: dict[str, int] = {}
|
|
905
|
+
for position, tab_name in enumerate(TAB_LAYOUT):
|
|
906
|
+
tab_ids[tab_name] = next_tab_id
|
|
907
|
+
tabs_payload.append({"id": next_tab_id, "name": tab_name, "position": position})
|
|
908
|
+
next_tab_id -= 1
|
|
909
|
+
|
|
910
|
+
next_dashcard_id = -1
|
|
911
|
+
dashcards = []
|
|
912
|
+
|
|
913
|
+
for virtual in VIRTUALS:
|
|
914
|
+
dashcards.append(build_virtual_dashcard(virtual, tab_ids[virtual.tab], next_dashcard_id))
|
|
915
|
+
next_dashcard_id -= 1
|
|
916
|
+
|
|
917
|
+
created_cards = []
|
|
918
|
+
for card in CARDS:
|
|
919
|
+
card_id = create_card(card)
|
|
920
|
+
created_cards.append(card_id)
|
|
921
|
+
dashcards.append(build_real_dashcard(card, tab_ids[card.tab], next_dashcard_id, card_id))
|
|
922
|
+
next_dashcard_id -= 1
|
|
923
|
+
|
|
924
|
+
update_dashboard(dashboard_id, tabs_payload, dashcards)
|
|
925
|
+
dashboard = api("GET", f"/dashboard/{dashboard_id}")
|
|
926
|
+
|
|
927
|
+
print(
|
|
928
|
+
json.dumps(
|
|
929
|
+
{
|
|
930
|
+
"dashboard_id": dashboard_id,
|
|
931
|
+
"tabs_total": len(dashboard.get("tabs", [])),
|
|
932
|
+
"dashcards_total": len(dashboard.get("dashcards", [])),
|
|
933
|
+
"cards_created": len(created_cards),
|
|
934
|
+
"filters_total": len(dashboard.get("parameters", [])),
|
|
935
|
+
},
|
|
936
|
+
ensure_ascii=True,
|
|
937
|
+
)
|
|
938
|
+
)
|
|
939
|
+
return 0
|
|
940
|
+
|
|
941
|
+
|
|
942
|
+
if __name__ == "__main__":
|
|
943
|
+
sys.exit(main())
|