diff --git a/cyberapps/ccmkg/README.txt b/cyberapps/ccmkg/README.txt new file mode 100644 index 0000000..4eb970e --- /dev/null +++ b/cyberapps/ccmkg/README.txt @@ -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 diff --git a/cyberapps/ccmkg/__init__.py b/cyberapps/ccmkg/__init__.py new file mode 100644 index 0000000..38314f3 --- /dev/null +++ b/cyberapps/ccmkg/__init__.py @@ -0,0 +1,3 @@ +""" +$Id$ +""" diff --git a/cyberapps/ccmkg/browser.py b/cyberapps/ccmkg/browser.py new file mode 100644 index 0000000..2f77f86 --- /dev/null +++ b/cyberapps/ccmkg/browser.py @@ -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 + diff --git a/cyberapps/ccmkg/configure.zcml b/cyberapps/ccmkg/configure.zcml new file mode 100644 index 0000000..d98d96f --- /dev/null +++ b/cyberapps/ccmkg/configure.zcml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + diff --git a/cyberapps/ccmkg/data.py b/cyberapps/ccmkg/data.py new file mode 100644 index 0000000..ae32985 --- /dev/null +++ b/cyberapps/ccmkg/data.py @@ -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) + diff --git a/cyberapps/ccmkg/interfaces.py b/cyberapps/ccmkg/interfaces.py new file mode 100644 index 0000000..ccc6fa3 --- /dev/null +++ b/cyberapps/ccmkg/interfaces.py @@ -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,) + diff --git a/cyberapps/ccmkg/macros.pt b/cyberapps/ccmkg/macros.pt new file mode 100644 index 0000000..2b7f19a --- /dev/null +++ b/cyberapps/ccmkg/macros.pt @@ -0,0 +1,34 @@ + + + +

+ Something


+ + + + + + + + + + + + + + + +
+ + + + + Fieldname: + Value
 
+
+ diff --git a/cyberapps/ccmkg/tests.py b/cyberapps/ccmkg/tests.py new file mode 100755 index 0000000..2f5f103 --- /dev/null +++ b/cyberapps/ccmkg/tests.py @@ -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') diff --git a/cyberapps/knowledge/README.txt b/cyberapps/knowledge/README.txt new file mode 100644 index 0000000..b95e219 --- /dev/null +++ b/cyberapps/knowledge/README.txt @@ -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 + + \ No newline at end of file diff --git a/cyberapps/knowledge/__init__.py b/cyberapps/knowledge/__init__.py new file mode 100644 index 0000000..972e910 --- /dev/null +++ b/cyberapps/knowledge/__init__.py @@ -0,0 +1,3 @@ +""" +cyberapps.knowledge +""" diff --git a/cyberapps/knowledge/browser/__init__.py b/cyberapps/knowledge/browser/__init__.py new file mode 100644 index 0000000..afe7e3b --- /dev/null +++ b/cyberapps/knowledge/browser/__init__.py @@ -0,0 +1,3 @@ +""" +cyberapps.knowledge.browser +""" diff --git a/cyberapps/knowledge/browser/chart.js b/cyberapps/knowledge/browser/chart.js new file mode 100644 index 0000000..869a30e --- /dev/null +++ b/cyberapps/knowledge/browser/chart.js @@ -0,0 +1,32 @@ +/* */ diff --git a/cyberapps/knowledge/browser/configure.zcml b/cyberapps/knowledge/browser/configure.zcml new file mode 100644 index 0000000..facc4f6 --- /dev/null +++ b/cyberapps/knowledge/browser/configure.zcml @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cyberapps/knowledge/browser/knowledge.css b/cyberapps/knowledge/browser/knowledge.css new file mode 100644 index 0000000..5687a37 --- /dev/null +++ b/cyberapps/knowledge/browser/knowledge.css @@ -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; + } +} diff --git a/cyberapps/knowledge/browser/person.py b/cyberapps/knowledge/browser/person.py new file mode 100644 index 0000000..cb0af19 --- /dev/null +++ b/cyberapps/knowledge/browser/person.py @@ -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]) diff --git a/cyberapps/knowledge/browser/person_macros.pt b/cyberapps/knowledge/browser/person_macros.pt new file mode 100644 index 0000000..85bae9b --- /dev/null +++ b/cyberapps/knowledge/browser/person_macros.pt @@ -0,0 +1,340 @@ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + +

label_managers

label_employees

label_others

+
+ + + + + + label_person_fullname + label_jobs_assigned + label_qualifications + label_skills + label_ipskills + label_preferences + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+ +  
+
+ + + + + + + +
+ Organisation/Team: + +
+
+
+ + +
+
+ + +
+
+ + + + +
+ + +
+ +
+ + + + + + + + + + + + + + + + + + + + + +
label_categorylabel_knowledge_levellabel_certificate
+ + +
+
+ + label_certificate
+ + + + + + + + + +
+ +
+ + + + + + + +
+ + +
+ +
+ + + + + + + + + + + + + + + + + + +
label_skillslabel_experiencelabel_interest
+ + + + + + + +
+ +
+ + + + + + \ No newline at end of file diff --git a/cyberapps/knowledge/browser/qualification.py b/cyberapps/knowledge/browser/qualification.py new file mode 100644 index 0000000..994a1ca --- /dev/null +++ b/cyberapps/knowledge/browser/qualification.py @@ -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) + diff --git a/cyberapps/knowledge/browser/qualification_macros.pt b/cyberapps/knowledge/browser/qualification_macros.pt new file mode 100644 index 0000000..cf167da --- /dev/null +++ b/cyberapps/knowledge/browser/qualification_macros.pt @@ -0,0 +1,483 @@ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
label_jobposition_titlelabel_jpdescriptionlabel_qualifications_requiredlabel_ipskills_requiredlabel_persondata
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+

+

Create a New Job / Position

+ +
+ + label_jobposition_title: + + +
+
+ + + + + + +
+ + + + + + + + + + + + + +
Created at: + +
Responsible: + +
+ + : + + +
+
+ + +

+
+ + + + + + + +
+ + + +
+
+
+ +
+ +
+ + +

+
+ + + + + + + + + + + + + + + + + +
+ + + : +
+
1. +
+
+ +
+ + + + + + + +
+ + +
+ +
+ + + + + + + + + + + + + + + + + + + + + +
label_categorylabel_knowledge_levellabel_requirement
+ + +
+
+ + label_requirement
+ + + + + + + + +
+ +
+ + + + + + + +
+ +
+ +
+ + Number of currently selected skills: + +
+ + + + + + + + + + + + + +
+ weniger wichtig + äußerst wichtig
+ + + + +
+
+
    +
  1. +
+
+
+
+ +
+ +
+ + + + + + + + + + + + + + + +
+ + + +
+ + +
+ + +
+ + +
+ + +
+ + +
+
+
+ Back +
+
+ + + \ No newline at end of file diff --git a/cyberapps/knowledge/browser/report.py b/cyberapps/knowledge/browser/report.py new file mode 100644 index 0000000..6c14a0c --- /dev/null +++ b/cyberapps/knowledge/browser/report.py @@ -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) diff --git a/cyberapps/knowledge/browser/report_macros.pt b/cyberapps/knowledge/browser/report_macros.pt new file mode 100644 index 0000000..db550e7 --- /dev/null +++ b/cyberapps/knowledge/browser/report_macros.pt @@ -0,0 +1,350 @@ + + + + + + +
+ +
+
+ + + + + +
+ + + + + + + + + + + + + +
Created at:
Responsible:
Next Review:
+
+ + +

+ + + + + + +
+
+
+
+ +
+ + +

+ + + + + + + + + + + + + +
+ : +
1.
+ +
+ +

3.

+ + + + + + + + + + + + + + + + + + + + + + + +
label_categorylabel_knowledge_levellabel_requirement
+ +
+ + label_requirement
+ + +
+
+
+ +

4.

+ + + + + + + + + + + +
+ weniger wichtig + äußerst wichtig
+ + + + +
+
+
+
+
 
+
+

+
+ + + + +
+ Restrict report to person: + +
  +
+
+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
+ +
+ Required +
+ +
+ + +
blubb + + + Certificate: + + +
+
+
+ +

+ + + + + + + + + + + + + + + + + + +
 
+ + + +
+
+ +
Required
+
+ + + + +
+ +
+
+ + + + + + +
+ + + + + +
+
+
+
+
+
+ + + diff --git a/cyberapps/knowledge/configure.zcml b/cyberapps/knowledge/configure.zcml new file mode 100644 index 0000000..697f44b --- /dev/null +++ b/cyberapps/knowledge/configure.zcml @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cyberapps/knowledge/data.py b/cyberapps/knowledge/data.py new file mode 100644 index 0000000..26b8555 --- /dev/null +++ b/cyberapps/knowledge/data.py @@ -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 diff --git a/cyberapps/knowledge/interfaces.py b/cyberapps/knowledge/interfaces.py new file mode 100644 index 0000000..0761fd2 --- /dev/null +++ b/cyberapps/knowledge/interfaces.py @@ -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. + """ diff --git a/cyberapps/knowledge/knowledge_de.dmp b/cyberapps/knowledge/knowledge_de.dmp new file mode 100644 index 0000000..ee13fe9 --- /dev/null +++ b/cyberapps/knowledge/knowledge_de.dmp @@ -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') diff --git a/cyberapps/knowledge/locales/cyberapps.knowledge.pot b/cyberapps/knowledge/locales/cyberapps.knowledge.pot new file mode 100644 index 0000000..15d114c --- /dev/null +++ b/cyberapps/knowledge/locales/cyberapps.knowledge.pot @@ -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 \n" +"Language-Team: cyberapps.knowledge developers \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 "" diff --git a/cyberapps/knowledge/locales/de/LC_MESSAGES/cyberapps.knowledge.mo b/cyberapps/knowledge/locales/de/LC_MESSAGES/cyberapps.knowledge.mo new file mode 100644 index 0000000..718b1b7 Binary files /dev/null and b/cyberapps/knowledge/locales/de/LC_MESSAGES/cyberapps.knowledge.mo differ diff --git a/cyberapps/knowledge/locales/de/LC_MESSAGES/cyberapps.knowledge.po b/cyberapps/knowledge/locales/de/LC_MESSAGES/cyberapps.knowledge.po new file mode 100644 index 0000000..8f00705 --- /dev/null +++ b/cyberapps/knowledge/locales/de/LC_MESSAGES/cyberapps.knowledge.po @@ -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 \n" +"Language-Team: cyberapps.knowledge developers \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" + diff --git a/cyberapps/knowledge/locales/en/LC_MESSAGES/cyberapps.knowledge.mo b/cyberapps/knowledge/locales/en/LC_MESSAGES/cyberapps.knowledge.mo new file mode 100644 index 0000000..1627fe2 Binary files /dev/null and b/cyberapps/knowledge/locales/en/LC_MESSAGES/cyberapps.knowledge.mo differ diff --git a/cyberapps/knowledge/locales/en/LC_MESSAGES/cyberapps.knowledge.po b/cyberapps/knowledge/locales/en/LC_MESSAGES/cyberapps.knowledge.po new file mode 100644 index 0000000..5724fd5 --- /dev/null +++ b/cyberapps/knowledge/locales/en/LC_MESSAGES/cyberapps.knowledge.po @@ -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 \n" +"Language-Team: cyberapps.knowledge developers \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" diff --git a/cyberapps/knowledge/tests.py b/cyberapps/knowledge/tests.py new file mode 100755 index 0000000..9379215 --- /dev/null +++ b/cyberapps/knowledge/tests.py @@ -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') diff --git a/pyproject.toml b/pyproject.toml index 84700a4..a44a4d7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,9 +18,9 @@ dependencies = [ [project.optional-dependencies] -jwt = ["python_jwt", "jwcrypto"] +jwt = ["python-jwt", "jwcrypto"] test = ["zope.testrunner"] [tool.setuptools] -packages = ["cco"] +packages = ["cco", "cyberapps"]