Passed
Push — master ( 9bd27c...f2edaf )
by Viktor
01:52
created

weather.GetWeatherImage   F

Complexity

Conditions 13

Size

Total Lines 124
Code Lines 80

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 13
eloc 80
nop 1
dl 0
loc 124
rs 3.5945
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Complexity

Complex classes like weather.GetWeatherImage often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
package weather
2
3
import (
4
	"bytes"
5
	"encoding/json"
6
	"fmt"
7
	"image/png"
8
	"net/http"
9
	"strings"
10
	"time"
11
12
	"github.com/FlameInTheDark/dtbot/api/location"
13
	"github.com/FlameInTheDark/dtbot/bot"
14
	"github.com/fogleman/gg"
15
)
16
17
// Forecast Weather forecast struct
18
type Forecast struct {
19
	Cod     string        `json:"cod"`
20
	Weather []WeatherData `json:"list"`
21
	City    CityData      `json:"city"`
22
}
23
24
// WeatherData Weather data struct
25
type WeatherData struct {
0 ignored issues
show
introduced by
type name will be used as weather.WeatherData by other packages, and that stutters; consider calling this Data
Loading history...
26
	Time   int64       `json:"dt"`
27
	Main   MainData    `json:"main"`
28
	Wind   WindData    `json:"wind"`
29
	Clouds CloudsData  `json:"clouds"`
30
	WDesc  []WDescData `json:"weather"`
31
}
32
33
// TZTime returns time in specified timezone
34
func (w WeatherData) TZTime(tz int) time.Time {
35
	return time.Unix(w.Time, 0).UTC().Add(time.Hour * time.Duration(tz))
36
}
37
38
// WDescData Weather description struct
39
type WDescData struct {
40
	Id   int64  `json:"id"`
0 ignored issues
show
introduced by
struct field Id should be ID
Loading history...
41
	Main string `json:"main"`
42
	Desc string `json:"description"`
43
	Icon string `json:"icon"`
44
}
45
46
// MainData Weather main data struct
47
type MainData struct {
48
	Temp     float64 `json:"temp"`
49
	Pressure float64 `json:"pressure"`
50
	TempMin  float64 `json:"temp_min"`
51
	TempMax  float64 `json:"temp_max"`
52
	Humidity int     `json:"humidity"`
53
}
54
55
// WindData Weather wind data struct
56
type WindData struct {
57
	Speed float64 `json:"speed"`
58
	Deg   float64 `json:"deg"`
59
}
60
61
// CloudsData Weather cloud data struct
62
type CloudsData struct {
63
	All int `json:"all"`
64
}
65
66
// CityData Weather city data struct
67
type CityData struct {
68
	Name string `json:"name"`
69
}
70
71
// GetWeatherImage returns buffer with weather image
72
func GetWeatherImage(ctx *bot.Context) (buf *bytes.Buffer, err error) {
73
	var (
74
		forecast Forecast
75
		city     = ctx.GetGuild().WeatherCity
76
	)
77
78
	if len(ctx.Args) > 0 {
79
		city = strings.Join(ctx.Args, "+")
80
	}
81
82
	loc, err := location.New(ctx.Conf.General.GeonamesUsername, city)
83
	if err != nil {
84
		fmt.Printf("Location API: %v", err)
85
		return
86
	}
87
88
	cityName := loc.Geonames[0].CountryName + ", " + loc.Geonames[0].Name
89
90
	// Get coordinates and get weather data
91
	newlat, newlng := loc.GetCoordinates()
92
	resp, err := http.Get(fmt.Sprintf("https://api.openweathermap.org/data/2.5/forecast?lat=%v&lon=%v&lang=%v&units=metric&appid=%v",
93
		newlat, newlng, ctx.Conf.General.Language, ctx.Conf.Weather.WeatherToken))
94
	if err != nil {
95
		fmt.Printf("Weather API: %v", err)
96
		return
97
	}
98
99
	err = json.NewDecoder(resp.Body).Decode(&forecast)
100
	if err != nil {
101
		fmt.Printf("Weather Decode: %v", err)
102
		return
103
	}
104
105
	gc := gg.NewContext(400, 650)
106
	gc.SetRGBA(0,0,0,0)
107
	gc.Clear()
108
109
	// Template
110
	gc.SetRGB255(242, 97, 73)
111
	gc.DrawRoundedRectangle(0, 0, 400, 650, 10)
112
	gc.Fill()
113
114
	// Weather lines
115
	gc.SetRGB255(234, 89, 65)
116
	gc.DrawRectangle(0, 250, 400, 100)
117
	gc.DrawRectangle(0, 450, 400, 100)
118
	gc.Fill()
119
120
	gc.SetLineWidth(2)
121
	gc.SetRGBA(0, 0, 0,0.05)
122
	gc.DrawLine(0, 250, 400, 250)
123
	gc.DrawLine(0, 349, 400, 348)
124
	gc.DrawLine(0, 450, 400, 450)
125
	gc.DrawLine(0, 549, 400, 548)
126
	gc.Stroke()
127
128
	// Text
129
	if err := gc.LoadFontFace("lato.ttf", 20); err != nil {
130
		panic(err)
131
	}
132
	// Header
133
	gc.SetRGBA(1, 1, 1, 0.7)
134
	gc.DrawStringAnchored(cityName, 10, 15, 0, 0.5)
135
	gc.SetRGBA(1, 1, 1, 0.4)
136
	gc.DrawStringAnchored(time.Now().Format("Jan 2, 2006"), 280, 15, 0, 0.5)
137
138
	// First weather data
139
	gc.SetRGBA(1, 1, 1, 0.5)
140
	if err := gc.LoadFontFace("lato.ttf", 30); err != nil {
141
		panic(err)
142
	}
143
	gc.DrawStringAnchored(fmt.Sprintf("%.2v:00", forecast.Weather[0].TZTime(ctx.Conf.General.Timezone).Hour()), 50, 200, 0.5, 0.5)
144
	gc.DrawStringAnchored(fmt.Sprintf("H:%v%%", forecast.Weather[0].Main.Humidity), 200, 200, 0.5, 0.5)
145
	gc.DrawStringAnchored(fmt.Sprintf("C:%v%%", int(forecast.Weather[0].Clouds.All)), 350, 200, 0.5, 0.5)
146
147
	gc.SetRGBA(1, 1, 1, 1)
148
	if err := gc.LoadFontFace("lato.ttf", 90); err != nil {
149
		panic(err)
150
	}
151
152
	gc.DrawStringAnchored(fmt.Sprintf("%v°", int(forecast.Weather[0].Main.TempMin)), 30, 120, 0, 0.5)
153
154
	if err := gc.LoadFontFace("owfont-regular.ttf", 90); err != nil {
155
		panic(err)
156
	}
157
158
	gc.DrawStringAnchored(ctx.WeatherCode(fmt.Sprintf("%v", forecast.Weather[0].WDesc[0].Id)), 250, 120, 0, 0.7)
159
160
	if err := gc.LoadFontFace("lato.ttf", 30); err != nil {
161
		panic(err)
162
	}
163
164
	// Time
165
	gc.DrawStringAnchored(fmt.Sprintf("%.2v:00", forecast.Weather[1].TZTime(ctx.Conf.General.Timezone).Hour()), 100, 300, 0, 0.5)
166
	gc.DrawStringAnchored(fmt.Sprintf("%.2v:00", forecast.Weather[2].TZTime(ctx.Conf.General.Timezone).Hour()), 100, 400, 0, 0.5)
167
	gc.DrawStringAnchored(fmt.Sprintf("%.2v:00", forecast.Weather[3].TZTime(ctx.Conf.General.Timezone).Hour()), 100, 500, 0, 0.5)
168
	gc.DrawStringAnchored(fmt.Sprintf("%.2v:00", forecast.Weather[4].TZTime(ctx.Conf.General.Timezone).Hour()), 100, 600, 0, 0.5)
169
170
	if err := gc.LoadFontFace("lato.ttf", 50); err != nil {
171
		panic(err)
172
	}
173
174
	// Temperature
175
	gc.DrawStringAnchored(fmt.Sprintf("%v°", int(forecast.Weather[1].Main.TempMin)), 250, 300, 0, 0.5)
176
	gc.DrawStringAnchored(fmt.Sprintf("%v°", int(forecast.Weather[2].Main.TempMin)), 250, 400, 0, 0.5)
177
	gc.DrawStringAnchored(fmt.Sprintf("%v°", int(forecast.Weather[3].Main.TempMin)), 250, 500, 0, 0.5)
178
	gc.DrawStringAnchored(fmt.Sprintf("%v°", int(forecast.Weather[4].Main.TempMin)), 250, 600, 0, 0.5)
179
180
	if err := gc.LoadFontFace("owfont-regular.ttf", 60); err != nil {
181
		panic(err)
182
	}
183
184
	// Weather icon
185
	gc.DrawStringAnchored(ctx.WeatherCode(fmt.Sprintf("%v", forecast.Weather[1].WDesc[0].Id)), 20, 300, 0, 0.7)
186
	gc.DrawStringAnchored(ctx.WeatherCode(fmt.Sprintf("%v", forecast.Weather[2].WDesc[0].Id)), 20, 400, 0, 0.7)
187
	gc.DrawStringAnchored(ctx.WeatherCode(fmt.Sprintf("%v", forecast.Weather[3].WDesc[0].Id)), 20, 500, 0, 0.7)
188
	gc.DrawStringAnchored(ctx.WeatherCode(fmt.Sprintf("%v", forecast.Weather[4].WDesc[0].Id)), 20, 600, 0, 0.7)
189
190
	buf = new(bytes.Buffer)
191
	pngerr := png.Encode(buf, gc.Image())
192
	if pngerr != nil {
193
		fmt.Printf("Image: %v", pngerr)
194
	}
195
	return
196
}
197