paul@676 | 1 | #!/usr/bin/env python |
paul@676 | 2 | |
paul@676 | 3 | """ |
paul@676 | 4 | A WebStack application for "playing" questionnaires designed by the |
paul@676 | 5 | questionnaire editor. |
paul@676 | 6 | """ |
paul@676 | 7 | |
paul@676 | 8 | import WebStack.Generic |
paul@676 | 9 | from XSLForms.Resources.WebResources import \ |
paul@676 | 10 | XSLFormsResource, input, output, resources, prepare_resources as xslforms_prepare_resources |
paul@676 | 11 | import os |
paul@676 | 12 | import libxml2dom |
paul@676 | 13 | from WebStack.Repositories.Directory import DirectoryRepository |
paul@676 | 14 | import time |
paul@676 | 15 | |
paul@676 | 16 | # Site map imports. |
paul@676 | 17 | |
paul@683 | 18 | from WebStack.Resources.ResourceMap import SimpleMap |
paul@683 | 19 | from WebStack.Resources.Selectors import EncodingSelector, PathSelector, AuthInfoSelector |
paul@676 | 20 | from WebStack.Resources.Static import DirectoryResource |
paul@683 | 21 | from WebStack.Resources.Login import LoginAuthenticator, LoginResource |
paul@683 | 22 | from WebStack.Resources.LoginRedirect import LoginRedirectAuthenticator, SiteLoginRedirectResource |
paul@676 | 23 | |
paul@676 | 24 | # Configuration settings. |
paul@676 | 25 | |
paul@676 | 26 | encoding = "utf-8" |
paul@676 | 27 | storage_directory = os.path.join(os.getcwd(), "Responses") |
paul@676 | 28 | |
paul@676 | 29 | # Resource classes. |
paul@676 | 30 | |
paul@676 | 31 | class QuestionnairePlayerResource(XSLFormsResource): |
paul@676 | 32 | |
paul@676 | 33 | "A resource providing a questionnaire editor." |
paul@676 | 34 | |
paul@676 | 35 | resource_dir = resources(__file__) |
paul@676 | 36 | template_resources = { |
paul@676 | 37 | "questionnaire" : output("player_template.xhtml") |
paul@676 | 38 | } |
paul@681 | 39 | init_resources = { |
paul@681 | 40 | "questionnaire" : input("player_template.xhtml") |
paul@681 | 41 | } |
paul@676 | 42 | document_resources = { |
paul@676 | 43 | "questions" : "questions.xml" |
paul@676 | 44 | } |
paul@681 | 45 | transform_resources = { |
paul@681 | 46 | "distill" : ["distill_responses.xsl"] |
paul@681 | 47 | } |
paul@676 | 48 | |
paul@676 | 49 | def select_activity(self, trans, form): |
paul@676 | 50 | form.set_activity("questionnaire") |
paul@676 | 51 | |
paul@676 | 52 | def create_document(self, trans, form): |
paul@676 | 53 | if XSLFormsResource.create_document(self, trans, form): |
paul@683 | 54 | admin = trans.get_user() == "admin" |
paul@683 | 55 | response = trans.get_virtual_path_info().split("/") |
paul@683 | 56 | |
paul@683 | 57 | repository = DirectoryRepository(storage_directory) |
paul@683 | 58 | if admin and len(response) > 1: |
paul@683 | 59 | key = response[1] |
paul@683 | 60 | try: |
paul@683 | 61 | questions_xml = repository.lock(key) |
paul@683 | 62 | try: |
paul@683 | 63 | questions = libxml2dom.parse(questions_xml) |
paul@683 | 64 | finally: |
paul@683 | 65 | repository.unlock(key) |
paul@683 | 66 | except KeyError: |
paul@683 | 67 | trans.set_response_code(404) |
paul@683 | 68 | trans.set_content_type(WebStack.Generic.ContentType("text/plain")) |
paul@683 | 69 | trans.get_response_stream().write("Questionnaire not found") |
paul@683 | 70 | raise WebStack.Generic.EndOfResponse |
paul@683 | 71 | else: |
paul@683 | 72 | questions_xml = self.prepare_document("questions") |
paul@683 | 73 | questions = libxml2dom.parse(questions_xml) |
paul@683 | 74 | |
paul@676 | 75 | doc = form.get_document() |
paul@676 | 76 | imported_questions = doc.importNode(questions.documentElement, 1) |
paul@676 | 77 | doc.replaceChild(imported_questions, doc.documentElement) |
paul@676 | 78 | |
paul@676 | 79 | def respond_to_input(self, trans, form): |
paul@676 | 80 | |
paul@676 | 81 | parameters = form.get_parameters() |
paul@676 | 82 | selectors = form.get_selectors() |
paul@676 | 83 | questionnaire = form.get_document() |
paul@676 | 84 | |
paul@676 | 85 | # Send a response according to certain parameters. |
paul@676 | 86 | # When saved, an XML version of the data is employed. |
paul@676 | 87 | |
paul@676 | 88 | if parameters.has_key("finish"): |
paul@676 | 89 | repository = DirectoryRepository(storage_directory) |
paul@676 | 90 | key = str(time.time()) |
paul@676 | 91 | output_filename = repository.lock(key, 1) |
paul@681 | 92 | distilled_output_filename = repository.lock("distilled-" + key, 1) |
paul@676 | 93 | try: |
paul@681 | 94 | distill_xsl = self.prepare_transform("distill") |
paul@681 | 95 | distilled = self.get_result(distill_xsl, questionnaire) |
paul@681 | 96 | distilled.toStream(open(distilled_output_filename, "wb"), "utf-8", prettyprint=1) |
paul@676 | 97 | questionnaire.toStream(open(output_filename, "wb"), "utf-8", prettyprint=1) |
paul@681 | 98 | questionnaire.documentElement.setAttribute("submitted", "true") |
paul@676 | 99 | finally: |
paul@676 | 100 | repository.unlock(key) |
paul@681 | 101 | repository.unlock("distilled-" + key) |
paul@676 | 102 | |
paul@683 | 103 | def create_output(self, trans, form): |
paul@683 | 104 | XSLFormsResource.create_output(self, trans, form, stylesheet_parameters=trans.get_attributes()) |
paul@683 | 105 | |
paul@683 | 106 | class QuestionnaireAdminResource(XSLFormsResource): |
paul@683 | 107 | |
paul@683 | 108 | "A resource providing a list of questionnaires." |
paul@683 | 109 | |
paul@683 | 110 | resource_dir = resources(__file__) |
paul@683 | 111 | template_resources = { |
paul@683 | 112 | "questionnaires" : output("admin_template.xhtml") |
paul@683 | 113 | } |
paul@683 | 114 | init_resources = { |
paul@683 | 115 | "questionnaires" : input("admin_template.xhtml") |
paul@683 | 116 | } |
paul@683 | 117 | |
paul@683 | 118 | def select_activity(self, trans, form): |
paul@683 | 119 | form.set_activity("questionnaires") |
paul@683 | 120 | |
paul@683 | 121 | def create_document(self, trans, form): |
paul@683 | 122 | if XSLFormsResource.create_document(self, trans, form): |
paul@683 | 123 | doc = form.get_document() |
paul@683 | 124 | questionnaires = doc.documentElement |
paul@683 | 125 | for filename in os.listdir(storage_directory): |
paul@683 | 126 | if filename[0].isdigit(): |
paul@683 | 127 | questionnaire = doc.createElement("questionnaire") |
paul@683 | 128 | questionnaire.setAttribute("name", filename) |
paul@683 | 129 | questionnaire.setAttribute("date-text", time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime(float(filename)))) |
paul@683 | 130 | questionnaires.appendChild(questionnaire) |
paul@683 | 131 | |
paul@683 | 132 | def create_output(self, trans, form): |
paul@683 | 133 | XSLFormsResource.create_output(self, trans, form, stylesheet_parameters=trans.get_attributes()) |
paul@683 | 134 | |
paul@676 | 135 | # Site map initialisation. |
paul@676 | 136 | |
paul@676 | 137 | def get_site(): |
paul@676 | 138 | |
paul@676 | 139 | "Return a simple Web site resource." |
paul@676 | 140 | |
paul@683 | 141 | # Authentication objects. |
paul@683 | 142 | |
paul@683 | 143 | secret_key="this_must_be_secret" |
paul@683 | 144 | credentials = [ |
paul@683 | 145 | ("admin", "123"), |
paul@683 | 146 | ] |
paul@683 | 147 | login_authenticator = LoginAuthenticator(secret_key, credentials) |
paul@683 | 148 | login_redirect_authenticator = LoginRedirectAuthenticator(secret_key) |
paul@683 | 149 | |
paul@676 | 150 | # Get the main resource and the directory used by the application. |
paul@676 | 151 | |
paul@676 | 152 | questionnaire_resource = QuestionnairePlayerResource() |
paul@683 | 153 | admin_resource = QuestionnaireAdminResource() |
paul@676 | 154 | directory = questionnaire_resource.resource_dir |
paul@676 | 155 | |
paul@683 | 156 | # Get some login resources. |
paul@683 | 157 | |
paul@683 | 158 | login_redirector = SiteLoginRedirectResource( |
paul@683 | 159 | authenticator=login_redirect_authenticator, |
paul@683 | 160 | login_url="login", |
paul@683 | 161 | app_url="", |
paul@683 | 162 | resource=SimpleMap({ |
paul@683 | 163 | "" : admin_resource, |
paul@683 | 164 | None : questionnaire_resource |
paul@683 | 165 | }) |
paul@683 | 166 | ) |
paul@683 | 167 | login_redirector.logout_parameter_name="logout" |
paul@683 | 168 | |
paul@683 | 169 | login_resource = LoginResource(login_authenticator) |
paul@683 | 170 | |
paul@676 | 171 | # Make a simple Web site. |
paul@676 | 172 | |
paul@683 | 173 | resource = SimpleMap({ |
paul@676 | 174 | # Static resources: |
paul@676 | 175 | "styles" : DirectoryResource(os.path.join(directory, "styles"), {"css" : "text/css"}), |
paul@683 | 176 | # Login resource: |
paul@683 | 177 | "login" : login_resource, |
paul@683 | 178 | # Administration resource: |
paul@683 | 179 | "admin" : login_redirector, |
paul@676 | 180 | # Main page: |
paul@676 | 181 | "" : questionnaire_resource |
paul@676 | 182 | }) |
paul@676 | 183 | |
paul@683 | 184 | return EncodingSelector(PathSelector(AuthInfoSelector(resource, login_redirect_authenticator)), encoding) |
paul@676 | 185 | |
paul@676 | 186 | # Resource preparation ahead of time - useful for making installations. |
paul@676 | 187 | |
paul@676 | 188 | def prepare_resources(): |
paul@683 | 189 | for cls in [QuestionnairePlayerResource, QuestionnaireAdminResource]: |
paul@676 | 190 | xslforms_prepare_resources(cls) |
paul@676 | 191 | |
paul@676 | 192 | # vim: tabstop=4 expandtab shiftwidth=4 |