Passed
Push — master ( 9ddaaa...1828d7 )
by Stanislav
01:15
created

main.initApp   F

Complexity

Conditions 16

Size

Total Lines 90
Code Lines 58

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 16
eloc 58
dl 0
loc 90
rs 2.4
c 0
b 0
f 0
nop 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 main.initApp 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 main
2
3
import (
4
	"fmt"
5
	"io"
6
	"log"
7
	"os"
8
	"time"
9
10
	"flag"
11
	"github.com/gen2brain/beeep"
12
	"github.com/getlantern/systray"
13
	"github.com/popstas/go-toggl"
14
	"github.com/viasite/planfix-toggl-server/app/client"
15
	"github.com/viasite/planfix-toggl-server/app/config"
16
	"github.com/viasite/planfix-toggl-server/app/icon"
17
	"github.com/viasite/planfix-toggl-server/app/rest"
18
	"github.com/viasite/planfix-toggl-server/app/util"
19
	"runtime"
20
)
21
22
var version string
23
var trayMenu map[string] *systray.MenuItem
24
25
func getLogger(cfg config.Config) *log.Logger {
26
	// logging
27
	logger := log.New(os.Stderr, "[planfix-toggl] ", log.LstdFlags)
28
	if cfg.Debug {
29
		logger.SetFlags(log.Ldate | log.Ltime | log.Lmicroseconds | log.Lshortfile)
30
	} else {
31
		toggl.DisableLog()
32
	}
33
	if cfg.LogFile != "" {
34
		f, err := os.OpenFile(cfg.LogFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)
35
		if err != nil {
36
			logger.Fatalf("[ERROR] Failed to open log file: %s", cfg.LogFile)
37
		}
38
		//defer f.Close()
39
		mw := io.MultiWriter(os.Stdout, f)
40
		logger.SetOutput(mw)
41
	}
42
	return logger
43
}
44
45
func parseFlags(cfg *config.Config) {
46
	dryRun := flag.Bool("dry-run", false, "Don't actually change data")
47
	if runtime.GOOS == "windows" {
48
		// Allow user to hide the console window
49
		flag.BoolVar(&cfg.NoConsole, "no-console", false, "Hide console window")
50
	}
51
	flag.Parse()
52
53
	if *dryRun {
54
		cfg.DryRun = true
55
	}
56
}
57
58
func Notify(msg string) error {
0 ignored issues
show
introduced by
exported function Notify should have comment or be unexported
Loading history...
59
	err := beeep.Notify("", msg, "assets/icon.png")
60
	return err
61
}
62
63
func connectServices(cfg *config.Config, logger *log.Logger, togglClient *client.TogglClient) (err error) {
64
	// toggl
65
	logger.Println("[INFO] подключение к Toggl...")
66
	account, err := togglClient.GetTogglUser()
67
	cfg.TogglUserID = account.Data.ID
68
	if err != nil {
69
		return err
70
	}
71
72
	ok, err := togglClient.IsWorkspaceExists(cfg.TogglWorkspaceID)
73
	if err != nil {
74
		return err
75
	}
76
	if !ok {
77
		return fmt.Errorf("Toggl workspace ID %d не найден", cfg.TogglWorkspaceID)
78
	}
79
80
	// planfix
81
	if cfg.PlanfixUserName != "" && cfg.PlanfixUserPassword != "" {
82
		logger.Println("[INFO] подключение к Планфиксу...")
83
		user, err := togglClient.GetPlanfixUser()
84
		cfg.PlanfixUserID = user.ID
85
		if err != nil {
86
			return err
87
		}
88
89
		logger.Println("[INFO] получение данных аналитики Планфикса...")
90
		_, err = togglClient.GetAnaliticDataCached(
91
			cfg.PlanfixAnaliticName,
92
			cfg.PlanfixAnaliticTypeName,
93
			cfg.PlanfixAnaliticTypeValue,
94
			cfg.PlanfixAnaliticCountName,
95
			cfg.PlanfixAnaliticCommentName,
96
			cfg.PlanfixAnaliticDateName,
97
			cfg.PlanfixAnaliticUsersName,
98
		)
99
		if err != nil {
100
			return fmt.Errorf("Поля аналитики указаны неправильно: %s", err.Error())
101
		}
102
	}
103
	return nil
104
}
105
106
func initApp() {
107
	fmt.Printf("planfix-toggl %s\n", version)
108
	cfg := config.GetConfig()
109
110
	parseFlags(&cfg)
111
112
	logger := getLogger(cfg)
113
114
	errors, isValid := cfg.Validate()
115
	if !isValid {
116
		for _, e := range errors {
117
			log.Println(e)
118
		}
119
	}
120
121
	if cfg.NoConsole {
122
		util.HideConsole()
123
	}
124
125
	togglClient := client.TogglClient{
126
		Config:  &cfg,
127
		Logger:  logger,
128
		SentLog: make(map[string]int),
129
		Opts:    map[string]string{"LastSent": ""},
130
	}
131
	togglClient.ReloadConfig()
132
133
	// get planfix and toggl user IDs, for early API check
134
	err := connectServices(&cfg, logger, &togglClient)
135
	if err != nil {
136
		isValid = false
137
		logger.Printf("[ERROR] %s", err.Error())
138
		Notify(err.Error())
139
	}
140
141
	trayMenu["web"].Enable()
142
	trayMenu["log"].Enable()
143
144
	if isValid {
145
		trayMenu["send"].Enable()
146
		togglClient.Run()
147
	} else {
148
		util.OpenBrowser(fmt.Sprintf("https://localhost:%d", cfg.PortSSL))
149
	}
150
151
	// update last sent on menuitem
152
	go func() {
153
		for {
154
			if togglClient.Opts["LastSent"] != "" {
155
				t := togglClient.Opts["LastSent"]
156
				trayMenu["send"].SetTitle(fmt.Sprintf("Sync (last at %s)", t))
157
			}
158
			time.Sleep(time.Duration(60 * time.Second))
159
		}
160
	}()
161
162
	go func() {
163
		// tray menu actions
164
		for {
165
			select {
166
			case <-trayMenu["send"].ClickedCh:
167
				err := togglClient.SendToPlanfix()
168
				t := togglClient.Opts["LastSent"]
169
				trayMenu["send"].SetTitle(fmt.Sprintf("Sync (last at %s)", t))
170
				if err != nil {
171
					logger.Println(err)
172
				}
173
174
			case <-trayMenu["web"].ClickedCh:
175
				cfg := config.GetConfig()
176
				util.OpenBrowser(fmt.Sprintf("https://localhost:%d", cfg.PortSSL))
177
178
			case <-trayMenu["log"].ClickedCh:
179
				cfg := config.GetConfig()
180
				systray.ShowAppWindow(fmt.Sprintf("https://localhost:%d/log", cfg.PortSSL))
181
182
			case <-trayMenu["quit"].ClickedCh:
183
				onExit()
184
			}
185
		}
186
	}()
187
188
	// start API server
189
	server := rest.Server{
190
		Version:     version,
191
		TogglClient: &togglClient,
192
		Config:      &cfg,
193
		Logger:      logger,
194
	}
195
	server.Run(cfg.PortSSL)
196
}
197
198
func onReady() {
199
	go initApp()
200
201
	// systray.EnableAppWindow("Lantern", 1024, 768) // in next systray versions
202
	systray.SetIcon(icon.Data)
203
	systray.SetTitle("planfix-toggl")
204
	systray.SetTooltip(fmt.Sprintf("planfix-toggl %s", version))
205
206
	trayMenu = make(map[string]*systray.MenuItem)
207
	trayMenu["send"] = systray.AddMenuItem("Sync", "")
208
	trayMenu["web"] = systray.AddMenuItem("Open web interface", "")
209
	trayMenu["log"] = systray.AddMenuItem("Open log", "")
210
	trayMenu["quit"] = systray.AddMenuItem("Quit", "Quit the whole app")
211
212
	trayMenu["send"].Disable()
213
	trayMenu["web"].Disable()
214
	trayMenu["log"].Disable()
215
}
216
217
func onExit() {
218
	systray.Quit()
219
	//os.Exit(0)
220
}
221
222
func main() {
223
	//systray.Run(onReady, onExit)
224
	systray.RunWithAppWindow("planfix-toggl", 1024, 768, onReady, onExit)
225
}
226