Skip to content

Commit

Permalink
New page "Search with AQL"
Browse files Browse the repository at this point in the history
  • Loading branch information
askask committed Aug 15, 2024
1 parent d384175 commit f7ea386
Show file tree
Hide file tree
Showing 14 changed files with 289 additions and 16 deletions.
13 changes: 13 additions & 0 deletions src/app/app-routing.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,19 @@ export const routes: Routes = [
(m) => m.SearchModule
),
},
{
path: 'search-with-aql',
canLoad: [RoleGuard, AuthGuard],
data: {
navId: 'search-with-aql',
roles: [AvailableRoles.Manager],
onlyApprovedUsers: true,
},
loadChildren: () =>
import(
/* webpackChunkName: "SearchWithAql.Module" */ './modules/search-with-aql/search-with-aql.module'
).then((m) => m.SearchWithAqlModule),
},
{
path: 'projects',
canLoad: [RoleGuard, AuthGuard],
Expand Down
5 changes: 5 additions & 0 deletions src/app/core/constants/navigation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ export const mainNavItems: INavItem[] = [
icon: 'num-welcome',
translationKey: 'NAVIGATION.DASHBOARD',
},
{
routeTo: 'search-with-aql',
icon: 'search',
translationKey: 'NAVIGATION.SEARCH_WITH_AQL',
},
{
routeTo: 'search',
icon: 'search',
Expand Down
46 changes: 46 additions & 0 deletions src/app/core/services/query/query.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { HttpClient, HttpErrorResponse } from '@angular/common/http'
import { Injectable } from '@angular/core'
import { BehaviorSubject, Observable, throwError } from 'rxjs'
import { catchError, tap } from 'rxjs/operators'
import { AppConfigService } from 'src/app/config/app-config.service'
import { IAqlExecutionResponse } from 'src/app/shared/models/aql/execution/aql-execution-response.interface'

@Injectable({
providedIn: 'root',
})
export class QueryService {
private baseUrl: string

private projectData: IAqlExecutionResponse = null
private projectDataSubject$ = new BehaviorSubject(this.projectData)
private query: string

constructor(
private appConfigService: AppConfigService,
private httpClient: HttpClient
) {
this.baseUrl = `${this.appConfigService.config.api.baseUrl}`
}

setQuery(query: string) {
this.query = query
}

getData(): Observable<IAqlExecutionResponse> {
return this.httpClient
.post<IAqlExecutionResponse>(`${this.baseUrl}/query/execute`, {
aql: this.query,
})
.pipe(
tap((res) => {
this.projectData = res
this.projectDataSubject$.next(res)
}),
catchError(this.handleError)
)
}

handleError(error: HttpErrorResponse): Observable<never> {
return throwError(() => error)
}
}
1 change: 1 addition & 0 deletions src/app/modules/aqls/aqls.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,6 @@ import { AqlBuilderWhereGroupComponent } from './components/aql-builder-where-gr
AqlBuilderWhereGroupComponent,
],
imports: [CommonModule, AqlsRoutingModule, SharedModule, LayoutModule, CodeEditorModule],
exports: [AqlEditorCeatorComponent],
})
export class AqlsModule {}
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export class AqlEditorCeatorComponent {
private dialogService: DialogService,
private aqlEditorService: AqlEditorService,
private aqlService: AqlService,
private toastMessageService: ToastMessageService
public toastMessageService: ToastMessageService
) {}
formatter = new NumAqlFormattingProvider()
formatSubject$ = new Subject<monaco.editor.IMarkerData[] | void>()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
<div fxLayout="column">
<section role="region" aria-labelledby="creator-label" fxLayoutGap="20px" class="num-margin-b-60">
<h2>{{ 'BUILD_QUERY' | translate }}</h2>
<mat-card>
<mat-card-content fxLayout="column" fxFlex="100%">
<p id="creator-label">
{{ 'SEARCH_WITH_AQL.BUILD_QUERY_CONTENT' | translate }}
</p>

<num-button
type="primary"
icon="plus"
class="num-margin-b-40"
(singleClick)="openBuilderDialog()"
data-test="aql-editor__aql-creator__open-builder-button"
>{{ 'BUTTON.OPEN_QUERY_BUILDER' | translate }}</num-button
>

<div style="height: 300px" class="num-margin-b-40">
<num-code-editor
class="editor"
style="height: 100%"
[(value)]="aqlQuery"
[formatObservable$]="formatObservable$"
[validationObservable$]="validationObservable$"
(editorInit)="onEditorInit($event)"
></num-code-editor>
</div>

<div class="button-row num-margin-b-20" fxLayout="row" fxLayoutGap="20px">
<num-button
type="primary"
icon="align-left"
(singleClick)="format()"
data-test="aql-editor__aql-creator__format-button"
>{{ 'BUTTON.FORMAT_QUERY' | translate }}</num-button
>

<num-button
type="primary"
icon="align-left"
(singleClick)="validate(true)"
data-test="aql-editor__aql-creator__validate-button"
>{{ 'BUTTON.VALIDATE_QUERY' | translate }}</num-button
>
</div>

<num-editor-determine-hits
[isButtonDisabled]="!isValidForExecution || isExecutionLoading"
[content]="determineHitsContent"
(clicked)="determineHits()"
></num-editor-determine-hits>
</mat-card-content>
</mat-card>
</section>

<section role="region" fxLayout="column" fxFlex="100%">
<h2>{{ 'SEARCH_WITH_AQL.QUERY_DATA' | translate }}</h2>
<num-button
icon="play"
class="num-margin-b-60"
(singleClick)="getData()"
data-test="manager-data-explorer__retrieval-button"
>{{ 'BUTTON.GET_DATA' | translate }}</num-button
>

<div
*ngIf="resultSet && resultSet.length && !isDataSetLoading"
fxFlex
fxLayout="row"
fxLayoutGap="10px"
fxLayoutAlign="start center"
class="num-margin-b-40"
></div>

<ng-container *ngIf="!isDataSetLoading; else loading">
<num-result-table
*ngFor="let template of resultSet; let i = index"
[resultSet]="template"
[isDataSetLoading]="isDataSetLoading"
[index]="i"
[totalTables]="resultSet.length"
class="num-margin-b-20"
data-test="manager-data-explorer__table"
></num-result-table>
</ng-container>

<ng-template #loading>
<mat-card class="mat-elevation-z1">
<div fxLayout="row" fxLayoutAlign="center center" fxLayoutGap="8px">
<mat-spinner color="accent" [diameter]="24"></mat-spinner>
<span>{{ 'DATA_EXPLORER.LOADING_RESULT_SET' | translate }}</span>
</div>
</mat-card>
</ng-template>
</section>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
:host {
width: 100%;
}

.editor {
border-width: 1px;
border-style: solid;
border-color: grey;
border-radius: 6px;
padding: 20px 20px 20px 3px;
position: relative;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { Component, OnDestroy } from '@angular/core'
import { AqlEditorCeatorComponent } from '../../../aqls/components/aql-editor-creator/aql-editor-creator.component'
import { DialogService } from '../../../../core/services/dialog/dialog.service'
import { AqlEditorService } from '../../../../core/services/aql-editor/aql-editor.service'
import { AqlService } from '../../../../core/services/aql/aql.service'
import { ToastMessageService } from '../../../../core/services/toast-message/toast-message.service'
import { QueryService } from '../../../../core/services/query/query.service'
import { RESULT_SET_LOADING_ERROR } from '../../../search/components/manager-data-explorer/constants'
import { IAqlExecutionResponse } from '../../../../shared/models/aql/execution/aql-execution-response.interface'
import { Subscription } from 'rxjs'

@Component({
templateUrl: './search.component.html',
styleUrls: ['../../../aqls/components/aql-editor-creator/aql-editor-creator.component.scss'],
})
export class SearchComponent extends AqlEditorCeatorComponent implements OnDestroy {
constructor(
dialogService: DialogService,
aqlEditorService: AqlEditorService,
aqlService: AqlService,
toastMessageService: ToastMessageService,
private queryService: QueryService
) {
super(dialogService, aqlEditorService, aqlService, toastMessageService)
}
private subscriptions = new Subscription()

resultSet: IAqlExecutionResponse[]
isDataSetLoading = false

ngOnDestroy(): void {
this.subscriptions.unsubscribe()
}

getData(): void {
this.isDataSetLoading = true
this.queryService.setQuery(this.aqlQuery)
this.subscriptions.add(
this.queryService.getData().subscribe({
next: (result) => {
this.resultSet = [result]
this.isDataSetLoading = false
},
error: () => {
this.isDataSetLoading = false
this.toastMessageService.openToast(RESULT_SET_LOADING_ERROR)
},
})
)
}
}
16 changes: 16 additions & 0 deletions src/app/modules/search-with-aql/search-with-aql-routing.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { NgModule } from '@angular/core'
import { RouterModule, Routes } from '@angular/router'
import { SearchComponent } from './components/search-aql/search.component'

const routes: Routes = [
{
path: '',
component: SearchComponent,
},
]

@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class SearchWithAqlRoutingModule {}
25 changes: 25 additions & 0 deletions src/app/modules/search-with-aql/search-with-aql.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { NgModule } from '@angular/core'
import { CommonModule } from '@angular/common'
import { SharedModule } from 'src/app/shared/shared.module'
import { LayoutModule } from 'src/app/layout/layout.module'
import { CohortBuilderModule } from '../cohort-builder/cohort-builder.module'
import { SharedProjectsModule } from '../projects/shared-projects.module'
import { CodeEditorModule } from '../code-editor/code-editor.module'
import { SearchComponent } from './components/search-aql/search.component'
import { SearchWithAqlRoutingModule } from './search-with-aql-routing.module'
import { AqlsModule } from '../aqls/aqls.module'

@NgModule({
declarations: [SearchComponent],
imports: [
CohortBuilderModule,
CommonModule,
LayoutModule,
SharedModule,
SharedProjectsModule,
CodeEditorModule,
SearchWithAqlRoutingModule,
AqlsModule,
],
})
export class SearchWithAqlModule {}
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,10 @@ export class ResultTableComponent {
path: ' ',
name: '#',
}
const columnNamePattern = new RegExp('([^/]+$)')
const resultSetColumns: IAqlExecutionColumn[] = [firstColumn, ...this.resultSet.columns]

this.displayedColumns = resultSetColumns.map((column) => column.path)
this.displayedColumnNames = resultSetColumns.map(
(column) => column.name.match(columnNamePattern)[0]
)
this.displayedColumnNames = resultSetColumns.map((column) => column.name)
this.dataSource.data = this.resultSet.rows.map((row, index) => [index + 1, ...row])
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ export interface IAqlExecutionResponse {
q: string
columns: IAqlExecutionColumn[]
rows: any[]
name: string
name?: string
}
17 changes: 11 additions & 6 deletions src/assets/i18n/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
"MANAGER_TOOLS": "Manager Tools",
"PROFILE": "Benutzerkonto",
"SEARCH": "Suche",
"SEARCH_WITH_AQL": "Suche mit AQL",
"SIGNOUT": "Abmelden",
"SIGNIN": "Anmelden",
"USER_MANUAL": "Benutzerhandbuch"
Expand Down Expand Up @@ -115,10 +116,10 @@
"CREATE_QUERY": "Kriterium erstellen",
"CREATE_QUERY_CONTENT": "Auf dieser Seite können Sie ein Kriterium erstellen. Zum Erstellen des Kriteriums steht Ihnen ein Kriterien-Builder zur Verfügung.\n\n*Pflichtfeld",
"GENERAL_INFO_CONTENT": "Bitte geben Sie einen aussagekräftigen Titel sowie den Zweck und die Verwendung des Kriteriums ein.",
"BUILD_QUERY_CONTENT": "Erstellen Sie ein Kriterium im Kriterien-Builder. Danach können Sie das Kriterium im untenstehenden Editor bearbeiten.",
"BUILD_QUERY_CONTENT": "Erstellen Sie eine Abfrage im Abfrage-Builder. Danach können Sie die Abfrage im untenstehenden Editor bearbeiten.",
"SEARCH_QUERIES": "Kriterium suchen",
"SELECT_AT_LEAST_ONE": "Bitte wählen sie eine oder mehrere Kriterien aus",
"QUERY_BUILDER_DIALOG_HEADER": "Kriterien erstellen",
"QUERY_BUILDER_DIALOG_HEADER": "Abfrage erstellen",
"AVAILABLE_QUERIES": "Verfügbare Kriterien",
"SAVE_SUCCESS_MESSAGE": "Ihr Kriterium wurde erfolgreich erstellt.",
"SAVE_ERROR_MESSAGE": "Ihr Kriterium konnte nicht erstellt werden.",
Expand All @@ -131,7 +132,7 @@
"VALIDATION_ERROR": "Die Syntax des Kriteriums ist nicht valide",
"VALIDATION_SUCCESS": "Die Syntax des Kriteriums ist valide",
"HITS": {
"MESSAGE_SET_ALL_PARAMETERS": "Sie sollten zuerst das Kriterium definieren, um ein valides Ergebnis zu erhalten.",
"MESSAGE_SET_ALL_PARAMETERS": "Sie sollten zuerst die die Abfrage definieren, um ein valides Ergebnis zu erhalten.",
"MESSAGE_ERROR_FEW_HITS": "Übereinstimmende Patienten liegen unter der Mindestschwelle.",
"MESSAGE_ERROR_MESSAGE": "Konnte keine Patienten ermitteln. Bitte versuchen Sie es erneut."
},
Expand All @@ -147,6 +148,10 @@
"MINUTES": "Minuten",
"SECONDS": "Sekunden"
},
"SEARCH_WITH_AQL": {
"BUILD_QUERY_CONTENT": "Erstellen Sie eine Abfrage im Abfrage-Builder. Danach können Sie die Abfrage im untenstehenden Editor bearbeiten.",
"QUERY_DATA": "Daten abrufen"
},
"CONTENT_EDITOR": {
"SAVE_NAVIGATION_SUCCESS": "Die Navigationselemente wurden erfolgreich veröffentlicht.",
"SAVE_NAVIGATION_ERROR": "Die Navigationselemente konnten nicht veröffentlicht werden. Bitte überprüfen Sie Ihre Eingabe und versuchen es erneut.",
Expand Down Expand Up @@ -190,7 +195,7 @@
"CONFIGURE": "Konfigurieren",
"HITS": "Patienten",
"AVAILABLE_PROJECTS": "Verfügbare Projekte",
"BUILD_QUERY": "Kriterium erstellen",
"BUILD_QUERY": "Abfrage erstellen",
"AVAILABLE_EHR_TEMPLATES": "Verfügbare Rückgabeparameter",
"SELECT_EHR_TEMPLATE": "Rückgabeparameter auswählen",
"SELECTED_EHR_TEMPLATES": "Ausgewählte Rückgabeparameter",
Expand Down Expand Up @@ -263,7 +268,7 @@
"CLEAR_ENTRY": "Eingabe löschen",
"REQUEST_APPROVAL": "Genehmigung anfragen",
"APPLY_SELECTION": "Auswahl Übernehmen",
"FORMAT_QUERY": "Abfrage formatieren",
"FORMAT_QUERY": "Formatieren",
"VALIDATE_QUERY": "Überprüfen",
"ADD_RESEARCHERS": "Forscher hinzufügen",
"ADD_GROUP": "Gruppe hinzufügen",
Expand All @@ -277,7 +282,7 @@
"SELECT": "Auswählen",
"DESELECT": "Abwählen",
"APPROVE_USER": "Benutzer bestätigen",
"OPEN_QUERY_BUILDER": "Kriterium erstellen",
"OPEN_QUERY_BUILDER": "Builder öffnen",
"PREVIEW": "Vorschau",
"EDIT": "Bearbeiten",
"DELETE": "Löschen",
Expand Down
Loading

0 comments on commit f7ea386

Please sign in to comment.