class ResetPasswordFormSchema(model.Schema): new_password = schema.Password(title=_("label_new_password", default="Desired password"), ) new_password_confirmation = schema.Password(title=_( "label_confirm_password", default="Confirm your password"), ) @invariant def validate_same_value(data): if data.new_password != data.new_password_confirmation: raise Invalid( _("Password doesn't compare with confirmation value"))
def do_save(self): """Execute the save action""" (data, errors) = self.extractData() if errors: for err in errors: if isinstance(err.error, Exception): self.error = _("error_password_mismatch", default="Passwords do not match") return key = self.key ppr = api.portal.get_tool("portal_password_reset") try: ppr.verifyKey(key) except InvalidRequestError: self.error = _("Invalid security token, try to request a new one") return login_view = api.content.get_view( name="login", context=self.context, request=self.request, ) error = login_view.check_password_policy(data["new_password"]) if error: self.error = error return account_id, expiry = ppr._requests.get(key) if ppr.expired(expiry): del ppr._requests[key] ppr._p_changed = 1 self.error = _("This URL has expired, try to request a new one") return account = Session().query(Account).filter( Account.id == account_id).one() account.password = data["new_password"] # clean out the request del ppr._requests[key] ppr._p_changed = 1 current_url = self.context.absolute_url() return self.redirect( "{}/@@login?{}".format(current_url, urlencode(dict(came_from=current_url))), msg=_("Your password was successfully changed."), )
def validate(self): previous_question_id = self.previous_question_id if not previous_question_id: return training = self.get_or_create_training() try: answers = loads(training.answers) except ValueError: answers = {} if all([answer is None for answer in answers]): raise Unauthorized( _("You should start the training from the beginning")) if answers.get(previous_question_id) is None: raise Unauthorized(_("It seems you missed a slide"))
class RisksOverview(Status): """Implements the "Overview of Risks" report, see #10967""" label = _("Overview of risks") def is_skipped_from_risk_list(self, risk): if risk.identification == "yes": return True
def update(self): super(ResetPasswordForm, self).update() key = self.key ppr = api.portal.get_tool("portal_password_reset") try: ppr.verifyKey(key) except InvalidRequestError: self.error = _("Invalid security token, try to request a new one")
def get_pat_messages_above_title(self): """List of messages we want to display above the risk assesment title""" if not self.webhelpers.can_edit_session: link_download_section = _( "no_translate_link_download_section", default="<a href='%s/@@report'>${text_download_section}</a>" % self.context.absolute_url(), mapping={"text_download_section": _("download section")}, ) message = _( "You don't have edit rights for this risk assesment, " "but you can download " "this risk assessment in various forms in the ${download_section}.", mapping={"download_section": link_download_section}, ) return [api.portal.translate(message)] return []
def pat_validation_messages(self): lang = getattr(self.request, "LANGUAGE", "en") if "-" in lang: elems = lang.split("-") lang = "{0}_{1}".format(elems[0], elems[1].upper()) messages = { "message-date": translate( _("error_validation_date", default="This value must be a valid date"), target_language=lang, ), "message-datetime": translate( _( "error_validation_datetime", default="This value must be a valid date and time", ), target_language=lang, ), "message-email": translate( _( "error_validation_email", default="This value must be a valid email address", ), target_language=lang, ), "message-number": translate( _("error_validation_number", default="This value must be a number"), target_language=lang, ), "message-required": translate( _("message_field_required", default="Please fill out this field."), target_language=lang, ), } return "; ".join([ "{key}: {value}".format(key=key, value=value) for key, value in messages.items() ])
def send_mail(self, email): account = Session.query(Account).filter( Account.loginname == email).first() if not account: # We returned True even if the account # does not exist to not leak any information return True ppr = api.portal.get_tool("portal_password_reset") # Clean out previous requests by this user for token, value in ppr._requests.items(): if value[0] == account.id: del ppr._requests[token] ppr._p_changed = 1 reset_info = ppr.requestReset(account.id) reset_info["host"] = self.get_remote_host() mailhost = api.portal.get_tool("MailHost") body = self.email_template(**reset_info) subject = translate( _("OiRA password reset instructions"), context=self.request, ) mail = CreateEmailTo( self.email_from_name, self.email_from_address, account.email, subject, body, ) try: mailhost.send( mail, account.email, self.email_from_address, immediate=True, ) logger.info( "Sent password reset instructions to %s", account.email, ) except MailHostError as e: msg = ( "MailHost error sending password reset instructions to {}: {}" ).format(account.email, e) return self.log_error(msg) except smtplib.SMTPException as e: msg = ( "smtplib error sending password reset instructions to {}: {}" ).format(account.email, e) return self.log_error(msg) except socket.error as e: msg = ("Socket error sending password reset instructions to {}: {}" ).format(account.email, e[1]) return self.log_error(msg) return True
def get_progress_indicator_title(self, completion_percentage=None): if completion_percentage is None and self.traversed_session is not None: completion_percentage = self.traversed_session.session.completion_percentage title = _( "progress_indicator_title", default="${completion_percentage}% Complete", mapping={"completion_percentage": completion_percentage or 0}, ) return api.portal.translate(title)
class IStartFormSchema(model.Schema): title = schema.TextLine( title=_("label_session_title", default="Enter a title for your Risk Assessment"), required=True, description=_( "session_title_tooltip", default="Once you have started an OiRA tool " "session, you will be able to stop before the end. You can restart it " "again at a later date by selecting the title of the OiRA tool session. " "You can save more than one session, provided you have given them " "different title names. Please remember your e-mail address, password " "and the title that you have given the OiRA tool session. By clicking " "on the Logout button, you are logged out actively. You are also logged " "out if you close your Browser. For security reasons it is better to " "actively log out.", ), )
def profile_questions(self): """Return information for all profile questions in this survey. The data is returned as a list of dictionaries with the following keys: - ``id``: object id of the question - ``title``: title of the question - ``question``: question about the general occurance - ``label_multiple_present``: question about single or multiple occurance - ``label_single_occurance``: label for single occurance - ``label_multiple_occurances``: label for multiple occurance """ return [{ "id": child.id, "title": child.title, "question": child.question or child.title, "use_location_question": getattr(child, "use_location_question", True), "label_multiple_present": getattr( child, "label_multiple_present", _("Does this happen in multiple places?"), ), "label_single_occurance": getattr( child, "label_single_occurance", _("Enter the name of the location"), ), "label_multiple_occurances": getattr( child, "label_multiple_occurances", _("Enter the names of each location"), ), } for child in self.context.ProfileQuestions()]
def sector_title(self): """Return the title to use for the current sector. If the current context is not in a sector return the agency name instead. """ sector = self.sector if sector is not None and getattr(aq_base(sector), "logo", None) is not None: return sector.Title() else: return _( "title_tool", default="OiRA - Online interactive Risk Assessment", )
def clone(self): """Clone this session and redirect to the start view""" if not self.webhelpers.can_view_session: # The user cannot call this view to go the sessions overview. return self.request.response.redirect(self.webhelpers.client_url) new_session = self.get_cloned_session() api.portal.show_message(_("The risk assessment has been cloned"), self.request, "success") target = "{contexturl}/++session++{sessionid}/@@start?new_clone=1".format( contexturl=aq_parent(self.context).absolute_url(), sessionid=new_session.id) self.request.response.redirect(target)
def __call__(self): if not self.webhelpers.can_delete_session: raise Unauthorized() Session.delete(self.context.session) api.portal.show_message( _( "Session `${name}` has been deleted.", mapping={"name": self.context.session.title}, ), self.request, "success", ) self.request.response.redirect(self.webhelpers.country_url)
def __call__(self): if not self.webhelpers.can_archive_session: raise Unauthorized() session = self.context.session session.archived = localized_now() self.notify_modified() api.portal.show_message( _("Session `${name}` has been archived.", mapping={"name": session.title}), self.request, "success", ) return self.redirect()
def extra_text(self): have_extra = api.portal.get_registry_record( "euphorie.extra_text_identification", default="") if not have_extra: return None lang = getattr(self.request, "LANGUAGE", "en") # Special handling for Flemish, for which LANGUAGE is "nl-be". For # translating the date under plone locales, we reduce to generic "nl". # For the specific oira translation, we rewrite to "nl_BE" if "-" in lang: elems = lang.split("-") lang = "{0}_{1}".format(elems[0], elems[1].upper()) return translate(_("extra_text_identification", default=""), target_language=lang)
def __call__(self): if not self.webhelpers.can_edit_session: return self.request.response.redirect(self.context.absolute_url() + "/@@start") if not self.next_url: msg = _( "There is not enough information to proceed to the identification phase" ) api.portal.show_message(msg, self.request, "error") return self.request.response.redirect(self.context.absolute_url() + "/@@start") utils.setLanguage(self.request, self.survey, self.survey.language) if self.webhelpers.use_involve_phase: self.request.RESPONSE.redirect(self.next_url) else: return super(Identification, self).__call__()
def do_next(self): data, error = self.extractData() if error: return email = data.get("email") if not self.send_mail(email): return msg = _( "message_password_recovery_email_sent", default="An email will be sent to ${email} " "if we can find an account for this email address. Please use the " "link inside the e-mail to reset your password.", mapping={"email": email}, ) webhelpers = api.content.get_view(name="webhelpers", context=self.context, request=self.request) redir_url = webhelpers.get_came_from( default=self.context.absolute_url()) if not redir_url.endswith("login"): redir_url = "{0}/@@login?{1}#login".format( redir_url, urlencode({"came_from": redir_url})) self.redirect(redir_url, msg)
def closetext(self): return api.portal.translate(_("button_close", default="Close"))
def get_cloned_session(self): sql_session = Session old_session = self.session new_session = sql_clone( old_session, skip={ "id", "created", "modified", "last_modifier_id", "company", "published", "group_id", "archived", }, session=sql_session, ) lang = getattr(self.request, "LANGUAGE", "en") new_session.title = "{}: {}".format( translate(_("prefix_cloned_title", default="COPY"), target_language=lang), new_session.title, ) account = self.webhelpers.get_current_account() new_session.group = account.group new_session.modified = new_session.created = datetime.now() new_session.account = account if old_session.company: new_session.company = sql_clone(old_session.company, skip={"id", "session"}, session=sql_session) risk_module_skipped_attributes = { "id", "session", "sql_module_id", "parent_id", "session_id", "sql_risk_id", "risk_id", } module_mapping = {} old_modules = sql_session.query(Module).filter( SurveyTreeItem.session == old_session) for old_module in old_modules: new_module = sql_clone(old_module, skip=risk_module_skipped_attributes, session=sql_session) module_mapping[old_module.id] = new_module new_module.session = new_session old_risks = sql_session.query(Risk).filter( SurveyTreeItem.session == old_session) for old_risk in old_risks: new_risk = sql_clone(old_risk, skip=risk_module_skipped_attributes, session=sql_session) new_risk.parent_id = module_mapping[old_risk.parent_id].id new_risk.session = new_session for old_plan in old_risk.action_plans: new_plan = sql_clone(old_plan, skip={"id", "risk_id"}, session=sql_session) new_plan.risk = new_risk notify(ObjectModifiedEvent(new_session)) return new_session
def validate_same_value(data): if data.new_password != data.new_password_confirmation: raise Invalid( _("Password doesn't compare with confirmation value"))
class ResetPasswordForm(BaseForm): ignoreContext = True schema = ResetPasswordFormSchema label = _( "title_reset_password_form", default="Reset password", ) description = _( "description_reset_password_form", default="", ) button_label = _("Save changes") def update(self): super(ResetPasswordForm, self).update() key = self.key ppr = api.portal.get_tool("portal_password_reset") try: ppr.verifyKey(key) except InvalidRequestError: self.error = _("Invalid security token, try to request a new one") def publishTraverse(self, request, name): return self @property @memoize_contextless def key(self): """Extract the key from the URL""" return self.request.getURL().rpartition("/")[-1] def do_save(self): """Execute the save action""" (data, errors) = self.extractData() if errors: for err in errors: if isinstance(err.error, Exception): self.error = _("error_password_mismatch", default="Passwords do not match") return key = self.key ppr = api.portal.get_tool("portal_password_reset") try: ppr.verifyKey(key) except InvalidRequestError: self.error = _("Invalid security token, try to request a new one") return login_view = api.content.get_view( name="login", context=self.context, request=self.request, ) error = login_view.check_password_policy(data["new_password"]) if error: self.error = error return account_id, expiry = ppr._requests.get(key) if ppr.expired(expiry): del ppr._requests[key] ppr._p_changed = 1 self.error = _("This URL has expired, try to request a new one") return account = Session().query(Account).filter( Account.id == account_id).one() account.password = data["new_password"] # clean out the request del ppr._requests[key] ppr._p_changed = 1 current_url = self.context.absolute_url() return self.redirect( "{}/@@login?{}".format(current_url, urlencode(dict(came_from=current_url))), msg=_("Your password was successfully changed."), ) @button.buttonAndHandler(_("Save")) def save_handler(self, action): """Check if the security token is correct and if it is change the account password with the provided value """ self.do_save() @button.buttonAndHandler(_("Cancel")) def handleCancel(self, action): self.redirect(self.context.absolute_url())
class ResetPasswordRequest(BaseForm): """Request a link to reset the password""" ignoreContext = True schema = ResetPasswordRequestSchema label = _( "title_reset_password_request", default="Password recovery", ) description = _( "description_reset_password_request", ("We will send you an email " "with the instructions to reset your password."), ) button_label = _( "label_send_password_reminder", default="Send password reminder", ) email_template = ViewPageTemplateFile( "templates/password_recovery_email.pt") @property def email_from_name(self): return api.portal.get_registry_record("plone.email_from_name") @property def email_from_address(self): return api.portal.get_registry_record("plone.email_from_address") def expiration_timeout(self): ppr = api.portal.get_tool("portal_password_reset") timeout = ppr.getExpirationTimeout() or 0 return int(timeout * 24) # timeout is in days, but templates want in hours. def log_error(self, msg): """Log an error message, set the view error attribute and return False""" logger.error(msg) self.error = _( "An error occured while sending the password reset instructions", ) return False def get_remote_host(self): forwarded_for = self.request.get("HTTP_X_FORWARDED_FOR") if is_ipv4_address(forwarded_for): return forwarded_for return self.request.get("REMOTE_ADDR") def send_mail(self, email): account = Session.query(Account).filter( Account.loginname == email).first() if not account: # We returned True even if the account # does not exist to not leak any information return True ppr = api.portal.get_tool("portal_password_reset") # Clean out previous requests by this user for token, value in ppr._requests.items(): if value[0] == account.id: del ppr._requests[token] ppr._p_changed = 1 reset_info = ppr.requestReset(account.id) reset_info["host"] = self.get_remote_host() mailhost = api.portal.get_tool("MailHost") body = self.email_template(**reset_info) subject = translate( _("OiRA password reset instructions"), context=self.request, ) mail = CreateEmailTo( self.email_from_name, self.email_from_address, account.email, subject, body, ) try: mailhost.send( mail, account.email, self.email_from_address, immediate=True, ) logger.info( "Sent password reset instructions to %s", account.email, ) except MailHostError as e: msg = ( "MailHost error sending password reset instructions to {}: {}" ).format(account.email, e) return self.log_error(msg) except smtplib.SMTPException as e: msg = ( "smtplib error sending password reset instructions to {}: {}" ).format(account.email, e) return self.log_error(msg) except socket.error as e: msg = ("Socket error sending password reset instructions to {}: {}" ).format(account.email, e[1]) return self.log_error(msg) return True def do_next(self): data, error = self.extractData() if error: return email = data.get("email") if not self.send_mail(email): return msg = _( "message_password_recovery_email_sent", default="An email will be sent to ${email} " "if we can find an account for this email address. Please use the " "link inside the e-mail to reset your password.", mapping={"email": email}, ) webhelpers = api.content.get_view(name="webhelpers", context=self.context, request=self.request) redir_url = webhelpers.get_came_from( default=self.context.absolute_url()) if not redir_url.endswith("login"): redir_url = "{0}/@@login?{1}#login".format( redir_url, urlencode({"came_from": redir_url})) self.redirect(redir_url, msg) @button.buttonAndHandler(_("Save")) def next_handler(self, action): """Check if the security token is correct and if it is change the account password with the provided value """ self.do_next() @button.buttonAndHandler(_("Cancel")) def handleCancel(self, action): self.redirect(self.context.absolute_url())
def log_error(self, msg): """Log an error message, set the view error attribute and return False""" logger.error(msg) self.error = _( "An error occured while sending the password reset instructions", ) return False
def label_start_session(self): label = api.portal.translate( _("link_start_session", default="Start a new risk assessment")) return capitalize(label)
class TrainingView(BrowserView, survey._StatusHelper): """The view that shows the main-menu Training module""" variation_class = "variation-risk-assessment" skip_unanswered = False for_download = False more_menu_contents = [] heading_measures = _("header_measures", default="Measures") show_slide_byline = True @property @view_memoize def webhelpers(self): return api.content.get_view("webhelpers", self.context, self.request) @property @view_memoize def session(self): """Return the session for this context/request""" return self.context.session @property @memoize def timestamp(self): return self.session.modified.strftime("%s%M%H%d%m") def get_initial_answers(self): """Pick a subset of questions, shuffle them, and initialize the answers with None. """ survey = self.webhelpers._survey all_questions = survey.listFolderContents( {"portal_type": "euphorie.training_question"}) num_training_questions = min( getattr(survey, "num_training_questions", None) or len(all_questions), len(all_questions), ) questions = sample(all_questions, k=num_training_questions) return {q.getId(): None for q in questions} @memoize def get_or_create_training(self): """Return the training for this session""" account_id = self.webhelpers.get_current_account().id session_id = self.webhelpers.session_id try: return (Session.query(Training).filter( Training.session_id == session_id, Training.account_id == account_id).one()) except NoResultFound: pass answers = self.get_initial_answers() status = "in_progress" if answers else "correct" training = Training( account_id=account_id, session_id=session_id, status=status, time=datetime.now(), answers=dumps(answers), ) Session.add(training) return training @property @view_memoize def training_status(self): return self.get_or_create_training().status @property @view_memoize def question_intro_url(self): survey = self.webhelpers._survey if not getattr(survey, "enable_web_training", False): return "" view_name = "slide_question_success" if survey.listFolderContents( {"portal_type": "euphorie.training_question" }) and self.training_status not in ("correct", "success"): view_name = "slide_question_intro" return "{}/@@{}".format(self.context.absolute_url(), view_name) @property @view_memoize def question_ids(self): training = self.get_or_create_training() answer_history = loads(training.answers) return list(answer_history) @property def enable_training_questions(self): """Explicit property that can be overwritten in subpackages""" return bool(self.question_intro_url) def slicePath(self, path): while path: yield path[:3].lstrip("0") path = path[3:] @property @view_memoize def title_image(self): try: return self.context.aq_parent.external_site_logo.data except AttributeError: logger.warning( "Image data (logo) could not be fetched on survey %s", self.context.absolute_url(), ) return @property @view_memoize def tool_image_url(self): survey = self.context.aq_parent if getattr(survey, "image", None): return f"{survey.absolute_url()}/@@images/image/large" @property def logo_url(self): logo = self.webhelpers.get_sector_logo if logo: return f"{self.webhelpers.portal_url}/{logo.url}" return f"{self.webhelpers.portal_url}/++resource++euphorie.resources/media/oira-logo-colour.png" # noqa: E501 @property @view_memoize def slide_data(self): modules = self.getModulePaths() risks = self.getRisks(modules, skip_unanswered=self.skip_unanswered) seen_modules = [] data = OrderedDict() for (module, risk) in risks: module_path = module.path if module_path not in seen_modules: module_in_context = module.__of__( self.webhelpers.traversed_session) module_in_context.REQUEST["for_download"] = self.for_download _view = module_in_context.restrictedTraverse("training_slide") slide_contents = _view.slide_contents() data.update({ module_path: { "item": module_in_context, "training_view": _view, "slide_contents": slide_contents, } }) seen_modules.append(module_path) risk_in_context = risk.__of__(self.webhelpers.traversed_session) risk_in_context.REQUEST["for_download"] = self.for_download _view = risk_in_context.restrictedTraverse("training_slide") slide_contents = _view.slide_contents() data.update({ risk.path: { "item": risk_in_context, "training_view": _view, "slide_contents": slide_contents, } }) return data @property def slide_total_count(self): count = 0 for data in self.slide_data.values(): count += 1 for measure_id in data["slide_contents"]["measures"]: if data["slide_contents"]["measures"][measure_id]["active"]: count += 1 break if (data["item"].type != "module" and data["slide_contents"]["training_notes"]): count += 1 return count def handle_measure_configuration(self, reply): session = Session() risk = session.query(Risk).filter(Risk.id == reply["risk_id"]).first() if not risk: return # Gather all (database-) ids of the active measures. That means, those # measures where the checkboxes are ticked in the training configuration. # Remember: a measure that has been deselected (checkbox unticked) # does not appear in the REQUEST active_measures_in_place = [] active_measures_planned = [] for entry in reply: if entry.startswith( "training-measure-in-place") and entry.find("-") >= 0: measure_id = entry.split("-")[-1] active_measures_in_place.append(measure_id) elif entry.startswith( "training-measure-planned") and entry.find("-") >= 0: measure_id = entry.split("-")[-1] active_measures_planned.append(measure_id) # Get the (database-) ids of all measures-in-place / planned measures all_in_place_measures = { str(measure.id) for measure in list(risk.in_place_standard_measures) + list(risk.in_place_custom_measures) } all_planned_measures = { str(measure.id) for measure in list(risk.standard_measures) + list(risk.custom_measures) } # Additionally store the (database-) ids of all measures that have been # deactivated. deselected_in_place_measures = [ k for k in all_in_place_measures if k not in active_measures_in_place ] deselected_planned_measures = [ k for k in all_planned_measures if k not in active_measures_planned ] changed = False if active_measures_in_place: session.execute( "UPDATE action_plan set used_in_training=true where id in ({ids})" .format( # noqa: E501 ids=",".join(active_measures_in_place))) changed = True if deselected_in_place_measures: session.execute( "UPDATE action_plan set used_in_training=false where id in ({ids})" .format( # noqa: E501 ids=",".join(deselected_in_place_measures))) changed = True if active_measures_planned: session.execute( "UPDATE action_plan set used_in_training=true where id in ({ids})" .format( # noqa: E501 ids=",".join(active_measures_planned))) changed = True if deselected_planned_measures: session.execute( "UPDATE action_plan set used_in_training=false where id in ({ids})" .format( # noqa: E501 ids=",".join(deselected_planned_measures))) changed = True if changed: self.webhelpers.traversed_session.session.touch() def __call__(self): if self.webhelpers.redirectOnSurveyUpdate(): return survey = self.webhelpers._survey utils.setLanguage(self.request, survey, survey.language) if self.request.environ["REQUEST_METHOD"] == "POST": reply = self.request.form if "risk_id" in reply: self.handle_measure_configuration(reply) self.request.RESPONSE.addHeader("Cache-Control", "public,max-age=60") return self.index()
class ResetPasswordRequestSchema(model.Schema): email = Email(title=_("label_email", default="Email address"), )
def email_sharing_text(self): return api.portal.translate( _("I wish to share the following with you"))
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 tool_byline(self): title = api.portal.translate( _("title_tool", default="OiRA - Online interactive Risk Assessment")) return title.split("-")[-1].strip()