diff --git a/liquid-prep-app/src/app/components/dashboard/fields/add-field/add-field.component.ts b/liquid-prep-app/src/app/components/dashboard/fields/add-field/add-field.component.ts index 207c8aa..863fff2 100644 --- a/liquid-prep-app/src/app/components/dashboard/fields/add-field/add-field.component.ts +++ b/liquid-prep-app/src/app/components/dashboard/fields/add-field/add-field.component.ts @@ -9,10 +9,11 @@ import { FieldDataService } from 'src/app/service/FieldDataService'; import { Guid } from 'guid-typescript'; import { Field } from 'src/app/models/Field'; import { MatDialog } from '@angular/material/dialog'; -import { SENSORS_MOCK_DATA } from './../../sensors/sensor-data'; -import { SensorListComponent } from './../sensor-list/sensor-list.component'; +import { SENSORS_MOCK_DATA } from '../../sensors/sensor-data'; +import { SensorListComponent } from '../sensor-list/sensor-list.component'; import { CropDataService } from 'src/app/service/CropDataService'; import { forkJoin, from } from 'rxjs'; +import {CropInfoResp} from "../../../../models/api/CropInfoResp"; @Component({ selector: 'app-add-field', @@ -130,6 +131,8 @@ export class AddFieldComponent implements OnInit { } const sensorList = this.sensors; const id = Guid.create().toString(); + this.cropValue.seedingDate = new Date(formattedDate); + this.cropValue.waterDate = new Date(formattedDate);//Assume that each time a crop is planted, it will be watered. const params: Field = { id, fieldName: name, @@ -144,13 +147,24 @@ export class AddFieldComponent implements OnInit { openDialog(dialogTemplate: TemplateRef): void { this.dialog.open(dialogTemplate, { - height: '300px', + height: '430px', width: '400px', }); } clickCropNext() { this.cropValue = this.fieldForm.get('cropSelect').value; + this.cropService.getCropInfo(this.cropValue.id).subscribe( + (resp: CropInfoResp)=>{ + this.cropValue.id = resp.data.docs[0]._id; + this.cropValue.cropName = resp.data.docs[0].cropName; + this.cropValue.facts = resp.data.docs[0]; + }, + (error) =>{ + alert('clickCropNext Could not get crop info: ' + error); + console.error('clickCropNext Error getting CropInfo:', error); + } + ); this.fieldForm.patchValue({ crop: this.fieldForm.get('cropSelect').value.cropName, }); diff --git a/liquid-prep-app/src/app/components/dashboard/fields/edit-field/edit-field.component.ts b/liquid-prep-app/src/app/components/dashboard/fields/edit-field/edit-field.component.ts index f9a25a3..c3c5abc 100644 --- a/liquid-prep-app/src/app/components/dashboard/fields/edit-field/edit-field.component.ts +++ b/liquid-prep-app/src/app/components/dashboard/fields/edit-field/edit-field.component.ts @@ -19,6 +19,7 @@ import { MatSnackBar } from '@angular/material/snack-bar'; import { SensorListComponent } from './../sensor-list/sensor-list.component'; import { CropDataService } from 'src/app/service/CropDataService'; import { forkJoin, from } from 'rxjs'; +import {CropInfoResp} from "../../../../models/api/CropInfoResp"; @Component({ selector: 'app-edit-field', @@ -113,12 +114,23 @@ export class EditFieldComponent implements OnInit { openCropDialog(dialogTemplate: TemplateRef): void { this.dialog.open(dialogTemplate, { - height: '300px', + height: '430px', width: '400px', }); } clickCropNext() { + this.cropService.getCropInfo(this.cropValue.id).subscribe( + (resp: CropInfoResp)=>{ + this.cropValue.id = resp.data.docs[0]._id; + this.cropValue.cropName = resp.data.docs[0].cropName; + this.cropValue.facts = resp.data.docs[0]; + }, + (error) =>{ + alert('clickCropNext Could not get crop info: ' + error); + console.error('clickCropNext Error getting CropInfo:', error); + } + ); this.fieldForm.patchValue({ crop: this.fieldForm.get('cropSelect').value.cropName, }); @@ -183,6 +195,7 @@ export class EditFieldComponent implements OnInit { formattedDate = formatDate(plantDateValue, 'yyyy-MM-dd', 'en-US'); const sensorList = this.sensors; const id = this.id; + this.cropValue.seedingDate = new Date(formattedDate); const params: Field = { id, fieldName: name, diff --git a/liquid-prep-app/src/app/components/dashboard/home/home.component.html b/liquid-prep-app/src/app/components/dashboard/home/home.component.html index e91fe84..b17f688 100644 --- a/liquid-prep-app/src/app/components/dashboard/home/home.component.html +++ b/liquid-prep-app/src/app/components/dashboard/home/home.component.html @@ -33,20 +33,20 @@ - +
- {{ crop.cropName }} + {{ field.crop.cropName }}
-

Northwest Field 3

+

{{field.fieldName}}

- {{crop.cropName}} + {{field.crop.cropName}}

-
@@ -64,16 +64,16 @@

Northwest Field 3

- +
- {{ crop.cropName }} + {{ field.crop.cropName }}
-

Northwest Field 3

+

{{field.fieldName}}

- {{crop.cropName}} + {{ field.crop.cropName}}

diff --git a/liquid-prep-app/src/app/components/dashboard/home/home.component.ts b/liquid-prep-app/src/app/components/dashboard/home/home.component.ts index c6302b7..035796e 100644 --- a/liquid-prep-app/src/app/components/dashboard/home/home.component.ts +++ b/liquid-prep-app/src/app/components/dashboard/home/home.component.ts @@ -7,11 +7,12 @@ import { TodayWeather } from '../../../models/TodayWeather'; import { formatDate } from '@angular/common'; import { WeatherDataService } from '../../../service/WeatherDataService'; import {Crop} from '../../../models/Crop'; -import {CropDataService} from '../../../service/CropDataService'; import {MAT_DIALOG_DATA, MatDialog} from '@angular/material/dialog'; import {WaterConfirmDialogComponent} from './water-confirm-dialog.component'; import {DateTimeUtil} from '../../../utility/DateTimeUtil'; import {WaterAdviceService} from '../../../service/WaterAdviceService'; +import {Field} from "../../../models/Field"; +import {FieldDataService} from "../../../service/FieldDataService"; const RECENTLY = 48; @@ -44,18 +45,17 @@ export class HomeComponent implements OnInit { public nextDayTemperatureMin: number; public currentDate = ''; public location: string; - public myCrops: Crop[]; - public needsWateringCrops: Crop[]; - public recentlyWateredCrops: Crop[]; - public nonRecentlyWateredCrops: Crop[]; - public myCropStatus: 'no-crop' | 'crop-selected' = 'no-crop'; + public myFields: Field[] + public needsWateringFields: Field[]; + public recentlyWateredFields: Field[]; + public nonRecentlyWateredFields: Field[]; constructor( private waterAdviceService: WaterAdviceService, private weatherService: WeatherDataService, private headerService: HeaderService, private geoLocationService: GeoLocationService, - private cropDataService: CropDataService, + private fieldDataService: FieldDataService, private dialog: MatDialog, @Inject(MAT_DIALOG_DATA) public data: { crop: Crop } ) {} @@ -63,40 +63,39 @@ export class HomeComponent implements OnInit { this.headerService.updateHeader(this.headerConfig); this.updateWeatherInfo(); this.getLocation(); - this.cropDataService.getLocalStorageMyCrops().then( - (myCrops) => { - this.myCrops = myCrops; - if (this.myCrops.length > 0){ - this.myCropStatus = 'crop-selected'; - this.FilterWateredCrops(this.myCrops); - this.FilterNeedsWaterCrop(this.nonRecentlyWateredCrops); - } - } - ); + this.fieldDataService.getLocalStorageMyFields() + .then((fields: Field[]) => { + this.myFields = fields; + this.FilterWateredFields(this.myFields); + this.FilterNeedsWaterField(this.nonRecentlyWateredFields) + }) + .catch((error) => { + console.error('Error loading fields:', error); + }); } - private FilterWateredCrops(myCrops: Crop[]){ - this.recentlyWateredCrops = []; - this.nonRecentlyWateredCrops = []; + private FilterWateredFields(MyFields: Field[]){ + this.recentlyWateredFields = []; + this.nonRecentlyWateredFields=[]; const now = new Date(); const recentlyAgo = new Date(now.getTime() - RECENTLY * 60 * 60 * 1000); - myCrops.forEach((cropItem) => { - const waterDate = new Date(cropItem.waterDate); + MyFields.forEach((item:Field)=>{ + const waterDate = new Date(item.crop.waterDate); if (waterDate >= recentlyAgo && waterDate <= now) { - this.recentlyWateredCrops.push(cropItem); + this.recentlyWateredFields.push(item); } else { - this.nonRecentlyWateredCrops.push(cropItem); + this.nonRecentlyWateredFields.push(item); } - }); + }) } - private FilterNeedsWaterCrop(crops: Crop[]){ - this.needsWateringCrops = []; - this.nonRecentlyWateredCrops.forEach((item) => { - this.waterAdviceService.getWaterAdviceByCrop(item).subscribe(advice => { - console.log('water advice:', item.cropName, advice.wateringDecision, advice.waterRecommended); + private FilterNeedsWaterField(fields: Field[]){ + this.needsWateringFields = []; + this.nonRecentlyWateredFields.forEach((item:Field)=>{ + this.waterAdviceService.getWaterAdviceByCrop(item.crop).subscribe(advice => { + console.log('water advice:', item.crop.cropName, advice.wateringDecision, advice.waterRecommended); if (advice.wateringDecision !== 'None'){ - this.needsWateringCrops.push(item); + this.needsWateringFields.push(item); } }); }); @@ -157,25 +156,26 @@ export class HomeComponent implements OnInit { ); } - onWaterClick(crop: Crop) { + onWaterClick(field: Field) { const dialogRef = this.dialog.open(WaterConfirmDialogComponent, { width: '500px', panelClass: ['form-dialog'], - data: { crop }, + data: { field }, }); dialogRef.afterClosed().subscribe(result => { if (result === 'confirm') { - crop.waterDate = new DateTimeUtil().getTodayDate(); - this.cropDataService.storeMyCropsInLocalStorage(crop).then( + field.crop.waterDate = new DateTimeUtil().getTodayDate(); + this.fieldDataService.storeFieldsInLocalStorage(field).then( (r) => { - console.log(`water ${crop.cropName} onConfirm water date ${crop.waterDate}`); - this.needsWateringCrops = this.needsWateringCrops.filter(item => item !== crop); - this.recentlyWateredCrops.push(crop); + console.log(`water ${field.crop.cropName} onConfirm water date ${field.crop.waterDate}`); + this.needsWateringFields = this.needsWateringFields.filter(item => item !== field); + this.recentlyWateredFields.push(field); }, - (e) => { console.error('save water data fail:', crop.cropName, e); } + (e) => { console.error('save water data fail:', field.fieldName,field.crop.cropName, e); } ); } }); } + } diff --git a/liquid-prep-app/src/app/components/dashboard/home/water-confirm-dialog.component.ts b/liquid-prep-app/src/app/components/dashboard/home/water-confirm-dialog.component.ts index 27d4e54..51c3f5d 100644 --- a/liquid-prep-app/src/app/components/dashboard/home/water-confirm-dialog.component.ts +++ b/liquid-prep-app/src/app/components/dashboard/home/water-confirm-dialog.component.ts @@ -1,13 +1,13 @@ import {Component, Inject, OnInit} from '@angular/core'; -import {Crop} from '../../../models/Crop'; import {MAT_DIALOG_DATA} from '@angular/material/dialog'; +import {Field} from "../../../models/Field"; @Component({ selector: 'app-water-confirm-dialog', template: `

Confirm Irrigation Status

- Would you like to mark {{ data.crop.cropName }} as watered? + Would you like to mark {{ data.field.crop.cropName }} as watered?

The updated task will then appear in the "Recently Watered" list @@ -29,7 +29,7 @@ import {MAT_DIALOG_DATA} from '@angular/material/dialog'; }) export class WaterConfirmDialogComponent implements OnInit { - constructor(@Inject(MAT_DIALOG_DATA) public data: { crop: Crop }) { } + constructor(@Inject(MAT_DIALOG_DATA) public data: { field: Field }) { } ngOnInit(): void { } diff --git a/liquid-prep-app/src/app/models/Crop.ts b/liquid-prep-app/src/app/models/Crop.ts index 39a9fb6..b38567e 100644 --- a/liquid-prep-app/src/app/models/Crop.ts +++ b/liquid-prep-app/src/app/models/Crop.ts @@ -4,7 +4,6 @@ export class Crop { id: string; cropName: string; type: string; - cropGrowthStage: CropGrowthStage; url: string; // crop image mapping url facts: CropFacts; seedingDate: Date; @@ -18,22 +17,3 @@ export class Measure{ measureValue: number; } -export class CropGrowthStage { - numberOfStages: number; - waterMeasurementMetric: string; // cm - waterUsage: string; // daily - growthStageLengthMeasure: string; - totalGrowthStageLength: number; - rootDepthMetric: string; - stages: Stage[]; -} - -export class Stage { - stageNumber: number; - stage: string; - waterUse: number; - stageLength: number; - rootDepth: number; - url: string; - age: number; // the days since the crop was planted -} diff --git a/liquid-prep-app/src/app/models/Field.ts b/liquid-prep-app/src/app/models/Field.ts index 1c58d64..b352635 100644 --- a/liquid-prep-app/src/app/models/Field.ts +++ b/liquid-prep-app/src/app/models/Field.ts @@ -4,7 +4,7 @@ export class Field { id: string; fieldName: string; description?: string; - crop: Crop; //Get Crop Data + crop: Crop; //Get Crop Data, one field-one crop plantDate: Date; sensors?: any; // Get Sensor Data } diff --git a/liquid-prep-app/src/app/service/CropDataService.ts b/liquid-prep-app/src/app/service/CropDataService.ts index b330962..62a7f60 100644 --- a/liquid-prep-app/src/app/service/CropDataService.ts +++ b/liquid-prep-app/src/app/service/CropDataService.ts @@ -40,59 +40,7 @@ export class CropDataService { private cropStaticInfoFile = 'assets/json/CropStaticInfoMapping.json'; private crop: Crop; // used for passing data between components - - public getCropsListData(): Observable { - return new Observable((observer: Observer) => { - this.dataService.getCropsList().subscribe( - (cropsList: any) => { - const cropListData = cropsList.data.docs; - // Map the crop images - if (cropListData) { - cropListData.map((crop) => { - crop.id = crop._id; - this.fetchCropListImage(crop); - }); - this.storeCropListInSession(cropListData); - const filteredCropList = this.filterOutExistingCrops(cropListData); - observer.next(filteredCropList); - observer.complete(); - } else { - observer.error('crops list is null or empty'); - } - }, - (err) => { - observer.error( - 'Error getting crop data: ' + (err.message ? err.message : err) - ); - } - ); - }); - } - - public getCropData(id): Observable { - return new Observable((observer: Observer) => { - this.dataService.getCropInfo(id).subscribe( - (cropInfo: any) => { - const cropData: Crop = cropInfo.data.docs[0]; - if (cropData) { - cropData.id = cropInfo.data.docs[0]._id; - this.fetchCropStageImages(cropData); - observer.next(cropData); - observer.complete(); - } else { - observer.error('crops data is null or empty'); - } - }, - (err) => { - observer.error( - 'Error getting crop data: ' + (err.message ? err.message : err) - ); - } - ); - }); - } - - // store crops list in session storage +// store crops list in session storage public storeCropListInSession(cropsListData) { this.getCropListFromSessionStorage().subscribe( (cropsList: Crop[]) => { @@ -231,36 +179,6 @@ export class CropDataService { }); }); } - - private fetchCropStageImages(crop: Crop) { - this.getCropGrowthStageImageMapping().subscribe( - (cropGrowthStageImageMapping: ImageMapping) => { - if (cropGrowthStageImageMapping != null) { - if (crop.cropGrowthStage) { - crop.cropGrowthStage.stages.forEach((stage) => { - const stageUrl = - cropGrowthStageImageMapping.cropStageMap[ - stage.stageNumber.toString() - ].url; - stage.url = stageUrl; - }); - } - } else { - if (crop.cropGrowthStage) { - crop.cropGrowthStage.stages.forEach((stage) => { - const stageUrl = - '../assets/crops-images/stage' + stage.stageNumber + '.png'; - stage.url = stageUrl; - }); - } - } - }, - (err) => { - console.error('fetchCropStageImages', err); - } - ); - } - private fetchCropListImage(crop: Crop) { this.getCropImageMapping().subscribe( (cropImageMapping: ImageMapping) => { @@ -378,7 +296,6 @@ export class CropDataService { id: item._id, cropName: item.cropName, type: '', - cropGrowthStage: null, url: imageUrl, facts: null, seedingDate: null, diff --git a/liquid-prep-app/src/app/service/FieldDataService.ts b/liquid-prep-app/src/app/service/FieldDataService.ts index 3ea86b8..5f0176f 100644 --- a/liquid-prep-app/src/app/service/FieldDataService.ts +++ b/liquid-prep-app/src/app/service/FieldDataService.ts @@ -1,21 +1,11 @@ -import { Inject, Injectable } from '@angular/core'; -import { from, Observable, Observer, of } from 'rxjs'; -import { ImageMapping } from '../models/ImageMapping'; -import { DataService } from './DataService'; -import { HttpClient } from '@angular/common/http'; -import { - LOCAL_STORAGE, - SESSION_STORAGE, - StorageService, -} from 'ngx-webstorage-service'; +import {Inject, Injectable} from '@angular/core'; +import {DataService} from './DataService'; +import {HttpClient} from '@angular/common/http'; +import {LOCAL_STORAGE, SESSION_STORAGE, StorageService,} from 'ngx-webstorage-service'; -import { DateTimeUtil } from '../utility/DateTimeUtil'; -import { catchError, map, mergeMap, toArray } from 'rxjs/operators'; -import { Field } from '../models/Field'; -import config from '../../config.json'; +import {Field} from '../models/Field'; -const FIELD_LIST_KEY = 'field-list-test'; -const FIELD_STORAGE_KEY = 'my-fields-test'; +const FIELD_STORAGE_KEY = 'my-fields'; @Injectable({ providedIn: 'root', @@ -85,7 +75,6 @@ export class FieldDataService { private getEmptyMyFields(): Field[] { - const emptyArray: Field[] = []; - return emptyArray; + return []; } } diff --git a/liquid-prep-app/src/app/service/WaterAdviceService.ts b/liquid-prep-app/src/app/service/WaterAdviceService.ts index aabdefb..dbbe147 100644 --- a/liquid-prep-app/src/app/service/WaterAdviceService.ts +++ b/liquid-prep-app/src/app/service/WaterAdviceService.ts @@ -81,7 +81,7 @@ export class WaterAdviceService { } public getWaterAdviceByCrop(crop: Crop): Observable{ - const lastReading = crop.measureRecord[crop.measureRecord.length - 1].measureValue; + const lastReading = crop.measureRecord && crop.measureRecord.length > 0 ? crop.measureRecord[crop.measureRecord.length - 1].measureValue : -1; const soilMoisture = this.soilMoistureService.getSoilMoistureByPercentage(lastReading); return new Observable((observer: Observer) => { this.weatherDataService.getTodayWeather().subscribe(todayWeather => {