Skip to content

Commit cf522fd

Browse files
committed
Add Related Actions.
A related action can be attached to a job. It is shown to the user as a button on the form view of the jobs. When the button is used, the related action is called and must return an OpenERP "client action". Examples of related actions: * Open the form view of the record that is concerned by the job. * Open a browser on the URL of the record on an external system.
2 parents c5965af + 385c7ef commit cf522fd

10 files changed

+347
-4
lines changed

connector/CHANGES.rst

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ Changelog
77
* Job arguments can now contain unicode strings (thanks to Stéphane Bidoul) lp:1288187
88
* List view of the jobs improved
99
* Jobs now support multicompany (thanks to Laurent Mignon) https://lists.launchpad.net/openerp-connector-community/msg00253.html)
10+
* An action can be assigned to a job. The action is called with a button on the job and could be something like open a form view or an url.
1011

1112
2.1.1 (2014-02-06)
1213
~~~~~~~~~~~~~~~~~~

connector/connector.py

+19
Original file line numberDiff line numberDiff line change
@@ -304,3 +304,22 @@ def bind(self, external_id, binding_id):
304304
:type binding_id: int
305305
"""
306306
raise NotImplementedError
307+
308+
def unwrap_binding(self, binding_id, browse=False):
309+
""" For a binding record, gives the normal record.
310+
311+
Example: when called with a ``magento.product.product`` id,
312+
it will return the corresponding ``product.product`` id.
313+
314+
:param browse: when True, returns a browse_record instance
315+
rather than an ID
316+
"""
317+
raise NotImplementedError
318+
319+
def unwrap_model(self):
320+
""" For a binding model, gives the normal model.
321+
322+
Example: when called on a binder for ``magento.product.product``,
323+
it will return ``product.product``.
324+
"""
325+
raise NotImplementedError

connector/queue/job.py

+70-3
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,11 @@
1919
#
2020
##############################################################################
2121

22-
import sys
23-
import logging
2422
import inspect
23+
import functools
24+
import logging
2525
import uuid
26+
import sys
2627
from datetime import datetime, timedelta, MINYEAR
2728
from pickle import loads, dumps, UnpicklingError
2829

@@ -598,6 +599,11 @@ def postpone(self, result=None, seconds=None):
598599
if result is not None:
599600
self.result = result
600601

602+
def related_action(self, session):
603+
if not hasattr(self.func, 'related_action'):
604+
return None
605+
return self.func.related_action(session, self)
606+
601607

602608
def job(func):
603609
""" Decorator for jobs.
@@ -630,7 +636,7 @@ def job(func):
630636
infinite retries. Default is 5.
631637
* eta: the job can be executed only after this datetime
632638
(or now + timedelta if a timedelta or integer is given)
633-
639+
634640
* description : a human description of the job,
635641
intended to discriminate job instances
636642
(Default is the func.__doc__ or 'Function %s' % func.__name__)
@@ -655,6 +661,9 @@ def export_one_thing(session, model_name, one_thing):
655661
# => the job will be executed with a low priority and not before a
656662
# delay of 5 hours from now
657663
664+
See also: :py:func:`related_action` a related action can be attached
665+
to a job
666+
658667
"""
659668
def delay(session, model_name, *args, **kwargs):
660669
"""Enqueue the function. Return the uuid of the created job."""
@@ -665,3 +674,61 @@ def delay(session, model_name, *args, **kwargs):
665674
**kwargs)
666675
func.delay = delay
667676
return func
677+
678+
679+
def related_action(action=lambda session, job: None, **kwargs):
680+
""" Attach a *Related Action* to a job.
681+
682+
A *Related Action* will appear as a button on the OpenERP view.
683+
The button will execute the action, usually it will open the
684+
form view of the record related to the job.
685+
686+
The ``action`` must be a callable that responds to arguments::
687+
688+
session, job, **kwargs
689+
690+
Example usage:
691+
692+
.. code-block:: python
693+
694+
def related_action_partner(session, job):
695+
model = job.args[0]
696+
partner_id = job.args[1]
697+
# eventually get the real ID if partner_id is a binding ID
698+
action = {
699+
'name': _("Partner"),
700+
'type': 'ir.actions.act_window',
701+
'res_model': model,
702+
'view_type': 'form',
703+
'view_mode': 'form',
704+
'res_id': partner_id,
705+
}
706+
return action
707+
708+
@job
709+
@related_action(action=related_action_partner)
710+
def export_partner(session, model_name, partner_id):
711+
# ...
712+
713+
The kwargs are transmitted to the action:
714+
715+
.. code-block:: python
716+
717+
def related_action_product(session, job, extra_arg=1):
718+
assert extra_arg == 2
719+
model = job.args[0]
720+
product_id = job.args[1]
721+
722+
@job
723+
@related_action(action=related_action_product, extra_arg=2)
724+
def export_product(session, model_name, product_id):
725+
# ...
726+
727+
"""
728+
def decorate(func):
729+
if kwargs:
730+
func.related_action = functools.partial(action, **kwargs)
731+
else:
732+
func.related_action = action
733+
return func
734+
return decorate

connector/queue/model.py

+16
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,22 @@ class QueueJob(orm.Model):
8080
'active': True,
8181
}
8282

83+
def open_related_action(self, cr, uid, ids, context=None):
84+
""" Open the related action associated to the job """
85+
if hasattr(ids, '__iter__'):
86+
assert len(ids) == 1, "1 ID expected, got %s" % ids
87+
ids = ids[0]
88+
session = ConnectorSession(cr, uid, context=context)
89+
storage = OpenERPJobStorage(session)
90+
job = self.browse(cr, uid, ids, context=context)
91+
job = storage.load(job.uuid)
92+
action = job.related_action(session)
93+
if action is None:
94+
raise orm.except_orm(
95+
_('Error'),
96+
_('No action available for this job'))
97+
return action
98+
8399
def _change_job_state(self, cr, uid, ids, state, result=None, context=None):
84100
""" Change the state of the `Job` object itself so it
85101
will change the other fields (date, result, ...)

connector/queue/model_view.xml

+4
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,10 @@
6666
string="Set to 'Done'"
6767
type="object"
6868
groups="connector.group_connector_manager"/>
69+
<button name="open_related_action"
70+
string="Related"
71+
type="object"
72+
/>
6973
<field name="state"
7074
widget="statusbar"
7175
statusbar_visible="pending,enqueued,started,done"

connector/related_action.py

+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# -*- coding: utf-8 -*-
2+
##############################################################################
3+
#
4+
# Author: Guewen Baconnier
5+
# Copyright 2014 Camptocamp SA
6+
#
7+
# This program is free software: you can redistribute it and/or modify
8+
# it under the terms of the GNU Affero General Public License as
9+
# published by the Free Software Foundation, either version 3 of the
10+
# License, or (at your option) any later version.
11+
#
12+
# This program is distributed in the hope that it will be useful,
13+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
# GNU Affero General Public License for more details.
16+
#
17+
# You should have received a copy of the GNU Affero General Public License
18+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
19+
#
20+
##############################################################################
21+
22+
"""
23+
Related Actions
24+
25+
Related actions are associated with jobs.
26+
When called on a job, they will return an action to the client.
27+
28+
"""
29+
30+
from openerp.tools.translate import _
31+
from .connector import Environment, Binder
32+
33+
34+
def unwrap_binding(session, job, id_pos=2, binder_class=Binder):
35+
""" Open a form view with the unwrapped record.
36+
37+
For instance, for a job on a ``magento.product.product``,
38+
it will open a ``product.product`` form view with the unwrapped
39+
record.
40+
41+
:param id_pos: position of the binding ID in the args
42+
:param binder_class: base class to search for the binder
43+
"""
44+
binding_model = job.args[0]
45+
# shift one to the left because session is not in job.args
46+
binding_id = job.args[id_pos - 1]
47+
action = {
48+
'name': _('Related Record'),
49+
'type': 'ir.actions.act_window',
50+
'view_type': 'form',
51+
'view_mode': 'form',
52+
}
53+
# try to get an unwrapped record
54+
binding = session.browse(binding_model, binding_id)
55+
if not binding.exists():
56+
# it has been deleted
57+
return None
58+
env = Environment(binding.backend_id, session, binding_model)
59+
binder = env.get_connector_unit(binder_class)
60+
try:
61+
model = binder.unwrap_model()
62+
record_id = binder.unwrap_binding(binding_id)
63+
except ValueError:
64+
# the binding record will be displayed
65+
action.update({
66+
'res_model': binding_model,
67+
'res_id': binding_id,
68+
})
69+
else:
70+
# the unwrapped record will be displayed
71+
action.update({
72+
'res_model': model,
73+
'res_id': record_id,
74+
})
75+
return action

connector/session.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ def change_context(self, values):
138138
:param values: values to apply on the context
139139
:type values: dict
140140
"""
141-
original_context = self._context
141+
original_context = self.context
142142
self._context = original_context.copy()
143143
self._context.update(values)
144144
yield

connector/tests/__init__.py

+2
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import test_producer
2929
import test_connector
3030
import test_mapper
31+
import test_related_action
3132

3233
fast_suite = [
3334
]
@@ -42,4 +43,5 @@
4243
test_producer,
4344
test_connector,
4445
test_mapper,
46+
test_related_action,
4547
]

0 commit comments

Comments
 (0)