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;