From c60ebc49d23623dc945d499fdf4fc87ec509c5cd Mon Sep 17 00:00:00 2001 From: Ambrus Simon <ambrussimon@invenshure.com> Date: Fri, 23 Jun 2017 19:33:51 +0200 Subject: [PATCH] POST /api/{cname}/{cid}/analyses working (including job=true) --- api/api.py | 10 +++---- api/dao/containerstorage.py | 51 ++++++++++++++++++---------------- api/handlers/refererhandler.py | 33 +++++++++++----------- bin/database.py | 2 +- 4 files changed, 50 insertions(+), 46 deletions(-) diff --git a/api/api.py b/api/api.py index c4766f1f..d80ea88c 100644 --- a/api/api.py +++ b/api/api.py @@ -248,11 +248,11 @@ endpoints = [ route('/<list_name:files>/<name:{fname}>', FileListHandler, m=['GET', 'DELETE']), route('/<list_name:files>/<name:{fname}>/info', FileListHandler, h='get_info', m=['GET']), - route( '/analyses>', AnalysesHandler, m=['POST']), - prefix('/analyses>', [ - route('/<_id:{cid}>', AnalysesHandler, m=['GET', 'DELETE']), - route('/<_id:{cid}>/files', AnalysesHandler, h='download', m=['GET']), - route('/<_id:{cid}>/files/<name:{fname}>', AnalysesHandler, h='download', m=['GET']), + route( '/analyses', AnalysesHandler, m=['POST']), + prefix('/analyses', [ + route('/<_id:{cid}>', AnalysesHandler, m=['GET', 'DELETE']), + route('/<_id:{cid}>/files', AnalysesHandler, h='download', m=['GET']), + route('/<_id:{cid}>/files/<name:{fname}>', AnalysesHandler, h='download', m=['GET']), ]), route('/<list_name:notes>', NotesListHandler, m=['POST']), diff --git a/api/dao/containerstorage.py b/api/dao/containerstorage.py index 3a49b47d..ccd2cb00 100644 --- a/api/dao/containerstorage.py +++ b/api/dao/containerstorage.py @@ -461,14 +461,19 @@ class AnalysisStorage(ContainerStorage): @staticmethod - def default_analysis(origin): - analysis_obj = {} - analysis_obj['_id'] = str(bson.objectid.ObjectId()) - analysis_obj['created'] = datetime.datetime.utcnow() - analysis_obj['modified'] = datetime.datetime.utcnow() - analysis_obj['user'] = origin.get('id') - - return analysis_obj + def fill_values(analysis, cont_name, cid, origin): + defaults = { + 'parent': { + 'type': containerutil.singularize(cont_name), + 'id': bson.objectid.ObjectId(cid) + }, + '_id': bson.objectid.ObjectId(), + 'created': datetime.datetime.utcnow(), + 'modified': datetime.datetime.utcnow(), + 'user': origin.get('id'), + } + defaults.update(analysis) + return defaults def create_job_and_analysis(self, cont_name, cid, analysis, job, origin): @@ -480,11 +485,7 @@ class AnalysisStorage(ContainerStorage): from ..jobs.gears import validate_gear_config, get_gear from ..jobs.jobs import Job - cid = bson.objectid.ObjectId(cid) - - default = self.default_analysis(origin) - default.update(analysis) - analysis = default + self.fill_values(analysis, cont_name, cid, origin) # Save inputs to analysis and job inputs = {} # For Job object (map of FileReferences) @@ -502,33 +503,35 @@ class AnalysisStorage(ContainerStorage): files.append(file_) analysis['files'] = files - result = self._create_el(cid, analysis, None) - if result.modified_count != 1: - raise APIStorageException('Element not added in list analyses of container {} {}'.format(cont_name, cid)) + result = self.create_el(analysis) + if not result.acknowledged: + raise APIStorageException('Analysis not created for container {} {}'.format(cont_name, cid)) # Prepare job tags = job.get('tags', []) if 'analysis' not in tags: tags.append('analysis') - gear_id = job['gear_id'] - # Config manifest check - gear = get_gear(gear_id) + gear = get_gear(job['gear_id']) if gear.get('gear', {}).get('custom', {}).get('flywheel', {}).get('invalid', False): raise APIConflictException('Gear marked as invalid, will not run!') validate_gear_config(gear, job.get('config')) - destination = containerutil.create_containerreference_from_dictionary({'type': 'analysis', 'id': analysis['_id']}) + destination = containerutil.create_containerreference_from_dictionary( + {'type': 'analysis', 'id': str(analysis['_id'])}) - job = Job(gear_id, inputs, destination=destination, tags=tags, config_=job.get('config'), origin=origin) + job = Job(job['gear_id'], inputs, + destination=destination, tags=tags, config_=job.get('config'), origin=origin) job_id = job.insert() if not job_id: - raise APIStorageException(500, 'Job not created for analysis {} of container {} {}'.format(analysis['_id'], cont_name, cid)) + # NOTE #775 remove unusable analysis - until jobs have a 'hold' state + self.delete_el(analysis['_id']) + raise APIStorageException(500, 'Job not created for analysis of container {} {}'.format(cont_name, cid)) - result = self._update_el(cid, {'_id': analysis['_id']}, {'job': job_id}, None) - return { 'analysis': analysis, 'job_id':job_id, 'job': job} + result = self.update_el(analysis['_id'], {'job': job_id}, None) + return {'analysis': analysis, 'job_id': job_id, 'job': job} @staticmethod diff --git a/api/handlers/refererhandler.py b/api/handlers/refererhandler.py index eeff1063..d1675f8f 100644 --- a/api/handlers/refererhandler.py +++ b/api/handlers/refererhandler.py @@ -50,7 +50,8 @@ class RefererHandler(base.RequestHandler): elif self.public_request: return containerauth.public_request(self, container=parent_container) else: - return self.permchecker(self, parent_container=parent_container) + # NOTE The handler (self) is passed implicitly + return self.permchecker(parent_container=parent_container) class AnalysesHandler(RefererHandler): @@ -69,26 +70,26 @@ class AnalysesHandler(RefererHandler): analyses are only allowed at the session level. """ parent = self.storage.get_parent(cont_name, cid) - permchecker = self._get_permchecker(parent) + permchecker = self.get_permchecker(parent) permchecker(noop)('POST') if self.is_true('job'): - if cont_name == 'sessions': - payload = self.request.json_body - self.input_validator(payload.get('analysis', {}), 'POST') - analysis = payload.get('analysis') - job = payload.get('job') - if job is None or analysis is None: - self.abort(400, 'JSON body must contain map for "analysis" and "job"') - result = self.storage.create_job_and_analysis(cont_name, cid, analysis, job, self.origin) - return {'_id': result['analysis']['_id']} - else: + if cont_name != 'sessions': self.abort(400, 'Analysis created via a job must be at the session level') - payload = upload.process_upload(self.request, upload.Strategy.analysis, origin=self.origin) - analysis = self.storage.default_analysis(self.origin) - analysis.update(payload) - result = self.storage.exec_op('POST', payload=analysis) + payload = self.request.json_body + analysis = payload.get('analysis') + job = payload.get('job') + if not analysis or not job: + self.abort(400, 'JSON body must contain map for "analysis" and "job"') + self.input_validator(analysis, 'POST') + result = self.storage.create_job_and_analysis(cont_name, cid, analysis, job, self.origin) + return {'_id': result['analysis']['_id']} + + analysis = upload.process_upload(self.request, upload.Strategy.analysis, origin=self.origin) + self.storage.fill_values(analysis, cont_name, cid, self.origin) + self.input_validator(analysis, 'POST') + result = self.storage.create_el(analysis) if result.acknowledged: return {'_id': result.inserted_id} diff --git a/bin/database.py b/bin/database.py index c912fcc0..30e7005b 100755 --- a/bin/database.py +++ b/bin/database.py @@ -20,7 +20,7 @@ from api.jobs.jobs import Job from api.jobs import gears from api.types import Origin -CURRENT_DATABASE_VERSION = 31 # An int that is bumped when a new schema change is made +CURRENT_DATABASE_VERSION = 32 # An int that is bumped when a new schema change is made def get_db_version(): -- GitLab