From b307b11405e29bd0aeb5e5607d8a9fd882be8c0e Mon Sep 17 00:00:00 2001 From: Erin Schnabel Date: Sun, 18 Nov 2018 00:12:31 -0500 Subject: [PATCH] traverse and swap --- manualCompare.js | 2 +- manualTraverse.js | 8 +-- src/ScoreBook.js | 61 +++++++++++---------- src/SlackNotification.js | 17 ++---- src/SweepActions.js | 112 ++++++++++++++++++--------------------- src/actionInvoke.js | 7 ++- 6 files changed, 96 insertions(+), 111 deletions(-) diff --git a/manualCompare.js b/manualCompare.js index a82dfe0..5ebb6fd 100644 --- a/manualCompare.js +++ b/manualCompare.js @@ -25,7 +25,7 @@ if ( process.argv.length >= 4 ) { fs.writeFileSync('./result.manualCompareOne.json', JSON.stringify(result, null, 2)); }); } else { - new SweepActions().compareAll().then((result) => { + new SweepActions({debug: true}).compareAll().then((result) => { fs.writeFileSync('./result.manualCompareAll.json', JSON.stringify(result, null, 2)); }); } diff --git a/manualTraverse.js b/manualTraverse.js index 4f5a386..b944115 100644 --- a/manualTraverse.js +++ b/manualTraverse.js @@ -17,11 +17,7 @@ const SweepActions = require('./src/SweepActions.js'); const util = require('util'); const fs = require('fs'); -debug = true; - -new SweepActions().traverse().filter((x) => { - let filter = ! ( debug ); - return filter ? (x.status && x.status.indexOf('ok') < 0) : true; -}).then((result) => { +new SweepActions({debug: true}).traverse().then((result) => { fs.writeFileSync('./result.manualTraverse.json', JSON.stringify(result, null, 2)); }); + diff --git a/src/ScoreBook.js b/src/ScoreBook.js index 2835cf8..1e6a696 100644 --- a/src/ScoreBook.js +++ b/src/ScoreBook.js @@ -137,7 +137,13 @@ class ScoreBook { } getScores() { - return this.scoreDb.view('scores', 'all_scores'); + return this.scoreDb.view('scores', 'all_scores') + .then((view) => { return view.rows; }); + } + + getPaths(){ + return this.scoreDb.view('scores', 'by_path') + .then((view) => { return view.rows; }); } /* @@ -146,10 +152,11 @@ class ScoreBook { * when we're done. We're relying on _many subsequent * invocations to eventually get sites sorted-ish. */ - findSiteSwaps(all_scores) { + findSiteSwaps(all_scores, paths) { // Filter out firstRoom and rooms with unassigned/null values let scores = all_scores.filter(elem => elem.id !== 'firstroom' ); // Scores come back: [ { id: id, key: score, value: path }, ... ] + // Paths are the opposite [ { id: id, key: path, value: score }, .. ] // keep track of rooms eligible for swapping let swap_eligible = {}; @@ -159,6 +166,7 @@ class ScoreBook { let result = { swaps: [], + longest_path: (paths[paths.length - 1].key), high: (scores[scores.length - 1].key), low: 0, median: 0, @@ -169,16 +177,17 @@ class ScoreBook { // iterate by ascending score.. for(let i = 0; i < scores.length; i++) { - let score1 = scores[i]; + let score = scores[i]; + // ignore empty rooms when calculating stats - if ( result.median === 0 && score1.key >= 0 ) { - console.log("low score", i, score1); - result.low = score1.key; // first non-empty room - result.non_empty = scores.length - i +1; - console.log("scores.length", scores.length); + if ( result.median === 0 && score.key >= 0 ) { + console.log("low score", i, score); console.log("first non-empty i", i); + result.non_empty = scores.length - i +1; console.log("number of non-empty rooms", result.non_empty); + result.low = score.key; // first non-empty room + console.log("scores.length", scores.length); let median_x = Math.floor(result.non_empty / 2) + i; let quart_x = Math.floor(result.non_empty / 4); @@ -193,29 +202,23 @@ class ScoreBook { } // if this score is still eligible for swapping, then.. - if ( !!swap_eligible[score1.id] ) { - // iterate from high score to low score: favor big jumps - for(let j = scores.length - 1; j >= 0; j--) { - let score2 = scores[j]; + if ( !!swap_eligible[score.id] ) { + //console.log(`****: [${i}] ${score.key} ${score.value}`); + // Iterate by paths: lower paths are closer to the center + for(let j = 1; j < paths.length; j++) { + let path = paths[j]; - if ( score1.key >= score2.key ) { - // done with this. We're now looking at scores smaller than ours. - //console.log(`LAST: [${i}] ${score1.key} ${score1.value} --> [${j}] ${score2.key} ${score2.value} -- ${score1.id}, ${score2.id}`); - break; - } else if ( !swap_eligible[score2.id] ) { - continue; - } else if ( score1.value === score2.value ) { - //console.log(`SKIP: [${i}] ${score1.key} ${score1.value} --> [${j}] ${score2.key} ${score2.value} -- ${score1.id}, ${score2.id}`); + if ( score.id === path.id || score.value === path.key ) { continue; - } else if ( score1.value < score2.value ) { - console.log(`SWAP: [${i}] ${score1.key} ${score1.value} <-- [${j}] ${score2.key} ${score2.value} -- ${score1.id}, ${score2.id}`); - // remove scores from eligibility - delete swap_eligible[score1.id]; - delete swap_eligible[score2.id]; - result.swaps.push([score1, score2]); - break; - } else { - //console.log(`????: [${i}] ${score1.key} ${score1.value} --> [${j}] ${score2.key} ${score2.value} -- ${score1.id}, ${score2.id}`); + } else if ( !!swap_eligible[path.id] ) { + if ( score.value < path.key && score.key < path.value ) { + console.log(`SWAP: [${i}] ${score.key}pts ${score.value} <-- ${path.value}pts ${path.key} -- ${score.id}, ${path.id}`); + delete swap_eligible[score.id]; + delete swap_eligible[path.id]; + result.swaps.push([{id: score.id, path: score.value, score: score.key}, + {id: path.id, path: path.key, score: path.value}]); + break; + } } } } diff --git a/src/SlackNotification.js b/src/SlackNotification.js index b8b1303..69e719b 100644 --- a/src/SlackNotification.js +++ b/src/SlackNotification.js @@ -51,23 +51,12 @@ class SlackNotification { this.channel = '#sweep'; } - scoreStart() { + scoring(count) { let body = { username: 'Evaluator', icon_emoji: ':sleuth_or_spy:', channel: this.channel, - text: 'Off to do the rounds...' - }; - - return offItGoes(body, this.slack_url, this.env); - } - - scoreEnd(count) { - let body = { - username: 'Evaluator', - icon_emoji: ':sleuth_or_spy:', - channel: this.channel, - text: `Tallied scores for ${count} rooms` + text: `Tallying scores for ${count} rooms` }; return offItGoes(body, this.slack_url, this.env); @@ -108,7 +97,7 @@ class SlackNotification { swap(text) { let body = { username: 'Permutare', - icon_emoji: ':revolving_hearts:', + icon_emoji: ':dizzy:', channel: this.channel, text: text }; diff --git a/src/SweepActions.js b/src/SweepActions.js index a5c4c03..cd4c69d 100644 --- a/src/SweepActions.js +++ b/src/SweepActions.js @@ -61,20 +61,15 @@ class SweepActions { assert.ok(this.params.invoke, 'Specify action to invoke'); switch(this.params.invoke) { - case 'scoreAll': // (1) no parameters - return this.scoreAll(); - case 'evaluate': // (1a) requires site to evaluate - return this.evaluate(); - case 'compareAll': // (2) requires no parameters - return this.compareAll(); - case 'swap': // (2a) requires two sites to swap - return this.swap(); - case 'traverse': // (3) no parameters - return this.traverse(); - case 'orphans': // (3a) requires list of known sites - return this.orphans(); - case 'holes': // (3b) requires list of known sites - return this.holes(); + case 'traverseSwap': + return this.traverse() + .then((result1) => { + return this.compareAll() + .then((result2) => Promise.resolve({ + traverse: result1, + compare: result2 + })); + }); default: throw new Error(`Unkown invoke target ${this.params.invoke}`); } @@ -85,9 +80,6 @@ class SweepActions { .then((all_sites) => { let action_list = []; - // notify that we're starting.. - action_list.push(this.slack.scoreStart()); - // kick off a new asynchronous action for each site for(let i = 0; i < all_sites.length; i++ ) { let px = JSON.parse(JSON.stringify(this.params)); // copy / prevent mutation @@ -99,7 +91,7 @@ class SweepActions { // Note how many actions action_list.unshift(Promise.resolve({status: `${action_list.length} actions`})); - action_list.push(this.slack.scoreEnd(action_list.length)); + action_list.push(this.slack.scoring(action_list.length)); return Promise.all(action_list); }) @@ -144,32 +136,34 @@ class SweepActions { compareAll() { return this.scorebook.getScores() .then((all_scores) => { - console.log(all_scores); - let action_list = []; - let stats = this.scorebook.findSiteSwaps(all_scores.rows); - console.log(stats); - - action_list.push(this.slack.swapStats( - `All sorted. Out of ${stats.non_empty} rooms: \n` - + ` :+1: The high score was ${stats.high}\n` - + ` :ok_hand: The third quartile score was ${stats.third_quartile}\n` - + ` :v: The median score was ${stats.median}\n` - + ` :point_up: The first quartile score was ${stats.first_quartile}\n` - + ` :-1: The low score was ${stats.low}\n` - )); - - for (let i = 0; i < stats.swaps.length; i++) { - let px = JSON.parse(JSON.stringify(this.params)); // copy / prevent mutation - px.compare = { - a: stats.swaps[i][0], - b: stats.swaps[i][1] - }; - action_list.push(action(this.ow, this.env, 'sweep/actionCompare', px, this.compare(px.compare.a, px.compare.b))); - } + return this.scorebook.getPaths() + .then((all_paths) => { + let action_list = []; + let stats = this.scorebook.findSiteSwaps(all_scores, all_paths); + + action_list.push(this.slack.swapStats( + `All sorted. Out of ${stats.non_empty} rooms: \n` + + ` :+1: The high score was ${stats.high}\n` + + ` :ok_hand: The third quartile score was ${stats.third_quartile}\n` + + ` :v: The median score was ${stats.median}\n` + + ` :point_up: The first quartile score was ${stats.first_quartile}\n` + + ` :-1: The low score was ${stats.low}\n` + + ` :running: The longest path is ${stats.longest_path}` + )); + + for (let i = 0; i < stats.swaps.length; i++) { + let px = JSON.parse(JSON.stringify(this.params)); // copy / prevent mutation + px.compare = { + a: stats.swaps[i][0], + b: stats.swaps[i][1] + }; + action_list.push(action(this.ow, this.env, 'sweep/actionCompare', px, this.compare(px.compare.a, px.compare.b))); + } - // Note how many actions - action_list.unshift(Promise.resolve({status: `${action_list.length} actions`})); - return Promise.all(action_list); + // Note how many actions + action_list.unshift(Promise.resolve({status: `${action_list.length} actions`})); + return Promise.all(action_list); + }); }) .then((result) => { return this.filterResult(result); }) .then((result) => Promise.resolve({ @@ -189,34 +183,34 @@ class SweepActions { assert.ok(a && b, 'Specify two sites to swap'); return this.mapClient.fetch(a.id) - .then((site_1) => { - a.site = site_1; - a.path = Math.abs(site_1.coord.x) + Math.abs(site_1.coord.y); - if ( a.value !== a.path ) { + .then((a_site) => { + a.site = a_site; + let path = Math.abs(a.site.coord.x) + Math.abs(a.site.coord.y); + if ( path !== a.path ) { return Promise.resolve({ id: a.id, status: 'compare-ok', skip: true, - msg: `SKIPPED: ${a.value} != ${a.path} for ${a.id}` }); + msg: `SKIPPED: ${path} != ${a.path} for ${a.id}` }); } return this.mapClient.fetch(b.id) - .then((site_2) => { - b.site = site_2; - b.path = Math.abs(site_2.coord.x) + Math.abs(site_2.coord.y); - if ( b.value !== b.path ) { + .then((b_site) => { + b.site = b_site; + let path = Math.abs(b.site.coord.x) + Math.abs(b.site.coord.y); + if ( path !== b.path ) { return Promise.resolve({ id: b.id, status: 'compare-ok', skip: true, - msg: `SKIPPED: ${b.value} != ${b.path} for ${b.id}` }); + msg: `SKIPPED: ${path} != ${b.path} for ${b.id}` }); } let a_name = !!a.site.info ? a.site.info.name : a.id; let b_name = !!b.site.info ? b.site.info.name : b.id; - let msg = `${a_name}[score=${a.key}/path=${a.value}] swapped with ${b_name}[score=${b.key}/path=${b.value}]`; + let msg = `${a_name}[score=${a.score}/path=${a.path}] swapped with ${b_name}[score=${b.score}/path=${b.path}]`; return Promise.try(() => { - // if ( this.env === 'production' ) { - // return this.mapClient.swap_sites(a.site, b.site); - // } else { + if ( this.env !== 'unittest' ) { + return this.mapClient.swap_sites(a.site, b.site); + } else { return Promise.resolve([a.site, b.site]); - // } + } }) - //.then(() => this.slack.swap(msg)) + .then(() => this.slack.swap(msg)) .then(() => Promise.resolve({ marker: this.params.marker, status: 'swap-ok', diff --git a/src/actionInvoke.js b/src/actionInvoke.js index 2a54b47..a0338a9 100644 --- a/src/actionInvoke.js +++ b/src/actionInvoke.js @@ -16,9 +16,12 @@ const SweepActions = require('./SweepActions.js'); function invoke (params) { + params = params || {}; + params.invoke = params.invoke || 'traverseSwap'; return new SweepActions(params).invoke() - .then((result) => { - return { payload: result }; + .catch((err) => { + console.log("error", JSON.stringify(err)); + throw err; }); }