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
Esempio n. 2
0
    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