Commit 42c58b20 authored by François Agneray's avatar François Agneray
Browse files

Image list + cone-search => done

parent 0a27a401
<div class="row mb-1">
<div class="col-12">
<button title="Add new criteria family" (click)="openModal(template); $event.stopPropagation()" class="btn btn-outline-success float-right">
<button title="Add new criteria family" (click)="openModal(template); $event.stopPropagation()" class="btn btn-outline-success">
<span class="fas fa-plus"></span> New criteria family
</button>
</div>
......
......@@ -127,7 +127,6 @@ export class DatasetFormComponent implements OnInit, OnChanges {
this.form.controls.download_csv.enable();
this.form.controls.download_ascii.enable();
this.form.controls.download_vo.enable();
this.form.controls.download_archive.enable();
} else {
this.form.controls.download_opened.setValue(false);
this.form.controls.download_opened.disable();
......@@ -139,8 +138,6 @@ export class DatasetFormComponent implements OnInit, OnChanges {
this.form.controls.download_ascii.disable();
this.form.controls.download_vo.setValue(false);
this.form.controls.download_vo.disable();
this.form.controls.download_archive.setValue(false);
this.form.controls.download_archive.disable();
}
}
......
<div class="row mb-1">
<div class="col-12">
<button title="Add new image" (click)="openModal(template); $event.stopPropagation()" class="btn btn-outline-success">
<span class="fas fa-plus"></span> New image
</button>
</div>
</div>
<ng-template #template>
<div class="modal-header">
<h4 class="modal-title pull-left"><strong>Add new image</strong></h4>
</div>
<div class="modal-body">
<app-image-form
[instance]="instance"
[dataset]="dataset"
[files]="files"
[filesIsLoading]="filesIsLoading"
[filesIsLoaded]="filesIsLoaded"
[fitsImageLimits]="fitsImageLimits"
[fitsImageLimitsIsLoading]="fitsImageLimitsIsLoading"
[fitsImageLimitsIsLoaded]="fitsImageLimitsIsLoaded"
(loadRootDirectory)="loadRootDirectory.emit($event)"
(retrieveFitsImageLimits)="retrieveFitsImageLimits.emit($event)"
(onSubmit)="add.emit($event); modalRef.hide()"
#formNewImage>
<button [disabled]="!formNewImage.form.valid || formNewImage.form.pristine" type="submit" class="btn btn-primary">
<span class="fa fa-database"></span> Add image
</button>
&nbsp;
<button (click)="modalRef.hide()" type="button" class="btn btn-danger">Cancel</button>
</app-image-form>
</div>
</ng-template>
/**
* This file is part of Anis Client.
*
* @copyright Laboratoire d'Astrophysique de Marseille / CNRS
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { Component, ChangeDetectionStrategy, TemplateRef, Input, Output, EventEmitter } from '@angular/core';
import { BsModalService } from 'ngx-bootstrap/modal';
import { BsModalRef } from 'ngx-bootstrap/modal/bs-modal-ref.service';
import { Dataset, Image, Instance } from 'src/app/metamodel/models';
import { FileInfo, FitsImageLimits } from 'src/app/admin/store/models';
@Component({
selector: 'app-add-image',
templateUrl: 'add-image.component.html',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class AddImageComponent {
@Input() instance: Instance;
@Input() dataset: Dataset;
@Input() files: FileInfo[];
@Input() filesIsLoading: boolean;
@Input() filesIsLoaded: boolean;
@Input() fitsImageLimits: FitsImageLimits;
@Input() fitsImageLimitsIsLoading: boolean;
@Input() fitsImageLimitsIsLoaded: boolean;
@Output() loadRootDirectory: EventEmitter<string> = new EventEmitter();
@Output() retrieveFitsImageLimits: EventEmitter<string> = new EventEmitter();
@Output() add: EventEmitter<Image> = new EventEmitter();
modalRef: BsModalRef;
constructor(private modalService: BsModalService) { }
openModal(template: TemplateRef<any>) {
this.modalRef = this.modalService.show(template);
}
}
<button title="Edit this image" (click)="openModal(template); $event.stopPropagation()" class="btn btn-outline-primary">
<span class="fas fa-edit"></span>
</button>
<ng-template #template>
<div class="modal-header">
<h4 class="modal-title pull-left"><strong>Edit image</strong></h4>
</div>
<div class="modal-body">
<app-image-form
[image]="image"
[instance]="instance"
[dataset]="dataset"
[files]="files"
[filesIsLoading]="filesIsLoading"
[filesIsLoaded]="filesIsLoaded"
[fitsImageLimits]="fitsImageLimits"
[fitsImageLimitsIsLoading]="fitsImageLimitsIsLoading"
[fitsImageLimitsIsLoaded]="fitsImageLimitsIsLoaded"
(loadRootDirectory)="loadRootDirectory.emit($event)"
(retrieveFitsImageLimits)="retrieveFitsImageLimits.emit($event)"
(onSubmit)="edit.emit($event); modalRef.hide()"
#formEditImage>
<button [disabled]="!formEditImage.form.valid || formEditImage.form.pristine" type="submit" class="btn btn-primary">
<span class="fa fa-database"></span> Update image information
</button>
&nbsp;
<button (click)="modalRef.hide()" type="button" class="btn btn-danger">Cancel</button>
</app-image-form>
</div>
</ng-template>
/**
* This file is part of Anis Client.
*
* @copyright Laboratoire d'Astrophysique de Marseille / CNRS
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { Component, Input, ChangeDetectionStrategy, TemplateRef, Output, EventEmitter } from '@angular/core';
import { BsModalService } from 'ngx-bootstrap/modal';
import { BsModalRef } from 'ngx-bootstrap/modal/bs-modal-ref.service';
import { Dataset, Image, Instance } from 'src/app/metamodel/models';
import { FileInfo, FitsImageLimits } from 'src/app/admin/store/models';
@Component({
selector: 'app-edit-image',
templateUrl: 'edit-image.component.html',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class EditImageComponent {
@Input() image: Image;
@Input() instance: Instance;
@Input() dataset: Dataset;
@Input() files: FileInfo[];
@Input() filesIsLoading: boolean;
@Input() filesIsLoaded: boolean;
@Input() fitsImageLimits: FitsImageLimits;
@Input() fitsImageLimitsIsLoading: boolean;
@Input() fitsImageLimitsIsLoaded: boolean;
@Output() loadRootDirectory: EventEmitter<string> = new EventEmitter();
@Output() retrieveFitsImageLimits: EventEmitter<string> = new EventEmitter();
@Output() edit: EventEmitter<Image> = new EventEmitter();
modalRef: BsModalRef;
constructor(private modalService: BsModalService) { }
openModal(template: TemplateRef<any>) {
this.modalRef = this.modalService.show(template);
}
}
......@@ -12,6 +12,10 @@
(loadDirectory)="onChangeFileSelect($event)"
(select)="onFileSelect($event)">
</app-path-select-form-control>
<div class="form-group">
<label for="label">Label</label>
<input type="text" class="form-control" id="label" name="label" formControlName="label">
</div>
<div class="form-group">
<label for="file_size">File size</label>
<input type="number" class="form-control" id="file_size" name="file_size" formControlName="file_size">
......
......@@ -32,6 +32,7 @@ export class ImageFormComponent implements OnInit, OnChanges {
@Output() onSubmit: EventEmitter<Image> = new EventEmitter();
public form = new FormGroup({
label: new FormControl('', [Validators.required]),
file_path: new FormControl('', [Validators.required]),
file_size: new FormControl('', [Validators.required]),
ra_min: new FormControl('', [Validators.required]),
......
<div class="mt-2 table-responsive">
<table class="table">
<app-add-image
[instance]="instance"
[dataset]="dataset"
[files]="files"
[filesIsLoading]="filesIsLoading"
[filesIsLoaded]="filesIsLoaded"
[fitsImageLimits]="fitsImageLimits"
[fitsImageLimitsIsLoading]="fitsImageLimitsIsLoading"
[fitsImageLimitsIsLoaded]="fitsImageLimitsIsLoaded"
(loadRootDirectory)="loadRootDirectory.emit($event)"
(retrieveFitsImageLimits)="retrieveFitsImageLimits.emit($event)"
(add)="add.emit($event)">
</app-add-image>
<div class="row" *ngIf="imageList.length === 0">
<div class="col-12 lead text-center font-weight-bold">
No images available...
</div>
</div>
<div *ngIf="imageList.length > 0" class="table-responsive">
<table class="table table-striped" aria-describedby="Image list">
<thead>
<tr>
<th scope="col">ID</th>
<th scope="col">Label</th>
<th scope="col">File path</th>
<th scope="col">File size</th>
<th scope="col">Ra [min,max]</th>
......@@ -15,6 +37,8 @@
</thead>
<tbody>
<tr *ngFor="let image of imageList">
<td class="align-middle">{{ image.id }}</td>
<td class="align-middle">{{ image.label }}</td>
<td class="align-middle">{{ image.file_path }}</td>
<td class="align-middle">{{ image.file_size | formatFileSize:false }}</td>
<td class="align-middle">[{{ image.ra_min }},{{ image.ra_max }}]</td>
......@@ -23,20 +47,29 @@
<td class="align-middle">{{ image.pmin }}</td>
<td class="align-middle">{{ image.pmax }}</td>
<td class="align-middle">
<a title="Edit this image"
routerLink="/admin/instance/configure-instance/{{ instance.name }}/dataset/configure-dataset/{{ dataset.name }}/edit-image/{{ image.id }}"
class="btn btn-outline-primary">
<span class="fas fa-edit"></span>
</a>
<app-edit-image
[image]="image"
[instance]="instance"
[dataset]="dataset"
[files]="files"
[filesIsLoading]="filesIsLoading"
[filesIsLoaded]="filesIsLoaded"
[fitsImageLimits]="fitsImageLimits"
[fitsImageLimitsIsLoading]="fitsImageLimitsIsLoading"
[fitsImageLimitsIsLoaded]="fitsImageLimitsIsLoaded"
(loadRootDirectory)="loadRootDirectory.emit($event)"
(retrieveFitsImageLimits)="retrieveFitsImageLimits.emit($event)"
(edit)="edit.emit($event)">
</app-edit-image>
</td>
<td class="align-middle">
<app-delete-btn
[type]="'image'"
[label]="image.file_path"
(confirm)="deleteImage.emit(image)">
(confirm)="delete.emit(image)">
</app-delete-btn>
</td>
</tr>
</tbody>
</table>
</div>
</div>
\ No newline at end of file
import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy } from '@angular/core';
import { Dataset, Image, Instance } from 'src/app/metamodel/models';
import { FileInfo, FitsImageLimits } from 'src/app/admin/store/models';
@Component({
selector: 'app-image-list',
templateUrl: 'image-list.component.html',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ImageListComponent {
@Input() imageList: Image[];
@Input() instance: Instance;
@Input() dataset: Dataset;
@Input() files: FileInfo[];
@Input() filesIsLoading: boolean;
@Input() filesIsLoaded: boolean;
@Input() fitsImageLimits: FitsImageLimits;
@Input() fitsImageLimitsIsLoading: boolean;
@Input() fitsImageLimitsIsLoaded: boolean;
@Output() loadRootDirectory: EventEmitter<string> = new EventEmitter();
@Output() retrieveFitsImageLimits: EventEmitter<string> = new EventEmitter();
@Output() add: EventEmitter<Image> = new EventEmitter();
@Output() edit: EventEmitter<Image> = new EventEmitter();
@Output() delete: EventEmitter<Image> = new EventEmitter();
}
/**
* This file is part of Anis Client.
*
* @copyright Laboratoire d'Astrophysique de Marseille / CNRS
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { Component, Input, Output, ChangeDetectionStrategy, EventEmitter } from '@angular/core';
import { Dataset, Image, Instance } from 'src/app/metamodel/models';
@Component({
selector: 'app-image-table',
templateUrl: 'image-table.component.html',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ImageTableComponent {
@Input() instance: Instance;
@Input() dataset: Dataset;
@Input() imageList: Image[];
@Output() deleteImage: EventEmitter<Image> = new EventEmitter();
}
import { ImageListComponent } from './image-list.component';
import { ImageFormComponent } from './image-form.component';
import { ImageTableComponent } from './image-table.component';
import { AddImageComponent } from './add-image.component';
import { EditImageComponent } from './edit-image.component';
export const imageComponents = [
ImageListComponent,
ImageFormComponent,
ImageTableComponent
AddImageComponent,
EditImageComponent
];
<div class="row mb-1">
<div class="col-12">
<button title="Add new output category" (click)="openModal(template); $event.stopPropagation()" class="btn btn-outline-success float-right">
<button title="Add new output category" (click)="openModal(template); $event.stopPropagation()" class="btn btn-outline-success">
<span class="fas fa-plus"></span> New output category
</button>
</div>
......
<div class="row mb-1">
<div class="col-12">
<button title="Add new output family" (click)="openModal(template); $event.stopPropagation()" class="btn btn-outline-success float-right">
<button title="Add new output family" (click)="openModal(template); $event.stopPropagation()" class="btn btn-outline-success">
<span class="fas fa-plus"></span> New output family
</button>
</div>
......
......@@ -3,7 +3,8 @@
|| (criteriaFamilyListIsLoading | async)
|| (outputFamilyListIsLoading | async)
|| (outputCategoryListIsLoading | async)
|| (selectOptionListIsLoading | async)">
|| (selectOptionListIsLoading | async)
|| (imageListIsLoading | async)">
</app-spinner>
<div *ngIf="(attributeListIsLoaded | async)
......@@ -11,7 +12,8 @@
&& (criteriaFamilyListIsLoaded | async)
&& (outputFamilyListIsLoaded | async)
&& (outputCategoryListIsLoaded | async)
&& (selectOptionListIsLoaded | async)" class="container-fluid">
&& (selectOptionListIsLoaded | async)
&& (imageListIsLoaded | async)" class="container-fluid">
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item">
......@@ -39,11 +41,6 @@
(loadColumnList)="loadColumnList()"
(add)="addAttribute($event)">
</app-add-attribute>
<div class="btn-group mr-2" role="group" aria-label="Second group">
<button routerLink="/admin/instance/configure-instance/{{ (instance | async).name }}/dataset/configure-dataset/{{ (dataset | async).name }}/image-list" title="Images" class="btn btn-outline-primary">
<span class="fas fa-image"></span> Images
</button>
</div>
<div class="btn-group mr-2" role="group" aria-label="Third group">
<button routerLink="/admin/instance/configure-instance/{{ (instance | async).name }}/dataset/configure-dataset/{{ (dataset | async).name }}/configure-cone-search" title="Configure cone-search" class="btn btn-outline-primary">
<span class="fas fa-search"></span> Configure cone-search
......@@ -87,6 +84,9 @@
<li class="nav-item">
<a class="nav-link" routerLink="./" [queryParams]="{tab_selected: 'ocategories'}" [ngClass]="{'active': (tabSelected | async) === 'ocategories'}">Output Categories</a>
</li>
<li class="nav-item">
<a class="nav-link" routerLink="./" [queryParams]="{tab_selected: 'images'}" [ngClass]="{'active': (tabSelected | async) === 'images'}">Images</a>
</li>
</ul>
</div>
<div class="card-body" [ngSwitch]="tabSelected | async">
......@@ -166,6 +166,23 @@
(edit)="editOutputCategory($event)"
(delete)="deleteOutputCategory($event)">
</app-output-category-list>
<app-image-list
*ngSwitchCase="'images'"
[imageList]="imageList | async"
[instance]="instance | async"
[dataset]="dataset | async"
[files]="files | async"
[filesIsLoading]="filesIsLoading | async"
[filesIsLoaded]="filesIsLoaded | async"
[fitsImageLimits]="fitsImageLimits | async"
[fitsImageLimitsIsLoading]="fitsImageLimitsIsLoading | async"
[fitsImageLimitsIsLoaded]="fitsImageLimitsIsLoaded | async"
(loadRootDirectory)="loadRootDirectory($event)"
(retrieveFitsImageLimits)="retrieveFitsImageLimits($event)"
(add)="addImage($event)"
(edit)="editImage($event)"
(delete)="deleteImage($event)">
</app-image-list>
</div>
</div>
</div>
......
......@@ -14,8 +14,8 @@ import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { Store } from '@ngrx/store';
import { SelectOption, Instance, Dataset, Attribute, CriteriaFamily, OutputCategory, OutputFamily } from 'src/app/metamodel/models';
import { Column } from 'src/app/admin/store/models';
import { SelectOption, Instance, Dataset, Attribute, CriteriaFamily, OutputCategory, OutputFamily, Image } from 'src/app/metamodel/models';
import { Column, FileInfo, FitsImageLimits } from 'src/app/admin/store/models';
import * as instanceSelector from 'src/app/metamodel/selectors/instance.selector';
import * as datasetSelector from 'src/app/metamodel/selectors/dataset.selector';
import * as attributeActions from 'src/app/metamodel/actions/attribute.actions';
......@@ -31,6 +31,12 @@ import * as columnActions from 'src/app/admin/store/actions/column.actions';
import * as columnSelector from 'src/app/admin/store/selectors/column.selector';
import * as attributeDistinctActions from 'src/app/admin/store/actions/attribute-distinct.actions';
import * as attributeDistinctSelector from 'src/app/admin/store/selectors/attribute-distinct.selector';
import * as imageActions from 'src/app/metamodel/actions/image.actions';
import * as imageSelector from 'src/app/metamodel/selectors/image.selector';
import * as adminFileExplorerActions from 'src/app/admin/store/actions/admin-file-explorer.actions';
import * as adminFileExplorerSelector from 'src/app/admin/store/selectors/admin-file-explorer.selector';
import * as fitsImageActions from 'src/app/admin/store/actions/fits-image.actions';
import * as fitsImageSelector from 'src/app/admin/store/selectors/fits-image.selector';
@Component({
selector: 'app-attribute-list',
......@@ -64,6 +70,15 @@ export class AttributeListComponent implements OnInit {
public attributeDistinctList: Observable<string[]>;
public attributeDistinctListIsLoading: Observable<boolean>;
public attributeDistinctListIsLoaded: Observable<boolean>;
public imageList: Observable<Image[]>;
public imageListIsLoading: Observable<boolean>;
public imageListIsLoaded: Observable<boolean>;
public files: Observable<FileInfo[]>;
public filesIsLoading: Observable<boolean>;
public filesIsLoaded: Observable<boolean>;
public fitsImageLimits: Observable<FitsImageLimits>;
public fitsImageLimitsIsLoading: Observable<boolean>;
public fitsImageLimitsIsLoaded: Observable<boolean>;
constructor(private store: Store<{ }>, private route: ActivatedRoute) {
this.instance = store.select(instanceSelector.selectInstanceByRouteName);
......@@ -91,6 +106,16 @@ export class AttributeListComponent implements OnInit {
this.attributeDistinctList = store.select(attributeDistinctSelector.selectAllAttributeDistincts);
this.attributeDistinctListIsLoading = store.select(attributeDistinctSelector.selectAttributeDistinctListIsLoading);
this.attributeDistinctListIsLoaded = store.select(attributeDistinctSelector.selectAttributeDistinctListIsLoaded);
this.imageList = store.select(imageSelector.selectAllImages);
this.imageListIsLoading = store.select(imageSelector.selectImageListIsLoading);
this.imageListIsLoaded = store.select(imageSelector.selectImageListIsLoaded);
this.files = store.select(adminFileExplorerSelector.selectFiles);
this.filesIsLoading = store.select(adminFileExplorerSelector.selectFilesIsLoading);
this.filesIsLoaded = store.select(adminFileExplorerSelector.selectFilesIsLoaded);
this.fitsImageLimits = store.select(fitsImageSelector.selectFitsImageLimits);
this.fitsImageLimitsIsLoading = store.select(fitsImageSelector.selectFitsImageLimitsIsLoading);
this.fitsImageLimitsIsLoaded = store.select(fitsImageSelector.selectFitsImageLimitsIsLoaded);
}
ngOnInit() {
......@@ -158,4 +183,24 @@ export class AttributeListComponent implements OnInit {
deleteAttribute(attribute: Attribute) {
this.store.dispatch(attributeActions.deleteAttribute({ attribute }));
}
loadRootDirectory(path: string) {
this.store.dispatch(adminFileExplorerActions.loadFiles({ path }));
}
retrieveFitsImageLimits(filePath: string) {
this.store.dispatch(fitsImageActions.retrieveFitsImageLimits({ filePath }));
}
addImage(image: Image) {
this.store.dispatch(imageActions.addImage({ image }));
}
editImage(image: Image) {
this.store.dispatch(imageActions.editImage({ image }));
}
deleteImage(image: Image) {
this.store.dispatch(imageActions.deleteImage({ image }));
}
}
<app-spinner *ngIf="(datasetListIsLoading | async) || (imageListIsLoading | async)"></app-spinner>
<div *ngIf="(datasetListIsLoaded | async) && (imageListIsLoaded | async)" class="container-fluid">
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a routerLink="/admin/instance/instance-list">Instances</a></li>
<li class="breadcrumb-item">
<a routerLink="/admin/instance/configure-instance/{{ (instance | async).name }}/dataset/dataset-list">
Configure instance {{ (instance | async).label }}
</a>
</li>
<li class="breadcrumb-item">
<a routerLink="/admin/instance/configure-instance/{{ (instance | async).name }}/dataset/configure-dataset/{{ (dataset | async).name }}" [queryParams]="{tab_selected: 'design'}">
Configure dataset {{ (dataset | async).label }}
</a>
</li>
<li class="breadcrumb-item">
<a routerLink="/admin/instance/configure-instance/{{ (instance | async).name }}/dataset/configure-dataset/{{ (dataset | async).name }}/image-list">
Image list
</a>
</li>
<li class="breadcrumb-item active" aria-current="page">Edit image</li>
</ol>
</nav>
</div>
<div *ngIf="(datasetListIsLoaded | async) && (imageListIsLoaded | async)" class="container">
<div class="row">
<div class="col-12">
<app-image-form
[image]="image | async"
[instance]="instance | async"
[dataset]="dataset | async"
[files]="files | async"
[filesIsLoading]="filesIsLoading | async"
[filesIsLoaded]="filesIsLoaded | async"
[fitsImageLimits]="fitsImageLimits | async"
[fitsImageLimitsIsLoading]="fitsImageLimitsIsLoading | async"
[fitsImageLimitsIsLoaded]="fitsImageLimitsIsLoaded | async"
(loadRootDirectory)="loadRootDirectory($event)"
(retrieveFitsImageLimits)="retrieveFitsImageLimits($event)"
(onSubmit)="editImage($event)"
#formEditImage>
<button [disabled]="!formEditImage.form.valid || formEditImage.form.pristine" type="submit" class="btn btn-primary">
<span class="fa fa-database"></span> Edit image
</button>
&nbsp;
<button routerLink="/admin/instance/configure-instance/{{ (instance | async).name }}/dataset/image-list/{{ (dataset | async).name }}" type="button" class="btn btn-danger">Cancel</button>
</app-image-form>
</div>