def next(self, xml, state, mongo, config, *, skip_reverse=False): xmliter = iter(xml) # consume up to this node ifnode = xmliter.find(lambda e: e.getAttribute('id') == self.id) if not get_values(state)[self.id]['condition']: xmliter.expand(ifnode) return make_node(xmliter.next_skipping_elifelse(), xmliter)
def work(self, config, state, channel, mongo): data_forms = self.make_request(get_values(state)) return [ Form.state_json(data_form['id'], [{ 'name': item['name'], 'state': 'valid', 'type': item['type'], 'value': item['value'], 'label': item['label'], 'value_caption': item['value_caption'], 'hidden': False, } for item in data_form['items']]) for data_form in data_forms ]
def work(self, config, state, channel, mongo): xml = Xml.load(config, self.procname) xmliter = iter(xml) node = make_node(next(xmliter), xmliter) context = get_values(state) data = { 'form_array': [f.render(context) for f in self.forms], } collected_input = node.validate_input(data) xml.start(node, collected_input, mongo, channel, '__system__') return []
def test_get_values(): execution = { 'values': { 'form1': [ { 'input1': 'A', }, { 'input1': 'B', }, ], }, } context = get_values(execution) assert context['form1']['input1'] == 'B' assert list(context['form1'].all())[0]['input1'] == 'A'
def work(self, config, state, channel, mongo): tree = Condition().parse(self.condition) try: value = ConditionTransformer(get_values(state)).transform(tree) except ValueError as e: raise InconsistentState('Could not evaluate condition: {}'.format( str(e))) return [ Form.state_json(self.id, [{ 'name': 'condition', 'state': 'valid', 'type': 'bool', 'value': value, 'value_caption': str(value), }]) ]
def resolve_params(self, state=None): computed_params = {} for param in self.auth_params: if state is not None and param.type == 'ref': element_ref, req = param.value.split('#') if element_ref == 'user': value = state['actors'][req] elif element_ref == 'form': try: _form, _input = req.split('.') value = get_values(state)[_form][_input] except ValueError: value = None else: value = param.value computed_params[param.name] = value return computed_params
def next(self, xml, state, mongo, config, *, skip_reverse=False): context = get_values(state) if skip_reverse or context[self.id]['response'] == 'accept': return super().next(xml, state, mongo, config) state_updates = cascade_invalidate(xml, state, context[self.id]['inputs'], context[self.id]['comment']) # update state collection = mongo[config['EXECUTION_COLLECTION']] collection.update_one({ 'id': state['id'], }, { '$set': state_updates, }) # reload state state = next(collection.find({'id': state['id']})) first_invalid_node = track_next_node(xml, state, mongo, config) return first_invalid_node
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 teardown(self, node, pointer, user, input): ''' finishes the node's lifecycle ''' execution = pointer.proxy.execution.get() execution.proxy.actors.add(user) actor_json = { '_type': 'actor', 'state': 'valid', 'user': user.to_json(include=[ '_type', 'fullname', 'identifier', ]), 'forms': input, } # update pointer collection = self.get_mongo()[self.config['POINTER_COLLECTION']] collection.update_one({ 'id': pointer.id, }, { '$set': { 'state': 'finished', 'finished_at': datetime.now(), 'actors': Map([actor_json], key=lambda a: a['user']['identifier']).to_json(), }, }) values = self.compact_values(input) # update state exe_collection = self.get_mongo()[self.config['EXECUTION_COLLECTION']] ptr_collection = self.get_mongo()[self.config['POINTER_COLLECTION']] mongo_exe = exe_collection.find_one_and_update( {'id': execution.id}, { '$addToSet': { 'actor_list': { 'node': node.id, 'identifier': user.identifier, }, }, '$set': { **{ 'state.items.{node}.state'.format(node=node.id): 'valid', 'state.items.{node}.actors.items.{identifier}'.format( node=node.id, identifier=user.identifier, ): actor_json, 'actors.{}'.format(node.id): user.identifier, }, **values }, }, return_document=pymongo.collection.ReturnDocument.AFTER, ) context = get_values(mongo_exe) # update execution's name and description execution.name = render_or( execution.name_template, execution.name, context, ) execution.description = render_or(execution.description_template, execution.description, context) execution.save() exe_collection.update_one( {'id': execution.id}, { '$set': { 'name': execution.name, 'description': execution.description, 'values._execution.0.name': execution.name, 'values._execution.0.description': execution.description, } }, ) ptr_collection.update_many( {'execution.id': execution.id}, {'$set': { 'execution': execution.to_json(), }}, ) LOGGER.debug('Deleted pointer p:{} n:{} e:{}'.format( pointer.id, pointer.node_id, execution.id, )) pointer.delete()
def wakeup(self, node, execution, channel, state): ''' Waking up a node often means to notify someone or something about the execution, this is the first step in node's lifecycle ''' # TODO remove this code from here since it doesn't belong to the limits # of the handler # BEGIN ------------ exc_col = self.get_mongo()[self.config['EXECUTION_COLLECTION']] ptr_col = self.get_mongo()[self.config['POINTER_COLLECTION']] # get currect execution context exc_doc = next(exc_col.find({'id': execution.id})) context = get_values(exc_doc) # interpolate rendered_name = render_or(node.name, node.name, context) rendered_description = render_or(node.description, node.description, context) node.name = rendered_name node.description = rendered_description # END ------------ # TODO read note above, this code does not belong here # create a pointer in this node pointer = self.create_pointer(node, execution) LOGGER.debug('Created pointer p:{} n:{} e:{}'.format( pointer.id, node.id, execution.id, )) # mark this node as ongoing exc_col.update_one({ 'id': execution.id, }, { '$set': { 'state.items.{}.state'.format(node.id): 'ongoing', }, }) # update registry about this pointer ptr_col.insert_one(node.pointer_entry(execution, pointer)) # notify someone (can raise an exception if isinstance(node, UserAttachedNode): notified_users = self.notify_users(node, pointer, channel, state) else: notified_users = [] # do some work (can raise an exception if not node.is_async(): input = node.work(self.config, state, channel, self.get_mongo()) else: input = [] # set actors to this pointer (means everything succeeded) ptr_col.update_one({ 'id': pointer.id, }, { '$set': { 'notified_users': notified_users, }, }) # nodes with forms are not queued if not node.is_async(): return pointer, input