Skip to content

Commit

Permalink
Created schema for flexible and extensible forms (#11)
Browse files Browse the repository at this point in the history
* created basic user and form struct

* edited and expanded schema for flexibility

* Removed unused HashMap and created module for models

* resolved issues raised in pull request

* created doc comments and added the example to the doc comment

* fixed non-public test issue

* fixed _id problems

* finally fixed id issues and tested
  • Loading branch information
xandermcleod authored Sep 11, 2024
1 parent 288b9b1 commit 8cd876e
Show file tree
Hide file tree
Showing 2 changed files with 208 additions and 0 deletions.
3 changes: 3 additions & 0 deletions src/app/mod.rs
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;
Expand Down
205 changes: 205 additions & 0 deletions src/app/models/mod.rs
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,
}

0 comments on commit 8cd876e

Please sign in to comment.