-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.py
168 lines (133 loc) · 6.45 KB
/
main.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
from kivy.app import App
import json
import datetime
from kivy.clock import Clock
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.listview import ListItemButton
from kivy.properties import ObjectProperty, ListProperty, StringProperty, NumericProperty
from kivy.network.urlrequest import UrlRequest
from kivy.storage.jsonstore import JsonStore
from kivy.uix.modalview import ModalView
from kivy.factory import Factory
api_url = "http://api.openweathermap.org/data/2.5/"
def locations_args_converter(index, data_item):
city, country = data_item
return {'location': (city, country)}
class LocationButton(ListItemButton):
location = ListProperty()
class WeatherRoot(BoxLayout):
current_weather = ObjectProperty()
locations = ObjectProperty()
carousel = ObjectProperty()
forecast = ObjectProperty()
add_location_form = ObjectProperty()
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.store = JsonStore("weather_store.json")
if self.store.exists('locations'):
locations = self.store.get('locations')
self.locations.locations_list.adapter.data.extend(locations['locations'])
current_location = locations["current_location"]
self.show_current_weather(current_location)
else:
Clock.schedule_once(lambda dt: self.show_add_location_form())
def show_add_location_form(self):
self.add_location_form = AddLocationForm()
self.add_location_form.open()
def show_current_weather(self, location):
if location not in self.locations.locations_list.adapter.data:
self.locations.locations_list.adapter.data.append(location)
self.locations.locations_list._trigger_reset_populate()
self.store.put("locations", locations=list(self.locations.locations_list.adapter.data),
current_location=location)
self.current_weather.location = location
self.forecast.location = location
self.current_weather.update_weather()
self.forecast.update_weather()
self.carousel.load_slide(self.current_weather)
if self.add_location_form is not None:
self.add_location_form.dismiss()
class CurrentWeather(BoxLayout):
location = ListProperty(['Vienna', 'AT'])
conditions = StringProperty()
conditions_image = StringProperty()
temp = NumericProperty()
temp_min = NumericProperty()
temp_max = NumericProperty()
def update_weather(self):
config = WeatherApp.get_running_app().config
temp_type = config.getdefault("General", "temp_type", "metric").lower()
weather_template = api_url + "weather?q={},{}&units={}"
weather_url = weather_template.format(self.location[0], self.location[1], temp_type)
request = UrlRequest(weather_url, self.weather_retrieved)
def weather_retrieved(self, request, data):
data = json.loads(data.decode()) if not isinstance(data, dict) else data
self.conditions = data['weather'][0]['description']
self.conditions_image = "http://openweathermap.org/img/w/{}.png".format(
data['weather'][0]['icon'])
self.temp = data['main']['temp']
self.temp_min = data['main']['temp_min']
self.temp_max = data['main']['temp_max']
class Forecast(BoxLayout):
location = ListProperty(['Vienna', 'AT'])
forecast_container = ObjectProperty()
# TODO: get rid of repeated code
def update_weather(self):
config = WeatherApp.get_running_app().config
temp_type = config.getdefault("General", "temp_type", "metric").lower()
weather_template = api_url + "forecast/daily?q={},{}&units={}&cnt=3" # TODO: settings number of days forecast
weather_url = weather_template.format(self.location[0], self.location[1], temp_type)
request = UrlRequest(weather_url, self.weather_retrieved)
def weather_retrieved(self, request, data):
data = json.loads(data.decode()) if not isinstance(data, dict) else data
self.forecast_container.clear_widgets()
for day in data['list']:
label = Factory.ForecastLabel()
label.date = datetime.datetime.fromtimestamp(day['dt']).strftime("%a %b %d")
label.conditions = day['weather'][0]['description']
label.conditions_image = "http://openweathermap.org/img/w/{}.png".format(
day['weather'][0]['icon'])
label.temp_min = day['temp']['min']
label.temp_max = day['temp']['max']
self.forecast_container.add_widget(label)
class AddLocationForm(ModalView):
search_input = ObjectProperty()
search_results = ObjectProperty()
location_form = ObjectProperty()
def search_location(self):
if len(self.search_input.text) == 0:
self.search_results.item_strings = ["You did not enter a location"]
return
search_template = api_url + "find?q={}&type=like"
search_url = search_template.format(self.search_input.text)
request = UrlRequest(search_url, self.found_location)
def found_location(self, request, data):
data = json.loads(data.decode()) if not isinstance(data, dict) else data
cities = [(d['name'], d['sys']['country']) for d in data['list']]
self.search_results.item_strings = cities
self.search_results.adapter.data.clear()
self.search_results.adapter.data.extend(cities)
self.search_results._trigger_reset_populate()
class WeatherApp(App):
def build_config(self, config):
config.setdefaults('General', {'temp_type': "metric"})
def build_settings(self, settings):
settings.add_json_panel("Weather Settings",
self.config, data="""
[
{"type": "options",
"title": "Temperature System",
"section": "General",
"key": "temp_type",
"options":["Metric", "Imperial"]
}
]""")
def on_config_change(self, config, section, key, value):
if config is self.config and key == "temp_type":
try:
self.root.current_weather.update_weather()
self.root.forecast.update_weather()
except AttributeError:
pass
if __name__ == '__main__':
WeatherApp().run()