diff --git a/.env.example b/.env.example
index e37cca7f9..627d1398c 100644
--- a/.env.example
+++ b/.env.example
@@ -22,6 +22,10 @@ SOLID_CABLE_POLLING=0.1.seconds
# /case page.
FORCE_SSL=false
+# This makes Quepid show detailed error messages in the UI instead of a generic 500 page,
+# useful while testing a deployment in Production.
+QUEPID_CONSIDER_ALL_REQUESTS_LOCAL=false
+
DB_HOST=mysql
DB_USERNAME=root
DB_PASSWORD=password
diff --git a/Gemfile b/Gemfile
index a39beede0..bd596d75c 100644
--- a/Gemfile
+++ b/Gemfile
@@ -91,3 +91,5 @@ group :test do
gem 'capybara'
gem 'selenium-webdriver'
end
+
+gem 'mini_racer', '~> 0.16.0'
diff --git a/Gemfile.lock b/Gemfile.lock
index bd3b1e0c5..cd81fc391 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -249,6 +249,8 @@ GEM
childprocess (~> 5.0)
letter_opener (1.10.0)
launchy (>= 2.2, < 4)
+ libv8-node (18.19.0.0-x86_64-darwin)
+ libv8-node (18.19.0.0-x86_64-linux)
listen (3.9.0)
rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10)
@@ -267,6 +269,8 @@ GEM
memory_profiler (1.1.0)
mini_histogram (0.3.1)
mini_mime (1.1.5)
+ mini_racer (0.16.0)
+ libv8-node (~> 18.19.0.0)
minitest (5.25.4)
minitest-reporters (1.7.1)
ansi
@@ -575,6 +579,7 @@ DEPENDENCIES
listen (~> 3.3)
local_time
memory_profiler
+ mini_racer (~> 0.16.0)
minitest-reporters (>= 0.5.0)
mission_control-jobs (~> 0.5.0)
mocha (~> 2.7)
diff --git a/README.md b/README.md
index 4bf856edc..2a21d83b8 100644
--- a/README.md
+++ b/README.md
@@ -272,9 +272,9 @@ bin/docker r bundle exec derailed bundle:mem
### Debugging JS
-While running the application, you can debug the javascript using your favorite tool, the way you've always done it.
+While running the application, you can debug the JavaScript using your favorite tool, the way you've always done it.
-The javascript files will be concatenated into one file, using the rails asset pipeline.
+The JavaScript files will be concatenated into one file, using the rails asset pipeline.
You can turn that off by toggling the following flag in `config/environments/development.rb`:
diff --git a/app.json b/app.json
index a5dc73496..8690206d1 100644
--- a/app.json
+++ b/app.json
@@ -90,5 +90,6 @@
}
},
"scripts": {
+ "postdeploy": "bundle exec rake db:migrate"
}
}
diff --git a/app/assets/javascripts/components/case_listing/case_listing.html b/app/assets/javascripts/components/case_listing/case_listing.html
index 6d34ae3cb..84c8ade75 100644
--- a/app/assets/javascripts/components/case_listing/case_listing.html
+++ b/app/assets/javascripts/components/case_listing/case_listing.html
@@ -11,6 +11,8 @@
ng-click="ctrl.goToCase()"
>
{{ ctrl.thisCase.caseName }}
+
+
diff --git a/app/assets/javascripts/components/export_case/_modal.html b/app/assets/javascripts/components/export_case/_modal.html
index 5cc671f43..b6ad3b2d8 100644
--- a/app/assets/javascripts/components/export_case/_modal.html
+++ b/app/assets/javascripts/components/export_case/_modal.html
@@ -29,7 +29,7 @@ Export Case: {{ ctrl.theCase.ca
Detailed export is only supported from the individual Case view.
Team Name,Case Name,Case ID,Query Text,Doc ID,Title,Rating,Field1,...,FieldN
where Field1,...,FieldN
are specified under Settings in the Displayed Fields field.
+ CSV file with Team Name,Case Name,Case ID,Query Text,Doc ID,Position,Title,Rating,Field1,...,FieldN
where Field1,...,FieldN
are specified under Settings in the Displayed Fields field.
diff --git a/app/assets/javascripts/controllers/case.js b/app/assets/javascripts/controllers/case.js
index ddefdc029..061270ba1 100644
--- a/app/assets/javascripts/controllers/case.js
+++ b/app/assets/javascripts/controllers/case.js
@@ -14,6 +14,11 @@ angular.module('QuepidApp')
$scope.caseModel.reorderEnabled = false;
$scope.scores = [];
$scope.theCase = caseSvc.getSelectedCase();
+
+ $scope.updateNightly = function () {
+ caseSvc.updateNightly($scope.theCase);
+ };
+
$scope.caseName = {
name: null,
startRename: false,
diff --git a/app/assets/javascripts/controllers/wizardModal.js b/app/assets/javascripts/controllers/wizardModal.js
index 2a6c1d20c..cdf3aeeb3 100644
--- a/app/assets/javascripts/controllers/wizardModal.js
+++ b/app/assets/javascripts/controllers/wizardModal.js
@@ -674,11 +674,15 @@ angular.module('QuepidApp')
$scope.pendingWizardSettings.submit = function() {
$log.debug('Submitting settings (from wizard modal)');
- // if we aren't using a demo, then lets finalize our queryParams with our title field.
+ // if we aren't using a demo, then lets finalize our queryParams with our best guess
if (!settingsSvc.demoSettingsChosen($scope.pendingWizardSettings.searchEngine, $scope.pendingWizardSettings.searchUrl)){
if ($scope.pendingWizardSettings.searchEngine === 'os' || $scope.pendingWizardSettings.searchEngine === 'es'){
$scope.pendingWizardSettings.queryParams = $scope.pendingWizardSettings.queryParams.replace('REPLACE_ME', $scope.pendingWizardSettings.titleField);
}
+
+ if ($scope.pendingWizardSettings.searchEngine === 'solr'){
+ $scope.pendingWizardSettings.queryParams = settingsSvc.defaultSettings['solr'].queryParams;
+ }
}
settingsSvc.update($scope.pendingWizardSettings)
diff --git a/app/assets/javascripts/factories/ScorerFactory.js b/app/assets/javascripts/factories/ScorerFactory.js
index 8c9aeed15..1c326f9bc 100644
--- a/app/assets/javascripts/factories/ScorerFactory.js
+++ b/app/assets/javascripts/factories/ScorerFactory.js
@@ -7,6 +7,9 @@
ScorerFactory
]);
+ // This file contains JavaScript logic that supports running the Scorers on the client side.
+ // Many of the methods are duplicated in lib/scorer_logic.js to support running Scorers on the server side,
+ // so be aware if you make any changes.
function ScorerFactory($q, $timeout) {
var Scorer = function(data) {
var self = this;
@@ -63,10 +66,12 @@
// public functions
+ // NOT in scorer_logic.js
function getColors() {
return scaleToColors(self.scale);
}
+ // NOT in scorer_logic.js
function scaleToArray(string) {
return string.replace(/^\s+|\s+$/g,'')
.split(/\s*,\s*/)
@@ -75,6 +80,7 @@
});
}
+ // NOT in scorer_logic.js
function scaleToColors (scale) {
var colorMap = {};
@@ -110,6 +116,7 @@
return colorMap;
}
+ // NOT in scorer_logic.js
function scaleToScaleWithLabels(scale, scaleWithLabels) {
if ( angular.isUndefined(scaleWithLabels) || scaleWithLabels === null ) {
scaleWithLabels = {};
@@ -128,6 +135,7 @@
return scaleWithLabels;
}
+ // NOT in scorer_logic.js
function setDisplayName(name, communal) {
if ( communal === true ) {
return name + ' (Communal)';
@@ -136,6 +144,7 @@
}
}
+ // NOT in scorer_logic.js
function showScaleLabel(value) {
return self.showScaleLabels === true &&
self.scaleWithLabels !== null &&
@@ -143,6 +152,7 @@
angular.isDefined(self.scaleWithLabels[value]);
}
+ // NOT in scorer_logic.js
function teamNames() {
var teams = [];
angular.forEach(self.teams, function(team) {
@@ -152,6 +162,7 @@
return self.teamName || teams.join(', ');
}
+ // NOT in scorer_logic.js
function baseAvg(docs, count) {
var sum = 0.0;
var docsRated = 0;
@@ -177,11 +188,13 @@
}
}
+ // NOT in scorer_logic.js
function baseAvgRounded(docs, count) {
var avg = self.baseAvg(docs, count);
return Math.floor(avg);
}
+ // NOT in scorer_logic.js
function avg100(docs, count) {
var max = self.scale[self.scale.length -1];
var multiplier = 100 / max;
@@ -194,6 +207,7 @@
}
}
+ // NOT in scorer_logic.js
function editDistance(str1, str2) {
var makeZeroArr = function(len) {
@@ -240,6 +254,7 @@
return bestDocsRatings;
}
+ // NOT in scorer_logic.js
function distanceFromBest(docs, bestDocs, count) {
if ( angular.isUndefined(count) ) {
count = DEFAULT_NUM_DOCS;
@@ -301,6 +316,7 @@
// We could not get this to work/test, and spent too much time on it.
// Leaving it here until we do figure out
// -YC
+ // NOT in scorer_logic.js
function checkCodeExecutionTime() {
return $q(function(resolve, reject) {
var myWorker = new Worker('scripts/scorerEvalTest.js');
@@ -320,6 +336,7 @@
});
}
+ // NOT in scorer_logic.js
function checkCode() {
var deferred = $q.defer();
var loopPromise = hasLoop();
@@ -336,6 +353,7 @@
}
// mode may no longer be used.. maybe it was for unit test style scorers?
+ // NOT in scorer_logic.js
function runCode(query, total, docs, bestDocs, mode, options) {
var scale = self.scale;
var max = scale[scale.length-1];
@@ -423,12 +441,12 @@
return baseAvg(docs, count);
};
- // Used in the original v1 scorer, which were replaced by the
- // classic search relevance metrics @P etc.
+ // NOT in scorer_logic.js
var avgRating100 = function(count) {
return avg100(docs, count);
};
+ // NOT in scorer_logic.js
var editDistanceFromBest = function(count) {
return distanceFromBest(docs, bestDocs, count);
};
@@ -459,6 +477,8 @@
}
};
+ // may not be called
+ // NOT in scorer_logic.js
var refreshRatedDocs = function(k) {
return query.refreshRatedDocs(k);
};
@@ -468,6 +488,8 @@
// param that is passed, and calls the callback function on
// each doc.
// Even those that are not in the top 10 current.
+ // may not be used?
+ // NOT in scorer_logic.js
//
// @param score, Int
// @param f, Callback
@@ -503,16 +525,19 @@
}
};
+ // NOT in scorer_logic.js
var recordDepthOfRanking = function (k){
query.depthOfRating = k;
self.depthOfRating = k;
};
+ // NOT in scorer_logic.js
/*jshint unused:false */
function pass() {
scorerDeferred.resolve(100);
}
+ // NOT in scorer_logic.js
function fail() {
scorerDeferred.reject(0);
}
@@ -521,12 +546,14 @@
scorerDeferred.resolve(score);
}
+ // NOT in scorer_logic.js
function assert(cond) {
if (!cond) {
fail();
}
}
+ // NOT in scorer_logic.js
function assertOrScore(cond, score) {
if (!cond) {
setScore(score);
diff --git a/app/assets/javascripts/factories/snapshotFactory.js b/app/assets/javascripts/factories/snapshotFactory.js
index 96672cd2e..3b711f30a 100644
--- a/app/assets/javascripts/factories/snapshotFactory.js
+++ b/app/assets/javascripts/factories/snapshotFactory.js
@@ -6,12 +6,13 @@
angular.module('QuepidApp')
.factory('SnapshotFactory', [
'$log',
+ '$filter',
'docCacheSvc',
'normalDocsSvc',
SnapshotFactory
]);
- function SnapshotFactory($log, docCacheSvc, normalDocsSvc) {
+ function SnapshotFactory($log, $filter, docCacheSvc, normalDocsSvc) {
var Snapshot = function(params) {
var self = this;
@@ -24,8 +25,7 @@
self.allDocIds = allDocIds;
self.getSearchResults = getSearchResults;
- self.timestamp = timestamp;
-
+
self.docIdsPerQuery = {};
// Map from snake_case to camelCase.
@@ -45,7 +45,7 @@
});
function snapshotName () {
- return 'Snapshot: ' + params.name;
+ return '(' + $filter('date')(params.time, 'shortDate') + ') ' + params.name;
}
function allDocIds () {
@@ -97,24 +97,6 @@
return searchResults;
}
-
- function timestamp () {
- var date = new Date(self.time * 1000);
-
- var hour = date.getHours();
- var minutes = date.getMinutes();
- var year = date.getFullYear();
- var day = date.getDate();
-
- var months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
- var month = months[date.getMonth()];
-
- if (minutes < 10) {
- minutes = '0' + ('' + minutes);
- }
-
- return day + '-' + month + '-' + year + ' ' + hour + ':' + minutes;
- }
};
// Return factory object
diff --git a/app/assets/javascripts/services/caseCSVSvc.js b/app/assets/javascripts/services/caseCSVSvc.js
index 871281b29..04ac91c81 100644
--- a/app/assets/javascripts/services/caseCSVSvc.js
+++ b/app/assets/javascripts/services/caseCSVSvc.js
@@ -79,6 +79,7 @@
'Case ID',
'Query Text',
'Doc ID',
+ 'Doc Position',
'Title',
'Rating',
];
@@ -275,7 +276,7 @@
});
/*global saveAs */
- saveAs(blob, formatDownloadFileName(aCase.caseName + '_quepid.json'));
+ saveAs(blob, formatDownloadFileName(aCase.caseName + '_case.json'));
});
}
@@ -353,7 +354,7 @@
csvContent += dataString + EOL;
}
else {
- angular.forEach(docs, function (doc) {
+ angular.forEach(docs, function (doc, index) {
var dataString;
var infoArray = [];
@@ -362,6 +363,7 @@
infoArray.push(stringifyField(aCase.lastScore.case_id));
infoArray.push(stringifyField(query.queryText));
infoArray.push(stringifyField(doc.id));
+ infoArray.push(stringifyField(index+1));
infoArray.push(stringifyField(doc.title));
infoArray.push(stringifyField(doc.getRating()));
diff --git a/app/assets/javascripts/services/caseSvc.js b/app/assets/javascripts/services/caseSvc.js
index 24ce309dd..37332a2de 100644
--- a/app/assets/javascripts/services/caseSvc.js
+++ b/app/assets/javascripts/services/caseSvc.js
@@ -34,6 +34,7 @@ angular.module('QuepidApp')
svc.refetchCaseLists = refetchCaseLists;
svc.saveDefaultScorer = saveDefaultScorer;
svc.renameCase = renameCase;
+ svc.updateNightly = updateNightly;
svc.associateBook = associateBook;
// an individual case, ie
@@ -54,6 +55,7 @@ angular.module('QuepidApp')
theCase.queriesCount = data.queries_count;
theCase.public = data.public;
theCase.archived = data.archived;
+ theCase.nightly = data.nightly;
theCase.teams = data.teams || [];
theCase.tries = data.tries || [];
theCase.scores = data.scores || [];
@@ -438,6 +440,27 @@ angular.module('QuepidApp')
});
}
}
+
+ /*
+ * update the recurrent status of the case. This could be refactored into a more
+ * general "update" method.
+ */
+ function updateNightly(theCase) {
+
+ // http PUT api/cases/+ + + + + +
+<%=query.query_text%>
+ <%= query_count - counter %>
.
+ <%=query.query_text%>
+ <% end %>
+ <%= query_count - counter %>
.
+ + This tool is for testing out background query running in Quepid for a case. +
++ After clicking the button you should see a set of updates in a alert box. +
+ + + + + + +<%= form_with(url: run_case_admin_run_case_index_path, method: :post) do |form| %> +This tool is for testing out websocket and background job handling in Quepid.
++ After clicking the button you should see a count down counter that updates every second in a alert box. +
diff --git a/app/views/api/v1/cases/_case.json.jbuilder b/app/views/api/v1/cases/_case.json.jbuilder index 9dd25acb1..13d19c5c7 100644 --- a/app/views/api/v1/cases/_case.json.jbuilder +++ b/app/views/api/v1/cases/_case.json.jbuilder @@ -19,6 +19,7 @@ json.owner_id acase.owner.id if acase.owner.present? json.book_name acase.book.name if acase.book.present? json.public acase.public.presence || false json.archived acase.archived +json.nightly acase.nightly json.options acase.options json.last_try_number acase.last_try_number diff --git a/app/views/books/_blah.turbo_stream.erb b/app/views/books/_blah.turbo_stream.erb index 98bb44f50..b4533a5d0 100644 --- a/app/views/books/_blah.turbo_stream.erb +++ b/app/views/books/_blah.turbo_stream.erb @@ -1,12 +1,15 @@ <%= turbo_stream.replace "notification-book-#{book.id}" do %> - <% if counter > 0 %> + <% if counter > 0 %><%= counter %>
.
-
- Processing query <%= qdp.query_text %>
.
-
+ Processing query <%= qdp.query_text %>
.
+
<%= @book.populate_job %>
.
+ <% end %>
<% end %>
<% if @book.import_file.attached? %>
<%= link_to 'Import file', rails_blob_path(@book.import_file.blob, only_path: true) %> made <%= time_ago_in_words @book.import_file.created_at %> ago.
<%if @book.import_job %>
- There is currently a import job for this book. It is <%=@book.import_job%>
.
+ There is currently a import job for this book set up. It is <%= @book.import_job %>
.
<% end %>
<% end %>
<% if @book.export_file.attached? %>
+ <%if @book.export_job %>
+ There is currently a export job for this book set up. It is <%= @book.export_job %>
.
+ <% end %>
<%= link_to 'Export file', rails_blob_path(@book.export_file.blob, only_path: true) %> made <%= time_ago_in_words @book.export_file.created_at %> ago.
<% end %>
diff --git a/app/views/home/_case.html.erb b/app/views/home/_case.html.erb
index 4f5e4c650..1ba39a410 100644
--- a/app/views/home/_case.html.erb
+++ b/app/views/home/_case.html.erb
@@ -1,6 +1,10 @@
<% if @case.scores.empty? %>
no scores yet
@@ -40,5 +43,6 @@
.height(60)
.config(axis: {title: nil, labelFontSize: 12}) %>
-
+
+