#!/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()