def get_water_register_list(report_date, crop_season, field): # Different formulas whether today is during or after crop season: # Within crop season: everything till today plus 10 days # After crop season: everything cs = CropSeason.objects.get(pk=crop_season) fld = Field.objects.get(pk=field) start_date = cs.season_start_date end_date = min(report_date + datetime.timedelta(days = 10), cs.season_end_date) wr_list = WaterRegister.objects.filter(crop_season = cs, field = fld, datetime__lte = d2dt_min(end_date), datetime__gte = d2dt_max(start_date) ).order_by('datetime') return wr_list
def get_queryset(self): user = self.request.user queryset = super(ProbeReadingFormsetView, self).get_queryset() query = Q() radioIds = self.getRadioIds(user, self.season, self.field) if len(radioIds) < 1: query = query | Q( radio_id="" ) else: for radio_id in radioIds: query = query | Q( radio_id=radio_id ) queryset = queryset.filter(query).distinct().order_by('radio_id','datetime') if self.season: crop_season = CropSeason.objects.get(pk=self.season.pk) queryset = queryset.filter( datetime__gte=d2dt_min(crop_season.season_start_date), datetime__lte=d2dt_max(crop_season.season_end_date)) return queryset.distinct()
def generate_water_register(crop_season, field, user, start_date=None, report_date=None, force=False): #### ## Determine planting date, and stop calculation if no planting has been done planting_event = CropSeasonEvent.objects.filter(crop_season=crop_season, field=field, crop_event__name='Planting').order_by("-date").first() if not planting_event: return 0 ## #### #### ## Determine the first and last first event date to compute if start_date is None: start_date = planting_event.date if report_date is None: report_date = datetime.now().date() if COMPUTE_FULL_SEASON: end_date = crop_season.season_end_date + timedelta(1) else: today_plus_delta = report_date + timedelta(days=WATER_REGISTER_DELTA) latest_water_register = WaterRegister.objects.filter(crop_season=crop_season, field=field ).order_by('datetime').last() if latest_water_register: last_register_date = max(today_plus_delta, latest_water_register.datetime.date()) else: last_register_date = today_plus_delta end_date = min(last_register_date, crop_season.season_end_date) + timedelta(1) ## #### ## Cache values / queries for later use water_history_query = WaterHistory.objects.filter(crop_season=crop_season, field=field, ignore = False)[:] crop_season_events_query = CropSeasonEvent.objects.filter(crop_season=crop_season, field=field).distinct().select_related('crop_event')[:] #### if force: ## force recomputation from first date first_process_date = start_date else: ## Use modification times to find the earliest water register to update first_process_date = max(crop_season.season_start_date, earliest_register_to_update(report_date, crop_season, field)) wr_query = WaterRegister.objects.filter(crop_season=crop_season, field=field, datetime__gte=d2dt_min(first_process_date), datetime__lte=d2dt_max(end_date) ).all() maxWater = float(field.soil_type.max_available_water) minWater = calculateAWC_min( crop_season, field ) if first_process_date <= crop_season.season_start_date: ## Assume that each field starts with a full water profile AWC_initial = maxWater else: try: wr_yesterday = WaterRegister.objects.get(crop_season=crop_season, field=field, datetime__range=d2dt_range(first_process_date - timedelta(days=1)) ) AWC_initial = wr_yesterday.average_water_content except: raise RuntimeError("No previous water_register record on " + str(first_process_date) ); ## First pass, calculate water profile (AWC) #if DEBUG: print "First pass, calculate water profile (AWC)" temps_since_last_water_date = [] wr_prev = None # if DEBUG: print "Date range: %s to %s" % (first_process_date, end_date) ## Some optimization to do here: After the first pass we know the prev record is there. for date in daterange(first_process_date, end_date): #### ## Get AWC for yesterday, and copy the irrigate_to_max_seen, irrigate_to_max_achieved flags ## yesterday = date - timedelta(days=1) ## Check if we have (cached) the water register object for ## yesterday, if so grab the AWC, otherwise use the default ## maximum for the soil type if (wr_prev is None) or (wr_prev.date != yesterday): try: wr_prev = wr_query.filter(datetime__range=d2dt_range(yesterday))[0] AWC_prev = wr_prev.average_water_content irrigate_to_max_seen_prev = wr_prev.irrigate_to_max_seen irrigate_to_max_achieved_prev = wr_prev.irrigate_to_max_achieved except ( ObjectDoesNotExist, IndexError, ): AWC_prev = AWC_initial irrigate_to_max_seen_prev = False irrigate_to_max_achieved_prev = False else: AWC_prev = wr_prev.average_water_content irrigate_to_max_seen_prev = wr_prev.irrigate_to_max_seen irrigate_to_max_achieved_prev = wr_prev.irrigate_to_max_achieved irrigate_to_max_seen_prev = wr_prev.irrigate_to_max_seen irrigate_to_max_achieved_prev = wr_prev.irrigate_to_max_achieved #### #### ## Get or Create a water register object (db record) for today ## Delete previous water register entries # wr_query.filter(datetime__range=d2dt_range(date)).delete() # wr = WaterRegister(crop_season = crop_season, # field = field, # datetime__range = d2dt_range(date) # ) try: wr = wr_query.filter(datetime__range=d2dt_range(date))[0] computed_from_probes = False irrigate_flag = False too_hot_flag = False check_sensors_flag = False dry_down_flag = False # clear out existing flags wr.irrigate_flag = False wr.too_hot_flag = False wr.days_to_irrigation = -1 wr.check_sensors_flag = False except ( ObjectDoesNotExist, IndexError, ): wr = WaterRegister( crop_season = crop_season, field = field, datetime = d2dt_min(date) ) #### ## Copy information from crop event record ## ## * It is currently possible to assign out-of-order ## * CropSeasonEvent dates. To manage this, first select ## * CropSeasonEvents that occur _on_or_before_ today, then select the ## * last by CropEvent.order cse = crop_season_events_query.filter(date__lte=date).order_by('crop_event__order').last() ## if DEBUG: print cse if cse is None: print "No CropSeasonEvent defined for %s %s on %s. Deleting WaterRegister record and skipping computation." % ( crop_season, field, date ) del(wr) continue ce = cse.crop_event wr.crop_stage = ce.name wr.daily_water_use = ce.daily_water_use wr.max_temp_2in = ce.max_temp_2in wr.irrigate_to_max = ce.irrigate_to_max wr.do_not_irrigate = ce.do_not_irrigate wr.message = ce.irrigation_message # if DEBUG: # print "Date: %s ce.Crop Stage: %s" % ( date, ce.name ) # print "Date: %s wr.Crop Stage: %s" % ( date, wr.crop_stage ) ## #### #### ## Get (automatic) probe reading information and calculate AWC #AWC_probe = None #temp = None # Moved this validation to calculateAWC, # since there is already code to validate. AWC_probe = calculateAWC(crop_season, field, date, water_history_query) # if DEBUG: print " AWC_probe=", AWC_probe ## #### #### ## Get (manually entered) water register entries wr.rain, wr.irrigation, wr.min_temp_24_hours, wr.max_temp_24_hours = calculate_total_RainIrrigation(crop_season, field, date, water_history_query) AWC_register = float(AWC_prev) - float(wr.daily_water_use) + float(wr.rain) + float(wr.irrigation) ##if DEBUG: print " AWC_register=", AWC_register ## #### #### ## Prefer AWC from probe reading over AWC from water registry if AWC_probe is not None: wr.average_water_content = quantize(AWC_probe) wr.computed_from_probes = True else: wr.average_water_content = quantize(AWC_register) wr.computed_from_probes = False # if DEBUG: print " wr.average_water_content=", wr.average_water_content # if DEBUG: print " wr.computed_from_probes=", wr.computed_from_probes ## #### ## Enforce min and maximum soil water content based on soil type if wr.average_water_content > maxWater: #if DEBUG: print " Enforce max soil AWC: ", maxWater wr.average_water_content = maxWater if wr.average_water_content < minWater: #if DEBUG: print " Enforce min soil AWC: ", minWater wr.average_water_content = minWater ## Store user into accounting info.. if wr.cuser_id is None: wr.cuser_id = user.pk wr.muser_id = user.pk ## Calculate and store max temperature since last appreciable rainfall or irrigation if wr.average_water_content >= float(AWC_prev) + 0.1: # Max temp is only today's value wr.max_observed_temp_2in = wr.max_temp_24_hours # Reset max temp calculation temps_since_last_water_date = [] else: # Add today's temperature temps_since_last_water_date.append(wr.max_temp_24_hours) # Calculate max temp wr.max_observed_temp_2in = max(temps_since_last_water_date) ## Cache this entry for tomorrow wr_prev = wr ## Write to the database wr.save() ## Refresh query wr_query = WaterRegister.objects.filter(crop_season=crop_season, field=field, datetime__gte=d2dt_min(first_process_date), datetime__lte=d2dt_max(end_date) ).all() ## Second pass, calculate flags #if DEBUG: print "Second pass, calculate flags" irrigate_to_max_flag_seen = False irrigate_to_max_achieved = False drydown_flag = False irrigate_to_max_days = 0 nChanged = 0 for date in daterange(first_process_date, end_date): if not wr_query.filter(datetime__range=d2dt_range(date)): continue wr = wr_query.filter(datetime__range=d2dt_range(date))[0] ## Will handle both the case where the first irrigate_to_flag set to ## true was in a register not updated for this report. if wr.irrigate_to_max or wr.irrigate_to_max_seen: irrigate_to_max_flag_seen = True wr.irrigate_to_max_seen = True ##### ## If the irrigate_to_max flag has been seen, irrigate & check ## sensors until maxWater is achieved (or 3 watering days have ## occured), then no more irrigation and no more sensor checks ## ('drydown') ##### if not irrigate_to_max_flag_seen: #### ## Check if we need to irrigate *today* # clear out existing flags wr.irrigate_flag = False wr.too_hot_flag = False wr.days_to_irrigation = -1 wr.check_sensors_flag = False # never irrigate if flag is set if wr.do_not_irrigate: wr.irrigate_flag = False else: # Too dry: if wr.average_water_content <= 0.00: wr.irrigate_flag = True wr.days_to_irrigation = 0 # Too hot: if wr.max_temp_2in is not None and \ wr.max_observed_temp_2in is not None and \ wr.max_observed_temp_2in > wr.max_temp_2in: wr.irrigate_flag = True wr.days_to_irrigation = 0 wr.too_hot_flag = True #### ## Check if we need to irrigate in the next few days, based on WATER_REGISTER_DELTA #### wr.check_sensors_flag = False if wr.days_to_irrigation < 0: date_plus_delta = date + timedelta(days=(WATER_REGISTER_DELTA+1)) for date_future in daterange(date + timedelta(days=1), date_plus_delta): try: wr_future = wr_query.get(datetime__range=d2dt_range(date_future)) #wr.check_sensors_flag = wr.check_sensors_flag or (wr_future.average_water_content <= 0.00) if ( wr_future.average_water_content <= 0.00 ) and ( wr_future.do_not_irrigate == False ): wr.check_sensors_flag = True wr.days_to_irrigation = (date_future - date).days break except ObjectDoesNotExist: pass else: # execute below if irrigate_to_max_flag_seen is true if wr.irrigate_to_max_achieved or irrigate_to_max_achieved or irrigate_to_max_days >= 3: wr.irrigate_flag = False wr.dry_down_flag = True else: irrigate_to_max_days += 1 if wr.average_water_content >= maxWater: wr.irrigate_to_max_achieved = True irrigate_to_max_achieved = True wr.irrigate_flag = False wr.check_sensors_flag = False wr.dry_down_flag = True else: wr.irrigate_flag = True wr.check_sensors_flag = True ## Write to the database wr.save() nChanged += 1 # reset the dependency date field.earliest_changed_dependency_date = None field.save() return nChanged
def get_daily_report(farm, field, crop_season, user, report_date): generate_water_register(crop_season, field, user, None, report_date) # Will add an entry for this field and farm srf = SummaryReportFields() srf.field = field srf.farm = farm srf.crop = crop_season.crop srf.water_register_url = reverse('unified_water_season_field_date', kwargs={'season': crop_season.pk, 'field': field.pk, 'date': report_date} ) # Get the water registry for the crop season / field wr_list = WaterRegister.objects.filter(crop_season = crop_season, field = field).order_by('-datetime').filter(Q(datetime__lte = d2dt_max(report_date))) if len(wr_list) == 0: return None wr = wr_list[0] if (wr is not None): srf.growth_stage = wr.crop_stage srf.message = wr.message (srf.cumulative_rain, srf.cumulative_irrigation_vol) = cumulative_water(wr_list) srf.days_to_irrigation = wr.days_to_irrigation # Get the last event for the field. It will be either rain, irrigation, # manual reading or automated reading. # First get the latest water event from water history # The water history contains a field list (although right now it # may be limited to one field.) Still, assume there could be more # than one. whList = WaterHistory.objects.filter(crop_season = crop_season) # Just keep the ones with one of the fields equal to current field. latest_water_record = None if len(whList) > 0: latestWH = whList.filter(field = field) latest_water_record = whList.latest('datetime') # Now get the latest measurement record from the probes. # First all the probes for a given pair (field, crop season) probe_list = Probe.objects.filter(field = field, crop_season = crop_season) latest_probe_reading = None if (len(probe_list) > 0): radio_id = probe_list[0].radio_id probe_readings = ProbeReading.objects.filter(radio_id = radio_id) # Same radio id can be used across seasons. Filter based on (Start, end) of # crop season probe_readings = probe_readings.filter(datetime__gte = crop_season.season_start_date, datetime__lte = crop_season.season_end_date) if (probe_readings is not None and len(probe_readings) > 0): latest_probe_reading = probe_readings.latest('datetime') # Compare both records, keep most recent. # Probably easier way in python to do this. Can worry later. latest_is_wr = None if (latest_water_record is None and latest_probe_reading is None): pass elif (latest_water_record is not None and latest_probe_reading is not None): latest_is_wr = (latest_water_record.datetime.date() > latest_probe_reading.datetime.date()) else: if (latest_water_record is not None): latest_is_wr = True else: latest_is_wr = False if (latest_is_wr is not None): if (latest_is_wr): srf.last_data_entry_type = "Rain or irrigation" srf.time_last_data_entry = latest_water_record.datetime else: srf.last_data_entry_type = "Probe reading" srf.time_last_data_entry = latest_probe_reading.datetime # Add link to water register # Add the water register object to get next irrigation date, or status. # Only add if planting season is not over. if (crop_season.season_end_date >= report_date): srf.water_register_object = wr return srf
def get_cumulative_report(farm, field, crop_season, user, start_date, end_date): generate_water_register(crop_season, field, user, None, end_date) # Will add an entry for this field and farm crf = CumulativeReportFields() crf.field = field crf.farm = farm crf.crop = crop_season.crop crf.start_date = crop_season.season_start_date crf.end_date = crop_season.season_end_date # Get the water registry for the crop season / field wr_list = WaterRegister.objects.filter(crop_season = crop_season, field = field).order_by('-datetime').filter(Q(datetime__gte = d2dt_min(start_date), \ datetime__lte = d2dt_max(end_date) ) ) if len(wr_list) == 0: return None wr = wr_list[0] if (wr is not None): (crf.cumulative_rain, crf.cumulative_irrigation_vol, crf.cumulative_water_use, crf.days_of_irrigation) = cumulative_water(wr_list) # Add link to water register crf.water_register_url = reverse('unified_water_season_field_date', kwargs={'season':crop_season.pk, 'field':field.pk, 'date':end_date } ) return crf