Welcome to the Maturity Models Survey app!
The Maturity Models survey app uses a three-tiered architecture with a React web client, a small Python-Flask server API, and a REDCap project as a database.
To run it locally, do the following steps:
# Clone the repo
$ git clone git@github.com:data2health/maturity-model.git
# Setup a python virtual environment
$ cd maturity-models/src/server
$ python3 -m venv venv
$ . venv/bin/activate
# Install API dependencies
$ python3 -m pip install Flask requests
Next, create a config.json
file to tell the API the REDCap instance to point to under /maturity-models/src/server/flaskr/app/modules/services/config.json
.
{
"redcap": {
"token": "<API_token>",
"url": "https://redcap.example.org/api/"
}
}
Run the API
$ python3 run.py
Now that the API is running, you can setup the React web client. Make sure you have NPM installed first.
# Install client dependencies
$ cd maturity-models/src/ui-client
$ npm install
# Run the client
$ npm start
Note that in order to be allowed into the app (even in development), you'll need a record in the REDCap project User
form with your email
address and entry_code
.
The User
form is a record of all the users and their login info. To add new users, this form must be completed to grant them access.
The following fields in maturity-model/src/server/flaskr/app/modules/fields.py are currrently being used in the app and can be modified to project needs.
RECORD_ID = 'record_id' (REDCap automatically makes this field)
EMAIL_ADDRESS = 'email'
ENTRY_CODE = 'entry_code'
APPROVED = 'approved'
FNAME = 'user_fname'
LNAME = 'user_lname'
-
The app uses the variable names
email
andentry_code
to log in, which must be included in the form. -
There is an
approved
field, which validates whether the login is approved or not. If the variable is set tofalse
, the login will fail. -
The app also uses the
user_fname
anduser_lname
to display the user name in the app. If the field is left blank, no name will be shown.
The User
form can also store additional information about the user which may or may not be used.
Adding models to the Maturity Models Survey app is straightforward.
- Add a new
Form
to the REDCap project to represent the Model you'd like to add.
Name the variables in the project uses the convention <form_name>_q<question_num>_<short_name>
, and be sure to use Multiple Radio Buttons (Single Answer)
as the type.
So for example, a form called Example
could have variables of the form:
example_complete
(REDCap automatically makes this field)example_q1_month_of_birth
example_q2_fav_monty_python_joke
- ...
- Next, add the model to the client app. Begin by adding the new variables you added to the REDCap project above to the existing
AnswerField
type in maturity-models/src/ui-client/src/model.User.ts.
export type AnswerField =
// User name
'user_fname' |
'user_lname' |
...
// Example <- These are new
`example_complete` |
`example_q1_month_of_birth` |
`example_q2_fav_monty_python_joke` |
...
- Create a new file for your Model under maturity-models/src/ui-client/src/model/Models/. Inside the file, create a new
BaseModel
object. This is the data that will populate the form actually seen by users.
import React from "react";
import { BaseModel } from "../ModelsState";
import { UserAnswers } from "../User";
import { ExampleForm } from "../../components/Models/Example/Example"; // <- Don't worry, this won't work yet but is the next step.
export const Example: BaseModel =
{
completeField: 'example_complete',
name: 'The Super Awesome Example Survey (SAES)',
description: 'The Super Awesome Example Survey is just that, an example. But also super awesome.',
render: (dispatch: any, answers: UserAnswers) => <ExampleForm dispatch={dispatch} answers={answers} />,
questions: [
{
answerField: 'example_q1_month_of_birth',
text: 'Select the month of your birth:',
options: [
{
text: 'January',
value: '1'
},
{
text: 'February',
value: '2'
}
]
},
{
answerField: 'example_q2_fav_monty_python_joke',
text: 'Select your favorite Monty Python joke:',
options: [
{
text: 'The dead parrot one',
value: '1'
},
{
text: 'Tis but a scratch',
value: '2'
}
]
}
],
}
Note that the import { ExampleForm } from ...
statement won't work yet, we'll do that next.
- Under maturity-models/src/ui-client/src/components/Models/, add a new file called
Example.tsx
, subsituting "Example" for the actual name of your Model. This is the React component we want to render when your model is selected by the user. Most models can work using existing React components we've created (specifically ModelForm), so you should rarely need to do anything custom here.
import React from 'react';
import { Example } from '../../model/Models/Example'; // <- This is the file created in step #3.
import { UserAnswers } from '../../model/User';
import { ModelForm } from '../BaseForms/ModelForm/ModelForm';
interface Props {
dispatch: any;
answers: UserAnswers;
}
export class ExampleForm extends React.PureComponent<Props> {
public render() {
const { dispatch, answers } = this.props;
return <ModelForm dispatch={dispatch} answers={answers} model={Example}/>;
}
}
- Last step: add your Model to the
defaultModelState() function
under maturity-models/src/ui-client/src/reducers/model.ts. This "officially" adds the model to the list that users can select.
import { MODEL_SET_CURRENT, ModelAction, MODEL_SET_SELECTED } from '../actions/model';
import { ModelsState } from '../model/ModelsState';
import { RIOSM } from '../model/Models/RIOSM';
import { HIMSS_EMRAM } from '../model/Models/HIMSS_EMRAM';
import { Example } from '../model/Models/Example'; // <- Import your Model first
export const defaultModelState = (): ModelsState => {
return {
all: [ RIOSM, HIMSS_EMRAM, Example ] // <- Then add it to the array here
};
};
Voilà! ;)