def to_faren(temp_c): return quantize(Decimal(9.0) * temp_c /Decimal(5.0) + Decimal(32.0))
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 to_inches(original, original_units): if original_units == "mm": original = original / Decimal(10) return quantize(original * Decimal(0.3937008))