diff --git a/backend/addcorpus/constants.py b/backend/addcorpus/constants.py index a98884358..b0d307e43 100644 --- a/backend/addcorpus/constants.py +++ b/backend/addcorpus/constants.py @@ -41,13 +41,7 @@ class VisualizationType(Enum): 'visualize', 'visualizedField', 'normalize', - 'size', - 'positions', - 'freqCompensation', - 'analysis', - 'maxDocuments', - 'numberOfNgrams', - 'dateField', + 'ngramSettings' ] ''' Field names that cannot be used because they are also query parameters in frontend routes. diff --git a/frontend/Dockerfile b/frontend/Dockerfile index 7a3d3149b..b80b97408 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -2,6 +2,9 @@ FROM node:14-alpine RUN apk update && apk add --no-cache --virtual .gyp python3 make g++ +# Install Chrome +RUN apk add chromium +ENV CHROME_BIN='/usr/bin/chromium-browser' # create directory frontend on container WORKDIR /frontend diff --git a/frontend/karma.conf.js b/frontend/karma.conf.js index 6f4468c63..ef4935f03 100644 --- a/frontend/karma.conf.js +++ b/frontend/karma.conf.js @@ -35,6 +35,7 @@ module.exports = function (config) { // '--disable-gpu', this might not be needed http://cvuorinen.net/2017/05/running-angular-tests-in-headless-chrome/ // Without a remote debugging port, Google Chrome exits immediately. '--remote-debugging-port=9222', + '--no-sandbox' ], } } diff --git a/frontend/src/app/models/visualization.spec.ts b/frontend/src/app/models/visualization.spec.ts new file mode 100644 index 000000000..b89edb49f --- /dev/null +++ b/frontend/src/app/models/visualization.spec.ts @@ -0,0 +1,37 @@ +import { NgramParameters } from './visualization'; + +describe('NgramParameters', ()=> { + let ngramParameters: NgramParameters; + + beforeEach(() => { + ngramParameters = new NgramParameters( + 2, + 'any', + false, + 'none', + 50, + 10, + 'date' + ); + }); + + it('should convert itself to a param string', () => { + const paramString = ngramParameters.toRouteParam(); + expect(paramString).toEqual( + 's:2,p:any,c:false,a:none,m:50,n:10,f:date' + ); + }); + + it('should set itself from a param string', () => { + ngramParameters.fromRouteParam( + 's:3,p:first,c:true,a:none,m:50,n:20,f:date' + ); + expect(ngramParameters.size).toEqual(3); + expect(ngramParameters.positions).toEqual('first'); + expect(ngramParameters.freqCompensation).toEqual(true); + expect(ngramParameters.analysis).toEqual('none'); + expect(ngramParameters.maxDocuments).toEqual(50); + expect(ngramParameters.numberOfNgrams).toEqual(20); + expect(ngramParameters.dateField).toEqual('date'); + }); +}); diff --git a/frontend/src/app/models/visualization.ts b/frontend/src/app/models/visualization.ts index 7dc4ec506..09ca2a9e0 100644 --- a/frontend/src/app/models/visualization.ts +++ b/frontend/src/app/models/visualization.ts @@ -1,5 +1,4 @@ import { AggregateResult, DateResult } from '.'; -import { EsQuery, EsQuerySorted } from './elasticsearch'; import { QueryParameters } from './search-requests'; export interface TermFrequencyResult { @@ -111,7 +110,7 @@ export type NGramRequestParameters = { date_field: string; } & QueryParameters; -export interface NgramParameters { +export class NgramParameters { size: number; positions: string; freqCompensation: boolean; @@ -119,6 +118,47 @@ export interface NgramParameters { maxDocuments: number; numberOfNgrams: number; dateField: string; + + ngramSettings: string []; + + constructor(size: number, + positions: string, + freqCompensation: boolean, + analysis: string, + maxDocuments: number, + numberOfNgrams: number, + dateField: string + ) { + this.size = size; + this.positions = positions; + this.freqCompensation = freqCompensation; + this.analysis = analysis; + this.maxDocuments = maxDocuments; + this.numberOfNgrams = numberOfNgrams; + this.dateField = dateField; + } + + toRouteParam(): string { + return [`s:${this.size}`,`p:${this.positions}`,`c:${this.freqCompensation}`, + `a:${this.analysis}`,`m:${this.maxDocuments}`,`n:${this.numberOfNgrams}`, + `f:${this.dateField}`].join(','); + } + + fromRouteParam(paramString: string) { + this.ngramSettings = paramString.split(','); + this.size = parseInt(this.findSetting('s'), 10); + this.positions = this.findSetting('p'); + this.freqCompensation = this.findSetting('c') === 'true'; + this.analysis = this.findSetting('a'); + this.maxDocuments = parseInt(this.findSetting('m'), 10); + this.numberOfNgrams = parseInt(this.findSetting('n'), 10); + this.dateField = this.findSetting('f'); + } + + findSetting(abbreviation: string): string | undefined{ + const setting = this.ngramSettings.find(s => s[0] === abbreviation); + return setting.split(':')[1]; + } } export interface FieldCoverage { diff --git a/frontend/src/app/visualization/ngram/ngram.component.spec.ts b/frontend/src/app/visualization/ngram/ngram.component.spec.ts index 5f1521637..c6c994cfd 100644 --- a/frontend/src/app/visualization/ngram/ngram.component.spec.ts +++ b/frontend/src/app/visualization/ngram/ngram.component.spec.ts @@ -32,12 +32,4 @@ describe('NgramComponent', () => { expect(component).toBeTruthy(); }); - it('should set the currentParameters with the right type', () => { - const params = convertToParamMap({size: '5'}); - component.setParameters(params); - expect(component.currentParameters.size).toEqual(5); - const newParams = convertToParamMap({size: '2'}); - component.setParameters(newParams); - expect(component.currentParameters.size).toEqual(2); - }); }); diff --git a/frontend/src/app/visualization/ngram/ngram.component.ts b/frontend/src/app/visualization/ngram/ngram.component.ts index 0fa273305..5f0c432b9 100644 --- a/frontend/src/app/visualization/ngram/ngram.component.ts +++ b/frontend/src/app/visualization/ngram/ngram.component.ts @@ -34,8 +34,6 @@ export class NgramComponent extends ParamDirective implements OnChanges { currentResults: NgramResults; - - // options sizeOptions = [{label: 'bigrams', value: 2}, {label: 'trigrams', value: 3}, {label: 'fourgrams', value: 4}]; positionsOptions = ['any', 'first', 'second'].map(n => ({label: `${n}`, value: n})); @@ -50,11 +48,12 @@ export class NgramComponent extends ParamDirective implements OnChanges { currentParameters: NgramParameters; lastParameters: NgramParameters; parametersChanged = false; + ngramSettings: string[]; faCheck = faCheck; faTimes = faTimes; - nullableParameters = ['size', 'position', 'freqCompensation', 'analysis', 'maxDocuments', 'numberOfNgrams', 'dateField']; + nullableParameters = ['ngramSettings']; constructor( private apiService: ApiService, @@ -64,6 +63,15 @@ export class NgramComponent extends ParamDirective implements OnChanges { paramService: ParamService ) { super(route, router, paramService); + this.currentParameters = new NgramParameters( + this.sizeOptions[0].value, + this.positionsOptions[0].value, + this.freqCompensationOptions[0].value, + 'none', + this.maxDocumentsOptions[0].value, + this.numberOfNgramsOptions[0].value, + 'date' + ); } initialize() {} @@ -82,6 +90,7 @@ export class NgramComponent extends ParamDirective implements OnChanges { this.resultsCache = {}; this.allDateFields = this.corpus.fields.filter(field => field.mappingType === 'date'); this.dateField = this.allDateFields[0]; + this.currentParameters.dateField = this.dateField.name; if (this.visualizedField.multiFields) { this.analysisOptions = [{label: 'None', value: 'none'}] .concat(this.visualizedField.multiFields.map(subfield => { @@ -99,17 +108,10 @@ export class NgramComponent extends ParamDirective implements OnChanges { } setParameters(params: Params) { - this.currentParameters = { - size: parseInt(params.get('size'), 10) || this.sizeOptions[0].value, - positions: params.get('positions') || this.positionsOptions[0].value, - freqCompensation: params.get('freqCompensation') !== undefined ? - params.get('freqCompensation') === 'true' : - this.freqCompensationOptions[0].value, - analysis: params.get('analysis') || 'none', - maxDocuments: parseInt(params.get('maxDocuments'), 10) || 50, - numberOfNgrams: parseInt(params.get('numberOfNgrams'), 10) || 10, - dateField: params.get('dateField') || 'date', - }; + const ngramSettings = params.get('ngramSettings'); + if (ngramSettings) { + this.currentParameters.fromRouteParam(ngramSettings); + } } loadGraph() { @@ -158,22 +160,17 @@ export class NgramComponent extends ParamDirective implements OnChanges { } cacheResult(result: any, params: NgramParameters): void { - const key = this.parametersKey(params); + const key = params.toRouteParam(); this.resultsCache[key] = result; } getCachedResult(params: NgramParameters): any { - const key = this.parametersKey(params); + const key = params.toRouteParam(); if (_.has(this.resultsCache, key)) { return this.resultsCache[key]; } } - parametersKey(params: NgramParameters): string { - const values = _.values(params); - return _.join(values, '/'); - } - setPositionsOptions(size) { // set positions dropdown options and reset its value this.positionsOptions = ['any'].concat(['first', 'second', 'third', 'fourth'].slice(0, size)).map( @@ -186,8 +183,8 @@ export class NgramComponent extends ParamDirective implements OnChanges { this.currentParameters[parameter] = value; if (parameter === 'size' && value) { - this.setPositionsOptions(value); -} + this.setPositionsOptions(value); + } this.parametersChanged = true; } @@ -200,7 +197,7 @@ export class NgramComponent extends ParamDirective implements OnChanges { confirmChanges() { this.parametersChanged = false; - this.setParams(this.currentParameters); + this.setParams({ ngramSettings: this.currentParameters.toRouteParam() }); } get currentSizeOption() {