-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: maxwellgithinji <maxwellgithinji@gmail.com>
- Loading branch information
1 parent
893fe34
commit f52576a
Showing
10 changed files
with
419 additions
and
0 deletions.
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
Empty file.
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,48 @@ | ||
from django.contrib.admin import site | ||
from nested_admin import NestedModelAdmin, NestedStackedInline, NestedTabularInline | ||
|
||
from .models import ( | ||
Question, | ||
QuestionInputChoice, | ||
Questionnaire, | ||
QuestionnaireResponse, | ||
ResponseInstance, | ||
ScreeningTool, | ||
) | ||
|
||
|
||
# Questionnaire Admin | ||
class QuestionInputChoiceInline(NestedTabularInline): | ||
model = QuestionInputChoice | ||
extra = 0 | ||
|
||
|
||
class QuestionInline(NestedStackedInline): | ||
model = Question | ||
inlines = (QuestionInputChoiceInline,) | ||
extra = 0 | ||
|
||
|
||
class ScreeningToolInline(NestedStackedInline): | ||
model = ScreeningTool | ||
extra = 0 | ||
|
||
|
||
class QuestionnaireAdmin(NestedModelAdmin): | ||
inlines = (ScreeningToolInline, QuestionInline) | ||
|
||
|
||
site.register(Questionnaire, QuestionnaireAdmin) | ||
|
||
|
||
# Responses Admin | ||
class ResponseInstanceAdminInline(NestedStackedInline): | ||
model = ResponseInstance | ||
extra = 0 | ||
|
||
|
||
class QuestionnaireResponseAdmin(NestedModelAdmin): | ||
inlines = (ResponseInstanceAdminInline,) | ||
|
||
|
||
site.register(QuestionnaireResponse, QuestionnaireResponseAdmin) |
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,6 @@ | ||
from django.apps import AppConfig | ||
|
||
|
||
class QuestionnairesConfig(AppConfig): | ||
default_auto_field = "django.db.models.BigAutoField" | ||
name = "mycarehub.questionnaires" |
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,144 @@ | ||
# Generated by Django 3.2.14 on 2022-07-29 13:19 | ||
|
||
import datetime | ||
from django.conf import settings | ||
import django.contrib.postgres.fields | ||
from django.db import migrations, models | ||
import django.db.models.deletion | ||
import django.utils.timezone | ||
import mycarehub.common.models.base_models | ||
import mycarehub.utils.general_utils | ||
import uuid | ||
|
||
|
||
class Migration(migrations.Migration): | ||
|
||
initial = True | ||
|
||
dependencies = [ | ||
('common', '0023_feedback_phone_number'), | ||
migrations.swappable_dependency(settings.AUTH_USER_MODEL), | ||
] | ||
|
||
operations = [ | ||
migrations.CreateModel( | ||
name='Question', | ||
fields=[ | ||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), | ||
('text', models.TextField(max_length=5000)), | ||
('question_type', models.CharField(choices=[('OPEN_ENDED', 'Open Ended'), ('CLOSE_ENDED', 'Close Ended')], max_length=20)), | ||
('response_value_type', models.CharField(choices=[('STRING', 'String'), ('NUMBER', 'Number'), ('BOOLEAN', 'Boolean'), ('DATE', 'Date'), ('TIME', 'Time'), ('DATE_TIME', 'DateTime')], max_length=20)), | ||
('select_multiple', models.BooleanField(default=False)), | ||
('required', models.BooleanField(default=False)), | ||
('sequence', models.IntegerField()), | ||
], | ||
), | ||
migrations.CreateModel( | ||
name='Questionnaire', | ||
fields=[ | ||
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), | ||
('active', models.BooleanField(default=True)), | ||
('created', models.DateTimeField(default=django.utils.timezone.now)), | ||
('created_by', models.UUIDField(blank=True, null=True)), | ||
('updated', models.DateTimeField(default=django.utils.timezone.now)), | ||
('updated_by', models.UUIDField(blank=True, null=True)), | ||
('deleted_at', models.DateTimeField(blank=True, null=True)), | ||
('name', models.CharField(max_length=60)), | ||
('description', models.TextField(max_length=1000)), | ||
('valid_from', models.DateField(default=datetime.date.today)), | ||
('valid_days', models.IntegerField(default=0)), | ||
('valid_weeks', models.IntegerField(default=0)), | ||
('valid_months', models.IntegerField(default=0)), | ||
('valid_to', models.DateField(blank=True, null=True)), | ||
('frequency_days', models.IntegerField(default=0)), | ||
('frequency_weeks', models.IntegerField(default=0)), | ||
('frequency_months', models.IntegerField(default=0)), | ||
('next_survey_date', models.DateField(blank=True, null=True)), | ||
('organisation', models.ForeignKey(default=mycarehub.utils.general_utils.default_organisation, on_delete=django.db.models.deletion.PROTECT, related_name='questionnaires_questionnaire_related', to='common.organisation')), | ||
], | ||
options={ | ||
'ordering': ('-updated', '-created'), | ||
'abstract': False, | ||
}, | ||
managers=[ | ||
('objects', mycarehub.common.models.base_models.AbstractBaseManager()), | ||
], | ||
), | ||
migrations.CreateModel( | ||
name='QuestionnaireResponse', | ||
fields=[ | ||
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), | ||
('active', models.BooleanField(default=True)), | ||
('created', models.DateTimeField(default=django.utils.timezone.now)), | ||
('created_by', models.UUIDField(blank=True, null=True)), | ||
('updated', models.DateTimeField(default=django.utils.timezone.now)), | ||
('updated_by', models.UUIDField(blank=True, null=True)), | ||
('deleted_at', models.DateTimeField(blank=True, null=True)), | ||
('flavour', models.CharField(choices=[('PRO', 'PRO'), ('CONSUMER', 'CONSUMER')], max_length=20)), | ||
('facility', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='common.facility')), | ||
('organisation', models.ForeignKey(default=mycarehub.utils.general_utils.default_organisation, on_delete=django.db.models.deletion.PROTECT, related_name='questionnaires_questionnaireresponse_related', to='common.organisation')), | ||
('questionnaire', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='questionnaires.questionnaire')), | ||
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), | ||
], | ||
options={ | ||
'ordering': ('-updated', '-created'), | ||
'abstract': False, | ||
}, | ||
managers=[ | ||
('objects', mycarehub.common.models.base_models.AbstractBaseManager()), | ||
], | ||
), | ||
migrations.CreateModel( | ||
name='ScreeningTool', | ||
fields=[ | ||
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), | ||
('active', models.BooleanField(default=True)), | ||
('created', models.DateTimeField(default=django.utils.timezone.now)), | ||
('created_by', models.UUIDField(blank=True, null=True)), | ||
('updated', models.DateTimeField(default=django.utils.timezone.now)), | ||
('updated_by', models.UUIDField(blank=True, null=True)), | ||
('deleted_at', models.DateTimeField(blank=True, null=True)), | ||
('threshold', models.IntegerField(default=0)), | ||
('client_types', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(choices=[('PMTCT', 'PMTCT'), ('OTZ', 'OTZ'), ('OTZ_PLUS', 'OTZ Plus'), ('HVL', 'HVL'), ('OVC', 'OVC'), ('DREAMS', 'DREAMS'), ('HIGH_RISK', 'High Risk Clients'), ('SPOUSES', 'SPOUSES'), ('YOUTH', 'Youth'), ('KenyaEMR', 'Kenya EMR')], max_length=64), blank=True, default=list, null=True, size=None)), | ||
('genders', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(choices=[('MALE', 'Male'), ('FEMALE', 'Female'), ('OTHER', 'Other')], max_length=64), blank=True, default=list, null=True, size=None)), | ||
('min_age', models.IntegerField(default=14)), | ||
('max_age', models.IntegerField(default=25)), | ||
('organisation', models.ForeignKey(default=mycarehub.utils.general_utils.default_organisation, on_delete=django.db.models.deletion.PROTECT, related_name='questionnaires_screeningtool_related', to='common.organisation')), | ||
('questionnaire', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='questionnaires.questionnaire')), | ||
], | ||
options={ | ||
'ordering': ('-updated', '-created'), | ||
'abstract': False, | ||
}, | ||
managers=[ | ||
('objects', mycarehub.common.models.base_models.AbstractBaseManager()), | ||
], | ||
), | ||
migrations.CreateModel( | ||
name='ResponseInstance', | ||
fields=[ | ||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), | ||
('answer', models.TextField(max_length=1000)), | ||
('question', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='questionnaires.question')), | ||
('questionnaire', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='questionnaires.questionnaireresponse')), | ||
], | ||
), | ||
migrations.AddField( | ||
model_name='question', | ||
name='questionnaire', | ||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='questionnaires.questionnaire'), | ||
), | ||
migrations.CreateModel( | ||
name='QuestionInputChoice', | ||
fields=[ | ||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), | ||
('choice', models.CharField(max_length=100)), | ||
('value', models.CharField(max_length=100)), | ||
('score', models.IntegerField(default=0)), | ||
('question', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='questionnaires.question')), | ||
], | ||
options={ | ||
'unique_together': {('choice', 'question')}, | ||
}, | ||
), | ||
] |
Empty file.
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,167 @@ | ||
import datetime | ||
|
||
from django.contrib.auth import get_user_model | ||
from django.contrib.postgres.fields import ArrayField | ||
from django.db import models | ||
from django.db.models.enums import TextChoices | ||
from django.utils.translation import gettext_lazy as _ | ||
|
||
from mycarehub.clients.models import ClientType, FlavourChoices | ||
from mycarehub.common.models import AbstractBase, Facility | ||
from mycarehub.users.models import GenderChoices | ||
|
||
User = get_user_model() | ||
|
||
|
||
class QuestionTypeChoices(TextChoices): | ||
OpenEnded = "OPEN_ENDED", _("Open Ended") | ||
CloseEnded = "CLOSE_ENDED", _("Close Ended") | ||
|
||
|
||
class ResponseValueChoices(TextChoices): | ||
String = "STRING", _("String") | ||
Number = "NUMBER", _("Number") | ||
Boolean = "BOOLEAN", _("Boolean") | ||
Date = "DATE", _("Date") | ||
Time = "TIME", _("Time") | ||
DateTime = "DATE_TIME", _("DateTime") | ||
|
||
|
||
class Questionnaire(AbstractBase): | ||
""" | ||
Questionnaire Model defines the survey that is asked to a user | ||
a survey can be of type Open Ended or Closed Ended. Open Ended surveys are | ||
surveys that don't have choices. | ||
Closed Ended surveys are surveys that have choices. | ||
Closed Ended surveys can have multiple responses. | ||
all surveys share response value type, which is a string, number, boolean, date, time, datetime | ||
""" | ||
|
||
name = models.CharField(max_length=60) | ||
description = models.TextField(max_length=1000) | ||
valid_from = models.DateField(default=datetime.date.today) | ||
valid_days = models.IntegerField(default=0) | ||
valid_weeks = models.IntegerField(default=0) | ||
valid_months = models.IntegerField(default=0) | ||
valid_to = models.DateField(null=True, blank=True) | ||
frequency_days = models.IntegerField(default=0) | ||
frequency_weeks = models.IntegerField(default=0) | ||
frequency_months = models.IntegerField(default=0) | ||
next_survey_date = models.DateField(null=True, blank=True) | ||
|
||
def __str__(self): | ||
return "{}".format(self.name) | ||
|
||
|
||
class ScreeningTool(AbstractBase): | ||
""" | ||
Screening Tool Model defines the screening tool that is asked to a client user | ||
they are a subset of the surveys. | ||
they contain properties that are specific to a screening tool type of questionnaire. | ||
they also contain properties that are common to a client user. | ||
""" | ||
|
||
questionnaire = models.ForeignKey(Questionnaire, on_delete=models.CASCADE) | ||
threshold = models.IntegerField(default=0) | ||
client_types = ArrayField( | ||
models.CharField( | ||
max_length=64, | ||
choices=ClientType.choices, | ||
), | ||
null=True, | ||
blank=True, | ||
default=list, | ||
) | ||
genders = ArrayField( | ||
models.CharField( | ||
max_length=64, | ||
choices=GenderChoices.choices, | ||
), | ||
null=True, | ||
blank=True, | ||
default=list, | ||
) | ||
min_age = models.IntegerField(default=14) | ||
max_age = models.IntegerField(default=25) | ||
|
||
def __str__(self): | ||
return "{}".format(self.questionnaire) | ||
|
||
|
||
class Question(models.Model): | ||
""" | ||
Question Model defines the questions that are asked to a user | ||
A question belongs to a questionnaire, a questionnaire can have more than one. | ||
A question can be of type Open Ended or Closed Ended. | ||
Open Ended questions are surveys that don't have choices. | ||
Closed Ended questions are surveys that have choices. | ||
Closed Ended questions can have multiple responses. | ||
""" | ||
|
||
text = models.TextField(max_length=5000) | ||
questionnaire = models.ForeignKey(Questionnaire, on_delete=models.CASCADE) | ||
question_type = models.CharField( | ||
max_length=20, choices=QuestionTypeChoices.choices, null=False | ||
) | ||
response_value_type = models.CharField( | ||
max_length=20, choices=ResponseValueChoices.choices, null=False | ||
) | ||
select_multiple = models.BooleanField(default=False) | ||
required = models.BooleanField(default=False) | ||
sequence = models.IntegerField() | ||
|
||
def __str__(self): | ||
return "{}".format(self.text) | ||
|
||
|
||
class QuestionInputChoice(models.Model): | ||
""" | ||
Question Input Choice Model defines the choices that belong to a closed ended question | ||
A question can have more than one choice. | ||
based on the select_multiple property, a response can have multiple choices. | ||
score represents the score that is given to a choice. | ||
a score can be aggregated to a screening tool questionnaire and compared to the threshold. | ||
this will help with the logic of separating responses into positive or negative per user. | ||
""" | ||
|
||
question = models.ForeignKey(Question, on_delete=models.CASCADE) | ||
choice = models.CharField(max_length=100, null=False) | ||
value = models.CharField(max_length=100, null=False) | ||
score = models.IntegerField(default=0) | ||
|
||
class Meta: | ||
unique_together = ("choice", "question") | ||
|
||
def __str__(self): | ||
return "{}:{}".format(self.choice, self.value) | ||
|
||
|
||
class QuestionnaireResponse(AbstractBase): | ||
""" | ||
Questionnaire Response Model defines the responses that a user gives to a questionnaire | ||
a response belongs to a questionnaire, a questionnaire can have more than one responses | ||
based on the number of question instances. | ||
All required questions must be answered. | ||
""" | ||
|
||
questionnaire = models.ForeignKey(Questionnaire, on_delete=models.CASCADE) | ||
user = models.ForeignKey(User, on_delete=models.CASCADE) | ||
flavour = models.CharField(max_length=20, choices=FlavourChoices.choices, null=False) | ||
facility = models.ForeignKey(Facility, on_delete=models.CASCADE) | ||
|
||
def __str__(self): | ||
return "{} response for {}".format(self.questionnaire, self.user) | ||
|
||
|
||
class ResponseInstance(models.Model): | ||
""" | ||
Response Instance Model defines the instances of a question that a user gives a response to. | ||
The answer given is validated against the question settings. | ||
""" | ||
|
||
questionnaire = models.ForeignKey(QuestionnaireResponse, on_delete=models.CASCADE) | ||
question = models.ForeignKey(Question, on_delete=models.CASCADE) | ||
answer = models.TextField(max_length=1000) | ||
|
||
def __str__(self): | ||
return "{}".format(self.answer) |
Empty file.
Oops, something went wrong.