Convert - an app of SPPAS

Brigitte Bigi

User manual

Description

Annotated speech corpora circulate between tools. A corpus is transcribed in one software, aligned in another, analysed in a third, archived in a fourth. Each of these tools defines its own file format, and each format embodies a particular view of what an annotation is: a label on a point in time, a labelled interval, a hierarchy of tiers, a document carrying metadata. No format is a superset of the others. Moving annotations from one tool to another is therefore not a matter of translation, but of transposition: what the source expresses and the destination cannot hold has to be left behind.

This asymmetry is rarely visible to the people who depend on it. Annotation files are, for most of their users, opaque: opening one in a text editor reveals syntax, not structure. The user knows the corpus — the speakers, the tiers, the annotation scheme — but has no direct way to know whether the format they are exporting to is able to carry it. Conversion tools generally answer this by silence: either the operation succeeds, and the loss goes unnoticed, or it fails, and the reason belongs to the internals of the format rather than to anything the user can act upon.

The application "Convert" takes the opposite stance. Its purpose is not only to produce a file in another format, but to make the transposition itself intelligible. What a format is able to hold is stated before the conversion is launched, not discovered afterwards. What a format is unable to hold is expressed as a choice offered to the user, at the moment where the target format is selected, and applied uniformly to the whole set of files. What cannot be transposed at all is reported explicitly, file by file, with its cause.

This stance has a consequence on the shape of the tool. "Convert" operates on sets of files, heterogeneous in format and unknown in content at the time the user makes their decisions. It does not inspect a file in order to propose a tailored treatment; it lets the user state, once, how the annotations are to be written, and applies that decision to every file. The intelligibility "Convert" aims at is therefore not the intelligibility of a particular file, but of the destination format and of what reaching it costs.

"Convert" belongs to the SPPAS ecosystem and relies on its reading and writing capabilities, which cover a wide range of formats used in speech and multimodal annotation. It is available both as a page of the SPPAS Dashboard and as an online application, addressing the same need in two contexts of use: within a working session on a local corpus, or as an occasional service.

Accessibility is treated here as a functional requirement rather than an added quality: the information "Convert" exposes — supported properties, options, statuses — is of no use if it is not perceivable, and the tool is designed accordingly.

Defining the requirements

Problem statement and objectives

Converting annotation files raises a difficulty that is neither technical nor linguistic, but epistemic: the user must decide, before anything happens, what to do with information that the destination cannot receive. Formats differ in what they are able to express, and those differences are not documented anywhere the user is likely to look. The decision is therefore taken blindly, and its consequences appear only afterwards — as a file that fails to be written, or worse, as a file that is written and silently amputated.

The central problem addressed by "Convert" is thus:

how to let a user convert a set of annotation files to a chosen format, while making the cost of that choice knowable in advance and the outcome verifiable afterwards, without requiring any knowledge of how formats are internally organised.

This problem is sharpened by the conditions of use. Conversion is a batch operation: it applies to a set of files whose contents are not examined at the moment the user decides. It is also a directed operation: users convert because they are moving from one tool to another, with a definite destination in mind. Convert must therefore support decisions taken once, in ignorance of individual file contents, and applied uniformly — while remaining accountable for each file individually.

The objectives follow:

  • to let the user select one destination format among those that can be written, and to expose what that format is able to hold at the moment of the choice;
  • to let the user state, before conversion, how annotations are to be written when the destination cannot hold them as they are, these statements being attached to the format and not to any file;
  • to apply the same decisions to every file of the batch, and to report the outcome of each file separately, an individual failure never interrupting the others;
  • to give the user access to the converted files, in a manner adapted to the context of use;
  • to guarantee accessibility, of the interface and of the information the tool exposes;
  • to remain part of the SPPAS ecosystem, reusing its reading and writing capabilities without exposing them.

"Convert" does not aim to prepare files, to edit annotations, nor to correct a corpus. It aims to make one operation — writing annotations in another format — deliberate rather than accidental.

Expressing the requirements

Choosing a destination in full knowledge

A user selects a destination format because a tool downstream requires it, not because they know what that format can hold. The choice is therefore made under a constraint the user cannot evaluate. Convert answers this by presenting, alongside each writable format, the properties it supports — several tiers or one, metadata, hierarchies, controlled vocabularies, overlapping annotations, typed labels, and so on. This information is not documentation placed beside the tool; it is part of the act of choosing, available at the moment the decision is taken. Its presentation must make a comparison possible at a glance and remain legible to someone who has never opened an annotation file in an editor: the properties of a format are a technical matter, but understanding them must not require technical expertise.

Stating export options before reading anything

When a destination cannot hold what a source expresses, something must be decided. That decision belongs to the user, and it must be expressible without knowing which files will be affected: the batch is heterogeneous and its contents are not examined beforehand. Convert therefore attaches export options to the destination format itself. The options describe how to write, not what any file contains. Chosen once, they apply to the whole batch, and their meaning must be intelligible independently of any particular file.

Delimiting the set of files to convert

The files to be converted come from the working context, not from the tool: in a working session, they are the files currently selected in the workspace; in an occasional use, they are the files the user brings to the tool. The requirement is the same in both cases — the user delimits a set, Convert operates on that set, and the set is not silently altered. The two contexts differ only in their material constraints, an occasional use being bounded by what a service can reasonably receive.

Reporting each file individually

A batch is not an operation, but a series of operations sharing the same decisions. Some will succeed, some will not, for reasons belonging to the individual file rather than to the choices made. The user must be able to see, for every file, what happened to it: written, refused, or left untouched — and why. A refusal is stated in terms the user can act upon, and it never stops the files that follow. Reporting is therefore not a side effect of the conversion; it is what makes the operation verifiable.

Reaching the converted files

A conversion that produces files the user cannot find has not converted anything. In a working session, the produced files join the workspace and remain available to the following operations. In an occasional use, they are handed back to the user. The requirement is that the outcome be reachable without the user having to know where the tool put it.

Accessibility

Everything above rests on information being exposed: supported properties, available options, statuses, causes. This information is useless if it cannot be perceived. Contrast, legibility, text alternatives to any graphical encoding, keyboard operation, consistent terminology between the interface and the concepts it manipulates: these are conditions of the tool working at all, not qualities added to it. Convert exposes a technical subject to a non-technical audience; the burden of intelligibility lies entirely on the interface.

Modelling

Requirements

The previous sections state what users need and why; this section states what the system does to answer it, and what it manipulates in order to do so.

Two markers are used throughout. [API-OK] denotes what is achievable with the current SPPAS API. [API-EXT] denotes what requires an extension of that API. A requirement marked [API-EXT] is part of the model; it remains inactive until the API supports it.

Concepts manipulated by the application

The data dictionary of this application. Thirteen concepts, and the model below names no other.

D1. Format
A way of writing annotations on disk, identified by an extension and associated with a piece of software.
D2. Format property
A boolean characteristic of a format, stating what that format is able to hold. Properties belong to formats, never to files.
D3. Destination format
The single format, among those that can be written, into which the batch is converted.
D4. Batch
The set of files delimited by the user, heterogeneous in format, whose contents are unknown at the time decisions are taken.
D5. Export option
A question raised by what the destination format cannot hold, together with the answers available for it. Options are derived from properties, not written per format. An option covers one property where answering alters the annotations, and several where it does not.
D6. Answer
The user's decision for one option, applied to every file of the batch.
D7. General option
A decision independent of the destination format: overriding an existing output, detecting the format of an input whose extension is unknown.
D8. Remediation
A transformation applied to the annotations of one file, after reading and before writing, implementing the answer chosen for an option.
D9. Structural loss
Information not written because the destination format cannot hold it. Determined by the destination alone, identical for the whole batch, announced once.
D10. Contingent loss
Information not written, or content transformed, because of what one particular file contained. Determined at reading, reported for that file.
D11. Status
The outcome of one file: written, written with remarks, refused, or skipped.
D12. Remark
A contingent loss reported on one file.
D13. Cause
The explicit reason a file was refused.

Deriving the options from the destination format

The correspondence between an unsupported property, the question it raises, and the answers available for it is data of the model, not code. Adding a property to the API adds an entry to this table; it does not modify Convert.

The options are of two kinds, and the line between them is whether answering alters the annotations. Some unsupported properties concern what surrounds the annotations — metadata, controlled vocabularies, media declarations. Keeping them changes nothing of what was annotated. Others concern the annotations themselves, or the shape of the result: answering transforms the content, or the number of files produced. The first kind is decided once; the second, property by property.

Preservation: one decision

Where the destination cannot hold metadata, controlled vocabularies or media declarations, the user is not asked where to put them, but whether to keep them:

The preservation option
Question raised Answers Properties covered
keep what the format cannot hold yes / no metadata, ctrl_vocab, media

Answering yes lets SPPAS write that information wherever the destination allows: as comments where the format holds them (a), in a tier otherwise (b). Answering no discards it. Where the destination allows neither, yes has the same effect as no, and the option is not offered.

The answer has a default no user has to look for: the setting of the application, interoperability, true [API-OK]. Maintaining interoperability is the normal state; accepting the loss is a choice.

That setting is a default, not the decision. It is global — it weighs on the annotations as much as on the conversions — while its effect is seen here, on a page a user reaches without ever opening the settings. Convert therefore states the effective answer where it applies, and lets it be changed for this conversion alone. Online, where no setting is kept for a given user, the value of the page is the only one there is.

Where SPPAS writes what it preserves is a mechanism, not a decision. Users divide into those who want their file and care for nothing else, and those who want nothing lost; neither arbitrates between metadata and controlled vocabularies. Offering three answers for each of three properties would ask nine questions no one has.

Structure: one decision per property

The remaining options alter the annotations or the shape of the result. They are decided individually, because answering one says nothing of the others:

Unsupported properties raising a structural option
Unsupported property Question raised Answers
multi_tiers several tiers in the file one file per tier / skip the file
point tiers of points convert into intervals / skip the tier
alt_tag alternative labels keep all / keep the best scored
tag_types typed labels convert into strings / skip the tier
tag_geometry geometric labels convert into strings / skip the tier

alt_tag belongs here and not to preservation: discarding the alternatives of a label, however poorly scored, alters the annotation. What is written is no longer what was annotated, and that is a decision only the user can take.

(a) The property accept_comments states it, read by comments_support() [API-OK]. The API was already writing metadata as comments when the format allows it, and forgetting them otherwise; it now exposes that capability.

(b) Writing metadata, controlled vocabularies and media as a tier is done by create_unsupported_tier(), and read back by parse_unsupported_tier() [API-OK]. The tier is named DoNotEdit; see the annex for what it holds.

Properties raising no question

The remaining unsupported properties raise no question, because nothing can be fabricated and no transformation preserves the content:

  • no_tiers — a file holding no tier at all cannot be written.
  • empty_tier [API-OK] — a tier holding no annotation is removed where the destination refuses it, which empty_tier_support() states beforehand; see 3.3.
  • interval — intervals cannot be reduced to points. A file holding intervals is refused. The converse is remediable, hence the point option above.
  • hierarchy, disjoint, alt_localization, radius, gaps, overlaps — cause a structural loss, announced once for the batch.

Extensions of the API required by this section

None: the three extensions this section was stating are available [API-OK]. They are empty_tier_support(), comments_support(), and the pair create_unsupported_tier() / parse_unsupported_tier(). The annex states what they do and what they cost.

Remediation chaining

Remediations are not independent, and their order is not neutral. Removing empty tiers can leave a file with no tier, which the destination may refuse. Splitting a multi-tier file into one file per tier can produce a file whose single tier is empty. A remediation is therefore never final: it produces a new content, which is verified again against the properties of the destination.

A chain may end in a refusal. This is not a failure of the model: it means the content, once adapted as far as the destination allows, still exceeds what that destination can hold. The cause reported is the one that ended the chain, stated in terms of the user's data.

Conversion of one file

Anticipation comes first; the typed exceptions of the API act as a safety net for what anticipation did not catch.

  1. Read the file, applying the general option on format detection.
  2. Anticipate: compare the content read to the properties of the destination, apply the remediations chosen, chain and re-verify (3.3).
  3. Write.
  4. If writing fails on a typed exception identifying a remediable cause, remediate and write once more. One second pass only.
  5. If it fails again, or on a non-remediable cause, refuse the file and state the cause.

The failure of one file never interrupts the batch.

The typed exceptions of the current API relevant to writing are AioMultiTiersError (1510), AioNoTiersError (1515), AioEmptyTierError (1525) and AioLocationTypeError (1530) [API-OK]. They identify refusals bound to the properties of the destination, and are usable as causes as they stand.

Requirements as specified

[010] Starting a conversion

  • [011] The user can initiate a conversion of a set of annotation files.
  • [012] The system initialises a conversion with no destination and no batch.

[020] Understanding the process

  • [021] The user can identify what is required before a conversion can run.
  • [022] The system states which decisions are missing.

[030] Controlled progression

  • [031] The system allows a conversion to run only if a destination format is selected and the batch is not empty.

[100] Destination format

  • [101] The user can select one destination format.
  • [102] The system restricts the choice to the formats it is able to write.
  • [103] The system presents, for each format, the properties it supports.
  • [104] The system presents this information in a form comparable at a glance and intelligible without knowledge of file internals.
  • [105] The user can select at most one destination format at a time.
  • [106] The user can cancel the current selection.

[200] Batch

  • [201] The user delimits the set of files to convert.
  • [202] The system operates on that set and does not alter it.
  • [203] The system reports when the set is empty.

[300] Export options

  • [301] The system derives the options from the properties of the selected destination format.
  • [302] The system presents an option for each unsupported property for which a remediation exists and answering alters the annotations, and a single option covering those for which it does not.
  • [303] The user can choose one answer per option.
  • [304] The system applies a default answer to any option left unanswered.
  • [305] The answers apply to every file of the batch.
  • [306] The options are stated before any file is read.
  • [307] The system restricts the answers offered to those the destination format allows.
  • [308] The system takes the answer of the preservation option from the settings of the application, states it on the page, and lets the user change it for this conversion without altering the settings.

[400] General options

  • [401] The user can allow or forbid overriding an existing output file.
  • [402] The user can allow or forbid detecting the format of an input file whose extension is unknown.

[500] Announcing structural loss

  • [501] The system announces, once for the batch, what the destination format cannot hold.
  • [502] The system does not report structural loss per file.

[600] Converting

  • [601] The system reads each file of the batch.
  • [602] The system applies the chosen remediations before writing.
  • [603] The system re-verifies the content against the destination after each remediation.
  • [604] The system writes each file in the destination format.
  • [605] The system attempts a second write after remediating a typed failure.
  • [606] The failure of one file never interrupts the batch.

[700] Reporting

  • [701] The system reports a status for every file of the batch.
  • [702] The system reports the remarks attached to a file.
  • [703] The system states the cause of every refusal.
  • [704] The system states the cause in terms of the user's data, not of the internals of the format.

[800] Reaching the results

  • [801] The system makes the converted files reachable to the user.
  • [802] The system makes them reachable without the user knowing where they were written.

[900] Restarting

  • [901] The user can start a new conversion.
  • [902] The system resets the decisions without resetting the batch.

[1000] UX and accessibility

  • [1001] The system provides text alternatives to any graphical encoding of a property.
  • [1002] The system uses consistent terminology between interface and concepts.
  • [1003] The system is operable by keyboard and by screen reader.
  • [1004] The system provides explicit messages at every decision point.

Conceptual model

Convert holds no persistent data. Everything it manipulates belongs to one conversion in progress, and is derived from a small number of user decisions. This section states who it exchanges with, which data exist, what produces them, what invalidates them, and on which event each treatment runs.

Conceptual model of communication

Four actors border Convert, and only one of them is a person. Three are neighbouring systems, and Convert writes into none of them.

Actors

  • A1. The user — she chooses a destination format, answers the options it raises, sets the general options, runs the conversion, reads what happened to each file, reaches what was produced, and starts again.
  • A2. The provider of files — it exposes a set of files, possibly empty, in both contexts of execution. It is the domain of the provision of files, and the batch is what it exposes.
  • A3. The API of SPPAS — it says which formats can be written and what each of them holds, it reads annotations, it writes them, and it names the cause when it will not.
  • A4. The settings of SPPAS — they hold interoperability, which is the default answer of the preservation option and nothing more.

Flows

  • F1. A1 → Convert — the destination chosen, or its cancellation [101], [106].
  • F2. Convert → A1 — the formats which can be written, and what each of them holds, comparable at a glance [102], [103], [104].
  • F3. Convert → A1 — the options that destination raises, the answers each offers, and the answer which holds if none is given [301], [302], [307], [308].
  • F4. A1 → Convert — one answer for one option [303].
  • F5. A1 → Convert — a general option [401], [402].
  • F6. Convert → A1 — what the destination cannot hold, once for the batch [501].
  • F7. Convert → A1 — what is still missing before a conversion can run [021], [022], [203].
  • F8. A1 → Convert — run the conversion [031].
  • F9. A1 → Convert — start a new conversion [901].
  • F10. Convert → A1 — a status for every file, the remarks attached to it, and the cause of a refusal, stated in terms of her data [701] to [704].
  • F11. Convert → A1 — the converted files, reached without her knowing where they were written [801], [802].
  • F12. A2 → Convert — the batch: a set of files, possibly empty [201].
  • F13. Convert → A3 — which formats can be written, and what each of them holds.
  • F14. Convert → A3 — a file to read, and whether its format may be detected [402].
  • F15. A3 → Convert — the annotations of that file, or a typed cause.
  • F16. Convert → A3 — the annotations to write, in the destination format [604].
  • F17. A3 → Convert — written, or a typed cause [605].
  • F18. A4 → Convert — the value of interoperability [308].

Two flows do not exist, and each of them is a requirement. Nothing goes from Convert to A2: the batch is read and never altered [202]. Nothing goes from Convert to A4: the answer is stated on the page and changed for one conversion, and the settings do not move [308].

A3 is the only neighbour which is written to, and what is written there is a file, never a state: Convert relies on the reading and writing capabilities of SPPAS and never exposes them.

Conceptual model of data

Nine classes. Three of them are the knowledge of formats, which is data of the model and not code; three belong to one conversion in progress; three belong to what it produced. Nothing here survives a conversion but the first three.

The classes of entities

ClassPropertiesWhat it is
FORMAT# format, extension, software, can be writtenA way of writing annotations on disk (D1). What it holds is R1, and it is read from the API
PROPERTY# propertyA boolean characteristic of a format, stating what that format is able to hold (D2). It belongs to formats, never to files
OPTION# option, question, default answer, alters the annotationsThe question raised by what a destination cannot hold (D5). It is the catalogue, and it is data of the model
ANSWER# option, # answer, remediationOne of the answers an option offers (D6). It exists only inside its option, and names the remediation it asks for (D8)
CONVERSION# conversion, destination, override, heuristicOne conversion in progress: the destination chosen and the two general options (D3, D7). It exists while it is decided upon
FILE# fileOne file of the batch (D4). It is exposed by A2, and this domain holds its identity and nothing else
CHOICE# conversion, # option, # answerThe answer this conversion holds for one option. Absent, the default of the option is what holds [304]
RESULT# conversion, # file, status, causeWhat happened to one file of one batch: its status (D11) and, when it was refused, the cause (D13)
REMARK# conversion, # file, # remark, what it saysA contingent loss reported on one file (D10, D12). There may be several, and there may be none

The relations

No.VerbLeg 1Leg 2
R1supportsFORMAT (0,n)PROPERTY (0,n)
R2coversOPTION (1,n)PROPERTY (0,1)
R3offersANSWER (1,1)OPTION (1,n)
R4is converted byFILE (0,n)CONVERSION (0,n)
R5is decided inCHOICE (1,1)CONVERSION (0,n)
R6answersCHOICE (1,1)OPTION (1,n)
R7reports onRESULT (1,1)FILE (0,n)
R8belongs toRESULT (1,1)CONVERSION (0,n)
R9carriesREMARK (1,1)RESULT (0,n)

R2 is where the model is data and not code. An option covers one property where answering alters the annotations, and several where it does not: the preservation option covers metadata, ctrl_vocab and media, which is why its leg reads (1,n). A property covered by no option is (0,1) on its side, and it is exactly what makes a structural loss. Adding a property to the API adds a row; it modifies nothing.

R3 and R9 are aggregations: an answer is named inside the option which offers it, a remark inside the result which carries it, and neither has any existence outside it. R7 and R8 together identify a result: one file of one conversion has one result and no more.

A conversion holds no file. R4 is a relation and not a holding: the batch is exposed by A2, Convert reads it and never alters it [202]. What this domain knows of a file is its identity, which is what it takes to give it a result.

Computed, and never held

  • The properties of the destination, which are R1 read for one format.
  • The options raised, which are R2 read for the properties that format does not support, restricted to the answers that format allows [307].
  • The structural loss, which is what the destination does not support and no option covers (D9). It depends on the destination alone, and is the same for every file of the batch.
  • The answer which holds for an option, which is the choice when there is one and the default otherwise [304].
  • The effective default of the preservation option, which is interoperability read from A4 [308].
  • What is still missing before a conversion can run, which is a destination and a batch which is not empty [031].
  • The files produced, which stand where the API wrote them: one file of the batch may produce several, and none of them is held here.

What one conversion carries, in the words the code will use

  • destination — one format, among those that can be written.
  • properties — the boolean characteristics of destination, stating what it is able to hold.
  • options — the questions raised by the unsupported properties for which a remediation exists, each with its available answers.
  • answers — one decision per option.
  • general_options — override (boolean), heuristic (boolean).
  • batch — a set of files, possibly empty.
  • structural_loss — what destination cannot hold.
  • results — one result per file of the batch, each holding a status ∈ {written, written with remarks, refused, skipped}, the remarks attached to it, and the cause of a refusal.

Their dependencies, which the treatments below turn into events:

  • destination → properties
  • properties → options
  • properties → structural_loss
  • options → answers
  • (batch ∧ destination ∧ answers ∧ general_options) → results

What is not modelled, and why

  • The annotations themselves, which are the API's: this domain reads them, transforms them through a remediation, and writes them back, holding none.
  • The files of the batch beyond their identity, which are A2's [202].
  • The settings, which are A4's and are read and never written [308].
  • The remediations themselves (D8), which are what an answer names and what the API performs. The model says which answer asks for which; how it is done is not of this level.

Conceptual model of treatments

Eight treatments. Six are the decisions and the conversion; two were brought out by writing the events, three requirements having had no treatment to stand in. Each one says on which event it runs, what it does, and under which condition each of its results is emitted.

The events

No.EventKindComes from
E1The user chooses a destination formatexternalF1
E2The user cancels the destinationexternalF1
E3The user answers an optionexternalF4
E4The user sets a general optionexternalF5
E5The batch is exposedexternalF12
E6The user asks for the conversion to runexternalF8
E7The user asks for a new conversionexternalF9
E8The destination is definedinternalT01
E9The options are knowninternalT02
E10A decision has changedinternalT01, T03, T04, T05
E11The batch has been convertedinternalT06

E10 is the event this application turns on. Every treatment which takes a decision emits it, and two treatments listen: the results are invalidated, and what is still missing before a conversion can run is said again [021], [022]. No treatment is written for that announcement: it is a result of the four, because a decision which changed is the only moment at which what is missing can have changed.

Nothing here waits for two events. T06 is the only treatment with a condition on its synchronisation, and that condition is read on the data it already holds, not on a second event.

T01 — Set the destination format

  • Event: E1 or E2.
  • Purpose: define the format into which the batch is to be converted.
  • Inputs: user choice (one format).
  • Outputs: destination.
  • Preconditions: none.
  • Postconditions: destination is available to T02 and T06.
  • Rules:
    • the format must be one the system is able to write;
    • at most one destination is defined at a time;
    • the destination may be cancelled, leaving it undefined;
    • any change of destination invalidates properties, options, answers, structural_loss and results.
  • Emission: destination and E8, when a format is chosen and it is one the system can write; destination undefined and E10, when it is cancelled; F7, always; E10, always.

T02 — Derive the options and the structural loss

  • Event: E8.
  • Purpose: state what the destination cannot hold, and which of it can be decided upon.
  • Inputs: destination.
  • Outputs: properties, options, structural_loss.
  • Preconditions: destination defined.
  • Postconditions: options are available to T03; structural_loss is available for announcement.
  • Rules:
    • the correspondence between an unsupported property, the question it raises and the answers available is data of the model, not code;
    • an unsupported property with no remediation produces no option and contributes to structural_loss;
    • the answers offered are restricted to those the destination allows, which may depend on other properties of that same destination;
    • structural_loss depends on the destination alone: it is identical for every file of the batch;
    • this treatment reads no file.
  • Emission: properties, options and E9, always; F3, when at least one option is raised; F6, when the destination does not support a property no option covers.

T03 — Answer the options

  • Event: E3, the options being known (E9).
  • Purpose: decide how annotations are to be written when the destination cannot hold them as they are.
  • Inputs: options, user choices.
  • Outputs: answers.
  • Preconditions: options defined.
  • Postconditions: answers are available to T06.
  • Rules:
    • one answer per option, chosen among the answers that option offers;
    • an option left unanswered takes its default answer;
    • the answers apply to every file of the batch;
    • the answers are decided without any file being read;
    • any change of an answer invalidates results.
  • Emission: answers, always; E10, always. Nothing is emitted for an option left unanswered: its default is what holds [304].

T04 — Set the general options

  • Event: E4.
  • Purpose: decide what does not depend on the destination.
  • Inputs: user choices.
  • Outputs: general_options.
  • Preconditions: none.
  • Postconditions: general_options are available to T06.
  • Rules:
    • override states whether an existing output file may be replaced;
    • heuristic states whether the format of an input file whose extension is unknown may be detected;
    • both default to false;
    • any change invalidates results.
  • Emission: general_options, always; E10, always.

T05 — Delimit the batch

  • Event: E5.
  • Purpose: define the set of files to convert.
  • Inputs: the working context.
  • Outputs: batch.
  • Preconditions: none.
  • Postconditions: batch is available to T06.
  • Rules:
    • the batch is delimited by the user, not by the system;
    • the system does not alter the batch;
    • the batch is heterogeneous in format and unknown in content;
    • the batch may be empty, which forbids T06;
    • any change of the batch invalidates results.
  • Emission: batch, always; E10, always; F7 saying the set is empty, when it is [203].

T06 — Produce the results

  • Event: E6, and a destination defined and a batch which is not empty [031].
  • Purpose: write the annotations of every file of the batch in the destination format, and state what happened to each.
  • Inputs: batch, destination, answers, general_options.
  • Outputs: results, converted files.
  • Preconditions: destination defined; batch not empty; answers defined for every option.
  • Postconditions: every file of the batch holds a result; the converted files are reachable by the user.
  • Rules:
    • each file of the batch is treated independently; the failure of one never interrupts the others;
    • a file is read first, applying heuristic;
    • the content read is then compared to properties, and the remediations stated by answers are applied before writing;
    • a remediation produces a new content, which is verified again against properties; remediations therefore chain, and a chain may end in a refusal;
    • writing that fails on a typed cause for which a remediation exists is retried once, after that remediation; there is no further pass;
    • a file that cannot be written is refused, with a cause stated in terms of the user's data;
    • a file whose output already exists is skipped, unless override allows it;
    • a remediation that transformed the content of one file produces a remark on that file;
    • what destination cannot hold is not remarked per file: it belongs to structural_loss, announced once;
    • one file may produce several converted files;
    • this treatment does not alter the batch.
  • Emission: One result per file, always; F10, always; E11, always. Nothing is emitted before the whole batch has been treated: a file which fails emits its result and never interrupts the others [606].

T07 — Start a new conversion

  • Event: E7.
  • Purpose: take the decisions back to what they were before any were taken, without taking the batch away.
  • Inputs: user choice.
  • Outputs: destination, options, answers, general_options, structural_loss and results, all undefined.
  • Preconditions: none.
  • Postconditions: the conversion is in the state T01 finds it in at the beginning [012].
  • Emission: F2, always; F7, always; E10, always.
  • Rules:
    • the batch is not reset: it was not decided here, and it is not this application which delimits it [902];
    • the general options are decisions and are reset with the others;
    • nothing of what was written on disk is undone: a new conversion is a new set of decisions, not the undoing of a conversion which ran.

T08 — Make the converted files reachable

  • Event: E11.
  • Purpose: that the user reaches what was produced without knowing where it was written.
  • Inputs: results, and the files the API wrote.
  • Outputs: the way each produced file is reached.
  • Preconditions: the batch has been converted.
  • Postconditions: every file which was written is reachable, and the user was told where none of them stands [801], [802].
  • Emission: F11, for every file which was written; nothing at all for a file which was refused or skipped.
  • Rules:
    • one file of the batch may have produced several files, and each of them is reached;
    • this application does not say where a file stands: where it was written is the business of what provided the files, and reaching it is asked of that one [802];
    • this treatment writes nothing and moves nothing.

Organisational level

Organisational model of treatments

One procedure per treatment, numbered as it is. There are two contexts of execution and one organisation: what differs between the local server and uwsgi is who exposes the batch, and nothing of what is done. Convert is served identically by the two, which is why there is one table and not two.

PFWhat it doesTriggered byIn chargeWhereWhenNature
PF1 (T01)Sets the destination format, or cancels itE1, E2the user, and the server which restricts the choicethe browser, then the serverwhenever she chooses or cancelsconversational
PF2 (T02)Derives the options and the structural lossE8the serverthe serveras soon as a destination is definedautomated
PF3 (T03)Answers an optionE3the userthe browser, then the serverwhenever she answers oneconversational
PF4 (T04)Sets a general optionE4the userthe browser, then the serverwhenever she sets oneconversational
PF5 (T05)Delimits the batchE5the provider of files, and never Convertoutside this applicationbefore Convert is reached locally; when the files are given, onlineautomated
PF6 (T06)Produces the resultsE6the serverthe serverwhen she asks, and only thenautomated
PF7 (T07)Starts a new conversionE7the serverthe serverwhen she asksautomated
PF8 (T08)Makes the converted files reachableE11the provider of filesthe server, then her machineas soon as the batch has been converted, then when she followsautomated, then conversational

Every procedure ends on the server, and none of them ends in the browser. Three are conversational, and in all three what the browser holds is the gesture: the format clicked, the answer chosen, the box ticked. What that gesture means — which options a format raises, which answers it allows, what is still missing — is knowledge of formats, and the client holds none of it. RO1

PF5 is in nobody's charge here. The batch is exposed by a neighbour in both contexts, and this application reads it. It appears in this table so that the one procedure Convert does not perform is written down, and not passed over in silence. RO5

PF6 is one request, and it is the only long one. The results replace what was shown when it ends; nothing is shown per file while it runs. A batch which takes minutes holds that request for minutes, and this level says so rather than leaving it to be discovered. What would show progress per file is not modelled, and it is the one thing this table leaves to be decided. RO6

What the user acts on

Client-side triggers

  • Selecting the destination format
  • Cancelling the selected destination format
  • Answering an export option
  • Setting a general option
  • Requesting the conversion to run (triggers T06)
  • Requesting a new conversion (resets the decisions)

Selecting a destination triggers T02 and produces the options and the structural loss. This is a server request: the correspondence between properties and options is data of the model, and the client holds no knowledge of formats.

Navigation

Convert is a single page. The conversion is not a pathway in the sense TextCueS gives that word: there are no successive steps, but decisions taken in any order, and one operation which requires them. The page shows the formats, the options derived from the selected one, the general options, and the results once the conversion has run.

From the page:

  • one can select and cancel a destination without leaving it;
  • one can run a conversion, which replaces the results shown;
  • one can start a new conversion, which resets the decisions.

Rules of organisation

  • RO1 — The client holds no knowledge of formats. Every derivation is a server request, the correspondence between properties and options being data of the model.
  • RO2 — The two contexts of execution are one organisation. What differs is who exposes the batch, never what is done with it, and the two deployments are one implementation.
  • RO3 — The decisions are taken in any order. None of them is a step, and none is a precondition of another: only the conversion requires them all.
  • RO4 — A decision is never refused because another is missing. What is still missing is said [022], and running is forbidden until it is not [031].
  • RO5 — The batch is exposed by a neighbour and read. Convert selects no file, in either context, and alters none [202].
  • RO6 — The conversion is one request. The results replace what was shown when it ends, and the failure of one file never ends it [606].
  • RO7 — A setting is read and never written. The answer which holds is carried by the page, for this conversion alone [308].
  • RO8 — What one conversion has decided is carried between two requests by what the page transmits, and by nothing held on the server. Online, no setting and no session is kept for a given user, and a decision held on the server would be a decision belonging to somebody.

RO8 is what makes the two contexts one organisation: a conversion which keeps nothing on the server is served the same way by a server which knows the person and by one which does not. It is also what T07 rests on — starting again is transmitting nothing rather than undoing something.

Constraints

What the code holds to

Gathered from the rules of the treatments and from the requirements they answer, and written here once so that each of them has one place and one only. Nothing is added: what follows was already said, and what this chapter buys is that a constraint can be pointed at.

No.ConstraintImposed by
C1At most one destination is defined at a time, and it may be cancelled, leaving none.[105], [106], T01
C2A destination is one of the formats the system is able to write.[102], T01
C3A decision which changes invalidates everything derived from it, which is then neither reused nor shown.T01, T03, T04, T05, and E10
C4The correspondence between an unsupported property, the question it raises and the answers available is data of the model, never code.T02, and what adding a property to the API must cost
C5The answers offered for an option are restricted to those the destination allows.[307], T02
C6The structural loss depends on the destination alone: it is the same for every file, and it is announced once and never per file.[501], [502], T02
C7The options are stated before any file is read.[306], T02, T03
C8An option left unanswered takes its default answer, which reproduces what the API does today where it does something.[304], T03
C9An answer applies to every file of the batch.[305], T03
C10The general options default to false.T04
C11The batch is read and never altered, and no file is selected here.[201], [202], T05, RO5
C12A conversion runs only with a destination defined and a batch which is not empty.[031], T06
C13Each file is treated on its own, and the failure of one never interrupts the others.[606], T06
C14A remediation produces a new content, which is verified against the destination again.[603], T06
C15A remediation is applied at most once for one file, which is what ends the chain.C14, and a loop needing a reason to stop
C16A write which fails on a typed cause for which a remediation exists is attempted once more, and there is no third attempt.[605], T06
C17A file whose output already exists is skipped, unless override allows replacing it.T04, T06
C18A cause is stated in terms of the user's data, and never of the internals of a format.[704], T06
C19One file of the batch may produce several converted files, and the report is not one line per file of the batch.T06, T08
C20The setting interoperability is read and never written: the answer which holds is carried by the page, for one conversion.[308], RO7
C21Nothing of a conversion is held on the server between two requests.RO8, and a context where nothing is kept for a given user
C22Where a converted file stands is not said by this application.[802], T08
C23What a failed write left on the disk is not removed here.The API, which writes; and C13, nothing being undone for the batch

C4 is the one which decides the shape of the code. It is why the correspondence is a file and not a branch, why a property added to the API costs a row, and why ModelFormats is the only class to be read again that day.

C15 and C21 were not in the dossier before the levels below the conceptual were written. The first is what makes the chain of C14 terminate; the second is what makes one implementation serve the two contexts of execution. Both are decisions, and they are written here rather than left to be discovered in the code.

Two constraints are refusals this application accepts rather than avoids. C13 says a batch is never undone, and C23 says what that costs: a file which was refused may have left something where it was to be written, and override is what the next conversion decides on.

Logical level

The tables

Each class of entities becomes a table, and its identifier becomes its key. Ten tables: five are the knowledge of formats and are permanent, five belong to one conversion and outlive nothing.

TableColumnsComes from
FORMATformat, extension, software, can be writtenThe class FORMAT
PROPERTYproperty, optionThe class PROPERTY, and R2
SUPPORTSformat, propertyR1, which is many to many
OPTIONoption, question, default answer, alters the annotationsThe class OPTION
ANSWERoption, answer, remediationThe class ANSWER, and R3
CONVERSIONconversion, destination, override, heuristicThe class CONVERSION
BATCHconversion, fileR4, which is many to many
CHOICEconversion, option, answerThe class CHOICE, and R5 with R6
RESULTconversion, file, status, causeThe class RESULT, and R7 with R8
REMARKconversion, file, remark, what it saysThe class REMARK, and R9

The structural loss is a column which is empty. R2 has its low cardinality on the side of PROPERTY, which is (0,1): the table takes the key of OPTION, and it may hold none. A property which a destination does not support and whose option is empty is exactly what that destination cannot hold and no answer can remedy (D9). Nothing counts it and no rule states it: it is read where it stands.

R3 and R9 are aggregations: the aggregated table takes the key of the aggregating one. An answer is named inside its option and two options offer an answer of one name without offering the same answer; a remark is named inside the result which carries it.

R5 and R6 both have their low cardinality on the side of CHOICE, which is (1,1): the table takes the two keys, and the two together identify it. One conversion answers one option once. The same holds of RESULT with R7 and R8: one file of one conversion has one result and no more.

Two tables are read and never written. SUPPORTS is what the API answers, and the table is the shape of that answer, not something this application keeps. BATCH is what the provider of files exposes, read at every request and altered by nothing here [202]. They are written down because the model needs their shape, not because anything of this domain holds them.

Not in the tables, and computed every time they are wanted: the properties of the destination, the options it raises, the structural loss, the answer which holds for an option, the effective default of the preservation option, what is still missing before a conversion can run, and the files which were produced. None of the seven would stay true if it were held, three decisions out of four invalidating them.

Physical level

Where each table stands

Here the machines are named, and not before. There is no database: three tables stand in the API of SPPAS, two in a file of this application, and five stand nowhere at all.

TableWhere it standsBetween two requestsWhat the user sees
FORMATThe API of SPPASIts ownThe formats she may choose among, and what each of them holds
SUPPORTSThe API of SPPAS, read at every requestIts ownThe same, read as a property supported or not
PROPERTYThe API for its identity, a file of this application for the option which covers itThe fileNothing of a property by itself
OPTIONA file of this application, read and never writtenThe fileThe question, and the answers it offers
ANSWERThe same fileThe sameThe answers of one option, restricted to those the destination allows
CONVERSIONWhat the page transmitsNothing: it is rebuilt at every requestHer decisions, as she took them
BATCHWhat the provider of files exposesThat domain'sThe files she is converting
CHOICEWhat the page transmitsNothingThe answer standing for each option
RESULTMade by the conversion and sent to the pageNothingA status, and a cause when it was refused
REMARKThe sameNothingWhat was transformed in her file

Five tables stand nowhere between two requests, and that is RO8 written as a place. A conversion is rebuilt from what the page transmitted, used, and dropped. Nothing on the server belongs to anybody, which is what lets one implementation serve a context which knows the person and one which does not.

The correspondence is a file, and that is the whole of « data of the model, not code ». Adding a property to the API adds a row to that file: a property with an option and an answer becomes a question asked; a property with none becomes a structural loss announced. No treatment is written, no branch is added, and nothing of this application is read again.

The converted files stand where the API wrote them. This application does not say where that is, and does not have to: reaching them is asked of what provided them [802].

Operational model of treatments

The organisational level said by what and when; this says in what tasks, and what among them cannot be half done.

One request is one unit. Every request begins by rebuilding the conversion from what the page transmitted, runs the one treatment it was asked for, and sends back what is to be shown. Nothing is begun in one request and finished in another — T06 included, which is long and is one request all the same.

PFThe tasks, in orderWhat cannot be half done
PF1Rebuild; read the writable formats from the API; verify the format chosen is one of them; hold itNothing: nothing is written
PF2Rebuild; read the properties of the destination; read the correspondence; raise the options and restrict their answers; gather what no option coversNothing: nothing is written
PF3, PF4Rebuild; hold the answer or the general optionNothing
PF6Rebuild; verify a destination and a batch which is not empty; then, file by file: read, anticipate, write, and at most one second writeThe result of one file. Not the batch, and not the writing itself
PF7Rebuild nothing: answer the page in the state T01 finds at the beginningNothing
PF8Ask the provider of files for the way each produced file is reachedNothing

One file is one unit, and the batch is not

The failure of one file never interrupts the others [606], so nothing here holds for the batch as a whole: there is no moment at which a conversion is half done and has to be undone. A file has its result, or it has not been reached yet.

What a failed write left on the disk is the API's, and this application removes nothing. It states a refusal on what the API answered. A file which was refused may therefore have left something where it was to be written, and the general option override is what decides whether the next conversion may replace it. That is a consequence of writing where the user asked, and not a fault of the model.

The chain of remediations ends

A remediation produces a new content which is verified again, so the chain is a loop, and a loop needs a reason to stop. It is this: a remediation is applied at most once for one file. A content which, after every remediation its answers allow, still exceeds what the destination can hold is refused, and the cause reported is the one which ended the chain.

Without that rule, removing empty tiers and splitting a multi-tier file could ask for each other in turn. With it, the chain is as long as the number of options and no longer.

The second write is not a second chance

Anticipation comes first and the typed exceptions are a safety net. The second write happens only on a typed cause for which a remediation exists, and there is no third: a second failure is a refusal. What that costs is bounded — two writes per file at the worst — and what it buys is the file which anticipation did not catch.

Where the code goes

In sppas/ui/swapp/app_convert/. One module per kind of knowledge, and every procedure has one module and one only. The separation this application must keep is the one stated below, and these are its four lines.

ModuleWhat it doesProcedure
formatsKnows which formats can be written and what each holds, and derives from a destination the options, their answers and the structural loss. Reads no filePF1, PF2
remediationTransforms annotations in memory: applies what the answers require, verifies the result again, chains, and says which transformation touched what. Reads and writes no fileWhat PF6 asks of it
conversionReads a file, calls the remediation, writes, retries once, and produces the result of that filePF6
provisionReads the batch the provision of files exposes, and asks it by what each produced file is reached. Alters nothing of itPF5, PF8
decisionsRebuilds a conversion from what the page transmitted, holds what was decided, reads the settings, and says what is still missingPF3, PF4, PF7, and the beginning of every other
presentationRenders the state, collects the decisions, triggers the eventsThe view of every procedure

What each one promises, which is the reason for the cutting:

  • The formats module is the only one which knows what a format holds, and it holds it as data. The day the API gains a property, it is the only one to read again — and what is read again is a file, not a branch.
  • Remediation touches no disk: it is given a content and gives back a content. It is where the business logic of this application lies, and it is what can be tested with no file at all.
  • The conversion module is the only one which reads and writes files, so it is the only one which meets a typed cause, and the only one to read again the day the API raises another.
  • Provision is the only one which speaks to the neighbouring domain. It is given a batch and a way to reach a file, never a path, so there is nowhere here for the place of a converted file to be known.
  • Decisions is the only one which knows what the page transmits. It holds no state of its own between two requests, and it is where the invalidation is done: what was derived from a decision which changed is not rebuilt.
  • Presentation decides nothing. What it shows was decided before it was called, and the JavaScript it carries renders and nothing else.

This section states the principles retained for implementing Convert, in keeping with the requirements and the conceptual model, without prejudging detailed technical choices. Its purpose is to bind every implemented component to the requirement it satisfies.

Convert holds no persistent data of its own. Everything it manipulates belongs to one conversion in progress. The implementation is therefore organised around a chain of treatments, each producing clearly typed derived data, and each answering an identified requirement.

The implementation must keep a strict separation between:

Convert relies on the SPPAS reading and writing capabilities and never exposes them. It is served identically by the local SPPAS server and by uwsgi: the two deployments are one implementation, not two.

The batch is provided, not constituted

Convert does not select files. It receives a batch from its execution context and does not alter it. In a local session, that context is the SPPAS workspace, whose files are checked elsewhere, before Convert is reached: no file selection belongs to Convert. In an online use, the batch results from files brought by the user to the service.

Convert therefore depends on a provider of files, of which it requires only a contract:

  • it exposes a set of files, possibly empty;
  • Convert reads that set and never modifies it;
  • it exists in both contexts of execution.

That component was not designed when this was written, and it is now: it is the provision of files, a service of swapp, and it has its own dossier. Convert is one of the apps it calls A2, and the contract above is what that dossier answers — a workspace holding the files to be treated and them only. What Convert receives is made for it, and the workspace of the person never leaves that domain.

Two things follow, and neither changes the model. What Convert reads is checked elsewhere, so no file selection belongs here [202]; and what it produces is given back to that same service, which is what makes it reachable without the user knowing where it stands [801], [802]. T05 still states a provided batch and not a constituted one, and T08 asks rather than answers.

Implementation model: MVC

  • Model: knows the formats, performs T02 and T06, and is the only one which speaks to the provision of files — for the batch (T05) and for reaching what was produced (T08). It is a façade over the SPPAS reading and writing capabilities.
  • Controller: identifies the task requested, checks its preconditions, chooses the treatment to run, invalidates the derived data, holds the transient state, calls the model, and prepares the data for display.
  • View: renders the state, collects the decisions, triggers the events. No business logic; JavaScript is rendering and interaction support only.

What receives a request is not the controller. The WhakerPy response handles the URL, receives the GET or POST and the events it carries, instantiates the controller, calls it once for that request, and bakes the returned tree — either a full page or an update payload. The controller is called; it does not listen.

The task to run is carried by the request, as an event. The controller reads it and handles it. This holds because Convert has one controller; an application with several would have to dispatch, which is not the case here.

Model

The model is the functional core. It holds the knowledge of formats and the transformation of annotations, and exposes neither to the rest of the application. The façade ConvertModel exposes the single interface the application expects and delegates to three specialised sub-models.

ModelFormats holds the knowledge of formats. It exposes the formats that can be written, their properties, and derives from a destination the options, their available answers, and the structural loss (T02). The correspondence between an unsupported property, the question it raises and the answers available is data it holds, not code it runs: adding a property to the SPPAS API adds an entry, and modifies nothing else. This sub-model reads no file.

ModelRemediation holds the transformations applied to annotations. Given a content and the properties of a destination, it states which remediations the answers require, applies them, and verifies the resulting content against those properties again — a remediation producing a new content, remediations chain, and a chain may end in a refusal. It also states which remediations transformed the content, so that remarks can be attached to the file. This sub-model reads and writes no file: it transforms annotations in memory. It is the place where the business logic of Convert lies, and it is testable on its own.

ModelConversion performs T06. For each file of the batch it reads, calls ModelRemediation, writes, retries once after remediating a typed failure, and produces the result of that file: its status, its remarks, the cause of a refusal. It never interrupts the batch, and one file may produce several converted files.

ModelProvision is the only class which speaks to the provision of files. It exposes the batch that domain holds, and asks it by what each produced file is reached (T08). It is given a way to reach a file and never a path: where a converted file stands is not said by this application C22. It alters nothing of the batch C11, and it is the only place to be read again the day that domain changes.

This structure lets the knowledge of formats, the transformation of annotations, the conversion of files and the dependency on a neighbour evolve separately, behind one stable façade.

Controller

The controller is called once per request, in a stateless context. It initialises an empty state, then rebuilds the current state exclusively from the data transmitted to it. What carries that state is one object, ConvertRecord: the view serialises it at each user action and the controller parses it back, and neither of them holds it between two requests C21. The controller identifies the task requested, checks its preconditions, calls the model, and prepares the data for display.

The controller is also the only one which reads the settings of the application. It takes interoperability from them, hands it to the conversion as the default answer of the preservation option, and writes nothing back: the value is changed for this conversion alone, on the page [308] C20. The model knows no setting, and the view decides nothing.

The controller handles invalidation. Any change of an upstream datum invalidates the data derived from it, which are then neither reused nor displayed: a change of destination invalidates the properties, the options, the answers, the structural loss and the results; a change of an answer, of a general option or of the batch invalidates the results. This invalidation is implicit: only the data explicitly transmitted and recomputed during the current request are used.

Tasks:

  • start — transmitted: nothing. Preconditions: none. Triggers: none. Produces: the page in its initial state, with the writable formats and no destination.
  • destination — transmitted: the destination. Preconditions: the format can be written. Triggers: T02. Produces: the options, their answers, the structural loss.
  • answer — transmitted: the destination, the answers taken so far, and the one just given. Preconditions: the option belongs to the destination, and the answer is one it offers. Triggers: T03. Produces: the answers, and the results invalidated.
  • general — transmitted: everything decided so far, and the general option just set. Preconditions: none. Triggers: T04. Produces: the general options, and the results invalidated.
  • convert — transmitted: the destination, the answers, the general options. Preconditions: destination defined; batch not empty; every option answered, defaults applying to those left unanswered. Triggers: T06. Produces: the results.
  • reset — transmitted: nothing. Preconditions: none. Triggers: none. Produces: the page in its initial state. The batch is not reset: it belongs to the context, not to Convert.
Default answers

An option left unanswered takes a default. Two principles apply, in this order: the default reproduces the current behaviour of the SPPAS API where one exists; failing that, it takes no initiative.

Default answer per option
Option Default Reason
preservation yes current behaviour
multi_tiers skip the file no initiative
point skip the tier no initiative
alt_tag keep all current behaviour
tag_types skip the tier no initiative
tag_geometry skip the tier no initiative

Preservation defaults to yes because that is what the API does today: it writes as comments what a format cannot hold, wherever comments are allowed. Where the destination allows neither comments nor a tier, the option is not offered and the information is lost — which is, again, the current behaviour.

Where a precondition is not satisfied, the controller runs no treatment and states which decision is missing. A decision is never refused because another is missing RO4.

Six tasks for eight treatments, and the two which have none have none for a reason: T02 is triggered by destination and never asked for on its own, and T08 follows the conversion in the same request. Every other treatment is one task, and every task is one request.

The controller is implemented by a single class ConvertController, exposing one entry point handle(request_data).

Views

As in TextCueS, the WhakerPy response builds the whole HTML tree and the structural elements common to the application — head, header, nav, footer — are factored there. The Convert views build only the content of body > main, and of body > script where support scripts are required. They create no structural element, handle no navigation, take no functional decision and perform no linguistic treatment.

Convert has one applicative view, ViewConvert, whose content varies with the data the controller transmits. It is built from five fragments, each with a defined scope.

The shape of these fragments follows from an observation about the users: they do not choose a destination format. They know it already — they are moving to Elan, to Praat, to subtitles — and they come to Convert to reach it. The properties of formats are therefore not there to help decide which format to pick; they are there to state what reaching the one already picked costs. The two are not served by the same presentation, and conflating them is what makes a matrix of formats by properties both indispensable and unreadable.

FragmentFormats presents the formats that can be written, and lets one of them be selected or cancelled. It is a list of extensions, grouped where grouping is possible: the extensions of one piece of software together, the subtitle formats together, the rest as they come. The grouping is heterogeneous by nature — Praat holds three extensions, some formats hold no software at all — and it exists only to let the user find what they came for. This fragment carries no property.

FragmentDestination presents what the selected format holds, and is where the export options are answered. It is an aside: consulting it is not leaving the list. It shows every property, always, in the same order, whatever the format selected. What varies from one format to the next is the state of each line, and whether a decision appears on it. A property has three states, and no more: held; not held, with nothing to decide; not held, with a decision to take. Preservation, covering several properties at once, appears as one line among them.

The stability of that list is what makes it usable. Labels that do not move let the user click from one format to another and see what changed, without reading again. It also answers requirement [104] without hiding anything: a scientific application states its data. Some properties are widely understood — several tiers, media, metadata — and others are specificities that exist almost nowhere but in SPPAS. The latter are made discreet, not absent.

FragmentCompare presents the full matrix of formats by properties, as a details element the user unfolds. This is the one place where formats are compared to one another, which is a legitimate thing to want and a poor way to choose a destination. Unfolded in place rather than opened as a dialog, because comparing is consulting, not deciding: the page stays where it is. Given the room a details element affords, the matrix can here be what it never is when it drives the choice — readable.

FragmentGeneral presents the general options.

FragmentResults presents the result of every file of the batch: its status, the remarks attached to it, and the cause of a refusal. It exists only once a conversion has run. One file of the batch may produce several converted files: the report is not a line per input file.

JavaScript is interface support. It handles local interactions — selecting a format, answering an option, refreshing the aside once a destination is known, refreshing the results — and transmits the state to the server. It holds no business logic and takes no decision on the treatments to run: it knows nothing of formats, of properties, or of what a remediation is. Which options a destination raises is derived by the model, server-side; the client displays what it is given.

The view serialises the current state at each user action. In a strictly stateless context, it transmits to the controller everything the requested treatment needs, without relying on any persistence, client-side or server-side.

The view carries the cross-cutting requirements: contrast, legibility, text alternatives to any graphical encoding, keyboard and screen-reader operation, consistent terminology between the interface and the concepts of the model, explicit messages at every decision point. Convert exposes a technical subject to a non-technical audience; the burden of intelligibility lies entirely here.

UML

The classes

Those which carry the data come from the tables; those which work come from the modules and from the MVC stated above. The names of the working classes are the ones that section fixed, and nothing is renamed here.

What carries the data

ClassFromWhat it is
FormatFORMAT, SUPPORTSAn extension, the software which reads it, and what it holds. Read from the API and kept by nobody
OptionOPTION, ANSWER, PROPERTYA question, the answers it offers, the answer which holds when none is given, and whether answering alters the annotations. It is read from the file which holds the correspondence
ConversionCONVERSION, CHOICEWhat was decided: the destination, the two general options, and one answer per option. It is rebuilt at every request and held between none
FileResultRESULT, REMARKWhat happened to one file: a status, the remarks attached to it, and the cause of a refusal
ConvertRecord—What one request holds: the task it carries, the conversion it was given, and what is to be shown. It serialises itself for transport and parses itself back, and it is held by nobody between two requests
noneBATCHThe files are exposed by the provision of files, which names them already

ConvertRecord is the class RO8 asks for and which no section named. The view serialises the state at each action and the controller rebuilds it: between the two there is an object, and it is better named than implied. It is what makes the two contexts of execution one implementation.

What does the work

ClassModuleWhat it is alone in knowing
ConvertModel—The façade. It is what the controller sees, and it sees nothing behind it
ModelFormatsformatsWhat a format holds, and what a destination raises as options and as structural loss. Reads no file
ModelRemediationremediationHow annotations are transformed, chained and verified again. Reads and writes no file
ModelConversionconversionHow a file is read, written, retried once and given a result. The only class which meets a typed cause
ModelProvisionprovisionHow the provision of files is spoken to: the batch it exposes, and by what a produced file is reached
ConvertControllerdecisionsWhat a request transmits, which task it carries, what a decision which changed invalidates, and what the settings say
ViewConvertpresentationWhat is shown, in five fragments, and nothing of what it means

The four sub-models are what the façade holds, and the fourth is ModelProvision: the batch and the way a produced file is reached are asked of a neighbour, and one class is what asks it.

The five fragments — FragmentFormats, FragmentDestination, FragmentCompare, FragmentGeneral, FragmentResults — are classes of the view and build each their own part of body > main. They decide nothing, count nothing and refuse nothing.

What assembles

The response of WhakerPy handles the URL, builds the view and the controller, calls the controller once for that request, and bakes the tree it was given. It is named here ConvertResponseRecipe, and it is the only object of this application anything outside it holds. The controller assembles nothing: it is built, it is called once, and it is dropped.

The class diagram

/ marks what is derived and held by nobody. What the operations promise is the chapter on the contracts; what they are called is a proposal, and one pass renames them all.

ClassAttributesOperations
Formatextension, software, propertiesholds(property), can_be_written()
Optionoption, question, answers, default answer, altersanswers_allowed_by(format), default_for(settings)
Conversiondestination, override, heuristic, answers, /what is missinganswer_of(option), what_is_missing(batch), invalidates()
FileResultfile, status, cause, remarks—
ConvertRecordtask, conversion, to_be_shownserialize(), parse(data)
ConvertModel—writable_formats(), options_of(destination), structural_loss_of(destination), convert(batch, conversion)
ModelFormats—writable_formats(), properties_of(format), options_of(format), structural_loss_of(format)
ModelRemediation—remediate(content, properties, answers), what_was_transformed()
ModelConversion—convert(batch, destination, answers, general_options), convert_one(file, …)
ModelProvision—batch(), reached_by(file)
ConvertControllerrecordhandle(request_data), populate_view()
ViewConvert—populate_tree_content(record)
ConvertResponseRecipe—bake(), _process_events()

Conversion.what_is_missing() is given the batch and does not hold it: what is missing is a destination or a batch which is not empty [031], and the batch belongs to a neighbour. A class which held it would hold what it is forbidden to alter.

ModelRemediation is given a content and gives back a content. It never sees a file, a path or a batch, which is what lets the business logic of this application be tested with nothing installed.

What is tied to what

TieLegsWhat it is
holdsConvertResponseRecipe 1 — 1 ViewConvert, 1 ConvertControllerA composition: it builds them, and they go when it goes
holdsConvertController 1 — 1 ConvertRecordA composition: one record per request, made by the controller and kept by nobody
holdsConvertRecord 1 — 1 ConversionA composition: what was decided lives inside what one request carries
holdsConvertModel 1 — 1 each of the four sub-modelsA composition: the façade makes them, and nothing else reaches them
holdsViewConvert 1 — 1 each of the five fragmentsA composition: a fragment builds one part of one page and exists for it
usesConvertController → ConvertModelA dependency: it calls the façade, once per request
usesConvertController → ViewConvertA dependency: it hands it what to build from
usesViewConvert → ConvertRecordA dependency: it is given it and builds from it
usesModelFormats → Format, OptionA dependency: it makes them from the API and from the correspondence, and keeps none
usesModelConversion → ModelRemediation, FileResultA dependency: it asks for a content and gives back a result
usesModelConversion → the API of SPPASA dependency, and the only tie of this application to what reads and writes files
usesModelProvision → the provision of filesA dependency, and the only tie of this application to that domain
noneModelRemediationTied to no file and to no neighbour: a content in, a content out

In yUML

To be read at yuml.me, class diagram.

[ConvertResponseRecipe|bake();_process_events()]++1-1>[ConvertController|record|handle();populate_view()]
[ConvertResponseRecipe]++1-1>[ViewConvert|populate_tree_content()]
[ConvertController]++1-1>[ConvertRecord|task;conversion;to_be_shown|serialize();parse()]
[ConvertRecord]++1-1>[Conversion|destination;override;heuristic;answers;/missing|answer_of();what_is_missing();invalidates()]
[ConvertController]-.->[ConvertModel]
[ConvertController]-.->[ViewConvert]
[ViewConvert]-.->[ConvertRecord]

[ViewConvert]++1-1>[FragmentFormats]
[ViewConvert]++1-1>[FragmentDestination]
[ViewConvert]++1-1>[FragmentCompare]
[ViewConvert]++1-1>[FragmentGeneral]
[ViewConvert]++1-1>[FragmentResults]

[ConvertModel|writable_formats();options_of();structural_loss_of();convert()]++1-1>[ModelFormats|writable_formats();properties_of();options_of();structural_loss_of()]
[ConvertModel]++1-1>[ModelRemediation|remediate();what_was_transformed()]
[ConvertModel]++1-1>[ModelConversion|convert();convert_one()]
[ConvertModel]++1-1>[ModelProvision|batch();reached_by()]

[ModelFormats]-.->[Format|extension;software;properties|holds();can_be_written()]
[ModelFormats]-.->[Option|option;question;answers;default;alters|answers_allowed_by();default_for()]
[ModelConversion]-.->[ModelRemediation]
[ModelConversion]-.->[FileResult|file;status;cause;remarks]

The states

Two things have states in this application, and they are the two the user reads.

A property, for one destination
StateWhenWhat is shown
heldThe destination supports itThe line, and no decision on it
not held, nothing to decideIt is not supported and no option covers itThe line, and it is a structural loss
not held, a decision to takeIt is not supported and an option covers itThe line, and the answers that destination allows

Three states and no more. The list of properties does not move from one format to the next: what moves is the state of each line, and that is what makes it readable.

A file of the batch, once the conversion has run
StateWhen
writtenIt was read, adapted where an answer asked for it, and written
written with remarksThe same, and a remediation transformed its content
refusedWhat it held exceeds what the destination can hold, after every remediation its answers allow
skippedIts output already exists and override forbids replacing it

A file has exactly one of the four. What is skipped was never read, and what is refused was: the two are told apart because the user can act on one of them and not on the other.

The sequence, from the first request to the results

  1. The page is asked for. The formats which can be written are read, and no destination is defined [012].
  2. The batch is read from the provision of files. It is empty, and what is missing is said [203].
  3. The user chooses a destination. The request carries it; the controller rebuilds a conversion which holds nothing else.
  4. The options that destination raises are derived, their answers restricted to what it allows, and what it cannot hold at all is announced once [501].
  5. She answers an option, or leaves it to its default; she sets a general option. Each is one task of its own — answer, general — one request, and each carries back everything decided so far.
  6. She asks for the conversion. The preconditions are read on what was transmitted: a destination, a batch which is not empty, an answer for every option.
  7. File by file: read, anticipate, write, and at most one second write. Each file has its result, and the failure of one ends nothing.
  8. The results replace what was shown: a status for each file, its remarks, and the cause of each refusal, in the words of her data.
  9. What was produced is reached, without her being told where it stands.
  10. She starts again: the decisions are gone and the batch is not.

Steps 3 to 6 happen in any order, and any of them may happen twice. There is no step which has to come before another, and the only thing which requires them all is step 7.

The error policy

LevelWhat happensWhat is done
1A decision is missing: no destination, an empty batchNo treatment runs, and which decision is missing is said [022]. It is not an error and nothing is raised
2One file is concerned: what it holds exceeds the destination, or its output exists and may not be replacedThat file is refused or skipped, the others are converted, and the cause is stated in terms of her data and not of the internals of a format [704]
3Whoever runs the machine has to know: a file which cannot be read at all, a folder which cannot be writtenSaid where he reads it, and the file carries a refusal like any other so that the batch goes on

What is raised is caught by the response which answers the request, and by nothing below it. A typed cause of the API is not an error of this application: it is an answer, and it is what the second write is decided on.

The contracts

How to read them

pre is what has to be true on the way in, and what the operation does not check; post is what the caller can count on without looking. The treatments state their preconditions for one treatment; these state them for one operation, and the two say the same thing at two levels.

What holds everywhere

  • What is absent gives back an empty thing — an empty list, no destination, no remark — and never nothing at all.
  • Nothing is held between two requests. Every operation is given what it works on and keeps none of it. C21
  • Only ModelConversion reads or writes a file of the user. No other class of this application touches a disk.
  • Only ModelFormats knows what a format holds, and it holds the correspondence as data. C4
  • A view decides nothing: what it shows was decided before it was called.
  • What is raised is caught by the response which answers the request, and no class below it writes a catch. A typed cause of the API is not raised to the caller: it is an answer.

Class by class

ConvertModel
OperationContract
Give the formats which can be writtenpre: none. post: every format the system is able to write, with what each of them holds, and never one it cannot write. It reads no file of the user. C2
Give the options of a destinationpre: the destination is a format which can be written. post: one option per unsupported property an option covers, with the answers that destination allows and the answer which holds if none is given. No file is read. C5, C7, C8
Give the structural loss of a destinationpre: the same. post: the unsupported properties no option covers; it depends on the destination alone, and is the same for every file. C6
Convertpre: a destination, a batch which is not empty, and one answer standing for every option. post: one result per file of the batch, each holding a status, the remarks attached to it and the cause of a refusal; the batch is what it was. C11, C12
ModelFormats
OperationContract
Give the writable formatspre: none. post: what the API answers, relayed; no list is written here
Give the properties of a formatpre: a format. post: what it holds, as the API says it. It belongs to the format and never to a file
Give the options of a formatpre: a format. post: read from the file which holds the correspondence, restricted to the answers that format allows. Adding a property to the API adds a row and changes nothing here. C4, C5
Give the structural loss of a formatpre: a format. post: the unsupported properties whose option is empty. C6

It reads no file of the user and no content whatever. Every rule of what a destination raises is therefore verified with nothing installed but the API.

ModelRemediation
OperationContract
Remediate a contentpre: a content, the properties of the destination, and one answer standing for every option. post: a content the destination holds, or the statement that it does not — every remediation the answers allow having been applied at most once, and the content verified again after each of them. C14, C15
Say what was transformedpre: a content was remediated. post: the remediations which changed it, which is what a remark is made of; empty when none did

It is given a content and gives back a content: no file, no path, no batch. It is where the business logic of this application lies, and it is what can be tested with nothing at all.

ModelConversion
OperationContract
Convert a batchpre: the preconditions of the conversion are verified. post: one result per file, in the order of the batch; the failure of one is a result and never an interruption; nothing of the batch is altered. C11, C13
Convert one filepre: a file of the batch. post: written, written with remarks, refused with a cause, or skipped — one of the four and no other; at most two writes were attempted; the cause is stated in terms of the user's data. C16, C17, C18

It is the only class which meets a typed cause of the API, and the only one to be read again the day that API raises another. What a failed write left on the disk is not removed by it. C23

ModelProvision
OperationContract
Give the batchpre: none. post: the set of files that domain exposes, possibly empty; read at every request and altered by nothing here. C11
Say by what a file is reachedpre: the file was produced by the conversion. post: the way it is reached, as that domain gives it — never a path, and never a place of the machine. C22

It is the only class which speaks to that domain, so it is the only one to be read again the day it changes. It is also where C22 stands: nothing here is ever given a path, so no code of this application can show one.

Conversion, ConvertRecord
OperationContract
Give the answer of an optionpre: none. post: the answer which was chosen, or the default of that option; never nothing. C8
Say what is missingpre: a batch is given to it. post: a destination when none is defined, the batch when it is empty, nothing when the conversion may run. It holds no batch: what it is forbidden to alter, it does not keep. C12
Say what a change invalidatespre: none. post: a change of destination invalidates the properties, the options, the answers, the structural loss and the results; a change of an answer, of a general option or of the batch invalidates the results. C3
Serialise a recordpre: none. post: everything a next request needs in order to rebuild this conversion, and nothing of the machine
Parse a recordpre: what a page transmitted. post: the conversion as it was, or an empty conversion when nothing was transmitted. Parsed twice, it gives the same conversion. C21
ConvertController
OperationContract
Handle a requestpre: called once for that request, and never twice. post: the record is rebuilt from what was transmitted, the task is identified, its preconditions are read; the treatment ran, or no treatment ran and the missing decision is stated. Nothing derived from a decision which changed is reused. C3, RO4
Populate the viewpre: the request was handled. post: the view has the record; nothing is decided here and nothing is read back but a tree

It assembles nothing: it is built by the response, called once, and dropped. A decision is never refused because another is missing.

ViewConvert and its fragments
OperationContract
Populate the treepre: a record. post: a tree holding what that record says and nothing more — no refusal it invented, no total it counted. It reaches no disk and no neighbour
FragmentFormatspost: the formats which can be written, grouped so that one is found, and carrying no property
FragmentDestinationpost: every property, always, in the same order, whatever the destination; what varies is the state of each line, which is one of three
FragmentComparepost: the matrix of formats by properties, unfolded in place; it decides nothing and the page stays where it is
FragmentGeneralpost: the two general options, false when nothing was transmitted. C10
FragmentResultspost: one entry per file of the batch, with its status, its remarks and its cause; it exists only once a conversion has run, and it is not one line per file produced. C19
ConvertResponseRecipe
OperationContract
Bakepre: a request, carrying a task or carrying none. post: the tree of the page, or the payload of an update. The view and the controller were built, the controller was called once, and neither of them outlives the request

Where each constraint is held

One place each. A constraint held in two would be a constraint held in neither.

No.Held by
C1Conversion, which holds one destination or none
C2ModelFormats, which is what says a format can be written; the façade reads it before holding a destination
C3Conversion, which says what a change invalidates, and ConvertController, which rebuilds only what was transmitted
C4ModelFormats, reading the file which holds the correspondence
C5ModelFormats, giving the options of a format
C6ModelFormats, giving the structural loss of a format
C7ModelFormats, which reads no file: the options exist before anything is read
C8Conversion, giving the answer of an option
C9ModelConversion, which is given one set of answers for the whole batch and reads no other
C10Conversion, whose general options are false when nothing was transmitted
C11ModelProvision, which is what reads the batch, and ModelConversion, which writes nothing into it
C12Conversion, saying what is missing, read by ConvertController before it runs anything
C13ModelConversion, converting a batch
C14ModelRemediation, which verifies again after each remediation
C15ModelRemediation, which applies each remediation at most once for one file
C16ModelConversion, converting one file
C17ModelConversion, converting one file
C18ModelConversion, which names the cause; the view shows it as it was given and writes none of its own
C19FragmentResults
C20Conversion, which is given the setting as a default and never writes it
C21ConvertRecord, and ConvertController, which keeps nothing after the request
C22ModelProvision, which is given a way to reach a file and never a path. It is held by what that operation is not given: nothing else of this application ever sees one, so there is nowhere for it to be broken
C23ModelConversion, which removes nothing

C22 is held by what one operation is not given, which is the strongest way to hold a rule: no code can break it, because no code is handed what it would take to break it. Every one of the twenty-three names exactly one place, C11 excepted, which names two — and they are two halves of one thing: one class reads the batch, another writes nothing into it, and neither can do the other's half.

What is tested, and where

The models

Twenty-five tests for the four sub-models and the façade. The controller, the view and its fragments are not covered here and are owed.

ModelRemediation, with nothing installed

It is given a content and gives back a content: no file, no path, no batch, and no API to read a format with. It is the business logic of this application, and it is the cheapest thing here to test.

No.What is checkedWhat it holds
TE1A content the destination already holds comes back as it was, and nothing was transformedThe contract of remediating
TE2An answer which asks for a transformation transforms the content, and what was transformed is named — which is what a remark is made ofT06, D12
TE3The content is verified against the destination again after each remediationC14
TE4A remediation is applied at most once for one file: a chain which would ask for it twice ends insteadC15
TE5A chain which cannot reach a content the destination holds ends in a refusal, and the cause is the one which ended itT06
TE6Nothing appears on the disk, whatever it is givenIts own promise, and what makes it testable

TE4 is the one which would have been forgotten. Nothing in the conceptual model asks for it: it was the operational level which found that a chain needs a reason to stop, and this is that reason checked.

ModelFormats, with the API and no file

No.What is checkedWhat it holds
TE7The formats offered are those the API says can be written, and one which cannot is not among themC2
TE8The properties of a format are what the API answers, relayed and not decided hereD2
TE9The options are read from the file which holds the correspondence: a row added there becomes a question asked, and no code is touchedC4
TE10The answers offered are restricted to those the destination allowsC5
TE11An unsupported property no option covers is a structural loss, and it is the same whatever the files areC6
TE12The preservation option appears once, covering the three properties it covers, and not three timesR2
TE13No file of the user is read: every answer is given before anything is openedC7

TE9 is C4 made checkable. A row is added to the correspondence and an option appears; nothing else in the test changes. The day that assertion has to be rewritten, the correspondence has become code again.

ModelConversion, with a disk

No.What is checkedWhat it holds
TE14One result per file of the batch, in the order of the batchT06
TE15The failure of one file never interrupts the others: the files after it have their resultsC13
TE16The batch is what it was: not one file added, removed or rewrittenC11
TE17A file whose output exists is skipped; with override it is writtenC17
TE18A write which fails on a typed cause is attempted once more, and never a third timeC16
TE19A refused file carries a cause, and that cause names what was in her file and not what is in the formatC18
TE20What a failed write left on the disk is still there afterwardsC23
TE21One file of the batch may produce several converted files, and it has one result all the sameC19

TE20 asserts that something is left behind. It is written that way on purpose: C23 is a renouncement, and a renouncement which is not tested becomes, six months later, a bug somebody fixes.

ModelProvision, against the neighbour

No.What is checkedWhat it holds
TE22The batch is what that domain exposes, empty when it exposes nothing, and nothing is written into itC11
TE23What is given for a produced file is a way to reach it, and never a path of the machineC22

ConvertModel, the façade

No.What is checkedWhat it holds
TE24It converts only with a destination, a batch which is not empty, and an answer standing for every optionC12
TE25The options and the structural loss are given with no file read, and the same answer is given whatever the batch holdsC7, C6

What no test covers, and where they stand

  • The controller, the view and its fragments. They are owed, and they are what will check that a decision which changed invalidates what was derived from it, that a record parsed twice gives the same conversion, and that a view shows what its record says and nothing more.
  • What the API does with the annotations it writes. It is established, it has its own tests or it has not, and this application is answerable for what it asked of it.
  • The mockup and the wording. Of the interface, decided against the markup and not against an assertion.

Thirteen of the twenty-five need nothing but the API, six need a disk, two need the neighbouring domain. The cutting into sub-models is what makes that count possible, and the count is what says whether the cutting was worth anything.

They stand beside the code, in the folder of this application.

Annexes

Annex: Extensions of the SPPAS API

The model of Convert stated three requirements the SPPAS API did not cover. All three are available: they were added to sppas.src.anndata, and this annex now states what they are, what they hold, and what they cost. Nothing of the model is left waiting for the API.

E1 — accept_comments [API-OK]

What A boolean property of formats, stating whether a format is able to hold comments.
Why The API already writes metadata as comments where the format allows it, and forgets them otherwise. That capability is internal and not exposed. Convert cannot know whether preserving is possible, and therefore whether the option is worth offering.
Bound to The preservation option — section 3.2. Requirement [307].
Available as comments_support() of any reader-writer. The formats holding comments are txt, ctm, stm, arff and tdf; every other one answers False.

E2 — accept_empty_tier [API-OK]

What A boolean property of formats, stating whether a format is able to hold a tier carrying no annotation.
Why The API raises AioEmptyTierError (1525) when a format refuses an empty tier, but nothing states beforehand which formats do. The refusal is knowable only by attempting to write.
Bound to Anticipation and chaining — sections 3.3, 4.3 (T06). Requirements [602], [603].
Available as empty_tier_support() of any reader-writer. The formats holding an empty tier are the ones declaring their tiers apart from their annotations: xra, eaf, antx, ant, tei, mrk, and TextGrid — which writes such a tier as one interval with no text, adding an empty annotation rather than removing the tier.
Note Not to be confused with no_tiers, which states whether a format accepts a file holding no tier at all. Removing empty tiers can produce such a file: the two properties interact, which is why chaining re-verifies.

E3 — Writing metadata, controlled vocabularies and media as a tier [API-OK]

What The ability to write, into a tier of the output, what a format cannot hold as metadata, as a controlled vocabulary, or as a media declaration.
Why Where a destination holds neither the information nor comments, that information is currently lost. A tier is the one container every format holds by definition.
Bound to The preservation option — section 3.2.
Available as create_unsupported_tier() and parse_unsupported_tier(), with unsupported_entries() and fill_unsupported_entry() underneath — a reader holding the information its own way, as a comment for instance, fills the objects with the latter. Written by TextGrid, csv and mrk: the formats holding several tiers but neither metadata nor comments.
What it holds A tier named DoNotEdit, whose first annotation holds the keyword Metadata, then one annotation per key: the key, the value, the nature — metadata, ctrl_vocab or media — and the name of the tier it belongs to, one in each of its labels. The whole time span of the transcription is shared into intervals of equal duration. Reading it assigns the entries back to their object and removes the tier: it is a way to write, not data.
Decided by The settings of the application give the default: interoperability of sppasAppConfig, true. Each reader-writer takes it when created and holds it as its own, so a caller writing one file decides for that file only, with set_preserve_unsupported() — of the format, or of sppasTrsRW, which passes it on. The settings are left untouched. The tier is written only when there is something to preserve: what SPPAS assigns to any object it creates does not count.
What it costs csv and mrk write the labels of an annotation separated by a whitespace, so the whitespaces of the entries are turned into underscores and stay so when read back: an assumed loss, stated in the docstring of both. The descriptions of a controlled vocabulary and of its tags are not preserved.

What the current API already provides

For contrast, and to bound the extensions above, the following are available and require nothing:

  • the list of formats and, for each, whether it can be read and written;
  • the boolean properties of formats, which Convert derives its options from;
  • typed and numbered exceptions on writing — AioMultiTiersError (1510), AioNoTiersError (1515), AioEmptyTierError (1525), AioLocationTypeError (1530) — usable as causes of a refusal as they stand;
  • overriding an existing output, and detecting the format of an input whose extension is unknown.

Annex: Mockup of the interface

This annex is not a picture of the page. It is the page — the fragment below is HTML, styled by Whakerexa, rendered here by the same stylesheets that will render the application. What is unreadable here will be unreadable there.

It shows one state: the destination TextGrid selected, on a batch of four files, after a conversion has run. Interactions are inert; the structure, the labels and the wording are not.

Convert

Write into

Praat
Elan
SPPAS
Subtitles
Other

… and the remaining writable formats.

Compare the formats

The full matrix of formats by properties. It is here because comparing formats is a legitimate thing to want, and a poor way to choose a destination. Unfolded in place: the page stays where it is.

Formats and the properties each holds (excerpt)
Format Several tiers Metadata Hierarchy Overlaps Ctrl. vocab.
xra yesyesyesyesyes
TextGrid yesnononono
eaf yesyesyesnoyes
srt nonononono

… and the remaining formats and properties.

Options

Replace an output file that already exists

Guess the format of a file whose extension is unknown

Results

One line per file written, refused or skipped — not one per file of the batch
File Status Remarks
interview-01.xra written —
interview-02.xra written, with remarks An empty tier was removed: Comments.
gestures.eaf refused The file holds a tier of geometric labels, and you chose to skip such tiers. Nothing was left to write.
notes.TextGrid skipped The output file already exists.

What the mockup settles, and what it does not

The labels of the aside do not move. Selecting another format changes what is said of each property, never the property said. Clicking from TextGrid to eaf and back shows what differs without anything being read twice.

The properties are a list, not a table. There is one format here: a table would assert a second dimension that does not exist, and would weigh on the page as though every line had to be analysed rather than read. The full matrix, which does have two dimensions, remains a table — in the details element, where it belongs.

Every property is on the same footing. This is a scientific application: it states its data. Some properties exist almost nowhere but in SPPAS, and a user who wants to know what vagueness is has the right to find it — in the same list, in the same order, as the ones everybody knows.

Two things are shown here and are not settled: the wording of the properties in plain language, and the wording of the causes of refusal. They are interface questions, and they are decided against this markup rather than in the abstract.

A third was open when this markup was drawn and is now settled elsewhere: the results replace what was shown, because a conversion is one request and its results are what that request answers. The organisational level says it and the sequence repeats it; the mockup is read with that settled.

Annex: Legal notices

  • Author: Brigitte Bigi
  • Document License: GNU Free Documentation License (GFDL) 1.3
  • Copyright (C) 2026 Brigitte Bigi, CNRS
  • Creation Date: 2026-07-16
  • Last update: 2026-09-17