Some checks are pending
NordaBiz Tests / Unit & Integration Tests (push) Waiting to run
NordaBiz Tests / E2E Tests (Playwright) (push) Blocked by required conditions
NordaBiz Tests / Smoke Tests (Production) (push) Blocked by required conditions
NordaBiz Tests / Send Failure Notification (push) Blocked by required conditions
- Display up to 3 next events with RSVP status instead of just one - Add import script for WhatsApp Norda group data (Feb 2026): events, company updates, Alter Energy, Croatia announcement Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
350 lines
15 KiB
Python
350 lines
15 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Import danych z WhatsApp grupy Norda Biznes (4-11.02.2026)
|
|
|
|
Zawiera:
|
|
- Nowe wydarzenia: Szkolenie KSeF 23.02, Chwila dla Biznesu 26.02 (jeśli brak)
|
|
- Update speaker info: Śniadanie 20.02 (ID=43)
|
|
- Uzupełnienie opisów firm: Fiume Studio (128), PG Construction (49), Green House Systems (24)
|
|
- Nowa firma: Alter Energy (child brand pod Fiume Studio)
|
|
- UserCompany: Irmina (user 67) → Alter Energy
|
|
- Ogłoszenie: Chorwacja pod Żaglami
|
|
|
|
Użycie:
|
|
python3 scripts/import_whatsapp_feb2026.py # dry-run
|
|
python3 scripts/import_whatsapp_feb2026.py --apply # zapis do bazy
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
from pathlib import Path
|
|
from datetime import date, time, datetime
|
|
|
|
sys.path.insert(0, str(Path(__file__).parent.parent))
|
|
|
|
from sqlalchemy import create_engine, text
|
|
from sqlalchemy.orm import sessionmaker
|
|
|
|
|
|
def load_env():
|
|
"""Załaduj .env z katalogu projektu."""
|
|
env_path = Path(__file__).parent.parent / '.env'
|
|
if env_path.exists():
|
|
with open(env_path, 'r') as f:
|
|
for line in f:
|
|
line = line.strip()
|
|
if line and not line.startswith('#') and '=' in line:
|
|
key, value = line.split('=', 1)
|
|
os.environ.setdefault(key.strip(), value.strip())
|
|
|
|
|
|
load_env()
|
|
|
|
DATABASE_URL = os.getenv('DATABASE_URL', 'postgresql://nordabiz_app:CHANGE_ME@127.0.0.1:5432/nordabiz')
|
|
|
|
SOURCE = 'whatsapp_norda_feb2026'
|
|
|
|
|
|
def main():
|
|
dry_run = '--apply' not in sys.argv
|
|
|
|
print("=" * 70)
|
|
print("IMPORT: WhatsApp Norda Biznes (4-11.02.2026)")
|
|
print(f"MODE: {'DRY-RUN (brak zapisu)' if dry_run else 'APPLY (zapis do bazy!)'}")
|
|
print("=" * 70)
|
|
|
|
engine = create_engine(DATABASE_URL)
|
|
Session = sessionmaker(bind=engine)
|
|
session = Session()
|
|
|
|
stats = {'added': 0, 'updated': 0, 'skipped': 0}
|
|
|
|
try:
|
|
# ============================================================
|
|
# 1. WYDARZENIA
|
|
# ============================================================
|
|
print("\n--- 1. WYDARZENIA ---\n")
|
|
|
|
# 1a. Szkolenie KSeF 23.02
|
|
existing = session.execute(
|
|
text("SELECT id FROM norda_events WHERE event_date = :d AND title ILIKE :t"),
|
|
{"d": date(2026, 2, 23), "t": "%KSeF%"}
|
|
).fetchone()
|
|
|
|
if existing:
|
|
print(f" SKIP: Szkolenie KSeF 23.02 juz istnieje (ID={existing[0]})")
|
|
stats['skipped'] += 1
|
|
else:
|
|
print(" + ADD: Szkolenie KSeF 23.02")
|
|
if not dry_run:
|
|
session.execute(text("""
|
|
INSERT INTO norda_events
|
|
(title, description, event_type, event_date, time_start, time_end,
|
|
location, source, source_note, access_level, created_at)
|
|
VALUES (:title, :desc, :etype, :edate, :tstart, :tend,
|
|
:loc, :src, :src_note, :access, NOW())
|
|
"""), {
|
|
"title": "Szkolenie — Zmiany podatkowe i KSeF 2026",
|
|
"desc": "Szkolenie z zakresu zmian podatkowych obowiazujacych od nowego roku oraz KSeF. "
|
|
"Dostepne dla czlonkow Izby NORDA, ich pracownikow i osob prowadzacych rozliczenia ksiegowe. "
|
|
"Firmy spoza Izby — koszt 250 zl/os. Kontakt: sekretariat Nordy (Magda).",
|
|
"etype": "meeting",
|
|
"edate": date(2026, 2, 23),
|
|
"tstart": time(9, 0),
|
|
"tend": time(14, 30),
|
|
"loc": "Urzad Miejski Wejherowo, ul. 12 Marca, ostatnie pietro",
|
|
"src": SOURCE,
|
|
"src_note": "WhatsApp grupa Norda, Roman, 8.02.2026",
|
|
"access": "members_only",
|
|
})
|
|
stats['added'] += 1
|
|
|
|
# 1b. Chwila dla Biznesu 26.02 — sprawdz czy istnieje
|
|
existing = session.execute(
|
|
text("SELECT id FROM norda_events WHERE event_date = :d AND title ILIKE :t"),
|
|
{"d": date(2026, 2, 26), "t": "%Chwila%"}
|
|
).fetchone()
|
|
|
|
if existing:
|
|
print(f" SKIP: Chwila dla Biznesu 26.02 juz istnieje (ID={existing[0]})")
|
|
stats['skipped'] += 1
|
|
else:
|
|
print(" + ADD: Chwila dla Biznesu 26.02")
|
|
if not dry_run:
|
|
session.execute(text("""
|
|
INSERT INTO norda_events
|
|
(title, description, event_type, event_date, time_start,
|
|
location, source, source_note, access_level, created_at)
|
|
VALUES (:title, :desc, :etype, :edate, :tstart,
|
|
:loc, :src, :src_note, :access, NOW())
|
|
"""), {
|
|
"title": "Chwila dla Biznesu — luty 2026",
|
|
"desc": "Comiesięczne spotkanie networkingowe członków Izby NORDA.",
|
|
"etype": "networking",
|
|
"edate": date(2026, 2, 26),
|
|
"tstart": time(19, 0),
|
|
"loc": "Hotel Olimp, V piętro, Wejherowo",
|
|
"src": SOURCE,
|
|
"src_note": "WhatsApp grupa Norda, Roman, 11.02.2026",
|
|
"access": "members_only",
|
|
})
|
|
stats['added'] += 1
|
|
|
|
# 1c. Śniadanie 20.02 (ID=43) — UPDATE speaker info
|
|
event43 = session.execute(
|
|
text("SELECT id, speaker_name, speaker_company_id FROM norda_events WHERE id = 43")
|
|
).fetchone()
|
|
|
|
if not event43:
|
|
print(" WARN: Wydarzenie ID=43 nie istnieje!")
|
|
elif event43[1] and event43[2]:
|
|
print(f" SKIP: Sniadanie ID=43 ma juz speaker: {event43[1]} (company_id={event43[2]})")
|
|
stats['skipped'] += 1
|
|
else:
|
|
print(f" ~ UPD: Sniadanie ID=43 — speaker: Alicja Domachowska, company_id=40 (Lean Idea)")
|
|
if not dry_run:
|
|
session.execute(text("""
|
|
UPDATE norda_events
|
|
SET speaker_name = :speaker, speaker_company_id = :cid
|
|
WHERE id = 43
|
|
"""), {"speaker": "Alicja Domachowska", "cid": 40})
|
|
stats['updated'] += 1
|
|
|
|
# ============================================================
|
|
# 2. UZUPEŁNIENIE OPISÓW FIRM
|
|
# ============================================================
|
|
print("\n--- 2. UZUPELNIENIE OPISOW FIRM ---\n")
|
|
|
|
company_updates = [
|
|
{
|
|
"id": 128,
|
|
"name": "Fiume Studio",
|
|
"description_short": "Fiume Studio — atelier kulinarne i sala eventowa w Redzie. Warsztaty kulinarne, eventy firmowe, catering.",
|
|
"services_offered": "Warsztaty kulinarne, sala bankietowa, eventy firmowe, warsztaty pizzy, catering",
|
|
},
|
|
{
|
|
"id": 49,
|
|
"name": "PG Construction",
|
|
"description_short": "Projektowanie, nadzór budowlany, kompleksowa obsługa inwestycji budowlanych, obsługa deweloperów.",
|
|
"services_offered": "Projektowanie budowlane, nadzór budowlany, kompleksowa obsługa inwestycji budowlanych, obsługa deweloperów",
|
|
},
|
|
{
|
|
"id": 24,
|
|
"name": "Green House Systems",
|
|
"description_short": "Firma instalacyjna — wod-kan, ogrzewanie podłogowe, rekuperacja, klimatyzacja, instalacje elektryczne z automatyką budynkową, fotowoltaika z magazynem energii.",
|
|
"services_offered": "Instalacje wod-kan, ogrzewanie podłogowe, rekuperacja, klimatyzacja, instalacje elektryczne, automatyka budynkowa, fotowoltaika, magazyny energii",
|
|
"website": "https://www.greenhousesystems.pl",
|
|
},
|
|
]
|
|
|
|
for cu in company_updates:
|
|
row = session.execute(
|
|
text("SELECT id, name, description_short, services_offered, website FROM companies WHERE id = :id"),
|
|
{"id": cu["id"]}
|
|
).fetchone()
|
|
|
|
if not row:
|
|
print(f" WARN: Firma ID={cu['id']} ({cu['name']}) nie istnieje!")
|
|
continue
|
|
|
|
updates = []
|
|
params = {"id": cu["id"]}
|
|
|
|
# description_short — update only if empty
|
|
if not row[2] or row[2].strip() == '':
|
|
updates.append("description_short = :desc_short")
|
|
params["desc_short"] = cu["description_short"]
|
|
else:
|
|
print(f" SKIP: {cu['name']} (ID={cu['id']}) — description_short juz wypelnione")
|
|
|
|
# services_offered — update only if empty
|
|
if not row[3] or row[3].strip() == '':
|
|
updates.append("services_offered = :services")
|
|
params["services"] = cu["services_offered"]
|
|
else:
|
|
print(f" SKIP: {cu['name']} (ID={cu['id']}) — services_offered juz wypelnione")
|
|
|
|
# website — update only if empty and provided
|
|
if "website" in cu:
|
|
if not row[4] or row[4].strip() == '':
|
|
updates.append("website = :website")
|
|
params["website"] = cu["website"]
|
|
else:
|
|
print(f" SKIP: {cu['name']} (ID={cu['id']}) — website juz wypelnione")
|
|
|
|
if updates:
|
|
set_clause = ", ".join(updates)
|
|
print(f" ~ UPD: {cu['name']} (ID={cu['id']}) — {', '.join(u.split(' =')[0] for u in updates)}")
|
|
if not dry_run:
|
|
session.execute(
|
|
text(f"UPDATE companies SET {set_clause}, last_updated = NOW() WHERE id = :id"),
|
|
params
|
|
)
|
|
stats['updated'] += 1
|
|
else:
|
|
stats['skipped'] += 1
|
|
|
|
# ============================================================
|
|
# 3. NOWA FIRMA: Alter Energy
|
|
# ============================================================
|
|
print("\n--- 3. NOWA FIRMA: Alter Energy ---\n")
|
|
|
|
existing_alter = session.execute(
|
|
text("SELECT id FROM companies WHERE slug = 'alter-energy'")
|
|
).fetchone()
|
|
|
|
alter_energy_id = None
|
|
|
|
if existing_alter:
|
|
alter_energy_id = existing_alter[0]
|
|
print(f" SKIP: Alter Energy juz istnieje (ID={alter_energy_id})")
|
|
stats['skipped'] += 1
|
|
else:
|
|
print(" + ADD: Alter Energy (child brand pod Fiume Studio, ID=128)")
|
|
if not dry_run:
|
|
result = session.execute(text("""
|
|
INSERT INTO companies
|
|
(name, slug, phone, parent_company_id, status, data_quality, data_source, created_at, last_updated)
|
|
VALUES (:name, :slug, :phone, :parent_id, :status, :dq, :ds, NOW(), NOW())
|
|
RETURNING id
|
|
"""), {
|
|
"name": "Alter Energy",
|
|
"slug": "alter-energy",
|
|
"phone": "508259086",
|
|
"parent_id": 128,
|
|
"status": "active",
|
|
"dq": "basic",
|
|
"ds": SOURCE,
|
|
})
|
|
alter_energy_id = result.fetchone()[0]
|
|
print(f" → ID={alter_energy_id}")
|
|
stats['added'] += 1
|
|
|
|
# 3b. UserCompany: Irmina (user 67) → Alter Energy
|
|
if alter_energy_id:
|
|
existing_uc = session.execute(
|
|
text("SELECT id FROM user_companies WHERE user_id = 67 AND company_id = :cid"),
|
|
{"cid": alter_energy_id}
|
|
).fetchone()
|
|
|
|
if existing_uc:
|
|
print(f" SKIP: UserCompany Irmina(67) → Alter Energy({alter_energy_id}) juz istnieje")
|
|
stats['skipped'] += 1
|
|
else:
|
|
print(f" + ADD: UserCompany Irmina(67) → Alter Energy({alter_energy_id}), role=MANAGER, is_primary=false")
|
|
if not dry_run:
|
|
session.execute(text("""
|
|
INSERT INTO user_companies (user_id, company_id, role, is_primary, created_at, updated_at)
|
|
VALUES (67, :cid, 'MANAGER', false, NOW(), NOW())
|
|
"""), {"cid": alter_energy_id})
|
|
stats['added'] += 1
|
|
else:
|
|
if dry_run:
|
|
print(" (dry-run) UserCompany Irmina → Alter Energy zostanie dodane przy --apply")
|
|
|
|
# ============================================================
|
|
# 4. OGŁOSZENIE: Chorwacja pod Żaglami
|
|
# ============================================================
|
|
print("\n--- 4. OGLOSZENIE: Chorwacja pod Zaglami ---\n")
|
|
|
|
existing_ann = session.execute(
|
|
text("SELECT id FROM announcements WHERE slug = 'chorwacja-pod-zaglami'")
|
|
).fetchone()
|
|
|
|
if existing_ann:
|
|
print(f" SKIP: Ogłoszenie 'Chorwacja pod Zaglami' juz istnieje (ID={existing_ann[0]})")
|
|
stats['skipped'] += 1
|
|
else:
|
|
# Find admin user for created_by
|
|
admin_user = session.execute(
|
|
text("SELECT id FROM users WHERE is_admin = true ORDER BY id LIMIT 1")
|
|
).fetchone()
|
|
admin_id = admin_user[0] if admin_user else 1
|
|
|
|
print(" + ADD: Ogloszenie 'Chorwacja pod Zaglami'")
|
|
if not dry_run:
|
|
session.execute(text("""
|
|
INSERT INTO announcements
|
|
(title, slug, excerpt, content, categories, status, published_at, created_by, created_at, updated_at)
|
|
VALUES (:title, :slug, :excerpt, :content, :cats, 'published', NOW(), :created_by, NOW(), NOW())
|
|
"""), {
|
|
"title": "Chorwacja pod Żaglami — wyjazd integracyjny",
|
|
"slug": "chorwacja-pod-zaglami",
|
|
"excerpt": "Pomysł Prezesa Leszka Glazy — wspólny wyjazd pod żaglami do Chorwacji. Kontakt: sekretariat Nordy (Magda) lub Prezes.",
|
|
"content": "<p>Zapraszamy na wyjazd integracyjny do Chorwacji pod żaglami! Pomysł naszego Prezesa Leszka Glazy.</p>"
|
|
"<p>Poprzednie wspólne wyjazdy (USA, Hawaje) były wielkim sukcesem. "
|
|
"Szczegóły do uzgodnienia — prosimy o kontakt z sekretariatem Nordy (Magda) lub bezpośrednio z Prezesem.</p>",
|
|
"cats": ["event"],
|
|
"created_by": admin_id,
|
|
})
|
|
stats['added'] += 1
|
|
|
|
# ============================================================
|
|
# COMMIT
|
|
# ============================================================
|
|
if not dry_run:
|
|
session.commit()
|
|
print("\n>>> COMMIT OK")
|
|
|
|
# ============================================================
|
|
# PODSUMOWANIE
|
|
# ============================================================
|
|
print("\n" + "=" * 70)
|
|
print(f"PODSUMOWANIE: dodano={stats['added']}, zaktualizowano={stats['updated']}, pominieto={stats['skipped']}")
|
|
if dry_run:
|
|
print("[DRY-RUN] Zadne dane NIE zostaly zapisane. Uzyj --apply aby zapisac.")
|
|
print("=" * 70)
|
|
|
|
except Exception as e:
|
|
session.rollback()
|
|
print(f"\nBLAD: {e}")
|
|
import traceback
|
|
traceback.print_exc()
|
|
sys.exit(1)
|
|
finally:
|
|
session.close()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|