def checkMaxDrop(city, name, recentLimit=180):
    maxDiff = 0.0
    maxDiffD = None
    lastV = None
    lastD = None
    lastUtcTime = None
    for utcTime in sorted(data.keys(), reverse=True):
        localTime = utcTime.astimezone(stations.city[city].timezone)
        date = localTime.date()
        v = data[utcTime].TEMP
        if v is None:
            lastV = None
            continue
        if lastV != None and (lastUtcTime - utcTime) == dt.timedelta(hours=1):
            diff = v - lastV
            if diff >= maxDiff:
                if (dt.date.today() - date).days > recentLimit:
                    dayName = dayOfWeek(maxDiffD[1].date())
                    if (dt.date.today() - maxDiffD[0].date()).days == 1:
                        dayName = 'Yesterday'
                    elif (dt.date.today() - maxDiffD[0].date()).days == 0:
                        dayName = 'Today'
                    since = sinceWhen.sinceWhen(city, daily.AVG_WIND.index,
                                                date)
                    text = ("{} between {}➜{},"
                            " the temp dropped from {:.1f}℃➜{:.1f}℃"
                            " which was #{}'s largest 1-hour drop {}".format(
                                dayName,
                                clock12(city, 'H{}'.format(maxDiffD[0].hour)),
                                clock12(city, 'H{}'.format(maxDiffD[1].hour)),
                                float(maxDiffValue[0]), float(maxDiffValue[1]),
                                stations.city[city].name, since))
                    (use, tweet) = shouldTweetSuffix(city, text)
                    if use:
                        delayedTweets.addToListForCity(city,
                                                       tweet,
                                                       urgent=True)
                        print('ok')
                    else:
                        print('skipping')
                    break
                if (utcTime.timestamp() <= lastdatetimestamp.get(name, 0)
                        or (dt.date.today() - date).days >= 7):
                    break
                maxDiff = diff
                maxDiffValue = v, lastV
                maxDiffD = localTime, lastD
        lastV = v
        lastD = localTime
        lastUtcTime = utcTime
    lastdatetimestamp[name] = int(sorted(data.keys())[-1].timestamp())
def checkMaxDrop2(city, name, recentLimit=180):
    maxDiff = 0.0
    maxDiffD = None
    lastV = None
    lastD = None
    lastUtcTime = None
    for utcTime, v in genReverseTemperatures():
        localTime = utcTime.astimezone(stations.city[city].timezone)
        date = localTime.date()
        print(v)
        if v is None:
            lastV = None
            continue
        if lastV != None:
            diff = v - lastV
            if diff >= maxDiff:
                if (dt.date.today() - date).days > recentLimit:
                    dayName = dayOfWeek(maxDiffD[1].date())
                    if (dt.date.today() - maxDiffD[0].date()).days == 1:
                        dayName = 'Yesterday'
                    elif (dt.date.today() - maxDiffD[0].date()).days == 0:
                        dayName = 'Today'
                    since = sinceWhen.sinceWhen(
                        city,
                        daily.AVG_WIND.index,
                        date)
                    text = ("{} between {}➜{},"
                            " the temp dropped from {:.1f}℃➜{:.1f}℃"
                            " which was #{}'s largest 1-hour drop {}"
                            .format(dayName,
                                    clock12(city, 'H{}'.format(maxDiffD[0].hour)),
                                    clock12(city, 'H{}'.format(maxDiffD[1].hour)),
                                    float(maxDiffValue[0]),
                                    float(maxDiffValue[1]),
                                    stations.city[city].name,
                                    since))
                    (use, tweet) = shouldTweetSuffix(
                        city, text)
                    if use:
                        delayedTweets.addToListForCity(city, tweet, urgent=True)
                        print('ok')
                    else:
                        print('skipping')
                    break
                maxDiff = diff
                maxDiffValue = v, lastV
                maxDiffD = localTime, lastD
        lastV = v
        lastD = localTime
        lastUtcTime = utcTime
def checkMinWindchill(city, name):
    #import pudb; pu.db
    if 'MIN_WINDCHILL' in stations.city[city].skipDailyFields:
        return None
    minWindchill = 99.0
    minWindchillD = None
    for utcTime in reversed(sorted(data.keys())):
        localTime = utcTime.astimezone(stations.city[city].timezone)
        date = localTime.date()
        windchill = data[utcTime].windchill
        if windchill is not None:
            if windchill <= minWindchill:
                if (dt.date.today() - date).days > 180:
                    dayName = dayOfWeek(minWindchillD.date())
                    if (dt.date.today() - minWindchillD.date()).days == 1:
                        dayName = 'Yesterday'
                    elif (dt.date.today() - minWindchillD.date()).days == 0:
                        dayName = 'Today'
                    since = sinceWhen.sinceWhen(city,
                                                daily.MIN_WINDCHILL.index,
                                                date)
                    text = (
                        "{} at {}, the windchill was {:.1f}"
                        " which made it #{}'s windchilliest hour {}".format(
                            dayName,
                            clock12(city, 'H{}'.format(minWindchillD.hour)),
                            float(minWindchill), stations.city[city].name,
                            since))
                    (use, tweet) = shouldTweetSuffix(city, text)
                    if use:
                        delayedTweets.addToListForCity(city,
                                                       tweet,
                                                       urgent=True)
                        print('ok')
                    else:
                        print('skipping')
                    break
                if (utcTime.timestamp() <= lastdatetimestamp.get(name, 0)
                        or (dt.date.today() - date).days >= 7):
                    break
                minWindchill = windchill
                minWindchillD = localTime
    lastdatetimestamp[name] = int(sorted(data.keys())[-1].timestamp())
def checkMaxHumidity(city, name):
    if 'MEAN_HUMIDITY' in stations.city[city].skipDailyFields:
        return None
    maxHumidity = 0
    maxHumidityD = None
    for utcTime in reversed(sorted(data.keys())):
        localTime = utcTime.astimezone(stations.city[city].timezone)
        date = localTime.date()
        v = data[utcTime].REL_HUM
        if (v is not None
                and ((type(v) is str and len(v) > 0) or type(v) is not str)):
            humidity = int(v)
            if humidity >= maxHumidity:
                if (dt.date.today() - date).days > 180:
                    dayName = dayOfWeek(maxHumidityD.date())
                    if (dt.date.today() - maxHumidityD.date()).days == 1:
                        dayName = 'Yesterday'
                    elif (dt.date.today() - maxHumidityD.date()).days == 0:
                        dayName = 'Today'
                    since = sinceWhen.sinceWhen(city, daily.AVG_WIND.index,
                                                date)
                    text = ("{} at {}, the humidity was {}%"
                            " which made it #{}'s moistest hour {}".format(
                                dayName,
                                clock12(city, 'H{}'.format(maxHumidityD.hour)),
                                maxHumidity, stations.city[city].name, since))
                    #import pudb; pu.db
                    (use, tweet) = shouldTweetSuffix(city, text)
                    if use:
                        delayedTweets.addToListForCity(city,
                                                       tweet,
                                                       urgent=True)
                        print('ok')
                    else:
                        print('skipping')
                    break
                if (utcTime.timestamp() <= lastdatetimestamp.get(name, 0)
                        or (dt.date.today() - date).days >= 7):
                    break
                maxHumidity = humidity
                maxHumidityD = localTime
    lastdatetimestamp[name] = int(sorted(data.keys())[-1].timestamp())
def checkMaxWind(city, name):
    maxWind = 0.0
    maxWindD = None
    for utcTime in reversed(sorted(data.keys())):
        localTime = utcTime.astimezone(stations.city[city].timezone)
        date = localTime.date()
        v = data[utcTime].WIND_SPD
        if (v is not None
                and ((type(v) is str and len(v) > 0) or type(v) is not str)):
            wind = int(v)
            if wind >= maxWind:
                if (dt.date.today() - date).days > 180:
                    dayName = dayOfWeek(maxWindD.date())
                    if (dt.date.today() - maxWindD.date()).days == 1:
                        dayName = 'Yesterday'
                    elif (dt.date.today() - maxWindD.date()).days == 0:
                        dayName = 'Today'
                    since = sinceWhen.sinceWhen(city, daily.AVG_WIND.index,
                                                date)
                    text = ("{} at {}, the wind was {}km/h"
                            " which made it #{}'s windiest hour {}".format(
                                dayName,
                                clock12(city, 'H{}'.format(maxWindD.hour)),
                                maxWindValue, stations.city[city].name, since))
                    (use, tweet) = shouldTweetSuffix(city, text)
                    if use:
                        delayedTweets.addToListForCity(city,
                                                       tweet,
                                                       urgent=True)
                        print('ok')
                    else:
                        print('skipping')
                    break
                if (utcTime.timestamp() <= lastdatetimestamp.get(name, 0)
                        or (dt.date.today() - date).days >= 7):
                    break
                maxWind = wind
                maxWindValue = wind
                maxWindD = localTime
    lastdatetimestamp[name] = int(sorted(data.keys())[-1].timestamp())
def checkMaxRise(city, name):
    offsets = tuple(range(2))
    maxDiff = [0.0 for o in offsets]
    maxDiffValue = [None for o in offsets]
    maxDiffD = [None for o in offsets]
    lastV = deque([None for o in offsets])
    lastD = deque([None for o in offsets])
    done = [False for o in offsets]
    for utcTime, vals in fillReverseSortedGapsWithNone(data):
        if sum(done) == len(done):
            break
        if vals is None:
            lastV.insert(0, None)
            lastD.insert(0, None)
            continue
        localTime = utcTime.astimezone(stations.city[city].timezone)
        #print dateTime
        date = localTime.date()
        v = vals.TEMP
        if v is None:
            lastV.insert(0, None)
            lastD.insert(0, None)
            continue
        #print v
        for offset in offsets:
            if lastV[offset] == None or done[offset]:
                continue
            diff = lastV[offset] - v
            if diff >= maxDiff[offset]:
                if (dt.date.today() - date).days > 180:
                    dayName = dayOfWeek(maxDiffD[offset][1].date())
                    if (dt.date.today() -
                            maxDiffD[offset][0].date()).days == 1:
                        dayName = 'Yesterday'
                    elif (dt.date.today() -
                          maxDiffD[offset][0].date()).days == 0:
                        dayName = 'Today'
                    #import pudb; pu.db
                    since = sinceWhen.sinceWhen(city, daily.MAX_TEMP.index,
                                                date)
                    text = ("{} between {} and {},"
                            " the temp jumped from {:.1f}℃➜{:.1f}℃"
                            " which was #{}'s largest {}-hour jump {}".format(
                                dayName,
                                clock12(city, 'H{}'.format(
                                    maxDiffD[offset][0].hour)),
                                clock12(city, 'H{}'.format(
                                    maxDiffD[offset][1].hour)),
                                float(maxDiffValue[offset][0]),
                                float(maxDiffValue[offset][1]),
                                stations.city[city].name, offset + 1, since))
                    (use, tweet) = shouldTweetSuffix(city, text)
                    if use:
                        delayedTweets.addToListForCity(city,
                                                       tweet,
                                                       urgent=True)
                        print('ok')
                    else:
                        print('skipping')
                    done[offset] = True
                if (utcTime.timestamp() <= lastdatetimestamp.get(name, 0)
                        or (dt.date.today() - date).days >= 7):
                    done[offset] = True
                maxDiff[offset] = diff
                maxDiffValue[offset] = v, lastV[offset]
                maxDiffD[offset] = localTime, lastD[offset]
        lastV.insert(0, v)
        lastD.insert(0, localTime)
    lastdatetimestamp[name] = int(sorted(data.keys())[-1].timestamp())
    def maybeTweetRecordSince(
            self, city,
            v, recordValue, alreadyReportedRecord,
            hour, sampleDay, datemarker, maxRecord):

        #if sampleDay.day == 3 and self.field.name == 'TOTAL_SNOW_CM':
        #    import pudb; pu.db
        if ( maxRecord is False
             and self.field in (daily.MAX_HUMIDEX,
                                daily.TOTAL_RAIN_MM,
                                daily.SNOW_ON_GRND_CM,
                                daily.SPD_OF_MAX_GUST_KPH,
             ) ):
            return
        if ( maxRecord is True and self.field in (daily.MIN_WINDCHILL, ) ):
            return

        media=None
        limit=90 #days
        if self.field in (daily.MAX_TEMP, daily.MIN_TEMP,
                          daily.MAX_HUMIDEX, daily.MIN_WINDCHILL,
                          daily.AVG_WINDCHILL, daily.MEAN_TEMP,
                          daily.AVG_DEWPOINT):
            limit=400 #days
        if self.limitToThisMonth:
            limit=365*4+32 #days
        #print (sampleDay, float(v), self.field.name, repr(recordValue))
        if recordValue != None:
            ageOfRecord = (today(city) - recordValue[1]).days
        if recordValue == None or ageOfRecord > limit:
            alreadyReportedRecordTweet = None
            if alreadyReportedRecord != None:
                alreadyReportedRecordTweet = alreadyReportedRecord.tweet
                #print("\n--------------------------------------------------------------------------------\nCorrecting a previous record of {:.1f}".format(alreadyReportedRecord.value))
                #print("OldTweet: ", alreadyReportedRecord.tweet)
            dayString = 'today is'
            possessiveDayString = 'Today\'s'
            if sampleDay == today(city)-datetime.timedelta(days=1):
                dayString = 'yesterday was'
                possessiveDayString = 'Yesterday\'s'
            elif sampleDay != today(city):
                dayString = '{} was'.format(dayOfWeek(sampleDay))
                possessiveDayString = '{}\'s'.format(dayOfWeek(sampleDay))
            sinceWhat = sinceWhen.sinceWhen(
                city, self.field.index,
                recordDate = recordValue[1] if recordValue is not None else None)

            if self.field == daily.MAX_TEMP:
                recordDescription = 'warmest'
                if v > 24:
                    recordDescription = 'hottest'
                if maxRecord is False:
                    recordDescription = 'coldest'
                    tweet = ( 'With a high of just {value:.1f}{deg}, {day} {cityName}'
                              ' {record} {filter}day {since}.' )
                else:
                    tweet = ( 'With a high of {value:.1f}{deg}, {day} {cityName}'
                              ' {record} {filter}day {since}.' )
                    if hour != None:
                        tweet = ( 'With a {hour} temp of {value:.1f}{deg}, {day}'
                                  ' {cityName} {record} {filter}day {since}.' )
            elif self.field == daily.MIN_TEMP:
                recordDescription = 'warmest'
                if v > 24:
                    recordDescription = 'hottest'
                tweet = ( 'With a low of {value:.1f}{deg}, {day} {cityName} {record}'
                          ' {filter}day {since}.' )
                if maxRecord is False:
                    tweet = ( '{possessiveDayString} temp of {value:.1f}{deg} is the'
                              ' coldest {simpleCityName} has been {since}.' )
                    if self.limitToThisMonth:
                        tweet = ( '{possessiveDayString} temp of {value:.1f}{deg} is'
                                  ' the coldest {simpleCityName} has been during'
                                  ' {filter}{since}.' )
                    recordDescription = 'coldest'
                    if hour != None:
                        recordDescription = None
                        tweet = ( '{possessiveDayString} {hour} temp of {value:.1f}{deg}'
                                  ' was {cityName} coldest {since}.' )
                        if self.limitToThisMonth:
                            tweet = ( '{possessiveDayString} {hour} temp of'
                                      ' {value:.1f}{deg} was {cityName} coldest'
                                      ' {filter}{since}.' )
            elif self.field == daily.MAX_HUMIDEX:
                recordDescription = 'muggiest'
                tweet = 'With a humidex of {value:.1f}, {day} {cityName} {record} {filter}day {since}.'
                if hour != None:
                    tweet = 'With a {hour} humidex of {value:.1f}, {day} {cityName} {record} {filter}day {since}.'
            elif self.field == daily.MIN_WINDCHILL:
                assert(hour is not None)
                recordDescription = 'windchilliest'
                #tweet = 'With a windchill of {value:.1f}, {day} {cityName} {record} {filter}day {since}.'
                #if hour != None:
                tweet = 'With a {hour} windchill of {value:.1f}, {day} {cityName} {record} {filter}day {since}.'
            elif self.field == daily.TOTAL_RAIN_MM:
                recordDescription = 'wettest'
                tweet = 'With {value:.1f}mm of rain, {day} {cityName} {record} {filter}day {since}.'
            elif self.field == daily.TOTAL_SNOW_CM:
                recordDescription = 'snowiest'
                tweet = ( 'With {value:.1f}cm of snow, {day} {cityName}'
                          ' {record} {filter}day {since}.' )
                if hour != None:
                    tweet = ( 'With a {hour} snow total of {value:.0f}cm, {day} {cityName}'
                              ' {record} {filter}day {since}.' )
                    media = metarSnowChart.main(
                        city, sampleDay, recently=True, thisDayInHistory=False,
                        thisMonthInHistory=False,
                        days=1, field=daily.TOTAL_SNOW_CM)

            elif self.field == daily.SNOW_ON_GRND_CM:
                recordDescription = 'deepest'
                tweet = ( 'With {value}cm of snow-on-the-ground, {day} {cityName}'
                          ' {record} {filter}day {since}.' )
                media = recordSinceChart.main(
                    city, sampleDay,
                    recently=True, thisDayInHistory=False,
                    field=daily.SNOW_ON_GRND_CM)
            elif self.field == daily.SPD_OF_MAX_GUST_KPH:
                recordDescription = 'windiest'
                tweet = ( 'With a wind gust of {value:.1f}km/h, {day} {cityName}'
                          ' {record} {filter}day {since}.' )
            elif self.field == daily.AVG_WIND:
                recordDescription = 'windiest'
                if maxRecord is False:
                    recordDescription = 'calmest'
                tweet = 'With an avg wind of {value:.0f}km/h, {day} {cityName} {record} {filter}day {since}.'
            elif self.field == daily.MIN_HUMIDITY:
                assert(hour is not None)
                recordDescription = 'moistest'
                if maxRecord is False:
                    recordDescription = 'driest'
                    if hour != None:
                        tweet = 'With a {hour} humidity of {value:.0f}%, {day} {cityName} {record} {filter}day {since}.'
                else:
                    tweet = 'With a min humidity of {value:.0f}%, {day} {cityName} {record} {filter}day {since}.'
            elif self.field == daily.MEAN_HUMIDITY:
                recordDescription = 'moistest'
                if maxRecord is False:
                    recordDescription = 'driest'
                tweet = 'With a humidity of {value:.1f}%, {day} {cityName} {record} {filter}day {since}.'
            hourText = clock12(city, hour)
            tweet = tweet.format(value=v,
                                 deg=chr(0x2103),
                                 hour=hourText,
                                 day=dayString,
                                 possessiveDayString=possessiveDayString,
                                 cityName='#'+possessive(stations.city[city].name),
                                 simpleCityName='#'+stations.city[city].name,
                                 record=recordDescription,
                                 since=sinceWhat,
                                 filter=['',monthName(sampleDay.month, long=True)+' '][self.limitToThisMonth])
            (use, tweet) = shouldTweetSuffix(
                city, tweet,
                oldText=alreadyReportedRecordTweet)
            if use is True:
                delayedTweets.addToEndOfListForCity(city, tweet, media)
                if city == 'ottawa':
                    sendMail.sendMailConsole("*****@*****.**",
                                             self.address,
                                             subject = self.title,
                                             text = tweet,
                                             auto = True)
            self.datemarker[datemarker] = PreviousReport(v, tweet)
    def maybeTweetRecordSince(
            self, city,
            v, recordValue, alreadyReportedRecord,
            dayCount, sampleDay, datemarker):

        limit=365 #days
        #print (sampleDay, float(v), self.field.name, repr(recordValue))
        if recordValue != None:
            ageOfRecord = (today(city) - recordValue[1]).days
        if recordValue == None or ageOfRecord > limit:
            alreadyReportedRecordTweet = None
            if alreadyReportedRecord != None:
                alreadyReportedRecordTweet = alreadyReportedRecord.tweet
                #print("\n--------------------------------------------------------------------------------\nCorrecting a previous record of {:.1f}".format(alreadyReportedRecord.value))
                #print("OldTweet: ", alreadyReportedRecord.tweet)
            if dayCount == 1:
                dayString = '{} was'.format(
                    dayStr(city, sampleDay))
            else:
                dayString = '{}-{} were'.format(
                    dayStr(city, sampleDay-datetime.timedelta(days=dayCount-1)),
                    dayStr(city, sampleDay))
            sinceWhat = sinceWhen.sinceWhen(
                city, self.field.index,
                recordDate = recordValue[1] if recordValue is not None else None)

            if self.field == daily.MAX_TEMP:
                recordDescription = 'warmest'
                if v > 24:
                    recordDescription = 'hottest'
                if maxRecord is False:
                    recordDescription = 'coldest'
                    tweet = 'With a high of just {value:.1f}{deg}, {day} {cityName} {record} day {since}.'
                else:
                    tweet = 'With a high of {value:.1f}{deg}, {day} {cityName} {record} day {since}.'
                    if hour != None:
                        tweet = 'With a {hour} temp of {value:.1f}{deg}, {day} {cityName} {record} day {since}.'
            elif self.field == daily.MIN_TEMP:
                recordDescription = 'warmest'
                if v > 24:
                    recordDescription = 'hottest'
                tweet = 'With a low of {value:.1f}{deg}, {day} {cityName} {record} day {since}.'
                if maxRecord is False:
                    recordDescription = 'coldest'
                    if hour != None:
                        tweet = 'With a {hour} temp of {value:.1f}{deg}, {day} {cityName} {record} day {since}.'
            elif self.field == daily.MAX_HUMIDEX:
                recordDescription = 'muggiest'
                tweet = 'With a humidex of {value:.1f}, {day} {cityName} {record} day {since}.'
                if hour != None:
                    tweet = 'With a {hour} humidex of {value:.1f}, {day} {cityName} {record} day {since}.'
            elif self.field == daily.MIN_WINDCHILL:
                assert(hour is not None)
                recordDescription = 'windchilliest'
                #tweet = 'With a windchill of {value:.1f}, {day} {cityName} {record} day {since}.'
                #if hour != None:
                tweet = 'With a {hour} windchill of {value:.1f}, {day} {cityName} {record} day {since}.'
            elif self.field == daily.TOTAL_RAIN_MM:
                recordDescription = 'wettest'
                tweet = 'With {dayCount}-day rainfall of {value:.1f}mm, {day} {cityName} {record} {dayCount} days {since}.'
            elif self.field == daily.TOTAL_SNOW_CM:
                recordDescription = 'snowiest'
                tweet = 'With {dayCount}-day snowfall of {value:.1f}cm, {day} {cityName} {record} {dayCount} days {since}.'
            elif self.field == daily.SNOW_ON_GRND_CM:
                recordDescription = 'snowiest'
                tweet = 'With {value:.1f}cm of snow-on-the-ground, {day} {cityName} {record} day {since}.'
            elif self.field == daily.SPD_OF_MAX_GUST_KPH:
                recordDescription = 'windiest'
                tweet = 'With a wind gust of {value:.1f}km/h, {day} {cityName} {record} day {since}.'
            elif self.field == daily.AVG_WIND:
                recordDescription = 'windiest'
                tweet = 'With a {dayCount}-day wind of {value:.1f}km/h, {day} {cityName} {record} {dayCount} days {since}.'
            elif self.field == daily.MIN_HUMIDITY:
                assert(hour is not None)
                recordDescription = 'moistest'
                if maxRecord is False:
                    recordDescription = 'driest'
                    if hour != None:
                        tweet = 'With a {hour} humidity of {value:.0f}%, {day} {cityName} {record} day {since}.'
                else:
                    tweet = 'With a min humidity of {value:.0f}%, {day} {cityName} {record} day {since}.'
            elif self.field == daily.MEAN_HUMIDITY:
                recordDescription = 'moistest'
                #if maxRecord is False:
                #    recordDescription = 'driest'
                tweet = 'With a {dayCount}-day humidity of {value:.1f}%, {day} {cityName} {record} {dayCount} days {since}.'
            tweet = tweet.format(value=float(v),
                                 deg=chr(0x2103),
                                 day=dayString,
                                 cityName='#'+possessive(stations.city[city].name),
                                 record=recordDescription,
                                 since=sinceWhat,
                                 dayCount=dayCount)
            (use, tweet) = shouldTweetSuffix(
                city, tweet,
                oldText=alreadyReportedRecordTweet)
            if use is True:
                delayedTweets.addToEndOfListForCity(city, tweet)
                if city == 'ottawa':
                    sendMail.sendMailConsole("*****@*****.**",
                                             self.address,
                                             subject = self.title,
                                             text = tweet,
                                             auto = True)
            self.datemarker[datemarker] = PreviousReport(round(float(v),1), tweet)