From f3871ec3c57310c00f92ee919b832d72309faf80 Mon Sep 17 00:00:00 2001 From: BeritJanssen Date: Wed, 4 Dec 2024 11:47:11 +0100 Subject: [PATCH 01/11] inherit StoreSync from ngram component --- .../visualization/ngram/ngram.component.ts | 28 +++++++++++-------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/frontend/src/app/visualization/ngram/ngram.component.ts b/frontend/src/app/visualization/ngram/ngram.component.ts index 43f9d1887..83fb4d77b 100644 --- a/frontend/src/app/visualization/ngram/ngram.component.ts +++ b/frontend/src/app/visualization/ngram/ngram.component.ts @@ -1,5 +1,4 @@ import { Component, ElementRef, EventEmitter, HostBinding, Input, OnChanges, Output, SimpleChanges, ViewChild } from '@angular/core'; -import { ActivatedRoute, ParamMap, Params, Router } from '@angular/router'; import * as _ from 'lodash'; import { Subject } from 'rxjs'; @@ -16,17 +15,19 @@ import { import { ApiService, NotificationService, - ParamService, VisualizationService, } from '@services'; -import { ParamDirective } from '../../param/param-directive'; + +import { StoreSync } from '../../store/store-sync'; +import { RouterStoreService } from 'app/store/router-store.service'; + @Component({ selector: 'ia-ngram', templateUrl: './ngram.component.html', styleUrls: ['./ngram.component.scss'], }) -export class NgramComponent extends ParamDirective implements OnChanges { +export class NgramComponent extends StoreSync implements OnChanges { @HostBinding('style.display') display = 'block'; // needed for loading spinner positioning @Input() queryModel: QueryModel; @@ -39,6 +40,8 @@ export class NgramComponent extends ParamDirective implements OnChanges { @ViewChild('chart-container') chartContainer: ElementRef; + keysInStore = ['ngramSettings']; + allDateFields: CorpusField[]; dateField: CorpusField; @@ -100,11 +103,9 @@ export class NgramComponent extends ParamDirective implements OnChanges { private apiService: ApiService, private visualizationService: VisualizationService, private notificationService: NotificationService, - route: ActivatedRoute, - router: Router, - paramService: ParamService + store: RouterStoreService, ) { - super(route, router, paramService); + super(store); this.currentParameters = new NgramParameters( this.sizeOptions[0].value, this.positionsOptions[0].value, @@ -170,9 +171,14 @@ export class NgramComponent extends ParamDirective implements OnChanges { this.stopPolling$.next(); } - setStateFromParams(params: ParamMap) { - this.setParameters(params); - this.loadGraph(); + storeToState(params): NgramParameters { + return params['ngramSettings'] as NgramParameters + } + + stateToStore(state: NgramParameters) { + return { + ngramSettings: state || null + } } ngOnChanges(changes: SimpleChanges): void { From 5c6c40f109740ce7f323d24141b237dc1d3881e0 Mon Sep 17 00:00:00 2001 From: BeritJanssen Date: Wed, 4 Dec 2024 17:13:11 +0100 Subject: [PATCH 02/11] fix: let ngramParameters inherit StoreSync --- frontend/src/app/models/ngram.spec.ts | 33 +++++++ frontend/src/app/models/ngram.ts | 64 ++++++++++++++ frontend/src/app/models/visualization.spec.ts | 37 -------- frontend/src/app/models/visualization.ts | 76 +++------------- .../src/app/services/visualization.service.ts | 6 +- .../ngram/ngram.component.spec.ts | 14 +++ .../visualization/ngram/ngram.component.ts | 88 ++++++------------- 7 files changed, 156 insertions(+), 162 deletions(-) create mode 100644 frontend/src/app/models/ngram.spec.ts create mode 100644 frontend/src/app/models/ngram.ts delete mode 100644 frontend/src/app/models/visualization.spec.ts diff --git a/frontend/src/app/models/ngram.spec.ts b/frontend/src/app/models/ngram.spec.ts new file mode 100644 index 000000000..50232dcb7 --- /dev/null +++ b/frontend/src/app/models/ngram.spec.ts @@ -0,0 +1,33 @@ +import { SimpleStore } from '../store/simple-store'; +import { RouterStoreService } from '../store/router-store.service'; +import { NgramParameters, NgramSettings } from './ngram'; + +describe('NgramParameters', ()=> { + let store: RouterStoreService = new SimpleStore() as any; + let ngramParameters: NgramParameters; + const testState = { + size: 3, + positions: 'first', + freqCompensation: true, + analysis: 'clean', + maxDocuments: 100, + numberOfNgrams: 20, + dateField: 'releaseDate' + } as NgramSettings; + const testParams = {ngramSettings: 's:2,p:first,c:true,a:clean,m:100,n:20,f:releaseDate'} + + beforeEach(() => { + ngramParameters = new NgramParameters(store); + }); + + it('should correctly convert internal state to a route parameter', () => { + const params = ngramParameters.stateToStore(testState); + expect(params).toEqual(testParams); + }); + + it('should correctly convert a route parameter to an internal state', () => { + const state = ngramParameters.storeToState(testParams); + expect(state).toEqual(testState); + }); + +}); diff --git a/frontend/src/app/models/ngram.ts b/frontend/src/app/models/ngram.ts new file mode 100644 index 000000000..570062bd6 --- /dev/null +++ b/frontend/src/app/models/ngram.ts @@ -0,0 +1,64 @@ +import { Params } from '@angular/router'; +import * as _ from 'lodash'; + +import { StoreSync } from '../store/store-sync'; +import { Store } from '../store/types'; + +export interface NgramSettings { + size: number; + positions: string; + freqCompensation: boolean; + analysis: string; + maxDocuments: number; + numberOfNgrams: number; + dateField: string; +} + +export class NgramParameters extends StoreSync { + + keysInStore = ['ngramSettings']; + + constructor(store: Store) { + super(store); + this.connectToStore(); + } + + stateToStore(state: NgramSettings): Params { + return { ngramSettings: [`s:${state.size}`,`p:${state.positions}`,`c:${state.freqCompensation}`, + `a:${state.analysis}`,`m:${state.maxDocuments}`,`n:${state.numberOfNgrams}`, + `f:${state.dateField}`].join(',') } + } + + storeToState(params: Params): NgramSettings { + if (_.has(params, 'ngramSettings')) { + const stringComponents = params['ngramSettings'].split(','); + return { + size: parseInt(this.findSetting('s', stringComponents), 10), + positions: this.findSetting('p', stringComponents), + freqCompensation: this.findSetting('c', stringComponents) === 'true', + analysis: this.findSetting('a', stringComponents), + maxDocuments: parseInt(this.findSetting('m', stringComponents), 10), + numberOfNgrams: parseInt(this.findSetting('n', stringComponents), 10), + dateField: this.findSetting('f', stringComponents) + } + } + return { + size: 2, + positions: 'any', + freqCompensation: false, + analysis: 'none', + maxDocuments: 50, + numberOfNgrams: 10, + dateField: 'date' + } as NgramSettings; + } + + findSetting(abbreviation: string, stringComponents: string[]): string | undefined{ + const setting = stringComponents.find(s => s[0] === abbreviation); + return setting.split(':')[1]; + } + + getCurrentRouterState(): string { + return _.get(this.store.currentParams(), 'ngramSettings'); + } +} diff --git a/frontend/src/app/models/visualization.spec.ts b/frontend/src/app/models/visualization.spec.ts deleted file mode 100644 index b89edb49f..000000000 --- a/frontend/src/app/models/visualization.spec.ts +++ /dev/null @@ -1,37 +0,0 @@ -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 6fe8fac7e..29c69b060 100644 --- a/frontend/src/app/models/visualization.ts +++ b/frontend/src/app/models/visualization.ts @@ -75,6 +75,19 @@ export type WordcloudParameters = { } & APIQuery; +export type NGramRequestParameters = { + corpus_name: string; + field: string; + ngram_size?: number; + term_position?: string; + freq_compensation?: boolean; + subfield?: string; + max_size_per_interval?: number; + number_of_ngrams?: number; + date_field: string; +} & APIQuery; + + export interface FreqTableHeader { key: string; label: string; @@ -96,69 +109,6 @@ export interface ChartParameters { chartType: ChartType; } -export type NGramRequestParameters = { - corpus_name: string; - field: string; - ngram_size?: number; - term_position?: string; - freq_compensation?: boolean; - subfield?: string; - max_size_per_interval?: number; - number_of_ngrams?: number; - date_field: string; -} & APIQuery; - -export class NgramParameters { - size: number; - positions: string; - freqCompensation: boolean; - analysis: string; - 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 { [field: string]: number; }; diff --git a/frontend/src/app/services/visualization.service.ts b/frontend/src/app/services/visualization.service.ts index 5ef5daeee..7dddd74ab 100644 --- a/frontend/src/app/services/visualization.service.ts +++ b/frontend/src/app/services/visualization.service.ts @@ -7,13 +7,13 @@ import { GeoLocation, MostFrequentWordsResult, NGramRequestParameters, - NgramParameters, QueryModel, TaskResult, TimeCategory, } from '@models'; import { ApiService } from './api.service'; import { Observable } from 'rxjs'; +import { NgramSettings } from '@models/ngram'; @Injectable({ providedIn: 'root' @@ -96,7 +96,7 @@ export class VisualizationService { corpus: Corpus, queryModel: QueryModel, field: string, - params: NgramParameters + params: NgramSettings ): NGramRequestParameters { const query = queryModel.toAPIQuery(); return { @@ -121,7 +121,7 @@ export class VisualizationService { return this.apiService.getDateTermFrequency(params); } - getNgramTasks(queryModel: QueryModel, corpus: Corpus, field: string, params: NgramParameters): Promise { + getNgramTasks(queryModel: QueryModel, corpus: Corpus, field: string, params: NgramSettings): Promise { const ngramRequestParams = this.makeNgramRequestParameters(corpus, queryModel, field, params); return this.apiService.ngramTasks(ngramRequestParams); } diff --git a/frontend/src/app/visualization/ngram/ngram.component.spec.ts b/frontend/src/app/visualization/ngram/ngram.component.spec.ts index 60e44a175..369d02317 100644 --- a/frontend/src/app/visualization/ngram/ngram.component.spec.ts +++ b/frontend/src/app/visualization/ngram/ngram.component.spec.ts @@ -9,6 +9,7 @@ import { NgramComponent } from './ngram.component'; import { ApiService } from '@services'; import { ApiServiceMock } from '../../../mock-data/api'; import { Subject } from 'rxjs'; +import { NgramSettings } from '@models/ngram'; describe('NgramComponent', () => { let component: NgramComponent; @@ -45,6 +46,19 @@ describe('NgramComponent', () => { expect(component).toBeTruthy(); }); + it('should initialize ngramParameters with default values', () => { + const defaultSettings = { + size: 2, + positions: 'any', + freqCompensation: false, + analysis: 'none', + maxDocuments: 50, + numberOfNgrams: 10, + dateField: 'date' + } as NgramSettings; + expect(component.ngramParameters.state$.value).toEqual(defaultSettings); + }) + it('should stop polling and abort running tasks when changing settings', () => { const dropdown = fixture.debugElement.query(By.css('ia-dropdown')); const changeSizeDropdown = (value: number) => { diff --git a/frontend/src/app/visualization/ngram/ngram.component.ts b/frontend/src/app/visualization/ngram/ngram.component.ts index 83fb4d77b..6059252ea 100644 --- a/frontend/src/app/visualization/ngram/ngram.component.ts +++ b/frontend/src/app/visualization/ngram/ngram.component.ts @@ -9,7 +9,6 @@ import { QueryModel, CorpusField, NgramResults, - NgramParameters, SuccessfulTask, } from '@models'; import { @@ -18,8 +17,8 @@ import { VisualizationService, } from '@services'; -import { StoreSync } from '../../store/store-sync'; -import { RouterStoreService } from 'app/store/router-store.service'; +import { RouterStoreService } from '../../store/router-store.service'; +import { NgramParameters, NgramSettings } from '@models/ngram'; @Component({ @@ -27,7 +26,7 @@ import { RouterStoreService } from 'app/store/router-store.service'; templateUrl: './ngram.component.html', styleUrls: ['./ngram.component.scss'], }) -export class NgramComponent extends StoreSync implements OnChanges { +export class NgramComponent implements OnChanges { @HostBinding('style.display') display = 'block'; // needed for loading spinner positioning @Input() queryModel: QueryModel; @@ -88,10 +87,9 @@ export class NgramComponent extends StoreSync implements OnChan tasksToCancel: string[]; resultsCache: { [parameters: string]: any } = {}; - currentParameters: NgramParameters; - lastParameters: NgramParameters; + ngramParameters: NgramParameters; + changedSettings: NgramSettings; parametersChanged = false; - ngramSettings: string[]; dataHasLoaded: boolean; isLoading = false; @@ -105,62 +103,55 @@ export class NgramComponent extends StoreSync implements OnChan private notificationService: NotificationService, store: RouterStoreService, ) { - super(store); - 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' + this.ngramParameters = new NgramParameters( + store, ); } get currentSizeOption() { - if (this.currentParameters) { + if (this.ngramParameters) { return this.sizeOptions.find( - (item) => item.value === this.currentParameters.size + (item) => item.value === this.ngramParameters.state$.value.size ); } } get currentPositionsOption() { - if (this.currentParameters) { + if (this.ngramParameters) { return this.positionsOptions.find( - (item) => item.value === this.currentParameters.positions + (item) => item.value === this.ngramParameters.state$.value.positions ); } } get currentFreqCompensationOption() { - if (this.currentParameters) { + if (this.ngramParameters) { return this.freqCompensationOptions.find( - (item) => item.value === this.currentParameters.freqCompensation + (item) => item.value === this.ngramParameters.state$.value.freqCompensation ); } } get currentAnalysisOption() { - if (this.currentParameters) { + if (this.ngramParameters) { return this.analysisOptions.find( - (item) => item.value === this.currentParameters.analysis + (item) => item.value === this.ngramParameters.state$.value.analysis ); } } get currentMaxDocumentsOption() { - if (this.currentParameters) { + if (this.ngramParameters) { return this.maxDocumentsOptions.find( - (item) => item.value === this.currentParameters.maxDocuments + (item) => item.value === this.ngramParameters.state$.value.maxDocuments ); } } get currentNumberOfNgramsOption() { - if (this.currentParameters) { + if (this.ngramParameters) { return this.numberOfNgramsOptions.find( - (item) => item.value === this.currentParameters.numberOfNgrams + (item) => item.value === this.ngramParameters.state$.value.numberOfNgrams ); } } @@ -171,16 +162,6 @@ export class NgramComponent extends StoreSync implements OnChan this.stopPolling$.next(); } - storeToState(params): NgramParameters { - return params['ngramSettings'] as NgramParameters - } - - stateToStore(state: NgramParameters) { - return { - ngramSettings: state || null - } - } - ngOnChanges(changes: SimpleChanges): void { if (changes.queryModel || changes.visualizedField) { this.resultsCache = {}; @@ -188,7 +169,6 @@ export class NgramComponent extends StoreSync implements OnChan (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' }, @@ -207,32 +187,27 @@ export class NgramComponent extends StoreSync implements OnChan } else { this.analysisOptions = undefined; } + this.ngramParameters.setParams({dateField: this.dateField.name}); } - if (this.currentParameters) { + if (this.ngramParameters.state$.value) { this.loadGraph(); } - } - setParameters(params: Params) { - const ngramSettings = params.get('ngramSettings'); - if (ngramSettings) { - this.currentParameters.fromRouteParam(ngramSettings); - } } loadGraph() { this.isLoading = true; this.dataHasLoaded = false; - this.lastParameters = _.clone(this.currentParameters); - const cachedResult = this.getCachedResult(this.currentParameters); + this.changedSettings = _.clone(this.ngramParameters.state$.value); + const cachedResult = this.getCachedResult(this.ngramParameters); if (cachedResult) { this.onDataLoaded(cachedResult); } else { this.visualizationService.getNgramTasks( this.queryModel, this.corpus, this.visualizedField.name, - this.currentParameters).then( + this.ngramParameters.state$.value).then( response => { this.tasksToCancel = response.task_ids; // tasksToCancel contains ids of the parent task and its subtasks @@ -279,12 +254,12 @@ export class NgramComponent extends StoreSync implements OnChan } cacheResult(result: any, params: NgramParameters): void { - const key = params.toRouteParam(); + const key = params.getCurrentRouterState(); this.resultsCache[key] = result; } getCachedResult(params: NgramParameters): any { - const key = params.toRouteParam(); + const key = params.getCurrentRouterState(); if (_.has(this.resultsCache, key)) { return this.resultsCache[key]; } @@ -295,11 +270,10 @@ export class NgramComponent extends StoreSync implements OnChan this.positionsOptions = ['any'] .concat(['first', 'second', 'third', 'fourth'].slice(0, size)) .map((item) => ({ value: item, label: item })); - this.currentParameters.positions = this.positionsOptions[0].value; } onParameterChange(parameter: string, value: any) { - this.currentParameters[parameter] = value; + _.assign(this.changedSettings, {[parameter]: value}); if (parameter === 'size' && value) { this.setPositionsOptions(value); @@ -310,17 +284,13 @@ export class NgramComponent extends StoreSync implements OnChan } cancelChanges() { - this.setPositionsOptions(this.lastParameters.size); - this.currentParameters = this.lastParameters; this.parametersChanged = false; } confirmChanges() { this.isLoading = true; this.parametersChanged = false; - this.setParams({ - ngramSettings: this.currentParameters.toRouteParam(), - }); + this.ngramParameters.setParams(this.changedSettings); } formatValue(value: number): string { @@ -344,7 +314,7 @@ export class NgramComponent extends StoreSync implements OnChan this.corpus, this.queryModel, this.visualizedField.name, - this.currentParameters + this.ngramParameters.state$.value ); this.apiService .requestFullData({ From 63500bbebeb7886c4491af53c92f3099aa45fe96 Mon Sep 17 00:00:00 2001 From: BeritJanssen Date: Wed, 4 Dec 2024 17:13:22 +0100 Subject: [PATCH 03/11] chore: removed unused dependencies field-filter.spec --- frontend/src/app/models/field-filter.spec.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/frontend/src/app/models/field-filter.spec.ts b/frontend/src/app/models/field-filter.spec.ts index 2ec71c117..a266b8563 100644 --- a/frontend/src/app/models/field-filter.spec.ts +++ b/frontend/src/app/models/field-filter.spec.ts @@ -1,9 +1,5 @@ -import { convertToParamMap } from '@angular/router'; import { mockFieldMultipleChoice, mockFieldDate, mockField } from '../../mock-data/corpus'; -import { EsDateFilter, EsTermsFilter } from './elasticsearch'; import { BooleanFilter, DateFilter, DateFilterData, MultipleChoiceFilter } from './field-filter'; -import { of } from 'rxjs'; -import { distinct } from 'rxjs/operators'; import { SimpleStore } from '../store/simple-store'; import { Store } from '../store/types'; From ca6f59f5988b851adb57003aa7e3da01aefa50a9 Mon Sep 17 00:00:00 2001 From: BeritJanssen Date: Thu, 5 Dec 2024 11:01:10 +0100 Subject: [PATCH 04/11] fix unit tests --- frontend/src/app/models/ngram.spec.ts | 16 +++++++++++++- .../visualization/ngram/ngram.component.ts | 21 ++++++++++--------- 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/frontend/src/app/models/ngram.spec.ts b/frontend/src/app/models/ngram.spec.ts index 50232dcb7..f52515ffb 100644 --- a/frontend/src/app/models/ngram.spec.ts +++ b/frontend/src/app/models/ngram.spec.ts @@ -14,7 +14,7 @@ describe('NgramParameters', ()=> { numberOfNgrams: 20, dateField: 'releaseDate' } as NgramSettings; - const testParams = {ngramSettings: 's:2,p:first,c:true,a:clean,m:100,n:20,f:releaseDate'} + const testParams = {ngramSettings: 's:3,p:first,c:true,a:clean,m:100,n:20,f:releaseDate'} beforeEach(() => { ngramParameters = new NgramParameters(store); @@ -30,4 +30,18 @@ describe('NgramParameters', ()=> { expect(state).toEqual(testState); }); + it('should return default values if no relevant route parameter is present', () => { + const defaultSettings = { + size: 2, + positions: 'any', + freqCompensation: false, + analysis: 'none', + maxDocuments: 50, + numberOfNgrams: 10, + dateField: 'date' + } as NgramSettings; + const state = ngramParameters.storeToState({irrelevant: 'parameter'}) + expect(state).toEqual(defaultSettings); + }) + }); diff --git a/frontend/src/app/visualization/ngram/ngram.component.ts b/frontend/src/app/visualization/ngram/ngram.component.ts index 6059252ea..adc85032a 100644 --- a/frontend/src/app/visualization/ngram/ngram.component.ts +++ b/frontend/src/app/visualization/ngram/ngram.component.ts @@ -88,7 +88,7 @@ export class NgramComponent implements OnChanges { resultsCache: { [parameters: string]: any } = {}; ngramParameters: NgramParameters; - changedSettings: NgramSettings; + currentSettings: NgramSettings; parametersChanged = false; dataHasLoaded: boolean; isLoading = false; @@ -106,12 +106,13 @@ export class NgramComponent implements OnChanges { this.ngramParameters = new NgramParameters( store, ); + this.currentSettings = this.ngramParameters.state$.value; } get currentSizeOption() { if (this.ngramParameters) { return this.sizeOptions.find( - (item) => item.value === this.ngramParameters.state$.value.size + (item) => item.value === this.currentSettings.size ); } } @@ -119,7 +120,7 @@ export class NgramComponent implements OnChanges { get currentPositionsOption() { if (this.ngramParameters) { return this.positionsOptions.find( - (item) => item.value === this.ngramParameters.state$.value.positions + (item) => item.value === this.currentSettings.positions ); } } @@ -127,7 +128,7 @@ export class NgramComponent implements OnChanges { get currentFreqCompensationOption() { if (this.ngramParameters) { return this.freqCompensationOptions.find( - (item) => item.value === this.ngramParameters.state$.value.freqCompensation + (item) => item.value === this.currentSettings.freqCompensation ); } } @@ -135,7 +136,7 @@ export class NgramComponent implements OnChanges { get currentAnalysisOption() { if (this.ngramParameters) { return this.analysisOptions.find( - (item) => item.value === this.ngramParameters.state$.value.analysis + (item) => item.value === this.currentSettings.analysis ); } } @@ -143,7 +144,7 @@ export class NgramComponent implements OnChanges { get currentMaxDocumentsOption() { if (this.ngramParameters) { return this.maxDocumentsOptions.find( - (item) => item.value === this.ngramParameters.state$.value.maxDocuments + (item) => item.value === this.currentSettings.maxDocuments ); } } @@ -151,7 +152,7 @@ export class NgramComponent implements OnChanges { get currentNumberOfNgramsOption() { if (this.ngramParameters) { return this.numberOfNgramsOptions.find( - (item) => item.value === this.ngramParameters.state$.value.numberOfNgrams + (item) => item.value === this.currentSettings.numberOfNgrams ); } } @@ -199,7 +200,7 @@ export class NgramComponent implements OnChanges { loadGraph() { this.isLoading = true; this.dataHasLoaded = false; - this.changedSettings = _.clone(this.ngramParameters.state$.value); + this.currentSettings = _.clone(this.ngramParameters.state$.value); const cachedResult = this.getCachedResult(this.ngramParameters); if (cachedResult) { @@ -273,7 +274,7 @@ export class NgramComponent implements OnChanges { } onParameterChange(parameter: string, value: any) { - _.assign(this.changedSettings, {[parameter]: value}); + _.assign(this.currentSettings, {[parameter]: value}); if (parameter === 'size' && value) { this.setPositionsOptions(value); @@ -290,7 +291,7 @@ export class NgramComponent implements OnChanges { confirmChanges() { this.isLoading = true; this.parametersChanged = false; - this.ngramParameters.setParams(this.changedSettings); + this.ngramParameters.setParams(this.currentSettings); } formatValue(value: number): string { From 792aabbf88bba03330abbba1ae9d6e9638eb1013 Mon Sep 17 00:00:00 2001 From: BeritJanssen Date: Thu, 5 Dec 2024 14:02:55 +0100 Subject: [PATCH 05/11] ngram component refinements --- .../visualization/ngram/ngram.component.ts | 52 +++++++------------ 1 file changed, 19 insertions(+), 33 deletions(-) diff --git a/frontend/src/app/visualization/ngram/ngram.component.ts b/frontend/src/app/visualization/ngram/ngram.component.ts index adc85032a..8fd77ac55 100644 --- a/frontend/src/app/visualization/ngram/ngram.component.ts +++ b/frontend/src/app/visualization/ngram/ngram.component.ts @@ -110,56 +110,42 @@ export class NgramComponent implements OnChanges { } get currentSizeOption() { - if (this.ngramParameters) { - return this.sizeOptions.find( - (item) => item.value === this.currentSettings.size - ); - } + return this.sizeOptions.find( + (item) => item.value === this.currentSettings.size + ); } get currentPositionsOption() { - if (this.ngramParameters) { - return this.positionsOptions.find( - (item) => item.value === this.currentSettings.positions - ); - } + return this.positionsOptions.find( + (item) => item.value === this.currentSettings.positions + ); } get currentFreqCompensationOption() { - if (this.ngramParameters) { - return this.freqCompensationOptions.find( - (item) => item.value === this.currentSettings.freqCompensation - ); - } + return this.freqCompensationOptions.find( + (item) => item.value === this.currentSettings.freqCompensation + ); } get currentAnalysisOption() { - if (this.ngramParameters) { - return this.analysisOptions.find( - (item) => item.value === this.currentSettings.analysis - ); - } + return this.analysisOptions.find( + (item) => item.value === this.currentSettings.analysis + ); } get currentMaxDocumentsOption() { - if (this.ngramParameters) { - return this.maxDocumentsOptions.find( - (item) => item.value === this.currentSettings.maxDocuments - ); - } + return this.maxDocumentsOptions.find( + (item) => item.value === this.currentSettings.maxDocuments + ); } get currentNumberOfNgramsOption() { - if (this.ngramParameters) { - return this.numberOfNgramsOptions.find( - (item) => item.value === this.currentSettings.numberOfNgrams - ); - } + return this.numberOfNgramsOptions.find( + (item) => item.value === this.currentSettings.numberOfNgrams + ); } - initialize() {} - - teardown(): void { + ngOnDestroy(): void { this.stopPolling$.next(); } From b410da366e4e8ef907655640925a4464be677914 Mon Sep 17 00:00:00 2001 From: BeritJanssen Date: Thu, 5 Dec 2024 14:03:15 +0100 Subject: [PATCH 06/11] add unit test for ngramComponent.confirmChanges() --- frontend/src/app/common-test-bed.ts | 4 +-- .../ngram/ngram.component.spec.ts | 29 +++++++++++++++---- .../visualization/ngram/ngram.component.ts | 23 ++++++++------- frontend/src/mock-data/visualization.ts | 10 ++++--- 4 files changed, 45 insertions(+), 21 deletions(-) diff --git a/frontend/src/app/common-test-bed.ts b/frontend/src/app/common-test-bed.ts index 888762621..678338635 100644 --- a/frontend/src/app/common-test-bed.ts +++ b/frontend/src/app/common-test-bed.ts @@ -20,7 +20,7 @@ import { EntityService } from './services/entity.service'; import { WordmodelsService } from './services/wordmodels.service'; import { WordmodelsServiceMock } from '../mock-data/wordmodels'; import { VisualizationService } from './services/visualization.service'; -import { visualizationServiceMock } from '../mock-data/visualization'; +import { VisualizationServiceMock } from '../mock-data/visualization'; import { TagService } from './services/tag.service'; import { TagServiceMock } from '../mock-data/tag'; import { RouterStoreService } from './store/router-store.service'; @@ -72,7 +72,7 @@ export const commonTestBed = () => { }, { provide: VisualizationService, - useValue: new visualizationServiceMock(), + useValue: new VisualizationServiceMock(), }, { provide: TagService, diff --git a/frontend/src/app/visualization/ngram/ngram.component.spec.ts b/frontend/src/app/visualization/ngram/ngram.component.spec.ts index 369d02317..5d5def614 100644 --- a/frontend/src/app/visualization/ngram/ngram.component.spec.ts +++ b/frontend/src/app/visualization/ngram/ngram.component.spec.ts @@ -6,15 +6,19 @@ import { mockCorpus } from '../../../mock-data/corpus'; import { MockCorpusResponse } from '../../../mock-data/corpus-response'; import { commonTestBed } from '../../common-test-bed'; import { NgramComponent } from './ngram.component'; -import { ApiService } from '@services'; -import { ApiServiceMock } from '../../../mock-data/api'; +import { ApiService, VisualizationService } from '@services'; +import { ApiServiceMock, fakeNgramResult } from '../../../mock-data/api'; +import { VisualizationServiceMock } from '../../../mock-data/visualization'; import { Subject } from 'rxjs'; import { NgramSettings } from '@models/ngram'; + describe('NgramComponent', () => { let component: NgramComponent; let fixture: ComponentFixture; let apiService: ApiServiceMock; + let visualizationService: VisualizationService; + let cacheKey = 's:2,p:any,c:false,a:none,m:50,n:10,f:date'; beforeEach(waitForAsync(() => { commonTestBed().testingModule.compileComponents(); @@ -22,11 +26,14 @@ describe('NgramComponent', () => { beforeEach(() => { apiService = new ApiServiceMock({}); - spyOn(apiService, 'abortTasks'); + visualizationService = new VisualizationServiceMock() as any; + spyOn(visualizationService, 'getNgramTasks').and.callThrough(); + spyOn(apiService, 'abortTasks').and.callThrough(); fixture = TestBed.overrideComponent(NgramComponent, { set: { providers: [ - { provide: ApiService, useValue: apiService} + { provide: ApiService, useValue: apiService }, + { provide: VisualizationService, useValue: visualizationService } ] } }).createComponent(NgramComponent); @@ -39,6 +46,7 @@ describe('NgramComponent', () => { component.visualizedField = {name: 'speech'} as any; component.asTable = false; component.palette = ['yellow', 'blue']; + spyOn(component.ngramParameters, 'getCurrentRouterState').and.returnValue(cacheKey); fixture.detectChanges(); }); @@ -74,10 +82,21 @@ describe('NgramComponent', () => { it('should stop polling and abort running tasks on destroy', () => { spyOn(component.stopPolling$, 'next'); - component.teardown(); + component.ngOnDestroy(); expect(component.stopPolling$.next).toHaveBeenCalled(); component.dataHasLoaded = false; // fake working response expect(component.tasksToCancel).toBeUndefined(); }); + it('should send a new ngram request after confirmed changes', () => { + component.confirmChanges(); + expect(visualizationService.getNgramTasks).toHaveBeenCalled(); + }); + + it('should not send a new ngram request when the result is cached', () => { + component.resultsCache = {[cacheKey]: fakeNgramResult}; + component.confirmChanges(); + expect(visualizationService.getNgramTasks).not.toHaveBeenCalled(); + }) + }); diff --git a/frontend/src/app/visualization/ngram/ngram.component.ts b/frontend/src/app/visualization/ngram/ngram.component.ts index 8fd77ac55..51b69bf33 100644 --- a/frontend/src/app/visualization/ngram/ngram.component.ts +++ b/frontend/src/app/visualization/ngram/ngram.component.ts @@ -106,7 +106,7 @@ export class NgramComponent implements OnChanges { this.ngramParameters = new NgramParameters( store, ); - this.currentSettings = this.ngramParameters.state$.value; + this.currentSettings = _.clone(this.ngramParameters.state$.value); } get currentSizeOption() { @@ -186,15 +186,14 @@ export class NgramComponent implements OnChanges { loadGraph() { this.isLoading = true; this.dataHasLoaded = false; - this.currentSettings = _.clone(this.ngramParameters.state$.value); - const cachedResult = this.getCachedResult(this.ngramParameters); + const cachedResult = this.getCachedResult(); if (cachedResult) { this.onDataLoaded(cachedResult); } else { this.visualizationService.getNgramTasks( this.queryModel, this.corpus, this.visualizedField.name, - this.ngramParameters.state$.value).then( + this.currentSettings).then( response => { this.tasksToCancel = response.task_ids; // tasksToCancel contains ids of the parent task and its subtasks @@ -224,6 +223,7 @@ export class NgramComponent implements OnChanges { onDataLoaded(result: NgramResults) { this.dataHasLoaded = true; this.currentResults = result; + this.cacheResult(result); this.tableData = this.makeTableData(result); this.isLoading = false; } @@ -240,14 +240,16 @@ export class NgramComponent implements OnChanges { ); } - cacheResult(result: any, params: NgramParameters): void { - const key = params.getCurrentRouterState(); - this.resultsCache[key] = result; + cacheResult(result: any): void { + const key = this.ngramParameters.getCurrentRouterState(); + if (key) { + this.resultsCache[key] = result; + } } - getCachedResult(params: NgramParameters): any { - const key = params.getCurrentRouterState(); - if (_.has(this.resultsCache, key)) { + getCachedResult(): any { + const key = this.ngramParameters.getCurrentRouterState(); + if (key && _.has(this.resultsCache, key)) { return this.resultsCache[key]; } } @@ -278,6 +280,7 @@ export class NgramComponent implements OnChanges { this.isLoading = true; this.parametersChanged = false; this.ngramParameters.setParams(this.currentSettings); + this.loadGraph(); } formatValue(value: number): string { diff --git a/frontend/src/mock-data/visualization.ts b/frontend/src/mock-data/visualization.ts index 15aa34482..c81f9baa1 100644 --- a/frontend/src/mock-data/visualization.ts +++ b/frontend/src/mock-data/visualization.ts @@ -1,6 +1,8 @@ -export class visualizationServiceMock { - public async getRelatedWords() {} - public async getNgramTasks() { - return Promise.resolve({task_ids: ['ngram-task-id']}); +import { TaskResult } from '@models'; + +export class VisualizationServiceMock { + public getRelatedWords() {} + public async getNgramTasks(): Promise { + return Promise.resolve({task_ids: ['ngram-task-id', 'another-task-id']}); } } From cfaa837d94bc8fa288017351c4adefb63c12b687 Mon Sep 17 00:00:00 2001 From: BeritJanssen Date: Thu, 12 Dec 2024 09:36:06 +0100 Subject: [PATCH 07/11] fix intialization and destroy behaviour --- .../ngram/ngram.component.spec.ts | 28 +++++++++++-------- .../visualization/ngram/ngram.component.ts | 4 +++ 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/frontend/src/app/visualization/ngram/ngram.component.spec.ts b/frontend/src/app/visualization/ngram/ngram.component.spec.ts index 5d5def614..27144aedf 100644 --- a/frontend/src/app/visualization/ngram/ngram.component.spec.ts +++ b/frontend/src/app/visualization/ngram/ngram.component.spec.ts @@ -19,6 +19,15 @@ describe('NgramComponent', () => { let apiService: ApiServiceMock; let visualizationService: VisualizationService; let cacheKey = 's:2,p:any,c:false,a:none,m:50,n:10,f:date'; + let defaultSettings = { + size: 2, + positions: 'any', + freqCompensation: false, + analysis: 'none', + maxDocuments: 50, + numberOfNgrams: 10, + dateField: 'date' + } as NgramSettings; beforeEach(waitForAsync(() => { commonTestBed().testingModule.compileComponents(); @@ -55,16 +64,13 @@ describe('NgramComponent', () => { }); it('should initialize ngramParameters with default values', () => { - const defaultSettings = { - size: 2, - positions: 'any', - freqCompensation: false, - analysis: 'none', - maxDocuments: 50, - numberOfNgrams: 10, - dateField: 'date' - } as NgramSettings; expect(component.ngramParameters.state$.value).toEqual(defaultSettings); + }); + + it('should not abort tasks when `onParameterChange` is triggered during initialization', () => { + spyOn(component.stopPolling$, 'next'); + component.onParameterChange('size', 2); + expect(component.stopPolling$.next).not.toHaveBeenCalled(); }) it('should stop polling and abort running tasks when changing settings', () => { @@ -73,9 +79,9 @@ describe('NgramComponent', () => { const eventObj = { parameter: 'size', value }; dropdown.triggerEventHandler('onChange', eventObj); }; - spyOn(fixture.componentInstance.stopPolling$, 'next'); + spyOn(component.stopPolling$, 'next'); changeSizeDropdown(10); - expect(fixture.componentInstance.stopPolling$.next).toHaveBeenCalled(); + expect(component.stopPolling$.next).toHaveBeenCalled(); component.dataHasLoaded = false; // fake working response expect(component.tasksToCancel).toBeUndefined(); }); diff --git a/frontend/src/app/visualization/ngram/ngram.component.ts b/frontend/src/app/visualization/ngram/ngram.component.ts index 51b69bf33..7227451a1 100644 --- a/frontend/src/app/visualization/ngram/ngram.component.ts +++ b/frontend/src/app/visualization/ngram/ngram.component.ts @@ -147,6 +147,7 @@ export class NgramComponent implements OnChanges { ngOnDestroy(): void { this.stopPolling$.next(); + this.ngramParameters.complete(); } ngOnChanges(changes: SimpleChanges): void { @@ -262,6 +263,9 @@ export class NgramComponent implements OnChanges { } onParameterChange(parameter: string, value: any) { + if (_.get(this.currentSettings, parameter) === value) { + return; + } _.assign(this.currentSettings, {[parameter]: value}); if (parameter === 'size' && value) { From 4aa1f7efc4aea1127b04f4304aefb51af1900397 Mon Sep 17 00:00:00 2001 From: BeritJanssen Date: Thu, 12 Dec 2024 10:47:29 +0100 Subject: [PATCH 08/11] fix: reorganize key retrieval for results cache --- frontend/src/app/models/ngram.ts | 14 +++++++------- .../visualization/ngram/ngram.component.spec.ts | 1 - .../src/app/visualization/ngram/ngram.component.ts | 6 +++--- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/frontend/src/app/models/ngram.ts b/frontend/src/app/models/ngram.ts index 570062bd6..a287d9c52 100644 --- a/frontend/src/app/models/ngram.ts +++ b/frontend/src/app/models/ngram.ts @@ -23,10 +23,14 @@ export class NgramParameters extends StoreSync { this.connectToStore(); } - stateToStore(state: NgramSettings): Params { - return { ngramSettings: [`s:${state.size}`,`p:${state.positions}`,`c:${state.freqCompensation}`, + stringifyNgramSettings(state: NgramSettings): string { + return [`s:${state.size}`,`p:${state.positions}`,`c:${state.freqCompensation}`, `a:${state.analysis}`,`m:${state.maxDocuments}`,`n:${state.numberOfNgrams}`, - `f:${state.dateField}`].join(',') } + `f:${state.dateField}`].join(',') + } + + stateToStore(state: NgramSettings): Params { + return { ngramSettings: this.stringifyNgramSettings(state)} } storeToState(params: Params): NgramSettings { @@ -57,8 +61,4 @@ export class NgramParameters extends StoreSync { const setting = stringComponents.find(s => s[0] === abbreviation); return setting.split(':')[1]; } - - getCurrentRouterState(): string { - return _.get(this.store.currentParams(), 'ngramSettings'); - } } diff --git a/frontend/src/app/visualization/ngram/ngram.component.spec.ts b/frontend/src/app/visualization/ngram/ngram.component.spec.ts index 27144aedf..19e17baf8 100644 --- a/frontend/src/app/visualization/ngram/ngram.component.spec.ts +++ b/frontend/src/app/visualization/ngram/ngram.component.spec.ts @@ -55,7 +55,6 @@ describe('NgramComponent', () => { component.visualizedField = {name: 'speech'} as any; component.asTable = false; component.palette = ['yellow', 'blue']; - spyOn(component.ngramParameters, 'getCurrentRouterState').and.returnValue(cacheKey); fixture.detectChanges(); }); diff --git a/frontend/src/app/visualization/ngram/ngram.component.ts b/frontend/src/app/visualization/ngram/ngram.component.ts index 7227451a1..49c6484ea 100644 --- a/frontend/src/app/visualization/ngram/ngram.component.ts +++ b/frontend/src/app/visualization/ngram/ngram.component.ts @@ -242,14 +242,14 @@ export class NgramComponent implements OnChanges { } cacheResult(result: any): void { - const key = this.ngramParameters.getCurrentRouterState(); + const key = this.ngramParameters.stringifyNgramSettings(this.currentSettings); if (key) { this.resultsCache[key] = result; } } getCachedResult(): any { - const key = this.ngramParameters.getCurrentRouterState(); + const key = this.ngramParameters.stringifyNgramSettings(this.currentSettings); if (key && _.has(this.resultsCache, key)) { return this.resultsCache[key]; } @@ -281,9 +281,9 @@ export class NgramComponent implements OnChanges { } confirmChanges() { + this.ngramParameters.setParams(this.currentSettings); this.isLoading = true; this.parametersChanged = false; - this.ngramParameters.setParams(this.currentSettings); this.loadGraph(); } From 47a006020f1c31d7a2cb8f7a632cb25096c50cc2 Mon Sep 17 00:00:00 2001 From: BeritJanssen Date: Mon, 23 Dec 2024 11:37:35 +0100 Subject: [PATCH 09/11] remove date field from routing --- frontend/src/app/models/ngram.spec.ts | 4 +--- frontend/src/app/models/ngram.ts | 6 +----- frontend/src/app/services/visualization.service.ts | 9 +++++---- .../app/visualization/ngram/ngram.component.spec.ts | 4 ++-- .../src/app/visualization/ngram/ngram.component.ts | 12 +++++++----- 5 files changed, 16 insertions(+), 19 deletions(-) diff --git a/frontend/src/app/models/ngram.spec.ts b/frontend/src/app/models/ngram.spec.ts index f52515ffb..8f2748422 100644 --- a/frontend/src/app/models/ngram.spec.ts +++ b/frontend/src/app/models/ngram.spec.ts @@ -12,9 +12,8 @@ describe('NgramParameters', ()=> { analysis: 'clean', maxDocuments: 100, numberOfNgrams: 20, - dateField: 'releaseDate' } as NgramSettings; - const testParams = {ngramSettings: 's:3,p:first,c:true,a:clean,m:100,n:20,f:releaseDate'} + const testParams = {ngramSettings: 's:3,p:first,c:true,a:clean,m:100,n:20'} beforeEach(() => { ngramParameters = new NgramParameters(store); @@ -38,7 +37,6 @@ describe('NgramParameters', ()=> { analysis: 'none', maxDocuments: 50, numberOfNgrams: 10, - dateField: 'date' } as NgramSettings; const state = ngramParameters.storeToState({irrelevant: 'parameter'}) expect(state).toEqual(defaultSettings); diff --git a/frontend/src/app/models/ngram.ts b/frontend/src/app/models/ngram.ts index a287d9c52..1cc9670e5 100644 --- a/frontend/src/app/models/ngram.ts +++ b/frontend/src/app/models/ngram.ts @@ -11,7 +11,6 @@ export interface NgramSettings { analysis: string; maxDocuments: number; numberOfNgrams: number; - dateField: string; } export class NgramParameters extends StoreSync { @@ -25,8 +24,7 @@ export class NgramParameters extends StoreSync { stringifyNgramSettings(state: NgramSettings): string { return [`s:${state.size}`,`p:${state.positions}`,`c:${state.freqCompensation}`, - `a:${state.analysis}`,`m:${state.maxDocuments}`,`n:${state.numberOfNgrams}`, - `f:${state.dateField}`].join(',') + `a:${state.analysis}`,`m:${state.maxDocuments}`,`n:${state.numberOfNgrams}`].join(',') } stateToStore(state: NgramSettings): Params { @@ -43,7 +41,6 @@ export class NgramParameters extends StoreSync { analysis: this.findSetting('a', stringComponents), maxDocuments: parseInt(this.findSetting('m', stringComponents), 10), numberOfNgrams: parseInt(this.findSetting('n', stringComponents), 10), - dateField: this.findSetting('f', stringComponents) } } return { @@ -53,7 +50,6 @@ export class NgramParameters extends StoreSync { analysis: 'none', maxDocuments: 50, numberOfNgrams: 10, - dateField: 'date' } as NgramSettings; } diff --git a/frontend/src/app/services/visualization.service.ts b/frontend/src/app/services/visualization.service.ts index 7dddd74ab..52ff1c4cc 100644 --- a/frontend/src/app/services/visualization.service.ts +++ b/frontend/src/app/services/visualization.service.ts @@ -96,7 +96,8 @@ export class VisualizationService { corpus: Corpus, queryModel: QueryModel, field: string, - params: NgramSettings + params: NgramSettings, + dateField: string ): NGramRequestParameters { const query = queryModel.toAPIQuery(); return { @@ -109,7 +110,7 @@ export class VisualizationService { subfield: params.analysis, max_size_per_interval: params.maxDocuments, number_of_ngrams: params.numberOfNgrams, - date_field: params.dateField + date_field: dateField }; } @@ -121,8 +122,8 @@ export class VisualizationService { return this.apiService.getDateTermFrequency(params); } - getNgramTasks(queryModel: QueryModel, corpus: Corpus, field: string, params: NgramSettings): Promise { - const ngramRequestParams = this.makeNgramRequestParameters(corpus, queryModel, field, params); + getNgramTasks(queryModel: QueryModel, corpus: Corpus, field: string, params: NgramSettings, dateField: string): Promise { + const ngramRequestParams = this.makeNgramRequestParameters(corpus, queryModel, field, params, dateField); return this.apiService.ngramTasks(ngramRequestParams); } diff --git a/frontend/src/app/visualization/ngram/ngram.component.spec.ts b/frontend/src/app/visualization/ngram/ngram.component.spec.ts index 19e17baf8..8a7c0e075 100644 --- a/frontend/src/app/visualization/ngram/ngram.component.spec.ts +++ b/frontend/src/app/visualization/ngram/ngram.component.spec.ts @@ -18,7 +18,7 @@ describe('NgramComponent', () => { let fixture: ComponentFixture; let apiService: ApiServiceMock; let visualizationService: VisualizationService; - let cacheKey = 's:2,p:any,c:false,a:none,m:50,n:10,f:date'; + let cacheKey = 's:2,p:any,c:false,a:none,m:50,n:10'; let defaultSettings = { size: 2, positions: 'any', @@ -26,7 +26,6 @@ describe('NgramComponent', () => { analysis: 'none', maxDocuments: 50, numberOfNgrams: 10, - dateField: 'date' } as NgramSettings; beforeEach(waitForAsync(() => { @@ -53,6 +52,7 @@ describe('NgramComponent', () => { component.queryModel = queryModel; component.corpus = MockCorpusResponse[0] as any; component.visualizedField = {name: 'speech'} as any; + component.dateField = {name: 'date'} as any; component.asTable = false; component.palette = ['yellow', 'blue']; fixture.detectChanges(); diff --git a/frontend/src/app/visualization/ngram/ngram.component.ts b/frontend/src/app/visualization/ngram/ngram.component.ts index 49c6484ea..e9a4a6cc0 100644 --- a/frontend/src/app/visualization/ngram/ngram.component.ts +++ b/frontend/src/app/visualization/ngram/ngram.component.ts @@ -151,12 +151,14 @@ export class NgramComponent implements OnChanges { } ngOnChanges(changes: SimpleChanges): void { - if (changes.queryModel || changes.visualizedField) { - this.resultsCache = {}; + if (changes.corpus) { this.allDateFields = this.corpus.fields.filter( (field) => field.mappingType === 'date' ); this.dateField = this.allDateFields[0]; + } + if (changes.queryModel || changes.visualizedField) { + this.resultsCache = {}; if (this.visualizedField.multiFields) { this.analysisOptions = [ { label: 'None', value: 'none' }, @@ -175,7 +177,6 @@ export class NgramComponent implements OnChanges { } else { this.analysisOptions = undefined; } - this.ngramParameters.setParams({dateField: this.dateField.name}); } if (this.ngramParameters.state$.value) { @@ -194,7 +195,7 @@ export class NgramComponent implements OnChanges { } else { this.visualizationService.getNgramTasks( this.queryModel, this.corpus, this.visualizedField.name, - this.currentSettings).then( + this.currentSettings, this.dateField.name).then( response => { this.tasksToCancel = response.task_ids; // tasksToCancel contains ids of the parent task and its subtasks @@ -308,7 +309,8 @@ export class NgramComponent implements OnChanges { this.corpus, this.queryModel, this.visualizedField.name, - this.ngramParameters.state$.value + this.ngramParameters.state$.value, + this.dateField.name ); this.apiService .requestFullData({ From 66236defbd5f229253c6ce4071278eab3bb08876 Mon Sep 17 00:00:00 2001 From: BeritJanssen Date: Mon, 23 Dec 2024 14:42:55 +0100 Subject: [PATCH 10/11] fix: add dateField to resultsCache key --- .../src/app/visualization/ngram/ngram.component.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/visualization/ngram/ngram.component.ts b/frontend/src/app/visualization/ngram/ngram.component.ts index e9a4a6cc0..d53c69fe7 100644 --- a/frontend/src/app/visualization/ngram/ngram.component.ts +++ b/frontend/src/app/visualization/ngram/ngram.component.ts @@ -243,19 +243,28 @@ export class NgramComponent implements OnChanges { } cacheResult(result: any): void { - const key = this.ngramParameters.stringifyNgramSettings(this.currentSettings); + const key = this.concatenateDateField(this.ngramParameters.stringifyNgramSettings(this.currentSettings)); if (key) { this.resultsCache[key] = result; } } getCachedResult(): any { - const key = this.ngramParameters.stringifyNgramSettings(this.currentSettings); + const key = this.concatenateDateField(this.ngramParameters.stringifyNgramSettings(this.currentSettings)); if (key && _.has(this.resultsCache, key)) { return this.resultsCache[key]; } } + concatenateDateField(key: string): string { + // add the date field to the resultsCache key: it is currently not handled by ngramParameters + // TO DO: this is a workaround, remove if ngramParameters are implemented to handle date field + if (this.allDateFields.length) { + key.concat(`f:${this.dateField.name}`) + } + return key + } + setPositionsOptions(size) { // set positions dropdown options and reset its value this.positionsOptions = ['any'] From 6bf5203e8c044ccf9a5e22230136f95edcb810a1 Mon Sep 17 00:00:00 2001 From: BeritJanssen Date: Mon, 23 Dec 2024 14:55:25 +0100 Subject: [PATCH 11/11] fix unit test --- frontend/src/app/visualization/ngram/ngram.component.spec.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frontend/src/app/visualization/ngram/ngram.component.spec.ts b/frontend/src/app/visualization/ngram/ngram.component.spec.ts index 8a7c0e075..975d49098 100644 --- a/frontend/src/app/visualization/ngram/ngram.component.spec.ts +++ b/frontend/src/app/visualization/ngram/ngram.component.spec.ts @@ -51,8 +51,9 @@ describe('NgramComponent', () => { component.stopPolling$ = new Subject(); component.queryModel = queryModel; component.corpus = MockCorpusResponse[0] as any; - component.visualizedField = {name: 'speech'} as any; + component.visualizedField = {name: 'speech'} as any; component.dateField = {name: 'date'} as any; + component.allDateFields = [component.dateField]; component.asTable = false; component.palette = ['yellow', 'blue']; fixture.detectChanges();