@flumens/models 0.5.3 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1,687 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("tslib"),t=require("mobx"),r=require("axios"),s=require("lodash"),n=require("@flumens/utils/dist/errors"),a=require("../Model.js"),i=require("./Media.js"),o=require("./Occurrence.js"),u=require("./helpers.js");function c(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var d=c(r);function l(e,t){var r,s,n,a;if(null==t?void 0:t.values)e.id=t.values.id,e.samples&&t.samples&&(null===(s=e.samples)||void 0===s||s.forEach((function(e,r){return l(e,t.samples[r])}))),e.occurrences&&t.occurrences&&(null===(n=e.occurrences)||void 0===n||n.forEach((function(e,r){return l(e,t.occurrences[r])}))),e.media&&t.media&&(null===(a=e.media)||void 0===a||a.forEach((function(e,r){return l(e,t.media[r])})));else{if("test"===(null===(r=null===process||void 0===process?void 0:process.env)||void 0===r?void 0:r.NODE_ENV))return;console.warn("Model didn't receive an id from the server")}}var h=function(t){return e.__awaiter(void 0,void 0,void 0,(function(){var r,s,n;return e.__generator(this,(function(a){switch(a.label){case 0:return r=[],t.media?(s=function(t){return e.__awaiter(void 0,void 0,void 0,(function(){var r;return e.__generator(this,(function(e){switch(e.label){case 0:return[4,t.getFormData()];case 1:return r=e.sent(),[2,[t,r]]}}))}))},[4,Promise.all(t.media.map(s))]):[3,2];case 1:n=a.sent(),r.push.apply(r,n),a.label=2;case 2:return t.occurrences?[4,Promise.all(t.occurrences.map(h))]:[3,4];case 3:n=a.sent(),r.push.apply(r,n.flat()),a.label=4;case 4:return t.samples?[4,Promise.all(t.samples.map(h))]:[3,6];case 5:n=a.sent(),r.push.apply(r,n.flat()),a.label=6;case 6:return[2,r]}}))}))};function m(e,t){var r={values:{id:t||-1}};return e.occurrences&&(r.occurrences=e.occurrences.map((function(e){return m(e)}))),e.samples&&(r.samples=e.samples.map((function(e){return m(e)}))),e.media&&(r.media=e.media.map((function(e){return m(e)}))),r}function p(e){try{console.warn(JSON.stringify(e).substring(0,1e5))}catch(e){}}var f=function(r){function a(s){var n=this;void 0===s&&(s={});var i=s.metadata,o=void 0===i?{}:i,c=s.samples,d=void 0===c?[]:c,l=s.occurrences,h=void 0===l?[]:l,m=s.media,p=void 0===m?[]:m,f=s.attrs,v=void 0===f?{}:f,_=e.__rest(s,["metadata","samples","occurrences","media","attrs"]);(n=r.call(this,e.__assign(e.__assign({},_),{attrs:e.__assign({},v)}))||this).remote=t.observable({synchronising:!1,url:null,headers:{},timeout:12e4}),n.validateRemote=u.validateRemoteModel,n.error=t.observable({message:""}),n.debouncedValue=300,n.keys=function(){return e.__assign(e.__assign({},a.keys),function(t){return Object.keys(t).reduce((function(r,s){var n;return e.__assign(e.__assign({},r),((n={})[s]=t[s].remote||t[s],n))}),{})}(n.getSurvey().attrs||{}))},n.requiresRemoteSync=function(){return!n.syncedAt||n.updatedAt>n.syncedAt},n.metadata=t.observable(o),n.samples=t.observable(d),n.occurrences=t.observable(h),n.media=t.observable(p);var y=function(e){return e.parent=n};n.samples.forEach(y),n.occurrences.forEach(y),n.media.forEach(y);var g=function(e){var t;return(null===(t=e.added)||void 0===t?void 0:t.length)?(e.added.forEach(y),n.setupdatedAtTimestamp(Date.now())):e.removedCount&&n.setupdatedAtTimestamp(Date.now()),e};return t.intercept(n.samples,g),t.intercept(n.occurrences,g),t.intercept(n.media,g),n}return e.__extends(a,r),a.fromJSON=function(t,r,s,n){var a,u,c,d,l,h,m;void 0===r&&(r=o.default),void 0===s&&(s=this),void 0===n&&(n=i.default);var p=(null===(a=null==t?void 0:t.attrs)||void 0===a?void 0:a.attrs)?null==t?void 0:t.attrs:t;return new this(e.__assign(e.__assign({},t),{attrs:p.attrs,metadata:p.metadata,createdAt:t.createdAt||(null===(u=t.metadata)||void 0===u?void 0:u.createdOn),updatedAt:t.updatedAt||(null===(c=t.metadata)||void 0===c?void 0:c.updatedOn),syncedAt:t.syncedAt||(null===(d=t.metadata)||void 0===d?void 0:d.syncedOn),samples:null===(l=p.samples)||void 0===l?void 0:l.map((function(e){return s.fromJSON(e,r,s,n)})),occurrences:null===(h=p.occurrences)||void 0===h?void 0:h.map((function(e){return r.fromJSON(e,n)})),media:null===(m=p.media)||void 0===m?void 0:m.map((function(e){return n.fromJSON(e)}))}))},a.prototype.setupdatedAtTimestamp=function(e){var t;r.prototype.setUpdatedAtTimestamp.call(this,e),null===(t=this.parent)||void 0===t||t.setupdatedAtTimestamp(e)},a.prototype.toJSON=function(){var s,n,a,i,o=r.prototype.toJSON.call(this);return this.parent?JSON.parse(JSON.stringify(e.__assign(e.__assign({},o),{metadata:t.toJS(this.metadata)||{},occurrences:this.occurrences.map((function(e){return e.toJSON()}))||[],samples:(null===(a=this.samples)||void 0===a?void 0:a.map((function(e){return e.toJSON()})))||[],media:(null===(i=this.media)||void 0===i?void 0:i.map((function(e){return e.toJSON()})))||[]}))):JSON.parse(JSON.stringify(e.__assign(e.__assign({},o),{attrs:{attrs:o.attrs,metadata:t.toJS(this.metadata)||{},occurrences:this.occurrences.map((function(e){return e.toJSON()}))||[],samples:(null===(s=this.samples)||void 0===s?void 0:s.map((function(e){return e.toJSON()})))||[],media:(null===(n=this.media)||void 0===n?void 0:n.map((function(e){return e.toJSON()})))||[]}})))},a.prototype.getSurvey=function(){return this.survey?this.parent?this.parent.getSurvey().smp||{}:this.survey:{}},a.prototype.save=function(){return e.__awaiter(this,void 0,void 0,(function(){var t;return e.__generator(this,(function(r){switch(r.label){case 0:if(this.attrs.deleted)return[2];if(this.parent)return this.parent.save(),[2];if(!this.store)throw new Error("Trying to sync locally without a store");return t=this.toJSON(),[4,this.store.save(e.__assign(e.__assign({},t),{data:t.attrs}))];case 1:return r.sent(),[2]}}))}))},a.prototype.destroy=function(t){var r;return e.__awaiter(this,void 0,void 0,(function(){var s,n=this;return e.__generator(this,(function(e){switch(e.label){case 0:return s=function(){return Promise.all([Promise.all(n.media.map((function(e){return e.destroy(!0)}))),Promise.all(n.occurrences.map((function(e){return e.destroy(!0)})))])},this.parent?(this.parent.samples.remove(this),[4,s()]):[3,2];case 1:return e.sent(),t?[2]:(this.parent.save(),[2]);case 2:if(!this.store)throw new Error("Trying to sync locally without a store");return[4,this.store.delete(this.cid)];case 3:return e.sent(),null===(r=this.collection)||void 0===r||r.remove(this),[4,s()];case 4:return e.sent(),this.attrs.deleted=!0,[2]}}))}))},a.prototype.sync=function(){return e.__awaiter(this,void 0,void 0,(function(){return e.__generator(this,(function(e){return[2,this.parent?this.parent.sync():r.prototype.sync.call(this)]}))}))},a.prototype.saveRemote=function(){return e.__awaiter(this,void 0,void 0,(function(){var t,r,s,n,a;return e.__generator(this,(function(e){switch(e.label){case 0:if(!this.remote.url)throw new Error('A "remote" property is not configured.');e.label=1;case 1:return e.trys.push([1,4,,5]),this.remote.synchronising=!0,[4,this.uploadMedia()];case 2:return t=e.sent(),r=this.getSubmission(t),[4,this.postRemote(r)];case 3:return s=e.sent(),this.remote.synchronising=!1,l(this,s),n=(new Date).getTime(),this.updatedAt=n,this.syncedAt=n,this.save(),[3,5];case 4:throw a=e.sent(),this.remote.synchronising=!1,a;case 5:return[2]}}))}))},a.prototype.postRemote=function(t){var r,s,a;return e.__awaiter(this,void 0,void 0,(function(){var i,o,c,l,h;return e.__generator(this,(function(e){switch(e.label){case 0:return i=this.remote.url,"function"!=typeof this.remote.headers?[3,2]:[4,this.remote.headers()];case 1:return c=e.sent(),[3,3];case 2:c=this.remote.headers,e.label=3;case 3:o=c,e.label=4;case 4:return e.trys.push([4,6,,7]),[4,d.default.post("".concat(i,"/samples"),t,{headers:o,timeout:12e4})];case 5:return[2,e.sent().data];case 6:if(l=e.sent(),409===(h=l).status&&(null===(r=h.response)||void 0===r?void 0:r.data.duplicate_of))return[2,m(this,h.response.data.duplicate_of.id)];if(n.isAxiosNetworkError(h))throw new n.HandledError("Request aborted because of a network issue (timeout or similar).");if(400===h.status)throw p(t),new Error(u.getErrorMessageFromObject(null===(a=null===(s=h.response)||void 0===s?void 0:s.data)||void 0===a?void 0:a.message));throw new Error(h.message);case 7:return[2]}}))}))},a.prototype.fetchRemote=function(){return e.__awaiter(this,void 0,void 0,(function(){return e.__generator(this,(function(e){throw new Error("Not implemented.")}))}))},a.prototype.updateRemote=function(){return e.__awaiter(this,void 0,void 0,(function(){var t,r,s,n;return e.__generator(this,(function(e){switch(e.label){case 0:if(!this.remote.url)throw new Error('A "remote" property is not configured.');e.label=1;case 1:return e.trys.push([1,3,,4]),this.remote.synchronising=!0,t={},r=this.getSubmission(t),[4,this.putRemote(this.id,r)];case 2:return l(this,e.sent()),s=(new Date).getTime(),this.updatedAt=s,this.syncedAt=s,this.save(),this.remote.synchronising=!1,[3,4];case 3:throw n=e.sent(),this.remote.synchronising=!1,n;case 4:return[2]}}))}))},a.prototype.putRemote=function(t,r){var s,a;return e.__awaiter(this,void 0,void 0,(function(){var i,o,c,l,h;return e.__generator(this,(function(e){switch(e.label){case 0:return i=this.remote.url,"function"!=typeof this.remote.headers?[3,2]:[4,this.remote.headers()];case 1:return c=e.sent(),[3,3];case 2:c=this.remote.headers,e.label=3;case 3:o=c,e.label=4;case 4:return e.trys.push([4,6,,7]),[4,d.default.put("".concat(i,"/samples/").concat(t),r,{headers:o,timeout:12e4})];case 5:return[2,e.sent().data];case 6:if(l=e.sent(),h=l,n.isAxiosNetworkError(h))throw new n.HandledError("Request aborted because of a network issue (timeout or similar).");if(400===h.status)throw p(r),new Error(u.getErrorMessageFromObject(null===(a=null===(s=h.response)||void 0===s?void 0:s.data)||void 0===a?void 0:a.message));throw new Error(h.message);case 7:return[2]}}))}))},a.prototype.uploadMedia=function(){return e.__awaiter(this,void 0,void 0,(function(){var t,r,s,a,i,o,u,c,l=this;return e.__generator(this,(function(m){switch(m.label){case 0:return t={},[4,h(this)];case 1:if(r=m.sent(),!r.length)return[2,t];s=function(t){return e.__awaiter(l,void 0,void 0,(function(){var r,s,a,i;return e.__generator(this,(function(e){switch(e.label){case 0:return r=this.remote.url,"function"!=typeof this.remote.headers?[3,2]:[4,this.remote.headers()];case 1:return a=e.sent(),[3,3];case 2:a=this.remote.headers,e.label=3;case 3:s=a,e.label=4;case 4:return e.trys.push([4,6,,7]),[4,d.default.post("".concat(r,"/media-queue"),t,{headers:s,timeout:12e4})];case 5:return[2,e.sent().data];case 6:if("timeout"===(i=e.sent()).message)throw new n.HandledError("Request aborted because of a network issue (timeout or similar).");throw i;case 7:return[2]}}))}))},a=[],i=Date.now()-432e5,r.forEach((function(e){var r=e[0];r.syncedAt>i&&r.attrs.queued?t[r.cid]={name:r.attrs.queued}:a.push(e)})),o=5,u=function(r){var n,i,u,c;return e.__generator(this,(function(d){switch(d.label){case 0:return n=a.slice(r,r+o),i=new FormData,n.forEach((function(e){var t=e[1];return i.append.apply(i,t)})),[4,s(i)];case 1:return u=d.sent(),c=(new Date).getTime(),n.forEach((function(e){var t=e[0];t.syncedAt=c,t.attrs.queued=u[t.cid].name})),t=e.__assign(e.__assign({},t),u),[2]}}))},c=0,m.label=2;case 2:return c<a.length?[5,u(c)]:[3,5];case 3:m.sent(),m.label=4;case 4:return c+=o,[3,2];case 5:return[2,t]}}))}))},a.prototype.getSubmission=function(t){var r=this;void 0===t&&(t={});var n="function"==typeof this.keys?this.keys():this.keys,i=e.__assign(e.__assign({},a.keys),n),o={values:{external_key:this.cid},media:[],samples:[],occurrences:[]};this.id&&(o.values.id=this.id);var u=function(e,t){var s=i[e].values;if(!s)return t;if("function"==typeof s)return s(t,o,r);if(t instanceof Array)return t.map((function(t){return u(e,t)}));if(s instanceof Array){var n=s.find((function(e){return e.value===t}));if(!n||!n.id)throw new Error('A "'.concat(e,'" attribute "').concat(t,'" value could not be mapped to a remote database field.'));return n.id}return s[t]};Object.keys(this.attrs).forEach((function(e){var t=r.attrs[e],n=function(e){return null==e};if(!n(t)){if(!i[e])return e=e.includes("smpAttr:")?e:s.snakeCase(e),void(o.values[e]=t);if(!n(t=u(e,t))){var a=i[e].id||e,c=Number.isNaN(Number(a))?a:"smpAttr:".concat(a);c=c.includes("smpAttr:")?c:s.snakeCase(c),o.values[c]=t}}})),this.samples.forEach((function(e){r.attrs.training&&(e.attrs.training=r.attrs.training);var s=e.getSubmission(t);s&&o.samples.push(s)})),this.occurrences.forEach((function(e){r.attrs.training&&(e.attrs.training=r.attrs.training);var s=e.getSubmission(t);s&&o.occurrences.push(s)})),this.media.forEach((function(e){var r=e.getSubmission(t);r&&o.media.push(r)}));var c=this.getSurvey();return c.modifySubmission?c.modifySubmission(o,this):o},a.prototype.isUploaded=function(){return this.parent?this.parent.isUploaded():!!this.syncedAt},a.prototype.isDisabled=function(){return this.isUploaded()},a.keys={location:{id:"entered_sref"},location_type:{id:"entered_sref_system",values:{british:"OSGB",irish:"OSIE",channel:"utm30ed50",latlon:4326}},group:{id:"group_id"},training:{id:"training",values:u.boolToWarehouseValue},deleted:{id:"deleted",values:u.boolToWarehouseValue}},a}(a.default);exports.default=f,exports.getMediaFormData=h;
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var tslib = require('tslib');
6
+ var mobx = require('mobx');
7
+ var axios = require('axios');
8
+ var lodash = require('lodash');
9
+ var utils = require('@flumens/utils');
10
+ var Model = require('../Model.js');
11
+ var Media = require('./Media.js');
12
+ var Occurrence = require('./Occurrence.js');
13
+ var helpers = require('./helpers.js');
14
+
15
+ function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
16
+
17
+ var axios__default = /*#__PURE__*/_interopDefaultLegacy(axios);
18
+
19
+ function setNewRemoteID(model, responseData) {
20
+ var _a, _b, _c, _d;
21
+ if (!(responseData === null || responseData === void 0 ? void 0 : responseData.values)) {
22
+ if (((_a = process === null || process === void 0 ? void 0 : process.env) === null || _a === void 0 ? void 0 : _a.NODE_ENV) === 'test')
23
+ return;
24
+ console.warn("Model didn't receive an id from the server");
25
+ return;
26
+ }
27
+ // eslint-disable-next-line no-param-reassign
28
+ model.id = responseData.values.id;
29
+ // do that for all submodels
30
+ if (model.samples && responseData.samples) {
31
+ (_b = model.samples) === null || _b === void 0 ? void 0 : _b.forEach(function (subModel, index) {
32
+ return setNewRemoteID(subModel, responseData.samples[index]);
33
+ });
34
+ }
35
+ if (model.occurrences && responseData.occurrences) {
36
+ (_c = model.occurrences) === null || _c === void 0 ? void 0 : _c.forEach(function (subModel, index) {
37
+ return setNewRemoteID(subModel, responseData.occurrences[index]);
38
+ });
39
+ }
40
+ if (model.media && responseData.media) {
41
+ (_d = model.media) === null || _d === void 0 ? void 0 : _d.forEach(function (subModel, index) {
42
+ return setNewRemoteID(subModel, responseData.media[index]);
43
+ });
44
+ }
45
+ }
46
+ var getMediaFormData = function (model) { return tslib.__awaiter(void 0, void 0, void 0, function () {
47
+ var formDataArgs, getFormData, formData, formData, formData;
48
+ return tslib.__generator(this, function (_a) {
49
+ switch (_a.label) {
50
+ case 0:
51
+ formDataArgs = [];
52
+ if (!model.media) return [3 /*break*/, 2];
53
+ getFormData = function (m) { return tslib.__awaiter(void 0, void 0, void 0, function () {
54
+ var formData;
55
+ return tslib.__generator(this, function (_a) {
56
+ switch (_a.label) {
57
+ case 0: return [4 /*yield*/, m.getFormData()];
58
+ case 1:
59
+ formData = _a.sent();
60
+ return [2 /*return*/, [m, formData]];
61
+ }
62
+ });
63
+ }); };
64
+ return [4 /*yield*/, Promise.all(model.media.map(getFormData))];
65
+ case 1:
66
+ formData = _a.sent();
67
+ formDataArgs.push.apply(formDataArgs, formData);
68
+ _a.label = 2;
69
+ case 2:
70
+ if (!model.occurrences) return [3 /*break*/, 4];
71
+ return [4 /*yield*/, Promise.all(model.occurrences.map(getMediaFormData))];
72
+ case 3:
73
+ formData = _a.sent();
74
+ formDataArgs.push.apply(formDataArgs, formData.flat());
75
+ _a.label = 4;
76
+ case 4:
77
+ if (!model.samples) return [3 /*break*/, 6];
78
+ return [4 /*yield*/, Promise.all(model.samples.map(getMediaFormData))];
79
+ case 5:
80
+ formData = _a.sent();
81
+ formDataArgs.push.apply(formDataArgs, formData.flat());
82
+ _a.label = 6;
83
+ case 6: return [2 /*return*/, formDataArgs];
84
+ }
85
+ });
86
+ }); };
87
+ function handleDuplicates(model, existingId) {
88
+ var fullResponse = {
89
+ values: {
90
+ id: existingId || -1,
91
+ },
92
+ };
93
+ if (model.occurrences) {
94
+ fullResponse.occurrences = model.occurrences.map(function (m) {
95
+ return handleDuplicates(m);
96
+ });
97
+ }
98
+ if (model.samples) {
99
+ fullResponse.samples = model.samples.map(function (m) {
100
+ return handleDuplicates(m);
101
+ });
102
+ }
103
+ if (model.media) {
104
+ fullResponse.media = model.media.map(function (m) {
105
+ return handleDuplicates(m);
106
+ });
107
+ }
108
+ return fullResponse;
109
+ }
110
+ function logTruncatedJSON(payload) {
111
+ try {
112
+ var MAX_PAYLOAD = 200000; // kb. https://develop.sentry.dev/sdk/event-payloads/
113
+ console.warn(JSON.stringify(payload).substring(0, MAX_PAYLOAD / 2));
114
+ }
115
+ catch (e) {
116
+ // do nothing
117
+ }
118
+ }
119
+ var Sample = /** @class */ (function (_super) {
120
+ tslib.__extends(Sample, _super);
121
+ function Sample(_a) {
122
+ if (_a === void 0) { _a = {}; }
123
+ var _this = this;
124
+ var _b = _a.metadata, metadata = _b === void 0 ? {} : _b, _c = _a.samples, samples = _c === void 0 ? [] : _c, _d = _a.occurrences, occurrences = _d === void 0 ? [] : _d, _e = _a.media, media = _e === void 0 ? [] : _e, _f = _a.attrs, attrs = _f === void 0 ? {} : _f, options = tslib.__rest(_a, ["metadata", "samples", "occurrences", "media", "attrs"]);
125
+ _this = _super.call(this, tslib.__assign(tslib.__assign({}, options), { attrs: tslib.__assign({}, attrs) })) || this;
126
+ _this.remote = mobx.observable({
127
+ synchronising: false,
128
+ url: null, // must be set up for remote sync
129
+ headers: {}, // auth and other headers
130
+ timeout: 120000, // 120s
131
+ });
132
+ _this.validateRemote = helpers.validateRemoteModel;
133
+ _this.error = mobx.observable({ message: '' });
134
+ _this.debouncedValue = 300;
135
+ _this.keys = function () {
136
+ var getRemoteProps = function (attrs) {
137
+ var extractRemoteIfExists = function (agg, key) {
138
+ var _a;
139
+ return (tslib.__assign(tslib.__assign({}, agg), (_a = {}, _a[key] = attrs[key].remote || attrs[key], _a)));
140
+ };
141
+ return Object.keys(attrs).reduce(extractRemoteIfExists, {});
142
+ };
143
+ return tslib.__assign(tslib.__assign({}, Sample.keys), getRemoteProps(_this.getSurvey().attrs || {}));
144
+ };
145
+ _this.requiresRemoteSync = function () { return !_this.syncedAt || _this.updatedAt > _this.syncedAt; };
146
+ _this.metadata = mobx.observable(metadata);
147
+ _this.samples = mobx.observable(samples);
148
+ _this.occurrences = mobx.observable(occurrences);
149
+ _this.media = mobx.observable(media);
150
+ // eslint-disable-next-line no-param-reassign, no-return-assign
151
+ var attachParent = function (model) { return (model.parent = _this); };
152
+ _this.samples.forEach(attachParent);
153
+ _this.occurrences.forEach(attachParent);
154
+ _this.media.forEach(attachParent);
155
+ var onAddedSetParentAndUpdateTime = function (change) {
156
+ var _a;
157
+ if ((_a = change.added) === null || _a === void 0 ? void 0 : _a.length) {
158
+ change.added.forEach(attachParent);
159
+ _this.setupdatedAtTimestamp(Date.now());
160
+ }
161
+ else if (change.removedCount) {
162
+ _this.setupdatedAtTimestamp(Date.now());
163
+ }
164
+ return change;
165
+ };
166
+ mobx.intercept(_this.samples, onAddedSetParentAndUpdateTime);
167
+ mobx.intercept(_this.occurrences, onAddedSetParentAndUpdateTime);
168
+ mobx.intercept(_this.media, onAddedSetParentAndUpdateTime);
169
+ return _this;
170
+ }
171
+ Sample.fromJSON = function (jsonProp, OccurrenceClass, Sample, // eslint-disable-line
172
+ MediaClass) {
173
+ var _a, _b, _c, _d, _e, _f, _g;
174
+ if (OccurrenceClass === void 0) { OccurrenceClass = Occurrence["default"]; }
175
+ if (Sample === void 0) { Sample = this; }
176
+ if (MediaClass === void 0) { MediaClass = Media["default"]; }
177
+ var json = ((_a = jsonProp === null || jsonProp === void 0 ? void 0 : jsonProp.attrs) === null || _a === void 0 ? void 0 : _a.attrs) ? jsonProp === null || jsonProp === void 0 ? void 0 : jsonProp.attrs : jsonProp;
178
+ var sample = new this(tslib.__assign(tslib.__assign({}, jsonProp), { attrs: json.attrs, metadata: json.metadata, createdAt: jsonProp.createdAt || ((_b = jsonProp.metadata) === null || _b === void 0 ? void 0 : _b.createdOn), updatedAt: jsonProp.updatedAt || ((_c = jsonProp.metadata) === null || _c === void 0 ? void 0 : _c.updatedOn), syncedAt: jsonProp.syncedAt || ((_d = jsonProp.metadata) === null || _d === void 0 ? void 0 : _d.syncedOn), samples: (_e = json.samples) === null || _e === void 0 ? void 0 : _e.map(function (mJson) {
179
+ return Sample.fromJSON(mJson, OccurrenceClass, Sample, MediaClass);
180
+ }), occurrences: (_f = json.occurrences) === null || _f === void 0 ? void 0 : _f.map(function (mJson) {
181
+ return OccurrenceClass.fromJSON(mJson, MediaClass);
182
+ }), media: (_g = json.media) === null || _g === void 0 ? void 0 : _g.map(function (mJson) { return MediaClass.fromJSON(mJson); }) }));
183
+ return sample;
184
+ };
185
+ Sample.prototype.setupdatedAtTimestamp = function (newUpdatedAt) {
186
+ var _a;
187
+ _super.prototype.setUpdatedAtTimestamp.call(this, newUpdatedAt);
188
+ (_a = this.parent) === null || _a === void 0 ? void 0 : _a.setupdatedAtTimestamp(newUpdatedAt);
189
+ };
190
+ /**
191
+ * Returns a clean (no observables) JSON representation of the model.
192
+ */
193
+ Sample.prototype.toJSON = function () {
194
+ var _a, _b, _c, _d;
195
+ var json = _super.prototype.toJSON.call(this);
196
+ if (!this.parent) {
197
+ return JSON.parse(JSON.stringify(tslib.__assign(tslib.__assign({}, json), { attrs: {
198
+ attrs: json.attrs,
199
+ metadata: mobx.toJS(this.metadata) || {},
200
+ occurrences: this.occurrences.map(function (model) { return model.toJSON(); }) || [],
201
+ samples: ((_a = this.samples) === null || _a === void 0 ? void 0 : _a.map(function (model) { return model.toJSON(); })) || [],
202
+ media: ((_b = this.media) === null || _b === void 0 ? void 0 : _b.map(function (model) { return model.toJSON(); })) || [],
203
+ } })));
204
+ }
205
+ return JSON.parse(JSON.stringify(tslib.__assign(tslib.__assign({}, json), { metadata: mobx.toJS(this.metadata) || {}, occurrences: this.occurrences.map(function (model) { return model.toJSON(); }) || [], samples: ((_c = this.samples) === null || _c === void 0 ? void 0 : _c.map(function (model) { return model.toJSON(); })) || [], media: ((_d = this.media) === null || _d === void 0 ? void 0 : _d.map(function (model) { return model.toJSON(); })) || [] })));
206
+ };
207
+ Sample.prototype.getSurvey = function () {
208
+ if (!this.survey)
209
+ return {};
210
+ if (this.parent)
211
+ return this.parent.getSurvey().smp || {};
212
+ return this.survey;
213
+ };
214
+ /**
215
+ * Save the model to the offline store.
216
+ */
217
+ Sample.prototype.save = function () {
218
+ return tslib.__awaiter(this, void 0, void 0, function () {
219
+ var json;
220
+ return tslib.__generator(this, function (_a) {
221
+ switch (_a.label) {
222
+ case 0:
223
+ if (this.attrs.deleted)
224
+ return [2 /*return*/]; // we don't want to store deleted samples yet
225
+ if (this.parent) {
226
+ this.parent.save();
227
+ return [2 /*return*/];
228
+ }
229
+ if (!this.store) {
230
+ throw new Error('Trying to sync locally without a store');
231
+ }
232
+ json = this.toJSON();
233
+ return [4 /*yield*/, this.store.save(tslib.__assign(tslib.__assign({}, json), { data: json.attrs }))];
234
+ case 1:
235
+ _a.sent();
236
+ return [2 /*return*/];
237
+ }
238
+ });
239
+ });
240
+ };
241
+ /**
242
+ * Destroy the model and remove from the offline store.
243
+ */
244
+ Sample.prototype.destroy = function (silent) {
245
+ return tslib.__awaiter(this, void 0, void 0, function () {
246
+ var destroySubModels;
247
+ var _this = this;
248
+ var _a;
249
+ return tslib.__generator(this, function (_b) {
250
+ switch (_b.label) {
251
+ case 0:
252
+ destroySubModels = function () {
253
+ return Promise.all([
254
+ Promise.all(_this.media.map(function (media) { return media.destroy(true); })),
255
+ Promise.all(_this.occurrences.map(function (occ) { return occ.destroy(true); })),
256
+ ]);
257
+ };
258
+ if (!this.parent) return [3 /*break*/, 2];
259
+ this.parent.samples.remove(this);
260
+ return [4 /*yield*/, destroySubModels()];
261
+ case 1:
262
+ _b.sent();
263
+ if (silent)
264
+ return [2 /*return*/];
265
+ this.parent.save();
266
+ return [2 /*return*/];
267
+ case 2:
268
+ if (!this.store) {
269
+ throw new Error('Trying to sync locally without a store');
270
+ }
271
+ return [4 /*yield*/, this.store.delete(this.cid)];
272
+ case 3:
273
+ _b.sent();
274
+ (_a = this.collection) === null || _a === void 0 ? void 0 : _a.remove(this);
275
+ return [4 /*yield*/, destroySubModels()];
276
+ case 4:
277
+ _b.sent();
278
+ this.attrs.deleted = true;
279
+ return [2 /*return*/];
280
+ }
281
+ });
282
+ });
283
+ };
284
+ Sample.prototype.sync = function () {
285
+ return tslib.__awaiter(this, void 0, void 0, function () {
286
+ return tslib.__generator(this, function (_a) {
287
+ return [2 /*return*/, this.parent ? this.parent.sync() : _super.prototype.sync.call(this)];
288
+ });
289
+ });
290
+ };
291
+ Sample.prototype.saveRemote = function () {
292
+ return tslib.__awaiter(this, void 0, void 0, function () {
293
+ var warehouseMediaNames, submission, remoteResponse, timeNow, e_1;
294
+ return tslib.__generator(this, function (_a) {
295
+ switch (_a.label) {
296
+ case 0:
297
+ if (!this.remote.url) {
298
+ throw new Error('A "remote" property is not configured.');
299
+ }
300
+ _a.label = 1;
301
+ case 1:
302
+ _a.trys.push([1, 4, , 5]);
303
+ this.remote.synchronising = true;
304
+ return [4 /*yield*/, this.uploadMedia()];
305
+ case 2:
306
+ warehouseMediaNames = _a.sent();
307
+ submission = this.getSubmission(warehouseMediaNames);
308
+ return [4 /*yield*/, this.postRemote(submission)];
309
+ case 3:
310
+ remoteResponse = _a.sent();
311
+ this.remote.synchronising = false;
312
+ // update the model and occurrences with new remote IDs
313
+ setNewRemoteID(this, remoteResponse);
314
+ timeNow = new Date().getTime();
315
+ // TODO: use server times
316
+ this.updatedAt = timeNow;
317
+ this.syncedAt = timeNow;
318
+ this.save();
319
+ return [3 /*break*/, 5];
320
+ case 4:
321
+ e_1 = _a.sent();
322
+ this.remote.synchronising = false;
323
+ throw e_1;
324
+ case 5: return [2 /*return*/];
325
+ }
326
+ });
327
+ });
328
+ };
329
+ Sample.prototype.postRemote = function (data) {
330
+ return tslib.__awaiter(this, void 0, void 0, function () {
331
+ var url, headers, _a, res, _1, e;
332
+ var _b, _c, _d;
333
+ return tslib.__generator(this, function (_e) {
334
+ switch (_e.label) {
335
+ case 0:
336
+ url = this.remote.url;
337
+ if (!(typeof this.remote.headers === 'function')) return [3 /*break*/, 2];
338
+ return [4 /*yield*/, this.remote.headers()];
339
+ case 1:
340
+ _a = _e.sent();
341
+ return [3 /*break*/, 3];
342
+ case 2:
343
+ _a = this.remote.headers;
344
+ _e.label = 3;
345
+ case 3:
346
+ headers = _a;
347
+ _e.label = 4;
348
+ case 4:
349
+ _e.trys.push([4, 6, , 7]);
350
+ return [4 /*yield*/, axios__default["default"].post("".concat(url, "/samples"), data, {
351
+ headers: headers,
352
+ timeout: 120000,
353
+ })];
354
+ case 5:
355
+ res = _e.sent();
356
+ return [2 /*return*/, res.data];
357
+ case 6:
358
+ _1 = _e.sent();
359
+ e = _1;
360
+ if (e.status === 409 && ((_b = e.response) === null || _b === void 0 ? void 0 : _b.data.duplicate_of)) {
361
+ // TODO: we're doing updates so must handle sub-model IDs too
362
+ return [2 /*return*/, handleDuplicates(this, e.response.data.duplicate_of.id)];
363
+ }
364
+ if (utils.isAxiosNetworkError(e)) {
365
+ throw new utils.HandledError('Request aborted because of a network issue (timeout or similar).');
366
+ }
367
+ if (e.status === 400) {
368
+ logTruncatedJSON(data);
369
+ throw new Error(helpers.getErrorMessageFromObject((_d = (_c = e.response) === null || _c === void 0 ? void 0 : _c.data) === null || _d === void 0 ? void 0 : _d.message));
370
+ }
371
+ throw new Error(e.message);
372
+ case 7: return [2 /*return*/];
373
+ }
374
+ });
375
+ });
376
+ };
377
+ Sample.prototype.fetchRemote = function () {
378
+ return tslib.__awaiter(this, void 0, void 0, function () {
379
+ return tslib.__generator(this, function (_a) {
380
+ throw new Error('Not implemented.');
381
+ });
382
+ });
383
+ };
384
+ Sample.prototype.updateRemote = function () {
385
+ return tslib.__awaiter(this, void 0, void 0, function () {
386
+ var warehouseMediaNames, submission, remoteResponse, timeNow, e_2;
387
+ return tslib.__generator(this, function (_a) {
388
+ switch (_a.label) {
389
+ case 0:
390
+ if (!this.remote.url) {
391
+ throw new Error('A "remote" property is not configured.');
392
+ }
393
+ _a.label = 1;
394
+ case 1:
395
+ _a.trys.push([1, 3, , 4]);
396
+ this.remote.synchronising = true;
397
+ warehouseMediaNames = {};
398
+ submission = this.getSubmission(warehouseMediaNames);
399
+ return [4 /*yield*/, this.putRemote(this.id, submission)];
400
+ case 2:
401
+ remoteResponse = _a.sent();
402
+ // update the model and occurrences with new remote IDs
403
+ setNewRemoteID(this, remoteResponse);
404
+ timeNow = new Date().getTime();
405
+ // TODO: use server times
406
+ this.updatedAt = timeNow;
407
+ this.syncedAt = timeNow;
408
+ this.save();
409
+ this.remote.synchronising = false;
410
+ return [3 /*break*/, 4];
411
+ case 3:
412
+ e_2 = _a.sent();
413
+ this.remote.synchronising = false;
414
+ throw e_2;
415
+ case 4: return [2 /*return*/];
416
+ }
417
+ });
418
+ });
419
+ };
420
+ Sample.prototype.putRemote = function (sampleId, data) {
421
+ return tslib.__awaiter(this, void 0, void 0, function () {
422
+ var url, headers, _a, res, _2, e;
423
+ var _b, _c;
424
+ return tslib.__generator(this, function (_d) {
425
+ switch (_d.label) {
426
+ case 0:
427
+ url = this.remote.url;
428
+ if (!(typeof this.remote.headers === 'function')) return [3 /*break*/, 2];
429
+ return [4 /*yield*/, this.remote.headers()];
430
+ case 1:
431
+ _a = _d.sent();
432
+ return [3 /*break*/, 3];
433
+ case 2:
434
+ _a = this.remote.headers;
435
+ _d.label = 3;
436
+ case 3:
437
+ headers = _a;
438
+ _d.label = 4;
439
+ case 4:
440
+ _d.trys.push([4, 6, , 7]);
441
+ return [4 /*yield*/, axios__default["default"].put("".concat(url, "/samples/").concat(sampleId), data, {
442
+ headers: headers,
443
+ timeout: 120000,
444
+ })];
445
+ case 5:
446
+ res = _d.sent();
447
+ return [2 /*return*/, res.data];
448
+ case 6:
449
+ _2 = _d.sent();
450
+ e = _2;
451
+ if (utils.isAxiosNetworkError(e)) {
452
+ throw new utils.HandledError('Request aborted because of a network issue (timeout or similar).');
453
+ }
454
+ if (e.status === 400) {
455
+ logTruncatedJSON(data);
456
+ throw new Error(helpers.getErrorMessageFromObject((_c = (_b = e.response) === null || _b === void 0 ? void 0 : _b.data) === null || _c === void 0 ? void 0 : _c.message));
457
+ }
458
+ throw new Error(e.message);
459
+ case 7: return [2 /*return*/];
460
+ }
461
+ });
462
+ });
463
+ };
464
+ Sample.prototype.uploadMedia = function () {
465
+ return tslib.__awaiter(this, void 0, void 0, function () {
466
+ var warehouseMediaNames, media, hasPhotos, upload, mediaNotInRemote, twelveHrsAgo, chunk, _loop_1, index;
467
+ var _this = this;
468
+ return tslib.__generator(this, function (_a) {
469
+ switch (_a.label) {
470
+ case 0:
471
+ warehouseMediaNames = {};
472
+ return [4 /*yield*/, getMediaFormData(this)];
473
+ case 1:
474
+ media = _a.sent();
475
+ hasPhotos = media.length;
476
+ if (!hasPhotos)
477
+ return [2 /*return*/, warehouseMediaNames];
478
+ upload = function (data) { return tslib.__awaiter(_this, void 0, void 0, function () {
479
+ var url, headers, _a, res, e_3;
480
+ return tslib.__generator(this, function (_b) {
481
+ switch (_b.label) {
482
+ case 0:
483
+ url = this.remote.url;
484
+ if (!(typeof this.remote.headers === 'function')) return [3 /*break*/, 2];
485
+ return [4 /*yield*/, this.remote.headers()];
486
+ case 1:
487
+ _a = _b.sent();
488
+ return [3 /*break*/, 3];
489
+ case 2:
490
+ _a = this.remote.headers;
491
+ _b.label = 3;
492
+ case 3:
493
+ headers = _a;
494
+ _b.label = 4;
495
+ case 4:
496
+ _b.trys.push([4, 6, , 7]);
497
+ return [4 /*yield*/, axios__default["default"].post("".concat(url, "/media-queue"), data, {
498
+ headers: headers,
499
+ timeout: 120000,
500
+ })];
501
+ case 5:
502
+ res = _b.sent();
503
+ return [2 /*return*/, res.data];
504
+ case 6:
505
+ e_3 = _b.sent();
506
+ if (e_3.message === 'timeout') {
507
+ throw new utils.HandledError('Request aborted because of a network issue (timeout or similar).');
508
+ }
509
+ throw e_3;
510
+ case 7: return [2 /*return*/];
511
+ }
512
+ });
513
+ }); };
514
+ mediaNotInRemote = [];
515
+ twelveHrsAgo = Date.now() - 1000 * 60 * 60 * 12;
516
+ media.forEach(function (m) {
517
+ var model = m[0];
518
+ if (model.syncedAt > twelveHrsAgo && model.attrs.queued) {
519
+ warehouseMediaNames[model.cid] = { name: model.attrs.queued };
520
+ return;
521
+ }
522
+ mediaNotInRemote.push(m);
523
+ });
524
+ chunk = 5;
525
+ _loop_1 = function (index) {
526
+ var mediaChunkToUpload, data, ids, now;
527
+ return tslib.__generator(this, function (_b) {
528
+ switch (_b.label) {
529
+ case 0:
530
+ mediaChunkToUpload = mediaNotInRemote.slice(index, index + chunk);
531
+ data = new FormData();
532
+ mediaChunkToUpload.forEach(function (_a) {
533
+ var formData = _a[1];
534
+ return data.append.apply(data, formData);
535
+ });
536
+ return [4 /*yield*/, upload(data)];
537
+ case 1:
538
+ ids = _b.sent();
539
+ now = new Date().getTime();
540
+ mediaChunkToUpload.forEach(function (_a) {
541
+ var m = _a[0];
542
+ m.syncedAt = now;
543
+ m.attrs.queued = ids[m.cid].name;
544
+ });
545
+ warehouseMediaNames = tslib.__assign(tslib.__assign({}, warehouseMediaNames), ids);
546
+ return [2 /*return*/];
547
+ }
548
+ });
549
+ };
550
+ index = 0;
551
+ _a.label = 2;
552
+ case 2:
553
+ if (!(index < mediaNotInRemote.length)) return [3 /*break*/, 5];
554
+ return [5 /*yield**/, _loop_1(index)];
555
+ case 3:
556
+ _a.sent();
557
+ _a.label = 4;
558
+ case 4:
559
+ index += chunk;
560
+ return [3 /*break*/, 2];
561
+ case 5: return [2 /*return*/, warehouseMediaNames];
562
+ }
563
+ });
564
+ });
565
+ };
566
+ Sample.prototype.getSubmission = function (warehouseMediaNames) {
567
+ var _this = this;
568
+ if (warehouseMediaNames === void 0) { warehouseMediaNames = {}; }
569
+ var sampleKeys = typeof this.keys === 'function' ? this.keys() : this.keys;
570
+ var keys = tslib.__assign(tslib.__assign({}, Sample.keys), sampleKeys); // warehouse keys/values to transform
571
+ var submission = {
572
+ values: {
573
+ external_key: this.cid,
574
+ },
575
+ media: [],
576
+ samples: [],
577
+ occurrences: [],
578
+ };
579
+ if (this.id) {
580
+ submission.values.id = this.id;
581
+ }
582
+ var mapValue = function (attr, value) {
583
+ var valuesMapping = keys[attr].values;
584
+ if (!valuesMapping) {
585
+ return value;
586
+ }
587
+ if (typeof valuesMapping === 'function') {
588
+ return valuesMapping(value, submission, _this);
589
+ }
590
+ if (value instanceof Array) {
591
+ return value.map(function (v) { return mapValue(attr, v); });
592
+ }
593
+ if (valuesMapping instanceof Array) {
594
+ var mapping = valuesMapping.find(function (_a) {
595
+ var val = _a.value;
596
+ return val === value;
597
+ });
598
+ if (!mapping || !mapping.id) {
599
+ throw new Error("A \"".concat(attr, "\" attribute \"").concat(value, "\" value could not be mapped to a remote database field."));
600
+ }
601
+ return mapping.id;
602
+ }
603
+ return valuesMapping[value];
604
+ };
605
+ var getValue = function (attr) {
606
+ var value = _this.attrs[attr];
607
+ var isEmpty = function (val) { return val === null || val === undefined; };
608
+ if (isEmpty(value)) {
609
+ return;
610
+ }
611
+ if (!keys[attr]) {
612
+ attr = attr.includes('smpAttr:') ? attr : lodash.snakeCase(attr);
613
+ submission.values[attr] = value;
614
+ return;
615
+ }
616
+ value = mapValue(attr, value);
617
+ if (isEmpty(value)) {
618
+ return;
619
+ }
620
+ var warehouseAttr = keys[attr].id || attr;
621
+ var attrKey = !Number.isNaN(Number(warehouseAttr))
622
+ ? "smpAttr:".concat(warehouseAttr)
623
+ : warehouseAttr;
624
+ attrKey = attrKey.includes('smpAttr:') ? attrKey : lodash.snakeCase(attrKey);
625
+ submission.values[attrKey] = value;
626
+ };
627
+ Object.keys(this.attrs).forEach(getValue);
628
+ this.samples.forEach(function (model) {
629
+ if (_this.attrs.training)
630
+ model.attrs.training = _this.attrs.training; // eslint-disable-line no-param-reassign
631
+ var modelSubmission = model.getSubmission(warehouseMediaNames);
632
+ if (!modelSubmission)
633
+ return;
634
+ submission.samples.push(modelSubmission);
635
+ });
636
+ this.occurrences.forEach(function (model) {
637
+ if (_this.attrs.training)
638
+ model.attrs.training = _this.attrs.training; // eslint-disable-line no-param-reassign
639
+ var modelSubmission = model.getSubmission(warehouseMediaNames);
640
+ if (!modelSubmission)
641
+ return;
642
+ submission.occurrences.push(modelSubmission);
643
+ });
644
+ this.media.forEach(function (model) {
645
+ var modelSubmission = model.getSubmission(warehouseMediaNames);
646
+ if (!modelSubmission)
647
+ return;
648
+ submission.media.push(modelSubmission);
649
+ });
650
+ var survey = this.getSurvey();
651
+ if (survey.modifySubmission) {
652
+ return survey.modifySubmission(submission, this);
653
+ }
654
+ return submission;
655
+ };
656
+ Sample.prototype.isUploaded = function () {
657
+ if (this.parent) {
658
+ return this.parent.isUploaded();
659
+ }
660
+ return !!this.syncedAt;
661
+ };
662
+ Sample.prototype.isDisabled = function () {
663
+ return this.isUploaded();
664
+ };
665
+ /**
666
+ * Warehouse attributes and their values.
667
+ */
668
+ Sample.keys = {
669
+ location: { id: 'entered_sref' },
670
+ location_type: {
671
+ id: 'entered_sref_system',
672
+ values: {
673
+ british: 'OSGB', // for British National Grid
674
+ irish: 'OSIE', // for Irish Grid
675
+ channel: 'utm30ed50', // for Channel Islands Grid
676
+ latlon: 4326, // for Latitude and Longitude in decimal form (WGS84 datum)
677
+ },
678
+ },
679
+ group: { id: 'group_id' },
680
+ training: { id: 'training', values: helpers.boolToWarehouseValue },
681
+ deleted: { id: 'deleted', values: helpers.boolToWarehouseValue },
682
+ };
683
+ return Sample;
684
+ }(Model["default"]));
685
+
686
+ exports["default"] = Sample;
687
+ exports.getMediaFormData = getMediaFormData;