Skip to content

Imports drop total time, unread times, nutrition and difficulty without a word #434

Closed

Description

@BattermanZ

Parent

#411

What to build

The Mealie and Paprika readers drop what a cook typed without a word. Neither the recipe nor the Import Report keeps it, so a cook who deletes the old app after checking the Report has lost it. #411 promises the opposite: "nothing is lost silently before the old app is deleted".

Found by reading every field of the real test files against what each reader reads:

  • Total time. Kamosu keeps prep and cook time only. Mealie's total_time is dropped on 8 of the 16 recipes in tests/fixtures/mealie/, and Paprika's on 30 of the 746 in the corpus archive. The Mealie pre-v1 beef stew has a total time and no other, so it arrives with no time at all.
  • A prep or cook time Kamosu cannot read. The timer reader knows seconds, minutes and hours in English, French and Spanish. 1 Day (the pre-v1 pizza dough), 15 Minuten, 45 Minuten and 1 Stunde 30 Minuten (the German recipes of v2-export.zip), a bare 2 from Mealie, and 1 day from Paprika (two corpus recipes) all vanish.
  • Nutrition beyond calories. Mealie's protein, fat, carbohydrate, fibre, sugar, sodium and the rest are dropped on 5 recipes. The pre-v1 beef stew has six figures and no calories, so all of it goes.
  • Paprika's difficulty (Easy, Medium…), on 83 corpus recipes.

The rule, for every reader: what a cook typed either lands in a field of the recipe or is written into its Note, word for word. Bookkeeping (ids, hashes, file names, created and updated dates, settings) is neither. For these four:

  • Each goes into the Note as a line of its own, after what the Note already holds, worded in the recipe's Language as Mealie's Equipment: line is: Total time: 2 Hours 30 Minutes, Prep time: 1 Day, Cook time: 15 Minuten, Difficulty: Easy, Nutrition: protein 30, fat 12, ….
  • A time that does land in its field is not repeated in the Note. Total time always goes into the Note, since no field holds it.
  • Nutrition lines name each figure as the app names it, with its unit where the app gave one, and leave out calories when they landed in the nutrition field.
  • Paprika's free-text nutrition is written into the Note as it stands, and is no longer counted on the Report as nutrition: nothing of it stays in Paprika. The nutrition kind stays declared for a reader that cannot keep a figure.

Crouton is read by the same rule. Its neutritionalInfo is dropped by ADR 0025 as scrape debris; check the real export for anything else a cook typed, and record the result on this ticket either way.

Re-importing after this fix. A recipe that gains a Note line is a changed recipe, so importing the same file again offers it instead of finding it unchanged. Say so in the changelog line.

Acceptance criteria

  • A table on this ticket lists every field present in the real Mealie, Paprika and Crouton test files, and says for each: the field it lands in, written into the Note, counted on the Report, or bookkeeping.
  • The pre-v1 beef stew arrives with Total time: 2 Hours 30 Minutes and its six nutrition figures in its Note; the pre-v1 pizza dough with Prep time: 1 Day.
  • A German Mealie recipe keeps its 15 Minuten times in its Note.
  • A Paprika recipe with a difficulty and a total time keeps both in its Note; one checked in the corpus test.
  • A French recipe's lines are worded in French.
  • A time that landed in its field is not repeated in the Note.
  • The corpus checks for Paprika and Crouton still pass, with the new Note lines counted.
  • The changelog line from One place to import from another app, recognising the file #412 says what now arrives, and that recipes imported before are offered once on the next import.

Blocked by

None.

Activity

  1. added
    bugSomething isn't working
    ready-for-agentSpecified in full and ready for an agent to implement
    reasoning-mediumImplement at medium reasoning effort (Sonnet/Terra-class)
    on Oct 23, 2026
  2. BattermanZ commented on Oct 24, 2026

    @BattermanZ
    MemberAuthor

    The Mealie half of this ticket shipped with #418 (PR #441), since a backup goes through the export's reader and #418's field table needed it. For the export and the backup alike, src/mealie.rs now writes a total time, a prep or cook time Kamosu cannot read, and nutrition beyond calories into the Note, each a line of its own worded in the recipe's Language, and leaves out a time that landed in its field. A total held in seconds alone is said in words. Tested in the_export_from_before_mealie_1_is_read (the beef stew's Total time: 2 Hours 30 Minutes and six figures; the pizza dough's Prep time: 1 Day) and the_export_of_2025_is_read (German 15 Minuten and 45 Minuten, French Temps total : 50 minutes, a landed time not repeated). The changelog line says Mealie recipes imported before are offered once. What is left here is Paprika's difficulty, total time and free-text nutrition, and the Crouton check.

  3. self-assigned this
    on Oct 24, 2026
  4. BattermanZ commented on Oct 24, 2026

    @BattermanZ
    MemberAuthor

    Every field of the real Mealie, Paprika and Crouton files, accounted for

    Lands is the recipe field it goes to. Note means it is written into the Note word for word, as a paragraph of its own after the description and notes, led in the recipe's Language (Temps total :, Tiempo total:; the table gives the English). Report means it is counted on the Import Report under what stayed in the old app. Bookkeeping covers ids, hashes, file names, dates and settings.

    Mealie's export

    Every field present in tests/fixtures/mealie/pre-v1-export.zip, v2-export.zip and v3-export*.zip. A backup goes through this same reader; its tables are accounted for on #418.

    Field Where it goes
    name Lands: title
    description Note, whole; a picture in it is taken out and counted as an extra photo
    notes (title, text) Note: a paragraph each
    prep_time, perform_time, cook_time Lands: prep and cook minutes where Kamosu reads them; otherwise Note, Prep time: or Cook time: and the time as typed
    prep_time_seconds, perform_time_seconds Lands: prep and cook minutes, before the text
    total_time, total_time_seconds Note, Total time: (no field holds it); a total held in seconds alone is said in words
    recipe_yield, recipe_yield_quantity, recipe_servings Lands: Yield
    org_url Lands: Source
    recipe_ingredient: display, quantity, unit, food, note, disable_amount Lands: the Ingredient Line, as Mealie shows it
    recipe_ingredient: title Lands: a Section before the line
    recipe_ingredient: referenced_recipe Lands: a Component of that recipe
    recipe_ingredient: original_text Bookkeeping: the wording first imported into Mealie; the line as Mealie shows it now wins (#413)
    recipe_ingredient: reference_id Bookkeeping
    recipe_instructions: text, title, summary Lands: a Step, a Section before it, the Step's first sentence; a picture placed in it becomes the Step's photo
    recipe_instructions: id Bookkeeping
    nutrition: calories Lands: Nutrition, per serving, where it is a number; otherwise Note
    nutrition: every other figure Note: one Nutrition: line, each figure named in the recipe's Language
    recipe_category, tags (name) Lands: Tags
    tools (name) Note: the Equipment: line
    assets Lands: a Step's photo where placed in one; Report: extra_photos for any other picture, attached_files for any other file
    image Bookkeeping (a cache key); the picture lands as the Main Photo
    rating Report: ratings
    comments Report: comments
    settings (public, show_nutrition, show_assets, landscape_view) Bookkeeping: settings
    id, slug, group_id, household_id, user_id, created_at, updated_at, date_added, date_updated, last_made, extras Bookkeeping (last_made and extras are empty in every file)

    Paprika

    Every field present in tests/fixtures/paprika/mealie-sample.zip and the 746-recipe corpus archive.

    Field Where it goes
    name Lands: title
    ingredients Lands: an Ingredient Line per line, as written
    directions Lands: a Step per line, as written
    description, notes Note: a paragraph each
    prep_time, cook_time Lands: prep and cook minutes where Kamosu reads them. Where it cannot (1 day, 14 days, 30 d), or reads only part (35 to 40 mins, about 1 1/2 hours, 40 mins (plus proving)), also Note, Prep time: or Cook time: and the time as typed: 8 prep and 29 cook in the corpus (new)
    total_time Note, Total time: and the time as typed (30 in the corpus) (new)
    servings Lands: Yield, its first count and the words after it. Where it holds no count (a big pot) or the count is a range's lower end (Serves 4 to 6), also Note, Servings: and the servings as typed (89 in the corpus, nearly all ranges) (new)
    difficulty Note, Difficulty: (83 in the corpus) (new)
    nutritional_info Note, as it stands, after Nutrition: unless its first line already names it (141 led, 19 already labelled). No longer counted on the Report (new)
    source, source_url Lands: Source
    categories Lands: Tags
    photo_data Lands: Main Photo
    image_url Lands: Main Photo, downloaded, where the file holds no photo of its own; otherwise Bookkeeping, since it is the website's picture, not the cook's
    photos Report: extra_photos
    rating Report: ratings
    uid Bookkeeping: the ledger's key
    photo, photo_large, photo_hash, hash, created Bookkeeping (file names, hashes, a date)

    on_favorites is read as Report: favourites, though no real file sets it.

    Crouton

    Every field present in the 86-recipe export of 23 September 2026. Result of the check: nothing a cook typed is dropped, apart from neutritionalInfo, which ADR 0025 drops on purpose. It was not even counted, so the Report now counts it under nutrition (17 recipes), the kind this ticket keeps "for a reader that cannot keep a figure".

    Field Where it goes
    name Lands: title
    ingredients: ingredient.name, quantity (amount, secondaryAmount, quantityType) Lands: an Ingredient Line rebuilt from its parts, or a Section for SECTION; every unit in the export has a word
    ingredients: order Lands: the order of the lines
    steps: step, isSection, order Lands: a Step or a Section, in order
    duration, cookingDuration Lands: prep and cook minutes (whole minutes, so always read)
    serves Lands: Yield
    notes Note
    sourceName, webLink Lands: Source
    tags (name) Lands: Tags
    images Lands: Main Photo; Report: extra_photos past the first, unreadable_photos
    sourceImage Report: site_icon (the site's favicon, ADR 0017)
    neutritionalInfo Report: nutrition (new); dropped by ADR 0025 as scrape debris
    defaultScale Bookkeeping: a saved scaling setting (6 recipes not at 1)
    folderIDs, isPublicRecipe Bookkeeping: settings, empty in every recipe
    uuid, duplicatedFromRecipeUUID, tags.uuid, tags.color, ingredients.uuid, ingredient.uuid, steps.uuid Bookkeeping
  5. BattermanZ commented on Oct 24, 2026

    @BattermanZ
    MemberAuthor

    Paprika and Crouton done, live pass recorded

    The Paprika half and the Crouton check are built on fix/434-paprika-note-lines. The field table is the comment above.

    What changed

    • src/paprika.rs writes into the Note, each a line of its own led in the recipe's Language: the total time as typed, a prep or cook time Kamosu cannot read or reads only in part (1 day, 35 to 40 mins, 40 mins (plus proving)), servings with no count or a range's upper end (Servings: Serves 4 to 6), the difficulty, and the free-text nutrition as it stands (no Nutrition: lead where its first line already names it). Nutrition is no longer counted on the Report. A time that landed whole is not repeated.
    • src/crouton.rs counts a recipe's neutritionalInfo on the Report as nutrition. ADR 0025 still keeps it out of the recipe.
    • src/schema_org.rs gives Words a Servings: and a Difficulty: lead in English, French and Spanish.

    Two calls beyond the ticket's text, both under its rule that what a cook typed lands in a field or the Note: servings, which the ticket did not name (89 corpus recipes, nearly all ranges), and times read only in part, which the defects review found (8 prep and 29 cook in the corpus).

    Checks. just check and just test pass. Both corpus checks pass: cargo test --test paprika_corpus -- --ignored with the new counts pinned and the Blueberry Pie checked (Total time: 3 hours, Difficulty: Medium, prep and cook landed and not repeated), and cargo test --test crouton_corpus -- --ignored with 17 nutrition rows.

    Live pass, through POST /api/uploads and import_from_another_app

    • On the dev instance (HTTPS dev URL, Session live-434, since ended): tests/fixtures/paprika/mealie-sample.zip. Three recipes arrive with Difficulty: Easy as their Note, the others with none. Imported again: all five unchanged. The recipe page shows the line under the steps (phone, 390 px).
    • On a throwaway instance, to prove the changelog's "offered once": the same file, with five changes (a French recipe, a total time, 40 mins (plus proving), Serves 4 to 6, nutrition), imported first by the code before this change (b5d4541) and then by this branch on the same data. The old code counted nutrition on the Report. The new code offered the four recipes that gained a line, once, left the fifth unchanged, and counted no nutrition.
    • On a fresh throwaway library, the same file: the French recipe's Note reads Temps total : 50 minutes, Temps de cuisson : 1 jour, Portions : pour une tablée, Difficulté : Facile. Zucchinipuffer's reads Total time: 1 hr 15 mins, Prep time: 40 mins (plus proving), Servings: Serves 4 to 6, Difficulty: Medium, Nutrition: Calories: 250 / Protein: 8 g, and its 20 mins cook time is in the field only.
    • Three real recipes from the Crouton export on the throwaway: the two with neutritionalInfo each have a nutrition row on the Report, and the third has none.

    Found along the way and not fixed here: the Report's "needs you" line says "changed in Crouton since the last import" on every app's import (report_changed in ui/messages/*.json). It came in before this ticket.

  6. added a commit that references this issue on Oct 24, 2026
    3dfb84d
  7. github-actions commented on Oct 24, 2026

    @github-actions

    Closed by #443, merged into dev.

  8. github-actions commented on Oct 24, 2026

    @github-actions

    Closed by #443, merged into dev.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingready-for-agentSpecified in full and ready for an agent to implementreasoning-mediumImplement at medium reasoning effort (Sonnet/Terra-class)

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions