add cyberapps package with 2 sub-packages, + Python3 fixes

This commit is contained in:
Helmut Merz 2024-09-30 17:42:27 +02:00
parent 812bd28bc2
commit 3524df21a1
31 changed files with 3787 additions and 2 deletions

View file

@ -0,0 +1,86 @@
=======================
CyberConcepts Marketing
=======================
Note: This package depends on loops.
Let's do some basic set up
>>> from zope.app.testing.setup import placefulSetUp, placefulTearDown
>>> site = placefulSetUp(True)
>>> from zope import component, interface
and setup a simple loops site with a concept manager and some concepts
(with all the type machinery, what in real life is done via standard
ZCML setup):
>>> from loops.interfaces import ILoops, IConcept
>>> from loops.concept import Concept
>>> from loops.setup import ISetupManager
from loops.knowledge.setup import SetupManager
component.provideAdapter(SetupManager, (ILoops,), ISetupManager,
name='knowledge')
>>> from loops.tests.setup import TestSite
>>> t = TestSite(site)
>>> concepts, resources, views = t.setup()
Project References
==================
>>> from cyberapps.ccmkg.interfaces import IProjectReference
>>> from cyberapps.ccmkg.data import ProjectReferenceAdapter
>>> component.provideAdapter(ProjectReferenceAdapter)
>>> typeConcept = concepts.getTypeConcept()
>>> from loops.setup import addAndConfigureObject
We can now create the project reference concept type...
>>> tProjRef = addAndConfigureObject(concepts, Concept, 'projectreference',
... title=u'Project Reference', conceptType=typeConcept,
... typeInterface=IProjectReference)
... and a few projectreferences.
>>> ref1 = addAndConfigureObject(concepts, Concept, 'ref1',
... title=u'Reference #1', conceptType=tProjRef,
... timeRange=u'2006-06', task=u'Development',
... customerInfo=u'Goggle Inc', technology=u'Zope 3')
>>> ref2 = addAndConfigureObject(concepts, Concept, 'ref2',
... title=u'Reference #2', conceptType=tProjRef,
... timeRange=u'2007-01 to 2007-04', task=u'Development',
... customerInfo=u'San Narciso College', technology=u'Python')
>>> ref3 = addAndConfigureObject(concepts, Concept, 'ref3',
... title=u'Reference #3', conceptType=tProjRef,
... timeRange=u'2007-01 to 2007-05', task=u'Consulting',
... customerInfo=u'MASA', technology=u'Linux')
The Project Listing view
------------------------
>>> from cybertools.reporter.resultset import ContentRow
>>> from cybertools.reporter.interfaces import IRow
>>> component.provideAdapter(ContentRow, provides=IRow)
>>> from cyberapps.ccmkg.browser import ProjectListing
>>> from zope.publisher.browser import TestRequest
>>> from loops.view import Node
>>> node = views['n1'] = Node()
>>> node.target = tProjRef
>>> #view = ProjectListing(node, TestRequest())
>>> view = ProjectListing(tProjRef, TestRequest())
>>> rs = view.resultSet
>>> rows = list(rs.getRows())
>>> for r in rows:
... data = r.applyTemplate()
... print(data['title'], data['timeRange'], data['customerInfo'])
Reference #3 2007-01 to 2007-05 MASA
Reference #2 2007-01 to 2007-04 San Narciso College
Reference #1 2006-06 Goggle Inc

View file

@ -0,0 +1,3 @@
"""
$Id$
"""

View file

@ -0,0 +1,93 @@
#
# Copyright (c) 2007 Helmut Merz helmutm@cy55.de
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
"""
View classes for cyberconcepts marketing.
$Id$
"""
from zope.app.pagetemplate import ViewPageTemplateFile
from zope.cachedescriptors.property import Lazy
from cybertools.composer.schema import Schema
from cybertools.composer.schema import Field
from cybertools.reporter.browser.report import DetailView, ListingView
from cybertools.reporter.resultset import ResultSet, Cell
#from loops.browser.node import NodeView
from loops.browser.action import DialogAction
from loops.browser.concept import ConceptView
from loops.common import adapted
from loops import util
class ProjectDetail(ConceptView, DetailView):
def getActions(self, category='object', page=None, target=None):
actions = []
if category == 'portlet':
actions.append(DialogAction(self, title='Edit Project Reference...',
description='Modify project reference.',
viewName='edit_concept.html',
dialogName='editProjectReference',
page=page, target=target))
return actions
listingTemplate = ViewPageTemplateFile('macros.pt')
class ProjectListing(ConceptView, ListingView):
@property
def macro(self):
return listingTemplate.macros['listing']
def getActions(self, category='object', page=None, target=None):
actions = []
if category == 'portlet':
actions.append(DialogAction(self, title='Create Project Reference...',
description='Create a new project reference.',
viewName='create_concept.html',
dialogName='createProjectReference',
typeToken='.loops/concepts/projectreference',
fixedType=True,
innerForm='inner_concept_form.html',
page=page, target=target))
return actions
@property
def children(self):
objs = sorted((adapted(c) for c in self.context.getChildren(sort=None)),
key=lambda x: x.timeRange, reverse=True)
return objs
@Lazy
def resultSet(self):
result = ResultSet(self.children)
result.schema = Schema(
Field(u'title'),
Field(u'timeRange', u'Zeitraum'),
Field(u'customerInfo', u'Kunde'),
Field(u'task', u'Taetigkeit'),
Field(u'technology', u'Technik'),
)
result.view = self
return result

View file

@ -0,0 +1,36 @@
<!-- $Id$ -->
<configure
xmlns:zope="http://namespaces.zope.org/zope"
xmlns:browser="http://namespaces.zope.org/browser"
i18n_domain="zope">
<zope:adapter factory="cyberapps.ccmkg.data.ProjectReferenceAdapter"
trusted="True" />
<zope:class class="cyberapps.ccmkg.data.ProjectReferenceAdapter">
<require permission="zope.View"
interface="cyberapps.ccmkg.interfaces.IProjectReference" />
<require permission="zope.ManageContent"
set_schema="cyberapps.ccmkg.interfaces.IProjectReference" />
</zope:class>
<zope:adapter
name="ccmkg_projects.html"
for="loops.interfaces.IConcept
zope.publisher.interfaces.browser.IBrowserRequest"
provides="zope.interface.Interface"
factory="cyberapps.ccmkg.browser.ProjectListing"
permission="zope.View"
/>
<zope:adapter
name="ccmkg_projectref.html"
for="loops.interfaces.IConcept
zope.publisher.interfaces.browser.IBrowserRequest"
provides="zope.interface.Interface"
factory="cyberapps.ccmkg.browser.ProjectDetail"
permission="zope.View"
/>
</configure>

22
cyberapps/ccmkg/data.py Normal file
View file

@ -0,0 +1,22 @@
# cyberapps.ccmkg.data
""" Classes for Cyberconcepts Marketing.
"""
from zope.component import adapts
from zope.interface import implementer
from cyberapps.ccmkg.interfaces import IProjectReference
from loops.common import AdapterBase
from loops.interfaces import IConcept
from loops.type import TypeInterfaceSourceList
TypeInterfaceSourceList.typeInterfaces += (IProjectReference,)
@implementer(IProjectReference)
class ProjectReferenceAdapter(AdapterBase):
_contextAttributes = list(IProjectReference) + list(IConcept)

View file

@ -0,0 +1,55 @@
#
# Copyright (c) 2007 Helmut Merz helmutm@cy55.de
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
"""
Interfaces for organizational stuff like persons, addresses, tasks, ...
$Id$
"""
from zope.interface import Interface, Attribute
from zope import schema
from loops.interfaces import IConceptSchema, ILoopsAdapter
from loops.util import _
class IProjectReference(IConceptSchema, ILoopsAdapter):
""" Project information to be used as customer reference for
marketing purposes.
"""
customerInfo = schema.TextLine(
title=_(u'Customer'),
description=_(u'Short description or - if appropriate - '
'name of the customer'),
required=False,)
timeRange = schema.TextLine(
title=_(u'Time range'),
description=_(u'Info about the time range of the project'),
required=False,)
task = schema.TextLine(
title=_(u'Task'),
description=_(u'Short description of our task in the project'),
required=False,)
technology = schema.TextLine(
title=_(u'Technology'),
description=_(u'Info about the technology employed in '
'the project'),
required=False,)

34
cyberapps/ccmkg/macros.pt Normal file
View file

@ -0,0 +1,34 @@
<!-- cyberapps.ccmkg project references listing -->
<metal:listing define-macro="listing">
<h2 tal:content="item/title"
tal:attributes="ondblclick python: item.openEditWindow('configure.html')">
Something</h2><br />
<table tal:define="result item/resultSet">
<tbody tal:repeat="row result/getRows">
<tal:row define="data row/applyTemplate">
<tr tal:repeat="field row/fields">
<tal:cell define="attr field/name">
<td colspan="2" bgcolor="#d0d0ff"
tal:condition="python: attr == 'title'">
<a href="#"
tal:attributes="href python: view.getUrlForTarget(row.context.context);
title row/context/description">
<b tal:content="data/?attr" />
</a>
</td>
<tal:field tal:condition="python: attr != 'title'">
<td width="20%">
<span tal:content="field/title"
i18n:translate="">Fieldname</span>:
</td>
<td tal:content="data/?attr">Value</td>
</tal:field>
</tal:cell>
</tr>
<tr colspan="2"><td>&nbsp;</td></tr>
</tal:row>
</tbody>
</table>
</metal:listing>

23
cyberapps/ccmkg/tests.py Executable file
View file

@ -0,0 +1,23 @@
# cyberapps.ccmkg.tests
import unittest, doctest
from zope.testing.doctestunit import DocFileSuite
from zope.interface.verify import verifyClass
class Test(unittest.TestCase):
"Basic tests for the cyberapps.ccmkg package."
def testSomething(self):
pass
def test_suite():
flags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS
return unittest.TestSuite((
unittest.makeSuite(Test),
DocFileSuite('README.txt', optionflags=flags),
))
if __name__ == '__main__':
unittest.main(defaultTest='test_suite')

View file

@ -0,0 +1,62 @@
=============================
Knowledge Management Specials
=============================
Note: This package depends on loops.
Let's do some basic set up
>>> from zope.app.testing.setup import placefulSetUp, placefulTearDown
>>> site = placefulSetUp(True)
>>> from zope import component, interface
and setup a simple loops site with a concept manager and some concepts
(with all the type machinery, what in real life is done via standard
ZCML setup):
>>> from loops.interfaces import ILoops, IConcept
>>> from loops.concept import Concept
>>> from loops.tests.setup import TestSite
>>> t = TestSite(site)
>>> concepts, resources, views = t.setup()
>>> loopsRoot = site['loops']
We then import a loops .dmp file containing all necessary types and
predicates.
>>> from loops.knowledge.tests import importData
>>> importData(loopsRoot)
>>> from cyberapps.knowledge.tests import importData
>>> importData(loopsRoot)
Job Positions
=============
Job positions, qualifications, interpersonal skills
---------------------------------------------------
>>> from cyberapps.knowledge.browser.qualification import JobPositionsOverview
>>> from cyberapps.knowledge.browser.qualification import IPSkillsForm
Person data
-----------
>>> from cyberapps.knowledge.browser.person import JobPersonsOverview
Competences Questionnaire
=========================
>>> from cyberapps.knowledge.data import IPSkillsQuestionnaire
Reporting
=========
>>> from cyberapps.knowledge.browser.report import JobsListing, JobDescription

View file

@ -0,0 +1,3 @@
"""
cyberapps.knowledge
"""

View file

@ -0,0 +1,3 @@
"""
cyberapps.knowledge.browser
"""

View file

@ -0,0 +1,32 @@
/* <script> */
chartData1 = [4, 5, 3];
chartData2 = [3, 3, 1];
function drawBars() {
require([
"dojox/charting/Chart", "dojox/charting/plot2d/ClusteredBars",
"dojox/charting/themes/Claro", "dojox/charting/axis2d/Default",
"dojo/domReady!"],
function(Chart, Bars, theme) {
var chart = new Chart("chartNode");
chart.setTheme(theme);
chart.addPlot("default", {
type: Bars,
gap: 5,
maxBarSize: 20,
labels: true, labelStyle: "outside", labelOffset: -10});
chart.addAxis("x", {
vertical: true, minorTicks: false,
labels: [{value: 1, text: 'Beharrlichkeit'},
{value: 2, text: 'Belastbarkeit'},
{value: 3, text: 'Einsatzbereitschaft'}]});
chart.addAxis("y", {fixLower: "major", fixUpper: "major", min: 0,
minorTicks: false});
chart.addSeries("test 1", chartData1, {
stroke: {color: "blue", width: 2}, fill: "lightblue"});
chart.addSeries("test 2", chartData2, {
stroke: {color: "red", width: 2}, fill: "#ffaaaa"});
chart.render();
});
}
/* </script> */

View file

@ -0,0 +1,102 @@
<configure
xmlns:zope="http://namespaces.zope.org/zope"
xmlns:browser="http://namespaces.zope.org/browser"
i18n_domain="cyberapps.knowledge">
<browser:resource name="cyberapps.knowledge.css" file="knowledge.css" />
<zope:adapter
name="jobpositions.html"
for="loops.interfaces.IConcept
zope.publisher.interfaces.browser.IBrowserRequest"
provides="zope.interface.Interface"
factory="cyberapps.knowledge.browser.qualification.JobPositionsOverview"
permission="zope.View" />
<zope:adapter
name="jobpersons.html"
for="loops.interfaces.IConcept
zope.publisher.interfaces.browser.IBrowserRequest"
provides="zope.interface.Interface"
factory="cyberapps.knowledge.browser.person.JobPersonsOverview"
permission="zope.View" />
<browser:page
name="del_jobposition"
for="loops.interfaces.IConceptSchema"
class="cyberapps.knowledge.browser.qualification.DeleteJobPosition"
permission="zope.View" />
<browser:page
name="copy_jpprofiles"
for="loops.interfaces.IConceptSchema"
class="cyberapps.knowledge.browser.qualification.CopyJPProfiles"
permission="zope.View" />
<browser:page
name="edit_jpdescription.html"
for="loops.interfaces.IConceptSchema"
class="cyberapps.knowledge.browser.qualification.JPDescForm"
permission="zope.View" />
<browser:page
name="edit_ipskillsreq.html"
for="loops.interfaces.IConceptSchema"
class="cyberapps.knowledge.browser.qualification.IPSkillsForm"
permission="zope.View" />
<browser:page
name="edit_qualificationsreq.html"
for="loops.interfaces.IConceptSchema"
class="cyberapps.knowledge.browser.qualification.QualificationsForm"
permission="zope.View" />
<browser:page
name="edit_jobassignments.html"
for="loops.interfaces.IConceptSchema"
class="cyberapps.knowledge.browser.person.JobAssignmentsForm"
permission="zope.View" />
<browser:page
name="edit_qualifications.html"
for="loops.interfaces.IConceptSchema"
class="cyberapps.knowledge.browser.person.QualificationsForm"
permission="zope.View" />
<browser:page
name="edit_skills.html"
for="loops.interfaces.IConceptSchema"
class="cyberapps.knowledge.browser.person.SkillsForm"
permission="zope.View" />
<zope:adapter
name="jobslisting.html"
for="loops.interfaces.IConcept
zope.publisher.interfaces.browser.IBrowserRequest"
provides="zope.interface.Interface"
factory="cyberapps.knowledge.browser.report.JobsListing"
permission="zope.View" />
<zope:adapter
name="referred_listing.html"
for="loops.interfaces.IConcept
zope.publisher.interfaces.browser.IBrowserRequest"
provides="zope.interface.Interface"
factory="cyberapps.knowledge.browser.person.ReferredListing"
permission="zope.View" />
<zope:adapter
name="jobdescription.html"
for="loops.interfaces.IConcept
zope.publisher.interfaces.browser.IBrowserRequest"
provides="zope.interface.Interface"
factory="cyberapps.knowledge.browser.report.JobDescription"
permission="zope.View" />
<browser:page
name="job_report.html"
for="cyberapps.knowledge.interfaces.IJobPosition"
class="cyberapps.knowledge.browser.report.JobReport"
permission="zope.View" />
</configure>

View file

@ -0,0 +1,112 @@
/*
Basic settings for knowledge management.
*/
.input input, div.dijitInputField {
font-size: 100%;
font-family: monospace;
}
table.grid td, th {
vertical-align: top;
border: 1px solid black;
}
tr.grid td, th {
vertical-align: top;
border: 1px solid black;
}
tr.grid td.rbcolumn {
border-left: none;
border-right: none;
text-align: center;
}
tr.grid td.rbcolumn input {
margin-top: 0;
}
tr.grid td.rbcolumnlast {
border-left: none;
text-align: center;
}
tr.grid td.rbcolumnlast input {
margin-top: 0;
}
tr.headline td, th {
font-size: 110%;
}
td.optional {
color: #666666;
}
table.jpdesc_admin div.label {
font-size: 140%;
}
table.jpdesc_admin div.description {
font-style: italic;
}
table.jpdesc_admin textarea {
border: none;
width: 100%;
height: 100%;
padding: 0;
background-color: #f8f8f8;
}
table.jpdesc_admin textarea[disabled] {
background-color: white;
}
table.jpdesc_workdesc tr.label td {
padding-top: 1.5em;
}
table.jpdesc_workdesc span.label {
font-size: 140%;
}
table.jpdesc_workdesc div.description {
font-style: italic;
}
table.jpdesc_workdesc td.input input {
border: none;
width: 100%;
padding: 0;
background-color: #f8f8f8;
}
table.jpdesc_qualif tr.label td {
padding-top: 1.5em;
}
table.jpdesc_qualif span.label {
font-weight: bold;
font-size: 120%;
}
table.jpdesc_qualif div.description {
font-style: italic;
}
table.jpdesc_qualif input {
border: none;
width: 100%;
padding: 0;
background-color: #f8f8f8;
}
@media print {
td.optional {
color: Black;
}
}

View file

@ -0,0 +1,428 @@
#
# Copyright (c) 2015 Helmut Merz helmutm@cy55.de
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
"""
Definition of view classes and other browser related stuff for the
cyberapps.knowledge package.
Person data (competences, preferences, qualifications) views.
"""
from zope.app.container.interfaces import INameChooser
from zope import interface, component
from zope.app.container.interfaces import INameChooser
from zope.app.pagetemplate import ViewPageTemplateFile
from zope.i18n import translate
from zope.cachedescriptors.property import Lazy
from zope.traversing.api import getName
from cybertools.organize.interfaces import IPerson
from cybertools.stateful.interfaces import IStateful
from loops.browser.concept import ConceptView
from loops.common import adapted, baseObject
from loops.concept import Concept
from loops.knowledge.survey.interfaces import IQuestionnaire
from loops.organize.party import getPersonForUser
from loops.setup import addObject
from loops import util
from cyberapps.knowledge.browser.qualification import QualificationBaseView
from cyberapps.knowledge.browser.qualification import template as baseTemplate
from cyberapps.knowledge.interfaces import IJobPosition
from cyberapps.knowledge.interfaces import IQualificationsRecorded, ISkillsRecorded
from cyberapps.knowledge.interfaces import _
template = ViewPageTemplateFile('person_macros.pt')
class JobPersonsOverview(QualificationBaseView, ConceptView):
template = template
baseTemplate = baseTemplate
macroName = 'persons'
def update(self):
form = self.request.form
instUid = form.get('select_institution')
if instUid:
return self.setInstitution(instUid)
@Lazy
def persons(self):
self.setupController()
result = {}
if self.options('hide_master'):
result ['master'] = []
else:
result['master'] = [PersonView(p, self.request)
for p in self.institution.getChildren([self.masterPredicate])]
result['member'] = [PersonView(p, self.request)
for p in self.institution.getChildren([self.memberPredicate])]
result['other'] = [PersonView(p, self.request,)
for p in self.institution.getChildren([self.defaultPredicate])
if IPerson.providedBy(adapted(p))]
return result
class PersonView(QualificationBaseView, ConceptView):
template = template
baseTemplate = baseTemplate
pageTitle = None
@Lazy
def title(self):
return self.getTitle()
def getTitle(self):
if self.pageTitle is None:
return self.context.title
lang = self.languageInfo.language
pageTitle = translate(_(self.pageTitle), target_language=lang)
return '%s: %s' % (pageTitle, self.context.title)
@Lazy
def breadcrumbsParent(self):
for p in self.context.conceptType.getParents([self.queryTargetPredicate]):
return self.nodeView.getViewForTarget(p)
for p in self.context.getParents([self.queryTargetPredicate]):
return self.nodeView.getViewForTarget(p)
@Lazy
def jobs(self):
result = []
for ch in self.institution.getChildren([self.defaultPredicate]):
job = adapted(ch)
if IJobPosition.providedBy(job):
result.append(job)
return result
def jobAssignments(self):
jobs = []
for p in self.context.getParents([self.defaultPredicate]):
job = adapted(p)
if IJobPosition.providedBy(job):
jobs.append(job)
if jobs:
state = 'active'
text = 'active'
else:
state = 'none'
text = 'to be done'
action = 'edit_jobassignments.html'
editUrl = '%s/%s' % (self.nodeView.getUrlForTarget(self.context), action)
return dict(text=text, editUrl=editUrl, jobs=jobs)
def qualifications(self):
qualifications = self.getQualifications(adapted(self.target))
if qualifications is None:
state = 'none'
text = 'to be done'
else:
stf = component.getAdapter(baseObject(qualifications), IStateful,
name='task_states')
state = stf.state
text = stf.getStateObject().title
action = 'edit_qualifications.html'
editUrl = '%s/%s' % (self.nodeView.getUrlForTarget(self.context), action)
return dict(text=text, editUrl=editUrl)
def skills(self):
skills = self.getSkills(adapted(self.target))
if not skills:
state = 'none'
text = 'to be done'
else:
stf = component.getAdapter(baseObject(skills), IStateful,
name='task_states')
state = stf.state
text = stf.getStateObject().title
action = 'edit_skills.html'
editUrl = '%s/%s' % (self.nodeView.getUrlForTarget(self.context), action)
return dict(text=text, editUrl=editUrl)
def ipskills(self):
ipskills = []
if not ipskills:
state = 'none'
text = 'to be done'
questionnaire = None
for ch in self.breadcrumbsParent.context.getChildren(
[self.defaultPredicate]):
qu = adapted(ch)
if IQuestionnaire.providedBy(qu):
questionnaire = ch
break
if questionnaire is None:
return dict(text='questionnaire missing', editUrl=None)
personUid = util.getUidForObject(self.context)
storage = self.loopsRoot['records']['survey_responses']
tracks = storage.getUserTracks(qu.uid, 0, personUid)
if tracks:
#text = state = 'draft'
text = state = tracks[0].data.get('state') or 'draft'
editUrl = '%s?person=%s' % (
self.nodeView.getUrlForTarget(questionnaire), personUid)
return dict(text=text, editUrl=editUrl)
def preferences(self):
preferences = []
if not preferences:
state = 'none'
text = 'to be done'
questionnaire = None
idx = 0
for ch in self.breadcrumbsParent.context.getChildren(
[self.defaultPredicate]):
qu = adapted(ch)
if IQuestionnaire.providedBy(qu):
if idx > 0:
questionnaire = ch
break
idx += 1
if questionnaire is None:
return dict(text='questionnaire missing', editUrl=None)
personUid = util.getUidForObject(self.context)
storage = self.loopsRoot['records']['survey_responses']
tracks = storage.getUserTracks(qu.uid, 0, personUid)
if tracks:
text = state = 'draft'
editUrl = '%s?person=%s' % (
self.nodeView.getUrlForTarget(questionnaire), personUid)
return dict(text=text, editUrl=editUrl)
def getQualifications(self, person):
for c in baseObject(person).getChildren():
obj = adapted(c)
if IQualificationsRecorded.providedBy(obj):
return obj
def getSkills(self, person):
for c in baseObject(person).getChildren():
obj = adapted(c)
if ISkillsRecorded.providedBy(obj):
return obj
class ReferredListing(JobPersonsOverview):
macroName = 'referred_listing'
@Lazy
def persons(self):
self.setupController()
for ch in self.context.getChildren([self.defaultPredicate]):
if IQuestionnaire.providedBy(adapted(ch)):
baseUrl = self.nodeView.getUrlForTarget(ch)
break
else:
return [dict(title='Questionnaire missing')]
me = getPersonForUser(self.context, self.request)
result = [adapted(p) for p in self.institution.getChildren()
if p != me]
result = [dict(title=p.title,
url='%s?person=%s' % (baseUrl, p.uid))
for p in result if IPerson.providedBy(p)]
return result
class JobAssignmentsForm(PersonView):
""" Form for assigning jobs to a person.
"""
macroName = 'jobassignmentsform'
pageTitle = 'label_jobs_assigned'
def getData(self):
result = []
assignments = self.jobAssignments()
for job in self.jobs:
checked = job in assignments['jobs']
result.append(dict(title=job.title, uid=job.uid, checked=checked))
return result
def update(self):
form = self.request.form
if form.get('button_cancel'):
url = self.breadcrumbsParent.targetUrl
self.request.response.redirect(url)
return False
if not form.get('submit_save'):
return True
current = self.jobAssignments()['jobs']
newUids = form.get('assignments') or []
for job in self.jobs:
if job.uid in newUids:
if job not in current:
self.context.assignParent(baseObject(job))
else:
if job in current:
self.context.deassignParent(baseObject(job))
return True
class QualificationsForm(PersonView):
""" Form for entering qualifications for a person.
"""
macroName = 'qualificationsform'
pageTitle = 'label_qualifications'
textParentName = 'qualificationsrecorded'
def getTitle(self):
if not IPerson.providedBy(self.adapted):
dummy = self.breadcrumbsParent # evaluate before tweaking context
self.context = getPersonForUser(self.context, self.request)
self.adapted = adapted(self.context)
return super(QualificationsForm, self).getTitle()
def getData(self):
self.setupController()
self.registerDojoComboBox()
form = self.request.form
if form.get('button_cancel'):
url = self.breadcrumbsParent.targetUrl
self.request.response.redirect(url)
return []
data = {}
input = form.get('qualifications')
for item in (input or []):
data[item['key']] = item
if data:
self.update(data)
else:
qu = self.getQualifications(adapted(self.target))
if qu is not None:
data = qu.data
result = []
qualifications = self.conceptManager['qualifications']
for obj in qualifications.getChildren([self.defaultPredicate]):
uid = util.getUidForObject(obj)
dataRow = data.get(uid) or {}
item = dict(key=uid, label=obj.title,
desc=obj.description,
certVocabulary=adapted(obj).certVocabulary or [],
subitems=[], schema=[],
value=dataRow.get('value') or (3 * [u'']),
cert=dataRow.get('cert') or (3 * [u'']))
for subitem in obj.getChildren([self.defaultPredicate]):
item['subitems'].append(dict(
uid=util.getUidForObject(subitem),
title=subitem.title))
for row in adapted(obj).data.values():
key = row[0]
if len(row) < 6:
continue
value = dataRow.get('qu_' + key) or (3 * [u''])
item['schema'].append(dict(
key=key, label=row[1],
level=row[2], type=row[4],
vocabulary=row[5].split(';'),
value=value))
result.append(item)
return result
def update(self, data):
person = adapted(self.target)
qu = self.getQualifications(person)
if qu is None:
qu = self.createQualifications(person)
qu.data = data
return self.processStateTransition(qu)
def createQualifications(self, person):
concepts = self.conceptManager
name = 'qu.' + person.name
name = INameChooser(concepts).chooseName(name, None)
type = concepts['qualificationsrecorded']
obj = addObject(concepts, Concept, name, type=type,
title='Qualifications: ' + person.title)
baseObject(person).assignChild(obj)
return adapted(obj)
class SkillsForm(QualificationsForm):
""" Form for entering skills for a person.
"""
macroName = 'skillsform'
pageTitle = 'label_skills'
textParentName = 'skillsrecorded'
def getData(self):
self.setupController()
self.registerDojoComboBox()
form = self.request.form
if form.get('button_cancel'):
url = self.breadcrumbsParent.targetUrl
self.request.response.redirect(url)
return {}
data = {}
input = form.get('skills') or []
for key in input:
row = form.get('skills.' + key)
if row['value']:
if not data:
data = dict(value=[], exp=[], int=[])
data['value'].append(row['value'])
data['exp'].append(row['exp'])
data['int'].append(row['int'])
if data:
self.update(data)
else:
sk = self.getSkills(adapted(self.target))
if sk is not None:
data = sk.data
obj = self.conceptManager['skills']
item = dict(subitems=[],
value=data.get('value') or [],
exp=data.get('exp') or [],
int=data.get('int') or [])
fill(item['value'], u'', 15)
fill(item['exp'], u'0', 15)
fill(item['int'], u'0', 15)
for subitem in obj.getChildren([self.defaultPredicate]):
item['subitems'].append(dict(
uid=util.getUidForObject(subitem),
title=subitem.title))
return item
def update(self, data):
person = adapted(self.target)
qu = self.getSkills(person)
if qu is None:
qu = self.createSkills(person)
qu.data = data
return self.processStateTransition(qu)
def createSkills(self, person):
concepts = self.conceptManager
name = 'sk.' + person.name
name = INameChooser(concepts).chooseName(name, None)
type = concepts['skillsrecorded']
obj = addObject(concepts, Concept, name, type=type,
title='Skills: ' + person.title)
baseObject(person).assignChild(obj)
return adapted(obj)
def fill(lst, v, length):
lst.extend((length - len(lst)) * [v])

View file

@ -0,0 +1,340 @@
<html i18n:domain="cyberapps.knowledge">
<metal:persons define-macro="persons"
tal:define="dummy item/update">
<metal:block use-macro="view/concept_macros/concepttitle" />
<metal:buttons use-macro="item/knowledge_macros/select_institution" />
<form method="post">
<table class="grid"
tal:define="persons item/persons">
<tal:block condition="persons/master">
<tr><td colspan="6"><h3 i18n:translate="">label_managers</h3></td></tr>
<tr metal:use-macro="item/template/macros/person_headline" />
<tr tal:repeat="person persons/master">
<metal:person use-macro="item/template/macros/person" />
</tr>
</tal:block>
<tal:block condition="persons/member">
<tr><td colspan="6"><h3 i18n:translate="">label_employees</h3></td></tr>
<tr metal:use-macro="item/template/macros/person_headline" />
<tr tal:repeat="person persons/member">
<metal:person use-macro="item/template/macros/person" />
</tr>
</tal:block>
<tal:block condition="persons/other">
<tr><td colspan="6"><h3 i18n:translate="">label_others</h3></td></tr>
<tr metal:use-macro="item/template/macros/person_headline" />
<tr tal:repeat="person persons/other">
<metal:person use-macro="item/template/macros/person" />
</tr>
</tal:block>
</table>
<div metal:use-macro="item/baseTemplate/macros/backbutton" />
</form>
</metal:persons>
<tr metal:define-macro="person_headline">
<th width="20%"
i18n:translate="">label_person_fullname</th>
<th width="16%"
i18n:translate="">label_jobs_assigned</th>
<th width="16%"
i18n:translate="">label_qualifications</th>
<th width="16%"
i18n:translate="">label_skills</th>
<th width="16%"
i18n:translate="">label_ipskills</th>
<th width="16%"
i18n:translate="">label_preferences</th>
</tr>
<metal:person define-macro="person">
<td><a tal:attributes="href python:view.getUrlForTarget(person)"
tal:content="person/title" /></td>
<tal:block define="jobs person/jobAssignments">
<td>
<span i18n:translate=""
tal:content="jobs/text" />
<a title="edit job assignments"
i18n:attributes="title"
tal:attributes="href jobs/editUrl">
<img tal:attributes="src
string:${resourceBase}cybertools.icons/page_edit.png" />
</a>
</td>
</tal:block>
<tal:block define="qualifications person/qualifications">
<td>
<span i18n:translate=""
tal:content="qualifications/text" />
<a title="edit qualifications"
i18n:attributes="title"
tal:attributes="href qualifications/editUrl">
<img tal:attributes="src
string:${resourceBase}cybertools.icons/page_edit.png" />
</a>
</td>
</tal:block>
<tal:block define="skills person/skills">
<td>
<span i18n:translate=""
tal:content="skills/text" />
<a title="edit skills"
i18n:attributes="title"
tal:attributes="href skills/editUrl">
<img tal:attributes="src
string:${resourceBase}cybertools.icons/page_edit.png" />
</a>
</td>
</tal:block>
<tal:block define="ipskills person/ipskills">
<td>
<span i18n:translate=""
tal:content="ipskills/text" />
<a title="edit ipskills"
tal:condition="ipskills/editUrl"
i18n:attributes="title"
tal:attributes="href ipskills/editUrl">
<img tal:attributes="src
string:${resourceBase}cybertools.icons/page_edit.png" />
</a>
</td>
</tal:block>
<tal:block define="preferences person/preferences">
<td>
<span i18n:translate=""
tal:content="preferences/text" />
<a title="edit preferences"
i18n:attributes="title"
tal:attributes="href preferences/editUrl">
<img tal:attributes="src
string:${resourceBase}cybertools.icons/page_edit.png" />
</a>
</td>
</tal:block>
</metal:person>
<metal:referred define-macro="referred_listing"
tal:define="dummy item/update">
<metal:block use-macro="view/concept_macros/concepttitle" />
<metal:buttons use-macro="item/knowledge_macros/select_institution" />
<form method="post">
<table class="grid"
tal:define="persons item/persons">
<tr tal:repeat="person persons">
<td>
<a tal:attributes="href person/url|nothing"
tal:content="person/title" />
</td>
<td>&nbsp;</td>
</tr>
</table>
<div metal:use-macro="item/baseTemplate/macros/backbutton" />
</form>
</metal:referred>
<metal:form define-macro="jobassignmentsform"
tal:define="show item/update">
<tal:show condition="show">
<metal:block use-macro="view/concept_macros/concepttitle" />
<div style="font-size: 120%; padding-bottom: 10px">
<span i18n:translate="">Organisation/Team</span>:
<b tal:content="item/institution/title" />
</div>
<form method="post">
<div tal:repeat="job item/getData">
<input type="checkbox" name="assignments:list"
tal:attributes="value job/uid;
checked job/checked" />
<span tal:content="job/title" />
</div>
<br />
<metal:buttons use-macro="item/baseTemplate/macros/buttons2back" />
</form>
</tal:show>
</metal:form>
<metal:form define-macro="qualificationsform">
<metal:block use-macro="view/concept_macros/concepttitle" />
<form method="post"
tal:define="data item/getData;
texts item/getTexts">
<tal:qualifications define="fname string:qualifications">
<tal:text define="info texts/1|nothing"
condition="info">
<div class="noprint"
tal:content="structure info/text" />
</tal:text>
<br />
<table class="jpdesc_qualif" width="100%">
<tr class="grid headline">
<td width="35%"
i18n:translate="">label_category</td>
<td width="30%"
i18n:translate="">label_knowledge_level</td>
<td width="35%"
i18n:translate="">label_certificate</td>
</tr>
<tal:quitem repeat="row data">
<tr class="grid">
<td colspan="3">
<input type="hidden"
tal:attributes="name string:$fname.key:records;
value row/key;" />
<span class="label"
tal:content="row/label" />
<div class="description noprint"
tal:content="row/desc" />
</td>
</tr>
<tal:group repeat="field python:row['schema'] or [None]">
<tr class="grid"
tal:condition="field">
<td tal:content="field/label" />
<td tal:content="field/level" />
<td i18n:translate="">label_certificate</td>
</tr>
<tr class="grid"
tal:repeat="textRow python:range(3)">
<td class="input"
tal:attributes="colspan python:field and '1' or '2'">
<select data-dojo-type="dijit/form/ComboBox"
style="width: 100%"
tal:condition="row/subitems"
tal:attributes="name string:$fname.value:list:records;
value python:row['value'][textRow]">
<option selected></option>
<option tal:repeat="value row/subitems"
tal:content="value/title"></option>
</select>
<input tal:condition="not:row/subitems"
tal:attributes="name string:$fname.value:list:records;
value python:row['value'][textRow]" />
</td>
<td class="input"
tal:condition="field">
<tal:field define="name string:qu_${field/key};
macro field/type;
value field/value">
<metal:input use-macro="item/baseTemplate/macros/?macro" />
</tal:field>
</td>
<td class="input"
tal:define="voc row/certVocabulary">
<select data-dojo-type="dijit/form/ComboBox"
style="width: 100%"
tal:condition="voc"
tal:attributes="name string:$fname.cert:list:records;
value python:row['cert'][textRow]">
<option tal:repeat="vitem voc"
tal:content="vitem" />
</select>
<input tal:condition="not:voc"
tal:attributes="name string:$fname.cert:list:records;
value python:row['cert'][textRow]" />
</td>
</tr>
</tal:group>
</tal:quitem>
</table>
</tal:qualifications>
<br />
<metal:buttons use-macro="item/baseTemplate/macros/buttons" />
</form>
</metal:form>
<metal:form define-macro="skillsform">
<metal:block use-macro="view/concept_macros/concepttitle" />
<form method="post"
tal:define="data item/getData;
texts item/getTexts">
<tal:skills define="fname string:skills">
<tal:text define="info texts/1|nothing"
condition="info">
<div class="noprint"
tal:content="structure info/text" />
</tal:text>
<br />
<table class="jpdesc_qualif" width="100%">
<tr class="grid headline">
<td i18n:translate="">label_skills</td>
<td width="20%" class="center" colspan="5"
i18n:translate="">label_experience</td>
<td width="20%" class="center" colspan="5"
i18n:translate="">label_interest</td>
</tr>
<tr class="grid">
<td></td>
<td tal:repeat="value python:range(5)"
i18n:attributes="title"
tal:attributes="title string:option_exp_$value;
class python:
repeat['value'].end() and 'rbcolumnlast' or 'rbcolumn';"
tal:content="python:value + 1"></td>
<td tal:repeat="value python:range(5)"
i18n:attributes="title"
tal:attributes="title string:option_int_$value;
class python:
repeat['value'].end() and 'rbcolumnlast' or 'rbcolumn'"
tal:content="python:value + 1"></td>
</tr>
<tal:quitem define="row data"
condition="row">
<tr class="grid"
tal:repeat="textRow python:range(15)">
<td>
<input type="hidden"
tal:attributes="name string:$fname:list;
value textRow" />
<select data-dojo-type="dijit/form/ComboBox"
tal:condition="row/subitems"
tal:attributes="name string:$fname.$textRow.value:record;
value python:row['value'][textRow]">
<option selected></option>
<option tal:repeat="value row/subitems"
tal:content="value/title"></option>
</select>
<input tal:condition="not:row/subitems"
tal:attributes="name string:$fname.$textRow.value:record;
value python:row['value'][textRow]" />
</td>
<td tal:repeat="value python:range(5)"
tal:attributes="class python:
repeat['value'].end() and 'rbcolumnlast' or 'rbcolumn'">
<input type="radio" style="width: auto"
i18n:attributes="title"
tal:attributes="name string:$fname.$textRow.exp:record;
title string:option_exp_$value;
value value;
checked python:
str(value) == row['exp'][textRow]" />
</td>
<td tal:repeat="value python:range(5)"
tal:attributes="class python:
repeat['value'].end() and 'rbcolumnlast' or 'rbcolumn'">
<input type="radio" style="width: auto"
i18n:attributes="title"
tal:attributes="name string:$fname.$textRow.int:record;
title string:option_int_$value;
value value;
checked python:
str(value) == row['int'][textRow]" />
</td>
</tr>
</tal:quitem>
</table>
</tal:skills>
<br />
<metal:buttons use-macro="item/baseTemplate/macros/buttons" />
</form>
</metal:form>
</html>

View file

@ -0,0 +1,501 @@
#
# Copyright (c) 2015 Helmut Merz helmutm@cy55.de
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
"""
Definition of view classes and other browser related stuff for the
cyberapps.knowledge package.
Base classes, job position and requirements views.
"""
from copy import deepcopy
from zope import interface, component
from zope.app.container.interfaces import INameChooser
from zope.app.pagetemplate import ViewPageTemplateFile
from zope.cachedescriptors.property import Lazy
from zope.traversing.api import getName
from cybertools.browser.action import actions
from cybertools.stateful.interfaces import IStateful
from loops.browser.action import DialogAction
from loops.browser.concept import ConceptView
from loops.common import adapted, baseObject, generateNameFromTitle
from loops.concept import Concept
from loops.knowledge.browser import InstitutionMixin
from loops.organize.party import getPersonForUser
from loops.organize.personal import favorite
from loops.organize.personal.interfaces import IFavorites
from loops.security.common import checkPermission
from loops.setup import addObject
from loops import util
from cyberapps.knowledge.interfaces import _
template = ViewPageTemplateFile('qualification_macros.pt')
actions.register('createJobPosition', 'portlet', DialogAction,
title=_(u'Create Job...'),
description=_(u'Create a new job / position.'),
viewName='create_concept.html',
dialogName='createPosition',
typeToken='.loops/concepts/jobposition',
fixedType=True,
innerForm='inner_concept_form.html',
permission='loops.AssignAsParent',
)
class QualificationBaseView(InstitutionMixin):
template = template
templateName = 'knowledge.qualification'
showInBreadcrumbs = True
textKeys = ['1'] # should be overridden by subclass
textParentName = None # to be specified by subclass
def setupController(self):
cm = self.controller.macros
cm.register('css',
identifier='cyberapps.knowledge.css',
resourceName='cyberapps.knowledge.css',
media='all', priority=90)
def getTexts(self):
result = {}
if not self.textParentName:
return result
textKeys = self.textKeys
parent = self.conceptManager[self.textParentName]
for idx, r in enumerate(parent.getResources()[:len(textKeys)]):
result[textKeys[idx]] = dict(
title=r.title, text=self.renderText(r.data, r.contentType))
return result
@Lazy
def jobPositionType(self):
return self.conceptManager['jobposition']
def registerDojoSlider(self):
self.registerDojo()
jsCall = ('dojo.require("dijit.form.HorizontalSlider");'
'dojo.require("dijit.form.HorizontalRule");'
'dojo.require("dijit.form.HorizontalRuleLabels");')
self.controller.macros.register('js-execute',
'dojo.require.HorizontalSlider', jsCall=jsCall)
def registerDojoCharting(self):
self.registerDojo()
jsCall = ('dojo.require("dojox.charting.Chart");'
'dojo.require("dojox.charting.plot2d.ClusteredBars");'
'dojo.require("dojox.charting.themes.Claro");')
self.controller.macros.register('js-execute',
'dojo.require.Charting', jsCall=jsCall)
def processStateTransition(self, obj):
stf = component.getAdapter(baseObject(obj), IStateful,
name='task_states')
state = stf.state
if self.request.form.get('submit_activate'):
if state == 'draft':
stf.doTransition('release')
url = self.breadcrumbsParent.targetUrl
self.request.response.redirect(url)
return False
elif state == 'active':
stf.doTransition('reopen')
return True
def createJob(self, title):
concepts = self.conceptManager
inst = self.institution
#name = 'jobposition.%s_%s' % (
# getName(inst), generateNameFromTitle(title))
name = 'jobposition.%s' % generateNameFromTitle(title)
name = INameChooser(concepts).chooseName(name, None)
obj = addObject(concepts, Concept, name, title=title,
type=self.jobPositionType)
obj.assignParent(inst)
return adapted(obj)
class JobPositionsOverview(QualificationBaseView, ConceptView):
macroName = 'jobpositions'
textParentName = 'data_entry'
def update(self):
form = self.request.form
if form.get('create_jobposition'):
title = form.get('form_jptitle')
if not title:
# TODO: provide error message
return True
job = self.createJob(title)
return True
instUid = form.get('select_institution')
if instUid:
return self.setInstitution(instUid)
@Lazy
def positions(self):
result = []
self.setupController()
self.registerDojoComboBox()
inst = baseObject(self.institution)
if inst is not None:
for child in inst.getChildren([self.defaultPredicate]):
if child.conceptType == self.jobPositionType:
result.append(PositionView(child, self.request))
return result
@Lazy
def jobTitles(self):
table = self.conceptManager.get('job_names')
if table is None:
return []
return [v[0] for v in adapted(table).data.values()]
class PositionView(QualificationBaseView, ConceptView):
parentName = None
@Lazy
def breadcrumbsParent(self):
parent = None
if self.parentName is not None:
parent = self.conceptManager.get(self.parentName)
if parent is None:
for p in self.context.conceptType.getParents([self.queryTargetPredicate]):
parent = p
return self.nodeView.getViewForTarget(parent)
@Lazy
def copyUrl(self):
return '%s/copy_jpprofiles' % (self.nodeView.getUrlForTarget(self.context))
@Lazy
def deleteUrl(self):
#for f in (self.jpDescription, self.ipskillsRequired,
# self.qualificationsRequired):
# if f['state'] != 'none':
# return None
return '%s/del_jobposition' % (self.nodeView.getUrlForTarget(self.context))
@Lazy
def jpDescription(self):
jpDesc = adapted(self.target).getJPDescription()
if jpDesc is None:
state = 'none'
text = 'to be done'
else:
stf = component.getAdapter(baseObject(jpDesc), IStateful,
name='task_states')
state = stf.state
text = stf.getStateObject().title
action = 'edit_jpdescription.html'
editUrl = '%s/%s' % (self.nodeView.getUrlForTarget(self.context), action)
return dict(text=text, editUrl=editUrl, state=state)
@Lazy
def ipskillsRequired(self):
ipsReq = adapted(self.target).getIPSkillsRequired()
if ipsReq is None:
state = 'none'
text = 'to be done'
else:
stf = component.getAdapter(baseObject(ipsReq), IStateful,
name='task_states')
state = stf.state
text = stf.getStateObject().title
action = 'edit_ipskillsreq.html'
editUrl = '%s/%s' % (self.nodeView.getUrlForTarget(self.context), action)
ipskillsUrl = None
return dict(text=text, editUrl=editUrl, ipskillsUrl=ipskillsUrl,
state=state)
@Lazy
def qualificationsRequired(self):
quReq = adapted(self.target).getQualificationsRequired()
if quReq is None:
state = 'none'
text = 'to be done'
else:
stf = component.getAdapter(baseObject(quReq), IStateful,
name='task_states')
state = stf.state
text = stf.getStateObject().title
action = 'edit_qualificationsreq.html'
editUrl = '%s/%s' % (self.nodeView.getUrlForTarget(self.context), action)
return dict(text=text, editUrl=editUrl, state=state)
@Lazy
def ipskills(self):
#action = 'edit_ipskills.html'
#editUrl = '%s/%s' % (self.nodeView.getUrlForTarget(self.context), action)
ipskillsUrl = None
return dict(text='to be done') #, editUrl=editUrl, ipskillsUrl=ipskillsUrl)
class DeleteJobPosition(PositionView):
isToplevel = True
parentName = 'data_entry'
def __call__(self):
obj = self.adapted
jobdesc = obj.getJPDescription()
ipskills = obj.getIPSkillsRequired()
qualif = obj.getQualificationsRequired()
for subobj in (jobdesc, ipskills, qualif):
if subobj is not None:
name = getName(baseObject(subobj))
del self.conceptManager[name]
targetUrl = self.breadcrumbsParent.targetUrl
name = getName(self.context)
del self.conceptManager[name]
return self.request.response.redirect(targetUrl)
class CopyJPProfiles(PositionView):
isToplevel = True
parentName = 'data_entry'
def __call__(self):
source = self.adapted
jobdesc = source.getJPDescription()
ipskills = source.getIPSkillsRequired()
qualif = source.getQualificationsRequired()
new = self.createJob(self.context.title)
if jobdesc is not None:
newJobdesc = new.createJPDescription()
newJobdesc.header = deepcopy(jobdesc.header)
newJobdesc.administrativeData = deepcopy(jobdesc.administrativeData)
newJobdesc.workDescription = deepcopy(jobdesc.workDescription)
if ipskills is not None:
newIpskills = new.createIPSkillsRequired()
newIpskills.requirements = deepcopy(ipskills.requirements)
if qualif is not None:
newQualif = new.createQualificationsRequired()
newQualif.requirements = deepcopy(qualif.requirements)
url = self.breadcrumbsParent.targetUrl
return self.request.response.redirect(url)
class JPDescForm(PositionView):
""" Form for entering job description for a certain position.
"""
macroName = 'jpdescform'
textKeys = ['administrative', 'workdesc', 'footer']
textParentName = 'jpdescription'
parentName = 'data_entry'
def getData(self):
self.setupController()
self.registerDojoComboBox()
result = dict(header={}, administrative=[], workdesc=[])
jp = adapted(self.target)
jpDesc = jp.getJPDescription()
form = self.request.form
if form.get('button_cancel'):
url = self.breadcrumbsParent.targetUrl
self.request.response.redirect(url)
return result
data = dict(header={}, administrative={}, workdesc={})
for k, v in (form.get('header') or {}).items():
data['header'][k] = v
for item in (form.get('administrative') or []):
data['administrative'][item['key']] = item
for item in (form.get('workdesc') or []):
data['workdesc'][item['key']] = item
if data['administrative']:
self.update(data)
elif jpDesc is not None:
if jpDesc.administrativeData:
data['administrative'] = jpDesc.administrativeData
if jpDesc.workDescription:
data['workdesc'] = jpDesc.workDescription
if jpDesc.header:
data['header'] = jpDesc.header
titleData = data['administrative'].get('title')
if not titleData or not titleData.get('text'):
data['administrative']['title'] = dict(key='title', text=jp.title)
result['header'] = data['header']
adminDT = adapted(self.conceptManager['jpdesc_administrative'])
for row in adminDT.data.values():
if len(row) < 4:
continue
key, label, desc, optional = row
dataRow = data['administrative'].get(key) or {}
result['administrative'].append(
dict(key=key, label=label, desc=desc, optional=bool(optional),
text=dataRow.get('text') or u'',
inactive=dataRow.get('inactive')))
workdescDT = adapted(self.conceptManager['jpdesc_workdesc'])
for row in workdescDT.data.values():
if len(row) < 4:
continue
key, label, desc, optional = row
dataRow = data['workdesc'].get(key) or {}
result['workdesc'].append(
dict(key=key, label=label, desc=desc, optional=bool(optional),
text=dataRow.get('text') or (5 * [u'']),
inactive=dataRow.get('inactive')))
return result
def update(self, data):
jp = adapted(self.target)
jobdesc = jp.getJPDescription()
if jobdesc is None:
jobdesc = jp.createJPDescription()
jobdesc.header = data['header']
jobdesc.administrativeData = data['administrative']
titleData = data['administrative'].get('title')
if titleData and titleData.get('text') != jp.title:
jp.title = titleData['text']
jobdesc.workDescription = data['workdesc']
return self.processStateTransition(jobdesc)
class QualificationsForm(PositionView):
""" Form for entering qualifications required for a certain position.
"""
macroName = 'qualificationsform'
textKeys = ['1', '2']
textParentName = 'qualificationsrequired'
parentName = 'data_entry'
def getData(self):
self.setupController()
self.registerDojoComboBox()
form = self.request.form
if form.get('button_cancel'):
url = self.breadcrumbsParent.targetUrl
self.request.response.redirect(url)
return []
data = {}
input = form.get('qualifications')
for item in (input or []):
data[item['key']] = item
if data:
self.update(data)
else:
qureq = adapted(self.target).getQualificationsRequired()
if qureq is not None:
data = qureq.requirements
result = []
qualifications = self.conceptManager['qualifications']
for obj in qualifications.getChildren([self.defaultPredicate]):
uid = util.getUidForObject(obj)
dataRow = data.get(uid) or {}
item = dict(key=uid, label=obj.title,
desc=obj.description,
subitems=[], schema=[],
value=dataRow.get('value') or (3 * [u'']),
req=dataRow.get('req') or (3 * [u'0']))
for subitem in obj.getChildren([self.defaultPredicate]):
item['subitems'].append(dict(
uid=util.getUidForObject(subitem),
title=subitem.title))
for row in adapted(obj).data.values():
if len(row) < 6:
continue
key = row[0]
value = dataRow.get('qu_' + key) or (3 * [u''])
item['schema'].append(dict(
key=key, label=row[1],
level=row[2], type=row[4],
vocabulary=row[5].split(';'),
value=value))
result.append(item)
return result
def update(self, data):
jp = adapted(self.target)
qureq = jp.getQualificationsRequired()
if qureq is None:
qureq = jp.createQualificationsRequired()
qureq.requirements = data
return self.processStateTransition(qureq)
class IPSkillsForm(PositionView):
""" Form for entering interpersonal skills required for a certain position.
"""
macroName = 'ipskillsform'
textKeys = ['1', '2']
textParentName = 'ipskillsrequired'
parentName = 'data_entry'
numberSelected = 0
def getData(self):
self.setupController()
form = self.request.form
if form.get('button_cancel'):
url = self.breadcrumbsParent.targetUrl
self.request.response.redirect(url)
return []
data = {}
input = form.get('ipskills')
for item in (input or []):
data[item['uid']] = item
self.registerDojoSlider()
if data:
self.update(data)
else:
skillsreq = adapted(self.target).getIPSkillsRequired()
if skillsreq is not None:
data = skillsreq.requirements
result = []
ipskills = self.conceptManager['ipskills']
for parent in ipskills.getChildren([self.defaultPredicate]):
#toplevelSkill = adapted(parent)
uid = util.getUidForObject(parent)
item = dict(uid=uid, label=parent.title,
description=parent.description, skills=[])
for child in parent.getChildren([self.defaultPredicate]):
#skill = adapted(child)
uid = util.getUidForObject(child)
row = data.get(uid) or {}
selected = row.get('selected')
if selected:
self.numberSelected += 1
item['skills'].append(
dict(uid=uid, label=child.title,
description=child.description,
selected=row.get('selected'),
expected=row.get('expected') or 0))
result.append(item)
return result
def update(self, data):
jp = adapted(self.target)
skillsreq = jp.getIPSkillsRequired()
if skillsreq is None:
skillsreq = jp.createIPSkillsRequired()
skillsreq.requirements = data
return self.processStateTransition(skillsreq)

View file

@ -0,0 +1,483 @@
<html i18n:domain="cyberapps.knowledge">
<metal:jobpositions define-macro="jobpositions"
tal:define="dummy item/update">
<metal:block use-macro="view/concept_macros/concepttitle" />
<metal:buttons use-macro="item/knowledge_macros/select_institution" />
<form method="post"
tal:define="texts item/getTexts">
<table class="grid">
<tr>
<th i18n:translate="">label_jobposition_title</th>
<th width="120px"
i18n:translate="">label_jpdescription</th>
<th width="120px"
i18n:translate="">label_qualifications_required</th>
<th width="120px"
i18n:translate="">label_ipskills_required</th>
<th i18n:translate=""
tal:condition="nothing">label_persondata</th>
</tr>
<tr tal:repeat="pos item/positions">
<td style="position: relative; width: 40%">
<a tal:attributes="href python:view.getUrlForTarget(pos)"
tal:content="pos/title" />
<span style="position: absolute; right: 5px">
<a title="delete job position" onclick="onclick_delete_jobposition"
tal:condition="pos/deleteUrl"
i18n:attributes="title; onclick"
tal:attributes="href pos/deleteUrl">
<img tal:attributes="src
string:${resourceBase}cybertools.icons/page_delete.png" />
</a>
<a title="copy job position" onclick="onclick_copy_jobposition"
i18n:attributes="title; onclick"
tal:attributes="href pos/copyUrl">
<img tal:attributes="src
string:${resourceBase}cybertools.icons/page_copy.png" />
</a>
</span>
</td>
<tal:block define="desc pos/jpDescription">
<td style="position: relative">
<span i18n:translate=""
tal:content="desc/text" />
<a title="edit job description" style="position: absolute; right: 5px"
i18n:attributes="title"
tal:attributes="href desc/editUrl">
<img tal:attributes="src
string:${resourceBase}cybertools.icons/page_edit.png" />
</a>
</td>
</tal:block>
<tal:block define="required pos/qualificationsRequired">
<td style="position: relative">
<span i18n:translate=""
tal:content="required/text" />
<a title="edit profile" style="position: absolute; right: 5px"
i18n:attributes="title"
tal:attributes="href required/editUrl">
<img tal:attributes="src
string:${resourceBase}cybertools.icons/page_edit.png" />
</a>
</td>
</tal:block>
<tal:block define="required pos/ipskillsRequired">
<td style="position: relative">
<span i18n:translate=""
tal:content="required/text" />
<a title="edit profile" style="position: absolute; right: 5px"
i18n:attributes="title"
tal:attributes="href required/editUrl">
<img tal:attributes="src
string:${resourceBase}cybertools.icons/page_edit.png" />
</a>
</td>
</tal:block>
<tal:block define="ipskills pos/ipskills"
condition="nothing">
<td>
<span i18n:translate=""
tal:content="ipskills/text" />
</td>
</tal:block>
</tr>
</table>
<div><br />
<h3 i18n:translate="">Create a New Job / Position</h3>
<tal:text define="info texts/1|nothing"
condition="info">
<div class="noprint"
tal:content="structure info/text" />
</tal:text>
<span i18n:translate="">label_jobposition_title</span>:
<select data-dojo-type="dijit/form/ComboBox"
id="form_jptitle" name="form_jptitle">
<option selected></option>
<option tal:repeat="value item/jobTitles"
tal:content="value"></option>
</select>
<input type="submit" name="create_jobposition"
value="Create Job"
i18n:attributes="value" />
</div>
<div metal:use-macro="item/template/macros/backbutton" />
</form>
</metal:jobpositions>
<metal:form define-macro="jpdescform">
<metal:block use-macro="view/concept_macros/concepttitle" />
<form method="post"
tal:define="data item/getData;
texts item/getTexts">
<table style="width: auto">
<tr>
<td><b><span i18n:translate="">Created at</span>:</b></td>
<td>
<input type="text" name="header.date:record"
tal:define="date data/header/date|nothing"
tal:attributes="value python:date or item.todayFormatted()" />
</td>
</tr>
<tr>
<td><b><span i18n:translate="">Responsible</span>:</b></td>
<td>
<input type="text" name="header.responsible:record"
tal:attributes="value data/header/responsible|nothing" />
</td>
</tr>
<tr>
<td class="optional">
<input type="checkbox" id="header.review"
name="header.review:record" value="yes"
tal:attributes="checked data/header/review|nothing" />
<b><label for="header.review"
i18n:translate="">Next Review</label>:</b>
</td>
<td>
<select data-dojo-type="dijit/form/ComboBox"
name="header.reviewDate:record"
tal:attributes="value data/header/reviewDate|nothing">
<option selected></option>
<option tal:repeat="num python:range(5)"
i18n:translate=""
tal:content="string:option_review_$num"></option>
</select>
</td>
</tr>
</table>
<br />
<tal:administrative define="fname string:administrative">
<tal:text define="info texts/?fname|nothing"
condition="info">
<h3 style="width: 700px"
tal:content="info/title" />
<div class="noprint"
tal:content="structure info/text" />
</tal:text>
<table class="grid jpdesc_admin" width="100%">
<tr tal:repeat="row data/?fname"
tal:attributes="class python:row['inactive'] and 'noprint' or None">
<td class="noprint">
<input type="hidden"
tal:attributes="name string:$fname.key:records;
value row/key;" />
<input type="checkbox" value="yes"
tal:condition="row/optional"
tal:attributes="name string:$fname.inactive:records;
checked row/inactive;
onclick string:
dojo.byId('${fname}_${repeat/row/index}').setAttribute('style',
'display: ' + (this.checked ? 'none' : 'auto'))" />
</td>
<td tal:attributes="class python:row['optional'] and 'optional' or None">
<div class="label" tal:content="row/label" />
<div class="description noprint"
tal:content="row/desc" />
</td>
<td class="input" width="60%">
<textarea tal:attributes="name string:$fname.text:records;
id string:${fname}_${repeat/row/index};
style python:'display: ' +
(row['inactive'] and 'none' or 'auto')"
tal:content="row/text"></textarea>
</td>
</tr>
</table>
</tal:administrative>
<br />
<tal:workdesc define="fname string:workdesc">
<tal:text define="info texts/?fname|nothing"
condition="info">
<h3 tal:content="info/title" />
<div class="noprint"
tal:content="structure info/text" />
</tal:text>
<table class="jpdesc_workdesc" width="100%">
<tal:wditem repeat="row data/?fname">
<tbody tal:attributes="class python:row['inactive'] and 'noprint' or None">
<tr class="label">
<td colspan="2"
tal:attributes="class python:
row['optional'] and 'optional' or None">
<input type="hidden"
tal:attributes="name string:$fname.key:records;
value row/key;" />
<input type="checkbox" value="yes" class="noprint"
tal:condition="row/optional"
tal:attributes="name string:$fname.inactive:records;
checked row/inactive;
onclick string:
dojo.byId('${fname}_${repeat/row/index}').setAttribute('style',
'display: ' + (this.checked ? 'none' : 'auto'))" />
<span class="label"
tal:content="row/label" />:
<div class="description noprint"
tal:content="row/desc" />
</td>
</tr>
<tbody tal:attributes="id string:${fname}_${repeat/row/index};
style python:'display: ' +
(row['inactive'] and 'none' or 'auto')">
<tr class="grid"
tal:repeat="textRow python:range(5)">
<td class="center" style="width: 2em"
tal:define="number python:textRow + 1"
tal:content="string:$number.">1.</td>
<td class="input">
<input tal:attributes="name string:$fname.text:list:records;
value python:row['text'][textRow];
disabled python:
row['inactive'] and 'true' or None" /></td>
</tr>
</tbody>
<tr>
<td tal:condition="nothing" colspan="2">
<button onclick="return false;">+</button></td></tr>
</tbody>
</tal:wditem>
</table>
</tal:workdesc>
<br />
<metal:buttons use-macro="item/template/macros/buttons" />
</form>
</metal:form>
<metal:form define-macro="qualificationsform">
<metal:block use-macro="view/concept_macros/concepttitle" />
<form method="post"
tal:define="data item/getData;
texts item/getTexts">
<tal:qualifications define="fname string:qualifications">
<tal:text define="info texts/1|nothing"
condition="info">
<div class="noprint"
tal:content="structure info/text" />
</tal:text>
<br />
<table class="jpdesc_qualif">
<tr class="grid headline">
<td i18n:translate="">label_category</td>
<td i18n:translate="">label_knowledge_level</td>
<td width="10%"
i18n:translate="">label_requirement</td>
</tr>
<tal:quitem repeat="row data">
<tr class="grid">
<td colspan="3">
<input type="hidden"
tal:attributes="name string:$fname.key:records;
value row/key;" />
<span class="label"
tal:content="row/label" />
<div class="description noprint"
tal:content="row/desc" />
</td>
</tr>
<tal:group repeat="field python:row['schema'] or [None]">
<tr class="grid"
tal:condition="field">
<td tal:content="field/label" />
<td tal:content="field/level" />
<td i18n:translate="">label_requirement</td>
</tr>
<tr class="grid"
tal:repeat="textRow python:range(3)">
<td class="input"
tal:attributes="colspan python:field and '1' or '2'">
<select data-dojo-type="dijit/form/ComboBox"
style="width: 100%"
tal:condition="row/subitems"
tal:attributes="name string:$fname.value:list:records;
value python:row['value'][textRow]">
<option selected></option>
<option tal:repeat="value row/subitems"
tal:content="value/title"></option>
</select>
<input style="width: 100%"
tal:condition="not:row/subitems"
tal:attributes="name string:$fname.value:list:records;
value python:row['value'][textRow]" />
</td>
<td class="input"
tal:condition="field">
<tal:field define="name string:qu_${field/key};
macro field/type;
value field/value">
<metal:input use-macro="item/template/macros/?macro" />
</tal:field>
</td>
<td class="input">
<select tal:attributes="name string:$fname.req:list:records"
tal:define="svalue python:row['req'][textRow]">
<option tal:repeat="value python:(0, 2, 1)"
i18n:translate=""
tal:attributes="value value;
selected python:svalue == str(value)"
tal:content="string:option_req_$value" />
</select>
</td>
</tr>
</tal:group>
</tal:quitem>
</table>
</tal:qualifications>
<br />
<metal:buttons use-macro="item/template/macros/buttons" />
</form>
</metal:form>
<metal:form define-macro="ipskillsform">
<metal:block use-macro="view/concept_macros/concepttitle" />
<form method="post"
tal:define="data item/getData;
texts item/getTexts">
<tal:text define="info texts/1|nothing"
condition="info">
<div tal:content="structure info/text" />
</tal:text>
<div>
<span i18n:translate="label_ipskills_number_selected">
Number of currently selected skills:</span>
<span id="ipskills_number_selected"
tal:attributes="style python:item.numberSelected > 15 and
'color: Red' or ''"
tal:content="item/numberSelected" />
</div>
<script>
function updateNumberSelected(obj) {
var node = dojo.byId('ipskills_number_selected');
var value = parseInt(node.textContent);
var newValue = value + (obj.checked ? 1 : -1);
node.childNodes[0].textContent = newValue;
node.setAttribute('style', 'color: ' + (newValue > 15 ? 'Red' : 'Black'));
}
</script>
<table style="width: auto"
tal:define="fname string:ipskills">
<tal:group repeat="parent data">
<tr>
<td colspan="2"
style="padding-top: 15px; padding-bottom: 8px; font-size: 120%">
<b tal:content="parent/label" /></td>
<td style="vertical-align: bottom">weniger wichtig</td>
<td style="vertical-align: bottom; text-align: right">
äußerst wichtig</td>
</tr>
<tr style="border-top: 1px solid #dddddd; border-bottom: 1px solid #dddddd"
tal:repeat="child parent/skills">
<td style="width: 5%; vertical-align: top">
<input type="hidden"
tal:attributes="name string:$fname.uid:records;
value child/uid;" />
<input type="checkbox" value="yes"
tal:attributes="name string:$fname.selected:records;
checked child/selected;
onclick string:
dojo.byId('${fname}_expected_${child/uid}').setAttribute('style',
'display: ' + (this.checked ? 'auto' : 'none'));;
updateNumberSelected(this);;" />
</td>
<td style="width: 60%; vertical-align: top"
tal:content="child/label"
tal:attributes="title child/description" />
<td colspan="2"
style="width: 35%; padding-top: 10px">
<div data-dojo-type="dijit/form/HorizontalSlider"
tal:define="disabled python:child['selected'] and 'false' or 'true';
props string:value:${child/expected},
minimum:0, maximum:4,
discreteValues:5, intermediateChanges:true,
showButtons:false"
tal:attributes="id string:${fname}_expected_${child/uid};
name string:$fname.expected:records;
data-dojo-props props;
style python:'display: ' +
(child['selected'] and 'auto' or 'none')">
<div data-dojo-type="dijit/form/HorizontalRule"
container="bottomDecoration"
count="5" style="height:5px;"></div>
<ol data-dojo-type="dijit/form/HorizontalRuleLabels"
container="bottomDecoration"
style="height:1.5em;font-size:75%;color:gray;">
<li tal:repeat="v python:range(1, 6)"
tal:content="v" />
</ol>
</div>
</td>
</tr>
</tal:group>
</table>
<br />
<tal:text define="info texts/2|nothing"
condition="info">
<div tal:content="structure info/text" />
</tal:text>
<br />
<metal:buttons use-macro="item/template/macros/buttons" />
</form>
</metal:form>
<metal:input define-macro="combo">
<select data-dojo-type="dijit/form/ComboBox"
style="width: 100%"
tal:attributes="name string:$fname.$name:list:records;
value python:value[textRow]">
<option tal:repeat="vitem field/vocabulary"
tal:content="vitem" />
</select>
</metal:input>
<metal:input define-macro="text">
<input tal:attributes="name string:$fname.$name:list:records;
value python:value[textRow]" />
</metal:input>
<div metal:define-macro="buttons"
class="noprint">
<input type="submit" name="submit_save" value="button_save"
i18n:attributes="value" />
<input type="submit" name="submit_activate" value="button_activate"
i18n:attributes="value" />
<input type="submit" name="button_cancel" value="Cancel"
i18n:attributes="value" />
</div>
<div metal:define-macro="buttons2"
class="noprint">
<input type="submit" name="submit_save" value="Save"
i18n:attributes="value" />
<input type="submit" name="button_cancel" value="Cancel"
i18n:attributes="value" />
</div>
<div metal:define-macro="buttons2back"
class="noprint">
<input type="submit" name="submit_save" value="Save"
i18n:attributes="value" />
<input type="submit" name="button_cancel" value="Back"
i18n:attributes="value" />
</div>
<div metal:define-macro="backbutton">
<br />
<div class="button">
<a title="Back to Resource Management Page"
tal:attributes="href item/breadcrumbsParent/targetUrl"
i18n:translate=""
i18n:attributes="title">Back</a>
</div>
</div>
</html>

View file

@ -0,0 +1,312 @@
#
# Copyright (c) 2016 Helmut Merz helmutm@cy55.de
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
"""
Definition of classes for viewing reporting data in cyberapps.knowledge.
"""
from zope.app.pagetemplate import ViewPageTemplateFile
from zope.cachedescriptors.property import Lazy
from zope.i18n import translate
from zope.i18nmessageid import MessageFactory
from loops.common import adapted, baseObject
from loops.knowledge.survey.interfaces import IQuestionGroup
from loops.knowledge.survey.response import Responses
from loops import util
from cyberapps.knowledge.browser.qualification import \
JobPositionsOverview, PositionView, JPDescForm
from cyberapps.knowledge.browser.qualification import template as baseTemplate
from cyberapps.knowledge.interfaces import IQualificationsRecorded
_ = MessageFactory('cyberapps.knowledge')
template = ViewPageTemplateFile('report_macros.pt')
class ReportBaseView(object):
template = template
templateName = 'knowledge.report'
baseTemplate = baseTemplate
class JobsListing(ReportBaseView, JobPositionsOverview):
macroName = 'jobs'
def update(self):
instUid = self.request.form.get('select_institution')
if instUid:
return self.setInstitution(instUid)
def getItemUrl(self, item):
itemViewName = self.options('item_viewname')
baseUrl = self.nodeView.getUrlForTarget(item)
if itemViewName:
return '/'.join((baseUrl, itemViewName[0]))
return baseUrl
class JobDescription(ReportBaseView, JPDescForm):
macroName = 'jobdescription'
parentName = None
def getData(self):
self.setupController()
self.registerDojoSlider()
result = dict(header={}, administrative=[], workdesc=[],
qualifications=[], ipskills=[])
data = dict(header={}, administrative={}, workdesc={},
qualifications={}, ipskills={})
# load data
jp = adapted(self.target)
jpDesc = jp.getJPDescription()
if jpDesc is not None:
if jpDesc.administrativeData:
data['administrative'] = jpDesc.administrativeData
if jpDesc.workDescription:
data['workdesc'] = jpDesc.workDescription
if jpDesc.header:
data['header'] = result['header'] = jpDesc.header
qureq = adapted(self.target).getQualificationsRequired()
if qureq is not None:
data['qualifications'] = qureq.requirements
skillsreq = adapted(self.target).getIPSkillsRequired()
if skillsreq is not None:
data['ipskills'] = skillsreq.requirements
# administrative data
adminDT = adapted(self.conceptManager['jpdesc_administrative'])
for row in adminDT.data.values():
key, label, desc, optional = row
dataRow = data['administrative'].get(key) or {}
if dataRow.get('inactive'):
continue
result['administrative'].append(
dict(key=key, label=label, desc=desc, optional=bool(optional),
text=dataRow.get('text') or u''))
# work description
workdescDT = adapted(self.conceptManager['jpdesc_workdesc'])
for row in workdescDT.data.values():
key, label, desc, optional = row
dataRow = data['workdesc'].get(key) or {}
if dataRow.get('inactive'):
continue
result['workdesc'].append(
dict(key=key, label=label, desc=desc, optional=bool(optional),
text=dataRow.get('text') or (5 * [u''])))
# qualifications
qualifications = self.conceptManager['qualifications']
for obj in qualifications.getChildren([self.defaultPredicate]):
uid = util.getUidForObject(obj)
dataRow = data['qualifications'].get(uid) or {}
item = dict(key=uid, label=obj.title,
desc=obj.description, schema=[],
value=dataRow.get('value') or (3 * [u'']),
req=dataRow.get('req') or (3 * [u'0']))
for row in adapted(obj).data.values():
if len(row) < 5:
continue
key = row[0]
value = dataRow.get('qu_' + key) or (3 * [u''])
item['schema'].append(dict(
key=key, label=row[1],
level=row[2], type=row[4],
value=value))
result['qualifications'].append(item)
# ipskills
ipskills = self.conceptManager['ipskills']
for parent in ipskills.getChildren([self.defaultPredicate]):
uid = util.getUidForObject(parent)
item = dict(uid=uid, label=parent.title,
description=parent.description, skills=[])
for child in parent.getChildren([self.defaultPredicate]):
uid = util.getUidForObject(child)
row = data['ipskills'].get(uid) or {}
if row.get('selected'):
item['skills'].append(
dict(uid=uid, label=child.title,
description=child.description,
expected=row.get('expected') or 0))
result['ipskills'].append(item)
return result
class JobReport(ReportBaseView, PositionView):
macroName = 'job_report'
parentName = 'qkb'
qualificationData = None
questionnaires = None
quGroups = None
ipskillsInputData = None
def getData(self):
self.setupController()
self.registerDojoCharting()
lang = self.languageInfo.language
result = dict(qualifications=[], ipskills=[])
reqData = dict(qualifications={}, ipskills={})
persons = self.adapted.getPersons()
selectedPerson = self.request.form.get('person')
if selectedPerson:
p = adapted(util.getObjectForUid(selectedPerson))
if p in persons:
persons = [p]
# load requirement data
qureq = adapted(self.target).getQualificationsRequired()
if qureq is not None:
reqData['qualifications'] = qureq.requirements
skillsreq = adapted(self.target).getIPSkillsRequired()
if skillsreq is not None:
reqData['ipskills'] = skillsreq.requirements
# qualification data
qualifications = self.conceptManager['qualifications']
for obj in qualifications.getChildren([self.defaultPredicate]):
qualification = adapted(obj)
uid = qualification.uid
dataRow = reqData['qualifications'].get(uid) or {}
personData = self.getQualificationData(uid, persons)
item = dict(key=uid, label=qualification.title,
desc=qualification.description, schema=[],
value=dataRow.get('value') or (3 * [u'']),
req=dataRow.get('req') or (3 * [u'0']),
personData=personData)
for row in qualification.data.values():
if len(row) < 5:
continue
key = row[0]
value = dataRow.get('qu_' + key) or (3 * [u''])
item['schema'].append(dict(
key=key, label=row[1],
level=row[2], type=row[4],
value=value))
result['qualifications'].append(item)
# ipskills data
ipskills = self.conceptManager['ipskills']
for parent in ipskills.getChildren([self.defaultPredicate]):
uid = util.getUidForObject(parent)
item = dict(uid=uid, label=parent.title,
description=parent.description, skills=[])
for child in parent.getChildren([self.defaultPredicate]):
uid = util.getUidForObject(child)
row = reqData['ipskills'].get(uid) or {}
if row.get('selected'):
ipskillsInput = self.getIPSkillsInput(child, persons)
v = int(row.get('expected') or 0) + 1
vstr = '%s: %s' % (
translate(_('ipskills_required'), target_language=lang), v)
item['skills'].append(
dict(uid=uid, label=child.title,
description=child.description,
expected=v,
expStr=vstr,
ipskillsInput=ipskillsInput))
result['ipskills'].append(item)
return result
def getQualificationData(self, quUid, persons):
result = []
personUids = [p.uid for p in persons]
if self.qualificationData is None:
self.qualificationData = {}
for p in persons:
for c in baseObject(p).getChildren():
obj = adapted(c)
if IQualificationsRecorded.providedBy(obj):
self.qualificationData[p.uid] = obj.data
break
else:
self.qualificationData[p.uid] = {}
for p in persons:
data = self.qualificationData[p.uid].get(quUid) or {}
if data:
item = dict(name=p.title)
item.update(data)
result.append(item)
return result
def getIPSkillsInput(self, competence, persons):
result = []
lang = self.languageInfo.language
personUids = [p.uid for p in persons]
questionGroup = refQuestionGroup = None
for c in baseObject(competence).getChildren():
qug = adapted(c)
if IQuestionGroup.providedBy(qug):
questionnaire = self.getQuestionnaire(qug, 'standard')
if (questionnaire is not None and
qug in self.quGroups.get('standard')):
questionGroup = qug
else:
refQuestionnaire = self.getQuestionnaire(qug, 'person')
if (refQuestionnaire is not None and
qug in self.quGroups.get('person')):
refQuestionGroup = qug
#break
if questionGroup is None and refQuestionGroup is None:
return result
if self.ipskillsInputData is None:
self.ipskillsInputData = {}
for uid in personUids:
respManager = Responses(baseObject(questionnaire))
self.ipskillsInputData[uid] = respManager.load(uid)
if refQuestionGroup is not None:
refRespManager = Responses(baseObject(refQuestionnaire))
self.ipskillsInputData[uid].update(
refRespManager.loadRange(uid + '.*'))
for idx, uid in enumerate(personUids):
person = persons[idx]
data = self.ipskillsInputData.get(uid)
if data is not None:
value = data.get(questionGroup.uid)
refValues = refQuestionGroup and data.get(refQuestionGroup.uid)
if value is None and refValues is None:
continue
item = dict(name=person.title, value=None, avg=None, vstr=None,
refValues=dict(values=[], avg=None, vstr=None))
if value is not None:
v = int(round(value * 4 + 1))
item['value'] = v
item['vstr'] = '%s: %s' % (
translate(_('label_skillValues'), target_language=lang), v)
if refValues:
refValues = sorted([int(round(v * 4 + 1)) for v in refValues])
avg = int(round(sum(refValues) / len(refValues)))
vstr = '%s: %s' % (
translate(_('label_refValues'), target_language=lang),
', '.join(str(v) for v in refValues))
item['refValues']=dict(values=refValues, avg=avg, vstr=vstr)
result.append(item)
return result
def getQuestionnaire(self, quGroup, quType):
if self.questionnaires is None:
self.questionnaires = {}
self.quGroups = {}
if quType in self.questionnaires:
return self.questionnaires[quType]
for qu in quGroup.getQuestionnaires():
if qu.questionnaireType == quType:
self.questionnaires[quType] = qu
self.quGroups[quType] = qu.getAllQuestionGroups()
break
return self.questionnaires.get(quType)

View file

@ -0,0 +1,350 @@
<html i18n:domain="cyberapps.knowledge">
<metal:jobs define-macro="jobs"
tal:define="dummy item/update">
<metal:block use-macro="view/concept_macros/concepttitle" />
<metal:buttons use-macro="item/knowledge_macros/select_institution" />
<div tal:repeat="pos item/positions">
<a tal:attributes="href python:item.getItemUrl(pos)"
tal:content="pos/title" />
</div>
<div metal:use-macro="item/baseTemplate/macros/backbutton" />
</metal:jobs>
<metal:jobdesc define-macro="jobdescription">
<metal:block use-macro="view/concept_macros/concepttitle" />
<div tal:define="data item/getData;
texts item/getTexts">
<table style="width: auto">
<tr>
<td><b><span i18n:translate="">Created at</span>:</b></td>
<td tal:content="data/header/date|nothing"></td>
</tr>
<tr>
<td><b><span i18n:translate="">Responsible</span>:</b></td>
<td tal:content="data/header/responsible|nothing"></td>
</tr>
<tr tal:condition="data/header/review|nothing">
<td><b><span i18n:translate="">Next Review</span>:</b></td>
<td tal:content="data/header/reviewDate|nothing"></td>
</tr>
</table>
<br />
<tal:administrative define="fname string:administrative">
<tal:text define="info texts/?fname|nothing"
condition="info">
<h3 style="width: 700px"
tal:content="info/title" />
</tal:text>
<table class="grid jpdesc_admin" width="100%">
<tr tal:repeat="row data/?fname">
<td>
<div tal:content="row/label" />
</td>
<td class="input" width="60%"
tal:content="row/text">
</td>
</tr>
</table>
</tal:administrative>
<br />
<tal:workdesc define="fname string:workdesc">
<tal:text define="info texts/?fname|nothing"
condition="info">
<h3 tal:content="info/title" />
</tal:text>
<table class="jpdesc_workdesc" width="100%">
<tal:wditem repeat="row data/?fname">
<tr class="label">
<td colspan="2">
<span tal:content="row/label" />:
</td>
</tr>
<tal:text repeat="textRow python:range(5)">
<tr class="grid"
tal:define="value python:row['text'][textRow]"
tal:condition="value">
<td class="center" style="width: 2em"
tal:define="number python:textRow + 1"
tal:content="string:$number.">1.</td>
<td class="input"
tal:content="value"></td>
</tr>
</tal:text>
</tal:wditem>
</table>
</tal:workdesc>
<br />
<tal:qualifications define="fname string:qualifications">
<h3>3. <span i18n:translate="label_qualifications" /></h3>
<table class="jpdesc_qualif">
<tr class="grid headline">
<td i18n:translate="">label_category</td>
<td i18n:translate="">label_knowledge_level</td>
<td width="10%"
i18n:translate="">label_requirement</td>
</tr>
<tal:quitem repeat="row data/?fname">
<tr class="grid">
<td colspan="3">
<span class="label"
tal:content="row/label" />
</td>
</tr>
<tal:group repeat="field python:row['schema'] or [None]">
<tr class="grid"
tal:condition="field">
<td tal:content="field/label" />
<td tal:content="field/level" />
<td i18n:translate="">label_requirement</td>
</tr>
<tal:row repeat="textRow python:range(3)">
<tr class="grid"
tal:define="value python:row['value'][textRow]"
tal:condition="value">
<td class="input"
tal:attributes="colspan python:field and '1' or '2'"
tal:content="value">
</td>
<td class="input"
tal:condition="field"
tal:content="python:field['value'][textRow]">
</td>
<td class="input"
i18n:translate=""
tal:define="value python:row['req'][textRow]"
tal:content="string:option_req_$value">
</td>
</tr>
</tal:row>
</tal:group>
</tal:quitem>
</table>
</tal:qualifications>
<br />
<tal:ipskills define="fname string:ipskills">
<h3>4. <span i18n:translate="label_ipskills" /></h3>
<table style="width: 100%"
tal:define="fname string:ipskills">
<tal:group repeat="parent data/?fname">
<tr>
<td style="padding-top: 15px; padding-bottom: 8px; font-size: 120%">
<b tal:content="parent/label" /></td>
<td style="vertical-align: bottom">weniger wichtig</td>
<td style="vertical-align: bottom; text-align: right">
äußerst wichtig</td>
</tr>
<tr style="border-top: 1px solid #dddddd; border-bottom: 1px solid #dddddd"
tal:repeat="child parent/skills">
<td style="width: 70%; vertical-align: top;
padding-top: 0; padding-bottom: 0"
tal:content="child/label"
tal:attributes="title child/description" />
<td colspan="2"
style="width: 200px; padding: 0">
<table width="100%">
<tr>
<td style="border-left: 1px solid #dddddd;
border-right: 1px solid #dddddd;
text-align: center; width: 20%; padding: 2px"
tal:repeat="num python:range(5)"
tal:content="python:
int(child['expected']) == num and (num+1) or ''"
xtal:content="child/expected" />
</tr>
</table>
</td>
</tr>
</tal:group>
</table>
</tal:ipskills>
<br />&nbsp;<br />
<div tal:define="footer texts/footer|nothing"
tal:condition="footer"
tal:content="structure footer/text"></div>
</div>
</metal:jobdesc>
<metal:jobrep define-macro="job_report">
<metal:block use-macro="view/concept_macros/concepttitle" />
<form method="get"
tal:define="persons item/adapted/getPersons"
tal:condition="python:len(persons) > 1">
<span i18n:translate="select_person">Restrict report to person</span>:
<select name="person" id="select_person"
onchange="submit()"
tal:define="person request/person|nothing">
<option value=""
i18n:translate="all_persons">All persons</option>
<option tal:repeat="row persons"
tal:content="row/title"
tal:attributes="value row/uid;
selected python:row.uid == person">
</option>
</select>
<br />&nbsp;
</form>
<div tal:define="data item/getData;
texts item/getTexts">
<tal:qualifications define="fname string:qualifications">
<h2><span i18n:translate="label_qualifications" /></h2>
<table class="jpdesc_qualif">
<tal:quitem repeat="row data/?fname">
<tr><td colspan="3">&nbsp;</td></tr>
<tr>
<td colspan="3">
<span class="label"
tal:content="row/label" />
</td>
</tr>
<tr>
<td colspan="3">
<b i18n:translate="qualifications_required">Required</b>
</td></tr>
<tal:group repeat="field python:row['schema'] or [None]">
<tr class="grid"
tal:condition="field">
<td tal:content="field/label" />
<td tal:content="field/level" />
<td></td>
</tr>
<tal:row repeat="textRow python:range(3)">
<tr class="grid"
tal:define="value python:row['value'][textRow]"
tal:condition="python:
repeat['textRow'].start() or value">
<td class="input"
tal:attributes="colspan python:field and '1' or '2'"
tal:content="value">
</td>
<td class="input"
tal:condition="field"
tal:content="python:field['value'][textRow]">
</td>
<td class="input"
i18n:translate=""
tal:define="value python:row['req'][textRow]"
tal:content="string:option_req_$value">
</td>
</tr>
</tal:row>
</tal:group>
<tal:person repeat="personRow row/personData">
<tr colspan="3">
<td><b tal:content="personRow/name" /></td>
</tr>
<tal:group repeat="field python:row['schema'] or [None]">
<tal:row repeat="textRow python:range(3)">
<tr class="grid"
tal:define="value python:personRow['value'][textRow]"
tal:condition="python:
repeat['textRow'].start() or value">
<td tal:attributes="colspan python:field and '1' or '2'"
tal:content="structure python:value or u'&nbsp;'">blubb</td>
<td tal:condition="field"
tal:content="python:personRow['qu_'+field['key']][textRow]">
</td>
<td tal:define="value python:personRow['cert'][textRow]">
<tal:value condition="value">
<span i18n:translate="label_certificate">Certificate</span>:
<span tal:content="value" />
</tal:value>
</td>
</tr>
</tal:row>
</tal:group>
</tal:person>
</tal:quitem>
</table>
</tal:qualifications>
<br />
<tal:ipskills define="fname string:ipskills">
<h2><span i18n:translate="label_ipskills" /></h2>
<table style="width: 100%">
<tal:group repeat="parent data/?fname">
<tr><td colspan="3" />&nbsp;</tr>
<tr>
<td style="padding-bottom: 6px; font-size: 120%">
<b tal:content="parent/label" /></td>
<td style="vertical-align: bottom"></td>
<td style="vertical-align: bottom; text-align: right;
padding: 0">
<table width="100%">
<tr>
<td style="border-left: 1px solid #dddddd;
border-right: 1px solid #dddddd;
text-align: center"
tal:repeat="num python:range(1, 6)"
tal:content="num"></td></tr></table>
</td>
</tr>
<tal:child repeat="child parent/skills">
<tr style="border-top: 1px solid #dddddd">
<td style="vertical-align: top; padding-top: 0; padding-bottom: 0"
tal:content="child/label"
tal:attributes="title child/description" />
<td style="padding-top: 0; padding-bottom: 0">
<div i18n:translate="ipskills_required">Required</div>
</td>
<td style="width: 200px; padding: 0">
<table width="100%">
<tr>
<td style="border-left: 1px solid #dddddd;
border-right: 1px solid #dddddd;
text-align: center; width: 20%; padding: 0;
height: 18px"
tal:repeat="num python:range(1, 6)">
<img src="/@@/cybertools.icons/ledyellow.png"
tal:attributes="title child/expStr"
tal:condition="python:child['expected'] == num" />
</td>
</tr>
</table>
</td>
</tr>
<tr tal:repeat="row child/ipskillsInput"
tal:attributes="style python:
repeat['row'].end() and 'border-bottom: 1px solid #dddddd' or None">
<td />
<td style="padding-top: 0; padding-bottom: 0"
tal:content="row/name" />
<td style="width: 200px; padding: 0">
<table width="100%">
<tr tal:define="value row/value;
valStr row/vstr;
refAvg row/refValues/avg;
refStr row/refValues/vstr">
<td style="border-left: 1px solid #dddddd;
border-right: 1px solid #dddddd;
text-align: center; width: 20%; padding: 0;
height: 18px"
tal:repeat="num python:range(1, 6)">
<tal:separate condition="python:value != refAvg">
<img src="/@@/cybertools.icons/ledgreen.png"
tal:attributes="title valStr"
tal:condition="python:value == num" />
<img src="/@@/cybertools.icons/ledblue.png"
tal:attributes="title refStr"
tal:condition="python:refAvg == num" />
</tal:separate>
<img src="/@@/cybertools.icons/ledgreenblue.png"
tal:attributes="title string:$valStr;; $refStr"
tal:condition="python:value == refAvg == num" />
</td>
</tr>
</table>
</td>
</tr>
</tal:child>
</tal:group>
</table>
</tal:ipskills>
</div>
<br />
</metal:jobrep>
</html>

View file

@ -0,0 +1,80 @@
<configure
xmlns:zope="http://namespaces.zope.org/zope"
xmlns:i18n="http://namespaces.zope.org/i18n"
i18n_domain="cyberapps.knowledge">
<i18n:registerTranslations directory="locales" />
<zope:adapter factory="cyberapps.knowledge.data.JobPosition" trusted="True" />
<zope:class class="cyberapps.knowledge.data.JobPosition">
<require permission="zope.View"
interface="cyberapps.knowledge.interfaces.IJobPosition" />
<require permission="zope.ManageContent"
set_schema="cyberapps.knowledge.interfaces.IJobPosition" />
</zope:class>
<zope:adapter factory="cyberapps.knowledge.data.JPDescription" trusted="True" />
<zope:class class="cyberapps.knowledge.data.JPDescription">
<require permission="zope.View"
interface="cyberapps.knowledge.interfaces.IJPDescription" />
<require permission="zope.ManageContent"
set_schema="cyberapps.knowledge.interfaces.IJPDescription" />
</zope:class>
<zope:adapter factory="cyberapps.knowledge.data.IPSkillsRequired" trusted="True" />
<zope:class class="cyberapps.knowledge.data.IPSkillsRequired">
<require permission="zope.View"
interface="cyberapps.knowledge.interfaces.IIPSkillsRequired" />
<require permission="zope.ManageContent"
set_schema="cyberapps.knowledge.interfaces.IIPSkillsRequired" />
</zope:class>
<zope:adapter factory="cyberapps.knowledge.data.QualificationsRequired"
trusted="True" />
<zope:class class="cyberapps.knowledge.data.QualificationsRequired">
<require permission="zope.View"
interface="cyberapps.knowledge.interfaces.IQualificationsRequired" />
<require permission="zope.ManageContent"
set_schema="cyberapps.knowledge.interfaces.IQualificationsRequired" />
</zope:class>
<zope:adapter factory="cyberapps.knowledge.data.Qualification" trusted="True"
provides="cyberapps.knowledge.interfaces.IQualification" />
<zope:class class="cyberapps.knowledge.data.Qualification">
<require permission="zope.View"
interface="cyberapps.knowledge.interfaces.IQualification" />
<require permission="zope.ManageContent"
set_schema="cyberapps.knowledge.interfaces.IQualification" />
</zope:class>
<zope:adapter factory="cyberapps.knowledge.data.QualificationsRecorded"
trusted="True" />
<zope:class class="cyberapps.knowledge.data.QualificationsRecorded">
<require permission="zope.View"
interface="cyberapps.knowledge.interfaces.IQualificationsRecorded" />
<require permission="zope.ManageContent"
set_schema="cyberapps.knowledge.interfaces.IQualificationsRecorded" />
</zope:class>
<zope:adapter factory="cyberapps.knowledge.data.SkillsRecorded"
trusted="True" />
<zope:class class="cyberapps.knowledge.data.SkillsRecorded">
<require permission="zope.View"
interface="cyberapps.knowledge.interfaces.ISkillsRecorded" />
<require permission="zope.ManageContent"
set_schema="cyberapps.knowledge.interfaces.ISkillsRecorded" />
</zope:class>
<zope:adapter factory="cyberapps.knowledge.data.IPSkillsQuestionnaire"
provides="cyberapps.knowledge.interfaces.IIPSkillsQuestionnaire"
trusted="True" />
<zope:class class="cyberapps.knowledge.data.IPSkillsQuestionnaire">
<require permission="zope.View"
interface="cyberapps.knowledge.interfaces.IIPSkillsQuestionnaire" />
<require permission="zope.ManageContent"
set_schema="cyberapps.knowledge.interfaces.IIPSkillsQuestionnaire" />
</zope:class>
<include package=".browser" />
</configure>

174
cyberapps/knowledge/data.py Normal file
View file

@ -0,0 +1,174 @@
# cyberapps.knowlege.data
""" Classes for Knowledge and Skills Management.
"""
from zope.container.interfaces import INameChooser
from zope.component import adapts
from zope.interface import implementer
from zope.traversing.api import getName
from cyberapps.knowledge.interfaces import IJobPosition, IQualification
from cyberapps.knowledge.interfaces import IJPDescription, IIPSkillsRequired
from cyberapps.knowledge.interfaces import IQualificationsRequired
from cyberapps.knowledge.interfaces import IQualificationsRecorded
from cyberapps.knowledge.interfaces import ISkillsRecorded
from cyberapps.knowledge.interfaces import IIPSkillsQuestionnaire
from cybertools.organize.interfaces import IPerson
from loops.common import AdapterBase, adapted, baseObject
from loops.concept import Concept
from loops.interfaces import IConcept
from loops.knowledge.survey.base import Questionnaire
from loops.organize.party import getPersonForUser
from loops.setup import addObject
from loops.table import DataTable
from loops.type import TypeInterfaceSourceList
from loops import util
TypeInterfaceSourceList.typeInterfaces += (
IJobPosition, IJPDescription, IIPSkillsRequired,
IQualificationsRequired, IQualification,
IQualificationsRecorded, ISkillsRecorded,
IIPSkillsQuestionnaire)
@implementer(IJobPosition)
class JobPosition(AdapterBase):
def getPersons(self):
result = [adapted(c) for c in self.context.getChildren()]
return [p for p in result if IPerson.providedBy(p)]
def getJPDescription(self):
for c in self.context.getChildren():
obj = adapted(c)
if IJPDescription.providedBy(obj):
return obj
def createJPDescription(self):
concepts = self.getLoopsRoot().getConceptManager()
name = 'jpdesc.' + self.name
name = INameChooser(concepts).chooseName(name, None)
type = concepts['jpdescription']
obj = addObject(concepts, Concept, name, type=type,
title='JP Description: ' + self.title)
self.context.assignChild(obj)
return adapted(obj)
def getIPSkillsRequired(self):
for c in self.context.getChildren():
obj = adapted(c)
if IIPSkillsRequired.providedBy(obj):
return obj
def createIPSkillsRequired(self):
concepts = self.getLoopsRoot().getConceptManager()
name = 'ipsreq.' + self.name
name = INameChooser(concepts).chooseName(name, None)
type = concepts['ipskillsrequired']
obj = addObject(concepts, Concept, name, type=type,
title='IP Skills Req: ' + self.title)
self.context.assignChild(obj)
return adapted(obj)
def getQualificationsRequired(self):
for c in self.context.getChildren():
obj = adapted(c)
if IQualificationsRequired.providedBy(obj):
return obj
def createQualificationsRequired(self):
concepts = self.getLoopsRoot().getConceptManager()
name = 'qureq.' + self.name
name = INameChooser(concepts).chooseName(name, None)
type = concepts['qualificationsrequired']
obj = addObject(concepts, Concept, name, type=type,
title='Qualifications Req: ' + self.title)
self.context.assignChild(obj)
return adapted(obj)
@implementer(IJPDescription)
class JPDescription(AdapterBase):
_contextAttributes = AdapterBase._contextAttributes + list(IJPDescription)
@implementer(IIPSkillsRequired)
class IPSkillsRequired(AdapterBase):
_contextAttributes = AdapterBase._contextAttributes + list(IIPSkillsRequired)
@implementer(IQualificationsRequired)
class QualificationsRequired(AdapterBase):
_contextAttributes = (AdapterBase._contextAttributes +
list(IQualificationsRequired))
@implementer(IQualification)
class Qualification(DataTable):
_contextAttributes = AdapterBase._contextAttributes + list(IQualification)
@implementer(IQualificationsRecorded)
class QualificationsRecorded(AdapterBase):
_contextAttributes = (AdapterBase._contextAttributes +
list(IQualificationsRecorded))
@implementer(ISkillsRecorded)
class SkillsRecorded(AdapterBase):
_contextAttributes = (AdapterBase._contextAttributes +
list(ISkillsRecorded))
@implementer(IIPSkillsQuestionnaire)
class IPSkillsQuestionnaire(Questionnaire):
def getQuestionGroups(self, personId=None):
if personId is None:
person = getPersonForUser(self.context)
else:
person = util.getObjectForUid(personId)
result = []
required = self.getRequiredIPSkills(person)
groups = super(IPSkillsQuestionnaire, self).getQuestionGroups()
if not required:
return groups
for group in groups:
skills = self.getIPSkillsForGroup(group)
if skills:
for skill in skills:
if skill in required:
result.append(group)
break
else:
result.append(group)
return result
def getIPSkillsForGroup(self, group):
result = []
for p in baseObject(group).getParents():
if getName(p.conceptType) == 'ipskill':
result.append(adapted(p))
return result
def getRequiredIPSkills(self, person):
result = []
for p in person.getParents():
job = adapted(p)
if IJobPosition.providedBy(job):
ipskills = job.getIPSkillsRequired()
if ipskills is not None:
requirements = ipskills.requirements
for item in requirements.values():
if item.get('selected'):
result.append(util.getObjectForUid(item['uid']))
return result

View file

@ -0,0 +1,83 @@
#
# Copyright (c) 2015 Helmut Merz helmutm@cy55.de
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
"""
Interfaces for knowledge and skills management specials.
"""
from zope.i18nmessageid import MessageFactory
from zope.interface import Interface, Attribute
from zope import schema
from cybertools.composer.schema.grid.interfaces import KeyTable
from loops.interfaces import ILoopsAdapter
from loops.knowledge.survey.interfaces import IQuestionnaire
from loops.table import IDataTable
from loops.util import _
_ = MessageFactory('cyberapps.knowledge')
class IJobPosition(ILoopsAdapter):
pass
class IJPDescription(ILoopsAdapter):
header = Attribute('Header data.')
administrativeData = Attribute('Administrative job data.')
workDescription = Attribute('Work description.')
class IIPSkillsRequired(ILoopsAdapter):
requirements = Attribute('Required interpersonal skills.')
class IQualificationsRequired(ILoopsAdapter):
requirements = Attribute('Required qualifications.')
class IQualification(IDataTable):
data = KeyTable(title=_(u'Qualification Schema'),
description=_(u'Data fields for specifying the qualification.'),
required=False)
certVocabulary = schema.List(title=_(u'Certification Vocabulary'),
description=_(u'List of proposed certificates.'),
required=False)
class IQualificationsRecorded(ILoopsAdapter):
data = Attribute('Qualifications recorded for person.')
class ISkillsRecorded(ILoopsAdapter):
data = Attribute('Skills recorded for person.')
class IIPSkillsQuestionnaire(IQuestionnaire):
""" Allow specialized questionnaire implementation that limits
question groups according to competences required for person.
"""

View file

@ -0,0 +1,56 @@
# this depends on import of loops/knowledge/data/knowledge_de.dmp
type(u'datatable', u'Datentabelle', viewName=u'',
typeInterface=u'loops.table.IDataTable',
options=u'action.portlet:edit_concept')
type(u'ipskill', u'Kompetenz', viewName=u'',
options=u'action.portlet:edit_concept')
type(u'ipskillsrequired', u'Soll-Profil Kompetenzen', viewName=u'',
typeInterface=u'cyberapps.knowledge.interfaces.IIPSkillsRequired',
options=u'action.portlet:edit_concept\n'
u'organize.stateful:task_states\nportlet_states:task_states')
type(u'qualificationsrequired', u'Soll-Profil Qualifikationen', viewName=u'',
typeInterface=u'cyberapps.knowledge.interfaces.IQualificationsRequired',
options=u'action.portlet:edit_concept\n'
u'organize.stateful:task_states\nportlet_states:task_states')
type(u'jobposition', u'Stelle', viewName=u'',
typeInterface=u'cyberapps.knowledge.interfaces.IJobPosition',
options=u'action.portlet:edit_concept')
type(u'jpdescription', u'Stellenbeschreibung', viewName=u'',
typeInterface=u'cyberapps.knowledge.interfaces.IJPDescription',
options=u'action.portlet:edit_concept\n'
u'organize.stateful:task_states\nportlet_states:task_states')
type(u'qualification', u'Qualifikation', viewName=u'',
options=u'action.portlet:edit_concept')
type(u'qualificationsrecorded', u'Ist-Profil Qualifikationen', viewName=u'',
typeInterface=u'cyberapps.knowledge.interfaces.IQualificationsRecorded',
options=u'action.portlet:edit_concept\n'
u'organize.stateful:task_states\nportlet_states:task_states')
type(u'skillsrecorded', u'Ist-Profil F\xe4higkeiten', viewName=u'',
typeInterface=u'cyberapps.knowledge.interfaces.ISkillsRecorded',
options=u'action.portlet:edit_concept\n'
u'organize.stateful:task_states\nportlet_states:task_states')
# data tables
#concept(u'jpdesc_administrative', u'Stellenbeschreibung administrativ', u'datatable',
# columns=[u'number', u'key', u'label', u'description', u'optional'])
#concept(u'jpdesc_workdesc', u'Stellenbeschreibung Tätigkeit',
# u'datatable',
# columns=[u'number', u'key', u'label', u'description', u'optional'])
#concept(u'jpdesc_qualifications', u'Qualifikationen',
# u'datatable',
# columns=[u'number', u'key', u'label', u'description', u'optional'])
#concept(u'job_names', u'Stellenbezeichnungen', u'datatable')
# structure
child(u'general', u'jpdescription', u'standard')
child(u'general', u'ipskill', u'standard')
child(u'general', u'ipskillsrequired', u'standard')
child(u'general', u'qualificationsrequired', u'standard')
child(u'general', u'jobposition', u'standard')
child(u'general', u'qualificationsrecorded', u'standard')
child(u'general', u'skillsrecorded', u'standard')
# records
#records(u'qualification',
# u'cyberapps.knowledge.qualification.data.QualificationRecord')

View file

@ -0,0 +1,24 @@
msgid ""
msgstr ""
"Project-Id-Version: 0.0.1\n"
"POT-Creation-Date: 2014-08-08 12:00 CET\n"
"PO-Revision-Date: 2014-08-08 12:00 CET\n"
"Last-Translator: Helmut Merz <helmutm@cy55.de>\n"
"Language-Team: cyberapps.knowledge developers <helmutm@cy55.de>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: kwrite\n"
msgid "Jobs / Positions"
msgstr ""
msgid "Create Job..."
msgstr ""
msgid "Create a new job / position"
msgstr ""
msgid "label_jobposition_title"
msgstr ""

View file

@ -0,0 +1,241 @@
msgid ""
msgstr ""
"Project-Id-Version: 0.0.1\n"
"POT-Creation-Date: 2014-08-08 12:00 CET\n"
"PO-Revision-Date: 2016-10-10 12:00 CET\n"
"Last-Translator: Helmut Merz <helmutm@cy55.de>\n"
"Language-Team: cyberapps.knowledge developers <helmutm@cy55.de>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: kwrite\n"
msgid "Jobs / Positions"
msgstr "Stellen"
msgid "Create Job"
msgstr "Stelle anlegen"
msgid "Create a New Job / Position"
msgstr "Eine neue Stelle anlegen"
msgid "Back"
msgstr "Zurück"
msgid "Back to Resource Management Page"
msgstr "Zurück zur Ressourcenmanagement-Übersichtsseite"
# job positions
msgid "label_jobposition_title"
msgstr "Stellenbezeichnung"
msgid "label_jpdescription"
msgstr "Stellenbeschreibung"
msgid "Created at"
msgstr "Erstellt am"
msgid "Responsible"
msgstr "Verantwortlich"
msgid "Next Review"
msgstr "Nächste Überprüfung"
msgid "option_review_0"
msgstr "in 6 Monaten"
msgid "option_review_1"
msgstr "in 9 Monaten"
msgid "option_review_2"
msgstr "in 12 Monaten"
msgid "option_review_3"
msgstr "in 18 Monaten"
msgid "option_review_4"
msgstr "in 24 Monaten"
# requirements
msgid "label_qualifications_required"
msgstr "Sollprofil Qualifikationen"
msgid "label_ipskills_required"
msgstr "Sollprofil Kompetenzen"
msgid "label_persondata"
msgstr "Ist-Profile"
msgid "label_ipskills_number_selected"
msgstr "Anzahl der ausgewählten Kompetenzen:"
# persons
msgid "label_managers"
msgstr "Leitung"
msgid "label_employees"
msgstr "Mitarbeiter/innen"
msgid "label_others"
msgstr "Weitere Personen"
msgid "label_person_fullname"
msgstr "Name"
msgid "label_jobs_assigned"
msgstr "Stelle(n)"
msgid "label_qualifications"
msgstr "Qualifikationen"
msgid "label_skills"
msgstr "Fähigkeiten"
msgid "label_ipskills"
msgstr "Kompetenzen"
msgid "label_professional_experience"
msgstr "Berufserfahrung"
msgid "label_certificate"
msgstr "Nachweis"
msgid "label_experience"
msgstr "Erfahrung"
msgid "label_interest"
msgstr "Interesse"
msgid "label_preferences"
msgstr "Präferenzen"
msgid "option_cert_0"
msgstr "Zertifikat"
msgid "option_cert_1"
msgstr "Zeugnis"
msgid "option_cert_2"
msgstr "Teilnahmebescheinigung"
msgid "option_cert_3"
msgstr "Immatrikulationsbescheinigung"
msgid "option_cert_4"
msgstr "Ausbildungsvertrag"
msgid "option_exp_0"
msgstr "1 - Damit habe ich ein wenig Erfahrung"
msgid "option_exp_1"
msgstr "2"
msgid "option_exp_2"
msgstr "3"
msgid "option_exp_3"
msgstr "4"
msgid "option_exp_4"
msgstr "5 - Damit habe ich sehr viel Erfahrung"
msgid "option_int_0"
msgstr "1 - Interessiert mich ein wenig"
msgid "option_int_1"
msgstr "2"
msgid "option_int_2"
msgstr "3"
msgid "option_int_3"
msgstr "4"
msgid "option_int_4"
msgstr "5 - Interessiert mich sehr"
# forms / fields
msgid "label_category"
msgstr "Kategorie"
msgid "label_knowledge_level"
msgstr "Ausprägungsgrad"
msgid "label_requirement"
msgstr "Erforderlich/Wünschenswert"
msgid "option_req_0"
msgstr "-"
msgid "option_req_1"
msgstr "wünschenswert"
msgid "option_req_2"
msgstr "erforderlich"
# state / buttons
msgid "to be done"
msgstr "Ausstehend"
msgid "draft"
msgstr "In Arbeit"
msgid "active"
msgstr "Fertig"
msgid "edit job description"
msgstr "Stellenbeschreibung bearbeiten"
msgid "edit profile"
msgstr "Profil bearbeiten"
msgid "button_save"
msgstr "Zwischenspeichern"
msgid "button_activate"
msgstr "Freigeben"
msgid "Save"
msgstr "Speichern"
msgid "Cancel"
msgstr "Abbrechen"
msgid "delete job position"
msgstr "Stelle löschen"
msgid "onclick_delete_jobposition"
msgstr "return confirm('Wollen Sie die Stelle mit allen zugehörigen Daten wirklich löschen?')"
msgid "copy job position"
msgstr "Stelle kopieren"
msgid "onclick_copy_jobposition"
msgstr "return confirm('Wollen Sie die Stelle mit allen zugehörigen Daten kopieren?')"
# reports
msgid "select_person"
msgstr "Person für Einzelauswertung auswählen"
msgid "all_persons"
msgstr "Auswertung für alle anzeigen"
msgid "qualifications_required"
msgstr "Anforderungen"
msgid "ipskills_required"
msgstr "Soll-Wert"
msgid "label_skillValues"
msgstr "Selbsteinschätzung"
msgid "label_refValues"
msgstr "Fremdeinschätzung"

View file

@ -0,0 +1,15 @@
msgid ""
msgstr ""
"Project-Id-Version: 0.0.1\n"
"POT-Creation-Date: 2014-08-08 12:00 CET\n"
"PO-Revision-Date: 2014-08-08 12:00 CET\n"
"Last-Translator: Helmut Merz <helmutm@cy55.de>\n"
"Language-Team: cyberapps.knowledge developers <helmutm@cy55.de>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: kwrite\n"
msgid "label_jobposition_title"
msgstr "Position"

32
cyberapps/knowledge/tests.py Executable file
View file

@ -0,0 +1,32 @@
# cyberapps.knowledge.tests
import os
import unittest, doctest
from zope.interface.verify import verifyClass
from loops.setup import importData as baseImportData
importPath = os.path.dirname(__file__)
def importData(loopsRoot):
baseImportData(loopsRoot, importPath, 'knowledge_de.dmp')
class Test(unittest.TestCase):
"Basic tests for the cyberapps.knowledge package."
def testSomething(self):
pass
def test_suite():
flags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS
return unittest.TestSuite((
unittest.makeSuite(Test),
doctest.DocFileSuite('README.txt', optionflags=flags),
))
if __name__ == '__main__':
unittest.main(defaultTest='test_suite')

View file

@ -18,9 +18,9 @@ dependencies = [
[project.optional-dependencies]
jwt = ["python_jwt", "jwcrypto"]
jwt = ["python-jwt", "jwcrypto"]
test = ["zope.testrunner"]
[tool.setuptools]
packages = ["cco"]
packages = ["cco", "cyberapps"]