From b1bbfa832199611ad7e9df7c8077ad93b508df4d Mon Sep 17 00:00:00 2001 From: Nathaniel Kofalt <nathaniel@kofalt.com> Date: Wed, 7 Sep 2016 14:53:12 -0500 Subject: [PATCH] Add gear suggestion endpoint --- api/api.py | 1 + api/dao/containerstorage.py | 14 ++++++++++++++ api/jobs/gears.py | 31 +++++++++++++++++++++++++++++-- api/jobs/handlers.py | 18 +++++++++++++++--- 4 files changed, 59 insertions(+), 5 deletions(-) diff --git a/api/api.py b/api/api.py index dee8d8e5..b2d19c3a 100644 --- a/api/api.py +++ b/api/api.py @@ -125,6 +125,7 @@ routes = [ webapp2_extras.routes.PathPrefixRoute(r'/api/gears', [ webapp2.Route(r'/<:[^/]+>', GearHandler), webapp2.Route(r'/<:[^/]+>/invocation', GearHandler, handler_method='get_invocation'), + webapp2.Route(r'/<:[^/]+>/suggest/<:[^/]+>/<:[^/]+>', GearHandler, handler_method='suggest'), ]), webapp2.Route(r'/api/rules', RulesHandler), webapp2.Route(r'/api/groups', grouphandler.GroupHandler, handler_method='get_all', methods=['GET']), diff --git a/api/dao/containerstorage.py b/api/dao/containerstorage.py index 175b9f26..37105127 100644 --- a/api/dao/containerstorage.py +++ b/api/dao/containerstorage.py @@ -122,3 +122,17 @@ class GroupStorage(ContainerStorage): }, upsert=True) +def inflate_container(cr): + """ + Given a container reference, inflate its hierarchy into a map. + Eeventually, this might want to deduplicate with logic in hierarchy.py. + """ + + if cr.type != 'session': + raise Exception('Only sessions are supported for inflation right now') + + oid = bson.ObjectId(cr.id) + root = ContainerStorage('sessions', True).exec_op('GET', oid, projection={'permissions': 0}) + root['acquisitions'] = ContainerStorage('acquisitions', True).exec_op('GET', query={'session': oid}, projection={'permissions': 0}) + + return root diff --git a/api/jobs/gears.py b/api/jobs/gears.py index f236370b..664573f0 100644 --- a/api/jobs/gears.py +++ b/api/jobs/gears.py @@ -2,10 +2,15 @@ Gears """ +import bson.objectid +import jsonschema +from jsonschema import Draft4Validator +import gear_tools + from .. import config from .jobs import Job +from ..dao.containerstorage import inflate_container -import gear_tools log = config.log @@ -51,9 +56,31 @@ def get_gear_by_name(name): # Mongo returns the full document: { '_id' : 'gears', 'gear_list' : [ { .. } ] }, so strip that out return gear_doc[SINGLETON_KEY][0] -def get_invocation(gear): +def get_invocation_schema(gear): return gear_tools.derive_invocation_schema(gear['manifest']) +def suggest_container(gear, cr): + """ + Given a container reference, suggest files that would work well for each input on a gear. + """ + + root = inflate_container(cr) + invocation_schema = get_invocation_schema(gear) + + schemas = {} + for x in gear['manifest']['inputs']: + schema = gear_tools.isolate_file_invocation(invocation_schema, x) + schemas[x] = Draft4Validator(schema) + + # It would be nice to have use a visitor here instead of manual key loops. + for acq in root['acquisitions']: + for f in acq.get('files', []): + f['suggested'] = {} + for x in schemas: + f['suggested'][x] = schemas[x].is_valid({}) + + return root + def insert_gear(doc): config.db.singletons.update( {"_id" : "gears"}, diff --git a/api/jobs/handlers.py b/api/jobs/handlers.py index b7a52de9..a2ab54a1 100644 --- a/api/jobs/handlers.py +++ b/api/jobs/handlers.py @@ -5,11 +5,11 @@ API request handlers for the jobs module import json import StringIO -from ..dao.containerutil import create_filereference_from_dictionary, create_containerreference_from_dictionary, create_containerreference_from_filereference +from ..dao.containerutil import create_filereference_from_dictionary, create_containerreference_from_dictionary, create_containerreference_from_filereference, ContainerReference from .. import base from .. import config -from .gears import get_gears, get_gear_by_name, get_invocation, remove_gear, upsert_gear +from .gears import get_gears, get_gear_by_name, get_invocation_schema, remove_gear, upsert_gear, suggest_container from .jobs import Job from .queue import Queue @@ -47,7 +47,19 @@ class GearHandler(base.RequestHandler): self.abort(403, 'Request requires login') gear = get_gear_by_name(_id) - return get_invocation(gear) + return get_invocation_schema(gear) + + def suggest(self, _id, cont_name, cid): + + if self.public_request: + self.abort(403, 'Request requires login') + + cr = ContainerReference(cont_name, cid) + if not self.superuser_request: + cr.check_access(self.uid, 'ro') + + gear = get_gear_by_name(_id) + return suggest_container(gear, cr) def post(self, _id): """Upsert an entire gear document.""" -- GitLab