This repository is a combination of applications that interact to
- conduct surveys,
- collect them in a database
- and visualize the results.
flowchart TB
web["web app (wasm)"] -- REST --> backend
native["native app"] -- REST --> backend
subgraph backend [prono-backend]
rest["REST endpoints"] --> core["prono: surveys, submit / results / scores, Repo"]
end
cli["prono-cli"] --> core
core -- sqlx --> db[(MariaDB)]
api["prono_api: survey model and definition, answers, rules, REST contract"]
web & native & backend & cli -.-> api
| Crate | Role |
|---|---|
api (prono_api) |
The model every binary shares: Survey and how it is defined (Survey::from_json), Answer, the validation rules and the REST contract. Compiles to wasm. api/surveys/*.json are example definitions, not part of any binary. |
prono |
Server-side core: the use cases (upload_survey, submit, results, scores), the Repo trait with its MySQL and in-memory implementations, and the database config. |
backend |
actix-web REST server around prono. |
app |
egui app for desktop and web. Both talk to the backend over REST; the UI only draws a small state machine (app/src/state.rs). |
cli |
Admin tool on top of prono: a menu to navigate, or commands for scripts. |
There is one definition of each survey type (in prono_api), and the rules
live there too, so the app checks a submission with exactly the code the
backend uses to accept it.
The diagram in
docs/architecture.drawio.pngpredates this layout.
Surveys live in the database, not in the binaries: the backend serves them to
the apps, so a new one needs no redeploy. Write a definition and upload it
(it replaces the survey with the same survey_id):
cargo run --bin prono-cli -- surveys upload my_survey.json{
"survey_id": 3,
"title": "My survey",
"description": "What the survey is about",
"link": "https://example.com",
"questions": [
{ "question": "When will it happen?", "answer_type": "prediction_date" },
{ "question": "Why?", "answer_type": "text" },
{ "question": "Which?", "answer_type": "poll", "possible_answers": ["A", "B"] },
{ "question": "Any allergies?", "answer_type": "poll", "possible_answers": ["No"], "other_label": "Yes, namely:" }
]
}-
Pass phrase: every survey needs one, so that nobody sees or fills in a survey unasked. It is not part of the definition (there is no
accesskey): set it withprono-cli surveys pass-phrase <survey id> <pass phrase>. A participant enters one pass phrase in the app and gets every survey that has it, so surveys for the same group can share one. Until a survey has a pass phrase it stays closed. -
name_listis optional (defaultfalse). With"name_list": trueonly the participants on the survey's name list may answer it, on top of the pass phrase, which is always needed: put names on it withprono-cli surveys names allow <survey id> <first name> <last name>(names are matched ignoring case). A name that is not on the list is refused when the answers are submitted. -
linkis optional;possible_answersis forpollquestions only. So isother_label, the name of the free answer next to the options (default:Other), for questions where that does not fit. -
Scores: give every question a
correct_answer(text or option fortextandpoll,{"month": 7, "year": 2028}forprediction_date) and the survey is scored: participants see a ranking of the correct answers after submitting. All questions need one, or none. The correct answers never leave the backend, so a prediction survey can be uploaded again with them once the events happened; the answers already given are kept.prono-cli surveys scores <id>shows the ranking. -
Answers:
prono-cli surveys answers <id>prints a table of everybody's answers (a row per participant, a column per question); add--user "First Last"for the answers of one participant. The interactive menu (prono-cliwithout a command) offers both under Surveys.
A question's id is derived from its text and answer type, and stored answers reference that id: editing a question's text starts it from scratch.
This repository contains the following applications: