def update(self): if redirectOnSurveyUpdate(self.request): return if self.request.environ["REQUEST_METHOD"] == "POST": return self._update() context = aq_inner(self.context) module = self.request.survey.restrictedTraverse( self.context.zodb_path.split("/")) self.module = module if ((IProfileQuestion.providedBy(module) and context.depth == 2) or (ICustomRisksModule.providedBy(module) and self.phase == 'actionplan')): next = FindNextQuestion(context, filter=self.question_filter) if next is None: if self.phase == 'identification': url = "%s/actionplan" % self.request.survey.absolute_url() elif self.phase == 'evaluation': url = "%s/actionplan" % self.request.survey.absolute_url() elif self.phase == 'actionplan': url = "%s/report" % self.request.survey.absolute_url() else: url = QuestionURL(self.request.survey, next, phase=self.phase) return self.request.response.redirect(url) else: return self._update()
def update(self): if redirectOnSurveyUpdate(self.request): return if self.request.environ["REQUEST_METHOD"] == "POST": return self._update() context = aq_inner(self.context) module = self.request.survey.restrictedTraverse( self.context.zodb_path.split("/")) self.module = module if ( (IProfileQuestion.providedBy(module) and context.depth == 2) or (ICustomRisksModule.providedBy(module) and self.phase == 'actionplan') ): next = FindNextQuestion(context, filter=self.question_filter) if next is None: if self.phase == 'identification': url = "%s/actionplan" % self.request.survey.absolute_url() elif self.phase == 'evaluation': url = "%s/actionplan" % self.request.survey.absolute_url() elif self.phase == 'actionplan': url = "%s/report" % self.request.survey.absolute_url() else: url = QuestionURL(self.request.survey, next, phase=self.phase) return self.request.response.redirect(url) else: return self._update()
def BuildSurveyTree(survey, profile, dbsession, old_session=None): """(Re)build the survey SQL tree. The existing tree for the session is deleted before a new tree is created. :param survey: survey to build tree for :type survey: :py:class:`euphorie.content.survey.Survey` :param profile: desired profile to be used for the tree :type profile: dictionary :param dbsession: session to build tree in. Defaults to currently active session. :type dbsession: :py:class:`euphorie.client.model.SurveySession` """ dbsession.reset() for child in survey.values(): if ICustomRisksModule.providedBy(child): AddToTree(dbsession, child) # Now copy over the custom risks risks = get_custom_risks(old_session) if risks: # find the module that holds the custom risks modules = (Session.query(model.Module).filter( sql.and_( model.Module.session_id == dbsession.id, model.Module.module_id == child.id, )).all()) # there should only ever be 1 result if modules: for risk in risks: modules[0].addChild(risk) elif IProfileQuestion.providedBy(child): # Safeguard against double adding of profile questions existing = [ getattr(item, "module_id") for item in dbsession.children() ] if child.id in existing: continue p = profile.get(child.id) if not p: continue if isinstance(p, list): profile_question = AddToTree( dbsession, child, title=child.title, profile_index=-1, skip_children=True, ) for (index, title) in enumerate(p): AddToTree(profile_question, child, title=title, profile_index=index) # If we get a bool, it will be True, because of `if not p` above # Simply add the profile to the tree, don't care about locations elif isinstance(p, bool): AddToTree(dbsession, child, title=child.title) else: AddToTree(dbsession, child)
def BuildSurveyTree(survey, profile={}, dbsession=None, old_session=None): """(Re)build the survey SQL tree. The existing tree for the session is deleted before a new tree is created. :param survey: survey to build tree for :type survey: :py:class:`euphorie.content.survey.Survey` :param profile: desired profile to be used for the tree :type profile: dictionary :param dbsession: session to build tree in. Defaults to currently active session. :type dbsession: :py:class:`euphorie.client.model.SurveySession` """ if dbsession is None: dbsession = SessionManager.session dbsession.reset() for child in survey.values(): if ICustomRisksModule.providedBy(child): AddToTree(dbsession, child) # Now copy over the custom risks risks = get_custom_risks(old_session) if risks: # find the module that holds the custom risks modules = Session.query(model.Module).filter(sql.and_( model.Module.session_id==dbsession.id, model.Module.module_id==child.id)).all() # there should only ever be 1 result if modules: for risk in risks: modules[0].addChild(risk) elif IProfileQuestion.providedBy(child): # Safeguard against double adding of profile questions existing = [ getattr(item, 'module_id') for item in dbsession.children()] if child.id in existing: continue p = profile.get(child.id) if not p: continue if isinstance(p, list): profile_question = AddToTree( dbsession, child, title=child.title, profile_index=-1, skip_children=True) for (index, title) in enumerate(p): AddToTree( profile_question, child, title=title, profile_index=index) # If we get a bool, it will be True, because of `if not p` above # Simply add the profile to the tree, don't care about locations elif isinstance(p, bool): AddToTree( dbsession, child, title=child.title) else: AddToTree(dbsession, child)
def save_and_continue(self, module): """We received a POST request. Submit the form and figure out where to go next. """ context = aq_inner(self.context) reply = self.request.form _next = reply.get("next", None) # In Safari browser we get a list if isinstance(_next, list): _next = _next.pop() if module.optional: if "skip_children" in reply: context.skip_children = reply.get("skip_children") context.postponed = False else: context.postponed = True self.context.session.touch() if _next == "previous": if self.previous_question is None: # We ran out of questions, step back to intro page url = "%s/@@identification" % self.context.aq_parent.absolute_url() self.request.response.redirect(url) return self.request.response.redirect(self.previous_question_url) return else: if ICustomRisksModule.providedBy(module): if _next == "add_custom_risk": self.add_custom_risk() notify(CustomRisksModifiedEvent(self.context)) risk_id = self.context.children().count() url = "{parent_url}/{risk_id}/@@identification".format( parent_url=self.context.absolute_url(), risk_id=risk_id, ) return self.request.response.redirect(url) else: # We ran out of questions, proceed to the action plan return self.request.response.redirect(self.next_phase_url) if self.next_question is None: # We ran out of questions, proceed to the action plan return self.request.response.redirect(self.next_phase_url) self.request.response.redirect(self.next_question_url)
def __call__(self): # Render the page only if the user has edit rights, # otherwise redirect to the start page of the session. if not self.webhelpers.can_edit_session: return self.request.response.redirect( self.context.aq_parent.absolute_url() + "/@@start" ) if self.webhelpers.redirectOnSurveyUpdate(): return context = aq_inner(self.context) utils.setLanguage(self.request, self.survey, self.survey.language) module = self.webhelpers.traversed_session.restrictedTraverse( context.zodb_path.split("/") ) if self.request.environ["REQUEST_METHOD"] == "POST": return self.save_and_continue(module) if IProfileQuestion.providedBy(module) and context.depth == 2: if self.next_question is None: url = self.next_phase_url else: url = self.next_question_url return self.request.response.redirect(url) self.title = module.title self.module = module number_files = 0 for i in range(1, 5): number_files += getattr(self.module, "file{0}".format(i), None) and 1 or 0 self.has_files = number_files > 0 self.next_is_actionplan = not self.next_question if ICustomRisksModule.providedBy(module): template = ViewPageTemplateFile( "templates/module_identification_custom.pt" ).__get__(self, "") else: template = self.template return template()
def save_and_continue(self, module): """ We received a POST request. Submit the form and figure out where to go next. """ context = aq_inner(self.context) reply = self.request.form if module.optional: if "skip_children" in reply: context.skip_children = reply.get("skip_children") context.postponed = False else: context.postponed = True SessionManager.session.touch() if reply["next"] == "previous": next = FindPreviousQuestion(context, filter=self.question_filter) if next is None: # We ran out of questions, step back to intro page url = "%s/identification" % self.request.survey.absolute_url() self.request.response.redirect(url) return else: if ICustomRisksModule.providedBy(module): if not context.skip_children: # The user will now be allowed to create custom # (user-defined) risks. url = "%s/customization/%d" % ( self.request.survey.absolute_url(), int(self.context.path)) return self.request.response.redirect(url) else: # We ran out of questions, proceed to the evaluation url = "%s/actionplan" % self.request.survey.absolute_url() return self.request.response.redirect(url) next = FindNextQuestion(context, filter=self.question_filter) if next is None: # We ran out of questions, proceed to the evaluation url = "%s/actionplan" % self.request.survey.absolute_url() return self.request.response.redirect(url) url = QuestionURL(self.request.survey, next, phase="identification") self.request.response.redirect(url)
def update(self): if redirectOnSurveyUpdate(self.request): return context = aq_inner(self.context) module = self.request.survey.restrictedTraverse( context.zodb_path.split("/")) if self.request.environ["REQUEST_METHOD"] == "POST": self.save_and_continue(module) else: if IProfileQuestion.providedBy(module) and context.depth == 2: next = FindNextQuestion(context, filter=self.question_filter) if next is None: url = "%s/actionplan" % self.request.survey.absolute_url() else: url = QuestionURL(self.request.survey, next, phase=self.phase) return self.request.response.redirect(url) elif ICustomRisksModule.providedBy(module) \ and not self.context.skip_children \ and len(self.get_custom_risks()): url = "%s/customization/%d" % ( self.request.survey.absolute_url(), int(self.context.path)) return self.request.response.redirect(url) self.tree = getTreeData(self.request, context, filter=model.NO_CUSTOM_RISKS_FILTER) self.title = module.title self.module = module number_files = 0 for i in range(1, 5): number_files += getattr(self.module, 'file{0}'.format(i), None) and 1 or 0 self.has_files = number_files > 0 self.next_is_actionplan = not FindNextQuestion( context, filter=self.question_filter) super(IdentificationView, self).update()
def update(self): if redirectOnSurveyUpdate(self.request): return context = aq_inner(self.context) module = self.request.survey.restrictedTraverse( context.zodb_path.split("/")) if self.request.environ["REQUEST_METHOD"] == "POST": self.save_and_continue(module) else: if IProfileQuestion.providedBy(module) and context.depth == 2: next = FindNextQuestion(context, filter=self.question_filter) if next is None: url = "%s/actionplan" % self.request.survey.absolute_url() else: url = QuestionURL(self.request.survey, next, phase=self.phase) return self.request.response.redirect(url) elif ICustomRisksModule.providedBy(module) \ and not self.context.skip_children \ and len(self.get_custom_risks()): url = "%s/customization/%d" % ( self.request.survey.absolute_url(), int(self.context.path)) return self.request.response.redirect(url) self.tree = getTreeData( self.request, context, filter=model.NO_CUSTOM_RISKS_FILTER) self.title = module.title self.module = module number_files = 0 for i in range(1, 5): number_files += getattr( self.module, 'file{0}'.format(i), None) and 1 or 0 self.has_files = number_files > 0 self.next_is_actionplan = not FindNextQuestion( context, filter=self.question_filter) super(IdentificationView, self).update()
def use_node(elem): # Recursively find the nodes that are not disabled global use_nodes # Skip this elem? # If this is an optional module, check the "postponed" flag. # As long as the optional question has not been answered, skip # showing its children. # Only a "Yes" answer on the module will be considered as "do # not skip children" zodb_elem = self.context.aq_parent.restrictedTraverse( elem.zodb_path.split("/")) if getattr(zodb_elem, "optional", False): if (elem.postponed in (True, None) or elem.skip_children ) and not ICustomRisksModule.providedBy(zodb_elem): return children = [ x for x in s_paths if x.path.startswith(elem.path) and len(x.path) == len(elem.path) + 3 ] if children: for child in children: use_node(child) else: use_nodes.append(elem.path)
def getTreeData(request, context, phase="identification", filter=None): """Assemble data for a navigation tree This function returns a nested dictionary structure reflecting the elements for a navigation tree. The tree will all sibling questions of the current context, the current module and all its module siblings, its parents up to the root module, and all modules at the root level. Optionally a SQLAlchemy clause can be provided, which will be used to filter items shown in the tree. The current item and its parents will always be shown. Each element is reflect as a dictionary item with the following keys: - id: the SQL object id - type: the SQL object type - number: a human presentable numbering of the item - title: the object title - current: boolean indicating if this is the current context or its direct parent module - active: boolean indicating if this is a parent node of the current context - class: CSS classes to use for this node - children: a list of child nodes (in the right order) - url: URL for this item """ query = Session.query(model.SurveyTreeItem) title_custom_risks = utils.get_translated_custom_risks_title(request) root = context parents = [] while root.parent_id is not None: parent = query.get(root.parent_id) parents.append(parent) root = parent parents.reverse() base_url = "%s/%s/" % (request.survey.absolute_url(), phase) def morph(obj): info = {'id': obj.id, 'number': obj.number, 'title': obj.title, 'active': obj.path != context.path and context.path.startswith(obj.path), 'current': (obj.path == context.path), 'current_parent': (obj.path == context.path[:-3]), 'path': context.path, 'children': [], 'type': obj.type, 'leaf_module': False, 'depth': obj.depth, 'url': base_url + "/".join(obj.short_path) } cls = [] for key in ["active", "current", "current_parent"]: if info[key]: cls.append(key) if obj.postponed: cls.append("postponed") else: if isinstance(obj, model.Risk): if obj.identification: cls.append("answered") if obj.identification == "no": cls.append("risk") info["class"] = cls and " ".join(cls) or None return info # Result is always pointing to the level *above* the current level. # At the end it will be the virtual tree root result = {'children': [], 'leaf_module': False, 'current': False, 'id': None, 'title': None} result["class"] = None children = [] for obj in context.siblings(filter=filter): info = morph(obj) if obj.type != 'risk' and obj.zodb_path.find('custom-risks') > -1: info['title'] = title_custom_risks children.append(info) result["children"] = children if isinstance(context, model.Module): if not context.skip_children: # For modules which do not skip children, include the list of # children. me = first(lambda x: x["current"], result["children"]) children = [] for obj in context.children(filter=filter): info = morph(obj) # XXX: The check for SurveySession is due to Euphorie tests which don't # have a proper canonical ZODB survey object and don't test the # following OiRA-specific code. if obj.depth == 2 \ and not getattr(obj, 'is_custom_risk', False) \ and not isinstance(request.survey, SurveySession): module = request.survey.restrictedTraverse(obj.zodb_path.split('/')) if IProfileQuestion.providedBy(module) and \ not ICustomRisksModule.providedBy(aq_parent(module)): info['type'] = u'location' info['children'] = [ morph(sub) for sub in obj.children(filter=filter)] children.append(info) me["children"] = children types = set([c["type"] for c in me["children"]]) me["leaf_module"] = "risk" in types elif isinstance(context, model.Risk): # For a risk we also want to include all siblings of its module parent parent = parents.pop() siblings = [] for obj in parent.siblings(model.Module, filter=filter): info = morph(obj) if obj.zodb_path.find('custom-risks') > -1: info['title'] = title_custom_risks siblings.append(info) myparent = first(lambda x: x["active"], siblings) myparent["children"] = result["children"] myparent["leaf_module"] = True result["children"] = siblings if parents: # Add all parents up to the root while len(parents) > 1: parent = parents.pop() new = morph(parent) if isinstance(parent, model.Module) and parent.depth == 2: module = request.survey.restrictedTraverse(parent.zodb_path.split('/')) if IProfileQuestion.providedBy(module) and \ not ICustomRisksModule.providedBy(aq_parent(module)): new['type'] = u'location' new["children"] = result["children"] result["children"] = [new] # Finally list all modules at the root level parent = parents.pop() roots = [] for obj in parent.siblings(model.Module, filter=filter): info = morph(obj) if obj.zodb_path.find('custom-risks') > -1: info['title'] = title_custom_risks roots.append(info) myroot = first(lambda x: x["active"], roots) myroot["children"] = result["children"] result["children"] = roots return result
def update(self): self.verify_view_permission() super(MeasuresOverview, self).update() lang = getattr(self.request, "LANGUAGE", "en") if "-" in lang: lang = lang.split("-")[0] now = datetime.now() next_month = datetime(now.year, (now.month + 1) % 12 or 12, 1) month_after_next = datetime(now.year, (now.month + 2) % 12 or 12, 1) self.months = [] self.months.append(now.strftime("%b")) self.months.append(next_month.strftime("%b")) self.months.append(month_after_next.strftime("%b")) self.monthstrings = [ translate( PloneLocalesMessageFactory("month_{0}_abbr".format( month.lower()), default=month), target_language=lang, ) for month in self.months ] query = (Session.query( Module, Risk, ActionPlan).select_from(Module).filter( sql.and_( Module.session == self.session, Module.profile_index > -1)).filter(sql.not_(SKIPPED_PARENTS)).filter( sql.or_( MODULE_WITH_RISK_OR_TOP5_FILTER, RISK_PRESENT_OR_TOP5_FILTER)).join( Risk, Risk.parent_id == Module.id).join( ActionPlan, ActionPlan.risk_id == Risk.id).order_by( sql.case(value=Risk.priority, whens={ "high": 0, "medium": 1 }, else_=2), Risk.path, )) measures = [ t for t in query.all() if ((t[-1].planning_end is not None and t[-1].planning_end.strftime("%b") in self.months) and (t[-1].planning_start is not None or t[-1].responsible is not None or t[-1].prevention_plan is not None or t[-1].requirements is not None or t[-1].budget is not None or t[-1].action_plan is not None)) ] modulesdict = defaultdict(lambda: defaultdict(list)) for module, risk, action in measures: if "custom-risks" not in risk.zodb_path: risk_obj = self.context.restrictedTraverse( risk.zodb_path.split("/")) title = risk_obj and risk_obj.problem_description or risk.title else: title = risk.title modulesdict[module][risk.priority or "low"].append({ "title": title, "description": action.action_plan, "months": [ action.planning_end and action.planning_end.month == m.month for m in [now, next_month, month_after_next] ], }) main_modules = {} for module, risks in sorted(modulesdict.items(), key=lambda m: m[0].zodb_path): module_obj = self.context.restrictedTraverse( module.zodb_path.split("/")) if (IProfileQuestion.providedBy(module_obj) or ICustomRisksModule.providedBy(module_obj) or module.depth >= 3): path = module.path[:6] else: path = module.path[:3] if path in main_modules: for prio in risks.keys(): if prio in main_modules[path]["risks"]: main_modules[path]["risks"][prio].extend(risks[prio]) else: main_modules[path]["risks"][prio] = risks[prio] else: title = module.title number = module.number if "custom-risks" in module.zodb_path: num_elems = number.split(".") number = ".".join(["Ω"] + num_elems[1:]) title = api.portal.translate(_(title)) main_modules[path] = { "name": title, "number": number, "risks": risks } self.modules = [] for key in sorted(main_modules.keys()): self.modules.append(main_modules[key])
def update(self): super(MeasuresOverview, self).update() lang = getattr(self.request, "LANGUAGE", "en") if "-" in lang: lang = lang.split("-")[0] if self.session is not None and self.session.title != ( callable(getattr(self.context, "Title", None)) and self.context.Title() or "" ): self.session_title = self.session.title else: self.session_title = ( callable(getattr(self.context, "Title", None)) and self.context.Title() or "" ) today = date.today() this_month = date(today.year, today.month, 1) self.label_page = translate( _(u"label_page", default=u"Page"), target_language=lang ) self.label_page_of = translate( _(u"label_page_of", default=u"of"), target_language=lang ) def get_next_month(this_month): month = this_month.month + 1 year = this_month.year if month == 13: month = 1 year = year + 1 return date(year, month, 1) next_month = get_next_month(this_month) month_after_next = get_next_month(next_month) self.months = [] self.months.append(today.strftime("%b")) self.months.append(next_month.strftime("%b")) self.months.append(month_after_next.strftime("%b")) self.monthstrings = [ translate( PloneLocalesFactory( "month_{0}_abbr".format(month.lower()), default=month, ), target_language=lang, ) for month in self.months ] query = ( Session.query(model.Module, model.Risk, model.ActionPlan) .select_from(model.Module) .filter( sql.and_( model.Module.session == self.session, model.Module.profile_index > -1, ) ) .filter(sql.not_(model.SKIPPED_PARENTS)) .filter( sql.or_( model.MODULE_WITH_RISK_OR_TOP5_FILTER, model.RISK_PRESENT_OR_TOP5_FILTER, ) ) .join(model.Risk, model.Risk.parent_id == model.Module.id) .join(model.ActionPlan, model.ActionPlan.risk_id == model.Risk.id) .order_by( sql.case( value=model.Risk.priority, whens={"high": 0, "medium": 1}, else_=2 ), model.Risk.path, ) ) measures = [ t for t in query.all() if ( ( ( t[-1].planning_start is not None and t[-1].planning_start.strftime("%b") in self.months ) or ( t[-1].planning_end is not None and t[-1].planning_end.strftime("%b") in self.months ) or ( t[-1].planning_start is not None and ( t[-1].planning_end is None or t[-1].planning_end >= month_after_next ) and t[-1].planning_start <= this_month ) ) and t[1].identification not in ("n/a", "yes") and ( t[-1].responsible is not None or t[-1].requirements is not None or t[-1].budget is not None or t[-1].action is not None ) ) ] modulesdict = defaultdict(lambda: defaultdict(list)) for module, risk, action in measures: if "custom-risks" not in risk.zodb_path: risk_obj = self.survey.restrictedTraverse(risk.zodb_path.split("/")) title = risk_obj and risk_obj.problem_description or risk.title else: title = risk.title classes = [] start_month = action.planning_start and date( action.planning_start.year, action.planning_start.month, 1 ) end_month = action.planning_end and date( action.planning_end.year, action.planning_end.month, 1 ) for m in [this_month, next_month, month_after_next]: cls = None if start_month: if start_month == m: cls = "start" if end_month: if end_month == m: if end_month == (start_month is not None and start_month): cls = "start-end" else: cls = "end" elif start_month < m and end_month > m: cls = "ongoing" elif start_month < m: cls = "ongoing" elif end_month: if end_month == m: cls = "end" elif end_month > m: cls = "ongoing" classes.append(cls) modulesdict[module][risk.priority].append( { "title": title, "description": action.action, "months": [ ( action.planning_start and action.planning_start.month == m.month ) or ( action.planning_end and action.planning_end.month == m.month ) for m in [today, next_month, month_after_next] ], "classes": classes, } ) main_modules = {} for module, risks in sorted(modulesdict.items(), key=lambda m: m[0].zodb_path): module_obj = self.survey.restrictedTraverse(module.zodb_path.split("/")) if ( IProfileQuestion.providedBy(module_obj) or ICustomRisksModule.providedBy(module_obj) or module.depth >= 3 ): path = module.path[:6] else: path = module.path[:3] if path in main_modules: for prio in risks.keys(): if prio in main_modules[path]["risks"]: main_modules[path]["risks"][prio].extend(risks[prio]) else: main_modules[path]["risks"][prio] = risks[prio] else: title = module.title number = module.number main_modules[path] = {"name": title, "number": number, "risks": risks} self.modules = [] for key in sorted(main_modules.keys()): self.modules.append(main_modules[key])
def getTreeData(request, context, phase="identification", filter=None): """Assemble data for a navigation tree This function returns a nested dictionary structure reflecting the elements for a navigation tree. The tree will all sibling questions of the current context, the current module and all its module siblings, its parents up to the root module, and all modules at the root level. Optionally a SQLAlchemy clause can be provided, which will be used to filter items shown in the tree. The current item and its parents will always be shown. Each element is reflect as a dictionary item with the following keys: - id: the SQL object id - type: the SQL object type - number: a human presentable numbering of the item - title: the object title - current: boolean indicating if this is the current context or its direct parent module - active: boolean indicating if this is a parent node of the current context - class: CSS classes to use for this node - children: a list of child nodes (in the right order) - url: URL for this item """ query = Session.query(model.SurveyTreeItem) title_custom_risks = utils.get_translated_custom_risks_title(request) root = context parents = [] while root.parent_id is not None: parent = query.get(root.parent_id) parents.append(parent) root = parent parents.reverse() base_url = "%s/%s/" % (request.survey.absolute_url(), phase) def morph(obj): info = { 'id': obj.id, 'number': obj.number, 'title': obj.title, 'active': obj.path != context.path and context.path.startswith(obj.path), 'current': (obj.path == context.path), 'current_parent': (obj.path == context.path[:-3]), 'path': context.path, 'children': [], 'type': obj.type, 'leaf_module': False, 'depth': obj.depth, 'url': base_url + "/".join(obj.short_path) } cls = [] for key in ["active", "current", "current_parent"]: if info[key]: cls.append(key) if obj.postponed: cls.append("postponed") else: if isinstance(obj, model.Risk): if obj.identification: cls.append("answered") if obj.identification == "no": cls.append("risk") info["class"] = cls and " ".join(cls) or None return info # Result is always pointing to the level *above* the current level. # At the end it will be the virtual tree root result = { 'children': [], 'leaf_module': False, 'current': False, 'id': None, 'title': None } result["class"] = None children = [] for obj in context.siblings(filter=filter): info = morph(obj) if obj.type != 'risk' and obj.zodb_path.find('custom-risks') > -1: info['title'] = title_custom_risks children.append(info) result["children"] = children if isinstance(context, model.Module): # If this is an optional module, check the "postponed" flag. # As long as the optional question has not been answered, skip # showing its children. # Only a "Yes" answer will set skip_children to False module = request.survey.restrictedTraverse( context.zodb_path.split("/")) if (getattr(module, 'optional', False) and context.postponed in (True, None)): context.skip_children = True if not context.skip_children: # For modules which do not skip children, include the list of # children. me = first(lambda x: x["current"], result["children"]) children = [] for obj in context.children(filter=filter): info = morph(obj) # XXX: The check for SurveySession is due to Euphorie tests which don't # have a proper canonical ZODB survey object and don't test the # following OiRA-specific code. if obj.depth == 2 \ and not getattr(obj, 'is_custom_risk', False) \ and not isinstance(request.survey, SurveySession): module = request.survey.restrictedTraverse( obj.zodb_path.split('/')) if IProfileQuestion.providedBy(module) and \ not ICustomRisksModule.providedBy(aq_parent(module)): info['type'] = u'location' info['children'] = [ morph(sub) for sub in obj.children(filter=filter) ] children.append(info) me["children"] = children types = set([c["type"] for c in me["children"]]) me["leaf_module"] = "risk" in types elif isinstance(context, model.Risk): # For a risk we also want to include all siblings of its module parent parent = parents.pop() siblings = [] for obj in parent.siblings(model.Module, filter=filter): info = morph(obj) if obj.zodb_path.find('custom-risks') > -1: info['title'] = title_custom_risks siblings.append(info) myparent = first(lambda x: x["active"], siblings) myparent["children"] = result["children"] myparent["leaf_module"] = True result["children"] = siblings if parents: # Add all parents up to the root while len(parents) > 1: parent = parents.pop() new = morph(parent) if isinstance(parent, model.Module) and parent.depth == 2: module = request.survey.restrictedTraverse( parent.zodb_path.split('/')) if IProfileQuestion.providedBy(module) and \ not ICustomRisksModule.providedBy(aq_parent(module)): new['type'] = u'location' new["children"] = result["children"] result["children"] = [new] # Finally list all modules at the root level parent = parents.pop() roots = [] for obj in parent.siblings(model.Module, filter=filter): info = morph(obj) if obj.zodb_path.find('custom-risks') > -1: info['title'] = title_custom_risks roots.append(info) myroot = first(lambda x: x["active"], roots) myroot["children"] = result["children"] result["children"] = roots return result
def __call__(self): # Render the page only if the user has edit rights, # otherwise redirect to the start page of the session. if not self.webhelpers.can_edit_session: return self.request.response.redirect( self.context.aq_parent.aq_parent.absolute_url() + "/@@start" ) if self.webhelpers.redirectOnSurveyUpdate(): return context = aq_inner(self.context) utils.setLanguage(self.request, self.survey, self.survey.language) if (IProfileQuestion.providedBy(self.module) and context.depth == 2) or ( ICustomRisksModule.providedBy(self.module) and self.phase == "actionplan" ): next_question = FindNextQuestion( context, self.context.session, filter=self.question_filter ) if next_question is None: if self.phase == ("identification", "evaluation"): url = ( self.webhelpers.traversed_session.absolute_url() + "/@@actionplan" ) elif self.phase == "actionplan": url = self.webhelpers.traversed_session.absolute_url() + "/@@report" else: url = "{session_url}/{path}/@@actionplan".format( session_url=self.webhelpers.traversed_session.absolute_url(), path="/".join(next_question.short_path), ) return self.request.response.redirect(url) self.title = self.context.title previous = FindPreviousQuestion( self.context, self.context.session, filter=self.question_filter ) if previous is None: self.previous_url = "%s/@@%s" % ( self.context.aq_parent.absolute_url(), self.phase, ) else: self.previous_url = "{session_url}/{path}/@@{phase}".format( session_url=self.webhelpers.traversed_session.absolute_url(), path="/".join(previous.short_path), phase=self.phase, ) next_question = FindNextQuestion( self.context, self.context.session, filter=self.question_filter ) if next_question is None: self.next_url = ( self.webhelpers.traversed_session.absolute_url() + "/@@report" ) else: self.next_url = "{session_url}/{path}/@@{phase}".format( session_url=self.webhelpers.traversed_session.absolute_url(), path="/".join(next_question.short_path), phase=self.phase, ) return self.index()
def getTreeData( request, context, element=None, phase="identification", filter=None, survey=None, no_current=False, ): """Assemble data for a navigation tree This function returns a nested dictionary structure reflecting the elements for a navigation tree. The tree will all sibling questions of the current context, the current module and all its module siblings, its parents up to the root module, and all modules at the root level. Optionally a SQLAlchemy clause can be provided, which will be used to filter items shown in the tree. The current item and its parents will always be shown. Each element is reflect as a dictionary item with the following keys: - id: the SQL object id - type: the SQL object type - number: a human presentable numbering of the item - title: the object title - current: boolean indicating if this is the current context or its direct parent module - active: boolean indicating if this is a parent node of the current context - class: CSS classes to use for this node - children: a list of child nodes (in the right order) - url: URL for this item """ if not survey: # Standard, real-world case webhelpers = api.content.get_view("webhelpers", context, request) survey = webhelpers._survey traversed_session = webhelpers.traversed_session else: # XXX Fixme # Only in tests... # In some tests in test_navigation, the view "webhelpers" cannot be found # for the given context. That's why we pass in the survey item directly. traversed_session = survey # This is the tree element that we start from. # It can be the same as the context that gets passed in, if it has an Acquisition # chain. On views that are called outside of the context of a module or risk, # e.g. the initial @@identification view, the tree-element that we find is not # in an acqusition context, so that we cannot use it for fetching the traversed # session via webhelpers. if not element: element = context query = Session.query(model.SurveyTreeItem) title_custom_risks = utils.get_translated_custom_risks_title(request) root = element parents = [] while root.parent_id is not None: parent = query.get(root.parent_id) parents.append(parent) root = parent parents.reverse() def morph(obj): number = obj.number # The custom risks don't have a real number, but an Omega instead if obj.zodb_path.find("custom-risks") > -1: num_elems = number.split(".") number = ".".join(["Ω"] + num_elems[1:]) info = { "id": obj.id, "number": number, "title": obj.title, "active": (obj.path != element.path and element.path.startswith(obj.path)), "current": (obj.path == element.path), "current_parent": (obj.path == element.path[:-3]), "path": element.path, "children": [], "type": obj.type, "leaf_module": False, "depth": obj.depth, "url": "{session_url}/{obj_path}/@@{phase}".format( session_url=traversed_session.absolute_url(), obj_path="/".join(obj.short_path), phase=phase, ), "css_id": "", } cls = [] for key in ["active", "current", "current_parent"]: if info[key]: if key == "current" and no_current: continue cls.append(key) if obj.postponed: cls.append("postponed") else: if isinstance(obj, model.Risk): if obj.identification: cls.append("answered") if obj.identification == "no": cls.append("risk") info["class"] = cls and " ".join(cls) or None return info # Result is always pointing to the level *above* the current level. # At the end it will be the virtual tree root result = { "children": [], "leaf_module": False, "current": False, "id": None, "title": None, } result["class"] = None children = [] for obj in element.siblings(filter=filter): info = morph(obj) if obj.type != "risk" and obj.zodb_path.find("custom-risks") > -1: info["title"] = title_custom_risks info["css_id"] = "other-risks" children.append(info) result["children"] = children if isinstance(element, model.Module): # If this is an optional module, check the "postponed" flag. # As long as the optional question has not been answered, skip # showing its children. # Only a "Yes" answer will set skip_children to False module = survey.restrictedTraverse(element.zodb_path.split("/")) # In the custom risks module, we never skip children # Due to historical reasons, some custom modules might be set to # postponed. Here, we ignore that setting. if ICustomRisksModule.providedBy(module): element.skip_children = False elif getattr(module, "optional", False) and element.postponed in (True, None): element.skip_children = True if not element.skip_children: # For modules which do not skip children, include the list of # children. me = first(lambda x: x["current"], result["children"]) children = [] for obj in element.children(filter=filter): info = morph(obj) # XXX: The check for SurveySession is due to Euphorie tests which don't # have a proper canonical ZODB survey object and don't test the # following OiRA-specific code. if (obj.depth == 2 and not getattr(obj, "is_custom_risk", False) and not isinstance(survey, SurveySession)): module = survey.restrictedTraverse( obj.zodb_path.split("/")) if IProfileQuestion.providedBy( module) and not ICustomRisksModule.providedBy( aq_parent(module)): info["type"] = "location" info["children"] = [ morph(sub) for sub in obj.children(filter=filter) ] children.append(info) me["children"] = children types = set([c["type"] for c in me["children"]]) me["leaf_module"] = "risk" in types elif isinstance(element, model.Risk): # For a risk we also want to include all siblings of its module parent parent = parents.pop() siblings = [] for obj in parent.siblings(model.Module, filter=filter): info = morph(obj) if obj.zodb_path.find("custom-risks") > -1: info["title"] = title_custom_risks info["css_id"] = "other-risks" siblings.append(info) myparent = first(lambda x: x["active"], siblings) myparent["children"] = result["children"] myparent["leaf_module"] = True result["children"] = siblings if parents: # Add all parents up to the root while len(parents) > 1: parent = parents.pop() new = morph(parent) if isinstance(parent, model.Module) and parent.depth == 2: module = survey.restrictedTraverse(parent.zodb_path.split("/")) if IProfileQuestion.providedBy( module) and not ICustomRisksModule.providedBy( aq_parent(module)): new["type"] = "location" new["children"] = result["children"] result["children"] = [new] # Finally list all modules at the root level parent = parents.pop() roots = [] for obj in parent.siblings(model.Module, filter=filter): info = morph(obj) if obj.zodb_path.find("custom-risks") > -1: info["title"] = title_custom_risks roots.append(info) myroot = first(lambda x: x["active"], roots) myroot["children"] = result["children"] result["children"] = roots return result