-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Created schema for flexible and extensible forms #11
Merged
+208
−0
Merged
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
eaf3fb1
created basic user and form struct
xandermcleod 403418b
edited and expanded schema for flexibility
xandermcleod 340a355
Merge branch 'main' of https://github.com/BigLNotation/parkinsons_pul…
xandermcleod 4e9cd34
Removed unused HashMap and created module for models
xandermcleod 3208cf4
resolved issues raised in pull request
xandermcleod 48f9b5c
created doc comments and added the example to the doc comment
xandermcleod 88d71b8
fixed non-public test issue
xandermcleod 5ba3174
fixed _id problems
xandermcleod 48e0bfa
finally fixed id issues and tested
xandermcleod File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,6 @@ | ||
pub mod models; | ||
|
||
use std::sync::Arc; | ||
use std::time::Duration; | ||
|
||
use anyhow::Context; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,205 @@ | ||
use mongodb::bson::{oid::ObjectId, DateTime}; | ||
use serde::{Deserialize, Serialize}; | ||
|
||
#[derive(Serialize, Deserialize, Clone, Debug)] | ||
pub struct User { | ||
#[serde(rename = "_id", skip_serializing_if = "Option::is_none")] | ||
id: Option<ObjectId>, | ||
first_name: String, | ||
last_name: String, | ||
national_health_identifer: String, | ||
email_address: String, | ||
hashed_password: String, | ||
is_patient: bool, | ||
caregivers: Vec<ObjectId>, | ||
form_templates: Vec<Form>, | ||
} | ||
|
||
/// A form that clients fill in is represented here | ||
/// | ||
/// | ||
/// # Examples | ||
/// ``` | ||
/// use parkinsons_pulse_service::app::models::*; | ||
/// use mongodb::bson::{oid::ObjectId, DateTime}; | ||
/// | ||
/// Form { | ||
/// id: Some(ObjectId::new()), | ||
/// title: String::from("Tremors"), | ||
/// created_by: ObjectId::new(), | ||
/// created_at: DateTime::now(), | ||
/// questions: vec![ | ||
/// Question::Multichoice(MultichoiceQuestion { | ||
/// id: Some(ObjectId::new()), | ||
/// title: String::from("How many times have you experienced this in the last week?"), | ||
/// options: vec![MultichoiceQuestionOption { | ||
/// name: String::from("Once"), | ||
/// id: ObjectId::new(), | ||
/// }], | ||
/// min_selected: 1, | ||
/// max_selected: 2, | ||
/// }), | ||
/// Question::FreeForm(FreeFormQuestion { | ||
/// id: Some(ObjectId::new()), | ||
/// title: String::from("Is there anything else you would like to add?"), | ||
/// max_length: 200, | ||
/// min_length: 0, | ||
/// }), | ||
/// ], | ||
/// events: vec![ | ||
/// Event::QuestionEdited(QuestionEdited { | ||
/// question_id: ObjectId::new(), | ||
/// former_question: Question::FreeForm(FreeFormQuestion { | ||
/// id: Some(ObjectId::new()), | ||
/// title: String::from("How are you feeling this week?"), | ||
/// max_length: 100, | ||
/// min_length: 10, | ||
/// }), | ||
/// new_question: Question::FreeForm(FreeFormQuestion { | ||
/// id: Some(ObjectId::new()), | ||
/// title: String::from("Is there anything else you would like to add?"), | ||
/// max_length: 200, | ||
/// min_length: 0, | ||
/// }), | ||
/// edited_at: DateTime::now(), | ||
/// edited_by: ObjectId::new(), | ||
/// }), | ||
/// Event::FormSubmitted(FormSubmitted { | ||
/// answers: vec![ | ||
/// QuestionAndAnswer::Multichoice( | ||
/// MultichoiceQuestion { | ||
/// id: Some(ObjectId::new()), | ||
/// title: String::from( | ||
/// "How many times have you experienced this in the last week?", | ||
/// ), | ||
/// options: vec![MultichoiceQuestionOption { | ||
/// name: String::from("Once"), | ||
/// id: ObjectId::new(), | ||
/// }], | ||
/// min_selected: 1, | ||
/// max_selected: 2, | ||
/// }, | ||
/// ObjectId::new(), | ||
/// ), | ||
/// QuestionAndAnswer::FreeForm( | ||
/// FreeFormQuestion { | ||
/// id: Some(ObjectId::new()), | ||
/// title: String::from("Is there anything else you would like to add?"), | ||
/// max_length: 200, | ||
/// min_length: 0, | ||
/// }, | ||
/// String::from("I wasn't able to press the elevator buttons this morning"), | ||
/// ), | ||
/// ], | ||
/// submitted_at: DateTime::now(), | ||
/// submitted_by: ObjectId::new(), | ||
/// }), | ||
/// ], | ||
/// }; | ||
/// ``` | ||
#[derive(Serialize, Deserialize, Clone, Debug)] | ||
pub struct Form { | ||
#[serde(rename = "_id", skip_serializing_if = "Option::is_none")] | ||
pub id: Option<ObjectId>, | ||
/// Title of the form for clients | ||
pub title: String, | ||
pub created_by: ObjectId, | ||
pub created_at: DateTime, | ||
/// List of questions in the form | ||
pub questions: Vec<Question>, | ||
/// List of events such as a user filling in a form or a moderator updating the form | ||
pub events: Vec<Event>, | ||
} | ||
|
||
/// This represents a form event, either filling in the form and submitting it, or changing a question | ||
#[derive(Serialize, Deserialize, Clone, Debug)] | ||
pub enum Event { | ||
FormSubmitted(FormSubmitted), | ||
QuestionEdited(QuestionEdited), | ||
} | ||
|
||
/// This represents how a question may change | ||
#[derive(Serialize, Deserialize, Clone, Debug)] | ||
pub struct QuestionEdited { | ||
/// This is the ID of the question in the form being changed | ||
pub question_id: ObjectId, | ||
pub former_question: Question, | ||
pub new_question: Question, | ||
pub edited_by: ObjectId, | ||
pub edited_at: DateTime, | ||
} | ||
|
||
/// This is how we represent a form being filled | ||
#[derive(Serialize, Deserialize, Clone, Debug)] | ||
pub struct FormSubmitted { | ||
/// This is a list of all of the questions and the answers that were selected or entered | ||
pub answers: Vec<QuestionAndAnswer>, | ||
/// This is the ID of the user that submitted the form | ||
pub submitted_by: ObjectId, | ||
/// This is the time that they submitted it | ||
pub submitted_at: DateTime, | ||
} | ||
|
||
/// This represents a form question for clients to answer | ||
#[derive(Serialize, Deserialize, Clone, Debug)] | ||
pub enum Question { | ||
/// This is a list of multiple choices for a question | ||
Multichoice(MultichoiceQuestion), | ||
/// This is a numeric slider | ||
Slider(SliderQuestion), | ||
/// This is for free form questions where the client may type whatever | ||
FreeForm(FreeFormQuestion), | ||
} | ||
|
||
/// ID of choice in the questions that is selected | ||
pub type MultichoiceAnswer = ObjectId; | ||
/// Numerical value that the user selects | ||
pub type SliderAnswer = f64; | ||
/// String for the answer that the client types | ||
pub type FreeFormAnswer = String; | ||
|
||
/// Combination of both the question and answer | ||
#[derive(Serialize, Deserialize, Clone, Debug)] | ||
pub enum QuestionAndAnswer { | ||
Multichoice(MultichoiceQuestion, MultichoiceAnswer), | ||
Slider(SliderQuestion, SliderAnswer), | ||
FreeForm(FreeFormQuestion, FreeFormAnswer), | ||
} | ||
|
||
/// Free form question with some validation rules you could apply | ||
#[derive(Serialize, Deserialize, Clone, Debug, Default)] | ||
pub struct FreeFormQuestion { | ||
#[serde(rename = "_id", skip_serializing_if = "Option::is_none")] | ||
pub id: Option<ObjectId>, | ||
pub title: String, | ||
pub max_length: u64, | ||
pub min_length: u64, | ||
} | ||
|
||
#[derive(Serialize, Deserialize, Clone, Debug, Default)] | ||
pub struct SliderQuestion { | ||
#[serde(rename = "_id", skip_serializing_if = "Option::is_none")] | ||
pub id: Option<ObjectId>, | ||
pub title: String, | ||
pub units: Option<String>, | ||
pub low: f64, | ||
pub high: f64, | ||
pub step: f64, | ||
} | ||
|
||
#[derive(Serialize, Deserialize, Clone, Debug, Default)] | ||
pub struct MultichoiceQuestion { | ||
#[serde(rename = "_id", skip_serializing_if = "Option::is_none")] | ||
pub id: Option<ObjectId>, | ||
pub title: String, | ||
pub options: Vec<MultichoiceQuestionOption>, | ||
pub min_selected: u64, | ||
pub max_selected: u64, | ||
} | ||
|
||
#[derive(Serialize, Deserialize, Clone, Debug, Default)] | ||
pub struct MultichoiceQuestionOption { | ||
pub name: String, | ||
#[serde(rename = "_id")] | ||
pub id: ObjectId, | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How will questions being added/removed (so that the total number of questions changes) work?
If a given question is edited in such a way that it has a different semantic meaning (i.e. we just replace it entirely), is there any reason to retain the same id rather than just treating it as a deletion + a creation? Is this intended specifically for cases where questions are edited while keeping the same meaning (e.g. typo corrections, or rephrasing the same general question, etc)?