Commit 94ae747a authored by LAVENIER's avatar LAVENIER
Browse files

Merge branch 'release/1.3.2'

parents 7c4c7122 f865acab
<?xml version='1.0' encoding='utf-8'?>
<widget android-versionCode="10301" id="net.sumaris.app" version="1.3.1" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
<widget android-versionCode="10302" id="net.sumaris.app" version="1.3.2" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
<name>SUMARiS</name>
<description>Halieutic data capture</description>
<author email="contact@e-is.pro" href="http://www.e-is.pro">Environmental Information Systems</author>
......
......@@ -15,7 +15,7 @@ if [[ "_$INSTALL_DIR" == "_" ]]; then
fi
latest_version() {
echo "v1.3.1" #lastest
echo "v1.3.2" #lastest
}
api_release_url() {
......
{
"name": "sumaris-app",
"description": "SUMARiS app",
"version": "1.3.1",
"version": "1.3.2",
"author": "contact@e-is.pro",
"license": "AGPL-3.0",
"readmeFilename": "README.md",
......
......@@ -32,16 +32,12 @@ import {InMemoryEntitiesService} from "../../shared/services/memory-entity-servi
export class BatchTreeComponent extends AppTabEditor<Batch, any> implements OnInit, AfterViewInit{
private _gearId: number;
private _defaultTaxonGroups: string[];
enableCatchForm = false;
enableSubBatchesTab = false;
subBatchesService: InMemoryEntitiesService<SubBatch, SubBatchFilter>
saveOptions = {
computeBatchRankOrder: false,
computeBatchIndividualCount: false,
}
// Need when subbatches table not visible
subBatchesService: InMemoryEntitiesService<SubBatch, SubBatchFilter>;
data: Batch;
programSubject = new BehaviorSubject<string>(undefined);
......@@ -81,14 +77,11 @@ export class BatchTreeComponent extends AppTabEditor<Batch, any> implements OnIn
}
@Input() set defaultTaxonGroups(value: string[]) {
if (this._defaultTaxonGroups !== value) {
this._defaultTaxonGroups = value;
if (!this.loading) this.batchGroupsTable.defaultTaxonGroups = value;
}
this.batchGroupsTable.defaultTaxonGroups = value;
}
get defaultTaxonGroups(): string[] {
return this._defaultTaxonGroups;
return this.batchGroupsTable.defaultTaxonGroups;
}
get dirty(): boolean {
......@@ -261,6 +254,8 @@ export class BatchTreeComponent extends AppTabEditor<Batch, any> implements OnIn
/* -- protected method -- */
async setValue(catchBatch: Batch) {
//this.batchGroupsTable.markAsLoading({emitEvent: false});
// Make sure this is catch batch
if (catchBatch && catchBatch.label !== AcquisitionLevelCodes.CATCH_BATCH) {
throw new Error('Catch batch should have label=' + AcquisitionLevelCodes.CATCH_BATCH)
......@@ -275,7 +270,7 @@ export class BatchTreeComponent extends AppTabEditor<Batch, any> implements OnIn
// Set catch batch
this.catchBatchForm.gearId = this._gearId;
this.catchBatchForm.value = catchBatch.clone();
this.catchBatchForm.value = catchBatch.clone({withChildren: false});
if (this.batchGroupsTable) {
// Retrieve batch group (make sure label start with acquisition level)
......@@ -346,8 +341,8 @@ export class BatchTreeComponent extends AppTabEditor<Batch, any> implements OnIn
}
autoFill(): Promise<void> {
return this.batchGroupsTable.autoFillTable();
autoFill(opts?: {defaultTaxonGroups?: string[]; }): Promise<void> {
return this.batchGroupsTable.autoFillTable(opts);
}
setSelectedTabIndex(value: number, opts?: {emitEvent?: boolean; realignInkBar?: boolean; }) {
......
......@@ -8,12 +8,12 @@ import {FormArray, FormBuilder, FormGroup, Validators} from "@angular/forms";
import {ProgramService} from "../../../referential/services/program.service";
import {ReferentialRefService} from "../../../referential/services/referential-ref.service";
import {EntityUtils} from "../../../core/services/model/entity.model";
import {referentialToString, ReferentialUtils} from "../../../core/services/model/referential.model";
import {IReferentialRef, referentialToString, ReferentialUtils} from "../../../core/services/model/referential.model";
import {UsageMode} from "../../../core/services/model/settings.model";
import {debounceTime, filter, first} from "rxjs/operators";
import {AcquisitionLevelCodes, MethodIds, PmfmLabelPatterns} from "../../../referential/services/model/model.enum";
import {BehaviorSubject, Subscription} from "rxjs";
import {BehaviorSubject, Observable, Subscription} from "rxjs";
import {LocalSettingsService} from "../../../core/services/local-settings.service";
import {AppFormUtils, FormArrayHelper, isNil, isNotNil} from "../../../core/core.module";
import {MeasurementValuesUtils} from "../../services/model/measurement.model";
......@@ -78,6 +78,8 @@ export class BatchForm<T extends Batch<any> = Batch<any>> extends MeasurementVal
@Input() showError = true;
@Input() availableTaxonGroups: IReferentialRef[] | Observable<IReferentialRef[]>;
@Input() mapPmfmFn: (pmfms: PmfmStrategy[]) => PmfmStrategy[];
$allPmfms = new BehaviorSubject<PmfmStrategy[]>(null);
......@@ -181,11 +183,21 @@ export class BatchForm<T extends Batch<any> = Batch<any>> extends MeasurementVal
this.$initialized.next(true);
// Taxon group combo
this.registerAutocompleteField('taxonGroup', {
suggestFn: (value: any, filter?: any) => this.programService.suggestTaxonGroups(value, {...filter, program: this.program}),
mobile: this.settings.mobile
if (isNotNil(this.availableTaxonGroups)) {
// Set items (useful to speed up the batch group modal)
this.registerAutocompleteField('taxonGroup', {
items: this.availableTaxonGroups,
mobile: this.settings.mobile
});
}
else {
this.registerAutocompleteField('taxonGroup', {
suggestFn: (value: any, filter?: any) => this.programService.suggestTaxonGroups(value, {...filter, program: this.program}),
mobile: this.settings.mobile
});
}
// Taxon name combo
this.updateTaxonNameFilter();
this.registerAutocompleteField('taxonName', {
......@@ -405,8 +417,7 @@ export class BatchForm<T extends Batch<any> = Batch<any>> extends MeasurementVal
};
}
else {
this.taxonNameFilter =
{
this.taxonNameFilter = {
program: this.program,
taxonGroupId: opts && opts.taxonGroup && opts.taxonGroup.id
};
......
......@@ -30,6 +30,7 @@
[program]="program"
[showTaxonGroup]="showTaxonGroup"
[showTaxonName]="showTaxonName"
[availableTaxonGroups]="availableTaxonGroups"
[taxonGroupsNoWeight]="taxonGroupsNoWeight"
(onSubmit)="close($event)">
......
......@@ -10,9 +10,9 @@ import {
} from "@angular/core";
import {Batch, BatchUtils} from "../../services/model/batch.model";
import {LocalSettingsService} from "../../../core/services/local-settings.service";
import {AppFormUtils, isNil} from "../../../core/core.module";
import {AppFormUtils, IReferentialRef, isNil} from "../../../core/core.module";
import {AlertController, ModalController} from "@ionic/angular";
import {BehaviorSubject, Subscription} from "rxjs";
import {BehaviorSubject, Observable, Subscription} from "rxjs";
import {TranslateService} from "@ngx-translate/core";
import {AcquisitionLevelCodes} from "../../../referential/services/model/model.enum";
import {PmfmStrategy} from "../../../referential/services/model/pmfm-strategy.model";
......@@ -54,6 +54,8 @@ export class BatchGroupModal implements OnInit, OnDestroy {
@Input() taxonGroupsNoWeight: string[];
@Input() availableTaxonGroups: IReferentialRef[] | Observable<IReferentialRef[]>;
@Input() qvPmfm: PmfmStrategy;
@Input()
......@@ -85,6 +87,20 @@ export class BatchGroupModal implements OnInit, OnDestroy {
return !this.disabled;
}
enable(opts?: {
onlySelf?: boolean;
emitEvent?: boolean;
}) {
this.form.enable(opts);
}
disable(opts?: {
onlySelf?: boolean;
emitEvent?: boolean;
}) {
this.form.disable(opts);
}
constructor(
protected injector: Injector,
protected alertCtrl: AlertController,
......@@ -111,10 +127,10 @@ export class BatchGroupModal implements OnInit, OnDestroy {
this.disabled = toBoolean(this.disabled, false);
if (this.disabled) {
this.form.disable();
this.disable();
}
else {
this.form.enable();
this.enable();
}
// Update title each time value changes
......@@ -164,7 +180,7 @@ export class BatchGroupModal implements OnInit, OnDestroy {
this.loading = true;
// Force enable form, before use value
if (!this.enabled) this.form.enable({emitEvent: false});
if (!this.enabled) this.enable({emitEvent: false});
// Wait end of async validation
await AppFormUtils.waitWhilePending(this.form);
......
......@@ -26,12 +26,13 @@ import {BatchGroupModal} from "../modal/batch-group.modal";
import {FormFieldDefinition} from "../../../shared/form/field.model";
import {firstFalsePromise} from "../../../shared/observables";
import {BatchGroup} from "../../services/model/batch-group.model";
import {ReferentialUtils} from "../../../core/services/model/referential.model";
import {IReferentialRef, ReferentialUtils} from "../../../core/services/model/referential.model";
import {PlatformService} from "../../../core/services/platform.service";
import {SubBatch} from "../../services/model/subbatch.model";
import {of, Subject} from "rxjs";
import {Observable, of, Subject} from "rxjs";
import {map, takeUntil} from "rxjs/operators";
import {SubBatchesModal} from "../modal/sub-batches.modal";
import {TaxonGroupRef} from "../../../referential/services/model/taxon.model";
const DEFAULT_USER_COLUMNS = ["weight", "individualCount"];
......@@ -105,12 +106,14 @@ export class BatchGroupsTable extends BatchesTable<BatchGroup> {
estimatedWeightPmfm: PmfmStrategy;
dynamicColumns: ColumnDefinition[];
@Input() availableTaxonGroups: IReferentialRef[] | Observable<IReferentialRef[]>;
@Input() set defaultTaxonGroups(value: string[]) {
// If empty, replace with undefined (need by autoFill button - see template)
value = isNotEmptyArray(value) ? value : undefined;
if (this._defaultTaxonGroups !== value) {
this._defaultTaxonGroups = value;
this.loadAvailableTaxonGroups();
this.markForCheck();
}
}
......@@ -248,22 +251,28 @@ export class BatchGroupsTable extends BatchesTable<BatchGroup> {
}
/**
* Allow to fill table (e.g. with taxon groups found in strategies) - #176
* @params opts.includeTaxonGroups : include taxon label
*/
async autoFillTable(opts?: {defaultTaxonGroups?: string[]; }) {
// Wait table is ready
if (this.loading || !this.program) {
await this.onReady();
// Wait table loaded
if (this.loading) {
await firstFalsePromise(this.loadingSubject);
}
if (this.disabled || !this.confirmEditCreate()) return; // Skip when disabled or still editing a row
// Skip when disabled or still editing a row
if (this.disabled || !this.confirmEditCreate()) {
console.warn("[batch-group-table] Skipping autofill, as table is disabled or still editing a row");
return;
}
this.markAsLoading();
try {
const defaultTaxonGroups = opts && opts.defaultTaxonGroups || this._defaultTaxonGroups || null;
console.debug("[batch-group-table] Auto fill table, using options:", opts);
// Read existing taxonGroup
......@@ -271,14 +280,9 @@ export class BatchGroupsTable extends BatchesTable<BatchGroup> {
.map(batch => batch.taxonGroup)
.filter(isNotNil);
const sortAttributes = this.autocompleteFields.taxonGroup && this.autocompleteFields.taxonGroup.attributes || ['label', 'name'];
const taxonGroups = (await this.programService.loadTaxonGroups(this.program) || [])
// Filter on expected labels (as prefix)
.filter(taxonGroup => !defaultTaxonGroups || taxonGroup.label && defaultTaxonGroups.findIndex(label => taxonGroup.label.startsWith(label)) !== -1)
const taxonGroups = (await this.loadAvailableTaxonGroups(opts))
// Exclude species that already exists in table
.filter(taxonGroup => !rowsTaxonGroups.find(tg => ReferentialUtils.equals(tg, taxonGroup)))
// Sort using order configure in the taxon group column
.sort(propertiesPathComparator(sortAttributes));
.filter(taxonGroup => !rowsTaxonGroups.find(tg => ReferentialUtils.equals(tg, taxonGroup)));
for (const taxonGroup of taxonGroups) {
const batch = new BatchGroup();
......@@ -743,6 +747,7 @@ export class BatchGroupsTable extends BatchesTable<BatchGroup> {
qvPmfm: this.qvPmfm,
showTaxonGroup: this.showTaxonGroupColumn,
showTaxonName: this.showTaxonNameColumn,
availableTaxonGroups: this.availableTaxonGroups,
taxonGroupsNoWeight: this.taxonGroupsNoWeight,
showSubBatchesCallback: (parent, valid) => this.onOpenSubBatchesFromModal(parent, valid)
},
......@@ -849,5 +854,21 @@ export class BatchGroupsTable extends BatchesTable<BatchGroup> {
row.currentData = parent;
}
}
async loadAvailableTaxonGroups(opts?: {defaultTaxonGroups?: string[]}): Promise<TaxonGroupRef[]> {
const defaultTaxonGroups = opts && opts.defaultTaxonGroups || this._defaultTaxonGroups || null;
console.debug("[batch-group-table] Loading available taxon groups, using options:", opts);
const sortAttributes = this.autocompleteFields.taxonGroup && this.autocompleteFields.taxonGroup.attributes || ['label', 'name'];
const taxonGroups = ((await this.programService.loadTaxonGroups(this.program)) || [])
// Filter on expected labels (as prefix)
.filter(taxonGroup => !defaultTaxonGroups || taxonGroup.label && defaultTaxonGroups.findIndex(label => taxonGroup.label.startsWith(label)) !== -1)
// Sort using order configure in the taxon group column
.sort(propertiesPathComparator(sortAttributes));
this.availableTaxonGroups = isNotEmptyArray(taxonGroups) ? taxonGroups : undefined;
return taxonGroups;
}
}
......@@ -418,7 +418,7 @@ export class OperationPage extends AppEntityEditor<Operation, OperationService>
updateView(data: Operation | null, opts?: { emitEvent?: boolean; openTabIndex?: number; updateRoute?: boolean }) {
super.updateView(data, opts);
if (this.isNewData && this.showBatchTables && this.batchTree.defaultTaxonGroups) {
if (this.isNewData && this.showBatchTables && isNotEmptyArray(this.batchTree.defaultTaxonGroups)) {
this.batchTree.autoFill();
}
}
......@@ -648,10 +648,13 @@ export class OperationPage extends AppEntityEditor<Operation, OperationService>
await this.batchTree.save()
// TODO check this
// Get batch tree,rom the batch tree component
data.catchBatch = this.batchTree.value;
BatchUtils.logTree(data.catchBatch);
//data.catchBatch = this.catchBatchForm.value;
// Make sure to clean species groups, if not batch enable
if (!this.showBatchTables) {
data.catchBatch.children = undefined;
}
// save survival tables
if (this.showSampleTables) {
......@@ -672,23 +675,6 @@ export class OperationPage extends AppEntityEditor<Operation, OperationService>
data.samples = undefined;
}
// Save batch sampling tables
if (this.showBatchTables) {
/* await this.batchGroupsTable.save();
let subBatches: Batch[];
if (this.subBatchesTable) {
await this.subBatchesTable.save();
subBatches = this.subBatchesTable.value;
} else {
subBatches = this.tempSubBatches;
}
data.catchBatch.children = BatchUtils.prepareRootBatchesForSaving(this.batchGroupsTable.value, subBatches, this.batchGroupsTable.qvPmfm);
BatchUtils.logTree(data.catchBatch);
} else {
data.catchBatch.children = undefined;*/
}
return data;
}
......@@ -721,7 +707,7 @@ export class OperationPage extends AppEntityEditor<Operation, OperationService>
}
if (this.debug) console.debug("[operation] Check if can auto fill species...");
let includeTaxonGroups: string[];
let defaultTaxonGroups: string[];
// Retrieve the trip measurements on SELF_SAMPLING_PROGRAM, if any
const qvMeasurement = (this.trip.measurements || []).find(m => m.pmfmId === PmfmIds.SELF_SAMPLING_PROGRAM);
......@@ -734,7 +720,7 @@ export class OperationPage extends AppEntityEditor<Operation, OperationService>
// Transform QV.label has a list of TaxonGroup.label
if (qualitativeValue && qualitativeValue.label) {
includeTaxonGroups = qualitativeValue.label
defaultTaxonGroups = qualitativeValue.label
.split(/[^\w]+/) // Split by separator (= not a word)
.filter(isNotNilOrBlank)
.map(label => label.trim().toUpperCase());
......@@ -742,11 +728,11 @@ export class OperationPage extends AppEntityEditor<Operation, OperationService>
}
// Set table's default taxon groups
this.batchTree.defaultTaxonGroups = includeTaxonGroups;
this.batchTree.defaultTaxonGroups = defaultTaxonGroups;
// If new data, auto fill the table
if (this.isNewData && !this.loading) {
await this.batchTree.autoFill();
await this.batchTree.autoFill({defaultTaxonGroups});
}
}
......@@ -759,35 +745,6 @@ export class OperationPage extends AppEntityEditor<Operation, OperationService>
});
}
protected loadLastOperations(tripId: number): Observable<Operation[]> {
if (this._lastOperationsSubscription) {
this._lastOperationsSubscription.unsubscribe();
this.unregisterSubscription(this._lastOperationsSubscription);
}
//let resortComparator: (a: Operation, b: Operation) => number;
// Load last trip's operations
this._lastOperationsSubscription = this.dataService.watchAll(0, 5, 'startDateTime', 'desc', {
tripId
}, {
withBatchTree: false,
withSamples: false,
computeRankOrder: false,
fetchPolicy: 'cache-first'
}).pipe(
// Ignore event due to save action
filter(res => this.mobile || !this.saving),
debounceTime(500),
map(res => res && res.data || [])
).subscribe(data => this.$lastOperations.next(data));
this.registerSubscription(this._lastOperationsSubscription);
return this.$lastOperations;
}
protected markForCheck() {
this.cd.markForCheck();
}
......
......@@ -46,7 +46,7 @@ export class BatchGroup extends Batch<BatchGroup> {
export class BatchGroupUtils {
static fromBatchTree(catchBatch: Batch) {
static fromBatchTree(catchBatch: Batch): BatchGroup[] {
// Retrieve batch group (make sure label start with acquisition level)
// Then convert into batch group entities
......
......@@ -148,9 +148,9 @@ export class Batch<T extends Batch<any> = Batch<any>,
this.weight = null;
}
clone(): T {
clone(opts?: O & F): T {
const target = new Batch();
target.fromObject(this.asObject());
target.fromObject(this.asObject(opts), opts);
return target as T;
}
......
......@@ -2,7 +2,7 @@
"name": "ADAP",
"short_name": "ADAP",
"manifest_version": 1,
"version": "1.3.1",
"version": "1.3.2",
"default_locale": "fr",
"description": "ADAP App.",
"icons": [{
......
......@@ -578,13 +578,14 @@ mat-table {
bottom: 9px !important;
}
app-form-field {
mat-form-field,
mat-autocomplete-field,
app-form-field,
.mat-form-field {
width: 100%;
}
.mat-form-field {
width: 100%;
.mat-form-field-label {
width: 100%;
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment