add cyberapps package with 2 sub-packages, + Python3 fixes
This commit is contained in:
parent
812bd28bc2
commit
3524df21a1
31 changed files with 3787 additions and 2 deletions
86
cyberapps/ccmkg/README.txt
Normal file
86
cyberapps/ccmkg/README.txt
Normal 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
|
3
cyberapps/ccmkg/__init__.py
Normal file
3
cyberapps/ccmkg/__init__.py
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
"""
|
||||||
|
$Id$
|
||||||
|
"""
|
93
cyberapps/ccmkg/browser.py
Normal file
93
cyberapps/ccmkg/browser.py
Normal 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
|
||||||
|
|
36
cyberapps/ccmkg/configure.zcml
Normal file
36
cyberapps/ccmkg/configure.zcml
Normal 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
22
cyberapps/ccmkg/data.py
Normal 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)
|
||||||
|
|
55
cyberapps/ccmkg/interfaces.py
Normal file
55
cyberapps/ccmkg/interfaces.py
Normal 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
34
cyberapps/ccmkg/macros.pt
Normal 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> </td></tr>
|
||||||
|
</tal:row>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</metal:listing>
|
||||||
|
|
23
cyberapps/ccmkg/tests.py
Executable file
23
cyberapps/ccmkg/tests.py
Executable 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')
|
62
cyberapps/knowledge/README.txt
Normal file
62
cyberapps/knowledge/README.txt
Normal 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
|
||||||
|
|
||||||
|
|
3
cyberapps/knowledge/__init__.py
Normal file
3
cyberapps/knowledge/__init__.py
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
"""
|
||||||
|
cyberapps.knowledge
|
||||||
|
"""
|
3
cyberapps/knowledge/browser/__init__.py
Normal file
3
cyberapps/knowledge/browser/__init__.py
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
"""
|
||||||
|
cyberapps.knowledge.browser
|
||||||
|
"""
|
32
cyberapps/knowledge/browser/chart.js
Normal file
32
cyberapps/knowledge/browser/chart.js
Normal 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> */
|
102
cyberapps/knowledge/browser/configure.zcml
Normal file
102
cyberapps/knowledge/browser/configure.zcml
Normal 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>
|
112
cyberapps/knowledge/browser/knowledge.css
Normal file
112
cyberapps/knowledge/browser/knowledge.css
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
428
cyberapps/knowledge/browser/person.py
Normal file
428
cyberapps/knowledge/browser/person.py
Normal 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])
|
340
cyberapps/knowledge/browser/person_macros.pt
Normal file
340
cyberapps/knowledge/browser/person_macros.pt
Normal 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> </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>
|
501
cyberapps/knowledge/browser/qualification.py
Normal file
501
cyberapps/knowledge/browser/qualification.py
Normal 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)
|
||||||
|
|
483
cyberapps/knowledge/browser/qualification_macros.pt
Normal file
483
cyberapps/knowledge/browser/qualification_macros.pt
Normal 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>
|
312
cyberapps/knowledge/browser/report.py
Normal file
312
cyberapps/knowledge/browser/report.py
Normal 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)
|
350
cyberapps/knowledge/browser/report_macros.pt
Normal file
350
cyberapps/knowledge/browser/report_macros.pt
Normal 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 /> <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 />
|
||||||
|
</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"> </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' '">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" /> </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>
|
80
cyberapps/knowledge/configure.zcml
Normal file
80
cyberapps/knowledge/configure.zcml
Normal 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
174
cyberapps/knowledge/data.py
Normal 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
|
83
cyberapps/knowledge/interfaces.py
Normal file
83
cyberapps/knowledge/interfaces.py
Normal 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.
|
||||||
|
"""
|
56
cyberapps/knowledge/knowledge_de.dmp
Normal file
56
cyberapps/knowledge/knowledge_de.dmp
Normal 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')
|
24
cyberapps/knowledge/locales/cyberapps.knowledge.pot
Normal file
24
cyberapps/knowledge/locales/cyberapps.knowledge.pot
Normal 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 ""
|
Binary file not shown.
|
@ -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"
|
||||||
|
|
Binary file not shown.
|
@ -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
32
cyberapps/knowledge/tests.py
Executable 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')
|
|
@ -18,9 +18,9 @@ dependencies = [
|
||||||
|
|
||||||
[project.optional-dependencies]
|
[project.optional-dependencies]
|
||||||
|
|
||||||
jwt = ["python_jwt", "jwcrypto"]
|
jwt = ["python-jwt", "jwcrypto"]
|
||||||
|
|
||||||
test = ["zope.testrunner"]
|
test = ["zope.testrunner"]
|
||||||
|
|
||||||
[tool.setuptools]
|
[tool.setuptools]
|
||||||
packages = ["cco"]
|
packages = ["cco", "cyberapps"]
|
||||||
|
|
Loading…
Add table
Reference in a new issue