From a0766f20f0878631df912e33f320ecd27fdc2238 Mon Sep 17 00:00:00 2001 From: Laetitia Fesselier Date: Mon, 14 Jun 2021 14:47:33 -0400 Subject: [PATCH 1/7] [API] Visit patch request to start next stage --- modules/api/docs/LorisRESTAPI.md | 2 +- modules/api/docs/LorisRESTAPI_v0.0.4-dev.md | 4 +- .../endpoints/candidate/candidate.class.inc | 18 +- .../endpoints/candidate/visit/visit.class.inc | 108 +++++++----- .../candidate/visit/visit_0_0_4_dev.class.inc | 155 ++++++++++++++++++ 5 files changed, 241 insertions(+), 46 deletions(-) create mode 100644 modules/api/php/endpoints/candidate/visit/visit_0_0_4_dev.class.inc diff --git a/modules/api/docs/LorisRESTAPI.md b/modules/api/docs/LorisRESTAPI.md index f290e3987be..4b57f72c15a 100644 --- a/modules/api/docs/LorisRESTAPI.md +++ b/modules/api/docs/LorisRESTAPI.md @@ -416,7 +416,7 @@ The JSON object is of the form: } ``` -A PUT of the same format but with only the Meta fields will create the VisitLabel +A PUT with only the Meta fields will create the VisitLabel for this candidate, in an unstarted stage if the Visit label provided is valid. PATCH is not supported for Visit Labels. diff --git a/modules/api/docs/LorisRESTAPI_v0.0.4-dev.md b/modules/api/docs/LorisRESTAPI_v0.0.4-dev.md index a03be523543..82225b589c6 100644 --- a/modules/api/docs/LorisRESTAPI_v0.0.4-dev.md +++ b/modules/api/docs/LorisRESTAPI_v0.0.4-dev.md @@ -416,10 +416,10 @@ The JSON object is of the form: } ``` -A PUT of the same format but with only the Meta fields will create the VisitLabel +A PUT request with only the Meta fields will create the VisitLabel for this candidate, in an unstarted stage if the Visit label provided is valid. -PATCH is not supported for Visit Labels. +A PATCH request with the Meta fields and a date NextStageDate will start the next stage, if the stage is 'Not Started' and the CandID and Visit label provided are valid. It will return a 404 Not Found if the visit label does not exist for this candidate (as well as anything under the /candidates/$CandID/$VisitLabel hierarchy) diff --git a/modules/api/php/endpoints/candidate/candidate.class.inc b/modules/api/php/endpoints/candidate/candidate.class.inc index 16fbd5d44d3..863b2de531b 100644 --- a/modules/api/php/endpoints/candidate/candidate.class.inc +++ b/modules/api/php/endpoints/candidate/candidate.class.inc @@ -95,7 +95,7 @@ class Candidate extends Endpoint implements \LORIS\Middleware\ETagCalculator } // Delegate to sub-endpoints - $visit_label = array_shift($pathparts); + $visit_label = urldecode(array_shift($pathparts)); $newrequest = $request ->withAttribute('pathparts', $pathparts); @@ -112,10 +112,18 @@ class Candidate extends Endpoint implements \LORIS\Middleware\ETagCalculator $visit = null; } - $handler = new Visit\Visit( - $this->_candidate, - $visit - ); + $version = $request->getAttribute("LORIS-API-Version"); + if ($version === 'v0.0.3') { + $handler = new Visit\Visit( + $this->_candidate, + $visit + ); + } else { + $handler = new Visit\Visit_0_0_4_Dev( + $this->_candidate, + $visit + ); + } return $handler->process( $newrequest, diff --git a/modules/api/php/endpoints/candidate/visit/visit.class.inc b/modules/api/php/endpoints/candidate/visit/visit.class.inc index 526a18279e9..84deef74bfb 100644 --- a/modules/api/php/endpoints/candidate/visit/visit.class.inc +++ b/modules/api/php/endpoints/candidate/visit/visit.class.inc @@ -33,14 +33,28 @@ class Visit extends Endpoint implements \LORIS\Middleware\ETagCalculator * * @var \Candidate */ - private $_candidate; + protected $_candidate; /** * The requested Visit * * @var ?\Timepoint */ - private $_visit; + protected $_visit; + + /** + * The visit centerID + * + * @var ?\CenterID + */ + protected $_centerID; + + /** + * The visit Project + * + * @var ?\Project + */ + protected $_project; /** * Contructor @@ -76,8 +90,7 @@ class Visit extends Endpoint implements \LORIS\Middleware\ETagCalculator protected function supportedVersions() : array { return [ - 'v0.0.3', - 'v0.0.4-dev', + 'v0.0.3' ]; } @@ -95,23 +108,19 @@ class Visit extends Endpoint implements \LORIS\Middleware\ETagCalculator if (!$this->_visit->isAccessibleBy($user)) { return new \LORIS\Http\Response\JSON\Forbidden('Permission denied'); } - } $pathparts = $request->getAttribute('pathparts'); if (count($pathparts) === 0) { - switch ($request->getMethod()) { - case 'GET': - return $this->_handleGET(); + $method = $request->getMethod(); - case 'PUT': - return $this->_handlePUT($request); - - case 'OPTIONS': + if (in_array($method, $this->allowedMethods())) { + $handle = 'handle'.$method; + return $this->$handle($request); + } else if ($method === 'OPTIONS') { return (new \LORIS\Http\Response()) ->withHeader('Allow', $this->allowedMethods()); - - default: + } else { return new \LORIS\Http\Response\JSON\MethodNotAllowed( $this->allowedMethods() ); @@ -159,7 +168,7 @@ class Visit extends Endpoint implements \LORIS\Middleware\ETagCalculator * * @return ResponseInterface The outgoing PSR7 response */ - private function _handleGET(): ResponseInterface + protected function handleGET(): ResponseInterface { if ($this->_visit === null) { return new \LORIS\Http\Response\JSON\NotFound('Visit not found'); @@ -172,7 +181,7 @@ class Visit extends Endpoint implements \LORIS\Middleware\ETagCalculator } /** - * Handles a PUT request that creates or replace a candidate visit + * Validate a PUT request * * TODO: There is no way to validate the the visit_label in the url * fits the json data of the request because it is removed from the @@ -182,13 +191,13 @@ class Visit extends Endpoint implements \LORIS\Middleware\ETagCalculator * * @param ServerRequestInterface $request The incoming PSR7 request * - * @return ResponseInterface The outgoing PSR7 response + * @return ?ResponseInterface The outgoing PSR7 response */ - private function _handlePUT(ServerRequestInterface $request): ResponseInterface - { - $user = $request->getAttribute('user'); - $data = json_decode((string) $request->getBody(), true); - $visitinfo = $data ?? []; + protected function validatePUT( + ServerRequestInterface $request + ): ?ResponseInterface { + $user = $request->getAttribute('user'); + $data = json_decode((string) $request->getBody(), true) ?? []; $requiredfields = [ 'CandID', @@ -197,7 +206,7 @@ class Visit extends Endpoint implements \LORIS\Middleware\ETagCalculator 'Battery', 'Project', ]; - $diff = array_diff($requiredfields, array_keys($visitinfo)); + $diff = array_diff($requiredfields, array_keys($data)); if (!empty($diff)) { return new \LORIS\Http\Response\JSON\BadRequest( @@ -206,39 +215,61 @@ class Visit extends Endpoint implements \LORIS\Middleware\ETagCalculator } try { - $project = \NDB_Factory::singleton()->project($visitinfo['Project']); + $this->_project = \NDB_Factory::singleton()->project($data['Project']); } catch (\NotFound $e) { return new \LORIS\Http\Response\JSON\BadRequest( $e->getMessage() ); } - if ($visitinfo['CandID'] !== $this->_candidate->getCandID()) { + if (strval($data['CandID']) !== strval($this->_candidate->getCandID())) { return new \LORIS\Http\Response\JSON\BadRequest( - 'CandID do not match this candidate' + 'The CandID field does not match this candidate' ); } if (!$this->_candidate->isAccessibleBy($user)) { return new \LORIS\Http\Response\JSON\Forbidden( - "You can't create or modify that candidate" + "You can't modify that candidate" ); } - $centerid = array_search($visitinfo['Site'], \Utility::getSiteList()); + $centerID = array_search($data['Site'], \Utility::getSiteList()); - if ($centerid === false - || !in_array(new \CenterID("$centerid"), $user->getCenterIDs()) + if ($centerID === false || !in_array( + new \CenterID(strval($centerID)), + $user->getCenterIDs() + ) ) { return new \LORIS\Http\Response\JSON\Forbidden( "You can't create or modify candidates visit for the site " . - $centerid + $centerID ); } - // \Utility::getSiteList key was a string. Now that the - // validation is done, convert to an object. - $centerid = new \CenterID("$centerid"); + // Now that the validation is done, convert to an object. + $this->_centerID = new \CenterID(strval($centerID)); + + return null; + } + + /** + * Handles a PUT request that creates or replace a candidate visit + * + * @param ServerRequestInterface $request The incoming PSR7 request + * + * @return ResponseInterface The outgoing PSR7 response + */ + protected function handlePUT(ServerRequestInterface $request): ResponseInterface + { + $user = $request->getAttribute('user'); + $data = json_decode((string) $request->getBody(), true); + $visitinfo = $data ?? []; + + $response = $this->validatePUT($request); + if ($response !== null) { + return $response; + } $subprojectid = array_search( $visitinfo['Battery'], @@ -255,6 +286,7 @@ class Visit extends Endpoint implements \LORIS\Middleware\ETagCalculator $timepoint = \NDB_Factory::singleton()->timepoint( new \SessionID(strval($sessionid)) ); + if ($timepoint->getCurrentStage() !== 'Not Started') { return new \LORIS\Http\Response\JSON\Conflict( 'This visit is already started' @@ -266,7 +298,7 @@ class Visit extends Endpoint implements \LORIS\Middleware\ETagCalculator $timepoint->setData( [ - 'CenterID' => $centerid, + 'CenterID' => $this->_centerID, 'Visit_label' => $visitinfo['Visit'], 'SubprojectID' => $subprojectid, 'Active' => 'Y', @@ -275,7 +307,7 @@ class Visit extends Endpoint implements \LORIS\Middleware\ETagCalculator 'UserID' => $username, 'Date_registered' => $today, 'Testdate' => $today, - 'ProjectID' => $project->getId(), + 'ProjectID' => $this->_project->getId(), ] ); @@ -301,8 +333,8 @@ class Visit extends Endpoint implements \LORIS\Middleware\ETagCalculator $this->_candidate, $subprojectid, $visitinfo['Visit'], - \Site::singleton($centerid), - $project + \Site::singleton($this->_centerID), + $this->_project ); $link = '/' . $request->getUri()->getPath(); diff --git a/modules/api/php/endpoints/candidate/visit/visit_0_0_4_dev.class.inc b/modules/api/php/endpoints/candidate/visit/visit_0_0_4_dev.class.inc new file mode 100644 index 00000000000..66fc951f31e --- /dev/null +++ b/modules/api/php/endpoints/candidate/visit/visit_0_0_4_dev.class.inc @@ -0,0 +1,155 @@ + + * @license http://www.gnu.org/licenses/gpl-3.0.txt GPLv3 + * @link https://github.com/aces/Loris + */ +namespace LORIS\api\Endpoints\Candidate\Visit; + +use \Psr\Http\Message\ServerRequestInterface; +use \Psr\Http\Message\ResponseInterface; + +/** + * A class for handling the /candidates/$candidate/$visit_label endpoint. + * + * @category API + * @package Loris + * @author Xavier Lecours Boucher + * @license http://www.gnu.org/licenses/gpl-3.0.txt GPLv3 + * @link https://github.com/aces/Loris + */ +class Visit_0_0_4_Dev extends Visit +{ + /** + * Return which methods are supported by this endpoint. + * + * @return array supported HTTP methods + */ + protected function allowedMethods() : array + { + return [ + 'GET', + 'PATCH', + 'PUT', + ]; + } + + /** + * Versions of the LORIS API which are supported by this + * endpoint. + * + * @return array a list of supported API versions. + */ + protected function supportedVersions() : array + { + return [ + 'v0.0.4-dev' + ]; + } + + /** + * Handles a PATCH request. + * For now, only support a NextStageDate argument to start next stage. + * + * @param ServerRequestInterface $request The incoming PSR7 request + * + * @return ResponseInterface The outgoing PSR7 response + */ + protected function handlePATCH( + ServerRequestInterface $request + ): ResponseInterface { + $data = json_decode((string) $request->getBody(), true); + $nextStageDate = $data['NextStageDate'] ?? null; + + if ($this->_visit === null) { + return new \LORIS\Http\Response\JSON\NotFound('Visit not found'); + } + + $response = $this->validatePUT($request); + if ($response !== null) { + return $response; + } + + if (!isset($data['Visit']) + || $data['Visit'] !== $this->_visit->getVisitLabel() + ) { + return new \LORIS\Http\Response\JSON\BadRequest( + 'The Visit field does not match this visit' + ); + } + + if (!$nextStageDate) { + return new \LORIS\Http\Response\JSON\BadRequest( + 'There is no stage date specified' + ); + } + + $date_format = 'Y-m-d'; + $date = \DateTime::createFromFormat($date_format, $nextStageDate); + + if (!($date && $date->format($date_format) === $nextStageDate)) { + return new \LORIS\Http\Response\JSON\BadRequest( + 'The date specified is invalid. Date must be of the form YYYY-MM-DD' + ); + } + + if ($nextStageDate > date($date_format)) { + return new \LORIS\Http\Response\JSON\BadRequest( + 'The date specified must be not later than today' + ); + } + + if ($this->_visit->getCurrentStage() !== 'Not Started') { + return new \LORIS\Http\Response\JSON\Conflict( + 'This visit is already started' + ); + } + + $newStage = $this->_visit->getNextStage(); + if (!$newStage) { + return new \LORIS\Http\Response\JSON\Conflict( + "Next stage is null" + ); + } + + // start that stage + $this->_visit->startStage($newStage); + + // set the date for the new stage + $this->_visit->setData(["Date_".$newStage => $nextStageDate]); + + // create a new battery object && new battery + $candidate = \Candidate::singleton($this->_visit->getCandID()); + + // First visit ? + $firstVisit = false; + try { + if ($candidate->getFirstVisit() == $this->_visit->getVisitLabel()) { + $firstVisit = true; + } + } catch (\LorisException $e) { + } + + $battery = new \NDB_BVL_Battery; + // select a specific time point (sessionID) for the battery + $battery->selectBattery($this->_visit->getSessionID()); + + // add instruments to the time point + $battery->createBattery( + $request->getAttribute("loris"), + $this->_visit->getSubprojectID(), + $newStage, + $this->_visit->getVisitLabel(), + $this->_visit->getCenterID(), + $firstVisit + ); + + return new \LORIS\Http\Response\JSON\OK(); + } +} From f03fcc7614bd35cac7c122afad6655ca68e54072 Mon Sep 17 00:00:00 2001 From: Laetitia Fesselier Date: Tue, 24 Aug 2021 16:16:13 -0400 Subject: [PATCH 2/7] Addressing QA comments --- .../endpoints/candidate/visit/visit.class.inc | 107 ++-- .../candidate/visit/visit_0_0_4_dev.class.inc | 474 ++++++++++++++++-- 2 files changed, 456 insertions(+), 125 deletions(-) diff --git a/modules/api/php/endpoints/candidate/visit/visit.class.inc b/modules/api/php/endpoints/candidate/visit/visit.class.inc index 84deef74bfb..128a0ca1ba0 100644 --- a/modules/api/php/endpoints/candidate/visit/visit.class.inc +++ b/modules/api/php/endpoints/candidate/visit/visit.class.inc @@ -33,28 +33,14 @@ class Visit extends Endpoint implements \LORIS\Middleware\ETagCalculator * * @var \Candidate */ - protected $_candidate; + private $_candidate; /** * The requested Visit * * @var ?\Timepoint */ - protected $_visit; - - /** - * The visit centerID - * - * @var ?\CenterID - */ - protected $_centerID; - - /** - * The visit Project - * - * @var ?\Project - */ - protected $_project; + private $_visit; /** * Contructor @@ -90,7 +76,7 @@ class Visit extends Endpoint implements \LORIS\Middleware\ETagCalculator protected function supportedVersions() : array { return [ - 'v0.0.3' + 'v0.0.3', ]; } @@ -108,19 +94,23 @@ class Visit extends Endpoint implements \LORIS\Middleware\ETagCalculator if (!$this->_visit->isAccessibleBy($user)) { return new \LORIS\Http\Response\JSON\Forbidden('Permission denied'); } + } $pathparts = $request->getAttribute('pathparts'); if (count($pathparts) === 0) { - $method = $request->getMethod(); + switch ($request->getMethod()) { + case 'GET': + return $this->_handleGET(); - if (in_array($method, $this->allowedMethods())) { - $handle = 'handle'.$method; - return $this->$handle($request); - } else if ($method === 'OPTIONS') { + case 'PUT': + return $this->_handlePUT($request); + + case 'OPTIONS': return (new \LORIS\Http\Response()) ->withHeader('Allow', $this->allowedMethods()); - } else { + + default: return new \LORIS\Http\Response\JSON\MethodNotAllowed( $this->allowedMethods() ); @@ -168,7 +158,7 @@ class Visit extends Endpoint implements \LORIS\Middleware\ETagCalculator * * @return ResponseInterface The outgoing PSR7 response */ - protected function handleGET(): ResponseInterface + private function _handleGET(): ResponseInterface { if ($this->_visit === null) { return new \LORIS\Http\Response\JSON\NotFound('Visit not found'); @@ -181,7 +171,7 @@ class Visit extends Endpoint implements \LORIS\Middleware\ETagCalculator } /** - * Validate a PUT request + * Handles a PUT request that creates or replace a candidate visit * * TODO: There is no way to validate the the visit_label in the url * fits the json data of the request because it is removed from the @@ -191,13 +181,13 @@ class Visit extends Endpoint implements \LORIS\Middleware\ETagCalculator * * @param ServerRequestInterface $request The incoming PSR7 request * - * @return ?ResponseInterface The outgoing PSR7 response + * @return ResponseInterface The outgoing PSR7 response */ - protected function validatePUT( - ServerRequestInterface $request - ): ?ResponseInterface { - $user = $request->getAttribute('user'); - $data = json_decode((string) $request->getBody(), true) ?? []; + private function _handlePUT(ServerRequestInterface $request): ResponseInterface + { + $user = $request->getAttribute('user'); + $data = json_decode((string) $request->getBody(), true); + $visitinfo = $data ?? []; $requiredfields = [ 'CandID', @@ -206,7 +196,7 @@ class Visit extends Endpoint implements \LORIS\Middleware\ETagCalculator 'Battery', 'Project', ]; - $diff = array_diff($requiredfields, array_keys($data)); + $diff = array_diff($requiredfields, array_keys($visitinfo)); if (!empty($diff)) { return new \LORIS\Http\Response\JSON\BadRequest( @@ -215,61 +205,39 @@ class Visit extends Endpoint implements \LORIS\Middleware\ETagCalculator } try { - $this->_project = \NDB_Factory::singleton()->project($data['Project']); + $project = \NDB_Factory::singleton()->project($visitinfo['Project']); } catch (\NotFound $e) { return new \LORIS\Http\Response\JSON\BadRequest( $e->getMessage() ); } - if (strval($data['CandID']) !== strval($this->_candidate->getCandID())) { + if ($visitinfo['CandID'] !== $this->_candidate->getCandID()) { return new \LORIS\Http\Response\JSON\BadRequest( - 'The CandID field does not match this candidate' + 'CandID does not match this candidate' ); } if (!$this->_candidate->isAccessibleBy($user)) { return new \LORIS\Http\Response\JSON\Forbidden( - "You can't modify that candidate" + "You can't create or modify that candidate" ); } - $centerID = array_search($data['Site'], \Utility::getSiteList()); + $centerid = array_search($visitinfo['Site'], \Utility::getSiteList()); - if ($centerID === false || !in_array( - new \CenterID(strval($centerID)), - $user->getCenterIDs() - ) + if ($centerid === false + || !in_array(new \CenterID("$centerid"), $user->getCenterIDs()) ) { return new \LORIS\Http\Response\JSON\Forbidden( "You can't create or modify candidates visit for the site " . - $centerID + $centerid ); } - // Now that the validation is done, convert to an object. - $this->_centerID = new \CenterID(strval($centerID)); - - return null; - } - - /** - * Handles a PUT request that creates or replace a candidate visit - * - * @param ServerRequestInterface $request The incoming PSR7 request - * - * @return ResponseInterface The outgoing PSR7 response - */ - protected function handlePUT(ServerRequestInterface $request): ResponseInterface - { - $user = $request->getAttribute('user'); - $data = json_decode((string) $request->getBody(), true); - $visitinfo = $data ?? []; - - $response = $this->validatePUT($request); - if ($response !== null) { - return $response; - } + // \Utility::getSiteList key was a string. Now that the + // validation is done, convert to an object. + $centerid = new \CenterID("$centerid"); $subprojectid = array_search( $visitinfo['Battery'], @@ -286,7 +254,6 @@ class Visit extends Endpoint implements \LORIS\Middleware\ETagCalculator $timepoint = \NDB_Factory::singleton()->timepoint( new \SessionID(strval($sessionid)) ); - if ($timepoint->getCurrentStage() !== 'Not Started') { return new \LORIS\Http\Response\JSON\Conflict( 'This visit is already started' @@ -298,7 +265,7 @@ class Visit extends Endpoint implements \LORIS\Middleware\ETagCalculator $timepoint->setData( [ - 'CenterID' => $this->_centerID, + 'CenterID' => $centerid, 'Visit_label' => $visitinfo['Visit'], 'SubprojectID' => $subprojectid, 'Active' => 'Y', @@ -307,7 +274,7 @@ class Visit extends Endpoint implements \LORIS\Middleware\ETagCalculator 'UserID' => $username, 'Date_registered' => $today, 'Testdate' => $today, - 'ProjectID' => $this->_project->getId(), + 'ProjectID' => $project->getId(), ] ); @@ -333,8 +300,8 @@ class Visit extends Endpoint implements \LORIS\Middleware\ETagCalculator $this->_candidate, $subprojectid, $visitinfo['Visit'], - \Site::singleton($this->_centerID), - $this->_project + \Site::singleton($centerid), + $project ); $link = '/' . $request->getUri()->getPath(); diff --git a/modules/api/php/endpoints/candidate/visit/visit_0_0_4_dev.class.inc b/modules/api/php/endpoints/candidate/visit/visit_0_0_4_dev.class.inc index 66fc951f31e..a69ef8b2fe8 100644 --- a/modules/api/php/endpoints/candidate/visit/visit_0_0_4_dev.class.inc +++ b/modules/api/php/endpoints/candidate/visit/visit_0_0_4_dev.class.inc @@ -14,6 +14,9 @@ namespace LORIS\api\Endpoints\Candidate\Visit; use \Psr\Http\Message\ServerRequestInterface; use \Psr\Http\Message\ResponseInterface; +use \LORIS\api\Endpoint; +use \LORIS\StudyEntities\Candidate\CandID; + /** * A class for handling the /candidates/$candidate/$visit_label endpoint. @@ -24,8 +27,49 @@ use \Psr\Http\Message\ResponseInterface; * @license http://www.gnu.org/licenses/gpl-3.0.txt GPLv3 * @link https://github.com/aces/Loris */ -class Visit_0_0_4_Dev extends Visit +class Visit_0_0_4_Dev extends Endpoint implements \LORIS\Middleware\ETagCalculator { + + /** + * The requested Candidate + * + * @var \Candidate + */ + private $_candidate; + + /** + * The requested Visit + * + * @var ?\Timepoint + */ + private $_visit; + + /** + * The visit centerID + * + * @var ?\CenterID + */ + private $_centerID; + + /** + * The visit Project + * + * @var ?\Project + */ + private $_project; + + /** + * Contructor + * + * @param \Candidate $candidate The requested candidate + * @param ?\Timepoint $visit The requested visit; can be null in PUT requests + */ + public function __construct(\Candidate $candidate, ?\Timepoint $visit) + { + $this->_candidate = $candidate; + $this->_visit = $visit; + } + /** * Return which methods are supported by this endpoint. * @@ -54,102 +98,422 @@ class Visit_0_0_4_Dev extends Visit } /** - * Handles a PATCH request. - * For now, only support a NextStageDate argument to start next stage. + * Handles a request that starts with /candidates/$candid * * @param ServerRequestInterface $request The incoming PSR7 request * * @return ResponseInterface The outgoing PSR7 response */ - protected function handlePATCH( - ServerRequestInterface $request - ): ResponseInterface { - $data = json_decode((string) $request->getBody(), true); - $nextStageDate = $data['NextStageDate'] ?? null; + public function handle(ServerRequestInterface $request) : ResponseInterface + { + if ($this->_visit !== null) { + $user = $request->getAttribute('user'); + if (!$this->_visit->isAccessibleBy($user)) { + return new \LORIS\Http\Response\JSON\Forbidden('Permission denied'); + } + } + + $pathparts = $request->getAttribute('pathparts'); + if (count($pathparts) === 0) { + $method = $request->getMethod(); + + if (in_array($method, $this->allowedMethods())) { + $handle = '_handle'.$method; + return $this->$handle($request); + } + + if ($method === 'OPTIONS') { + return (new \LORIS\Http\Response()) + ->withHeader('Allow', $this->allowedMethods()); + } + + return new \LORIS\Http\Response\JSON\MethodNotAllowed( + $this->allowedMethods() + ); + } if ($this->_visit === null) { + // Subendpoint requires a Timepoint. see Constructor comment for $visit return new \LORIS\Http\Response\JSON\NotFound('Visit not found'); } - $response = $this->validatePUT($request); - if ($response !== null) { - return $response; + // Delegate to sub-endpoints + $subendpoint = array_shift($pathparts); + switch($subendpoint) { + case 'instruments': + $handler = new Instruments($this->_visit); + break; + case 'images': + $handler = new Images($this->_visit); + break; + case 'qc': + $handler = new Qc($this->_visit); + break; + case 'dicoms': + $handler = new Dicoms($this->_visit); + break; + case 'recordings': + $handler = new Recordings($this->_visit); + break; + default: + return new \LORIS\Http\Response\JSON\NotFound(); } - if (!isset($data['Visit']) - || $data['Visit'] !== $this->_visit->getVisitLabel() - ) { + $newrequest = $request + ->withAttribute('pathparts', $pathparts); + + return $handler->process( + $newrequest, + $handler + ); + } + + /** + * Generates a response fitting the API specification for this endpoint. + * + * @return ResponseInterface The outgoing PSR7 response + */ + private function _handleGET(): ResponseInterface + { + if ($this->_visit === null) { + return new \LORIS\Http\Response\JSON\NotFound('Visit not found'); + } + + return new \LORIS\Http\Response\JsonResponse( + (new \LORIS\api\Views\Visit($this->_visit)) + ->toArray() + ); + } + + /** + * Validate a PUT request + * + * TODO: There is no way to validate the the visit_label in the url + * fits the json data of the request because it is removed from the + * pathparts in the calling class. The correct way to do it would be + * to pass a "light" timepoint class that contains the visit_label but + * no sessionid in the constructor. + * + * @param ServerRequestInterface $request The incoming PSR7 request + * + * @return ?ResponseInterface The outgoing PSR7 response + */ + private function _validatePUT( + ServerRequestInterface $request + ): ?ResponseInterface { + $user = $request->getAttribute('user'); + $data = json_decode((string) $request->getBody(), true) ?? []; + + $requiredfields = [ + 'CandID', + 'Visit', + 'Site', + 'Battery', + 'Project', + ]; + + $diff = array_diff($requiredfields, array_keys($data)); + if (!empty($diff)) { return new \LORIS\Http\Response\JSON\BadRequest( - 'The Visit field does not match this visit' + 'Field(s) missing: ' . implode(', ', $diff) + ); + } + + try { + $this->_project = \NDB_Factory::singleton()->project($data['Project']); + } catch (\NotFound $e) { + return new \LORIS\Http\Response\JSON\BadRequest( + $e->getMessage() ); } - if (!$nextStageDate) { + if (strval($data['CandID']) !== strval($this->_candidate->getCandID())) { return new \LORIS\Http\Response\JSON\BadRequest( - 'There is no stage date specified' + 'The CandID field does not match this candidate' + ); + } + + if (!$this->_candidate->isAccessibleBy($user)) { + return new \LORIS\Http\Response\JSON\Forbidden( + "You can't modify that candidate" + ); + } + + $centerID = array_search($data['Site'], \Utility::getSiteList()); + + if ($centerID === false + || !in_array( + new \CenterID(strval($centerID)), + $user->getCenterIDs() + ) + ) { + return new \LORIS\Http\Response\JSON\Forbidden( + "You can't create or modify candidates' visits for the site " . + $data['Site'] ); } - $date_format = 'Y-m-d'; - $date = \DateTime::createFromFormat($date_format, $nextStageDate); + // Now that the validation is done, convert to an object. + $this->_centerID = new \CenterID(strval($centerID)); + + return null; + } + + /** + * Handles a PUT request that creates or replace a candidate visit + * + * @param ServerRequestInterface $request The incoming PSR7 request + * + * @return ResponseInterface The outgoing PSR7 response + */ + private function _handlePUT(ServerRequestInterface $request): ResponseInterface + { + $user = $request->getAttribute('user'); + $data = json_decode((string) $request->getBody(), true); + $visitinfo = $data ?? []; + + $response = $this->_validatePUT($request); + if ($response !== null) { + return $response; + } + + $subprojectid = array_search( + $visitinfo['Battery'], + \Utility::getSubprojectList() + ); + + $sessionid = array_search( + $visitinfo['Visit'], + $this->_candidate->getListOfVisitLabels() + ); + + if ($sessionid !== false) { + // If the visit label already exists for that candidate + $timepoint = \NDB_Factory::singleton()->timepoint( + new \SessionID(strval($sessionid)) + ); + + if ($timepoint->getCurrentStage() !== 'Not Started') { + return new \LORIS\Http\Response\JSON\Conflict( + 'This visit is already started' + ); + } - if (!($date && $date->format($date_format) === $nextStageDate)) { + $username = $user->getUsername(); + $today = date("Y-m-d"); + + $timepoint->setData( + [ + 'CenterID' => $this->_centerID, + 'Visit_label' => $visitinfo['Visit'], + 'SubprojectID' => $subprojectid, + 'Active' => 'Y', + 'Date_active' => $today, + 'RegisteredBy' => $username, + 'UserID' => $username, + 'Date_registered' => $today, + 'Testdate' => $today, + 'ProjectID' => $this->_project->getId(), + ] + ); + + $link = '/' . $request->getUri()->getPath(); + return (new \LORIS\Http\Response()) + ->withStatus(204) + ->withHeader('Content-Location', $link); + } + + try { + \TimePoint::isValidVisitLabel( + new CandID($visitinfo['CandID']), + $subprojectid, + $visitinfo['Visit'] + ); + } catch (\LorisException $e) { return new \LORIS\Http\Response\JSON\BadRequest( - 'The date specified is invalid. Date must be of the form YYYY-MM-DD' + $e->getMessage() ); } - if ($nextStageDate > date($date_format)) { + \TimePoint::createNew( + $this->_candidate, + $subprojectid, + $visitinfo['Visit'], + \Site::singleton($this->_centerID), + $this->_project + ); + + $link = '/' . $request->getUri()->getPath(); + return (new \LORIS\Http\Response()) + ->withStatus(201) + ->withHeader('Content-Location', $link); + } + + /** + * Handles a PATCH request. + * + * @param ServerRequestInterface $request The incoming PSR7 request + * + * @return ResponseInterface The outgoing PSR7 response + */ + private function _handlePATCH( + ServerRequestInterface $request + ): ResponseInterface { + $data = json_decode((string) $request->getBody(), true); + + if (is_null($data)) { return new \LORIS\Http\Response\JSON\BadRequest( - 'The date specified must be not later than today' + 'The request is not a valid json object' ); } - if ($this->_visit->getCurrentStage() !== 'Not Started') { - return new \LORIS\Http\Response\JSON\Conflict( - 'This visit is already started' + if (is_null($this->_visit)) { + return new \LORIS\Http\Response\JSON\NotFound('Visit not found'); + } + + // a PATCH request should contain the same mandatory fields as a PUT request. + $response = $this->_validatePUT($request); + if ($response !== null) { + return $response; + } + + if ($data['Visit'] !== $this->_visit->getVisitLabel()) { + return new \LORIS\Http\Response\JSON\BadRequest( + 'The Visit field does not match this visit' ); } - $newStage = $this->_visit->getNextStage(); - if (!$newStage) { - return new \LORIS\Http\Response\JSON\Conflict( - "Next stage is null" + if (!isset($data['Stages'])) { + return new \LORIS\Http\Response\JSON\BadRequest( + 'There is no stage data specified' ); } - // start that stage - $this->_visit->startStage($newStage); + $allowedStages = ['Screening', 'Visit', 'Approval']; + $allowedStatuses = ['Pass', 'Failure', 'Withdrawal', 'In Progress']; - // set the date for the new stage - $this->_visit->setData(["Date_".$newStage => $nextStageDate]); + // loop on all stages to validate + foreach ($data['Stages'] as $stage => $stageData) { + if (!in_array($stage, $allowedStages)) { + return new \LORIS\Http\Response\JSON\BadRequest( + "Stage $stage is not valid" + ); + } - // create a new battery object && new battery - $candidate = \Candidate::singleton($this->_visit->getCandID()); + // TODO: Implement and remove + if (in_array($stage, ['Screening', 'Approval'])) { + return new \LORIS\Http\Response\JSON\NotImplemented( + "Stage $stage is not implemented yet" + ); + } - // First visit ? - $firstVisit = false; - try { - if ($candidate->getFirstVisit() == $this->_visit->getVisitLabel()) { - $firstVisit = true; + if (!isset($stageData['Status'])) { + return new \LORIS\Http\Response\JSON\BadRequest( + "There is no $stage stage status specified" + ); + } + + if (!in_array($stageData['Status'], $allowedStatuses)) { + return new \LORIS\Http\Response\JSON\BadRequest( + "Stage $stage status {$stageData['Status']} is not valid" + ); + } + + // TODO: Implement and remove + if (in_array($stageData['Status'], ['Pass', 'Failure', 'Withdrawal'])) { + return new \LORIS\Http\Response\JSON\NotImplemented( + "Stage $stage status transition + {$stageData['Status']} is not implemented yet" + ); + } + + if (!isset($stageData['Date'])) { + return new \LORIS\Http\Response\JSON\BadRequest( + "There is no $stage stage date specified" + ); + } + + $stageData['Date'] = \DateTime::createFromFormat( + 'Y-m-d', + $stageData['Date'] + ); + if (!$stageData['Date']) { + return new \LORIS\Http\Response\JSON\BadRequest( + "The stage date specified for $stage is invalid. + Date must be of the form YYYY-MM-DD" + ); } - } catch (\LorisException $e) { } - $battery = new \NDB_BVL_Battery; - // select a specific time point (sessionID) for the battery - $battery->selectBattery($this->_visit->getSessionID()); + // Passed this point, the request should be valid + // Loop again and process the request + foreach ($data['Stages'] as $stage => $stageData) { + switch ($stage) { + // Only Visit | In progress is supported at the moment + case 'Visit': + switch ($stageData['Status']) { + case 'In Progress': + if ($this->_visit->getCurrentStage() !== 'Not Started') { + return new \LORIS\Http\Response\JSON\Conflict( + "This visit is already started" + ); + } - // add instruments to the time point - $battery->createBattery( - $request->getAttribute("loris"), - $this->_visit->getSubprojectID(), - $newStage, - $this->_visit->getVisitLabel(), - $this->_visit->getCenterID(), - $firstVisit - ); + $newStage = $this->_visit->getNextStage(); + if (!$newStage) { + return new \LORIS\Http\Response\JSON\Conflict( + "Next stage is null" + ); + } + + // start that stage + $this->_visit->startStage($newStage); + + // set the date for the new stage + $this->_visit->setData(["Date_$newStage" => $stageData["Date"]]); + + // create a new battery object && new battery + $candidate = \Candidate::singleton($this->_visit->getCandID()); + $visitLabel = $this->_visit->getVisitLabel(); + + // First visit ? + $firstVisit = false; + try { + if ($candidate->getFirstVisit() == $visitLabel) { + $firstVisit = true; + } + } catch (\LorisException $e) { + } + + $battery = new \NDB_BVL_Battery; + // select a specific time point (sessionID) for the battery + $battery->selectBattery($this->_visit->getSessionID()); - return new \LORIS\Http\Response\JSON\OK(); + // add instruments to the time point + $battery->createBattery( + $request->getAttribute("loris"), + $this->_visit->getSubprojectID(), + $newStage, + $this->_visit->getVisitLabel(), + $this->_visit->getCenterID(), + $firstVisit + ); + + return new \LORIS\Http\Response\JSON\OK(); + } + } + } + } + + /** + * Implements the ETagCalculator interface + * + * @param ServerRequestInterface $request The PSR7 incoming request. + * + * @return string etag summarizing value of this request. + */ + public function ETag(ServerRequestInterface $request) : string + { + return md5(json_encode($this->_visit)); } } From c91468323c300c99dabc0c10ce71357685d4e1b8 Mon Sep 17 00:00:00 2001 From: Laetitia Fesselier Date: Wed, 25 Aug 2021 15:19:19 -0400 Subject: [PATCH 3/7] schema.yml changes --- modules/api/static/schema.yml | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/modules/api/static/schema.yml b/modules/api/static/schema.yml index 62c7b522ab1..e1fe1a2848c 100644 --- a/modules/api/static/schema.yml +++ b/modules/api/static/schema.yml @@ -334,6 +334,36 @@ paths: application/json: schema: $ref: '#/components/schemas/inline_response_409' + patch: + tags: + - Visit + summary: Updates a timepoint. Providing a "Visit" stage with "In Progress" as status will attemp to start the visiti (start next stage). + parameters: + - name: candid + in: path + description: The candidate identifier + required: true + style: simple + explode: false + schema: + type: string + - name: visit + in: path + description: The visit label + required: true + style: simple + explode: false + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/VisitPatchFields' + responses: + '200': + description: The visit was updated successfully + '/candidates/{candid}/{visit}/instruments': get: tags: @@ -1888,6 +1918,11 @@ components: - Failure - Withdrawal - In Progress + VisitPatchFields: + type: object + allOf: + - $ref: '#/components/schemas/VisitMetaFields' + - $ref: '#/components/schemas/VisitStages' InstrumentScreening: type: object properties: From f710b17bf5d4a10c5508d418928f7814cac1b6a7 Mon Sep 17 00:00:00 2001 From: Laetitia Fesselier Date: Tue, 28 Sep 2021 13:23:44 -0400 Subject: [PATCH 4/7] Xavier's suggestions and documentation --- CHANGELOG.md | 1 + modules/api/docs/LorisRESTAPI.md | 2 +- modules/api/docs/LorisRESTAPI_v0.0.4-dev.md | 29 +- .../endpoints/candidate/candidate.class.inc | 21 +- modules/api/static/schema-v0.0.4-dev.yml | 2493 +++++++++++++++++ modules/api/static/schema.yml | 35 - 6 files changed, 2533 insertions(+), 48 deletions(-) create mode 100644 modules/api/static/schema-v0.0.4-dev.yml diff --git a/CHANGELOG.md b/CHANGELOG.md index b2e102f6b8f..63b7b2efbe8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,7 @@ requesting a new account and will be displayed in the User Accounts module (PR # #### API - Creation of a new version of the API under development (v0.0.4-dev) (PR #6944) - Deletion of support for the oldest version of the API (v0.0.2) (PR #6944) +- Addition of a PATCH request for /candidates/$CandID/$VisitLabel to start next stage when the payload contains a "Visit" stage with "In Progress" as Status, when the current status of the Visit stage is "Not Started". #### Candidate Parameters - Consents may now be grouped in UI of consent tab (PR #6042, PR #6044) #### API Documentation (**New Module**) diff --git a/modules/api/docs/LorisRESTAPI.md b/modules/api/docs/LorisRESTAPI.md index 4b57f72c15a..87a81aa3cf6 100644 --- a/modules/api/docs/LorisRESTAPI.md +++ b/modules/api/docs/LorisRESTAPI.md @@ -416,7 +416,7 @@ The JSON object is of the form: } ``` -A PUT with only the Meta fields will create the VisitLabel +A PUT request with only the Meta fields will create the VisitLabel for this candidate, in an unstarted stage if the Visit label provided is valid. PATCH is not supported for Visit Labels. diff --git a/modules/api/docs/LorisRESTAPI_v0.0.4-dev.md b/modules/api/docs/LorisRESTAPI_v0.0.4-dev.md index 82225b589c6..ed1adcc0953 100644 --- a/modules/api/docs/LorisRESTAPI_v0.0.4-dev.md +++ b/modules/api/docs/LorisRESTAPI_v0.0.4-dev.md @@ -419,7 +419,34 @@ The JSON object is of the form: A PUT request with only the Meta fields will create the VisitLabel for this candidate, in an unstarted stage if the Visit label provided is valid. -A PATCH request with the Meta fields and a date NextStageDate will start the next stage, if the stage is 'Not Started' and the CandID and Visit label provided are valid. +A PATCH request of the form: +```js +{ + "CandID" : 317604, + "Visit" : 'Visit 01', + "Site" : 'DCC', + "Battery" : 'Stale', + "Project" : 'Pumpernickel', + "Stages" : { + "Screening" : { + "Date" : "YYYY-MM-DD", + "Status" : "Pass|Failure|Withdrawal|In Progress" + }, + "Visit" : { + "Date" : "YYYY-MM-DD", + "Status" : "Pass|Failure|Withdrawal|In Progress" + }, + "Approval" : { + "Date" : "YYYY-MM-DD", + "Status" : "Pass|Failure|Withdrawal|In Progress" + } + } +} +``` +will update the stages date and status. + +At this moment, we only support requests for the Visit stage with the In progress status, +to start the next stage if the Visit stage is 'Not Started' and the CandID and Visit label provided are valid. It will return a 404 Not Found if the visit label does not exist for this candidate (as well as anything under the /candidates/$CandID/$VisitLabel hierarchy) diff --git a/modules/api/php/endpoints/candidate/candidate.class.inc b/modules/api/php/endpoints/candidate/candidate.class.inc index 863b2de531b..e56dac9c00c 100644 --- a/modules/api/php/endpoints/candidate/candidate.class.inc +++ b/modules/api/php/endpoints/candidate/candidate.class.inc @@ -112,19 +112,18 @@ class Candidate extends Endpoint implements \LORIS\Middleware\ETagCalculator $visit = null; } - $version = $request->getAttribute("LORIS-API-Version"); - if ($version === 'v0.0.3') { - $handler = new Visit\Visit( - $this->_candidate, - $visit - ); - } else { - $handler = new Visit\Visit_0_0_4_Dev( - $this->_candidate, - $visit - ); + $version = $request->getAttribute("LORIS-API-Version"); + $subendpoint = __NAMESPACE__ . '\\' . 'Visit\Visit'; + + if ($version == 'v0.0.4-dev') { + $subendpoint = __NAMESPACE__ . '\\' .'Visit\Visit_0_0_4_Dev'; } + $handler = new $subendpoint( + $this->_candidate, + $visit + ); + return $handler->process( $newrequest, $handler diff --git a/modules/api/static/schema-v0.0.4-dev.yml b/modules/api/static/schema-v0.0.4-dev.yml new file mode 100644 index 00000000000..026ff842342 --- /dev/null +++ b/modules/api/static/schema-v0.0.4-dev.yml @@ -0,0 +1,2493 @@ +openapi: 3.0.1 +info: + title: LORIS - REST API endpoints + description: The LORIS API uses standard HTTP error codes and the body of any response will either be empty or contain only a JSON object for any request. + contact: + name: LORIS Development Team + url: 'https://github.com/aces/loris' + license: + name: 'GNU Public License, Version 3' + url: 'https://opensource.org/licenses/GPL-3.0' + version: 0.0.3 +servers: + - url: /api/v0.0.3 +security: + - ApiKeyAuth: [] +paths: + /login: + post: + tags: + - Login + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/body' + responses: + '200': + description: The JWT + content: + application/json: + schema: + $ref: '#/components/schemas/inline_response_200' + /projects: + get: + tags: + - Projects + summary: List all projects + responses: + '200': + description: An object with projects as properties + content: + application/json: + schema: + $ref: '#/components/schemas/inline_response_200_1' + '/projects/{project}': + get: + tags: + - Projects + summary: Get that project descriptor + parameters: + - name: project + in: path + description: The project name + required: true + style: simple + explode: false + schema: + type: string + responses: + '200': + description: An object containing the project descriptor + content: + application/json: + schema: + $ref: '#/components/schemas/Project' + '/projects/{project}/candidates': + get: + tags: + - Projects + summary: List all candidates of that project + parameters: + - name: project + in: path + description: The project name + required: true + style: simple + explode: false + schema: + type: string + responses: + '200': + description: An object containing the list of candidates + content: + application/json: + schema: + $ref: '#/components/schemas/ProjectCandidates' + '/projects/{project}/images': + get: + tags: + - Projects + summary: List all images of that project + parameters: + - name: project + in: path + description: The project name + required: true + style: simple + explode: false + schema: + type: string + - name: since + in: query + description: A date + required: false + style: form + explode: true + schema: + type: string + responses: + '200': + description: An object containing the list of image descriptors + content: + application/json: + schema: + $ref: '#/components/schemas/ProjectImages' + '/projects/{project}/instruments': + get: + tags: + - Projects + summary: List all instruments of that project + parameters: + - name: project + in: path + description: The project name + required: true + style: simple + explode: false + schema: + type: string + responses: + '200': + description: An object containing the list of instrument summary descriptors + content: + application/json: + schema: + $ref: '#/components/schemas/ProjectInstruments' + '/projects/{project}/instruments/{instrument}': + get: + tags: + - Projects + summary: Get an instrument descriptor in that project + parameters: + - name: project + in: path + description: The project name + required: true + style: simple + explode: false + schema: + type: string + - name: instrument + in: path + description: The instrument short name + required: true + style: simple + explode: false + schema: + type: string + responses: + '200': + description: An JSON representation of the instrument form + content: + application/json: + schema: + $ref: '#/components/schemas/ProjectInstrument' + '/projects/{project}/visits': + get: + tags: + - Projects + summary: List all existing visit label of that project + parameters: + - name: project + in: path + description: The project name + required: true + style: simple + explode: false + schema: + type: string + responses: + '200': + description: An object containing the list of instrument summary descriptors + content: + application/json: + schema: + $ref: '#/components/schemas/ProjectVisits' + '/projects/{project}/recordings': + get: + tags: + - Projects + summary: List all existing visit label of that project + parameters: + - name: project + in: path + description: The project name + required: true + style: simple + explode: false + schema: + type: string + responses: + '200': + description: An object containing the list of instrument summary descriptors + content: + application/json: + schema: + $ref: '#/components/schemas/ProjectRecordings' + /candidates: + get: + tags: + - Candidates + summary: List all candidates in this LORIS instance + responses: + '200': + description: An object containing the list of instrument summary descriptors + content: + application/json: + schema: + $ref: '#/components/schemas/Candidates' + post: + tags: + - Candidates + summary: Create a candidate + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/NewCandidate' + responses: + '201': + description: The new candidate + content: + application/json: + schema: + $ref: '#/components/schemas/Candidate' + '/candidates/{candid}': + get: + tags: + - Candidates + summary: Get a JSON object representing that candidate + parameters: + - name: candid + in: path + description: The candidate identifier + required: true + style: simple + explode: false + schema: + type: string + responses: + '200': + description: The new candidate + content: + application/json: + schema: + $ref: '#/components/schemas/inline_response_200_2' + '/candidates/{candid}/{visit}': + get: + tags: + - Visit + summary: Get a visit descriptor for that candidate + parameters: + - name: candid + in: path + description: The candidate identifier + required: true + style: simple + explode: false + schema: + type: string + - name: visit + in: path + description: The visit label + required: true + style: simple + explode: false + schema: + type: string + responses: + '200': + description: The new candidate + content: + application/json: + schema: + $ref: '#/components/schemas/inline_response_200_3' + put: + tags: + - Visit + summary: Add a timepoint to that candidate + parameters: + - name: candid + in: path + description: The candidate identifier + required: true + style: simple + explode: false + schema: + type: string + - name: visit + in: path + description: The visit label + required: true + style: simple + explode: false + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/VisitMetaFields' + responses: + '201': + description: The visit was created successfully + headers: + Content-Location: + description: The URL of the new visit. + style: simple + explode: false + schema: + type: string + '204': + description: The visit was updated successfully. + headers: + Content-Location: + description: The URL of the new visit. + style: simple + explode: false + schema: + type: string + '409': + description: The visit is already started. It can't be updated. + content: + application/json: + schema: + $ref: '#/components/schemas/inline_response_409' + patch: + tags: + - Visit + summary: Updates a timepoint. Providing a "Visit" stage with "In Progress" as status will attemp to start the visit (start next stage). + parameters: + - name: candid + in: path + description: The candidate identifier + required: true + style: simple + explode: false + schema: + type: string + - name: visit + in: path + description: The visit label + required: true + style: simple + explode: false + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/VisitPatchFields' + responses: + '200': + description: The visit stage was updated successfully + + '/candidates/{candid}/{visit}/instruments': + get: + tags: + - Instruments + summary: List all instruments of that timepoint + parameters: + - name: candid + in: path + description: The candidate identifier + required: true + style: simple + explode: false + schema: + type: string + - name: visit + in: path + description: The visit label + required: true + style: simple + explode: false + schema: + type: string + responses: + '200': + description: The list of instrument short names + content: + application/json: + schema: + $ref: '#/components/schemas/inline_response_200_4' + '/candidates/{candid}/{visit}/instruments/{instrument}': + get: + tags: + - Instruments + summary: Get an instrument data values + parameters: + - name: candid + in: path + description: The candidate identifier + required: true + style: simple + explode: false + schema: + type: string + - name: visit + in: path + description: The visit label + required: true + style: simple + explode: false + schema: + type: string + - name: instrument + in: path + description: The instrument short name + required: true + style: simple + explode: false + schema: + type: string + responses: + '200': + description: This instrument data values + content: + application/json: + schema: + $ref: '#/components/schemas/Instrument' + put: + tags: + - Instruments + summary: Set an instrument data values + parameters: + - name: candid + in: path + description: The candidate identifier + required: true + style: simple + explode: false + schema: + type: string + - name: visit + in: path + description: The visit label + required: true + style: simple + explode: false + schema: + type: string + - name: instrument + in: path + description: The instrument short name + required: true + style: simple + explode: false + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Instrument' + responses: + '204': + description: This instrument data values have been saved + patch: + tags: + - Instruments + summary: Update some data values of an instrument + parameters: + - name: candid + in: path + description: The candidate identifier + required: true + style: simple + explode: false + schema: + type: string + - name: visit + in: path + description: The visit label + required: true + style: simple + explode: false + schema: + type: string + - name: instrument + in: path + description: The instrument short name + required: true + style: simple + explode: false + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Instrument' + responses: + '204': + description: This instrument data values have been saved + '/candidates/{candid}/{visit}/instruments/{instrument}/flags': + get: + tags: + - Instruments + summary: Get an instrument flags values + parameters: + - name: candid + in: path + description: The candidate identifier + required: true + style: simple + explode: false + schema: + type: string + - name: visit + in: path + description: The visit label + required: true + style: simple + explode: false + schema: + type: string + - name: instrument + in: path + description: The instrument short name + required: true + style: simple + explode: false + schema: + type: string + responses: + '200': + description: This instrument flags values + content: + application/json: + schema: + $ref: '#/components/schemas/InstrumentFlags' + put: + tags: + - Instruments + summary: Set an instrument flags values + parameters: + - name: candid + in: path + description: The candidate identifier + required: true + style: simple + explode: false + schema: + type: string + - name: visit + in: path + description: The visit label + required: true + style: simple + explode: false + schema: + type: string + - name: instrument + in: path + description: The instrument short name + required: true + style: simple + explode: false + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/InstrumentFlags' + responses: + '501': + description: This instrument flags values + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + patch: + tags: + - Instruments + summary: Update some flags values of an instrument + parameters: + - name: candid + in: path + description: The candidate identifier + required: true + style: simple + explode: false + schema: + type: string + - name: visit + in: path + description: The visit label + required: true + style: simple + explode: false + schema: + type: string + - name: instrument + in: path + description: The instrument short name + required: true + style: simple + explode: false + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/InstrumentFlags' + responses: + '501': + description: This instrument flags values + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '/candidates/{candid}/{visit}/instruments/{instrument}/dde': + get: + tags: + - Instruments + summary: Get an instrument data values + parameters: + - name: candid + in: path + description: The candidate identifier + required: true + style: simple + explode: false + schema: + type: string + - name: visit + in: path + description: The visit label + required: true + style: simple + explode: false + schema: + type: string + - name: instrument + in: path + description: The instrument short name + required: true + style: simple + explode: false + schema: + type: string + responses: + '200': + description: This instrument data values + content: + application/json: + schema: + $ref: '#/components/schemas/Instrument' + put: + tags: + - Instruments + summary: Set an instrument data values + parameters: + - name: candid + in: path + description: The candidate identifier + required: true + style: simple + explode: false + schema: + type: string + - name: visit + in: path + description: The visit label + required: true + style: simple + explode: false + schema: + type: string + - name: instrument + in: path + description: The instrument short name + required: true + style: simple + explode: false + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Instrument' + responses: + '204': + description: This instrument data values have been saved + patch: + tags: + - Instruments + summary: Update some data values of an instrument + parameters: + - name: candid + in: path + description: The candidate identifier + required: true + style: simple + explode: false + schema: + type: string + - name: visit + in: path + description: The visit label + required: true + style: simple + explode: false + schema: + type: string + - name: instrument + in: path + description: The instrument short name + required: true + style: simple + explode: false + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Instrument' + responses: + '204': + description: This instrument data values have been saved + '/candidates/{candid}/{visit}/instruments/{instrument}/dde/flags': + get: + tags: + - Instruments + summary: Get an instrument flags values + parameters: + - name: candid + in: path + description: The candidate identifier + required: true + style: simple + explode: false + schema: + type: string + - name: visit + in: path + description: The visit label + required: true + style: simple + explode: false + schema: + type: string + - name: instrument + in: path + description: The instrument short name + required: true + style: simple + explode: false + schema: + type: string + responses: + '200': + description: This instrument flags values + content: + application/json: + schema: + $ref: '#/components/schemas/InstrumentFlags' + put: + tags: + - Instruments + summary: Set an instrument flags values + parameters: + - name: candid + in: path + description: The candidate identifier + required: true + style: simple + explode: false + schema: + type: string + - name: visit + in: path + description: The visit label + required: true + style: simple + explode: false + schema: + type: string + - name: instrument + in: path + description: The instrument short name + required: true + style: simple + explode: false + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/InstrumentFlags' + responses: + '501': + description: This instrument flags values + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + patch: + tags: + - Instruments + summary: Update some flags values of an instrument + parameters: + - name: candid + in: path + description: The candidate identifier + required: true + style: simple + explode: false + schema: + type: string + - name: visit + in: path + description: The visit label + required: true + style: simple + explode: false + schema: + type: string + - name: instrument + in: path + description: The instrument short name + required: true + style: simple + explode: false + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/InstrumentFlags' + responses: + '501': + description: This instrument flags values + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '/candidates/{candid}/{visit}/images': + get: + tags: + - Images + summary: List all images of a timepoint + parameters: + - name: candid + in: path + description: The candidate identifier + required: true + style: simple + explode: false + schema: + type: string + - name: visit + in: path + description: The visit label + required: true + style: simple + explode: false + schema: + type: string + responses: + '200': + description: The images list + content: + application/json: + schema: + $ref: '#/components/schemas/Images' + '/candidates/{candid}/{visit}/images/{filename}': + get: + tags: + - Images + summary: Get an images of a timepoint + parameters: + - name: candid + in: path + description: The candidate identifier + required: true + style: simple + explode: false + schema: + type: string + - name: visit + in: path + description: The visit label + required: true + style: simple + explode: false + schema: + type: string + - name: filename + in: path + description: The image filename + required: true + style: simple + explode: false + schema: + type: string + responses: + '200': + description: The file content + content: + application/octet-stream: + schema: + type: string + format: binary + '/candidates/{candid}/{visit}/images/{filename}/qc': + get: + tags: + - Images + summary: Get the qc values of an image + parameters: + - name: candid + in: path + description: The candidate identifier + required: true + style: simple + explode: false + schema: + type: string + - name: visit + in: path + description: The visit label + required: true + style: simple + explode: false + schema: + type: string + - name: filename + in: path + description: The image filename + required: true + style: simple + explode: false + schema: + type: string + responses: + '200': + description: The images qc values + content: + application/json: + schema: + $ref: '#/components/schemas/ImageQc' + put: + tags: + - Images + summary: Set the qc values of an image + parameters: + - name: candid + in: path + description: The candidate identifier + required: true + style: simple + explode: false + schema: + type: string + - name: visit + in: path + description: The visit label + required: true + style: simple + explode: false + schema: + type: string + - name: filename + in: path + description: The image filename + required: true + style: simple + explode: false + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/ImageQc' + responses: + '204': + description: Qc value updated + '/candidates/{candid}/{visit}/images/{filename}/format/brainbrowser': + get: + tags: + - Images + summary: Get the brainbrowser format of an image + parameters: + - name: candid + in: path + description: The candidate identifier + required: true + style: simple + explode: false + schema: + type: string + - name: visit + in: path + description: The visit label + required: true + style: simple + explode: false + schema: + type: string + - name: filename + in: path + description: The image filename + required: true + style: simple + explode: false + schema: + type: string + responses: + '200': + description: The images list + content: + application/json: + schema: + $ref: '#/components/schemas/FormatBrainbrowser' + '/candidates/{candid}/{visit}/images/{filename}/format/raw': + get: + tags: + - Images + summary: Get the raw format of an image + parameters: + - name: candid + in: path + description: The candidate identifier + required: true + style: simple + explode: false + schema: + type: string + - name: visit + in: path + description: The visit label + required: true + style: simple + explode: false + schema: + type: string + - name: filename + in: path + description: The image filename + required: true + style: simple + explode: false + schema: + type: string + responses: + '200': + description: The the output of mnc2raw + content: + application/octet-stream: + schema: + type: string + format: binary + '/candidates/{candid}/{visit}/images/{filename}/format/thumbnail': + get: + tags: + - Images + summary: Get the thumbnail format of an image + parameters: + - name: candid + in: path + description: The candidate identifier + required: true + style: simple + explode: false + schema: + type: string + - name: visit + in: path + description: The visit label + required: true + style: simple + explode: false + schema: + type: string + - name: filename + in: path + description: The image filename + required: true + style: simple + explode: false + schema: + type: string + responses: + '200': + description: The thumbnail representation of this image + content: + image/jpeg: + schema: + type: string + format: binary + '/candidates/{candid}/{visit}/images/{filename}/headers': + get: + tags: + - Images + summary: Get the summary headers of an image + parameters: + - name: candid + in: path + description: The candidate identifier + required: true + style: simple + explode: false + schema: + type: string + - name: visit + in: path + description: The visit label + required: true + style: simple + explode: false + schema: + type: string + - name: filename + in: path + description: The image filename + required: true + style: simple + explode: false + schema: + type: string + responses: + '200': + description: The images summary headers + content: + application/json: + schema: + $ref: '#/components/schemas/ImageSummaryHeaders' + '/candidates/{candid}/{visit}/images/{filename}/headers/full': + get: + tags: + - Images + summary: Get all headers of an image + parameters: + - name: candid + in: path + description: The candidate identifier + required: true + style: simple + explode: false + schema: + type: string + - name: visit + in: path + description: The visit label + required: true + style: simple + explode: false + schema: + type: string + - name: filename + in: path + description: The image filename + required: true + style: simple + explode: false + schema: + type: string + responses: + '200': + description: The images complete headers + content: + application/json: + schema: + $ref: '#/components/schemas/ImageHeaders' + '/candidates/{candid}/{visit}/images/{filename}/headers/{headername}': + get: + tags: + - Images + summary: Get a specific header of an image + parameters: + - name: candid + in: path + description: The candidate identifier + required: true + style: simple + explode: false + schema: + type: string + - name: visit + in: path + description: The visit label + required: true + style: simple + explode: false + schema: + type: string + - name: filename + in: path + description: The image filename + required: true + style: simple + explode: false + schema: + type: string + - name: headername + in: path + description: The requested header name + required: true + style: simple + explode: false + schema: + type: string + responses: + '200': + description: The header value + content: + application/json: + schema: + $ref: '#/components/schemas/inline_response_200_5' + '/candidates/{candid}/{visit}/qc/imaging': + get: + tags: + - Visit + summary: Get the imaging qc values of a visit + parameters: + - name: candid + in: path + description: The candidate identifier + required: true + style: simple + explode: false + schema: + type: string + - name: visit + in: path + description: The visit label + required: true + style: simple + explode: false + schema: + type: string + responses: + '200': + description: The images list + content: + application/json: + schema: + $ref: '#/components/schemas/ImagingQc' + put: + tags: + - Visit + parameters: + - name: candid + in: path + description: The candidate identifier + required: true + style: simple + explode: false + schema: + type: string + - name: visit + in: path + description: The visit label + required: true + style: simple + explode: false + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/ImagingQc' + responses: + '204': + description: Qc value updated + '/candidates/{candid}/{visit}/dicoms': + get: + tags: + - Dicoms + summary: List all dicoms for that timepoint + parameters: + - name: candid + in: path + description: ID of the candidate + required: true + style: simple + explode: false + schema: + type: string + - name: visit + in: path + description: This timepoint visit label + required: true + style: simple + explode: false + schema: + type: string + responses: + '200': + description: An object containing the list of dicoms + content: + application/json: + schema: + $ref: '#/components/schemas/Dicoms' + + '/candidates/{candid}/{visit}/dicoms/{tarname}': + get: + tags: + - Dicoms + summary: Get the dicom file + parameters: + - name: candid + in: path + description: ID of the candidate + required: true + style: simple + explode: false + schema: + type: string + - name: visit + in: path + description: This timepoint visit label + required: true + style: simple + explode: false + schema: + type: string + - name: tarname + in: path + description: The dicom filename + required: true + style: simple + explode: false + schema: + type: string + responses: + '200': + description: The dicom file content + content: + application/octet-stream: + schema: + type: string + format: binary + + '/candidates/{candid}/{visit}/recordings': + get: + tags: + - Electrophysiology + summary: Get the recording files for that visit + parameters: + - name: candid + in: path + description: ID of the candidate + required: true + style: simple + explode: false + schema: + type: string + - name: visit + in: path + description: This timepoint visit label + required: true + style: simple + explode: false + schema: + type: string + responses: + '200': + description: Recordings which have been acquired for that visit + content: + application/json: + schema: + $ref: '#/components/schemas/Recordings' + '/candidates/{candid}/{visit}/recordings/{filename}': + get: + tags: + - Electrophysiology + summary: Get a recording file + parameters: + - name: candid + in: path + description: ID of the candidate + required: true + schema: + type: string + - name: visit + in: path + description: This timepoint visit label + required: true + schema: + type: string + - name: filename + in: path + description: The recording filename + required: true + schema: + type: string + responses: + '200': + description: The recording file content + content: + application/octet-stream: + schema: + type: string + format: binary + '/candidates/{candid}/{visit}/recordings/{filename}/metadata': + get: + tags: + - Electrophysiology + summary: Get a recording file metadata + parameters: + - name: candid + in: path + description: ID of the candidate + required: true + schema: + type: string + - name: visit + in: path + description: This timepoint visit label + required: true + schema: + type: string + - name: filename + in: path + description: The recording filename + required: true + schema: + type: string + responses: + '200': + description: All metadata about that recording + content: + application/json: + schema: + $ref: '#/components/schemas/RecordingMetadata' + '/candidates/{candid}/{visit}/recordings/{filename}/metadata/{headername}': + get: + tags: + - Electrophysiology + summary: Get a specific metadata from a recording file + parameters: + - name: candid + in: path + description: ID of the candidate + required: true + schema: + type: string + - name: visit + in: path + description: This timepoint visit label + required: true + schema: + type: string + - name: filename + in: path + description: The recording filename + required: true + schema: + type: string + - name: headername + in: path + description: The metadata header name + required: true + schema: + type: string + responses: + '200': + description: The metadata header value + content: + application/json: + schema: + $ref: '#/components/schemas/RecordingMetadataSpecific' + '/candidates/{candid}/{visit}/recordings/{filename}/channels': + get: + tags: + - Electrophysiology + summary: Get a recording file channels + parameters: + - name: candid + in: path + description: ID of the candidate + required: true + schema: + type: string + - name: visit + in: path + description: This timepoint visit label + required: true + schema: + type: string + - name: filename + in: path + description: The recording filename + required: true + schema: + type: string + responses: + '200': + description: All metadata about that recording + content: + application/json: + schema: + $ref: '#/components/schemas/RecordingChannels' + '/candidates/{candid}/{visit}/recordings/{filename}/electrodes': + get: + tags: + - Electrophysiology + summary: Get a recording file electrodes + parameters: + - name: candid + in: path + description: ID of the candidate + required: true + schema: + type: string + - name: visit + in: path + description: This timepoint visit label + required: true + schema: + type: string + - name: filename + in: path + description: The recording filename + required: true + schema: + type: string + responses: + '200': + description: All metadata about that recording + content: + application/json: + schema: + $ref: '#/components/schemas/RecordingElectrodes' + '/candidates/{candid}/{visit}/recordings/{filename}/events': + get: + tags: + - Electrophysiology + summary: Get a recording file events + parameters: + - name: candid + in: path + description: ID of the candidate + required: true + schema: + type: string + - name: visit + in: path + description: This timepoint visit label + required: true + schema: + type: string + - name: filename + in: path + description: The recording filename + required: true + schema: + type: string + responses: + '200': + description: All metadata about that recording + content: + application/json: + schema: + $ref: '#/components/schemas/RecordingEvents' + '/candidates/{candid}/{visit}/recordings/{filename}/bidsfiles/archive': + get: + tags: + - Electrophysiology + summary: Get a recording archive with all associated files + parameters: + - name: candid + in: path + description: ID of the candidate + required: true + schema: + type: string + - name: visit + in: path + description: This timepoint visit label + required: true + schema: + type: string + - name: filename + in: path + description: The recording filename + required: true + schema: + type: string + responses: + '200': + description: The recording archive + content: + application/octet-stream: + schema: + type: string + format: binary + '/candidates/{candid}/{visit}/recordings/{filename}/bidsfiles/channels': + get: + tags: + - Electrophysiology + summary: Get a recording channels in BIDS format + parameters: + - name: candid + in: path + description: ID of the candidate + required: true + schema: + type: string + - name: visit + in: path + description: This timepoint visit label + required: true + schema: + type: string + - name: filename + in: path + description: The recording filename + required: true + schema: + type: string + responses: + '200': + description: The recording channels in BIDS format + content: + application/octet-stream: + schema: + type: string + format: binary + '/candidates/{candid}/{visit}/recordings/{filename}/bidsfiles/electrodes': + get: + tags: + - Electrophysiology + summary: Get a recording electrodes in BIDS format + parameters: + - name: candid + in: path + description: ID of the candidate + required: true + schema: + type: string + - name: visit + in: path + description: This timepoint visit label + required: true + schema: + type: string + - name: filename + in: path + description: The recording filename + required: true + schema: + type: string + responses: + '200': + description: The recording electrodes in BIDS format + content: + application/octet-stream: + schema: + type: string + format: binary + '/candidates/{candid}/{visit}/recordings/{filename}/bidsfiles/events': + get: + tags: + - Electrophysiology + summary: Get a recording events in BIDS format + parameters: + - name: candid + in: path + description: ID of the candidate + required: true + schema: + type: string + - name: visit + in: path + description: This timepoint visit label + required: true + schema: + type: string + - name: filename + in: path + description: The recording filename + required: true + schema: + type: string + responses: + '200': + description: The recording events in BIDS format + content: + application/octet-stream: + schema: + type: string + format: binary +components: + schemas: + Project: + type: object + properties: + Meta: + $ref: '#/components/schemas/Project_Meta' + Visits: + type: array + items: + type: string + Instruments: + type: array + items: + type: string + Candidates: + type: array + items: + type: string + ProjectCandidates: + type: object + properties: + Meta: + $ref: '#/components/schemas/Project_Meta' + Candidates: + type: array + items: + type: string + ProjectImages: + type: object + properties: + Meta: + $ref: '#/components/schemas/Project_Meta' + Images: + type: array + items: + $ref: '#/components/schemas/ProjectImage' + ProjectImage: + type: object + properties: + Candidate: + type: string + PSCID: + type: string + Visit: + type: string + Visit_date: + type: string + Site: + type: string + ScanType: + type: string + QC_status: + type: string + enum: + - Pass + - Fail + - 'null' + Selected: + type: string + enum: + - 'true' + - 'false' + - 'null' + Link: + type: string + InsertTime: + type: string + ProjectInstruments: + type: object + properties: + Meta: + $ref: '#/components/schemas/Project_Meta' + Instruments: + type: object + ProjectInstrument: + type: object + properties: + Meta: + $ref: '#/components/schemas/ProjectInstrument_Meta' + LorisFormElement: + type: object + properties: + Type: + type: string + enum: + - advcheckbox + - date + - file + - group + - header + - hidden + - link + - page + - password + - radio + - select + - static + - submit + - text + - textarea + - time + Name: + type: string + Description: + type: string + Options: + $ref: '#/components/schemas/LorisFormElement_Options' + RequireResponse: + type: boolean + ProjectVisits: + type: object + properties: + Meta: + $ref: '#/components/schemas/Project_Meta' + Visits: + type: array + items: + type: string + ProjectRecordings: + type: object + properties: + Recordings: + type: array + items: + $ref: '#/components/schemas/ProjectRecording' + ProjectRecording: + type: object + properties: + Candidate: + type: string + PSCID: + type: string + Visit: + type: string + Visit_date: + type: string + Site: + type: string + File: + type: string + Modality: + type: string + InsertTime: + type: string + Link: + type: string + Candidates: + type: object + properties: + Candidates: + $ref: '#/components/schemas/Candidate' + Candidate: + type: object + properties: + CandID: + type: string + Project: + type: string + PSCID: + type: string + Site: + type: string + EDC: + type: string + DoB: + type: string + Sex: + type: string + enum: + - Female + - Male + NewCandidate: + type: object + properties: + Candidate: + $ref: '#/components/schemas/NewCandidate_Candidate' + VisitMeta: + type: object + properties: + Meta: + $ref: '#/components/schemas/VisitMetaFields' + VisitMetaFields: + type: object + properties: + CandID: + type: string + Visit: + type: string + Site: + type: string + Battery: + type: string + Project: + type: string + VisitStages: + type: object + properties: + Stages: + type: object + anyOf: + - $ref: '#/components/schemas/InstrumentScreening' + - $ref: '#/components/schemas/InstrumentVisit' + - $ref: '#/components/schemas/InstrumentApproval' + VisitStage: + type: object + properties: + Date: + type: string + Status: + type: string + enum: + - Pass + - Failure + - Withdrawal + - In Progress + VisitPatchFields: + type: object + allOf: + - $ref: '#/components/schemas/VisitMetaFields' + - $ref: '#/components/schemas/VisitStages' + InstrumentScreening: + type: object + properties: + Screening: + $ref: '#/components/schemas/VisitStage' + InstrumentVisit: + type: object + properties: + Visit: + $ref: '#/components/schemas/VisitStage' + InstrumentApproval: + type: object + properties: + Approval: + $ref: '#/components/schemas/VisitStage' + TimepointMeta: + type: object + properties: + CandID: + type: string + Visit: + type: string + Instrument: + type: object + properties: + Meta: + $ref: '#/components/schemas/InstrumentMeta' + $InstrumentShortName: + $ref: '#/components/schemas/InstrumentData' + InstrumentMeta: + type: object + properties: + Instrument: + type: string + Visit: + type: string + Candidate: + type: string + DDE: + type: boolean + InstrumentData: + type: object + additionalProperties: + type: string + InstrumentFlags: + type: object + properties: + Meta: + $ref: '#/components/schemas/InstrumentMeta' + Flags: + $ref: '#/components/schemas/InstrumentFlags_Flags' + Images: + type: object + properties: + Meta: + $ref: '#/components/schemas/TimepointMeta' + Files: + type: array + items: + $ref: '#/components/schemas/Images_Files' + ImagingQc: + type: object + properties: + Meta: + $ref: '#/components/schemas/TimepointMeta' + SessionQC: + type: string + enum: + - Pass + - Fail + Pending: + type: boolean + ImageQc: + type: object + properties: + Meta: + $ref: '#/components/schemas/ImageMeta' + QC: + type: string + enum: + - Pass + - Fail + Selected: + type: boolean + Caveats: + type: array + items: + $ref: '#/components/schemas/ImageCaveat' + ImageCaveat: + type: object + properties: + Severity: + type: string + Header: + type: string + Value: + type: string + ValidRange: + type: string + ValidRegex: + type: string + ImageMeta: + type: object + properties: + CandID: + type: string + Visit: + type: string + File: + type: string + FormatBrainbrowser: + type: object + properties: + xspace: + $ref: '#/components/schemas/BrainbrowserSpace' + yspace: + $ref: '#/components/schemas/BrainbrowserSpace' + zspace: + $ref: '#/components/schemas/BrainbrowserSpace' + order: + type: string + enum: + - xspace + - zspace + - yspace + BrainbrowserSpace: + type: object + properties: + start: + type: string + space_length: + type: string + step: + type: string + ImageSummaryHeaders: + type: object + properties: + Meta: + $ref: '#/components/schemas/ImageMeta' + Physical: + $ref: '#/components/schemas/ImageSummaryHeaders_Physical' + Description: + $ref: '#/components/schemas/ImageSummaryHeaders_Description' + Dimensions: + $ref: '#/components/schemas/ImageSummaryHeaders_Dimensions' + ScannerInfo: + $ref: '#/components/schemas/ImageSummaryHeaders_ScannerInfo' + DimensionHeaders: + type: object + properties: + Lenght: + type: string + StepSize: + type: string + ImageHeaders: + type: object + properties: + Meta: + $ref: '#/components/schemas/ImageMeta' + Headers: + type: object + additionalProperties: + type: string + Dicoms: + type: object + properties: + Meta: + $ref: '#/components/schemas/Dicoms_Meta' + DicomTars: + type: array + items: + $ref: '#/components/schemas/DicomTar' + DicomTar: + type: object + properties: + Tarname: + type: string + SeriesInfo: + type: array + items: + $ref: '#/components/schemas/SeriesInfo' + SeriesInfo: + type: object + properties: + SeriesDescription: + type: string + SeriesNumber: + type: string + EchoTime: + type: string + RepetitionTime: + type: string + InversionTime: + type: string + SliceThickness: + type: string + Modality: + type: string + enum: + - MR + - PT + SeriesUID: + type: string + Recordings: + type: object + properties: + Meta: + $ref: '#/components/schemas/TimepointMeta' + Files: + type: array + items: + $ref: '#/components/schemas/Recording' + Recording: + type: object + properties: + OutputType: + type: string + Filename: + type: string + AcquisitionModality: + type: string + + Error: + required: + - error + type: object + properties: + error: + type: string + description: The error message + body: + required: + - password + - username + type: object + properties: + username: + type: string + password: + type: string + inline_response_200: + type: object + properties: + token: + type: string + inline_response_200_1: + type: object + properties: + Projects: + type: object + inline_response_200_2: + type: object + properties: + Candidate: + $ref: '#/components/schemas/Candidate' + Visits: + type: array + items: + type: string + inline_response_200_3: + allOf: + - $ref: '#/components/schemas/VisitMeta' + - $ref: '#/components/schemas/VisitStages' + inline_response_409: + type: object + properties: + error: + type: string + inline_response_200_4: + type: object + properties: + Meta: + $ref: '#/components/schemas/TimepointMeta' + Instruments: + type: array + items: + type: string + inline_response_200_5: + type: object + properties: + Meta: + $ref: '#/components/schemas/inline_response_200_5_Meta' + Value: + type: string + + Project_Meta: + type: object + properties: + Project: + type: string + ProjectInstrument_Meta: + type: object + properties: + InstrumentVersion: + type: string + InstrumentFormatVersion: + type: string + ShortName: + type: string + LongName: + type: string + IncludeMetaDataFields: + type: string + Elements: + type: array + items: + $ref: '#/components/schemas/LorisFormElement' + LorisFormElement_Options: + type: object + properties: + Values: + type: object + additionalProperties: + type: string + NewCandidate_Candidate: + type: object + properties: + Project: + type: string + PSCID: + type: string + Site: + type: string + EDC: + type: string + DoB: + type: string + Sex: + type: string + enum: + - Female + - Male + InstrumentFlags_Flags: + type: object + properties: + Data_entry: + type: string + enum: + - In Progress + - Complete + Administration: + type: string + enum: + - None + - Partial + - All + Validity: + type: string + enum: + - Questionable + - Invalid + - Valid + Images_Files: + type: object + properties: + OutputType: + type: string + Filename: + type: string + AcquisitionType: + type: string + ImageSummaryHeaders_Physical: + type: object + properties: + TE: + type: string + TR: + type: string + TI: + type: string + SliceThickness: + type: string + ImageSummaryHeaders_Description: + type: object + properties: + SeriesName: + type: string + SeriesDescription: + type: string + ImageSummaryHeaders_Dimensions: + type: object + properties: + XSpace: + $ref: '#/components/schemas/DimensionHeaders' + YSpace: + $ref: '#/components/schemas/DimensionHeaders' + ZSpace: + $ref: '#/components/schemas/DimensionHeaders' + TimeDimension: + $ref: '#/components/schemas/DimensionHeaders' + ImageSummaryHeaders_ScannerInfo: + type: object + properties: + Manufacturer: + type: string + Model: + type: string + SoftwareVersion: + type: string + SerialNumber: + type: string + FieldStrength: + type: string + Dicoms_Meta: + type: object + properties: + CandID: + type: string + Visit: + type: string + inline_response_200_5_Meta: + type: object + properties: + CandID: + type: string + Visit: + type: string + File: + type: string + Header: + type: string + RecordingMeta: + type: object + properties: + CandID: + type: string + Visit: + type: string + File: + type: string + RecordingMetadata: + type: object + properties: + Meta: + $ref: '#/components/schemas/RecordingMeta' + Data: + type: object + additionalProperties: + type: string + RecordingMetadataSpecific: + type: object + properties: + Meta: + type: object + properties: + CandID: + type: string + Visit: + type: string + Filename: + type: string + Header: + type: string + Value: + type: string + RecordingChannels: + type: object + properties: + Meta: + $ref: '#/components/schemas/RecordingMeta' + Channels: + $ref: '#/components/schemas/RecordingChannel' + + RecordingChannel: + type: object + properties: + ChannelName: + type: string + ChannelDescription: + type: string + ChannelType: + type: string + ChannelTypeDescription: + type: string + ChannelStatus: + type: string + StatusDescription: + type: string + SamplingFrequency: + type: number + LowCutoff: + type: string + HighCutoff: + type: string + ManualFlag: + type: string + Notch: + type: string + Reference: + type: string + Unit: + type: string + ChannelFilePath: + type: string + + RecordingElectrodes: + type: object + properties: + Meta: + $ref: '#/components/schemas/RecordingMeta' + Electrodes: + type: array + items: + $ref: '#/components/schemas/RecordingElectrode' + + RecordingElectrode: + type: object + properties: + ElectrodeName: + type: string + ElectrodeType: + type: string + ElectrodeMaterial: + type: string + X: + type: string + Y: + type: string + Z: + type: string + Impedance: + type: string + ElectrodeFilePath: + type: string + + RecordingEvents: + type: object + properties: + Meta: + $ref: '#/components/schemas/RecordingMeta' + TaskEvents: + type: array + items: + $ref: '#/components/schemas/RecordingTaskEvent' + + RecordingTaskEvent: + type: object + properties: + Onset: + type: string + Duration: + type: number + EventCode: + type: string + EventSample: + type: string + EventType: + type: string + TrialType: + type: string + ResponseTime: + type: string + EventFilePath: + type: string + + responses: + InternalServerError: + description: 500 Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + securitySchemes: + ApiKeyAuth: + type: apiKey + name: Authorization + in: header + diff --git a/modules/api/static/schema.yml b/modules/api/static/schema.yml index e1fe1a2848c..62c7b522ab1 100644 --- a/modules/api/static/schema.yml +++ b/modules/api/static/schema.yml @@ -334,36 +334,6 @@ paths: application/json: schema: $ref: '#/components/schemas/inline_response_409' - patch: - tags: - - Visit - summary: Updates a timepoint. Providing a "Visit" stage with "In Progress" as status will attemp to start the visiti (start next stage). - parameters: - - name: candid - in: path - description: The candidate identifier - required: true - style: simple - explode: false - schema: - type: string - - name: visit - in: path - description: The visit label - required: true - style: simple - explode: false - schema: - type: string - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/VisitPatchFields' - responses: - '200': - description: The visit was updated successfully - '/candidates/{candid}/{visit}/instruments': get: tags: @@ -1918,11 +1888,6 @@ components: - Failure - Withdrawal - In Progress - VisitPatchFields: - type: object - allOf: - - $ref: '#/components/schemas/VisitMetaFields' - - $ref: '#/components/schemas/VisitStages' InstrumentScreening: type: object properties: From 25c6d86a9af753ba4e3c925c586e5261e23d8d31 Mon Sep 17 00:00:00 2001 From: Laetitia Fesselier Date: Wed, 29 Sep 2021 16:02:03 -0400 Subject: [PATCH 5/7] Documentation improvements --- CHANGELOG.md | 2 +- modules/api/docs/LorisRESTAPI_v0.0.4-dev.md | 16 +++------------- 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 63b7b2efbe8..f50bceaaa85 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,7 +34,7 @@ requesting a new account and will be displayed in the User Accounts module (PR # #### API - Creation of a new version of the API under development (v0.0.4-dev) (PR #6944) - Deletion of support for the oldest version of the API (v0.0.2) (PR #6944) -- Addition of a PATCH request for /candidates/$CandID/$VisitLabel to start next stage when the payload contains a "Visit" stage with "In Progress" as Status, when the current status of the Visit stage is "Not Started". +- Addition of a PATCH request for /candidates/$CandID/$VisitLabel to start next stage when the payload contains a "Visit" stage with "In Progress" as Status, when the current status of the Visit stage is "Not Started". (PR #7479) #### Candidate Parameters - Consents may now be grouped in UI of consent tab (PR #6042, PR #6044) #### API Documentation (**New Module**) diff --git a/modules/api/docs/LorisRESTAPI_v0.0.4-dev.md b/modules/api/docs/LorisRESTAPI_v0.0.4-dev.md index ed1adcc0953..b1435b61fb6 100644 --- a/modules/api/docs/LorisRESTAPI_v0.0.4-dev.md +++ b/modules/api/docs/LorisRESTAPI_v0.0.4-dev.md @@ -428,25 +428,15 @@ A PATCH request of the form: "Battery" : 'Stale', "Project" : 'Pumpernickel', "Stages" : { - "Screening" : { - "Date" : "YYYY-MM-DD", - "Status" : "Pass|Failure|Withdrawal|In Progress" - }, "Visit" : { "Date" : "YYYY-MM-DD", - "Status" : "Pass|Failure|Withdrawal|In Progress" - }, - "Approval" : { - "Date" : "YYYY-MM-DD", - "Status" : "Pass|Failure|Withdrawal|In Progress" + "Status" : "In Progress" } } } ``` -will update the stages date and status. - -At this moment, we only support requests for the Visit stage with the In progress status, -to start the next stage if the Visit stage is 'Not Started' and the CandID and Visit label provided are valid. +will update the Visit stage date and status and start the next stage if the Visit stage +is 'Not Started' and the CandID and Visit label provided are valid. It will return a 404 Not Found if the visit label does not exist for this candidate (as well as anything under the /candidates/$CandID/$VisitLabel hierarchy) From 98c9fab1eb481f4ba844b01604e71164d9d528fd Mon Sep 17 00:00:00 2001 From: Laetitia Fesselier Date: Tue, 5 Oct 2021 15:01:48 -0400 Subject: [PATCH 6/7] Update modules/api/static/schema-v0.0.4-dev.yml Co-authored-by: Xavier Lecours --- modules/api/static/schema-v0.0.4-dev.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/api/static/schema-v0.0.4-dev.yml b/modules/api/static/schema-v0.0.4-dev.yml index 026ff842342..55a2c2a17ac 100644 --- a/modules/api/static/schema-v0.0.4-dev.yml +++ b/modules/api/static/schema-v0.0.4-dev.yml @@ -1922,7 +1922,10 @@ components: type: object allOf: - $ref: '#/components/schemas/VisitMetaFields' - - $ref: '#/components/schemas/VisitStages' + - type: object + properties: + Stages: + $ref: '#/components/schemas/InstrumentVisit' InstrumentScreening: type: object properties: From c7a83f7a6d5bbe7d0d7e591476db5b04423c7036 Mon Sep 17 00:00:00 2001 From: xlecours Date: Thu, 21 Oct 2021 14:48:34 -0400 Subject: [PATCH 7/7] pruning the schema file to highlight the new feature removing checks and handling of other stages Adding a NoContent response --- .../candidate/visit/visit_0_0_4_dev.class.inc | 153 +- modules/api/static/schema-v0.0.4-dev.yml | 2425 +---------------- src/Http/Response/JSON/NoContent.php | 40 + 3 files changed, 100 insertions(+), 2518 deletions(-) create mode 100644 src/Http/Response/JSON/NoContent.php diff --git a/modules/api/php/endpoints/candidate/visit/visit_0_0_4_dev.class.inc b/modules/api/php/endpoints/candidate/visit/visit_0_0_4_dev.class.inc index a69ef8b2fe8..20a74962f71 100644 --- a/modules/api/php/endpoints/candidate/visit/visit_0_0_4_dev.class.inc +++ b/modules/api/php/endpoints/candidate/visit/visit_0_0_4_dev.class.inc @@ -383,126 +383,63 @@ class Visit_0_0_4_Dev extends Endpoint implements \LORIS\Middleware\ETagCalculat ); } - if (!isset($data['Stages'])) { + if ($this->_visit->getCurrentStage() !== 'Not Started') { + return new \LORIS\Http\Response\JSON\Conflict( + "This visit is already started" + ); + } + + try { + $date = new \DateTime($data['Stages']['Visit']['Date'] ?? 0); + } catch (\Exception $e) { return new \LORIS\Http\Response\JSON\BadRequest( - 'There is no stage data specified' + 'Stages.Visit.Date is invalid or missing' ); } + // TODO :: Check for min and max year - $allowedStages = ['Screening', 'Visit', 'Approval']; - $allowedStatuses = ['Pass', 'Failure', 'Withdrawal', 'In Progress']; + $status = $data['Stages']['Visit']['Status'] ?? ''; + if ($status != 'In Progress') { + return new \LORIS\Http\Response\JSON\BadRequest( + 'Stages.Visit.Status invalid or missing' + ); + } - // loop on all stages to validate - foreach ($data['Stages'] as $stage => $stageData) { - if (!in_array($stage, $allowedStages)) { - return new \LORIS\Http\Response\JSON\BadRequest( - "Stage $stage is not valid" - ); - } + $newStage = $this->_visit->getNextStage(); - // TODO: Implement and remove - if (in_array($stage, ['Screening', 'Approval'])) { - return new \LORIS\Http\Response\JSON\NotImplemented( - "Stage $stage is not implemented yet" - ); - } + $this->_visit->startStage($newStage); - if (!isset($stageData['Status'])) { - return new \LORIS\Http\Response\JSON\BadRequest( - "There is no $stage stage status specified" - ); - } + // set the date for the new stage + $this->_visit->setData(["Date_$newStage" => $date->format('Y-m-d')]); - if (!in_array($stageData['Status'], $allowedStatuses)) { - return new \LORIS\Http\Response\JSON\BadRequest( - "Stage $stage status {$stageData['Status']} is not valid" - ); - } + // TODO :: Add support for scan done - // TODO: Implement and remove - if (in_array($stageData['Status'], ['Pass', 'Failure', 'Withdrawal'])) { - return new \LORIS\Http\Response\JSON\NotImplemented( - "Stage $stage status transition - {$stageData['Status']} is not implemented yet" - ); - } + // create a new battery object && new battery + $candidate = \Candidate::singleton($this->_visit->getCandID()); + $visitLabel = $this->_visit->getVisitLabel(); - if (!isset($stageData['Date'])) { - return new \LORIS\Http\Response\JSON\BadRequest( - "There is no $stage stage date specified" - ); - } - - $stageData['Date'] = \DateTime::createFromFormat( - 'Y-m-d', - $stageData['Date'] - ); - if (!$stageData['Date']) { - return new \LORIS\Http\Response\JSON\BadRequest( - "The stage date specified for $stage is invalid. - Date must be of the form YYYY-MM-DD" - ); - } + // First visit ? + $firstVisit = false; + try { + $firstVisit = ($candidate->getFirstVisit() == $visitLabel); + } catch (\LorisException $e) { } - // Passed this point, the request should be valid - // Loop again and process the request - foreach ($data['Stages'] as $stage => $stageData) { - switch ($stage) { - // Only Visit | In progress is supported at the moment - case 'Visit': - switch ($stageData['Status']) { - case 'In Progress': - if ($this->_visit->getCurrentStage() !== 'Not Started') { - return new \LORIS\Http\Response\JSON\Conflict( - "This visit is already started" - ); - } - - $newStage = $this->_visit->getNextStage(); - if (!$newStage) { - return new \LORIS\Http\Response\JSON\Conflict( - "Next stage is null" - ); - } - - // start that stage - $this->_visit->startStage($newStage); - - // set the date for the new stage - $this->_visit->setData(["Date_$newStage" => $stageData["Date"]]); - - // create a new battery object && new battery - $candidate = \Candidate::singleton($this->_visit->getCandID()); - $visitLabel = $this->_visit->getVisitLabel(); - - // First visit ? - $firstVisit = false; - try { - if ($candidate->getFirstVisit() == $visitLabel) { - $firstVisit = true; - } - } catch (\LorisException $e) { - } - - $battery = new \NDB_BVL_Battery; - // select a specific time point (sessionID) for the battery - $battery->selectBattery($this->_visit->getSessionID()); - - // add instruments to the time point - $battery->createBattery( - $request->getAttribute("loris"), - $this->_visit->getSubprojectID(), - $newStage, - $this->_visit->getVisitLabel(), - $this->_visit->getCenterID(), - $firstVisit - ); - - return new \LORIS\Http\Response\JSON\OK(); - } - } - } + $battery = new \NDB_BVL_Battery; + // select a specific time point (sessionID) for the battery + $battery->selectBattery($this->_visit->getSessionID()); + + // add instruments to the time point + $battery->createBattery( + $request->getAttribute("loris"), + $this->_visit->getSubprojectID(), + $newStage, + $this->_visit->getVisitLabel(), + $this->_visit->getCenterID(), + $firstVisit + ); + + return new \LORIS\Http\Response\JSON\NoContent(); } /** diff --git a/modules/api/static/schema-v0.0.4-dev.yml b/modules/api/static/schema-v0.0.4-dev.yml index 55a2c2a17ac..8a8dd80ab01 100644 --- a/modules/api/static/schema-v0.0.4-dev.yml +++ b/modules/api/static/schema-v0.0.4-dev.yml @@ -8,332 +8,13 @@ info: license: name: 'GNU Public License, Version 3' url: 'https://opensource.org/licenses/GPL-3.0' - version: 0.0.3 + version: 0.0.4-dev servers: - - url: /api/v0.0.3 + - url: /api/v0.0.4-dev security: - ApiKeyAuth: [] paths: - /login: - post: - tags: - - Login - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/body' - responses: - '200': - description: The JWT - content: - application/json: - schema: - $ref: '#/components/schemas/inline_response_200' - /projects: - get: - tags: - - Projects - summary: List all projects - responses: - '200': - description: An object with projects as properties - content: - application/json: - schema: - $ref: '#/components/schemas/inline_response_200_1' - '/projects/{project}': - get: - tags: - - Projects - summary: Get that project descriptor - parameters: - - name: project - in: path - description: The project name - required: true - style: simple - explode: false - schema: - type: string - responses: - '200': - description: An object containing the project descriptor - content: - application/json: - schema: - $ref: '#/components/schemas/Project' - '/projects/{project}/candidates': - get: - tags: - - Projects - summary: List all candidates of that project - parameters: - - name: project - in: path - description: The project name - required: true - style: simple - explode: false - schema: - type: string - responses: - '200': - description: An object containing the list of candidates - content: - application/json: - schema: - $ref: '#/components/schemas/ProjectCandidates' - '/projects/{project}/images': - get: - tags: - - Projects - summary: List all images of that project - parameters: - - name: project - in: path - description: The project name - required: true - style: simple - explode: false - schema: - type: string - - name: since - in: query - description: A date - required: false - style: form - explode: true - schema: - type: string - responses: - '200': - description: An object containing the list of image descriptors - content: - application/json: - schema: - $ref: '#/components/schemas/ProjectImages' - '/projects/{project}/instruments': - get: - tags: - - Projects - summary: List all instruments of that project - parameters: - - name: project - in: path - description: The project name - required: true - style: simple - explode: false - schema: - type: string - responses: - '200': - description: An object containing the list of instrument summary descriptors - content: - application/json: - schema: - $ref: '#/components/schemas/ProjectInstruments' - '/projects/{project}/instruments/{instrument}': - get: - tags: - - Projects - summary: Get an instrument descriptor in that project - parameters: - - name: project - in: path - description: The project name - required: true - style: simple - explode: false - schema: - type: string - - name: instrument - in: path - description: The instrument short name - required: true - style: simple - explode: false - schema: - type: string - responses: - '200': - description: An JSON representation of the instrument form - content: - application/json: - schema: - $ref: '#/components/schemas/ProjectInstrument' - '/projects/{project}/visits': - get: - tags: - - Projects - summary: List all existing visit label of that project - parameters: - - name: project - in: path - description: The project name - required: true - style: simple - explode: false - schema: - type: string - responses: - '200': - description: An object containing the list of instrument summary descriptors - content: - application/json: - schema: - $ref: '#/components/schemas/ProjectVisits' - '/projects/{project}/recordings': - get: - tags: - - Projects - summary: List all existing visit label of that project - parameters: - - name: project - in: path - description: The project name - required: true - style: simple - explode: false - schema: - type: string - responses: - '200': - description: An object containing the list of instrument summary descriptors - content: - application/json: - schema: - $ref: '#/components/schemas/ProjectRecordings' - /candidates: - get: - tags: - - Candidates - summary: List all candidates in this LORIS instance - responses: - '200': - description: An object containing the list of instrument summary descriptors - content: - application/json: - schema: - $ref: '#/components/schemas/Candidates' - post: - tags: - - Candidates - summary: Create a candidate - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/NewCandidate' - responses: - '201': - description: The new candidate - content: - application/json: - schema: - $ref: '#/components/schemas/Candidate' - '/candidates/{candid}': - get: - tags: - - Candidates - summary: Get a JSON object representing that candidate - parameters: - - name: candid - in: path - description: The candidate identifier - required: true - style: simple - explode: false - schema: - type: string - responses: - '200': - description: The new candidate - content: - application/json: - schema: - $ref: '#/components/schemas/inline_response_200_2' '/candidates/{candid}/{visit}': - get: - tags: - - Visit - summary: Get a visit descriptor for that candidate - parameters: - - name: candid - in: path - description: The candidate identifier - required: true - style: simple - explode: false - schema: - type: string - - name: visit - in: path - description: The visit label - required: true - style: simple - explode: false - schema: - type: string - responses: - '200': - description: The new candidate - content: - application/json: - schema: - $ref: '#/components/schemas/inline_response_200_3' - put: - tags: - - Visit - summary: Add a timepoint to that candidate - parameters: - - name: candid - in: path - description: The candidate identifier - required: true - style: simple - explode: false - schema: - type: string - - name: visit - in: path - description: The visit label - required: true - style: simple - explode: false - schema: - type: string - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/VisitMetaFields' - responses: - '201': - description: The visit was created successfully - headers: - Content-Location: - description: The URL of the new visit. - style: simple - explode: false - schema: - type: string - '204': - description: The visit was updated successfully. - headers: - Content-Location: - description: The URL of the new visit. - style: simple - explode: false - schema: - type: string - '409': - description: The visit is already started. It can't be updated. - content: - application/json: - schema: - $ref: '#/components/schemas/inline_response_409' patch: tags: - Visit @@ -354,1536 +35,17 @@ paths: style: simple explode: false schema: - type: string - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/VisitPatchFields' - responses: - '200': - description: The visit stage was updated successfully - - '/candidates/{candid}/{visit}/instruments': - get: - tags: - - Instruments - summary: List all instruments of that timepoint - parameters: - - name: candid - in: path - description: The candidate identifier - required: true - style: simple - explode: false - schema: - type: string - - name: visit - in: path - description: The visit label - required: true - style: simple - explode: false - schema: - type: string - responses: - '200': - description: The list of instrument short names - content: - application/json: - schema: - $ref: '#/components/schemas/inline_response_200_4' - '/candidates/{candid}/{visit}/instruments/{instrument}': - get: - tags: - - Instruments - summary: Get an instrument data values - parameters: - - name: candid - in: path - description: The candidate identifier - required: true - style: simple - explode: false - schema: - type: string - - name: visit - in: path - description: The visit label - required: true - style: simple - explode: false - schema: - type: string - - name: instrument - in: path - description: The instrument short name - required: true - style: simple - explode: false - schema: - type: string - responses: - '200': - description: This instrument data values - content: - application/json: - schema: - $ref: '#/components/schemas/Instrument' - put: - tags: - - Instruments - summary: Set an instrument data values - parameters: - - name: candid - in: path - description: The candidate identifier - required: true - style: simple - explode: false - schema: - type: string - - name: visit - in: path - description: The visit label - required: true - style: simple - explode: false - schema: - type: string - - name: instrument - in: path - description: The instrument short name - required: true - style: simple - explode: false - schema: - type: string - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/Instrument' - responses: - '204': - description: This instrument data values have been saved - patch: - tags: - - Instruments - summary: Update some data values of an instrument - parameters: - - name: candid - in: path - description: The candidate identifier - required: true - style: simple - explode: false - schema: - type: string - - name: visit - in: path - description: The visit label - required: true - style: simple - explode: false - schema: - type: string - - name: instrument - in: path - description: The instrument short name - required: true - style: simple - explode: false - schema: - type: string - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/Instrument' - responses: - '204': - description: This instrument data values have been saved - '/candidates/{candid}/{visit}/instruments/{instrument}/flags': - get: - tags: - - Instruments - summary: Get an instrument flags values - parameters: - - name: candid - in: path - description: The candidate identifier - required: true - style: simple - explode: false - schema: - type: string - - name: visit - in: path - description: The visit label - required: true - style: simple - explode: false - schema: - type: string - - name: instrument - in: path - description: The instrument short name - required: true - style: simple - explode: false - schema: - type: string - responses: - '200': - description: This instrument flags values - content: - application/json: - schema: - $ref: '#/components/schemas/InstrumentFlags' - put: - tags: - - Instruments - summary: Set an instrument flags values - parameters: - - name: candid - in: path - description: The candidate identifier - required: true - style: simple - explode: false - schema: - type: string - - name: visit - in: path - description: The visit label - required: true - style: simple - explode: false - schema: - type: string - - name: instrument - in: path - description: The instrument short name - required: true - style: simple - explode: false - schema: - type: string - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/InstrumentFlags' - responses: - '501': - description: This instrument flags values - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - patch: - tags: - - Instruments - summary: Update some flags values of an instrument - parameters: - - name: candid - in: path - description: The candidate identifier - required: true - style: simple - explode: false - schema: - type: string - - name: visit - in: path - description: The visit label - required: true - style: simple - explode: false - schema: - type: string - - name: instrument - in: path - description: The instrument short name - required: true - style: simple - explode: false - schema: - type: string - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/InstrumentFlags' - responses: - '501': - description: This instrument flags values - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '/candidates/{candid}/{visit}/instruments/{instrument}/dde': - get: - tags: - - Instruments - summary: Get an instrument data values - parameters: - - name: candid - in: path - description: The candidate identifier - required: true - style: simple - explode: false - schema: - type: string - - name: visit - in: path - description: The visit label - required: true - style: simple - explode: false - schema: - type: string - - name: instrument - in: path - description: The instrument short name - required: true - style: simple - explode: false - schema: - type: string - responses: - '200': - description: This instrument data values - content: - application/json: - schema: - $ref: '#/components/schemas/Instrument' - put: - tags: - - Instruments - summary: Set an instrument data values - parameters: - - name: candid - in: path - description: The candidate identifier - required: true - style: simple - explode: false - schema: - type: string - - name: visit - in: path - description: The visit label - required: true - style: simple - explode: false - schema: - type: string - - name: instrument - in: path - description: The instrument short name - required: true - style: simple - explode: false - schema: - type: string - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/Instrument' - responses: - '204': - description: This instrument data values have been saved - patch: - tags: - - Instruments - summary: Update some data values of an instrument - parameters: - - name: candid - in: path - description: The candidate identifier - required: true - style: simple - explode: false - schema: - type: string - - name: visit - in: path - description: The visit label - required: true - style: simple - explode: false - schema: - type: string - - name: instrument - in: path - description: The instrument short name - required: true - style: simple - explode: false - schema: - type: string - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/Instrument' - responses: - '204': - description: This instrument data values have been saved - '/candidates/{candid}/{visit}/instruments/{instrument}/dde/flags': - get: - tags: - - Instruments - summary: Get an instrument flags values - parameters: - - name: candid - in: path - description: The candidate identifier - required: true - style: simple - explode: false - schema: - type: string - - name: visit - in: path - description: The visit label - required: true - style: simple - explode: false - schema: - type: string - - name: instrument - in: path - description: The instrument short name - required: true - style: simple - explode: false - schema: - type: string - responses: - '200': - description: This instrument flags values - content: - application/json: - schema: - $ref: '#/components/schemas/InstrumentFlags' - put: - tags: - - Instruments - summary: Set an instrument flags values - parameters: - - name: candid - in: path - description: The candidate identifier - required: true - style: simple - explode: false - schema: - type: string - - name: visit - in: path - description: The visit label - required: true - style: simple - explode: false - schema: - type: string - - name: instrument - in: path - description: The instrument short name - required: true - style: simple - explode: false - schema: - type: string - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/InstrumentFlags' - responses: - '501': - description: This instrument flags values - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - patch: - tags: - - Instruments - summary: Update some flags values of an instrument - parameters: - - name: candid - in: path - description: The candidate identifier - required: true - style: simple - explode: false - schema: - type: string - - name: visit - in: path - description: The visit label - required: true - style: simple - explode: false - schema: - type: string - - name: instrument - in: path - description: The instrument short name - required: true - style: simple - explode: false - schema: - type: string - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/InstrumentFlags' - responses: - '501': - description: This instrument flags values - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '/candidates/{candid}/{visit}/images': - get: - tags: - - Images - summary: List all images of a timepoint - parameters: - - name: candid - in: path - description: The candidate identifier - required: true - style: simple - explode: false - schema: - type: string - - name: visit - in: path - description: The visit label - required: true - style: simple - explode: false - schema: - type: string - responses: - '200': - description: The images list - content: - application/json: - schema: - $ref: '#/components/schemas/Images' - '/candidates/{candid}/{visit}/images/{filename}': - get: - tags: - - Images - summary: Get an images of a timepoint - parameters: - - name: candid - in: path - description: The candidate identifier - required: true - style: simple - explode: false - schema: - type: string - - name: visit - in: path - description: The visit label - required: true - style: simple - explode: false - schema: - type: string - - name: filename - in: path - description: The image filename - required: true - style: simple - explode: false - schema: - type: string - responses: - '200': - description: The file content - content: - application/octet-stream: - schema: - type: string - format: binary - '/candidates/{candid}/{visit}/images/{filename}/qc': - get: - tags: - - Images - summary: Get the qc values of an image - parameters: - - name: candid - in: path - description: The candidate identifier - required: true - style: simple - explode: false - schema: - type: string - - name: visit - in: path - description: The visit label - required: true - style: simple - explode: false - schema: - type: string - - name: filename - in: path - description: The image filename - required: true - style: simple - explode: false - schema: - type: string - responses: - '200': - description: The images qc values - content: - application/json: - schema: - $ref: '#/components/schemas/ImageQc' - put: - tags: - - Images - summary: Set the qc values of an image - parameters: - - name: candid - in: path - description: The candidate identifier - required: true - style: simple - explode: false - schema: - type: string - - name: visit - in: path - description: The visit label - required: true - style: simple - explode: false - schema: - type: string - - name: filename - in: path - description: The image filename - required: true - style: simple - explode: false - schema: - type: string - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/ImageQc' - responses: - '204': - description: Qc value updated - '/candidates/{candid}/{visit}/images/{filename}/format/brainbrowser': - get: - tags: - - Images - summary: Get the brainbrowser format of an image - parameters: - - name: candid - in: path - description: The candidate identifier - required: true - style: simple - explode: false - schema: - type: string - - name: visit - in: path - description: The visit label - required: true - style: simple - explode: false - schema: - type: string - - name: filename - in: path - description: The image filename - required: true - style: simple - explode: false - schema: - type: string - responses: - '200': - description: The images list - content: - application/json: - schema: - $ref: '#/components/schemas/FormatBrainbrowser' - '/candidates/{candid}/{visit}/images/{filename}/format/raw': - get: - tags: - - Images - summary: Get the raw format of an image - parameters: - - name: candid - in: path - description: The candidate identifier - required: true - style: simple - explode: false - schema: - type: string - - name: visit - in: path - description: The visit label - required: true - style: simple - explode: false - schema: - type: string - - name: filename - in: path - description: The image filename - required: true - style: simple - explode: false - schema: - type: string - responses: - '200': - description: The the output of mnc2raw - content: - application/octet-stream: - schema: - type: string - format: binary - '/candidates/{candid}/{visit}/images/{filename}/format/thumbnail': - get: - tags: - - Images - summary: Get the thumbnail format of an image - parameters: - - name: candid - in: path - description: The candidate identifier - required: true - style: simple - explode: false - schema: - type: string - - name: visit - in: path - description: The visit label - required: true - style: simple - explode: false - schema: - type: string - - name: filename - in: path - description: The image filename - required: true - style: simple - explode: false - schema: - type: string - responses: - '200': - description: The thumbnail representation of this image - content: - image/jpeg: - schema: - type: string - format: binary - '/candidates/{candid}/{visit}/images/{filename}/headers': - get: - tags: - - Images - summary: Get the summary headers of an image - parameters: - - name: candid - in: path - description: The candidate identifier - required: true - style: simple - explode: false - schema: - type: string - - name: visit - in: path - description: The visit label - required: true - style: simple - explode: false - schema: - type: string - - name: filename - in: path - description: The image filename - required: true - style: simple - explode: false - schema: - type: string - responses: - '200': - description: The images summary headers - content: - application/json: - schema: - $ref: '#/components/schemas/ImageSummaryHeaders' - '/candidates/{candid}/{visit}/images/{filename}/headers/full': - get: - tags: - - Images - summary: Get all headers of an image - parameters: - - name: candid - in: path - description: The candidate identifier - required: true - style: simple - explode: false - schema: - type: string - - name: visit - in: path - description: The visit label - required: true - style: simple - explode: false - schema: - type: string - - name: filename - in: path - description: The image filename - required: true - style: simple - explode: false - schema: - type: string - responses: - '200': - description: The images complete headers - content: - application/json: - schema: - $ref: '#/components/schemas/ImageHeaders' - '/candidates/{candid}/{visit}/images/{filename}/headers/{headername}': - get: - tags: - - Images - summary: Get a specific header of an image - parameters: - - name: candid - in: path - description: The candidate identifier - required: true - style: simple - explode: false - schema: - type: string - - name: visit - in: path - description: The visit label - required: true - style: simple - explode: false - schema: - type: string - - name: filename - in: path - description: The image filename - required: true - style: simple - explode: false - schema: - type: string - - name: headername - in: path - description: The requested header name - required: true - style: simple - explode: false - schema: - type: string - responses: - '200': - description: The header value - content: - application/json: - schema: - $ref: '#/components/schemas/inline_response_200_5' - '/candidates/{candid}/{visit}/qc/imaging': - get: - tags: - - Visit - summary: Get the imaging qc values of a visit - parameters: - - name: candid - in: path - description: The candidate identifier - required: true - style: simple - explode: false - schema: - type: string - - name: visit - in: path - description: The visit label - required: true - style: simple - explode: false - schema: - type: string - responses: - '200': - description: The images list - content: - application/json: - schema: - $ref: '#/components/schemas/ImagingQc' - put: - tags: - - Visit - parameters: - - name: candid - in: path - description: The candidate identifier - required: true - style: simple - explode: false - schema: - type: string - - name: visit - in: path - description: The visit label - required: true - style: simple - explode: false - schema: - type: string - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/ImagingQc' - responses: - '204': - description: Qc value updated - '/candidates/{candid}/{visit}/dicoms': - get: - tags: - - Dicoms - summary: List all dicoms for that timepoint - parameters: - - name: candid - in: path - description: ID of the candidate - required: true - style: simple - explode: false - schema: - type: string - - name: visit - in: path - description: This timepoint visit label - required: true - style: simple - explode: false - schema: - type: string - responses: - '200': - description: An object containing the list of dicoms - content: - application/json: - schema: - $ref: '#/components/schemas/Dicoms' - - '/candidates/{candid}/{visit}/dicoms/{tarname}': - get: - tags: - - Dicoms - summary: Get the dicom file - parameters: - - name: candid - in: path - description: ID of the candidate - required: true - style: simple - explode: false - schema: - type: string - - name: visit - in: path - description: This timepoint visit label - required: true - style: simple - explode: false - schema: - type: string - - name: tarname - in: path - description: The dicom filename - required: true - style: simple - explode: false - schema: - type: string - responses: - '200': - description: The dicom file content - content: - application/octet-stream: - schema: - type: string - format: binary - - '/candidates/{candid}/{visit}/recordings': - get: - tags: - - Electrophysiology - summary: Get the recording files for that visit - parameters: - - name: candid - in: path - description: ID of the candidate - required: true - style: simple - explode: false - schema: - type: string - - name: visit - in: path - description: This timepoint visit label - required: true - style: simple - explode: false - schema: - type: string - responses: - '200': - description: Recordings which have been acquired for that visit - content: - application/json: - schema: - $ref: '#/components/schemas/Recordings' - '/candidates/{candid}/{visit}/recordings/{filename}': - get: - tags: - - Electrophysiology - summary: Get a recording file - parameters: - - name: candid - in: path - description: ID of the candidate - required: true - schema: - type: string - - name: visit - in: path - description: This timepoint visit label - required: true - schema: - type: string - - name: filename - in: path - description: The recording filename - required: true - schema: - type: string - responses: - '200': - description: The recording file content - content: - application/octet-stream: - schema: - type: string - format: binary - '/candidates/{candid}/{visit}/recordings/{filename}/metadata': - get: - tags: - - Electrophysiology - summary: Get a recording file metadata - parameters: - - name: candid - in: path - description: ID of the candidate - required: true - schema: - type: string - - name: visit - in: path - description: This timepoint visit label - required: true - schema: - type: string - - name: filename - in: path - description: The recording filename - required: true - schema: - type: string - responses: - '200': - description: All metadata about that recording - content: - application/json: - schema: - $ref: '#/components/schemas/RecordingMetadata' - '/candidates/{candid}/{visit}/recordings/{filename}/metadata/{headername}': - get: - tags: - - Electrophysiology - summary: Get a specific metadata from a recording file - parameters: - - name: candid - in: path - description: ID of the candidate - required: true - schema: - type: string - - name: visit - in: path - description: This timepoint visit label - required: true - schema: - type: string - - name: filename - in: path - description: The recording filename - required: true - schema: - type: string - - name: headername - in: path - description: The metadata header name - required: true - schema: - type: string - responses: - '200': - description: The metadata header value - content: - application/json: - schema: - $ref: '#/components/schemas/RecordingMetadataSpecific' - '/candidates/{candid}/{visit}/recordings/{filename}/channels': - get: - tags: - - Electrophysiology - summary: Get a recording file channels - parameters: - - name: candid - in: path - description: ID of the candidate - required: true - schema: - type: string - - name: visit - in: path - description: This timepoint visit label - required: true - schema: - type: string - - name: filename - in: path - description: The recording filename - required: true - schema: - type: string - responses: - '200': - description: All metadata about that recording - content: - application/json: - schema: - $ref: '#/components/schemas/RecordingChannels' - '/candidates/{candid}/{visit}/recordings/{filename}/electrodes': - get: - tags: - - Electrophysiology - summary: Get a recording file electrodes - parameters: - - name: candid - in: path - description: ID of the candidate - required: true - schema: - type: string - - name: visit - in: path - description: This timepoint visit label - required: true - schema: - type: string - - name: filename - in: path - description: The recording filename - required: true - schema: - type: string - responses: - '200': - description: All metadata about that recording - content: - application/json: - schema: - $ref: '#/components/schemas/RecordingElectrodes' - '/candidates/{candid}/{visit}/recordings/{filename}/events': - get: - tags: - - Electrophysiology - summary: Get a recording file events - parameters: - - name: candid - in: path - description: ID of the candidate - required: true - schema: - type: string - - name: visit - in: path - description: This timepoint visit label - required: true - schema: - type: string - - name: filename - in: path - description: The recording filename - required: true - schema: - type: string - responses: - '200': - description: All metadata about that recording - content: - application/json: - schema: - $ref: '#/components/schemas/RecordingEvents' - '/candidates/{candid}/{visit}/recordings/{filename}/bidsfiles/archive': - get: - tags: - - Electrophysiology - summary: Get a recording archive with all associated files - parameters: - - name: candid - in: path - description: ID of the candidate - required: true - schema: - type: string - - name: visit - in: path - description: This timepoint visit label - required: true - schema: - type: string - - name: filename - in: path - description: The recording filename - required: true - schema: - type: string - responses: - '200': - description: The recording archive - content: - application/octet-stream: - schema: - type: string - format: binary - '/candidates/{candid}/{visit}/recordings/{filename}/bidsfiles/channels': - get: - tags: - - Electrophysiology - summary: Get a recording channels in BIDS format - parameters: - - name: candid - in: path - description: ID of the candidate - required: true - schema: - type: string - - name: visit - in: path - description: This timepoint visit label - required: true - schema: - type: string - - name: filename - in: path - description: The recording filename - required: true - schema: - type: string - responses: - '200': - description: The recording channels in BIDS format - content: - application/octet-stream: - schema: - type: string - format: binary - '/candidates/{candid}/{visit}/recordings/{filename}/bidsfiles/electrodes': - get: - tags: - - Electrophysiology - summary: Get a recording electrodes in BIDS format - parameters: - - name: candid - in: path - description: ID of the candidate - required: true - schema: - type: string - - name: visit - in: path - description: This timepoint visit label - required: true - schema: - type: string - - name: filename - in: path - description: The recording filename - required: true - schema: - type: string - responses: - '200': - description: The recording electrodes in BIDS format - content: - application/octet-stream: - schema: - type: string - format: binary - '/candidates/{candid}/{visit}/recordings/{filename}/bidsfiles/events': - get: - tags: - - Electrophysiology - summary: Get a recording events in BIDS format - parameters: - - name: candid - in: path - description: ID of the candidate - required: true - schema: - type: string - - name: visit - in: path - description: This timepoint visit label - required: true - schema: - type: string - - name: filename - in: path - description: The recording filename - required: true - schema: - type: string - responses: - '200': - description: The recording events in BIDS format - content: - application/octet-stream: - schema: - type: string - format: binary -components: - schemas: - Project: - type: object - properties: - Meta: - $ref: '#/components/schemas/Project_Meta' - Visits: - type: array - items: - type: string - Instruments: - type: array - items: - type: string - Candidates: - type: array - items: - type: string - ProjectCandidates: - type: object - properties: - Meta: - $ref: '#/components/schemas/Project_Meta' - Candidates: - type: array - items: - type: string - ProjectImages: - type: object - properties: - Meta: - $ref: '#/components/schemas/Project_Meta' - Images: - type: array - items: - $ref: '#/components/schemas/ProjectImage' - ProjectImage: - type: object - properties: - Candidate: - type: string - PSCID: - type: string - Visit: - type: string - Visit_date: - type: string - Site: - type: string - ScanType: - type: string - QC_status: - type: string - enum: - - Pass - - Fail - - 'null' - Selected: - type: string - enum: - - 'true' - - 'false' - - 'null' - Link: - type: string - InsertTime: - type: string - ProjectInstruments: - type: object - properties: - Meta: - $ref: '#/components/schemas/Project_Meta' - Instruments: - type: object - ProjectInstrument: - type: object - properties: - Meta: - $ref: '#/components/schemas/ProjectInstrument_Meta' - LorisFormElement: - type: object - properties: - Type: - type: string - enum: - - advcheckbox - - date - - file - - group - - header - - hidden - - link - - page - - password - - radio - - select - - static - - submit - - text - - textarea - - time - Name: - type: string - Description: - type: string - Options: - $ref: '#/components/schemas/LorisFormElement_Options' - RequireResponse: - type: boolean - ProjectVisits: - type: object - properties: - Meta: - $ref: '#/components/schemas/Project_Meta' - Visits: - type: array - items: - type: string - ProjectRecordings: - type: object - properties: - Recordings: - type: array - items: - $ref: '#/components/schemas/ProjectRecording' - ProjectRecording: - type: object - properties: - Candidate: - type: string - PSCID: - type: string - Visit: - type: string - Visit_date: - type: string - Site: - type: string - File: - type: string - Modality: - type: string - InsertTime: - type: string - Link: - type: string - Candidates: - type: object - properties: - Candidates: - $ref: '#/components/schemas/Candidate' - Candidate: - type: object - properties: - CandID: - type: string - Project: - type: string - PSCID: - type: string - Site: - type: string - EDC: - type: string - DoB: - type: string - Sex: - type: string - enum: - - Female - - Male - NewCandidate: - type: object - properties: - Candidate: - $ref: '#/components/schemas/NewCandidate_Candidate' - VisitMeta: - type: object - properties: - Meta: - $ref: '#/components/schemas/VisitMetaFields' + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/VisitPatchFields' + responses: + '204': + description: The visit stage was updated successfully +components: + schemas: VisitMetaFields: type: object properties: @@ -1897,15 +59,6 @@ components: type: string Project: type: string - VisitStages: - type: object - properties: - Stages: - type: object - anyOf: - - $ref: '#/components/schemas/InstrumentScreening' - - $ref: '#/components/schemas/InstrumentVisit' - - $ref: '#/components/schemas/InstrumentApproval' VisitStage: type: object properties: @@ -1926,571 +79,23 @@ components: properties: Stages: $ref: '#/components/schemas/InstrumentVisit' - InstrumentScreening: - type: object - properties: - Screening: - $ref: '#/components/schemas/VisitStage' + InstrumentVisit: type: object properties: Visit: $ref: '#/components/schemas/VisitStage' - InstrumentApproval: - type: object - properties: - Approval: - $ref: '#/components/schemas/VisitStage' - TimepointMeta: - type: object - properties: - CandID: - type: string - Visit: - type: string - Instrument: - type: object - properties: - Meta: - $ref: '#/components/schemas/InstrumentMeta' - $InstrumentShortName: - $ref: '#/components/schemas/InstrumentData' - InstrumentMeta: - type: object - properties: - Instrument: - type: string - Visit: - type: string - Candidate: - type: string - DDE: - type: boolean - InstrumentData: - type: object - additionalProperties: - type: string - InstrumentFlags: - type: object - properties: - Meta: - $ref: '#/components/schemas/InstrumentMeta' - Flags: - $ref: '#/components/schemas/InstrumentFlags_Flags' - Images: - type: object - properties: - Meta: - $ref: '#/components/schemas/TimepointMeta' - Files: - type: array - items: - $ref: '#/components/schemas/Images_Files' - ImagingQc: - type: object - properties: - Meta: - $ref: '#/components/schemas/TimepointMeta' - SessionQC: - type: string - enum: - - Pass - - Fail - Pending: - type: boolean - ImageQc: - type: object - properties: - Meta: - $ref: '#/components/schemas/ImageMeta' - QC: - type: string - enum: - - Pass - - Fail - Selected: - type: boolean - Caveats: - type: array - items: - $ref: '#/components/schemas/ImageCaveat' - ImageCaveat: - type: object - properties: - Severity: - type: string - Header: - type: string - Value: - type: string - ValidRange: - type: string - ValidRegex: - type: string - ImageMeta: - type: object - properties: - CandID: - type: string - Visit: - type: string - File: - type: string - FormatBrainbrowser: - type: object - properties: - xspace: - $ref: '#/components/schemas/BrainbrowserSpace' - yspace: - $ref: '#/components/schemas/BrainbrowserSpace' - zspace: - $ref: '#/components/schemas/BrainbrowserSpace' - order: - type: string - enum: - - xspace - - zspace - - yspace - BrainbrowserSpace: - type: object - properties: - start: - type: string - space_length: - type: string - step: - type: string - ImageSummaryHeaders: - type: object - properties: - Meta: - $ref: '#/components/schemas/ImageMeta' - Physical: - $ref: '#/components/schemas/ImageSummaryHeaders_Physical' - Description: - $ref: '#/components/schemas/ImageSummaryHeaders_Description' - Dimensions: - $ref: '#/components/schemas/ImageSummaryHeaders_Dimensions' - ScannerInfo: - $ref: '#/components/schemas/ImageSummaryHeaders_ScannerInfo' - DimensionHeaders: - type: object - properties: - Lenght: - type: string - StepSize: - type: string - ImageHeaders: - type: object - properties: - Meta: - $ref: '#/components/schemas/ImageMeta' - Headers: - type: object - additionalProperties: - type: string - Dicoms: - type: object - properties: - Meta: - $ref: '#/components/schemas/Dicoms_Meta' - DicomTars: - type: array - items: - $ref: '#/components/schemas/DicomTar' - DicomTar: - type: object - properties: - Tarname: - type: string - SeriesInfo: - type: array - items: - $ref: '#/components/schemas/SeriesInfo' - SeriesInfo: - type: object - properties: - SeriesDescription: - type: string - SeriesNumber: - type: string - EchoTime: - type: string - RepetitionTime: - type: string - InversionTime: - type: string - SliceThickness: - type: string - Modality: - type: string - enum: - - MR - - PT - SeriesUID: - type: string - Recordings: - type: object - properties: - Meta: - $ref: '#/components/schemas/TimepointMeta' - Files: - type: array - items: - $ref: '#/components/schemas/Recording' - Recording: - type: object - properties: - OutputType: - type: string - Filename: - type: string - AcquisitionModality: - type: string Error: - required: - - error - type: object - properties: - error: - type: string - description: The error message - body: - required: - - password - - username - type: object - properties: - username: - type: string - password: - type: string - inline_response_200: - type: object - properties: - token: - type: string - inline_response_200_1: - type: object - properties: - Projects: - type: object - inline_response_200_2: - type: object - properties: - Candidate: - $ref: '#/components/schemas/Candidate' - Visits: - type: array - items: - type: string - inline_response_200_3: - allOf: - - $ref: '#/components/schemas/VisitMeta' - - $ref: '#/components/schemas/VisitStages' - inline_response_409: type: object properties: error: type: string - inline_response_200_4: - type: object - properties: - Meta: - $ref: '#/components/schemas/TimepointMeta' - Instruments: - type: array - items: - type: string - inline_response_200_5: - type: object - properties: - Meta: - $ref: '#/components/schemas/inline_response_200_5_Meta' - Value: - type: string - - Project_Meta: - type: object - properties: - Project: - type: string - ProjectInstrument_Meta: - type: object - properties: - InstrumentVersion: - type: string - InstrumentFormatVersion: - type: string - ShortName: - type: string - LongName: - type: string - IncludeMetaDataFields: - type: string - Elements: - type: array - items: - $ref: '#/components/schemas/LorisFormElement' - LorisFormElement_Options: - type: object - properties: - Values: - type: object - additionalProperties: - type: string - NewCandidate_Candidate: - type: object - properties: - Project: - type: string - PSCID: - type: string - Site: - type: string - EDC: - type: string - DoB: - type: string - Sex: - type: string - enum: - - Female - - Male - InstrumentFlags_Flags: - type: object - properties: - Data_entry: - type: string - enum: - - In Progress - - Complete - Administration: - type: string - enum: - - None - - Partial - - All - Validity: - type: string - enum: - - Questionable - - Invalid - - Valid - Images_Files: - type: object - properties: - OutputType: - type: string - Filename: - type: string - AcquisitionType: - type: string - ImageSummaryHeaders_Physical: - type: object - properties: - TE: - type: string - TR: - type: string - TI: - type: string - SliceThickness: - type: string - ImageSummaryHeaders_Description: - type: object - properties: - SeriesName: - type: string - SeriesDescription: - type: string - ImageSummaryHeaders_Dimensions: - type: object - properties: - XSpace: - $ref: '#/components/schemas/DimensionHeaders' - YSpace: - $ref: '#/components/schemas/DimensionHeaders' - ZSpace: - $ref: '#/components/schemas/DimensionHeaders' - TimeDimension: - $ref: '#/components/schemas/DimensionHeaders' - ImageSummaryHeaders_ScannerInfo: - type: object - properties: - Manufacturer: - type: string - Model: - type: string - SoftwareVersion: - type: string - SerialNumber: - type: string - FieldStrength: - type: string - Dicoms_Meta: - type: object - properties: - CandID: - type: string - Visit: - type: string - inline_response_200_5_Meta: - type: object - properties: - CandID: - type: string - Visit: - type: string - File: - type: string - Header: - type: string - RecordingMeta: - type: object - properties: - CandID: - type: string - Visit: - type: string - File: - type: string - RecordingMetadata: - type: object - properties: - Meta: - $ref: '#/components/schemas/RecordingMeta' - Data: - type: object - additionalProperties: - type: string - RecordingMetadataSpecific: - type: object - properties: - Meta: - type: object - properties: - CandID: - type: string - Visit: - type: string - Filename: - type: string - Header: - type: string - Value: - type: string - RecordingChannels: - type: object - properties: - Meta: - $ref: '#/components/schemas/RecordingMeta' - Channels: - $ref: '#/components/schemas/RecordingChannel' - - RecordingChannel: - type: object - properties: - ChannelName: - type: string - ChannelDescription: - type: string - ChannelType: - type: string - ChannelTypeDescription: - type: string - ChannelStatus: - type: string - StatusDescription: - type: string - SamplingFrequency: - type: number - LowCutoff: - type: string - HighCutoff: - type: string - ManualFlag: - type: string - Notch: - type: string - Reference: - type: string - Unit: - type: string - ChannelFilePath: - type: string - RecordingElectrodes: - type: object - properties: - Meta: - $ref: '#/components/schemas/RecordingMeta' - Electrodes: - type: array - items: - $ref: '#/components/schemas/RecordingElectrode' - - RecordingElectrode: - type: object - properties: - ElectrodeName: - type: string - ElectrodeType: - type: string - ElectrodeMaterial: - type: string - X: - type: string - Y: - type: string - Z: - type: string - Impedance: - type: string - ElectrodeFilePath: - type: string - - RecordingEvents: - type: object - properties: - Meta: - $ref: '#/components/schemas/RecordingMeta' - TaskEvents: - type: array - items: - $ref: '#/components/schemas/RecordingTaskEvent' - - RecordingTaskEvent: - type: object - properties: - Onset: - type: string - Duration: - type: number - EventCode: - type: string - EventSample: - type: string - EventType: - type: string - TrialType: - type: string - ResponseTime: - type: string - EventFilePath: - type: string - - responses: - InternalServerError: - description: 500 Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/Error' securitySchemes: ApiKeyAuth: type: apiKey name: Authorization in: header + diff --git a/src/Http/Response/JSON/NoContent.php b/src/Http/Response/JSON/NoContent.php new file mode 100644 index 00000000000..540d0c0c8b6 --- /dev/null +++ b/src/Http/Response/JSON/NoContent.php @@ -0,0 +1,40 @@ + + * @license http://www.gnu.org/licenses/gpl-3.0.txt GPLv3 + * @link https://www.github.com/aces/Loris/ + * + * @see https://www.php-fig.org/psr/psr-7/ + * @see https://www.php-fig.org/psr/psr-15/ + */ +namespace LORIS\Http\Response\JSON; + +use \LORIS\Http\Response\JsonResponse; + +/** + * A LORIS Http Response is an implementation of the PSR15 ResponseInterface + * to use in LORIS specific for 204 No Content. + * + * @category PSR15 + * @package Http + * @author Xavier Lecours Boucher + * @license http://www.gnu.org/licenses/gpl-3.0.txt GPLv3 + * @link https://www.github.com/aces/Loris/ + */ +class NoContent extends JsonResponse +{ + /** + * Create a Json response specific to 204 No Content + */ + public function __construct() + { + parent::__construct('', 204); + } +}