From 2f6081bf72bd963d2f7886feeb591584960520d1 Mon Sep 17 00:00:00 2001 From: Stanislav Rassokhin Date: Thu, 22 Jul 2021 20:36:42 +0300 Subject: [PATCH 1/3] CE-1143 Update "unvalidated" filter on Pattern Matching details page to return results per site --- CHANGELOG.md | 4 +++ TEST_NOTES.md | 7 ++++ app/model/pattern_matchings.js | 33 ++++++++++--------- .../app/app/analysis/patternmatching/index.js | 12 ++++--- 4 files changed, 36 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index daef1ffe5..9d7ebfc4d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Arbimon Release Notes +## v3.0.33 - July XX, 2021 + +- CE-1143 Update "unvalidated" filter on Pattern Matching details page to return results per site + ## v3.0.32 - June XX, 2021 Resolved issues: diff --git a/TEST_NOTES.md b/TEST_NOTES.md index d02938f8d..eecbbcf65 100644 --- a/TEST_NOTES.md +++ b/TEST_NOTES.md @@ -1,6 +1,13 @@ # Arbimon Test Notes Test Notes are used to list what pages / components / features / user flows are affected by each update. +## v3.0.33 + +- CE-1143 Update "unvalidated" filter on Pattern Matching details page to return results per site + - Check that "unvalidated" filter still returns correct data + - Check that "unvalidated" filter works quicker than before + - Check that other filters still work correctly + ## v3.0.32 - CE-1065 Patten Matching list loads quicker diff --git a/app/model/pattern_matchings.js b/app/model/pattern_matchings.js index 4d5fefe33..8d0aa4659 100644 --- a/app/model/pattern_matchings.js +++ b/app/model/pattern_matchings.js @@ -221,6 +221,7 @@ var PatternMatchings = { SEARCH_ROIS_SCHEMA : { patternMatching: joi.number().required(), + site: joi.string(), csValidationsFor: joi.number().integer(), expertCSValidations: joi.boolean(), perUserCSValidations: joi.boolean(), @@ -253,7 +254,6 @@ var PatternMatchings = { buildRoisQuery(parameters){ var builder = new SQLBuilder(); return q.ninvoke(joi, 'validate', parameters, PatternMatchings.SEARCH_ROIS_SCHEMA).then(function(parameters){ - var outputs = parameters.output instanceof Array ? parameters.output : [parameters.output]; var show = parameters.show || {}; var presteps=[]; @@ -269,15 +269,11 @@ var PatternMatchings = { builder.addTable("JOIN recordings", "R", "R.recording_id = PMR.recording_id"); builder.addTable("JOIN sites", "S", "S.site_id = R.site_id"); + builder.addProjection( - 'SUBSTRING_INDEX(R.`uri`, "/", -1) as `recording`, R.meta ', - 'S.`name` as `site`', - 'S.`site_id`', - 'EXTRACT(year FROM R.`datetime`) as `year`', - 'EXTRACT(month FROM R.`datetime`) as `month`', - 'EXTRACT(day FROM R.`datetime`) as `day`', - 'EXTRACT(hour FROM R.`datetime`) as `hour`', - 'EXTRACT(minute FROM R.`datetime`) as `min`' + 'R.uri as recording, R.meta ', + 'S.name as site, S.site_id', + 'PMR.denorm_recording_datetime as datetime', ); if(show.datetime){ @@ -312,9 +308,11 @@ var PatternMatchings = { } } - builder.addConstraint("PMR.pattern_matching_id = ?", [ - parameters.patternMatching - ]); + builder.addConstraint("PMR.pattern_matching_id = ?", [ parameters.patternMatching ]); + + if (parameters.site) { + builder.addConstraint("PMR.denorm_site_id = ?", [ parameters.site ]); + } if(parameters.expertCSValidations){ if(show.names){ @@ -491,6 +489,7 @@ var PatternMatchings = { } return this.buildRoisQuery({ patternMatching: options.patternMatchingId, + site: options.site, perSiteCount: options.perSiteCount, csValidationsFor: options.csValidationsFor, expertCSValidations: options.expertCSValidations, @@ -510,9 +509,11 @@ var PatternMatchings = { offset: options.offset, show: { patternMatchingId: true, datetime: true, names: options.showNames }, sortBy, - }).then( - builder => dbpool.query(builder.getSQL()) - ).then(function (results) { + }).then((builder) => { + return dbpool.query(builder.getSQL()) + }) + .then(this.completePMRResults) + .then(function (results) { // Fill the original filename from the meta column. for (let _1 of results) { _1.meta = _1.meta ? PatternMatchings.__parse_meta_data(_1.meta) : null; @@ -640,7 +641,7 @@ var PatternMatchings = { rois, ]) : Promise.resolve(); }, - + getRoi(patternMatchingId, roisId){ return dbpool.query( "SELECT *\n" + diff --git a/assets/app/app/analysis/patternmatching/index.js b/assets/app/app/analysis/patternmatching/index.js index 9803e713c..171325935 100644 --- a/assets/app/app/analysis/patternmatching/index.js +++ b/assets/app/app/analysis/patternmatching/index.js @@ -242,7 +242,7 @@ angular.module('a2.analysis.patternmatching', [ }, setSiteBookmark: function(site){ - if (this.isTopRoisResults()) { + if (this.shouldGetPerSite()) { console.log(this.siteIndex.indexOf(site)+1); this.selected.page = this.siteIndex.indexOf(site)+1; return this.loadPage(this.selected.page); @@ -252,8 +252,8 @@ angular.module('a2.analysis.patternmatching', [ $anchorScroll(bookmark) }, - isTopRoisResults: function(){ - return this.search && (this.search.value === 'top_200_per_site' || this.search.value === 'best_per_site' || this.search.value === 'best_per_site_day'); + shouldGetPerSite: function() { + return this.search && ['unvalidated', 'top_200_per_site', 'best_per_site', 'best_per_site_day'].includes(this.search.value); }, loadPage: function(pageNumber){ @@ -262,7 +262,7 @@ angular.module('a2.analysis.patternmatching', [ var search = this.search && this.search.value ? this.search.value : undefined this.splitAllSites = search === 'by_score'; var opts = { search: search }; - if (this.isTopRoisResults()) { + if (this.shouldGetPerSite()) { var selectedSite = this.siteIndex[pageNumber - 1]; opts.site = selectedSite && selectedSite.site_id; } @@ -276,6 +276,10 @@ angular.module('a2.analysis.patternmatching', [ limit = 1; offset = 0 break; + case 'unvalidated': + limit = 100000000; + offset = 0; + break; default: limit = this.limit offset = (pageNumber - 1) * this.limit From 213bb45e4b8ebff192ba8f5f70a920e2d8ef8a1d Mon Sep 17 00:00:00 2001 From: Stanislav Rassokhin Date: Thu, 22 Jul 2021 21:42:51 +0300 Subject: [PATCH 2/3] CE-1158 update all, present, not_present filters on PM to work by site --- app/model/pattern_matchings.js | 5 +--- .../data-api/project/pattern_matchings.js | 4 ++-- .../app/a2services/patternmatching-service.js | 2 +- .../app/app/analysis/patternmatching/index.js | 24 +++++++++++-------- 4 files changed, 18 insertions(+), 17 deletions(-) diff --git a/app/model/pattern_matchings.js b/app/model/pattern_matchings.js index 8d0aa4659..68ea79619 100644 --- a/app/model/pattern_matchings.js +++ b/app/model/pattern_matchings.js @@ -481,10 +481,7 @@ var PatternMatchings = { getRoisForId(options) { let sortBy = [['S.name', 1], ['R.datetime', 1]] // default sorting - if (options.byScorePerSite) { - sortBy = [['S.name', 1], ['PMR.score', 0]] - } - else if (options.byScore) { + if (options.byScore || options.byScorePerSite) { sortBy = [['PMR.score', 0]] } return this.buildRoisQuery({ diff --git a/app/routes/data-api/project/pattern_matchings.js b/app/routes/data-api/project/pattern_matchings.js index 3d14168d7..732ee00be 100644 --- a/app/routes/data-api/project/pattern_matchings.js +++ b/app/routes/data-api/project/pattern_matchings.js @@ -93,11 +93,11 @@ router.get('/:patternMatching/rois/:paging', function(req, res, next) { default: prom = model.patternMatchings.getRoisForId({ patternMatchingId: req.params.patternMatching, + site: req.query.site, wherePresent: req.query.search == 'present', whereNotPresent: req.query.search == 'not_present', whereUnvalidated: req.query.search == 'unvalidated', byScorePerSite: req.query.search == 'by_score_per_site', - site: req.query.site, byScore: req.query.search == 'by_score', limit: req.paging.limit || 100, offset: req.paging.offset || 0, @@ -185,7 +185,7 @@ router.post('/:patternMatching/validate', function(req, res, next) { const updatedRois = rois.filter(function(roi) { return roi.validated != validation }); const updatedRoiIds = updatedRois.map(function(roi) { return roi.pattern_matching_roi_id }); - + model.patternMatchings.validateRois(req.params.patternMatching, updatedRoiIds, validation) .then(async function(validatedRois) { for (let roi of updatedRois) { diff --git a/assets/app/a2services/patternmatching-service.js b/assets/app/a2services/patternmatching-service.js index b18eb711c..d6c97a77c 100644 --- a/assets/app/a2services/patternmatching-service.js +++ b/assets/app/a2services/patternmatching-service.js @@ -47,7 +47,7 @@ angular.module('a2.srv.patternmatching', [ return a2APIService.get('/pattern-matchings/' + patternMatchingId + '/rois/' + (offset||0) + '_' + (limit||0) + (query ? '?'+query : '')).catch(notify.serverError); }, - getSiteIndexFor: function(patternMatchingId, options) { + getSitesListFor: function(patternMatchingId, options) { var query = Object.keys(options || {}).map(function(option){ return option + '=' + encodeURIComponent(options[option]); }).join('&'); diff --git a/assets/app/app/analysis/patternmatching/index.js b/assets/app/app/analysis/patternmatching/index.js index 171325935..3ea4c7b20 100644 --- a/assets/app/app/analysis/patternmatching/index.js +++ b/assets/app/app/analysis/patternmatching/index.js @@ -149,7 +149,7 @@ angular.module('a2.analysis.patternmatching', [ this.offset = 0; this.limit = 100; this.selected = { roi_index: 0, roi: null, page: 1 }; - this.siteIndex = []; + this.sitesList = []; this.total = {rois:0, pages:0}; this.loading = {details: false, rois:false}; this.validation = this.lists.validation[2]; @@ -158,7 +158,7 @@ angular.module('a2.analysis.patternmatching', [ this.projecturl = Project.getUrl(); this.fetchDetails() .then(function() { - return this.loadSiteIndex(); + return this.loadSitesList(); }.bind(this)) .then(function() { return this.loadPage(this.selected.page); @@ -233,18 +233,18 @@ angular.module('a2.analysis.patternmatching', [ this.select($item.value); }, - loadSiteIndex: function() { - return a2PatternMatching.getSiteIndexFor(this.id) + loadSitesList: function() { + return a2PatternMatching.getSitesListFor(this.id) .then(function (index) { - this.siteIndex = index; - this.sitesTotal = this.siteIndex.length * 200; + this.sitesList = index; + this.sitesTotal = this.sitesList.length * 200; }.bind(this)) }, setSiteBookmark: function(site){ if (this.shouldGetPerSite()) { - console.log(this.siteIndex.indexOf(site)+1); - this.selected.page = this.siteIndex.indexOf(site)+1; + console.log(this.sitesList.indexOf(site)+1); + this.selected.page = this.sitesList.indexOf(site)+1; return this.loadPage(this.selected.page); } var bookmark = 'site-' + site.site_id; @@ -253,7 +253,7 @@ angular.module('a2.analysis.patternmatching', [ }, shouldGetPerSite: function() { - return this.search && ['unvalidated', 'top_200_per_site', 'best_per_site', 'best_per_site_day'].includes(this.search.value); + return this.search && ['all', 'present', 'not_present', 'unvalidated', 'top_200_per_site', 'best_per_site', 'best_per_site_day', 'by_score_per_site'].includes(this.search.value); }, loadPage: function(pageNumber){ @@ -263,7 +263,7 @@ angular.module('a2.analysis.patternmatching', [ this.splitAllSites = search === 'by_score'; var opts = { search: search }; if (this.shouldGetPerSite()) { - var selectedSite = this.siteIndex[pageNumber - 1]; + var selectedSite = this.sitesList[pageNumber - 1]; opts.site = selectedSite && selectedSite.site_id; } var limit, offset; @@ -276,7 +276,11 @@ angular.module('a2.analysis.patternmatching', [ limit = 1; offset = 0 break; + case 'all': + case 'present': + case 'not_present': case 'unvalidated': + case 'by_score_per_site': limit = 100000000; offset = 0; break; From 840eaba0dcf87e70309e5b12d28f41b09ae35459 Mon Sep 17 00:00:00 2001 From: Stanislav Rassokhin Date: Tue, 27 Jul 2021 22:28:10 +0300 Subject: [PATCH 3/3] CE-1158 update PM filters logic so "Best per Site" and "Best per Site, Day" return results by 20 sites batches --- CHANGELOG.md | 3 +- TEST_NOTES.md | 13 +- app/model/pattern_matchings.js | 55 +++++- .../data-api/project/pattern_matchings.js | 27 +-- .../app/analysis/patternmatching/details.html | 10 +- .../app/app/analysis/patternmatching/index.js | 173 +++++++++++++----- 6 files changed, 198 insertions(+), 83 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d7ebfc4d..435964f45 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,8 @@ ## v3.0.33 - July XX, 2021 -- CE-1143 Update "unvalidated" filter on Pattern Matching details page to return results per site +- CE-1143 Update "Unvalidated", "All", "Present", "Not present", "200 top scores per site", "Score per site" filters on Pattern Matching details page to return results per site +- CE-1158 update PM filters logic so "Best per Site" and "Best per Site, Day" return results by 20 sites batches ## v3.0.32 - June XX, 2021 diff --git a/TEST_NOTES.md b/TEST_NOTES.md index eecbbcf65..d86e8e2f6 100644 --- a/TEST_NOTES.md +++ b/TEST_NOTES.md @@ -3,10 +3,17 @@ Test Notes are used to list what pages / components / features / user flows are ## v3.0.33 -- CE-1143 Update "unvalidated" filter on Pattern Matching details page to return results per site - - Check that "unvalidated" filter still returns correct data - - Check that "unvalidated" filter works quicker than before +- CE-1143 Update "Unvalidated", "All", "Present", "Not present", "200 top scores per site", "Score per site" filters on Pattern Matching details page to return results per site + - Check that all filters still return correct data + - Check that pagination works correctly for every filter + - Check that selecting a site with Site List dropdown (icon with map pin) works fine + - Check that switching between filters does not break any logic + - Check that you validation feature still works correctly +- CE-1158 update PM filters logic so "Best per Site" and "Best per Site, Day" return results by 20 sites batches + - Check that these two filters return 20 sites per page (or less if project is small) + - Check that pagination works correctly for these two filters - Check that other filters still work correctly + - Check that switching between filters, pages, sites, etc works correctly ## v3.0.32 diff --git a/app/model/pattern_matchings.js b/app/model/pattern_matchings.js index 68ea79619..2590e57c1 100644 --- a/app/model/pattern_matchings.js +++ b/app/model/pattern_matchings.js @@ -222,6 +222,7 @@ var PatternMatchings = { SEARCH_ROIS_SCHEMA : { patternMatching: joi.number().required(), site: joi.string(), + sites: joi.array().items(joi.string()), csValidationsFor: joi.number().integer(), expertCSValidations: joi.boolean(), perUserCSValidations: joi.boolean(), @@ -314,6 +315,10 @@ var PatternMatchings = { builder.addConstraint("PMR.denorm_site_id = ?", [ parameters.site ]); } + if (parameters.sites) { + builder.addConstraint("PMR.denorm_site_id IN ?", [ parameters.sites ]); + } + if(parameters.expertCSValidations){ if(show.names){ builder.addProjection( @@ -487,6 +492,7 @@ var PatternMatchings = { return this.buildRoisQuery({ patternMatching: options.patternMatchingId, site: options.site, + sites: options.sites, perSiteCount: options.perSiteCount, csValidationsFor: options.csValidationsFor, expertCSValidations: options.expertCSValidations, @@ -528,6 +534,45 @@ var PatternMatchings = { }) }, + async getPmRois (req) { + // let prom + let rois = [] + let sites = [undefined] // a hack to make a loop workable even if no sites are specified + if (req.query.site) { + sites = [req.query.site] + } + if (req.query.sites) { + sites = req.query.sites.split(',') + } + for (let site of sites) { + const opts = { + patternMatchingId: req.params.patternMatching, + site: site, + limit: req.paging.limit || 100 + } + switch (req.query.search) { + case 'best_per_site': + case 'top_200_per_site': + rois = rois.concat(await this.getTopRoisByScoresPerSite(opts)); + break; + case 'best_per_site_day': + rois = rois.concat(await this.getTopRoisByScoresPerSiteDay(opts)); + break; + default: + rois = rois.concat(await this.getRoisForId({ + ...opts, + wherePresent: req.query.search == 'present', + whereNotPresent: req.query.search == 'not_present', + whereUnvalidated: req.query.search == 'unvalidated', + byScorePerSite: req.query.search == 'by_score_per_site', + byScore: req.query.search == 'by_score', + offset: req.paging.offset || 0, + })) + } + } + return rois + }, + pmrSqlSelect: `SELECT S.site_id, S.name as site, PMR.score, R.uri as recording, PMR.pattern_matching_roi_id as id, PMR.pattern_matching_id, PMR.denorm_recording_datetime as datetime, PMR.recording_id, PMR.species_id, PMR.songtype_id, PMR.x1, PMR.y1, PMR.x2, PMR.y2, PMR.uri, PMR.score, PMR.validated FROM pattern_matching_rois AS PMR`, @@ -546,17 +591,19 @@ var PatternMatchings = { return pmrs }, - getTopRoisByScoresPerSite (pmId, siteId, limit = 200) { + getTopRoisByScoresPerSite (opts) { const base = `${this.pmrSqlSelect} JOIN recordings AS R ON R.recording_id = PMR.recording_id JOIN sites AS S ON PMR.denorm_site_id = S.site_id WHERE PMR.pattern_matching_id = ? AND PMR.denorm_site_id = ? ORDER BY PMR.score DESC LIMIT ?` - return dbpool.query({ sql: base, typeCast: sqlutil.parseUtcDatetime }, [pmId, siteId, limit]) + return dbpool.query({ sql: base, typeCast: sqlutil.parseUtcDatetime }, [opts.patternMatchingId, opts.site, opts.limit || 200]) .then(this.completePMRResults) }, - getTopRoisByScoresPerSiteDay (pmId, siteId) { + getTopRoisByScoresPerSiteDay (opts) { + const pmId = opts.patternMatchingId + const site = opts.site const base = `${this.pmrSqlSelect} JOIN ( SELECT MAX(score) as max_score, denorm_recording_date @@ -568,7 +615,7 @@ var PatternMatchings = { JOIN recordings AS R ON R.recording_id = PMR.recording_id JOIN sites AS S ON PMR.denorm_site_id = S.site_id WHERE PMR.pattern_matching_id = ? AND PMR.denorm_site_id = ?;` - return dbpool.query({ sql: base, typeCast: sqlutil.parseUtcDatetime }, [pmId, siteId, pmId, siteId]) + return dbpool.query({ sql: base, typeCast: sqlutil.parseUtcDatetime }, [pmId, site, pmId, site]) .then((data) => { const pmrs = this.completePMRResults(data) return pmrs.sort((a, b) => { diff --git a/app/routes/data-api/project/pattern_matchings.js b/app/routes/data-api/project/pattern_matchings.js index 732ee00be..6c4344840 100644 --- a/app/routes/data-api/project/pattern_matchings.js +++ b/app/routes/data-api/project/pattern_matchings.js @@ -79,31 +79,10 @@ router.param('paging', function(req, res, next, paging){ return next(); }); -router.get('/:patternMatching/rois/:paging', function(req, res, next) { +router.get('/:patternMatching/rois/:paging', async function(req, res, next) { res.type('json'); - let prom - switch (req.query.search) { - case 'best_per_site': - case 'top_200_per_site': - prom = model.patternMatchings.getTopRoisByScoresPerSite(req.params.patternMatching, req.query.site, req.paging.limit); - break; - case 'best_per_site_day': - prom = model.patternMatchings.getTopRoisByScoresPerSiteDay(req.params.patternMatching, req.query.site, req.paging.limit); - break; - default: - prom = model.patternMatchings.getRoisForId({ - patternMatchingId: req.params.patternMatching, - site: req.query.site, - wherePresent: req.query.search == 'present', - whereNotPresent: req.query.search == 'not_present', - whereUnvalidated: req.query.search == 'unvalidated', - byScorePerSite: req.query.search == 'by_score_per_site', - byScore: req.query.search == 'by_score', - limit: req.paging.limit || 100, - offset: req.paging.offset || 0, - }) - } - prom.then((json) => res.json(json)) + model.patternMatchings.getPmRois(req) + .then((json) => res.json(json)) .catch(next); }); diff --git a/assets/app/app/analysis/patternmatching/details.html b/assets/app/app/analysis/patternmatching/details.html index 099c7ca07..bded276d9 100644 --- a/assets/app/app/analysis/patternmatching/details.html +++ b/assets/app/app/analysis/patternmatching/details.html @@ -101,7 +101,7 @@ - + {{$item.site}} @@ -169,7 +169,7 @@
No results found per selected site.
-
+
-
+
+ items-per-page="controller.sitesListBatchSize">
diff --git a/assets/app/app/analysis/patternmatching/index.js b/assets/app/app/analysis/patternmatching/index.js index 3ea4c7b20..30d84e0b6 100644 --- a/assets/app/app/analysis/patternmatching/index.js +++ b/assets/app/app/analysis/patternmatching/index.js @@ -150,6 +150,8 @@ angular.module('a2.analysis.patternmatching', [ this.limit = 100; this.selected = { roi_index: 0, roi: null, page: 1 }; this.sitesList = []; + this.sitesListBatchSize = 20; + this.sitesBatches = []; this.total = {rois:0, pages:0}; this.loading = {details: false, rois:false}; this.validation = this.lists.validation[2]; @@ -161,7 +163,7 @@ angular.module('a2.analysis.patternmatching', [ return this.loadSitesList(); }.bind(this)) .then(function() { - return this.loadPage(this.selected.page); + return this.loadData(1); }.bind(this)); }, @@ -211,7 +213,8 @@ angular.module('a2.analysis.patternmatching', [ onSearchChanged: function(){ this.selected.page = 1; - this.loadPage(1); + this.recalculateSiteListBatch(); + this.loadData(1); }, setupExportUrl: function(){ @@ -235,17 +238,44 @@ angular.module('a2.analysis.patternmatching', [ loadSitesList: function() { return a2PatternMatching.getSitesListFor(this.id) - .then(function (index) { - this.sitesList = index; - this.sitesTotal = this.sitesList.length * 200; + .then(function (list) { + this.sitesList = list; + this.sitesTotal = this.sitesBatches.length + this.splitSitesListIntoBatches(); }.bind(this)) }, - setSiteBookmark: function(site){ + splitSitesListIntoBatches: function () { + var batches = []; + for (var i = 0; i< this.sitesList.length; i += this.sitesListBatchSize) { + batches.push(this.sitesList.slice(i, i + this.sitesListBatchSize)); + } + this.sitesBatches = batches + }, + + recalculateSiteListBatch: function () { + var search = this.search && this.search.value ? this.search.value : undefined; + var shouldRecalculate = false + if (['all', 'present', 'not_present', 'unvalidated', 'top_200_per_site', 'by_score_per_site'].includes(search)) { + if (this.sitesListBatchSize !== 1) { + shouldRecalculate = true + } + this.sitesListBatchSize = 1; + } else if (['best_per_site', 'best_per_site_day'].includes(search)) { + if (this.sitesListBatchSize !== 20) { + shouldRecalculate = true + } + this.sitesListBatchSize = 20; + } + if (shouldRecalculate) { + this.splitSitesListIntoBatches(); + } + }, + + setSiteBookmark: function(site) { if (this.shouldGetPerSite()) { - console.log(this.sitesList.indexOf(site)+1); - this.selected.page = this.sitesList.indexOf(site)+1; - return this.loadPage(this.selected.page); + this.selected.page = this.getSiteBatchIndexBySiteId(site.site_id) + 1 + return this.loadData(); } var bookmark = 'site-' + site.site_id; $anchorScroll.yOffset = $('.a2-page-header').height() + 60; @@ -256,15 +286,40 @@ angular.module('a2.analysis.patternmatching', [ return this.search && ['all', 'present', 'not_present', 'unvalidated', 'top_200_per_site', 'best_per_site', 'best_per_site_day', 'by_score_per_site'].includes(this.search.value); }, - loadPage: function(pageNumber){ - this.rois = []; - this.loading.rois = true; + getSiteBatchIndexBySiteId: function (siteId) { + if (!this.sitesBatches || !this.sitesBatches.length) { + return undefined + } + return this.sitesBatches.findIndex(function (batch) { + return !!batch.find(function (site) { + return site.site_id === siteId + }) + }) + }, + + getSiteBatchBySiteId: function (siteId) { + const batchIndex = this.getSiteBatchIndexBySiteId(siteId); + if (batchIndex === undefined) { + return undefined + } + return this.sitesBatches[batchIndex] + }, + + getSiteBatchByPageNumber: function (page) { + if (!this.sitesBatches || !this.sitesBatches.length) { + return undefined + } + return this.sitesBatches[page - 1] + }, + + combOpts: function (data) { var search = this.search && this.search.value ? this.search.value : undefined - this.splitAllSites = search === 'by_score'; var opts = { search: search }; - if (this.shouldGetPerSite()) { - var selectedSite = this.sitesList[pageNumber - 1]; - opts.site = selectedSite && selectedSite.site_id; + if (data.site) { + opts.site = data.site; + } + if (data.sites) { + opts.sites = data.sites } var limit, offset; switch (search) { @@ -286,38 +341,64 @@ angular.module('a2.analysis.patternmatching', [ break; default: limit = this.limit - offset = (pageNumber - 1) * this.limit + offset = (data.pageNumber - 1) * this.limit } - return a2PatternMatching.getRoisFor(this.id, limit, offset, opts).then((function(rois){ - this.loading.rois = false; - if (this.splitAllSites) { - this.rois = [{ - list: rois - }] - } - else { - this.rois = rois.reduce(function(_, roi){ - var site_id = roi.site_id; - var sitename = roi.site; - var recname = roi.recording; - - if(!_.idx[sitename]){ - _.idx[sitename] = {list:[], idx:{}, name:sitename, id:site_id}; - _.list.push(_.idx[sitename]); - } + return { + limit: limit, + offset: offset, + opts: opts + } + }, - var site = _.idx[sitename]; - site.list.push(roi); + parseRoisResult: function (rois) { + console.log('rois', rois) + var search = this.search && this.search.value ? this.search.value : undefined + this.splitAllSites = search === 'by_score'; + if (this.splitAllSites) { + return [{ list: rois }] + } + else { + return rois.reduce(function(_, roi){ + var site_id = roi.site_id; + var sitename = roi.site; + + if(!_.idx[sitename]){ + _.idx[sitename] = {list:[], idx:{}, name:sitename, id:site_id}; + _.list.push(_.idx[sitename]); + } - return _; - }, {list:[], idx:{}}).list; - } - this.selected.roi = Math.min() - return rois; - }).bind(this)).catch((function(err){ - this.loading.rois = false; - return notify.serverError(err); - }).bind(this)); + var site = _.idx[sitename]; + site.list.push(roi); + + return _; + }, { list:[], idx:{} }).list; + } + }, + + loadData: function (page) { + var params; + if (this.shouldGetPerSite()) { + page = page || this.selected.page + var siteBatch = this.getSiteBatchByPageNumber(page) + var siteIds = siteBatch.map(function(s) { + return s.site_id; + }) + params = this.combOpts({ sites: siteIds }); + } else { + params = this.combOpts({ pageNumber: this.selected.page }); + } + this.rois = []; + this.loading.rois = true; + return a2PatternMatching.getRoisFor(this.id, params.limit, params.offset, params.opts) + .then(function (rois) { + this.loading.rois = false; + this.rois = this.parseRoisResult(rois); + this.selected.roi = Math.min() + }.bind(this)) + .catch((function(err){ + this.loading.rois = false; + return notify.serverError(err); + }).bind(this)); }, playRoiAudio: function(roi, $event){ @@ -361,7 +442,7 @@ angular.module('a2.analysis.patternmatching', [ } else { if(page != this.selected.page || force){ this.selected.page = page; - return this.loadPage(page); + return this.loadData(page); } }