source: main/waeup.sirp/trunk/src/waeup/sirp/applicants/workflow.py @ 6318

Last change on this file since 6318 was 6318, checked in by uli, 13 years ago

Add subscriber that replaces the redundant code from browser.py.

  • Property svn:keywords set to Id
File size: 5.8 KB
Line 
1"""Workflow for applicants.
2"""
3import grok
4from datetime import datetime
5from hurry.workflow.workflow import (
6    Transition, Workflow, WorkflowVersions, WorkflowInfo, WorkflowState,
7    NullCondition)
8from hurry.workflow.interfaces import (
9    IWorkflow, IWorkflowState, IWorkflowInfo, IWorkflowVersions,
10    InvalidTransitionError, IWorkflowTransitionEvent)
11from waeup.sirp.applicants.interfaces import IApplicantBaseData
12
13
14INITIALIZED = 'initialized'
15STARTED = 'started'
16SUBMITTED = 'submitted'
17ADMITTED = 'admitted'
18NOT_ADMITTED = 'not admitted'
19CREATED = 'created'
20
21def create_workflow():
22    init_transition = Transition(
23        transition_id = 'init',
24        title = 'Initialize application',
25        source = None,
26        condition = NullCondition,
27        destination = INITIALIZED)
28
29    start_transition = Transition(
30        transition_id = 'start',
31        title = 'Start application',
32        source = INITIALIZED,
33        destination = STARTED)
34
35    submit_transition = Transition(
36        transition_id = 'submit',
37        title = 'Submit application',
38        source = STARTED,
39        destination = SUBMITTED)
40
41    admit_transition = Transition(
42        transition_id = 'admit',
43        title = 'Admit applicant',
44        source = SUBMITTED,
45        destination = ADMITTED)
46
47    refuse1_transition = Transition(
48        transition_id = 'refuse1',
49        title = 'Refuse application',
50        source = SUBMITTED,
51        destination = NOT_ADMITTED)
52
53    refuse2_transition = Transition(
54        transition_id = 'refuse2',
55        title = 'Refuse application',
56        source = ADMITTED,
57        destination = NOT_ADMITTED)
58
59    create_transition = Transition(
60        transition_id = 'create',
61        title = 'Create student record',
62        source = ADMITTED,
63        destination = CREATED)
64
65    reset1_transition = Transition(
66        transition_id = 'reset1',
67        title = 'Reset application',
68        source = SUBMITTED,
69        destination = STARTED)
70
71    reset2_transition = Transition(
72        transition_id = 'reset2',
73        title = 'Reset application',
74        source = ADMITTED,
75        destination = STARTED)
76
77    reset3_transition = Transition(
78        transition_id = 'reset3',
79        title = 'Reset application',
80        source = NOT_ADMITTED,
81        destination = STARTED)
82
83    reset4_transition = Transition(
84        transition_id = 'reset4',
85        title = 'Reset application',
86        source = CREATED,
87        destination = STARTED)
88
89    return [init_transition, start_transition, submit_transition,
90            admit_transition, create_transition, refuse1_transition,
91            refuse2_transition, reset1_transition, reset2_transition,
92            reset3_transition, reset4_transition]
93
94
95class ApplicationWorkflow(Workflow):
96    """A hurry.workflow Workflow with more appropriate error messages.
97    """
98    grok.provides(IWorkflow)
99    def __init__(self):
100        super(Workflow, self).__init__()
101        self.refresh(create_workflow())
102
103    def getTransition(self, source, transition_id):
104        transition = self._id_transitions[transition_id]
105        if transition.source != source:
106            raise InvalidTransitionError(
107                "Transition '%s' requires '%s' as source state (is: '%s')" % (
108                    transition_id, transition.source, source))
109        return transition
110
111
112class ApplicationWorkflowNullVersions(WorkflowVersions):
113    """A workflow versions manager that does not handle versions.
114
115    Sounds odd, but the default implementation of
116    :class:`hurry.workflow.workflow.WorkflowVersions` is a base
117    implementation that raises :exc:`NotImplemented` exceptions for
118    most of the methods defined below.
119
120    If we want to register a versionless workflow, an utility
121    implementing IWorkflowVersions is looked up nevertheless by
122    WorkflowInfo and WorkflowState components so we **have** to
123    provide workflow versions, even if we do not support versioned
124    workflows.
125
126    This implementation returns empty result sets for any requests,
127    but does not raise :exc:`NotImplemented`.
128    """
129    def getVersions(self, state, id):
130        return []
131
132    def getVersionsWithAutomaticTransitions(self):
133        return []
134
135    def hasVersion(self, id, state):
136        return False
137
138    def hasVersionId(self, id):
139        return False
140
141# Register global utilities for workflows and workflow versions...
142grok.global_utility(ApplicationWorkflow, IWorkflow)
143grok.global_utility(ApplicationWorkflowNullVersions, IWorkflowVersions)
144
145class ApplicationState(grok.Adapter, WorkflowState):
146    """An adapter to adapt Applicant objects to workflow states.
147    """
148    grok.context(IApplicantBaseData)
149    grok.provides(IWorkflowState)
150
151class ApplicationInfo(grok.Adapter, WorkflowInfo):
152    """Adapter to adapt Applicant objects to workflow info objects.
153    """
154    grok.context(IApplicantBaseData)
155    grok.provides(IWorkflowInfo)
156
157
158@grok.subscribe(IApplicantBaseData, IWorkflowTransitionEvent)
159def handle_applicant_transition_event(obj, event):
160    """Append message to applicant when transition happened.
161    """
162    timestamp = datetime.now().strftime("%d/%m/%Y %H:%M:%S")
163    comment = event.comment or '(no comment)'
164    # XXX: `messages` is meta data which shouldn't really be part of
165    #      the Applicant class. Furthermore we should not store HTML
166    #      code but simple messages. This has to be cleaned up
167    #      further. For now we replace the redundant code in browser
168    #      mod.  As we're here: one could use permissions and similar
169    #      to finetune transitions instead of doing that manually in
170    #      UI stuff.
171    msg = '%s - %s to %s: %s: %s' % (
172        timestamp, event.source, event.destination, event.transition.title,
173        comment)
174    msgs = getattr(obj, 'messages', None)
175    if msgs != '':
176        msgs += '<br />'
177    obj.messages += msg
178    return
Note: See TracBrowser for help on using the repository browser.