Mikrofab SuiteMikrofab SuiteMeasurement & Analysis
Manual  /  8. Reporting and Export
TR
Simulation · v5.85.0

Reporting and Export

This chapter covers the full set of report and export outputs produced by the Mikrofab measurement and analysis software: how a measurement becomes a traceable, machine-readable and tamper-evident record, which presentation forms (HTML/PDF) are derived from that record, which table formats (CSV/TXT/XLSX/JSON) are written, and how the files are named.

ℹ️
Note Core principle — one model, many formats. A report is the official, traceable record of a measurement. First a canonical data object (JSON) is produced; every human-facing presentation (HTML, PDF, on-screen tables) is derived from this single object. This way "the number you see on screen" and "the number that is archived" always come from the same source; the format may change, but the data never does.
Data and Reports workspace main screen
Figure. The Data and Reports workspace: report generation and the library are gathered here.
🎓 What is it for? — What reporting and export are, and why they exist

You ran a measurement and saw a nice plot on screen — but will that result still stand as reliable evidence tomorrow, a month from now, or on another computer? Reporting is exactly what guarantees this: it turns the measurement into a permanent official record that carries who/what/when/which instrument information and that reveals any tampering. Export then converts this record into formats (CSV, Excel, PDF, JSON) that others can open and use.

  • Why it is done: so the result is traceable, shareable and archivable — "I saw it on screen" is not enough; evidence is required.
  • What it teaches / measures: which files a measurement is written to, what each file carries, and which one counts as the "official record".
  • Where it is used: lab notebook, quality control, customer delivery, attachments to papers/reports, and long-term archiving.

1. Overview: Three Report Paths

The software produces reports/outputs from three different places. They all share the same discipline (header + sha256 integrity + "missing != zero" + deterministic output), but they serve different purposes.

Report pathWhere producedTriggerCanonical outputPresentation
Measurement reportAutomatically when a measurement finishesEvery save (DataWriter.save)*_report.json + *_report.csv(Optional) "Generate report" → HTML + PDF
Analysis reportAnalysis workspace"Export" menureport_jsonreport_pdf (HTML → Qt PDF)
Composed reportData and Reports workspace"Generate report" button*_rapor.jsonHTML + PDF (+ CSV)

Architecturally, the flow is as follows:

                          +- write_json  (canonical, machine-readable, checksum source)
   report dict -----------+- write_csv   (table)             -- DATA format
   (build_report / compose)
                          +- build_report_html (escaped)        -- PRESENTATION (derived from data)
                                                                    PDF: Qt QTextDocument

The report engine lives inside this repository, under app/reporting/; there is no shared external reporting package. The core files:

  • app/reporting/report_model.py — canonical measurement report model + JSON/CSV writers.
  • app/reporting/report_builder.py — pure HTML generator (build_report_html).
  • app/reporting/analysis_report.py — analysis report (header + payload, same sealing discipline).
  • app/reporting/report_compose.py — the Report Builder engine that merges multiple sources.
  • app/reporting/report_index.py — the index that scans and lists generated reports (database-free).
  • app/storage/data_writer.py — the layer that writes raw + report files during/after a measurement.
  • app/gui/analyze/exporters.py — Analysis workspace export writers.

2. Measurement Report (Automatic, on Every Save)

🎓 What is it for? — Measurement report (automatic record)

This is the "default" output you get without pressing any button: every time a measurement finishes, the software automatically opens a folder for that run and writes both the raw data and the official record to disk. Like jotting a note in your lab notebook the instant something happens during an experiment — every run becomes a traceable record without you doing anything extra.

  • Why it is done: so no measurement is ever lost; so every run lands by itself in a unique, date-and-time-stamped folder.
  • What it teaches / measures: exactly which files (CSV, TXT, XLSX, canonical JSON...) a run is written to on disk.
  • Where it is used: every day, on every measurement — this is your fundamental data archive; the other report paths are built on top of it.

When a measurement completes, DataWriter.save(...) is called and all files for that single run are written into the subfolder belonging to that run. This is the software's "default" report generator: without the user pressing any extra button, every measurement lands on disk as a traceable record.

Measurement workspace screen
Figure. The Measurement workspace: every completed run is automatically saved to its own folder.

2.1 Run folder and file naming

Every measurement run is written to its own subfolder under the output folder. The folder name is generated by run_folder_name(...):

<output_folder>/YYYY-MM-DD-<Day>-<Type>-<HHMMSS>/
example: measurements/2026-06-12-Wed-PV-103045/
PartDescriptionExample
YYYY-MM-DDRun date2026-06-12
<Day>3-letter day abbreviation, according to the interface language (not the OS locale)Wed / Çar
<Type>Measurement family abbreviation (from the instrument family)TFT / Thin / PV / Gen
<HHMMSS>Run time (makes each run unique)103045
ℹ️
Note (OneDrive compatibility): Because the trailing HHMMSS makes each run unique, the same folder is never written concurrently on two machines; this prevents OneDrive from producing a PC-named conflict copy (such as ..._IK4ULCE-7). The PC name is not added to the folder name.

The base name (base_name) of the files inside the folder is:

<prefix>_<TFT>_<MOD>_<YYYYMMDD_HHMMSS>_<suffix>

If the prefix (filename_prefix) and suffix (filename_suffix) are empty they are skipped; forbidden file characters (<>:"/\|?* and control characters) are cleaned with sanitize_token.

2.2 Files written

DataWriter.save writes the following formats by default (configurable via export_formats; the default is ("csv", "txt", "xlsx") + always the report + metadata):

FileFormatSeparator / encodingContent
<base>.csvRaw data tablecomma, UTF-8CSV_COLUMNS (13 columns, raw points)
<base>.txtRaw data tabletab, UTF-8Same columns, tab-separated
<base>.xlsxExcel workbookopenpyxl"measurement" sheet + "Summary" sheet
<base>_metadata.jsonFree-form metadataUTF-8, indentedInstrument identity, parameters, summary, counts
<base>_report.jsonCanonical reportUTF-8, sorted keysHeader + columns + rows + sha256 seal
<base>_report.csvCanonical tablecomma, UTF-8 + BOMFamily columns, dot-decimal
<base>.h5(Optional) HDF5h5pyBinary scientific format; seal preserved
<base>_partial.csvCrash-safe interim filecomma, UTF-8During the run; deleted on successful completion

The raw-data CSV/TXT/XLSX carry these 13 columns (CSV_COLUMNS):

timestamp, measurement_mode, tft_id, sweep_direction, step_index,
vds_set, vgs_set, vds_measured, vgs_measured, ids, igs, compliance_hit, elapsed_s
Tip (crash-safe writing): In long measurements (Bias Stress, Recipe) each point is appended to *_partial.csv immediately and flushed to disk. If the application crashes or power is lost, the data up to that moment is preserved. When the measurement finishes successfully and the final files are written, the partial file is deleted; otherwise it remains on disk and can later be reloaded with read_points_csv.

2.3 Canonical report (*_report.json) — Header / Traceability

🎓 What is it for? — Canonical report and the traceability header

The canonical report (*_report.json) is the machine-readable file considered the "single source of truth" for a measurement; every number you see on screen is derived from it. The header is the identity card at the top of this file: like the manufacturing information on a medicine box, it answers which instrument, which version, which sample, and when.

  • Why it is done: so the result and its origin (instrument/version/time/sample) are stored together, inseparably.
  • What it teaches / measures: where a measurement came from; that unknown fields are left as null rather than invented (the honesty rule).
  • Where it is used: accredited/traceable laboratory records, audits, and answering the question "where did this number come from?".

The canonical report is produced by report_model.build_report(...) and carries a three-level traceability header. The table below shows the top-level fields of the report and their source.

FieldDescriptionSource / rule
schema_versionSchema versionConstant "1.0"
report_idUnique report identifieruuid4().hex
report_revisionRevision numberIncrements on revision; reports are not silently edited
levelReport level"base" / "customer" / "accredited" (default customer)
statusRun status"complete" / "partial" — an interrupted run is not hidden
created_atGeneration UTC (ms)ISO-8601, ...Z; comes from the caller (determinism)
created_at_localOperator local timecontext.started_at (display only)
app_id / app_versionSoftware identity/versionConstant "tft-measurement" + __version__
recipe{name, version}context.mode + software version
station{id, serial}VISA resource + instrument IDN (serial number is not PII)
calibration{status, cert_no, valid_until}Currently a stub (status:"unknown"); PLANNED for accredited
sample{id, lot, wafer}tft_id; lot/wafer are currently null
operatorOperatorNo field in the application → not invented, null
environmentOutdoor environment headerOpen-Meteo instantaneous value; labeled outdoor_*
simulatedMock/real flag"MOCK"/"SIM" in the instrument IDN → true; null if unknown
⚠️
Warning (honesty rule): Information the software does not have is never invented; an unknown field is written as null (e.g. operator). The simulated flag stays null if there is no IDN — the report never claims to be a real measurement. This is the foundation of accredited-laboratory traceability.
ℹ️
Note (environment header): The environment block is an outdoor (Open-Meteo) indicator; it is NOT the laboratory indoor condition that actually affects the instrument. For this reason the keys are explicitly labeled outdoor_temp_c, outdoor_humidity_pct, outdoor_cloud_cover_pct, outdoor_condition, with the source open-meteo. If there is no data, all fields are null.

2.4 Column families — the right schema per measurement

🎓 What is it for? — Column families (the right schema per measurement)

Every measurement type produces different quantities: for a transistor, gate/drain voltages are meaningful, whereas for a solar cell, power (W) is meaningful. Column families automatically fill the report's table with the columns appropriate to that measurement — like a toolbox where the right tool shows up for each job, so you do not see pointless "Vgs is always 0" columns in a solar-cell report.

  • Why it is done: so each measurement family carries only its own meaningful columns, with no misleading/empty columns.
  • What it teaches / measures: which measurement mode produces which columns (and derived quantities, e.g. PV power P, resistance R).
  • Where it is used: obtaining correct, readable tables when reporting different instrument/measurement types with the same software.

An important design decision: the columns of the report table are chosen by measurement family. In older versions there was a single fixed schema, TFT-shaped (Vds/Vgs/Ids/Igs); a solar-cell report therefore looked like a "transistor report", with Vgs/Igs always coming out as 0. Now each family picks its own columns; because all writers (JSON/CSV/HDF5) use report["columns"], the correct schema is applied automatically.

Measurement modeFamilyColumns (unit)
TRANSFER, IV, PULSED_IV, BIAS_STRESS…tftvds_set(V), vgs_set(V), vds_measured(V), vgs_measured(V), ids(A), igs(A)
DIODE, SCHOTTKY, T_IV, KELVIN, PROBE_IVtwo_terminalv_set(V), v_measured(V), i(A)
PV_JV, PV_DARK_JV, PV_HYST, PV_MPPT…pvv_set(V), v_measured(V), i(A), p(W)
FOUR_POINT, VDPfour_probei(A), v_measured(V)
ENDURANCEendurancecycle, state, v_read(V), v_measured(V), i_read(A), resistance_ohm(Ω)

Common head/tail columns across all families: point (integer), step_index, sweep_direction, elapsed_s (s), status. The unit and type live in the column definition and are not repeated in each row (single source); the label locale comes from i18n via label_key.

Derived-column formulas:

Formula PV power (p): for each point P = |V_measured · I| (unit: W). Inputs are vds_measured (V) and ids (A); if either is missing the output is null (missing != zero).
Formula Endurance read resistance (resistance_ohm): R = |V_measured / I| (unit: Ω). If the current is 0 or missing, the output is null.

2.5 Rows and status — "Missing != zero"

🎓 What is it for? — Row status and the "missing ≠ zero" principle

During a measurement, some points may be unmeasurable, or the instrument may hit a limit. The software does not hide these cases: it places a status (ok / na / fail) label on every row and never fills an unmeasured value with 0 — it leaves it empty (null). Like the difference between "left blank" and "0 points" on a questionnaire — confusing the two leads to a wrong interpretation of the result.

  • Why it is done: so unmeasured data and genuinely-zero data are not confused, and so analysis is not corrupted by fake zeros.
  • What it teaches / measures: whether each point is valid (ok), could not be measured (na), or hit a limit (fail).
  • Where it is used: when assessing data quality and understanding how missing points affect the result.

_point_to_row(...) converts each MeasurementPoint into a row. Non-numeric / NaN / Inf values map to null (never to 0). Each row's status field:

StatusMeaningCondition
okValid measurementAll other cases
naCould not be measuredAll of the family's "na attributes" are None (e.g. both ids and igs in TFT)
failLimit/compliance hitcompliance_hit = true (the compliance limit was hit)
"rows": [
  { "point": 1, "vds_set": 0.0, "ids": 1.2e-6, "igs": 1e-9, "status": "ok"   },
  { "point": 2, "vds_set": 0.1, "ids": null,   "igs": null, "status": "na"   },  // missing != 0
  { "point": 3, "vds_set": 0.2, "ids": 2.0e-3, "igs": 1e-9, "status": "fail" }   // compliance
]
⚠️
Warning (partial run): If len(points) < parameters.step_count, the report status automatically becomes status="partial"; an interrupted run is never shown as if it had "completed". This is a requirement of metrological honesty.

2.6 Integrity (checksum)

🎓 What is it for? — Integrity seal (sha256 checksum)

Every canonical report carries a "fingerprint" called sha256, computed from its content. If even a single digit in the file is later changed, this fingerprint no longer matches; this is how you tell whether the report has been tampered with. It works like a seal on an envelope that visibly breaks if opened.

  • Why it is done: to be able to prove that a report has not been altered since it was produced (a tamper-evident record).
  • What it teaches / measures: that the same data always yields the same digest; if the digest does not match, the file has changed.
  • Where it is used: archive security, verifying that two files came from the same measurement, and auditing.

Every canonical report carries a sha256 digest, so that if the file is later modified it can be detected (tamper-evident).

input  : report dict (with integrity.value set to "")
formula: sha256( canonical_json(report) )   // sort_keys=True, ensure_ascii=False, compact
output : integrity = { "algo": "sha256", "value": "<64-hex>" }

Before writing the file, write_json calls seal_integrity(...); this is why the JSON on disk carries the sealed value. The checksum is computed without referencing itself (the value is left empty first). Same report → byte-identical output → same checksum.

3. Measurement Report — HTML and PDF Presentation ("Generate Report")

🎓 What is it for? — HTML/PDF report presentation ("Generate report")

The canonical JSON/CSV is for machines; for humans, a readable, single-file HTML (and PDF) report with the plot and tables embedded is produced. This is like a nicely bound, ready-to-share version of the same data from the canonical record — the numbers are exactly the same, only the presentation is human-oriented.

  • Why it is done: to give a colleague, advisor or customer a single printable document with the result.
  • What it teaches / measures: how the title, plot, measurement conditions and extracted parameters are presented together.
  • Where it is used: sharing, presentations, printing and file attachments — whenever a single portable file is needed.

The canonical JSON/CSV is for machines; the human-facing single-file HTML report (and, where possible, PDF) is produced from the measurement result screen with the "Generate report" button (_generate_report). This presentation is the visible form of the canonical object.

Generated measurement report HTML/PDF view
Figure. Measurement report presentation (HTML/PDF): title block, embedded plot, measurement conditions and extracted parameters.

3.1 Report structure (sections)

The generated HTML/PDF consists of the following sections in order (build_report_html):

  1. Title block — Report title (e.g. "TFT Measurement Report"), subtitle (<TFT> — <Mode>) and generation time (generated_at).
  2. Plot — The current measurement plot is embedded as PNG (markers included). In HTML as a base64 data: URI; in PDF via QTextDocument.addResource as a plot.png resource.
  3. Measurement Conditions table — Mode, TFT ID, instrument IDN, VISA resource, channels (drain/gate), relay state, point count, software version; followed by the sweep parameters and (if present) outdoor environment rows.
  4. Extracted Parameters table — Mode-aware polished labels (Voc/Isc/FF/PCE, Vth, Ion/Ioff…). If there is no dedicated generator, it falls back to a raw summary dump. This table uses the single source (summary_format) shared with the Result page; the numbers you see in the report are exactly the same as those on screen.
  5. User Note — A free-text box if user_comment is present.
  6. Footer — Software version stamp.

3.2 Output files and naming

"Generate report" writes two files (directly into the output folder, not the run subfolder):

<TFT>_<MOD>_<YYYYMMDD_HHMMSS>_report.html
<TFT>_<MOD>_<YYYYMMDD_HHMMSS>_report.pdf
  • HTML: Because the plot is base64-embedded, the single file is portable and opens in a browser.
  • PDF: Produced via Qt QTextDocumentQPrinter; there are no extra dependencies. The plot is embedded as a plot.png resource.
ℹ️
Note Numeric values are smart-formatted in the report: very small (<1e-3) or very large (>=1e5) values use scientific notation (1.2345e-06), others use {:.4g}. All dynamic values are written escaped with html.escape (security).

4. Analysis Report (Analysis Workspace — "Export" Menu)

🎓 What is it for? — Analysis report (Export menu)

While the measurement report documents the "raw run", the analysis report documents the interpreted result extracted from a file (e.g. Voc/Isc/FF/PCE, threshold voltage). After solving an analysis in the analysis workspace, you export it in the format of your choice from the "Export" menu. If the raw measurement is the "photo that was taken", the analysis report is the "caption written under the photo."

  • Why it is done: to record the computed metrics, their uncertainties, and by which method/standard they were extracted.
  • What it teaches / measures: the metric values + their uncertainties, the method used, the reliability verdict, and the data provenance.
  • Where it is used: when reporting results, when sharing with a reliability stamp (confirmation is required for an unreliable result).

After an analysis is solved in the analysis workspace, the "Export" button (analyze2.header.export) in the top bar opens a menu. The menu contents are the intersection of the formats supported by the active analysis module's descriptor and the formats that are implemented.

Analysis workspace and PV metric results
Figure. The analysis workspace: after an analysis is solved, a report/format is produced from the "Export" menu.

4.1 Export menu options

Menu item (EN)TokenWriterOutput
CSV (data columns)csvwrite_dataset_csvCanonical data table (dot-decimal, comma)
CSV (Excel-compatible: ; separator, comma decimal)csv_excelwrite_dataset_csv_excelLocal-Excel convenience copy
PNG (plot)pngplot_canvas.export_pngPNG image of the active plot
Excel (.xlsx)xlsxwrite_metrics_xlsx"Metrics" + "Provenance" sheets
JSON reportreport_jsonanalysis_report.write_jsonCanonical analysis report (header + payload + sha256)
PDF reportreport_pdfwrite_report_pdfHTML → Qt PDF (plot embedded)
Tip When "CSV (data columns)" is selected, its Excel-compatible companion is added to the menu right after it; this way Turkish/European-locale Excel users can get the correctly-opening copy with two clicks (see Section 6.2).
⚠️
Warning (unreliable data): When report_json or report_pdf is selected, if the analysis is marked ✕ Unreliable (e.g. non-STC conditions, low fit), a confirmation is requested before the writer runs (_confirm_report_unreliable). This prevents an unreliable result from being exported as an official report by mistake.

4.2 Analysis report (report_json / report_pdf) structure

The analysis report is produced by build_analysis_report(...); it carries the same header discipline as the measurement report and reuses report_model's sealing/checksum/write_json functions (i.e. the JSON is byte-stable and integrity-sealed). The top-level fields:

FieldValue
schema_version"1.0"
report_iduuid4().hex
report_type"analysis"
created_atUTC ISO-8601 (ms)
app_id / app_version"tft-measurement" + version
simulatednull (analysis is file-based; no instrument claim)
analysisPayload (below)
integrity{algo: "sha256", value: ...}

The analysis payload (Qt-free, language-independent; labels are kept as i18n keys):

  • descriptor — Analysis module identifier, version, category, title/summary keys, engine.
  • metrics[] — For each metric, raw GUM fields + formatted display: {key, label_key, unit, value, u_std, k, n, r2, display, primary}. If there is no value, value=null, display="—".
  • method{description_key, equations[], conventions[], fit_method}. Equations are carried as plain text (plain) + a note; conventions are i18n keys.
  • standards[] — Cited standards: {id, designation, title, edition, scope} (resolved from the registry).
  • reliability — Quality verdict: ✓ Reliable (reliable) / ✕ Unreliable (unreliable) / ⚠ Non-STC (non_stc).
  • provenance — Origin/trace envelope: {data_file, file_hash, column_mapping, parameters, software_version, timestamp_utc}.
  • narrative, notes[], conditions[] — Narrative, notes, and the condition tokens required by the descriptor.

4.3 Uncertainty notation (Quantity / GUM)

🎓 What is it for? — Uncertainty notation (Quantity / GUM)

No measurement is perfectly exact; every result has a "± how much" margin. This module displays metrics as value ± uncertainty in accordance with the international GUM guide. Like a scale reading "100 g ± 2 g" — a number is meaningful not on its own but together with its confidence interval.

  • Why it is done: to express numerically how much a result can be trusted (scientific honesty).
  • What it teaches / measures: standard uncertainty (u), coverage factor (k), expanded uncertainty (U = k·u) and sample count n.
  • Where it is used: accredited reports, comparative measurements, and "is this difference significant?" decisions.

Analysis metrics are formatted through a Quantity object along the lines of GUM (Guide to the Expression of Uncertainty in Measurement) (format_quantity). The display rules:

Formula value ± uncertainty (coverage note)
ConditionOutput
u_std > 0 and n >= 2± term is shown
k set (expanded)U = k · u_std, suffix k={k}, n={n}
k absent (standard)suffix std. unc., n={n}
u_std absent / n<2only the value is shown
  • Expanded uncertainty: U = k · u_std (k = coverage factor).
  • In data files (JSON/Excel) the raw fields (value, u_std, k, n) are kept separately; the formatted display is for convenience only.

4.4 Excel export (write_metrics_xlsx)

The analysis Excel output contains two sheets:

  1. "Metrics" — Columns: metric, value, unit, u_std, n, display, reliability. One row per descriptor metric; if there is no Quantity, the value is empty and display="—".
  2. "Provenance" — Origin envelope as key/value: data_file, file_hash, software_version, timestamp_utc, plus column_mapping and parameters as JSON.
ℹ️
Note write_metrics_xlsx imports the openpyxl package lazily. If the package is missing or the file cannot be written, the writer silently returns False — it does not raise an exception. No Analysis export writer ever crashes; an error is always returned as False.

5. Composed Report (Data and Reports Workspace)

🎓 What is it for? — Composed report (Report Builder)

You use this path when, instead of a single measurement, you want to gather multiple measurements/analyses and fabrication steps into one professional report. Like collecting scattered pages into a single bound file — you pick a template, check the sections, and build the report you want with a live preview.

  • Why it is done: to gather all the evidence for a sample (measurements + steps + plots) into a single, shareable document.
  • What it teaches / measures: how multiple sources are selected and arranged by section into a single report.
  • Where it is used: sample file, customer delivery, project report — whenever a multi-source summary is needed.

Beyond individual measurement/analysis reports, the Data and Reports workspace offers a Report Builder that combines multiple sources into a single professional report. The engine is report_compose.py: pure, Qt-free; it does not add a new writer on top of the writers above.

Data and Reports workspace overview
Figure. The Data and Reports workspace: the Report Builder that combines multiple sources into a single report lives here.

5.1 Workspace tabs

TabKeyFunction
OverviewoverviewKPI cards (measurement/analysis/sample/report counts) + quick actions + recent activity
Data LibrarylibraryEmbedded database table + detail/inspection panel for the selected record
Report BuilderbuilderTemplate + section selector + live preview + "Build"
ReportsreportsLibrary of generated reports (header + staleness + "Open")
Compare / OverlaycompareOverlay multiple curves on top of each other
Database / StatisticsstatsMean±σ / min / max / n across a sample's measurements
Overview tab KPI cards
Figure. The Overview tab: KPI cards showing measurement/analysis/sample/report counts, and quick actions.

5.2 Report Builder — three-column flow

The Report Builder consists of three columns (there is no drag-and-drop field well; the column families are deterministic):

Column 1 — Setup:

SettingOptions
SourceSelected data (records added from the library/compare) or Lab Notebook (sample bridge)
TemplateTFT Characterization / Sample File / Comparison / PV Cell Summary / Blank
FormatPDF + HTML / PDF / HTML / Full (PDF + HTML + CSV + JSON)
LevelCustomer (sha256 header) / Base

Column 2 — Content: Depending on the selected source, either a checkbox list of selected measurements or the Lab Notebook sample selector (fabrication steps + linked measurements). Below it are the section checkboxes.

Column 3 — Live preview: As selections change, the HTML produced by report_compose.compose(...) is previewed instantly (the preview never crashes; on error it shows a red message).

Report Builder live preview example
Figure. Report Builder output: a professional report previewed live according to the selections.

5.3 Report sections

SectionKeyDefaultLockedContent
Header / traceabilitykunyeOnYesschema_version, app_version, sample.id, created_at, source_sha256, integrity (always first, cannot be turned off)
Extracted parameter tableparamsOnNoMeasurement metric table (Ion/Ioff, Vth, µFE, SS, gm_max, V_on, Rect., Ron, µ_sat, Points)
Fabrication steps (Lab Notebook)stepsOnNoSample header + fabrication steps + parameters
Measurement conditionsconditionsOnNoMode, VDS, NPLC, Averaging, Sweep, Instrument, VISA (from the first measurement)
Plot (curves)plotOffNoFigures passed from the GUI (data-URI)
Method notes + R²methodOffNoExtraction method, SS fit R², Y-function R²
Raw data appendix (CSV/JSON)rawOffNoPath note of the associated raw CSV files
ℹ️
Note The header section is always mandatory and is processed first — its checkbox is checked and disabled and cannot be turned off. Because a report without traceability information is metrologically invalid.

5.4 Lab Notebook bridge (linking without a schema change)

The Report Builder links measurements to Lab Notebook samples without adding a new table. The link fields: samples.sample_id == measurements.tft_id and sample_steps.linked_measurement_id. The three-hop bridge:

  1. get_sample(sample_db_id)sample_id.
  2. Wide pool: measurement_db.query(tft_id=sample_id).
  3. The "called" set: the linked_measurement_id values of the steps (union, unique by id).
ℹ️
Note (graceful skip): Orphan measurements with no sample and soft-deleted records are gracefully skipped — report generation never crashes.

5.5 Output files and canonical JSON

"Build" → report_compose.write(...) writes in the selected formats. The file root name: <sample_id or template>_rapor (forbidden characters cleaned).

FormatFileWriter
HTML<name>_rapor.htmlComposer
JSON<name>_rapor.jsonreport_model.write_json (header + sha256 sealed, sorted keys)
CSV<name>_rapor.csvMetric table (UTF-8 BOM, dot-decimal; header in the companion JSON)
PDF<name>_rapor.pdfexporters.write_report_pdf (Qt; lazy import)

The canonical JSON additionally carries these traceability fields: report_type="composed", source (type, sample_id, measurement_ids, step_ids), source_sha256 and measurements_sha256 (the staleness sub-digest), sections[], and a summarized sample and measurements. Deterministic: a fixed created_at + report_id → byte-identical output.

5.6 Reports library and "staleness" (stale)

🎓 What is it for? — Reports library and the "stale" badge

A generated report is a sealed snapshot of the data at that moment and does not update on its own. If the source measurements change or are deleted after you produce the report, the software warns you by placing a "stale" badge on that row. Like the expiry date on food in the fridge — it is still there, but you should check its freshness.

  • Why it is done: so you can notice that the numbers in an old report may no longer match the current data.
  • What it teaches / measures: whether a report is still consistent with its source data, or "stale" (an sha256 comparison).
  • Where it is used: when reviewing the report archive; if you see a stale one, re-generate the report for current numbers.

The "Reports" tab scans the output folder without a database: report_index.list_reports(...) reads every *_rapor.json (only report_type=="composed") and lists the header + the generated sibling formats (pdf/html/csv). Double-clicking a row or "Open" opens the report in the default application (priority order: PDF > HTML > JSON).

⚠️
Warning (stale badge): If the source measurements change/are deleted after a report is produced, _is_stale(...) compares the stored measurements_sha256 with one recomputed from the current DB state and, if they do not match, places a "stale" badge on the row. Reports are sealed snapshots — they are never silently auto-updated. If you see a stale report, you must re-generate it for current numbers.
Library of generated reports
Figure. The reports library: generated reports are listed with header, staleness badge and an "Open" action.

5.7 Statistics tab (aggregation)

🎓 What is it for? — Statistics tab (aggregation)

Instead of a single measurement, it brings together all measurements of the same sample and computes summaries such as mean, standard deviation, minimum/maximum and count (n). Like looking at a class average and distribution instead of individual grades — you see repeatability and scatter.

  • Why it is done: to understand how repeatable/consistent the measurement is and the typical value range.
  • What it teaches / measures: Mean ± σ, min, max and the valid sample count n (missing/NaN values are excluded).
  • Where it is used: process stability (SPC) in manufacturing/quality control and sample-to-sample comparison.

The "Database / Statistics" tab aggregates the extracted parameters across all measurements of a selected sample (tft_id) (the benchtop SPC/distribution equivalent):

Mean = Σxᵢ / n
σ (sample std, n-1) = sqrt( Σ(xᵢ − Mean)² / (n − 1) )   // n>1; if n=1 then σ=0
min, max, n

None/NaN values are excluded; if n=0, all statistics are (missing != 0). The "Report from selection" button opens the Report Builder with that sample.

Descriptive statistics analysis screen
Figure. The Statistics tab: Mean±σ, min, max and n values across a sample's measurements.

6. Export Formats (Reference)

🎓 What is it for? — Export formats — which one, when?

The same data can be exported in many formats; the right format depends on what you will do with the file. Roughly: if you will process it by machine, use canonical CSV/JSON; if you will double-click it open in Turkish Excel, use Excel-compatible CSV or XLSX; if you will share/print it, use PDF/HTML; if you will place the plot somewhere, use PNG. Like sending the same document by email, fax or paper — the content is the same, the carrier differs.

  • Why it is done: so you can pick the most suitable, least error-prone format for each use (archiving, analysis, sharing, presentation).
  • What it teaches / measures: which format is the machine-readable "canonical" record, and which is merely a "convenience copy".
  • Where it is used: when transferring data to another tool, archiving it, or sending it to a colleague.

This section gathers all the formats in one place.

6.1 Canonical CSV (machine format)

A locale-independent, single, documented format (report_model.write_csv / exporters.write_dataset_csv):

  • Encoding UTF-8 + BOM (utf-8-sig — so Excel opens it correctly), dot decimal, comma separator, line ending \n.
  • Headers are stable key + unit (e.g. ids_A, vds_set_V), and are never localized.
  • Missing point → empty cell (""); 0 is never written. The na/fail/ok distinction lives in the status column.
  • The CSV carries only the table; the header + checksum live in the companion JSON file.
point,step_index,sweep_direction,vds_set_V,vgs_set_V,vds_measured_V,vgs_measured_V,ids_A,igs_A,elapsed_s_s,status
1,0,Forward,0.0,0.0,0.0,0.0,1.2e-6,1e-09,0.01,ok
2,0,Forward,0.1,0.0,,,,,0.02,na

6.2 Excel-compatible CSV (convenience copy)

write_dataset_csv_excel — for Turkish/European-locale Excel:

ParameterUnitDescriptionDefault
decimalDecimal mark, (comma)
delimiterColumn separator; (semicolon)
encodingUTF-8 with BOMutf-8-sig
⚠️
Warning This is a CONVENIENCE copy, not the machine-readable canonical file. Turkish Excel, when you double-click open the canonical dot-decimal CSV, mistakes 1.5 for a date/thousands separator and corrupts it; this variant opens correctly in that locale. The separator is ; because it must not collide with the , decimal. For a file to be archived/processed, always use the canonical CSV (Section 6.1).

6.3 TXT (tab-separated)

<base>.txt — the tab-separated version of the raw measurement table; the same 13 columns, UTF-8. Practical for quickly pasting into tools like Gnuplot/Origin.

6.4 XLSX (Excel workbook)

There are two paths:

  • Raw measurement (DataWriter._write_xlsx): a "measurement" sheet (13 columns) + a mode-aware "Summary" sheet (label/value). If openpyxl is missing, a dependency-free minimal XLSX (direct zip + XML) is written (_write_minimal_xlsx) — i.e. the Excel output is produced in every environment.
  • Analysis metrics (write_metrics_xlsx): the "Metrics" + "Provenance" sheets (Section 4.4).

6.5 JSON metadata vs canonical JSON

Two different JSONs must not be confused:

FileProducerPurposeSeal
<base>_metadata.jsonDataWriter.saveFree-form, human-readable metadata (instrument, parameters, summary, counts)No
<base>_report.jsonreport_model.write_jsonCanonical report (header + columns + rows)Yes (sha256)
<name>_rapor.jsonreport_compose.writeCanonical composed reportYes (sha256)

metadata.json contains the following fields: device_identity (IDN/VISA/switch_port), relay_enabled, selected_tft, sweep_parameters, date_time, started_at, user_comment, software_version, smu_mapping (drain/gate), point_count, planned_point_count, stopped_early, compliance_hit and (if present) <mode>_summary.

6.6 PNG (plot image)

Plots are exported as PNG:

  • Measurement report: The plot is rendered to PNG at 150 DPI and embedded into the HTML as base64 and into the PDF as a plot.png resource.
  • Analysis: Via "Export → PNG", the active plot is written directly to a PNG file (plot_canvas.export_png).

The generated report files are named so that their source is evident: measurement reports follow <TFT>_<MOD>_<date>_<time>_report.json / _report.csv, while composed reports follow the *_rapor.json pattern. This way each output reveals from its name which measurement or session it was derived from.

6.7 File naming convention — summary

OutputTemplate
Run folderYYYY-MM-DD-<Day>-<Type>-<HHMMSS>/
Raw + report files<prefix>_<TFT>_<MOD>_<YYYYMMDD_HHMMSS>_<suffix>.<ext>
Canonical report<base>_report.json / <base>_report.csv
Measurement HTML/PDF report<TFT>_<MOD>_<YYYYMMDD_HHMMSS>_report.{html,pdf}
Composed report<sample or template>_rapor.{html,json,csv,pdf}
Crash-safe interim file<base>_partial.csv

7. Determinism, the Locale Trap and Levels

7.1 Determinism (the rule every writer obeys)

  • Same input → byte-identical output. _canonical_json uses sort_keys=True, ensure_ascii=False and compact separators; there is no hidden wall-clock in the payload — created_at comes from the caller. A caller wanting bit-identical output passes a fixed created_at + fixed report_id.
  • Locale-independent data. JSON/CSV numbers are of numeric type and use dot-decimal. Only the human HTML/PDF presentation is localized.
  • Escaped HTML. build_report_html passes every dynamic value through html.escape.
⚠️
Warning (locale trap): This is a common and costly mistake. Data files (JSON/CSV) must never leak into the UI format (comma decimal, local date). _csv_value forces dot-decimal with repr/{:.0f}; _utc_iso_ms writes ISO-8601 UTC. The local view is only for the application interface.

7.2 Level → required-field mapping

Fieldbasecustomeraccredited
Header (id, app/recipe version, station, sample, created_at, status, simulated)YesYesYes
columns.unit + typeYesYesYes
integrity (sha256 checksum)YesYes
report_revision + stable archival languageYesYes
columns.spec {min,max}PLANNED
columns.uncertainty {..., k}PLANNED
calibration.cert_no / valid_untilPLANNED
signoff (prepared by/approved by/signature)Field exists, empty; filling is PLANNED
Native PDF/A + embedded fonts / write_xlsxPLANNED; PDF today via Qt

7.3 What is currently PLANNED

The following are defined in the contract but not yet implemented; the report does not claim them:

  • A native .xlsx report writer and an archival PDF/A profile (currently PDF is produced from HTML via Qt).
  • A GUM Type-B uncertainty column for measurement metrics (the measurement DB carries bare scalars; uncertainty lives on the analysis-kit Quantity path).
  • A Level-2 uncertainty budget, a calibration certificate and a signature chain (e.g. PAdES for PDF); calibration and signoff are currently stubs.
  • Background-thread generation for large/bulk reports.

8. Standard References

The behaviors in this chapter are based on the in-repo contracts and standards:

  • docs/standards/06-reporting.md — The reporting standard (determinism, checksum, three-level header, locale trap, "missing != zero", auto-transmission ban).
  • docs/guides/reporting-guide.md — The concrete contract: canonical JSON skeleton, column definitions, CSV convention, writer contract, composed report (report_compose).
  • docs/standards/03-measurement-and-analysis.md — Result + R² + uncertainty.
  • docs/standards/05-data-and-database.md — Stored data / NULL discipline.
  • docs/standards/12-i18n-l10n.md — The locale/language distinction.
  • Analysis standards (citation): analysis reports carry the relevant measurement standard (designation/title/edition/scope) via the standards[] in the module descriptor.
ℹ️
Note (auto-transmission ban): Reports may contain raw data but are never sent anywhere automatically. Reporting is completely separate from telemetry; all writers write to a local path chosen by the user. Telemetry sends only anonymous metrics such as format + point-bucket (subject to the consent gate); the file name/path is never sent.

9. Quick Reference and FAQ

Which file is the official record of a measurement?

<base>_report.json (canonical, sha256-sealed). The human-facing presentation is derived from it.

My Excel shows comma-formatted numbers as broken — what should I do?

Use the "CSV (Excel-compatible)" variant (Section 6.2) or prefer XLSX. For archiving, always keep the canonical CSV (dot-decimal).

Why does a value appear empty/ in the report?

That point could not be measured (status="na") or the metric could not be computed. The software never fills a missing value with 0 (missing != zero).

What does the "stale" badge mean?

The source measurements changed/were deleted after the report was produced. Re-generate the report for current numbers; the existing report remains as a sealed snapshot.

How do I verify that two files came from the same measurement?

Via integrity.value (sha256). The same report dict always yields the same digest; if the file changed, the digest does not match.

Why is the PDF sometimes not produced?

The PDF is produced via Qt (QTextDocumentQPrinter); if Qt is missing or there is a write error, the writer silently skips it and at least the HTML/JSON is preserved (robust-generation principle).