1176 lines
75 KiB
Python
1176 lines
75 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
build_hospital_graph.py
|
||
|
||
Builds a Neo4j graph linking hospitals in Podlaskie voivodeship,
|
||
their directors, supervisory-board members, political parties,
|
||
and regional councilors.
|
||
|
||
Usage:
|
||
python3 build_hospital_graph.py
|
||
|
||
Neo4j connection (env vars):
|
||
NEO4J_URI (default: bolt://localhost:7687)
|
||
NEO4J_USER (default: neo4j)
|
||
NEO4J_PASS (default: graphdb123)
|
||
"""
|
||
|
||
import json
|
||
import os
|
||
import re
|
||
import textwrap
|
||
import unicodedata
|
||
import uuid
|
||
from dataclasses import dataclass, field, asdict
|
||
from datetime import datetime
|
||
from typing import Optional
|
||
|
||
from neo4j import GraphDatabase
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# Data model
|
||
# ---------------------------------------------------------------------------
|
||
|
||
@dataclass
|
||
class Party:
|
||
slug: str # normalized key, e.g. "pis", "koalicja-obywatelska"
|
||
name: str # display name, e.g. "Prawo i Sprawiedliwość"
|
||
|
||
|
||
@dataclass
|
||
class Person:
|
||
uid: str
|
||
name: str
|
||
party_note: str = "" # context string (e.g. "brak/niepotwierdzona", or compound)
|
||
party_slugs: list = field(default_factory=list) # list of Party slugs
|
||
source_urls: list = field(default_factory=list)
|
||
note: str = ""
|
||
|
||
@property
|
||
def norm_name(self):
|
||
return norm_name(self.name)
|
||
|
||
|
||
@dataclass
|
||
class Hospital:
|
||
id: str
|
||
name: str
|
||
city: str
|
||
legal_form: str = ""
|
||
website: str = ""
|
||
notes: str = ""
|
||
|
||
|
||
@dataclass
|
||
class Role:
|
||
hospital_id: str
|
||
person: Person
|
||
role: str # "DYREKTOR", "Z_CA", "RADA_SPOLECZNA", "RADNIA", "PREZES_ZARZADU", "RADA_NADZORCZA"
|
||
sub_role: str = "" # e.g. "Przewodniczacy", "Z-ca ds. Lecznictwa"
|
||
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# Helpers
|
||
# ---------------------------------------------------------------------------
|
||
|
||
def norm_name(s: str) -> str:
|
||
"""Normalise a person name for deduplication."""
|
||
s = unicodedata.normalize("NFKD", s)
|
||
s = re.sub(r"[^\w\s\-\.]+", "", s, flags=re.UNICODE)
|
||
s = re.sub(r"\s+", " ", s).strip().lower()
|
||
# Remove academic titles
|
||
s = re.sub(
|
||
r"\b(prof\.\s*dr\s*hab\s*dr\s*inż?\s*inż\.\s*mgr\s*dr\s*inż?\s*)"
|
||
r"|(\binż?\.\s*dr\s*hab\s*hab\.\s*dr\s*hab)\b",
|
||
"", s, flags=re.IGNORECASE
|
||
)
|
||
return s
|
||
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# Party normalisation
|
||
# ---------------------------------------------------------------------------
|
||
|
||
_PARTY_CANONICAL = {
|
||
# Major parties
|
||
"pis": ("Prawo i Sprawiedliwość", "pis"),
|
||
"pi-sprawiedliwosc": ("Prawo i Sprawiedliwość", "pis"),
|
||
"prawo-i-sprawiedliwosc": ("Prawo i Sprawiedliwość", "pis"),
|
||
"koalicja-obywatelska": ("Koalicja Obywatelska", "ko"),
|
||
"ko": ("Koalicja Obywatelska", "ko"),
|
||
"po": ("Platforma Obywatelska", "ko"),
|
||
"ko-po": ("Koalicja Obywatelska", "ko"),
|
||
"po-ko": ("Koalicja Obywatelska", "ko"),
|
||
"psl": ("Polskie Stronnictwo Ludowe", "psl"),
|
||
"polskie-ustronnictwo-ludowe": ("Polskie Stronnictwo Ludowe", "psl"),
|
||
"polska-2050": ("Polska 2050", "polska-2050"),
|
||
"trzecia-droga": ("Trzecia Droga", "trzecia-droga"),
|
||
"slp": ("SLP", "slp"),
|
||
"sld": ("Sojusz Lewicy Demokratycznej", "sld"),
|
||
"lewica": ("Lewica", "lewica"),
|
||
"prawica-rzeczypospolitej": ("Prawica Rzeczypospolitej", "prawica-rzeczpospolita"),
|
||
"prawa-rzeczypospolitej": ("Prawica Rzeczypospolitej", "prawica-rzeczpospolita"),
|
||
|
||
# KWW / local committees - keep them as-is but normalized
|
||
"kww-ziemia-hajnowska": ("KWW Ziemia Hajnowska", "kww-ziemia-hajnowska"),
|
||
"kww-razem-dla-sokolki": ("KWW Razem dla Sokółki", "kww-razem-dla-sokolki"),
|
||
"kww-nasze-podlasie": ("KWW Nasze Podlasie", "kww-nasze-podlasie"),
|
||
"kww-przyjazna-lomza": ("KWW Przyjazna Łomża", "kww-przyjazna-lomza"),
|
||
"kww-samorzad-powiatu-augustowskiego": ("KWW Samorząd Powiatu Augustowskiego", "kww-spa"),
|
||
"kww-spa": ("KWW Samorząd Powiatu Augustowskiego", "kww-spa"),
|
||
"kww-bau-2024": ("KWW BAU 2024", "kww-bau-2024"),
|
||
"kww-suwalskie-porozumienie-prawicy": ("KWW Suwalskie Porozumienie Prawicy", "kww-sup"),
|
||
"kww-hajnowa-razem": ("KWW Hajnówka Razem", "kww-hajnowa-razem"),
|
||
"kww-nasza-przyszlosc": ("KWW Nasza Przyszłość", "kww-nasza-przyszlosc"),
|
||
"kww-roztrotna-troska-o-dobro-gminy": ("KWW Roztrotna Troska o Dobro Gminy", "kww-rt"),
|
||
"kww-narew-odnowa": ("KWW Narew Odnowa", "kww-narew-odnowa"),
|
||
"kww-wspolna-narewka": ("KWW Wspólna Narewka", "kww-wspolna-narewka"),
|
||
"kww-spw": ("KWW SPW", "kww-spw"),
|
||
"kww-wlasny": ("KWW Własny", "kww-wlasny"),
|
||
"kww-bmn-aktywny-samorzad": ("KWW BMN Aktywny Samorząd", "kww-bmn"),
|
||
"kww-dla-siemiatycz-razem": ("KWW Dla Siemiatycz Razem", "kww-dla-siemiatycz-razem"),
|
||
"kww-macieja-pawla-bednarko": ("KWW Macieja Pawła Bednarko", "kww-mpb"),
|
||
"kww-dobry-powiat": ("KWW Dobry Powiat", "kww-dobry-powiat"),
|
||
"kww-skuteczny-i-przyjazny-samorzad": ("KWW Skuteczny i Przyjazny Samorząd", "kww-sps"),
|
||
"kww-ziemia-sejneńska": ("KWW Ziemia Sejneńska", "kww-sejny"),
|
||
"kww-razem-dla-suwalk": ("KWW Razem dla Suwałk", "kww-razem-dla-suwalk"),
|
||
"kww-porozumienie-samorzadowe-powiatu-kolno": ("KWW Porozumienie Samorządowe Powiatu Kolno", "kww-pspk"),
|
||
"kww-pspk": ("KWW Porozumienie Samorządowe Powiatu Kolno", "kww-pspk"),
|
||
"kw-koalicja-bielska": ("KW Koalicja Bielska", "kw-koalicja-bielska"),
|
||
}
|
||
|
||
# Second-level mapping for names that didn't match the first pass
|
||
_PARTY_SECONDARY = {
|
||
"historycznie sld/lewica": ("historycznie SLD/Lewica", "sld"),
|
||
"możliwie trzecia droga": ("Trzecia Droga", "trzecia-droga"),
|
||
"wicemarszałek woj.; koalicja obywatelska; niezrzeszony": ("Koalicja Obywatelska", "ko"),
|
||
}
|
||
|
||
|
||
def normalise_party(raw: str) -> tuple[str, str]:
|
||
"""
|
||
Given a raw party string, return (canonical_name, slug).
|
||
If no match, normalise the input itself.
|
||
"""
|
||
key = re.sub(r"[^\w\s\-\/\.]+", "", raw).strip().lower()
|
||
# Replace Polish characters
|
||
key = unicodedata.normalize("NFD", key)
|
||
key = re.sub(r"[^\w\s\-]+", "", key)
|
||
|
||
# Check if it's a KWW/KW local committee
|
||
if "kww" in key or "kw " in key:
|
||
display = re.sub(r"\s+", " ", raw.strip().title())
|
||
slug = re.sub(r"[^\w\-]+", "-", display.lower())
|
||
slug = re.sub(r"-+", "-", slug).strip("-")
|
||
return display, slug
|
||
|
||
if key in _PARTY_CANONICAL:
|
||
return _PARTY_CANONICAL[key]
|
||
|
||
# Check secondary (full-string) matches
|
||
if key in _PARTY_SECONDARY:
|
||
return _PARTY_SECONDARY[key]
|
||
|
||
# Check for compound strings like "Trzecia Droga (PSL / Polska 2050)" - extract main parties
|
||
# Split on " / " or " - " or "(" and process each
|
||
parts = re.split(r"[\(\)\/\-\,\;]+", raw)
|
||
parts = [p.strip() for p in parts if p.strip()]
|
||
|
||
slugs_found = []
|
||
names_found = []
|
||
for part in parts:
|
||
part_key = unicodedata.normalize("NFD", re.sub(r"[^\w\s\-]+", "", part).strip().lower())
|
||
|
||
# Check primary canonical matching
|
||
if part_key in _PARTY_CANONICAL:
|
||
name, slug = _PARTY_CANONICAL[part_key]
|
||
if slug not in slugs_found:
|
||
slugs_found.append(slug)
|
||
names_found.append(name)
|
||
continue
|
||
|
||
# Check secondary canonical matching
|
||
if part_key in _PARTY_SECONDARY:
|
||
name, slug = _PARTY_SECONDARY[part_key]
|
||
if slug not in slugs_found:
|
||
slugs_found.append(slug)
|
||
names_found.append(name)
|
||
continue
|
||
|
||
if slugs_found:
|
||
return " + ".join(names_found), "|".join(slugs_found)
|
||
|
||
# No match - use the raw string as-is (normalised)
|
||
display = re.sub(r"\s+", " ", raw.strip().title())
|
||
slug = re.sub(r"[^\w\-]+", "-", display.lower())
|
||
slug = re.sub(r"-+", "-", slug).strip("-")
|
||
return display, slug
|
||
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# Neo4j helpers
|
||
# ---------------------------------------------------------------------------
|
||
|
||
BATCH_SIZE = 500
|
||
|
||
|
||
def _connect():
|
||
uri = os.getenv("NEO4J_URI", "bolt://localhost:7687")
|
||
user = os.getenv("NEO4J_USER", "neo4j")
|
||
pwd = os.getenv("NEO4J_PASS", "graphdb123")
|
||
driver = GraphDatabase.driver(uri, auth=(user, pwd))
|
||
# Verify connectivity
|
||
driver.verify_connectivity()
|
||
return driver
|
||
|
||
|
||
def load_hospitals(driver, hospitals: list[Hospital]):
|
||
"""Bulk merge Hospital nodes."""
|
||
with driver.session() as session:
|
||
batches = [hospitals[i:i + BATCH_SIZE] for i in range(0, len(hospitals), BATCH_SIZE)]
|
||
for batch in batches:
|
||
records = [
|
||
{
|
||
"id": h.id,
|
||
"name": h.name,
|
||
"city": h.city,
|
||
"legal_form": h.legal_form,
|
||
"website": h.website,
|
||
"notes": h.notes,
|
||
}
|
||
for h in batch
|
||
]
|
||
session.execute_write(_merge_hospitals, records)
|
||
|
||
|
||
def _merge_hospitals(tx, records):
|
||
cypher = """
|
||
UNWIND $records AS r
|
||
MERGE (h:Hospital {uid: r.id})
|
||
ON CREATE SET h.name = r.name, h.city = r.city,
|
||
h.legalForm = r.legal_form, h.website = r.website, h.notes = r.notes
|
||
ON MATCH SET h.name = r.name, h.city = r.city,
|
||
h.legalForm = r.legal_form, h.website = r.website, h.notes = r.notes
|
||
"""
|
||
tx.run(cypher, records=records)
|
||
|
||
|
||
def load_persons(driver, persons: list[Person]):
|
||
"""Bulk merge Person nodes."""
|
||
with driver.session() as session:
|
||
batches = [persons[i:i + BATCH_SIZE] for i in range(0, len(persons), BATCH_SIZE)]
|
||
for batch in batches:
|
||
records = [
|
||
{
|
||
"uid": p.uid,
|
||
"name": p.name,
|
||
"partyNote": p.party_note,
|
||
"partySlugs": p.party_slugs,
|
||
"sourceUrls": p.source_urls,
|
||
"note": p.note,
|
||
"normName": p.norm_name,
|
||
}
|
||
for p in batch
|
||
]
|
||
session.execute_write(_merge_persons, records)
|
||
|
||
|
||
def _merge_persons(tx, records):
|
||
cypher = """
|
||
UNWIND $records AS r
|
||
MERGE (p:Person {uid: r.uid})
|
||
ON CREATE SET p.name = r.name, p.partyNote = r.partyNote,
|
||
p.sourceUrls = r.sourceUrls, p.note = r.note, p.normName = r.normName
|
||
ON MATCH SET p.name = r.name, p.partyNote = r.partyNote,
|
||
p.note = r.note, p.normName = r.normName,
|
||
p.sourceUrls = p.sourceUrls + r.sourceUrls
|
||
WITH p, r
|
||
// Link to Party nodes via slug
|
||
UNWIND r.partySlugs AS slug
|
||
MATCH (pt:Party {slug: slug})
|
||
MERGE (p)-[rel:AFILIACJA_PARTYJNA]->(pt)
|
||
ON CREATE SET rel.createdAt = timestamp()
|
||
"""
|
||
tx.run(cypher, records=records)
|
||
|
||
|
||
def load_parties(driver, parties: list[Party]):
|
||
"""Bulk merge Party nodes."""
|
||
with driver.session() as session:
|
||
records = [
|
||
{
|
||
"slug": p.slug,
|
||
"name": p.name,
|
||
}
|
||
for p in parties
|
||
]
|
||
session.execute_write(_merge_parties, records)
|
||
|
||
|
||
def _merge_parties(tx, records):
|
||
cypher = """
|
||
UNWIND $records AS r
|
||
MERGE (pt:Party {slug: r.slug})
|
||
ON CREATE SET pt.name = r.name
|
||
ON MATCH SET pt.name = r.name
|
||
"""
|
||
tx.run(cypher, records=records)
|
||
|
||
|
||
def load_roles(driver, roles: list[Role], persons_by_uid: dict):
|
||
"""Create relationships between hospitals and persons."""
|
||
with driver.session() as session:
|
||
batches = [roles[i:i + BATCH_SIZE] for i in range(0, len(roles), BATCH_SIZE)]
|
||
for batch in batches:
|
||
records = [
|
||
{
|
||
"hospitalId": r.hospital_id,
|
||
"personUid": r.person.uid,
|
||
"relationship": r.role,
|
||
"subRole": r.sub_role,
|
||
}
|
||
for r in batch
|
||
]
|
||
session.execute_write(_merge_roles, records)
|
||
|
||
|
||
def _merge_roles(tx, records):
|
||
type_to_cypher = {
|
||
"DYREKTOR": "MERGE (h)-[rel:DYREKTOR]->(p)",
|
||
"Z_CA": "MERGE (h)-[rel:Z_CA]->(p)",
|
||
"RADA_SPOLECZNA": "MERGE (h)-[rel:RADA_SPOLECZNA]->(p)",
|
||
"RADNIA": "MERGE (h)-[rel:RADNIA]->(p)",
|
||
"PREZES_ZARZADU": "MERGE (h)-[rel:PREZES_ZARZADU]->(p)",
|
||
"RADA_NADZORCZA": "MERGE (h)-[rel:RADA_NADZORCZA]->(p)",
|
||
}
|
||
for rel_type, pattern in type_to_cypher.items():
|
||
rel_records = [r for r in records if r["relationship"] == rel_type]
|
||
if rel_records:
|
||
tx.run(
|
||
f"""
|
||
UNWIND $records AS r
|
||
MATCH (h:Hospital {{uid: r.hospitalId}})
|
||
MATCH (p:Person {{uid: r.personUid}})
|
||
{pattern}
|
||
ON CREATE SET rel.createdAt = timestamp()
|
||
""",
|
||
records=rel_records
|
||
)
|
||
|
||
|
||
def validate(driver):
|
||
"""Run validation queries and print results."""
|
||
with driver.session() as session:
|
||
queries = {
|
||
"Total hospitals": "MATCH (h:Hospital) RETURN count(h) AS total",
|
||
"Total persons": "MATCH (p:Person) RETURN count(p) AS total",
|
||
"Total parties": "MATCH (pt:Party) RETURN count(pt) AS total",
|
||
"Persons linked to parties": (
|
||
"MATCH (p:Person)-[r:AFILIACJA_PARTYJNA]->(:Party) RETURN count(p) AS total"
|
||
),
|
||
"Person->Party relationships": "MATCH ()-[r:AFILIACJA_PARTYJNA]->() RETURN count(r) AS total",
|
||
"Director relationships": "MATCH ()-[rel:DYREKTOR]->() RETURN count(rel) AS total",
|
||
"Deputy director relationships": "MATCH ()-[rel:Z_CA]->() RETURN count(rel) AS total",
|
||
"Social board relationships": "MATCH ()-[rel:RADA_SPOLECZNA]->() RETURN count(rel) AS total",
|
||
"Supervisory board relationships": "MATCH ()-[rel:RADA_NADZORCZA]->() RETURN count(rel) AS total",
|
||
"Board member relationships": "MATCH ()-[rel:RADNIA]->() RETURN count(rel) AS total",
|
||
}
|
||
print("\n=== VALIDATION ===")
|
||
for label, query in queries.items():
|
||
result = session.run(query)
|
||
record = result.single()
|
||
val = record.get('total', record.get('overlappingPersons', 0))
|
||
print(f" {label}: {val}")
|
||
print("==================\n")
|
||
|
||
|
||
def export_overlap_csv(driver, filename="overlap_report.csv"):
|
||
"""Export persons who hold multiple roles to CSV."""
|
||
cypher = """
|
||
MATCH (p:Person)
|
||
OPTIONAL MATCH (p)-[r:AFILIACJA_PARTYJNA]->(pt:Party)
|
||
RETURN p.name AS name, pt.name AS party, p.sourceUrls AS sourceUrls, p.note AS note
|
||
ORDER BY p.name
|
||
"""
|
||
with driver.session() as session:
|
||
result = session.run(cypher)
|
||
rows = [{"name": r["name"], "party": r["party"] or "", "sourceUrls": r["sourceUrls"], "note": r["note"]} for r in result]
|
||
|
||
with open(filename, "w", encoding="utf-8") as f:
|
||
f.write("name,party,sourceUrls,note\n")
|
||
for row in rows:
|
||
urls = "|".join(row["sourceUrls"]) if row["sourceUrls"] else ""
|
||
note = row["note"].replace('"', '""') if row["note"] else ""
|
||
f.write(f'"{row["name"]}","{row["party"]}","{urls}","{note}"\n')
|
||
|
||
print(f"Exported {len(rows)} persons to {filename}")
|
||
return rows
|
||
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# Build the graph from embedded research data
|
||
# ---------------------------------------------------------------------------
|
||
|
||
HOSPITALS = [] # (id, name, city, legalForm, website, notes)
|
||
|
||
# === CZESC I — Szpitale wojewodzkie, uniwersyteckie i resortowe ===
|
||
|
||
HOSPITALS.append((
|
||
"uskwb",
|
||
"Uniwersytecki Szpital Kliniczny w Białymstoku (USK)",
|
||
"Białystok",
|
||
"SPZOZ",
|
||
"https://uskwb.pl/",
|
||
"Organ nadzorczy: Rada Społeczna"
|
||
))
|
||
|
||
HOSPITALS.append((
|
||
"udsk",
|
||
"Uniwersytecki Dziecięcy Szpital Kliniczny im. L. Zamenhofa (UDSK)",
|
||
"Białystok",
|
||
"SPZOZ",
|
||
"https://szpitaldzieciecybialystok.sisco.info/",
|
||
"Organ nadzorczy: Rada Społeczna"
|
||
))
|
||
|
||
HOSPITALS.append((
|
||
"sniadeckiego",
|
||
"SP ZOZ Wojewódzki Szpital Zespolony im. J. Śniadeckiego",
|
||
"Białystok",
|
||
"SPZOZ",
|
||
"https://podlaskie.eu/zdrowie/bogdan-kalicki-oficjalnie-objal-stanowisko-dyrektora-sniadecji.html",
|
||
"Organ tworzący: Województwo Podlaskie. Organ nadzorczy: Rada Społeczna"
|
||
))
|
||
|
||
HOSPITALS.append((
|
||
"bco",
|
||
"Białostockie Centrum Onkologii im. M. Skłodowskiej-Curie",
|
||
"Białystok",
|
||
"SPZOZ",
|
||
"https://www.onkologia.bialystok.pl/",
|
||
"Organ tworzący: Województwo Podlaskie. Rada Społeczna powołana Uchwałą Nr 72/1442/2025"
|
||
))
|
||
|
||
HOSPITALS.append((
|
||
"mswia",
|
||
"SP ZOZ MSWiA w Białymstoku im. M. Zyndrama-Kościałkowskiego",
|
||
"Białystok",
|
||
"SPZOZ",
|
||
"https://bip.zozmswia.bialystok.pl/",
|
||
"Organ tworzący: Minister Spraw Wewnętrznych i Administracji"
|
||
))
|
||
|
||
HOSPITALS.append((
|
||
"choroszcz",
|
||
"SP Psychiatryczny ZOZ im. dr. St. Deresza",
|
||
"Choroszcz",
|
||
"SP Psychiatryczny ZOZ",
|
||
"https://bip-sppzozc.podlaskie.eu/",
|
||
"Organ tworzący: Województwo Podlaskie"
|
||
))
|
||
|
||
HOSPITALS.append((
|
||
"lomza",
|
||
"Szpital Wojewódzki im. Kardynała Stefana Wyszyńskiego",
|
||
"Łomża",
|
||
"SPZOZ",
|
||
"https://www.szpital-lomza.pl/",
|
||
"Organ tworzący: Samorząd Województwa Podlaskiego"
|
||
))
|
||
|
||
HOSPITALS.append((
|
||
"suwalki_rundyger",
|
||
"Szpital Wojewódzki im. dr. Ludwika Rydygiera",
|
||
"Suwałki",
|
||
"SPZOZ",
|
||
"https://bip-spsws.podlaskie.eu/",
|
||
"Organ tworzący: Województwo Podlaskie"
|
||
))
|
||
|
||
HOSPITALS.append((
|
||
"suwalki_psych",
|
||
"Specjalistyczny Psychiatryczny SP ZOZ",
|
||
"Suwałki",
|
||
"SPZOZ",
|
||
"https://bip-spspzozs.podlaskie.eu/",
|
||
"Organ tworzący: Województwo Podlaskie"
|
||
))
|
||
|
||
# === CZESC II — Szpitale powiatowe ===
|
||
|
||
HOSPITALS.append((
|
||
"augustow",
|
||
"SP ZOZ",
|
||
"Augustów",
|
||
"SPZOZ",
|
||
"https://bip-spzoz-staugustow.podlaskie.eu/",
|
||
"Organ tworzący: Powiat Augustowski"
|
||
))
|
||
|
||
HOSPITALS.append((
|
||
"bielsk",
|
||
"SP ZOZ",
|
||
"Bielsk Podlaski",
|
||
"SPZOZ",
|
||
"https://www.spzoz-bielsk.pl/",
|
||
"Organ tworzący: Powiat Bielski"
|
||
))
|
||
|
||
HOSPITALS.append((
|
||
"hajnowka",
|
||
"SP ZOZ",
|
||
"Hajnówka",
|
||
"SPZOZ",
|
||
"https://spzozhajnowka.pl/",
|
||
"Organ tworzący: Powiat Hajnowski"
|
||
))
|
||
|
||
HOSPITALS.append((
|
||
"siemiatycze",
|
||
"SP ZOZ (Szpital Powiatowy)",
|
||
"Siemiatycze",
|
||
"SPZOZ",
|
||
"https://spzozsiemiatycze.pl/",
|
||
"Organ tworzący: Powiat Siemiatycki"
|
||
))
|
||
|
||
HOSPITALS.append((
|
||
"sokolka",
|
||
"SP ZOZ",
|
||
"Sokółka",
|
||
"SPZOZ",
|
||
"https://szpitalsokolka.pl/",
|
||
"Organ tworzący: Powiat Sokólski"
|
||
))
|
||
|
||
HOSPITALS.append((
|
||
"monki",
|
||
"SP ZOZ",
|
||
"Mońki",
|
||
"SPZOZ",
|
||
"https://spzoz.szpital-monki.h2.pl/",
|
||
"Organ tworzący: Powiat Moniecki"
|
||
))
|
||
|
||
HOSPITALS.append((
|
||
"grajewo",
|
||
"Szpital Ogólny im. dr. Witolda Ginela",
|
||
"Grajewo",
|
||
"SPZOZ",
|
||
"https://www.szpital-grajewo.pl/",
|
||
"Organ tworzący: Powiat Grajewski"
|
||
))
|
||
|
||
HOSPITALS.append((
|
||
"kolno",
|
||
"Szpital Ogólny w Kolnie",
|
||
"Kolno",
|
||
"SPZOZ",
|
||
"https://www.szpitalkolno.pl/",
|
||
"Organ tworzący: Powiat Kolneński"
|
||
))
|
||
|
||
HOSPITALS.append((
|
||
"wysokie",
|
||
"Szpital Ogólny w Wysokiem Mazowieckiem",
|
||
"Wysokie Mazowieckie",
|
||
"SPZOZ",
|
||
"https://szpitalwysmaz.pl/",
|
||
"Organ tworzący: Powiat Wysokomazowiecki"
|
||
))
|
||
|
||
HOSPITALS.append((
|
||
"sejny",
|
||
"SP ZOZ (Szpital Powiatowy)",
|
||
"Sejny",
|
||
"SPZOZ",
|
||
"https://szpital.sejny.pl/",
|
||
"Organ tworzący: Powiat Sejneński"
|
||
))
|
||
|
||
HOSPITALS.append((
|
||
"zambrow",
|
||
"Szpital Powiatowy w Zambrowie sp. z o.o.",
|
||
"Zambrow",
|
||
"sp. z o.o.",
|
||
"",
|
||
"Jedyny szpital jako spółka handlowa. Organ: Rada Nadzorcza"
|
||
))
|
||
|
||
|
||
def person_key(hospital_id: str, name: str, role: str) -> str:
|
||
"""Deterministic key to detect duplicates within hospital+role."""
|
||
raw = f"{hospital_id}:{name}:{role}"
|
||
return hash(raw)
|
||
|
||
|
||
def build_data():
|
||
"""
|
||
Returns (hospitals, persons_list, roles_list) from the embedded data.
|
||
Deduplicates persons by normalised name across all roles/hospitals.
|
||
"""
|
||
persons_map = {} # norm_name -> person_uid -> Person
|
||
roles = []
|
||
hospitals = []
|
||
|
||
# Part-of-speech helper (for KWW/local committees and "brak danych" entries)
|
||
def is_kww_local(raw: str) -> bool:
|
||
return raw.startswith("KWW") or raw.startswith("KW") or "KWW" in raw or "KW " in raw or "komitet" in raw.lower()
|
||
|
||
def is_uncertain(raw: str) -> bool:
|
||
return raw in ("brak/niepotwierdzona", "brak danych", "NIEZWERYFIKOWANE", "brak/niepotwierdzona", "niepotwierdzona", "brak potwierdzonej afiliacji", "komitet niepotwierdzony", "no confirmed affiliation")
|
||
|
||
# ---- Register a person and optionally link a party ----
|
||
def add_person(party_raw: str) -> list[Person]:
|
||
"""Normalize the party string, look up Person, link Party. Returns list of matched persons."""
|
||
party_display, party_slug = normalise_party(party_raw)
|
||
persons = [p for p in persons_by_name.values()]
|
||
return persons
|
||
|
||
# ---- Helper to register person ----
|
||
def get_person(name: str, party: str = "", source_url: str = "", note: str = "") -> Person:
|
||
n = norm_name(name)
|
||
if n not in persons_map:
|
||
persons_map[n] = {}
|
||
# Check exact name match first
|
||
for uid, p in persons_map[n].items():
|
||
if p.name == name:
|
||
if source_url and source_url not in p.source_urls:
|
||
p.source_urls.append(source_url)
|
||
# If party is uncertain, keep it as note; if it's a KWW or real party, add slug
|
||
if party and not is_uncertain(party) and not party.startswith("histor"):
|
||
_, slug = normalise_party(party)
|
||
if slug and slug not in p.party_slugs:
|
||
p.party_slugs.append(slug)
|
||
else:
|
||
if party and not is_uncertain(party) and not party.startswith("histor"):
|
||
p.party_note = party
|
||
elif is_uncertain(party) and (not p.party_note or p.party_note == "brak/niepotwierdzona"):
|
||
p.party_note = party
|
||
return p
|
||
uid = str(uuid.uuid4())
|
||
p = Person(uid=uid, name=name, party_note=party, source_urls=[] if not source_url else [source_url], note=note)
|
||
if party and not is_uncertain(party) and not party.startswith("histor"):
|
||
_, slug = normalise_party(party)
|
||
if slug:
|
||
p.party_slugs.append(slug)
|
||
persons_map[n][uid] = p
|
||
return p
|
||
|
||
# =========================================================================
|
||
# 1. Uniwersytecki Szpital Kliniczny w Białymstoku (USK)
|
||
# =========================================================================
|
||
src_dyr = "https://uskwb.pl/dyrekcja-szpitala/"
|
||
src_rada = "https://uskbialystok.sisco.info/wladze,36_8"
|
||
hospitals.append(Hospital(id="uskwb", name="Uniwersytecki Szpital Kliniczny w Białymstoku (USK)",
|
||
city="Białystok", legal_form="SPZOZ", website=src_dyr))
|
||
roles.append(Role("uskwb", get_person("prof. dr hab. n. med. Jan Kochanowicz", party="brak/niepotwierdzona", source_url=src_dyr), "DYREKTOR", "Dyrektor Naczelny"))
|
||
roles.append(Role("uskwb", get_person("dr hab. n. med. Marzena Wojewódzka-Żelezniakowicz", party="brak/niepotwierdzona", source_url=src_dyr), "Z_CA", "Z-ca ds. Lecznictwa"))
|
||
roles.append(Role("uskwb", get_person("mgr Beata Kropiewnicka", party="brak/niepotwierdzona", source_url=src_dyr), "Z_CA", "Z-ca ds. Finansowych / Gł. Księgowy"))
|
||
roles.append(Role("uskwb", get_person("mgr Piotr Kołos", party="brak/niepotwierdzona", source_url=src_dyr), "Z_CA", "Z-ca ds. Techniczno-Inwestycyjnych"))
|
||
roles.append(Role("uskwb", get_person("prof. dr hab. Dariusz Lebensztejn", party="brak/niepotwierdzona", source_url=src_rada), "RADA_SPOLECZNA", "Przewodniczący"))
|
||
roles.append(Role("uskwb", get_person("prof. dr hab. Wojciech Miltyk", party="brak/niepotwierdzona", source_url=src_rada), "RADA_SPOLECZNA", "Z-ca Przewodniczącego"))
|
||
roles.append(Role("uskwb", get_person("dr hab. Joanna Reszeć-Giełażyn", party="brak/niepotwierdzona", source_url=src_rada), "RADA_SPOLECZNA", "Sekretarz"))
|
||
roles.append(Role("uskwb", get_person("Bartosz Niebrzydowski", party="brak/niepotwierdzona", source_url=src_rada), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("uskwb", get_person("Marcin Borsuk", party="brak/niepotwierdzona", source_url=src_rada), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("uskwb", get_person("Marek Malinowski", party="Wicemarszałek woj.; koalicja Obywatelska; niezrzeszony", source_url=src_rada, note="do potwierdzenia"), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("uskwb", get_person("Henryk Grzesiak", party="brak/niepotwierdzona", source_url=src_rada), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("uskwb", get_person("Elżbieta Sienkiewicz", party="brak/niepotwierdzona", source_url=src_rada), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("uskwb", get_person("prof. dr hab. Sławomir Terlikowski", party="brak/niepotwierdzona", source_url=src_rada), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("uskwb", get_person("prof. dr hab. Jerzy Pałka", party="brak/niepotwierdzona", source_url=src_rada), "RADA_SPOLECZNA", "członek"))
|
||
|
||
# =========================================================================
|
||
# 2. UDSK
|
||
# =========================================================================
|
||
src_udsk = "https://szpitaldzieciecybialystok.sisco.info/?id=316"
|
||
src_udsk_rada = "https://szpitaldzieciecybialystok.sisco.info/?id=416"
|
||
hospitals.append(Hospital(id="udsk", name="Uniwersytecki Dziecięcy Szpital Kliniczny im. L. Zamenhofa",
|
||
city="Białystok", legal_form="SPZOZ", website=src_udsk))
|
||
roles.append(Role("udsk", get_person("prof. dr hab. n. med. Anna Wasilewska", party="brak/niepotwierdzona", source_url=src_udsk), "DYREKTOR", "Dyrektor"))
|
||
roles.append(Role("udsk", get_person("dr n. o zdr. Cezary Rzemek", party="brak/niepotwierdzona", source_url=src_udsk), "Z_CA", "Z-ca ds. Ekonomicznych"))
|
||
roles.append(Role("udsk", get_person("mgr inż. Wojciech Roszkowski", party="brak/niepotwierdzona", source_url=src_udsk), "Z_CA", "Z-ca ds. Administr.-Techn."))
|
||
roles.append(Role("udsk", get_person("Elżbieta Sienkiewicz", party="brak/niepotwierdzona", source_url=src_udsk), "Z_CA", "Z-ca ds. Pielęgniarstwa"))
|
||
roles.append(Role("udsk", get_person("prof. dr hab. n. med. Beata Naumnik", party="brak/niepotwierdzona", source_url=src_udsk_rada), "RADA_SPOLECZNA", "Przewodnicząca"))
|
||
roles.append(Role("udsk", get_person("mgr Sylwia Ignaczak-Piotrowska", party="brak/niepotwierdzona", source_url=src_udsk_rada), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("udsk", get_person("mgr Katarzyna Bardłowska", party="brak/niepotwierdzona", source_url=src_udsk_rada), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("udsk", get_person("mgr Monika Anna Borkowska", party="brak/niepotwierdzona", source_url=src_udsk_rada), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("udsk", get_person("dr n. med. Ryta Filipkowska", party="brak/niepotwierdzona", source_url=src_udsk_rada), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("udsk", get_person("mgr Bożena Kostro", party="brak/niepotwierdzona", source_url=src_udsk_rada), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("udsk", get_person("prof. dr hab. n. med. Anna Niemcunowicz-Janica", party="brak/niepotwierdzona", source_url=src_udsk_rada), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("udsk", get_person("prof. dr hab. n. med. Elżbieta Tryniszewska", party="brak/niepotwierdzona", source_url=src_udsk_rada), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("udsk", get_person("prof. dr hab. n. med. Halina Car", party="brak/niepotwierdzona", source_url=src_udsk_rada), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("udsk", get_person("prof. dr hab. n. med. Jarosław Daniluk", party="brak/niepotwierdzona", source_url=src_udsk_rada), "RADA_SPOLECZNA", "członek"))
|
||
|
||
# =========================================================================
|
||
# 3. Śniadeckiego
|
||
# =========================================================================
|
||
src_sn = "https://podlaskie.eu/zdrowie/bogdan-kalicki-oficjalnie-objal-stanowisko-dyrektora-sniadecji.html"
|
||
src_sn_rada = "https://bip-spzozwszjs.podlaskie.eu/rada.html"
|
||
hospitals.append(Hospital(id="sniaskiego", name="SP ZOZ Wojewódzki Szpital Zespolony im. J. Śniadeckiego",
|
||
city="Białystok", legal_form="SPZOZ", website=src_sn))
|
||
roles.append(Role("sniaskiego", get_person("Bogdan Kalicki", party="brak/niepotwierdzona", source_url=src_sn), "DYREKTOR", "Dyrektor (p.o.)"))
|
||
roles.append(Role("sniaskiego", get_person("Karol Pilecki", party="Koalicja Obywatelska", source_url=src_sn_rada, note="radny Sejmiku; historycznie PSL"), "RADA_SPOLECZNA", "Przewodniczący"))
|
||
roles.append(Role("sniaskiego", get_person("Joanna Sosnowska", party="brak danych", source_url=src_sn_rada), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("sniaskiego", get_person("Bartosz Czaczkowski", party="brak danych", source_url=src_sn_rada), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("sniaskiego", get_person("Bogusław Dębski", party="Prawica Rzeczypospolitej", source_url=src_sn_rada, note="radny Sejmiku; historycznie PSL"), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("sniaskiego", get_person("Jarosław Zygmunt Dworzański", party="Koalicja Obywatelska", source_url=src_sn_rada, note="radny Sejmiku; b. marszałek woj."), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("sniaskiego", get_person("Henryk Gryko", party="brak danych", source_url=src_sn_rada), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("sniaskiego", get_person("Paweł Jamróz", party="brak danych", source_url=src_sn_rada), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("sniaskiego", get_person("Jan Maksymilian Janiszewski", party="brak danych", source_url=src_sn_rada), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("sniaskiego", get_person("Katarzyna Kluczyk-Ceckowska", party="brak danych", source_url=src_sn_rada), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("sniaskiego", get_person("Rafał Kołakowski", party="brak danych", source_url=src_sn_rada), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("sniaskiego", get_person("Wojciech Łuczaj", party="brak danych", source_url=src_sn_rada, note="możliwie Łuczaj-Pogorzelski, PSL — NIEZWERYFIKOWANE"), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("sniaskiego", get_person("Tomasz Stefan Madras", party="Prawo i Sprawiedliwość", source_url=src_sn_rada, note="radny Sejmiku; b. wicewojewoda"), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("sniaskiego", get_person("Jarosław Matwiejuk", party="historycznie SLD/Lewica", source_url=src_sn_rada, note="b. poseł SLD — NIEZWERYFIKOWANE"), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("sniaskiego", get_person("Andrzej Parafiniuk", party="brak danych", source_url=src_sn_rada), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("sniaskiego", get_person("Lech Rutkowski", party="brak danych", source_url=src_sn_rada), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("sniaskiego", get_person("Wojciech Strzałkowski", party="brak danych", source_url=src_sn_rada, note="NIEZWERYFIKOWANE"), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("sniaskiego", get_person("Irina Kowalska", party="brak/niepotwierdzona", source_url=src_sn_rada), "RADA_SPOLECZNA", "członek"))
|
||
|
||
# =========================================================================
|
||
# 4. Białostockie Centrum Onkologii
|
||
# =========================================================================
|
||
src_bco = "https://bip-bco.podlaskie.eu/wadze_i_struktura_organizacyjna/kierownictwo-szpitala.html"
|
||
src_bco_rada = "https://www.onkologia.bialystok.pl/pages/rada-spoleczna"
|
||
hospitals.append(Hospital(id="bco", name="Białostockie Centrum Onkologii im. M. Skłodowskiej-Curie",
|
||
city="Białystok", legal_form="SPZOZ", website=src_bco))
|
||
roles.append(Role("bco", get_person("dr n. o zdr. Magdalena Joanna Borkowska", party="brak/niepotwierdzona", source_url=src_bco), "DYREKTOR", "Dyrektor"))
|
||
roles.append(Role("bco", get_person("dr n. med. Dorota Elżbieta Kazberuk", party="brak/niepotwierdzona", source_url=src_bco), "Z_CA", "Z-ca ds. Lecznictwa"))
|
||
roles.append(Role("bco", get_person("dr n. o zdr. Elżbieta Potentas", party="brak/niepotwierdzona", source_url=src_bco), "Z_CA", "Z-ca ds. Koordynacji Onkologicznej"))
|
||
roles.append(Role("bco", get_person("Anna Augustyn", party="Koalicja Obywatelska", source_url=src_bco_rada, note="radna Sejmiku"), "RADA_SPOLECZNA", "Przewodnicząca"))
|
||
roles.append(Role("bco", get_person("Cezary Cieślukowski", party="PSL", source_url=src_bco_rada, note="Przewodniczący Sejmiku VII kad."), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("bco", get_person("Jarosław Zygmunt Dworzański", party="Koalicja Obywatelska", source_url=src_bco_rada, note="radny Sejmiku"), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("bco", get_person("Bogdan Dyjuk", party="PSL", source_url=src_bco_rada, note="radny Sejmiku; czł. Zarządu Woj."), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("bco", get_person("Janusz Dzięcioł", party="NIEZWERYFIKOWANE", source_url=src_bco_rada, note="inna osoba niż zmarły poseł PO; afiliacji nie potwierdzono"), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("bco", get_person("Ewa Feszler", party="brak danych", source_url=src_bco_rada), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("bco", get_person("Robert Jabłoński", party="Prawo i Sprawiedliwość", source_url=src_bco_rada, note="prawdopodobnie PiS; NIEZWERYFIKOWANE"), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("bco", get_person("Leszek Maciej Lulewicz", party="brak danych", source_url=src_bco_rada), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("bco", get_person("Jacek Piorunek", party="Koalicja Obywatelska", source_url=src_bco_rada, note="radny Sejmiku"), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("bco", get_person("Agnieszka Zaręba", party="brak danych", source_url=src_bco_rada), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("bco", get_person("Agnieszka Zawistowska", party="brak danych", source_url=src_bco_rada), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("bco", get_person("Maciej Żywno", party="Polska 2050", source_url="https://www.senat.gov.pl/sklad/senatorowie/senator,1072,11,maciej-zywno.html", note="senator, wicemarszałek Senatu"), "RADA_SPOLECZNA", "członek"))
|
||
|
||
# =========================================================================
|
||
# 5. MSWiA
|
||
# =========================================================================
|
||
src_mswia = "https://bip.zozmswia.bialystok.pl/wladze-i-struktura-organizacyjna"
|
||
src_mswia_rada = "https://bip.zozmswia.bialystok.pl/wladze-i-struktura-organizacyjna/rada-spoleczna/"
|
||
hospitals.append(Hospital(id="mswia", name="SP ZOZ MSWiA w Białymstoku im. M. Zyndrama-Kościałkowskiego",
|
||
city="Białystok", legal_form="SPZOZ", website=src_mswia))
|
||
roles.append(Role("mswia", get_person("Marek Stanisław Karp", party="brak/niepotwierdzona", source_url=src_mswia, note="p.o. od 20.02.2024"), "DYREKTOR", "Dyrektor"))
|
||
roles.append(Role("mswia", get_person("Daniel Szutko", party="brak/niepotwierdzona", source_url=src_mswia_rada), "RADA_SPOLECZNA", "Przewodniczący"))
|
||
roles.append(Role("mswia", get_person("mgr inż. Sławomir Wilczewski", party="brak/niepotwierdzona", source_url=src_mswia_rada), "RADA_SPOLECZNA", "Z-ca Przewodniczącego (Policja)"))
|
||
roles.append(Role("mswia", get_person("st. bryg. Marcin Wierel", party="brak/niepotwierdzona", source_url=src_mswia_rada), "RADA_SPOLECZNA", "członek (PSP)"))
|
||
roles.append(Role("mswia", get_person("płk SG Marek Sochański", party="brak/niepotwierdzona", source_url=src_mswia_rada), "RADA_SPOLECZNA", "członek (Straż Graniczna)"))
|
||
roles.append(Role("mswia", get_person("ppłk Małgorzata Błażewicz", party="brak/niepotwierdzona", source_url=src_mswia_rada), "RADA_SPOLECZNA", "członek (Służba Więzienna)"))
|
||
roles.append(Role("mswia", get_person("dr n. med. Anatol Aksiucik", party="brak/niepotwierdzona", source_url=src_mswia_rada), "RADA_SPOLECZNA", "członek (Naczelna Rada Lekarska)"))
|
||
roles.append(Role("mswia", get_person("dr n. med. Beata Olejnik", party="brak/niepotwierdzona", source_url=src_mswia_rada), "RADA_SPOLECZNA", "członek (NRPiP)"))
|
||
roles.append(Role("mswia", get_person("prof. dr hab. n. med. Andrzej Dąbrowski", party="brak/niepotwierdzona", source_url=src_mswia_rada), "RADA_SPOLECZNA", "członek (UMB)"))
|
||
roles.append(Role("mswia", get_person("Krzysztof Turowski", party="brak/niepotwierdzona", source_url=src_mswia_rada), "RADA_SPOLECZNA", "członek (MSWiA)"))
|
||
|
||
# =========================================================================
|
||
# 6. Choroszcz
|
||
# =========================================================================
|
||
src_ch = "https://bip-sppzozc.podlaskie.eu/"
|
||
hospitals.append(Hospital(id="choroszcz", name="SP Psychiatryczny ZOZ im. dr. St. Deresza",
|
||
city="Choroszcz", legal_form="SP Psychiatryczny ZOZ", website=src_ch))
|
||
roles.append(Role("choroszcz", get_person("Alicja Skindzielewska", party="brak/niepotwierdzona", source_url=src_ch, note="p.o. Dyrektora"), "DYREKTOR", "p.o. Dyrektora"))
|
||
roles.append(Role("choroszcz", get_person("Andrzej Szewczuk", party="brak/niepotwierdzona", source_url=src_ch, note="dyrektor-elekt od VIII.2026"), "DYREKTOR", "Dyrektor-elekt (od VIII.2026)"))
|
||
roles.append(Role("choroszcz", get_person("lek. Justyna Anna Piłasiewicz", party="brak/niepotwierdzona", source_url=src_ch), "Z_CA", "Z-ca ds. Lecznictwa"))
|
||
roles.append(Role("choroszcz", get_person("Ewelina Cecotka-Białokozowicz", party="brak/niepotwierdzona", source_url=src_ch), "Z_CA", "Gł. Księgowa"))
|
||
roles.append(Role("choroszcz", get_person("dr n. med. Nadzieja Sołowiej", party="brak/niepotwierdzona", source_url=src_ch), "Z_CA", "Naczelna Pielęgniarka"))
|
||
roles.append(Role("choroszcz", get_person("Monika Miklaszewicz", party="brak danych", source_url=src_ch), "RADA_SPOLECZNA", "Przewodnicząca"))
|
||
|
||
# =========================================================================
|
||
# 7. Łomża
|
||
# =========================================================================
|
||
src_lomza = "https://www.szpital-lomza.pl/"
|
||
src_lomza_rada = "https://www.szpital-lomza.pl/index.php/wiadomosc/rada-spoleczna"
|
||
src_lomza_dyr = "https://www.rynekzdrowia.pl/.../Nowy-dyrektor-w-Szpitalu-Wojewodzkim-w-Lomzy,269250,1.html"
|
||
hospitals.append(Hospital(id="lomza", name="Szpital Wojewódzki im. Kardynała Stefana Wyszyńskiego",
|
||
city="Łomża", legal_form="SPZOZ", website=src_lomza))
|
||
roles.append(Role("lomza", get_person("Dariusz Domasiewicz", party="KWW Przyjazna Łomża", source_url=src_lomza_dyr, note="Radny Rady Miejskiej Łomży"), "DYREKTOR", "Dyrektor"))
|
||
roles.append(Role("lomza", get_person("Maria Konopka", party="Koalicja Obywatelska", source_url=src_lomza_rada, note="kandydatka PO do sejmiku 2024"), "RADA_SPOLECZNA", "Przewodnicząca"))
|
||
roles.append(Role("lomza", get_person("Marek Olbryś", party="PiS", source_url="https://www.portalsamorzadowy.pl/osoba/marek-olbrys,876.html", note="wicemarszałek woj. / radny Sejmiku"), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("lomza", get_person("Jacek Piorunek", party="Koalicja Obywatelska", source_url="https://www.portalsamorzadowy.pl/osoba/jacek-piorunek,6388.html", note="radny Sejmiku"), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("lomza", get_person("Bernadeta Krynicka", party="PiS", source_url="https://pl.wikipedia.org/wiki/Bernadeta_Krynicka", note="radna Sejmiku; b. posłanka PiS"), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("lomza", get_person("Lech Marek Szabłowski", party="PiS", source_url="https://www.portalsamorzadowy.pl/osoba/lech-szablowski,55269.html", note="starosta łomżyński / radny powiatu"), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("lomza", get_person("Krzysztof Ryszard Kozicki", party="PiS", source_url="https://samorzad2024.pkw.gov.pl/samorzad2024/pl/wbp/kandydat/3469307", note="wójt Piątnicy"), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("lomza", get_person("Andrzej Borusiewicz", party="brak potwierdzonej afiliacji", source_url="https://pl.wikipedia.org/wiki/Andrzej_Borusiewicz", note="prorektor MANS w Łomży, wiceprezes ARiMR"), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("lomza", get_person("Karol Kossakowski", party="niepotwierdzona", source_url=src_lomza_rada, note="kandydat do sejmiku; rej. Zambrów"), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("lomza", get_person("Stanisław Kossakowski", party="brak danych", source_url=src_lomza_rada), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("lomza", get_person("Andrzej Szymański", party="brak danych", source_url=src_lomza_rada), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("lomza", get_person("Anna Malwina Szynkiewicz", party="brak danych", source_url=src_lomza_rada), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("lomza", get_person("Dorota Waszkiewicz", party="brak danych", source_url=src_lomza_rada), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("lomza", get_person("Renata Szydłowska", party="brak danych", source_url=src_lomza_rada), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("lomza", get_person("Milena Siok", party="brak danych", source_url=src_lomza_rada, note="NIEZWERYFIKOWANE"), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("lomza", get_person("Marcin Biedrzycki", party="brak danych", source_url=src_lomza_rada), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("lomza", get_person("Janusz Józef Cieciórski", party="brak danych", source_url=src_lomza_rada), "RADA_SPOLECZNA", "członek"))
|
||
|
||
# =========================================================================
|
||
# 8. Suwałki — Rydygiera
|
||
# =========================================================================
|
||
src_sw = "https://bip-spsws.podlaskie.eu/6b54de5acb7.html"
|
||
src_sw_rada1 = "https://samorzad.podlaskie.eu/radni/naszkiewicz-anna.html"
|
||
src_sw_rada2 = "https://www.portalsamorzadowy.pl/osoba/bogdan-dyjuk,2177.html"
|
||
hospitals.append(Hospital(id="suwalki_rundyger", name="Szpital Wojewódzki im. dr. Ludwika Rydygiera",
|
||
city="Suwałki", legal_form="SPZOZ", website=src_sw))
|
||
roles.append(Role("suwalki_rundyger", get_person("Adam Szałanda", party="brak/niepotwierdzona", source_url=src_sw), "DYREKTOR", "Dyrektor"))
|
||
roles.append(Role("suwalki_rundyger", get_person("lek. Włodzimierz Żaworonok", party="brak danych", source_url=src_sw), "Z_CA", "Z-ca ds. Lecznictwa"))
|
||
roles.append(Role("suwalki_rundyger", get_person("Agnieszka Kapczyńska", party="brak danych", source_url=src_sw), "Z_CA", "Gł. Księgowy"))
|
||
roles.append(Role("suwalki_rundyger", get_person("Anna Naszkiewicz", party="Koalicja Obywatelska", source_url=src_sw_rada1, note="radna Sejmiku; b. wicemarszałek"), "RADA_SPOLECZNA", "Przewodnicząca"))
|
||
roles.append(Role("suwalki_rundyger", get_person("Bogdan Dyjuk", party="Trzecia Droga (PSL–Polska 2050)", source_url=src_sw_rada2, note="radny Sejmiku, czł. Zarządu Woj."), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("suwalki_rundyger", get_person("Tadeusz Buchowiecki", party="brak/niepotwierdzona", source_url=src_sw, note="NIEZWERYFIKOWANE"), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("suwalki_rundyger", get_person("Wojciech Wasilewski", party="brak/niepotwierdzona", source_url=src_sw, note="NIEZWERYFIKOWANE"), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("suwalki_rundyger", get_person("Marta Żybura", party="Koalicja Obywatelska", source_url=src_sw, note="kandydatka KO do Rady Miejskiej 2024; NIEZWERYFIKOWANE"), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("suwalki_rundyger", get_person("Dariusz Bogdan", party="brak/niepotwierdzona", source_url="https://pl.wikipedia.org/wiki/Dariusz_Bogdan", note="b. wiceminister gospodarki; przynależność partyjna niepotwierdzona"), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("suwalki_rundyger", get_person("Joanna Jędzul", party="KWW Razem dla Suwałk", source_url=src_sw), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("suwalki_rundyger", get_person("Dorota Iwanowicz", party="Trzecia Droga", source_url="https://www.farmer.pl/wybory/osoba/dorota-iwanowicz,221972.html", note="radna gm. Suwałki; NIEZWERYFIKOWANE"), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("suwalki_rundyger", get_person("Daniel Leszek Makarewicz", party="KWW Suwalskie Porozumienie Prawicy", source_url=src_sw, note="NIEZWERYFIKOWANE"), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("suwalki_rundyger", get_person("Elżbieta Luto", party="brak/niepotwierdzona", source_url=src_sw), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("suwalki_rundyger", get_person("Justyna Drażba", party="brak/niepotwierdzona", source_url=src_sw), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("suwalki_rundyger", get_person("Grzegorz Paweł Matys", party="brak/niepotwierdzona", source_url=src_sw), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("suwalki_rundyger", get_person("Krystian Małyszko", party="brak/niepotwierdzona", source_url=src_sw), "RADA_SPOLECZNA", "członek"))
|
||
|
||
# =========================================================================
|
||
# 9. Suwałki — Psychiatryczny
|
||
# =========================================================================
|
||
src_spp_s = "https://bip-spspzozs.podlaskie.eu/"
|
||
src_spp_s_rada = "https://bip-spspzozs.podlaskie.eu/rada_spoleczna.html"
|
||
hospitals.append(Hospital(id="suwalki_psych", name="Specjalistyczny Psychiatryczny SP ZOZ",
|
||
city="Suwałki", legal_form="SPZOZ", website=src_spp_s))
|
||
roles.append(Role("suwalki_psych", get_person("Bożena Łapińska", party="brak/niepotwierdzona", source_url=src_spp_s), "DYREKTOR", "Dyrektor"))
|
||
roles.append(Role("suwalki_psych", get_person("lek. Michał Naczas", party="brak danych", source_url=src_spp_s), "Z_CA", "Z-ca ds. Lecznictwa"))
|
||
roles.append(Role("suwalki_psych", get_person("Marek Wasilewski", party="brak danych", source_url=src_spp_s), "Z_CA", "Gł. Księgowy"))
|
||
roles.append(Role("suwalki_psych", get_person("Władysław Renowicki", party="brak/niepotwierdzona", source_url=src_spp_s_rada), "RADA_SPOLECZNA", "Przewodniczący"))
|
||
roles.append(Role("suwalki_psych", get_person("Eliza Kupich", party="brak danych", source_url=src_spp_s_rada), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("suwalki_psych", get_person("Waldemar Kwaterski", party="Koalicja Obywatelska", source_url=src_spp_s_rada, note="radny Sejmiku; dyr. SP ZOZ Sejny; NIEZWERYFIKOWANE"), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("suwalki_psych", get_person("Jacek Niedźwiedzki", party="KO/PO", source_url=src_spp_s_rada, note="poseł z Suwałk; NIEZWERYFIKOWANE"), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("suwalki_psych", get_person("Elżbieta Baranowska", party="brak danych", source_url=src_spp_s_rada), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("suwalki_psych", get_person("Natalia Bogdan-Czarnowska", party="brak danych", source_url=src_spp_s_rada), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("suwalki_psych", get_person("Anna Maria Gawlińska", party="brak danych", source_url=src_spp_s_rada), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("suwalki_psych", get_person("Anna Kumiszcza", party="brak danych", source_url=src_spp_s_rada), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("suwalki_psych", get_person("Tomasz Romualdowski", party="brak danych", source_url=src_spp_s_rada), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("suwalki_psych", get_person("Bożena Jadwiga Chmielewska", party="brak danych", source_url=src_spp_s_rada), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("suwalki_psych", get_person("Dorota Iwanowicz", party="brak danych", source_url=src_spp_s_rada), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("suwalki_psych", get_person("Sławomir Jan Szeszko", party="brak danych", source_url=src_spp_s_rada), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("suwalki_psych", get_person("Aneta Jolanta Szmidt", party="brak danych", source_url=src_spp_s_rada), "RADA_SPOLECZNA", "członek"))
|
||
|
||
# =========================================================================
|
||
# 10. Augustów
|
||
# =========================================================================
|
||
src_a = "https://bip-spzoz-staugustow.podlaskie.eu/organy_wadze/kierownictwo_szpitala/"
|
||
src_a_rada = "https://bip-staugustow.podlaskie.eu/resource/116181/14.pdf/attachment.1"
|
||
src_a_dziennik = "https://www.dziennikpowiatowy.pl/artykul/13664"
|
||
hospitals.append(Hospital(id="augustow", name="SP ZOZ", city="Augustów",
|
||
legal_form="SPZOZ", website=src_a))
|
||
roles.append(Role("augustow", get_person("Adam Dębski", party="brak/niepotwierdzona", source_url=src_a, note="dyrektor od 1.09.2024"), "DYREKTOR", "Dyrektor"))
|
||
roles.append(Role("augustow", get_person("Wioletta Tomaszycka-Bednarczyk", party="brak danych", source_url=src_a), "Z_CA", "Z-ca ds. Lecznictwa"))
|
||
roles.append(Role("augustow", get_person("Piotr Rusiecki", party="PiS", source_url=src_a_rada, note="starosta augustowski, radny powiatu"), "RADA_SPOLECZNA", "Przewodniczący"))
|
||
roles.append(Role("augustow", get_person("Marek Dobkowski", party="KWW Samorząd Powiatu Augustowskiego", source_url=src_a_dziennik), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("augustow", get_person("Waldemar Jedliński", party="PiS", source_url=src_a_dziennik, note="członek Zarządu Powiatu"), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("augustow", get_person("Elżbieta Puczyłowska", party="PiS", source_url=src_a_dziennik, note="wiczeprzewodnicząca Rady Powiatu"), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("augustow", get_person("Alicja Dobrowolska", party="PiS", source_url=src_a_rada, note="członek Zarządu Powiatu"), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("augustow", get_person("Dariusz Jan Szkiłądź", party="KWW BAU 2024", source_url=src_a_dziennik), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("augustow", get_person("Jadwiga Józefa Milewska", party="brak danych", source_url=src_a_rada, note="NIEZWERYFIKOWANE"), "RADA_SPOLECZNA", "członek"))
|
||
|
||
# =========================================================================
|
||
# 11. Bielsk Podlaski
|
||
# =========================================================================
|
||
src_b = "https://www.spzoz-bielsk.pl/o-nas/dyrekcja/"
|
||
src_b_portal = "https://www.portalsamorzadowy.pl/powiat/bielski,295.html"
|
||
src_b_rada = "https://samorzad.gov.pl/web/powiat-bielski/ii-sesja-rady-powiatu-w-bielsku-podlaskim"
|
||
hospitals.append(Hospital(id="bielsk", name="SP ZOZ", city="Bielsk Podlaski",
|
||
legal_form="SPZOZ", website=src_b))
|
||
roles.append(Role("bielsk", get_person("Piotr Selwesiuk", party="KW Koalicja Bielska", source_url=src_b, note="wypowiedzenie z 29.06.2026"), "DYREKTOR", "Dyrektor"))
|
||
roles.append(Role("bielsk", get_person("dr n. med. Arsalan Azzaddin", party="brak/niepotwierdzona", source_url=src_b), "Z_CA", "Z-ca ds. Lecznictwa"))
|
||
roles.append(Role("bielsk", get_person("Piotr Bożko", party="KW Koalicja Bielska", source_url=src_b_rada, note="wicestarosta, radny powiatu"), "RADA_SPOLECZNA", "Przewodniczący"))
|
||
roles.append(Role("bielsk", get_person("Elżbieta Fionik", party="KW Koalicja Bielska", source_url=src_b_portal, note="radna powiatu"), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("bielsk", get_person("Maria Ryżyk", party="KW Koalicja Bielska", source_url=src_b_portal, note="radna powiatu"), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("bielsk", get_person("Andrzej Leszczyński", party="KWW Nasze Podlasie", source_url=src_b_portal, note="Przew. Rady Powiatu"), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("bielsk", get_person("Adam Łęczycki", party="KWW Nasze Podlasie", source_url=src_b_portal, note="radny powiatu"), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("bielsk", get_person("Zbigniew Jan Szpakowski", party="KWW Nasze Podlasie", source_url=src_b_portal, note="radny powiatu, czł. Zarządu"), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("bielsk", get_person("Jan Niewiński", party="PiS", source_url="https://www.portalsamorzadowy.pl/osoba/jan-niewinski,48951.html", note="radny powiatu"), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("bielsk", get_person("Walentyna Tyszko-Dubow", party="NIEZWERYFIKOWANE", source_url=src_b_rada, note="PO/KO z Hajnówki"), "RADA_SPOLECZNA", "członek"))
|
||
|
||
# =========================================================================
|
||
# 12. Hajnówka
|
||
# =========================================================================
|
||
src_h = "https://spzozhajnowka.pl/strona-3343-dyrekcja.html"
|
||
src_h_rada = "Uchwała III/13/2024 Rady Powiatu Hajnowskiego"
|
||
src_h_portal = "https://www.portalsamorzadowy.pl/"
|
||
hospitals.append(Hospital(id="hajnowka", name="SP ZOZ", city="Hajnówka",
|
||
legal_form="SPZOZ", website=src_h))
|
||
roles.append(Role("hajnowka", get_person("Grzegorz Tomaszuk", party="brak/niepotwierdzona", source_url=src_h), "DYREKTOR", "Dyrektor"))
|
||
roles.append(Role("hajnowka", get_person("Tomasz Musiuk", party="brak/niepotwierdzona", source_url=src_h), "Z_CA", "Z-ca ds. Lecznictwa"))
|
||
roles.append(Role("hajnowka", get_person("Andrzej Skiepko", party="KWW Ziemia Hajnowska", source_url=src_h_portal + "osoba/andrzej-skiepko,17764.html", note="Źródła rozbieżne: KWW Ziemia Hajnowska lub KWW Porozumienie Samorządowe"), "RADA_SPOLECZNA", "Przewodniczący"))
|
||
roles.append(Role("hajnowka", get_person("Jerzy Sirak", party="NIEZWERYFIKOWANE", source_url=src_h_portal + "osoba/jerzy-sirak,1302.html", note="rozbieżne: KO vs historycznie SLD; b. burmistrz"), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("hajnowka", get_person("Justyna Jarmocik", party="Trzecia Droga (PSL / Polska 2050)", source_url=src_h_portal + "powiat/hajnowski,350.html", note="Wicestarosta"), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("hajnowka", get_person("Albert Waldemar Litwinowicz", party="komitet niepotwierdzony", source_url=src_h_rada, note="wójt Białowieży"), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("hajnowka", get_person("Jerzy Wasiluk", party="możliwie Trzecia Droga", source_url=src_h_rada, note="wójt Czyż; do potwierdzenia"), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("hajnowka", get_person("Leon Małaszewski", party="KWW Ziemia Hajnowska", source_url=src_h_rada, note="wójt; niepotwierdzone partyjnie"), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("hajnowka", get_person("Lucyna Smoktunowicz", party="KWW Nasza Przyszłość; bezpartyjna", source_url="https://samorzad2024.pkw.gov.pl/samorzad2024/pl/wbp/kandydat/3412858", note="wójt"), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("hajnowka", get_person("Ireneusz Roman Kiendyś", party="KWW Hajnówka Razem; bezpartyjny", source_url="https://samorzad2024.pkw.gov.pl/samorzad2024/pl/wbp/kandydat/3459046", note="burmistrz"), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("hajnowka", get_person("Mirosław Markiewicz", party="KWW Roztropna Troska o Dobro Gminy", source_url="https://w.radio.bialystok.pl/wiadomosci/index/id/236847", note="burmistrz Kleszczele"), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("hajnowka", get_person("Aneta Leonowicz", party="KWW Narew Odnowa", source_url="https://samorzad2024.pkw.gov.pl/samorzad2024/pl/wbp/kandydat/3588266", note="wójt"), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("hajnowka", get_person("Jarosław Gołubowski", party="KWW Wspólna Narewka", source_url="https://samorzad2024.pkw.gov.pl/samorzad2024/pl/wbp/kandydat/3421407", note="wójt"), "RADA_SPOLECZNA", "członek"))
|
||
|
||
# =========================================================================
|
||
# 13. Siemiatycze
|
||
# =========================================================================
|
||
src_sm = "https://spzozsiemiatycze.pl/?page_id=395"
|
||
src_sm_radio = "https://www.radio.bialystok.pl/wiadomosci/index/id/238459"
|
||
src_sm_siem = "https://siemiatycze.podlasie24.pl/region/zarzad-powiatu-siemiatyckiego-z-absolutorium-za-rok-2023-20240615102143"
|
||
src_sm_portal = "https://www.portalsamorzadowy.pl/"
|
||
hospitals.append(Hospital(id="siemiatycze", name="SP ZOZ (Szpital Powiatowy)", city="Siemiatycze",
|
||
legal_form="SPZOZ", website=src_sm))
|
||
roles.append(Role("siemiatycze", get_person("Andrzej Szewczuk", party="brak/niepotwierdzona", source_url=src_sm, note="przejście do Choroszczy od VIII.2026"), "DYREKTOR", "Dyrektor"))
|
||
roles.append(Role("siemiatycze", get_person("Lech Zenon Hackiewicz", party="brak/niepotwierdzona", source_url=src_sm), "Z_CA", "Z-ca ds. Lecznictwa"))
|
||
roles.append(Role("siemiatycze", get_person("Mariusz Cieślik", party="PSL", source_url=src_sm_radio, note="wybrany z KWW Ruch Ludowy"), "RADA_SPOLECZNA", "Przewodniczący"))
|
||
roles.append(Role("siemiatycze", get_person("Sylwester Zgierun", party="PSL", source_url=src_sm_siem, note="KWW Ruch Ludowy"), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("siemiatycze", get_person("Mariusz Pyzowski", party="Koalicja Obywatelska", source_url=src_sm_portal + "osoba/mariusz-pyzowski,34169.html", note="Przew. Rady Powiatu"), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("siemiatycze", get_person("Jan Żeruń", party="KWW SPW", source_url=src_sm_siem.replace("2023-20240615102143", "")), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("siemiatycze", get_person("Edward Krasowski", party="KWW własny", source_url="https://samorzad2024.pkw.gov.pl/samorzad2024/pl/wbp/kandydat/3739772", note="wójt; afiliacja niepotwierdzona"), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("siemiatycze", get_person("Piotr Siniakowicz", party="PO/KO", source_url="https://pl.wikipedia.org/wiki/Piotr_Siniakowicz", note="KWW Dla Siemiatycz Razem"), "RADA_SPOLECZNA", "członek"))
|
||
|
||
# =========================================================================
|
||
# 14. Sokółka
|
||
# =========================================================================
|
||
src_sk = "https://szpitalsokolka.pl/dyrekcja/"
|
||
src_sk_radio = "https://www.radio.bialystok.pl/wiadomosci/index/id/228040"
|
||
src_sk_pkw = "https://samorzad2024.pkw.gov.pl/samorzad2024/pl/rada_powiatu/kandydat/3296821"
|
||
src_sk_radio2 = "https://www.radio.bialystok.pl/wiadomosci/index/id/237569"
|
||
hospitals.append(Hospital(id="sokolka", name="SP ZOZ", city="Sokółka",
|
||
legal_form="SPZOZ", website=src_sk))
|
||
roles.append(Role("sokolka", get_person("Anna Marta Aniśkiewicz", party="niepotwierdzona", source_url=src_sk_radio, note="KWW Razem dla Sokółki"), "DYREKTOR", "Dyrektor"))
|
||
roles.append(Role("sokolka", get_person("Adam Pietruczuk", party="brak/niepotwierdzona", source_url=src_sk), "Z_CA", "Z-ca ds. Lecznictwa"))
|
||
roles.append(Role("sokolka", get_person("Maria Julita Budrowska", party="brak/niepotwierdzona", source_url=src_sk), "Z_CA", "Z-ca ds. Ekonomicznych"))
|
||
roles.append(Role("sokolka", get_person("Piotr Rećko", party="PiS", source_url=src_sk_pkw, note="starosta sokólski, radny powiatu"), "RADA_SPOLECZNA", "Przewodniczący"))
|
||
roles.append(Role("sokolka", get_person("Cezary Możejko", party="NIEZWERYFIKOWANE", source_url=src_sk, note="możliwie wójt gm. Sidra; zbieżność nazwisk"), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("sokolka", get_person("Krzysztof Pawłowski", party="PiS", source_url=src_sk_radio2, note="radny powiatu, wiceprzew. Rady Powiatu"), "RADA_SPOLECZNA", "członek"))
|
||
|
||
# =========================================================================
|
||
# 15. Mońki
|
||
# =========================================================================
|
||
src_m = "https://spzoz.szpital-monki.h2.pl/kadra-zarzadzajaca/"
|
||
src_m_portal = "https://www.portalsamorzadowy.pl/powiat/moniecki,362.html"
|
||
hospitals.append(Hospital(id="monki", name="SP ZOZ", city="Mońki",
|
||
legal_form="SPZOZ", website=src_m))
|
||
roles.append(Role("monki", get_person("lek. med. Sebastian Wysocki", party="brak/niepotwierdzona", source_url=src_m), "DYREKTOR", "Dyrektor"))
|
||
roles.append(Role("monki", get_person("lek. med. Łucja Chojnowska", party="brak/niepotwierdzona", source_url=src_m, note="p.o. Z-ca ds. Lecznictwa"), "Z_CA", "Z-ca ds. Lecznictwa (p.o.)"))
|
||
roles.append(Role("monki", get_person("mgr Halina Skibicka", party="brak/niepotwierdzona", source_url=src_m), "Z_CA", "Z-ca ds. Pielęgniarstwa"))
|
||
roles.append(Role("monki", get_person("Barbara Lubecka", party="brak/niepotwierdzona", source_url=src_m, note="p.o. Gł. Księgowa"), "Z_CA", "Gł. Księgowa (p.o.)"))
|
||
roles.append(Role("monki", get_person("Joanna Kitlas", party="KWW BMN Aktywny Samorząd", source_url=src_m_portal, note="NIEZWERYFIKOWANE"), "RADA_SPOLECZNA", "Przewodnicząca"))
|
||
|
||
# =========================================================================
|
||
# 16. Grajewo
|
||
# =========================================================================
|
||
src_gr = "https://www.szpital-grajewo.pl/dyrekcja"
|
||
src_gr_portal = "https://www.portalsamorzadowy.pl/powiat/grajewski,329.html"
|
||
src_gr_biels = "https://bip.starostwograjewo.pl/612-kadencja-vii-2024-2029.html"
|
||
src_gr_e = "https://e-grajewo.pl/wiadomosc,barbara-ciszewska--kandydatka-na-burmistrza-grajewa,54754.html"
|
||
src_gr_grajewo = "https://www.grajewo.pl/pl/aktualnosci/maciej-pawel-bednarko-zaprzysiezony..."
|
||
hospitals.append(Hospital(id="grajewo", name="Szpital Ogólny im. dr. Witolda Ginela",
|
||
city="Grajewo", legal_form="SPZOZ", website=src_gr))
|
||
roles.append(Role("grajewo", get_person("mgr Marta Romanowska", party="brak/niepotwierdzona", source_url=src_gr), "DYREKTOR", "Dyrektor"))
|
||
roles.append(Role("grajewo", get_person("lek. med. Adam Leszczyński", party="brak/niepotwierdzona", source_url=src_gr, note="p.o. Z-ca ds. Lecznictwa"), "Z_CA", "Z-ca ds. Lecznictwa (p.o.)"))
|
||
roles.append(Role("grajewo", get_person("mgr Małgorzata Zdanewicz", party="brak/niepotwierdzona", source_url=src_gr), "Z_CA", "Gł. Księgowa"))
|
||
roles.append(Role("grajewo", get_person("mgr Beata Szyszkowska", party="brak/niepotwierdzona", source_url=src_gr), "Z_CA", "Naczelna Pielęgniarka"))
|
||
roles.append(Role("grajewo", get_person("Waldemar Remfeld", party="PiS", source_url=src_gr_portal, note="starosta grajewski, radny powiatu"), "RADA_SPOLECZNA", "Przewodniczący"))
|
||
roles.append(Role("grajewo", get_person("Milena Formejster", party="brak danych", source_url=src_gr), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("grajewo", get_person("Barbara Ciszewska", party="komitet 2024 niepotwierdzony", source_url=src_gr_e, note="b. radna powiatu; Stow. Nasza Przyszłość"), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("grajewo", get_person("Maciej Paweł Bednarko", party="KWW Macieja Pawła Bednarko", source_url=src_gr_grajewo, note="burmistrz Grajewa"), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("grajewo", get_person("Marek Kostrzewski", party="KWW Dobry Powiat", source_url=src_gr_biels, note="radny powiatu"), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("grajewo", get_person("Jerzy Jan Nikliński", party="brak danych", source_url=src_gr, note="NIEZWERYFIKOWANE"), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("grajewo", get_person("Bolesław Ruszczyk", party="PiS", source_url=src_gr_biels, note="radny powiatu"), "RADA_SPOLECZNA", "członek"))
|
||
|
||
# =========================================================================
|
||
# 17. Kolno
|
||
# =========================================================================
|
||
src_kol = "https://www.szpitalkolno.pl/struktura/dyrekcja/"
|
||
src_kol_portal = "https://www.portalsamorzadowy.pl/"
|
||
hospitals.append(Hospital(id="kolno", name="Szpital Ogólny w Kolnie",
|
||
city="Kolno", legal_form="SPZOZ", website=src_kol))
|
||
roles.append(Role("kolno", get_person("mgr Aneta Długozima", party="brak/niepotwierdzona", source_url=src_kol), "DYREKTOR", "Dyrektor"))
|
||
roles.append(Role("kolno", get_person("lek. Oksana Dowlaszewicz", party="brak/niepotwierdzona", source_url=src_kol, note="p.o. Z-ca ds. Lecznictwa"), "Z_CA", "p.o. Z-ca ds. Lecznictwa"))
|
||
roles.append(Role("kolno", get_person("mgr Izabela Górska", party="brak/niepotwierdzona", source_url=src_kol, note="p.o. Z-ca ds. Ekon.-Fin. / Gł. Księgowy"), "Z_CA", "p.o. Z-ca ds. Ekon.-Fin. / Gł. Księgowy"))
|
||
roles.append(Role("kolno", get_person("mgr Alicja Urbańska", party="brak/niepotwierdzona", source_url=src_kol), "Z_CA", "Przełożona Pielęgniarek"))
|
||
roles.append(Role("kolno", get_person("Tadeusz Klama", party="KWW Porozumienie Samorządowe Powiatu Kolno", source_url=src_kol_portal + "osoba/tadeusz-klama,58658.html", note="radny powiatu"), "RADA_SPOLECZNA", "Przewodniczący"))
|
||
roles.append(Role("kolno", get_person("Michał Kulczewski", party="KWW Porozumienie Samorządowe Powiatu Kolno", source_url=src_kol_portal + "osoba/michal-kulczewski,57069.html", note="radny powiatu"), "RADA_SPOLECZNA", "Wiceprzewodniczący"))
|
||
roles.append(Role("kolno", get_person("Marek Samul", party="PO", source_url=src_kol, note="b. starosta kolneński; NIEZWERYFIKOWANE"), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("kolno", get_person("Anita Krasińska", party="brak/niepotwierdzona", source_url=src_kol, note="kier. OPS Grabowo"), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("kolno", get_person("Beata Helena Muniak", party="brak/niepotwierdzona", source_url=src_kol, note="Sekretarz Gminy"), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("kolno", get_person("Robert Wacław Nadara", party="KWW Skuteczny i Przyjazny Samorząd", source_url=src_sk_radio2, note="radny powiatu"), "RADA_SPOLECZNA", "członek"))
|
||
roles.append(Role("kolno", get_person("Mariusz Rakowski", party="PiS", source_url=src_kol_portal + "osoba/mariusz-rakowski,106463.html", note="radny Rady Miasta Kolno"), "RADA_SPOLECZNA", "członek"))
|
||
|
||
# =========================================================================
|
||
# 18. Wysokie Mazowieckie
|
||
# =========================================================================
|
||
src_wm = "https://szpitalwysmaz.pl/pl/onas"
|
||
src_wm_portal = "https://www.portalsamorzadowy.pl/osoba/bogdan-zielinski,4307.html"
|
||
src_wm_pkw = "https://samorzad2024.pkw.gov.pl/samorzad2024/pl/wbp/kandydat/3739309"
|
||
hospitals.append(Hospital(id="wysokie", name="Szpital Ogólny w Wysokiem Mazowieckiem",
|
||
city="Wysokie Mazowieckie", legal_form="SPZOZ", website=src_wm))
|
||
roles.append(Role("wysokie", get_person("mgr Wojciech Łuczaj", party="PiS", source_url=src_wm_pkw, note="kandydat na burmistrza Czyżewa 2024 z KW PiS"), "DYREKTOR", "Dyrektor"))
|
||
roles.append(Role("wysokie", get_person("lek. Julita Nikołajuk-Stasiuk", party="brak/niepotwierdzona", source_url=src_wm), "Z_CA", "Z-ca ds. Lecznictwa"))
|
||
roles.append(Role("wysokie", get_person("mgr Aleksandra Łapiak", party="brak/niepotwierdzona", source_url=src_wm), "Z_CA", "Z-ca ds. Ekonomicznych"))
|
||
roles.append(Role("wysokie", get_person("mgr Anna Wądołkowska", party="brak/niepotwierdzona", source_url=src_wm), "Z_CA", "Przełożona Pielęgniarek"))
|
||
roles.append(Role("wysokie", get_person("Bogdan Zieliński", party="PiS", source_url=src_wm_portal, note="starosta, radny powiatu — NIEZWERYFIKOWANE czy przewodniczy"), "RADA_SPOLECZNA", "Przewodniczący"))
|
||
|
||
# =========================================================================
|
||
# 19. Sejny
|
||
# =========================================================================
|
||
src_se = "https://szpital.sejny.pl/kadra"
|
||
src_se_radio = "https://www.radio.bialystok.pl/gosc/suwalki/id/202941"
|
||
src_se_suwalki = "https://www.suwalki24.pl/article/63,powiat-sejnenski-maciej-plesiewicz-nadal-starosta..."
|
||
hospitals.append(Hospital(id="sejny", name="SP ZOZ (Szpital Powiatowy)",
|
||
city="Sejny", legal_form="SPZOZ", website=src_se))
|
||
roles.append(Role("sejny", get_person("Waldemar Kwaterski", party="Koalicja Obywatelska", source_url=src_se_radio, note="radny Sejmiku od 2010, przew. Komisji Zdrowia"), "DYREKTOR", "Dyrektor"))
|
||
roles.append(Role("sejny", get_person("Urszula Ordowska", party="brak/niepotwierdzona", source_url=src_se), "Z_CA", "Z-ca ds. Lecznictwa"))
|
||
roles.append(Role("sejny", get_person("Maciej Plesiewicz", party="KW Ziemia Sejneńska", source_url=src_se_suwalki, note="starosta; NIEZWERYFIKOWANE czy przewodniczy"), "RADA_SPOLECZNA", "Przewodniczący"))
|
||
|
||
# =========================================================================
|
||
# 20. Zambrow — SPÓŁKA
|
||
# =========================================================================
|
||
src_zb = "https://aleo.com/int/company/szpital-powiatowy-w-zambrowie"
|
||
src_zb_rada = "https://zambrowiacy.pl/info/bartosz-ziemianowicz/"
|
||
hospitals.append(Hospital(id="zambrow", name="Szpital Powiatowy w Zambrowie sp. z o.o.",
|
||
city="Zambrow", legal_form="sp. z o.o.", website=src_zb,
|
||
notes="Jedyny szpital jako spółka handlowa. KRS 0000130730"))
|
||
roles.append(Role("zambrow", get_person("Kamil Kowaleczko", party="brak/niepotwierdzona", source_url=src_zb, note="Prezes Zarządu od 16.07.2025"), "PREZES_ZARZADU", "Prezes Zarządu"))
|
||
roles.append(Role("zambrow", get_person("Bartosz Rafał Ziemianowicz", party="NIEZWERYFIKOWANE", source_url=src_zb_rada, note="b. radny miasta; historycznie PO; PO vs PiS"), "RADA_NADZORCZA", "członek"))
|
||
roles.append(Role("zambrow", get_person("Krzysztof Rodzik", party="brak danych", source_url=src_zb), "RADA_NADZORCZA", "członek"))
|
||
roles.append(Role("zambrow", get_person("Michał Kulesza", party="brak danych", source_url=src_zb), "RADA_NADZORCZA", "członek"))
|
||
roles.append(Role("zambrow", get_person("Patrycja Magdalena Bugaj", party="brak danych", source_url=src_zb), "RADA_NADZORCZA", "członek"))
|
||
|
||
# =========================================================================
|
||
# Deduplicate persons across all hospitals
|
||
# =========================================================================
|
||
persons = []
|
||
party_set = {} # canonical_name -> Party
|
||
person_party_map = {} # person name -> list of (display_name, slug)
|
||
for uid_map in persons_map.values():
|
||
for p in uid_map.values():
|
||
persons.append(p)
|
||
# Collect party slugs
|
||
for slug in p.party_slugs:
|
||
if slug not in party_set:
|
||
party_name, _ = normalise_party(slug)
|
||
party_set[slug] = Party(slug=slug, name=party_name)
|
||
# Parse party_note for additional parties
|
||
if p.party_note and p.party_note not in ("brak/niepotwierdzona", "brak danych", "NIEZWERYFIKOWANE",
|
||
"niepotwierdzona", "brak potwierdzonej afiliacji",
|
||
"komitet niepotwierdzony", "histor"):
|
||
_, slug = normalise_party(p.party_note)
|
||
if slug not in party_set and not any(s.isdigit() for s in slug):
|
||
party_name, _ = normalise_party(p.party_note)
|
||
party_set[slug] = Party(slug=slug, name=party_name)
|
||
person_party_map.setdefault(p.name, []).append((p.party_note, slug))
|
||
|
||
# Deduplicate party nodes: merge entries with same canonical name but different slugs
|
||
# Keep the "best" slug (shortest and most canonical) for each canonical name
|
||
best_slug_for_name = {}
|
||
for slug, party in party_set.items():
|
||
canonical = party.name.lower()
|
||
if canonical not in best_slug_for_name or len(slug) < len(best_slug_for_name[canonical]):
|
||
best_slug_for_name[canonical] = slug
|
||
|
||
# Build a mapping: old_slug -> best_slug
|
||
slug_mapping = {}
|
||
for old_slug, party in party_set.items():
|
||
canonical = party.name.lower()
|
||
if canonical in best_slug_for_name:
|
||
slug_mapping[old_slug] = best_slug_for_name[canonical]
|
||
|
||
# Replace slugs in persons
|
||
for uid_map in persons_map.values():
|
||
for p in uid_map.values():
|
||
new_slugs = []
|
||
for slug in p.party_slugs:
|
||
new_slug = slug_mapping.get(slug, slug)
|
||
if new_slug not in new_slugs:
|
||
new_slugs.append(new_slug)
|
||
# Also add slugs from party_note
|
||
if p.party_note and p.party_note not in ("brak/niepotwierdzona", "brak danych", "NIEZWERYFIKOWANE",
|
||
"niepotwierdzona", "brak potwierdzonej afiliacji",
|
||
"komitet niepotwierdzony", "histor"):
|
||
_, slug = normalise_party(p.party_note)
|
||
new_slug = slug_mapping.get(slug, slug)
|
||
if new_slug and new_slug not in new_slugs:
|
||
new_slugs.append(new_slug)
|
||
p.party_slugs = new_slugs
|
||
|
||
# Build final party list using only the "best" slugs
|
||
final_parties = {}
|
||
for slug, party in party_set.items():
|
||
canonical = party.name.lower()
|
||
if canonical in best_slug_for_name and best_slug_for_name[canonical] == slug:
|
||
final_parties[slug] = party
|
||
parties = list(final_parties.values())
|
||
|
||
print(f"Collected {len(hospitals)} hospitals, {len(persons)} unique persons, {len(roles)} roles, {len(parties)} deduplicated parties")
|
||
return hospitals, persons, roles, parties
|
||
|
||
|
||
# =========================================================================
|
||
# Main
|
||
# =========================================================================
|
||
|
||
def main():
|
||
print("=" * 60)
|
||
print("Hospital Graph Builder — Podlaskie Voivodeship")
|
||
print("=" * 60)
|
||
|
||
# Step 1: Build data
|
||
print("\n[1/5] Building graph data from research...")
|
||
hospitals, persons, roles, parties = build_data()
|
||
|
||
# Step 2: Connect to Neo4j
|
||
print("[2/5] Connecting to Neo4j...")
|
||
driver = _connect()
|
||
print(f" ✓ Connected to {os.getenv('NEO4J_URI', 'bolt://localhost:7687')}")
|
||
|
||
# Step 3: Load data
|
||
print("[3/5] Loading data into Neo4j...")
|
||
# Clear existing data
|
||
with driver.session() as session:
|
||
session.execute_write(_clear_database)
|
||
load_hospitals(driver, hospitals)
|
||
load_parties(driver, parties)
|
||
load_persons(driver, persons)
|
||
load_roles(driver, roles, {p.uid: p for p in persons})
|
||
print(" ✓ Data loaded successfully")
|
||
|
||
# Step 4: Validate
|
||
print("[4/5] Validating graph...")
|
||
validate(driver)
|
||
|
||
# Export
|
||
print("[5/5] Exporting reports...")
|
||
export_overlap_csv(driver)
|
||
print(" ↓ overlap_report.csv")
|
||
|
||
# Create indexes (for performance)
|
||
with driver.session() as session:
|
||
session.execute_write(_create_indexes)
|
||
|
||
driver.close()
|
||
print("\nDone! Graph has been built in Neo4j.")
|
||
print("Access Neo4j Browser at: http://localhost:7474")
|
||
print(" Username: neo4j")
|
||
print(" Password: graphdb123")
|
||
|
||
|
||
def _clear_database(tx):
|
||
tx.run("MATCH (n) DETACH DELETE n")
|
||
|
||
|
||
def _create_indexes(tx):
|
||
tx.run("CREATE INDEX hospital_uid IF NOT EXISTS FOR (h:Hospital) ON (h.uid)")
|
||
tx.run("CREATE INDEX person_uid IF NOT EXISTS FOR (p:Person) ON (p.uid)")
|
||
tx.run("CREATE INDEX person_normname IF NOT EXISTS FOR (p:Person) ON (p.normName)")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|