diff --git a/report_completo.py b/report_completo.py
index 1ac3faf..28b7d6e 100644
--- a/report_completo.py
+++ b/report_completo.py
@@ -1,152 +1,171 @@
from pathlib import Path
-from pylatex import (Document, Section, Subsection, Command, Figure,
- Tabular, Math, NoEscape, Package)
-from pylatex.utils import bold
+from reportlab.lib.pagesizes import A4
+from reportlab.lib.units import cm
+from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
+from reportlab.lib import colors
+from reportlab.platypus import (SimpleDocTemplate, Paragraph, Spacer, Table,
+ TableStyle, Image, HRFlowable, PageBreak)
BASE = Path(__file__).parent
INDUTTORI = [
{
- "nome": "Induttore 1",
+ "nome": "Induttore 1",
"intervallo": "110.81 – 111.56 s",
- "t0": "110.81",
- "t_end": "111.56",
- "n_punti": "5",
- "zona_exc": None,
- "A": "87.3140",
- "A_err": "0.0260",
- "tau": "15.7231",
- "tau_err": "0.2021",
- "r2": "0.999505",
- "png": BASE / "induttore1" / "induttore1.png",
+ "t0": "110.81",
+ "t_end": "111.56",
+ "n_punti": "5",
+ "zona_exc": None,
+ "A": "87.3140",
+ "A_err": "0.0260",
+ "tau": "15.7231",
+ "tau_err": "0.2021",
+ "r2": "0.999505",
+ "png": BASE / "induttore1" / "induttore1.png",
},
{
- "nome": "Induttore 2",
+ "nome": "Induttore 2",
"intervallo": "112.02 – 113.95 s",
- "t0": "112.02",
- "t_end": "113.95",
- "n_punti": "13",
- "zona_exc": None,
- "A": "158.3430",
- "A_err": "0.2088",
- "tau": "13.2844",
- "tau_err": "0.2183",
- "r2": "0.997314",
- "png": BASE / "induttore2" / "induttore2.png",
+ "t0": "112.02",
+ "t_end": "113.95",
+ "n_punti": "13",
+ "zona_exc": None,
+ "A": "158.3430",
+ "A_err": "0.2088",
+ "tau": "13.2844",
+ "tau_err": "0.2183",
+ "r2": "0.997314",
+ "png": BASE / "induttore2" / "induttore2.png",
},
{
- "nome": "Induttore 3",
+ "nome": "Induttore 3",
"intervallo": "115.0 – 133.7 s",
- "t0": "115.0",
- "t_end": "133.7",
- "n_punti": "125",
- "zona_exc": "115.9 – 117.2 s",
- "A": "185.1847",
- "A_err": "0.2706",
- "tau": "16.2654",
- "tau_err": "0.0464",
- "r2": "0.993827",
- "png": BASE / "induttore3" / "induttore3.png",
+ "t0": "115.0",
+ "t_end": "133.7",
+ "n_punti": "125",
+ "zona_exc": "115.9 – 117.2 s",
+ "A": "185.1847",
+ "A_err": "0.2706",
+ "tau": "16.2654",
+ "tau_err": "0.0464",
+ "r2": "0.993827",
+ "png": BASE / "induttore3" / "induttore3.png",
},
]
+HEADER_COLOR = colors.HexColor("#2c5f8a")
+LIGHT_GRAY = colors.HexColor("#f2f2f2")
+
+TABLE_STYLE = TableStyle([
+ ("BACKGROUND", (0, 0), (-1, 0), HEADER_COLOR),
+ ("TEXTCOLOR", (0, 0), (-1, 0), colors.white),
+ ("FONTNAME", (0, 0), (-1, 0), "Helvetica-Bold"),
+ ("FONTSIZE", (0, 0), (-1, 0), 10),
+ ("ROWBACKGROUNDS", (0, 1), (-1, -1), [colors.white, LIGHT_GRAY]),
+ ("FONTNAME", (0, 1), (-1, -1), "Helvetica"),
+ ("FONTSIZE", (0, 1), (-1, -1), 9),
+ ("GRID", (0, 0), (-1, -1), 0.5, colors.HexColor("#cccccc")),
+ ("TOPPADDING", (0, 0), (-1, -1), 5),
+ ("BOTTOMPADDING", (0, 0), (-1, -1), 5),
+ ("LEFTPADDING", (0, 0), (-1, -1), 8),
+])
+
+
def build_doc():
- doc = Document(geometry_options={"margin": "2.5cm"})
+ out = str(BASE / "report_completo.pdf")
+ doc = SimpleDocTemplate(out, pagesize=A4,
+ leftMargin=2.5*cm, rightMargin=2.5*cm,
+ topMargin=2.5*cm, bottomMargin=2.5*cm)
- doc.packages.append(Package("booktabs"))
- doc.packages.append(Package("graphicx"))
- doc.packages.append(Package("amsmath"))
- doc.packages.append(Package("float"))
- doc.packages.append(Package("caption"))
- doc.packages.append(Package("inputenc", options="utf8"))
+ styles = getSampleStyleSheet()
+ style_title = styles["Title"]
+ style_h1 = styles["Heading1"]
+ style_h2 = styles["Heading2"]
+ style_body = styles["BodyText"]
+ style_eq = ParagraphStyle("Equation", parent=styles["BodyText"],
+ fontName="Courier", fontSize=10,
+ leftIndent=20, spaceAfter=6)
- # Titolo
- doc.preamble.append(Command("title", "Report Analisi Termica Forno"))
- doc.preamble.append(Command("author", "Analisi IR -- Cevolani"))
- doc.preamble.append(Command("date", NoEscape(r"\today")))
- doc.append(NoEscape(r"\maketitle"))
+ story = []
- # Introduzione
- with doc.create(Section("Modello di fitting")):
- doc.append(
- "Ogni intervallo è interpolato con il modello di raffreddamento di Newton:"
- )
- doc.append(Math(data=[NoEscape(
- r"T(t) = T_\infty + A \cdot e^{-(t - t_0)/\tau}"
- )], escape=False))
- doc.append(NoEscape(
- r"dove $T_\infty = 22.99\,^\circ\mathrm{C}$ è la temperatura ambiente media ponderata. "
- r"La regressione non lineare usa il metodo \textit{Trust Region Reflective} (TRF) "
- r"di \texttt{scipy.optimize.curve\_fit}."
- ))
+ # --- Titolo ---
+ story.append(Paragraph("Report Analisi Termica Forno", style_title))
+ story.append(Paragraph("Analisi IR – Cevolani", styles["Normal"]))
+ story.append(Spacer(1, 0.5*cm))
+ story.append(HRFlowable(width="100%", thickness=1, color=HEADER_COLOR))
+ story.append(Spacer(1, 0.5*cm))
- # Sezione per ogni induttore
+ # --- Modello ---
+ story.append(Paragraph("Modello di fitting", style_h1))
+ story.append(Paragraph(
+ "Ogni intervallo è interpolato con il modello di raffreddamento di Newton:",
+ style_body))
+ story.append(Spacer(1, 0.2*cm))
+ story.append(Paragraph("T(t) = T_inf + A · exp( -(t - t0) / τ )", style_eq))
+ story.append(Paragraph(
+ "dove T_inf = 22.99 °C è la temperatura ambiente media ponderata. "
+ "La regressione non lineare usa il metodo Trust Region Reflective (TRF) "
+ "di scipy.optimize.curve_fit.",
+ style_body))
+ story.append(Spacer(1, 0.4*cm))
+
+ # --- Sezione per ogni induttore ---
for ind in INDUTTORI:
- with doc.create(Section(ind["nome"])):
+ story.append(HRFlowable(width="100%", thickness=0.5, color=colors.lightgrey))
+ story.append(Spacer(1, 0.2*cm))
+ story.append(Paragraph(ind["nome"], style_h1))
- with doc.create(Subsection("Intervallo di campionamento")):
- with doc.create(Tabular("|l|l|")) as tab:
- tab.add_hline()
- tab.add_row([bold("Parametro"), bold("Valore")])
- tab.add_hline()
- tab.add_row(["Inizio", f"{ind['t0']} s"])
- tab.add_row(["Fine", f"{ind['t_end']} s"])
- tab.add_row(["Numero di punti", ind["n_punti"]])
- if ind["zona_exc"]:
- tab.add_row(["Zona esclusa (peso = 0)", ind["zona_exc"]])
- tab.add_hline()
+ # Tabella intervallo
+ story.append(Paragraph("Intervallo di campionamento", style_h2))
+ rows = [["Parametro", "Valore"],
+ ["Inizio", f"{ind['t0']} s"],
+ ["Fine", f"{ind['t_end']} s"],
+ ["Numero di punti", ind["n_punti"]]]
+ if ind["zona_exc"]:
+ rows.append(["Zona esclusa (peso = 0)", ind["zona_exc"]])
+ t = Table(rows, colWidths=[8*cm, 6*cm])
+ t.setStyle(TABLE_STYLE)
+ story.append(t)
+ story.append(Spacer(1, 0.3*cm))
- with doc.create(Subsection("Equazione trovata")):
- t0 = ind["t0"]
- A = ind["A"]
- tau = ind["tau"]
- doc.append(Math(data=[NoEscape(
- rf"T(t) = 22.99 + {A} \cdot e^{{-(t - {t0}) / {tau}}}"
- )], escape=False))
+ # Equazione
+ story.append(Paragraph("Equazione trovata", style_h2))
+ eq = (f"T(t) = 22.99 + {ind['A']} · exp( -(t - {ind['t0']}) / {ind['tau']} )")
+ story.append(Paragraph(eq, style_eq))
+ story.append(Spacer(1, 0.2*cm))
- doc.append(NoEscape(r"\vspace{4pt}"))
- with doc.create(Tabular("|l|l|l|")) as tab:
- tab.add_hline()
- tab.add_row([bold("Parametro"), bold("Valore"), bold("Incertezza (1σ)")])
- tab.add_hline()
- tab.add_row(["A",
- NoEscape(rf"${ind['A']}\,^\circ\mathrm{{C}}$"),
- NoEscape(rf"$\pm\,{ind['A_err']}\,^\circ\mathrm{{C}}$")])
- tab.add_row([NoEscape(r"$\tau$"),
- NoEscape(rf"${ind['tau']}\,\mathrm{{s}}$"),
- NoEscape(rf"$\pm\,{ind['tau_err']}\,\mathrm{{s}}$")])
- tab.add_hline()
+ # Tabella parametri
+ param_rows = [
+ ["Parametro", "Valore", "Incertezza (1σ)"],
+ ["A", f"{ind['A']} °C", f"± {ind['A_err']} °C"],
+ ["τ", f"{ind['tau']} s", f"± {ind['tau_err']} s"],
+ ["R²", ind["r2"], ""],
+ ]
+ t2 = Table(param_rows, colWidths=[4*cm, 6*cm, 5*cm])
+ t2.setStyle(TABLE_STYLE)
+ story.append(t2)
+ story.append(Spacer(1, 0.3*cm))
- doc.append(NoEscape(r"\vspace{4pt}"))
- doc.append(NoEscape(rf"$R^2 = {ind['r2']}$"))
+ # Plot
+ story.append(Paragraph("Grafico", style_h2))
+ img = Image(str(ind["png"]), width=14*cm, height=8.4*cm)
+ story.append(img)
+ story.append(Spacer(1, 0.5*cm))
- with doc.create(Subsection("Grafico")):
- with doc.create(Figure(position="H")) as fig:
- fig.add_image(str(ind["png"]), width=NoEscape(r"0.9\textwidth"))
- fig.add_caption(f"Fit esponenziale — {ind['nome']} [{ind['intervallo']}]")
+ # --- Tabella riepilogativa ---
+ story.append(PageBreak())
+ story.append(Paragraph("Riepilogo", style_h1))
+ summary = [["Induttore", "Intervallo [s]", "A [°C]", "τ [s]", "R²"]]
+ for ind in INDUTTORI:
+ summary.append([ind["nome"], ind["intervallo"],
+ ind["A"], ind["tau"], ind["r2"]])
+ t3 = Table(summary, colWidths=[3.5*cm, 4.5*cm, 3*cm, 3*cm, 3*cm])
+ t3.setStyle(TABLE_STYLE)
+ story.append(t3)
- # Tabella riepilogativa
- with doc.create(Section("Riepilogo")):
- with doc.create(Tabular("|l|l|l|l|l|")) as tab:
- tab.add_hline()
- tab.add_row([bold("Induttore"), bold("Intervallo [s]"),
- bold("A [°C]"), bold(NoEscape(r"$\tau$ [s]")), bold(NoEscape(r"$R^2$"))])
- tab.add_hline()
- for ind in INDUTTORI:
- tab.add_row([ind["nome"], ind["intervallo"],
- ind["A"], ind["tau"], ind["r2"]])
- tab.add_hline()
+ doc.build(story)
+ print(f"report_completo.pdf generato.")
- out = BASE / "report_completo"
- doc.generate_tex(str(out))
- print(f"report_completo.tex generato.")
-
- try:
- doc.generate_pdf(str(out), clean_tex=False, compiler="pdflatex")
- print("report_completo.pdf generato.")
- except Exception as e:
- print(f"Compilazione PDF non disponibile ({e}).")
- print("Installa MikTeX o TeX Live per generare il PDF da report_completo.tex.")
if __name__ == "__main__":
build_doc()
diff --git a/requirements.txt b/requirements.txt
index 4a791a3..7685747 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,4 +1,4 @@
pandas
matplotlib
scipy
-pylatex
+reportlab