def recover_step(self, message: dict): ''' given an execution id and a pointer from the persistent storage, return the asociated process node to continue its execution ''' try: pointer = Pointer.get_or_exception(message['pointer_id']) if pointer.status != 'ongoing': raise ModelNotFoundError( 'Specified pointer never existed, and never will', ) except ModelNotFoundError: raise InconsistentState('Queued dead pointer') user = User.get_by('identifier', message.get('user_identifier')) if user is None: if message.get('user_identifier') == '__system__': user = User(identifier='__system__', fullname='System').save() else: raise InconsistentState('sent identifier of unexisten user') return ( pointer, user, message['input'], )
def get_by_or_exception(cls, field, value): obj = cls.get_by(field, value) if obj is None: raise ModelNotFoundError('This object does not exist in database') return obj
def get_or_exception(cls, id): ''' Tries to retrieve an instance of this model from the database or raises an exception in case of failure ''' obj = cls.get(id) if obj is None: raise ModelNotFoundError('This object does not exist in database') return obj
def process_status(id): collection = mongo.db[app.config['EXECUTION_COLLECTION']] try: exc = next(collection.find({'id': id})) except StopIteration: raise ModelNotFoundError( 'Specified execution never existed, and never will') return jsonify({ 'data': json_prepare(exc), })
def execution_template(id): # load values collection = mongo.db[flask.current_app.config['EXECUTION_COLLECTION']] try: execution = next(collection.find({'id': id})) except StopIteration: raise ModelNotFoundError( 'Specified execution never existed, and never will') if 'process_name' not in execution: return 'Not supported for old processes', 409 context = make_context(execution, flask.current_app.config) # load template process_name = execution['process_name'] name, version, _ = process_name.split('.') # Loaders will be inserted in inverse order and then reversed. The fallback # is the default template at ``templates/summary.html`` paths = [ path.join(path.dirname(path.realpath(__file__)), '../../templates'), ] app_template_path = flask.current_app.config['TEMPLATE_PATH'] if app_template_path is not None and path.isdir(app_template_path): paths.append(app_template_path) process_dir = path.join(app_template_path, name) if path.isdir(process_dir): paths.append(process_dir) process_version_dir = path.join(process_dir, version) if path.isdir(process_version_dir): paths.append(process_version_dir) env = Environment(loader=FileSystemLoader(reversed(paths)), autoescape=select_autoescape(['html', 'xml'])) env.filters['datetimeformat'] = datetimeformat for name, function in flask.current_app.config['JINJA_FILTERS'].items(): env.filters[name] = function return flask.make_response( env.get_template('summary.html').render(**context), 200, )
def reload(self): ''' reloads this object so if it was updated in the database it now contains the new values''' key = self.key() redis = type(self).get_redis() if not redis.exists(key): raise ModelNotFoundError('This object has been deleted') data = debyte_hash(redis.hgetall(key)) for fieldname, field in self.proxy: value = field.recover(data, redis) setattr(self, fieldname, value) return self
def cancel_execution(self, message): execution = Execution.get_or_exception(message['execution_id']) if execution.status != 'ongoing': raise ModelNotFoundError( 'Specified execution never existed, and never will', ) execution.status = 'cancelled' execution.finished_at = datetime.now() execution.save() for pointer in execution.pointers.q().filter(status='ongoing'): pointer.status = 'cancelled' pointer.finished_at = datetime.now() pointer.save() self.execution_collection().update_one({ 'id': execution.id, }, { '$set': { 'status': execution.status, 'finished_at': execution.finished_at } }) self.pointer_collection().update_many( { 'execution.id': execution.id, 'state': 'ongoing', }, { '$set': { 'state': 'cancelled', 'finished_at': execution.finished_at } }) self.pointer_collection().update_many({ 'execution.id': execution.id, }, {'$set': { 'execution': execution.to_json(), }})
def execution_template(id): # load values collection = mongo.db[app.config['EXECUTION_COLLECTION']] try: exc = next(collection.find({'id': id})) except StopIteration: raise ModelNotFoundError( 'Specified execution never existed, and never will' ) execution = json_prepare(exc) if 'process_name' not in exc: return 'Not supported for old processes', 409 # prepare default template default = ['<div><b>Available keys</b></div>'] context = get_values(execution) for key in context: token = '<div>{}</div>' default.append(token.format(key, key)) template_string = ''.join(default) # load template template_dir = app.config['TEMPLATE_PATH'] process_name = execution['process_name'] name, version, _ = process_name.split('.') # file or folder ff_name = '.'.join([name, version]) template_name = None # If template file exists... if os.path.isfile( os.path.join(template_dir, ff_name + '.html') ): template_name = ff_name + '.html' # Else check for any folder... elif os.path.isfile( os.path.join(template_dir, ff_name + '/', 'template.html') ): # set loader for "includes" custom_loader = jinja2.ChoiceLoader([ jinja2.FileSystemLoader([ app.config['TEMPLATE_PATH'] + '/' + ff_name, ]), ]) bp.jinja_loader = custom_loader # ... and return the "main template" template_name = ff_name + '/template.html' if template_name: with open(os.path.join(template_dir, template_name), 'r') as contents: template_string = contents.read() # return template interpolation return make_response( render_template_string(template_string, **context), 200, )
def patch(self, message): execution = Execution.get_or_exception(message['execution_id']) if execution.status != 'ongoing': raise ModelNotFoundError( 'Specified execution never existed, and never will', ) xml = Xml.load(self.config, execution.process_name, direct=True) # set nodes with pointers as unfilled, delete pointers updates = {} user = User.get_by( 'identifier', message.get('user_identifier'), ) if user is None: if message.get('user_identifier') == '__system__': user = User(identifier='__system__', fullname='System').save() else: raise InconsistentState('sent identifier of unexisten user') for pointer in execution.pointers.q().filter(status='ongoing'): updates['state.items.{node}.state'.format( node=pointer.node_id, )] = 'unfilled' pointer.status = 'cancelled' pointer.finished_at = datetime.now() pointer.save() self.pointer_collection().update_one({ 'id': pointer.id, }, { '$set': { 'state': 'cancelled', 'finished_at': pointer.finished_at, 'patch': { 'comment': message['comment'], 'inputs': message['inputs'], 'actor': user.to_json(include=[ '_type', 'fullname', 'identifier', ]), }, }, }) self.execution_collection().update_one({ 'id': execution.id, }, { '$set': updates, }) # retrieve updated state state = next(self.execution_collection().find({'id': execution.id})) state_updates, array_filters = cascade_invalidate( xml, state, message['inputs'], message['comment']) # update state self.execution_collection().update_one( {'id': state['id']}, {'$set': state_updates}, array_filters=array_filters, ) # retrieve updated state state = next(self.execution_collection().find({'id': execution.id})) first_invalid_node = track_next_node(xml, state, self.get_mongo(), self.config) # wakeup and start execution from the found invalid node self.wakeup_and_notify(first_invalid_node, execution, state)