feat: v3.0 - canvas editor, JSON-only, no Excel, new UI
Some checks failed
Build & Push Docker / build (push) Has been cancelled
Some checks failed
Build & Push Docker / build (push) Has been cancelled
- Remove all Excel code (import, export, template, pandas, openpyxl) - New canvas-based schedule editor with drag & drop (interact.js) - Modern 3-panel UI: sidebar, canvas, documentation tab - New data model: Block with id/date/start/end, ProgramType with id/name/color - Clean API: GET /api/health, POST /api/validate, GET /api/sample, POST /api/generate-pdf - Rewritten PDF generator using ScenarioDocument directly (no DataFrame) - Professional PDF output: dark header, colored blocks, merged cells, legend, footer - Sample JSON: "Zimní výjezd oddílu" with 11 blocks, 3 program types - 30 tests passing (API, core models, PDF generation) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,100 +1,95 @@
|
||||
"""
|
||||
PDF generation tests.
|
||||
PDF generation tests for Scenar Creator v3.
|
||||
"""
|
||||
|
||||
import pandas as pd
|
||||
import pytest
|
||||
from datetime import time
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from app.core.pdf_generator import generate_pdf
|
||||
from app.core.validator import ScenarsError
|
||||
from app.main import app
|
||||
from app.models.event import ScenarioDocument, Block, ProgramType, EventInfo
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def client():
|
||||
return TestClient(app)
|
||||
def make_doc(**kwargs):
|
||||
defaults = {
|
||||
"version": "1.0",
|
||||
"event": EventInfo(title="Test PDF", subtitle="Subtitle"),
|
||||
"program_types": [
|
||||
ProgramType(id="ws", name="Workshop", color="#0070C0"),
|
||||
],
|
||||
"blocks": [
|
||||
Block(id="b1", date="2026-03-01", start="09:00", end="10:00",
|
||||
title="Test Program", type_id="ws", responsible="John"),
|
||||
]
|
||||
}
|
||||
defaults.update(kwargs)
|
||||
return ScenarioDocument(**defaults)
|
||||
|
||||
|
||||
def test_generate_pdf_basic():
|
||||
df = pd.DataFrame({
|
||||
'Datum': [pd.Timestamp('2025-11-13').date()],
|
||||
'Zacatek': [time(9, 0)],
|
||||
'Konec': [time(10, 0)],
|
||||
'Program': ['Test Program'],
|
||||
'Typ': ['WORKSHOP'],
|
||||
'Garant': ['John Doe'],
|
||||
'Poznamka': ['Test note'],
|
||||
})
|
||||
|
||||
descriptions = {'WORKSHOP': 'Workshop Type'}
|
||||
colors = {'WORKSHOP': 'FF0070C0'}
|
||||
|
||||
pdf_bytes = generate_pdf(df, "Test PDF", "PDF Detail", descriptions, colors)
|
||||
|
||||
doc = make_doc()
|
||||
pdf_bytes = generate_pdf(doc)
|
||||
assert isinstance(pdf_bytes, bytes)
|
||||
assert len(pdf_bytes) > 0
|
||||
assert pdf_bytes[:5] == b'%PDF-'
|
||||
|
||||
|
||||
def test_generate_pdf_multiday():
|
||||
df = pd.DataFrame({
|
||||
'Datum': [pd.Timestamp('2025-11-13').date(), pd.Timestamp('2025-11-14').date()],
|
||||
'Zacatek': [time(9, 0), time(14, 0)],
|
||||
'Konec': [time(10, 0), time(15, 0)],
|
||||
'Program': ['Day 1', 'Day 2'],
|
||||
'Typ': ['KEYNOTE', 'WORKSHOP'],
|
||||
'Garant': ['Alice', 'Bob'],
|
||||
'Poznamka': [None, 'Hands-on'],
|
||||
})
|
||||
|
||||
descriptions = {'KEYNOTE': 'Keynote', 'WORKSHOP': 'Workshop'}
|
||||
colors = {'KEYNOTE': 'FFFF0000', 'WORKSHOP': 'FF0070C0'}
|
||||
|
||||
pdf_bytes = generate_pdf(df, "Multi-day", "Two days", descriptions, colors)
|
||||
|
||||
doc = make_doc(
|
||||
program_types=[
|
||||
ProgramType(id="key", name="Keynote", color="#FF0000"),
|
||||
ProgramType(id="ws", name="Workshop", color="#0070C0"),
|
||||
],
|
||||
blocks=[
|
||||
Block(id="b1", date="2026-03-01", start="09:00", end="10:00",
|
||||
title="Day 1", type_id="key", responsible="Alice"),
|
||||
Block(id="b2", date="2026-03-02", start="14:00", end="15:00",
|
||||
title="Day 2", type_id="ws", responsible="Bob"),
|
||||
]
|
||||
)
|
||||
pdf_bytes = generate_pdf(doc)
|
||||
assert isinstance(pdf_bytes, bytes)
|
||||
assert pdf_bytes[:5] == b'%PDF-'
|
||||
|
||||
|
||||
def test_generate_pdf_empty_data():
|
||||
df = pd.DataFrame(columns=['Datum', 'Zacatek', 'Konec', 'Program', 'Typ', 'Garant', 'Poznamka'])
|
||||
|
||||
def test_generate_pdf_empty_blocks():
|
||||
doc = make_doc(blocks=[])
|
||||
with pytest.raises(ScenarsError):
|
||||
generate_pdf(df, "Empty", "Detail", {}, {})
|
||||
generate_pdf(doc)
|
||||
|
||||
|
||||
def test_generate_pdf_missing_type():
|
||||
df = pd.DataFrame({
|
||||
'Datum': [pd.Timestamp('2025-11-13').date()],
|
||||
'Zacatek': [time(9, 0)],
|
||||
'Konec': [time(10, 0)],
|
||||
'Program': ['Test'],
|
||||
'Typ': ['UNKNOWN'],
|
||||
'Garant': [None],
|
||||
'Poznamka': [None],
|
||||
})
|
||||
|
||||
doc = make_doc(
|
||||
blocks=[
|
||||
Block(id="b1", date="2026-03-01", start="09:00", end="10:00",
|
||||
title="Test", type_id="UNKNOWN"),
|
||||
]
|
||||
)
|
||||
with pytest.raises(ScenarsError):
|
||||
generate_pdf(df, "Test", "Detail", {}, {})
|
||||
generate_pdf(doc)
|
||||
|
||||
|
||||
def test_generate_pdf_api(client):
|
||||
doc = {
|
||||
"event": {"title": "PDF Test", "detail": "API PDF"},
|
||||
"program_types": [{"code": "WS", "description": "Workshop", "color": "#0070C0"}],
|
||||
"blocks": [{
|
||||
"datum": "2025-11-13",
|
||||
"zacatek": "09:00:00",
|
||||
"konec": "10:00:00",
|
||||
"program": "Opening",
|
||||
"typ": "WS",
|
||||
"garant": "John",
|
||||
"poznamka": "Note"
|
||||
}]
|
||||
}
|
||||
r = client.post("/api/generate-pdf", json=doc)
|
||||
assert r.status_code == 200
|
||||
assert r.headers["content-type"] == "application/pdf"
|
||||
assert r.content[:5] == b'%PDF-'
|
||||
def test_generate_pdf_with_event_info():
|
||||
doc = make_doc(
|
||||
event=EventInfo(
|
||||
title="Full Event",
|
||||
subtitle="With all fields",
|
||||
date="2026-03-01",
|
||||
location="Prague"
|
||||
)
|
||||
)
|
||||
pdf_bytes = generate_pdf(doc)
|
||||
assert pdf_bytes[:5] == b'%PDF-'
|
||||
|
||||
|
||||
def test_generate_pdf_multiple_blocks_same_day():
|
||||
doc = make_doc(
|
||||
blocks=[
|
||||
Block(id="b1", date="2026-03-01", start="09:00", end="10:00",
|
||||
title="Morning", type_id="ws"),
|
||||
Block(id="b2", date="2026-03-01", start="10:00", end="11:30",
|
||||
title="Midday", type_id="ws"),
|
||||
Block(id="b3", date="2026-03-01", start="14:00", end="16:00",
|
||||
title="Afternoon", type_id="ws", responsible="Team"),
|
||||
]
|
||||
)
|
||||
pdf_bytes = generate_pdf(doc)
|
||||
assert pdf_bytes[:5] == b'%PDF-'
|
||||
|
||||
Reference in New Issue
Block a user