From 208e19940307a1b108eeacf552ca8c18963a52ea Mon Sep 17 00:00:00 2001 From: LaviniaStiliadou Date: Wed, 18 Sep 2024 16:38:09 +0200 Subject: [PATCH 1/6] add textmatching logic --- .../textmatcher/textmatcher.component.ts | 181 ++++++++++-------- .../default-pl-renderer.component.ts | 29 ++- src/app/core/service/algo-state.service.ts | 16 ++ 3 files changed, 145 insertions(+), 81 deletions(-) diff --git a/src/app/core/component/textmatcher/textmatcher.component.ts b/src/app/core/component/textmatcher/textmatcher.component.ts index e93b64f4..4b732cae 100644 --- a/src/app/core/component/textmatcher/textmatcher.component.ts +++ b/src/app/core/component/textmatcher/textmatcher.component.ts @@ -4,7 +4,7 @@ import { FormControl, FormsModule, ReactiveFormsModule } from '@angular/forms'; import { MatSelectModule } from '@angular/material/select'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatButtonModule } from '@angular/material/button'; -import { HttpClient } from '@angular/common/http'; +import { HttpClient, HttpHeaders } from '@angular/common/http'; import { MatTableModule, MatTableDataSource } from '@angular/material/table'; import { MatCheckboxModule } from '@angular/material/checkbox'; @@ -47,46 +47,56 @@ export class TextmatcherComponent implements OnInit { ngOnInit(): void { + this.showMatchingResults = false; + this.showRephrasedInput = false; + + if (this.data.prev.length > 0) { + // Step 1: Filter out entries with null cosineSimilarity + const filteredFullTableData = this.data.prev[0].fulltabledata.filter( + (r: any) => r.cosineSimilarity != null + ); + + this.resultAlgorithm = this.data.prev[0].resultAlgorithm; + this.fulltabledata = filteredFullTableData; + this.tabledata = new MatTableDataSource( + this.fulltabledata.slice(0, this.selectednumber) + ); + this.columnsToDisplay = this.data.prev[0].columnsToDisplay; + this.rephrasedInput = this.data.prev[0].rephrasedInput; + this.selectednumber = this.data.prev[0].selectedNumber; + + // Initialize inputfield with previous value if available + if (this.data.prev[0].inputfieldvalue != '') { + this.inputfield = new FormControl(this.data.prev[0].inputfieldvalue); + } else { + this.inputfield = new FormControl(''); + } + + this.showMatchingResults = true; + if (this.rephrasedInput != '') { + this.showRephrasedInput = true; + } + } else { + this.inputfield = new FormControl(''); + } + - this.showMatchingResults = false; - this.showRephrasedInput = false; - - if(this.data.prev.length > 0) { - this.resultAlgorithm = this.data.prev[0].resultAlgorithm; - this.tabledata = this.data.prev[0].tabledata; - this.fulltabledata = this.data.prev[0].fulltabledata; - this.columnsToDisplay = this.data.prev[0].columnsToDisplay; - this.rephrasedInput = this.data.prev[0].rephrasedInput; - this.selectednumber = this.data.prev[0].selectedNumber; - - if (this.data.prev[0].inputfieldvalue != ''){ - this.inputfield = new FormControl(this.data.prev[0].inputfieldvalue); - }else{ - this.inputfield = new FormControl(''); - } - this.showMatchingResults = true; - if(this.rephrasedInput != ''){ - this.showRephrasedInput = true; - } - }else{ - this.inputfield = new FormControl(''); + // Existing HTTP call to fetch algorithms + this.http.get("http://localhost:6626/atlas/algorithms").subscribe((algodata: any) => { + algodata.content.forEach((algorithm: any) => { + if(algorithm.id === "3c7722e2-09c3-4667-9a0d-a45d3ddc42ae"){ + algorithm.applicationAreas = ["Search", "clauses Problems", "Computers", "satisfiable"]; + } + + this.infos.push({ name: algorithm.name, data: algorithm }); + }); + }); + + console.log("Die Daten:", this.data); + console.log(this.infos); } - - - let href = 'https://platform.planqk.de/algorithms/fae60bca-d2b6-4aa2-88b7-58caace34179'; - this.data.data.forEach(algorithm => { - if((algorithm.href !== '')&&(algorithm.href != null)){ - let splitarray = algorithm.href.split('platform.planqk.de'); - let datahref = splitarray[0] + 'platform.planqk.de/qc-catalog' + splitarray[1]; - this.http.get(datahref).subscribe(algodata => { - this.infos.push({ name: algorithm.name, data: algodata }); - }); - }else{ - //do something if no href - } - }); - - } + + checkboxClicked(event){ if(this.checked){ @@ -144,43 +154,62 @@ export class TextmatcherComponent implements OnInit { }; } - extractInformation(isRake){ - let datatosend = { input: this.inputfield.value, algodata: this.infos }; - let url = 'http://localhost:1985/api/matcher/'; - if(isRake){ - url = url + 'rake/'; - } + extractInformation(isRake: boolean) { + const datatosend = { input: this.inputfield.value, algodata: this.infos }; + let url = 'http://localhost:1985/api/matcher/'; - if(this.checked){ - url = url + 'openai'; + if (isRake) { + url += 'rake/'; + } + + if (this.checked) { + url += 'openai'; + } + + // Reset rephrased input + this.rephrasedInput = ''; + this.showRephrasedInput = false; + + this.http.post(url, datatosend).subscribe(resultdata => { + // Step 1: Filter out results with null or undefined cosineSimilarity + const filteredResults = resultdata.result.filter( + (r: any) => r.cosineSimilarity != null + ); + + // Step 2: Assign filtered data to fulltabledata + this.fulltabledata = filteredResults; + + // Step 3: Update tabledata with the sliced data based on selectednumber + this.tabledata.data = this.fulltabledata.slice(0, this.selectednumber); + + console.log('Filtered tabledata:', this.fulltabledata); + + // Step 4: Handle rephrased input if available + if (resultdata.hasOwnProperty('rephrasedInput')) { + this.rephrasedInput = resultdata.rephrasedInput; + this.showRephrasedInput = true; + } + + // Step 5: Determine the algorithm with the highest cosineSimilarity + if (this.fulltabledata.length > 0) { + const maximumkey = this.fulltabledata.reduce((prev: any, current: any) => { + return prev.cosineSimilarity > current.cosineSimilarity ? prev : current; + }); + + if (maximumkey.cosineSimilarity > 0) { + this.showMatchingResults = true; + this.resultAlgorithm = maximumkey; + } else { + this.showMatchingResults = false; + console.log('No similarities found!'); + } + } else { + this.showMatchingResults = false; + console.log('No data available after filtering.'); + } + }, error => { + console.error('Error during information extraction:', error); + this.showMatchingResults = false; + }); } - this.rephrasedInput = ''; - this.showRephrasedInput = false; - - this.http.post(url, datatosend).subscribe(resultdata => { - this.tabledata.data = resultdata.result; - this.fulltabledata = this.tabledata.data; - console.log('tabledata:'); - console.log(this.fulltabledata); - this.tabledata.data = this.fulltabledata.slice(0, this.selectednumber); - - if(resultdata.hasOwnProperty('rephrasedInput')){ - this.rephrasedInput = resultdata.rephrasedInput; - this.showRephrasedInput = true; - } - - const maximumkey = this.fulltabledata.reduce(function(prev, current) { - return (prev.cosineSimilarity > current.cosineSimilarity) ? prev : current; - }); - - if(maximumkey.cosineSimilarity > 0){ - this.showMatchingResults = true; - this.resultAlgorithm = maximumkey; - }else{ - this.showMatchingResults = false; - console.log('no similarities found!'); - } - }); - } - } diff --git a/src/app/core/default-pl-renderer/default-pl-renderer.component.ts b/src/app/core/default-pl-renderer/default-pl-renderer.component.ts index 17cb5f1a..f7dd6af2 100644 --- a/src/app/core/default-pl-renderer/default-pl-renderer.component.ts +++ b/src/app/core/default-pl-renderer/default-pl-renderer.component.ts @@ -68,6 +68,7 @@ export class DefaultPlRendererComponent implements OnInit, OnDestroy { jdata: any = jsonData; previousTextmatcherData = []; isQuantumComputingPatternLanguage = false; + algorithms = []; constructor(private activatedRoute: ActivatedRoute, private cdr: ChangeDetectorRef, @@ -83,12 +84,26 @@ export class DefaultPlRendererComponent implements OnInit, OnDestroy { private toasterService: ToasterService) { } - openTextmatcherDialog(){ + async openTextmatcherDialog(){ + let result = await this.algoStateService.getAlgorithmData3(); + let applicationAreas = await this.algoStateService.getAlgorithmData4(); + console.log("hhhhhhhhhh") + console.log(result.content) + for(let i = 0; i< result.content.length; i++){ + if(result.content[i].id === "3c7722e2-09c3-4667-9a0d-a45d3ddc42ae"){ + result.content[i].applicationAreas = "Satisfiable"; + } + } + let r = result.content; + + console.log(result.content) + console.log(r); const dialogRef = this.dialog.open(TextmatcherComponent, { width: '1000px', data: { data: this.AlgorithmDataIds, prev: this.previousTextmatcherData, + algorithms: r }, }); @@ -112,7 +127,7 @@ export class DefaultPlRendererComponent implements OnInit, OnDestroy { }, }); - dialogRef.afterClosed().subscribe(result => { + dialogRef.afterClosed().subscribe(async result => { if((result != null) && (result != undefined) && (result.length > 0)) { result.forEach(algorithm => { this.AlgorithmDataIds = this.AlgorithmDataIds.filter(algids => algids.name !== algorithm.name); @@ -190,6 +205,7 @@ export class DefaultPlRendererComponent implements OnInit, OnDestroy { this.showAlgoPatterns(); } }); + } //old datastorage via json file @@ -207,6 +223,8 @@ export class DefaultPlRendererComponent implements OnInit, OnDestroy { }else{ this.AlgorithmDataIds = this.jdata.default; } + + } initializeAlgorithmPatternIds() { @@ -238,9 +256,10 @@ export class DefaultPlRendererComponent implements OnInit, OnDestroy { 'd4f7c247-e2bb-4301-ad06-f758fa58f2dc', '2229a430-fe92-4411-9d72-d10dd1d8da14'], href: 'https://platform.planqk.de/algorithms/533c90a5-5fbb-487b-b64d-a8f331aafb10/' }; this.AlgorithmDataIds.push(Deutsch); - const test = { name: 'test', - data: ['312bc9d3-26c0-40ae-b90b-56effd136c0d', 'bcd4c7a1-3c92-4f8c-a530-72b8b95d3750', '1a5e3708-da39-4356-ab3f-115264da6390'] }; - this.AlgorithmDataIds.push(test); + const Grover = { name: 'Grover', + data: ['312bc9d3-26c0-40ae-b90b-56effd136c0d', '2229a430-fe92-4411-9d72-d10dd1d8da14', '1cc7e9d6-ab37-412e-8afa-604a25de296e', '96b4d28a-a5ce-4c96-85df-d42587b13c57', 'b8c2dca0-563a-432d-adfd-8bd15ef0dfb8'] }; + + this.AlgorithmDataIds.push(Grover); //console.log("Complete Algorithm Data for initial values"); //console.log(this.AlgorithmDataIds); } diff --git a/src/app/core/service/algo-state.service.ts b/src/app/core/service/algo-state.service.ts index e44f66f4..ebc274f1 100644 --- a/src/app/core/service/algo-state.service.ts +++ b/src/app/core/service/algo-state.service.ts @@ -14,6 +14,7 @@ export class AlgoStateService { private tmpstore = ''; private repoEndpoint = environment.API_URL; + private qcAtlasEndpoint = "http://localhost:6626/atlas/algorithms" constructor(private http: HttpClient) { } @@ -62,6 +63,17 @@ export class AlgoStateService { const url = this.repoEndpoint + '/patternForAlgorithm/' + patternLanguageId; return this.http.get(url); } + + async getAlgorithmData3() { + const url = "http://localhost:6626/atlas/algorithms"; + return await this.http.get(url).toPromise(); + } + + async getAlgorithmData4() { + const url = "http://localhost:6626/atlas/algorithms/3c7722e2-09c3-4667-9a0d-a45d3ddc42ae/application-areas"; + return await this.http.get(url).toPromise(); + } + clearAlgorithmData() { localStorage.removeItem(this.variableForAlgoData); @@ -72,3 +84,7 @@ export class AlgoStateService { } } +function getAlgorithmData3() { + throw new Error('Function not implemented.'); +} + From c88637c3f760793178ab159dfb1523ebf07e3cc1 Mon Sep 17 00:00:00 2001 From: LaviniaStiliadou Date: Sun, 29 Sep 2024 23:48:45 +0200 Subject: [PATCH 2/6] add deployment logic --- package.json | 2 + .../textmatcher/textmatcher.component.html | 66 ++- .../textmatcher/textmatcher.component.ts | 505 +++++++++++------- src/app/core/util/http-util.ts | 32 ++ src/app/core/util/opentosca-utils.ts | 345 ++++++++++++ src/app/core/util/winery-utils.ts | 432 +++++++++++++++ yarn.lock | 10 + 7 files changed, 1179 insertions(+), 213 deletions(-) create mode 100644 src/app/core/util/http-util.ts create mode 100644 src/app/core/util/opentosca-utils.ts create mode 100644 src/app/core/util/winery-utils.ts diff --git a/package.json b/package.json index 4d7ce8b3..3e2265e2 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "core-js": "2.6.3", "d3": "^5.9.2", "file-saver": "^2.0.5", + "jquery": "^3.6.1", "keyword-extractor": "0.0.25", "lodash": "^4.17.21", "madr": "2.1.2", @@ -49,6 +50,7 @@ "rxjs": "6.5.5", "simplemde": "1.11.2", "tslib": "^1.10.0", + "whatwg-fetch": "^3.6.2", "zone.js": "~0.10.3" }, "devDependencies": { diff --git a/src/app/core/component/textmatcher/textmatcher.component.html b/src/app/core/component/textmatcher/textmatcher.component.html index 3a158232..7f38562a 100644 --- a/src/app/core/component/textmatcher/textmatcher.component.html +++ b/src/app/core/component/textmatcher/textmatcher.component.html @@ -1,19 +1,27 @@ +
Input the known Information - +
+
-

Rephrased input: {{rephrasedInput}}

+

Rephrased input: {{rephrasedInput}}

+
-

The best matching algorithm with cosine similarity is {{resultAlgorithm.name}}.

-

The Cosine Similarity is {{resultAlgorithm.cosineSimilarity}}

+

The best matching algorithm with cosine similarity is + {{resultAlgorithm.name}}. +

+

The Cosine Similarity is + {{resultAlgorithm.cosineSimilarity}} +

+
Number of displayed algorithms @@ -22,11 +30,14 @@
+
Name - {{result.name}} + + {{result.name}} + Cosine similarity @@ -36,38 +47,57 @@
+
- - - -
- rephrase Problem using OpenAI + +
+ + rephrase Problem using OpenAI
+ + + + +
+ + diff --git a/src/app/core/component/textmatcher/textmatcher.component.ts b/src/app/core/component/textmatcher/textmatcher.component.ts index 4b732cae..a1e4cc1f 100644 --- a/src/app/core/component/textmatcher/textmatcher.component.ts +++ b/src/app/core/component/textmatcher/textmatcher.component.ts @@ -1,13 +1,13 @@ import { Component, OnInit, Inject } from '@angular/core'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; -import { FormControl, FormsModule, ReactiveFormsModule } from '@angular/forms'; -import { MatSelectModule } from '@angular/material/select'; -import { MatFormFieldModule } from '@angular/material/form-field'; -import { MatButtonModule } from '@angular/material/button'; -import { HttpClient, HttpHeaders } from '@angular/common/http'; -import { MatTableModule, MatTableDataSource } from '@angular/material/table'; -import { MatCheckboxModule } from '@angular/material/checkbox'; +import { FormControl } from '@angular/forms'; +import { HttpClient } from '@angular/common/http'; +import { MatTableDataSource } from '@angular/material/table'; +import {createServiceTemplate, createDeploymentModel, updateServiceTemplate, createNodeType, updateNodeType} from '../../../core/util/winery-utils'; +import { uploadCSARToContainer } from '../../util/opentosca-utils'; +const QUANTME_NAMESPACE_PULL = "http://quantil.org/quantme/pull"; +const OPENTOSCA_NAMESPACE_NODETYPE = "http://opentosca.org/nodetypes"; @Component({ selector: 'pp-textmatcher', @@ -15,201 +15,316 @@ import { MatCheckboxModule } from '@angular/material/checkbox'; styleUrls: ['./textmatcher.component.scss'] }) export class TextmatcherComponent implements OnInit { - - - inputfield: FormControl; - extractedAlgorithmInformation = []; //array of arrays with extracted keywords - infos = []; - keyword_extractor: any; - - checked = true; - - showMatchingResults: boolean; - resultAlgorithm: any; - - numbers = [1,3,5,10]; - selectednumber = 3; - - tabledata = new MatTableDataSource([{ name: 'test', cosineSimilarity: 1 }]); // initial value - fulltabledata: any; - columnsToDisplay = ['name', 'cosineSimilarity']; - - rephrasedInput = ''; - showRephrasedInput: boolean; - - constructor(public dialogRef: MatDialogRef, - private http: HttpClient, - @Inject(MAT_DIALOG_DATA) public data) { - //onbackground click handler als andere lösung - dialogRef.disableClose = true; - - } + + inputfield: FormControl; + extractedAlgorithmInformation = []; // Array of arrays with extracted keywords + infos = []; + keyword_extractor: any; + + checked = true; + + showMatchingResults: boolean; + resultAlgorithm: any; + + numbers = [1, 3, 5, 10]; + selectednumber = 3; + + tabledata = new MatTableDataSource([{ name: 'test', cosineSimilarity: 1 }]); // Initial value + fulltabledata: any; + columnsToDisplay = ['name', 'cosineSimilarity']; + deploymentFileContentUrl:any; + + rephrasedInput = ''; + showRephrasedInput: boolean; + + // New state variable for aggregation + isAggregationComplete = false; + + constructor( + public dialogRef: MatDialogRef, + private http: HttpClient, + @Inject(MAT_DIALOG_DATA) public data + ) { + dialogRef.disableClose = true; + } + + ngOnInit(): void { + this.showMatchingResults = false; + this.showRephrasedInput = false; + + if (this.data.prev.length > 0) { + const filteredFullTableData = this.data.prev[0].fulltabledata.filter( + (r: any) => r.cosineSimilarity != null + ); + + this.resultAlgorithm = this.data.prev[0].resultAlgorithm; + this.fulltabledata = filteredFullTableData; + this.tabledata = new MatTableDataSource( + this.fulltabledata.slice(0, this.selectednumber) + ); + this.columnsToDisplay = this.data.prev[0].columnsToDisplay; + this.rephrasedInput = this.data.prev[0].rephrasedInput; + this.selectednumber = this.data.prev[0].selectedNumber; + + if (this.data.prev[0].inputfieldvalue != '') { + this.inputfield = new FormControl(this.data.prev[0].inputfieldvalue); + } else { + this.inputfield = new FormControl(''); + } + + this.showMatchingResults = true; + if (this.rephrasedInput != '') { + this.showRephrasedInput = true; + } + } else { + this.inputfield = new FormControl(''); + } + + // HTTP call to fetch algorithms + this.http.get("http://localhost:6626/atlas/algorithms").subscribe((algodata: any) => { + algodata.content.forEach((algorithm: any) => { + if (algorithm.id === "3c7722e2-09c3-4667-9a0d-a45d3ddc42ae") { + algorithm.applicationAreas = ["Search", "clauses Problems", "Computers", "satisfiable"]; + } + + this.infos.push({ name: algorithm.name, data: algorithm }); + }); + }); + + console.log("Received Data:", this.data); + console.log(this.infos); + } + + checkboxClicked(event) { + this.checked = !this.checked; + } + + numberChanged() { + this.tabledata.data = this.fulltabledata.slice(0, this.selectednumber); + } + + closeDialog(algorithmName: string) { + const previous = { + resultAlgorithm: this.resultAlgorithm, + tabledata: this.tabledata, + fulltabledata: this.fulltabledata, + columnsToDisplay: this.columnsToDisplay, + inputfieldvalue: this.inputfield.value, + rephrasedInput: this.rephrasedInput, + selectedNumber: this.selectednumber, + }; + + this.dialogRef.close({ algoname: algorithmName, prev: previous }); + } + + closeDialog2() { + if (this.inputfield.value != '' && this.showMatchingResults) { + const previous = { + resultAlgorithm: this.resultAlgorithm, + tabledata: this.tabledata, + fulltabledata: this.fulltabledata, + columnsToDisplay: this.columnsToDisplay, + inputfieldvalue: this.inputfield.value, + rephrasedInput: this.rephrasedInput, + selectedNumber: this.selectednumber, + }; + + this.dialogRef.close({ algoname: undefined, prev: previous }); + } else { + this.dialogRef.close(null); + } + } + + openLink() { + const alg = this.data.data.filter(algorithm => algorithm.name == this.resultAlgorithm.name); + if (alg.length > 0) { + this.closeDialog(this.resultAlgorithm.name); + } + } + + openLink2(algname) { + const alg = this.data.data.filter(algorithm => algorithm.name == algname); + if (alg.length > 0) { + this.closeDialog(algname); + } + } + + extractInformation(isRake: boolean) { + const datatosend = { input: this.inputfield.value, algodata: this.infos }; + let url = 'http://localhost:1985/api/matcher/'; + + if (isRake) { + url += 'rake/'; + } + + if (this.checked) { + url += 'openai'; + } + + this.rephrasedInput = ''; + this.showRephrasedInput = false; + + this.http.post(url, datatosend).subscribe(resultdata => { + const filteredResults = resultdata.result.filter( + (r: any) => r.cosineSimilarity != null + ); + + this.fulltabledata = filteredResults; + this.tabledata.data = this.fulltabledata.slice(0, this.selectednumber); + + if (resultdata.hasOwnProperty('rephrasedInput')) { + this.rephrasedInput = resultdata.rephrasedInput; + this.showRephrasedInput = true; + } + + if (this.fulltabledata.length > 0) { + const maximumkey = this.fulltabledata.reduce((prev: any, current: any) => { + return prev.cosineSimilarity > current.cosineSimilarity ? prev : current; + }); + + if (maximumkey.cosineSimilarity > 0) { + this.showMatchingResults = true; + this.resultAlgorithm = maximumkey; + } else { + this.showMatchingResults = false; + console.log('No similarities found!'); + } + } else { + this.showMatchingResults = false; + console.log('No data available after filtering.'); + } + }, error => { + console.error('Error during information extraction:', error); + this.showMatchingResults = false; + }); + } + + aggregateSolutions() { + console.log("Aggregation process started..."); - ngOnInit(): void { - this.showMatchingResults = false; - this.showRephrasedInput = false; - - if (this.data.prev.length > 0) { - // Step 1: Filter out entries with null cosineSimilarity - const filteredFullTableData = this.data.prev[0].fulltabledata.filter( - (r: any) => r.cosineSimilarity != null - ); - - this.resultAlgorithm = this.data.prev[0].resultAlgorithm; - this.fulltabledata = filteredFullTableData; - this.tabledata = new MatTableDataSource( - this.fulltabledata.slice(0, this.selectednumber) - ); - this.columnsToDisplay = this.data.prev[0].columnsToDisplay; - this.rephrasedInput = this.data.prev[0].rephrasedInput; - this.selectednumber = this.data.prev[0].selectedNumber; - - // Initialize inputfield with previous value if available - if (this.data.prev[0].inputfieldvalue != '') { - this.inputfield = new FormControl(this.data.prev[0].inputfieldvalue); - } else { - this.inputfield = new FormControl(''); - } - - this.showMatchingResults = true; - if (this.rephrasedInput != '') { - this.showRephrasedInput = true; - } - } else { - this.inputfield = new FormControl(''); - } - - - // Existing HTTP call to fetch algorithms - this.http.get("http://localhost:6626/atlas/algorithms").subscribe((algodata: any) => { - algodata.content.forEach((algorithm: any) => { - if(algorithm.id === "3c7722e2-09c3-4667-9a0d-a45d3ddc42ae"){ - algorithm.applicationAreas = ["Search", "clauses Problems", "Computers", "satisfiable"]; - } - - this.infos.push({ name: algorithm.name, data: algorithm }); + if (this.fulltabledata && this.fulltabledata.length > 0) { + const firstEntry = this.fulltabledata[0]; // Get the first entry of the table + + // Find matching algorithm by name from the provided algorithms list + const matchingAlgorithm = this.data.algorithms.find(algorithm => algorithm.name === firstEntry.name); + + if (matchingAlgorithm) { + console.log("Matching algorithm found:", matchingAlgorithm); + + // Make the first request to get implementations + const implementationsUrl = `http://localhost:6626/atlas/algorithms/${matchingAlgorithm.id}/implementations`; + this.http.get(implementationsUrl).subscribe(implementations => { + console.log("Implementations:", implementations); + + // For each implementation, get implementation packages + implementations.content.forEach(implementation => { + const packagesUrl = `http://localhost:6626/atlas/algorithms/${matchingAlgorithm.id}/implementations/${implementation.id}/implementation-packages`; + this.http.get(packagesUrl).subscribe(packages => { + console.log("Implementation Packages:", packages); + + // Filter packages where the description contains some word of the input field + // Split input field value by spaces into individual keywords + const inputKeywords = this.inputfield.value ? this.inputfield.value.split(' ') : []; + console.log("Input Keywords:", inputKeywords); + + // Filter packages whose description contains any of the input keywords + const filteredPackages = packages.content.filter(pkg => { + return inputKeywords.some(keyword => pkg.description.toLowerCase().includes(keyword.toLowerCase())); + }); + + // For each package, fetch the file content + filteredPackages.forEach(pkg => { + const fileContentUrl = `http://localhost:6626/atlas/algorithms/${matchingAlgorithm.id}/implementations/${implementation.id}/implementation-packages/${pkg.id}/file/content`; + + this.http.get(fileContentUrl, { responseType: 'text' }).subscribe(fileContent => { + //console.log("File content:", fileContent); + + // Store the file content in a variable for deployment + this.deploymentFileContentUrl = fileContentUrl; + this.isAggregationComplete = true; // Enable Deploy button once we have the file content + }, error => { + console.error("Error fetching file content:", error); + }); + }); + }, error => { + console.error("Error fetching implementation packages:", error); + }); }); + }, error => { + console.error("Error fetching implementations:", error); }); - - console.log("Die Daten:", this.data); - console.log(this.infos); - } - - - - checkboxClicked(event){ - if(this.checked){ - this.checked = false; - }else{ - this.checked = true; - } - } - - numberChanged() { - this.tabledata.data = this.fulltabledata.slice(0, this.selectednumber); - } - - closeDialog(algorithmName: string) { - let previous = { resultAlgorithm: this.resultAlgorithm, - tabledata: this.tabledata, - fulltabledata: this.fulltabledata, - columnsToDisplay: this.columnsToDisplay, - inputfieldvalue: this.inputfield.value, - rephrasedInput: this.rephrasedInput, - selectedNumber: this.selectednumber, }; - - this.dialogRef.close({ algoname: algorithmName, prev: previous }); - } - - closeDialog2() { - if((this.inputfield.value != '') && this.showMatchingResults){ - let previous = { resultAlgorithm: this.resultAlgorithm, - tabledata: this.tabledata, - fulltabledata: this.fulltabledata, - columnsToDisplay: this.columnsToDisplay, - inputfieldvalue: this.inputfield.value, - rephrasedInput: this.rephrasedInput, - selectedNumber: this.selectednumber, }; - - this.dialogRef.close({ algoname: undefined, prev: previous }); - }else{ - this.dialogRef.close(null); + + } else { + console.log("No matching algorithm found for the first entry."); } + } else { + console.log("No data available to aggregate."); } + } + + + async deploySolution() { + if (this.isAggregationComplete) { + if (this.fulltabledata && this.fulltabledata.length > 0) { + const wineryEndpoint = "http://localhost:8093/winery/"; + const firstEntryName = this.fulltabledata[0].name; // Get the first entry of the table + + // Function to generate a random string of length 6 + const generateRandomString = (length = 6) => { + return Math.random().toString(36).substring(2, 2 + length); // Generates a random string + }; - - openLink(){ - let alg = this.data.data.filter(algorithm => algorithm.name == this.resultAlgorithm.name); - if(alg.length > 0){ - this.closeDialog(this.resultAlgorithm.name); - }; - - } - - openLink2(algname){ - let alg = this.data.data.filter(algorithm => algorithm.name == algname); - if(alg.length > 0){ - this.closeDialog(algname); - }; - } + + // Make sure to properly wait for the file content to be fetched + try { + const fileContent = await this.http.get(this.deploymentFileContentUrl, { responseType: 'blob' }).toPromise(); + console.log("File content fetched successfully:", fileContent); + + const idwinery = firstEntryName.replaceAll("_", "") + generateRandomString(); + console.log("ID Winery:", idwinery); + + // First, create the service template with the new activity id + const serviceTemplate = await createServiceTemplate(idwinery, QUANTME_NAMESPACE_PULL); + console.log("Service Template created:", serviceTemplate); + + // Create the nodetype to add it to the created service template + await createNodeType(idwinery + "Container", OPENTOSCA_NAMESPACE_NODETYPE); + await updateNodeType(idwinery + "Container", OPENTOSCA_NAMESPACE_NODETYPE); + + // Update the service template with the created node type + const updatedServiceTemplate = await updateServiceTemplate(idwinery, QUANTME_NAMESPACE_PULL); + console.log("Service Template updated:", updatedServiceTemplate); + + // Finally, create the deployment model containing the implementation + const versionUrl = wineryEndpoint + "servicetemplates/" + updatedServiceTemplate; + const deploymentModelUrlResult = await createDeploymentModel( + fileContent, + wineryEndpoint, + idwinery + "_DA", + "http://opentosca.org/artifacttemplates", + "{http://opentosca.org/artifacttypes}DockerContainerArtifact", + idwinery + "_DA", + versionUrl + ); - extractInformation(isRake: boolean) { - const datatosend = { input: this.inputfield.value, algodata: this.infos }; - let url = 'http://localhost:1985/api/matcher/'; - - if (isRake) { - url += 'rake/'; - } - - if (this.checked) { - url += 'openai'; + let uploadResult = await uploadCSARToContainer( + "http://localhost:1337/", + idwinery, + "http://localhost:8093/winery/servicetemplates/http%253A%252F%252Fquantil.org%252Fquantme%252Fpull/Grover-SAToe3yau/?csar", + "http://localhost:8093/winery" + ); + console.log("Deployment Model URL Result:", deploymentModelUrlResult); + + console.log("Solution deployed successfully!"); + } catch (error) { + console.error("Error during solution deployment:", error); } - - // Reset rephrased input - this.rephrasedInput = ''; - this.showRephrasedInput = false; - - this.http.post(url, datatosend).subscribe(resultdata => { - // Step 1: Filter out results with null or undefined cosineSimilarity - const filteredResults = resultdata.result.filter( - (r: any) => r.cosineSimilarity != null - ); - - // Step 2: Assign filtered data to fulltabledata - this.fulltabledata = filteredResults; - - // Step 3: Update tabledata with the sliced data based on selectednumber - this.tabledata.data = this.fulltabledata.slice(0, this.selectednumber); - - console.log('Filtered tabledata:', this.fulltabledata); - - // Step 4: Handle rephrased input if available - if (resultdata.hasOwnProperty('rephrasedInput')) { - this.rephrasedInput = resultdata.rephrasedInput; - this.showRephrasedInput = true; - } - - // Step 5: Determine the algorithm with the highest cosineSimilarity - if (this.fulltabledata.length > 0) { - const maximumkey = this.fulltabledata.reduce((prev: any, current: any) => { - return prev.cosineSimilarity > current.cosineSimilarity ? prev : current; - }); - - if (maximumkey.cosineSimilarity > 0) { - this.showMatchingResults = true; - this.resultAlgorithm = maximumkey; - } else { - this.showMatchingResults = false; - console.log('No similarities found!'); - } - } else { - this.showMatchingResults = false; - console.log('No data available after filtering.'); - } - }, error => { - console.error('Error during information extraction:', error); - this.showMatchingResults = false; - }); + } else { + console.error("No fulltabledata available to deploy."); } -} + } else { + console.error("Aggregation process not completed, cannot deploy the solution."); + } + } +} diff --git a/src/app/core/util/http-util.ts b/src/app/core/util/http-util.ts new file mode 100644 index 00000000..9447b03a --- /dev/null +++ b/src/app/core/util/http-util.ts @@ -0,0 +1,32 @@ +/** + * Copyright (c) 2024 Institute of Architecture of Application Systems - + * University of Stuttgart + * + * This program and the accompanying materials are made available under the + * terms the Apache Software License 2.0 + * which is available at https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +import $ from 'jquery'; + +export function performAjax(targetUrl, dataToSend) { + return new Promise(function (resolve, reject) { + $.ajax({ + type: "POST", + url: targetUrl, + data: dataToSend, + processData: false, + crossDomain: true, + contentType: false, + beforeSend: function () {}, + success: function (data) { + resolve(data); + }, + error: function (err) { + reject(err); + }, + }); + }); +} \ No newline at end of file diff --git a/src/app/core/util/opentosca-utils.ts b/src/app/core/util/opentosca-utils.ts new file mode 100644 index 00000000..1d5f0b85 --- /dev/null +++ b/src/app/core/util/opentosca-utils.ts @@ -0,0 +1,345 @@ +/** + * Copyright (c) 2024 Institute of Architecture of Application Systems - + * University of Stuttgart + * + * This program and the accompanying materials are made available under the + * terms the Apache Software License 2.0 + * which is available at https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +import { fetch } from "whatwg-fetch"; +import { performAjax } from "./http-util"; + +/** + * Upload the CSAR located at the given URL to the connected OpenTOSCA Container and return the corresponding URL and required input parameters + * + * @param opentoscaEndpoint the endpoint of the OpenTOSCA Container + * @param csarName the name of the CSAR to upload + * @param url the URL pointing to the CSAR + * @param wineryEndpoint the endpoint of the Winery containing the CSAR to upload + */ +export async function uploadCSARToContainer( + opentoscaEndpoint, + csarName, + url, + wineryEndpoint +) { + if (opentoscaEndpoint === undefined) { + console.error("OpenTOSCA endpoint is undefined. Unable to upload CSARs..."); + return { success: false }; + } + + try { + if (url.startsWith("{{ wineryEndpoint }}")) { + url = url.replace("{{ wineryEndpoint }}", wineryEndpoint); + } + console.log( + "Checking if CSAR at following URL is already uploaded to the OpenTOSCA Container: ", + url + ); + + // check if CSAR is already uploaded + let getCSARResult = await getBuildPlanForCSAR(opentoscaEndpoint, csarName); + + if (!getCSARResult.success) { + console.log("CSAR is not yet uploaded. Uploading..."); + + const body = { + enrich: "false", + name: csarName, + url, + }; + + // upload the CSAR + await fetch(opentoscaEndpoint, { + method: "POST", + body: JSON.stringify(body), + headers: { "Content-Type": "application/json" }, + }); + console.log( + "CSAR sent to OpenTOSCA Container. Waiting for build plan..." + ); + + // check successful upload and retrieve corresponding url + getCSARResult = await getBuildPlanForCSAR(opentoscaEndpoint, csarName); + console.log("Retrieved result: ", getCSARResult); + } + + if (!getCSARResult.success) { + console.error("Uploading CSAR failed!"); + return { success: false }; + } + + // retrieve input parameters for the build plan + const buildPlanResult = await fetch(getCSARResult.url); + const buildPlanResultJson = await buildPlanResult.json(); + + return { + success: true, + url: getCSARResult.url, + inputParameters: buildPlanResultJson.input_parameters, + }; + } catch (e) { + console.error("Error while uploading CSAR: " + e); + return { success: false }; + } +} + +/** + * Get the link to the build plan of the CSAR with the given name if it is uploaded to the OpenTOSCA Container + * + * @param opentoscaEndpoint the endpoint of the OpenTOSCA Container + * @param csarName the name of the csar + * @return the status whether the given CSAR is uploaded and the corresponding build plan link if available + */ +async function getBuildPlanForCSAR(opentoscaEndpoint, csarName) { + console.log("Retrieving build plan for CSAR with name: ", csarName); + + // get all currently deployed CSARs + const response = await fetch(opentoscaEndpoint); + const responseJson = await response.json(); + + const deployedCSARs = responseJson.csars; + if (deployedCSARs === undefined) { + // no CSARs available + return { success: false }; + } + console.log("Retrieved all currently deployed CSARs: ", deployedCSARs); + + for (let i = 0; i < deployedCSARs.length; i++) { + const deployedCSAR = deployedCSARs[i]; + if (deployedCSAR.id === csarName) { + console.log("Found uploaded CSAR with id: %s", csarName); + const url = deployedCSAR._links.self.href; + + // retrieve the URl to the build plan required to get the input parameters and to instantiate the CSAR + return getBuildPlanUrl(url); + } + } + + // unable to find CSAR + return { success: false }; +} + +/** + * Get the URL to the build plan of the given CSAR + * + * @param csarUrl the URL to a CSAR + * @return the URL to the build plan for the given CSAR + */ +async function getBuildPlanUrl(csarUrl) { + let response = await fetch(csarUrl + "/servicetemplates"); + let responseJson = await response.json(); + + if ( + !responseJson.service_templates || + responseJson.service_templates.length !== 1 + ) { + console.error( + "Unable to find service template in CSAR at URL: %s", + csarUrl + ); + return { success: false }; + } + + const buildPlansUrl = + responseJson.service_templates[0]._links.self.href + "/buildplans"; + response = await fetch(buildPlansUrl); + responseJson = await response.json(); + + if (!responseJson.plans || responseJson.plans.length !== 1) { + console.error("Unable to find build plan at URL: %s", buildPlansUrl); + return { success: false }; + } + + return { success: true, url: responseJson.plans[0]._links.self.href }; +} + + +export function makeId(length) { + let result = ""; + const characters = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + const charactersLength = characters.length; + for (let i = 0; i < length; i++) { + result += characters.charAt(Math.floor(Math.random() * charactersLength)); + } + return result; +} + +/** + * Create a new ArtifactTemplate of the given type and add the given blob as file + * + * @param wineryEndpoint the endpoint of the Winery to create the ArtifactTemplate at + * @param localNamePrefix the prefix of the local name of the artifact to which a random suffix is added if an ArtifactTemplate with the name already exists + * @param namespace the namespace of the ArtifactTemplate to create + * @param type the type of the ArtifactTemplate to create + * @param blob the blob to upload to the ArtifactTemplate + * @param fileName the name of the file to upload as blob + * @return the final name of the created ArtifactTemplate + */ +export async function createNewArtifactTemplate( + wineryEndpoint, + localNamePrefix, + namespace, + type, + blob, + fileName +) { + console.log("Creating new ArtifactTemplate of type: ", type); + + // retrieve the currently available ArtifactTemplates + const getArtifactsResult = await fetch( + wineryEndpoint + "artifacttemplates/" + ); + const getArtifactsResultJson = await getArtifactsResult.json(); + + console.log(getArtifactsResultJson); + + // get unique name for the ArtifactTemplate + let artifactTemplateLocalName = localNamePrefix; + if ( + getArtifactsResultJson.some( + (e) => e.id === artifactTemplateLocalName && e.namespace === namespace + ) + ) { + let nameOccupied = true; + artifactTemplateLocalName += "-" + makeId(1); + while (nameOccupied) { + if ( + !getArtifactsResultJson.some( + (e) => e.id === artifactTemplateLocalName && e.namespace === namespace + ) + ) { + nameOccupied = false; + } else { + artifactTemplateLocalName += makeId(1); + } + } + } + console.log( + "Creating ArtifactTemplate with name: ", + artifactTemplateLocalName + ); + + // create ArtifactTemplate + const artifactCreationResponse = await fetch( + wineryEndpoint + "artifacttemplates/", + { + method: "POST", + body: JSON.stringify({ + localname: artifactTemplateLocalName, + namespace, + type, + }), + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + } + ); + const artifactCreationResponseText = await artifactCreationResponse.text(); + + // get URL for the file upload to the ArtifactTemplate + const fileUrl = + wineryEndpoint + + "artifacttemplates/" + + artifactCreationResponseText + + "files"; + + // upload the blob + const fd = new FormData(); + fd.append("file", blob, fileName); + await performAjax(fileUrl, fd); + + return artifactTemplateLocalName; +} + +/** + * Create a new version of the ServiceTemplate with the given ID and namespace and return its URL or an error if the ServiceTemplate does not exist + * + * @param wineryEndpoint the endpoint of the Winery to create the new ServiceTemplate version + * @param serviceTemplateId the ID of the ServiceTemplate to create a new version from + * @param serviceTemplateNamespace the namespace of the ServiceTemplate to create a new version from + * @return the URL to the new version, or an error if the base ServiceTemplate is not available at the given Winery endpoint + */ +export async function createNewServiceTemplateVersion( + wineryEndpoint, + serviceTemplateId, + serviceTemplateNamespace +) { + console.log( + "Creating new version of Service Template with ID %s and namespace %s", + serviceTemplateId, + serviceTemplateNamespace + ); + + // retrieve the currently available ServiceTemplates + const getTemplatesResult = await fetch(wineryEndpoint + "/servicetemplates/"); + const getTemplatesResultJson = await getTemplatesResult.json(); + + // abort if base service template is not available + if ( + !getTemplatesResultJson.some( + (e) => + e.id === serviceTemplateId && e.namespace === serviceTemplateNamespace + ) + ) { + console.log( + "Required base ServiceTemplate for deploying Qiskit Runtime programs not available in Winery!" + ); + return { + error: + "Required base ServiceTemplate for deploying Qiskit Runtime programs not available in Winery!", + }; + } + + // get unique name for the new version + let version = makeId(5); + let nameOccupied = true; + while (nameOccupied) { + if ( + !getTemplatesResultJson.some( + (e) => + e.id === serviceTemplateId + "_" + version + "-w1-wip1" && + e.namespace === serviceTemplateNamespace + ) + ) { + nameOccupied = false; + } else { + version += makeId(1); + } + } + console.log("Using component version: ", version); + + // create ServiceTemplate version + const versionUrl = + wineryEndpoint + + "/servicetemplates/" + + encodeURIComponent(encodeURIComponent(serviceTemplateNamespace)) + + "/" + + serviceTemplateId; + console.log("Creating new version under URL:", versionUrl); + const versionCreationResponse = await fetch(versionUrl, { + method: "POST", + body: JSON.stringify({ + version: { + componentVersion: version, + currentVersion: false, + editable: true, + latestVersion: false, + releasable: false, + wineryVersion: 1, + workInProgressVersion: 1, + }, + }), + headers: { "Content-Type": "application/json", Accept: "application/json" }, + }); + + // assemble URL to the created ServiceTemplate version + const versionCreationResponseText = await versionCreationResponse.text(); + return wineryEndpoint + "/servicetemplates/" + versionCreationResponseText; +} + diff --git a/src/app/core/util/winery-utils.ts b/src/app/core/util/winery-utils.ts new file mode 100644 index 00000000..e1c280c4 --- /dev/null +++ b/src/app/core/util/winery-utils.ts @@ -0,0 +1,432 @@ +/** + * Copyright (c) 2024 Institute of Architecture of Application Systems - + * University of Stuttgart + * + * This program and the accompanying materials are made available under the + * terms the Apache Software License 2.0 + * which is available at https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +import { fetch } from 'whatwg-fetch'; +import { createNewArtifactTemplate } from "./opentosca-utils"; + +const QUANTME_NAMESPACE_PUSH = "http://quantil.org/quantme/push"; +const QUANTME_NAMESPACE_PULL = "http://quantil.org/quantme/pull"; + +const nodeTypeDefinition: string = ` + + + + + + ContainerPort + xsd:string + + + Port + xsd:string + + + ENV_CAMUNDA_ENDPOINT + xsd:string + + + ENV_CAMUNDA_TOPIC + + + +`; + +const topologyTemplateDefinition: string = ` + + + + + + tcp://dind:2375 + + Running + + + + + + + + 80 + + + get_input: camundaEndpoint + get_input: camundaTopic + + + + + + + + + + + + +`; + +function getWineryEndpoint(){ + return "http://localhost:8093/winery"; +} + +export async function createArtifactTemplate(name, artifactTypeQName) { + const artifactTemplate = { + localname: name, + namespace: QUANTME_NAMESPACE_PUSH + "/artifacttemplates", + type: artifactTypeQName, + }; + const response = await fetch(`${getWineryEndpoint()}/artifacttemplates`, { + method: "POST", + headers: { + "Content-Type": "application/json", + Accept: "text/plain", + }, + body: JSON.stringify(artifactTemplate), + }); + return response.text(); +} + +export async function addFileToArtifactTemplate(artifactTemplateAddress, file) { + const formData = new FormData(); + formData.append("file", file); + const response = await fetch( + `${getWineryEndpoint()}/artifacttemplates/${artifactTemplateAddress}files`, + { + method: "POST", + body: formData, + headers: { + Accept: "*/*", + }, + } + ); + return response.json(); +} + +export async function createArtifactTemplateWithFile(name, artifactType, file) { + const artifactTemplateAddress = await createArtifactTemplate( + name, + artifactType + ); + await addFileToArtifactTemplate(artifactTemplateAddress, file); + return artifactTemplateAddress; +} + +export async function createServiceTemplate(name, namespace) { + console.log("create service template with name ", name); + const serviceTemplate = { + localname: name, + namespace: namespace, + }; + const response = await fetch(getWineryEndpoint() + "/servicetemplates", { + method: "POST", + headers: { + "Content-Type": "application/json", + Accept: "text/plain", + }, + body: JSON.stringify(serviceTemplate), + }); + return response.text(); +} + +export async function deleteArtifactTemplate(artifactTemplateName) { + // /artifacttemplates/http%253A%252F%252Fquantil.org%252Fquantme%252Fpush%252Fartifacttemplates/ArtifactTemplate-Activity_01b3qkz/ + const response = await fetch( + `${getWineryEndpoint()}/artifacttemplates/${encodeURIComponent( + encodeURIComponent(QUANTME_NAMESPACE_PUSH + "/") + )}artifacttemplates/${encodeURIComponent( + encodeURIComponent(artifactTemplateName) + )}`, + { + method: "DELETE", + headers: { + Accept: "application/json", + }, + } + ); + return response.status === 204; +} + +export async function serviceTemplateExists(serviceTemplateName) { + const response = await fetch( + `${getWineryEndpoint()}/servicetemplates/${encodeURIComponent( + encodeURIComponent(QUANTME_NAMESPACE_PUSH) + )}/${encodeURIComponent(encodeURIComponent(serviceTemplateName))}`, + { + method: "GET", + headers: { + Accept: "application/json", + }, + } + ); + return response.status === 200; +} + +export async function deleteTagByID(serviceTemplateAddress, tagID) { + const response = await fetch( + `${getWineryEndpoint()}/servicetemplates/${serviceTemplateAddress}tags/${tagID}`, + { + method: "DELETE", + headers: { + Accept: "application/json", + }, + } + ); + return response.status === 204; +} + +export async function getTags(serviceTemplateAddress) { + const response = await fetch( + `${getWineryEndpoint()}/servicetemplates/${serviceTemplateAddress}tags/`, + { + method: "GET", + headers: { + Accept: "application/json", + }, + } + ); + return response.json(); +} + +export async function deleteTopNodeTag(serviceTemplateAddress) { + const tags = await getTags(serviceTemplateAddress); + const topNodeTag = tags.find((tag) => tag.name === "top-node"); + if (topNodeTag) { + return deleteTagByID(serviceTemplateAddress, topNodeTag.id); + } + return true; +} + +export async function getServiceTemplateXML(serviceTemplateAddress) { + const response = await fetch( + `${getWineryEndpoint()}/servicetemplates/${serviceTemplateAddress}xml`, + { + method: "GET", + headers: { + Accept: "application/xml", + }, + } + ); + return response.json(); +} + + +export async function getArtifactTemplateInfo(artifactTemplateAddress) { + const response = await fetch( + `${getWineryEndpoint()}/artifacttemplates/${artifactTemplateAddress}`, + { + method: "GET", + headers: { + Accept: "application/json", + }, + } + ); + return response.json(); +} + +const nodeTypeQNameMapping = new Map([ + [ + "{http://opentosca.org/artifacttypes}WAR", + "{http://opentosca.org/nodetypes}TomcatApplication_WAR-w1", + ], + [ + "{http://opentosca.org/artifacttypes}WAR-Java17", + "{http://opentosca.org/nodetypes}TomcatApplication_WAR-w1", + ], + [ + "{http://opentosca.org/artifacttypes}WAR-Java8", + "{http://opentosca.org/nodetypes}TomcatApplication_WAR-w1", + ], + [ + "{http://opentosca.org/artifacttypes}PythonArchiveArtifact", + "{http://opentosca.org/nodetypes}PythonApp_3-w1", + ], +]); +export function getNodeTypeQName(artifactTypeQName) { + return nodeTypeQNameMapping.get(artifactTypeQName); +} + +export async function loadTopology(deploymentModelUrl) { + if (deploymentModelUrl.startsWith("{{ wineryEndpoint }}")) { + deploymentModelUrl = deploymentModelUrl.replace( + "{{ wineryEndpoint }}", + getWineryEndpoint() + ); + } + let topology; + let tags; + try { + topology = await fetch( + deploymentModelUrl.replace("?csar", "topologytemplate"), + { + method: "GET", + headers: { + Accept: "application/json", + }, + } + ).then((res) => res.json()); + tags = await fetch(deploymentModelUrl.replace("?csar", "tags")).then( + (res) => res.json() + ); + } catch (e) { + throw new Error( + "An unexpected error occurred during loading the deployments models topology." + ); + } + let topNode; + const topNodeTag = tags.find((tag) => tag.name === "top-node"); + if (topNodeTag) { + const topNodeId = topNodeTag.value; + topNode = topology.nodeTemplates.find( + (nodeTemplate) => nodeTemplate.id === topNodeId + ); + if (!topNode) { + throw new Error(`Top level node "${topNodeId}" not found.`); + } + } else { + let nodes = new Map( + topology.nodeTemplates.map((nodeTemplate) => [ + nodeTemplate.id, + nodeTemplate, + ]) + ); + for (let relationship of topology.relationshipTemplates) { + if (relationship.name === "HostedOn") { + nodes.delete(relationship.targetElement.ref); + } + } + if (nodes.size === 1) { + topNode = nodes.values().next().value; + } + } + if (!topNode) { + throw new Error("No top level node found."); + } + + return { + topNode, + nodeTemplates: topology.nodeTemplates, + relationshipTemplates: topology.relationshipTemplates, + }; +} + +/** + * Generate a deployment model to deploy the generated hybrid program and the corresponding agent + * + * @param programBlobs the blobs containing the data for the hybrid program and agent + * @param wineryEndpoint endpoint of the Winery instance to create the deployment model + * @return the URL of the generated deployment model, or an error if the generation failed + */ +export async function createDeploymentModel( + programBlobs, + wineryEndpoint, + localNamePrefix, + artifactTemplateNamespace, + artifactType, + fileName, + serviceTemplateURL +) { + // create a new ArtifactTemplate and upload the agent file (the agent currently also contains the program and we deploy them together) + let artifactName = await createNewArtifactTemplate( + wineryEndpoint, + localNamePrefix, + artifactTemplateNamespace, + artifactType, + programBlobs, + fileName + ); + + // create new ServiceTemplate for the hybrid program by adding a new version of the predefined template + //let serviceTemplateURL = await createNewServiceTemplateVersion( + // wineryEndpoint, + //serviceTemplateName, + //serviceTemplateNamespace + //); + //if (serviceTemplateURL.error !== undefined) { + // return { error: serviceTemplateURL.error }; + //} + + // replace concrete Winery endpoint with abstract placeholder to enable QAA transfer into another environment + let deploymentModelUrl = serviceTemplateURL.replace( + wineryEndpoint, + "{{ wineryEndpoint }}" + ); + deploymentModelUrl += "?csar"; + return { deploymentModelUrl: deploymentModelUrl }; +} + +export async function createNodeType(name, namespace) { + const nodetype = { + localname: name, + namespace: namespace, + }; + const response = await fetch(`${getWineryEndpoint()}/nodetypes`, { + method: "POST", + headers: { + "Content-Type": "application/json", + Accept: "text/plain", + }, + body: JSON.stringify(nodetype), + }); + return response.text(); +} + +export async function updateNodeType(name, namespace) { + const nodetype = nodeTypeDefinition.replace("NodetypeToReplace", name).replace("NodetypeToReplace", name).replace("NodetypeToReplace", name); + console.log( + getWineryEndpoint() + + "/nodetypes/" + + encodeURIComponent(encodeURIComponent(namespace)) + + "/" + + name + ); + console.log(nodetype); + const response = await fetch( + `${getWineryEndpoint()}/nodetypes/${encodeURIComponent( + encodeURIComponent(namespace) + )}/${name}`, + { + method: "PUT", + headers: { + "Content-Type": "application/xml", + Accept: "text/plain", + }, + body: nodetype, + } + ); + return response.text(); +} + +export async function updateServiceTemplate(name, namespace) { + const topologyTemplate = topologyTemplateDefinition.replace(/NodetypeToReplace/g, name); + console.log( + getWineryEndpoint() + + "/servicetemplates/" + + encodeURIComponent(encodeURIComponent(namespace)) + + "/" + + name + ); + console.log(topologyTemplate); + const response = await fetch( + `${getWineryEndpoint()}/servicetemplates/${encodeURIComponent( + encodeURIComponent(namespace) + )}/${name}`, + { + method: "PUT", + headers: { + "Content-Type": "application/xml", + Accept: "text/plain", + }, + body: topologyTemplate, + } + ); + return response.text(); +} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index b9095416..16332f99 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6011,6 +6011,11 @@ jest-worker@25.1.0, jest-worker@^25.1.0: merge-stream "^2.0.0" supports-color "^7.0.0" +jquery@^3.6.1: + version "3.7.1" + resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.7.1.tgz#083ef98927c9a6a74d05a6af02806566d16274de" + integrity sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg== + "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz" @@ -10781,6 +10786,11 @@ websocket-extensions@>=0.1.1: resolved "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz" integrity sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg== +whatwg-fetch@^3.6.2: + version "3.6.20" + resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz#580ce6d791facec91d37c72890995a0b48d31c70" + integrity sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg== + when@~3.6.x: version "3.6.4" resolved "https://registry.npmjs.org/when/-/when-3.6.4.tgz" From 7ed8e69b055c859406e627d69f9d962212320eca Mon Sep 17 00:00:00 2001 From: LaviniaStiliadou Date: Mon, 30 Sep 2024 20:37:39 +0200 Subject: [PATCH 3/6] fix layouting, add pattern graph button --- .../textmatcher/textmatcher.component.html | 196 +- .../default-pl-renderer.component.html | 2 +- src/app/core/util/constants.ts | 3598 +++++++++++++++++ 3 files changed, 3704 insertions(+), 92 deletions(-) create mode 100644 src/app/core/util/constants.ts diff --git a/src/app/core/component/textmatcher/textmatcher.component.html b/src/app/core/component/textmatcher/textmatcher.component.html index 7f38562a..83606732 100644 --- a/src/app/core/component/textmatcher/textmatcher.component.html +++ b/src/app/core/component/textmatcher/textmatcher.component.html @@ -1,103 +1,117 @@
- - Input the known Information - - -
- -
-

Rephrased input: {{rephrasedInput}}

-
- -
-

The best matching algorithm with cosine similarity is - {{resultAlgorithm.name}}. -

-

The Cosine Similarity is - {{resultAlgorithm.cosineSimilarity}} -

-
- -
- - Number of displayed algorithms - - {{number}} - - -
- -
- - - Name - - {{result.name}} - - - - Cosine similarity - {{result.cosineSimilarity}} - - - - + + Input the known Information + + + +
+

Rephrased input: {{rephrasedInput}}

+
+ +
+

The best matching algorithm with cosine similarity is + {{resultAlgorithm.name}}. +

+

The Cosine Similarity is + {{resultAlgorithm.cosineSimilarity}} +

+
+ +
+ + + Name + + {{result.name}} + + + + Cosine similarity + {{result.cosineSimilarity}} + + + + +
+ + + +

Aggregation in progress... {{ progressValue }}%

- - - - - - -
- - rephrase Problem using OpenAI -
- - - - - + + + + + + +
diff --git a/src/app/core/default-pl-renderer/default-pl-renderer.component.html b/src/app/core/default-pl-renderer/default-pl-renderer.component.html index ae08b69c..9d367eac 100644 --- a/src/app/core/default-pl-renderer/default-pl-renderer.component.html +++ b/src/app/core/default-pl-renderer/default-pl-renderer.component.html @@ -41,7 +41,7 @@