-
Notifications
You must be signed in to change notification settings - Fork 4
/
batch_job.py
133 lines (113 loc) · 5.55 KB
/
batch_job.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# ============================================================================
#
# Copyright (C) 2007-2012 Conceptive Engineering bvba. All rights reserved.
# www.conceptive.be / project-camelot@conceptive.be
#
# This file is part of the Camelot Library.
#
# This file may be used under the terms of the GNU General Public
# License version 2.0 as published by the Free Software Foundation
# and appearing in the file license.txt included in the packaging of
# this file. Please review this information to ensure GNU
# General Public Licensing requirements will be met.
#
# If you are unsure which license is appropriate for your use, please
# visit www.python-camelot.com or contact project-camelot@conceptive.be
#
# This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
# WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
#
# For use of this library in commercial applications, please contact
# project-camelot@conceptive.be
#
# ============================================================================
"""Most applications need to perform some scheduled jobs to process information.
Users need to be able to monitor the functioning of those scheduled jobs.
These classes provide the means to store the result of batch jobs to enable the
user to review or plan them.
"""
import sqlalchemy.types
from sqlalchemy import orm, sql
from camelot.core.orm import Entity, Field, ManyToOne, using_options
from camelot.core.utils import ugettext_lazy as _
from camelot.view import filters
from camelot.admin.entity_admin import EntityAdmin
from camelot.admin.action import Action
from camelot.core.document import documented_entity
import camelot.types
import datetime
@documented_entity()
class BatchJobType( Entity ):
"""The type of batch job, the user will be able to filter his
jobs based on their type. A type might be 'Create management reports' """
using_options( tablename = 'batch_job_type' )
name = Field( sqlalchemy.types.Unicode(256), required=True )
parent = ManyToOne( 'BatchJobType' )
def __unicode__(self):
return self.name
@classmethod
def get_or_create( cls, name ):
batch_job_type = cls.query.filter_by( name = name ).first()
if not batch_job_type:
batch_job_type = cls( name = name )
batch_job_type.flush()
return batch_job_type
class Admin(EntityAdmin):
verbose_name = _('Batch job type')
list_display = ['name', 'parent']
def hostname():
import socket
return socket.gethostname()
class CancelBatchJob( Action ):
verbose_name = _('Cancel')
def model_run( self, model_context ):
from camelot.view.action_steps import FlushSession
for batch_job in model_context.get_selection():
batch_job.status = 'canceled'
yield FlushSession( model_context.session )
@documented_entity()
class BatchJob( Entity ):
"""Information the batch job that is planned, running or has run"""
using_options( tablename = 'batch_job', order_by=['date desc'] )
date = Field( sqlalchemy.types.DateTime, required=True, default=datetime.datetime.now )
host = Field( sqlalchemy.types.Unicode(256), required=True, default=hostname )
type = ManyToOne( 'BatchJobType', required=True, ondelete = 'restrict', onupdate = 'cascade' )
status = Field( camelot.types.Enumeration([ (-2, 'planned'),
(-1, 'running'),
(0, 'success'),
(1, 'warnings'),
(2, 'errors'),
(3, 'canceled'), ]), required=True, default='planned' )
message = Field( camelot.types.RichText() )
def is_canceled(self):
"""Verifies if this Batch Job is canceled. Returns :keyword:`True` if
it is. This verification is done without using the ORM, so the verification
has no impact on the current session or the objects itself. This method
is thus suited to call inside a running batch job to verifiy if another
user has canceled the running job.
:return: :keyword:`True` or :keyword:`False`
"""
table = orm.class_mapper( BatchJob ).mapped_table
query = sql.select( [table.c.status] ).where( table.c.id == self.id )
for row in table.bind.execute( query ):
if row['status'] == 'canceled':
return True
return False
def add_exception_to_message(self, exception):
"""If an exception occurs in a batch job, this method can be used to add
the stack trace of the exception to the message"""
import traceback, cStringIO
sio = cStringIO.StringIO()
traceback.print_exc(file=sio)
traceback_print = sio.getvalue()
sio.close()
self.message = (self.message or '') + '<br/>' + unicode(exception) + '<br/><font color="grey">' + traceback_print.replace('\n', '<br/>') + '</font>'
def add_strings_to_message(self, strings):
""":param strings: a list or generator of strings"""
self.message = (self.message or '') + u'<br/>' + '<br/>'.join(list(strings))
class Admin(EntityAdmin):
verbose_name = _('Batch job')
list_display = ['date', 'host', 'type', 'status']
list_filter = ['status', filters.ComboBoxFilter('host')]
form_display = list_display + ['message']
form_actions = [ CancelBatchJob() ]