This commit is contained in:
2026-06-18 09:31:33 +02:00
parent 2b3e259de1
commit 18d3d74449
8 changed files with 1202 additions and 0 deletions
+216
View File
@@ -0,0 +1,216 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
sipiq_report_v1.0.py
====================
Verze: 1.0
Datum: 2026-06-17
Autor: Claude Code (pro MUDr. Vladimíra Buzalku)
Popis
-----
Přehledný Excel report SIPIQ odpovědí: OTÁZKY ve sloupci A (v pořadí SIPIQ, seskupené
dle sekcí), CENTRA ve sloupcích (B+). Buňka = odpověď daného centra na danou otázku.
Tj. vyplněné SIPIQ všech center vedle sebe (= transponovaná rekonstrukce dotazníku).
Zdroj: MongoDB feasibility (sipiq_questions = slovník + pořadí + sekce + items;
sipiq_responses = odpovědi + answers_supplement z doplnujici_dotazy).
Layout:
- hlavička 2 řádky: příjmení PI + název centra (a země)
- blok IDENTITA (země/město/PI/e-mail/sdl_site_id/datum vyplnění)
- sekce SIPIQ jako barevné mezititulky, otázky a položky matic v řádcích
- Yes = zelená, No = červená; doplněné odpovědi (answers_supplement) = oranžová + kurzíva + komentář
- freeze panes (sloupec A + hlavička), ohraničení, vhodné šířky
Výstup: u:\\Dropbox\\!!!Days\\Downloads Z230\\SIPIQ_prehled_center_<ts>.xlsx
Závislosti: pymongo, openpyxl (.venv). Mongo 192.168.1.76:27017, bez auth.
"""
import os
import re
from datetime import datetime
from pymongo import MongoClient
from openpyxl import Workbook
from openpyxl.styles import Font, PatternFill, Alignment, Border, Side
from openpyxl.comments import Comment
from openpyxl.utils import get_column_letter
MONGO_URI = "mongodb://192.168.1.76:27017"
DB = "feasibility"
OUT_DIR = r"u:\Dropbox\!!!Days\Downloads Z230"
SKIP_SECTIONS = {"Confidentiality Statement"} # dlouhé právní souhlasy, ne data
COUNTRY_ABBR = {"Czech Republic": "CZ", "Slovakia": "SK"}
# barvy
C_SECTION = PatternFill("solid", fgColor="1F4E78") # tmavě modrá
C_STEM = PatternFill("solid", fgColor="DDEBF7") # světle modrá
C_IDENT = PatternFill("solid", fgColor="F2F2F2") # světle šedá
C_HEAD = PatternFill("solid", fgColor="305496") # hlavička center
C_YES = PatternFill("solid", fgColor="C6EFCE") # zelená
C_NO = PatternFill("solid", fgColor="FFC7CE") # červená
C_SUPP = PatternFill("solid", fgColor="FFE699") # oranžová (doplněno)
THIN = Side(style="thin", color="BFBFBF")
BORDER = Border(left=THIN, right=THIN, top=THIN, bottom=THIN)
WRAP_TOP = Alignment(wrap_text=True, vertical="top")
WRAP_CTR = Alignment(wrap_text=True, vertical="center", horizontal="center")
def fmt_date(s):
if not s:
return ""
try:
return datetime.strptime(str(s)[:10], "%Y-%m-%d").strftime("%d%b%Y").upper()
except Exception:
return str(s)[:10]
def main():
client = MongoClient(MONGO_URI, serverSelectionTimeoutMS=8000)
db = client[DB]
client.admin.command("ping")
questions = list(db.sipiq_questions.find().sort("order", 1))
resp = list(db.sipiq_responses.find())
# seřadit centra: CZ -> SK, pak příjmení
resp.sort(key=lambda r: (COUNTRY_ABBR.get(r.get("site_country"), "ZZ"),
(r.get("pi_last_name") or "").lower()))
n = len(resp)
print(f"Otázek: {len(questions)} | center: {n}")
wb = Workbook()
ws = wb.active
ws.title = "SIPIQ přehled"
# --- hlavička center (2 řádky) ---
ws.cell(1, 1, "OTÁZKA \\ CENTRUM").font = Font(bold=True, color="FFFFFF")
ws.cell(2, 1, "").font = Font(bold=True, color="FFFFFF")
ws.merge_cells("A1:A2")
for j, r in enumerate(resp):
col = 2 + j
c1 = ws.cell(1, col, r.get("pi_last_name") or "?")
cc = COUNTRY_ABBR.get(r.get("site_country"), "?")
c2 = ws.cell(2, col, f"{r.get('site_name') or ''} [{cc}]")
for c in (c1, c2):
c.font = Font(bold=True, color="FFFFFF", size=9)
c.fill = C_HEAD
c.alignment = WRAP_CTR
c.border = BORDER
ws.cell(1, 1).fill = C_HEAD
ws.cell(1, 1).alignment = WRAP_CTR
row = 3
def ident_row(label, getter):
nonlocal row
a = ws.cell(row, 1, label)
a.font = Font(bold=True)
a.fill = C_IDENT
a.alignment = WRAP_TOP
a.border = BORDER
for j, r in enumerate(resp):
c = ws.cell(row, 2 + j, getter(r))
c.fill = C_IDENT
c.alignment = WRAP_TOP
c.border = BORDER
row += 1
# --- IDENTITA ---
sec = ws.cell(row, 1, "■ IDENTITA CENTRA")
sec.font = Font(bold=True, color="FFFFFF")
sec.fill = C_SECTION
for j in range(n):
ws.cell(row, 2 + j).fill = C_SECTION
row += 1
ident_row("Země", lambda r: COUNTRY_ABBR.get(r.get("site_country"), r.get("site_country") or ""))
ident_row("Město", lambda r: r.get("site_city") or "")
ident_row("PI", lambda r: f"{r.get('pi_first_name') or ''} {r.get('pi_last_name') or ''}".strip())
ident_row("E-mail PI", lambda r: r.get("pi_email") or "")
ident_row("sdl_site_id", lambda r: r.get("sdl_site_id") or "")
ident_row("Datum vyplnění", lambda r: fmt_date((r.get("meta") or {}).get("recorded_date")))
# --- OTÁZKY po sekcích ---
cur_section = None
for q in questions:
section = q.get("section") or "Other"
if section in SKIP_SECTIONS:
continue
if section != cur_section:
cur_section = section
sc = ws.cell(row, 1, f"{section.upper()}")
sc.font = Font(bold=True, color="FFFFFF")
sc.fill = C_SECTION
sc.alignment = WRAP_TOP
sc.border = BORDER
for j in range(n):
cc = ws.cell(row, 2 + j)
cc.fill = C_SECTION
cc.border = BORDER
row += 1
base = q["_id"]
items = q.get("items") or []
if not items:
_emit_answer_row(ws, row, f'{base}{q.get("text") or ""}', base, resp, stem=False)
row += 1
else:
# stem řádek (text otázky) — bez hodnot
sc = ws.cell(row, 1, f'{base}{q.get("text") or ""}')
sc.font = Font(bold=True)
sc.fill = C_STEM
sc.alignment = WRAP_TOP
sc.border = BORDER
for j in range(n):
cc = ws.cell(row, 2 + j)
cc.fill = C_STEM
cc.border = BORDER
row += 1
for it in items:
lbl = it.get("label") or "(odpověď)"
_emit_answer_row(ws, row, f" {lbl}", it["key"], resp, stem=False)
row += 1
# --- formátování ---
ws.freeze_panes = "B3"
ws.column_dimensions["A"].width = 58
for j in range(n):
ws.column_dimensions[get_column_letter(2 + j)].width = 20
ws.row_dimensions[1].height = 28
ws.row_dimensions[2].height = 40
os.makedirs(OUT_DIR, exist_ok=True)
ts = datetime.now().strftime("%Y%m%d_%H%M")
out = os.path.join(OUT_DIR, f"SIPIQ_prehled_center_{ts}.xlsx")
wb.save(out)
print(f"Uloženo: {out}")
client.close()
def _emit_answer_row(ws, row, label, key, resp, stem):
a = ws.cell(row, 1, label)
a.alignment = WRAP_TOP
a.border = BORDER
for j, r in enumerate(resp):
ans = (r.get("answers") or {})
supp = (r.get("answers_supplement") or {})
c = ws.cell(row, 2 + j)
c.alignment = WRAP_TOP
c.border = BORDER
if key in supp:
val = (supp[key] or {}).get("value")
c.value = val
c.fill = C_SUPP
c.font = Font(italic=True)
src = (supp[key] or {}).get("answer_source") or "doplněno"
c.comment = Comment(f"Doplněno mimo SIPIQ: {src}", "sipiq_report")
else:
val = ans.get(key)
c.value = val
if val == "Yes":
c.fill = C_YES
elif val == "No":
c.fill = C_NO
if __name__ == "__main__":
main()
+33
View File
@@ -0,0 +1,33 @@
# sipiq_report_v1.1 — přehledný Excel report SIPIQ (centra × otázky)
**Verze:** 1.1 · **Datum:** 2026-06-17 · **Studie:** 77242113UCO3002 (ICONIC / DAWN)
## Co dělá
Excel report: **otázky ve sloupci A** (v pořadí SIPIQ, seskupené dle sekcí), **centra ve sloupcích**
(B+). Buňka = odpověď centra na otázku → vyplněné SIPIQ všech center vedle sebe.
Čte z MongoDB `feasibility`: `sipiq_questions` (slovník+pořadí+sekce+items) + `sipiq_responses`
(answers + `answers_supplement`).
## Layout
- Hlavička 2 řádky: **příjmení PI** + **název centra [země]**; centra seřazena CZ→SK, pak příjmení.
- Blok **IDENTITA** (země/město/PI/e-mail/sdl_site_id/datum vyplnění).
- Sekce SIPIQ jako barevné mezititulky; jednotlivé otázky a položky matic v řádcích.
- **Yes=zelená, No=červená**; doplněné odpovědi (`answers_supplement`) = oranžová + kurzíva + komentář se zdrojem.
- **Freeze panes B3** (sloupec A + hlavička), ohraničení, šířky (A=58, centra=20).
## Změny proti v1.0
- **Contact Information SBALENA** do 5 řádků (má koordinátora? / koordinátor jméno+e-mail /
CDA vyjednává / dotazník vyplnil) místo ~44; PI je už v IDENTITA. → `COMPACT_CONTACT`.
- **Q54** (doporučení dalších center) = JEDEN zřetězený řádek místo rozbitých „PI Name: - No Name" řádků.
- Confidentiality Statement (Q14Q21, právní souhlasy) se vynechává (`SKIP_SECTIONS`).
- Řádků: 179 → 126.
## Použití
```
.venv\Scripts\python.exe Feasibility\sipiq_report_v1.1.py
```
Výstup: `u:\Dropbox\!!!Days\Downloads Z230\SIPIQ_prehled_center_<ts>.xlsx`.
Bez argumentů — vždy aktuální stav DB (vč. doplněných odpovědí). pymongo + openpyxl.
## Stav 17JUN2026
56 otázek × 15 center, 126 řádků. v1.0 v `Feasibility\TRASH`.
+220
View File
@@ -0,0 +1,220 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
sipiq_report_v1.1.py
====================
Verze: 1.1
Datum: 2026-06-17
Autor: Claude Code (pro MUDr. Vladimíra Buzalku)
Změny proti v1.0
----------------
- Contact Information SBALENA do kompaktního bloku (koordinátor jméno+e-mail, má koordinátora?,
kdo vyjednává CDA, kdo vyplnil dotazník) místo ~44 řádků; PI je už v bloku IDENTITA.
- Q54 (doporučení dalších center) sloučeno do JEDNOHO řádku (zřetězené neprázdné hodnoty)
místo rozbitých „PI Name: - No Name - -" řádků. Stará v1.0 v TRASH.
Popis
-----
Přehledný Excel report SIPIQ: OTÁZKY ve sloupci A (pořadí + sekce), CENTRA ve sloupcích (B+).
Zdroj MongoDB feasibility (sipiq_questions + sipiq_responses + answers_supplement).
Yes=zelená, No=červená; doplněné odpovědi oranžová+kurzíva+komentář; freeze panes B3.
Výstup: u:\\Dropbox\\!!!Days\\Downloads Z230\\SIPIQ_prehled_center_<ts>.xlsx
Závislosti: pymongo, openpyxl (.venv). Mongo 192.168.1.76:27017, bez auth.
"""
import os
from datetime import datetime
from pymongo import MongoClient
from openpyxl import Workbook
from openpyxl.styles import Font, PatternFill, Alignment, Border, Side
from openpyxl.comments import Comment
from openpyxl.utils import get_column_letter
MONGO_URI = "mongodb://192.168.1.76:27017"
DB = "feasibility"
OUT_DIR = r"u:\Dropbox\!!!Days\Downloads Z230"
SKIP_SECTIONS = {"Confidentiality Statement"}
COUNTRY_ABBR = {"Czech Republic": "CZ", "Slovakia": "SK"}
# kompaktní Contact Information: (label, [answer keys], spojovník)
COMPACT_CONTACT = [
("Má centrum koordinátora?", ["Q7"], ""),
("Koordinátor (jméno)", ["Q8_2", "Q8_1"], " "),
("Koordinátor (e-mail)", ["Q8_8"], ""),
("CDA vyjednává", ["Q10"], ""),
("Dotazník vyplnil", ["Q12"], ""),
]
C_SECTION = PatternFill("solid", fgColor="1F4E78")
C_STEM = PatternFill("solid", fgColor="DDEBF7")
C_IDENT = PatternFill("solid", fgColor="F2F2F2")
C_HEAD = PatternFill("solid", fgColor="305496")
C_YES = PatternFill("solid", fgColor="C6EFCE")
C_NO = PatternFill("solid", fgColor="FFC7CE")
C_SUPP = PatternFill("solid", fgColor="FFE699")
THIN = Side(style="thin", color="BFBFBF")
BORDER = Border(left=THIN, right=THIN, top=THIN, bottom=THIN)
WRAP_TOP = Alignment(wrap_text=True, vertical="top")
WRAP_CTR = Alignment(wrap_text=True, vertical="center", horizontal="center")
def fmt_date(s):
if not s:
return ""
try:
return datetime.strptime(str(s)[:10], "%Y-%m-%d").strftime("%d%b%Y").upper()
except Exception:
return str(s)[:10]
def main():
client = MongoClient(MONGO_URI, serverSelectionTimeoutMS=8000)
db = client[DB]
client.admin.command("ping")
questions = list(db.sipiq_questions.find().sort("order", 1))
resp = list(db.sipiq_responses.find())
resp.sort(key=lambda r: (COUNTRY_ABBR.get(r.get("site_country"), "ZZ"),
(r.get("pi_last_name") or "").lower()))
n = len(resp)
print(f"Otázek: {len(questions)} | center: {n}")
wb = Workbook()
ws = wb.active
ws.title = "SIPIQ přehled"
state = {"row": 1}
# --- hlavička center ---
h = ws.cell(1, 1, "OTÁZKA \\ CENTRUM")
h.font = Font(bold=True, color="FFFFFF"); h.fill = C_HEAD; h.alignment = WRAP_CTR
ws.cell(2, 1, "")
ws.merge_cells("A1:A2")
for j, r in enumerate(resp):
cc = COUNTRY_ABBR.get(r.get("site_country"), "?")
for rr, val in ((1, r.get("pi_last_name") or "?"), (2, f"{r.get('site_name') or ''} [{cc}]")):
c = ws.cell(rr, 2 + j, val)
c.font = Font(bold=True, color="FFFFFF", size=9); c.fill = C_HEAD
c.alignment = WRAP_CTR; c.border = BORDER
state["row"] = 3
def section_header(title):
row = state["row"]
sc = ws.cell(row, 1, f"{title.upper()}")
sc.font = Font(bold=True, color="FFFFFF"); sc.fill = C_SECTION
sc.alignment = WRAP_TOP; sc.border = BORDER
for j in range(n):
c = ws.cell(row, 2 + j); c.fill = C_SECTION; c.border = BORDER
state["row"] += 1
def combo_row(label, keys, sep, fill=None):
row = state["row"]
a = ws.cell(row, 1, label)
a.alignment = WRAP_TOP; a.border = BORDER
if fill:
a.fill = fill; a.font = Font(bold=True)
for j, r in enumerate(resp):
ans = r.get("answers") or {}
supp = r.get("answers_supplement") or {}
c = ws.cell(row, 2 + j); c.alignment = WRAP_TOP; c.border = BORDER
if fill:
c.fill = fill
vals, supp_hit, supp_src = [], False, None
for k in keys:
if k in supp:
vals.append((supp[k] or {}).get("value")); supp_hit = True
supp_src = (supp[k] or {}).get("answer_source") or "doplněno"
elif ans.get(k):
vals.append(ans.get(k))
val = sep.join(x for x in vals if x)
c.value = val or None
if supp_hit:
c.fill = C_SUPP; c.font = Font(italic=True)
c.comment = Comment(f"Doplněno mimo SIPIQ: {supp_src}", "sipiq_report")
elif val == "Yes":
c.fill = C_YES
elif val == "No":
c.fill = C_NO
state["row"] += 1
# --- IDENTITA ---
section_header("Identita centra")
for label, getter in [
("Země", lambda r: COUNTRY_ABBR.get(r.get("site_country"), r.get("site_country") or "")),
("Město", lambda r: r.get("site_city") or ""),
("PI", lambda r: f"{r.get('pi_first_name') or ''} {r.get('pi_last_name') or ''}".strip()),
("E-mail PI", lambda r: r.get("pi_email") or ""),
("sdl_site_id", lambda r: r.get("sdl_site_id") or ""),
("Datum vyplnění", lambda r: fmt_date((r.get("meta") or {}).get("recorded_date"))),
]:
row = state["row"]
a = ws.cell(row, 1, label); a.font = Font(bold=True); a.fill = C_IDENT
a.alignment = WRAP_TOP; a.border = BORDER
for j, r in enumerate(resp):
c = ws.cell(row, 2 + j, getter(r)); c.fill = C_IDENT
c.alignment = WRAP_TOP; c.border = BORDER
state["row"] += 1
# --- OTÁZKY po sekcích ---
cur_section = None
contact_done = False
for q in questions:
section = q.get("section") or "Other"
if section in SKIP_SECTIONS:
continue
if section == "Contact Information":
if contact_done:
continue
section_header("Kontakty (koordinátor / CDA / vyplnil)")
for label, keys, sep in COMPACT_CONTACT:
combo_row(label, keys, sep)
contact_done = True
cur_section = section
continue
if section != cur_section:
section_header(section)
cur_section = section
base = q["_id"]
# Q54 = doporučení dalších center -> jeden zřetězený řádek
if base == "Q54":
keys = [it["key"] for it in (q.get("items") or [])] or [base]
combo_row("Q54 — Doporučená další centra/investigátoři", keys, "; ")
continue
items = q.get("items") or []
if not items:
combo_row(f'{base}{q.get("text") or ""}', [base], "")
else:
row = state["row"]
sc = ws.cell(row, 1, f'{base}{q.get("text") or ""}')
sc.font = Font(bold=True); sc.fill = C_STEM
sc.alignment = WRAP_TOP; sc.border = BORDER
for j in range(n):
c = ws.cell(row, 2 + j); c.fill = C_STEM; c.border = BORDER
state["row"] += 1
for it in items:
lbl = it.get("label") or "(odpověď)"
combo_row(f" {lbl}", [it["key"]], "")
# --- formátování ---
ws.freeze_panes = "B3"
ws.column_dimensions["A"].width = 58
for j in range(n):
ws.column_dimensions[get_column_letter(2 + j)].width = 20
ws.row_dimensions[1].height = 28
ws.row_dimensions[2].height = 40
os.makedirs(OUT_DIR, exist_ok=True)
ts = datetime.now().strftime("%Y%m%d_%H%M")
out = os.path.join(OUT_DIR, f"SIPIQ_prehled_center_{ts}.xlsx")
wb.save(out)
print(f"Uloženo: {out}")
client.close()
if __name__ == "__main__":
main()
+34
View File
@@ -0,0 +1,34 @@
"""_list_cats_v0.py — DOČASNÉ: vypíše nedávné e-maily mailboxu (argv[1]) + jejich kategorie."""
import sys
sys.stdout.reconfigure(encoding="utf-8")
import msal, requests
TENANT_ID="7d269944-37a4-43a1-8140-c7517dc426e9"
CLIENT_ID="4b222bfd-78c9-4239-a53f-43006b3ed07f"
CLIENT_SECRET="Txg8Q~MjhocuopxsJyJBhPmDfMxZ2r5WpTFj1dfk"
AUTHORITY=f"https://login.microsoftonline.com/{TENANT_ID}"
mailbox = sys.argv[1] if len(sys.argv) > 1 else "vladimir.buzalka@buzalka.cz"
BASE=f"https://graph.microsoft.com/v1.0/users/{mailbox}"
app=msal.ConfidentialClientApplication(CLIENT_ID,authority=AUTHORITY,client_credential=CLIENT_SECRET)
tok=app.acquire_token_for_client(scopes=["https://graph.microsoft.com/.default"])
H={"Authorization":f"Bearer {tok['access_token']}"}
# 1) nejnovějších 20 e-mailů (napříč schránkou)
r=requests.get(f"{BASE}/messages",headers=H,params={
"$top":20,"$orderby":"receivedDateTime desc",
"$select":"subject,from,receivedDateTime,categories"},timeout=30)
print(f"[{mailbox}] nejnovějších {len(r.json().get('value',[]))}:")
for m in r.json().get("value",[]):
cats=m.get("categories") or []
frm=m.get("from",{}).get("emailAddress",{}).get("address","")
flag=" <<< KATEGORIE" if cats else ""
print(f" {m.get('receivedDateTime','')[:10]} | {frm:28.28} | {str(cats):28} | {m.get('subject','')[:50]}{flag}")
# 2) cokoliv s kategorií obsahující KPC/Claude (zkus víc názvů)
print("\nHledám kategorie ~ KPC/Claude:")
for cat in ["ForKPCGeneration","KPCzpracovánoClaudem","KPC","ForKPC","Claude","Pro Tebe","ProClaude"]:
rr=requests.get(f"{BASE}/messages",headers=H,params={
"$filter":f"categories/any(c:c eq '{cat}')","$select":"subject","$top":5},timeout=30)
n=len(rr.json().get("value",[])) if rr.ok else f"ERR{rr.status_code}"
print(f" '{cat}': {n}")
+92
View File
@@ -0,0 +1,92 @@
"""_process_clk_v0.py — DOČASNÉ: zpracuje manželčin ČLK příspěvek (mini-agent).
Telegram potvrzení → ano → KPC (plátce ordinace) → Dropbox → přehození kategorie.
"""
import sys
from datetime import date
from pathlib import Path
sys.stdout.reconfigure(encoding="utf-8")
sys.path.insert(0, r"U:\ordinaceprojekt")
import dropbox, msal, requests
from Knihovny.telegram_notify import zeptej_se_telegram, posli_telegram
# ── data platby ───────────────────────────────────────────────────────────
today = date.today()
ddmmyy = today.strftime("%d%m%y")
fdate = today.strftime("%Y-%m-%d")
DEBIT_NUM = "2800046620" # ordinace (plátce)
CREDIT_NUM = "244484339" # ČLK, bez předčíslí → holé číslo
CREDIT_BANK = "0800"
VS = "5141811171"; KS = "0000"
AMOUNT = 400000 # 4000,00 Kč v haléřích
AV = "clensky prispevek OS CLK 2026 Buzalkova"
SUMMARY = "ČLK příspěvek Buzalková 2026"
# e-mail ke zpracování (manželčina schránka)
MAILBOX = "michaela.buzalkova@buzalka.cz"
MSG_ID = "AAMkADFkMzE3NDlmLTg3NjQtNDQwNy05Nzc2LWJjYWJkNmNjMjIxZABGAAAAAABEaZtIoWAPQbVWi-ATA8XVBwB8gYSLibM0Q7crRJXlxBlcAAAAAAEMAAB8gYSLibM0Q7crRJXlxBlcAALrbxJcAAA="
# ── KPC ───────────────────────────────────────────────────────────────────
CRLF = "\r\n"
lines = [
"UHL1" + ddmmyy + " " * 20 + "0" * 28,
"1 1501 000000 2010",
f"2 000000-{DEBIT_NUM} {str(AMOUNT).zfill(14)} {ddmmyy}",
f"{CREDIT_NUM} {str(AMOUNT).zfill(12)} {VS} {CREDIT_BANK}{KS} AV:{AV}",
"3 +",
"5 +",
]
content = CRLF.join(lines) + CRLF
data = content.encode("ascii")
fname = f"{fdate} KPC k platbě [{SUMMARY}].kpc"
print("=== NÁHLED KPC ===")
print(content.replace("\r\n", "\\r\\n\n"), end="")
print(f"=== {fname} ({len(data)} B) ===\n")
# ── Telegram potvrzení ────────────────────────────────────────────────────
msg = (
"💳 Návrh platby (KPC agent)\n\n"
"ČLK členský příspěvek 2026 — MUDr. Buzalková\n"
"4 000 Kč → 244484339/0800, VS 5141811171\n"
"Z účtu: ordinace 2800046620\n"
"Splatnost: dnes (17.06.2026)\n\n"
"FIO: 2026 zatím nezaplaceno (poslední 2025-04-09) ✅\n\n"
"Vytvořit KPC a nahrát do Dropboxu? Odpověz: ano / ne"
)
odp = zeptej_se_telegram(msg, timeout=150)
print("TELEGRAM odpověď:", repr(odp))
if not odp or odp.strip().lower() not in ("ano", "ano.", "yes", "ok"):
print(">> NEPOTVRZENO (ano nepřišlo) — nic nevytvářím.")
sys.exit(0)
# ── lokální archiv + Dropbox upload ───────────────────────────────────────
out_dir = Path(__file__).parent / "output"; out_dir.mkdir(exist_ok=True)
(out_dir / fname).write_bytes(data)
print("lokální kopie:", out_dir / fname)
env = {}
for line in Path(r"U:\PythonProject\Janssen\EmailsImport\.env").read_text(encoding="utf-8").splitlines():
line = line.strip()
if "=" in line and not line.startswith("#"):
k, v = line.split("=", 1); env[k.strip()] = v.strip()
dbx = dropbox.Dropbox(app_key=env["DROPBOX_APP_KEY"], app_secret=env["DROPBOX_APP_SECRET"],
oauth2_refresh_token=env["DROPBOX_APP_REFRESH_TOKEN"])
res = dbx.files_upload(data, f"/!!!Days/Downloads Z230/{fname}",
mode=dropbox.files.WriteMode.add, autorename=True)
print("NAHRÁNO →", res.path_display)
# ── přehození kategorie na manželčině e-mailu ─────────────────────────────
TENANT="7d269944-37a4-43a1-8140-c7517dc426e9"; CID="4b222bfd-78c9-4239-a53f-43006b3ed07f"
SECRET="Txg8Q~MjhocuopxsJyJBhPmDfMxZ2r5WpTFj1dfk"
app=msal.ConfidentialClientApplication(CID,authority=f"https://login.microsoftonline.com/{TENANT}",client_credential=SECRET)
tok=app.acquire_token_for_client(scopes=["https://graph.microsoft.com/.default"])
H={"Authorization":f"Bearer {tok['access_token']}","Content-Type":"application/json"}
BASE=f"https://graph.microsoft.com/v1.0/users/{MAILBOX}"
rp=requests.patch(f"{BASE}/messages/{MSG_ID}",headers=H,json={"categories":["KPCzpracovánoClaudem"]},timeout=30)
print("PATCH kategorie:", rp.status_code, "" if rp.ok else rp.text[:300])
posli_telegram(f"✅ KPC vytvořeno a nahráno:\n{fname}\n4 000 Kč → 244484339/0800 (ČLK Buzalková), z ordinace.\nV bankingu stačí podepsat.")
print(">> HOTOVO.")
+51
View File
@@ -0,0 +1,51 @@
"""_read_v0.py — DOČASNÉ: přečte ForKPCGeneration e-maily z mailboxu (argv[1])."""
import sys
sys.stdout.reconfigure(encoding="utf-8")
import msal, requests
TENANT_ID = "7d269944-37a4-43a1-8140-c7517dc426e9"
CLIENT_ID = "4b222bfd-78c9-4239-a53f-43006b3ed07f"
CLIENT_SECRET = "Txg8Q~MjhocuopxsJyJBhPmDfMxZ2r5WpTFj1dfk"
AUTHORITY = f"https://login.microsoftonline.com/{TENANT_ID}"
SCOPE = ["https://graph.microsoft.com/.default"]
CATEGORY = "ForKPCGeneration"
mailbox = sys.argv[1] if len(sys.argv) > 1 else "vladimir.buzalka@buzalka.cz"
CATEGORY = sys.argv[2] if len(sys.argv) > 2 else CATEGORY
BASE = f"https://graph.microsoft.com/v1.0/users/{mailbox}"
app = msal.ConfidentialClientApplication(CLIENT_ID, authority=AUTHORITY, client_credential=CLIENT_SECRET)
tok = app.acquire_token_for_client(scopes=SCOPE)
assert "access_token" in tok, tok
H = {"Authorization": f"Bearer {tok['access_token']}"}
params = {
"$filter": f"categories/any(c:c eq '{CATEGORY}')",
"$select": "id,subject,from,receivedDateTime,categories,hasAttachments,body,bodyPreview",
"$top": 25,
}
r = requests.get(f"{BASE}/messages", headers=H, params=params, timeout=30)
if not r.ok:
print(f"CHYBA [{r.status_code}] pro {mailbox}: {r.text[:400]}")
sys.exit(1)
msgs = r.json().get("value", [])
print(f"[{mailbox}] e-mailů s kategorií '{CATEGORY}': {len(msgs)}\n")
for i, m in enumerate(msgs, 1):
frm = m.get("from", {}).get("emailAddress", {})
print("=" * 78)
print(f"[{i}] {m.get('subject')}")
print(f" od: {frm.get('name')} <{frm.get('address')}>")
print(f" datum: {m.get('receivedDateTime')}")
print(f" přílohy: {m.get('hasAttachments')}")
print(f" id: {m.get('id')}")
body = m.get("body", {})
print(f" --- TĚLO ({body.get('contentType')}) ---")
print(body.get("content", ""))
if m.get("hasAttachments"):
ra = requests.get(f"{BASE}/messages/{m['id']}/attachments",
headers=H, params={"$select": "id,name,size,contentType,isInline"}, timeout=30)
if ra.ok:
print(" --- PŘÍLOHY ---")
for a in ra.json().get("value", []):
print(f"{a.get('name')} ({a.get('size')} B, {a.get('contentType')}, inline={a.get('isInline')})")
@@ -0,0 +1,6 @@
UHL1170626 0000000000000000000000000000
1 1501 000000 2010
2 000000-2800046620 00000000400000 170626
244484339 000000400000 5141811171 08000000 AV:clensky prispevek OS CLK 2026 Buzalkova
3 +
5 +