diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 2bc6a2431..0bbebbd85 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -8,6 +8,10 @@ Changes ------- Unreleased ========== +2019-12-20: v0.8.1 +^^^^^^^^^^^^^^^^^^ +* :sparkles: Data access for reviewers + 2019-12-18: v0.7.5 ^^^^^^^^^^^^^^^^^^ * :racehorse: Check if user requesting user list is a coordinator just once diff --git a/girderformindlogger/api/v1/applet.py b/girderformindlogger/api/v1/applet.py index 203f178c3..98135cdc2 100644 --- a/girderformindlogger/api/v1/applet.py +++ b/girderformindlogger/api/v1/applet.py @@ -23,7 +23,7 @@ import uuid import requests from ..describe import Description, autoDescribeRoute -from ..rest import Resource +from ..rest import Resource, rawResponse from bson.objectid import ObjectId from girderformindlogger.constants import AccessType, SortDir, TokenScope, \ DEFINED_INFORMANTS, REPROLIB_CANONICAL, SPECIAL_SUBJECTS, USER_ROLES @@ -50,6 +50,7 @@ def __init__(self): self.resourceName = 'applet' self._model = AppletModel() self.route('GET', (':id',), self.getApplet) + self.route('GET', (':id', 'data'), self.getAppletData) self.route('GET', (':id', 'groups'), self.getAppletGroups) self.route('POST', (), self.createApplet) self.route('PUT', (':id', 'informant'), self.updateInformant) @@ -187,6 +188,44 @@ def createApplet(self, protocolUrl=None, name=None, informant=None): "an email when your applet is ready." }) + @access.user(scope=TokenScope.DATA_WRITE) + @autoDescribeRoute( + Description('Get all data you are authorized to see for an applet.') + .param( + 'id', + 'ID of the applet for which to fetch data', + required=True + ) + .param( + 'format', + 'JSON or CSV', + required=False + ) + .errorResponse('Write access was denied for this applet.', 403) + ) + def getAppletData(self, id, format='json'): + import pandas as pd + from datetime import datetime + from ..rest import setContentDisposition, setRawResponse, setResponseHeader + + format = ('json' if format is None else format).lower() + thisUser = self.getCurrentUser() + data = AppletModel().getResponseData(id, thisUser) + + setContentDisposition("{}-{}.{}".format( + str(id), + datetime.now().isoformat(), + format + )) + if format=='csv': + setRawResponse() + setResponseHeader('Content-Type', 'text/{}'.format(format)) + csv = pd.DataFrame(data).to_csv(index=False) + return(csv) + setResponseHeader('Content-Type', 'application/{}'.format(format)) + return(data) + + @access.user(scope=TokenScope.DATA_WRITE) @autoDescribeRoute( Description('(managers only) Update the informant of an applet.') @@ -356,7 +395,7 @@ def getAppletRoles(self, folder): ) .param( 'role', - 'Role to invite this user to. One of ' + str(USER_ROLE_KEYS), + 'Role to invite this user to. One of ' + str(set(USER_ROLE_KEYS)), default='user', required=False, strip=True diff --git a/girderformindlogger/models/ID_code.py b/girderformindlogger/models/ID_code.py index 198a112ea..f1b6f8f57 100644 --- a/girderformindlogger/models/ID_code.py +++ b/girderformindlogger/models/ID_code.py @@ -104,12 +104,20 @@ def load(self, id, level=AccessType.ADMIN, user=None, objectId=True, return doc def findIdCodes(self, profileId): - return([ + from .profile import Profile + + idCodes = [ i['code'] for i in list(self.find({'profileId': {'$in': [ str(profileId), ObjectId(profileId) ]}})) if isinstance(i, dict) and 'code' in i - ]) + ] + + if not len(idCodes): + self.createIdCode(Profile().load(profileId, force=True)) + return(self.findIdCodes(profileId)) + + return(idCodes) def removeCode(self, profileId, code): from .profile import Profile @@ -165,7 +173,6 @@ def createIdCode(self, profile, idCode=None): raise e print(sys.exc_info()) - def findProfile(self, idCode): """ Find a list of profiles for a given ID code. diff --git a/girderformindlogger/models/applet.py b/girderformindlogger/models/applet.py index d1f85c71b..8da03221c 100644 --- a/girderformindlogger/models/applet.py +++ b/girderformindlogger/models/applet.py @@ -219,7 +219,40 @@ def getResponseData(self, appletId, reviewer, filter={}): :type filter: dict :reutrns: TBD """ - pass + from .ID_code import IDCode + from .profile import Profile + from .response_folder import ResponseItem + from .user import User + from pymongo import DESCENDING + + if not self._hasRole(appletId, reviewer, 'reviewer'): + raise AccessException("You are not a reviewer for this applet.") + query = { + "baseParentType": "user", + "meta.applet.@id": ObjectId(appletId) + } + responses = list(ResponseItem().find( + query=query, + user=reviewer, + sort=[("created", DESCENDING)] + )) + respondents = { + str(response['baseParentId']): IDCode().findIdCodes( + Profile().createProfile( + appletId, + User().load(response['baseParentId'], force=True), + 'user' + )['_id'] + ) for response in responses if 'baseParentId' in response + } + return([ + { + "respondent": code, + **response.get('meta', {}) + } for response in responses for code in respondents[ + str(response['baseParentId']) + ] + ]) def updateRelationship(self, applet, relationship): """ diff --git a/girderformindlogger/utility/jsonld_expander.py b/girderformindlogger/utility/jsonld_expander.py index 77b1a15e3..ed336be73 100644 --- a/girderformindlogger/utility/jsonld_expander.py +++ b/girderformindlogger/utility/jsonld_expander.py @@ -316,6 +316,7 @@ def delanguageTag(obj): def expandOneLevel(obj): if obj is None: + # We only want to catch `None`s here, not other falsy objects return(obj) try: newObj = jsonld.expand(obj)