SPS V4 · Data contract
A read-only Postgres view schema, pbi_v1, on the SPS V4 production
database. Connect over JDBC as pbi_reader, read the seven pbi_v1.* views,
write to gold. Everything you need to build against is on this page.
JDBC URL
jdbc:postgresql://sps-v4-prod-db.postgres.database.azure.com:5432/sps_v4?sslmode=require
Databricks (the Postgres driver is bundled — no extra install)
url = "jdbc:postgresql://sps-v4-prod-db.postgres.database.azure.com:5432/sps_v4?sslmode=require"
props = {
"user": "pbi_reader",
"password": dbutils.secrets.get(scope="...", key="pbi_reader_pw"),
"driver": "org.postgresql.Driver",
}
df = spark.read.jdbc(url=url, table="pbi_v1.v_projects", properties=props)
read.jdbc connects from every cluster node — the
driver and each executor — not a single address. Workspace adb-8019543586872068
currently egresses via Azure default-outbound, so each node gets its own ephemeral public IP;
allow-listing them one by one can't hold across scale/recycle. Attach a NAT gateway to the
workspace subnets so all nodes share one stable egress IP, then send that IP to Vilius to
allow-list. Until that's in place the connection times out on the executors even though the driver
reaches the DB. The NAT gateway is your side; opening the firewall to that one IP is Vilius's.
pbi_v1.*. These views are a stable façade.
Internal column renames on the app's real tables are absorbed here — they will not break your pipeline.pbi_v1 — that would ship as a new pbi_v2 schema
so your existing reads keep working.v_projects.updated_at and
v_project_status_weekly.submitted_at are real row-level timestamps. Everything else exposes
only its natural grain (month, year/week_number) as a coarse
watermark. Do not assume row-level change deltas. If you need true incremental loading, that's a
pbi_v2 request (see §07).pbi_reader by design.Seven views. project_id is text and joins across all of them. Types are the
Postgres types you'll receive.
pbi_v1.v_projectsOne row per non-archived project. Master fields as seen on the STATUSAS / PROJEKTAI tabs. grain: project_id
| Column | Type | Meaning |
|---|---|---|
| project_id | text | Primary key. Joins to every other view. |
| code | text | Short project code. |
| name | text | Display name. |
| full_name | text | Long / legal name (nullable). |
| type | text | Single-letter type code (nullable). |
| stage | text | Stage code (nullable). Labels live in reference.project_stages, see §06. |
| country | text | 2-letter country code (nullable). |
| start_date | date | Project start. |
| agreed_end_date | date | Agreed end date. |
| contract_end_date | date | Contractual end date. |
| financial_close_date | date | Financial close (nullable). |
| contract_revenue | numeric(14,2) | Contract revenue, EUR. |
| contract_costs | numeric(14,2) | Contract costs, EUR. |
| contract_hours | numeric(10,2) | Contracted hours. |
| total_volume_m2 | numeric(12,3) | Total work volume, m² (nullable — set on few projects). |
| materials_not_needed | boolean | Project has no materials scope. |
| pv_name | text | Project manager(s), comma-joined, name only. Multiple possible. |
| av_name | text | Deputy(ies), comma-joined, name only. |
| ps_name | text | Site manager(s), comma-joined, name only. |
| updated_at | timestamptz | Real row watermark. Last change to the project row. |
pbi_v1.v_project_status_weeklyOne row per project per ISO week — the full weekly STATUSAS history. grain: project_id, year, week_number
| Column | Type | Meaning |
|---|---|---|
| project_id | text | FK → v_projects. |
| year | int | ISO year. |
| week_number | int | ISO week, 1–53. |
| iso_week_key | text | Convenience join key, format YYYY-Www (e.g. 2026-W28). ISO year+week. |
| expected_end_date | date | Expected end as of this week (nullable). |
| workers_hotrema | int | Hotrema headcount this week (nullable). |
| workers_other | int | Sub-contractor headcount (nullable). |
| sukaupimai | numeric(14,2) | Accruals. EUR ⚑ |
| atsargos_viso | numeric(14,2) | Inventory, total. EUR ⚑ |
| atsargos_30d | numeric(14,2) | Inventory, ≤30 days. EUR ⚑ |
| atsargos_90d | numeric(14,2) | Inventory, ≤90 days. EUR ⚑ |
| buffer_consumed_pct | numeric(8,4) | Buffer consumed, percent value — can exceed 100. ⚑ scale |
| work_done_pct | numeric(8,4) | Work completed, percent value. ⚑ scale |
| hours_done_cum | numeric(10,2) | Cumulative hours worked to date. |
| submitted_at | timestamptz | Real row watermark. When this week was submitted (nullable if never submitted). |
pbi_v1.v_latest_project_status_weeklyThe single most-recent weekly row per project. Same columns as above, plus one. grain: project_id
| Column | Type | Meaning |
|---|---|---|
| …all v_project_status_weekly columns… | — | Identical shape, latest (year, week_number) per project. |
| is_current_week | boolean | true when that latest row is the ISO current week relative to when you query. Computed at read time, not stored. |
pbi_v1.v_project_financialsOne row per project per month. grain: project_id, month
| Column | Type | Meaning |
|---|---|---|
| project_id | text | FK → v_projects. |
| month | date | Month-end date (canonical last-day-of-month). Use as your month grain. |
| rev_primary | numeric(14,2) | Primary revenue, EUR. |
| rev_additional | numeric(14,2) | Additional revenue, EUR. |
| rev_reklamacijos | numeric(14,2) | Reclamation/warranty revenue, EUR. |
| costs_primary | numeric(14,2) | Primary costs, EUR. |
| costs_additional | numeric(14,2) | Additional costs, EUR. |
| hours_primary | numeric(10,2) | Primary hours. |
| hours_additional | numeric(10,2) | Additional hours. |
| actual_workers | int | Actual headcount for the month (nullable). |
| planned_workers | numeric(10,2) | Planned headcount (fractional allowed, nullable). |
pbi_v1.v_project_materials_monthlyOne row per project per material per month. grain: project_id, material_name, month
| Column | Type | Meaning |
|---|---|---|
| project_id | text | FK → v_projects. |
| material_type | text | Category label. Admin-definable — do not hardcode; new/renamed values appear over time. |
| material_name | text | Catalog material name. |
| unit | text | Unit of qty (e.g. m², m). Key any KPI roll-ups off this, not off material_type. |
| month | date | Month-end date. |
| qty | numeric(12,3) | Quantity for the month, in unit. |
pbi_v1.v_project_work_volumesOne row per project per ISO week. grain: project_id, year, week_number
| Column | Type | Meaning |
|---|---|---|
| project_id | text | FK → v_projects. |
| year | int | ISO year. |
| week_number | int | ISO week, 1–53. |
| iso_week_key | text | YYYY-Www. |
| planned | numeric(12,3) | Planned volume. m² ⚑ |
| actual | numeric(12,3) | Actual volume. m² ⚑ |
pbi_v1.v_project_critical_activitiesOne row per active critical activity per project (current state, archived rows excluded). grain: project_id, sequence
| Column | Type | Meaning |
|---|---|---|
| project_id | text | FK → v_projects. |
| sequence | int | Ordering within the project. |
| task | text | Activity description. |
| basis | text | Basis / reference (nullable). |
| deadline | date | Target date (nullable). |
| done | text | ISO completion date as text when done, else null. Not a boolean. |
| is_done | boolean | Convenience flag = done IS NOT NULL. |
| owner_name | text | Owner display name, name only (nullable). |
Latest weekly status across all active projects — mirrors the STATUSAS browse screen.
SELECT p.code, p.name, p.stage, p.country, p.pv_name,
sw.expected_end_date, sw.workers_hotrema, sw.workers_other,
sw.sukaupimai, sw.atsargos_viso, sw.atsargos_30d, sw.atsargos_90d,
sw.year, sw.week_number, sw.is_current_week
FROM pbi_v1.v_projects p
JOIN pbi_v1.v_latest_project_status_weekly sw ON sw.project_id = p.project_id
ORDER BY p.code;
year/week_number and
iso_week_key are ISO — at year boundaries an early-January date can belong to week 52/53 of the
previous ISO year. Join on iso_week_key or on the (year, week_number) pair, never on a
calendar-derived week.month is month-end. The date is the last day of the month, by
design, so roll-ups stay canonical. Don't assume first-of-month.buffer_consumed_pct routinely does.
Treat them as raw percent values, not clamped ratios.null, not 0 —
coalesce deliberately where a 0 is meant.material_type is admin-managed and grows
over time; key measures off unit. (The old VATA / SKARDA / KEVALAI trio is gone.)pv_name can read "Jonas J., Petras P.". Split if you need them individually.B2s shared
with the live app. A nightly full pull won't disturb it; a wide concurrent read during business hours could.pbi_v1, so no stability promise, but stable in practice):
reference.project_stages, reference.deviation_reasons, reference.eu_holidays,
public.material_types, public.materials_catalog.pbi_v1 view
without breaking existing reads.pbi_v2
with added timestamp columns — raise it and it gets scoped separately.pbi_v1.*, treat columns as
add-only, and anything you need beyond the seven views is a short conversation with Vilius — not a workaround
against the base tables (they're not reachable anyway).