Completed
Push — master ( 257ced...98bfd2 )
by Matt
41s
created

Weather2.forecast()   B

Complexity

Conditions 5

Size

Total Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 4
Bugs 2 Features 1
Metric Value
cc 5
c 4
b 2
f 1
dl 0
loc 22
rs 8.3411
1
import requests 
2
import re
3
import json
4
from event import Event
5
try:
6
    from basemodule import BaseModule
7
except ImportError:
8
    from modules.basemodule import BaseModule
9
10
class Weather2(BaseModule):
11
12
    def post_init(self):
13
        weather2 = Event('__.weather2__')
14
        weather2.define(msg_definition='^\.[Ww]eather')
15
        weather2.subscribe(self)
16
17
        forecast = Event('__.forecast__')
18
        forecast.define(msg_definition='^\.[Ff]orecast')
19
        forecast.subscribe(self)
20
21
        self.bot.register_event(forecast, self)
22
        self.bot.register_event(weather2, self)
23
        self.api_key = "1fe31b3b4cfdab66"
24
    
25
    def forecast(self, url, channel):
26
      q = requests.get(url)
27
      try:
28
        q.raise_for_status()
29
      except requests.exceptions.HTTPError:
30
        self.say(channel, "Encountered an error with the WUnderground API")
31
        return None
32
      except requests.exceptions.MissingSchema:
33
        self.say(channel, "Badly formatted location?")
34
        return None
35
      results = q.json()
36
      # we're only doing two days for now
37
      counter = 0
38
      phrase = ""
39
      for item in results['forecast']['txt_forecast']['forecastday']:
40
        if counter > 3:
41
          break
42
        phrase = phrase + item['title'] + ": " + item['fcttext'] + " "
43
        counter += 1
44
45
      #print phrase
46
      return phrase[:-1] # super hackish way to remove the trailing comma
47
48
49
    def api_request(self, location, channel, command="conditions"):
50
        """location is a search string after the .weather command. This function
51
        will determine whether it is a zip code or a named location and return an
52
        appropriate API call"""
53
        #note for future: if wanted, add further detection of conditions or forecast searching
54
        query = None
55
        try:
56
          # test if is a zipcode or a single city name
57
          a = float(location.split()[0])
58
          if a and len(location.split()[0]) < 5:
59
            self.say(channel,"valid zipcode required, numbnuts")
60
            return None
61
          zipcode = re.match('[0-9]{5}', location)
62
          query = '/q/'+zipcode.string
63
        except ValueError: # it's a city name (or broken encoding on numbers or something)
64
          #create autocomplete search
65
          q = requests.get('http://autocomplete.wunderground.com/aq', params={'query':location})
66
          try:
67
              q.raise_for_status()
68
          except requests.exceptions.HTTPError:
69
              self.say(channel, "Encountered an error contacting the WUnderground API")
70
              return None
71
          results = q.json()
72
          try:
73
              #attempt to grab the 'l' field from the first result
74
              #assuming it exists, this field will be what we use to search the conditions api
75
              query = results['RESULTS'][0]['l']
76
          except (IndexError, KeyError):
77
              #in case there were no results, let channel know
78
              self.say(channel, "No results found")
79
              return None
80
81
        if query:
82
            #return the full URL of the query we want to make
83
            return 'http://api.wunderground.com/api/'+self.api_key+'/' + command + query+'.json'
84
        return None
85
86
    def get_conditions(self, query, channel):
87
        """given a fully formed query to the wundeground API, format an output string"""
88
        r = requests.get(query)
89
        try:
90
            r.raise_for_status()
91
        except requests.exceptions.HTTPError:
92
            self.say(channel, "Encountered an error contacting the WUnderground API")
93
            return
94
        weather = r.json()
95
        try:
96
            #grab the relevant data we want for formatting
97
            location = weather['current_observation']['display_location']['full']
98
            conditions = weather['current_observation']['weather']
99
            temp_f = str(weather['current_observation']['temp_f'])
100
            temp_c = str(weather['current_observation']['temp_c'])
101
            humidity = weather['current_observation']['relative_humidity']
102
        except KeyError:
103
            self.say(channel, "Unable to get weather data from results. Sorry.")
104
            return
105
        #return the formatted string of weather data
106
        return location + ': ' + conditions + ', ' + temp_f + 'F (' + temp_c + 'C). Humidity: ' + humidity
107
108
  
109
    def handle(self, event):
110
        #split the line beginning with .weather into 2 parts, the command and the search string
111
        weather_line = event.msg.split(None, 1)
112
        if len(weather_line) > 1:
113
            if event.msg.startswith(".forecast"):
114
              self.say(event.channel, "forecast " + weather_line[1] + ": " + self.forecast(self.api_request(weather_line[1], event.channel, "forecast"), event.channel))
115
              return
116
117
            #if we're sure there's actually a search string, then continue
118
            query = self.api_request(weather_line[1], event.channel)
119
            if not query:
120
                return
121
            weather = self.get_conditions(query, event.channel)
122
            if not weather:
123
                return
124
            self.say(event.channel, weather)
125
        else:
126
            #chastise the user for being silly and not actually searching for a location
127
            self.say(event.channel, "It would help if you supplied an actual location to search for.")
128