diff --git a/api/src/main/java/org/snomed/snap2snomed/model/DbMapView.java b/api/src/main/java/org/snomed/snap2snomed/model/DbMapView.java index d42bbc76..36b9c879 100644 --- a/api/src/main/java/org/snomed/snap2snomed/model/DbMapView.java +++ b/api/src/main/java/org/snomed/snap2snomed/model/DbMapView.java @@ -1,5 +1,6 @@ package org.snomed.snap2snomed.model; + import org.hibernate.annotations.Fetch; import org.hibernate.annotations.FetchMode; import org.hibernate.annotations.Immutable; @@ -28,6 +29,9 @@ public class DbMapView implements Serializable { @Column private String mapRowId; + @Column + private Long mapId; + @Column private MapStatus status; diff --git a/api/src/main/java/org/snomed/snap2snomed/service/MapViewService.java b/api/src/main/java/org/snomed/snap2snomed/service/MapViewService.java index 2f2b8b57..0f6b6207 100644 --- a/api/src/main/java/org/snomed/snap2snomed/service/MapViewService.java +++ b/api/src/main/java/org/snomed/snap2snomed/service/MapViewService.java @@ -606,7 +606,7 @@ protected JPAQuery getQueryMappedRowDetailsForMap(Long mapI } private BooleanExpression getMapViewWhereClause(Long mapId, Task task, MapViewFilter filter) { - BooleanExpression whereClause = mapView.mapRow.map.id.eq(mapId); + BooleanExpression whereClause = mapView.mapId.eq(mapId);//mapView.mapRow.map.id.eq(mapId); if (filter != null) { final BooleanExpression filterExpression = filter.getExpression(true); diff --git a/api/src/main/resources/db/migration/mysql/V19__dual_map_performance_tuning.sql b/api/src/main/resources/db/migration/mysql/V19__dual_map_performance_tuning.sql new file mode 100644 index 00000000..6372fc8d --- /dev/null +++ b/api/src/main/resources/db/migration/mysql/V19__dual_map_performance_tuning.sql @@ -0,0 +1,33 @@ +/* + * Copyright © 2023 SNOMED International + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +CREATE OR REPLACE VIEW map_view AS + SELECT UUID() as 'id', map_row.id AS map_row_id, map_row.map_id, status, blind_map_flag, null as sibling_row_author_task_id + FROM map_row, map, project + WHERE map_row.blind_map_flag = false + AND map_row.map_id = map.id + AND map.project_id = project.id + AND project.dual_map_mode = 1 +UNION + SELECT UUID() as 'id', mr1.id AS map_row_id, mr1.map_id, (CASE WHEN mr1.status != mr2.status THEN '1' ELSE mr1.status END), mr1.blind_map_flag, mr2.author_task_id + FROM map_row mr1, map_row mr2, map, project + WHERE mr1.source_code_id = mr2.source_code_id + AND mr1.id < mr2.id + and mr1.map_id = mr2.map_id + AND mr1.blind_map_flag = true + AND mr1.map_id = map.id + AND map.project_id = project.id + AND project.dual_map_mode = 1; diff --git a/terraform/api/locals.tf b/terraform/api/locals.tf index cd5cdab3..2e82dcf8 100644 --- a/terraform/api/locals.tf +++ b/terraform/api/locals.tf @@ -2,7 +2,7 @@ locals { api_ecs_environment = [ { name = "logging.level.org.hibernate.SQL" - value = "DEBUG" + value = var.db_log_level }, { name = "spring.datasource.url" diff --git a/terraform/api/variables.tf b/terraform/api/variables.tf index 8b4499ca..e750479d 100644 --- a/terraform/api/variables.tf +++ b/terraform/api/variables.tf @@ -196,3 +196,8 @@ variable "force_dex_deployment" { description = "Force DEX ECS service redeployment" type = bool } + +variable "db_log_level" { + description = "Database log level" + type = string +} diff --git a/terraform/main.tf b/terraform/main.tf index 32594263..753989e4 100644 --- a/terraform/main.tf +++ b/terraform/main.tf @@ -62,6 +62,7 @@ module "api" { database_backup_retention_period = var.database_backup_retention_period jumpbox_ami_id = var.jumpbox_ami_id identity_provider = var.identity_provider + db_log_level = var.db_log_level } module "ui" { diff --git a/terraform/variables.tf b/terraform/variables.tf index 3cec3b4d..d4f26ad4 100644 --- a/terraform/variables.tf +++ b/terraform/variables.tf @@ -274,3 +274,8 @@ variable "loki_url" { type = string sensitive = true } + +variable "db_log_level" { + type = string + default = "INFO" +} diff --git a/ui/snapclient/src/app/mapping/mapping-details-card/mapping-details-card.component.ts b/ui/snapclient/src/app/mapping/mapping-details-card/mapping-details-card.component.ts index d3db8eff..1878cefe 100644 --- a/ui/snapclient/src/app/mapping/mapping-details-card/mapping-details-card.component.ts +++ b/ui/snapclient/src/app/mapping/mapping-details-card/mapping-details-card.component.ts @@ -64,6 +64,7 @@ export class MappingDetailsCardComponent { ngOnChanges(changes: SimpleChanges): void { if (changes.mapping?.currentValue && !changes.mapping?.isFirstChange()) { this.mapping = changes.mapping.currentValue; + this.updateNumOutOfScopeTargets(); } } diff --git a/ui/snapclient/src/app/mapping/mapping-view/mapping-view.component.ts b/ui/snapclient/src/app/mapping/mapping-view/mapping-view.component.ts index 9c5041c4..d60c3467 100644 --- a/ui/snapclient/src/app/mapping/mapping-view/mapping-view.component.ts +++ b/ui/snapclient/src/app/mapping/mapping-view/mapping-view.component.ts @@ -39,7 +39,7 @@ import { } from '../../_models/map_row'; import {TranslateService} from '@ngx-translate/core'; import {MapService} from '../../_services/map.service'; -import {debounceTime, distinctUntilChanged, tap} from 'rxjs/operators'; +import {debounceTime, startWith, tap} from 'rxjs/operators'; import {merge, Subscription} from 'rxjs'; import {MatSort} from '@angular/material/sort'; import {Task, TaskType} from '../../_models/task'; @@ -321,16 +321,22 @@ export class MappingViewComponent implements OnInit, AfterViewInit, OnDestroy { } else { this.translate.get('ERROR.LOAD_MAP').subscribe((msg) => this.error = msg); } - })); - - this.subscription.add(this.route.queryParams.subscribe(qparams => { - this.filterEntity = ServiceUtils.paramsToFilterEntity(qparams); - if (this.filterEntity.hasFilters()) { - this._filterEnabled = true; - } - this.filterParams = ServiceUtils.filtersToParam(this.filterEntity); - this.paging = ServiceUtils.pagingParamsToMapViewPaging(qparams); - this.refreshPage(); + })); + + this.subscription.add(this.route.queryParams + .subscribe(qparams => { + // subscription emits a value immediately .. ignore if empty + // note that you can't simply ignore the first value here as it may be a page refresh and the + // value may be the selected filters + if (Object.keys(qparams).length > 0) { + this.filterEntity = ServiceUtils.paramsToFilterEntity(qparams); + if (this.filterEntity.hasFilters()) { + this._filterEnabled = true; + } + this.filterParams = ServiceUtils.filtersToParam(this.filterEntity); + this.paging = ServiceUtils.pagingParamsToMapViewPaging(qparams); + this.refreshPage(); + } })); } @@ -556,11 +562,20 @@ export class MappingViewComponent implements OnInit, AfterViewInit, OnDestroy { loadTaskList(): void { const self = this; - this.subscription.add(self.store.select(selectTaskList).pipe(debounceTime(200)).subscribe( + this.subscription.add(self.store.select(selectTaskList).pipe(startWith(null), debounceTime(200)).subscribe( data => { - this.refreshPage(); - self.myTasks = data.filter(task => task.assignee?.id === self.currentUser?.id) - .sort((a, b) => AssignedWorkComponent.sortTasks(a, b)); + if (data) { // cannot ignore empty lists here as it could indicate all tasks being removed + + const newTasks = data.filter(task => task.assignee?.id === self.currentUser?.id) + .sort((a, b) => AssignedWorkComponent.sortTasks(a, b)); + // TODO this equals checking is not working due to a slight time difference in times reported + // it would remove unnecessry calls to refreshpage and improve responsiveness + // if it could be fixed + if (JSON.stringify(self.myTasks) !== JSON.stringify(newTasks)) { + self.myTasks = newTasks; + this.refreshPage(); + } + } }, (error) => self.translate.get('TASK.FAILED_TO_LOAD_TASKS').subscribe((err) => { self.error.message = err; @@ -576,6 +591,7 @@ export class MappingViewComponent implements OnInit, AfterViewInit, OnDestroy { this.constantFilteredColumns.push("filter-assignedReconciler"); this.constantHideShowColumns.push("assignedReconciler"); this.constantColumns.push({columnId: 'assignedReconciler', columnDisplay: 'TABLE.RECONCILER', displayed: true}); + this.refreshPage(); } } @@ -769,6 +785,7 @@ export class MappingViewComponent implements OnInit, AfterViewInit, OnDestroy { refreshTable($event: string): void { if (this.mapping_id) { // this.store.dispatch(new LoadMapping({id: this.mapping_id})); + console.log("refresh table"); this.refreshPage(); } }