diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 709c7f49c..050cec929 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -80,6 +80,7 @@ jobs: # hack the output from the linting steps to avoid these stopping the builds - we are not going to get # to a clean output without considerable effort, but it's useful to see the output run: | + cp src/js/config_sample.json src/js/config.json node --version npm ci npm run build diff --git a/.gitignore b/.gitignore index ba6e91405..e94e75128 100644 --- a/.gitignore +++ b/.gitignore @@ -27,6 +27,7 @@ client/index.php client/dist client/index.html client/.env +client/src/js/config.json api/config.php api/vendor @@ -38,3 +39,5 @@ entrypoint.bash php-fpm.conf php-fpm.pid php.ini + +*~ diff --git a/api/composer.json b/api/composer.json index b2c642c44..8101fed18 100644 --- a/api/composer.json +++ b/api/composer.json @@ -22,8 +22,8 @@ "ralouphie/getallheaders": "2.0.5", "slim/slim": "2.6.2", "stomp-php/stomp-php": "3.0.6", - "symfony/http-foundation": "^2.8", - "symfony/filesystem": "^2.8", + "symfony/http-foundation": "^5.4", + "symfony/filesystem": "^5.4", "mpdf/qrcode": "^1.2", "mtcmedia/dhl-api": "dev-master#9b4b6315", "maennchen/zipstream-php": "2.1.0" @@ -45,4 +45,4 @@ "php": "7.3" } } -} \ No newline at end of file +} diff --git a/api/config_sample.php b/api/config_sample.php index 1267f8fe3..88e3dca87 100644 --- a/api/config_sample.php +++ b/api/config_sample.php @@ -309,7 +309,7 @@ # These map proposal types to their proposalcode # - If these are not defined for a proposal type, the api then uses bl_types below - $prop_types = array('mx', 'em'); + $prop_types = array('mx'); # This maps beamlinename in blsession to a proposal type # - Internal maps a beamline to an api "type", there are currently: @@ -338,6 +338,9 @@ ) ); + # Redirects, used internally when incrementally replacing parts of the application + $redirects = array('em' => 'https://ebic-pato.diamond.ac.uk'); + # Web-conexs URLs $conexs_url = ''; $conexs_mpapi_url = ''; @@ -374,10 +377,9 @@ ), ); - # Map of beamlinename to pv prefix - $bl_pv_map = array( - 'i02' => 'BL02I', - 'i03' => 'BL03I', + # Map of beamlinename to puck name pv + $bl_puck_names = array( + 'i03' => "BL03I-MO-ROBOT-01:PUCK_%02d_NAME" ); # Dials server values diff --git a/api/index.php b/api/index.php index 2095ba7cc..9dbe690f6 100644 --- a/api/index.php +++ b/api/index.php @@ -70,7 +70,7 @@ function setupApplication($mode): Slim global $motd, $authentication_type, $cas_url, $cas_sso, $sso_url, $package_description, $facility_courier_countries, $facility_courier_countries_nde, $dhl_enable, $dhl_link, $scale_grid, $scale_grid_end_date, $preset_proposal, $timezone, - $valid_components, $enabled_container_types, $ifsummary, $synchweb_version; + $valid_components, $enabled_container_types, $ifsummary, $synchweb_version, $redirects; $app->contentType('application/json'); $options = $app->container['options']; $app->response()->body(json_encode(array( @@ -91,7 +91,8 @@ function setupApplication($mode): Slim 'valid_components' => $valid_components, 'enabled_container_types' => $enabled_container_types, 'ifsummary' => $ifsummary, - 'synchweb_version' => $synchweb_version + 'synchweb_version' => $synchweb_version, + 'redirects' => $redirects ))); }); return $app; diff --git a/api/src/Controllers/AssignController.php b/api/src/Controllers/AssignController.php index 30bcbc79a..967168c0d 100644 --- a/api/src/Controllers/AssignController.php +++ b/api/src/Controllers/AssignController.php @@ -103,22 +103,21 @@ function getBeamlineVisits($visit = null) # BL03I-MO-ROBOT-01:PUCK_01_NAME function getPuckNames() { - global $bl_pv_map; + global $bl_puck_names; session_write_close(); if (!$this->has_arg('prop')) $this->_error('No proposal specified'); if (!$this->has_arg('bl')) $this->_error('No beamline specified'); - if (!array_key_exists($this->arg('bl'), $bl_pv_map)) + if (!array_key_exists($this->arg('bl'), $bl_puck_names)) $this->_error('No such beamline'); - $pv_prefix = $bl_pv_map[$this->arg('bl')]; + $pv_names = $bl_puck_names[$this->arg('bl')]; $pvs = array(); for ($i = 1; $i < 38; $i++) { - $id = $i < 10 ? '0' . $i : $i; - array_push($pvs, $pv_prefix . '-MO-ROBOT-01:PUCK_' . $id . '_NAME'); + array_push($pvs, sprintf($pv_names, $i)); } $rows = $this->assignData->getContainerBarcodesForProposal($this->proposalid); @@ -132,7 +131,8 @@ function getPuckNames() $vals = $this->pv(array_values($pvs), true, true); foreach ($vals as $k => $v) { - if (preg_match('/PUCK_(\d+)_NAME/', $k, $mat)) + $zero_id = array_search($k, $pvs); + if ($zero_id !== false) { if (is_array($v) && sizeof($v)) { @@ -140,10 +140,10 @@ function getPuckNames() } else $val = ''; - array_push($return, array('id' => intval($mat[1]), 'name' => $val)); + array_push($return, array('id' => $zero_id+1, 'name' => $val)); } } $this->_output($return); } -} \ No newline at end of file +} diff --git a/api/src/Controllers/AuthenticationController.php b/api/src/Controllers/AuthenticationController.php index 218e6d2cf..0d9aef954 100644 --- a/api/src/Controllers/AuthenticationController.php +++ b/api/src/Controllers/AuthenticationController.php @@ -161,12 +161,19 @@ private function processOneTimeUseTokens(): bool $tokenId = $this->app->request()->get('token'); if ($tokenId) { - # Remove tokens more than 10 seconds old, they should have been used - $this->dataLayer->deleteOldOneTimeUseTokens(); + $max_token_age = 10; $token = $this->dataLayer->getOneTimeUseToken($tokenId); if (sizeof($token)) { $token = $token[0]; + if ($token['AGE'] > $max_token_age) + { + $err = 'Authorisation token too old. Please press back and then try again.'; + $err .= ' If this problem persists, please try clearing your cookies or using a different browser.'; + error_log('Authorisation token too old. Age: '.$token['AGE'].'s. Max age: '.$max_token_age.'s.'); + error_log('User-agent: ' . $_SERVER['HTTP_USER_AGENT']); + $this->returnError(400, $err); + } $qs = $_SERVER['QUERY_STRING'] ? (preg_replace('/(&)?token=\w+/', '', str_replace('&', '&', $_SERVER['QUERY_STRING']))) : null; if ($qs) $qs = '?' . $qs; @@ -178,13 +185,26 @@ private function processOneTimeUseTokens(): bool $need_auth = false; $this->dataLayer->deleteOneTimeUseToken($tokenId); } + else + { + error_log('Authorisation token not valid for this URL.'); + error_log('Requested site: ' . $this->app->request->getResourceUri() . $qs); + error_log('Token valid for: ' . $token['VALIDITY']); + $err = 'Invalid one-time authorisation token.'; + $this->returnError(400, $err); + } } else { - $this->returnError(400, 'Invalid one time authorisation token'); + $err = 'No authorisation token found. '; + $err .= 'If this error persists, please try clearing your cookies or using a different browser.'; + error_log('No authorisation token found.'); + error_log('User-agent: ' . $_SERVER['HTTP_USER_AGENT']); + $this->returnError(400, $err); } + # Remove tokens more than $max_token_age seconds old, they should have been used + $this->dataLayer->deleteOldOneTimeUseTokens($max_token_age); } - return $need_auth; } @@ -396,4 +416,4 @@ private function authenticateByType() { } } -} \ No newline at end of file +} diff --git a/api/src/Model/Services/AuthenticationData.php b/api/src/Model/Services/AuthenticationData.php index a7d96457a..bbba7df86 100644 --- a/api/src/Model/Services/AuthenticationData.php +++ b/api/src/Model/Services/AuthenticationData.php @@ -28,7 +28,8 @@ function isUserLoggedIn($userId): bool function getOneTimeUseToken($tokenId) { - return $this->db->pq("SELECT o.validity, pe.personid, pe.login, CONCAT(p.proposalcode, p.proposalnumber) as prop + return $this->db->pq("SELECT o.validity, pe.personid, pe.login, CONCAT(p.proposalcode, p.proposalnumber) as prop, + NOW() - o.recordTimeStamp as age FROM SW_onceToken o INNER JOIN proposal p ON p.proposalid = o.proposalid INNER JOIN person pe ON pe.personid = o.personid @@ -40,10 +41,10 @@ function deleteOneTimeUseToken($tokenId) $this->db->pq("DELETE FROM SW_onceToken WHERE token=:1", array($tokenId)); } - function deleteOldOneTimeUseTokens() + function deleteOldOneTimeUseTokens($max_token_age) { - # Remove tokens more than 10 seconds old, they should have been used - $this->db->pq("DELETE FROM SW_onceToken WHERE recordTimeStamp < NOW() - INTERVAL 10 SECOND"); + # Remove tokens more than $max_token_age seconds old, they should have been used + $this->db->pq("DELETE FROM SW_onceToken WHERE recordTimeStamp < NOW() - INTERVAL :1 SECOND", array($max_token_age)); } @@ -100,4 +101,4 @@ function updateActivityTimestamp($loginId) } } } -} \ No newline at end of file +} diff --git a/api/src/Page.php b/api/src/Page.php index 72ffa2f98..3fc366473 100644 --- a/api/src/Page.php +++ b/api/src/Page.php @@ -161,10 +161,10 @@ function _get_type_from_beamline($bl) } /** - * Return a list of beamlines based on the type/group (mx, em, gen) + * Return a list of beamlines based on the type/group (mx, gen) * The return value can be checked with empty() if required * - * @param String $ty Beamline type/group 'mx', 'em', etc. or 'all' to get all beamlines + * @param String $ty Beamline type/group 'mx', etc. or 'all' to get all beamlines * @param bool $archived Default: false. Flag that allows archived beamlines to be included in result * @return Array Returns list of beamlines that are part of the beamline type */ diff --git a/api/src/Page/EM.php b/api/src/Page/EM.php deleted file mode 100644 index 8e31cd3a9..000000000 --- a/api/src/Page/EM.php +++ /dev/null @@ -1,210 +0,0 @@ - '\d+', - 'ids' => '\d+', - 'visit' => '\w+\d+-\d+', - 'session' => '\w+\d+-\d+', - 'classification' => '[23]D', - 'n' => '\d+', - 't' => '\d+', - 'programId' => '\d+', - 'attachId' => '\d+', - 'classificationId' => '\d+', - 'jobId' => '\d+', - 'imageNumber' => '\d+', - - // SCIPION - 'numberOfIndividualFrames' => '\d+', // Integer - 'patchX' => '\d+', // Integer - 'patchY' => '\d+', // Integer - 'samplingRate' => '\d*(\.\d+)?', // Decimal - 'particleSize' => '\d+', // Integer - 'minDist' => '\d+', // Integer - 'windowSize' => '\d+', // Integer - 'findPhaseShift' => '1?', // Boolean - 'dosePerFrame' => '\d*(\.\d+)?', // Decimal - ); - - public static $dispatch = array( - array('/aps', 'post', '_ap_status'), - array('/mc/fft/image/:id(/n/:ImageNumber)(/t/:t)', 'get', '_mc_fft'), - - // See Synchweb\Page\EM\ProcessingJobs - array('/jobs/:id', 'get', 'processingJobs'), - - // See Synchweb\Page\EM\Attachments - array('/attachments/:programId', 'get', 'attachmentsGetAll'), - array('/attachment/:attachId', 'get', 'attachmentsGetOne'), - - // See Synchweb\Page\EM\MotionCorrection - array('/mc/:programId/n/:imageNumber', 'get', 'motionCorrectionResult'), - array('/mc/drift/:programId(/n/:imageNumber)', 'get', 'motionCorrectionDriftPlot'), - array('/mc/snapshot/:programId(/n/:imageNumber)', 'get', 'motionCorrectionSnapshot'), - - // See Synchweb\Page\EM\Ctf - array('/ctf/:programId/n/:imageNumber', 'get', 'ctfResult'), - array('/ctf/image/:programId(/n/:imageNumber)', 'get', 'ctfImage'), - array('/ctf/summary/:programId', 'get', 'ctfSummary'), - - // See SynchWeb\Page\EM\Picker: - array('/picker/:programId/n/:imageNumber', 'get', 'pickerResult'), - array('/picker/image/:programId(/n/:imageNumber)', 'get', 'pickerImage'), - - // See SynchWeb\Page\EM\Classification: - array('/classification/:programId/type/:classification', 'get', 'classificationResult'), - array('/classification/image/:classificationId', 'get', 'classificationImage'), - - // See Synchweb\Page\EM\DataCollection - array('/dc/schema/', 'get', 'dataCollectionSchema'), - array('/dc/new/:session', 'post', 'dataCollectionCreate'), - array('/dc/:id', 'get', 'dataCollectionGet'), - array('/dc/comments/:id', 'post', 'dataCollectionComments'), - - // See Synchweb\Page\EM\Stats - array('/stats/ctf', 'get', 'statsCtf'), - array('/stats/mc', 'get', 'statsMcDrift'), - - // See Synchweb\Page\EM\Relion - array('/relion/schema/', 'get', 'relionSchema'), - array('/relion/start/:id', 'post', 'relionStart'), - array('/relion/stop/:jobId', 'get', 'relionStop'), - array('/relion/parameters/:jobId', 'get', 'relionParameters'), - - // See Synchweb\Page\EM\Scipion - array('/process/scipion/session/:session', 'post', 'scipionStart') - ); - - private function paginationArguments($args) - { - $perPage = $this->has_arg('per_page') ? - $this->arg('per_page') : 15; - $page = ($this->has_arg('page') && $this->arg('page') > 0) ? - $this->arg('page') - 1 : 0; - - array_push($args, $page * $perPage); // Offset - array_push($args, $perPage); // Row Count - - return $args; - } - - //////////////////////////////////////////////////////////////////////////// - - public function _mc_fft() - { - $im = $this->has_arg('n') ? $this->arg('n') : 1; - $t = $this->has_arg('t') ? 2 : 1; - - $imgs = $this->db->pq("SELECT mc.fftcorrectedfullpath, mc.fftfullpath - FROM motioncorrection mc - INNER JOIN movie m ON m.movieid = mc.movieid - INNER JOIN datacollection dc ON dc.datacollectionid = m.datacollectionid - WHERE dc.datacollectionid = :1 AND m.movienumber = :2", array($this->arg('id'), $im)); - - if (!sizeof($imgs)) { - $this->_error('No such fft'); - } - - $img = $imgs[0]; - - $this->sendImage( - $t == 2 ? $img['FFTCORRECTEDFULLPATH'] : $img['FFTFULLPATH'] - ); - } - - function _ap_status() - { - if (!($this->has_arg('visit') || $this->has_arg('prop'))) $this->_error('No visit or proposal specified'); - - $where = array(); - $ids = array(); - if ($this->has_arg('ids')) { - if (is_array($this->arg('ids'))) { - foreach ($this->arg('ids') as $i) { - array_push($ids, $i); - array_push($where, 'm.datacollectionid=:' . sizeof($ids)); - } - } - } - - if (!sizeof($ids)) { - $this->_output(array()); - return; - } - - $where = '(' . implode(' OR ', $where) . ')'; - - if ($this->has_arg('visit')) { - $where .= " AND CONCAT(p.proposalcode,p.proposalnumber,'-',s.visit_number) LIKE :" . (sizeof($ids) + 1); - array_push($ids, $this->arg('visit')); - } else { - $where .= " AND s.proposalid = :" . (sizeof($ids) + 1); - array_push($ids, $this->proposalid); - } - - $statuses = array(); - foreach ($this->arg('ids') as $d) { - $statuses[$d] = array('ID' => $d, 'MC' => array(), 'CTF' => array()); - } - - $mc = $this->db->pq("SELECT app.processingstatus, m.movienumber, dc.datacollectionid - FROM motioncorrection mc - INNER JOIN autoprocprogram app ON app.autoprocprogramid = mc.autoprocprogramid - INNER JOIN movie m ON m.movieid = mc.movieid - INNER JOIN datacollection dc ON dc.datacollectionid = m.datacollectionid - INNER JOIN datacollectiongroup dcg ON dcg.datacollectiongroupid = dc.datacollectiongroupid - INNER JOIN blsession s ON s.sessionid = dcg.sessionid - INNER JOIN proposal p ON p.proposalid = s.proposalid - WHERE $where", $ids); - - foreach ($mc as $m) { - $statuses[$m['DATACOLLECTIONID']]['MC'][$m['MOVIENUMBER']] = $m['PROCESSINGSTATUS']; - } - - $ctf = $this->db->pq("SELECT app.processingstatus, m.movienumber, dc.datacollectionid - FROM ctf c - INNER JOIN motioncorrection mc ON mc.motioncorrectionid = c.motioncorrectionid - INNER JOIN autoprocprogram app ON app.autoprocprogramid = c.autoprocprogramid - INNER JOIN movie m ON m.movieid = mc.movieid - INNER JOIN datacollection dc ON dc.datacollectionid = m.datacollectionid - INNER JOIN datacollectiongroup dcg ON dcg.datacollectiongroupid = dc.datacollectiongroupid - INNER JOIN blsession s ON s.sessionid = dcg.sessionid - INNER JOIN proposal p ON p.proposalid = s.proposalid - WHERE $where", $ids); - - foreach ($ctf as $c) { - $statuses[$c['DATACOLLECTIONID']]['CTF'][$c['MOVIENUMBER']] = $c['PROCESSINGSTATUS']; - } - - $this->_output(array_values($statuses)); - } -} diff --git a/api/src/Page/EM/Attachments.php b/api/src/Page/EM/Attachments.php deleted file mode 100644 index ebe5584c4..000000000 --- a/api/src/Page/EM/Attachments.php +++ /dev/null @@ -1,98 +0,0 @@ -db->pq( - "SELECT - AutoProcProgramAttachment.autoProcProgramAttachmentId, - CONCAT( - AutoProcProgramAttachment.filePath, - '/', - AutoProcProgramAttachment.fileName - ) AS file, - AutoProcProgramAttachment.recordTimeStamp, - AutoProcProgramAttachment.fileType, - AutoProcProgramAttachment.importanceRank - FROM AutoProcProgramAttachment - INNER JOIN AutoProcProgram - ON AutoProcProgram.autoProcProgramId = AutoProcProgramAttachment.autoProcProgramId - INNER JOIN ProcessingJob - ON ProcessingJob.processingJobId = AutoProcProgram.processingJobId - INNER JOIN DataCollection - ON DataCollection.dataCollectionId = ProcessingJob.dataCollectionId - INNER JOIN DataCollectionGroup - ON DataCollectionGroup.dataCollectionGroupId = DataCollection.dataCollectionGroupId - INNER JOIN BLSession - ON BLSession.sessionId = DataCollectionGroup.sessionId - INNER JOIN Proposal - ON Proposal.proposalId = BLSession.proposalId - WHERE CONCAT(Proposal.proposalCode, Proposal.proposalNumber) = :1 - AND AutoProcProgramAttachment.autoProcProgramId = :2", - array($this->arg('prop'), $this->arg('programId')), - false - ); - - // Don't report an error if no attachments found, just return an - // empty object - if (sizeof($rows) == 0) { - $this->_output(array()); - return; - } - - $result = array(); - foreach ($rows as $row) { - $file = $row['file']; - if (!file_exists($file)) { - continue; - } - - if (strtolower(pathinfo($file, PATHINFO_EXTENSION)) == 'json') { - $row['JSON'] = file_get_contents($file); - } - array_push($result, $row); - } - - $this->_output($result); - } - - public function attachmentsGetOne() - { - $rows = $this->db->pq( - "SELECT - AutoProcProgramAttachment.autoProcProgramAttachmentId, - CONCAT( - AutoProcProgramAttachment.filePath, - '/', - AutoProcProgramAttachment.fileName - ) AS file, - AutoProcProgramAttachment.fileType - FROM AutoProcProgramAttachment - INNER JOIN AutoProcProgram - ON AutoProcProgram.autoProcProgramId = AutoProcProgramAttachment.autoProcProgramId - INNER JOIN ProcessingJob - ON ProcessingJob.processingJobId = AutoProcProgram.processingJobId - INNER JOIN DataCollection - ON DataCollection.dataCollectionId = ProcessingJob.dataCollectionId - INNER JOIN DataCollectionGroup - ON DataCollectionGroup.dataCollectionGroupId = DataCollection.dataCollectionGroupId - INNER JOIN BLSession - ON BLSession.sessionId = DataCollectionGroup.sessionId - INNER JOIN Proposal - ON Proposal.proposalId = BLSession.proposalId - WHERE CONCAT(Proposal.proposalCode, Proposal.proposalNumber) = :1 - AND AutoProcProgramAttachment.autoProcProgramAttachmentId = :2", - array($this->arg('prop'), $this->arg('attachId')), - false - ); - - if (!sizeof($rows)) { - $this->_error('No such attachment'); - } - - $this->sendDownload($rows[0]['file']); - } -} diff --git a/api/src/Page/EM/Classification.php b/api/src/Page/EM/Classification.php deleted file mode 100644 index 5107346ea..000000000 --- a/api/src/Page/EM/Classification.php +++ /dev/null @@ -1,141 +0,0 @@ -db->pq( - "SELECT ParticleClassification.classImageFullPath - FROM ParticleClassification - INNER JOIN ParticleClassificationGroup - ON ParticleClassificationGroup.particleClassificationGroupId - = ParticleClassification.particleClassificationGroupId - INNER JOIN AutoProcProgram - ON AutoProcProgram.autoProcProgramId - = ParticleClassificationGroup.programId - INNER JOIN ProcessingJob - ON ProcessingJob.processingJobId - = AutoProcProgram.processingJobId - INNER JOIN DataCollection - ON DataCollection.dataCollectionId - = ProcessingJob.dataCollectionId - INNER JOIN DataCollectionGroup - ON DataCollectionGroup.dataCollectionGroupId - = DataCollection.dataCollectionGroupId - INNER JOIN BLSession - ON BLSession.sessionId - = DataCollectionGroup.sessionId - INNER JOIN Proposal - ON Proposal.proposalId - = BLSession.proposalId - WHERE CONCAT(Proposal.proposalCode, Proposal.proposalNumber) = :1 - AND ParticleClassification.particleClassificationId = :2", - array($this->arg('prop'), $this->arg('classificationId')), - false - ); - - $this->sendImage( - sizeof($images) > 0 ? $images[0]['classImageFullPath'] : null - ); - } - - /* ParticleClassificationGroup has load of null filled rows - hence the INNER JOIN (is that wrong?) */ - public function classificationResult() - { - $args = array( - $this->arg('prop'), - $this->arg('programId'), - $this->arg('classification') - ); - - $total = $this->classificationQuery( - 'COUNT(ParticleClassification.particleClassificationId) AS total', - $args - ); - - $sortBy = $this->has_arg('sort_by') ? - strtolower($this->arg('sort_by')) : - 'particles'; - - // 'CryoemInitialModel.numberOfParticles' - // 'CryoemInitialModel.resolution' - - $order = $sortBy == 'particles' ? - 'ParticleClassification.particlesPerClass DESC' : - // Sort in ascending order by with "0" last. - "ParticleClassification.estimatedResolution = 0, - ParticleClassification.estimatedResolution"; - - $particles = $this->classificationQuery( - implode(',', array( - 'ParticleClassificationGroup.type', - 'ParticleClassificationGroup.batchNumber', - 'ParticleClassificationGroup.numberOfParticlesPerBatch', - 'ParticleClassificationGroup.numberOfClassesPerBatch', - 'ParticleClassificationGroup.symmetry', - 'ParticleClassification.particleClassificationId', - 'ParticleClassification.classNumber', - 'ParticleClassification.classDistribution', - 'ParticleClassification.particlesPerClass', - 'ParticleClassification.rotationAccuracy', - 'ParticleClassification.translationAccuracy', - 'ParticleClassification.estimatedResolution', - 'ParticleClassification.overallFourierCompleteness', - 'CryoemInitialModel.resolution', - 'CryoemInitialModel.numberOfParticles' - )), - $this->paginationArguments($args), - "ORDER BY $order LIMIT :4, :5" - ); - - // No need for an error if no rows found - $this->_output(array( - 'total' => intval($total[0]['total']), - 'classes' => $particles, - )); - } - - private function classificationQuery($selection, $args, $options = '') - { - return $this->db->pq( - "SELECT $selection - FROM ParticleClassificationGroup - LEFT JOIN ParticleClassification - ON ParticleClassification.particleClassificationGroupId - = ParticleClassificationGroup.particleClassificationGroupId - LEFT JOIN ParticleClassification_has_CryoemInitialModel - ON ParticleClassification_has_CryoemInitialModel.particleClassificationId - = ParticleClassification.particleClassificationId - LEFT JOIN CryoemInitialModel - ON CryoemInitialModel.cryoemInitialModelId - = ParticleClassification_has_CryoemInitialModel.cryoemInitialModelId - INNER JOIN AutoProcProgram - ON AutoProcProgram.autoProcProgramId - = ParticleClassificationGroup.programId - INNER JOIN ProcessingJob - ON ProcessingJob.processingJobId - = AutoProcProgram.processingJobId - INNER JOIN DataCollection - ON DataCollection.dataCollectionId - = ProcessingJob.dataCollectionId - INNER JOIN DataCollectionGroup - ON DataCollectionGroup.dataCollectionGroupId - = DataCollection.dataCollectionGroupId - INNER JOIN BLSession - ON BLSession.sessionId - = DataCollectionGroup.sessionId - INNER JOIN Proposal - ON Proposal.proposalId - = BLSession.proposalId - WHERE CONCAT(Proposal.proposalCode, Proposal.proposalNumber) = :1 - AND ParticleClassificationGroup.programId = :2 - AND ParticleClassificationGroup.type = :3 - $options", - $args, - false - ); - } -} diff --git a/api/src/Page/EM/Config.php b/api/src/Page/EM/Config.php deleted file mode 100644 index 1ba699063..000000000 --- a/api/src/Page/EM/Config.php +++ /dev/null @@ -1,20 +0,0 @@ -_get_beamlines_from_type('em', false)) == 0) { - $message = 'Electron microscopes are not specified'; - error_log($message); - $this->_error($message, 500); - } - } -} diff --git a/api/src/Page/EM/Ctf.php b/api/src/Page/EM/Ctf.php deleted file mode 100644 index 7d5434df4..000000000 --- a/api/src/Page/EM/Ctf.php +++ /dev/null @@ -1,117 +0,0 @@ -db->pq( - "SELECT - c.ctfId, - c.boxSizeX, - c.boxSizeY, - c.minResolution, - c.maxResolution, - c.minDefocus, - c.maxDefocus, - c.defocusStepSize, - c.astigmatism / 10.0 as astigmatism, - c.astigmatismAngle, - c.estimatedResolution, - c.estimatedDefocus / 10000.0 as estimatedDefocus, - c.amplitudeContrast, - c.ccValue, - c.comments, - m.createdTimeStamp, - mc.imageNumber - FROM CTF c - INNER JOIN MotionCorrection mc ON mc.motionCorrectionId = c.motionCorrectionId - INNER JOIN Movie m ON m.movieId = mc.movieId - INNER JOIN DataCollection dc ON dc.dataCollectionId = m.dataCollectionId - INNER JOIN DataCollectionGroup dcg ON dcg.dataCollectionGroupId = dc.dataCollectionGroupId - INNER JOIN BLSession bls ON bls.sessionId = dcg.sessionId - INNER JOIN Proposal p ON p.proposalId = bls.proposalId - WHERE CONCAT(p.proposalCode, p.proposalNumber) = :1 - AND c.autoProcProgramId = :2 - AND mc.imageNumber = :3", - array( - $this->arg('prop'), - $this->arg('programId'), - $this->has_arg('imageNumber') ? $this->arg('imageNumber') : 1 - ), - false - ); - - if (!sizeof($rows)) { - $this->_error('No such ctf correction'); - } - $row = $rows[0]; - - $row['estimatedResolution'] = - number_format($row['estimatedResolution'], 2, '.', ''); - $row['astigmatismAngle'] = - number_format($row['astigmatismAngle'], 1, '.', ''); - $row['astigmatism'] = - number_format($row['astigmatism'], 4, '.', ''); - $row['estimatedDefocus'] = - number_format($row['estimatedDefocus'], 5, '.', ''); - - $this->_output($row); - } - - public function ctfImage() - { - $rows = $this->db->pq( - "SELECT c.fftTheoreticalFullPath - FROM CTF c - INNER JOIN MotionCorrection mc ON mc.motionCorrectionId = c.motionCorrectionId - INNER JOIN Movie m ON m.movieId = mc.movieId - INNER JOIN DataCollection dc ON dc.dataCollectionId = m.dataCollectionId - INNER JOIN DataCollectionGroup dcg ON dcg.dataCollectionGroupId = dc.dataCollectionGroupId - INNER JOIN BLSession bls ON bls.sessionId = dcg.sessionId - INNER JOIN Proposal p ON p.proposalId = bls.proposalId - WHERE CONCAT(p.proposalCode, p.proposalNumber) = :1 - AND c.autoProcProgramId = :2 - AND mc.imageNumber = :3", - array( - $this->arg('prop'), - $this->arg('programId'), - $this->has_arg('imageNumber') ? $this->arg('imageNumber') : 1 - ), - false - ); - - if (!sizeof($rows)) { - $this->_error('No such ctf correction'); - } - - $this->sendImage($rows[0]['fftTheoreticalFullPath']); - } - - public function ctfSummary() - { - $rows = $this->db->pq( - "SELECT - mc.imageNumber, - m.createdTimeStamp, - c.astigmatism / 10.0 as astigmatism, - c.estimatedResolution, - c.estimatedDefocus / 10000.0 as estimatedDefocus - FROM CTF c - INNER JOIN AutoProcProgram app ON app.autoProcProgramId = c.autoProcProgramId - INNER JOIN MotionCorrection mc ON mc.motionCorrectionId = c.motionCorrectionId - INNER JOIN Movie m ON m.movieId = mc.movieId - INNER JOIN DataCollection dc ON dc.dataCollectionId = m.dataCollectionId - INNER JOIN DataCollectionGroup dcg ON dcg.dataCollectionGroupId = dc.dataCollectionGroupId - INNER JOIN BLSession bls ON bls.sessionId = dcg.sessionId - INNER JOIN Proposal p ON p.proposalId = bls.proposalId - WHERE CONCAT(p.proposalCode, p.proposalNumber) = :1 - AND c.autoProcProgramId = :2 - ORDER BY m.createdTimeStamp", - array($this->arg('prop'), $this->arg('programId')), - false - ); - $this->_output($rows); - } -} diff --git a/api/src/Page/EM/DataCollection.php b/api/src/Page/EM/DataCollection.php deleted file mode 100644 index de777a903..000000000 --- a/api/src/Page/EM/DataCollection.php +++ /dev/null @@ -1,275 +0,0 @@ -_output($schema->clientSchema()); - } - - /** - * This is a "long term temporary workaround" for creating Data Collections - * via the Synchweb UI. - */ - public function dataCollectionCreate() - { - global $visit_directory; - - $schema = new DataCollectionSchema(); - $validator = new SchemaValidator($schema); - - list($invalid, $postData) = $validator->validatePostData( - json_decode($this->app->request->getBody(), true) - ); - if (count($invalid) > 0) { - $this->_error($invalid, 400); - } - - $session = $this->sessionFetch($this->arg('session')); - - $this->sessionExitIfNotActive($session); - - $imageDirectory = $this->sessionSubstituteValuesInPath( - $session, - $visit_directory - ) . '/' . $postData['imageDirectory'] . '/'; - - $fileTemplate = ( - $postData['acquisitionSoftware'] == 'EPU' ? - 'GridSquare_*/Data/*.' : 'Frames/*.' - ) . $postData['imageSuffix']; - - if (count(glob($imageDirectory . $fileTemplate, GLOB_NOSORT)) == 0) { - $this->dataCollectionFileError("Raw files don't exist"); - } - - $existingCollection = $this->dataCollectionFindExisting( - $session['sessionId'], - $imageDirectory, - $fileTemplate - ); - - if ($existingCollection !== null) { - $this->dataCollectionFileError("Data Collection already exists"); - } - - $dataCollectionId = null; - - try { - $this->db->start_transaction(); - - $this->db->pq( - "INSERT INTO DataCollectionGroup ( - sessionId, comments, experimentType - ) - VALUES (:1, :2, :3) - RETURNING dataCollectionGroupId INTO :id", - array($session['sessionId'], 'Created by SynchWeb', 'EM') - ); - - $inserts = $schema->inserts( - $postData, - array( - 'sessionId' => $session['sessionId'], - 'dataCollectionGroupId' => $this->db->id(), - 'endTime' => $session['endDate'], - 'runStatus' => 'Created by SynchWeb', - 'imageDirectory' => $imageDirectory, - 'fileTemplate' => $fileTemplate, - ) - ); - - $this->db->pq( - "INSERT INTO DataCollection ({$inserts['fieldNames']}) - VALUES ({$inserts['placeholders']}) - RETURNING dataCollectionId INTO :id", - $inserts['values'] - ); - $dataCollectionId = $this->db->id(); - - $this->db->end_transaction(); - } catch (\Exception $e) { - error_log("Failed to add DataCollection to database."); - $this->_error("Failed to add DataCollection to database.", 500); - } - - $this->_output(array('id' => $dataCollectionId)); - } - - /** - * Fetch the applicable (to EM) fields from the DataCollection - * - * Once the transfer script is available at eBIC, SynchWeb will no longer - * be required to create DataCollections and the use of dataCollectionSchema - * will be largely irrelevant and it may be worthwhile to just use an - * "ordinary" query here. - */ - public function dataCollectionGet() - { - $schema = new DataCollectionSchema(); - $selections = implode(', ', $schema->selections()); - $rows = $this->db->pq( - "SELECT - $selections - FROM DataCollection - LEFT JOIN DataCollectionFileAttachment - ON DataCollectionFileAttachment.dataCollectionId = DataCollection.dataCollectionId - LEFT JOIN DataCollectionComment - ON DataCollectionComment.dataCollectionId = DataCollection.dataCollectionId - INNER JOIN DataCollectionGroup - ON DataCollectionGroup.dataCollectionGroupId = DataCollection.dataCollectionGroupId - INNER JOIN BLSession - ON BLSession.sessionId = DataCollectionGroup.sessionId - INNER JOIN Proposal - ON Proposal.proposalId = BLSession.proposalId - WHERE CONCAT(Proposal.proposalCode, Proposal.proposalNumber) = :1 - AND DataCollection.dataCollectionId = :2", - array( - $this->arg('prop'), - $this->arg('id') - ), - false - ); - - if (sizeof($rows) == 0) { - $this->_error('No data collection'); - } - - $this->_output($schema->processRow($rows[0])); - } - - public function dataCollectionComments() - { - $rows = $this->db->pq( - "SELECT - DataCollection.comments - FROM DataCollection - INNER JOIN DataCollectionGroup - ON DataCollectionGroup.dataCollectionGroupId = DataCollection.dataCollectionGroupId - INNER JOIN BLSession - ON BLSession.sessionId = DataCollectionGroup.sessionId - INNER JOIN Proposal - ON Proposal.proposalId = BLSession.proposalId - WHERE CONCAT(Proposal.proposalCode, Proposal.proposalNumber) = :1 - AND DataCollection.dataCollectionId = :2", - array( - $this->arg('prop'), - $this->arg('id') - ), - false - ); - if (sizeof($rows) == 0) { - $this->_error('No data collection'); - } - - $this->db->pq( - "UPDATE DataCollection SET comments=:1 WHERE DataCollection.dataCollectionId = :2", - array( - json_decode($this->app->request->getBody(), true)['comments'], - $this->arg('id') - ) - ); - $this->_output(array('updated' => $this->arg('id'))); - } - - //////////////////////////////////////////////////////////////////////////// - - /** - * Returns an error message against all fields that make up imageTemplate - * - * @param string $message - */ - private function dataCollectionFileError($message) - { - $this->_error(array( - 'acquisitionSoftware' => $message, - 'imageDirectory' => $message, - 'imageSuffix' => $message, - ), 400); - } - - /** - * Get enough of a DataCollection to be able to set up processing jobs - * - * Also check the dataCollection and proposal exist and match - * - * @param string $proposal - * @param string $dataCollectionId - */ - private function dataCollectionForProcessing($proposal, $dataCollectionId) - { - $rows = $this->db->pq( - "SELECT - DataCollection.dataCollectionId, - DataCollection.imageDirectory, - DataCollection.fileTemplate, - COUNT(AutoProcProgram.autoProcProgramId) AS runningJobs - FROM DataCollection - LEFT JOIN ProcessingJob - ON ProcessingJob.dataCollectionId = DataCollection.dataCollectionId - LEFT JOIN AutoProcProgram - ON AutoProcProgram.processingJobId = ProcessingJob.processingJobId - AND AutoProcProgram.processingStatus IS NULL - INNER JOIN DataCollectionGroup - ON DataCollectionGroup.dataCollectionGroupId = DataCollection.dataCollectionGroupId - INNER JOIN BLSession - ON BLSession.sessionId = DataCollectionGroup.sessionId - INNER JOIN Proposal - ON Proposal.proposalId = BLSession.proposalId - WHERE CONCAT(Proposal.proposalCode, Proposal.proposalNumber) = :1 - AND DataCollection.dataCollectionId = :2", - array( - $proposal, - $dataCollectionId - ), - false - ); - - if (sizeof($rows) == 0) { - $this->_error('No data collection'); - } - - return $rows[0]; - } - - /** - * Returns dataCollectionId of first DataCollection associated with session - * - * Also checks for existing imageDirectory - * - * @param string $sessionId - * @param string $imageDirectory - * @param string $fileTemplate - * - * @return array|null - */ - private function dataCollectionFindExisting( - $sessionId, - $imageDirectory, - $fileTemplate - ) { - if (!$sessionId) { - return null; - } - - $rows = $this->db->pq( - "SELECT dataCollectionId FROM DataCollection - WHERE SESSIONID=:1 AND imageDirectory=:2 AND fileTemplate=:3 - LIMIT 1", - array($sessionId, $imageDirectory, $fileTemplate), - false - ); - - if (count($rows) == 0) { - return null; - } - - return $rows[0]['dataCollectionId']; - } -} diff --git a/api/src/Page/EM/DataCollectionSchema.php b/api/src/Page/EM/DataCollectionSchema.php deleted file mode 100644 index 0c83bc099..000000000 --- a/api/src/Page/EM/DataCollectionSchema.php +++ /dev/null @@ -1,437 +0,0 @@ - array( - 'label' => 'Acquisition Software', - 'required' => true, - 'default' => 'EPU', - 'options' => array('EPU', 'SerialEM'), - 'stored' => false, - /* [SCI-10082] Nowhere currently to store it. - onSelect, here is a temporary hack for the meantime: - */ - 'onSelect' => function ($row) { - $template = $row['fileTemplate']; - if (preg_match('/GridSquare_\*\/Data\/\*/', $template) == 1) { - return 'EPU'; - } - if (preg_match('/Frames\/\*/', $template) == 1) { - return 'SerialEM'; - } - return ''; - } - ), - 'imageDirectory' => array( - 'label' => 'Movie File Directory', - 'required' => true, - 'default' => 'raw', - 'pattern' => 'directory', - 'display' => false, - ), - 'shortImageDirectory' => array( - 'required' => false, - 'display' => false, - 'stored' => false, - 'onSelect' => function ($row) { - $visitCode = $row['proposal'] . '-' . $row['visit_number']; - return preg_replace( - "/.*\/{$visitCode}\//", - '', - $row['imageDirectory'] - ); - } - ), - 'imageSuffix' => array( - 'label' => 'Movie File Name Extension', - 'required' => true, - 'default' => 'tiff', - 'options' => array('tif', 'tiff', 'mrc', 'eer'), - 'displayOptions' => array('.tif', '.tiff', '.mrc', '.eer'), - 'display' => false, - ), - 'fileTemplate' => array( - 'required' => false, - 'display' => false, - ), - /* We could include `binning` here but it's something of a can of - worms... Sometimes binning is done on the microscope, sometimes - during motion correction. If `binning` in the data collection is - "1", then `motioncor_binning` in Relion should be "2" and vice-versa. - To avoid confusion, we've decided to omit it here because - (quote) "it's obvious from the image size" */ - 'pixelSizeOnImage' => array( - 'label' => 'Pixel Size', - 'unit' => 'Å/pixel', - 'required' => false, - /* - 'default' => '', - 'minValue' => 0.02, - 'maxValue' => 100.0, - 'type' => 'real' - */ - ), - /** - * In SynchWeb, imageSize is displayed as a string, but in ISpyB - * it is stored as two integers. - */ - 'imageSizeX' => array( - 'label' => 'Image Size X', - 'unit' => 'Pixels', - 'required' => false, - 'display' => false, - /* - 'type' => 'integer', - 'onUpdate' => function ($postData) { - preg_match( - '/(\d+)\sX\s(\d+)/i', - $postData['imageSize'], - $matches - ); - return $matches[1]; - }, - */ - ), - 'imageSizeY' => array( - 'label' => 'Image Size Y', - 'unit' => 'Pixels', - 'required' => false, - 'display' => false, - /* - 'type' => 'integer', - 'onUpdate' => function ($postData) { - preg_match( - '/(\d+)\sX\s(\d+)/i', - $postData['imageSize'], - $matches - ); - return $matches[2]; - }, - */ - ), - 'imageSize' => array( - 'label' => 'Image Size', - 'unit' => 'Pixels', - 'required' => false, - 'stored' => false, - /* - 'options' => array('11520 x 8184', '5760 x 4092', '4096 x 4096'), - 'default' => '5760 x 4092', - */ - 'onSelect' => function ($row) { - $isx = $row['imageSizeX']; - if (!$isx) { - $isx = 0; - } - $isy = $row['imageSizeY']; - if (!$isy) { - $isy = 0; - } - return "$isx x $isy"; - } - ), - 'numberOfImages' => array( - 'label' => 'Number of Movies', - 'required' => false, - /* - 'minValue' => 1000, - 'maxValue' => 50000, - 'type' => 'integer', - */ - ), - 'numberOfPasses' => array( - 'label' => 'Frames Per Movie', - 'display' => 'notBlank', - 'required' => false, - /* - 'required' => array( // everything except eer - 'imageSuffix' => array('tif', 'tiff', 'mrc'), - ), - 'minValue' => 30, - 'maxValue' => 60, - 'type' => 'integer', - */ - ), - 'exposureTime' => array( - 'label' => 'Total Exposure Time', - 'unit' => 'seconds', - 'required' => false, - /* - 'minValue' => 1, - 'maxValue' => 10, - 'type' => 'real', - */ - ), - 'frameLength' => array( - 'label' => 'Frame Length', - 'unit' => 'seconds', - 'required' => false, - 'stored' => false, - 'onSelect' => function ($row) { - return $row['numberOfPasses'] > 0 ? - $row['exposureTime'] / $row['numberOfPasses'] : 0; - } - ), - // Optics - 'phasePlate' => array( - 'label' => 'Phase Plate Used', - 'required' => false, - /* - 'default' => 0, - 'options' => array('0', '1', '2', '3', '4', '5', '6'), - 'displayOptions' => array( - 'None', - 'Ph P1', - 'Ph P2', - 'Ph P3', - 'Ph P4', - 'Ph P5', - 'Ph P6', - ), - */ - 'onSelect' => function ($row) { - return $row['phasePlate'] ? $row['phasePlate'] : '0'; - }, - ), - 'c2lens' => array( - 'label' => 'C2 Lens', - 'unit' => '%', - 'required' => false, - /* - 'minValue' => 40, - 'maxValue' => 65, - 'type' => 'integer', - */ - ), - 'c2aperture' => array( - 'label' => 'C2 Aperture', - 'unit' => 'μm', - 'required' => false, - /* - 'options' => array('50', '70'), - 'default' => '50', - 'type' => 'integer', - */ - ), - 'objAperture' => array( - 'label' => 'Objective Aperture', - 'unit' => 'μm', - 'display' => 'notBlank', - 'required' => false, - /* - 'required' => array( - 'phasePlate' => '0', - ), - 'options' => array('100', '70'), - 'default' => '100', - 'type' => 'integer', - */ - ), - 'magnification' => array( - 'label' => 'Magnification', - 'required' => false, - /* - 'minValue' => 53000, - 'maxValue' => 215000, - 'type' => 'integer', - */ - ), - // Electron Beam & Detector - 'voltage' => array( - 'label' => 'Voltage', - 'unit' => 'kV', - 'required' => false, - /* - 'default' => '300', - 'options' => array('200', '300'), - */ - ), - 'beamSizeAtSampleX' => array( - 'label' => 'Illuminated Area X', - 'unit' => 'nm', - 'display' => false, - 'required' => false, - /* - 'minValue' => 320, - 'maxValue' => 1500, - 'default' => 600, - 'type' => 'integer', - */ - // ISpyB uses 'μm' - SynchWeb uses 'nm' - /* - 'onUpdate' => function ($postData) { - return $postData['beamSizeAtSampleX'] * 1000.0; - }, - */ - 'onSelect' => function ($row) { - return $row['beamSizeAtSampleX'] / 1000.0; - }, - ), - 'beamSizeAtSampleY' => array( - 'label' => 'Illuminated Area Y', - 'unit' => 'nm', // ISpyB "uses" 'μm', - 'display' => false, - 'required' => false, - /* - 'minValue' => 320, - 'maxValue' => 1500, - 'default' => 600, - 'type' => 'integer', - */ - // ISpyB uses 'μm' - SynchWeb uses 'nm' - /* - 'onUpdate' => function ($postData) { - return $postData['beamSizeAtSampleY'] * 1000.0; - }, - */ - 'onSelect' => function ($row) { - return $row['beamSizeAtSampleY'] / 1000.0; - }, - ), - 'beamSizeAtSample' => array( - 'label' => 'Illuminated Area', - 'unit' => 'nm', - 'required' => false, - 'stored' => false, - 'onSelect' => function ($row) { - // ISpyB uses 'μm' - SynchWeb uses 'nm' - $bsx = $row['beamSizeAtSampleX'] / 1000.0; - $bsy = $row['beamSizeAtSampleY'] / 1000.0; - return "$bsx x $bsy"; - }, - ), - 'totalExposedDose' => array( - 'label' => 'Total Dose', - 'unit' => 'e⁻/Ų', - 'required' => false, - /* - 'default' => '0.5', - 'minValue' => 0.2, - 'maxValue' => 150.0, - 'type' => 'real', - */ - ), - 'frameDose' => array( - 'label' => 'Frame Dose', - 'unit' => 'e⁻/Ų', - 'required' => false, - 'stored' => false, - 'onSelect' => function ($row) { - return $row['numberOfPasses'] > 0 ? round( - $row['totalExposedDose'] / $row['numberOfPasses'], - 6 - ) : 0; - } - ), - 'slitGapHorizontal' => array( - 'label' => 'Energy Filter / Slit Width', - 'unit' => 'eV', - 'required' => false, - /* - 'minValue' => 5, - 'maxValue' => 20, - 'type' => 'real', - */ - ), - 'detectorManufacturer' => array( - 'label' => 'Detector Manufacturer', - 'required' => false, - 'stored' => false, - ), - 'detectorModel' => array( - 'label' => 'Detector Model', - 'required' => false, - 'stored' => false, - ), - 'detectorMode' => array( - 'label' => 'Detector Mode', - 'required' => false, - /* - 'options' => array('Counted', 'Super Resolution Counted', 'Linear'), - 'default' => 'Counted', - */ - ), - // Miscellanea - 'comments' => array( - 'label' => 'Comments', - 'required' => 'optional', - ), - 'dataCollectionId' => array( - 'display' => false, - 'required' => false, - ), - 'dataCollectionGroupId' => array( - 'display' => false, - 'required' => false, - ), - 'startTime' => array( - 'display' => false, - 'required' => false, - 'select' => 'DATE_FORMAT(DataCollection.startTime, "%d-%m-%Y %k:%i:%s")', - 'onUpdate' => function ($postData) { - return (new DateTime())->format('Y-m-d H:i:s'); - } - ), - 'endTime' => array( - 'display' => false, - 'required' => false, - 'select' => 'DATE_FORMAT(DataCollection.endTime, "%d-%m-%Y %k:%i:%s")', - ), - 'visit_number' => array( - 'display' => false, - 'required' => false, - 'stored' => false, - 'select' => 'BLSession.visit_number', - ), - 'archived' => array( - 'display' => false, - 'required' => false, - 'stored' => false, - 'select' => 'BLSession.archived', - ), - 'beamLineName' => array( - 'display' => false, - 'required' => false, - 'stored' => false, - 'select' => 'BLSession.beamLineName', - ), - 'proposal' => array( - 'display' => false, - 'required' => false, - 'stored' => false, - 'select' => 'CONCAT(Proposal.proposalCode, Proposal.proposalNumber)', - ), - 'attachmentsCount' => array( - 'display' => false, - 'required' => false, - 'stored' => false, - 'select' => 'COUNT(DataCollectionFileAttachment.dataCollectionFileAttachmentId)', - ), - 'commentsCount' => array( - 'display' => false, - 'required' => false, - 'stored' => false, - 'select' => 'COUNT(DataCollectionComment.dataCollectionCommentId)', - ), - ); - } -} diff --git a/api/src/Page/EM/Downloads.php b/api/src/Page/EM/Downloads.php deleted file mode 100644 index e87c79277..000000000 --- a/api/src/Page/EM/Downloads.php +++ /dev/null @@ -1,56 +0,0 @@ -app->contentType('image/png'); - readfile('assets/images/no_image.png'); - return; - } - - $this->browserCache(); - $this->app->response->headers->set('Content-length', filesize($file)); - $this->app->contentType('image/' . pathinfo($file, PATHINFO_EXTENSION)); - readfile($file); - } - - private function sendDownload($file) - { - if (!file_exists($file)) { - error_log("File: $file not found"); - $this->_error('No such document'); - } - - $this->browserCache(); - $pathInfo = pathinfo($file); - $this->app->response->headers->set('Content-length', filesize($file)); - $this->app->response->headers->set( - 'Content-Disposition', - 'attachment; filename="' . $pathInfo['basename'] - ); - $this->app->contentType('application/' . $pathInfo['extension']); - readfile($file); - } - - private function browserCache() - { - $expires = 60 * 60 * 24 * 14; - $this->app->response->headers->set( - 'Pragma', - 'public' - ); - $this->app->response->headers->set( - 'Cache-Control', - 'maxage=' . $expires - ); - $this->app->response->headers->set( - 'Expires', - gmdate('D, d M Y H:i:s', time() + $expires) . ' GMT' - ); - } -} diff --git a/api/src/Page/EM/MotionCorrection.php b/api/src/Page/EM/MotionCorrection.php deleted file mode 100644 index 084f46217..000000000 --- a/api/src/Page/EM/MotionCorrection.php +++ /dev/null @@ -1,116 +0,0 @@ -has_arg('imageNumber') ? $this->arg('imageNumber') : 1; - - $rows = $this->db->pq( - "SELECT - mc.firstFrame, - mc.lastFrame, - mc.dosePerFrame, - mc.doseWeight, - mc.totalMotion, - mc.averageMotionPerFrame, - mc.patchesUsedX, - mc.patchesUsedY, - mc.comments, - mc.imageNumber, - m.createdTimeStamp - FROM MotionCorrection mc - INNER JOIN AutoProcProgram app ON app.autoProcProgramId = mc.autoProcProgramId - INNER JOIN Movie m ON m.movieId = mc.movieId - INNER JOIN DataCollection dc ON dc.dataCollectionId = m.dataCollectionId - INNER JOIN DataCollectionGroup dcg ON dcg.dataCollectionGroupId = dc.dataCollectionGroupId - INNER JOIN BLSession bls ON bls.sessionId = dcg.sessionId - INNER JOIN Proposal p ON p.proposalId = bls.proposalId - WHERE CONCAT(p.proposalCode, p.proposalNumber) = :1 - AND mc.autoProcProgramId = :2 - AND mc.imageNumber = :3", - array($this->arg('prop'), $this->arg('programId'), $image), - false - ); - - if (sizeof($rows) == 0) { - $this->_error('No such micrograph'); - } - - $row = $rows[0]; - - $formatColumns = array( - 'totalMotion' => 1, - 'averageMotionPerFrame' => 2 - ); - foreach ($formatColumns as $column => $decimals) { - $row[$column] = number_format($row[$column], $decimals, '.', ''); - } - - $this->_output($row); - } - - public function motionCorrectionDriftPlot() - { - $rows = $this->db->pq( - "SELECT mcd.deltaX, mcd.deltaY - FROM MotionCorrectionDrift mcd - INNER JOIN MotionCorrection mc ON mc.motionCorrectionId = mcd.motionCorrectionId - INNER JOIN Movie m ON m.movieId = mc.movieId - INNER JOIN DataCollection dc ON dc.dataCollectionId = m.dataCollectionId - INNER JOIN DataCollectionGroup dcg ON dcg.dataCollectionGroupId = dc.dataCollectionGroupId - INNER JOIN BLSession bls ON bls.sessionId = dcg.sessionId - INNER JOIN Proposal p ON p.proposalId = bls.proposalId - WHERE CONCAT(p.proposalCode, p.proposalNumber) = :1 - AND mc.autoProcProgramId = :2 - AND mc.imageNumber = :3", - array( - $this->arg('prop'), - $this->arg('programId'), - $this->has_arg('imageNumber') ? $this->arg('imageNumber') : 1 - ), - false - ); - $xAxis = array(); - $yAxis = array(); - foreach ($rows as $row) { - array_push($xAxis, $row['deltaX']); - array_push($yAxis, $row['deltaY']); - } - $this->_output(array('xAxis' => $xAxis, 'yAxis' => $yAxis)); - } - - private function motionCorrectionImage($imageName) - { - $image = $this->has_arg('imageNumber') ? $this->arg('imageNumber') : 1; - - $rows = $this->db->pq( - "SELECT mc.{$imageName} - FROM MotionCorrection mc - INNER JOIN AutoProcProgram ap ON ap.autoProcProgramId = mc.autoProcProgramId - INNER JOIN Movie m ON m.movieId = mc.movieId - INNER JOIN DataCollection dc ON dc.dataCollectionId = m.dataCollectionId - INNER JOIN DataCollectionGroup dcg ON dcg.dataCollectionGroupId = dc.dataCollectionGroupId - INNER JOIN BLSession bls ON bls.sessionId = dcg.sessionId - INNER JOIN Proposal p ON p.proposalId = bls.proposalId - WHERE CONCAT(p.proposalCode, p.proposalNumber) = :1 - AND mc.autoProcProgramId = :2 - AND mc.imageNumber = :3", - array($this->arg('prop'), $this->arg('programId'), $image), - false - ); - - if (sizeof($rows) == 0) { - $this->_error('No such micrograph'); - } - - $this->sendImage($rows[0][$imageName]); - } - - public function motionCorrectionSnapshot() - { - $this->motionCorrectionImage('micrographSnapshotFullPath'); - } -} diff --git a/api/src/Page/EM/Picker.php b/api/src/Page/EM/Picker.php deleted file mode 100644 index 37b92ad26..000000000 --- a/api/src/Page/EM/Picker.php +++ /dev/null @@ -1,72 +0,0 @@ -db->pq( - "SELECT - pp.particleDiameter, - pp.numberOfParticles, - pp.summaryImageFullPath, - mc.imageNumber, - m.createdTimeStamp - FROM ParticlePicker pp - LEFT JOIN MotionCorrection mc ON mc.motionCorrectionId = pp.firstMotionCorrectionId - LEFT JOIN Movie m ON m.movieId = mc.movieId - INNER JOIN DataCollection dc ON dc.dataCollectionId = m.dataCollectionId - INNER JOIN DataCollectionGroup dcg ON dcg.dataCollectionGroupId = dc.dataCollectionGroupId - INNER JOIN BLSession bls ON bls.sessionId = dcg.sessionId - INNER JOIN Proposal p ON p.proposalId = bls.proposalId - WHERE CONCAT(p.proposalCode, p.proposalNumber) = :1 - AND pp.programId = :2 - AND mc.imageNumber = :3", - array( - $this->arg('prop'), - $this->arg('programId'), - $this->has_arg('imageNumber') ? $this->arg('imageNumber') : 1 - ), - false - ); - - if (!sizeof($rows)) { - $this->_error('No such pick'); - } - $row = $rows[0]; - - $row['summaryImageFullPath'] = file_exists($row['summaryImageFullPath']) ? 1 : 0; - - $this->_output($row); - } - - public function pickerImage() - { - $rows = $this->db->pq( - "SELECT pp.summaryImageFullPath - FROM ParticlePicker pp - INNER JOIN MotionCorrection mc ON mc.motionCorrectionId = pp.firstMotionCorrectionId - LEFT JOIN Movie m ON m.movieId = mc.movieId - INNER JOIN DataCollection dc ON dc.dataCollectionId = m.dataCollectionId - INNER JOIN DataCollectionGroup dcg ON dcg.dataCollectionGroupId = dc.dataCollectionGroupId - INNER JOIN BLSession bls ON bls.sessionId = dcg.sessionId - INNER JOIN Proposal p ON p.proposalId = bls.proposalId - WHERE CONCAT(p.proposalCode, p.proposalNumber) = :1 - AND pp.programId = :2 - AND mc.imageNumber = :3", - array( - $this->arg('prop'), - $this->arg('programId'), - $this->has_arg('imageNumber') ? $this->arg('imageNumber') : 1 - ), - false - ); - - if (sizeof($rows) == 0) { - $this->_error('No such particle picking'); - } - - $this->sendImage($rows[0]['summaryImageFullPath']); - } -} diff --git a/api/src/Page/EM/ProcessingJobs.php b/api/src/Page/EM/ProcessingJobs.php deleted file mode 100644 index d4ddc1531..000000000 --- a/api/src/Page/EM/ProcessingJobs.php +++ /dev/null @@ -1,159 +0,0 @@ -has_arg('id')) { - $this->_error('No data collection provided'); - } - - $subqueries = $this->processingJobsSubQueries(); - - /* FetchTime is to trigger re-fetches of the entities that depend on - ProcessingJob */ - $processingJobs = $this->db->pq( - "SELECT - ProcessingJob.processingJobId, - ProcessingJob.dataCollectionId, - ProcessingJob.recordTimestamp, - AutoProcProgram.autoProcProgramId, - AutoProcProgram.processingStartTime, - AutoProcProgram.processingEndTime, - $subqueries - NOW() as fetchTime, - CASE - WHEN ( - AutoProcProgram.processingJobId IS NULL - ) THEN 'submitted' - WHEN ( - AutoProcProgram.processingStartTime IS NULL - AND AutoProcProgram.processingStatus IS NULL - ) THEN 'queued' - WHEN ( - AutoProcProgram.processingStartTime IS NOT NULL - AND AutoProcProgram.processingStatus IS NULL - ) THEN 'running' - WHEN ( - AutoProcProgram.processingStartTime IS NOT NULL - AND AutoProcProgram.processingStatus = 0 - ) THEN 'failure' - WHEN ( - AutoProcProgram.processingStartTime IS NOT NULL - AND AutoProcProgram.processingStatus = 1 - ) THEN 'success' - ELSE '' - END AS processingStatusDescription - FROM ProcessingJob - LEFT JOIN AutoProcProgram - ON AutoProcProgram.processingJobId = ProcessingJob.processingJobId - INNER JOIN DataCollection - ON DataCollection.dataCollectionId = ProcessingJob.dataCollectionId - INNER JOIN DataCollectionGroup - ON DataCollectionGroup.dataCollectionGroupId = DataCollection.dataCollectionGroupId - INNER JOIN BLSession - ON BLSession.sessionId = DataCollectionGroup.sessionId - INNER JOIN Proposal - ON Proposal.proposalId = BLSession.proposalId - WHERE CONCAT(Proposal.proposalCode, Proposal.proposalNumber) = :1 - AND DataCollection.dataCollectionId = :2 - LIMIT :3, :4", - $this->paginationArguments( - array($this->arg('prop'), $this->arg('id')) - ), - false - ); - - $this->_output(array( - 'total' => $this->processingJobsTotal(), - 'data' => $this->processingJobsTidy($processingJobs), - )); - } - - //////////////////////////////////////////////////////////////////////////// - - private function processingJobsTotal() - { - $total = $this->db->pq( - "SELECT count(pj.processingJobId) as total - FROM ProcessingJob pj - LEFT JOIN AutoProcProgram app ON pj.processingJobId = app.processingJobId - INNER JOIN DataCollection dc ON dc.dataCollectionId = pj.dataCollectionId - INNER JOIN DataCollectionGroup dcg ON dcg.dataCollectionGroupId = dc.dataCollectionGroupId - INNER JOIN BLSession bls ON bls.sessionId = dcg.sessionId - INNER JOIN Proposal p ON p.proposalId = bls.proposalId - WHERE CONCAT(p.proposalCode, p.proposalNumber) = :1 - AND dc.dataCollectionId = :2", - array($this->arg('prop'), $this->arg('id')), - false - ); - return intval($total[0]['total']); - } - - //////////////////////////////////////////////////////////////////////////// - - private function processingJobsTidy($rows) - { - $noNullFields = array ( - 'ctfLatest', 'ctfMax', - 'mcLatest', 'mcMax', - 'pickLatest', 'pickMax', - ); - return array_map( - function ($row) use ($noNullFields) { - $result = array(); - foreach ($row as $key => $value) { - $result[$key] = in_array( - $key, - $noNullFields - ) && $value == null ? '' : $value; - } - return $result; - }, - $rows - ); - } - - - //////////////////////////////////////////////////////////////////////////// - - private function processingJobsLatestQuery($tables, $key) - { - return "(SELECT MotionCorrection.imageNumber FROM $tables - INNER JOIN Movie ON Movie.movieId = MotionCorrection.movieId - WHERE MotionCorrection.autoProcProgramId = AutoProcProgram.autoProcProgramId - ORDER BY Movie.createdTimeStamp DESC, MotionCorrection.imageNumber DESC - LIMIT 1) AS {$key}Latest,"; - } - - private function processingJobsMaxQuery($tables, $key) - { - return "(SELECT MAX(MotionCorrection.imageNumber) FROM $tables - WHERE MotionCorrection.autoProcProgramId = AutoProcProgram.autoProcProgramId) as {$key}Max,"; - } - - private function processingJobsSubQueries() - { - $specs = array( - 'mc' => 'MotionCorrection', - 'ctf' => 'CTF INNER JOIN MotionCorrection - ON MotionCorrection.motionCorrectionId = CTF.motionCorrectionId', - 'pick' => 'ParticlePicker INNER JOIN MotionCorrection - ON MotionCorrection.motionCorrectionId = ParticlePicker.firstMotionCorrectionId', - ); - $subqueries = array(); - foreach ($specs as $key => $tables) { - array_push( - $subqueries, - $this->processingJobsLatestQuery($tables, $key) - ); - array_push( - $subqueries, - $this->processingJobsMaxQuery($tables, $key) - ); - } - return implode("\n", $subqueries); - } -} diff --git a/api/src/Page/EM/Relion.php b/api/src/Page/EM/Relion.php deleted file mode 100644 index c6a33a774..000000000 --- a/api/src/Page/EM/Relion.php +++ /dev/null @@ -1,248 +0,0 @@ -_output($schema->clientSchema()); - } - - /** - * Create a data collection and a processing job then enqueue the job - * - * @SuppressWarnings(PHPMD.LongVariable) - */ - public function relionStart() - { - global $zocalo_mx_reprocess_queue; - - $this->configExitIfNoMicroscopes(); - - $proposalCode = $this->arg('prop'); - $dataCollectionId = $this->arg('id'); - - $session = $this->sessionFromDataCollection( - $proposalCode, - $dataCollectionId - ); - - $this->sessionExitIfNotActive($session); - - $dataCollection = $this->dataCollectionForProcessing( - $proposalCode, - $dataCollectionId - ); - - if ($dataCollection['runningJobs'] > 0) { - $message = 'A Relion job is already running on this data collection!'; - error_log("$message - $proposalCode $dataCollectionId"); - $this->_error($message, 400); - } - - $preparedData = $this->relionPreparePostData($dataCollection, $session); - - $processingJobId = $this->relionAddJob($dataCollectionId, $preparedData); - - // Send job to processing queue - $message = array( - 'parameters' => array('ispyb_process' => $processingJobId) - ); - - $this->_send_zocalo_message($zocalo_mx_reprocess_queue, $message); - - $output = array( - 'timestamp' => gmdate('c', time()), - 'message' => $message - ); - - $this->_output($output); - } - - /** - * Request that a currently running job be stopped - * - * @SuppressWarnings(PHPMD.LongVariable) - */ - public function relionStop() - { - global $zocalo_mx_reprocess_queue; - - $rows = $this->db->pq( - "SELECT ProcessingJob.processingJobId - FROM ProcessingJob - INNER JOIN DataCollection - ON DataCollection.dataCollectionId = ProcessingJob.dataCollectionId - INNER JOIN DataCollectionGroup - ON DataCollectionGroup.dataCollectionGroupId = DataCollection.dataCollectionGroupId - INNER JOIN BLSession - ON BLSession.sessionId = DataCollectionGroup.sessionId - INNER JOIN Proposal - ON Proposal.proposalId = BLSession.proposalId - WHERE CONCAT(Proposal.proposalCode, Proposal.proposalNumber) = :1 - AND processingJobId = :2", - array($this->arg('prop'), $this->arg('jobId')), - false - ); - - if (count($rows) == 0) { - $message = 'Processing job not found!'; - error_log($message); - $this->_error($message, 400); - } - - $result = $rows[0]; - - $parameters = array( - 'ispyb_process' => $result['processingJobId'], - ); - $recipe = 'relion-stop'; - $this->_submit_zocalo_recipe($recipe, $parameters); - - $this->_output(array( - 'timestamp' => gmdate('c', time()), - 'message' => array( - 'recipe' => $recipe, - 'parameters' => $parameters, - ) - )); - } - - /** - * Output the parameters used for a given processing job - * - * @SuppressWarnings(PHPMD.StaticAccess) - */ - public function relionParameters() - { - $rows = $this->db->pq( - "SELECT - ProcessingJobParameter.parameterKey, - ProcessingJobParameter.parameterValue - FROM ProcessingJobParameter - INNER JOIN ProcessingJob - ON ProcessingJob.processingJobId = ProcessingJobParameter.processingJobId - INNER JOIN DataCollection - ON DataCollection.dataCollectionId = ProcessingJob.dataCollectionId - INNER JOIN DataCollectionGroup - ON DataCollectionGroup.dataCollectionGroupId = DataCollection.dataCollectionGroupId - INNER JOIN BLSession - ON BLSession.sessionId = DataCollectionGroup.sessionId - INNER JOIN Proposal - ON Proposal.proposalId = BLSession.proposalId - WHERE CONCAT(Proposal.proposalCode, Proposal.proposalNumber) = :1 - AND ProcessingJobParameter.processingJobId = :2", - array($this->arg('prop'), $this->arg('jobId')), - false - ); - - if (!sizeof($rows)) { - $this->_error('No parameters for processing job'); - } - - $results = array(); - foreach ($rows as $row) { - $results[$row['parameterKey']] = $row['parameterValue']; - } - - $schema = new RelionSchema(); - $this->_output($schema->processRow($results)); - } - - //////////////////////////////////////////////////////////////////////////// - - private function relionPreparePostData($dataCollection, $session) - { - global $visit_directory; - - $postData = json_decode($this->app->request->getBody(), true); - - // used to validate eer_grouping - preg_match('/\.([\w]*)$/', $dataCollection['fileTemplate'], $matches); - $postData['import_images_ext'] = $matches[1]; - - $schema = new RelionSchema(); - $validator = new SchemaValidator($schema); - list($invalid, $cleanData) = $validator->validatePostData($postData); - - if (count($invalid) > 0) { - $this->_error($invalid, 400); - } - - // RelionSchema uses session_path to get a full path for gain_reference_file - $cleanData['session_path'] = $this->sessionSubstituteValuesInPath( - $session, - $visit_directory - ); - - // Relion will need import_images to fetch the raw images - $cleanData['import_images'] = $dataCollection['imageDirectory'] . - '/' . $dataCollection['fileTemplate']; - return $schema->prepareDataForInsert($cleanData); - } - - private function relionAddJob($dataCollectionId, $workflowParameters) - { - $processingJobId = null; - - try { - $this->db->start_transaction(); - - $this->db->pq( - "INSERT INTO ProcessingJob ( - dataCollectionId, - displayName, - comments, - recipe, - automatic - ) - VALUES (:1, :2, :3, :4, :5) - RETURNING processingJobId INTO :id", - array( - $dataCollectionId, - 'RELION', - 'Submitted via SynchWeb', - 'relion', - 0 - ) - ); - $processingJobId = $this->db->id(); - - foreach ($workflowParameters as $key => $value) { - $this->db->pq( - "INSERT INTO ProcessingJobParameter ( - processingJobId, - parameterKey, - parameterValue - ) - VALUES (:1, :2, :3)", - array( - $processingJobId, - $key, - (is_bool($value) ? var_export($value, true) : $value) - ) - ); - } - - $this->db->end_transaction(); - } catch (\Exception $exception) { - $message = 'Failed to add ProcessingJob to database.'; - error_log($message); - error_log($exception->getMessage()); - $this->_error($message, 500); - } - - return $processingJobId; - } -} diff --git a/api/src/Page/EM/RelionSchema.php b/api/src/Page/EM/RelionSchema.php deleted file mode 100644 index 432199684..000000000 --- a/api/src/Page/EM/RelionSchema.php +++ /dev/null @@ -1,327 +0,0 @@ - array( - 'label' => 'Acquisition Software', - 'required' => false, - 'onUpdate' => function ($postData) { - return strpos( - $postData['import_images'], - 'GridSquare_*/Data/*.' - ) == 'false' ? 'SerialEM' : 'EPU'; - }, - ), - 'import_images_ext' => array( - 'required' => false, // this is true if it's an on-screen field - 'display' => false, // this shouldn't be here for an on-screen field - 'stored' => false, - 'onSelect' => function ($row) { - preg_match('/\.([\w]*)$/', $row['import_images'], $matches); - return $matches[1]; - }, - ), - 'import_images' => array( - 'label' => 'Import Images', - 'required' => false, - ), - 'wantGainReferenceFile' => array( - 'label' => 'Gain Reference File', - 'default' => false, - 'required' => true, - 'stored' => false, - 'display' => false, - 'type' => 'boolean', - 'onSelect' => function ($row) { - return basename($row['motioncor_gainreference']) != 'gain.mrc'; - }, - ), - 'motioncor_gainreference' => array( - 'label' => 'Gain Reference File Name', - 'pattern' => 'filename', - 'default' => 'gain.mrc', - 'required' => array ( - 'wantGainReferenceFile' => true - ), - 'onSelect' => function ($row) { - return basename($row['motioncor_gainreference']); - }, - 'onUpdate' => function ($postData) { - $name = array_key_exists('motioncor_gainreference', $postData) ? - $postData['motioncor_gainreference'] : 'gain.mrc'; - return $postData['session_path'] . - "/processing/$name"; - }, - ), - 'session_path' => array( - /* session_path should be "injected" into the posted data - from BLSession - It's used to handle the full path name of the - Gain Reference File - */ - 'stored' => false, - 'display' => false, - 'required' => false, - ), - 'voltage' => array( - 'label' => 'Voltage', - 'unit' => 'kV', - 'default' => '300', - 'required' => true, - 'options' => array('200', '300'), - ), - 'Cs' => array( - 'label' => 'Spherical Aberration', - 'unit' => 'mm', - 'default' => '2.7', - 'required' => true, - 'options' => array('2.7'), - 'displayOptions' => array('2.7 (Talos/Krios)'), - 'type' => 'real' - ), - 'ctffind_do_phaseshift' => array( - 'label' => 'Phase Plate Used', - 'default' => false, - 'required' => true, - 'type' => 'boolean' - ), - 'angpix' => array( - 'label' => 'Pixel Size', - 'unit' => 'Å/pixel', - 'default' => '', - 'required' => true, - 'minValue' => 0.02, - 'maxValue' => 100.0, - 'type' => 'real' - ), - 'eer_grouping' => array( - 'label' => 'EER fractionation', - 'extraDescription' => array( - 'Number of frames to group into a fraction.', - 'Excess frames are discarded.' - ), - 'default' => '20', - 'required' => array( - 'import_images_ext' => 'eer' - ), - 'minValue' => 1, - 'type' => 'integer' - ), - 'motioncor_binning' => array( - 'label' => 'Motion Correction Binning', - 'default' => '1', - 'required' => true, - 'options' => array('1', '2'), - 'type' => 'integer' - ), - 'motioncor_doseperframe' => array( - 'label' => 'Dose per frame', - 'unit' => 'e⁻/Ų', - 'required' => true, - 'minValue' => 0.003, - 'maxValue' => 5, - 'type' => 'real' - ), - 'stop_after_ctf_estimation' => array( - 'label' => 'Stop after CTF estimation', - 'default' => false, - 'required' => true, - 'type' => 'boolean' - ), - 'do_class2d' => array( - 'label' => 'Do 2D Classification', - 'default' => true, - 'required' => array( - 'stop_after_ctf_estimation' => false - ), - 'stored' => array( - 'stop_after_ctf_estimation' => false - ), - 'type' => 'boolean' - ), - 'do_class3d' => array( - 'label' => 'Do 3D Classification', - 'default' => true, - 'required' => array( - 'stop_after_ctf_estimation' => false - ), - 'stored' => array( - 'stop_after_ctf_estimation' => false - ), - 'type' => 'boolean' - ), - 'use_fsc_criterion' => array( - 'label' => 'Best initial model from FSC', - 'default' => false, - 'required' => array( - 'stop_after_ctf_estimation' => false, - 'do_class3d' => true, - ), - 'stored' => array( - 'stop_after_ctf_estimation' => false, - 'do_class3d' => true, - ), - 'type' => 'boolean', - ), - 'autopick_do_cryolo' => array( - 'label' => 'Use crYOLO', - 'extraDescription' => array( - 'Academic users only.', - 'Not licensed for industry users.' - ), - 'default' => false, - 'required' => array( - 'stop_after_ctf_estimation' => false - ), - 'stored' => array( - 'stop_after_ctf_estimation' => false, - ), - 'type' => 'boolean', - ), - 'autopick_LoG_diam_min' => array( - 'label' => 'Minimum Diameter (Å)', - 'default' => '', - 'required' => array( - 'stop_after_ctf_estimation' => false - ), - 'stored' => array( - 'stop_after_ctf_estimation' => false - ), - 'minValue' => 0.02, - 'maxValue' => 1024.0, - 'lessThan' => 'autopick_LoG_diam_max', - 'type' => 'real' - ), - 'autopick_LoG_diam_max' => array( - 'label' => 'Maximum Diameter', - 'unit' => 'Å', - 'default' => '', - 'required' => array( - 'stop_after_ctf_estimation' => false, - ), - 'stored' => array( - 'stop_after_ctf_estimation' => false, - ), - 'minValue' => 0.02, - 'maxValue' => 4000.0, - 'greaterThan' => 'autopick_LoG_diam_min', - 'type' => 'real' - ), - 'mask_diameter' => array( - 'label' => 'Mask Diameter', - 'unit' => 'Å', - 'default' => '', - 'required' => array( - 'stop_after_ctf_estimation' => false - ), - 'stored' => array( - 'stop_after_ctf_estimation' => false - ), - 'minValue' => 0.1, - 'maxValue' => 1024, - 'type' => 'real' - ), - 'extract_downscale' => array( - // doesn't exist in form - // set to true if stop_after_ctf_estimation is false - 'label' => 'Extract Downscale', - 'required' => false, - ), - 'extract_boxsize' => array( - 'label' => 'Box Size', - 'unit' => 'pixels', - 'extraDescription' => array( - 'Box size before binning.' - ), - 'default' => '', - 'required' => array( - 'stop_after_ctf_estimation' => false - ), - 'stored' => array( - 'stop_after_ctf_estimation' => false - ), - 'minValue' => 0.1, - 'maxValue' => 1024.0, - 'type' => 'real' - ), - 'extract_small_boxsize' => array( - 'label' => 'Downsample Box Size', - 'unit' => 'pixels', - 'default' => '', - 'extraDescription' => array( - 'Box size before binning.' - ), - 'required' => array( - 'stop_after_ctf_estimation' => false - ), - 'stored' => array( - 'stop_after_ctf_estimation' => false - ), - 'minValue' => 0.1, - 'maxValue' => 1024.0, - 'type' => 'real', - ), - 'wantCalculate' => array( - 'label' => 'Calculate For Me', - 'default' => true, - 'required' => array( - 'stop_after_ctf_estimation' => false, - ), - 'stored' => false, - 'type' => 'boolean' - ), - 'want2ndPass' => array( - 'label' => 'Do Second Pass', - 'default' => false, - 'required' => array( - 'stop_after_ctf_estimation' => false, - ), - 'stored' => false, - 'type' => 'boolean', - ), - 'do_class2d_pass2' => array( - 'label' => 'Do 2D Classification', - 'default' => true, - 'required' => array( - 'stop_after_ctf_estimation' => false, - 'want2ndPass' => true - ), - 'stored' => array( - 'stop_after_ctf_estimation' => false, - 'want2ndPass' => true - ), - 'type' => 'boolean', - ), - 'do_class3d_pass2' => array( - 'label' => 'Do 3D Classification', - 'default' => false, - 'required' => array( - 'stop_after_ctf_estimation' => false, - 'want2ndPass' => true - ), - 'stored' => array( - 'stop_after_ctf_estimation' => false, - 'want2ndPass' => true - ), - 'type' => 'boolean', - ), - ); - } -} diff --git a/api/src/Page/EM/Schema.php b/api/src/Page/EM/Schema.php deleted file mode 100644 index 26e14874c..000000000 --- a/api/src/Page/EM/Schema.php +++ /dev/null @@ -1,303 +0,0 @@ - human readable label, - * 'unit' => (e.g. ), "Å/pixel", "kV" - * 'extraDescription' => Extra human readable description - * 'display' => true, false or 'notBlank' - * indicate if the value should be displayed in the UI - * 'default' => default value, - * - * validation options - * 'required' => a boolean field or a string with the value - * "optional" - the field can be blank but it will - * still be used - * 'options' => list of option values for option lists, - * 'displayOptions' => alternate option list for display, - * 'pattern' => pattern for validation ("filename", "directory"), - * 'minValue' => minimum value, - * 'maxValue' => maximum value, - * 'type' => check value is type ("boolean", "integer", "real") - * 'lessThan' => another field that this one must be less than, - * 'greaterThan' => another field that this one must be greater than, - * - * used by API only - * 'select' => select clause to get this field - * 'stored' => a boolean field - if it evaluates to zero - * the field should not be inserted or selected - * TODO: how can you use this in a SELECT when the - * other fields the boolean may depend on aren't - * available yet? - * 'onSelect' => PHP function to transform selected data - * 'onUpdate' => PHP function to transform data prior to insert - * - * If a field above is described as having a boolean option, the value can - * be either true or false (a boolean or a string) or an array keyed by - * other fields with an expected value. e.g.: - * 'required' => array( - * 'firstField' => 'true', - * 'secondField' => 10, - * ) - * indicates the given field is only required if 'firstField' is true and - * secondField is 10, otherwise the given field is not required. - * - * @return array - */ - - abstract public function schema(); - - //////////////////////////////////////////////////////////////////////////// - - public function __construct() - { - $this->allRules = $this->schema(); - } - - public function ruleSet() - { - return $this->allRules; - } - - /** - * Post process a row SELECTed from the database - * - * @param array $row - * - * @return array - */ - public function processRow($row) - { - $processedRow = array_map( - function ($value) { - return strval($value); - }, - $row - ); - foreach ($this->allRules as $fieldName => $rules) { - if (array_key_exists('onSelect', $rules)) { - $processedRow[$fieldName] = call_user_func( - $rules['onSelect'], - $row - ); - } - } - return $processedRow; - } - - /** - * List of fields for an SQL SELECT query - * - * @return string[] - */ - public function selections() - { - $selections = array(); - foreach ($this->allRules as $fieldName => $rules) { - $selection = $this->selection($fieldName, $rules); - if ($selection) { - $selections[] = $selection; - } - } - - return $selections; - } - - /** - * Single field for an SQL SELECT query - * - * @param string $fieldName - * @param array $rules - schema rules for the named field - * - * @return string - SQL SELECT clause item - */ - private function selection($fieldName, $rules) - { - if (array_key_exists('select', $rules)) { - return $rules['select'] . ' AS ' . $fieldName; - } - if (array_key_exists('stored', $rules) && $rules['stored'] === false) { - return false; - } - return $this->defaultTable() . '.' . $fieldName; - } - - //////////////////////////////////////////////////////////////////////////// - - /** - * Options such as required and stored can reference the values of - * other fields to determine their boolean value - * - * @param string|array $rule - * @param array $postData - * - * @return boolean - */ - public function checkBooleanOption($rule, $postData) - { - if (gettype($rule) != 'array') { - return $this->typedValue('boolean', $rule); - } - - foreach ($rule as $otherArgument => $expectedValue) { - $actualValue = $this->typedParameter($otherArgument, $postData); - $matched = gettype($expectedValue) == 'array' ? - in_array($actualValue, $expectedValue) : - $actualValue === $expectedValue; - if (!$matched) { - return false; - } - } - - return true; - } - - /** - * Type coerce the given value - * - * @param string $type - * @param mixed $value - the value to coerce - * - * @return mixed - the value coerced into the given type - */ - public function typedValue($type, $value) - { - if ($type == 'boolean') { - return in_array($value, array('true', '1')); - } - - $phpType = $type == 'real' ? 'double' : $type; - - $new = $value; - settype($new, $phpType); - return $new; - } - - /** - * Get the expected type of a field. - * - * @param string $fieldName - * @param array $theData - * - * @return mixed - */ - public function typedParameter($fieldName, $theData) - { - $rules = $this->allRules[$fieldName]; - $type = array_key_exists('type', $rules) ? $rules['type'] : 'string'; - return $this->typedValue($type, $theData[$fieldName]); - } - - //////////////////////////////////////////////////////////////////////////// - - /** - * Provide a list of key value pairs for inserting - * - * @param array $raw - data to insert - * @param array $prepared - any pre-set data already prepared for insert - * - * @return array - */ - public function prepareDataForInsert($raw, $prepared = array()) - { - $schema = $this->allRules; - foreach ($raw as $fieldName => $value) { - if (array_key_exists($fieldName, $prepared)) { - continue; - } - $rules = $schema[$fieldName]; - if (array_key_exists('stored', $rules)) { - $stored = $this->checkBooleanOption( - $rules['stored'], - $raw - ); - if (!$stored) { - continue; - } - } - if (gettype($value) == 'boolean') { - $value = $value ? 1 : 0; - } - $prepared[$fieldName] = $value; - } - foreach ($schema as $fieldName => $rules) { - if (array_key_exists('onUpdate', $rules)) { - $prepared[$fieldName] = call_user_func( - $rules['onUpdate'], - $raw - ); - } - } - return $prepared; - } - - /** - * Provide a list of field names to be inserted, a placeholder string, and an array of values - * - * @param array $raw - data to insert - * @param array $prePrepared - any pre-set data already prepared for insert - * - * @return array - */ - public function inserts($raw, $prePrepared = array()) - { - $prepared = $this->prepareDataForInsert($raw, $prePrepared); - $values = array_values($prepared); - return array( - 'fieldNames' => implode(',', array_keys($prepared)), - 'values' => $values, - 'placeholders' => implode(',', array_map( - function ($number) { - return ':' . ($number + 1); - }, - array_keys($values) - )) - ); - } - - //////////////////////////////////////////////////////////////////////////// - - /** - * A version of the schema useful on client side with some fields redacted - * - * @return array - */ - public function clientSchema() - { - $hiddenRules = array( - 'select', - 'stored', - 'onSelect', - 'onUpdate', - ); - $clientSchema = array(); - foreach ($this->allRules as $fieldName => $rules) { - $clientRules = array(); - foreach ($rules as $ruleName => $ruleData) { - if (!in_array($ruleName, $hiddenRules)) { - $clientRules[$ruleName] = $ruleData; - } - } - if (count($clientRules) > 0) { - $clientSchema[$fieldName] = $clientRules; - } - } - return $clientSchema; - } -} diff --git a/api/src/Page/EM/SchemaValidator.php b/api/src/Page/EM/SchemaValidator.php deleted file mode 100644 index b8d72a818..000000000 --- a/api/src/Page/EM/SchemaValidator.php +++ /dev/null @@ -1,283 +0,0 @@ -schema = $schema; - } - - public function validatePostData($postData) - { - $this->postData = $postData; - $invalid = array(); - $valid = array(); - - foreach ($this->schema->ruleSet() as $fieldName => $rules) { - $value = $this->postData[$fieldName]; - - if (!$this->isRequired($rules, $value)) { - continue; - } - - $result = $this->validateArgument($fieldName, $value, $rules); - if ($result === true) { - $valid[$fieldName] = $value; - continue; - } - - $invalid[$fieldName] = $result; - } - - return array($invalid, $valid); - } - - protected function validateArgument($fieldName, $value, $rules) - { - $typedValue = $this->schema->typedParameter( - $fieldName, - $this->postData - ); - foreach ($rules as $ruleName => $rule) { - $constraint = 'check' . ucfirst($ruleName); - /* Not all fields in a rule set are, strictly speaking, "rules" - If there's no checker here, it's probably a label or something - else "neutral" - */ - if (method_exists($this, $constraint)) { - $flagOrMessage = call_user_func( - array($this, $constraint), - $rule, - $value, - $typedValue - ); - if ($flagOrMessage !== true) { - return $flagOrMessage; - } - } - } - return true; - } - - /** - * Check for the 'required' validation rule - * - * @param boolean|string|array $required - * @param mixed $value - * @param mixed $typedValue - * - * @return boolean|string - either boolean true or an error message - * - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - * @SuppressWarnings(PHPMD.UnusedPrivateMethod) - */ - private function checkRequired($required, $value, $typedValue) - { - if ($required === 'optional') { - return true; - } - - return ($value !== '' && $value !== null) ? true : 'is required'; - } - - /** - * Check for the 'pattern' validation rule - * - * @param array $patternName - * @param mixed $value - * @param mixed $typedValue - * - * @return boolean|string - either boolean true or an error message - * - * @SuppressWarnings(PHPMD.UnusedPrivateMethod) - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - */ - private function checkPattern($patternName, $value, $typedValue) - { - $patterns = array( - 'filename' => array( - 'regex' => '/^[\w\-_]+\.[\w]{3,4}$/', - 'message' => 'must be a valid filename' - ), - 'directory' => array( - 'regex' => '/^[\w\-_]+$/', - 'message' => 'only alphanumerics, "-" and "_" allowed' - ) - ); - $pattern = $patterns[$patternName]; - - return preg_match($pattern['regex'], $value) === 1 ? - true : $pattern['message']; - } - - /** - * Check for the 'options' validation rule - * - * @param array $options - all possible values the field can have - * @param mixed $value - * @param mixed $typedValue - * - * @return boolean|string - either boolean true or an error message - * - * @SuppressWarnings(PHPMD.UnusedPrivateMethod) - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - */ - private function checkOptions($options, $value, $typedValue) - { - return in_array("$value", $options, true) ? - true : "$value is not known"; - } - - /** - * Assert that a value is of a given type - * - * @param string $type - the PHP type to assert - * @param mixed $value - the value to check - * - * @return boolean - */ - private function isCorrectType($type, $value, $typedValue) - { - if ($type == 'boolean') { - return in_array($value, array('true', '1', 1, 'false', '0', '', 0)); - } - - return "$typedValue" == "$value"; - } - - /** - * Check for the 'type' validation rule - * - * A "Schema type" is almost the same as a PHP type except we have - * "real" instead of "double" - * - * @param string $type - the "schema type" this field should have - * @param mixed $value - * @param mixed $typedValue - * - * @return boolean|string - either boolean true or an error message - * - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - * @SuppressWarnings(PHPMD.UnusedPrivateMethod) - */ - private function checkType($type, $value, $typedValue) - { - return $this->isCorrectType( - $type, - $value, - $typedValue - ) ? true : "should be $type"; - } - - /** - * Check for the 'mustEqual' validation rule - * - * @param mixed $checkValue - the value this field must have - * @param mixed $value - * @param mixed $typedValue - * - * @return boolean|string - either boolean true or an error message - * - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - * @SuppressWarnings(PHPMD.UnusedPrivateMethod) - */ - private function checkMustEqual($checkValue, $value, $typedValue) - { - return $typedValue === $checkValue ? true : 'invalid'; - } - - /** - * Check for the 'minValue' validation rule - * - * @param mixed $minimum - the minimum value this field must have - * @param mixed $value - * @param mixed $typedValue - * - * @return boolean|string - either boolean true or an error message - * - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - * @SuppressWarnings(PHPMD.UnusedPrivateMethod) - */ - private function checkMinValue($minimum, $value, $typedValue) - { - return $typedValue >= $minimum ? true : "should be at least $minimum"; - } - - /** - * Check for the 'maxValue' validation rule - * - * @param mixed $maximum - the maximum value this field must have - * @param mixed $value - * @param mixed $typedValue - * - * @return boolean|string - either boolean true or an error message - * - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - * @SuppressWarnings(PHPMD.UnusedPrivateMethod) - */ - private function checkMaxValue($maximum, $value, $typedValue) - { - return $typedValue <= $maximum ? true : "should be at most $maximum"; - } - - /** - * Check for the 'greaterThan' validation rule - * - * @param mixed $otherArgument - field to compare this one against - * @param mixed $value - * @param mixed $typedValue - * - * @return boolean|string - either boolean true or an error message - * - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - * @SuppressWarnings(PHPMD.UnusedPrivateMethod) - */ - private function checkGreaterThan($otherArgument, $value, $typedValue) - { - return $typedValue > $this->schema->typedParameter( - $otherArgument, - $this->postData - ) ? true : "must be greater than $otherArgument"; - } - - /** - * Check for the 'lessThan' validation rule - * - * @param mixed $otherArgument - field to compare this one against - * @param mixed $value - * @param mixed $typedValue - * - * @return boolean|string - either boolean true or an error message - * - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - * @SuppressWarnings(PHPMD.UnusedPrivateMethod) - */ - private function checkLessThan($otherArgument, $value, $typedValue) - { - return $typedValue < $this->schema->typedParameter( - $otherArgument, - $this->postData - ) ? true : "must be less than $otherArgument"; - } - - //////////////////////////////////////////////////////////////////////////// - - private function isRequired($rules, $value) - { - $required = $rules['required']; - - if ($required === 'optional') { - return $this->schema->typedValue('string', $value) != ''; - } - - return $this->schema->checkBooleanOption($required, $this->postData); - } -} diff --git a/api/src/Page/EM/Scipion.php b/api/src/Page/EM/Scipion.php deleted file mode 100644 index 0fdad4f86..000000000 --- a/api/src/Page/EM/Scipion.php +++ /dev/null @@ -1,140 +0,0 @@ -configExitIfNoMicroscopes(); - $session = $this->sessionFetch($this->arg('session')); - $this->sessionExitIfNotActive($session); - - $session_path = $this->sessionSubstituteValuesInPath($session, $visit_directory); - $template_path = $this->sessionSubstituteValuesInPath($session, $zocalo_scipion_template_path); - $template_file = $zocalo_scipion_template_file; - $workflow_path = $this->sessionSubstituteValuesInPath($session, $zocalo_scipion_workflow_path); - - // Validate form parameters - - // Setup rules to validate each parameter by isRequired, inArray, minValue, maxValue. - // Specify outputType so json_encode casts value correctly. This determines whether value is quoted. - - $validation_rules = array( - 'dosePerFrame' => array('isRequired' => true, 'minValue' => 0, 'maxValue' => 10, 'outputType' => 'float'), - 'numberOfIndividualFrames' => array('isRequired' => true, 'minValue' => 1, 'maxValue' => 500, 'outputType' => 'integer'), - 'patchX' => array('isRequired' => true, 'minValue' => 1, 'outputType' => 'integer'), - 'patchY' => array('isRequired' => true, 'minValue' => 1, 'outputType' => 'integer'), - 'samplingRate' => array('isRequired' => true, 'minValue' => 0.1, 'maxValue' => 10, 'outputType' => 'float'), - 'particleSize' => array('isRequired' => true, 'minValue' => 1, 'maxValue' => 1000, 'outputType' => 'integer'), - 'minDist' => array('isRequired' => true, 'minValue' => 1, 'maxValue' => 1000, 'outputType' => 'integer'), - 'windowSize' => array('isRequired' => true, 'minValue' => 128, 'maxValue' => 2048, 'outputType' => 'integer'), - 'findPhaseShift' => array('isRequired' => true, 'outputType' => 'boolean'), - ); - - list($invalid_parameters, $valid_parameters) = $this->validateParameters($validation_rules); - - // Determine other values to substitute in JSON i.e. parameters not specified in form submission. - $valid_parameters['filesPath'] = $session_path . '/raw/GridSquare_*/Data'; - $valid_parameters['session'] = $session['SESSION']; - - // TODO Better to return an array of invalid parameters for front end to display. (JPH) - if (sizeof($invalid_parameters) > 0) { - $message = 'Invalid parameters: ' . implode('; ', $invalid_parameters) . '.'; - - error_log($message); - $this->_error($message, 400); - } - - $template_json_string = null; - - // Read workflow template file - try { - $template_json_string = file_get_contents("{$template_path}/{$template_file}"); - } catch (\Exception $e) { - error_log("Failed to read workflow template: {$template_path}/{$template_file}"); - $this->_error("Failed to read workflow template for electron microscopy “{$session['BEAMLINENAME']}”.", 500); - } - - // Decode JSON string - $template_array = json_decode($template_json_string, true); - - // JSON is invalid if it cannot be decoded - if ($template_array == null) { - error_log("Invalid workflow template: {$template_path}/{$template_file}"); - $this->_error("Invalid workflow template for electron microscopy “{$session['BEAMLINENAME']}”.", 500); - } - - $updated_parameters = array(); - - // Iterate over each step in workflow template e.g. 0, 1, 2, etc. - foreach (array_keys($template_array) as $step_no) { - - // Iterate over each parameter in step e.g. acquisitionWizard, amplitudeContrast, copyFiles, etc. - foreach (array_keys($template_array[$step_no]) as $parameter) { - - // Determine whether user has specified value for parameter - if (array_key_exists($parameter, $valid_parameters)) { - - // Set parameter to user specified value - $template_array[$step_no][$parameter] = $valid_parameters[$parameter]; - - // Record parameters set to user specified value - array_push($updated_parameters, $parameter); - } - } - } - - // Determine which parameters with user specified values are absent from workflow template - $absent_parameters = array_diff(array_keys($valid_parameters), $updated_parameters); - - if (sizeof($absent_parameters) > 0) { - error_log("Parameters absent from workflow template: {$template_path}/{$template_file}"); - - $message = 'Parameters absent from workflow template: ' . implode('; ', $absent_parameters) . '.'; - error_log($message); - $this->_error($message, 500); - } - - // json_encode does not preserve zero fractions e.g. “1.0” is encoded as “1”. - // The json_encode option JSON_PRESERVE_ZERO_FRACTION was not introduced until PHP 5.6.6. - $workflow_json_string = json_encode($template_array, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); - - // Write workflow file - - $timestamp_epoch = time(); - - $workflow_file = 'scipion_workflow_' . gmdate('ymd.His', $timestamp_epoch) . '.json'; - - try { - file_put_contents("{$workflow_path}/{$workflow_file}", $workflow_json_string); - } catch (\Exception $e) { - error_log("Failed to write workflow file: {$workflow_path}/{$workflow_file}"); - $this->_error('Failed to write workflow file.', 500); - } - - // Send job to processing queue - - $message = array( - 'scipion_workflow' => "{$workflow_path}/{$workflow_file}" - ); - - // $this->_send_zocalo_message($zocalo_scipion_start_queue, $message); - - $output = array( - 'timestamp_iso8601' => gmdate('c', $timestamp_epoch), - 'template_path' => $template_path, - 'template_file' => $template_file, - 'workflow_path' => $workflow_path, - 'workflow_file' => $workflow_file - ); - - $this->_output($output); - } -} diff --git a/api/src/Page/EM/Session.php b/api/src/Page/EM/Session.php deleted file mode 100644 index 6689ca075..000000000 --- a/api/src/Page/EM/Session.php +++ /dev/null @@ -1,141 +0,0 @@ -sessionSelects(); - - $rows = $this->db->pq( - "SELECT $selects - FROM DataCollection - INNER JOIN DataCollectionGroup - ON DataCollectionGroup.dataCollectionGroupId = DataCollection.dataCollectionGroupId - INNER JOIN BLSession - ON BLSession.sessionId = DataCollectionGroup.sessionId - INNER JOIN Proposal - ON Proposal.proposalId = BLSession.proposalId - WHERE CONCAT(Proposal.proposalCode, Proposal.proposalNumber) = :1 - AND DataCollection.dataCollectionId = :2", - array( - $proposal, - $dataCollectionId - ), - false - ); - - if (sizeof($rows) == 0) { - $this->_error('Session not found'); - } - - return $rows[0]; - } - - private function sessionFetch($sessionReference) - { - $selects = $this->sessionSelects(); - $rows = $this->db->pq( - "SELECT $selects - FROM BLSession - INNER JOIN Proposal ON Proposal.proposalId = BLSession.proposalId - WHERE CONCAT( - Proposal.proposalCode, - Proposal.proposalNumber, - '-', - BLSession.visit_number - ) LIKE :1", - array($sessionReference), - false - ); - - if (sizeof($rows) == 0) { - $this->_error('Session not found'); - } - - return $rows[0]; - } - - /////////////////////////////////////////////////////////////////////////// - - private function sessionSelects() - { - return implode(', ', array( - 'BLSession.sessionId', - 'BLSession.beamLineName', - 'YEAR(BLSession.startDate) AS year', - 'CONCAT( - Proposal.proposalCode, - Proposal.proposalNumber, - "-", - BLSession.visit_number - ) AS session', - 'CONCAT( - Proposal.proposalCode, - Proposal.proposalNumber, - "-", - BLSession.visit_number - ) AS visit', - 'BLSession.startDate', - 'BLSession.endDate', - 'CURRENT_TIMESTAMP BETWEEN BLSession.startDate AND BLSession.endDate AS active', - )); - } - - /** - * Substitute session values in file or directory path - * - * Session keys are converted to upper case - * i.e. BEAMLINENAME, YEAR, and SESSION / VISIT - * so it's not required to use "old synchweb style all-upper-case" - * query results - * - * @param array $session - * @param string $path - * - * @return string - */ - private function sessionSubstituteValuesInPath($session, $path) - { - foreach ($session as $key => $value) { - $path = str_replace( - strtoupper("<%={$key}%>"), - $value, - $path - ); - } - - return $path; - } - - /** - * Do not permit processing before session has started or after session has ended - * - * @param array $session - */ - private function sessionExitIfNotActive($session) - { - $endDate = new DateTime($session['endDate']); - $gracePeriod = new DateTime(); - $gracePeriod->sub(new DateInterval('PT24H')); - - if ($endDate < $gracePeriod) { - $message = 'This visit ended over 24 hours ago, at ' . - $endDate->format('H:i:s \o\n jS F Y'); - error_log("{$session['session']} $message"); - $this->_error($message, 400); - } - } -} diff --git a/api/src/Page/EM/Stats.php b/api/src/Page/EM/Stats.php deleted file mode 100644 index 38928f2e2..000000000 --- a/api/src/Page/EM/Stats.php +++ /dev/null @@ -1,246 +0,0 @@ -statsWhereClause(); - - $hist = $this->db->pq( - "SELECT - AVG(diff) as avgDiff, - MIN(diff) as minDiff, - MAX(diff) as maxDiff, - COUNT(diff) as countDiff, - frameDiff, - beamLineName - FROM ( - SELECT - SQRT(POW(mcd.deltaX - @diffx, 2) + POW(mcd.deltaY - @diffY, 2)) as diff, - mcd.deltaX - @diffX as diffX, - @diffX := mcd.deltaX as deltaX, - mcd.deltaY - @diffY as diffY, - @diffY := mcd.deltaY as deltaY, - CONCAT(ABS(mcd.frameNumber-1), '-', mcd.frameNumber) as frameDiff, - s.beamLineName - FROM MotionCorrectionDrift mcd - JOIN (SELECT @diffX := 0) r - JOIN (SELECT @diffY := 0) r2 - INNER JOIN MotionCorrection mc ON mc.motionCorrectionId = mcd.motionCorrectionId - INNER JOIN Movie m ON m.movieId = mc.movieId - INNER JOIN DataCollection dc ON dc.dataCollectionId = m.dataCollectionId - INNER JOIN DataCollectionGroup dcg ON dcg.dataCollectionGroupId = dc.dataCollectionGroupId - INNER JOIN BLSession s ON s.sessionId = dcg.sessionId - INNER JOIN Proposal p ON p.proposalId = s.proposalId - INNER JOIN v_run vr ON s.startDate BETWEEN vr.startDate AND vr.endDate - WHERE $where - GROUP BY - s.beamLineName, - CONCAT(ABS(mcd.frameNumber-1), '-', mcd.frameNumber), - mcd.motionCorrectionId - ORDER BY mcd.motionCorrectionId, mcd.frameNumber - ) inr - GROUP BY frameDiff, beamLineName - ORDER BY frameDiff + 0, beamLineName", - $args, - false - ); - - $beamLines = $this->statsGetBeamLines($hist); - - $data = array(); - $ticks = array(); - foreach (array_keys($beamLines) as $beamLine) { - $ha = array(); - $max = array(); - $min = array(); - foreach ($hist as $rowNum => &$row) { - if ($row['frameDiff'] != '0-1' && $row['beamLineName'] == $beamLine) { - $ha[$rowNum - 1] = floatval($row['avgDiff']); - $min[$rowNum - 1] = floatval($row['minDiff']); - $max[$rowNum - 1] = floatval($row['maxDiff']); - $ticks[$row['frameDiff']] = 1; - } - } - array_push($data, array( - 'label' => $beamLine, - 'min' => $min, - 'max' => $max, - 'avg' => $ha - )); - } - $this->_output(array('data' => $data, 'ticks' => array_keys($ticks))); - } - - public function statsCtf() - { - $types = array('defocus', 'astigmatism', 'resolution'); - - $typeName = $this->has_arg('ty') && in_array($this->arg('ty'), $types) ? - $this->args['ty'] : 'defocus'; - - switch ($typeName) { - case 'defocus': - $unit = 'A'; - $startPoint = 0; - $endPoint = 60000; - $binSize = 1000; - $column = 'c.estimatedDefocus'; - break; - case 'astigmatism': - $unit = 'Number'; - $startPoint = 0.5; - $endPoint = 1.5; - $binSize = 0.005; - $column = 'c.astigmatism'; - break; - case 'resolution': - $unit = 'A'; - $startPoint = 0; - $endPoint = 30; - $binSize = 1; - $column = 'c.estimatedResolution'; - break; - } - - list($where, $args) = $this->statsWhereClause(); - - $limits = $this->db->pq( - "SELECT - max($column) as max, - min($column) as min, - s.beamLineName - FROM CTF c - INNER JOIN MotionCorrection mc ON mc.motionCorrectionId = c.motionCorrectionId - INNER JOIN Movie m ON m.movieId = mc.movieId - INNER JOIN DataCollection dc ON dc.dataCollectionId = m.dataCollectionId - INNER JOIN DataCollectionGroup dcg ON dcg.dataCollectionGroupId = dc.dataCollectionGroupId - INNER JOIN BLSession s ON s.sessionId = dcg.sessionId - INNER JOIN Proposal p ON p.proposalId = s.proposalId - INNER JOIN v_run vr ON s.startDate BETWEEN vr.startDate AND vr.endDate - WHERE $where - GROUP BY s.beamLineName", - $args, - false - ); - - if (sizeof($limits) > 0) { - $limits = $limits[0]; - $max = floatval(($limits['max'])); - $min = floatval(($limits['min'])); - $range = $max - $min; - if ($range > 0) { - $binSize = $range / 50; - if ($binSize < 0) { - $zeros = strspn($binSize, '0', strpos($binSize, '.') + 1); - $binSize = round($binSize, $zeros); - } elseif ($binSize < 1) { - $binSize = round($binSize, 3); - } else { - $zeros = strlen(number_format($binSize, 0, '.', '')); - $mp = pow(1, $zeros); - $binSize = ceil($binSize / $mp) * $mp; - } - $startPoint = $min - fmod($min, $binSize); - $endPoint = $max - fmod($max, $binSize) + $binSize; - } - } - - $hist = $this->db->pq( - "SELECT - ($column DIV $binSize) * $binSize as x, - count($column) as y, - s.beamLineName - FROM CTF c - INNER JOIN MotionCorrection mc ON mc.motionCorrectionId = c.motionCorrectionId - INNER JOIN Movie m ON m.movieId = mc.movieId - INNER JOIN DataCollection dc ON dc.dataCollectionId = m.dataCollectionId - INNER JOIN DataCollectionGroup dcg ON dcg.dataCollectionGroupId = dc.dataCollectionGroupId - INNER JOIN BLSession s ON s.sessionId = dcg.sessionId - INNER JOIN Proposal p ON p.proposalId = s.proposalId - INNER JOIN v_run vr ON s.startDate BETWEEN vr.startDate AND vr.endDate - WHERE $where - GROUP BY s.beamLineName, x - ORDER BY s.beamLineName", - $args, - false - ); - - $beamLines = $this->statsGetBeamLines($hist); - - $data = array(); - foreach (array_keys($beamLines) as $beamLine) { - $ha = array(); - foreach ($hist as &$row) { - if ($row['beamLineName'] == $beamLine) { - $ha[$row['x']] = floatval($row['y']); - } - } - $gram = array(); - for ($bin = $startPoint; $bin <= $endPoint; $bin += $binSize) { - $binString = number_format( - $bin, - strlen(substr(strrchr($binSize, '.'), 1)), - '.', - '' - ); - $gram[$binString] = array_key_exists($binString, $ha) ? - $ha[$binString] : 0; - } - - $label = ucfirst($typeName) . ' (' . $unit . ')'; - if (!$this->has_arg('bl')) { - $label = $beamLine . ': ' . $label; - } - array_push($data, array('label' => $label, 'data' => $gram)); - } - - $this->_output(array('histograms' => $data)); - } - - //////////////////////////////////////////////////////////////////////////// - - private function statsGetBeamLines($dataset) - { - $beamLines = array(); - foreach ($dataset as $row) { - $beamLines[$row['beamLineName']] = 1; - } - - if ($this->has_arg('visit') && sizeof(array_keys($beamLines)) == 0) { - $inSession = $this->db->pq( - "SELECT s.beamLineName - FROM BLSession s - INNER JOIN Proposal p ON p.proposalId = s.proposalId - WHERE CONCAT(p.proposalCode, p.proposalNumber, '-', s.visit_number) LIKE :1", - array($this->arg('visit')) - ); - if (sizeof($inSession) > 0) { - $beamLines[$inSession[0]['BeamLineName']] = 1; - } - } - return $beamLines; - } - - private function statsWhereClause() - { - $clauses = array( - 'bl' => 's.beamLineName=', - 'visit' => "CONCAT(p.proposalCode, p.proposalNumber, '-', s.visit_number) LIKE ", - 'runid' => 'vr.runId=', - ); - $where = array(); - $args = array(); - foreach ($clauses as $arg => $clause) { - if ($this->has_arg($arg)) { - $where[] = $clause . ':' . (sizeof($args) + 1); - $args[] = $this->arg($arg); - } - } - return array(implode(' AND ', $where), $args); - } - - -} diff --git a/api/src/Page/Vstat.php b/api/src/Page/Vstat.php index faf33e1d9..5de814a29 100644 --- a/api/src/Page/Vstat.php +++ b/api/src/Page/Vstat.php @@ -152,9 +152,7 @@ function _visit_breakdown() FROM datacollection dc INNER JOIN datacollectiongroup dcg ON dcg.datacollectiongroupid = dc.datacollectiongroupid INNER JOIN blsession s ON s.sessionid = dcg.sessionid - INNER JOIN proposal p ON p.proposalid = s.proposalid INNER JOIN screening sc ON sc.datacollectionid = dc.datacollectionid - INNER JOIN v_run vr ON s.startdate BETWEEN vr.startdate AND vr.enddate WHERE 1=1 $where GROUP BY dc.datacollectionid, dc.endtime ORDER BY dc.endtime DESC", $args); @@ -163,40 +161,25 @@ function _visit_breakdown() INNER JOIN datacollection dc ON r.blsampleid = dc.blsampleid AND r.endtimestamp < dc.starttime INNER JOIN datacollectiongroup dcg ON dcg.datacollectiongroupid = dc.datacollectiongroupid INNER JOIN blsession s ON s.sessionid = dcg.sessionid - INNER JOIN proposal p ON p.proposalid = s.proposalid - INNER JOIN v_run vr ON s.startdate BETWEEN vr.startdate AND vr.enddate WHERE 1=1 $where GROUP BY r.endtimestamp ORDER BY r.endtimestamp) inq WHERE dctime < 1000", $args); $sched = array(); - $ctf = $this->db->pq("SELECT TO_CHAR(m.createdtimestamp, 'DD-MM-YYYY HH24:MI:SS') as st, c.astigmatism, c.estimatedresolution, c.estimateddefocus - FROM ctf c - INNER JOIN autoprocprogram app ON app.autoprocprogramid = c.autoprocprogramid - INNER JOIN motioncorrection mc ON mc.motioncorrectionid = c.motioncorrectionid - INNER JOIN movie m ON m.movieid = mc.movieid - INNER JOIN datacollection dc ON dc.datacollectionid = m.datacollectionid - INNER JOIN datacollectiongroup dcg ON dcg.datacollectiongroupid = dc.datacollectiongroupid - INNER JOIN blsession s ON s.sessionid = dcg.sessionid - INNER JOIN proposal p ON p.proposalid = s.proposalid - INNER JOIN v_run vr ON s.startdate BETWEEN vr.startdate AND vr.enddate - WHERE 1=1 $where ORDER BY m.createdtimestamp", $args); - $newargs = array($info['SID'], $info['SID']); - $missed = $this->db->pq("SELECT COUNT(smp.name) as samplecount, - GROUP_CONCAT(smp.name SEPARATOR ', ') as missed + $missed = $this->db->pq("SELECT smp.blsampleid, + smp.name FROM blsample smp INNER JOIN robotaction r ON r.blsampleid = smp.blsampleid - INNER JOIN blsession s ON s.sessionid = r.blsessionid - WHERE 1=1 $where - AND smp.blsampleid not in - (SELECT blsmp.blSampleId FROM blsample blsmp - INNER JOIN datacollection dc ON dc.blsampleid = blsmp.blsampleid - WHERE dc.sessionId=:2)", $newargs); + WHERE r.blsessionid=:1 + AND NOT EXISTS + (SELECT 1 FROM datacollection dc + WHERE dc.blsampleid = smp.blsampleid + AND dc.sessionId=:2) + GROUP BY smp.blsampleid", $newargs); } else { $ai = array(); $cent = array(); - $ctf = array(); $missed = array(); $sched = $this->db->pq("SELECT CONCAT(p.proposalcode, p.proposalnumber, '-', s.visit_number) as visit, TO_CHAR(s.enddate, 'DD-MM-YYYY HH24:MI:SS') as en, TO_CHAR(s.startdate, 'DD-MM-YYYY HH24:MI:SS') as st, p.title, s.scheduled, p.proposalcode @@ -207,7 +190,6 @@ function _visit_breakdown() ORDER BY p.proposalcode, s.startdate", $args); } - # Get Faults $faultl = $this->db->pq("SELECT f.faultid, f.beamtimelost, f.title, TO_CHAR(f.beamtimelost_starttime, 'DD-MM-YYYY HH24:MI:SS') as st, TO_CHAR(f.beamtimelost_endtime, 'DD-MM-YYYY HH24:MI:SS') as en FROM bf_fault f @@ -223,8 +205,11 @@ function _visit_breakdown() $info['DC_TOT'] = sizeof($dc); $info['DC_GRID'] = sizeof($grid) ? $grid[0]['COUNT'] : 0; $info['DC_GRID_SAMPLES'] = $grid[0]['SAMPLECOUNT'] ? $grid[0]['SAMPLECOUNT'] : 0; - $info['DC_MISSED'] = sizeof($missed) ? $missed[0]['MISSED'] : ''; - $info['DC_MISSED_SAMPLES'] = sizeof($missed) ? $missed[0]['SAMPLECOUNT'] : 0; + $missed_sample_names = array(); + foreach ($missed as $m) + array_push($missed_sample_names, $m['NAME']); + $info['DC_MISSED'] = sizeof($missed_sample_names) ? $missed_sample_names : ''; + $info['DC_MISSED_SAMPLES'] = sizeof($missed) ? sizeof($missed) : 0; $info['DC_STOPPED'] = 0; $info['E_TOT'] = sizeof($edge); $info['FL_TOT'] = sizeof($fl); @@ -246,21 +231,6 @@ function _visit_breakdown() foreach ($linecols as $c) array_push($lines, array('data' => array())); - $scattercols = array('ASTIGMATISM', 'ESTIMATEDDEFOCUS', 'ESTIMATEDRESOLUTION'); - $scatters = array(); - foreach ($scattercols as $c) - array_push($scatters, array('data' => array())); - - foreach ($ctf as $c) { - foreach ($scattercols as $i => $f) { - $scatters[$i]['label'] = $f; - if ($i > 0) - $scatters[$i]['yaxis'] = $i + 1; - if (floatval($c[$f]) < 1e38) - array_push($scatters[$i]['data'], array($this->jst($c['ST']), floatval(round($c[$f], 4)))); - } - } - $queued = 0; $vis_map = array(); foreach ($sched as $s) { @@ -422,7 +392,7 @@ function _visit_breakdown() $info['start'] = $this->jst($first); $info['end'] = $this->jst($last); - $this->_output(array('info' => $info, 'data' => $data, 'lines' => $lines, 'scatters' => $scatters)); + $this->_output(array('info' => $info, 'data' => $data, 'lines' => $lines)); } diff --git a/api/tests/Controllers/AssignControllerTest.php b/api/tests/Controllers/AssignControllerTest.php index 8c0426f39..9a1f04883 100644 --- a/api/tests/Controllers/AssignControllerTest.php +++ b/api/tests/Controllers/AssignControllerTest.php @@ -48,10 +48,9 @@ protected function setUp(): void global $ip2bl; $ip2bl = array(103 => 'i03'); - global $bl_pv_map; - $bl_pv_map = array( - 'i02' => 'BL02I', - 'i03' => 'BL03I', + global $bl_puck_names; + $bl_puck_names = array( + 'i03' => "BL03I-MO-ROBOT-01:PUCK_%'02d_NAME", ); global $bl_pv_env; $bl_pv_env = 'EPICS_CA_ADDR_LIST_TEST=666.45.678.9'; @@ -241,7 +240,7 @@ public function testGetPuckNamesWithPropAndValidBlSingleValReturnsData(): void $this->assignController->args['prop'] = 3; $this->assignController->args['bl'] = 'i03'; $this->assignController->proposalid = 3; - $this->assignController->shouldReceive('pv')->times(1)->andReturn(array('PUCK_1_NAME' => 'puck1', 'PUCK_12_NAME' => 'puck12')); + $this->assignController->shouldReceive('pv')->times(1)->andReturn(array('BL03I-MO-ROBOT-01:PUCK_01_NAME' => 'puck1', 'BL03I-MO-ROBOT-01:PUCK_12_NAME' => 'puck12')); $this->dataLayerStub->expects($this->exactly(1))->method('getContainerBarcodesForProposal')->with(3)->willReturn(array(['BARCODE' => 1230, 'BL' => 'test03'])); $this->assignController->getPuckNames(); @@ -254,7 +253,7 @@ public function testGetPuckNamesWithPropAndValidBlArrayWithNoDataValReturnsObfus $this->assignController->args['prop'] = 3; $this->assignController->args['bl'] = 'i03'; $this->assignController->proposalid = 3; - $this->assignController->shouldReceive('pv')->times(1)->andReturn(array('PUCK_1_NAME' => array(11), 'PUCK_12_NAME' => array(12))); + $this->assignController->shouldReceive('pv')->times(1)->andReturn(array('BL03I-MO-ROBOT-01:PUCK_01_NAME' => array(11), 'BL03I-MO-ROBOT-01:PUCK_12_NAME' => array(12))); $this->dataLayerStub->expects($this->exactly(1))->method('getContainerBarcodesForProposal')->with(3)->willReturn(array(['BARCODE' => 1230, 'BL' => 'test03'])); $this->assignController->getPuckNames(); @@ -267,7 +266,7 @@ public function testGetPuckNamesWithPropAndValidBlArrayWithMatchingDataValReturn $this->assignController->args['prop'] = 3; $this->assignController->args['bl'] = 'i03'; $this->assignController->proposalid = 3; - $this->assignController->shouldReceive('pv')->times(1)->andReturn(array('PUCK_1_NAME' => array(11), 'PUCK_12_NAME' => array(1230))); + $this->assignController->shouldReceive('pv')->times(1)->andReturn(array('BL03I-MO-ROBOT-01:PUCK_01_NAME' => array(11), 'BL03I-MO-ROBOT-01:PUCK_12_NAME' => array(1230))); $this->dataLayerStub->expects($this->exactly(1))->method('getContainerBarcodesForProposal')->with(3)->willReturn(array(['BARCODE' => 1230, 'BL' => 'test03'])); $this->assignController->getPuckNames(); @@ -281,7 +280,7 @@ public function testGetPuckNamesWithPropAndValidBlArrayForStaffMemberReturnsReal $this->assignController->args['bl'] = 'i03'; $this->assignController->proposalid = 3; $this->assignController->staff = true; - $this->assignController->shouldReceive('pv')->times(1)->andReturn(array('PUCK_1_NAME' => array(11), 'PUCK_12_NAME' => array(1231))); + $this->assignController->shouldReceive('pv')->times(1)->andReturn(array('BL03I-MO-ROBOT-01:PUCK_01_NAME' => array(11), 'BL03I-MO-ROBOT-01:PUCK_12_NAME' => array(1231))); $this->dataLayerStub->expects($this->exactly(1))->method('getContainerBarcodesForProposal')->with(3)->willReturn(array(['BARCODE' => 1230, 'BL' => 'test03'])); $this->assignController->getPuckNames(); diff --git a/client/build/build.js b/client/build/build.js index 2a9ce7a39..de35cd246 100644 --- a/client/build/build.js +++ b/client/build/build.js @@ -17,7 +17,6 @@ 'modules/types/tomo/menu', 'modules/types/gen/menu', 'modules/types/saxs/menu', - 'modules/types/em/menu', 'modules/types/sm/menu', 'modules/types/pow/menu', diff --git a/client/src/js/app.js b/client/src/js/app.js index db71ee3a6..12241f789 100644 --- a/client/src/js/app.js +++ b/client/src/js/app.js @@ -213,8 +213,6 @@ function(Backbone, Marionette, _, $, HeaderView, SideBarView, DialogRegion, Logi 'modules/mc/router', 'modules/admin/router', 'modules/imaging/router', - 'modules/types/em/relion/router', - 'modules/types/em/scipion/router' ], function() { this.sidebarview = new SideBarView() diff --git a/client/src/js/app/router/router.js b/client/src/js/app/router/router.js index 43d885ae2..b47f041cf 100644 --- a/client/src/js/app/router/router.js +++ b/client/src/js/app/router/router.js @@ -37,7 +37,6 @@ import CellRoutes from 'modules/cell/routes.js' import StatusRoutes from 'modules/status/routes.js' import FaultRoutes from 'modules/fault/routes.js' import StatsRoutes from 'modules/stats/routes.js' -import EMRoutes from 'modules/types/em/routes.js' import SubmissionRoutes from 'modules/submission/routes.js' import VisitsRoutes from 'modules/visits/routes.js' import SummaryRoutes from 'modules/summary/routes.js' @@ -98,7 +97,6 @@ router.addRoutes(ProposalRoutes) router.addRoutes(TutorialRoutes) router.addRoutes(FeedbackRoutes) router.addRoutes(ShipmentRoutes) -router.addRoutes(EMRoutes) router.addRoutes(DCRoutes) router.addRoutes(BLStatsRoutes) router.addRoutes(ProjectRoutes) diff --git a/client/src/js/app/store/modules/store.menus.js b/client/src/js/app/store/modules/store.menus.js index 33f8776d7..2481c1295 100644 --- a/client/src/js/app/store/modules/store.menus.js +++ b/client/src/js/app/store/modules/store.menus.js @@ -1,4 +1,3 @@ -import EmMenu from 'modules/types/em/menu.js' import GenMenu from 'modules/types/gen/menu.js' import MxMenu from 'modules/types/mx/menu.js' import PowMenu from 'modules/types/pow/menu.js' @@ -13,7 +12,6 @@ const menuStore = { namespaced: true, state: () => ({ menus: { - 'em': EmMenu, 'gen': GenMenu, 'mx': MxMenu, 'pow': PowMenu, diff --git a/client/src/js/config.json b/client/src/js/config.json deleted file mode 100644 index acb016e9c..000000000 --- a/client/src/js/config.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "apiurl": "/api", - - "appurl": "", - - "production": false, - "skipHome": false, - - "pucks": { - "i02": 10, - "i03": 23, - "i04": 37, - "i04-1": 9, - "i15-1": 20, - "i24": 9, - "i23": 4 - }, - - "_gsMajorAxisOrientation" : "Determines whether the major grid scan axis determines the orientation of the view", - "gsMajorAxisOrientation" : true, - - "maintenance_message": "This is the maintenance message", - "maintenance": false, - - "ga_ident": false -} diff --git a/client/src/js/modules/dc/components/dc-wrapper.vue b/client/src/js/modules/dc/components/dc-wrapper.vue index ad30b5154..4447aa2be 100644 --- a/client/src/js/modules/dc/components/dc-wrapper.vue +++ b/client/src/js/modules/dc/components/dc-wrapper.vue @@ -9,10 +9,10 @@ :breadcrumbs="bc" /> + options property for pure Vue views they are better as individual + properties --> - - - -
- -
-
- -
- {{ heading }} - -
-
-
- - - - - diff --git a/client/src/js/modules/types/em/classification/parameters.vue b/client/src/js/modules/types/em/classification/parameters.vue deleted file mode 100644 index c0a51db49..000000000 --- a/client/src/js/modules/types/em/classification/parameters.vue +++ /dev/null @@ -1,80 +0,0 @@ - - - diff --git a/client/src/js/modules/types/em/classification/preview-image.vue b/client/src/js/modules/types/em/classification/preview-image.vue deleted file mode 100644 index 0d32f8c26..000000000 --- a/client/src/js/modules/types/em/classification/preview-image.vue +++ /dev/null @@ -1,82 +0,0 @@ - - - - - diff --git a/client/src/js/modules/types/em/classification/select-page.vue b/client/src/js/modules/types/em/classification/select-page.vue deleted file mode 100644 index 5a65b5f00..000000000 --- a/client/src/js/modules/types/em/classification/select-page.vue +++ /dev/null @@ -1,59 +0,0 @@ - - - - - diff --git a/client/src/js/modules/types/em/classification/select-sort.vue b/client/src/js/modules/types/em/classification/select-sort.vue deleted file mode 100644 index a5066385e..000000000 --- a/client/src/js/modules/types/em/classification/select-sort.vue +++ /dev/null @@ -1,31 +0,0 @@ - - - diff --git a/client/src/js/modules/types/em/components/button-with-marionette-dialog.vue b/client/src/js/modules/types/em/components/button-with-marionette-dialog.vue deleted file mode 100644 index ed5d5bfab..000000000 --- a/client/src/js/modules/types/em/components/button-with-marionette-dialog.vue +++ /dev/null @@ -1,82 +0,0 @@ - - - diff --git a/client/src/js/modules/types/em/components/custom-list-item.vue b/client/src/js/modules/types/em/components/custom-list-item.vue deleted file mode 100644 index 578b09713..000000000 --- a/client/src/js/modules/types/em/components/custom-list-item.vue +++ /dev/null @@ -1,44 +0,0 @@ - - - - - diff --git a/client/src/js/modules/types/em/components/dialog-image.vue b/client/src/js/modules/types/em/components/dialog-image.vue deleted file mode 100644 index 056dba223..000000000 --- a/client/src/js/modules/types/em/components/dialog-image.vue +++ /dev/null @@ -1,115 +0,0 @@ - - - - - - - diff --git a/client/src/js/modules/types/em/components/dialog-plotly.vue b/client/src/js/modules/types/em/components/dialog-plotly.vue deleted file mode 100644 index babea0862..000000000 --- a/client/src/js/modules/types/em/components/dialog-plotly.vue +++ /dev/null @@ -1,143 +0,0 @@ - - - - - diff --git a/client/src/js/modules/types/em/components/dialog-schema-form-section.vue b/client/src/js/modules/types/em/components/dialog-schema-form-section.vue deleted file mode 100644 index 011dde13e..000000000 --- a/client/src/js/modules/types/em/components/dialog-schema-form-section.vue +++ /dev/null @@ -1,31 +0,0 @@ - - - - - diff --git a/client/src/js/modules/types/em/components/dialog-schema-form.vue b/client/src/js/modules/types/em/components/dialog-schema-form.vue deleted file mode 100644 index 29907e76f..000000000 --- a/client/src/js/modules/types/em/components/dialog-schema-form.vue +++ /dev/null @@ -1,192 +0,0 @@ - - - - - diff --git a/client/src/js/modules/types/em/components/hide-button.vue b/client/src/js/modules/types/em/components/hide-button.vue deleted file mode 100644 index c346275fa..000000000 --- a/client/src/js/modules/types/em/components/hide-button.vue +++ /dev/null @@ -1,35 +0,0 @@ - - - diff --git a/client/src/js/modules/types/em/components/image-select.vue b/client/src/js/modules/types/em/components/image-select.vue deleted file mode 100644 index 2c3042466..000000000 --- a/client/src/js/modules/types/em/components/image-select.vue +++ /dev/null @@ -1,145 +0,0 @@ - - - diff --git a/client/src/js/modules/types/em/components/parameter-list-item.vue b/client/src/js/modules/types/em/components/parameter-list-item.vue deleted file mode 100644 index ec2e9f370..000000000 --- a/client/src/js/modules/types/em/components/parameter-list-item.vue +++ /dev/null @@ -1,73 +0,0 @@ - - - diff --git a/client/src/js/modules/types/em/components/parameter-list-with-schema.vue b/client/src/js/modules/types/em/components/parameter-list-with-schema.vue deleted file mode 100644 index b7d4cec1d..000000000 --- a/client/src/js/modules/types/em/components/parameter-list-with-schema.vue +++ /dev/null @@ -1,112 +0,0 @@ - - - diff --git a/client/src/js/modules/types/em/components/parameter-list.vue b/client/src/js/modules/types/em/components/parameter-list.vue deleted file mode 100644 index 6f37229b9..000000000 --- a/client/src/js/modules/types/em/components/parameter-list.vue +++ /dev/null @@ -1,35 +0,0 @@ - - - - - diff --git a/client/src/js/modules/types/em/components/processing-section-image-select.vue b/client/src/js/modules/types/em/components/processing-section-image-select.vue deleted file mode 100644 index bbf895c32..000000000 --- a/client/src/js/modules/types/em/components/processing-section-image-select.vue +++ /dev/null @@ -1,93 +0,0 @@ - - - diff --git a/client/src/js/modules/types/em/components/processing-section.vue b/client/src/js/modules/types/em/components/processing-section.vue deleted file mode 100644 index df919c077..000000000 --- a/client/src/js/modules/types/em/components/processing-section.vue +++ /dev/null @@ -1,97 +0,0 @@ - - - - - diff --git a/client/src/js/modules/types/em/components/reprocess-button.vue b/client/src/js/modules/types/em/components/reprocess-button.vue deleted file mode 100644 index 26a5b449b..000000000 --- a/client/src/js/modules/types/em/components/reprocess-button.vue +++ /dev/null @@ -1,94 +0,0 @@ - - - diff --git a/client/src/js/modules/types/em/components/schema-input.vue b/client/src/js/modules/types/em/components/schema-input.vue deleted file mode 100644 index a7ac30fc9..000000000 --- a/client/src/js/modules/types/em/components/schema-input.vue +++ /dev/null @@ -1,204 +0,0 @@ - - - - - diff --git a/client/src/js/modules/types/em/components/sign-url.js b/client/src/js/modules/types/em/components/sign-url.js deleted file mode 100644 index 2971713d8..000000000 --- a/client/src/js/modules/types/em/components/sign-url.js +++ /dev/null @@ -1,23 +0,0 @@ -import Backbone from 'backbone' - -export default { - 'methods': { - 'signUrl': function(url, callback) { - const apiUrl = this.$store.state.apiUrl - Backbone.ajax({ - 'url': apiUrl + '/download/sign', - 'method': 'POST', - /* This seems to be broken in `client/src/js/utils.js` - * It doesn't post in JSON and `api/src/Page/Download.php` - * doesn't see any of the args */ - 'contentType': 'application/json', - 'data': JSON.stringify({ - 'validity': url.replace(apiUrl, '') - }), - 'success': function(response) { - callback(url + '&token=' + response.token) - } - }) - }, - }, -} diff --git a/client/src/js/modules/types/em/components/toolbar-button.vue b/client/src/js/modules/types/em/components/toolbar-button.vue deleted file mode 100644 index fa304ccc4..000000000 --- a/client/src/js/modules/types/em/components/toolbar-button.vue +++ /dev/null @@ -1,81 +0,0 @@ - - - - - diff --git a/client/src/js/modules/types/em/ctf-summary/summary-charts.vue b/client/src/js/modules/types/em/ctf-summary/summary-charts.vue deleted file mode 100644 index 38b843964..000000000 --- a/client/src/js/modules/types/em/ctf-summary/summary-charts.vue +++ /dev/null @@ -1,208 +0,0 @@ - - - - - diff --git a/client/src/js/modules/types/em/ctf/ctf-estimation.vue b/client/src/js/modules/types/em/ctf/ctf-estimation.vue deleted file mode 100644 index 61f0e38d4..000000000 --- a/client/src/js/modules/types/em/ctf/ctf-estimation.vue +++ /dev/null @@ -1,71 +0,0 @@ - - - diff --git a/client/src/js/modules/types/em/ctf/params.vue b/client/src/js/modules/types/em/ctf/params.vue deleted file mode 100644 index c6ad6bbb9..000000000 --- a/client/src/js/modules/types/em/ctf/params.vue +++ /dev/null @@ -1,90 +0,0 @@ - - - diff --git a/client/src/js/modules/types/em/dc-list/em-dc-list.vue b/client/src/js/modules/types/em/dc-list/em-dc-list.vue deleted file mode 100644 index bb79b060f..000000000 --- a/client/src/js/modules/types/em/dc-list/em-dc-list.vue +++ /dev/null @@ -1,168 +0,0 @@ - - - - - diff --git a/client/src/js/modules/types/em/dc-list/new-data-collection.vue b/client/src/js/modules/types/em/dc-list/new-data-collection.vue deleted file mode 100644 index bc59758f6..000000000 --- a/client/src/js/modules/types/em/dc-list/new-data-collection.vue +++ /dev/null @@ -1,138 +0,0 @@ - - - diff --git a/client/src/js/modules/types/em/dc-list/refresh.vue b/client/src/js/modules/types/em/dc-list/refresh.vue deleted file mode 100644 index 77959d9e4..000000000 --- a/client/src/js/modules/types/em/dc-list/refresh.vue +++ /dev/null @@ -1,79 +0,0 @@ - - - - - diff --git a/client/src/js/modules/types/em/dc-list/search.vue b/client/src/js/modules/types/em/dc-list/search.vue deleted file mode 100644 index bb64e66a7..000000000 --- a/client/src/js/modules/types/em/dc-list/search.vue +++ /dev/null @@ -1,38 +0,0 @@ - - - diff --git a/client/src/js/modules/types/em/dc-list/toolbar.vue b/client/src/js/modules/types/em/dc-list/toolbar.vue deleted file mode 100644 index bc605816b..000000000 --- a/client/src/js/modules/types/em/dc-list/toolbar.vue +++ /dev/null @@ -1,104 +0,0 @@ - - - diff --git a/client/src/js/modules/types/em/dc-list/usage-stack.vue b/client/src/js/modules/types/em/dc-list/usage-stack.vue deleted file mode 100644 index c848b80d4..000000000 --- a/client/src/js/modules/types/em/dc-list/usage-stack.vue +++ /dev/null @@ -1,40 +0,0 @@ - - - - - diff --git a/client/src/js/modules/types/em/dc/data-collection.vue b/client/src/js/modules/types/em/dc/data-collection.vue deleted file mode 100644 index 1a0f87f7c..000000000 --- a/client/src/js/modules/types/em/dc/data-collection.vue +++ /dev/null @@ -1,198 +0,0 @@ - - - - - diff --git a/client/src/js/modules/types/em/dc/datacollections.js b/client/src/js/modules/types/em/dc/datacollections.js deleted file mode 100644 index edbb9cb4f..000000000 --- a/client/src/js/modules/types/em/dc/datacollections.js +++ /dev/null @@ -1,5 +0,0 @@ -/** - * TODO: this module is here purely for compatibility with - * modules/dc/views/getdcview.js - */ -export default {} diff --git a/client/src/js/modules/types/em/dc/redirect.vue b/client/src/js/modules/types/em/dc/redirect.vue new file mode 100644 index 000000000..92afee74b --- /dev/null +++ b/client/src/js/modules/types/em/dc/redirect.vue @@ -0,0 +1,46 @@ + + + diff --git a/client/src/js/modules/types/em/dc/toolbar.vue b/client/src/js/modules/types/em/dc/toolbar.vue deleted file mode 100644 index f157f8994..000000000 --- a/client/src/js/modules/types/em/dc/toolbar.vue +++ /dev/null @@ -1,260 +0,0 @@ - - - - - diff --git a/client/src/js/modules/types/em/ice/download.vue b/client/src/js/modules/types/em/ice/download.vue deleted file mode 100644 index c489d6a3f..000000000 --- a/client/src/js/modules/types/em/ice/download.vue +++ /dev/null @@ -1,43 +0,0 @@ - - - diff --git a/client/src/js/modules/types/em/ice/ice-breaker.vue b/client/src/js/modules/types/em/ice/ice-breaker.vue deleted file mode 100644 index 689dd7cba..000000000 --- a/client/src/js/modules/types/em/ice/ice-breaker.vue +++ /dev/null @@ -1,121 +0,0 @@ - - - - - diff --git a/client/src/js/modules/types/em/mc/drift.vue b/client/src/js/modules/types/em/mc/drift.vue deleted file mode 100644 index da4c978e9..000000000 --- a/client/src/js/modules/types/em/mc/drift.vue +++ /dev/null @@ -1,90 +0,0 @@ - - - - - \ No newline at end of file diff --git a/client/src/js/modules/types/em/mc/motion-correction.vue b/client/src/js/modules/types/em/mc/motion-correction.vue deleted file mode 100644 index fd1e586b0..000000000 --- a/client/src/js/modules/types/em/mc/motion-correction.vue +++ /dev/null @@ -1,79 +0,0 @@ - - - diff --git a/client/src/js/modules/types/em/mc/params.vue b/client/src/js/modules/types/em/mc/params.vue deleted file mode 100644 index 08b18f344..000000000 --- a/client/src/js/modules/types/em/mc/params.vue +++ /dev/null @@ -1,70 +0,0 @@ - - - diff --git a/client/src/js/modules/types/em/menu.js b/client/src/js/modules/types/em/menu.js deleted file mode 100644 index 7ac2a2b5f..000000000 --- a/client/src/js/modules/types/em/menu.js +++ /dev/null @@ -1,21 +0,0 @@ -define([], function() { - return { - menu: { - dc: 'View All Data', - visits: 'Visits', - cal: 'Calendar', - shipments: 'Shipments', - contacts: 'Lab Contacts', - 'dewars/registry': 'Registered Dewars', - stats: 'Statistics', - migrate: 'Migrate', - }, - extra: { - //projects: 'Projects', - }, - admin: { - 'dewars/overview': { title: 'Logistics', icon: 'fa-truck', permission: 'all_dewars' }, - faults: { title: 'Fault Reports', icon: 'fa-tasks' }, - }, - } -}) diff --git a/client/src/js/modules/types/em/picker/params.vue b/client/src/js/modules/types/em/picker/params.vue deleted file mode 100644 index 12406ae05..000000000 --- a/client/src/js/modules/types/em/picker/params.vue +++ /dev/null @@ -1,40 +0,0 @@ - - - diff --git a/client/src/js/modules/types/em/picker/picker.vue b/client/src/js/modules/types/em/picker/picker.vue deleted file mode 100644 index 4f1b3f0a8..000000000 --- a/client/src/js/modules/types/em/picker/picker.vue +++ /dev/null @@ -1,70 +0,0 @@ - - - diff --git a/client/src/js/modules/types/em/processing-jobs/job-header.vue b/client/src/js/modules/types/em/processing-jobs/job-header.vue deleted file mode 100644 index f1b9b4619..000000000 --- a/client/src/js/modules/types/em/processing-jobs/job-header.vue +++ /dev/null @@ -1,147 +0,0 @@ - - - - - diff --git a/client/src/js/modules/types/em/processing-jobs/processing-job.vue b/client/src/js/modules/types/em/processing-jobs/processing-job.vue deleted file mode 100644 index 9bf9a7ead..000000000 --- a/client/src/js/modules/types/em/processing-jobs/processing-job.vue +++ /dev/null @@ -1,168 +0,0 @@ - - - - - diff --git a/client/src/js/modules/types/em/processing-jobs/status-description.vue b/client/src/js/modules/types/em/processing-jobs/status-description.vue deleted file mode 100644 index a2d1b4fbe..000000000 --- a/client/src/js/modules/types/em/processing-jobs/status-description.vue +++ /dev/null @@ -1,55 +0,0 @@ - - - - - diff --git a/client/src/js/modules/types/em/processing-jobs/stop-button.vue b/client/src/js/modules/types/em/processing-jobs/stop-button.vue deleted file mode 100644 index 9548e6220..000000000 --- a/client/src/js/modules/types/em/processing-jobs/stop-button.vue +++ /dev/null @@ -1,92 +0,0 @@ - - - - - diff --git a/client/src/js/modules/types/em/relion/box-calculator.js b/client/src/js/modules/types/em/relion/box-calculator.js deleted file mode 100644 index 95349d964..000000000 --- a/client/src/js/modules/types/em/relion/box-calculator.js +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Auto calculate particle box parameters (if required) - * Runs whenever any parameter value is updated. - * - * @param {string} name the name of the parameter just updated - * @param {object} parameters a reference to all of the parameters - */ -export default function (name, parameters) { - const applicableParameters = [ - 'autopick_LoG_diam_max', - 'angpix', - 'wantCalculate', - 'stop_after_ctf_estimation', - ] - - if ( - parameters.stop_after_ctf_estimation || - !parameters.wantCalculate || - !applicableParameters.includes(name) - ) { - return - } - - const angpix = parseFloat(parameters.angpix) - const autopick_LoG_diam_max = parseFloat(parameters.autopick_LoG_diam_max) - - if (angpix == 0.0 || isNaN(angpix) || isNaN(autopick_LoG_diam_max)) { - parameters.mask_diameter = '' - parameters.extract_boxsize = '' - parameters.extract_small_boxsize = '' - return - } - - const particleSizePixels = autopick_LoG_diam_max / angpix - const mask_diameter = Math.round(autopick_LoG_diam_max * 1.1) - const boxSizeExact = particleSizePixels * 1.2 - const boxSizeInt = Math.ceil(boxSizeExact) - const extract_boxsize = boxSizeInt + (boxSizeInt % 2) - const extract_small_boxsize = [ - 48, 64, 96, 128, 160, 192, 256, 288, 300, 320, 360, 384, - 400, 420, 450, 480, 512, 640, 768, 896, 1024, - ].reduce( - function(result, boxSize) { - if (result !== null) { - return result - } - if (boxSize > extract_boxsize) { - return extract_boxsize - } - if (((angpix * extract_boxsize) / boxSize) < 4.25) { - return boxSize - } - return result - }, - null - ) - - parameters.mask_diameter = mask_diameter.toString() - parameters.extract_boxsize = extract_boxsize.toString() - parameters.extract_small_boxsize = extract_small_boxsize === null ? - 'Box size is too large!' : extract_small_boxsize.toString() -} diff --git a/client/src/js/modules/types/em/relion/relion-dialog.vue b/client/src/js/modules/types/em/relion/relion-dialog.vue deleted file mode 100644 index a23952606..000000000 --- a/client/src/js/modules/types/em/relion/relion-dialog.vue +++ /dev/null @@ -1,168 +0,0 @@ - - - diff --git a/client/src/js/modules/types/em/routes.js b/client/src/js/modules/types/em/routes.js deleted file mode 100644 index 81ae5fc58..000000000 --- a/client/src/js/modules/types/em/routes.js +++ /dev/null @@ -1,54 +0,0 @@ -import MarionetteView from 'app/views/marionette/marionette-wrapper.vue' -import emModule from 'modules/types/em/store/em-module' -import store from 'app/store/store' - -// Import style for lazy loading of Vue Single File Component -const DataCollectionView = () => import(/* webpackChunkName: "em" */ 'modules/types/em/dc/data-collection.vue') -const ScipionView = import(/* webpackChunkName: "em" */ 'modules/types/em/scipion/views/scipion') - -const routes = [ - { - 'path': '/dc/visit/:visit_str/collection/:collection_id', - 'component': DataCollectionView, - 'props': route => ({ - 'dataCollectionId': parseInt(route.params.collection_id, 10), - 'visit': route.params.visit_str - }), - 'beforeEnter': (to, from, next) => { - if (to.params.collection_id && to.params.visit_str) { - emModule.register(store) - next() - } else { - app.message({ - title: 'Data collection and/or visit not specified', - message: 'No data collection and/or visit specified' - }) - next('/notfound') - } - }, - }, - { - path: '/em/process/scipion/visit/:visit_str', - component: MarionetteView, - props: route => ({ - mview: ScipionView, - options: { - visit_str: route.params.visit_str - }, - breadcrumbs: [{ title: 'Scipion Processing' }, { title: route.params.visit_str }] - }), - beforeEnter: (to, from, next) => { - // Copying the logic from types/em/scipion/controller.js - if (to.params.visit_str) { - app.cookie(to.params.visit_str.split('-')[0]); - next() - } else { - // This path should never be entered. If there is no session_str then this path will not match - app.message({title: 'Visit not specified', message: 'No visit specified'}) - next('/notfound') - } - } - } -] - -export default routes diff --git a/client/src/js/modules/types/em/scipion/controller.js b/client/src/js/modules/types/em/scipion/controller.js deleted file mode 100644 index 9018c7178..000000000 --- a/client/src/js/modules/types/em/scipion/controller.js +++ /dev/null @@ -1,26 +0,0 @@ -define([ - 'marionette', - 'models/visit', - 'modules/types/em/scipion/views/scipion', -], function ( - Marionette, - Visit, - ScipionView -) { - let controller = { - scipion: function (visit_str) { - app.loading(); - - if (visit_str) { - app.cookie(visit_str.split('-')[0]); - - app.content.show(new ScipionView({visit_str: visit_str})); - } else { - app.bc.reset([{title: 'Error'}]); - app.message({title: 'Visit not specified', message: 'No visit specified'}) - } - } - }; - - return controller -}); diff --git a/client/src/js/modules/types/em/scipion/models/scipion.js b/client/src/js/modules/types/em/scipion/models/scipion.js deleted file mode 100644 index f2cd80d04..000000000 --- a/client/src/js/modules/types/em/scipion/models/scipion.js +++ /dev/null @@ -1,9 +0,0 @@ -define([ - 'backbone' -], function ( - Backbone -) { - return Backbone.Model.extend({ - urlRoot: '/em/process/scipion/visit' - }) -}); diff --git a/client/src/js/modules/types/em/scipion/router.js b/client/src/js/modules/types/em/scipion/router.js deleted file mode 100644 index 5c8f84b13..000000000 --- a/client/src/js/modules/types/em/scipion/router.js +++ /dev/null @@ -1,17 +0,0 @@ -define([ - 'marionette', - 'modules/types/em/scipion/controller' -], function ( - Marionette, - c -) { - let Router = Marionette.AppRouter.extend({ - appRoutes: { - 'em/process/scipion/visit/:visit_str': 'scipion', - } - }); - - return new Router({ - controller: c - }) -}); \ No newline at end of file diff --git a/client/src/js/modules/types/em/scipion/views/scipion.js b/client/src/js/modules/types/em/scipion/views/scipion.js deleted file mode 100644 index 917764dd6..000000000 --- a/client/src/js/modules/types/em/scipion/views/scipion.js +++ /dev/null @@ -1,194 +0,0 @@ -define([ - 'vue', - 'veevalidate', - 'promise', - 'utils/vuewrapper', - 'formatDate', - 'modules/types/em/scipion/models/scipion', - 'models/visit', - 'templates/vue/types/em/process/scipion.html', -], function ( - Vue, - VeeValidate, - Promise, - VueWrapper, - formatDate, - ScipionModel, - VisitModel, - template -) { - // Promise is not used, but required for IE if we want to use vee-validate - Vue.use(VeeValidate); - - return VueWrapper.extend({ - vueView: Vue.extend({ - - data: function () { - return { - showSpinner: false, - - // GUI - isVisitLoaded: false, - isVisitActive: false, - isFormReadOnly: true, - isJobQueued: false, - - // Visit - visit: {}, - - // Form fields - dosePerFrame: null, - numberOfIndividualFrames: null, - patchX: null, - patchY: null, - samplingRate: null, - particleSize: null, - minDist: null, - windowSize: null, - findPhaseShift: false, - } - }, - created: function () { - let self = this; - - self.showSpinner = true; - - let visitModel = new VisitModel({ - VISIT: this.$getOption('visit_str') - }); - - visitModel.fetch({ - success: function () { - self.visit = visitModel.attributes; - - self.isVisitLoaded = true; - self.isVisitActive = !!+self.visit['ACTIVE']; // ACTIVE represented by string value "0" or "1" in JSON - - if (self.isVisitActive) { - self.isFormReadOnly = false; - self.resetForm(); - } else { - self.visitEndDateAsString = formatDate.default(self.visit['ENISO'], "HH:mm 'on' do MMMM"); - } - - self.showSpinner = false; - - app.bc.reset([ - {title: 'Data Collections', url: '/dc'}, - {title: self.visit['BL']}, - {title: self.visit['VISIT'], url: '/dc/visit/' + self.visit['VISIT']}, - {title: 'Scipion Processing'} - ]); - }, - error: function () { - app.bc.reset([{title: 'Error'}]); - app.message({title: 'No such visit', message: 'The specified visit does not exist'}) - } - }); - }, - methods: { - // With new build and (IE polyfill) we could use - // Object.assign() to reset all data to initial state - // Using the method below is simple alternative that - // allows us to clear form data after submission - resetForm: function () { - this.dosePerFrame = 0.5; - this.numberOfIndividualFrames = null; - this.patchX = 5; - this.patchY = 5; - this.samplingRate = null; - this.particleSize = null; - this.minDist = 100; - this.windowSize = 512; - this.findPhaseShift = false; - - // To reset form validation, we should wait for next tick - // Vue reactivity means the DOM will not be updated immediately - this.$nextTick(function () { - this.$validator.reset() - }) - }, - onSubmit: function () { - let self = this; - - this.$validator.validateAll().then(function (result) { - if (result) { - self.submitProcessingJob() - } else { - console.log('Form submission prevented, validation failed.'); - } - }); - }, - submitProcessingJob: function () { - this.showSpinner = true; - - // Convert form inputs from string to float, integer, or boolean data type. - // Ensures values are correctly encoded in JSON, not as strings of text. - // parseInt() and parseFloat() trim whitespace characters. - // parseInt() removes fractional-part of numeric input. - // Updates form inputs with converted values. - - this.dosePerFrame = parseFloat(this.dosePerFrame); - this.numberOfIndividualFrames = parseInt(this.numberOfIndividualFrames); - this.patchX = parseInt(this.patchX); - this.patchY = parseInt(this.patchY); - this.samplingRate = parseFloat(this.samplingRate); - this.particleSize = parseInt(this.particleSize); - this.minDist = parseInt(this.minDist); - this.windowSize = parseInt(this.windowSize); - this.findPhaseShift = this.findPhaseShift === true; - - let model = new ScipionModel({ - id: this.visit['VISIT'], - - dosePerFrame: this.dosePerFrame, - numberOfIndividualFrames: this.numberOfIndividualFrames, - patchX: this.patchX, - patchY: this.patchY, - samplingRate: this.samplingRate, - particleSize: this.particleSize, - minDist: this.minDist, - windowSize: this.windowSize, - findPhaseShift: this.findPhaseShift - }); - - let self = this; - - model.save({}, { - type: 'POST', - success: function (model, response, options) { - self.isFormReadOnly = true; - self.isJobQueued = true; - self.showSpinner = false; - - let alertMessage = 'Job successfully submitted.'; - - if ('timestamp_iso8601' in response) { - alertMessage = alertMessage + ' Queued at ' + formatDate.default(response.timestamp_iso8601, 'HH:mm:ss') + '.'; - } - - app.alert({className: 'message notify', message: alertMessage}); - }, - error: function (model, response, options) { - self.showSpinner = false; - - let alertMessage = 'There was a problem submitting this job.'; - - let responseObj = JSON.parse(response.responseText); - - if ('message' in responseObj) { - alertMessage = responseObj.message; - } - - app.alert({message: alertMessage}); - } - }) - }, - onContinue: function () { - app.navigate('dc/visit/' + this.visit['VISIT'], {trigger: true}); - } - }, - template: template - }) - }) -}); diff --git a/client/src/js/modules/types/em/stats/views/interframe.js b/client/src/js/modules/types/em/stats/views/interframe.js deleted file mode 100644 index 545df9216..000000000 --- a/client/src/js/modules/types/em/stats/views/interframe.js +++ /dev/null @@ -1,131 +0,0 @@ -define([ - 'marionette', - 'utils', - 'jquery', - 'jquery.flot', - 'jquery.flot.resize', - 'jquery.flot.tickrotor', -], function( - Marionette, - utils, - $ -) { - return Marionette.ItemView.extend({ - 'template': _.template('

<%-title%>

'), - 'events': { - 'plotselected': 'zoom', - 'dblclick': 'reset', - }, - 'zoom': function(e, ranges) { - var opts = this.plot.getOptions() - opts.xaxes[0].min = ranges.xaxis.from - opts.xaxes[0].max = ranges.xaxis.to - this.plot.setupGrid() - this.plot.draw() - this.plot.clearSelection() - }, - 'reset': function(e) { - this.zoom(e, { - 'xaxis': { - 'from': null, - 'to': null - }, - }) - }, - 'templateHelpers': function() { - return { - 'title': this.getOption('title') - } - }, - // eslint-disable-next-line no-unused-vars - 'initialize': function(options) { - this.$el.css('opacity', 0) - this.listenTo( - this.collection, - 'add remove change:data', - this.render - ) - }, - 'getToolTip': function(lab, x, y, item) { - // var fh = this.collection.first() - // var f = fh.get('data')[0] - return ( - lab ? lab + ': ' : '' - ) + item.series.type + ' - ' + item.datapoint[1] - - }, - 'onRender': function() { - var options = { - 'xaxis': { - 'tickDecimals': 0, - 'rotateTicks': 45, - }, - 'series': { - 'lines': { 'show': false }, - 'points': { 'show': true }, - }, - 'grid': { - 'borderWidth': 0, - 'hoverable': true, - }, - 'selection': { 'mode': 'x' }, - 'tooltipOpts': { 'content': this.getToolTip.bind(this) }, - 'tooltip': true, - } - const fh = this.collection.first() - if ((!fh) || (!fh.get('data'))) { - return - } - options.xaxis.ticks = _.map( - fh.get('ticks'), - function(v, i) { return [i,v] } - ) - var series = 0 - this.collection.each(function(h) { - // eslint-disable-next-line no-unused-vars - _.each(h.get('data'), function(s) { - series++ - }) - }) - var cols = utils.shuffle(utils.getColors(series)) - var cid = 0 - var data = [] - this.collection.each(function(h) { - _.each(h.get('data'), function(s) { - data.push({ - 'data': _.map( - s.avg, - function(v,i) { return [i,v] } - ), - 'label': s.label, - 'color': cols[cid], - 'type': 'Avg' - }) - data.push({ - 'data': _.map( - s.min, - function(v,i) { return [i,v] } - ), - 'points': { 'radius': 1 }, - 'color': cols[cid], - 'type': 'Min' - }) - data.push({ - 'data': _.map( - s.max, - function(v,i) { return [i,v] } - ), - 'points': { 'radius': 1 }, - 'color': cols[cid], - 'type': 'Max' - }) - cid++ - }, this) - }) - if (data.length) { - this.plot = $.plot(this.$el.find('#visit_pie'), data, options) - this.$el.css('opacity', 1) - } - }, - }) -}) diff --git a/client/src/js/modules/types/em/stats/views/visit.js b/client/src/js/modules/types/em/stats/views/visit.js deleted file mode 100644 index ee9d15ae0..000000000 --- a/client/src/js/modules/types/em/stats/views/visit.js +++ /dev/null @@ -1,133 +0,0 @@ -define([ - 'marionette', - 'backbone', - 'modules/fault/collections/faults', - 'modules/fault/views/list', - 'modules/stats/views/breakdown', - 'modules/stats/views/details', - 'modules/stats/views/pie', - // 'modules/stats/views/hourlies', - 'modules/stats/views/ehc', - 'modules/blstats/models/histogram', - 'modules/blstats/views/histogram', - 'modules/types/em/stats/views/interframe', - 'templates/types/em/stats/visit.html' -], function( - Marionette, - Backbone, - Faults, - FaultListView, - BreakdownView, - DetailsView, - PieView, - // HourliesView, - EHCLogView, - Histogram, - HistogramPlot, - InterFramePlot, - template -) { - return Marionette.LayoutView.extend({ - 'template': template, - 'className': 'content', - 'regions': { - 'breakdown': '.breakdown', - 'det': '.details', - 'call': '.callouts', - 'ehc': '.ehclogs', - 'hrs': '.hrs', - 'faults': '.faults', - 'rDefocus': '.defocus', - 'rAstigmatism': '.astigmatism', - 'rResolution': '.resolution', - 'rDrift': '.drift', - }, - 'onShow': function() { - this.breakdown.show(new BreakdownView({ - 'model': this.getOption('breakdown'), - 'scatters': true - })) - - this.det.show(new DetailsView({ - 'model': this.getOption('breakdown') - })) - - if (app.staff) { - this.ehc.show(new EHCLogView({ - 'visit': this.model.get('VISIT') - })) - } - - this.pie = new PieView({ - 'visit': this.model.get('VISIT'), - 'el': this.$el.find('#visit_pie') - }) - - this.faultsModel = new Faults(null, { - 'queryParams': { - 'visit': this.model.get('VISIT') - } - }) - this.faultsModel.fetch() - this.faults.show(new FaultListView({ - 'collection': this.faultsModel, - 'filters': false, - 'search': false - })) - - var EMHistogram = Histogram.extend({ - 'urlRoot': '/em/stats/ctf', - }) - - this.defocus = new EMHistogram() - this.defocus.fetch({ - 'data': { - 'visit': this.model.get('VISIT'), - 'ty': 'defocus', - } - }) - this.rDefocus.show(new HistogramPlot({ - 'collection': new Backbone.Collection([this.defocus]), - 'title': 'Defocus Histogram' - })) - - this.astigmatism = new EMHistogram() - this.astigmatism.fetch({ - 'data': { - 'visit': this.model.get('VISIT'), - 'ty': 'astigmatism', - } - }) - this.rAstigmatism.show(new HistogramPlot({ - 'collection': new Backbone.Collection([this.astigmatism]), - 'title': 'Astigmatism Histogram' - })) - - this.resolution = new EMHistogram() - this.resolution.fetch({ - 'data': { - 'visit': this.model.get('VISIT'), - 'ty': 'resolution' - } - }) - this.rResolution.show(new HistogramPlot({ - 'collection': new Backbone.Collection([this.resolution]), - 'title': 'Resolution Histogram' - })) - - var DriftHistogram = Histogram.extend({ - urlRoot: '/em/stats/mc', - }) - this.drift = new DriftHistogram() - this.drift.fetch({ - 'data': { - 'visit': this.model.get('VISIT'), - } - }) - this.rDrift.show(new InterFramePlot({ - 'collection': new Backbone.Collection([this.drift]), - 'title': 'Drift vs. Frame difference' - })) - }, - }) -}) diff --git a/client/src/js/modules/types/em/store/api-module.js b/client/src/js/modules/types/em/store/api-module.js deleted file mode 100644 index d6c994528..000000000 --- a/client/src/js/modules/types/em/store/api-module.js +++ /dev/null @@ -1,130 +0,0 @@ -import Backbone from 'backbone' - -export default { - 'namespaced': true, - 'state': { - 'loadingCount': 0, - }, - 'getters': { - 'apiUrl': function( - state, // eslint-disable-line no-unused-vars - getters, // eslint-disable-line no-unused-vars - rootState - ) { - return rootState.apiUrl + '/em/'; - }, - }, - 'actions': { - 'loading': function(context, loading) { - if (loading) { - context.commit('loading', true, {'root': true }) - context.state.loadingCount++ - return - } - context.state.loadingCount-- - if (context.state.loadingCount == 0) { - context.commit('loading', false, {'root': true }) - } - }, - 'post': function(context, {url, requestData, humanName, errorHandler}) { - const fullUrl = context.getters.apiUrl + url - context.dispatch('loading', true) - - return new Promise((resolve, reject) => { - const extractErrorMessage = (jqXHR) => { - var message - try { - message = JSON.parse(jqXHR.responseText).message - } catch (error) { - return false - } - return message ? message : false - } - const reportError = (jqXHR) => { - const message = extractErrorMessage(jqXHR) - if ( - message !== false && - typeof errorHandler == 'function' && - errorHandler(message) === true - ) { - return - } - context.commit('notifications/addNotification', { - 'title': 'Error', - 'message': 'Error posting ' + humanName + ' ' + ( - message ? message : '' - ), - 'level': 'error' - }, {'root': true }) - } - - // Backbone.ajax is overridden in src/js/app/marionette-application.js - // to provide additional SynchWeb specific functionality - Backbone.ajax({ - 'type': 'POST', - 'url': fullUrl, - 'contentType': 'application/json', - 'data': JSON.stringify(requestData), - 'success': function ( - responseData, - textStatus, // eslint-disable-line no-unused-vars - jqXHR // eslint-disable-line no-unused-vars - ) { - console.log('post', humanName, requestData, responseData) - context.dispatch('loading', false) - resolve(responseData) - }, - 'error': function(jqXHR, textStatus, errorThrown) { - context.dispatch('loading', false) - console.log( - 'error', - textStatus, - errorThrown, - jqXHR, - requestData, - fullUrl - ); - reportError(jqXHR) - reject() - }, - }) - }) - }, - 'fetch': function(context, {url, humanName}) { - const fullUrl = context.getters.apiUrl + url - context.dispatch('loading', true) - return new Promise((resolve, reject) => { - // Backbone.ajax is overridden in src/js/app/marionette-application.js - // to provide additional SynchWeb specific functionality - Backbone.ajax({ - 'type': 'GET', - 'url': fullUrl, - 'success': function ( - responseData, - textStatus, // eslint-disable-line no-unused-vars - jqXHR // eslint-disable-line no-unused-vars - ) { - console.log('fetch', humanName, responseData) - context.dispatch('loading', false) - resolve(responseData) - }, - 'error': function(jqXHR, textStatus, errorThrown) { - console.log( - 'Error on GET ', humanName, fullUrl, - 'textStatus: ', textStatus, - 'errorThrown: ', errorThrown, - 'jqXHR: ', jqXHR - ) - context.commit('notifications/addNotification', { - 'title': 'Error', - 'message': 'Failed to request ' + humanName, - 'level': 'error' - }, {'root': true }) - context.dispatch('loading', false) - reject() - } - }) - }) - }, - }, -} diff --git a/client/src/js/modules/types/em/store/em-module.js b/client/src/js/modules/types/em/store/em-module.js deleted file mode 100644 index 132cd93fc..000000000 --- a/client/src/js/modules/types/em/store/em-module.js +++ /dev/null @@ -1,52 +0,0 @@ -import apiModule from 'modules/types/em/store/api-module' -import processingModule from 'modules/types/em/store/processing-module' - -const emModule = { - 'namespaced': true, - 'state': { - 'selectedImage': 0, - }, - 'getters': { - 'selectedImage': function( - state, - getters, // eslint-disable-line no-unused-vars - rootState // eslint-disable-line no-unused-vars - ) { - return state.selectedImage - }, - }, - 'mutations': { - 'selectImages': function(state, selectedImage) { - state.selectedImage = selectedImage - }, - }, -} - -export default { - 'register': function(store) { - const modules = { - 'em': emModule, - 'em/api': apiModule, - 'em/processing': processingModule, - } - Object.keys(modules).forEach(function(key) { - if (!store.hasModule(key)) { - store.registerModule(key, modules[key]) - console.log('registered storage module ' + key) - } - }) - }, - 'unregister': function(store) { - const modules = { - 'em/processing': processingModule, - 'em/api': apiModule, - 'em': emModule, - } - Object.keys(modules).forEach(function(key) { - if (store.hasModule(key)) { - store.unregisterModule(key) - console.log('unregistered storage module ' + key) - } - }) - }, -} diff --git a/client/src/js/modules/types/em/store/processing-module.js b/client/src/js/modules/types/em/store/processing-module.js deleted file mode 100644 index 4dd933ff5..000000000 --- a/client/src/js/modules/types/em/store/processing-module.js +++ /dev/null @@ -1,32 +0,0 @@ -export default { - 'namespaced': true, - 'state': { - 'dialogVisible': false, - 'defaultParameters' : null, - }, - 'getters': { - 'dialogVisible': function(state) { - return state.dialogVisible - }, - 'defaultParameters': function(state) { - return state.defaultParameters - }, - }, - 'mutations': { - 'cancelDialog': function(state) { - state.dialogVisible = false - }, - /* newState is null or a defaultParameters object - If null: - 1. Dialog will get defaults from dataCollection - 2. With wantCalculate set to true - If defaultParameters: - 1. Dialog will get defaults from defaultParameters - 2. With wantCalculate set to false - */ - 'showDialog': function(state, newState) { - state.defaultParameters = newState - state.dialogVisible = true - }, - }, -} diff --git a/client/src/js/utils/plotly-loader.js b/client/src/js/utils/plotly-loader.js index f38f182ca..5d942fc30 100644 --- a/client/src/js/utils/plotly-loader.js +++ b/client/src/js/utils/plotly-loader.js @@ -6,7 +6,6 @@ var Plotly = require('plotly.js/lib/core'); // Extend with other plot types as required... Plotly.register([ require('plotly.js/lib/scatter'), - require('plotly.js/lib/histogram'), // used by EM / Ice Breaker // require('plotly.js/lib/bar'), ]);