z230
This commit is contained in:
@@ -1,402 +0,0 @@
|
||||
"""
|
||||
create_report.py
|
||||
Verze: 1.2
|
||||
Datum: 2026-05-27
|
||||
|
||||
Generuje Excel report (.xlsm) pro studii 77242113UCO3001 z MongoDB databáze Clario.
|
||||
Výstup: U:/Dropbox/!!!Days/Downloads Z230/YYYY-MM-DD 77242113UCO3001 Clario Reports.xlsm
|
||||
|
||||
Listy:
|
||||
MayoScore — jeden řádek = pacient × visit; řádky I-0 s Modified Mayo < 5 červeně tučně
|
||||
MayoDiary — jeden řádek = denní záznam deníku pacienta
|
||||
EligibleDays — jeden řádek = jeden eligible day z MayoScore obohacený o data z MayoDiary;
|
||||
included/excluded flag, excluded dny šedě na žlutém pozadí
|
||||
|
||||
VBA makro (Worksheet_SelectionChange na listu MayoScore):
|
||||
Klik na řádek → automaticky přepne na EligibleDays a vyfiltruje záznamy
|
||||
pro daného pacienta a visit. Vyžaduje povolení maker při otevření souboru.
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
from pymongo import MongoClient
|
||||
from openpyxl import Workbook
|
||||
from openpyxl.styles import Font, PatternFill, Alignment, Border, Side
|
||||
from openpyxl.utils import get_column_letter
|
||||
import xlwings as xw
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Konfigurace
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
MONGO_URI = "mongodb://192.168.1.76:27017"
|
||||
DB_NAME = "Clario"
|
||||
OUTPUT_DIR = Path(r"U:\Dropbox\!!!Days\Downloads Z230")
|
||||
|
||||
VISIT_ORDER = ["I-0", "I-2", "I-4", "I-8", "I-12"]
|
||||
|
||||
COLUMNS_SCORE = [
|
||||
("Site", lambda d: d.get("site", {}).get("name", "")),
|
||||
("Subject ID", lambda d: d.get("subject", {}).get("id", "")),
|
||||
("Visit", lambda d: d["fields"].get("Visit", "")),
|
||||
("Visit Date", lambda d: d["fields"].get("Visit Date", "")),
|
||||
("Baseline Stool Frequency", lambda d: _num(d["fields"].get("Baseline Stool Frequency", ""))),
|
||||
("Central Endoscopy Score", lambda d: _num(d["fields"].get("Central Endoscopy Score", ""))),
|
||||
("PGA Score", lambda d: _num(d["fields"].get("PGA Score", ""))),
|
||||
("Stool Frequency Sub-score", lambda d: _num(d["fields"].get("Stool Frequency Sub-score", ""))),
|
||||
("Rectal Bleeding Sub-score", lambda d: _num(d["fields"].get("Rectal Bleeding Sub-score", ""))),
|
||||
("Partial Mayo Score", lambda d: _num(d["fields"].get("Partial Mayo Score", ""))),
|
||||
("Modified Mayo Score", lambda d: _num(d["fields"].get("Modified Mayo Score", ""))),
|
||||
("Full Mayo Score", lambda d: _num(d["fields"].get("Full Mayo Score", ""))),
|
||||
]
|
||||
|
||||
COLUMNS_DIARY = [
|
||||
("Subject ID", lambda d: d.get("subject", {}).get("id", "")),
|
||||
("Report Date", lambda d: d["fields"].get("Report Date", "")),
|
||||
("Baseline Stool Count", lambda d: _num(d["fields"].get("Baseline Stool Count", ""))),
|
||||
("Stool Frequency", lambda d: _num(d["fields"].get("Stool Frequency", ""))),
|
||||
("MAYO050", lambda d: d["fields"].get("MAYO050", "")),
|
||||
("Not Applicable", lambda d: d["fields"].get("Not Applicable", "")),
|
||||
("Constipation", lambda d: d["fields"].get("Constipation", "")),
|
||||
("Diarrhea", lambda d: d["fields"].get("Diarrhea", "")),
|
||||
("Irregularity", lambda d: d["fields"].get("Irregularity", "")),
|
||||
]
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Helpers
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def _num(value):
|
||||
"""Převede číselný string na int, jinak vrátí původní hodnotu nebo None."""
|
||||
if value == "" or value is None:
|
||||
return None
|
||||
try:
|
||||
return int(value)
|
||||
except (ValueError, TypeError):
|
||||
try:
|
||||
return float(value)
|
||||
except (ValueError, TypeError):
|
||||
return value
|
||||
|
||||
|
||||
def _visit_sort_key(doc):
|
||||
visit = doc["fields"].get("Visit", "")
|
||||
try:
|
||||
idx = VISIT_ORDER.index(visit)
|
||||
except ValueError:
|
||||
idx = len(VISIT_ORDER)
|
||||
return (doc.get("site", {}).get("name", ""), doc.get("subject", {}).get("id", ""), idx, visit)
|
||||
|
||||
|
||||
def _iso_to_date(value):
|
||||
"""ISO string → Python date pro Excel."""
|
||||
if not isinstance(value, str):
|
||||
return value
|
||||
try:
|
||||
return datetime.fromisoformat(value).date()
|
||||
except ValueError:
|
||||
return value
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Styly
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
HEADER_FILL = PatternFill("solid", fgColor="1F497D")
|
||||
HEADER_FONT = Font(bold=True, color="FFFFFF", size=10)
|
||||
CELL_FONT = Font(size=10)
|
||||
ALIGN_CTR = Alignment(horizontal="center", vertical="center", wrap_text=False)
|
||||
ALIGN_LEFT = Alignment(horizontal="left", vertical="center")
|
||||
|
||||
THIN = Side(style="thin", color="BFBFBF")
|
||||
BORDER = Border(left=THIN, right=THIN, top=THIN, bottom=THIN)
|
||||
|
||||
# zebra
|
||||
FILL_ODD = PatternFill("solid", fgColor="FFFFFF")
|
||||
FILL_EVEN = PatternFill("solid", fgColor="EBF1DE")
|
||||
|
||||
SCORE_COLS = {"Partial Mayo Score", "Modified Mayo Score", "Full Mayo Score"}
|
||||
SCORE_FILL = PatternFill("solid", fgColor="FFC7CE") # červená pro skóre ≥ 5 (placeholder — nepoužíváme podmíněné formátování)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Sestavení sheetu
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def _build_sheet(ws, docs, columns, date_cols, center_cols, col_widths, row_font_fn=None):
|
||||
headers = [c[0] for c in columns]
|
||||
|
||||
for col_idx, header in enumerate(headers, 1):
|
||||
cell = ws.cell(row=1, column=col_idx, value=header)
|
||||
cell.font = HEADER_FONT
|
||||
cell.fill = HEADER_FILL
|
||||
cell.alignment = ALIGN_CTR
|
||||
cell.border = BORDER
|
||||
ws.row_dimensions[1].height = 28
|
||||
|
||||
for row_idx, doc in enumerate(docs, 2):
|
||||
fill = FILL_EVEN if row_idx % 2 == 0 else FILL_ODD
|
||||
font = row_font_fn(doc) if row_font_fn else CELL_FONT
|
||||
for col_idx, (col_name, getter) in enumerate(columns, 1):
|
||||
value = getter(doc)
|
||||
if col_name in date_cols and isinstance(value, str):
|
||||
value = _iso_to_date(value)
|
||||
cell = ws.cell(row=row_idx, column=col_idx, value=value)
|
||||
cell.font = font
|
||||
cell.fill = fill
|
||||
cell.border = BORDER
|
||||
cell.alignment = ALIGN_CTR if col_name in center_cols else ALIGN_LEFT
|
||||
|
||||
for col_idx, (col_name, _) in enumerate(columns, 1):
|
||||
ws.column_dimensions[get_column_letter(col_idx)].width = col_widths.get(col_name, 14)
|
||||
|
||||
for col_name in date_cols:
|
||||
if col_name in headers:
|
||||
letter = get_column_letter(headers.index(col_name) + 1)
|
||||
for row_idx in range(2, len(docs) + 2):
|
||||
ws[f"{letter}{row_idx}"].number_format = "DD-MMM-YYYY"
|
||||
|
||||
ws.freeze_panes = "A2"
|
||||
ws.auto_filter.ref = f"A1:{get_column_letter(len(headers))}1"
|
||||
|
||||
|
||||
def _score_row_font(doc):
|
||||
visit = doc["fields"].get("Visit", "")
|
||||
try:
|
||||
mod_mayo = int(doc["fields"].get("Modified Mayo Score", ""))
|
||||
except (ValueError, TypeError):
|
||||
mod_mayo = None
|
||||
if visit == "I-0" and mod_mayo is not None and mod_mayo < 5:
|
||||
return Font(size=10, bold=True, color="FF0000")
|
||||
return CELL_FONT
|
||||
|
||||
|
||||
def build_mayo_score_sheet(ws, docs):
|
||||
_build_sheet(
|
||||
ws, docs, COLUMNS_SCORE,
|
||||
date_cols={"Visit Date"},
|
||||
center_cols={"Visit", "Central Endoscopy Score", "PGA Score",
|
||||
"Stool Frequency Sub-score", "Rectal Bleeding Sub-score",
|
||||
"Partial Mayo Score", "Modified Mayo Score", "Full Mayo Score",
|
||||
"Baseline Stool Frequency"},
|
||||
col_widths={
|
||||
"Site": 18, "Subject ID": 16, "Visit": 12, "Visit Date": 14,
|
||||
"Baseline Stool Frequency": 14, "Central Endoscopy Score": 14,
|
||||
"PGA Score": 10, "Stool Frequency Sub-score": 14,
|
||||
"Rectal Bleeding Sub-score": 14, "Partial Mayo Score": 14,
|
||||
"Modified Mayo Score": 14, "Full Mayo Score": 13,
|
||||
},
|
||||
row_font_fn=_score_row_font,
|
||||
)
|
||||
|
||||
|
||||
def build_mayo_diary_sheet(ws, docs):
|
||||
_build_sheet(
|
||||
ws, docs, COLUMNS_DIARY,
|
||||
date_cols={"Report Date"},
|
||||
center_cols={"Baseline Stool Count", "Stool Frequency", "Not Applicable",
|
||||
"Constipation", "Diarrhea", "Irregularity"},
|
||||
col_widths={
|
||||
"Subject ID": 16, "Report Date": 14, "Baseline Stool Count": 14,
|
||||
"Stool Frequency": 14, "MAYO050": 48, "Not Applicable": 14,
|
||||
"Constipation": 14, "Diarrhea": 12, "Irregularity": 14,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def build_eligible_days_sheet(ws, score_docs, diary_docs):
|
||||
# Lookup diary records by (subject_id, date_part YYYY-MM-DD)
|
||||
diary_lookup: dict[tuple, dict] = {}
|
||||
for d in diary_docs:
|
||||
subj = d.get("subject", {}).get("id", "")
|
||||
date_iso = d["fields"].get("Report Date", "")
|
||||
date_part = date_iso[:10] if date_iso else ""
|
||||
if subj and date_part:
|
||||
diary_lookup[(subj, date_part)] = d
|
||||
|
||||
headers = [
|
||||
"Included", "Subject ID", "Visit", "Visit Date", "Day",
|
||||
"Report Date", "Baseline Stool Count", "Stool Frequency",
|
||||
"MAYO050", "Not Applicable", "Constipation", "Diarrhea", "Irregularity",
|
||||
]
|
||||
col_widths = {
|
||||
"Included": 10, "Subject ID": 16, "Visit": 10, "Visit Date": 14, "Day": 8,
|
||||
"Report Date": 14, "Baseline Stool Count": 14, "Stool Frequency": 14,
|
||||
"MAYO050": 48, "Not Applicable": 14, "Constipation": 14,
|
||||
"Diarrhea": 12, "Irregularity": 14,
|
||||
}
|
||||
center_cols = {"Included", "Visit", "Day", "Baseline Stool Count", "Stool Frequency",
|
||||
"Not Applicable", "Constipation", "Diarrhea", "Irregularity"}
|
||||
date_cols = {"Visit Date", "Report Date"}
|
||||
no_fill = PatternFill("solid", fgColor="FFF2CC") # žlutá pro excluded dny
|
||||
|
||||
for col_idx, header in enumerate(headers, 1):
|
||||
cell = ws.cell(row=1, column=col_idx, value=header)
|
||||
cell.font = HEADER_FONT
|
||||
cell.fill = HEADER_FILL
|
||||
cell.alignment = ALIGN_CTR
|
||||
cell.border = BORDER
|
||||
ws.row_dimensions[1].height = 28
|
||||
|
||||
row_idx = 2
|
||||
for score_doc in score_docs:
|
||||
subj = score_doc.get("subject", {}).get("id", "")
|
||||
visit = score_doc["fields"].get("Visit", "")
|
||||
visit_date = score_doc["fields"].get("Visit Date", "")
|
||||
|
||||
for n in range(1, 11):
|
||||
day_date_iso = score_doc["fields"].get(f"Eligible Day (-{n})")
|
||||
if not day_date_iso or day_date_iso == "-":
|
||||
continue
|
||||
date_part = day_date_iso[:10]
|
||||
excl_reason = score_doc["fields"].get(f"Day (-{n}) Excluded Reason(s)", "")
|
||||
included = "No" if excl_reason and excl_reason != "-" else "Yes"
|
||||
|
||||
diary = diary_lookup.get((subj, date_part), {})
|
||||
df = diary.get("fields", {})
|
||||
|
||||
fill = no_fill if included == "No" else (FILL_EVEN if row_idx % 2 == 0 else FILL_ODD)
|
||||
font = Font(size=10, color="808080") if included == "No" else CELL_FONT
|
||||
|
||||
values = [
|
||||
included,
|
||||
subj,
|
||||
visit,
|
||||
_iso_to_date(visit_date) if isinstance(visit_date, str) else visit_date,
|
||||
f"-{n}",
|
||||
_iso_to_date(day_date_iso),
|
||||
_num(df.get("Baseline Stool Count", "")),
|
||||
_num(df.get("Stool Frequency", "")),
|
||||
df.get("MAYO050", ""),
|
||||
df.get("Not Applicable", ""),
|
||||
df.get("Constipation", ""),
|
||||
df.get("Diarrhea", ""),
|
||||
df.get("Irregularity", ""),
|
||||
]
|
||||
|
||||
for col_idx, (header, value) in enumerate(zip(headers, values), 1):
|
||||
cell = ws.cell(row=row_idx, column=col_idx, value=value)
|
||||
cell.font = font
|
||||
cell.fill = fill
|
||||
cell.border = BORDER
|
||||
if header in date_cols:
|
||||
cell.number_format = "DD-MMM-YYYY"
|
||||
cell.alignment = ALIGN_CTR if header in center_cols else ALIGN_LEFT
|
||||
|
||||
row_idx += 1
|
||||
|
||||
for col_idx, header in enumerate(headers, 1):
|
||||
ws.column_dimensions[get_column_letter(col_idx)].width = col_widths.get(header, 14)
|
||||
|
||||
ws.freeze_panes = "A2"
|
||||
ws.auto_filter.ref = f"A1:{get_column_letter(len(headers))}1"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Main
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def main():
|
||||
client = MongoClient(MONGO_URI, serverSelectionTimeoutMS=5000)
|
||||
client.admin.command("ping")
|
||||
db = client[DB_NAME]
|
||||
|
||||
score_docs = list(db["Clario.MayoScore"].find({}))
|
||||
diary_docs = list(db["Clario.MayoDiary"].find({}))
|
||||
client.close()
|
||||
|
||||
score_docs.sort(key=_visit_sort_key)
|
||||
diary_docs.sort(key=lambda d: (
|
||||
d.get("subject", {}).get("id", ""),
|
||||
d["fields"].get("Report Date", ""),
|
||||
))
|
||||
|
||||
wb = Workbook()
|
||||
ws_score = wb.active
|
||||
ws_score.title = "MayoScore"
|
||||
build_mayo_score_sheet(ws_score, score_docs)
|
||||
|
||||
ws_diary = wb.create_sheet("MayoDiary")
|
||||
build_mayo_diary_sheet(ws_diary, diary_docs)
|
||||
|
||||
ws_days = wb.create_sheet("EligibleDays")
|
||||
build_eligible_days_sheet(ws_days, score_docs, diary_docs)
|
||||
|
||||
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
|
||||
today = datetime.now().strftime("%Y-%m-%d")
|
||||
filename = f"{today} 77242113UCO3001 Clario Reports.xlsx"
|
||||
output_path = OUTPUT_DIR / filename
|
||||
|
||||
# Uložit jako .xlsx nejdřív, pak přepsat na .xlsm přes xlwings + injektovat VBA
|
||||
xlsx_path = output_path.with_suffix(".xlsx")
|
||||
xlsm_path = output_path.with_suffix(".xlsm")
|
||||
wb.save(str(xlsx_path))
|
||||
|
||||
inject_vba(xlsx_path, xlsm_path)
|
||||
xlsx_path.unlink(missing_ok=True)
|
||||
|
||||
print(f"Uloženo: {xlsm_path}")
|
||||
print(f"MayoScore: {len(score_docs)} záznamů")
|
||||
print(f"MayoDiary: {len(diary_docs)} záznamů")
|
||||
print(f"EligibleDays: generováno z {len(score_docs)} score záznamů")
|
||||
|
||||
|
||||
def inject_vba(xlsx_path: Path, xlsm_path: Path) -> None:
|
||||
vba_code = '''\
|
||||
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
|
||||
If Target.Row < 2 Or Target.Column < 1 Then Exit Sub
|
||||
If Target.Rows.Count > 1 Then Exit Sub
|
||||
|
||||
Dim subjectId As String
|
||||
Dim visit As String
|
||||
subjectId = CStr(Me.Cells(Target.Row, 2).Value)
|
||||
visit = CStr(Me.Cells(Target.Row, 3).Value)
|
||||
|
||||
If subjectId = "" Or visit = "" Then Exit Sub
|
||||
|
||||
Dim ws As Worksheet
|
||||
On Error Resume Next
|
||||
Set ws = ThisWorkbook.Sheets("EligibleDays")
|
||||
On Error GoTo 0
|
||||
If ws Is Nothing Then Exit Sub
|
||||
|
||||
Application.ScreenUpdating = False
|
||||
|
||||
ws.AutoFilterMode = False
|
||||
ws.Range("A1").AutoFilter
|
||||
ws.Range("A1").AutoFilter Field:=2, Criteria1:=subjectId
|
||||
ws.Range("A1").AutoFilter Field:=3, Criteria1:=visit
|
||||
|
||||
ws.Activate
|
||||
ws.Range("A2").Select
|
||||
|
||||
Application.ScreenUpdating = True
|
||||
End Sub
|
||||
'''
|
||||
|
||||
app = xw.App(visible=False)
|
||||
try:
|
||||
wb = app.books.open(str(xlsx_path))
|
||||
# Najdi VBComponent odpovídající listu "MayoScore" podle tab názvu
|
||||
vb_comp = None
|
||||
for comp in wb.api.VBProject.VBComponents:
|
||||
if comp.Type == 100: # xlSheet
|
||||
try:
|
||||
if comp.Properties("Name").Value == "MayoScore":
|
||||
vb_comp = comp
|
||||
break
|
||||
except Exception:
|
||||
pass
|
||||
if vb_comp is None:
|
||||
# fallback: první sheet (Sheet1)
|
||||
vb_comp = wb.api.VBProject.VBComponents("Sheet1")
|
||||
vb_comp.CodeModule.AddFromString(vba_code)
|
||||
wb.api.SaveAs(str(xlsm_path), FileFormat=52) # 52 = xlOpenXMLWorkbookMacroEnabled
|
||||
wb.close()
|
||||
finally:
|
||||
app.quit()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,3 +1,3 @@
|
||||
2026/05/28-09:34:01.521 26f4 Reusing MANIFEST U:\PythonProject\Janssen\Covance_UCO3001\browser_profile\Default\Extension State/MANIFEST-000001
|
||||
2026/05/28-09:34:01.522 26f4 Recovering log #3
|
||||
2026/05/28-09:34:01.522 26f4 Reusing old log U:\PythonProject\Janssen\Covance_UCO3001\browser_profile\Default\Extension State/000003.log
|
||||
2026/05/29-13:24:16.539 6b34 Reusing MANIFEST U:\PythonProject\Janssen\Covance_UCO3001\browser_profile\Default\Extension State/MANIFEST-000001
|
||||
2026/05/29-13:24:16.540 6b34 Recovering log #3
|
||||
2026/05/29-13:24:16.541 6b34 Reusing old log U:\PythonProject\Janssen\Covance_UCO3001\browser_profile\Default\Extension State/000003.log
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
2026/05/28-09:20:54.095 55ec Reusing MANIFEST U:\PythonProject\Janssen\Covance_UCO3001\browser_profile\Default\Extension State/MANIFEST-000001
|
||||
2026/05/28-09:20:54.096 55ec Recovering log #3
|
||||
2026/05/28-09:20:54.096 55ec Reusing old log U:\PythonProject\Janssen\Covance_UCO3001\browser_profile\Default\Extension State/000003.log
|
||||
2026/05/29-13:17:37.742 5acc Reusing MANIFEST U:\PythonProject\Janssen\Covance_UCO3001\browser_profile\Default\Extension State/MANIFEST-000001
|
||||
2026/05/29-13:17:37.742 5acc Recovering log #3
|
||||
2026/05/29-13:17:37.743 5acc Reusing old log U:\PythonProject\Janssen\Covance_UCO3001\browser_profile\Default\Extension State/000003.log
|
||||
|
||||
Binary file not shown.
@@ -1,3 +1,3 @@
|
||||
2026/05/28-09:34:11.834 4160 Reusing MANIFEST U:\PythonProject\Janssen\Covance_UCO3001\browser_profile\Default\GCM Store/MANIFEST-000001
|
||||
2026/05/28-09:34:11.835 4160 Recovering log #3
|
||||
2026/05/28-09:34:11.836 4160 Reusing old log U:\PythonProject\Janssen\Covance_UCO3001\browser_profile\Default\GCM Store/000003.log
|
||||
2026/05/29-13:24:32.087 1144 Reusing MANIFEST U:\PythonProject\Janssen\Covance_UCO3001\browser_profile\Default\GCM Store/MANIFEST-000001
|
||||
2026/05/29-13:24:32.087 1144 Recovering log #3
|
||||
2026/05/29-13:24:32.087 1144 Reusing old log U:\PythonProject\Janssen\Covance_UCO3001\browser_profile\Default\GCM Store/000003.log
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
2026/05/28-09:21:04.862 2184 Reusing MANIFEST U:\PythonProject\Janssen\Covance_UCO3001\browser_profile\Default\GCM Store/MANIFEST-000001
|
||||
2026/05/28-09:21:04.864 2184 Recovering log #3
|
||||
2026/05/28-09:21:04.865 2184 Reusing old log U:\PythonProject\Janssen\Covance_UCO3001\browser_profile\Default\GCM Store/000003.log
|
||||
2026/05/29-13:17:47.687 5428 Reusing MANIFEST U:\PythonProject\Janssen\Covance_UCO3001\browser_profile\Default\GCM Store/MANIFEST-000001
|
||||
2026/05/29-13:17:47.696 5428 Recovering log #3
|
||||
2026/05/29-13:17:47.696 5428 Reusing old log U:\PythonProject\Janssen\Covance_UCO3001\browser_profile\Default\GCM Store/000003.log
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
+3
-3
@@ -1,3 +1,3 @@
|
||||
2026/05/28-08:18:49.515 3070 Reusing MANIFEST U:\PythonProject\Janssen\Covance_UCO3001\browser_profile\Default\IndexedDB\https_xsp.covance.com_0.indexeddb.leveldb/MANIFEST-000001
|
||||
2026/05/28-08:18:49.515 3070 Recovering log #3
|
||||
2026/05/28-08:18:49.515 3070 Reusing old log U:\PythonProject\Janssen\Covance_UCO3001\browser_profile\Default\IndexedDB\https_xsp.covance.com_0.indexeddb.leveldb/000003.log
|
||||
2026/05/29-11:20:05.334 264 Reusing MANIFEST U:\PythonProject\Janssen\Covance_UCO3001\browser_profile\Default\IndexedDB\https_xsp.covance.com_0.indexeddb.leveldb/MANIFEST-000001
|
||||
2026/05/29-11:20:05.335 264 Recovering log #3
|
||||
2026/05/29-11:20:05.335 264 Reusing old log U:\PythonProject\Janssen\Covance_UCO3001\browser_profile\Default\IndexedDB\https_xsp.covance.com_0.indexeddb.leveldb/000003.log
|
||||
|
||||
+3
-3
@@ -1,3 +1,3 @@
|
||||
2026/05/28-08:03:00.480 474c Reusing MANIFEST U:\PythonProject\Janssen\Covance_UCO3001\browser_profile\Default\IndexedDB\https_xsp.covance.com_0.indexeddb.leveldb/MANIFEST-000001
|
||||
2026/05/28-08:03:00.481 474c Recovering log #3
|
||||
2026/05/28-08:03:00.481 474c Reusing old log U:\PythonProject\Janssen\Covance_UCO3001\browser_profile\Default\IndexedDB\https_xsp.covance.com_0.indexeddb.leveldb/000003.log
|
||||
2026/05/29-11:17:21.700 5a0c Reusing MANIFEST U:\PythonProject\Janssen\Covance_UCO3001\browser_profile\Default\IndexedDB\https_xsp.covance.com_0.indexeddb.leveldb/MANIFEST-000001
|
||||
2026/05/29-11:17:21.700 5a0c Recovering log #3
|
||||
2026/05/29-11:17:21.700 5a0c Reusing old log U:\PythonProject\Janssen\Covance_UCO3001\browser_profile\Default\IndexedDB\https_xsp.covance.com_0.indexeddb.leveldb/000003.log
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,6 +1,6 @@
|
||||
2026/05/28-09:34:01.216 1690 Reusing MANIFEST U:\PythonProject\Janssen\Covance_UCO3001\browser_profile\Default\Local Storage\leveldb/MANIFEST-000001
|
||||
2026/05/28-09:34:01.223 1690 Recovering log #106
|
||||
2026/05/28-09:34:01.225 1690 Reusing old log U:\PythonProject\Janssen\Covance_UCO3001\browser_profile\Default\Local Storage\leveldb/000106.log
|
||||
2026/05/28-09:34:15.864 8d0 Level-0 table #110: started
|
||||
2026/05/28-09:34:15.951 8d0 Level-0 table #110: 24294 bytes OK
|
||||
2026/05/28-09:34:16.039 8d0 Delete type=0 #106
|
||||
2026/05/29-13:24:16.084 988 Reusing MANIFEST U:\PythonProject\Janssen\Covance_UCO3001\browser_profile\Default\Local Storage\leveldb/MANIFEST-000001
|
||||
2026/05/29-13:24:16.091 988 Recovering log #160
|
||||
2026/05/29-13:24:16.093 988 Reusing old log U:\PythonProject\Janssen\Covance_UCO3001\browser_profile\Default\Local Storage\leveldb/000160.log
|
||||
2026/05/29-13:24:33.012 6ac4 Level-0 table #165: started
|
||||
2026/05/29-13:24:33.073 6ac4 Level-0 table #165: 20511 bytes OK
|
||||
2026/05/29-13:24:33.150 6ac4 Delete type=0 #160
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
2026/05/28-09:20:53.814 57f0 Reusing MANIFEST U:\PythonProject\Janssen\Covance_UCO3001\browser_profile\Default\Local Storage\leveldb/MANIFEST-000001
|
||||
2026/05/28-09:20:53.820 57f0 Recovering log #106
|
||||
2026/05/28-09:20:53.822 57f0 Reusing old log U:\PythonProject\Janssen\Covance_UCO3001\browser_profile\Default\Local Storage\leveldb/000106.log
|
||||
2026/05/29-13:17:37.366 6a60 Reusing MANIFEST U:\PythonProject\Janssen\Covance_UCO3001\browser_profile\Default\Local Storage\leveldb/MANIFEST-000001
|
||||
2026/05/29-13:17:37.373 6a60 Recovering log #157
|
||||
2026/05/29-13:17:37.375 6a60 Reusing old log U:\PythonProject\Janssen\Covance_UCO3001\browser_profile\Default\Local Storage\leveldb/000157.log
|
||||
2026/05/29-13:17:51.674 6264 Level-0 table #161: started
|
||||
2026/05/29-13:17:51.748 6264 Level-0 table #161: 19877 bytes OK
|
||||
2026/05/29-13:17:51.815 6264 Delete type=0 #157
|
||||
2026/05/29-13:17:51.816 6264 Compacting 4@0 + 1@1 files
|
||||
2026/05/29-13:17:51.881 6264 Generated table #162@0: 24 keys, 30021 bytes
|
||||
2026/05/29-13:17:51.882 6264 Compacted 4@0 + 1@1 files => 30021 bytes
|
||||
2026/05/29-13:17:51.914 6264 compacted to: files[ 0 1 1 0 0 0 0 ]
|
||||
2026/05/29-13:17:51.915 6264 Delete type=2 #149
|
||||
2026/05/29-13:17:51.915 6264 Delete type=2 #152
|
||||
2026/05/29-13:17:51.915 6264 Delete type=2 #155
|
||||
2026/05/29-13:17:51.915 6264 Delete type=2 #158
|
||||
2026/05/29-13:17:51.915 6264 Delete type=2 #161
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1 +1 @@
|
||||
{"net":{"http_server_properties":{"servers":[{"anonymization":["GAAAABMAAABodHRwczovL2NvdmFuY2UuY29tAA==",false,0],"server":"https://login.labcorp.com","supports_spdy":true},{"anonymization":["GAAAABMAAABodHRwczovL2NvdmFuY2UuY29tAA==",false,0],"server":"https://cdn.pendo.io","supports_spdy":true},{"anonymization":["GAAAABMAAABodHRwczovL2NvdmFuY2UuY29tAA==",false,0],"server":"https://data.pendo.io","supports_spdy":true},{"alternative_service":[{"advertised_alpns":["h3"],"expiration":"13427014730141327","port":443,"protocol_str":"quic"}],"anonymization":["GAAAABMAAABodHRwczovL2NvdmFuY2UuY29tAA==",false,0],"network_stats":{"srtt":7242},"server":"https://content-autofill.googleapis.com","supports_spdy":true},{"anonymization":["GAAAABMAAABodHRwczovL2NvdmFuY2UuY29tAA==",true,0],"server":"https://login.labcorp.com","supports_spdy":true},{"alternative_service":[{"advertised_alpns":["h3"],"expiration":"13427019241807994","port":443,"protocol_str":"quic"}],"anonymization":["GAAAABIAAABodHRwczovL2dvb2dsZS5jb20AAA==",false,0],"network_stats":{"srtt":14394},"server":"https://accounts.google.com","supports_spdy":true},{"alternative_service":[{"advertised_alpns":["h3"],"expiration":"13424513642283952","port":443,"protocol_str":"quic"}],"anonymization":["GAAAABMAAABodHRwczovL2NvdmFuY2UuY29tAA==",false,0],"network_stats":{"srtt":3782},"server":"https://unpkg.com"},{"alternative_service":[{"advertised_alpns":["h3"],"expiration":"13427019251909434","port":443,"protocol_str":"quic"}],"anonymization":["GAAAABIAAABodHRwczovL2dvb2dsZS5jb20AAA==",false,0],"network_stats":{"srtt":8718},"server":"https://android.clients.google.com","supports_spdy":true},{"anonymization":["GAAAABMAAABodHRwczovL2xhYmNvcnAuY29tAA==",false,0],"network_stats":{"srtt":3267},"server":"https://fonts.gstatic.com"},{"alternative_service":[{"advertised_alpns":["h3"],"expiration":"13427012816734610","port":443,"protocol_str":"quic"}],"anonymization":["GAAAABMAAABodHRwczovL2xhYmNvcnAuY29tAA==",false,0],"network_stats":{"srtt":6914},"server":"https://fonts.googleapis.com","supports_spdy":true},{"anonymization":["GAAAABMAAABodHRwczovL2xhYmNvcnAuY29tAA==",false,0],"server":"https://login.labcorp.com","supports_spdy":true},{"alternative_service":[{"advertised_alpns":["h3"],"expiration":"13427019284329367","port":443,"protocol_str":"quic"}],"anonymization":["GAAAABMAAABodHRwczovL2xhYmNvcnAuY29tAA==",false,0],"network_stats":{"srtt":7280},"server":"https://content-autofill.googleapis.com","supports_spdy":true},{"alternative_service":[{"advertised_alpns":["h3"],"expiration":"13427019266698306","port":443,"protocol_str":"quic"}],"anonymization":["MAAAACwAAABodHRwczovL3Bhc3N3b3Jkc2xlYWtjaGVjay1wYS5nb29nbGVhcGlzLmNvbQ==",false,0],"network_stats":{"srtt":3684},"server":"https://passwordsleakcheck-pa.googleapis.com","supports_spdy":true},{"alternative_service":[{"advertised_alpns":["h3"],"expiration":"13424513657366598","port":443,"protocol_str":"quic"}],"anonymization":["GAAAABMAAABodHRwczovL2xhYmNvcnAuY29tAA==",false,0],"network_stats":{"srtt":6122},"server":"https://unpkg.com"},{"alternative_service":[{"advertised_alpns":["h3"],"expiration":"13427019241781650","port":443,"protocol_str":"quic"}],"anonymization":["GAAAABIAAABodHRwczovL2dvb2dsZS5jb20AAA==",false,0],"network_stats":{"srtt":5175},"server":"https://www.google.com","supports_spdy":true}],"supports_quic":{"address":"192.168.1.87","used_quic":true},"version":5},"network_qualities":{"CAESABiAgICA+P////8B":"4G"}}}
|
||||
{"net":{"http_server_properties":{"servers":[{"anonymization":["GAAAABMAAABodHRwczovL2NvdmFuY2UuY29tAA==",false,0],"server":"https://login.labcorp.com","supports_spdy":true},{"anonymization":["GAAAABMAAABodHRwczovL2NvdmFuY2UuY29tAA==",false,0],"server":"https://cdn.pendo.io","supports_spdy":true},{"anonymization":["GAAAABMAAABodHRwczovL2NvdmFuY2UuY29tAA==",false,0],"server":"https://data.pendo.io","supports_spdy":true},{"alternative_service":[{"advertised_alpns":["h3"],"expiration":"13427112006055382","port":443,"protocol_str":"quic"}],"anonymization":["GAAAABMAAABodHRwczovL2NvdmFuY2UuY29tAA==",false,0],"network_stats":{"srtt":6621},"server":"https://content-autofill.googleapis.com","supports_spdy":true},{"anonymization":["GAAAABMAAABodHRwczovL2NvdmFuY2UuY29tAA==",true,0],"server":"https://login.labcorp.com","supports_spdy":true},{"alternative_service":[{"advertised_alpns":["h3"],"expiration":"13427119456814562","port":443,"protocol_str":"quic"}],"anonymization":["GAAAABIAAABodHRwczovL2dvb2dsZS5jb20AAA==",false,0],"network_stats":{"srtt":10524},"server":"https://accounts.google.com","supports_spdy":true},{"alternative_service":[{"advertised_alpns":["h3"],"expiration":"13424613857546976","port":443,"protocol_str":"quic"}],"anonymization":["GAAAABMAAABodHRwczovL2NvdmFuY2UuY29tAA==",false,0],"network_stats":{"srtt":5917},"server":"https://unpkg.com","supports_spdy":true},{"alternative_service":[{"advertised_alpns":["h3"],"expiration":"13427107435975955","port":443,"protocol_str":"quic"}],"anonymization":["GAAAABMAAABodHRwczovL2xhYmNvcnAuY29tAA==",false,0],"network_stats":{"srtt":6306},"server":"https://fonts.googleapis.com","supports_spdy":true},{"anonymization":["GAAAABMAAABodHRwczovL2xhYmNvcnAuY29tAA==",false,0],"network_stats":{"srtt":6502},"server":"https://fonts.gstatic.com"},{"alternative_service":[{"advertised_alpns":["h3"],"expiration":"13427119484319015","port":443,"protocol_str":"quic"}],"anonymization":["MAAAACwAAABodHRwczovL3Bhc3N3b3Jkc2xlYWtjaGVjay1wYS5nb29nbGVhcGlzLmNvbQ==",false,0],"network_stats":{"srtt":4832},"server":"https://passwordsleakcheck-pa.googleapis.com","supports_spdy":true},{"alternative_service":[{"advertised_alpns":["h3"],"expiration":"13427119496814314","port":443,"protocol_str":"quic"}],"anonymization":["GAAAABIAAABodHRwczovL2dvb2dsZS5jb20AAA==",false,0],"network_stats":{"srtt":7750},"server":"https://android.clients.google.com","supports_spdy":true},{"anonymization":["GAAAABMAAABodHRwczovL2xhYmNvcnAuY29tAA==",false,0],"server":"https://login.labcorp.com","supports_spdy":true},{"alternative_service":[{"advertised_alpns":["h3"],"expiration":"13427119978225009","port":443,"protocol_str":"quic"}],"anonymization":["GAAAABMAAABodHRwczovL2xhYmNvcnAuY29tAA==",false,0],"network_stats":{"srtt":6055},"server":"https://content-autofill.googleapis.com","supports_spdy":true},{"alternative_service":[{"advertised_alpns":["h3"],"expiration":"13424614330059042","port":443,"protocol_str":"quic"}],"anonymization":["GAAAABMAAABodHRwczovL2xhYmNvcnAuY29tAA==",false,0],"network_stats":{"srtt":6720},"server":"https://unpkg.com"},{"alternative_service":[{"advertised_alpns":["h3"],"expiration":"13427119456718085","port":443,"protocol_str":"quic"}],"anonymization":["GAAAABIAAABodHRwczovL2dvb2dsZS5jb20AAA==",false,0],"network_stats":{"srtt":6576},"server":"https://www.google.com","supports_spdy":true}],"supports_quic":{"address":"192.168.1.87","used_quic":true},"version":5},"network_qualities":{"CAESABiAgICA+P////8B":"4G"}}}
|
||||
Binary file not shown.
@@ -1 +1 @@
|
||||
{"sts":[{"expiry":1811489682.936012,"host":"AMsYuZ7IFgNWVHHQOkqKt9MmwK65kaqBH0lsKFOUFH8=","mode":"force-https","sts_include_subdomains":true,"sts_observed":1779953682.936014},{"expiry":1811489656.619357,"host":"CSChKrlj3luqm4YaUcREYcNSGviDSaiJLYYv+tJJLQY=","mode":"force-https","sts_include_subdomains":true,"sts_observed":1779953656.619369},{"expiry":1811485129.395782,"host":"Cz1hgA9AsWBjlwLNwma1z9V2NS3WBGpE+hAisJYAzJg=","mode":"force-https","sts_include_subdomains":true,"sts_observed":1779949129.395788},{"expiry":1811485129.780754,"host":"bwzZzlAVpSjLBr+9/WzKm/QotRxOYnqfzWRcWGuF4/M=","mode":"force-https","sts_include_subdomains":true,"sts_observed":1779949129.780757},{"expiry":1811489684.552903,"host":"eS7TxKKR6KM2rUe5fGp4j6+eHcckwVadZAWT/TaU2RQ=","mode":"force-https","sts_include_subdomains":true,"sts_observed":1779953684.552906},{"expiry":1811489657.426032,"host":"e3SziuwfuO2UvuBno+qkR1ObHAzZmSUoJhrc7dbP1Uo=","mode":"force-https","sts_include_subdomains":true,"sts_observed":1779953657.426039},{"expiry":1811483216.73492,"host":"nAuqgR4iEWti7SOdT3UHPl6rmZU/DeaIm38P2O2OkgA=","mode":"force-https","sts_include_subdomains":false,"sts_observed":1779947216.734924},{"expiry":1810638866.791394,"host":"rBPMDlZ3oSoS5nWA5b3qh68smMftsPTTcEHPHa/8Asc=","mode":"force-https","sts_include_subdomains":true,"sts_observed":1779102866.791399},{"expiry":1811489641.782165,"host":"5EdUoB7YUY9zZV+2DkgVXgho8WUvp+D+6KpeUOhNQIM=","mode":"force-https","sts_include_subdomains":false,"sts_observed":1779953641.782169},{"expiry":1811489641.808069,"host":"8/RrMmQlCD2Gsp14wUCE1P8r7B2C5+yE0+g79IPyRsc=","mode":"force-https","sts_include_subdomains":true,"sts_observed":1779953641.808072}],"version":2}
|
||||
{"sts":[{"expiry":1811590376.982667,"host":"AMsYuZ7IFgNWVHHQOkqKt9MmwK65kaqBH0lsKFOUFH8=","mode":"force-https","sts_include_subdomains":true,"sts_observed":1780054376.982671},{"expiry":1811589873.806784,"host":"CSChKrlj3luqm4YaUcREYcNSGviDSaiJLYYv+tJJLQY=","mode":"force-https","sts_include_subdomains":true,"sts_observed":1780053873.80679},{"expiry":1811581653.541007,"host":"Cz1hgA9AsWBjlwLNwma1z9V2NS3WBGpE+hAisJYAzJg=","mode":"force-https","sts_include_subdomains":true,"sts_observed":1780045653.54101},{"expiry":1811582405.562701,"host":"bwzZzlAVpSjLBr+9/WzKm/QotRxOYnqfzWRcWGuF4/M=","mode":"force-https","sts_include_subdomains":true,"sts_observed":1780046405.562703},{"expiry":1811590379.118582,"host":"eS7TxKKR6KM2rUe5fGp4j6+eHcckwVadZAWT/TaU2RQ=","mode":"force-https","sts_include_subdomains":true,"sts_observed":1780054379.118586},{"expiry":1811590330.059279,"host":"e3SziuwfuO2UvuBno+qkR1ObHAzZmSUoJhrc7dbP1Uo=","mode":"force-https","sts_include_subdomains":true,"sts_observed":1780054330.059282},{"expiry":1811577835.976147,"host":"nAuqgR4iEWti7SOdT3UHPl6rmZU/DeaIm38P2O2OkgA=","mode":"force-https","sts_include_subdomains":false,"sts_observed":1780041835.97615},{"expiry":1810638866.791394,"host":"rBPMDlZ3oSoS5nWA5b3qh68smMftsPTTcEHPHa/8Asc=","mode":"force-https","sts_include_subdomains":true,"sts_observed":1779102866.791399},{"expiry":1811589856.718672,"host":"5EdUoB7YUY9zZV+2DkgVXgho8WUvp+D+6KpeUOhNQIM=","mode":"force-https","sts_include_subdomains":false,"sts_observed":1780053856.718678},{"expiry":1811589856.814671,"host":"8/RrMmQlCD2Gsp14wUCE1P8r7B2C5+yE0+g79IPyRsc=","mode":"force-https","sts_include_subdomains":true,"sts_observed":1780053856.814676}],"version":2}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.
@@ -1,3 +1,3 @@
|
||||
2026/05/28-09:34:01.390 1690 Reusing MANIFEST U:\PythonProject\Janssen\Covance_UCO3001\browser_profile\Default\Session Storage/MANIFEST-000001
|
||||
2026/05/28-09:34:01.393 1690 Recovering log #4
|
||||
2026/05/28-09:34:01.411 1690 Reusing old log U:\PythonProject\Janssen\Covance_UCO3001\browser_profile\Default\Session Storage/000004.log
|
||||
2026/05/29-13:24:16.313 988 Reusing MANIFEST U:\PythonProject\Janssen\Covance_UCO3001\browser_profile\Default\Session Storage/MANIFEST-000001
|
||||
2026/05/29-13:24:16.314 988 Recovering log #7
|
||||
2026/05/29-13:24:16.318 988 Reusing old log U:\PythonProject\Janssen\Covance_UCO3001\browser_profile\Default\Session Storage/000007.log
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
2026/05/28-09:20:53.976 57f0 Reusing MANIFEST U:\PythonProject\Janssen\Covance_UCO3001\browser_profile\Default\Session Storage/MANIFEST-000001
|
||||
2026/05/28-09:20:53.978 57f0 Recovering log #4
|
||||
2026/05/28-09:20:53.995 57f0 Reusing old log U:\PythonProject\Janssen\Covance_UCO3001\browser_profile\Default\Session Storage/000004.log
|
||||
2026/05/29-13:17:37.578 6a60 Reusing MANIFEST U:\PythonProject\Janssen\Covance_UCO3001\browser_profile\Default\Session Storage/MANIFEST-000001
|
||||
2026/05/29-13:17:37.580 6a60 Recovering log #7
|
||||
2026/05/29-13:17:37.611 6a60 Reusing old log U:\PythonProject\Janssen\Covance_UCO3001\browser_profile\Default\Session Storage/000007.log
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,3 +1,3 @@
|
||||
2026/05/28-09:34:01.159 3804 Reusing MANIFEST U:\PythonProject\Janssen\Covance_UCO3001\browser_profile\Default\Site Characteristics Database/MANIFEST-000001
|
||||
2026/05/28-09:34:01.161 3804 Recovering log #3
|
||||
2026/05/28-09:34:01.161 3804 Reusing old log U:\PythonProject\Janssen\Covance_UCO3001\browser_profile\Default\Site Characteristics Database/000003.log
|
||||
2026/05/29-13:24:16.027 3a94 Reusing MANIFEST U:\PythonProject\Janssen\Covance_UCO3001\browser_profile\Default\Site Characteristics Database/MANIFEST-000001
|
||||
2026/05/29-13:24:16.029 3a94 Recovering log #3
|
||||
2026/05/29-13:24:16.030 3a94 Reusing old log U:\PythonProject\Janssen\Covance_UCO3001\browser_profile\Default\Site Characteristics Database/000003.log
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
2026/05/28-09:20:53.754 56d0 Reusing MANIFEST U:\PythonProject\Janssen\Covance_UCO3001\browser_profile\Default\Site Characteristics Database/MANIFEST-000001
|
||||
2026/05/28-09:20:53.757 56d0 Recovering log #3
|
||||
2026/05/28-09:20:53.758 56d0 Reusing old log U:\PythonProject\Janssen\Covance_UCO3001\browser_profile\Default\Site Characteristics Database/000003.log
|
||||
2026/05/29-13:17:37.311 662c Reusing MANIFEST U:\PythonProject\Janssen\Covance_UCO3001\browser_profile\Default\Site Characteristics Database/MANIFEST-000001
|
||||
2026/05/29-13:17:37.316 662c Recovering log #3
|
||||
2026/05/29-13:17:37.316 662c Reusing old log U:\PythonProject\Janssen\Covance_UCO3001\browser_profile\Default\Site Characteristics Database/000003.log
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
2026/05/28-09:34:01.147 5b28 Reusing MANIFEST U:\PythonProject\Janssen\Covance_UCO3001\browser_profile\Default\Sync Data\LevelDB/MANIFEST-000001
|
||||
2026/05/28-09:34:01.153 5b28 Recovering log #3
|
||||
2026/05/28-09:34:01.153 5b28 Reusing old log U:\PythonProject\Janssen\Covance_UCO3001\browser_profile\Default\Sync Data\LevelDB/000003.log
|
||||
2026/05/29-13:24:16.017 6b88 Reusing MANIFEST U:\PythonProject\Janssen\Covance_UCO3001\browser_profile\Default\Sync Data\LevelDB/MANIFEST-000001
|
||||
2026/05/29-13:24:16.020 6b88 Recovering log #3
|
||||
2026/05/29-13:24:16.021 6b88 Reusing old log U:\PythonProject\Janssen\Covance_UCO3001\browser_profile\Default\Sync Data\LevelDB/000003.log
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
2026/05/28-09:20:53.750 28cc Reusing MANIFEST U:\PythonProject\Janssen\Covance_UCO3001\browser_profile\Default\Sync Data\LevelDB/MANIFEST-000001
|
||||
2026/05/28-09:20:53.756 28cc Recovering log #3
|
||||
2026/05/28-09:20:53.757 28cc Reusing old log U:\PythonProject\Janssen\Covance_UCO3001\browser_profile\Default\Sync Data\LevelDB/000003.log
|
||||
2026/05/29-13:17:37.299 6564 Reusing MANIFEST U:\PythonProject\Janssen\Covance_UCO3001\browser_profile\Default\Sync Data\LevelDB/MANIFEST-000001
|
||||
2026/05/29-13:17:37.306 6564 Recovering log #3
|
||||
2026/05/29-13:17:37.306 6564 Reusing old log U:\PythonProject\Janssen\Covance_UCO3001\browser_profile\Default\Sync Data\LevelDB/000003.log
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1 +1 @@
|
||||
v10*κE0Iω4ΗΎη4�"�7>ΑXι�zE@κ‰Λ[�ρ�σί�Ο‡!�,�σΥ³ξxΉ#PΎ“ρτΟΣπ
|
||||
v10Œ%4Â{}x|P„Ì1d’lðá[5²ilpÒ(÷ZON~ éøBîžl\Û´²ÂD”°�²%¥Ðɺê»
|
||||
Binary file not shown.
@@ -1,3 +1,3 @@
|
||||
2026/05/28-09:34:01.405 26f4 Reusing MANIFEST U:\PythonProject\Janssen\Covance_UCO3001\browser_profile\Default\shared_proto_db/MANIFEST-000001
|
||||
2026/05/28-09:34:01.406 26f4 Recovering log #3
|
||||
2026/05/28-09:34:01.408 26f4 Reusing old log U:\PythonProject\Janssen\Covance_UCO3001\browser_profile\Default\shared_proto_db/000003.log
|
||||
2026/05/29-13:24:16.339 6b88 Reusing MANIFEST U:\PythonProject\Janssen\Covance_UCO3001\browser_profile\Default\shared_proto_db/MANIFEST-000001
|
||||
2026/05/29-13:24:16.339 6b88 Recovering log #3
|
||||
2026/05/29-13:24:16.341 6b88 Reusing old log U:\PythonProject\Janssen\Covance_UCO3001\browser_profile\Default\shared_proto_db/000003.log
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
2026/05/28-09:20:53.995 28cc Reusing MANIFEST U:\PythonProject\Janssen\Covance_UCO3001\browser_profile\Default\shared_proto_db/MANIFEST-000001
|
||||
2026/05/28-09:20:53.996 28cc Recovering log #3
|
||||
2026/05/28-09:20:53.997 28cc Reusing old log U:\PythonProject\Janssen\Covance_UCO3001\browser_profile\Default\shared_proto_db/000003.log
|
||||
2026/05/29-13:17:37.597 5d84 Reusing MANIFEST U:\PythonProject\Janssen\Covance_UCO3001\browser_profile\Default\shared_proto_db/MANIFEST-000001
|
||||
2026/05/29-13:17:37.598 5d84 Recovering log #3
|
||||
2026/05/29-13:17:37.600 5d84 Reusing old log U:\PythonProject\Janssen\Covance_UCO3001\browser_profile\Default\shared_proto_db/000003.log
|
||||
|
||||
Binary file not shown.
@@ -1,3 +1,3 @@
|
||||
2026/05/28-09:34:01.398 26f4 Reusing MANIFEST U:\PythonProject\Janssen\Covance_UCO3001\browser_profile\Default\shared_proto_db\metadata/MANIFEST-000001
|
||||
2026/05/28-09:34:01.399 26f4 Recovering log #3
|
||||
2026/05/28-09:34:01.399 26f4 Reusing old log U:\PythonProject\Janssen\Covance_UCO3001\browser_profile\Default\shared_proto_db\metadata/000003.log
|
||||
2026/05/29-13:24:16.334 6b88 Reusing MANIFEST U:\PythonProject\Janssen\Covance_UCO3001\browser_profile\Default\shared_proto_db\metadata/MANIFEST-000001
|
||||
2026/05/29-13:24:16.334 6b88 Recovering log #3
|
||||
2026/05/29-13:24:16.335 6b88 Reusing old log U:\PythonProject\Janssen\Covance_UCO3001\browser_profile\Default\shared_proto_db\metadata/000003.log
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
2026/05/28-09:20:53.987 28cc Reusing MANIFEST U:\PythonProject\Janssen\Covance_UCO3001\browser_profile\Default\shared_proto_db\metadata/MANIFEST-000001
|
||||
2026/05/28-09:20:53.988 28cc Recovering log #3
|
||||
2026/05/28-09:20:53.988 28cc Reusing old log U:\PythonProject\Janssen\Covance_UCO3001\browser_profile\Default\shared_proto_db\metadata/000003.log
|
||||
2026/05/29-13:17:37.591 5d84 Reusing MANIFEST U:\PythonProject\Janssen\Covance_UCO3001\browser_profile\Default\shared_proto_db\metadata/MANIFEST-000001
|
||||
2026/05/29-13:17:37.592 5d84 Recovering log #3
|
||||
2026/05/29-13:17:37.592 5d84 Reusing old log U:\PythonProject\Janssen\Covance_UCO3001\browser_profile\Default\shared_proto_db\metadata/000003.log
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1 +1 @@
|
||||
{"autofill":{"ablation_seed":"Pts3KboF9Gg="},"breadcrumbs":{"enabled":false,"enabled_time":"13423576449583774"},"browser":{"shortcut_migration_version":"145.0.7632.6","whats_new":{"enabled_order":["ReadAnythingReadAloud","SideBySide","PdfInk2"]}},"chrome_labs_activation_threshold":89,"chrome_labs_new_badge_dict":{},"hardware_acceleration_mode_previous":true,"legacy":{"profile":{"name":{"migrated":true}}},"local":{"password_hash_data_list":[]},"management":{"platform":{"azure_active_directory":8,"enterprise_mdm_win":0}},"network_time":{"network_time_mapping":{"local":1.77994815531412e+12,"network":1.779948155236e+12,"ticks":4838234648.0,"uncertainty":10134021.0}},"os_crypt":{"audit_enabled":true,"encrypted_key":"RFBBUEkBAAAA0Iyd3wEV0RGMegDAT8KX6wEAAADkezkwH4wQT6pFnkj5e+nkEAAAADQAAABHAG8AbwBnAGwAZQAgAEMAaAByAG8AbQBlACAAZgBvAHIAIABUAGUAcwB0AGkAbgBnAAAAA2YAAMAAAAAQAAAAD8GdBnyhPqKO+3clLuoubgAAAAAEgAAAoAAAABAAAADMD85LpYqkKeWfmsz19kEEKAAAADmxXqsmFE4qCIFVf7fbf6Lkktqe+GmWXWIQTJb2EHCeDDclpUygwFUUAAAAxudMuaV2vqrqS22DRgyN4Bw1/aI="},"performance_intervention":{"last_daily_sample":"13424420803777120"},"policy":{"last_statistics_update":"13424420803455233"},"profile":{"info_cache":{"Default":{"active_time":1779952853.947476,"avatar_icon":"chrome://theme/IDR_PROFILE_AVATAR_26","background_apps":false,"default_avatar_fill_color":-2890755,"default_avatar_stroke_color":-16166200,"enterprise_label":"","force_signin_profile_locked":false,"gaia_given_name":"","gaia_id":"","gaia_name":"","hosted_domain":"","is_consented_primary_account":false,"is_ephemeral":false,"is_glic_eligible":false,"is_managed":0,"is_using_default_avatar":true,"is_using_default_name":true,"managed_user_id":"","metrics_bucket_index":1,"name":"Your Chromium","profile_color_seed":-16033840,"profile_highlight_color":-2890755,"signin.with_credential_provider":false,"user_name":""}},"last_active_profiles":["Default"],"metrics":{"next_bucket_index":2},"profile_counts_reported":"13424420803460889","profiles_order":["Default"]},"profile_network_context_service":{"http_cache_finch_experiment_groups":"None None None None"},"session_id_generator_last_value":"1074085646","signin":{"active_accounts_last_emitted":"13424420803387923"},"subresource_filter":{"ruleset_version":{"checksum":0,"content":"","format":0}},"tab_stats":{"discards_external":0,"discards_frozen":0,"discards_proactive":0,"discards_suggested":0,"discards_urgent":0,"last_daily_sample":"13424420803441717","max_tabs_per_window":2,"reloads_external":0,"reloads_frozen":0,"reloads_proactive":0,"reloads_suggested":0,"reloads_urgent":0,"total_tab_count_max":2,"window_count_max":1},"toast":{"non_milestone_update_toast_version":"145.0.7632.6"},"ukm":{"persisted_logs":[]},"uninstall_metrics":{"installation_date2":"1779102849"},"user_experience_metrics":{"client_id2":"43a4d29b-dfa2-4f8b-9a46-fe2351fed503","client_id_timestamp":"1779102849","limited_entropy_randomization_source":"795E664817D8383D9992183FB246CB96","log_record_id":29,"low_entropy_source3":4488,"machine_id":6709466,"pseudo_low_entropy_source":1702,"session_id":28,"stability":{"browser_last_live_timestamp":"13424427286217033","exited_cleanly":true,"saved_system_profile":"CLzkm8sGEhUxNDUuMC43NjMyLjYtNjQtZGV2ZWwYsOKr0AYiBWVuLVVTKhgKCldpbmRvd3MgTlQSCjEwLjAuMTkwNDQyfgoGeDg2XzY0EMb9ARiAgNT7sP8fIhlIUCBaMjMwIFRvd2VyIFdvcmtzdGF0aW9uKAMwgBQ4oAtCCggAEAAaADIAOgBNiU3PQlXibM9CZQAAwD9qGAoMR2VudWluZUludGVsEMONDBgIIAEoAIIBAIoBAKoBBng4Nl82NLABAUoKDW0jOl4V0IbiWUoKDZK3V7MV3xdKP0oKDQUO8PQVgI19ylAAaggIABAAOABAAIABsOKr0AaYAQD4AYgjgAL///////////8BiAIAkgIkNDNhNGQyOWItZGZhMi00ZjhiLTlhNDYtZmUyMzUxZmVkNTAzqAKmDbICoAFkHjnSnstQPTfirX7FiAAqfsiwbNubM6kLj70v2MAtsf5bz4dGdDQ67motfyp+LMUZDNsJTLRUE0n9H/9UH43yOKroA8plfhtjdx59eUuqaR4MRo3j+04u52w1YviiiC9v2UFh7INtJFaBCRbIQET+tRCH0tbGCqYb9QXxOQvdoidEg0ktvAubY+tt3VOfvs9ZduhzQuDj64SLuP9M8w1d8QKiJMcGz0wjig==","saved_system_profile_hash":"D84CCF82A85C1BE2C9DA241E7E58399ECE4C5019","stats_buildtime":"1768354364","stats_version":"145.0.7632.6-64-devel","system_crash_count":0}},"variations_google_groups":{"Default":[]},"was":{"restarted":false}}
|
||||
{"autofill":{"ablation_seed":"Pts3KboF9Gg="},"breadcrumbs":{"enabled":false,"enabled_time":"13423576449583774"},"browser":{"shortcut_migration_version":"145.0.7632.6","whats_new":{"enabled_order":["ReadAnythingReadAloud","SideBySide","PdfInk2"]}},"chrome_labs_activation_threshold":89,"chrome_labs_new_badge_dict":{},"hardware_acceleration_mode_previous":true,"legacy":{"profile":{"name":{"migrated":true}}},"local":{"password_hash_data_list":[]},"management":{"platform":{"azure_active_directory":8,"enterprise_mdm_win":0}},"network_time":{"network_time_mapping":{"local":1.780042333499841e+12,"network":1.780042333448e+12,"ticks":8161390296.0,"uncertainty":10134152.0}},"os_crypt":{"audit_enabled":true,"encrypted_key":"RFBBUEkBAAAA0Iyd3wEV0RGMegDAT8KX6wEAAADkezkwH4wQT6pFnkj5e+nkEAAAADQAAABHAG8AbwBnAGwAZQAgAEMAaAByAG8AbQBlACAAZgBvAHIAIABUAGUAcwB0AGkAbgBnAAAAA2YAAMAAAAAQAAAAD8GdBnyhPqKO+3clLuoubgAAAAAEgAAAoAAAABAAAADMD85LpYqkKeWfmsz19kEEKAAAADmxXqsmFE4qCIFVf7fbf6Lkktqe+GmWXWIQTJb2EHCeDDclpUygwFUUAAAAxudMuaV2vqrqS22DRgyN4Bw1/aI="},"performance_intervention":{"last_daily_sample":"13424515424338664"},"policy":{"last_statistics_update":"13424515424054832"},"profile":{"info_cache":{"Default":{"active_time":1780050927.615976,"avatar_icon":"chrome://theme/IDR_PROFILE_AVATAR_26","background_apps":false,"default_avatar_fill_color":-2890755,"default_avatar_stroke_color":-16166200,"enterprise_label":"","force_signin_profile_locked":false,"gaia_given_name":"","gaia_id":"","gaia_name":"","hosted_domain":"","is_consented_primary_account":false,"is_ephemeral":false,"is_glic_eligible":false,"is_managed":0,"is_using_default_avatar":true,"is_using_default_name":true,"managed_user_id":"","metrics_bucket_index":1,"name":"Your Chromium","profile_color_seed":-16033840,"profile_highlight_color":-2890755,"signin.with_credential_provider":false,"user_name":""}},"last_active_profiles":[],"metrics":{"next_bucket_index":2},"profile_counts_reported":"13424515424059085","profiles_order":["Default"]},"profile_network_context_service":{"http_cache_finch_experiment_groups":"None None None None"},"session_id_generator_last_value":"1074088497","signin":{"active_accounts_last_emitted":"13424515423988667"},"subresource_filter":{"ruleset_version":{"checksum":0,"content":"","format":0}},"tab_stats":{"discards_external":0,"discards_frozen":0,"discards_proactive":0,"discards_suggested":0,"discards_urgent":0,"last_daily_sample":"13424515424040457","max_tabs_per_window":2,"reloads_external":0,"reloads_frozen":0,"reloads_proactive":0,"reloads_suggested":0,"reloads_urgent":0,"total_tab_count_max":2,"window_count_max":1},"toast":{"non_milestone_update_toast_version":"145.0.7632.6"},"ukm":{"persisted_logs":[]},"uninstall_metrics":{"installation_date2":"1779102849"},"user_experience_metrics":{"client_id2":"43a4d29b-dfa2-4f8b-9a46-fe2351fed503","client_id_timestamp":"1779102849","limited_entropy_randomization_source":"795E664817D8383D9992183FB246CB96","log_record_id":50,"low_entropy_source3":4488,"machine_id":6709466,"pseudo_low_entropy_source":1702,"session_id":49,"stability":{"browser_last_live_timestamp":"13424527980612017","exited_cleanly":true,"saved_system_profile":"CLzkm8sGEhUxNDUuMC43NjMyLjYtNjQtZGV2ZWwYsOKr0AYiBWVuLVVTKhgKCldpbmRvd3MgTlQSCjEwLjAuMTkwNDQyfgoGeDg2XzY0EMb9ARiAgJiqpv8fIhlIUCBaMjMwIFRvd2VyIFdvcmtzdGF0aW9uKAMwgBQ4oAtCCggAEAAaADIAOgBNiU3PQlXibM9CZQAAwD9qGAoMR2VudWluZUludGVsEMONDBgIIAEoAIIBAIoBAKoBBng4Nl82NLABAUoKDW0jOl4V0IbiWUoKDZK3V7MV3xdKP0oKDQUO8PQVgI19ylAAaggIABAAOABAAIABsOKr0AaYAQD4AYgjgAL///////////8BiAIAkgIkNDNhNGQyOWItZGZhMi00ZjhiLTlhNDYtZmUyMzUxZmVkNTAzqAKmDbICqAFkHjnSnstQPTfirX7FiAAqfsiwbNubM6kLj70v2MAtsf5bz4dGdDQ67motfyp+LMUZDNsJTLRUE0n9H/9UH43yOKroA8plfhtjdx59eUuqaR4MRo2A+Qiq4/tOLsZ/zmTnbDVi+KKIL2/ZQWHsg20kVoEJFshARP61EIfS1sYKphv1BfE5C92iJ0SDSS28C5tj623dU5++z1l26HNC4OPrhIu4/0zzDV3xAlCbVVVSD3qO","saved_system_profile_hash":"9DC8C008783143F7F046BE60F9A2BA31B59BCA3C","stats_buildtime":"1768354364","stats_version":"145.0.7632.6-64-devel","system_crash_count":0}},"variations_google_groups":{"Default":[]},"was":{"restarted":false}}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,525 +0,0 @@
|
||||
import glob
|
||||
import os
|
||||
import shutil
|
||||
import pandas as pd
|
||||
from openpyxl import load_workbook
|
||||
from openpyxl.styles import Font, PatternFill, Border, Side, Alignment
|
||||
from openpyxl.utils import get_column_letter
|
||||
from datetime import date, datetime
|
||||
|
||||
# Paths
|
||||
src_dir = os.path.dirname(os.path.abspath(__file__)) + "/"
|
||||
out_dir = "U:/Dropbox/!!!Days/Downloads Z230/"
|
||||
|
||||
# Find source files
|
||||
src_files = glob.glob(src_dir + "Protocol 77242113UCO3001 - All Samples*.xlsx")
|
||||
assert src_files, "Source file not found!"
|
||||
src_file = src_files[0]
|
||||
print(f"Source xlsx: {src_file}")
|
||||
|
||||
csv_files = glob.glob(src_dir + "_EDCStdRpt-DataListing.csv")
|
||||
assert csv_files, "CSV file not found!"
|
||||
csv_file = csv_files[0]
|
||||
print(f"Source csv: {csv_file}")
|
||||
|
||||
kit_csv_files = glob.glob(src_dir + "sponsor-study-36940-kit-inventory-on-hand-expiration.csv")
|
||||
assert kit_csv_files, "Kit inventory CSV not found!"
|
||||
kit_csv_file = kit_csv_files[0]
|
||||
print(f"Kit csv: {kit_csv_file}")
|
||||
|
||||
eq_csv_files = glob.glob(src_dir + "sponsor-study-36940-activity-reports-documents-equery.csv")
|
||||
assert eq_csv_files, "eQuery CSV not found!"
|
||||
eq_csv_file = eq_csv_files[0]
|
||||
print(f"eQuery csv: {eq_csv_file}")
|
||||
|
||||
timestamp = datetime.now().strftime("%Y-%m-%d_%H%M%S")
|
||||
out_filename = f"{timestamp} 77242113UCO3001 CZE Labcorp samples and kit inventory report.xlsx"
|
||||
out_path = out_dir + out_filename
|
||||
|
||||
# Copy source file to output — preserves all formatting perfectly
|
||||
shutil.copy2(src_file, out_path)
|
||||
|
||||
# Load data with pandas for analysis
|
||||
df = pd.read_excel(src_file, sheet_name=0, header=0)
|
||||
|
||||
# All unique patients
|
||||
all_patients = sorted(df['Patient No.'].dropna().unique())
|
||||
|
||||
# BXSCR and DNA rows
|
||||
bxscr = df[df['Protocol Visit Code'] == 'BXSCR']
|
||||
dna = df[df['Protocol Visit Code'] == 'DNA']
|
||||
|
||||
# Parse date value to datetime object
|
||||
def fmt_date(val):
|
||||
if pd.isna(val):
|
||||
return None
|
||||
if isinstance(val, str):
|
||||
return datetime.strptime(val, '%d-%b-%Y')
|
||||
return pd.to_datetime(val).to_pydatetime()
|
||||
|
||||
OK_STATUSES = {'Received', 'In Inventory', 'Shipped'}
|
||||
|
||||
def get_specimen_info(visit_df, patient, specimen_type=None):
|
||||
rows = visit_df[visit_df['Patient No.'] == patient]
|
||||
if specimen_type:
|
||||
rows = rows[rows['Specimen Type'] == specimen_type]
|
||||
rows = rows[rows['Sample Status'].isin(OK_STATUSES)]
|
||||
if rows.empty:
|
||||
return '', None
|
||||
row = rows.iloc[0]
|
||||
return fmt_date(row['Container Receipt Date']), rows.index[0] + 2
|
||||
|
||||
def get_label_info(patient, label_code, visit_code):
|
||||
rows = df[(df['Patient No.'] == patient) &
|
||||
(df['Protocol Visit Code'] == visit_code) &
|
||||
(df['Container Label Line 1'] == label_code)]
|
||||
rows = rows[rows['Sample Status'].isin(OK_STATUSES)]
|
||||
if rows.empty:
|
||||
return '', None
|
||||
row = rows.iloc[0]
|
||||
return fmt_date(row['Container Receipt Date']), rows.index[0] + 2
|
||||
|
||||
# Open copied workbook and add analysis sheet
|
||||
out_wb = load_workbook(out_path)
|
||||
|
||||
# Rename and autofit first sheet
|
||||
src_ws = out_wb.active
|
||||
src_ws.title = "Zdroj"
|
||||
for col in src_ws.columns:
|
||||
max_len = max((len(str(cell.value)) if cell.value is not None else 0) for cell in col)
|
||||
src_ws.column_dimensions[get_column_letter(col[0].column)].width = min(max_len + 2, 50)
|
||||
|
||||
# ── Styly ────────────────────────────────────────────────────────────────────
|
||||
thin = Side(style='thin')
|
||||
border = Border(left=thin, right=thin, top=thin, bottom=thin)
|
||||
header_fill = PatternFill("solid", fgColor="4472C4")
|
||||
header_font = Font(name='Calibri', bold=True, size=11, color="FFFFFF")
|
||||
data_font = Font(name='Calibri', size=11)
|
||||
date_font_link = Font(name='Calibri', size=11, color="000000", underline='single')
|
||||
yes_fill = PatternFill("solid", fgColor="E2EFDA")
|
||||
no_fill = PatternFill("solid", fgColor="FFE7E7")
|
||||
sum_header_font = Font(name='Calibri', bold=True, size=11, color="000000")
|
||||
sum_total_font = Font(name='Calibri', bold=True, size=11)
|
||||
zero_font = Font(name='Calibri', size=11, color="BFBFBF")
|
||||
zero_red_font = Font(name='Calibri', size=11, color="C00000")
|
||||
dark_blue_fill = PatternFill("solid", fgColor="203764")
|
||||
orange_fill = PatternFill("solid", fgColor="FFF2CC")
|
||||
green_fill = PatternFill("solid", fgColor="E2EFDA")
|
||||
total_fill = PatternFill("solid", fgColor="D9E1F2")
|
||||
exp_fill = PatternFill("solid", fgColor="FFE7E7")
|
||||
ok_fill = PatternFill("solid", fgColor="E2EFDA")
|
||||
|
||||
# ── List: Přehled vzorků ──────────────────────────────────────────────────────
|
||||
analysis_ws = out_wb.create_sheet("Přehled vzorků")
|
||||
|
||||
columns = [
|
||||
("Investigator Name", 24),
|
||||
("Číslo pacienta", 20),
|
||||
("Máme biopsii SM11", 20),
|
||||
("Máme RNA", 16),
|
||||
("Máme Cryostor", 16),
|
||||
("DNA", 14),
|
||||
("PLASMPK I-0 TROUGH", 18),
|
||||
("PLASMA PK I-0 PEAK", 18),
|
||||
("SERUM ADA I-0 PRE", 18),
|
||||
("SM06/SERUM BIOM", 16),
|
||||
("SM07/WB RNA", 14),
|
||||
("SM10/FECAL", 14),
|
||||
("PLASMPK I-2 TROUGH", 18),
|
||||
("PLASMA PK I-2 PEAK", 18),
|
||||
("SERUM ADA I-2 PRE", 18),
|
||||
("STOOL I-2", 12),
|
||||
("PLASMPK I-4 TROUGH", 18),
|
||||
("PLASMA PK I-4 PEAK", 18),
|
||||
("SERUM ADA I-4 PRE", 18),
|
||||
("SM06/SERUM BIOM", 16),
|
||||
("SM07/WB RNA", 14),
|
||||
("STOOL I-4", 12),
|
||||
]
|
||||
|
||||
group_font = Font(name='Calibri', bold=True, size=11)
|
||||
group_fill = PatternFill("solid", fgColor="FFFFFF")
|
||||
group_border = Border(left=thin, right=thin, top=thin, bottom=thin)
|
||||
|
||||
groups = [
|
||||
(3, 5, "SCREENING"),
|
||||
(7, 12, "RANDOMIZACE I-0"),
|
||||
(13, 16, "I-2"),
|
||||
(17, 22, "I-4"),
|
||||
]
|
||||
for start_col, end_col, label in groups:
|
||||
analysis_ws.merge_cells(start_row=1, start_column=start_col, end_row=1, end_column=end_col)
|
||||
cell = analysis_ws.cell(row=1, column=start_col, value=label)
|
||||
cell.font = group_font
|
||||
cell.fill = group_fill
|
||||
cell.alignment = Alignment(horizontal='center', vertical='center')
|
||||
cell.border = group_border
|
||||
for c in range(start_col, end_col + 1):
|
||||
analysis_ws.cell(row=1, column=c).border = group_border
|
||||
|
||||
analysis_ws.row_dimensions[1].height = 20
|
||||
|
||||
for col_idx, (hdr, width) in enumerate(columns, 1):
|
||||
cell = analysis_ws.cell(row=2, column=col_idx, value=hdr)
|
||||
cell.font = header_font
|
||||
cell.fill = header_fill
|
||||
cell.border = border
|
||||
cell.alignment = Alignment(horizontal='center', vertical='center', wrap_text=True)
|
||||
analysis_ws.column_dimensions[get_column_letter(col_idx)].width = width
|
||||
|
||||
analysis_ws.row_dimensions[2].height = 30
|
||||
analysis_ws.freeze_panes = "C3"
|
||||
|
||||
src_sheet_name = out_wb.sheetnames[0]
|
||||
pat_sheet_name = "Seznam pacientů"
|
||||
|
||||
_csv_df_pre = pd.read_csv(csv_file, encoding='utf-8')
|
||||
_pat_pre = _csv_df_pre[['SiteNumber', 'Subject', 'Field4Value']].copy()
|
||||
_pat_pre['Field4Value'] = _pat_pre['Field4Value'].apply(lambda v: datetime.strptime(str(v).strip(), '%d %b %Y') if pd.notna(v) else None)
|
||||
_pat_pre = _pat_pre.sort_values(['SiteNumber', 'Subject', 'Field4Value']).reset_index(drop=True)
|
||||
patient_row_map = {}
|
||||
for i, row in _pat_pre.iterrows():
|
||||
pat = row['Subject']
|
||||
if pat not in patient_row_map:
|
||||
patient_row_map[pat] = i + 2
|
||||
|
||||
bxscr_patients = sorted(bxscr['Patient No.'].dropna().unique())
|
||||
|
||||
for row_idx, patient in enumerate(bxscr_patients, 3):
|
||||
investigator = bxscr[bxscr['Patient No.'] == patient].iloc[0]['Investigator Name']
|
||||
sm11, sm11_row = get_specimen_info(bxscr, patient, 'Tissue , Paraffin Block')
|
||||
rna, rna_row = get_specimen_info(bxscr, patient, 'Biopsy RNA Later')
|
||||
cryo, cryo_row = get_specimen_info(bxscr, patient, 'Biopsy, Frozen Tissue')
|
||||
dna_date, dna_row = get_specimen_info(dna, patient)
|
||||
trough, trough_row = get_label_info(patient, 'PLASMPK I-0 TROUGH', 'I-0')
|
||||
peak, peak_row = get_label_info(patient, 'PLASMA PK I-0 PEAK', 'I-0')
|
||||
ada, ada_row = get_label_info(patient, 'SERUM ADA I-0 PRE', 'I-0')
|
||||
sm06, sm06_row = get_label_info(patient, 'SM06/SERUM BIOM', 'I-0')
|
||||
sm07, sm07_row = get_label_info(patient, 'SM07/WB RNA', 'I-0')
|
||||
sm10, sm10_row = get_label_info(patient, 'SM10/FECAL', 'I-0')
|
||||
trough2, trough2_row = get_label_info(patient, 'PLASMPK I-2 TROUGH', 'I-2')
|
||||
peak2, peak2_row = get_label_info(patient, 'PLASMA PK I-2 PEAK', 'I-2')
|
||||
ada2, ada2_row = get_label_info(patient, 'SERUM ADA I-2 PRE', 'I-2')
|
||||
stool2, stool2_row = get_label_info(patient, 'STOOL I-2', 'I-2')
|
||||
trough4, trough4_row = get_label_info(patient, 'PLASMPK I-4 TROUGH', 'I-4')
|
||||
peak4, peak4_row = get_label_info(patient, 'PLASMA PK I-4 PEAK', 'I-4')
|
||||
ada4, ada4_row = get_label_info(patient, 'SERUM ADA I-4 PRE', 'I-4')
|
||||
sm064, sm064_row = get_label_info(patient, 'SM06/SERUM BIOM', 'I-4')
|
||||
sm074, sm074_row = get_label_info(patient, 'SM07/WB RNA', 'I-4')
|
||||
stool4, stool4_row = get_label_info(patient, 'STOOL I-4', 'I-4')
|
||||
|
||||
row_data = [investigator, patient,
|
||||
(sm11, sm11_row), (rna, rna_row), (cryo, cryo_row), (dna_date, dna_row),
|
||||
(trough, trough_row), (peak, peak_row), (ada, ada_row),
|
||||
(sm06, sm06_row), (sm07, sm07_row), (sm10, sm10_row),
|
||||
(trough2, trough2_row), (peak2, peak2_row), (ada2, ada2_row), (stool2, stool2_row),
|
||||
(trough4, trough4_row), (peak4, peak4_row), (ada4, ada4_row),
|
||||
(sm064, sm064_row), (sm074, sm074_row), (stool4, stool4_row)]
|
||||
|
||||
for col_idx, value in enumerate(row_data, 1):
|
||||
if col_idx <= 2:
|
||||
cell = analysis_ws.cell(row=row_idx, column=col_idx, value=value)
|
||||
if col_idx == 2 and patient in patient_row_map:
|
||||
cell.hyperlink = f"#'{pat_sheet_name}'!B{patient_row_map[patient]}"
|
||||
cell.font = Font(name='Calibri', size=11, underline='single')
|
||||
else:
|
||||
cell.font = data_font
|
||||
else:
|
||||
dt, excel_row = value
|
||||
cell = analysis_ws.cell(row=row_idx, column=col_idx, value=dt)
|
||||
if dt and excel_row is not None:
|
||||
cell.hyperlink = f"#'{src_sheet_name}'!A{excel_row}"
|
||||
cell.font = date_font_link
|
||||
cell.fill = yes_fill
|
||||
cell.number_format = 'DD-MMM-YYYY'
|
||||
else:
|
||||
cell.font = Font(name='Calibri', size=11, color="C00000")
|
||||
cell.fill = no_fill
|
||||
cell.border = border
|
||||
cell.alignment = Alignment(horizontal='center', vertical='center')
|
||||
|
||||
# ── List: Seznam pacientů ─────────────────────────────────────────────────────
|
||||
csv_df = pd.read_csv(csv_file, encoding='utf-8')
|
||||
patients_ws = out_wb.create_sheet("Seznam pacientů")
|
||||
|
||||
pat_columns = [
|
||||
("Číslo centra", 20),
|
||||
("Číslo pacienta", 20),
|
||||
("Kód návštěvy", 20),
|
||||
("Datum návštěvy", 16),
|
||||
("Typ návštěvy", 16),
|
||||
]
|
||||
|
||||
for col_idx, (col_name, width) in enumerate(pat_columns, 1):
|
||||
cell = patients_ws.cell(row=1, column=col_idx, value=col_name)
|
||||
cell.font = header_font
|
||||
cell.fill = header_fill
|
||||
cell.border = border
|
||||
cell.alignment = Alignment(horizontal='center', vertical='center', wrap_text=True)
|
||||
patients_ws.column_dimensions[get_column_letter(col_idx)].width = width
|
||||
|
||||
patients_ws.row_dimensions[1].height = 30
|
||||
patients_ws.freeze_panes = "A2"
|
||||
|
||||
def parse_date_edcstd(val):
|
||||
if pd.isna(val) or str(val).strip() == '':
|
||||
return None
|
||||
try:
|
||||
return datetime.strptime(str(val).strip(), '%d %b %Y')
|
||||
except:
|
||||
return None
|
||||
|
||||
pat_df = csv_df[['SiteNumber', 'Subject', 'InstanceName', 'Field4Value', 'Field5Value']].copy()
|
||||
pat_df['Field4Value'] = pat_df['Field4Value'].apply(parse_date_edcstd)
|
||||
pat_df = pat_df.sort_values(['SiteNumber', 'Subject', 'Field4Value']).reset_index(drop=True)
|
||||
|
||||
for row_idx, row in enumerate(pat_df.itertuples(index=False), 2):
|
||||
for col_idx, value in enumerate(row, 1):
|
||||
cell = patients_ws.cell(row=row_idx, column=col_idx, value=value)
|
||||
cell.font = data_font
|
||||
cell.border = border
|
||||
cell.alignment = Alignment(horizontal='center', vertical='center')
|
||||
if col_idx == 4 and value is not None:
|
||||
cell.number_format = 'DD-MMM-YYYY'
|
||||
|
||||
# ── Kit inventory — načtení a příprava dat ────────────────────────────────────
|
||||
kit_df_raw = pd.read_csv(kit_csv_file, encoding="utf-8")
|
||||
cze = kit_df_raw[kit_df_raw["Country"] == "CZE"].copy()
|
||||
|
||||
def parse_kit_date(val):
|
||||
if pd.isna(val):
|
||||
return None
|
||||
try:
|
||||
return datetime.strptime(str(val).strip(), "%b %d, %Y")
|
||||
except:
|
||||
return None
|
||||
|
||||
cze["Shipped Date"] = cze["Shipped Date"].apply(parse_kit_date)
|
||||
cze["Expiration Date"] = cze["Expiration Date"].apply(parse_kit_date)
|
||||
cze = cze.sort_values(["Site", "Kit Type", "Expiration Date"]).reset_index(drop=True)
|
||||
|
||||
today_dt = datetime.combine(date.today(), datetime.min.time())
|
||||
|
||||
def bucket(exp_date):
|
||||
if exp_date is None:
|
||||
return None
|
||||
return "soon" if (exp_date - today_dt).days <= 30 else "ok"
|
||||
|
||||
cze["_bucket"] = cze["Expiration Date"].apply(bucket)
|
||||
|
||||
kit_order = sorted(cze["Kit Type"].unique(), key=lambda x: (str(x).lstrip("T-").zfill(5), str(x)))
|
||||
kit_desc = cze.drop_duplicates("Kit Type").set_index("Kit Type")["Description"].to_dict()
|
||||
kit_sites = sorted(cze["Site"].unique())
|
||||
|
||||
# ── Pomocná funkce pro souhrnné tabulky ───────────────────────────────────────
|
||||
def write_summary_table(ws, current_row, title, rows_data, col_a_header):
|
||||
for c in range(1, 5):
|
||||
cell = ws.cell(row=current_row, column=c)
|
||||
cell.fill = dark_blue_fill
|
||||
cell.border = border
|
||||
ws.cell(row=current_row, column=1, value=title).font = Font(name='Calibri', bold=True, size=12, color="FFFFFF")
|
||||
ws.cell(row=current_row, column=1).alignment = Alignment(horizontal="left", vertical="center")
|
||||
ws.merge_cells(start_row=current_row, start_column=1, end_row=current_row, end_column=4)
|
||||
ws.row_dimensions[current_row].height = 22
|
||||
current_row += 1
|
||||
|
||||
for col_idx, (h, f) in enumerate(zip(
|
||||
[col_a_header, "Description", "Expiruje do 30 dní", "Expiruje později"],
|
||||
[header_fill, header_fill, orange_fill, green_fill]
|
||||
), 1):
|
||||
cell = ws.cell(row=current_row, column=col_idx, value=h)
|
||||
cell.font = sum_header_font
|
||||
cell.fill = f
|
||||
cell.border = border
|
||||
cell.alignment = Alignment(horizontal="center", vertical="center", wrap_text=True)
|
||||
ws.row_dimensions[current_row].height = 28
|
||||
current_row += 1
|
||||
|
||||
totals = [0, 0]
|
||||
for col_a, col_b, n_soon, n_ok in rows_data:
|
||||
totals[0] += n_soon
|
||||
totals[1] += n_ok
|
||||
all_zero = (n_soon == 0 and n_ok == 0)
|
||||
row_vals = [col_a, col_b, n_soon, n_ok]
|
||||
row_fills = [None, None,
|
||||
orange_fill if n_soon > 0 else None,
|
||||
green_fill if n_ok > 0 else None]
|
||||
for col_idx, (val, rfill) in enumerate(zip(row_vals, row_fills), 1):
|
||||
cell = ws.cell(row=current_row, column=col_idx, value=val)
|
||||
if col_idx >= 3 and val == 0:
|
||||
cell.font = zero_red_font if all_zero else zero_font
|
||||
else:
|
||||
cell.font = data_font
|
||||
cell.border = border
|
||||
cell.alignment = Alignment(horizontal="center" if col_idx >= 2 else "left", vertical="center")
|
||||
if rfill:
|
||||
cell.fill = rfill
|
||||
current_row += 1
|
||||
|
||||
for col_idx, val in enumerate(["CELKEM", "", totals[0], totals[1]], 1):
|
||||
cell = ws.cell(row=current_row, column=col_idx, value=val)
|
||||
cell.font = sum_total_font
|
||||
cell.fill = total_fill
|
||||
cell.border = border
|
||||
cell.alignment = Alignment(horizontal="center" if col_idx >= 2 else "left", vertical="center")
|
||||
current_row += 2
|
||||
return current_row
|
||||
|
||||
# ── List: Kit Inventory CZE ───────────────────────────────────────────────────
|
||||
kit_ws = out_wb.create_sheet("Kit Inventory CZE")
|
||||
|
||||
listing_columns = [
|
||||
("Project No.", 14),
|
||||
("Region", 10),
|
||||
("Country", 10),
|
||||
("Site", 38),
|
||||
("Kit Type", 12),
|
||||
("Description", 22),
|
||||
("Accession", 18),
|
||||
("Shipped Date", 16),
|
||||
("Expiration Date", 16),
|
||||
("Days to Expiration", 20),
|
||||
]
|
||||
|
||||
for col_idx, (hdr, width) in enumerate(listing_columns, 1):
|
||||
cell = kit_ws.cell(row=1, column=col_idx, value=hdr)
|
||||
cell.font = header_font
|
||||
cell.fill = header_fill
|
||||
cell.border = border
|
||||
cell.alignment = Alignment(horizontal="center", vertical="center", wrap_text=True)
|
||||
kit_ws.column_dimensions[get_column_letter(col_idx)].width = width
|
||||
|
||||
kit_ws.row_dimensions[1].height = 30
|
||||
kit_ws.freeze_panes = "A2"
|
||||
|
||||
for row_idx, row in enumerate(cze.itertuples(index=False), 2):
|
||||
days = row[9]
|
||||
for col_idx, (col_name, _) in enumerate(listing_columns, 1):
|
||||
value = row[col_idx - 1]
|
||||
cell = kit_ws.cell(row=row_idx, column=col_idx, value=value)
|
||||
cell.font = data_font
|
||||
cell.border = border
|
||||
cell.alignment = Alignment(horizontal="center", vertical="center")
|
||||
if col_name in ("Shipped Date", "Expiration Date") and value is not None:
|
||||
cell.number_format = "DD-MMM-YYYY"
|
||||
if col_name == "Days to Expiration":
|
||||
cell.fill = exp_fill if (pd.notna(days) and days <= 60) else ok_fill
|
||||
|
||||
kit_ws.auto_filter.ref = f"A1:{get_column_letter(len(listing_columns))}1"
|
||||
|
||||
# ── List: Přehled po centrech ─────────────────────────────────────────────────
|
||||
ctr_ws = out_wb.create_sheet("Přehled po centrech")
|
||||
ctr_ws.column_dimensions["A"].width = 22
|
||||
ctr_ws.column_dimensions["B"].width = 24
|
||||
ctr_ws.column_dimensions["C"].width = 22
|
||||
ctr_ws.column_dimensions["D"].width = 20
|
||||
|
||||
current_row = 1
|
||||
for site in kit_sites:
|
||||
site_df = cze[cze["Site"] == site]
|
||||
rows_data = []
|
||||
for kit in kit_order:
|
||||
desc = kit_desc.get(kit, "")
|
||||
kit_site_df = site_df[site_df["Kit Type"] == kit]
|
||||
n_soon = (kit_site_df["_bucket"] == "soon").sum()
|
||||
n_ok = (kit_site_df["_bucket"] == "ok").sum()
|
||||
rows_data.append((f"{kit} — {desc}", desc, n_soon, n_ok))
|
||||
current_row = write_summary_table(ctr_ws, current_row, site, rows_data, "Kit Type")
|
||||
|
||||
# ── List: Přehled po typech kitů ──────────────────────────────────────────────
|
||||
sum_ws = out_wb.create_sheet("Přehled po typech")
|
||||
sum_ws.column_dimensions["A"].width = 38
|
||||
sum_ws.column_dimensions["B"].width = 22
|
||||
sum_ws.column_dimensions["C"].width = 22
|
||||
sum_ws.column_dimensions["D"].width = 20
|
||||
|
||||
current_row = 1
|
||||
for kit in kit_order:
|
||||
desc = kit_desc.get(kit, "")
|
||||
kit_df = cze[cze["Kit Type"] == kit]
|
||||
rows_data = []
|
||||
for site in sorted(kit_df["Site"].unique()):
|
||||
site_df = kit_df[kit_df["Site"] == site]
|
||||
n_soon = (site_df["_bucket"] == "soon").sum()
|
||||
n_ok = (site_df["_bucket"] == "ok").sum()
|
||||
rows_data.append((site, desc, n_soon, n_ok))
|
||||
current_row = write_summary_table(sum_ws, current_row, f"Kit Type {kit} — {desc}", rows_data, "Centrum")
|
||||
|
||||
# ── List: eQueries ───────────────────────────────────────────────────────────
|
||||
eq_df = pd.read_csv(eq_csv_file, encoding="utf-8")
|
||||
eq_cze = eq_df[eq_df["Country"] == "CZECH REPUBLIC"].copy()
|
||||
|
||||
status_order = {"Open": 0, "Response Received": 1, "Closed": 2}
|
||||
eq_cze["_status_order"] = eq_cze["Status"].map(status_order).fillna(99)
|
||||
eq_cze = eq_cze.sort_values(["_status_order", "Site"]).reset_index(drop=True)
|
||||
|
||||
def parse_eq_date(val):
|
||||
if pd.isna(val):
|
||||
return None
|
||||
for fmt in ("%b %d, %Y %I:%M %p", "%b %d, %Y %I:%M %p"):
|
||||
try:
|
||||
return datetime.strptime(str(val).strip(), fmt)
|
||||
except:
|
||||
pass
|
||||
try:
|
||||
return datetime.strptime(str(val).strip().split(" 12:00")[0], "%b %d, %Y")
|
||||
except:
|
||||
return None
|
||||
|
||||
eq_ws = out_wb.create_sheet("eQueries")
|
||||
|
||||
eq_columns = [
|
||||
("Site", 36),
|
||||
("Subject", 14),
|
||||
("Visit", 20),
|
||||
("Visit Collection Date", 20),
|
||||
("Accession", 16),
|
||||
("eQueryId", 12),
|
||||
("Issue Type", 30),
|
||||
("Status", 18),
|
||||
("Create Date", 20),
|
||||
("Response Date Time", 20),
|
||||
("Time Before Response", 20),
|
||||
("User Name", 20),
|
||||
]
|
||||
|
||||
status_fills = {
|
||||
"Open": PatternFill("solid", fgColor="FFE7E7"),
|
||||
"Response Received": PatternFill("solid", fgColor="FFF2CC"),
|
||||
"Closed": PatternFill("solid", fgColor="E2EFDA"),
|
||||
}
|
||||
|
||||
for col_idx, (hdr, width) in enumerate(eq_columns, 1):
|
||||
cell = eq_ws.cell(row=1, column=col_idx, value=hdr)
|
||||
cell.font = header_font
|
||||
cell.fill = header_fill
|
||||
cell.border = border
|
||||
cell.alignment = Alignment(horizontal="center", vertical="center", wrap_text=True)
|
||||
eq_ws.column_dimensions[get_column_letter(col_idx)].width = width
|
||||
|
||||
eq_ws.row_dimensions[1].height = 30
|
||||
eq_ws.freeze_panes = "A2"
|
||||
|
||||
for row_idx, row in enumerate(eq_cze.itertuples(index=False), 2):
|
||||
status = row[eq_cze.columns.get_loc("Status")]
|
||||
rfill = status_fills.get(status)
|
||||
for col_idx, (col_name, _) in enumerate(eq_columns, 1):
|
||||
value = row[eq_cze.columns.get_loc(col_name)]
|
||||
if col_name in ("Visit Collection Date", "Create Date", "Response Date Time"):
|
||||
value = parse_eq_date(value)
|
||||
cell = eq_ws.cell(row=row_idx, column=col_idx, value=value)
|
||||
cell.font = data_font
|
||||
cell.border = border
|
||||
cell.alignment = Alignment(horizontal="center" if col_idx > 1 else "left", vertical="center")
|
||||
if col_name in ("Visit Collection Date", "Create Date", "Response Date Time") and value:
|
||||
cell.number_format = "DD-MMM-YYYY HH:MM"
|
||||
if rfill:
|
||||
cell.fill = rfill
|
||||
|
||||
eq_ws.auto_filter.ref = f"A1:{get_column_letter(len(eq_columns))}1"
|
||||
|
||||
out_wb.save(out_path)
|
||||
print(f"Saved: {out_path}")
|
||||
print(f"Patients with BXSCR: {len(bxscr_patients)}, All unique patients: {len(all_patients)}")
|
||||
print(f"CZE kit rows: {len(cze)}, Kit types: {len(kit_order)}, Sites: {len(kit_sites)}")
|
||||
print(f"CZE eQueries: {len(eq_cze)} (Open: {(eq_cze['Status']=='Open').sum()}, Response Received: {(eq_cze['Status']=='Response Received').sum()}, Closed: {(eq_cze['Status']=='Closed').sum()})")
|
||||
@@ -23,6 +23,9 @@ PROFILE_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "browser_
|
||||
def login(page):
|
||||
page.goto(LOGIN_URL)
|
||||
page.wait_for_load_state("networkidle")
|
||||
if not page.get_by_label("Email").is_visible():
|
||||
print(f"Session aktivni, prihlasen: {page.url}")
|
||||
return
|
||||
page.get_by_label("Email").fill(EMAIL)
|
||||
page.get_by_role("button", name="Next").click()
|
||||
page.wait_for_load_state("networkidle")
|
||||
|
||||
@@ -45,6 +45,9 @@ REPORTS = [
|
||||
def login(page):
|
||||
page.goto(LOGIN_URL)
|
||||
page.wait_for_load_state("networkidle")
|
||||
if not page.get_by_label("Email").is_visible():
|
||||
print(f"Session aktivni, prihlasen: {page.url}")
|
||||
return
|
||||
page.get_by_label("Email").fill(EMAIL)
|
||||
page.get_by_role("button", name="Next").click()
|
||||
page.wait_for_load_state("networkidle")
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
# =============================================================================
|
||||
# Název: download_samples_report_v1.1.py
|
||||
# Verze: 1.1
|
||||
# Datum: 2026-05-28
|
||||
# Datum: 2026-05-29
|
||||
# Popis: Automatické stažení CSV reportu All Samples ze xsp.labcorp.com pro
|
||||
# studie 77242113UCO3001 (study 36940) a 42847922MDD3003 (study 35472).
|
||||
# Aplikuje country filtr CZ, date range od FROM_DATE, čeká na stabilní
|
||||
# Record Count. Výstup do příslušné složky Source/ každé studie.
|
||||
# Aplikuje country filtr CZ, date range od FROM_DATE, čeká na zmizení
|
||||
# "Fetching Data" před exportem. Výstup do Source/ každé studie.
|
||||
# =============================================================================
|
||||
from playwright.sync_api import sync_playwright
|
||||
from datetime import datetime
|
||||
@@ -31,6 +31,9 @@ TILE_SUFFIX = "allSamples"
|
||||
def login(page):
|
||||
page.goto(LOGIN_URL)
|
||||
page.wait_for_load_state("networkidle", timeout=120000)
|
||||
if not page.get_by_label("Email").is_visible():
|
||||
print(f"Session aktivni, prihlasen: {page.url}")
|
||||
return
|
||||
page.get_by_label("Email").fill(EMAIL)
|
||||
page.get_by_role("button", name="Next").click()
|
||||
page.wait_for_load_state("networkidle", timeout=120000)
|
||||
@@ -83,19 +86,14 @@ def export_tile(page, tile_label, file_suffix, timestamp, study_id, out_dir):
|
||||
page.wait_for_load_state("networkidle", timeout=120000)
|
||||
page.wait_for_timeout(3000)
|
||||
|
||||
# Čekej dokud:
|
||||
# 1. se neobjeví "No Data Available" (= record count je 0), nebo
|
||||
# 2. record count není nenulový
|
||||
page.wait_for_function("""() => {
|
||||
const noData = document.querySelector('div.table-row.no-data');
|
||||
if (noData) return true;
|
||||
const countEl = document.querySelector('div.grid-count span');
|
||||
if (countEl) {
|
||||
const n = parseInt(countEl.innerText.trim().replace(/,/g, ''), 10);
|
||||
return !isNaN(n) && n > 0;
|
||||
}
|
||||
return false;
|
||||
}""", timeout=30000)
|
||||
# Čekej až zmizí "Fetching Data": po filtru 5s, pak opakuj kontrolu každých 5s
|
||||
page.wait_for_timeout(5000)
|
||||
for _ in range(24): # max 2 minuty
|
||||
if not page.get_by_text("Fetching Data").is_visible():
|
||||
break
|
||||
print(" Fetching Data... cekam 5s")
|
||||
page.wait_for_timeout(5000)
|
||||
page.wait_for_timeout(5000) # extra buffer po zmizení
|
||||
|
||||
if page.locator("div.table-row.no-data").is_visible():
|
||||
print(f" Record Count: 0 — preskakuji.")
|
||||
@@ -128,7 +126,12 @@ if __name__ == "__main__":
|
||||
context = p.chromium.launch_persistent_context(
|
||||
user_data_dir=PROFILE_DIR,
|
||||
headless=False,
|
||||
args=["--disable-blink-features=AutomationControlled", "--start-maximized"],
|
||||
args=[
|
||||
"--disable-blink-features=AutomationControlled",
|
||||
"--start-maximized",
|
||||
"--disable-restore-session-state",
|
||||
"--disable-session-crashed-bubble",
|
||||
],
|
||||
user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36",
|
||||
accept_downloads=True,
|
||||
no_viewport=True,
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
# Název: download_test_results_v1.0.py
|
||||
# Verze: 1.0
|
||||
# Datum: 2026-05-28
|
||||
# Popis: Stahuje Standard Test Results ze Covance/Labcorp XSP
|
||||
# pro studii 77242113UCO3001 (study 36940, report 930556).
|
||||
# Login přes xsp.covance.com → Okta, pak přímá URL na report.
|
||||
# Výstup: timestampované CSV do adresáře Source/.
|
||||
|
||||
from playwright.sync_api import sync_playwright
|
||||
from datetime import datetime
|
||||
import os
|
||||
|
||||
EMAIL = "vbuzalka@its.jnj.com"
|
||||
PASSWORD = "%zT3Wqfc9)cWua5"
|
||||
LOGIN_URL = "https://xsp.covance.com/"
|
||||
OUT_DIR = r"U:\PythonProject\Janssen\Covance_UCO3001\Source"
|
||||
PROFILE_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "browser_profile")
|
||||
|
||||
REPORTS = [
|
||||
{
|
||||
"site": "CZ10003",
|
||||
"url": "https://xsp.labcorp.com/sponsor/study/36940/test-results/930556/standard-test-results",
|
||||
"filename": "sponsor-study-36940-test-results-930556-standard-test-results.csv",
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
def login(page):
|
||||
page.goto(LOGIN_URL)
|
||||
page.wait_for_load_state("networkidle")
|
||||
page.get_by_label("Email").fill(EMAIL)
|
||||
page.get_by_role("button", name="Next").click()
|
||||
page.wait_for_load_state("networkidle")
|
||||
page.get_by_label("Password").fill(PASSWORD)
|
||||
page.get_by_role("button", name="Verify").click()
|
||||
page.wait_for_url(lambda url: "code=" not in url, timeout=60000)
|
||||
page.wait_for_load_state("networkidle", timeout=60000)
|
||||
page.wait_for_timeout(2000)
|
||||
print(f"Prihlaseni OK: {page.url}")
|
||||
|
||||
|
||||
def download_report(page, report):
|
||||
print(f"\n--- {report['site']} ---")
|
||||
page.goto(report["url"])
|
||||
page.wait_for_load_state("networkidle", timeout=60000)
|
||||
page.wait_for_timeout(3000)
|
||||
print(f"Report nacteny: {page.url}")
|
||||
|
||||
# Kliknout 3 tečky (more_horiz) vpravo od filtrovacího řádku
|
||||
page.locator("button").filter(has_text="more_horiz").last.click()
|
||||
|
||||
timestamp = datetime.now().strftime("%Y-%m-%d_%H%M%S")
|
||||
dest = os.path.join(OUT_DIR, f"{timestamp} {report['filename']}")
|
||||
|
||||
with page.expect_download(timeout=60000) as dl:
|
||||
page.get_by_text("Export to CSV").click()
|
||||
|
||||
dl.value.save_as(dest)
|
||||
print(f"Stazeno: {dest}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
with sync_playwright() as p:
|
||||
context = p.chromium.launch_persistent_context(
|
||||
user_data_dir=PROFILE_DIR,
|
||||
headless=False,
|
||||
args=["--disable-blink-features=AutomationControlled", "--start-maximized"],
|
||||
no_viewport=True,
|
||||
user_agent=(
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
|
||||
"AppleWebKit/537.36 (KHTML, like Gecko) "
|
||||
"Chrome/136.0.0.0 Safari/537.36"
|
||||
),
|
||||
accept_downloads=True,
|
||||
)
|
||||
context.add_init_script(
|
||||
"Object.defineProperty(navigator, 'webdriver', {get: () => undefined})"
|
||||
)
|
||||
page = context.new_page()
|
||||
login(page)
|
||||
for report in REPORTS:
|
||||
download_report(page, report)
|
||||
context.close()
|
||||
@@ -1,3 +1,7 @@
|
||||
# app.py | v1.0 | 2026-05-29
|
||||
# FastAPI server pro příjem .msg a .db souborů a upload do Dropboxu.
|
||||
# Endpointy: /upload (.msg → /msgs), /upload-db (.db → /msgs/db), /upload-dropbox (→ Dropbox /!!!Days/Downloads Z230).
|
||||
|
||||
from fastapi import FastAPI, UploadFile, File, Header, HTTPException
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
@@ -47,6 +51,8 @@ async def upload_db(
|
||||
raise HTTPException(status_code=401, detail="Unauthorized")
|
||||
if not file.filename.endswith(".db"):
|
||||
raise HTTPException(status_code=400, detail="Only .db files accepted")
|
||||
for old in DB_DIR.glob("*.db"):
|
||||
old.unlink()
|
||||
dest = DB_DIR / file.filename
|
||||
with dest.open("wb") as f:
|
||||
shutil.copyfileobj(file.file, f)
|
||||
|
||||
Reference in New Issue
Block a user