mirror of
https://github.com/clearml/clearml-web
synced 2025-03-13 07:08:17 +00:00
Add delete artifact button. Refactor for updated Angular.
This commit is contained in:
parent
e4af78213d
commit
a982ad6c45
src/app
app.module.ts
webapp-common
experiments/dumb/experiment-artifact-item-view
settings/admin
shared/ui-components/overlay/upload-artifact-dialog
@ -27,6 +27,11 @@ import {MAT_TOOLTIP_DEFAULT_OPTIONS, MatTooltipDefaultOptions} from '@angular/ma
|
|||||||
import {UpdateNotifierComponent} from '@common/shared/ui-components/overlay/update-notifier/update-notifier.component';
|
import {UpdateNotifierComponent} from '@common/shared/ui-components/overlay/update-notifier/update-notifier.component';
|
||||||
import {ChooseColorModule} from '@common/shared/ui-components/directives/choose-color/choose-color.module';
|
import {ChooseColorModule} from '@common/shared/ui-components/directives/choose-color/choose-color.module';
|
||||||
import {SpinnerComponent} from '@common/shared/ui-components/overlay/spinner/spinner.component';
|
import {SpinnerComponent} from '@common/shared/ui-components/overlay/spinner/spinner.component';
|
||||||
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
|
import { MatInputModule } from '@angular/material/input';
|
||||||
|
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||||
|
import { UploadArtifactDialogComponent } from '@common/shared/ui-components/overlay/upload-artifact-dialog/upload-artifact-dialog.component';
|
||||||
|
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations : [AppComponent],
|
declarations : [AppComponent],
|
||||||
@ -61,6 +66,10 @@ import {SpinnerComponent} from '@common/shared/ui-components/overlay/spinner/spi
|
|||||||
UpdateNotifierComponent,
|
UpdateNotifierComponent,
|
||||||
ChooseColorModule,
|
ChooseColorModule,
|
||||||
SpinnerComponent,
|
SpinnerComponent,
|
||||||
|
MatButtonModule,
|
||||||
|
MatInputModule,
|
||||||
|
MatFormFieldModule,
|
||||||
|
UploadArtifactDialogComponent
|
||||||
],
|
],
|
||||||
providers : [
|
providers : [
|
||||||
UserPreferences,
|
UserPreferences,
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
</sm-labeled-row>
|
</sm-labeled-row>
|
||||||
<sm-labeled-row label="FILE SIZE" data-id="fileSizeId">{{(artifact?.content_size | filesize : fileSizeConfigStorage) || ''}}</sm-labeled-row>
|
<sm-labeled-row label="FILE SIZE" data-id="fileSizeId">{{(artifact?.content_size | filesize : fileSizeConfigStorage) || ''}}</sm-labeled-row>
|
||||||
<sm-labeled-row label="HASH" data-id="hashId">{{artifact?.hash}}</sm-labeled-row>
|
<sm-labeled-row label="HASH" data-id="hashId">{{artifact?.hash}}</sm-labeled-row>
|
||||||
|
<sm-labeled-row label="MODE" data-id="modeId">{{artifact?.mode | titlecase}}</sm-labeled-row>
|
||||||
<sm-labeled-row *ngFor="let data of artifact?.display_data" [label]="data[0]| uppercase">{{data[1]}}</sm-labeled-row>
|
<sm-labeled-row *ngFor="let data of artifact?.display_data" [label]="data[0]| uppercase">{{data[1]}}</sm-labeled-row>
|
||||||
</div>
|
</div>
|
||||||
</sm-editable-section>
|
</sm-editable-section>
|
||||||
@ -32,5 +33,5 @@
|
|||||||
[formData]="artifact?.type_data?.preview"
|
[formData]="artifact?.type_data?.preview"
|
||||||
></sm-scroll-textarea>
|
></sm-scroll-textarea>
|
||||||
</sm-editable-section>
|
</sm-editable-section>
|
||||||
|
<button *ngIf="enableDeleteButton()" class="btn btn-neon" style="margin: auto;" (click)="deleteArtifact(artifact?.key, artifact?.mode)">DELETE ARTIFACT</button>
|
||||||
|
|
||||||
|
@ -1,12 +1,27 @@
|
|||||||
import {ChangeDetectionStrategy, Component, Input} from '@angular/core';
|
import {ChangeDetectionStrategy, Component, Input, Inject} from '@angular/core';
|
||||||
|
import { MAT_DIALOG_DATA, MatDialog, MatDialogRef } from '@angular/material/dialog';
|
||||||
import {Artifact} from '~/business-logic/model/tasks/artifact';
|
import {Artifact} from '~/business-logic/model/tasks/artifact';
|
||||||
import {BaseClickableArtifactComponent} from '../base-clickable-artifact.component';
|
import {BaseClickableArtifactComponent} from '../base-clickable-artifact.component';
|
||||||
import {fileSizeConfigStorage} from '@common/shared/pipes/filesize.pipe';
|
import {fileSizeConfigStorage} from '@common/shared/pipes/filesize.pipe';
|
||||||
|
import { ApiTasksService } from '~/business-logic/api-services/tasks.service';
|
||||||
|
import { ConfirmDialogComponent } from '@common/shared/ui-components/overlay/confirm-dialog/confirm-dialog.component';
|
||||||
|
import { take } from 'rxjs/operators';
|
||||||
|
import { addMessage } from '@common/core/actions/layout.actions';
|
||||||
|
import { Observable } from 'rxjs/internal/Observable';
|
||||||
|
import { IExperimentInfo } from '~/features/experiments/shared/experiment-info.model';
|
||||||
|
import { selectExperimentInfoData } from '~/features/experiments/reducers';
|
||||||
|
import { ArtifactId } from '~/business-logic/model/tasks/artifactId';
|
||||||
|
import { deleteS3Sources } from '@common/shared/entity-page/entity-delete/common-delete-dialog.actions';
|
||||||
|
import { EXPERIMENTS_STATUS_LABELS } from '~/features/experiments/shared/experiments.const';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'sm-experiment-artifact-item-view',
|
selector: 'sm-experiment-artifact-item-view',
|
||||||
templateUrl: './experiment-artifact-item-view.component.html',
|
templateUrl: './experiment-artifact-item-view.component.html',
|
||||||
styleUrls: ['./experiment-artifact-item-view.component.scss'],
|
styleUrls: ['./experiment-artifact-item-view.component.scss'],
|
||||||
|
providers: [
|
||||||
|
{ provide: MAT_DIALOG_DATA, useValue: {} },
|
||||||
|
{ provide: MatDialogRef, useValue: {} }
|
||||||
|
],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush
|
changeDetection: ChangeDetectionStrategy.OnPush
|
||||||
})
|
})
|
||||||
export class ExperimentArtifactItemViewComponent extends BaseClickableArtifactComponent{
|
export class ExperimentArtifactItemViewComponent extends BaseClickableArtifactComponent{
|
||||||
@ -14,11 +29,28 @@ export class ExperimentArtifactItemViewComponent extends BaseClickableArtifactCo
|
|||||||
public isLinkable: boolean;
|
public isLinkable: boolean;
|
||||||
public fileSizeConfigStorage = fileSizeConfigStorage;
|
public fileSizeConfigStorage = fileSizeConfigStorage;
|
||||||
public inMemorySize: boolean;
|
public inMemorySize: boolean;
|
||||||
|
public experimentInfo$: Observable<IExperimentInfo>;
|
||||||
private _artifact: Artifact;
|
private _artifact: Artifact;
|
||||||
|
private artifactId: ArtifactId;
|
||||||
|
private _experiment: any;
|
||||||
|
|
||||||
@Input() editable: boolean;
|
@Input() editable: boolean;
|
||||||
@Input() downloading: boolean;
|
@Input() downloading: boolean;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
@Inject(MAT_DIALOG_DATA) public data: ConfirmDialogComponent,
|
||||||
|
public dialogRef: MatDialogRef<ConfirmDialogComponent>,
|
||||||
|
private dialog: MatDialog,
|
||||||
|
private tasksApi: ApiTasksService,
|
||||||
|
){
|
||||||
|
super();
|
||||||
|
this.experimentInfo$ = this.store.select(selectExperimentInfoData);
|
||||||
|
this.artifactId = {key: '', mode: 'output'};
|
||||||
|
this.experimentInfo$.subscribe((res) => {
|
||||||
|
this._experiment = res;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@Input() set artifact(artifact: Artifact) {
|
@Input() set artifact(artifact: Artifact) {
|
||||||
this._artifact = artifact;
|
this._artifact = artifact;
|
||||||
if(artifact){
|
if(artifact){
|
||||||
@ -39,6 +71,14 @@ export class ExperimentArtifactItemViewComponent extends BaseClickableArtifactCo
|
|||||||
return this._artifact;
|
return this._artifact;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getStatusLabel() {
|
||||||
|
return EXPERIMENTS_STATUS_LABELS[this._experiment?.status] || '';
|
||||||
|
}
|
||||||
|
|
||||||
|
enableDeleteButton() {
|
||||||
|
return this.getStatusLabel() === 'Draft';
|
||||||
|
}
|
||||||
|
|
||||||
linkClicked(event: Event) {
|
linkClicked(event: Event) {
|
||||||
this.signUrl(this.artifact.uri).subscribe(signed => {
|
this.signUrl(this.artifact.uri).subscribe(signed => {
|
||||||
const a = document.createElement('a');
|
const a = document.createElement('a');
|
||||||
@ -48,4 +88,34 @@ export class ExperimentArtifactItemViewComponent extends BaseClickableArtifactCo
|
|||||||
});
|
});
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
deleteArtifact(key, mode) {
|
||||||
|
this.artifactId = {key: key, mode: mode};
|
||||||
|
const confirmDialogRef: MatDialogRef<any, boolean> = this.dialog.open(ConfirmDialogComponent, {
|
||||||
|
data: {
|
||||||
|
title: 'Delete Artifact',
|
||||||
|
body: 'Are you sure you want to delete artifact ' + key + ' from the experiment and S3?<br /><strong>This cannot be undone.</strong>',
|
||||||
|
yes: 'Delete',
|
||||||
|
no: 'Cancel',
|
||||||
|
iconClass: 'al-icon al-ico-trash al-color blue-300',
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
confirmDialogRef.afterClosed().pipe(take(1)).subscribe((confirmed) => {
|
||||||
|
if (confirmed) {
|
||||||
|
this.tasksApi.tasksDeleteArtifacts({
|
||||||
|
/* eslint-disable @typescript-eslint/naming-convention */
|
||||||
|
task: this._experiment.id,
|
||||||
|
artifacts: [this.artifactId]
|
||||||
|
/* eslint-enable @typescript-eslint/naming-convention */
|
||||||
|
}, null, 'body', true).subscribe({
|
||||||
|
next: () => {
|
||||||
|
this.store.dispatch(deleteS3Sources({files: [this.artifact.uri]}))
|
||||||
|
},
|
||||||
|
error: err => this.store.dispatch(addMessage('error', `Error ${err.error?.meta?.result_msg}`)),
|
||||||
|
complete: () => this.store.dispatch(addMessage('success', 'Artifact deleted successfully.')),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -242,7 +242,7 @@ export class BaseAdminService {
|
|||||||
Bucket: bucketKeyEndpoint.Bucket,
|
Bucket: bucketKeyEndpoint.Bucket,
|
||||||
Delete: {
|
Delete: {
|
||||||
Quiet: true,
|
Quiet: true,
|
||||||
Objects: files.map(file => ({Key: file} as ObjectIdentifier))
|
Objects: files.map(() => ({Key: bucketKeyEndpoint.Key} as ObjectIdentifier))
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
/* eslint-enable @typescript-eslint/naming-convention */
|
/* eslint-enable @typescript-eslint/naming-convention */
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { Component, Inject, Input, OnDestroy, OnInit, TemplateRef } from '@angular/core';
|
import { Component, Inject, Input, OnDestroy, OnInit, TemplateRef } from '@angular/core';
|
||||||
import { MAT_DIALOG_DATA, MatDialog, MatDialogRef } from '@angular/material/dialog';
|
import { MAT_DIALOG_DATA, MatDialog, MatDialogRef } from '@angular/material/dialog';
|
||||||
import { FormControl, FormGroup, Validators } from '@angular/forms';
|
import { FormsModule, ReactiveFormsModule, FormControl, FormGroup, Validators } from '@angular/forms';
|
||||||
import { Store } from '@ngrx/store';
|
import { Store } from '@ngrx/store';
|
||||||
import { addMessage } from '@common/core/actions/layout.actions';
|
import { addMessage } from '@common/core/actions/layout.actions';
|
||||||
import { UploadArtifactDialogConfig, ArtifactType } from './upload-artifact-dialog.model';
|
import { UploadArtifactDialogConfig, ArtifactType } from './upload-artifact-dialog.model';
|
||||||
@ -15,11 +15,30 @@ import { getSignedUrl } from '@common/core/actions/common-auth.actions';
|
|||||||
import { selectExperimentExecutionInfoData } from '@common/experiments/reducers';
|
import { selectExperimentExecutionInfoData } from '@common/experiments/reducers';
|
||||||
import { ArtifactModeEnum } from '~/business-logic/model/tasks/artifactModeEnum';
|
import { ArtifactModeEnum } from '~/business-logic/model/tasks/artifactModeEnum';
|
||||||
import { ConfirmDialogComponent } from '../confirm-dialog/confirm-dialog.component';
|
import { ConfirmDialogComponent } from '../confirm-dialog/confirm-dialog.component';
|
||||||
|
import { DialogTemplateComponent } from '@common/shared/ui-components/overlay/dialog-template/dialog-template.component';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { MatInputModule } from '@angular/material/input';
|
||||||
|
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||||
|
import { SafePipe } from '@common/shared/pipes/safe.pipe';
|
||||||
|
import { MatSelectModule } from '@angular/material/select';
|
||||||
|
import { MatRadioModule } from '@angular/material/radio';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'sm-upload-artifact-dialog',
|
selector: 'sm-upload-artifact-dialog',
|
||||||
templateUrl: './upload-artifact-dialog.component.html',
|
templateUrl: './upload-artifact-dialog.component.html',
|
||||||
styleUrls: ['./upload-artifact-dialog.component.scss']
|
styleUrls: ['./upload-artifact-dialog.component.scss'],
|
||||||
|
standalone: true,
|
||||||
|
imports: [
|
||||||
|
SafePipe,
|
||||||
|
FormsModule,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
DialogTemplateComponent,
|
||||||
|
CommonModule,
|
||||||
|
MatInputModule,
|
||||||
|
MatFormFieldModule,
|
||||||
|
MatSelectModule,
|
||||||
|
MatRadioModule,
|
||||||
|
]
|
||||||
})
|
})
|
||||||
export class UploadArtifactDialogComponent implements OnInit, OnDestroy {
|
export class UploadArtifactDialogComponent implements OnInit, OnDestroy {
|
||||||
artifactType: ArtifactType[] = [
|
artifactType: ArtifactType[] = [
|
||||||
@ -216,7 +235,7 @@ export class UploadArtifactDialogComponent implements OnInit, OnDestroy {
|
|||||||
type: this.uploadForm.controls['artType'].value,
|
type: this.uploadForm.controls['artType'].value,
|
||||||
content_size: item.size,
|
content_size: item.size,
|
||||||
timestamp: ts,
|
timestamp: ts,
|
||||||
hash: 'SHA256: ' + fileHash,
|
hash: fileHash,
|
||||||
uri: this.uploadUrl + item.name,
|
uri: this.uploadUrl + item.name,
|
||||||
mode: this.uploadForm.controls['mode'].value as ArtifactModeEnum
|
mode: this.uploadForm.controls['mode'].value as ArtifactModeEnum
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user