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