Published 2021-03-03.
Time to read: 1 minutes.
This page is part of the
ancientWarmth collection.
Private, for my own use only. Do not publish!
survey Django App
Refer to my initial django-oscar notes.
models.py
"""Integrates with surveyjs.io"""
import json
import sys
from inspect import cleandoc
from pathlib import Path
from django.db import models
from django.utils import timezone
def default_questionnaire():
return '{ "pages": [ { "name": "page1" } ] }'
def group_by(data):
from collections import defaultdict
res = defaultdict(list)
for item in data:
for key, value in item:
res[key].append(value)
result = [ {key:value} for key, value in res.items() ]
return result
class SurveyQuestionnaire(models.Model):
name = models.CharField(max_length=50, unique=True)
questions_json = models.JSONField(default=default_questionnaire)
@classmethod
def from_file(cls, file: str):
return cls.from_path(Path(file))
@classmethod
def from_path(cls, path: Path):
try:
with open(path) as file:
dictionary = json.load(file)
string = json.dumps(dictionary)
name = dictionary['pages'][0]['name']
# print(f"{path}: {name}")
questionnaire = cls.objects.update_or_create(
name = name,
questions_json = string,
)
return questionnaire
except Exception as exception:
print(f"SurveyQuestionnaire.from_path: {exception}")
sys.exit(1)
@property
def questions(self) -> list:
"""@return list of question entriess in json format"""
return self.questions_json
@property
def keys(self) -> list:
"""@return ['birthdate', 'compareEpsomDeadSea', 'usedBathSalts' ] """
return [ x['name'] for x in self.questions ]
def analytics(self) -> str:
query_set = list(SurveyResult.objects.filter(questionnaire=self))
data = [ list(x.result_dict.items()) for x in query_set ]
result = group_by(data)
return result
def entry_for(self, name) -> str:
"""@return question entry as a dict
Example: {'type': 'text', 'name': 'birthdate', 'title': 'When were you born?', 'inputType': 'date'}
"""
return [ x for x in self.questions if name==x['name'] ][0]
def question_for(self, name) -> str:
"""@return 'When were you born?' """
return self.entry_for(name)['title']
def type_for(self, name) -> str:
"""@return 'text'"""
return self.entry_for(name)['type']
def inputType_for(self, name) -> str:
"""@return 'date'"""
return self.entry_for(name)['inputType']
def __str__(self):
return f"{self.name}; {len(self.questions)} questions"
class SurveyQuestionnaires(models.Model):
surveys = models.ForeignKey(to=SurveyQuestionnaire, null=True, on_delete=models.CASCADE)
@classmethod
def from_files(cls):
"""Saves all json into db"""
for path in Path("survey/static/questions/").iterdir():
if path.is_file and path.name.endswith(".json"):
SurveyQuestionnaire.from_path(path)
class Meta:
ordering = ['pk']
def true_false_yes_no(token: str) -> str:
"""Convert True/False to Yes/No"""
if token == "True":
return "Yes"
if token == "False":
return "No"
return token
class SurveyResult(models.Model):
created = models.DateTimeField(default=timezone.now)
questionnaire = models.ForeignKey(on_delete=models.CASCADE, to=SurveyQuestionnaire)
result_json = models.JSONField()
user_id = models.EmailField()
@property
def as_html(self):
lines = [ cleandoc(f"""<b>{self.question_for(key=x)}</b><br>
{true_false_yes_no(self.result_dict[x])}<br>
""") for x in self.result_dict ]
return "<br>\n".join(lines)
@property
def as_text(self):
lines = [ f"{self.question_for(key=x)}: {self.result_dict[x]}" for x in self.result_dict ]
return "\n".join(lines)
@property
def result_dict(self) -> dict:
return json.loads(self.result_json)
@property
def keys(self) -> list:
return list(self.result_dict.keys())
def email(self, email_address):
from django.core.mail import send_mail
# result is set to 1 for success, 0 for failure
result = send_mail(
subject = 'Your AncientWarmth Survey Response',
message = self.as_text,
html_message = self.as_html,
from_email = 'mslinn@ancientwarmth.com',
recipient_list = [ email_address ],
)
print(f"Result code for sending email={result}")
def inputtype_for(self, key):
"""@return 'date'"""
type_value = self.question_for(key).entry_for(key)['inputType']
return type_value
def question_for(self, key):
"""@return question keys"""
question = self.questionnaire.question_for(key)
return question
def result_value_of(self, key: str):
return self.result_dict[key]
def type_for(self, key):
"""@return 'text'"""
type_value = self.question_for(key).entry_for(key)['type']
return type_value
def __str__(self):
return f"{self.user_id} reponse to {self.questionnaire.name} at {self.created}"
Shell
$ ./manage.py makemigrations survey Migrations for 'pricing': pricing/migrations/0001_initial.py - Create model Bather - Create model Category - Create model Chemical - Create model Color - Create model Design - Create model Designer - Create model EssentialOil - Create model Scent - Create model Rating - Create model Package - Create model Mixture - Create model Ingredient - Add field designer to design - Add field mixture to design
Shell
$ ./manage.py sqlmigrate pricing 0001 BEGIN; -- -- Create model Bather -- CREATE TABLE "aw_pricing_bather" ("user_id" integer NOT NULL PRIMARY KEY); -- -- Create model Category -- CREATE TABLE "aw_pricing_category" ("id" serial NOT NULL PRIMARY KEY, "name" text NOT NULL); -- -- Create model Chemical -- CREATE TABLE "aw_pricing_chemical" ("id" serial NOT NULL PRIMARY KEY, "alt_names" text NULL, "full_description" text NOT NULL, "name" text NOT NULL, "short_description" text NOT NULL); -- -- Create model Color -- CREATE TABLE "aw_pricing_color" ("id" serial NOT NULL PRIMARY KEY, "kid_friendly" boolean NOT NULL, "name" text NOT NULL); -- -- Create model Design -- CREATE TABLE "aw_pricing_design" ("id" serial NOT NULL PRIMARY KEY, "created" timestamp with time zone NOT NULL, "graphics" varchar(100) NOT NULL, "name" text NOT NULL, "category_id" integer NOT NULL UNIQUE, "color_id" integer NOT NULL UNIQUE); -- -- Create model Designer -- CREATE TABLE "aw_pricing_designer" ("user_id" integer NOT NULL PRIMARY KEY); -- -- Create model EssentialOil -- CREATE TABLE "aw_pricing_essentialoil" ("id" serial NOT NULL PRIMARY KEY, "kid_friendly" boolean NOT NULL, "full_description" text NOT NULL, "name" text NOT NULL, "short_description" text NOT NULL); -- -- Create model Scent -- CREATE TABLE "aw_pricing_scent" ("id" serial NOT NULL PRIMARY KEY, "ml_per_liter" integer NOT NULL CHECK ("ml_per_liter" >= 0), "design_id" integer NOT NULL, "essential_oil_id" integer NOT NULL UNIQUE); -- -- Create model Rating -- CREATE TABLE "aw_pricing_rating" ("id" serial NOT NULL PRIMARY KEY, "value" integer NOT NULL CHECK ("value" >= 0), "bather_id" integer NOT NULL, "design_id" integer NOT NULL); -- -- Create model Package -- CREATE TABLE "aw_pricing_package" ("id" serial NOT NULL PRIMARY KEY, "grams" integer NOT NULL CHECK ("grams" >= 0), "bather_id" integer NOT NULL, "design_id" integer NOT NULL UNIQUE); -- -- Create model Mixture -- CREATE TABLE "aw_pricing_mixture" ("id" serial NOT NULL PRIMARY KEY, "created" timestamp with time zone NOT NULL, "name" text NOT NULL, "designer_id" integer NOT NULL UNIQUE); -- -- Create model Ingredient -- CREATE TABLE "aw_pricing_ingredient" ("id" serial NOT NULL PRIMARY KEY, "parts" integer NOT NULL CHECK ("parts" >= 0), "chemical_id" integer NOT NULL UNIQUE, "mixture_id" integer NOT NULL); -- -- Add field designer to design -- ALTER TABLE "aw_pricing_design" ADD COLUMN "designer_id" integer NOT NULL CONSTRAINT "aw_pricing_design_designer_id_0be65681_fk_aw_pricin" REFERENCES "aw_pricing_designer"("user_id") DEFERRABLE INITIALLY DEFERRED; SET CONSTRAINTS "aw_pricing_design_designer_id_0be65681_fk_aw_pricin" IMMEDIATE; -- -- Add field mixture to design -- ALTER TABLE "aw_pricing_design" ADD COLUMN "mixture_id" integer NOT NULL UNIQUE CONSTRAINT "aw_pricing_design_mixture_id_93091820_fk_aw_pricing_mixture_id" REFERENCES "aw_pricing_mixture"("id") DEFERRABLE INITIALLY DEFERRED; SET CONSTRAINTS "aw_pricing_design_mixture_id_93091820_fk_aw_pricing_mixture_id" IMMEDIATE; ALTER TABLE "aw_pricing_bather" ADD CONSTRAINT "aw_pricing_bather_user_id_6390db20_fk_auth_user_id" FOREIGN KEY ("user_id") REFERENCES "auth_user" ("id") DEFERRABLE INITIALLY DEFERRED; ALTER TABLE "aw_pricing_design" ADD CONSTRAINT "aw_pricing_design_category_id_c794e2d1_fk_aw_pricin" FOREIGN KEY ("category_id") REFERENCES "aw_pricing_category" ("id") DEFERRABLE INITIALLY DEFERRED; ALTER TABLE "aw_pricing_design" ADD CONSTRAINT "aw_pricing_design_color_id_a8f9e074_fk_aw_pricing_color_id" FOREIGN KEY ("color_id") REFERENCES "aw_pricing_color" ("id") DEFERRABLE INITIALLY DEFERRED; ALTER TABLE "aw_pricing_designer" ADD CONSTRAINT "aw_pricing_designer_user_id_8b9aabdf_fk_auth_user_id" FOREIGN KEY ("user_id") REFERENCES "auth_user" ("id") DEFERRABLE INITIALLY DEFERRED; ALTER TABLE "aw_pricing_scent" ADD CONSTRAINT "aw_pricing_scent_design_id_2ab15a89_fk_aw_pricing_design_id" FOREIGN KEY ("design_id") REFERENCES "aw_pricing_design" ("id") DEFERRABLE INITIALLY DEFERRED; ALTER TABLE "aw_pricing_scent" ADD CONSTRAINT "aw_pricing_scent_essential_oil_id_700d88e4_fk_aw_pricin" FOREIGN KEY ("essential_oil_id") REFERENCES "aw_pricing_essentialoil" ("id") DEFERRABLE INITIALLY DEFERRED; CREATE INDEX "aw_pricing_scent_design_id_2ab15a89" ON "aw_pricing_scent" ("design_id"); ALTER TABLE "aw_pricing_rating" ADD CONSTRAINT "aw_pricing_rating_bather_id_c8957e24_fk_aw_pricin" FOREIGN KEY ("bather_id") REFERENCES "aw_pricing_bather" ("user_id") DEFERRABLE INITIALLY DEFERRED; ALTER TABLE "aw_pricing_rating" ADD CONSTRAINT "aw_pricing_rating_design_id_17651e3b_fk_aw_pricing_design_id" FOREIGN KEY ("design_id") REFERENCES "aw_pricing_design" ("id") DEFERRABLE INITIALLY DEFERRED; CREATE INDEX "aw_pricing_rating_bather_id_c8957e24" ON "aw_pricing_rating" ("bather_id"); CREATE INDEX "aw_pricing_rating_design_id_17651e3b" ON "aw_pricing_rating" ("design_id"); ALTER TABLE "aw_pricing_package" ADD CONSTRAINT "aw_pricing_package_bather_id_d0441e4f_fk_aw_pricin" FOREIGN KEY ("bather_id") REFERENCES "aw_pricing_bather" ("user_id") DEFERRABLE INITIALLY DEFERRED; ALTER TABLE "aw_pricing_package" ADD CONSTRAINT "aw_pricing_package_design_id_90fc01b7_fk_aw_pricing_design_id" FOREIGN KEY ("design_id") REFERENCES "aw_pricing_design" ("id") DEFERRABLE INITIALLY DEFERRED; CREATE INDEX "aw_pricing_package_bather_id_d0441e4f" ON "aw_pricing_package" ("bather_id"); ALTER TABLE "aw_pricing_mixture" ADD CONSTRAINT "aw_pricing_mixture_designer_id_be94523d_fk_aw_pricin" FOREIGN KEY ("designer_id") REFERENCES "aw_pricing_designer" ("user_id") DEFERRABLE INITIALLY DEFERRED; ALTER TABLE "aw_pricing_ingredient" ADD CONSTRAINT "aw_pricing_ingredien_chemical_id_2a2fd6ae_fk_aw_pricin" FOREIGN KEY ("chemical_id") REFERENCES "aw_pricing_chemical" ("id") DEFERRABLE INITIALLY DEFERRED; ALTER TABLE "aw_pricing_ingredient" ADD CONSTRAINT "aw_pricing_ingredien_mixture_id_cd5b0b60_fk_aw_pricin" FOREIGN KEY ("mixture_id") REFERENCES "aw_pricing_mixture" ("id") DEFERRABLE INITIALLY DEFERRED; CREATE INDEX "aw_pricing_ingredient_mixture_id_cd5b0b60" ON "aw_pricing_ingredient" ("mixture_id"); CREATE INDEX "aw_pricing_design_designer_id_0be65681" ON "aw_pricing_design" ("designer_id"); COMMIT;