2014-12-26 03:59:13 +08:00
|
|
|
"""Defines the anvil logging module.
|
|
|
|
"""
|
|
|
|
|
|
|
|
from anvil import enums
|
|
|
|
from anvil import util
|
|
|
|
|
|
|
|
|
|
|
|
class WorkUnit(object):
|
|
|
|
"""A class used to keep track of work progress.
|
2014-12-27 00:15:18 +08:00
|
|
|
|
|
|
|
The WorkUnit class uses total and complete metrics to keep track of work
|
|
|
|
completed. WorkUnits can be chained in parent-child relationships with any
|
|
|
|
updates to child WorkUnits being aggregated in the parent WorkUnit. The
|
|
|
|
total/complete numbers are used to determine the current status of a given
|
|
|
|
WorkUnit. Once an exception is set on a WorkUnit, its status automatically
|
|
|
|
becomes FAILED along with all units up the parent chain.
|
|
|
|
|
|
|
|
WorkUnits can also have change listeners attached. When the total, complete
|
|
|
|
or exception attributes are updated on a WorkUnit, all listeners on that
|
|
|
|
unit and all units up the parent chain are notified via a call to #update
|
|
|
|
on the listener.
|
2014-12-26 03:59:13 +08:00
|
|
|
"""
|
|
|
|
|
|
|
|
def __init__(self, name):
|
2014-12-27 00:15:18 +08:00
|
|
|
self._complete = None
|
2014-12-26 03:59:13 +08:00
|
|
|
self._total = None
|
|
|
|
self._waiting = True
|
|
|
|
self._exception = None
|
|
|
|
|
|
|
|
self.name = name
|
|
|
|
self.parent = None
|
2014-12-27 00:15:18 +08:00
|
|
|
self.children = []
|
|
|
|
self.listeners = []
|
2014-12-26 03:59:13 +08:00
|
|
|
|
|
|
|
self.start_time = None
|
|
|
|
self.end_time = None
|
|
|
|
|
|
|
|
@property
|
2014-12-27 00:15:18 +08:00
|
|
|
def complete(self):
|
|
|
|
"""Returns a complete count including all child WorkUnits.
|
2014-12-27 08:24:30 +08:00
|
|
|
|
|
|
|
Returns:
|
|
|
|
A number representing the total complete work units encompassed by this
|
|
|
|
WorkUnit and all its child WorkUnits.
|
2014-12-27 00:15:18 +08:00
|
|
|
"""
|
|
|
|
complete = 0
|
2014-12-26 03:59:13 +08:00
|
|
|
for child in self.children:
|
2014-12-27 00:15:18 +08:00
|
|
|
complete += child.complete
|
|
|
|
if self._complete:
|
|
|
|
complete += self._complete
|
|
|
|
return complete
|
2014-12-26 03:59:13 +08:00
|
|
|
|
2014-12-27 00:15:18 +08:00
|
|
|
@complete.setter
|
|
|
|
def complete(self, complete):
|
|
|
|
"""Sets the complete count on this WorkUnit and validates all values.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
complete: A number value corresponding to complete units of work.
|
|
|
|
"""
|
|
|
|
self._complete = complete
|
|
|
|
self._validate_and_update('complete')
|
2014-12-26 03:59:13 +08:00
|
|
|
|
|
|
|
@property
|
|
|
|
def total(self):
|
2014-12-27 00:15:18 +08:00
|
|
|
"""Returns a complete count including all parent WorkUnits.
|
2014-12-27 08:24:30 +08:00
|
|
|
|
|
|
|
Returns:
|
|
|
|
A number representing the total work units encompassed by this WorkUnit
|
|
|
|
and all its child WorkUnits.
|
2014-12-27 00:15:18 +08:00
|
|
|
"""
|
2014-12-26 03:59:13 +08:00
|
|
|
total = 0
|
|
|
|
for child in self.children:
|
|
|
|
total += child.total
|
|
|
|
if self._total:
|
|
|
|
total += self._total
|
|
|
|
return total
|
|
|
|
|
|
|
|
@total.setter
|
|
|
|
def total(self, total):
|
2014-12-27 00:15:18 +08:00
|
|
|
"""Sets the total count on this WorkUnit and validates all values.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
complete: A number value corresponding to total units of work.
|
|
|
|
"""
|
2014-12-26 03:59:13 +08:00
|
|
|
self._total = total
|
2014-12-27 00:15:18 +08:00
|
|
|
self._validate_and_update('total')
|
2014-12-26 03:59:13 +08:00
|
|
|
|
|
|
|
@property
|
2014-12-27 08:24:30 +08:00
|
|
|
def exception(self):
|
2014-12-27 00:15:18 +08:00
|
|
|
"""Gets the exception value on this WorkUnit or any children.
|
2014-12-27 08:24:30 +08:00
|
|
|
|
|
|
|
Returns:
|
|
|
|
An exception object or None if one is not set.
|
2014-12-27 00:15:18 +08:00
|
|
|
"""
|
2014-12-26 03:59:13 +08:00
|
|
|
for child in self.children:
|
|
|
|
if child.exception:
|
|
|
|
return child.exception
|
|
|
|
return self._exception
|
|
|
|
|
|
|
|
@exception.setter
|
|
|
|
def exception(self, exception):
|
2014-12-27 00:15:18 +08:00
|
|
|
"""Sets an exception on this work unit and validates all values.
|
2014-12-26 03:59:13 +08:00
|
|
|
|
2014-12-27 00:15:18 +08:00
|
|
|
Args:
|
|
|
|
exception: A Python exception object.
|
|
|
|
"""
|
|
|
|
self._exception = exception
|
|
|
|
self._validate_and_update('exception')
|
2014-12-26 03:59:13 +08:00
|
|
|
|
|
|
|
def get_status(self):
|
2014-12-27 00:15:18 +08:00
|
|
|
"""Returns the status of this WorkUnit.
|
|
|
|
|
|
|
|
The WorkUnit is in a WAITING state until either its exception, total or
|
|
|
|
complete values are updated or any of its children change to a RUNNING
|
|
|
|
state. The WorkUnit immediately goes to a FAILED state if an exception is
|
|
|
|
set. If the total and completed units are both set to 0, the WorkUnit goes
|
|
|
|
to a SKIPPED state. If total units are set and are greater than the number
|
|
|
|
of completed units, it is set to a RUNNING state. Note that the complete
|
|
|
|
and total units used for this determination include those set on any child
|
|
|
|
WorkUnits. If total and complete are equal and greater than 0, then the
|
|
|
|
WorkUnit is set to SUCCEEDED.
|
2014-12-27 08:24:30 +08:00
|
|
|
|
|
|
|
Returns:
|
|
|
|
An enums.Status value corresponding to the state this WorkUnit is in.
|
2014-12-27 00:15:18 +08:00
|
|
|
"""
|
2014-12-26 03:59:13 +08:00
|
|
|
if self._is_waiting():
|
|
|
|
return enums.Status.WAITING
|
|
|
|
elif self.exception is not None:
|
|
|
|
return enums.Status.FAILED
|
2014-12-27 00:15:18 +08:00
|
|
|
elif self.total == 0 and self.complete == 0:
|
2014-12-26 03:59:13 +08:00
|
|
|
return enums.Status.SKIPPED
|
|
|
|
|
2014-12-27 00:15:18 +08:00
|
|
|
diff = (self.total or 0) - (self.complete or 0)
|
2014-12-26 03:59:13 +08:00
|
|
|
if diff > 0:
|
|
|
|
return enums.Status.RUNNING
|
|
|
|
elif diff == 0:
|
|
|
|
return enums.Status.SUCCEEDED
|
|
|
|
|
|
|
|
def add_child(self, child):
|
2014-12-27 00:15:18 +08:00
|
|
|
"""Adds a child WorkUnit.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
child: A child WorkUnit. The values of this child will be aggregated into
|
|
|
|
the values of this object.
|
|
|
|
"""
|
2014-12-26 03:59:13 +08:00
|
|
|
self.children.append(child)
|
|
|
|
child.parent = self
|
2014-12-27 00:15:18 +08:00
|
|
|
|
|
|
|
def add_change_listener(self, listener):
|
|
|
|
""""Adds a change listener to this object.
|
|
|
|
|
|
|
|
Change listeners will receive notifications of updates to values on this
|
|
|
|
WorkUnit or any of its children. Listeners must implement the following
|
|
|
|
methods:
|
2014-12-28 00:14:53 +08:00
|
|
|
will_listen(WorkUnit):Boolean - Should return true if this listener
|
2014-12-27 00:15:18 +08:00
|
|
|
should listen to this WorkUnit. Gives a listener a chance to keep
|
2014-12-28 00:14:53 +08:00
|
|
|
from receiving updates for a given WorkUnit. If the listener returns
|
|
|
|
True, it should be assumed that WorkUnit passed in will be run and
|
|
|
|
will send updates to the listener.
|
2014-12-27 00:15:18 +08:00
|
|
|
update(WorkUnit, String, *) - A method called when updates occur on a
|
|
|
|
WorkUnit. The method is called with the WorkUnit that changed, the
|
|
|
|
name of the attribute that was updated and the updated value.
|
|
|
|
"""
|
2014-12-28 00:14:53 +08:00
|
|
|
if listener.should_listen(self):
|
2014-12-27 00:15:18 +08:00
|
|
|
self.listeners.append(listener)
|
|
|
|
|
|
|
|
def _validate_and_update(self, attribute):
|
|
|
|
"""Validates the current values on this WorkUnit and propegates updates.
|
|
|
|
|
|
|
|
This method validates the current WorkUnit and performs any updates that
|
|
|
|
occur as side-effects, such as updating the start and end time. This
|
|
|
|
method also calls change listeners and propagates change calls to parent
|
|
|
|
WorkUnits.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
attribute: A string value corresponding to the name of the attribute
|
|
|
|
that was updated.
|
|
|
|
Raises:
|
|
|
|
ValueError: A ValueError is raised if any of the current values are
|
|
|
|
in an invalid state. For instance, if the completed count is greater
|
|
|
|
that the total count.
|
|
|
|
"""
|
|
|
|
if (not self._total == None and
|
|
|
|
not self._complete == None and
|
|
|
|
self._complete > self._total):
|
|
|
|
raise ValueError('Complete tasks cannot be more than the total tasks.')
|
|
|
|
if not self.start_time:
|
|
|
|
self.start_time = util.timer()
|
|
|
|
if self.total == self.complete and not self.total == None:
|
|
|
|
self.end_time = util.timer()
|
|
|
|
self._waiting = False
|
|
|
|
for listener in self.listeners:
|
|
|
|
listener.update(self, attribute, getattr(self, attribute))
|
|
|
|
if self.parent:
|
|
|
|
self.parent._validate_and_update(attribute)
|
|
|
|
|
|
|
|
def _is_waiting(self):
|
|
|
|
"""Returns true iff this WorkUnit and all its child units are WAITING.
|
2014-12-27 08:24:30 +08:00
|
|
|
|
|
|
|
Returns:
|
|
|
|
True iff this WorkUnit and all its child units are WAITING.
|
2014-12-27 00:15:18 +08:00
|
|
|
"""
|
|
|
|
if self._waiting:
|
|
|
|
for child in self.children:
|
|
|
|
if not child._waiting:
|
|
|
|
self._waiting = False
|
|
|
|
break
|
|
|
|
return self._waiting
|
2014-12-27 08:24:30 +08:00
|
|
|
|
|
|
|
|
|
|
|
class LogSource(object):
|
|
|
|
"""A LogSource can be used to log messages filtered on a verbosity level.
|
|
|
|
|
|
|
|
The LogSource class allows the buffered logging of messages at different
|
|
|
|
severity levels and filtered off of Verbosity levels. LogSources can be part
|
|
|
|
of a parent-child relationship, in which case child LogSources can make use
|
|
|
|
of the parent's Verbosity level and LogSinks.
|
|
|
|
|
|
|
|
Messages logged to a LogSource are buffered in the source's buffered_messages
|
|
|
|
collection iff the severity of the message passes the Verbosity filter on the
|
|
|
|
LogSource.
|
|
|
|
"""
|
|
|
|
|
|
|
|
def __init__(self, verbosity=enums.Verbosity.INHERIT):
|
|
|
|
"""Sets up the LogSource with a default Verbosity of INHERIT.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
verbosty: A enums.Verbosity value. Defaults to INHERIT. Note that if a
|
|
|
|
parent LogSource does not exist, then this LogSource will use a NORMAL
|
|
|
|
verbosity level.
|
|
|
|
"""
|
|
|
|
self._verbosity = verbosity
|
|
|
|
self.buffered_messages = []
|
2014-12-29 05:15:27 +08:00
|
|
|
self.log_sinks = []
|
2014-12-27 08:24:30 +08:00
|
|
|
self.parent = None
|
|
|
|
|
|
|
|
@property
|
|
|
|
def verbosity(self):
|
|
|
|
"""Returns the effective verbosity level of this LogSource.
|
|
|
|
|
|
|
|
If this LogSource has a verbosity level of INHERIT and a parent exists, then
|
|
|
|
this accessor will return the verbosity level of the parent.
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
The enums.Verbosity level of this LogSource.
|
|
|
|
"""
|
|
|
|
if not self.parent == None and self._verbosity == enums.Verbosity.INHERIT:
|
|
|
|
return self.parent.verbosity
|
|
|
|
elif self.parent == None and self._verbosity == enums.Verbosity.INHERIT:
|
|
|
|
return enums.Verbosity.NORMAL
|
|
|
|
return self._verbosity
|
|
|
|
|
|
|
|
@verbosity.setter
|
|
|
|
def verbosity(self, verbosity):
|
|
|
|
"""Sets the enums.Verbosity level of this LogSource.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
verbosity: A enums.Verbosity level.
|
|
|
|
"""
|
|
|
|
self._verbosity = verbosity
|
|
|
|
|
|
|
|
def add_child(self, child):
|
|
|
|
"""Adds a child log source.
|
|
|
|
|
2014-12-29 05:25:21 +08:00
|
|
|
Any LogSinks set on the parent will also be set on the child so that all
|
|
|
|
messages from children are received.
|
|
|
|
|
2014-12-27 08:24:30 +08:00
|
|
|
Args:
|
|
|
|
child: A LogSource that will be a child of this LogSource.
|
|
|
|
"""
|
|
|
|
child.parent = self
|
2014-12-29 05:25:21 +08:00
|
|
|
for log_sink in self.log_sinks:
|
|
|
|
child.add_log_sink(log_sink)
|
2014-12-27 08:24:30 +08:00
|
|
|
|
2014-12-29 05:15:27 +08:00
|
|
|
def add_log_sink(self, log_sink):
|
|
|
|
"""Adds a LogSink to this LogSource.
|
|
|
|
|
|
|
|
Adds a listening LogSink to this LogSource. If there any buffered messages,
|
|
|
|
they are delegated synchronously to the added LogSink.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
log_sink: A LogSink object capable of listening for LogSource messages.
|
|
|
|
"""
|
2014-12-29 05:25:21 +08:00
|
|
|
if log_sink in self.log_sinks:
|
|
|
|
return
|
|
|
|
|
2014-12-29 05:15:27 +08:00
|
|
|
self.log_sinks.append(log_sink)
|
|
|
|
for message in self.buffered_messages:
|
|
|
|
log_sink.log(message)
|
|
|
|
|
2014-12-28 00:14:53 +08:00
|
|
|
def log_debug(self, message, name=None):
|
2014-12-27 08:24:30 +08:00
|
|
|
"""Logs a message at DEBUG log level.
|
|
|
|
|
|
|
|
DEBUG log level is only recorded on LogSources with VERBOSE verbosity.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
message: A string message to be logged.
|
2014-12-28 00:14:53 +08:00
|
|
|
name: A string name representing the source of the message. Defaults to
|
|
|
|
none. How this is used is up to the LogSource.
|
2014-12-27 08:24:30 +08:00
|
|
|
"""
|
|
|
|
if self._should_log(enums.LogLevel.DEBUG):
|
2014-12-29 12:33:24 +08:00
|
|
|
message = '[%s] %s' % (
|
|
|
|
enums.log_level_to_string(enums.LogLevel.DEBUG), message)
|
2014-12-29 05:15:27 +08:00
|
|
|
self._log_internal(
|
2014-12-28 00:14:53 +08:00
|
|
|
(enums.LogLevel.DEBUG, util.timer(), name, message))
|
2014-12-27 08:24:30 +08:00
|
|
|
|
2014-12-28 00:14:53 +08:00
|
|
|
def log_info(self, message, name=None):
|
2014-12-27 08:24:30 +08:00
|
|
|
"""Logs a message at INFO log level.
|
|
|
|
|
|
|
|
INFO log level is recorded on LogSources with VERBOSE or NORMAL verbosity.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
message: A string message to be logged.
|
2014-12-28 00:14:53 +08:00
|
|
|
name: A string name representing the source of the message. Defaults to
|
|
|
|
none. How this is used is up to the LogSource.
|
2014-12-27 08:24:30 +08:00
|
|
|
"""
|
|
|
|
if self._should_log(enums.LogLevel.INFO):
|
2014-12-29 12:33:24 +08:00
|
|
|
message = '[%s] %s' % (
|
|
|
|
enums.log_level_to_string(enums.LogLevel.INFO), message)
|
2014-12-29 05:15:27 +08:00
|
|
|
self._log_internal(
|
2014-12-28 00:14:53 +08:00
|
|
|
(enums.LogLevel.INFO, util.timer(), name, message))
|
2014-12-27 08:24:30 +08:00
|
|
|
|
2014-12-28 00:14:53 +08:00
|
|
|
def log_warning(self, message, name=None):
|
2014-12-27 08:24:30 +08:00
|
|
|
"""Logs a message at WARNING log level.
|
|
|
|
|
|
|
|
WARNING log level is recorded on LogSources with VERBOSE or NORMAL
|
|
|
|
verbosity.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
message: A string message to be logged.
|
2014-12-28 00:14:53 +08:00
|
|
|
name: A string name representing the source of the message. Defaults to
|
|
|
|
none. How this is used is up to the LogSource.
|
2014-12-27 08:24:30 +08:00
|
|
|
"""
|
|
|
|
if self._should_log(enums.LogLevel.WARNING):
|
2014-12-29 12:33:24 +08:00
|
|
|
message = '[%s] %s' % (
|
|
|
|
enums.log_level_to_string(enums.LogLevel.WARNING), message)
|
2014-12-29 05:15:27 +08:00
|
|
|
self._log_internal(
|
2014-12-28 00:14:53 +08:00
|
|
|
(enums.LogLevel.WARNING, util.timer(), name, message))
|
2014-12-27 08:24:30 +08:00
|
|
|
|
2014-12-28 00:14:53 +08:00
|
|
|
def log_error(self, message, name=None):
|
2014-12-27 08:24:30 +08:00
|
|
|
"""Logs a message at ERROR log level.
|
|
|
|
|
|
|
|
ERROR log level is recorded on LogSources with any verbosity level.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
message: A string message to be logged.
|
2014-12-28 00:14:53 +08:00
|
|
|
name: A string name representing the source of the message. Defaults to
|
|
|
|
none. How this is used is up to the LogSource.
|
2014-12-27 08:24:30 +08:00
|
|
|
"""
|
|
|
|
if self._should_log(enums.LogLevel.ERROR):
|
2014-12-29 12:33:24 +08:00
|
|
|
message = '[%s] %s' % (
|
|
|
|
enums.log_level_to_string(enums.LogLevel.ERROR), message)
|
2014-12-29 05:15:27 +08:00
|
|
|
self._log_internal(
|
2014-12-28 00:14:53 +08:00
|
|
|
(enums.LogLevel.ERROR, util.timer(), name, message))
|
2014-12-27 08:24:30 +08:00
|
|
|
|
|
|
|
def _should_log(self, level):
|
|
|
|
"""Determines whether a log message should be recorded.
|
|
|
|
|
|
|
|
Given an enums.LogLevel value and the current enums.Verbosity level of this
|
|
|
|
LogSource, this method determines whether the message should be recorded.
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
Returns true if the passed in LogLevel should be recorded given the
|
|
|
|
Verbosity level of this LogSource.
|
|
|
|
"""
|
|
|
|
# Errors should always be shown.
|
|
|
|
if level == enums.LogLevel.ERROR:
|
|
|
|
return True
|
|
|
|
# Otherwise, switch off of the log level and the current verbosity.
|
|
|
|
if self.verbosity == enums.Verbosity.SILENT:
|
|
|
|
return False
|
|
|
|
elif self.verbosity == enums.Verbosity.NORMAL:
|
|
|
|
if level == enums.LogLevel.DEBUG:
|
|
|
|
return False
|
|
|
|
else:
|
|
|
|
return True
|
|
|
|
elif self.verbosity == enums.Verbosity.VERBOSE:
|
|
|
|
return True
|
2014-12-28 00:14:53 +08:00
|
|
|
|
2014-12-29 05:15:27 +08:00
|
|
|
def _log_internal(self, message):
|
|
|
|
"""A private helper method for logging messages.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
message: A tuple containing the LogLevel, the time the message was
|
|
|
|
received, the name sent with the message and the mesage itself.
|
|
|
|
"""
|
|
|
|
if self.log_sinks:
|
|
|
|
for log_sink in self.log_sinks:
|
|
|
|
log_sink.log(message)
|
|
|
|
else:
|
|
|
|
self.buffered_messages.append(message)
|
|
|
|
|
2014-12-28 00:14:53 +08:00
|
|
|
|
|
|
|
class WorkUnitLogSource(LogSource):
|
|
|
|
"""A LogSource meant to function as a listener on WorkUnits.
|
|
|
|
"""
|
|
|
|
|
|
|
|
def __init__(self, verbosity=enums.Verbosity.INHERIT):
|
|
|
|
super(WorkUnitLogSource, self).__init__(verbosity)
|
|
|
|
|
|
|
|
def should_listen(self, work_unit):
|
|
|
|
"""All work_units should be listened to.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
work_unit: The WorkUnit this listener is being asked to listen to.
|
|
|
|
Returns:
|
|
|
|
Returns True if the WorkUnit should be observed by this LogSource.
|
|
|
|
"""
|
|
|
|
self.log_debug(
|
|
|
|
'Adding listener to WorkUnit named \'%s\' with a status of %s.' %
|
|
|
|
(work_unit.name, enums.status_to_string(work_unit.get_status())),
|
|
|
|
work_unit.name)
|
|
|
|
self.log_info(
|
2014-12-29 12:33:24 +08:00
|
|
|
'%s: Logging %s' % (
|
2014-12-28 00:14:53 +08:00
|
|
|
enums.status_to_string(work_unit.get_status()), work_unit.name),
|
|
|
|
work_unit.name)
|
|
|
|
return True
|
|
|
|
|
|
|
|
def update(self, work_unit, attribute, value):
|
|
|
|
"""Receives updates from monitored WorkUnits.
|
|
|
|
|
|
|
|
Given updates for monitored WorkUnits, this method transforms a WorkUnit
|
|
|
|
update into a log message. It logs all calls to this method at DEBUG
|
|
|
|
level, passing all arguments. It then logs formatted messages at INFO
|
|
|
|
level recording the status of the WorkUnit being updated.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
work_unit: The WorkUnit that was updated.
|
|
|
|
attribute: The attribute who's value was updated.
|
|
|
|
value: The new attribute value.
|
|
|
|
"""
|
|
|
|
self.log_debug(
|
|
|
|
'Received an update - WorkUnit: %s, Attr: %s, Value: %s' % (
|
|
|
|
work_unit.name, attribute, value), work_unit.name)
|
|
|
|
if work_unit.get_status() == enums.Status.RUNNING:
|
|
|
|
running = enums.status_to_string(work_unit.get_status())
|
|
|
|
self.log_info(
|
2014-12-29 12:33:24 +08:00
|
|
|
'%s: %s - %s of %s' % (
|
2014-12-28 00:14:53 +08:00
|
|
|
running, work_unit.name, work_unit.complete, work_unit.total),
|
|
|
|
work_unit.name)
|
|
|
|
else:
|
|
|
|
status_string = enums.status_to_string(work_unit.get_status())
|
|
|
|
self.log_info(
|
2014-12-29 12:33:24 +08:00
|
|
|
'%s: %s' % (status_string, work_unit.name), work_unit.name)
|
2014-12-28 00:14:53 +08:00
|
|
|
|
|
|
|
|