Plugins - an app of SPPAS

Brigitte Bigi

User manual

Not written yet. This chapter is the help of the person who uses plugins: what the page shows, how a plugin is installed, set and started, and what is read of a run.

Developer manual

Not written yet. This chapter is for whoever reads or changes the code: how it is organised, what the page is made of, the events it answers, and the stylesheets.

Modelling

Problem and scope

Description

SPPAS does what it was written to do, and a plugin is how it does something else. A plugin is a program somebody wrote outside SPPAS, declared by a file which says what it is called, what it is for, what it can be set with, and which command line runs it. SPPAS installs it, shows it, sets it, runs it on files, and puts what it produced back among the files of the person. It never looks inside it.

That is what makes plugins worth having, and it is also what makes them delicate. A plugin extends SPPAS without SPPAS being touched: a laboratory writes the tool its own work needs, and hands it over as an archive. But what is handed over is a program, it runs on the machine of whoever installs it, and nothing of SPPAS reads what it does before it does it.

This application is what stands between a person and those programs. It shows what is installed and what each one claims to do, it installs and removes them, it lets their options be set, it runs one of them on the files she chose, and it says what happened to each of those files. It runs nothing of its own, and it decides nothing of what a plugin does.

Two services answer for what this application does not: the files it runs a plugin on are provided by the provision of files, and the options of a plugin are shown and set by the service which serves options. What is left is the plugins themselves — which ones are there, what they are, and what one run of one of them did.

Defining the needs

Problem statement and objectives

Running a plugin raises a difficulty which is not technical: the person is asked to run a program she did not write, on files which are hers, and to judge beforehand whether that is a good idea. She is given a name, a sentence of description and a version number, and the thing itself is a command line she never sees. What it will read, what it will write and where, and what it will leave behind are not written anywhere she looks.

The central problem addressed by this application is thus:

how to let a person use a tool written outside SPPAS as if it were part of it — knowing what she is installing, what she is running, on what, and what came of it.

The difficulty is sharpened by three conditions. A plugin is opaque by design: what it declares of itself is all there is, and this application cannot add to it. A run is long and mute: one command is started for every file, and nothing is known of its progress but the file it is on. And what a plugin produces is not announced: the files which appear after a run are found by looking at what was not there before.

The objectives follow:

  • to show what is installed, and what each plugin says of itself, before anything is run;
  • to let a plugin be installed from an archive and removed, and to say what stands in the way when either cannot be done;
  • to let the options of a plugin be set before a run, and to keep them from one run to the next;
  • to run one plugin on the files the person chose, and on no others;
  • to say what happened, file by file, and not as one block of text;
  • to make what was produced reachable among her own files;
  • to guarantee accessibility, of the interface and of what it exposes.

This application does not judge a plugin, does not read what it does, and does not repair what it produced. It aims at one thing: that running a program written elsewhere be a decision taken in the open rather than a button pressed in the dark.

Expressing the needs

The needs below are those of the person who uses plugins: a researcher, a student, someone who was handed an archive by a colleague and told that it does what she needs. None of them is a need of a machine, and none of them says how it is answered.

Knowing what a plugin is before anything else

A plugin arrives with a name, a description and a version, written by whoever made it, and that is everything there is. The need is that those three be shown where the decision is taken — which plugin to install, which one to run — and not hidden one click away. A description which has to be looked for is a description which is not read, and a person who did not read it runs a program she knows nothing about.

Installing, and being able to undo it

A plugin is handed over as an archive and has to be brought in. The need is that bringing it in be one gesture, and that it be reversible: a plugin which turns out to be the wrong one, or which was installed twice, has to be removable without the person having to know where it was put. What stands in the way — an archive which is not one, a plugin already there — has to be said in those words, and not as the failure of an operation she cannot name.

This need is hers where SPPAS runs on her machine, and nowhere else. Where SPPAS answers from a server, installing a plugin would be installing a program on a machine which is not hers and on which she is not alone, and removing one would be removing it for everybody. What is offered there is what whoever runs that server installed. Every other need below holds in both cases; this one is the exception, and it is the only one.

Choosing what it runs on

A plugin is run on files, and those files are the person's. The need is that she says which ones, that the plugin gets those and no others, and that she can see what she is about to submit before she submits it. A run on the wrong files is not an error which can be undone: the plugin has already written.

Setting a plugin before running it

A plugin declares what it can be set with, and the same plugin run with two settings does two different things. The need is that those settings be available where the plugin is run, that they be intelligible without the documentation of the plugin, and that what was set once not have to be set again at the next run.

Knowing what happened, file by file

A run is a series of runs, one per file, and they do not all go the same way. What a person needs after a run is not the output of a program: it is to know, for each of her files, whether something was done, whether something was produced, and whether something went wrong. A single block of text holding the output of twenty runs one after the other is a text nobody reads, and it is where a failure on the third file goes unnoticed.

Following a run which lasts

A plugin on twenty files takes as long as twenty runs of that plugin. The need is to know that it is working and where it stands — which file, how many are left — and to be able to tell a long run from one which is stuck.

Finding what was produced

A plugin writes files, and it writes them where it chooses. The need is that what appeared be found among her own files, ready for what she does next, without her having to look for it and without her having to know where the plugin put it.

Accessibility

Everything above rests on things being read: a description, a state, a result, a cause. Contrast, legibility, text alternatives, keyboard and screen-reader operation, and a stable terminology are the conditions under which this application does anything at all, and not qualities added to it.

Eliciting the needs

Eliciting the needs means turning them into things this application names and shows. What follows is what the person is put in front of, and what each of those things is called here.

Concepts manipulated by this application
Plugin
A program declared by what it says of itself: an identity, a name, a description, a version, an icon, the options it accepts, and the command which runs it. What it does is not declared and is not known here.
Archive
What a plugin is handed over as, and the only way one comes in.
Run
One plugin, the files it was given, and the options as they stood when it started. A run is a series: the plugin is started once per file.
Result
What happened to one file of a run: whether the plugin ran on it, what it said about it, and whether it failed.
Produced file
A file which appeared because a run happened. It belongs to the person, not to the plugin which wrote it.

Two things this application manipulates are named elsewhere and not here: the files a run works on, which the provision of files exposes, and the options of a plugin, which the service of the options shows and sets.

One card per plugin, and what it carries

The need to know a plugin before anything else is expressed by a card per installed plugin, carrying its icon, what it says of itself in one sentence, and what starts it. What has to be read before running is on the card and not one click away: a description which has to be opened is a description which is not read.

What is consulted once stands behind a button which says so: the version, and what the plugin says of itself at length. It is not hidden, it is elsewhere, and the difference is that nobody needs it twice.

The card is not chosen for itself. It is the shape swapp already uses where it shows things one chooses among, and a person who knows one page of SPPAS knows this one. Installing and removing are not on a card — they are not what one does to a plugin, they are what one does to the list — and they stand in the menu.

A run, and what is seen of it

The need to follow a run which lasts is expressed by what a run shows of itself while it happens: which file is being treated, and how many remain. A run which shows nothing cannot be told from a run which is stuck, and a person who cannot tell the two apart stops the first.

The need to know what happened is expressed by one result per file, and never by the output of the plugin as it came. What a plugin said about one file stands beside that file, and a file which made the plugin fail is a line among the others, not the end of the list.

What is said when something cannot be done

An archive which is not one, a plugin which is already installed, a plugin which cannot be removed: each of them is said in the words of what the person was trying to do, and not as the failure of an operation she cannot name. What a plugin says when it fails on a file is given as it came, because it is the plugin speaking and not this application.

A plugin which cannot run here (inferred need)

No person has asked for this, and it does not come from an expressed need: it comes from what a plugin declares. The command which runs it is declared once per system — one for Windows, one for macOS, one for Linux — and what that command names has to exist on the machine where SPPAS runs. A plugin can therefore be installed, listed, described, set, and be unable to run.

It is expressed here as a state of a plugin, and not as an error of a run: a plugin which cannot run on this machine says so where it is listed, and before anybody tries. What is verified, and how far, is not decided by this chapter.

Organising the needs

The needs above, written as what the person can do and what the system does in answer. Nothing here is new: every line follows from a paragraph of the two sections before it.

[010] Seeing what is installed
  • [011] The person sees every plugin which is installed, and no other.
  • [012] The system shows, for every plugin, its name, what it says of itself, and its version.
  • [013] The system shows of a plugin what that plugin declares, and writes nothing of its own.
[020] Installing and removing a plugin
  • [021] The person can install a plugin from an archive, where SPPAS runs on her machine.
  • [022] The person can remove a plugin which is installed, where SPPAS runs on her machine.
  • [023] The system says what stands in the way when a plugin cannot be installed, in the words of what was attempted.
  • [024] The system removes a plugin without the person having to know where that plugin stood.
  • [025] Where SPPAS runs on a server, the system installs and removes nothing: what is offered there is what whoever runs that server installed.
[030] Knowing that a plugin can run here
  • [031] The person sees, where a plugin is listed, that this plugin cannot be run on this machine.
  • [032] The system says it before a run is started, and not as the failure of one.
[100] Choosing what a run works on
  • [101] The person chooses the files a plugin is to run on.
  • [102] The system runs the plugin on those files, and on no others.
  • [103] The person sees how many files a run will work on before starting it.
[200] Setting a plugin
  • [201] The person can set the options a plugin declares, before running it.
  • [202] The system runs the plugin with the values which were set.
  • [203] The person finds the values of the previous run, without setting them again.
[300] Running a plugin
  • [301] The person starts a run, and a run starts only when she does.
  • [302] The system starts the plugin once per file of the run.
  • [303] The person sees which file is being treated and how many remain.
  • [304] The failure of one file never stops a run.
[400] Knowing what happened
  • [401] The person sees what happened to every file of a run.
  • [402] The system gives one result per file: whether the plugin ran on it, what was produced, and what went wrong.
  • [403] The system shows what the plugin said about a file beside that file, and as the plugin said it.
[500] Finding what was produced
  • [501] The person finds what a run produced among her own files.
  • [502] The system makes those files known without the person having to know where the plugin wrote them.
[1000] Accessibility
  • [1001] The system holds to the WCAG rules.

Conceptual level

Data dictionary

Three data, and they all belong to one run. What a plugin is, and which ones are installed, is held by the domain of the plugins and read from there.

  • D1. A run — one plugin, the files it was given, and the moment it was started.
  • D2. A result — what happened to one file of a run: whether the plugin ran on it, what the plugin said about it, and whether it failed.
  • D3. A produced file — a file which appeared because a run happened. There may be several for one file, and there may be none.

sppasPluginParam and sppasPluginsManager are not among them. What a plugin declares of itself, where it stands, how it is installed and how it is started are established, read where they are written, and this document adds nothing to them.

Conceptual model of communication

Four actors border this application: a person, and three neighbouring domains it calls. It writes into none of the three.

Actors

  • A1. The person — sees what is installed, installs and removes, sets a plugin, starts a run, follows it, and reads what happened to each of her files.
  • A2. The domain of the plugins — holds what is installed, says what each plugin declares of itself, installs one from an archive, removes one, and starts one on a file.
  • A3. The provision of files — gives the files a run works on, and makes what was produced reachable among the files of the person.
  • A4. The service of the options — shows the options a plugin declares and gives back what was set on them.

Flows

  • F1. A2 → app — the plugins which are installed, and what each of them declares of itself [012].
  • F2. app → A1 — one line per plugin: its name, what it says of itself, its version [011], [012].
  • F3. app → A1 — that a plugin cannot be run on this machine [031].
  • F4. A1 → app — an archive to install [021].
  • F5. app → A2 — the archive, to be installed.
  • F6. A2 → app — the plugin which was installed, or what stood in the way.
  • F7. A1 → app — the plugin to remove [022].
  • F8. app → A2 — the plugin to remove, and what came of it.
  • F9. app → A4 — the options a plugin declares, to be set.
  • F10. A4 → app — those options, with the values which were set [202].
  • F11. A3 → app — the files the run is to work on [101].
  • F12. app → A1 — how many files the run will work on [103].
  • F13. A1 → app — start the run [301].
  • F14. app → A2 — a plugin to start, on one file, with the values which were set [302].
  • F15. A2 → app — what the plugin said about that file, and whether it failed.
  • F16. app → A1 — which file is being treated, and how many remain [303].
  • F17. app → A1 — one result per file: what was done, what was produced, what went wrong [401], [402], [403].
  • F18. app → A3 — the files which were produced, to be made known [501].

Nothing goes from this application to a plugin but a file and the values which were set. What a plugin does with them is its own, and what it says comes back as it said it: this application relays and does not interpret.

F11 and F18 are the same neighbour at the two ends of a run. The files a plugin works on and the files it produced are both the person's, and both are known by the domain which knows her files. This application holds neither.

Conceptual model of data

Four classes. The plugin is one of them without being modelled: it belongs to the domain of the plugins, and it appears here with its identifier and no property.

The classes of entities

ClassPropertiesWhat it is
PLUGIN # plugin One plugin which is installed. What it is called, what it declares and how it is started are read where they are written
RUN # run, moment, values set One plugin started on a set of files at one moment, with the values the options held then (D1)
RESULT # run, # file, done, what was said What happened to one file of one run (D2). A file which the plugin could not be started on has a result too
PRODUCED # run, # file, # produced A file which appeared because that file was treated (D3)

The relations

No.VerbLeg 1Leg 2
R1startsRUN (1,1)PLUGIN (0,n)
R2reports onRESULT (1,1)RUN (1,n)
R3came ofPRODUCED (1,1)RESULT (0,n)

R2 and R3 are aggregations: a result exists only inside the run it belongs to, and a produced file only inside the result it came of. A run which has no result has not started, and a result with no produced file is a file the plugin read and did not write for.

R1 reads (1,1) on the side of the run: a run is one plugin and never two. Running two plugins on one set of files is two runs, each with its own results, and there is no moment at which they are one thing.

The values set belong to the run and not to the plugin. They are what the options held when the run started: a plugin set differently an hour later does not change what a run did, and a result read a week afterwards is read beside the values it was obtained with.

Computed, and never held

  • How many files a run will work on, and how many remain, which are read from the files it was given.
  • That a plugin cannot be run on this machine, which is read from what that plugin declares.
  • Whether a run is over, which is one result per file of it.

What is not modelled, and why

  • What a plugin declares of itself, where it stands, and how it is started: the domain of the plugins', and read from there.
  • The files a run works on, which are the provision of files'. This application holds their identity for the time of the run, and nothing else.
  • The options of a plugin, which the service of the options shows and sets. What is held here is the values, and only as they stood when a run started.
  • What a plugin does, and what it means: nothing of it is knowable here.

Conceptual model of treatments

Seven treatments. Six answer a person who asked; the seventh answers the one before it.

The events

No.EventKindComes from
E1The person asks to see what is installedexternalThe person (A1)
E2The person gives an archive to installexternalF4
E3The person asks to remove a pluginexternalF7
E4The person asks to set a pluginexternalThe person (A1)
E5The person starts a runexternalF13
E6A file of the run has been treatedinternalT05
E7Every file of the run has been treatedinternalT05

T01 — Show what is installed

WhatSays
SynchronisationE1
ActionsRead what the domain of the plugins holds; read, of every plugin, whether it can be started on this machine
ResultOne line per plugin (F2). Always
ResultThat a plugin cannot be run here (F3). For every plugin whose command names what this machine does not have

What is shown of a plugin is what that plugin declares, and nothing else.

T02 — Install a plugin

WhatSays
SynchronisationE2
ActionsGive the archive to the domain of the plugins
ResultThe plugin stands among the installed ones (F2). When it was installed
ResultWhat stood in the way, said in the words of what was attempted (F2). When the archive is not one, or when that plugin is already there

This application verifies nothing of the archive itself: what an archive has to be is the other domain's to say, and it says it by refusing.

T03 — Remove a plugin

WhatSays
SynchronisationE3
ActionsAsk the domain of the plugins to remove it
ResultThe plugin is no longer among the installed ones (F2). When it was removed
ResultWhat stood in the way (F2). Otherwise

What a removed plugin produced is not removed with it: those files are the person's, and they were hers the moment they appeared.

T04 — Set a plugin

WhatSays
SynchronisationE4
ActionsHand the options that plugin declares to the service of the options (F9); take back what was set (F10)
ResultThe options of that plugin hold the values which were set. Always

Nothing is judged here: what a value may be is the service of the options' to answer, and what it means is the plugin's.

T05 — Run a plugin on the files

Purpose: that a plugin is started on every file the person gave, and that what came of each of them is known.

WhatSays
SynchronisationE5, and a plugin which can be run here and files which are not none
ActionsWrite the moment the run started and the values the options hold; then, for every file: start the plugin on that file, and take back what it said
ResultWhich file is being treated and how many remain (F16). For every file, before it is treated
ResultE6, a file has been treated. For every file, whatever came of it
ResultE7, every file has been treated. When the last one has

A run is a series and not an operation. The plugin is started once per file, and a file which makes it fail is a file with a result: the run goes on to the next one, and what failed is said where that file is said. There is no moment at which a run is undone.

The values are read once, when the run starts. A plugin set while a run is happening is set for the next one.

T06 — Say what came of one file

WhatSays
SynchronisationE6
ActionsMake the result of that file: whether the plugin ran on it, what it said about it, and which files appeared
ResultThe result of that file (F17). Always

What the plugin said is given as it said it. This application does not shorten it, does not translate it, and does not decide that it means success: what it can say of its own is whether the plugin ran at all.

T07 — Make what was produced reachable

WhatSays
SynchronisationE7
ActionsGive the files which appeared to the provision of files, to be made known among the files of the person
ResultThe files which were produced (F18). When at least one appeared
ResultNothing at all. When none did

It runs at the end of a run and not at every file: what a plugin writes while it is working is not finished until it has finished.

Organisational level

Organisational model of treatments

One procedure per treatment, numbered as it is. There are two tables, and the reason is [025]: where SPPAS answers from a server, two procedures have nothing to do at all.

Where SPPAS runs on the machine of the person

PFWhat it doesTriggered byIn chargeWhereWhenNature
PF1 (T01)Shows what is installedE1the appher machinewhen she asksconversational
PF2 (T02)Installs a pluginE2the domain of the pluginsher machinewhen she gives an archiveconversational
PF3 (T03)Removes a pluginE3the domain of the pluginsher machinewhen she asksconversational
PF4 (T04)Sets a pluginE4the service of the optionsher machinebefore a run, as often as she wantsconversational
PF5 (T05)Runs a plugin on the filesE5the domain of the pluginsher machinewhen she starts a runautomated
PF6 (T06)Says what came of one fileE6the appher machineas soon as a file has been treatedautomated
PF7 (T07)Makes what was produced reachableE7the provision of filesher machinewhen the run is overautomated

Where SPPAS answers from a server

PFWhat it doesTriggered byIn chargeWhereWhenNature
PF1 (T01)Shows what is installedE1the appthe serverwhen she asksconversational
PF2 (T02)Installs a pluginE2——nevernone
PF3 (T03)Removes a pluginE3——nevernone
PF4 (T04)Sets a pluginE4the service of the optionsthe serverbefore a run, as often as she wantsconversational
PF5 (T05)Runs a plugin on the filesE5the domain of the pluginsthe serverwhen she starts a runautomated
PF6 (T06)Says what came of one fileE6the appthe serveras soon as a file has been treatedautomated
PF7 (T07)Makes what was produced reachableE7the provision of filesthe serverwhen the run is overautomated

PF2 and PF3 have nothing to do, and the two empty rows are the whole difference. What is offered on a server is what whoever runs that server installed, and a person who reaches SPPAS from a browser is not that one. The five other procedures are the same word for word: a run is a run, and what it says of itself does not depend on the machine it happens on. RO1

Several persons are on a server at once, and each has her own run. What a person may start is one run, which is RO7; what the machine can carry at one time is another question and another answer, and it belongs to whoever runs that machine. The two are not the same limit and are not written as one. RO8

How a run which lasts is followed

A run is started once and lasts as long as the files require. On her machine, what follows it is the interface itself, which is there for the whole run. On a server, nothing is: the request which started the run cannot hold the answer for minutes, and nothing of what happens reaches a page which is not asking.

A run is therefore started by one request and followed by others which ask where it stands. It is what the dashboard of swapp already does to know whether the interface it watches is alive. The consequence is stated plainly: a run outlives the request which started it, and it is the one thing this application holds between two requests. RO2

What those later requests obtain is what F16 and F17 carry: the file being treated, how many remain, and the results of the files which are done. A run which is over answers that it is over, and answers it as long as the person is reading it.

Rules of organisation

  • RO1 — Installing and removing a plugin happen where SPPAS runs on the machine of the person, and nowhere else [025].
  • RO2 — A run outlives the request which started it. It is advanced by a thread of the process which runs SPPAS, and it is the one thing this application holds between two requests. It lasts as long as that process and no longer.
  • RO3 — What a plugin said is relayed as it was said. This application does not shorten it, does not translate it, and reads nothing into it.
  • RO4 — The files a run works on and the files it produced are the provision of files', at both ends. This application holds their identity for the time of the run.
  • RO5 — The options of a plugin are set before a run and read once, when that run starts. A plugin set during a run is set for the next one.
  • RO6 — The options are shown in a container this application places in its own page. Setting a plugin is not leaving the list of plugins.
  • RO7 — One run at a time for one person. A second run started while one is happening is refused, and the refusal says which run is still going.
  • RO8 — Where SPPAS runs on the machine of the person, one run at a time is one run on that machine. Where it answers from a server, several persons run at once, and what bounds the number of runs the machine takes at one time is the business of whoever runs it.

RO7 is not a limit of the machine: a plugin which writes and a plugin which reads the same files at the same moment would leave results nobody can read, and the person could not tell which run produced what.

Logical level

The tables

Each class of entities becomes a table, and its identifier becomes its key. There are five and not four: one property of the run holds several things, and a property which holds several things is not a column.

TableColumnsComes from
PLUGINpluginThe class PLUGIN
RUNrun, moment, pluginThe class RUN, and R1
VALUErun, option, valueThe values the run was started with
RESULTrun, file, done, what was saidThe class RESULT, and R2
PRODUCEDrun, file, producedThe class PRODUCED, and R3

VALUE is what the conceptual model wrote as one property of the run. A run is started with the values of every option the plugin declares, which is as many values as there are options: they become a table of their own, keyed by the run and by the option. Written as a column, they would be one string nobody can read back option by option.

R2 and R3 are aggregations: the aggregated table takes the key of the aggregating one. A result is named by the run it belongs to and the file it is about, and those two together identify it: one file of one run has one result. A produced file is named inside the result it came of.

R1 has its low cardinality on the side of the run, which is (1,1): the table takes the key of the plugin, and it is never empty. A run without a plugin is not a run.

PLUGIN is a key and nothing else. The table exists so that a run may name what it started; what a plugin is made of is written in the domain which holds it.

Computed, and never held

  • How many files of a run remain, which is what it was given against the results it holds.
  • That a run is over, which is one result for every file it was given.
  • That a plugin cannot be started on this machine, which is read from what that plugin declares.

Physical level

Where each table stands

Here the machines are named, and not before. One table stands in a neighbouring domain; the four others stand in the process which runs SPPAS, held by what advances the run.

TableWhere it standsWhile a run lastsWhat the person sees
PLUGINThe domain of the plugins, on the machine where SPPAS runsIts ownOne line per plugin
RUNThe process which runs SPPAS, made when the run startsIt is what later requests readWhich plugin is running, and since when
VALUEThe same, made with the runRead again by nobody until the run is overWhat the plugin was run with
RESULTThe same, added as each file is treatedIt is what growsOne line per file which is done
PRODUCEDThe same, added with the result it came ofThe sameWhat appeared, among her own files

A run stands in the process, and it is advanced by a thread of it. This is what the setting up of SPPAS already does with the installation of its features: the model starts a thread, hands it something which holds where it stands, and answers later requests by reading that. A run of a plugin is the same thing with another long task, and there is no reason for this application to invent a second way.

Nothing is written on a disk, nothing is given a lifetime, and nothing sweeps anything: a run belongs to the process which holds it and goes when that process goes. A server which is restarted has no runs, and the person is told that the one she was following is not there any more.

The thread is a daemon: it does not hold the application alive, and killing the application kills it. What a plugin had already produced for the files which were done stays where the plugin wrote it, and is the person's.

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.

PFThe tasks, in orderWhat cannot be half done
PF1Read what the domain of the plugins holds; read, of each plugin, whether its command names something this machine hasNothing: nothing is written
PF2Hand the archive to the domain of the plugins; read what came backThe installation itself, which that domain does or refuses
PF3Ask the domain of the plugins to remove; read what came backThe removal itself, which is that domain's
PF4Hand the options to the service of the options; take back the valuesNothing
PF5Write the run, its moment and its values; then, for every file: start the plugin, take back what it said, write the result and what appearedThe result of one file, with the files which came of it. Not the run
PF6Read the run and give what it holdsNothing: nothing is written
PF7Give the files which appeared to the provision of filesNothing of this application's

The result of one file is the unit, and the run is not

A result is written with the files which came of it, or neither is. Between two results, nothing is owed: a run holds the files which are done and says nothing of the others, which is exactly what a person reading it has to be told.

A run which stops is not repaired. A plugin which never returns, a thread which died, an application which was killed: the run holds the results it had, and no more come. What was produced for the files which were done is the person's and stays hers.

What tells a run which stopped from a run which is slow is the thread itself. A run whose thread is no longer alive and which holds fewer results than it was given files has stopped, and says so. Nothing else is needed, and nothing else is written: the same question is asked the same way where the features of SPPAS are installed.

Two runs at once

RO7 says one run at a time for one person, and the operational level says what holds it: a run is made before the first file is started, and a request which would start a second one finds the first still alive and is refused. It is what the installation of the features answers to the same question, in the same words.

Where the code goes

In sppas/ui/swapp/app_plugins/, cut as every application of swapp is cut: one module for the model, one for the view, one for what dispatches, one for what answers the URL, and one module per part of the page.

ModuleWhat it doesProcedure
plugins_modelHolds what is installed and what each plugin declares, installs and removes, starts a run and says where it stands, and asks the two services for the files of a run and for the options of a plugin. The only module of this application which speaks to the domain of the pluginsPF1, PF2, PF3, PF4, PF5, PF6, PF7
plugins_viewBuilds the page and holds its partsThe view of every procedure
plugins_controllerReads the task a request carries, calls what has to be called, and has the view build its treeEvery procedure
plugins_makerAnswers the URL, builds the view and what dispatches, and bakes the treeEvery procedure
plugslistThe parts of the page: the plugins one under another, one plugin, and a run with what came of each fileThe view of PF1, PF5, PF6

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

  • The model is the only one which calls the domain of the plugins and the two services, so it is the only one to be read again the day one of them changes. It reads nothing of what a plugin says beyond relaying it, and it is the only one which knows that a run is a thread.
  • The view decides nothing, and shows what a plugin said as that plugin said it.
  • What dispatches holds nothing between two requests: what has to survive is what the page carries.
  • The parts of the page know the page and nothing else. None of them reads a file, a thread or a declaration.

No module here knows what a plugin does. There is nowhere for that knowledge to be held, and the first chapter says why there could not be.

Implementation model: MVC

The model holds the plugins, the run which is going and what came of it, and exposes neither the domain of the plugins nor the two services to the rest of the application. The controller is called once per request, reads the task it carries, and holds nothing afterwards. The view renders and collects, and takes no decision. Three components, three class diagrams.

The classes of the model

/ marks what is derived and held by nobody. What the operations promise is the chapter on the contracts.

ClassFromAttributesOperations
PluginsModelPLUGINmanager, thread, runplugins(), one(id), can_run_here(id), install(archive), remove(id), files_for(run), options_of(id), values_set(), start(run), is_running(), the_run()
PluginRunRUN, VALUErun, plugin, moment, values, files, /remaining, /overadd(result), remaining(), is_over(), serialize(), parse(data)
FileResultRESULT, PRODUCEDfile, done, what was said, produced—

One class, and the class of the core under it. What is installed, what each plugin declares, putting one in and taking one out, and starting one on files: sppasPluginsManager holds all of it, and a plugin is an sppasPluginParam. A class of this application holding any of that under another name would be a second truth.

The run is started one file at a time. The domain of the plugins is given a plugin and a list of files, and gives back one text for the whole list. It is given one file at a time here, for the reason the operational model gives: the result of one file is the unit, and a run which stops has done what it did file by file. What appeared for a file is what the files of the person gained for it, which is how the interface which exists today finds it as well.

Nothing of the core carries the run. sppasPluginsManager descends from a thread but defines nothing of one and is never started as one: the thread which advances a run is this model's own, started as the setting up of SPPAS starts the installation of its features. Whether that thread is alive is what RO7 leans on — a second run started while one is going finds it alive and is refused — and it is also what tells a run which stopped from a run which is slow.

What is tied to what

TieLegsWhat it is
holdsPluginsModel 1 — 0..1 PluginRunA composition: the run exists inside what advances it, and goes with the process
holdsPluginRun 1 — 0..n FileResultA composition: a result exists only inside the run it belongs to
usesPluginsModel → sppasPluginsManagerA dependency, and the only tie of this application to the domain of the plugins
usesPluginsModel → the provision of files, the service of the optionsA dependency, and the only tie to the two services

In yUML

To be read at yuml.me, class diagram.

[PluginsModel|manager;thread;run|plugins();one();can_run_here();install();remove();files_for();options_of();values_set();start();is_running();the_run()]++1-0..1>[PluginRun|run;plugin;moment;values;files;/remaining;/over|add();remaining();is_over();serialize();parse()]
[PluginRun]++1-0..*>[FileResult|file;done;what was said;produced]

[PluginsModel]-.->[sppasPluginsManager]

The classes of the view

The parts of the page are those of the interface which exists today, and they bear its names: the plugins one under another, one plugin with what it says of itself, and what a run gave.

ClassOperationsWhat it shows
PluginsViewpopulate_tree_content(record)The page, and the sections it holds
PluginsList—The installed plugins, one under another, and what puts one in or takes one out where that is offered
PluginDescription—One plugin: its icon, what it says of itself in one sentence, what starts it, and that it cannot be started here when it cannot; and, asked for, its version and what it says of itself at length
PluginRunPanel—A run as it advances: which plugin, which file, how many remain; then one line per file which is done, with what was said about it and what appeared

Putting a plugin in and taking one out are not on a plugin: they are not what one does to a plugin but what one does to the list, and they stand in the menu of the page. Where SPPAS answers from a server, they are not there at all. RO1

What is tied to what

TieLegsWhat it is
holdsPluginsView 1 — 1 PluginsList, 1 PluginRunPanelA composition: a section exists inside the page it is a part of
holdsPluginsList 1 — 0..n PluginDescriptionA composition: one per installed plugin, and none where nothing is installed
usesPluginsView → PluginsRecordA dependency: it is given what is to be shown and reads nothing else

In yUML

[PluginsView|populate_tree_content()]++1-1>[PluginsList]
[PluginsView]++1-1>[PluginRunPanel]
[PluginsList]++1-0..*>[PluginDescription]

[PluginsView]-.->[PluginsRecord]

The classes of the controller

ClassAttributesOperations
PluginsControllerrecordhandle(data), populate_view()
PluginsRecordtask, plugin, run, to_be_shownserialize(), parse(data)
PluginsResponseRecipe—bake(), _process_events()

What is tied to what

TieLegsWhat it is
holdsPluginsResponseRecipe 1 — 1 PluginsView, 1 PluginsControllerA composition: it builds them, and they go when it goes
holdsPluginsController 1 — 1 PluginsRecordA composition: one record per request, kept by nobody
usesPluginsController → PluginsModelA dependency: it reads, it starts a run, it asks where one stands, and it asks the neighbours through it
usesPluginsController → PluginsViewA dependency: it has the page built from the record

In yUML

[PluginsResponseRecipe|bake();_process_events()]++1-1>[PluginsController|record|handle();populate_view()]
[PluginsResponseRecipe]++1-1>[PluginsView]
[PluginsController]++1-1>[PluginsRecord|task;plugin;run;to_be_shown|serialize();parse()]

[PluginsController]-.->[PluginsModel]
[PluginsController]-.->[PluginsView]

The states

A plugin, on this machine
StateWhenWhat is shown
readyIts command names something this machine hasThe card, and what starts it
cannot run hereIt does notThe card, and that it cannot be started here, before anybody tries [031]
A run
StateWhenWhat is shown
runningIt holds fewer results than it was given filesWhich file, how many remain, and the results so far
overIt holds one result per file it was givenEvery result, and what was produced
stoppedIt holds fewer results than files, and the thread which advanced it is no longer aliveThe results it holds, and that it will get no more

Running and stopped hold the same data, and what tells them apart is not in the data: it is whether the thread which advances the run is alive. A run is in one of the three and never in two.

The sequence, from the page to the produced files

  1. The page is asked for. What is installed is read, and one card is built per plugin; a plugin which cannot be started here says so.
  2. The person opens what a plugin says of itself at length, and closes it. Nothing else happens.
  3. She sets the plugin: its options are handed to the service which shows them, and what was set comes back.
  4. She asks to run it. The files she chose are read from the provision of files, and she is told how many there are.
  5. She starts the run. The run is written with its moment and its values, before the first file is started.
  6. The plugin is started on the first file; what it said is written as the result of that file, with the files which appeared.
  7. Her page asks where the run stands, and is answered: this file, so many remaining, and the results already written.
  8. Steps 6 and 7 happen again, at their own pace, until every file has a result.
  9. What was produced is given to the provision of files, and is found among her own files.

Steps 1 to 4 happen in any order and any number of times. Step 5 happens once per run, and refuses while a run of that person is still going. RO7

The error policy

LevelWhat happensWhat is done
1What was asked cannot be done now: no file was chosen, a run is already going, the plugin cannot be started hereNothing is started, and what is missing or what stands in the way is said. Nothing is raised
2One file is concerned: the plugin failed on it, or said something which is not a successThat file has its result, what the plugin said stands beside it, and the run goes on to the next file
3The archive cannot be installed, or the plugin cannot be removedWhat the domain of the plugins refused is said in the words of what was attempted, and nothing of the list changes
4Whoever runs the machine has to know: a run which cannot be written, a plugin which cannot be readSaid where he reads it, and the person is told that the run could not be started

What is raised is caught by whatever answers a request, and by nothing below it. What a plugin writes on its own error output is not an error of this application: it is what that plugin said, and it is shown as such.

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.

What holds everywhere

  • Only PluginsModel speaks to the domain of the plugins and to the two services. No other class of this application knows that either exists.
  • What a plugin said is relayed as it was said: never shortened, never translated, and never read for a meaning. RO3
  • Nothing of a run is written on a disk. A run belongs to the process which holds it and goes with it.
  • What is absent gives back an empty thing — no plugin, no result, no produced file — and never nothing at all.
  • What is raised is caught by whatever answers a request, and no class below it writes a catch.

Class by class

PluginsModel
OperationContract
Give every pluginpre: none. post: the plugins the domain of the plugins holds, each with what it declares of itself; empty when none is installed. Nothing is read of a plugin beyond what it declares
Give one pluginpre: an identifier. post: that plugin, or nothing at all when no plugin bears that identifier
Say whether a plugin can run herepre: a plugin. post: true when the command that plugin declares for this system names something this machine has; false otherwise. Nothing is started to find out
Install an archivepre: where SPPAS runs on the machine of the person [025]. post: the plugin stands among the installed ones, or nothing was installed and what stood in the way is named. Nothing of the archive is verified here: the other domain refuses
Remove a pluginpre: the same, and an identifier. post: that plugin is no longer among the installed ones, or nothing changed and what stood in the way is named. What that plugin produced is not removed
Give the files of a runpre: none. post: the files the person chose, as the provision of files exposes them; empty when she chose none. Nothing of them is altered
Give the options of a plugin to be setpre: a plugin. post: the options that plugin declares are handed to the service of the options, with the values they hold
Take back the valuespre: the options were set. post: the values which were set, as that service gives them. Nothing of them is judged here
Start a runpre: a plugin which can run here, files which are not none, and the values as they stand. post: the run exists, holding its moment, its plugin, its values and its files, and holding no result yet; and a thread advances it, one file at a time. When a run of that person is already going, nothing is started and the refusal says so
Say whether a run is goingpre: none. post: true while the thread which advances the run is alive, false afterwards. It is what tells a run which stopped from a run which is slow
Give the runpre: none. post: the run with the results it holds now, which file is being treated and how many remain. Answered while the run goes, and answered after it is over. A run read twice in a row gives what it had each time, and never less
Give back what was producedpre: the files which appeared. post: they are known among the files of the person, and reachable as her own. This application holds none of them

The run is advanced by a thread which is a daemon: it holds nothing alive, and an application which is killed kills it. A run whose thread is gone and which holds fewer results than files has stopped, and says so rather than being repaired.

PluginRun, FileResult
OperationContract
Add a resultpre: the result of one file of this run. post: the run holds it, with the files which came of it; what it held before is untouched, and the order the results were added in is kept
Say how many remainpre: none. post: the files the run was given, less the results it holds
Say whether a run is overpre: none. post: true when the run holds one result per file it was given
Serialise, parsepre: none. post: everything a page needs to show a run, and nothing of the machine. A run parsed twice gives the same run
PluginsController
OperationContract
Handle a requestpre: called once for that request, and never twice. post: the record is rebuilt from what was transmitted, the task is read, and one treatment ran — or none ran and what stood in the way is said. Nothing of a record is held after the request
Populate the viewpre: the request was handled. post: the view has the record; nothing is decided here
PluginsView and its parts
OperationContract
Populate the treepre: a record. post: what the record says and nothing more — no plugin it invented, no result it counted, no text it wrote
PluginsListpost: one PluginDescription per installed plugin, in the order the domain of the plugins gives them, and none at all when nothing is installed
PluginDescriptionpost: the icon of that plugin, what it says of itself in one sentence, and what starts it; that it cannot be started here, for a plugin which cannot run here, before anybody tries; and its version with what it says of itself at length, when the record says that was asked for
PluginRunPanelpost: which plugin is running, which file is being treated, and how many remain; nothing at all when no run is going
the same, a run being overpost: one line per file which is done, with what the plugin said about it and the files which appeared. A file which is not done has no line

Where each requirement is held

No.Held by
[011], [012], [013]PluginDescription, which shows what PluginsModel read and writes nothing of its own
[021], [022], [024]PluginsModel, which hands the archive over and asks for the removal
[023]PluginsModel, which names what the other domain refused
[025]The menu of the page, which does not carry those two actions where SPPAS answers from a server
[031], [032]PluginsModel.can_run_here(), read by PluginDescription
[101], [102]PluginsModel, which gives the files the person chose, and PluginsModel, which starts the plugin on those and no others
[103]PluginRunPanel, which is given the files before the run starts
[201], [202]PluginsModel, which hands the options over and takes the values back
[203]Nobody here. The service of the options is handed the options as the plugin holds them, and that domain says where the values of a former run come from
[301]PluginsModel.start(), which is called by nothing but a request of the person
[302], [304]PluginsModel, which starts the plugin once per file and adds a result whatever came of it
[303]PluginsModel.the_run(), shown by PluginRunPanel
[401], [402], [403]PluginRunPanel, which is given one result per file which is done
[501], [502]PluginsModel, which gives what appeared to the provision of files
[1001]PluginsView, and nothing else of this application

[203] is the only requirement held nowhere in this application, and it is written down so that nobody looks for it here.

What is tested, and where

Without a plugin, and without a run

A run started here starts a program, so what can be tested without starting one is tested without starting one. These need a plugin which is installed and nothing else.

Installing, removing and starting a plugin are the business of the domain of the plugins, and are tested there. What is checked here is what this application does with what that domain answered: TE4 to TE6 assert on the plugins which are given and on what is named, and never on what was unzipped or deleted.

No.What is checkedWhat it holds
TE1Every plugin the domain of the plugins holds is given, and no other[011]
TE2What is given of a plugin is what that plugin declares: its name, what it says of itself, its version, its icon[012], [013]
TE3A plugin whose command names something this machine does not have is said not to run here, and nothing is started to find out[031], [032]
TE4What the domain of the plugins refuses to install is relayed as a refusal: the plugins which are given are what they were, and what stood in the way is named[023]
TE5What that domain installed is relayed: the plugin is among those which are given, with what it declares of itself[021]
TE6What that domain removed is relayed: the plugin is no longer among those which are given, and nothing else went with it[022], [024]
TE7A run holds, after one result is added, that result and what came of it; and the results keep the order they were added inT06
TE8A run which holds one result per file it was given is over; one which holds fewer is notThe states
TE9A run parsed twice gives the same runThe contract of the record

With a run which really happens

These need a plugin to start, and the plugin they start is written for the tests: one which writes a file, one which fails, one which says nothing, one which takes its time. What is tested is this application, never a plugin of the person.

No.What is checkedWhat it holds
TE10A run starts the plugin once per file, and on the files it was given and no others[102], [302]
TE11A file on which the plugin fails has a result, and the files after it have theirs[304]
TE12What the plugin said about a file is held as it said it, byte for byte[403], RO3
TE13The files which appeared for one file are held in the result of that file, and in no other[402]
TE14While the run goes, what it says of itself names the file being treated and how many remain[303]
TE15A second run started while one is going is refused, and the refusal says that one is goingRO7
TE16A run whose thread is over and which holds one result per file is over; the same run is not said to be goingThe states
TE17A run whose thread died before the last file is neither going nor over: it stopped, and it holds the results it hadThe states
TE18The values a run was started with are the ones the plugin was started with, even if the options are set again while the run goesRO5
TE19What was produced is given to the provision of files once, when the run is over, and not at every fileT07

TE17 is the one which needs a thread to die. It is written because a run which stopped and a run which is slow hold the same data, and because the only thing which tells them apart is outside the data.

What no test covers, and where they stand

  • What a plugin does. It is a program written elsewhere; this application is answerable for what it started it on and for what it said of it.
  • [203] — finding the values of a former run. It is held nowhere in this application.
  • The two services, which have their own dossiers and their own tests. What is checked here is the tie: which files were asked for, and which files were given back.
  • The WCAG rules, which are verified on the page and not on a fragment of it.

The tests stand where the project puts its own, in tests/swapp/. Nine of the nineteen need no run at all.

Annexes

Annex: Legal notices

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