Source code for univention.management.console.modules.mixins

#!/usr/bin/python3
#
# Univention Management Console
#  Mixins for UMC 2.0 modules
#
# SPDX-FileCopyrightText: 2013-2025 Univention GmbH
# SPDX-License-Identifier: AGPL-3.0-only

"""
Mixins for UMC module classes
=============================

This module provides some mixins that can be incorporated in existing UMC
modules. These mixins extend the functionality of the module in an easy-to-use
way. Just let your module derive from
:class:`~univention.management.console.module.Base` (as before) and the mixin
classes.
"""
import random

from univention.lib.i18n import Translation
from univention.management.console.error import BadRequest
from univention.management.console.modules.decorators import simple_response


_ = Translation('univention-management-console').translate


[docs] class Progress: """ Class to keep track of the progress during execution of a function. Used internally. """ def __init__(self, progress_id, title, total): self.id = progress_id self.title = title self.message = '' self.current = 0.0 self.total = total self.intermediate = [] self.finished = False self.exc_info = None self.retry_after = 200 self.location = None # there is another variable named # "result". it is only set if explicitly # calling finish_with_result
[docs] def progress(self, detail=None, message=None): self.current += 1 self.intermediate.append(detail) if message is not None: self.message = message
[docs] def finish(self): if self.finished: return False self.finished = True return True
[docs] def finish_with_result(self, result): if self.finish(): self.result = result
[docs] def initialised(self): return {'id': self.id, 'title': self.title}
[docs] def exception(self, exc_info): self.exc_info = exc_info
[docs] def poll(self): if self.exc_info: self.finish() raise self.exc_info[1].with_traceback(self.exc_info[2]) ret = { 'title': self.title, 'finished': self.finished, 'intermediate': self.intermediate[:], 'message': self.message, 'retry_after': self.retry_after, } try: ret['percentage'] = self.current / self.total * 100 except ZeroDivisionError: # ret['percentage'] = float('Infinity') FIXME: JSON cannot handle Infinity ret['percentage'] = 'Infinity' if self.location is not None: ret['location'] = self.location if hasattr(self, 'result'): ret['result'] = self.result del self.intermediate[:] return ret
[docs] class ProgressMixin: """ Mixin to provide two new functions: * *new_progress* to create a new :class:`~univention.management.console.modules.mixins.Progress`. * *progress* to let the client fetch the progress made up to this moment. The *progress* function needs to be made public by the XML definition of the module. To use this mixin, just do:: class Instance(Base, ProgressMixin): pass """
[docs] def new_progress(self, title=None, total=0): if not hasattr(self, '_progress_id'): self._progress_id = 0 if not hasattr(self, '_progress_objs'): self._progress_objs = {} if title is None: title = _('Please wait for operation to finish') self._progress_id += random.randint(1, 100000) self._progress_objs[self._progress_id] = progress = Progress(self._progress_id, title, total) return progress
[docs] @simple_response def progress(self, progress_id): if not hasattr(self, '_progress_objs'): self._progress_objs = {} try: progress_obj = self._progress_objs[progress_id] except KeyError: raise BadRequest(_('Invalid progress ID')) else: ret = progress_obj.poll() if ret['finished']: del self._progress_objs[progress_id] return ret
[docs] def thread_progress_finished_callback(self, thread, result, request, progress): if self._is_active(request): self.thread_finished_callback(thread, result, request) if isinstance(result, BaseException): progress.exception(thread.exc_info) # FIXME: broken since Bug #47114 return progress.progress(None, _('finished...')) if result: progress.finish_with_result(result) else: progress.finish()