Skip to content
Snippets Groups Projects
Sync.vue 31.49 KiB
<!--
    
  Copyright (c) 2022 NIBIO <http://www.nibio.no/>. 
  
  This file is part of VIPSObservationApp.
  VIPSObservationApp is free software: you can redistribute it and/or modify
  it under the terms of the NIBIO Open Source License as published by 
  NIBIO, either version 1 of the License, or (at your option) any
  later version.
  
  VIPSObservationApp is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  NIBIO Open Source License for more details.
  
  You should have received a copy of the NIBIO Open Source License
  along with VIPSObservationApp.  If not, see <http://www.nibio.no/licenses/>.
    
  Author: Bhabesh Bhabani Mukhopadhyay
  Author: Tor-Einar Skog <tor-einar.skog@nibio.no>
  Dated : 19-Aug-2021
    
-->
<template>
	<div>
		<div v-if="loading">
			<div class="spinner-border text-success" role="status">

			</div> <span class="text-danger"> Data Loading...</span>
			<!-- -- Sync :  <div v-html="isSyncNeeded"></div> -->
		</div>
		<div v-show="isMounted">
			<common-util ref="CommonUtil" />
		</div>
	</div>
</template>

<script>
	import CommonUtil from "@/components/CommonUtil";

	export default {
		name: "Sync",
		components: {CommonUtil},
		props: {
			isSyncNeeded: Boolean
		},
		data() {
			return {
				isMounted: false,
				CONST_URL_DOMAIN: '',
				booIsSyncOneWayReady: false,
				booIsSyncTwoWayReady: false,
				arrSyncOneWay: [
					{"name": CommonUtil.CONST_STORAGE_CROP_CATEGORY, "complete": false},
					{"name": CommonUtil.CONST_STORAGE_CROP_LIST, "complete": false},
					{"name": CommonUtil.CONST_STORAGE_PEST_LIST, "complete": false},
					{"name": CommonUtil.CONST_STORAGE_CROP_PEST_LIST, "complete": false},
					{"name": CommonUtil.CONST_STORAGE_VISIBILITY_POLYGON, "complete": false},
				],
				arrSyncTwoWay: [
					{"name": CommonUtil.CONST_STORAGE_OBSERVATION_LIST, "complete": false},
					{"name": CommonUtil.CONST_STORAGE_POI_LIST, "complete": false},

				],
				appUser: {},
				loading: false,
				counterTwoWaySyncPOST: 0,
				totalTwoWaySyncPOST: 0,
				counterTwoWaySyncPOST: 0,
			};
		},
		watch: {
			booIsSyncOneWayReady:
			{
				immediate: true,
				handler(val, oldVal) {
					if (val) {

						/* Starting of Sync */
						CommonUtil.logInfo('----- SYNC ONE WAY STARTED -----');
						this.syncOneWay();
					}
					if (typeof (booIsSyncOneWayReady) === undefined) {
						this.loading = false;
						/* refresh for next time */
						this.refreshOneWaySyncItems();
					}
					if (typeof (booIsSyncOneWayReady) == false) {
						this.loading = false;
					}
				}
			},
			booIsSyncTwoWayReady:
			{
				immediate: true,
				handler(val, oldVal) {
					if (val) {
						this.loading = true;
						CommonUtil.logInfo('----- SYNC TWO WAY STARTED -----');
						this.syncTwoWayAtLogin();
						this.loading = false;
					}
				}
			}
		},
		methods: {
			/** One way Sync. Fetching the data from server and stored in local storage */
			syncOneWay() {
				if (this.booIsSyncOneWayReady) {
					this.loading = true;

					let appUser = this.appUser;
					let funStorageSet = this.oneWaySyncStorageSet;

					if (typeof (appUser) == undefined || appUser == null || appUser === '') {
						// TODO if appUser is not available
					}
					else {
						let strUrl = '';
						let This = this;
						$.each(this.arrSyncOneWay, function (index, value) {
							switch (value.name) {
								case CommonUtil.CONST_STORAGE_CROP_CATEGORY:
									strUrl = This.CONST_URL_DOMAIN + CommonUtil.CONST_URL_CROP_CATEGORY + appUser.organization_id;
									break;
								case CommonUtil.CONST_STORAGE_CROP_LIST:
									strUrl = This.CONST_URL_DOMAIN + CommonUtil.CONST_URL_CROP_LIST;
									break;
								case CommonUtil.CONST_STORAGE_PEST_LIST:
									strUrl = This.CONST_URL_DOMAIN + CommonUtil.CONST_URL_PEST_LIST + '?organizationId=' + appUser.organization_id;
									break;
								case CommonUtil.CONST_STORAGE_CROP_PEST_LIST:
									strUrl = This.CONST_URL_DOMAIN + CommonUtil.CONST_URL_CROP_PEST_LIST;
									break;
								case CommonUtil.CONST_STORAGE_VISIBILITY_POLYGON:
									strUrl = This.CONST_URL_DOMAIN + CommonUtil.CONST_URL_POLYGON_SERVICES + appUser.organization_id;
									break;
								default:
							}

							funStorageSet(value, strUrl);
						});
					}

					this.loading = false;
				}
			},

			syncTwoWayAtLogin() {

				let appUser = this.appUser;
				let funStorageSet = this.oneWaySyncStorageSet;

				if (typeof (appUser) == undefined || appUser == null || appUser === '') {
					// TODO if appUser is not available
				}
				else {

					this.syncTwoWay();
				}
			},
			syncTwoWay() {
				// Run this only when we are online
				if (navigator.onLine) {
					let strUrl = '';
					let This = this;
					$.each(this.arrSyncTwoWay, function (index, value) {
						switch (value.name) {
							case CommonUtil.CONST_STORAGE_OBSERVATION_LIST:
								strUrl = This.CONST_URL_DOMAIN + CommonUtil.CONST_URL_USER_OBSERVATION_LIST;
								break;
							case CommonUtil.CONST_STORAGE_POI_LIST:
								strUrl = This.CONST_URL_DOMAIN + CommonUtil.CONST_URL_USER_POI;
								break;

							default:
						}

						This.syncTwoWayInitiate(value, strUrl);
					});
				}
			},

			/**  Writing server data to localstorage using sync */
			oneWaySyncStorageSet(value, strUrl) {

				fetch(strUrl)
					.then((response) => response.json())
					.then((data) => {
						localStorage.setItem(value.name, JSON.stringify(data));
						value.complete = true;
						this.setOneWaySyncStatus(value);
					});

			},

			/** Set status whether the sync of items completed or not */
			setOneWaySyncStatus(itemValue) {
				let booFlag;
				/* Set the status - array value status set by reference */
				$.each(this.arrSyncOneWay, function (index, value) {
					if (value.name === itemValue.name) {
						value.complete = itemValue.complete;
					}
				});

				/* Set the global flag  - to mark the end of sync*/
				$.each(this.arrSyncOneWay, function (index, value) {
					if (value.complete === false) {
						booFlag = false;
					}
				});
				this.booIsSyncOneWayReady = booFlag;

			},

			/** Reset One way sync items */
			refreshOneWaySyncItems() {
				$.each(this.arrSyncOneWay, function (index, value) {
					value.complete = false;
				})
				this.booIsSyncOneWayReady = false;
			},

			/** Deciding factor whether oneway should start or not */
			isSyncOnewayNeeded(loggedUser) {
				if (!this.booIsSyncOneWayReady) {
					this.syncOneWayEmptyProduct(loggedUser);
					this.syncOneWayDifferentUser(loggedUser);
					this.syncOneWayDifferentTimeStamp(loggedUser);
				}

			},

			/** Oneway Sync on empty products in store -- (e.g. first time) */
			syncOneWayEmptyProduct(appUser) {

				let booLocalIsSyncOneWayReady = false;
				if (!this.booIsSyncOneWayReady) {
					$.each(this.arrSyncOneWay, function (index, value) {
						let strItem = localStorage.getItem(value.name);
						/** Check empty local storage  */
						if (typeof (strItem) == undefined || strItem == null || strItem === '') {
							booLocalIsSyncOneWayReady = true;

							return false;
						}
					});
					this.booIsSyncOneWayReady = booLocalIsSyncOneWayReady;
					this.appUser = appUser;
				}
			},

			/** Oneway Sync For Different user login */
			syncOneWayDifferentUser(loggedUser) {

				if (!this.booIsSyncOneWayReady) {
					let userStored = JSON.parse(localStorage.getItem(CommonUtil.CONST_STORAGE_USER_DETAIL));
					if (userStored.userId != loggedUser.userId) {
						this.booIsSyncOneWayReady = true;
						this.appUser = loggedUser;
					}
				}

			},

			/** Oneway sync for different timestamp (e.g. changes in server) */
			syncOneWayDifferentTimeStamp(appUser) {
				if (!this.booIsSyncOneWayReady) {
					let strStoreTimeStamp = localStorage.getItem(CommonUtil.CONST_STORE_TIMESTAMP);

					let jsonHeader = {Authorization: appUser.userUuid};

					fetch(
						this.CONST_URL_DOMAIN + CommonUtil.CONST_URL_LAST_TIMESTAMP,
						{
							method: "GET",
							headers: jsonHeader,
						}
					)
						.then((response) => response.json())
						.then((data) => {

							if (typeof (strStoreTimeStamp) == undefined || strStoreTimeStamp == null || strStoreTimeStamp === '') {
								localStorage.setItem(CommonUtil.CONST_STORE_TIMESTAMP, JSON.stringify(data));
								this.booIsSyncOneWayReady = true;
								this.appUser = appUser;
							}
							else {
								let jsonServerTimeStamp = data;
								let jsonStoredTimeStamp = JSON.parse(strStoreTimeStamp);
								let jsTimeStamp = new Date(jsonServerTimeStamp.lastUpdated);
								let dtStoreTimeStamp = new Date(jsonStoredTimeStamp.lastUpdated);

								if (jsTimeStamp.getTime() != dtStoreTimeStamp.getTime()) {
									this.booIsSyncOneWayReady = true;
									this.appUser = appUser;

									localStorage.setItem(CommonUtil.CONST_STORE_TIMESTAMP, JSON.stringify(data));


								}

							}

						});

				}
			},

			/** Decide which two way sync  will start */
			syncTwoWayInitiate(value, strUrl) {

				switch (value.name) {
					case CommonUtil.CONST_STORAGE_OBSERVATION_LIST:
						this.syncTwoWayObservation(value, strUrl);
						break;
					case CommonUtil.CONST_STORAGE_POI_LIST:
						this.syncTwoWayPOI(value, strUrl);
						break;

					default:
				}

			},

			/** Two way Sync  POI */
			syncTwoWayPOI(value, strUrl) {
				this.syncPOISendPrepare(value);
			},

			/** Findout which data at POI need to be synced */
			syncPOISendPrepare(value) {
				let This = this;
				let lstPOI = JSON.parse(localStorage.getItem(value.name));

				if (lstPOI) {
					let lstPOIUpload = lstPOI.filter(poi => poi.uploaded === false);
					this.totalTwoWaySyncPOST = lstPOIUpload.length;
					if (lstPOIUpload && lstPOIUpload.length != 0) {
						lstPOIUpload.forEach(function (poi) {
							This.syncPOIPOST(poi, This.totalTwoWaySyncPOST);
						})
					}
					else {
						/** Create the list using GET */
						let totalTwoWaySyncPOST = 0;
						let updatedPOI = {};
						This.getPOIFromServerTwowaySync(totalTwoWaySyncPOST, updatedPOI);
					}
				}
				else {
					/** Create the list using GET */
					this.getPOIFromServerTwowaySync(0, undefined);
				}
			},

			/** Posting required POI data for sync */
			syncPOIPOST(poi, totalTwoWaySyncPOST) {
				let This = this;
				let userUUID = localStorage.getItem(CommonUtil.CONST_STORAGE_UUID);
				let jsonBody = null;

				if (poi.deleted) {
					/** prepare POI object for server Delete Operation */
					let delPOI = {};
					delPOI.pointOfInterestId = poi.pointOfInterestId;
					delPOI.deleted = poi.deleted;
					jsonBody = JSON.stringify(delPOI);

					this.removeLocalPOI(poi.pointOfInterestId);
				}
				else {
					jsonBody = JSON.stringify(poi);
				}

				fetch(
					This.CONST_URL_DOMAIN + CommonUtil.CONST_URL_SYNC_UPDATE_POI,
					{
						method: "POST",
						headers: {
							"Content-Type": "application/json",
							'Authorization': userUUID
						},
						body: jsonBody
					})
					.then(function (response) {
						if (response.status === 200) {

						}
						else {
							/** Even if the response is not success, still need to increase the counter, 
							 * to decide for next action  after all PUSH */
							This.counterTwoWaySyncPOST = This.counterTwoWaySyncPOST + 1;
							if (response.status === 500) {
								return false;
							}
						}
						return response.text()
					})
					.then((data) => {
						if (data) {
							let updatedPOI = JSON.parse(data);
							if (updatedPOI.deleted) {
								This.removeLocalPOI(poupdatedPOI.pointOfInterestId);
							}
							else {
								if (poi.pointOfInterestId < 0) {
									let indexPosition = null;
									let lstPOI = JSON.parse(localStorage.getItem(CommonUtil.CONST_STORAGE_POI_LIST));
									$.each(lstPOI, function (index, jsonPOI) {
										if (poi.pointOfInterestId == jsonPOI.pointOfInterestId) {
											indexPosition = index;
											return false;
										}
									})

									if (indexPosition) {
										/** Remove/delete the POI with nagative number (localy created ) */
										lstPOI.splice(indexPosition, 1);
										localStorage.setItem(CommonUtil.CONST_STORAGE_POI_LIST, JSON.stringify(lstPOI));
										this.getPOIFromServerTwowaySync(1, updatedPOI);
									}
								}
								if (poi.pointOfInterestId === updatedPOI.pointOfInterestId) {

									this.updatePOIPOST(updatedPOI, totalTwoWaySyncPOST);
								}
							}

						}
					})
			},

			/** Update POI after response from server */
			updatePOIPOST(updatedPOI, totalTwoWaySyncPOST) {
				let lstPOI = JSON.parse(localStorage.getItem(CommonUtil.CONST_STORAGE_POI_LIST));
				let counter = undefined;
				$.each(lstPOI, function (index, jsonPOI) {
					if (jsonPOI.pointOfInterestId === updatedPOI.pointOfInterestId) {
						counter = index;
						return false;
					}
				});

				if (counter) {
					lstPOI[counter] = updatedPOI;
					localStorage.setItem(CommonUtil.CONST_STORAGE_POI_LIST, JSON.stringify(lstPOI));

					this.counterTwoWaySyncPOST = this.counterTwoWaySyncPOST + 1;
					CommonUtil.logInfo('total number of upload : ' + totalTwoWaySyncPOST + ' ---- counter value : ' + this.counterTwoWaySyncPOST);

					if (this.counterTwoWaySyncPOST === totalTwoWaySyncPOST) {
						this.counterTwoWaySyncPOST = 0;
						this.getPOIFromServerTwowaySync(totalTwoWaySyncPOST, updatedPOI);
					}


				}

			},

			/** GET POIs */
			getPOIFromServerTwowaySync(totalTwoWaySyncPOST, updatedPOI) {
				let This = this;
				let strUUID = localStorage.getItem(CommonUtil.CONST_STORAGE_UUID);
				let jsonHeader = {Authorization: strUUID};
				fetch(This.CONST_URL_DOMAIN + CommonUtil.CONST_URL_USER_POI, {
					method: "GET",
					headers: jsonHeader,
				}).then((response) => response.json())
					.then((data) => {
						let serverPOIs = data;
						if (localStorage.getItem(CommonUtil.CONST_STORAGE_POI_LIST)) {
							let lstLocalPOI = JSON.parse(localStorage.getItem(CommonUtil.CONST_STORAGE_POI_LIST));
							serverPOIs.forEach(function (serverPOI) {
								let arrIndex = undefined;
								let booNoRecordFound = false;
								let booRecordFound = false;
								$.each(lstLocalPOI, function (index, localPOI) {
									if (serverPOI.pointOfInterestId === localPOI.pointOfInterestId) {
										booRecordFound = true;
										if (updatedPOI && (totalTwoWaySyncPOST === 1 && updatedPOI.pointOfInterestId === serverPOI.pointOfInterestId)) {
										}
										else {
											if (serverPOI.lastEditedTime) {
												let srvDate = new Date(serverPOI.lastEditedTime);
												let localDate = new Date(localPOI.lastEditedTime);

												if (srvDate >= localDate) {
													arrIndex = index;
													return false;
												}

											}
											else {
												arrIndex = index;
												return false;
											}
										}

									}
								});

								if (booRecordFound) {}
								else {
									booNoRecordFound = true;
								}
								if (arrIndex) {

									lstLocalPOI[arrIndex] = serverPOI;
								}
								if (booNoRecordFound) {
									lstLocalPOI.push(serverPOI);
									return false;
								}

							});

							localStorage.setItem(CommonUtil.CONST_STORAGE_POI_LIST, JSON.stringify(lstLocalPOI));

						}
						else {

							localStorage.setItem(CommonUtil.CONST_STORAGE_POI_LIST, JSON.stringify(serverPOIs));
						}

						// Update the POI list after sync
						if (this.$root.sharedState.placesListComponent != undefined) {
							this.$root.sharedState.placesListComponent.getPOIListFromStore();
						}
					})

			},
			/** Remove local POI */
			removeLocalPOI(id) {
				let lstPOI = JSON.parse(localStorage.getItem(CommonUtil.CONST_STORAGE_POI_LIST));
				let indexPosition = null;
				$.each(lstPOI, function (index, poi) {
					if (poi.pointOfInterestId === id) {
						indexPosition = index;
						return false;
					}
				});

				if (indexPosition) {

					lstPOI.splice(indexPosition, 1);
					localStorage.setItem(CommonUtil.CONST_STORAGE_POI_LIST, JSON.stringify(lstPOI));
				}
			},

			/** Two way Sync - Observation */
			syncTwoWayObservation(value, strUrl) {
				this.syncObservationSendPrepare(value);
			},

			/** Sync - Observation - embed with image data for POST */
			syncObservationSendPrepare(value) {
				let This = this;
				let lstObservations = JSON.parse(localStorage.getItem(value.name));
				if (lstObservations) {

					let lstObservationUpload = lstObservations.filter(observation => observation.uploaded === false);
					this.totalTwoWaySyncPOST = lstObservationUpload.length;

					if (lstObservationUpload && lstObservationUpload.length != 0) {
						lstObservationUpload.forEach(function (observation) {
							//CommonUtil.logInfo("syncObservationSendPrepare:observation.geoinfo= " + observation.geoinfo);

							let observationForStore = {};
							observationForStore.observationId = observation.observationId;
							observationForStore.cropOrganismId = observation.cropOrganismId;
							observationForStore.organismId = observation.organismId
							observationForStore.timeOfObservation = observation.timeOfObservation;
							observationForStore.isPositive = observation.isPositive;
							observationForStore.statusChangedTime = observation.statusChangedTime;
							observationForStore.statusTypeId = observation.statusTypeId;
							observationForStore.isQuantified = observation.isQuantified;
							observationForStore.userId = observation.userId;
							observationForStore.geoinfo = observation.geoinfo;
							observationForStore.locationPointOfInterestId = observation.locationPointOfInterestId;
							observationForStore.broadcastMessage = observation.broadcastMessage;
							observationForStore.statusRemarks = observation.statusRemarks;
							observationForStore.observationHeading = observation.observationHeading;
							observationForStore.observationText = observation.observationText;
							observationForStore.observationData = observation.observationData;
							observationForStore.locationIsPrivate = observation.locationIsPrivate;
							observationForStore.polygonService = observation.polygonService;
							observationForStore.uploaded = observation.uploaded;
							observationForStore.observationIllustrationSet = observation.observationIllustrationSet;
							if (observation.deleted) {
								observationForStore.deleted = observation.deleted;
							}

							This.syncObservationSendPrepareSingleObject(observationForStore, This.totalTwoWaySyncPOST);

						});
					}
					else {
						let totalTwoWaySyncPOST = 0;
						let updatedObservation = {};
						/** GET Observations  */
						this.getObservationsFromServerTwowaySync(totalTwoWaySyncPOST, updatedObservation);
					}
				}
				else {

					this.getObservationsFromServerTwowaySync(0, undefined);
				}

			},

			/** Prepare a single object includes image data */
			syncObservationSendPrepareSingleObject(observation, totalTwoWaySyncPOST, syncObservationPOST) {

				let This = this;
				let entityName = CommonUtil.CONST_DB_ENTITY_PHOTO;
				if (observation.uploaded === false) {
					let observationUpload = observation;
					let observationIllustrationSet = observationUpload.observationIllustrationSet;

					//  talk to Innodb,  embed image data to JSON POST and exit
					let dbRequest = indexedDB.open(CommonUtil.CONST_DB_NAME, CommonUtil.CONST_DB_VERSION);
					dbRequest.onsuccess = function (evt) {
						let db = evt.target.result;

						if (db.objectStoreNames.contains(entityName)) {
							let transaction = db.transaction([entityName], 'readwrite');
							let objectstore = transaction.objectStore(entityName);
							if (observationIllustrationSet) {
								observationIllustrationSet.forEach(function (illustration) {
									let fileName = illustration.observationIllustrationPK.fileName;

									let objectstoreRequest = objectstore.get(fileName);


									objectstoreRequest.onsuccess = function (event) {

										let observationImage = event.target.result;
										if (observationImage) {
											let imageTextData = observationImage.illustration.imageTextData;
											illustration.imageTextData = imageTextData;
										}
									}
								});
							}

							transaction.oncomplete = function () {
								This.syncObservationPOST(observation, totalTwoWaySyncPOST);
								
							}
						}
						else {
							This.syncObservationPOST(observation, totalTwoWaySyncPOST);
							db.close();
						}
						db.close();
					}
				}
			},

			/** Posting Observation data to VIPS server for sync */
			syncObservationPOST(observation, totalTwoWaySyncPOST) {
				//CommonUtil.logInfo("syncObservationPOST: observation.geoinfo=" + observation.geoinfo);
				let This = this;
				//if(this.isSyncNeeded)
				{
					let userUUID = localStorage.getItem(CommonUtil.CONST_STORAGE_UUID);
					let jsonBody = null;

					if (observation.deleted) {
						let delObservation = {};
						delObservation.observationId = observation.observationId;
						delObservation.deleted = observation.deleted;
						jsonBody = JSON.stringify(delObservation);
					}
					else {
						jsonBody = JSON.stringify(observation);
					}



					if (observation.deleted) {
						This.removeLocalObservation(observation.observationId);
					}
					/*CommonUtil.logInfo("syncObservationPOST: jsonbody=" + jsonBody);
								CommonUtil.logInfo("Sending observation");*/
					fetch(
						This.CONST_URL_DOMAIN + CommonUtil.CONST_URL_SYNC_UPDATE_OBSERVATION,
						{
							method: "POST",
							headers: {
								"Content-Type": "application/json",
								'Authorization': userUUID
							},
							body: jsonBody
						}
					)
						.then(function (response) {

							if (response.status === 200) {
								//CommonUtil.logInfo("Done POSTING")
							}
							else {
								/** Even if the response is not success, still need to increase the counter, 
								 * to decide for next action  after all PUSH */
								This.counterTwoWaySyncPOST = This.counterTwoWaySyncPOST + 1;
							}
							return response.text()
						})
						.then((data) => {
							if (data) {

								//console.info(data);
								let updatedObservation = JSON.parse(data);

								if (observation.observationId < 0) {
									let indexPosition = null;
									let lstObservations = JSON.parse(localStorage.getItem(CommonUtil.CONST_STORAGE_OBSERVATION_LIST));
									$.each(lstObservations, function (index, jsonObservation) {
										if (observation.observationId == jsonObservation.observationId) {
											indexPosition = index;
											return false;

										}
									})
									if (indexPosition || indexPosition === 0) {
										/** Remove/delete the Observation with nagative number (localy created ) */
										lstObservations.splice(indexPosition, 1);
										localStorage.setItem(CommonUtil.CONST_STORAGE_OBSERVATION_LIST, JSON.stringify(lstObservations));
										this.getObservationsFromServerTwowaySync(1, updatedObservation)
									}

								}

								if (updatedObservation.observationId === observation.observationId) {
									this.updateObservationPOST(updatedObservation, totalTwoWaySyncPOST);

								}

							}
						});
				}

			},

			/** After App's POST opecation, update local observation when response received from VIPS Server */
			updateObservationPOST(updatedObservation, totalTwoWaySyncPOST) {

				let lstObservations = JSON.parse(localStorage.getItem(CommonUtil.CONST_STORAGE_OBSERVATION_LIST));

				let observationOld = {};
				let counter = undefined;
				$.each(lstObservations, function (index, jsonObservation) {
					if (jsonObservation.observationId === updatedObservation.observationId) {
						observationOld = Object.assign({}, jsonObservation); // Deep cloning
						counter = index;
						return false;
					}

				});

				if (counter || counter === 0) {

					let illustrationsUpdated = updatedObservation.observationIllustrationSet;
					let illustrationsOld = observationOld.observationIllustrationSet;

					illustrationsOld.forEach(function (illOld) {
						let recFound = false;
						if (illustrationsUpdated) {
							illustrationsUpdated.forEach(function (illUpdated) {
								if (illOld.observationIllustrationPK.fileName === illUpdated.observationIllustrationPK.fileName) {
									recFound = true;
									return false;
								}
							});
						}
						if (recFound) {
							delete illOld.uploaded;
							if (illOld.deleted) {
								CommonUtil.logInfo('record to delete --- found');
							}
						}
					})
					updatedObservation.observationIllustrationSet = observationOld.observationIllustrationSet;


					lstObservations[counter] = updatedObservation;
					localStorage.setItem(CommonUtil.CONST_STORAGE_OBSERVATION_LIST, JSON.stringify(lstObservations));

					this.counterTwoWaySyncPOST = this.counterTwoWaySyncPOST + 1;
					CommonUtil.logInfo('total number of upload : ' + totalTwoWaySyncPOST + ' ---- counter value : ' + this.counterTwoWaySyncPOST);
					if (this.counterTwoWaySyncPOST === totalTwoWaySyncPOST) {
						this.counterTwoWaySyncPOST = 0;
						this.getObservationsFromServerTwowaySync(totalTwoWaySyncPOST, updatedObservation);
					}
				}

			},

			/** GET Observations from server to sync local data */
			getObservationsFromServerTwowaySync(totalTwoWaySyncPOST, updatedObservation) {

				let This = this;
				let strUUID = localStorage.getItem(CommonUtil.CONST_STORAGE_UUID);
				let jsonHeader = {Authorization: strUUID};

				fetch(This.CONST_URL_DOMAIN + CommonUtil.CONST_URL_USER_OBSERVATION_LIST, {
					method: "GET",
					headers: jsonHeader,
				}).then((response) => response.json())
					.then((data) => {
						let serverObservations = data;
						if (localStorage.getItem(CommonUtil.CONST_STORAGE_OBSERVATION_LIST)) {
							let lstLocalObservations = JSON.parse(localStorage.getItem(CommonUtil.CONST_STORAGE_OBSERVATION_LIST));

							serverObservations.forEach(function (srvObservation) {
								let arrIndex = undefined;
								let booNoRecordFound = false;
								let booRecordFound = false;

								$.each(lstLocalObservations, function (index, localObservation) {
									if (srvObservation.observationId === localObservation.observationId) {
										booRecordFound = true;
										if (updatedObservation && (totalTwoWaySyncPOST === 1 && updatedObservation.observationId === srvObservation.observationId)) {

										}
										else {
											if (srvObservation.lastEditedTime) {
												if (localObservation.lastEditedTime) {

													let srvDate = new Date(srvObservation.lastEditedTime);
													let localDate = new Date(localObservation.lastEditedTime);

													if (srvDate >= localDate) {
														arrIndex = index;
														return false;
													}
												}
												else {
													arrIndex = index;
													return false;
												}
											}
											else {
												arrIndex = index;
												return false;
											}
										}

									}
									else {
										//booNoRecordFound = true;
									}

								})
								if (booRecordFound) {}
								else {
									booNoRecordFound = true;
								}
								if (arrIndex || arrIndex === 0) {
									lstLocalObservations[arrIndex] = srvObservation;
								}
								if (booNoRecordFound) {
									lstLocalObservations.push(srvObservation);
									return false;
								}


							});
							//CommonUtil.logInfo(lstLocalObservations);
							localStorage.setItem(CommonUtil.CONST_STORAGE_OBSERVATION_LIST, JSON.stringify(lstLocalObservations));
						}
						else {
							localStorage.setItem(CommonUtil.CONST_STORAGE_OBSERVATION_LIST, JSON.stringify(serverObservations));
							//this.$router.replace({path:'/'});
							/*                     this.$router.push("/").catch(()=>{});
												this.$router.go(); */
						}

						serverObservations.forEach(function (srvObservation) {
							let observationId = srvObservation.observationId;
							let organismId = srvObservation.organismId;
							let illustrations = srvObservation.observationIllustrationSet;

							if (illustrations && illustrations.length != 0) {
								illustrations.forEach(function (illustration) {
									let imageFileName = illustration.observationIllustrationPK.fileName;
									if (imageFileName) {
										This.fetchImageFromServer(observationId, organismId, imageFileName);
									}
								})
							}
						});
						// Update the data model for the observation list.
						if (this.$root.sharedState.observationListComponent != undefined) {
							this.$root.sharedState.observationListComponent.getObservationsFromStore();
						}
					})
			},

			/** GET image from server */
			fetchImageFromServer(observationId, organismId, imageFileName) {

				let photoURL = this.CONST_URL_DOMAIN + CommonUtil.CONST_URL_STATIC_IMAGE_PATH + organismId + '/' + imageFileName;
				let imgTest;
				let This = this;
				let observationImage = {
					observationId: '',
					organismId: '',
					illustration: {
						fileName: '',
						imageTextData: '',
						deleted: false
					}
				};
				observationImage.observationId = observationId;
				observationImage.organismId = organismId;
				observationImage.illustration.fileName = imageFileName;



				if (organismId) {
					const toDataURL = url => fetch(url)
						.then(response => response.blob())
						.then(blob => new Promise((resolve, reject) => {
							const reader = new FileReader()
							reader.onloadend = () => resolve(reader.result)
							reader.onerror = reject
							reader.readAsDataURL(blob)
						}))


					toDataURL(photoURL)
						.then(imageTextData => {
							observationImage.illustration.imageTextData = imageTextData;
							This.storeImageData(observationImage);
						})
				}

			},
			/** Store image from server 
			 */
			storeImageData(observationImage) {
				let This = this;
				let entityName = CommonUtil.CONST_DB_ENTITY_PHOTO;

				let dbRequest = indexedDB.open(CommonUtil.CONST_DB_NAME, CommonUtil.CONST_DB_VERSION);
				dbRequest.onsuccess = function (evt) {
					let db = evt.target.result;
					if (db.objectStoreNames.contains(entityName)) {
						let transaction = db.transaction([entityName], 'readwrite');
						let objectstore = transaction.objectStore(entityName).add(observationImage, observationImage.illustration.fileName);
					}
					else {
						alert("Photo part of IndexedDB not created. Please report this error.");
					}
					db.close();
				}

			},

			/** Remove local Observation */
			removeLocalObservation(id) {
				let lstObservations = JSON.parse(localStorage.getItem(CommonUtil.CONST_STORAGE_OBSERVATION_LIST));
				let indexPosition = null;
				$.each(lstObservations, function (index, observation) {
					if (observation.observationId === id) {
						indexPosition = index;
						return false;
					}
				});
				if (indexPosition || indexPosition === 0) {
					this.removeImageRecord(id);

					lstObservations.splice(indexPosition, 1);
					localStorage.setItem(CommonUtil.CONST_STORAGE_OBSERVATION_LIST, JSON.stringify(lstObservations));
				}
			},
			/**
			 * Remove image data from indexed DB 
			 */
			removeImageRecord(observationId) {
				let entityName = CommonUtil.CONST_DB_ENTITY_PHOTO;
				let dbRequest = indexedDB.open(CommonUtil.CONST_DB_NAME, CommonUtil.CONST_DB_VERSION);
				let indexName = CommonUtil.CONST_DB_INDEX_NAME_OBSERVATION_ID;
				dbRequest.onsuccess = function (evt) {
					let db = evt.target.result;
					if (db.objectStoreNames.contains(entityName)) {
						let transaction = db.transaction([entityName], 'readwrite');
						let objectstore = transaction.objectStore(entityName);
						let indexStore = objectstore.index(indexName);
						let keyRange = IDBKeyRange.only(observationId);
						let cursorRequest = indexStore.openCursor(keyRange);
						cursorRequest.onsuccess = function (event) {
							let cursor = event.target.result;
							if (cursor) {
								cursor.delete();
								cursor.continue();
							}
						}
					}
					db.close();
				}

			},



		},
		mounted() {
			this.isMounted = true;
			this.CONST_URL_DOMAIN = CommonUtil.CONST_URL_DOMAIN;
		}

	};
</script>