Skip to content

Commit

Permalink
Add behavior to hide modules from trainings.
Browse files Browse the repository at this point in the history
Ref: scrum-1573
  • Loading branch information
thet committed Nov 16, 2023
1 parent d5d9adf commit 79ef584
Show file tree
Hide file tree
Showing 8 changed files with 239 additions and 3 deletions.
3 changes: 3 additions & 0 deletions docs/changes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ Changelog
15.1.0 (unreleased)
-------------------

- Add behavior to hide modules from trainings.
Ref: scrum-1573
[thet]
- Cleanup: Reuse similar code for survey related views.
[thet]
- Fix incorrect named entity in configure.zcml.
Expand Down
20 changes: 20 additions & 0 deletions src/euphorie/client/browser/training.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from euphorie.client import utils as client_utils
from euphorie.client.model import Risk
from euphorie.client.model import Training
from euphorie.content.behaviors.hide_from_training import IHideFromTraining
from json import dumps
from json import loads
from logging import getLogger
Expand Down Expand Up @@ -414,10 +415,29 @@ def slide_data(self):
modules = self.getModulePaths()
risks = self.getRisks(modules, skip_unanswered=self.skip_unanswered)
seen_modules = []
hidden_modules = []
data = OrderedDict()
for module, risk in risks:
module_path = module.path
if module_path in hidden_modules:
continue
if module_path not in seen_modules:
# Get the ZODB representation of the module.
zodb_module = self.webhelpers.traversed_session.restrictedTraverse(
module.zodb_path.split("/")
)
# Check if the module is allowed in trainings.
if (
getattr(
IHideFromTraining(zodb_module, None),
"hide_from_training",
False,
)
is True
):
hidden_modules.append(module_path)
continue

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")
Expand Down
142 changes: 142 additions & 0 deletions src/euphorie/client/tests/test_training__hide_from_training.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
from euphorie.client import model
from euphorie.client.interfaces import IClientSkinLayer
from euphorie.client.tests.utils import addAccount
from euphorie.client.tests.utils import addSurvey
from euphorie.testing import EuphorieIntegrationTestCase
from plone import api
from zope.interface import alsoProvides


SURVEY_1 = """
<sector xmlns="http://xml.simplon.biz/euphorie/survey/1.0">
<title>sector</title>
<survey>
<title>survey</title>
<language>nl</language>
<module>
<title>module1</title>
<risk>
<title>risk11</title>
<problem_description>problem-desc-11</problem_description>
<description>desc-11</description>
</risk>
</module>
<module>
<title>module2</title>
<risk>
<title>risk21</title>
<problem_description>problem-desc-21</problem_description>
<description>desc-21</description>
</risk>
</module>
</survey>
</sector>
"""

SURVEY_2 = """
<sector xmlns="http://xml.simplon.biz/euphorie/survey/1.0">
<title>sector</title>
<survey>
<title>survey</title>
<language>nl</language>
<module hide_from_training="True">
<title>module1</title>
<risk>
<title>risk11</title>
<problem_description>problem-desc-11</problem_description>
<description>desc-11</description>
</risk>
</module>
<module>
<title>module2</title>
<risk>
<title>risk21</title>
<problem_description>problem-desc-21</problem_description>
<description>desc-21</description>
</risk>
</module>
</survey>
</sector>
"""


class TestTrainingHideFromTraining(EuphorieIntegrationTestCase):
"""Tests the IHideFromTraining behavior.
Also tests importing an xml structure with the hide_from_training
attribute set.
"""

def setUp(self):
super().setUp()
self.account = addAccount(password="secret")
api.portal.set_registry_record("euphorie.use_training_module", True)

# Add the hide_from_training behavior to the module type
types = api.portal.get_tool("portal_types")
fti_module = types.get("euphorie.module")
fti_module.behaviors = tuple(
list(fti_module.behaviors) + ["euphorie.hide_from_training"]
)

def create_content(self, survey_xml):
with api.env.adopt_user("admin"):
addSurvey(self.portal, survey_xml)
self.survey = self.portal.client.nl.sector.survey
alsoProvides(self.survey.REQUEST, IClientSkinLayer)

self.survey_session = model.SurveySession(
title="Dummy session",
zodb_path="nl/sector/survey",
account=self.account,
)
model.Session.add(self.survey_session)

self.module1 = self.survey_session.addChild(
model.Module(title="module1", module_id="module1", zodb_path="1")
)
self.risk11 = self.module1.addChild(
model.Risk(title="risk11", risk_id="risk11", zodb_path="1/2")
)
self.module2 = self.survey_session.addChild(
model.Module(title="module2", module_id="module2", zodb_path="3")
)
self.risk21 = self.module2.addChild(
model.Risk(title="risk21", risk_id="risk21", zodb_path="3/4")
)

def test_training_module_visible(self):
"""Test the standard case - all modules and ristsk are visible in the
training.
"""
self.create_content(SURVEY_1)

traversed_session = self.survey.restrictedTraverse("++session++1")
with api.env.adopt_user(user=self.account):
with self._get_view(
"training", traversed_session, self.request.clone()
) as view:
self.survey.enable_web_training = True
data = list(view.slide_data.values())

self.assertEqual(len(data), 4)
self.assertEqual(data[0]["item"].title, "module1")
self.assertEqual(data[1]["item"].title, "risk11")
self.assertEqual(data[2]["item"].title, "module2")
self.assertEqual(data[3]["item"].title, "risk21")

def test_training_module_hidden(self):
"""Test module1 hidden from training."""
self.create_content(SURVEY_2)

traversed_session = self.survey.restrictedTraverse("++session++1")
with api.env.adopt_user(user=self.account):
with self._get_view(
"training", traversed_session, self.request.clone()
) as view:
self.survey.enable_web_training = True
data = list(view.slide_data.values())

self.assertEqual(len(data), 2)
self.assertEqual(data[0]["item"].title, "module2")
self.assertEqual(data[1]["item"].title, "risk21")
6 changes: 6 additions & 0 deletions src/euphorie/content/behaviors/configure.zcml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@
i18n_domain="untranslated"
>

<plone:behavior
name="euphorie.hide_from_training"
title="Hide from training"
provides=".hide_from_training.IHideFromTraining"
/>

<plone:behavior
title="Tool Category"
description="Add a tool category"
Expand Down
21 changes: 21 additions & 0 deletions src/euphorie/content/behaviors/hide_from_training.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from .. import MessageFactory as _
from plone.autoform.directives import order_after
from plone.autoform.interfaces import IFormFieldProvider
from plone.supermodel import model
from zope import schema
from zope.interface import provider


@provider(IFormFieldProvider)
class IHideFromTraining(model.Schema):
hide_from_training = schema.Bool(
title=_("label_hide_from_training", default="Hide from training"),
description=_(
"help_hide_from_training",
default="Do not include this module in the training, for example "
"if the module does not have any relevance for workers.",
),
required=False,
default=False,
)
order_after(hide_from_training="question")
11 changes: 8 additions & 3 deletions src/euphorie/content/browser/export.py
Original file line number Diff line number Diff line change
Expand Up @@ -413,9 +413,14 @@ def exportProfileQuestion(self, parent, profile):
def exportModule(self, parent, module):
""":returns: An XML node with the details of an
:obj:`euphorie.content.module`."""
node = etree.SubElement(
parent, "module", optional="true" if module.optional else "false"
)

module_kwargs = {
"optional": "true" if module.optional else "false"
}
if getattr(module, "hide_from_training", False):
module_kwargs["hide_from_training"] = "true"
node = etree.SubElement(parent, "module", **module_kwargs)

if getattr(module, "external_id", None):
node.attrib["external-id"] = module.external_id
etree.SubElement(node, "title").text = module.title
Expand Down
7 changes: 7 additions & 0 deletions src/euphorie/content/browser/upload.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from ..user import validLoginValue
from Acquisition import aq_inner
from euphorie.content import MessageFactory as _
from euphorie.content.behaviors.hide_from_training import IHideFromTraining
from euphorie.content.behaviors.toolcategory import IToolCategory
from euphorie.content.utils import IToolTypesInfo
from io import BytesIO
Expand Down Expand Up @@ -356,6 +357,12 @@ def ImportModule(self, node, survey):
(image, caption) = self.ImportImage(image)
module.image = image
module.caption = caption

if node.get("hide_from_training"):
behavior = IHideFromTraining(module, None)
if behavior:
behavior.hide_from_training = True

return module

def ImportProfileQuestion(self, node, survey):
Expand Down
32 changes: 32 additions & 0 deletions src/euphorie/content/tests/test_functional_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,3 +133,35 @@ def testEditTitleForSubModule(self):
browser.open("%s/@@edit" % submodule.absolute_url())
self.assertTrue("Edit Module" not in browser.contents)
self.assertTrue("Edit Submodule" in browser.contents)

def testInactiveHideFromTrainingBehavior(self):
"""When `euphorie.hide_from_training` is not activated, the
`hide_from_training` field should not be visible.
"""
self.createStructure()

browser = self.get_browser(logged_in=True)
browser.open("%s/@@edit" % self.module.absolute_url())

self.assertTrue(
'name="form.widgets.IHideFromTraining.hide_from_training:list"'
not in browser.contents
)

def testActiveHideFromTrainingBehavior(self):
"""When `euphorie.hide_from_training` is activated, the
`hide_from_training` field should be visible.
"""
self.createStructure()

portal_types = api.portal.get_tool("portal_types")
fti = portal_types["euphorie.module"]
fti.behaviors = tuple(list(fti.behaviors) + ["euphorie.hide_from_training"])

browser = self.get_browser(logged_in=True)
browser.open("%s/@@edit" % self.module.absolute_url())

self.assertTrue(
'name="form.widgets.IHideFromTraining.hide_from_training:list"'
in browser.contents
)

0 comments on commit 79ef584

Please sign in to comment.