Files
Copies/docs/final_output.md
T

13 KiB

Final output: A Rendre

This documents the current implementation, as of 2026-09-12. The return folder referred to as « À rendre » is named A Rendre on disk. JPEG files use the extension .jpg, not .jpeg.

Files and their sources

After grouped correction and review:

python -m copienator read-grouped Interro
python -m copienator giving-names Interro BGnot

The expected layout for a copy is:

Interro/A Rendre/
└── Student Name (01)/
    ├── Student Name.jpg
    ├── Student Name.pdf
    ├── score.json
    ├── info.json
    └── answers/                 # when individual answer export is enabled
        ├── 001 - Ex 1.jpg
        └── 002 - Ex 2.jpg

The name comes from Copies/Copie01.json (name), with filename sanitization. The copy ID distinguishes folders even when several copies have the same name. giving-names links the full JPEG, PDF and score file (or copies them when links are unavailable). It writes info.json and optionally composes the individual answer JPEGs.

Return file Source under BGnot/Copie01/ Contents
Student Name.jpg Concat.jpg Full continuous image of the compiled answers and corrections.
Student Name.pdf Concat_F.pdf Filtered, paginated correction with context, questions and solutions.
score.json score.json Per-question scores, including questions omitted from the filtered PDF.
info.json info.json Answer presence, empty-answer classification, PDF membership and score.
answers/*.jpg Final per-label JPEGs selected by info.json One annotated non-empty answer, with optional supplementary material.

Enabling or disabling outputs

Set these independent options in config.py (the defaults also apply when absent from an older personal configuration):

RETURN_JPEG_ENABLED = True
RETURN_PDF_ENABLED = True
RETURN_ANSWERS_ENABLED = False
RETURN_ANSWERS_CONTEXT = False
RETURN_ANSWERS_QUESTION = True
RETURN_ANSWERS_SOLUTION = False

The personal config.py enables RETURN_ANSWERS_ENABLED; the distributed default is False. Set a full-output option to False, then rerun giving-names to omit that file from A Rendre. For each prepared copy, any existing named return file of a disabled type is removed, including a symlink or fallback copy. Its annotation source remains intact. These options control return publication, not intermediate rendering or scoring. score.json and info.json are always included and have no disabling options.

Cleanup allows the JPEG to be absent when disabled, still requires score.json, and preserves return PDFs when present.

Individual answer JPEGs

With RETURN_ANSWERS_ENABLED = True, giving-names generates an answers/ subdirectory inside each student's return folder. It includes every non-empty compiled answer, even a perfect answer omitted from the filtered PDF. Labels marked empty-answer and labels without an answer are excluded. Every JPEG contains the final annotated student answer, including retained feedback and extracted handwriting.

The three supplementary options independently prepend, in this order:

  1. Applicable context PDFs, if RETURN_ANSWERS_CONTEXT is enabled.
  2. The question, if RETURN_ANSWERS_QUESTION is enabled (the default).
  3. The model solution, if RETURN_ANSWERS_SOLUTION is enabled.
  4. The annotated student answer, always.

These use the same Text2/Sol2 sources as the filtered PDF. Missing supplements are skipped; an unreadable existing file fails the export. They are concatenated vertically on white, without PDF pagination or its black/blue borders. The options affect only these individual images, not the full JPEG or filtered PDF. Disabling all supplements produces just the annotated answer.

Filenames use natural label order, a three-digit minimum sequence number, and a sanitized label (001 - Ex 1.jpg). Numbering prevents filename collisions when labels differ only by characters forbidden in filenames. JSON keys retain exact labels. The managed answers/ directory is replaced on successful generation, so removed/empty answers do not leave stale images; failures preserve the previous directory. Disabling the option clears this directory on the next giving-names run. Separate storage keeps these images out of the personal final-mark stamping step, which reads only JPEGs directly inside the student's folder.

Recompile annotations once before exporting old evaluations with this option: the compiler now saves every final answer block and writes info.json next to them. For the grouped workflow, run read-grouped, then giving-names. This avoids reconstructing a reviewed answer from outdated correction data.

info.json: per-question information

Every label in score.json has an object containing exactly four fields:

{
  "Ex 1": {"present": true, "not_empty": true, "touched": false, "score": "4"},
  "Ex 2": {"present": true, "not_empty": true, "touched": true, "score": "2"},
  "Empty": {"present": true, "not_empty": false, "touched": false, "score": "0"},
  "Absent": {"present": false, "not_empty": false, "touched": false, "score": ""}
}
  • present: an answer entry exists for this student and label in the compilation data. A supplied answer judged empty still has present: true.
  • not_empty: the answer was not marked empty-answer and was successfully compiled. Absent answers have not_empty: false. When individual export is enabled, a JPEG is generated if and only if both present and not_empty are true. These fields describe the answer regardless of export settings.
  • touched: the answer appears in the compiled filtered Concat_F.pdf, using the actual selection including handwriting and selective redo preservation. It is not a flag for human edits. Empty and absent answers have false.
  • score: the same value as score.json (normally a numeric string, or "" for an unpopulated score). Editing scores requires recompilation to update the images; return publication uses current score.json values for this field.

info.json is always exported, even when individual JPEGs or the named PDF are disabled. touched describes the source filtered PDF. In normal Anot and Bnot flows, no filtered PDF is produced and all touched values are false.

This file replaces touched.json and the internal answer_labels.json manifest. Recompile old annotations, then run giving-names; successful regeneration and publication remove the obsolete files from their respective folders. Missing or malformed metadata requires recompilation rather than guessing answer presence from scores. Cleanup preserves info.json alongside score.json.

JPEG: the full compiled correction

The JPEG stacks the rendered answer blocks vertically in natural label order (for example, Ex 2 precedes Ex 10). Each block contains the scanned answer, its label and score, retained global and local feedback, and detected handwritten review annotations. Local feedback can include red rectangles and comments in the left margin. Review checkboxes are applied as actions rather than reproduced as controls; internal error labels are hidden during recompilation.

The result is one RGB image of variable height, with no page breaks. It contains all successfully compiled answer blocks, including answers scored 4 with no remaining feedback. “Full” refers to those answer blocks, not the original scan pages or every question in the statement. Missing/unrenderable answers cannot be included, and the renderer normally suppresses empty-answer results. The grouped compiler refuses to publish a new set when compilation is incomplete.

The JPEG does not prepend the question, context or model solution PDFs.

PDF: a different selection and layout

The PDF is not a PDF conversion of the JPEG. During an ordinary full grouped recompilation, an answer is omitted only when all three conditions hold:

  • Its score is at least 4.
  • Every feedback item is marked to_delete (also true for an empty feedback list).
  • There are no significant detected handwritten annotations for that answer.

Thus, a 4/4 answer with retained feedback or handwriting still appears. Scores for omitted answers remain in score.json, and their answer blocks remain in the JPEG. Handwriting significance currently means more than 20 pixels with alpha greater than 50 in the extracted annotation layer.

For each retained answer, the PDF stacks the following available material:

  1. Applicable context PDFs from Text2/CTXT first_label -> last_label.pdf.
  2. The question from Text2/<label>.pdf.
  3. The model solution from Sol2/<label>.pdf.
  4. The same compiled answer block used for the JPEG.

Missing supplementary PDFs are skipped. Contexts can repeat for successive questions. Each question's complete group stays together on one page; groups are packed until the next would exceed the target height. An oversized group gets its own taller page, rather than being split. Pages are raster images saved as PDF at 100 dpi, with variable heights, not fixed A4 sheets or searchable text.

The target height is int(max_image_width * 1.414 * 1.25) pixels. Small white margins are added on the left and above/below the page. The first image of each group receives a black border and the second a blue border. These borders are assigned by position, so they do not consistently identify question and solution when contexts are present or supplementary files are missing.

If nothing survives filtering, read-grouped removes old Concat_F outputs and does not create a new PDF. During a selective --refaire merge, saved answer images outside the selection are kept in the filtered output without reapplying the perfect-answer filter; the resulting PDF can therefore retain more answers than a full recompilation.

JSON: per-question scores

score.json is a flat JSON object keyed by the exact question labels. For example (illustrative data):

{
  "Ex 1 : 1)": "4",
  "Ex 1 : 2)": "2.5",
  "Ex 2": ""
}

Values are strings, including numeric scores. The normal question scale is 0 to 4. "" means no score was populated for that label; it is distinct from "0". The compiler initializes all labels from the evaluation's labels file to "", then fills processed scores. This file contains no student identity, feedback, annotation coordinates, grading weights, or overall final mark.

Scores incorporate review checkbox changes and, when requested, existing score overrides via read-grouped --update-score (or read-annotations --update-score for Bnot). Editing the JSON alone does not update the rendered scores. Overrides are read from the annotation source folder: a return symlink points there, but an independent fallback copy does not. After regeneration, rerun giving-names to refresh copied return files.

update-ods reads these return JSON files; empty values become NT in the normal per-question export. Its --sum option sums numeric values and skips nonnumeric ones. Weighting and the final overall mark belong to the separate grading flow.

Availability and later steps

  • giving-names accepts BGnot, Bnot, or Anot. It selects the requested source if score.json and either Concat.jpg or info.json exist, otherwise it tries Anot/CopieXX. This also permits returns for an entirely empty copy. It links the PDF only if Concat_F.pdf exists. The normal Bnot reader produces a filtered Concat_F.jpg, and simple annotation produces Concat.jpg; these paths do not guarantee a filtered PDF.
  • Preparing returns removes disabled named outputs, and removes older named outputs whose source is now absent, including broken symlinks.
  • add-final-score writes to FINAL_SCORE_OUTPUT_DIR/<evaluation>/. It stamps the overall mark from the configured ODS in red at the JPEG's upper right, rounded down to one decimal place. It copies PDFs unchanged and does not export either JSON file or the answers/ directory. It does not add that mark to the files inside A Rendre.
  • The clean command retains return images (including individual answers), PDFs, score.json and info.json, materializing retained symlinks before deleting their sources.

Implementation references