Skip to content

Commit

Permalink
feat: add validation of questionnaire answers (#174)
Browse files Browse the repository at this point in the history
  • Loading branch information
kennedykori committed Jan 20, 2022
1 parent 0fc7f9e commit 539a05d
Show file tree
Hide file tree
Showing 43 changed files with 2,028 additions and 811 deletions.
2 changes: 1 addition & 1 deletion .coveragerc
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[run]
branch=True
source=fahari
omit=fahari/config/*,fahari/**/migrations/*,scripts/*,fahari/**/tests/*,fahari/sims/*
omit=fahari/config/*,fahari/**/migrations/*,scripts/*,fahari/**/tests/*,fahari/sims/*,fahari/utils/metadata_utils/*

[report]
exclude_lines =
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ env:
GOOGLE_APPLICATION_CREDENTIALS: "${{ secrets.GOOGLE_APPLICATION_CREDENTIALS }}"
DJANGO_GCP_STORAGE_BUCKET_NAME: ${{ secrets.DJANGO_GCP_STORAGE_BUCKET_NAME }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
DJANGO_EMAIL_BACKEND: ${{secrets.DJANGO_EMAIL_BACKEND}}
DJANGO_EMAIL_BACKEND: ${{ secrets.DJANGO_EMAIL_BACKEND }}

jobs:
linter:
Expand Down
144 changes: 99 additions & 45 deletions config/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,10 @@
env.read_env(io.StringIO(payload))
print("set env from secrets...")


# =============================================================================
# GENERAL
# ------------------------------------------------------------------------------
# =============================================================================
DEBUG = env.bool("DJANGO_DEBUG", False)
TIME_ZONE = "Africa/Nairobi"
LANGUAGE_CODE = "en-us"
Expand All @@ -35,8 +37,10 @@
USE_TZ = True
LOCALE_PATHS = [str(ROOT_DIR / "locale")]

# DATABASES
# ------------------------------------------------------------------------------

# =============================================================================
# DATABASE CONFIG
# =============================================================================
DATABASES = {
"default": {
"NAME": env.str("POSTGRES_DB"),
Expand All @@ -49,14 +53,18 @@
},
}


# =============================================================================
# URLS
# ------------------------------------------------------------------------------
# =============================================================================
ROOT_URLCONF = "config.urls"
ASGI_APPLICATION = "config.asgi.application"
WSGI_APPLICATION = "config.wsgi.application"


# =============================================================================
# APPS
# ------------------------------------------------------------------------------
# =============================================================================
DJANGO_APPS = [
"django.contrib.auth",
"django.contrib.contenttypes",
Expand Down Expand Up @@ -95,12 +103,16 @@
]
INSTALLED_APPS = DJANGO_APPS + THIRD_PARTY_APPS + LOCAL_APPS


# =============================================================================
# MIGRATIONS
# ------------------------------------------------------------------------------
# =============================================================================
MIGRATION_MODULES = {"sites": "fahari.contrib.sites.migrations"}


# =============================================================================
# AUTHENTICATION
# ------------------------------------------------------------------------------
# =============================================================================
AUTHENTICATION_BACKENDS = [
"django.contrib.auth.backends.ModelBackend",
"allauth.account.auth_backends.AuthenticationBackend",
Expand All @@ -109,8 +121,10 @@
LOGIN_REDIRECT_URL = "users:redirect"
LOGIN_URL = "account_login"


# =============================================================================
# PASSWORDS
# ------------------------------------------------------------------------------
# =============================================================================
PASSWORD_HASHERS = [
"django.contrib.auth.hashers.Argon2PasswordHasher",
"django.contrib.auth.hashers.PBKDF2PasswordHasher",
Expand All @@ -124,8 +138,10 @@
{"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator"},
]


# =============================================================================
# MIDDLEWARE
# ------------------------------------------------------------------------------
# =============================================================================
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"corsheaders.middleware.CorsMiddleware",
Expand All @@ -140,8 +156,10 @@
"django.middleware.clickjacking.XFrameOptionsMiddleware",
]


# =============================================================================
# STATIC
# ------------------------------------------------------------------------------
# =============================================================================
STATIC_ROOT = str(ROOT_DIR / "staticfiles")
STATIC_URL = "/static/"
STATICFILES_DIRS = [str(APPS_DIR / "static")]
Expand All @@ -150,13 +168,17 @@
"django.contrib.staticfiles.finders.AppDirectoriesFinder",
]


# =============================================================================
# MEDIA
# ------------------------------------------------------------------------------
# =============================================================================
MEDIA_ROOT = str(APPS_DIR / "media")
MEDIA_URL = "/media/"


# =============================================================================
# TEMPLATES
# ------------------------------------------------------------------------------
# =============================================================================
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
Expand Down Expand Up @@ -184,19 +206,26 @@
CRISPY_TEMPLATE_PACK = "bootstrap4"
CRISPY_FAIL_SILENTLY = not DEBUG


# =============================================================================
# FIXTURES
# ------------------------------------------------------------------------------
# =============================================================================
FIXTURE_DIRS = (str(APPS_DIR / "fixtures"),)


# =============================================================================
# SECURITY
# ------------------------------------------------------------------------------
SESSION_COOKIE_HTTPONLY = True
# =============================================================================
CSRF_COOKIE_HTTPONLY = True
CSRF_USE_SESSIONS = False
SESSION_COOKIE_HTTPONLY = True
SECURE_BROWSER_XSS_FILTER = True
X_FRAME_OPTIONS = "SAMEORIGIN" # needs to be SAMEORIGIN for the jet admin to work


# =============================================================================
# EMAIL
# ------------------------------------------------------------------------------
# =============================================================================
EMAIL_BACKEND = env(
"DJANGO_EMAIL_BACKEND",
default="anymail.backends.mailgun.EmailBackend",
Expand All @@ -219,8 +248,10 @@
}
EMAIL_TIMEOUT = 5


# =============================================================================
# ADMIN
# ------------------------------------------------------------------------------
# =============================================================================
ADMIN_URL = "admin/"
ADMINS = [
(
Expand All @@ -234,8 +265,10 @@
]
MANAGERS = ADMINS


# =============================================================================
# LOGGING
# ------------------------------------------------------------------------------
# =============================================================================
LOGGING = {
"version": 1,
"disable_existing_loggers": False,
Expand All @@ -254,8 +287,10 @@
"root": {"level": "INFO", "handlers": ["console"]},
}

# django-allauth
# ------------------------------------------------------------------------------

# =============================================================================
# DJANGO ALL AUTH
# =============================================================================
ACCOUNT_ALLOW_REGISTRATION = env.bool("DJANGO_ACCOUNT_ALLOW_REGISTRATION", True)
ACCOUNT_AUTHENTICATION_METHOD = "username"
ACCOUNT_EMAIL_REQUIRED = True
Expand All @@ -271,12 +306,17 @@
SOCIALACCOUNT_AUTO_SIGNUP = True
ACCOUNT_DEFAULT_HTTP_PROTOCOL = "https"

# django-compressor
# ------------------------------------------------------------------------------

# =============================================================================
# DJANGO COMPRESSOR
# =============================================================================
INSTALLED_APPS += ["compressor"]
STATICFILES_FINDERS += ["compressor.finders.CompressorFinder"]
# django-rest-framework
# -------------------------------------------------------------------------------


# =============================================================================
# DRF
# =============================================================================
REST_FRAMEWORK = {
"DEFAULT_MODEL_SERIALIZER_CLASS": ("rest_framework.serializers.ModelSerializer",),
"DEFAULT_RENDERER_CLASSES": (
Expand Down Expand Up @@ -317,55 +357,69 @@
"DATE_FORMAT": "iso-8601",
"TIME_FORMAT": "iso-8601",
}

CORS_URLS_REGEX = r"^/api/.*$"

# Project specific settings
# ------------------------------------------------------------------------------
# these are used by the base model classes for validation

# =============================================================================
# PROJECT SPECIFIC SETTINGS
# =============================================================================
DECIMAL_PLACES = 4
MAX_IMAGE_HEIGHT = 4320
MAX_IMAGE_WIDTH = 7680
ORGANISATION_EMAIL = env("ORGANISATION_EMAIL", default="info@savannahghi.org")
ORGANISATION_NAME = env(
"ORGANISATION_NAME", default="Savannah Informatics Global Health Institute"
)
ORGANISATION_EMAIL = env("ORGANISATION_EMAIL", default="info@savannahghi.org")
ORGANISATION_PHONE = env("ORGANISATION_PHONE", default="+254790360360")

# used by the user model to assign a default organisation to a user during creation
# Used by the user model to assign a default organisation to a user during creation
DEFAULT_ORG_ID = env("DEFAULT_ORG_ID", default="4181df12-ca96-4f28-b78b-8e8ad88b25df")

# BigAutoField needs migration of existing data and either changes to
# dependencies or overriding dependencies
DEFAULT_AUTO_FIELD = "django.db.models.AutoField"

# mjml responsive emails
MJML_BACKEND_MODE = "cmd"
MJML_EXEC_CMD = "mjml"
MJML_CHECK_CMD_ON_STARTUP = True
WHITELISTED_DOMAINS = env.list(
"WHITELISTED_DOMAINS",
default=[
"savannahghi.org",
],
)

# =============================================================================
# MJML CONFIG (FOR RESPONSIVE EMAILS)
# =============================================================================
MJML_BACKEND_MODE = "cmd"
MJML_CHECK_CMD_ON_STARTUP = True
MJML_EXEC_CMD = "mjml"


# =============================================================================
# METADATA PROCESSORS REGISTRATION
# =============================================================================
METADATA_PROCESSORS = {
"fahari.sims.models.Question": [
"fahari.sims.metadata_processors.DenominatorValueExistIfDenominatorNonEditable",
"fahari.sims.metadata_processors.DependentQuestionExistsMetadataProcessor",
],
"fahari.sims.models.QuestionAnswer": [
"fahari.sims.metadata_processors.ConstraintsCheckMetadataProcessor",
],
}

# =============================================================================
# SIMS APP CONFIG
# =============================================================================
SIMS = {
"QUESTION_METADATA_PROCESSORS": {
"constraints": [
"fahari.sims.question_metadata_processors.ConstraintsQuestionMetadataProcessor",
],
"depends_on": [
"fahari.sims.question_metadata_processors.DependsOnQuestionMetadataProcessor",
],
},
"CONSTRAINT_CHECKERS": {
"max_value": [
"fahari.sims.question_answer_constraints_checkers.MaxValueConstraintChecker",
"fahari.sims.constraints_checkers.MaxValueConstraintChecker",
],
"min_value": [
"fahari.sims.question_answer_constraints_checkers.MinValueConstraintChecker",
"fahari.sims.constraints_checkers.MinValueConstraintChecker",
],
"numerator_max_value": [
"fahari.sims.constraints_checkers.NumeratorMaxValueConstraintChecker"
],
"optional": ["fahari.sims.constraints_checkers.RequiredByDefaultConstraintChecker"],
},
}
18 changes: 14 additions & 4 deletions data/kajiodo_svr_sheet_to_db_mappings_metadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,18 @@
"commodity": {
"column_index": 4,
"value_mappings": {
"Tenofovir/Lamivudine/Dolutegravir TLD": "Tenofovir/Lamivudine/Dolutegravir (TDF/3TC/DTG) FDC (300/300/50mg) FDC Tablets",
"Dolutegravir 50mg": "Dolutegravir(DTG) 50mg tabs",
"Zidovudine/Lamivudine AZT/3tC": "Zidovudine/Lamivudine (AZT/3TC) FDC (300/150mg) Tablets"
"Abacavir/Lamivudine ABC/3Tc 600/300 MG": "Abacavir/Lamivudine (ABC/3TC) 600mg/300mg FDC Tablets",
"Atazanavir/Ritonavir ATV/r": "Atazanavir/Ritonavir (ATV/r) 300/100mg Tablets",
"Atazanavir/Ritonavir ATV/r 300/100 MG": "Atazanavir/Ritonavir (ATV/r) 300/100mg Tablets",
"Dolutegravir 50mg": "Dolutegravir(DTG) 50mg tabs",
"Lamivudine Syrup 3Tc 10mg/ml": "Lamivudine (3TC) liquid 10mg/ml (240ml Bottles)",
"Tenofovir/Lamivudine TDF/3Tc 300/300 MG": "Tenofovir/Lamivudine (TDF/3TC) FDC (300/300mg) Tablets",
"Tenofovir/Lamivudine/Dolutegravir TLD": "Tenofovir/Lamivudine/Dolutegravir (TDF/3TC/DTG) FDC (300/300/50mg) FDC Tablets",
"Tenofovir/Lamivudine/Dolutegravir TLD 300/300/50 MG": "Tenofovir/Lamivudine/Dolutegravir (TDF/3TC/DTG) FDC (300/300/50mg) FDC Tablets",
"Tenofovir/Lamivudine TDF/3Tc": "Tenofovir/Lamivudine (TDF/3TC) FDC (300/300mg) Tablets",
"Tenovofivir 300mg+Lamuvidine 300mg+Dolutengravir 50mg": "Tenofovir/Lamivudine/Dolutegravir (TDF/3TC/DTG) FDC (300/300/50mg) FDC Tablets",
"Zidovudine/Lamivudine AZT/3tC": "Zidovudine/Lamivudine (AZT/3TC) FDC (300/150mg) Tablets",
"Zidovudine/Lamivudine AZT/3tC 300/150 MG": "Zidovudine/Lamivudine (AZT/3TC) FDC (300/150mg) Tablets"
},
"lookup": "name"
},
Expand All @@ -39,7 +48,8 @@
"value_mappings": {
"30": "Packs of 30s",
"60": "Packs of 60s",
"90": "Packs of 90s"
"90": "Packs of 90s",
"240": "240ml bottle"
},
"lookup": "name"
},
Expand Down
19 changes: 15 additions & 4 deletions data/nairobi_svr_sheet_to_db_mappings_metadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,20 @@
"commodity": {
"column_index": 6,
"value_mappings": {
"Abacavir/Lamivudine ABC/3Tc": "Abacavir/Lamivudine (ABC/3TC) 600mg/300mg FDC Tablets",
"Abacavir/Lamivudine ABC/3Tc 600/300 MG": "Abacavir/Lamivudine (ABC/3TC) 600mg/300mg FDC Tablets",
"Atazanavir/Ritonavir ATV/r": "Atazanavir/Ritonavir (ATV/r) 300/100mg Tablets",
"Atazanavir/Ritonavir ATV/r 300/100 MG": "Atazanavir/Ritonavir (ATV/r) 300/100mg Tablets",
"Dolutegravir 50mg": "Dolutegravir(DTG) 50mg tabs",
"Lamivudine Syrup 3Tc": "Lamivudine (3TC) liquid 10mg/ml (240ml Bottles)",
"Lamivudine Syrup 3Tc 10mg/ml": "Lamivudine (3TC) liquid 10mg/ml (240ml Bottles)",
"Tenofovir/Lamivudine TDF/3Tc 300/300 MG": "Tenofovir/Lamivudine (TDF/3TC) FDC (300/300mg) Tablets",
"Tenofovir/Lamivudine/Dolutegravir TLD": "Tenofovir/Lamivudine/Dolutegravir (TDF/3TC/DTG) FDC (300/300/50mg) FDC Tablets",
"Dolutegravir 50mg": "Dolutegravir(DTG) 50mg tabs",
"Zidovudine/Lamivudine AZT/3tC": "Zidovudine/Lamivudine (AZT/3TC) FDC (300/150mg) Tablets"
},
"Tenofovir/Lamivudine/Dolutegravir TLD 300/300/50 MG": "Tenofovir/Lamivudine/Dolutegravir (TDF/3TC/DTG) FDC (300/300/50mg) FDC Tablets",
"Tenofovir/Lamivudine TDF/3Tc": "Tenofovir/Lamivudine (TDF/3TC) FDC (300/300mg) Tablets",
"Zidovudine/Lamivudine AZT/3tC": "Zidovudine/Lamivudine (AZT/3TC) FDC (300/150mg) Tablets",
"Zidovudine/Lamivudine AZT/3tC 300/150 MG": "Zidovudine/Lamivudine (AZT/3TC) FDC (300/150mg) Tablets"
},
"lookup": "name"
},
"delivery_date": {
Expand All @@ -39,7 +49,8 @@
"value_mappings": {
"30": "Packs of 30s",
"60": "Packs of 60s",
"90": "Packs of 90s"
"90": "Packs of 90s",
"240": "240ml bottle"
},
"lookup": "name"
},
Expand Down
Loading

0 comments on commit 539a05d

Please sign in to comment.