Skip to content

Commit

Permalink
Added annotations upload view
Browse files Browse the repository at this point in the history
* Add annotation import list, edit, delete, and detail pages
* Add tests for created pages
* Add models for audio event imports
* Add models for audio event import files (read and write)
* Add service for audio event imports
* Add support for sub-models through the `@bawSubModelCollection` and `@bawSubModel` decorators
* Add support for serialization of FormData arrays and `FileList`'s
* Add options object to `DateTime` pipe so that they are more verbosely expressed in templates e.g. `value | dateTime: true : false` is now `value | dateTime: { includeTime: true, localTime: false }`
* Migrated event summary report model's use of `Id[] | Ids` to the `CollectionIds` type alias
* Fixed bug in `isWorkInProgressPredicate` where it was only shown in production (now only shows in development)
* Fixed some TS typing issues inside the report generation component
* Re-enabled eslint maximum line length (it was incorrectly disabled during the angular upgrade)
* Add support for FormData requests that arn't part of a multipart request

Fixes: #1887
  • Loading branch information
hudson-newey committed Nov 29, 2023
1 parent aaf873e commit 7a1835c
Show file tree
Hide file tree
Showing 51 changed files with 2,539 additions and 194 deletions.
6 changes: 6 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@
"@typescript-eslint/no-inferrable-types": "off",
"@typescript-eslint/prefer-as-const": "off",
// beginning of explicitly enabled rules
"max-len": [
"error",
{
"code": 140
}
],
"no-console": [
"error",
{
Expand Down
4 changes: 3 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
"datatable",
"luxon",
"toastr",
"taggings",
"typeahead",
"Ecoacoustics"
"Ecoacoustics",
"datatable"
]
}
2 changes: 1 addition & 1 deletion src/app/app.menus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,4 +70,4 @@ export const isAdminPredicate = (user: User): boolean =>
* @param user User session data. This will be used to check if the user is an admin
*/
export const isWorkInProgressPredicate = (user: User): boolean =>
environment.production && isAdminPredicate(user);
!environment.production || isAdminPredicate(user);
2 changes: 2 additions & 0 deletions src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { formlyConfig } from "@shared/formly/custom-inputs.module";
import { ToastrModule } from "ngx-toastr";
import { environment } from "src/environments/environment";
import { TitleStrategy } from "@angular/router";
import { AnnotationsImportModule } from "@components/import-annotations/import-annotations.module";
import { AppRoutingModule } from "./app-routing.module";
import { AppComponent, PageTitleStrategy } from "./app.component";
import { toastrRoot } from "./app.helper";
Expand Down Expand Up @@ -62,6 +63,7 @@ export const appImports = [
DataRequestModule,
HarvestModule,
ReportsModule,
AnnotationsImportModule,
LibraryModule,
ListenModule,
MyAccountModule,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -354,5 +354,4 @@ describe("MetadataReviewComponent", () => {
flush();
discardPeriodicTasks();
}));

});
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"fields": [
{
"key": "name",
"type": "input",
"props": {
"type": "text",
"label": "Import Name",
"required": true,
"minLength": 2
}
},
{
"key": "description",
"type": "textarea",
"props": {
"label": "Description",
"required": false,
"rows": 8,
"description": "Description uses markdown formatting allowing you to apply some limited styling to the model. You can find a guide on the basics here: https://markdown-guide.readthedocs.io/en/latest/basics.html"
}
}
]
}
236 changes: 236 additions & 0 deletions src/app/components/import-annotations/details/details.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,236 @@
<h1>Import Annotations: {{ audioEventImport.name }}</h1>

<p
*ngIf="audioEventImport.descriptionHtml; else defaultDescription"
[innerHTML]="audioEventImport.descriptionHtml"
></p>

<div>
<section>
<h2>Events</h2>
<ngx-datatable
bawDatatableDefaults
[bawDatatablePagination]="{ filters: filters$, getModels: getModels }"
>
<ngx-datatable-column prop="">
<ng-template let-column="column" ngx-datatable-header-template>
Audio Recording
</ng-template>

<ng-template let-value="value" ngx-datatable-cell-template>
<baw-loading
*ngIf="value.audioRecording | isUnresolved; else audioRecordingTemplate"
size="sm"
></baw-loading>

<ng-template #audioRecordingTemplate>
<a [bawUrl]="value.audioRecording.viewUrl">
{{ value.audioRecording.id }}
</a>
</ng-template>
</ng-template>
</ngx-datatable-column>

<ngx-datatable-column prop="createdAt">
<ng-template let-column="column" ngx-datatable-header-template>
Created At
</ng-template>

<ng-template let-value="value" ngx-datatable-cell-template>
<span>
{{ value | dateTime : { includeTime: true, localTime: true } }}
</span>
</ng-template>
</ngx-datatable-column>

<ngx-datatable-column prop="">
<ng-template let-column="column" ngx-datatable-header-template>
Associated Tags
</ng-template>

<ng-template let-value="value" ngx-datatable-cell-template>
<baw-loading
*ngIf="value.tags | isUnresolved; else tagsTemplate"
size="sm"
></baw-loading>

<ng-template #tagsTemplate>
<baw-inline-list
[items]="value.tags"
[itemKey]="'text'"
[emptyTemplate]="noAssociatedTagsTemplate"
>
</baw-inline-list>
</ng-template>
</ng-template>
</ngx-datatable-column>
</ngx-datatable>
</section>

<section>
<h2>Files</h2>

<table
*ngIf="audioEventImport.files.length > 0; else noFilesTemplate"
class="table"
>
<thead>
<tr>
<th scope="col">File Name</th>
<th scope="col">Date Imported</th>
<th scope="col">Additional Tags</th>
</tr>
</thead>

<tbody>
<tr *ngFor="let file of audioEventImport.files" scope="row">
<td>{{ file.name }}</td>
<td>
<span>
{{ file.importedAt | dateTime : { includeTime: true, localTime: true } }}
</span>
</td>
<td>
<baw-inline-list
[items]="file.additionalTagModels"
[itemKey]="'text'"
[emptyTemplate]="noAdditionalTagsTemplate"
></baw-inline-list>
</td>
</tr>
</tbody>
</table>
</section>
</div>

<section>
<h2>Add more annotations</h2>
<form #ngForm="ngForm">
<div class="annotationsimportInput">
<div class="import-groups">
<ng-container *ngFor="let eventGroup of importGroups; let index = index">
<ng-template
[ngTemplateOutlet]="importGroupTemplate"
[ngTemplateOutletContext]="{ model: eventGroup, index: index }"
></ng-template>
</ng-container>
</div>
</div>

<span [ngbTooltip]="areImportGroupsValid() ? 'Import all new annotations' : 'Please fill out all required fields or fix errors'">
<button
type="submit"
class="btn btn-primary import-btn mb-3"
[disabled]="!areImportGroupsValid() || uploading"
(click)="uploadImportGroups()"
>
Import All
</button>
</span>
</form>
</section>

<ng-template #importGroupTemplate let-model="model" let-index="index">
<section class="import-group rounded border p-4 my-3">
<h3>Import Group {{ index + 1 }}</h3>

<div *ngIf="model?.errors?.length > 0" class="annotationimportOutput">
<div class="alert alert-danger">
<ul>
<li *ngFor="let error of model.errors">
{{ error }}
</li>
</ul>
</div>
</div>

<label for="annotationsFileInput" class="form-label required">
Annotation File(s)
</label>
<input
id="annotationsFileInput"
type="file"
accept=".csv, .selections.txt, .json"
class="form-control border"
[ngClass]="{'is-invalid': model?.errors?.length}"
[files]="model?.files"
[disabled]="uploading"
(change)="pushToImportGroups(model, $event)"
multiple
/>

<div class="mt-4">
<baw-typeahead-input
label="Associated Tags"
[inputPlaceholder]="'search for tags by name'"
[searchCallback]="searchTagsTypeaheadCallback"
[resultTemplate]="tagsTypeaheadTemplate"
[inputDisabled]="uploading"
(modelChange)="updateAdditionalTagIds(model, getIdsFromAbstractModelArray($event))"
></baw-typeahead-input>
</div>

<div
*ngIf="model?.identifiedEvents?.length > 0"
class="identified-annotations mt-4"
>
<h4>Identified Events</h4>

<div>
<table class="table">
<thead>
<tr>
<th class="col">Audio Recording</th>
<th>Tags</th>
</tr>
</thead>

<tbody>
<tr *ngFor="let audioEvent of model?.identifiedEvents">
<td scope="row">
{{ audioEvent.audioRecordingId }}
</td>
<td>
<baw-inline-list
[items]="audioEvent?.tags"
[itemKey]="'text'"
[emptyTemplate]="noAssociatedTagsTemplate"
></baw-inline-list>
</td>
</tr>
</tbody>
</table>
</div>
</div>

<button
*ngIf="model?.files?.length > 0"
class="remove-import-group-button btn btn-outline-danger mt-3"
[ngbTooltip]="'Remove this import group'"
[disabled]="uploading"
(click)="removeFromImport(model)"
>
Remove
</button>
</section>
</ng-template>

<ng-template #tagsTypeaheadTemplate let-result="result" let-searchTerm="term">
<ngb-highlight [result]="result.text" [term]="searchTerm"></ngb-highlight>
</ng-template>

<ng-template #noAssociatedTagsTemplate>
<span>No associated tags</span>
</ng-template>

<ng-template #noAdditionalTagsTemplate>
<span>No additional tags</span>
</ng-template>

<ng-template #defaultDescription>
<i>No description found</i>
</ng-template>

<ng-template #noFilesTemplate>
<i>No files found</i>
</ng-template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
section {
margin-top: 2rem;
}
Loading

0 comments on commit 7a1835c

Please sign in to comment.