diff --git a/__init__.py b/__init__.py index 28865f4b02b3b614d830ddfbb2e1132c9a4b06f0..e1e8b10697488c379dc643d1be9e177b23337379 100644 --- a/__init__.py +++ b/__init__.py @@ -1,3 +1,5 @@ # @author: Gunnar Schaefer import nimsapi + +app = nimsapi.app diff --git a/epochs.py b/epochs.py index 945e06fc794b973a92ec24f83e17a16e7dc01aaa..34629c898fd07d2b40238a8ac9a998953dd7241e 100644 --- a/epochs.py +++ b/epochs.py @@ -9,9 +9,43 @@ import nimsapiutil class Epochs(nimsapiutil.NIMSRequestHandler): + json_schema = { + '$schema': 'http://json-schema.org/draft-04/schema#', + 'title': 'Epoch List', + 'type': 'array', + 'items': { + 'title': 'Epoch', + 'type': 'object', + 'properties': { + '_id': { + 'title': 'Database ID', + }, + 'timestamp': { + 'title': 'Timestamp', + }, + 'datatype': { + 'title': 'Datatype', + 'type': 'string', + }, + 'series': { + 'title': 'Series', + 'type': 'integer', + }, + 'acquisition': { + 'title': 'Acquisition', + 'type': 'integer', + }, + 'description': { + 'title': 'Description', + 'type': 'string', + }, + } + } + } + def count(self, iid): """Return the number of Epochs.""" - self.response.write('epochs count\n') + self.response.write(json.dumps(self.app.db.epochs.count())) def post(self, iid): """Create a new Epoch.""" @@ -39,6 +73,142 @@ class Epochs(nimsapiutil.NIMSRequestHandler): class Epoch(nimsapiutil.NIMSRequestHandler): + json_schema = { + '$schema': 'http://json-schema.org/draft-04/schema#', + 'title': 'Epoch', + 'type': 'object', + 'properties': { + '_id': { + 'title': 'Database ID', + }, + 'session': { + 'title': 'Session ID', + }, + 'timestamp': { + 'title': 'Timestamp', + }, + 'session_uid': { + 'title': 'DICOM UID', + 'type': 'string', + }, + 'datatype': { + 'title': 'Datatype', + 'type': 'string', + }, + 'series': { + 'title': 'Series', + 'type': 'integer', + }, + 'acquisition': { + 'title': 'Acquisition', + 'type': 'integer', + }, + 'description': { + 'title': 'Description', + 'type': 'string', + 'maxLength': 64, + }, + 'protocol': { + 'title': 'Protocol', + 'type': 'string', + 'maxLength': 64, + }, + 'rx_coil': { + 'title': 'Coil', + 'type': 'string', + 'maxLength': 64, + }, + 'device': { + 'title': 'Device', + 'type': 'string', + 'maxLength': 64, + }, + 'size': { + 'title': 'Size', + 'type': 'array', + 'items': { + 'type': 'integer', + } + }, + 'acquisition_matrix': { + 'title': 'Acquisition Matrix', + 'type': 'array', + 'items': { + 'type': 'number', + } + }, + 'fov': { + 'title': 'Field of View', + 'type': 'array', + 'items': { + 'type': 'number', + } + }, + 'mm_per_voxel': { + 'title': 'mm per Voxel', + 'type': 'array', + 'items': { + 'type': 'number', + } + }, + 'flip_angle': { + 'title': 'Flip Angle', + 'type': 'integer', + }, + 'num_averages': { + 'title': 'Averages', + 'type': 'integer', + }, + 'num_bands': { + 'title': 'Bands', + 'type': 'integer', + }, + 'num_echos': { + 'title': 'Echos', + 'type': 'integer', + }, + 'num_slices': { + 'title': 'Slices', + 'type': 'integer', + }, + 'num_timepoints': { + 'title': 'Time Points', + 'type': 'integer', + }, + 'pixel_bandwidth': { + 'title': 'Pixel Bandwidth', + 'type': 'number', + }, + 'prescribed_duration': { + 'title': 'Prescribed Duration', + 'type': 'number', + }, + 'duration': { + 'title': 'Duration', + 'type': 'number', + }, + 'slice_encode_undersample': { + 'title': 'Slice Encode Undersample', + 'type': 'integer', + }, + 'te': { + 'title': 'Te', + 'type': 'number', + }, + 'tr': { + 'title': 'Tr', + 'type': 'number', + }, + 'files': { + 'title': 'Files', + 'type': 'array', + 'items': nimsapiutil.NIMSRequestHandler.file_schema, + 'uniqueItems': True, + }, + }, + 'required': ['_id'], + } + def get(self, iid, eid): """Return one Epoch, conditionally with details.""" epoch = self.app.db.epochs.find_one({'_id': bson.objectid.ObjectId(eid)}) diff --git a/experiments.py b/experiments.py index 8c0acfd767577866b7a4fd4a06bf678738a198dd..e617c338c2ad24416204a91c4dcdbad53b9db279 100644 --- a/experiments.py +++ b/experiments.py @@ -9,9 +9,35 @@ import nimsapiutil class Experiments(nimsapiutil.NIMSRequestHandler): + json_schema = { + '$schema': 'http://json-schema.org/draft-04/schema#', + 'title': 'Experiment List', + 'type': 'array', + 'items': { + 'title': 'Experiment', + 'type': 'object', + 'properties': { + '_id': { + 'title': 'Database ID', + }, + 'firstname': { + 'title': 'First Name', + 'type': 'string', + }, + 'lastname': { + 'title': 'Last Name', + 'type': 'string', + }, + 'email_hash': { + 'type': 'string', + }, + } + } + } + def count(self, iid): """Return the number of Experiments.""" - self.response.write('%d experiments\n' % self.app.db.experiments.count()) + self.response.write(json.dumps(self.app.db.experiments.count())) def post(self, iid): """Create a new Experiment.""" @@ -38,6 +64,41 @@ class Experiments(nimsapiutil.NIMSRequestHandler): class Experiment(nimsapiutil.NIMSRequestHandler): + json_schema = { + '$schema': 'http://json-schema.org/draft-04/schema#', + 'title': 'Experiment', + 'type': 'object', + 'properties': { + '_id': { + 'title': 'Database ID', + }, + 'timestamp': { + 'title': 'Timestamp', + }, + 'group': { + 'title': 'Group', + 'type': 'string', + }, + 'name': { + 'title': 'Name', + 'type': 'string', + 'maxLength': 32, + }, + 'permissions': { + 'title': 'Permissions', + 'type': 'object', + 'minProperties': 1, + }, + 'files': { + 'title': 'Files', + 'type': 'array', + 'items': nimsapiutil.NIMSRequestHandler.file_schema, + 'uniqueItems': True, + }, + }, + 'required': ['_id', 'group', 'name'], + } + def get(self, iid, xid): """Return one Experiment, conditionally with details.""" experiment = self.app.db.experiments.find_one({'_id': bson.objectid.ObjectId(xid)}) diff --git a/nimsapi.py b/nimsapi.py index 717f5f081a8cdc637c66de78b83dfdfebf0a9139..030afccd00346ddfeb7cb28512ef3888155a05a3 100755 --- a/nimsapi.py +++ b/nimsapi.py @@ -34,8 +34,44 @@ class NIMSAPI(nimsapiutil.NIMSRequestHandler): def get(self): """Return API documentation""" + resources = [ + ('/experiments', 'list of experiments'), + ('/collections', 'list of collections'), + ('/experiments/<xid>/sessions', 'list of sessions for one experiment'), + ('/collections/<xid>/sessions', 'list of sessions for one collection'), + ('/sessions/<sid>/epochs', 'list of epochs for one session'), + ('', ''), + ('/experiments/<xid>', 'details for one experiment'), + ('/collections/<xid>', 'details for one collection'), + ('/sessions/<sid>', 'details for one session'), + ('/epochs/<eid>', 'details for one epoch'), + ('', ''), + ('/users', 'list of users'), + ('/groups', 'list of groups'), + ('/users/<uid>', 'details for one user'), + ('/groups/<gid>', 'details for one group'), + ] self.response.headers['Content-Type'] = 'text/html; charset=utf-8' - self.response.write('nimsapi - {0}\n'.format(self.app.config['site_id'])) + self.response.write('<html>\n') + self.response.write('<head>\n') + self.response.write('<title>NIMSAPI</title>\n') + self.response.write('<style type="text/css">\n') + self.response.write('.tftable {font-size:12px;color:#333333;width:100%;border-width: 1px;border-color: #a9a9a9;border-collapse: collapse;}\n') + self.response.write('.tftable th {font-size:12px;background-color:#b8b8b8;border-width: 1px;padding: 8px;border-style: solid;border-color: #a9a9a9;text-align:left;}\n') + self.response.write('.tftable tr {background-color:#cdcdcd;}\n') + self.response.write('.tftable td {font-size:12px;border-width: 1px;padding: 8px;border-style: solid;border-color: #a9a9a9;}\n') + self.response.write('</style>\n') + self.response.write('</head>\n') + self.response.write('<body>\n') + self.response.write('<p>nimsapi - ' + self.app.config['site_id'] + '</p>\n') + self.response.write('<table class="tftable" border="1">\n') + self.response.write('<tr><th>Resource</th><th>Description</th></tr>\n') + for r, d in resources: + r = r.replace('<', '<').replace('>', '>') + self.response.write('<tr><td>%s</td><td>%s</td></tr>\n' % (r, d)) + self.response.write('</table>\n') + self.response.write('</body>\n') + self.response.write('</html>\n') def upload(self): # TODO add security: either authenticated user or machine-to-machine CRAM @@ -46,7 +82,7 @@ class NIMSAPI(nimsapiutil.NIMSRequestHandler): with nimsutil.TempDir(prefix='.tmp', dir=stage_path) as tempdir_path: hash_ = hashlib.md5() upload_filepath = os.path.join(tempdir_path, filename) - log.info(os.path.basename(upload_filepath)) + log.info('receiving upload ' + os.path.basename(upload_filepath)) with open(upload_filepath, 'wb') as upload_file: for chunk in iter(lambda: self.request.body_file.read(2**20), ''): hash_.update(chunk) @@ -55,7 +91,7 @@ class NIMSAPI(nimsapiutil.NIMSRequestHandler): self.abort(400, 'Content-MD5 mismatch.') if not tarfile.is_tarfile(upload_filepath) and not zipfile.is_zipfile(upload_filepath): self.abort(415) - os.rename(upload_filepath, os.path.join(stage_path, str(uuid.uuid1()) + '_' + fid)) # add UUID to prevent clobbering files + os.rename(upload_filepath, os.path.join(stage_path, str(uuid.uuid1()) + '_' + filename)) # add UUID to prevent clobbering files def download(self): paths = [] @@ -72,6 +108,42 @@ class NIMSAPI(nimsapiutil.NIMSRequestHandler): class Users(nimsapiutil.NIMSRequestHandler): + json_schema = { + '$schema': 'http://json-schema.org/draft-04/schema#', + 'title': 'User List', + 'type': 'array', + 'items': { + 'title': 'User', + 'type': 'object', + 'properties': { + '_id': { + 'title': 'Database ID', + 'type': 'string', + }, + 'firstname': { + 'title': 'First Name', + 'type': 'string', + 'default': '', + }, + 'lastname': { + 'title': 'Last Name', + 'type': 'string', + 'default': '', + }, + 'email': { + 'title': 'Email', + 'type': 'string', + 'format': 'email', + 'default': '', + }, + 'email_hash': { + 'type': 'string', + 'default': '', + }, + } + } + } + def count(self, iid): """Return the number of Users.""" self.response.write('%d users\n' % self.app.db.users.count()) @@ -93,6 +165,43 @@ class Users(nimsapiutil.NIMSRequestHandler): class User(nimsapiutil.NIMSRequestHandler): + json_schema = { + '$schema': 'http://json-schema.org/draft-04/schema#', + 'title': 'User', + 'type': 'object', + 'properties': { + '_id': { + 'title': 'Database ID', + 'type': 'string', + }, + 'firstname': { + 'title': 'First Name', + 'type': 'string', + 'default': '', + }, + 'lastname': { + 'title': 'Last Name', + 'type': 'string', + 'default': '', + }, + 'email': { + 'title': 'Email', + 'type': 'string', + 'format': 'email', + 'default': '', + }, + 'email_hash': { + 'type': 'string', + 'default': '', + }, + 'superuser': { + 'title': 'Superuser', + 'type': 'boolean', + }, + }, + 'required': ['_id'], + } + def get(self, iid, uid): """Return User details.""" user = self.app.db.users.find_one({'_id': uid}) @@ -127,6 +236,22 @@ class User(nimsapiutil.NIMSRequestHandler): class Groups(nimsapiutil.NIMSRequestHandler): + json_schema = { + '$schema': 'http://json-schema.org/draft-04/schema#', + 'title': 'Group List', + 'type': 'array', + 'items': { + 'title': 'Group', + 'type': 'object', + 'properties': { + '_id': { + 'title': 'Database ID', + 'type': 'string', + }, + } + } + } + def count(self, iid): """Return the number of Groups.""" self.response.write('%d groups\n' % self.app.db.groups.count()) @@ -148,6 +273,46 @@ class Groups(nimsapiutil.NIMSRequestHandler): class Group(nimsapiutil.NIMSRequestHandler): + json_schema = { + '$schema': 'http://json-schema.org/draft-04/schema#', + 'title': 'Group', + 'type': 'object', + 'properties': { + '_id': { + 'title': 'Database ID', + 'type': 'string', + }, + 'pis': { + 'title': 'PIs', + 'type': 'array', + 'default': [], + 'items': { + 'type': 'string', + }, + 'uniqueItems': True, + }, + 'admins': { + 'title': 'Admins', + 'type': 'array', + 'default': [], + 'items': { + 'type': 'string', + }, + 'uniqueItems': True, + }, + 'memebers': { + 'title': 'Members', + 'type': 'array', + 'default': [], + 'items': { + 'type': 'string', + }, + 'uniqueItems': True, + }, + }, + 'required': ['_id'], + } + def get(self, iid, gid): """Return Group details.""" group = self.app.db.groups.find_one({'_id': gid}) @@ -207,8 +372,8 @@ class ArgumentParser(argparse.ArgumentParser): super(ArgumentParser, self).__init__() self.add_argument('uri', help='NIMS DB URI') self.add_argument('stage_path', help='path to staging area') - self.add_argument('--pubkey', default='internims/NIMSpubkey.pub', help='path to ssl pubkey') - self.add_argument('-u', '--uid', default='local', help='site uid') + self.add_argument('-k', '--pubkey', help='path to public SSL key') + self.add_argument('-u', '--uid', default='local', help='site UID') self.add_argument('-f', '--logfile', help='path to log file') self.add_argument('-l', '--loglevel', default='info', help='path to log file') self.add_argument('-q', '--quiet', action='store_true', default=False, help='disable console logging') @@ -218,36 +383,53 @@ routes = [ webapp2_extras.routes.PathPrefixRoute(r'/nimsapi', [ webapp2.Route(r'/download', NIMSAPI, handler_method='download', methods=['GET']), webapp2.Route(r'/dump', NIMSAPI, handler_method='dump', methods=['GET']), - webapp2.Route(r'/upload/<fid>', NIMSAPI, handler_method='upload', methods=['PUT']), + webapp2.Route(r'/upload', NIMSAPI, handler_method='upload', methods=['PUT']), webapp2.Route(r'/remotes', Remotes), - ]), - # webapp2_extras.routes.PathPrefixRoute has bug, variable MUST have regex + ]), webapp2_extras.routes.PathPrefixRoute(r'/nimsapi/<iid:[^/]+>', [ webapp2.Route(r'/users', Users), webapp2.Route(r'/users/count', Users, handler_method='count', methods=['GET']), + webapp2.Route(r'/users/listschema', Users, handler_method='schema', methods=['GET']), + webapp2.Route(r'/users/schema', User, handler_method='schema', methods=['GET']), webapp2.Route(r'/users/<uid>', User), webapp2.Route(r'/groups', Groups), webapp2.Route(r'/groups/count', Groups, handler_method='count', methods=['GET']), + webapp2.Route(r'/groups/listschema', Groups, handler_method='schema', methods=['GET']), + webapp2.Route(r'/groups/schema', Group, handler_method='schema', methods=['GET']), webapp2.Route(r'/groups/<gid>', Group), webapp2.Route(r'/experiments', experiments.Experiments), webapp2.Route(r'/experiments/count', experiments.Experiments, handler_method='count', methods=['GET']), + webapp2.Route(r'/experiments/listschema', experiments.Experiments, handler_method='schema', methods=['GET']), + webapp2.Route(r'/experiments/schema', experiments.Experiment, handler_method='schema', methods=['GET']), webapp2.Route(r'/experiments/<xid:[0-9a-f]{24}>', experiments.Experiment), webapp2.Route(r'/experiments/<xid:[0-9a-f]{24}>/sessions', sessions.Sessions), webapp2.Route(r'/sessions/count', sessions.Sessions, handler_method='count', methods=['GET']), + webapp2.Route(r'/sessions/listschema', sessions.Sessions, handler_method='schema', methods=['GET']), + webapp2.Route(r'/sessions/schema', sessions.Session, handler_method='schema', methods=['GET']), webapp2.Route(r'/sessions/<sid:[0-9a-f]{24}>', sessions.Session), webapp2.Route(r'/sessions/<sid:[0-9a-f]{24}>/move', sessions.Session, handler_method='move'), webapp2.Route(r'/sessions/<sid:[0-9a-f]{24}>/epochs', epochs.Epochs), webapp2.Route(r'/epochs/count', epochs.Epochs, handler_method='count', methods=['GET']), + webapp2.Route(r'/epochs/listschema', epochs.Epochs, handler_method='schema', methods=['GET']), + webapp2.Route(r'/epochs/schema', epochs.Epoch, handler_method='schema', methods=['GET']), webapp2.Route(r'/epochs/<eid:[0-9a-f]{24}>', epochs.Epoch), ]), ] +app = webapp2.WSGIApplication(routes, debug=True) + if __name__ == '__main__': args = ArgumentParser().parse_args() nimsutil.configure_log(args.logfile, not args.quiet, args.loglevel) from paste import httpserver - app = webapp2.WSGIApplication(routes, debug=True, config=dict(stage_path=args.stage_path, site_id=args.uid, pubkey=args.pubkey)) + app.config = dict(stage_path=args.stage_path, site_id=args.uid, pubkey=args.pubkey) app.db = (pymongo.MongoReplicaSetClient(args.uri) if 'replicaSet' in args.uri else pymongo.MongoClient(args.uri)).get_default_database() httpserver.serve(app, host=httpserver.socket.gethostname(), port='8080') + +# import nimsapi, webapp2, pymongo, bson.json_util +# nimsapi.app.db = pymongo.MongoClient('mongodb://nims:cnimr750@slice.stanford.edu/nims').get_default_database() +# response = webapp2.Request.blank('/nimsapi/local/users').get_response(nimsapi.app) +# response.status +# response.body diff --git a/nimsapi.wsgi b/nimsapi.wsgi index 7a83f7e4bf06cdde4962f753038394d9519ea68b..da8167645281ee17df0ec622978a3c87142a90da 100644 --- a/nimsapi.wsgi +++ b/nimsapi.wsgi @@ -17,11 +17,12 @@ import nimsapi import nimsutil logfile = '/var/local/log/nimsapi.log' -db_uri = 'mongodb://nimsfs.stanford.edu,nimsbk.stanford.edu/nims?replicaSet=cni' +db_uri = 'mongodb://nims:cnimr750@cnifs.stanford.edu,cnibk.stanford.edu/nims?replicaSet=cni' stage_path = '/scratch/upload' nimsutil.configure_log(logfile, False) db_client = pymongo.MongoReplicaSetClient(db_uri) if 'replicaSet' in db_uri else pymongo.MongoClient(db_uri) -application = webapp2.WSGIApplication(nimsapi.nimsapi.routes, debug=False, config=dict(stage_path=stage_path)) +application = nimsapi.app +application.config = dict(stage_path=stage_path)) application.db = db_client.get_default_database() diff --git a/nimsapiutil.py b/nimsapiutil.py index 08d817be4766edbd25a068f25b2cbdcc8b362ace..231134761e95fa325ed45785d96b6a7b45827a0e 100644 --- a/nimsapiutil.py +++ b/nimsapiutil.py @@ -1,25 +1,62 @@ -# @author: Gunnar Schaefer -# @author: Kevin S. Hahn +# @author: Gunnar Schaefer, Kevin S. Hahn -import base64 -import datetime import json +import base64 +import socket # socket.gethostname() import logging -import requests import webapp2 -import socket # socket.gethostname() +import datetime +import requests +import bson.json_util from Crypto.Hash import HMAC from Crypto.Random import random -logging.basicConfig(level=logging.INFO) + +log = logging.getLogger('nimsapi') class NIMSRequestHandler(webapp2.RequestHandler): """fetches pubkey from own self.db.remotes. needs to be aware of OWN site uid""" + json_schema = None + + file_schema = { + 'title': 'File', + 'type': 'object', + 'properties': { + 'datakind': { + 'title': 'Data Kind', + 'type': 'string', + }, + 'datatype': { + 'title': 'Data Type', + 'type': 'string', + }, + 'filetype': { + 'title': 'File Type', + 'type': 'string', + }, + 'filename': { + 'title': 'File Name', + 'type': 'string', + }, + 'ext': { + 'title': 'File Name Extension', + 'type': 'string', + }, + 'md5': { + 'title': 'MD5', + 'type': 'string', + }, + 'size': { + 'title': 'Size', + 'type': 'integer', + }, + } + } + def __init__(self, request=None, response=None): - webapp2.RequestHandler.__init__(self, request, response) - # call initialize, isntead of __init__ + self.initialize(request, response) self.request.remote_user = self.request.get('user', None) # FIXME: auth system should set REMOTE_USER self.userid = self.request.remote_user or '@public' self.user = self.app.db.users.find_one({'_id': self.userid}) @@ -29,18 +66,17 @@ class NIMSRequestHandler(webapp2.RequestHandler): self.target_id = request.route_kwargs['iid'] # for what site is the request meant except KeyError: self.target_id = 'local' # change to site_id? - self.useragent = self.request.headers['user-agent'] # browser or internimsP2P request self.site_id = self.app.config['site_id'] # what is THIS site - self.pubkey = open(self.app.config['pubkey']).read() + self.pubkey = open(self.app.config['pubkey']).read() if self.app.config['pubkey'] is not None else None # requests coming from another NIMS instance are dealt with differently - if self.useragent.startswith('NIMS Instance'): - logging.info("request from '{0}', interNIMS p2p initiated".format(self.useragent)) + if self.request.user_agent.startswith('NIMS Instance'): + log.info("request from '{0}', interNIMS p2p initiated".format(self.request.user_agent)) try: authinfo = self.request.headers['authorization'] challenge_id, digest = base64.b64decode(authinfo).split() user, remote_site = challenge_id.split(':') - # logging.info('{0} {1} {2}'.format(user, remote_site, digest)) + # log.info('{0} {1} {2}'.format(user, remote_site, digest)) projection = {'_id': False, 'pubkey': True} remote_pubkey = self.app.db.remotes.find_one({'_id': remote_site}, projection)['pubkey'] # get the challenge from db.challenges @@ -52,7 +88,7 @@ class NIMSRequestHandler(webapp2.RequestHandler): h = HMAC.new(remote_pubkey, challenge) self.expected = base64.b64encode('%s %s' % (challenge_id, h.hexdigest())) if self.expected == authinfo: - logging.info('CRAM successful') + log.info('CRAM successful') else: self.abort(403, 'Not Authorized: cram failed') except KeyError, e: @@ -71,14 +107,14 @@ class NIMSRequestHandler(webapp2.RequestHandler): def dispatch(self): """dispatching and request forwarding""" if self.target_id in ['local', self.site_id]: - logging.info('{0} delegating to local {1}'.format(socket.gethostname(), self.request.url)) + log.info('{0} delegating to local {1}'.format(socket.gethostname(), self.request.url)) super(NIMSRequestHandler, self).dispatch() else: - logging.info('{0} delegating to remote {1}'.format(socket.gethostname(), self.target_id)) + log.info('{0} delegating to remote {1}'.format(socket.gethostname(), self.target_id)) # is target registered? target = self.app.db.remotes.find_one({'_id': self.target_id}, {'_id':False, 'hostname':True}) if not target: - logging.info('remote host {0} not in auth list. DENIED'.format(self.target_id)) + log.info('remote host {0} not in auth list. DENIED'.format(self.target_id)) self.abort(403, 'forbidden: site is not registered with interNIMS') self.cid = self.userid + ':' + self.site_id reqheaders = dict(self.request.headers) @@ -94,12 +130,15 @@ class NIMSRequestHandler(webapp2.RequestHandler): if r.status_code == 401: challenge = base64.b64decode(r.headers['www-authenticate']) - # logging.info('challenge {0} recieved'.format(challenge)) + # log.info('challenge {0} recieved'.format(challenge)) h = HMAC.new(self.pubkey, challenge) response = base64.b64encode('%s %s' % (self.cid, h.hexdigest())) - # logging.info('sending: {0} {1}'.format(self.cid, h.hexdigest())) + # log.info('sending: {0} {1}'.format(self.cid, h.hexdigest())) #adjust the request and try again reqheaders['authorization'] = response r = requests.request(method=self.request.method, url=target_api, params=reqparams, data=self.request.body, headers=reqheaders, cookies=self.request.cookies) self.response.write(r.content) + + def schema(self, *args, **kwargs): + self.response.write(json.dumps(self.json_schema, default=bson.json_util.default)) diff --git a/sessions.py b/sessions.py index 3be07e3efc38f461c872931c080ca6b34a05a7d8..4b69170606bcffedb8f60fcd357bbc247ca5beda 100644 --- a/sessions.py +++ b/sessions.py @@ -9,9 +9,31 @@ import nimsapiutil class Sessions(nimsapiutil.NIMSRequestHandler): + json_schema = { + '$schema': 'http://json-schema.org/draft-04/schema#', + 'title': 'Session List', + 'type': 'array', + 'items': { + 'title': 'Session', + 'type': 'object', + 'properties': { + '_id': { + 'title': 'Database ID', + }, + 'timestamp': { + 'title': 'Timestamp', + }, + 'subject': { + 'title': 'Subject Code', + 'type': 'string', + }, + } + } + } + def count(self, iid): """Return the number of Sessions.""" - self.response.write('sessions count\n') + self.response.write(json.dumps(self.app.db.sessions.count())) def post(self, iid): """Create a new Session""" @@ -36,6 +58,54 @@ class Sessions(nimsapiutil.NIMSRequestHandler): class Session(nimsapiutil.NIMSRequestHandler): + json_schema = { + '$schema': 'http://json-schema.org/draft-04/schema#', + 'title': 'Session', + 'type': 'object', + 'properties': { + '_id': { + 'title': 'Database ID', + }, + 'experiment': { + 'title': 'Experiment ID', + }, + 'timestamp': { + 'title': 'Timestamp', + }, + 'uid': { + 'title': 'DICOM UID', + 'type': 'string', + }, + 'firstname': { + 'title': 'First Name', + 'type': 'string', + }, + 'lastname': { + 'title': 'Last Name', + 'type': 'string', + }, + 'patient_id': { + 'title': 'Patient ID', + 'type': 'string', + }, + 'subject': { + 'title': 'Subject Code', + 'type': 'string', + }, + 'exam': { + 'title': 'Exam Number', + 'type': 'integer', + }, + 'files': { + 'title': 'Files', + 'type': 'array', + 'items': nimsapiutil.NIMSRequestHandler.file_schema, + 'uniqueItems': True, + }, + }, + 'required': ['_id', 'experiment', 'uid', 'patient_id', 'subject'], + } + def get(self, iid, sid): """Return one Session, conditionally with details.""" session = self.app.db.sessions.find_one({'_id': bson.objectid.ObjectId(sid)})