def setDatabase(self, settingsDatabase):
     self.__hourlyRestrictionsTable = RMUserSettingsHourlyRestrictionsTable(
         settingsDatabase)
 def setDatabase(self, settingsDatabase):
     self.__hourlyRestrictionsTable = RMUserSettingsHourlyRestrictionsTable(settingsDatabase)
class RMRestrictions:
    def __init__(self):

        self.globalRestrictions = RMUserSettingsGlobalRestrictions()
        self.rainSensor = RMRainSensor()
        self.rainSensorSoftware = RMRainSensorSoftware()
        self.hourlyRestrictions = {}

        self.__dayMinTemperature = {}  # Used by freeze protect
        self.__hourlyRestrictionsTable = None

    def setDatabase(self, settingsDatabase):
        self.__hourlyRestrictionsTable = RMUserSettingsHourlyRestrictionsTable(
            settingsDatabase)

    def loadSettings(self):
        self.__hourlyRestrictionsTable.loadAllRestrictions(
            self.hourlyRestrictions, RMUserSettingsHourlyRestriction)

    # -----------------------------------------------------------------------------------------------------------
    #
    #
    def newHourlyRestriction(self,
                             dayMinuteStart,
                             minuteDuration,
                             dayList=None):  # dayList all 1 - Daily

        if dayList is None:
            dayList = [1] * 7

        if len(dayList) < 7:
            log.error("Invalid day list received")
            return False

        r = RMUserSettingsHourlyRestriction()
        r.dayStartMinute = dayMinuteStart
        r.minuteDuration = minuteDuration
        r.onWeekDays = [d for d in dayList]

        # Save the restriction
        result = self.__hourlyRestrictionsTable.saveRestriction(r)

        self.hourlyRestrictions[r.uid] = r

        return r

    def updateHourlyRestriction(self,
                                uid,
                                dayMinuteStart,
                                minuteDuration,
                                dayList=None):

        r = self.hourlyRestrictions.get(uid, None)

        if r is not None:
            r.update(dayMinuteStart, minuteDuration, dayList)
            # Save the restriction in DB
            result = self.__hourlyRestrictionsTable.saveRestriction(r)

        return r

    def deleteHourlyRestriction(self, uid):
        if uid is None or not uid in self.hourlyRestrictions:
            return False

        if self.__hourlyRestrictionsTable.deleteRestriction(uid):
            self.hourlyRestrictions.pop(uid)
            return True
        return False

    # -----------------------------------------------------------------------------------------------------------
    # Checks if timestamp is in the raindelay restriction. Returns -1 if not or remaining seconds if it is
    #
    def getRainDelayRestriction(self, timestamp):

        rainDelayEndTimestamp = self.globalRestrictions.rainDelayStartTime + self.globalRestrictions.rainDelayDuration

        if self.globalRestrictions.rainDelayStartTime <= timestamp and timestamp <= rainDelayEndTimestamp:
            return (rainDelayEndTimestamp - timestamp)

        return -1

    # --------------------------------------------------------------------------------------------------------------
    # Checks if day is a restriction that affects the entire day (month, day of week, freeze protect, or rain delay)
    # Returns the exact restriction id
    #
    def getDayGlobalRestriction(self, timestamp, ignoreFreezeProtect=False):
        # Cyclic import workaround
        from RMDataFramework.rmMainDataRecords import RMZoneWateringFlag

        if self.isInMonthRestriction(timestamp):
            return RMZoneWateringFlag.zwfRestrictionMonth

        if self.isInDayRestriction(timestamp):
            return RMZoneWateringFlag.zwfRestrictionDay

        if self.getRainDelayRestriction(timestamp) > 0:
            log.debug("Restricted for rain delay")
            return RMZoneWateringFlag.zwfRestrictionRainDelay

        if not ignoreFreezeProtect and self.isInFreezeProtect(timestamp):
            return RMZoneWateringFlag.zwfRestrictionFreezeProtect

        return RMZoneWateringFlag.zwfNormal

    def getDayHourlyRestriction(self, timestamp):
        dayTimestamp = rmGetStartOfDay(timestamp)
        d = datetime.fromtimestamp(dayTimestamp)
        weekDay = d.weekday()
        rWithTimestamps = []

        # generate timestamp restriction array from dayMinuteDuration -> +minuteDuration taking onWeekDays in account
        for r in self.hourlyRestrictions.values():
            if r.onWeekDays[weekDay]:
                rWithTimestamps.append((dayTimestamp + r.dayStartMinute * 60,
                                        dayTimestamp + r.dayStartMinute * 60 +
                                        r.minuteDuration * 60))  # in seconds

        rUnion = []
        for begin, end in sorted(rWithTimestamps):
            if rUnion and rUnion[-1][
                    1] >= begin - 1 * 60:  # intersect by minute a timestamp in seconds
                rUnion[-1] = (rUnion[-1][0], end)
            else:
                rUnion.append((begin, end))

        # DEBUG Human readable format
        wantRestrictionTrace = False
        if wantRestrictionTrace:
            log.debug("USER RESTRICTIONS")
            for r in self.hourlyRestrictions.values():
                log.debug(" (%d:%d - %d:%d), " %
                          (r.dayStartMinute / 60, r.dayStartMinute % 60,
                           (r.dayStartMinute + r.minuteDuration) / 60,
                           (r.dayStartMinute + r.minuteDuration) % 60))

            log.debug("USER TIMESTAMP RESTRICTIONS")
            for t in sorted(rWithTimestamps):
                s = datetime.utcfromtimestamp(t[0])
                e = datetime.utcfromtimestamp(t[1])
                log.debug(" (%d:%d - %d:%d), " %
                          (s.hour, s.minute, e.hour, e.minute))

            log.debug("INTERSECTED RESTRICTIONS")
            for t in sorted(rUnion):
                s = datetime.utcfromtimestamp(t[0])
                e = datetime.utcfromtimestamp(t[1])
                log.debug(" (%d:%d - %d:%d), " %
                          (s.hour, s.minute, e.hour, e.minute))
        else:
            if rUnion:
                log.debug("Day %d restrictions %s" % (dayTimestamp, rUnion))
        ### END DEBUG Human readable format

        return rUnion

    def getMinTemperature(self):
        return self.__dayMinTemperature.get(
            rmGetStartOfDay(rmCurrentTimestamp()), None)

    def isInFreezeProtect(self, timestamp):
        if self.globalRestrictions.useFreezeControlTemp:
            dayMinTemp = self.__dayMinTemperature.get(
                rmGetStartOfDay(timestamp), None)

            if dayMinTemp is None:
                log.debug(
                    "No minimum temperature for %d found, won't restrict." %
                    rmGetStartOfDay(timestamp))
                return False

            if int(dayMinTemp) <= int(
                    self.globalRestrictions.minFreezeControlTemp):
                log.debug("Restricted for minimum temperature %s <= %s" % \
                          (`dayMinTemp`, `self.globalRestrictions.minFreezeControlTemp`))
                return True

        return False

    def isInMonthRestriction(self, timestamp):
        d = datetime.fromtimestamp(timestamp)
        currentMonth = d.month - 1

        if self.globalRestrictions.noWaterInMonths[currentMonth] == 1:
            log.debug("Restricted for %d month" % (currentMonth))
            return True

        return False

    def isInDayRestriction(self, timestamp):
        d = datetime.fromtimestamp(timestamp)
        currentWeekDay = d.weekday()

        if self.globalRestrictions.noWaterInWeekDays[currentWeekDay] == 1:
            log.debug("Restricted for %d weekday" % currentWeekDay)
            return True

        return False

    # Used by FreezeProtect restriction
    def setDayMinTemperature(self, dayTimestamp, minTemperature):
        if dayTimestamp is not None:
            self.__dayMinTemperature[dayTimestamp] = minTemperature
            log.debug("Setting minimum temperature %f for day: %d(%s)" %
                      (minTemperature, dayTimestamp,
                       rmTimestampToDateAsString(dayTimestamp)))

    def clearDayMinTemperature(self):
        self.__dayMinTemperature = {}
class RMRestrictions:
    def __init__(self):

        self.globalRestrictions = RMUserSettingsGlobalRestrictions()
        self.rainSensor = RMRainSensor()
        self.rainSensorSoftware = RMRainSensorSoftware()
        self.hourlyRestrictions = {}

        self.__dayMinTemperature = {} # Used by freeze protect
        self.__hourlyRestrictionsTable = None

    def setDatabase(self, settingsDatabase):
        self.__hourlyRestrictionsTable = RMUserSettingsHourlyRestrictionsTable(settingsDatabase)

    def loadSettings(self):
        self.__hourlyRestrictionsTable.loadAllRestrictions(self.hourlyRestrictions, RMUserSettingsHourlyRestriction)

    # -----------------------------------------------------------------------------------------------------------
    #
    #
    def newHourlyRestriction(self, dayMinuteStart, minuteDuration, dayList=None):  # dayList all 1 - Daily

        if dayList is None:
            dayList = [1] * 7

        if len(dayList) < 7:
            log.error("Invalid day list received")
            return False

        r = RMUserSettingsHourlyRestriction()
        r.dayStartMinute = dayMinuteStart
        r.minuteDuration = minuteDuration
        r.onWeekDays = [d for d in dayList]

        # Save the restriction
        result = self.__hourlyRestrictionsTable.saveRestriction(r)

        self.hourlyRestrictions[r.uid] = r

        return r

    def updateHourlyRestriction(self, uid, dayMinuteStart, minuteDuration, dayList=None):

        r = self.hourlyRestrictions.get(uid, None)

        if r is not None:
            r.update(dayMinuteStart, minuteDuration, dayList)
            # Save the restriction in DB
            result = self.__hourlyRestrictionsTable.saveRestriction(r)

        return r


    def deleteHourlyRestriction(self, uid):
        if uid is None or not uid in self.hourlyRestrictions:
            return False

        if self.__hourlyRestrictionsTable.deleteRestriction(uid):
            self.hourlyRestrictions.pop(uid)
            return True
        return False

    # -----------------------------------------------------------------------------------------------------------
    # Checks if timestamp is in the raindelay restriction. Returns -1 if not or remaining seconds if it is
    #
    def getRainDelayRestriction(self, timestamp):

        rainDelayEndTimestamp = self.globalRestrictions.rainDelayStartTime + self.globalRestrictions.rainDelayDuration

        if self.globalRestrictions.rainDelayStartTime <= timestamp and timestamp <= rainDelayEndTimestamp:
            return (rainDelayEndTimestamp - timestamp)

        return -1

    # --------------------------------------------------------------------------------------------------------------
    # Checks if day is a restriction that affects the entire day (month, day of week, freeze protect, or rain delay)
    # Returns the exact restriction id
    #
    def getDayGlobalRestriction(self, timestamp, ignoreFreezeProtect=False):
        # Cyclic import workaround
        from RMDataFramework.rmMainDataRecords import RMZoneWateringFlag

        if self.isInMonthRestriction(timestamp):
            return RMZoneWateringFlag.zwfRestrictionMonth

        if self.isInDayRestriction(timestamp):
            return RMZoneWateringFlag.zwfRestrictionDay

        if self.getRainDelayRestriction(timestamp) > 0:
            log.debug("Restricted for rain delay")
            return RMZoneWateringFlag.zwfRestrictionRainDelay

        if not ignoreFreezeProtect and self.isInFreezeProtect(timestamp):
            return RMZoneWateringFlag.zwfRestrictionFreezeProtect

        return RMZoneWateringFlag.zwfNormal

    def getDayHourlyRestriction(self, timestamp):
        dayTimestamp = rmGetStartOfDay(timestamp)
        d = datetime.fromtimestamp(dayTimestamp)
        weekDay = d.weekday()
        rWithTimestamps = []

        # generate timestamp restriction array from dayMinuteDuration -> +minuteDuration taking onWeekDays in account
        for r in self.hourlyRestrictions.values():
            if r.onWeekDays[weekDay]:
                rWithTimestamps.append((dayTimestamp + r.dayStartMinute * 60,
                                        dayTimestamp + r.dayStartMinute * 60 + r.minuteDuration * 60))  # in seconds

        rUnion = []
        for begin, end in sorted(rWithTimestamps):
            if rUnion and rUnion[-1][1] >= begin - 1 * 60:  # intersect by minute a timestamp in seconds
                rUnion[-1] = (rUnion[-1][0], end)
            else:
                rUnion.append((begin, end))

        # DEBUG Human readable format
        wantRestrictionTrace = False
        if wantRestrictionTrace:
            log.debug("USER RESTRICTIONS")
            for r in self.hourlyRestrictions.values():
                log.debug(" (%d:%d - %d:%d), " % (r.dayStartMinute / 60, r.dayStartMinute % 60,
                                                  (r.dayStartMinute + r.minuteDuration) / 60, (r.dayStartMinute + r.minuteDuration) % 60 ))

            log.debug("USER TIMESTAMP RESTRICTIONS")
            for t in sorted(rWithTimestamps):
                s = datetime.utcfromtimestamp(t[0])
                e = datetime.utcfromtimestamp(t[1])
                log.debug(" (%d:%d - %d:%d), " % (s.hour, s.minute, e.hour, e.minute))

            log.debug("INTERSECTED RESTRICTIONS")
            for t in sorted(rUnion):
                s = datetime.utcfromtimestamp(t[0])
                e = datetime.utcfromtimestamp(t[1])
                log.debug(" (%d:%d - %d:%d), " % (s.hour, s.minute, e.hour, e.minute))
        else:
            if rUnion:
                log.debug("Day %d restrictions %s" % (dayTimestamp, rUnion))
        ### END DEBUG Human readable format

        return rUnion

    def getMinTemperature(self):
        return self.__dayMinTemperature.get(rmGetStartOfDay(rmCurrentTimestamp()), None)

    def isInFreezeProtect(self, timestamp):
        if self.globalRestrictions.useFreezeControlTemp:
            dayMinTemp = self.__dayMinTemperature.get(rmGetStartOfDay(timestamp), None)

            if dayMinTemp is None:
                log.debug("No minimum temperature for %d found, won't restrict." % rmGetStartOfDay(timestamp))
                return False

            if int(dayMinTemp) <= int(self.globalRestrictions.minFreezeControlTemp):
                log.debug("Restricted for minimum temperature %s <= %s" % \
                          (`dayMinTemp`, `self.globalRestrictions.minFreezeControlTemp`))
                return True

        return False

    def isInMonthRestriction(self, timestamp):
        d = datetime.fromtimestamp(timestamp)
        currentMonth = d.month - 1

        if self.globalRestrictions.noWaterInMonths[currentMonth] == 1:
            log.debug("Restricted for %d month" % (currentMonth))
            return True

        return False

    def isInDayRestriction(self, timestamp):
        d = datetime.fromtimestamp(timestamp)
        currentWeekDay = d.weekday()

        if self.globalRestrictions.noWaterInWeekDays[currentWeekDay] == 1:
            log.debug("Restricted for %d weekday" % currentWeekDay)
            return True

        return False

    # Used by FreezeProtect restriction
    def setDayMinTemperature(self, dayTimestamp, minTemperature):
        if dayTimestamp is not None:
            self.__dayMinTemperature[dayTimestamp] = minTemperature
            log.debug("Setting minimum temperature %f for day: %d(%s)" %
                      (minTemperature, dayTimestamp, rmTimestampToDateAsString(dayTimestamp)))

    def clearDayMinTemperature(self):
        self.__dayMinTemperature = {}