cadrumo.application.user_profile.section_rows module¶
Adding one row to a repeatable profile section.
A repeatable section’s rows live at section.INDEX.field, and the write
door judges a whole fact batch at once: every required field of a row must
arrive together or none of it lands. That judgement is
reject_invalid_profile_facts(), which
every door shares and which judges “the whole resulting fact sequence rather
than the incoming change alone, so a patch is never left half-applied by a
later field’s refusal”; row writes reach it through
apply_profile_fact_changes().
That makes row creation a batch operation rather than a sequence of field
edits, so what a surface needs is the index a new row may occupy and the
facts that fill it – both derived from the schema’s own
ProfileSectionDefinition rather than
from a path convention restated per caller.
The application-owned row mutation below composes those two pure helpers with the canonical fact write door. Frontends collect values but never allocate a row or publish it themselves.
Core types:
UserProfileRecord.
- class ProfileRepeatableRowMutationOutcome(record, section_key, row_index)[source]¶
Bases:
objectThe exact encrypted record and row identity produced by one row addition.
- Parameters:
record (UserProfileRecord)
section_key (str)
row_index (int)
-
record:
UserProfileRecord¶
- next_section_row_index(section_key, present)[source]¶
Return the row index a new row of
section_keymay occupy.Row identity is
profile_section_rows(), the same reading the completeness check and the manager’s page already share, so a new row is numbered against the rows those two surfaces agree exist rather than against a fresh scan of the fact paths.Index
0is reserved whenever the section holds an UNINDEXED row. The setup wizard writes one –activities.descriptionwith no index – so an operator who completed setup already has an activity thatprofile_section_rowsreports under the""key. Numbering the next row0would leave that implicit row sitting beside an explicitly numbered one, and would collide with it outright if the implicit spelling were ever normalised toactivities.0.*: two rows would merge into one and an activity would vanish. Treating the implicit row as the occupant of slot0keeps the count honest now and leaves the normalisation reversible later.- Parameters:
- Returns:
one above the highest in use, and never below the floor the implicit row reserves. A gap left by a blanked-out row is NOT reclaimed – reusing it would silently give a new row the identity a reader may still hold from before.
- Return type:
An index no existing row occupies
- section_row_facts(section, *, row_index, values)[source]¶
Project one row’s collected values into facts at
section.INDEX.field.valuesis keyed by FIELD key, not by path: a caller collecting a row is answering “what goes in this row”, and the row it belongs to isrow_index. Keys naming no declared field are ignored, so a surface carrying its own bookkeeping entries alongside the answers does not persist them.A blank value yields NO fact rather than a fact carrying
None. On a new row there is nothing to clear, and a null fact would make an unanswered optional field indistinguishable from one deliberately emptied. A blank REQUIRED field is therefore not refused here – it simply does not arrive, and the write door refuses the incomplete row naming exactly what is missing, which keeps one authority over what a complete row is.Fields are emitted in declaration order, so the batch reads in the same order the section is displayed.
- Parameters:
section (
ProfileSectionDefinition) – The repeatable section being added to.row_index (
int) – The row these values fill, fromnext_section_row_index().values (
Mapping[str,str]) – Collected values keyed by field key.
- Return type:
- Returns:
The facts filling the row, empty when every value was blank.
- add_profile_repeatable_section_row(*, profile_id, section_key, values, schema, profile_decode_context)[source]¶
Allocate and publish one complete repeatable profile row atomically.
The schema selects the section, the current encrypted record determines the next stable index, and the shared profile-fact writer owns the one atomic publication and bucket event. A frontend must supply only the field-keyed values it collected.
schemamust come from the enclosing pinned authority operation; this operation does not consult bundled authoring data.- Return type:
- Parameters:
profile_id (str)
section_key (str)
schema (ProfileSchemaDefinition)
profile_decode_context (ProfileDecodeContext)