Merge pull request #18 from jhuapl-lglenden/develop-updates

Image annotation and MongoDB updates.
This commit is contained in:
Laura Glendenning
2020-12-10 13:01:43 -05:00
committed by GitHub
8 changed files with 117 additions and 27 deletions

View File

@@ -33,11 +33,15 @@ RUN apt-get -y update && \
pip3 install --upgrade pip gunicorn pipenv
# Install latest mongodb
# It can no longer be installed from packages due to the packages relying on systemctl which is not
# in the Ubuntu docker image.
# https://docs.mongodb.com/manual/tutorial/install-mongodb-on-ubuntu-tarball/
ARG MONGO_VERSION=ubuntu1804-4.2.11
RUN if [ -n "${DB_DIR}" ] ; then \
apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv E162F504A20CDF15827F718D4B7C549A058F8B6B && \
echo "deb [ arch=amd64 ] https://repo.mongodb.org/apt/ubuntu bionic/mongodb-org/4.2 multiverse" > /etc/apt/sources.list.d/mongodb-org-4.2.list && \
apt-get -y update && \
apt-get install -y mongodb-org mongodb-org-server mongodb-org-tools mongodb-org-shell; \
apt-get -y install libcurl4 openssl liblzma5 wget && \
wget --progress=dot https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-${MONGO_VERSION}.tgz && \
tar xzf mongodb-linux-x86_64-${MONGO_VERSION}.tgz && \
mv mongodb-linux-x86_64-${MONGO_VERSION}/bin/* /usr/local/bin/; \
fi
# Install python packages

View File

@@ -29,6 +29,28 @@
overflow-y: auto;
}
.image-annotation-container {
top: 50px;
left: 10px;
width: 204px;
bottom: 10px;
position: absolute;
z-index: 1000;
}
.image-annotation-divider {
margin-top: 10px;
margin-bottom: 10px;
position: relative !important;
}
.image-annotation-list {
flex-grow: 1;
overflow-y: auto;
padding-left: 8px;
padding-right:4px;
}
/* annotation tab */
.filter-bar {
position: absolute;

View File

@@ -1,7 +1,8 @@
<!-- (C) 2019 The Johns Hopkins University Applied Physics Laboratory LLC.-->
<div fxFlexFill class="page-container" fxLayout="column">
<mat-toolbar class="title-toolbar">
<button class="doc-back-button" mat-icon-button matTooltip="Go back to collection details" (click)="backToCollectionDetails()">
<button class="doc-back-button" mat-icon-button matTooltip="Go back to collection details"
(click)="backToCollectionDetails()">
<mat-icon>keyboard_arrow_left</mat-icon>
</button>
<span class="page-title">Document {{doc?._id}}</span>
@@ -56,8 +57,8 @@
<b>
Document Overall Agreement:
</b>
<span *ngIf="ann_agreement != undefined">{{ann_agreement | percent:'1.2-2'}}</span>
<span *ngIf="ann_agreement == undefined">N/A</span>
<span *ngIf="ann_agreement != null && ann_agreement != 'null'">{{ann_agreement | percent:'1.2-2'}}</span>
<span *ngIf="ann_agreement == null || ann_agreement == 'null'">N/A</span>
</span>
</div>
</div>
@@ -127,7 +128,8 @@
</button>
<app-error></app-error>
<span fxFlex></span>
<button class="annotate-button" mat-raised-button (click)="save(false)" [disabled]="!canCurrentlyAnnotate">
<button class="annotate-button" mat-raised-button (click)="save(false)"
[disabled]="!canCurrentlyAnnotate">
<span class="material-icons">save</span>Save
</button>
<span fxFlex="10px"></span>
@@ -140,6 +142,31 @@
<section *ngIf="tabIndex == 1 && !loading.loading && !loading.error" #imageSection fxFlexFill>
<div *ngIf="doc.metadata && doc.metadata['imageUrl']" id="myDocImage" class="image-container">
<mat-card class="image-annotation-container" fxLayout="column">
<div>Annotations</div>
<mat-divider class="image-annotation-divider"></mat-divider>
<div class="image-annotation-list" fxLayout="column">
<mat-chip-list class="mat-chip-list-stacked">
<mat-checkbox *ngFor="let annotation of myDocAnnotations;" [(ngModel)]="annotation.checked">
<mat-chip [style.background-color]="annotation.label.color"
class="shadowed cursor-pointer">
{{annotation.label.name}}</mat-chip>
</mat-checkbox>
</mat-chip-list>
</div>
<div fxLayout="row">
<mat-error *ngIf="!canAnnotate" id="cantAnnotate">
<h3>Note: you do not have authority to change or add annotations for this document.</h3>
</mat-error>
<span fxFlex></span>
<button class="btn-short annotate-button" mat-raised-button color="primary"
(click)="save(false)" [disabled]="!canCurrentlyAnnotate">
<span class="material-icons">save</span>Save
</button>
</div>
</mat-card>
<div style="position: absolute; top: 0px; bottom: 0; left: 0; right: 0;">
<button class="full-screen-btn" mat-raised-button
(click)="toggleImageFullscreen()">{{ isImageFullscreen() ? 'Close' : 'Open' }} Full

View File

@@ -62,8 +62,9 @@ export class AnnotateComponent implements OnInit, AfterViewInit {
@ViewChild(LoadingComponent)
public loading: LoadingComponent;
@ViewChild(ErrorComponent)
public error: ErrorComponent;
public htmlError: string;
public httpError: HttpErrorResponse;
public errorOperation: string;
@ViewChild("docElem")
public docElem: ElementRef;
@@ -558,6 +559,12 @@ export class AnnotateComponent implements OnInit, AfterViewInit {
}
}
private clearError() {
this.httpError = null;
this.htmlError = null;
this.errorOperation = null;
}
public save(andAdvance: boolean) {
if (!this.canAnnotate) { return; }
const docAnnotations = [];
@@ -566,7 +573,7 @@ export class AnnotateComponent implements OnInit, AfterViewInit {
docAnnotations.push(annotation.label.name);
}
}
this.error.clear();
this.clearError();
this.annotations.saveAnnotations(this.doc._id, docAnnotations, this.nerData.annotations).subscribe((id: string) => {
this.myNerAnnotations = this.nerData.annotations;
this.changed = false;
@@ -581,12 +588,13 @@ export class AnnotateComponent implements OnInit, AfterViewInit {
this.advanceToNext();
}
}, (error: HttpErrorResponse) => {
this.error.showHttp(error, "saving annotations");
this.httpError = error;
this.errorOperation = "saving annotations";
});
}
private advanceToNext() {
this.error.clear();
this.clearError();
this.pipelines.advanceToNextDocumentForClassifier(this.classifier._id, this.doc._id).subscribe((success: boolean) => {
if (success) {
this.pipelines.getNextDocumentIdForClassifier(this.classifier._id).subscribe((documentId: string) => {
@@ -599,13 +607,15 @@ export class AnnotateComponent implements OnInit, AfterViewInit {
this.router.navigate([`/${PATHS.collection.details}`, this.collection._id]);
}
}, (error: HttpErrorResponse) => {
this.error.showHttp(error, "advancing to next document");
this.httpError = error;
this.errorOperation = "advancing to next document";
});
} else {
this.error.showHtml("Unable to advance to next document.");
this.htmlError = "Unable to advance to next document.";
}
}, (error: HttpErrorResponse) => {
this.error.showHttp(error, "advancing to next document");
this.httpError = error;
this.errorOperation = "advancing to next document";
});
}

View File

@@ -1,6 +1,6 @@
/*(C) 2019 The Johns Hopkins University Applied Physics Laboratory LLC. */
import { Component, OnInit } from "@angular/core";
import { Component, Input, OnChanges, OnInit, SimpleChanges } from "@angular/core";
import { HttpErrorResponse } from "@angular/common/http";
@Component({
@@ -8,7 +8,11 @@ import { HttpErrorResponse } from "@angular/common/http";
templateUrl: "./error.component.html",
styleUrls: ["./error.component.css"]
})
export class ErrorComponent implements OnInit {
export class ErrorComponent implements OnInit, OnChanges {
@Input() httpError: HttpErrorResponse;
@Input() htmlError: string;
@Input() operation: string = null;
public visible = false;
@@ -19,6 +23,16 @@ export class ErrorComponent implements OnInit {
ngOnInit() {
}
ngOnChanges(changes: SimpleChanges) {
if(this.htmlError) {
this.showHtml(this.htmlError);
} else if(this.htmlError) {
this.showHttp(this.httpError);
} else {
this.clear();
}
}
public clear() {
this.visible = false;
this.innerHTML = "";

View File

@@ -54,4 +54,12 @@
.image-invert {
-webkit-filter: invert(1);
filter: invert(1);
}
.pan-zoom-container {
position: absolute;
top: 54px;
left: 224px;
right: 0;
bottom: 0;
}

View File

@@ -12,8 +12,8 @@
</button>
</div>
<button [color]="mode === 'invert' ? 'primary' : 'default'" class="image-btn image-btn-space image-btn-invert" mat-raised-button
(click)="setMode('invert')" matTooltip="Invert Colors">
<button [color]="mode === 'invert' ? 'primary' : 'default'" class="image-btn image-btn-space image-btn-invert"
mat-raised-button (click)="setMode('invert')" matTooltip="Invert Colors">
<mat-icon>compare</mat-icon>
</button>
<button [color]="mode === 'sharpen' ? 'primary' : 'default'" class="image-btn image-btn-space" mat-raised-button
@@ -22,14 +22,19 @@
</button>
<button [color]="mode === 'histogram' ? 'primary' : 'default'" class="image-btn image-btn-space" mat-raised-button
(click)="setMode('histogram')" matTooltip="Histogram Equalizer">
<mat-icon>bar_chart</mat-icon>
<mat-icon>insert_chart</mat-icon>
</button>
<button class="image-btn image-btn-space" mat-raised-button (click)="setMode('none')" matTooltip="Clear Filters">
<mat-icon>layers_clear</mat-icon>
</button>
</div>
<pan-zoom [config]="panZoomConfig">
<canvas id="imageCanvas" #imageCanvas [ngStyle]="{ 'filter': mode === 'invert' ? 'invert(1)' : 'none' }"></canvas>
</pan-zoom>
<div class="pan-zoom-container">
<pan-zoom [config]="panZoomConfig">
<canvas id="imageCanvas" #imageCanvas
[ngStyle]="{ 'filter': mode === 'invert' ? 'invert(1)' : 'none' }"></canvas>
</pan-zoom>
</div>
<div *ngIf="loadError" class="image-error"><mat-error>Error Loading Image</mat-error></div>
<div *ngIf="loadError" class="image-error">
<mat-error>Error Loading Image</mat-error>
</div>

View File

@@ -88,8 +88,8 @@ describe("Sanity Tests", function() {
.within(() => {
cy.contains("PINE", {timeout: 20 * 1000})
.should("be.visible", {timeout: 20 * 1000});
cy.contains("database")
.should("be.visible");
cy.contains("database", {timeout: 20 * 1000})
.should("be.visible", {timeout: 20 * 1000});
});
cy.get("app-about")
.find("button").contains("Close")