From 13c84cd9973458750305c72a919cf921d9b22b04 Mon Sep 17 00:00:00 2001 From: Teddy Wing Date: Sat, 3 Jun 2017 10:29:00 +0200 Subject: main.go: Read config from a new format TOML file Construct a new config format, written in TOML. Read that format in when starting the program. This new format has the benefit of using project name aliases as keys. The goal will be to allow users to send one of those aliases as a command line argument to the program, and thus to have the program post a TimeTask entry for that project. Here's an idea of what the new format looks like: [auth] username = "example" password_cmd = "" [projects.myprojectalias] client = ... project = ... module = ... task = ... work_type = ... time = 7 billable = true [projects.project2] client = ... project = ... module = ... task = ... work_type = ... time = 7 billable = true Eventually, we'll need to remove the `interface{}` from the `Projects` map value and replace it with a real type, but this was just to test that it was possible to get us a nice map from the TOML. --- main.go | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/main.go b/main.go index 5a9f0d1..bfb56b8 100644 --- a/main.go +++ b/main.go @@ -8,22 +8,33 @@ import ( "github.com/teddywing/timetasker/timetask" + "github.com/BurntSushi/toml" "gopkg.in/yaml.v2" ) +// type Config struct { +// Auth struct { +// Username string +// PasswordCmd string `yaml:"password_cmd"` +// } +// Fields timetask.Fields +// Defaults timetask.TimeEntry +// } + type Config struct { Auth struct { Username string - PasswordCmd string `yaml:"password_cmd"` + PasswordCmd string //`toml:"password_cmd"` } - Fields timetask.Fields - Defaults timetask.TimeEntry + Projects map[string]interface{} } var config Config func main() { loadConfig() + log.Printf("%+v", config) + return if len(os.Args) == 1 { fmt.Println("Not enough arguments") @@ -46,13 +57,13 @@ func main() { // timetask.SubmitTimeEntries(config.Fields, time_entries) - timetask.GenerateWeeklyTimesheet(os.Stdout, config.Defaults) + // timetask.GenerateWeeklyTimesheet(os.Stdout, config.Defaults) } func loadConfig() { - config_str, err := ioutil.ReadFile("config.yml") + // config_str, err := ioutil.ReadFile("config2.toml") config = Config{} - err = yaml.Unmarshal(config_str, &config) + _, err := toml.DecodeFile("config2.toml", &config) if err != nil { log.Println(err) } -- cgit v1.2.3 From 1a1d9c3c671d33f7013c94564007e2c5b33cea47 Mon Sep 17 00:00:00 2001 From: Teddy Wing Date: Sat, 3 Jun 2017 10:42:54 +0200 Subject: main.go: Cleanup from 13c84cd9973458750305c72a919cf921d9b22b04 * Get rid of the 'yaml' import since we're now using 'toml' instead. * Get rid of commented code that's no longer relevant. * Get rid of test code that checked that the config loaded correctly. --- main.go | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/main.go b/main.go index bfb56b8..61cdce6 100644 --- a/main.go +++ b/main.go @@ -9,22 +9,12 @@ import ( "github.com/teddywing/timetasker/timetask" "github.com/BurntSushi/toml" - "gopkg.in/yaml.v2" ) -// type Config struct { -// Auth struct { -// Username string -// PasswordCmd string `yaml:"password_cmd"` -// } -// Fields timetask.Fields -// Defaults timetask.TimeEntry -// } - type Config struct { Auth struct { Username string - PasswordCmd string //`toml:"password_cmd"` + PasswordCmd string } Projects map[string]interface{} } @@ -33,8 +23,6 @@ var config Config func main() { loadConfig() - log.Printf("%+v", config) - return if len(os.Args) == 1 { fmt.Println("Not enough arguments") @@ -61,7 +49,6 @@ func main() { } func loadConfig() { - // config_str, err := ioutil.ReadFile("config2.toml") config = Config{} _, err := toml.DecodeFile("config2.toml", &config) if err != nil { -- cgit v1.2.3 From e99c7b69e0981d4a7b87310cd811b90a2ffbe12f Mon Sep 17 00:00:00 2001 From: Teddy Wing Date: Sat, 3 Jun 2017 11:22:08 +0200 Subject: Comment out all the things! Half get rid of a lot of code. I don't like and don't want to use our old field types. Get rid of them and the code in 'http.go' that depends on them. Also get rid of the time entry submission code in 'main.go' as that's going to be redone. --- main.go | 44 +++++------ timetask/fields.go | 160 +++++++++++++++++++-------------------- timetask/http.go | 214 ++++++++++++++++++++++++++--------------------------- 3 files changed, 209 insertions(+), 209 deletions(-) diff --git a/main.go b/main.go index 61cdce6..b31a4f0 100644 --- a/main.go +++ b/main.go @@ -1,12 +1,12 @@ package main import ( - "fmt" - "io/ioutil" + // "fmt" + // "io/ioutil" "log" - "os" + // "os" - "github.com/teddywing/timetasker/timetask" + // "github.com/teddywing/timetasker/timetask" "github.com/BurntSushi/toml" ) @@ -24,24 +24,24 @@ var config Config func main() { loadConfig() - if len(os.Args) == 1 { - fmt.Println("Not enough arguments") - os.Exit(1) - } - - file_path := os.Args[len(os.Args)-1] - file, err := ioutil.ReadFile(file_path) - if err != nil { - log.Println(err) - } - - time_entries := []timetask.TimeEntry{} - err = yaml.Unmarshal(file, &time_entries) - if err != nil { - log.Println(err) - } - - log.Printf("%+v", time_entries) + // if len(os.Args) == 1 { + // fmt.Println("Not enough arguments") + // os.Exit(1) + // } + // + // file_path := os.Args[len(os.Args)-1] + // file, err := ioutil.ReadFile(file_path) + // if err != nil { + // log.Println(err) + // } + + // time_entries := []timetask.TimeEntry{} + // err = yaml.Unmarshal(file, &time_entries) + // if err != nil { + // log.Println(err) + // } + // + // log.Printf("%+v", time_entries) // timetask.SubmitTimeEntries(config.Fields, time_entries) diff --git a/timetask/fields.go b/timetask/fields.go index fb3a026..f3bb27d 100644 --- a/timetask/fields.go +++ b/timetask/fields.go @@ -1,85 +1,85 @@ package timetask -import "fmt" +// import "fmt" -type Client struct { - ID int - Name string - Projects []Project -} +// type Client struct { +// ID int +// Name string +// Projects []Project +// } -type Project struct { - ID int - Name string - Modules []Module - Tasks []Task - WorkTypes []WorkType `yaml:"work_types"` -} +// type Project struct { +// ID int +// Name string +// Modules []Module +// Tasks []Task +// WorkTypes []WorkType `yaml:"work_types"` +// } -type Module struct { - ID int - Name string -} -type Task struct { - ID int - Name string -} -type WorkType struct { - ID int - Name string -} - -type Fields struct { - PersonID int `yaml:"person_id"` - Clients []Client -} - -func (f *Fields) ClientByName(client_name string) (*Client, error) { - for _, client := range f.Clients { - if client.Name == client_name { - return &client, nil - } - } - - return nil, fmt.Errorf("Client %s not found", client_name) -} - -func (c *Client) ProjectByName(project_name string) (*Project, error) { - for _, project := range c.Projects { - if project.Name == project_name { - return &project, nil - } - } - - return nil, fmt.Errorf("Project %s not found", project_name) -} - -func (p *Project) ModuleByName(module_name string) (*Module, error) { - for _, module := range p.Modules { - if module.Name == module_name { - return &module, nil - } - } - - return nil, fmt.Errorf("Module %s not found", module_name) -} - -func (p *Project) TaskByName(task_name string) (*Task, error) { - for _, task := range p.Tasks { - if task.Name == task_name { - return &task, nil - } - } - - return nil, fmt.Errorf("Task %s not found", task_name) -} - -func (p *Project) WorkTypeByName(work_type_name string) (*WorkType, error) { - for _, work_type := range p.WorkTypes { - if work_type.Name == work_type_name { - return &work_type, nil - } - } - - return nil, fmt.Errorf("Work type %s not found", work_type_name) -} +// type Module struct { +// ID int +// Name string +// } +// type Task struct { +// ID int +// Name string +// } +// type WorkType struct { +// ID int +// Name string +// } +// +// type Fields struct { +// PersonID int `yaml:"person_id"` +// Clients []Client +// } +// +// func (f *Fields) ClientByName(client_name string) (*Client, error) { +// for _, client := range f.Clients { +// if client.Name == client_name { +// return &client, nil +// } +// } +// +// return nil, fmt.Errorf("Client %s not found", client_name) +// } +// +// func (c *Client) ProjectByName(project_name string) (*Project, error) { +// for _, project := range c.Projects { +// if project.Name == project_name { +// return &project, nil +// } +// } +// +// return nil, fmt.Errorf("Project %s not found", project_name) +// } +// +// func (p *Project) ModuleByName(module_name string) (*Module, error) { +// for _, module := range p.Modules { +// if module.Name == module_name { +// return &module, nil +// } +// } +// +// return nil, fmt.Errorf("Module %s not found", module_name) +// } +// +// func (p *Project) TaskByName(task_name string) (*Task, error) { +// for _, task := range p.Tasks { +// if task.Name == task_name { +// return &task, nil +// } +// } +// +// return nil, fmt.Errorf("Task %s not found", task_name) +// } +// +// func (p *Project) WorkTypeByName(work_type_name string) (*WorkType, error) { +// for _, work_type := range p.WorkTypes { +// if work_type.Name == work_type_name { +// return &work_type, nil +// } +// } +// +// return nil, fmt.Errorf("Work type %s not found", work_type_name) +// } diff --git a/timetask/http.go b/timetask/http.go index 83946ad..94a1597 100644 --- a/timetask/http.go +++ b/timetask/http.go @@ -1,13 +1,13 @@ package timetask import ( - "fmt" - "log" + // "fmt" + // "log" "net/http" "net/http/cookiejar" "net/url" - "strconv" - "strings" + // "strconv" + // "strings" "golang.org/x/net/publicsuffix" ) @@ -36,106 +36,106 @@ func Login(username, password string) (resp *http.Response, err error) { return resp, err } -func SubmitTimeEntries(fields Fields, time_entries []TimeEntry) (resp *http.Response, err error) { - v := buildSubmissionParams(fields, time_entries) - - v.Set("module", "time") - v.Set("action", "submitmultipletime") - - return nil, nil -} - -func buildSubmissionParams(fields Fields, time_entries []TimeEntry) url.Values { - v := url.Values{} - entry_indexes := []string{} - - for i, entry := range time_entries { - entry_indexes = append(entry_indexes, strconv.Itoa(i)) - - client, err := fields.ClientByName(entry.Client) - if err != nil { - log.Panic(err) - } - - project, err := client.ProjectByName(entry.Project) - if err != nil { - log.Panic(err) - } - - module, err := project.ModuleByName(entry.Module) - if err != nil { - log.Panic(err) - } - - task, err := project.TaskByName(entry.Task) - if err != nil { - log.Panic(err) - } - - work_type, err := project.WorkTypeByName(entry.WorkType) - if err != nil { - log.Panic(err) - } - - var billable string - if entry.Billable { - billable = "t" - } else { - billable = "f" - } - - v.Set( - fmt.Sprintf("f_personID%d", i), - strconv.Itoa(fields.PersonID), - ) - - v.Set( - fmt.Sprintf("f_clientID%d", i), - strconv.Itoa(client.ID), - ) - - v.Set( - fmt.Sprintf("f_projectID%d", i), - strconv.Itoa(project.ID), - ) - - v.Set( - fmt.Sprintf("f_moduleID%d", i), - strconv.Itoa(module.ID), - ) - - v.Set( - fmt.Sprintf("f_taskID%d", i), - strconv.Itoa(task.ID), - ) - - v.Set( - fmt.Sprintf("f_worktypeID%d", i), - strconv.Itoa(work_type.ID), - ) - - v.Set( - fmt.Sprintf("f_date%d", i), - entry.Date.Format("02/01/06"), // day/month/year - ) - - v.Set( - fmt.Sprintf("f_time%d", i), - strconv.Itoa(entry.Time), - ) - - v.Set( - fmt.Sprintf("f_billable%d", i), - billable, - ) - - v.Set( - fmt.Sprintf("f_description%d", i), - entry.Description, - ) - } - - v.Set("f_entryIndexes", strings.Join(entry_indexes, ",")) - - return v -} +// func SubmitTimeEntries(fields Fields, time_entries []TimeEntry) (resp *http.Response, err error) { +// v := buildSubmissionParams(fields, time_entries) +// +// v.Set("module", "time") +// v.Set("action", "submitmultipletime") +// +// return nil, nil +// } +// +// func buildSubmissionParams(fields Fields, time_entries []TimeEntry) url.Values { +// v := url.Values{} +// entry_indexes := []string{} +// +// for i, entry := range time_entries { +// entry_indexes = append(entry_indexes, strconv.Itoa(i)) +// +// client, err := fields.ClientByName(entry.Client) +// if err != nil { +// log.Panic(err) +// } +// +// project, err := client.ProjectByName(entry.Project) +// if err != nil { +// log.Panic(err) +// } +// +// module, err := project.ModuleByName(entry.Module) +// if err != nil { +// log.Panic(err) +// } +// +// task, err := project.TaskByName(entry.Task) +// if err != nil { +// log.Panic(err) +// } +// +// work_type, err := project.WorkTypeByName(entry.WorkType) +// if err != nil { +// log.Panic(err) +// } +// +// var billable string +// if entry.Billable { +// billable = "t" +// } else { +// billable = "f" +// } +// +// v.Set( +// fmt.Sprintf("f_personID%d", i), +// strconv.Itoa(fields.PersonID), +// ) +// +// v.Set( +// fmt.Sprintf("f_clientID%d", i), +// strconv.Itoa(client.ID), +// ) +// +// v.Set( +// fmt.Sprintf("f_projectID%d", i), +// strconv.Itoa(project.ID), +// ) +// +// v.Set( +// fmt.Sprintf("f_moduleID%d", i), +// strconv.Itoa(module.ID), +// ) +// +// v.Set( +// fmt.Sprintf("f_taskID%d", i), +// strconv.Itoa(task.ID), +// ) +// +// v.Set( +// fmt.Sprintf("f_worktypeID%d", i), +// strconv.Itoa(work_type.ID), +// ) +// +// v.Set( +// fmt.Sprintf("f_date%d", i), +// entry.Date.Format("02/01/06"), // day/month/year +// ) +// +// v.Set( +// fmt.Sprintf("f_time%d", i), +// strconv.Itoa(entry.Time), +// ) +// +// v.Set( +// fmt.Sprintf("f_billable%d", i), +// billable, +// ) +// +// v.Set( +// fmt.Sprintf("f_description%d", i), +// entry.Description, +// ) +// } +// +// v.Set("f_entryIndexes", strings.Join(entry_indexes, ",")) +// +// return v +// } -- cgit v1.2.3 From d998a82d4019b1fc5a15734f091852b1b0f086d4 Mon Sep 17 00:00:00 2001 From: Teddy Wing Date: Sat, 3 Jun 2017 11:24:55 +0200 Subject: Create a new Project type This corresponds to a "project" entry in the new config2.toml file. (See 13c84cd9973458750305c72a919cf921d9b22b04). Instead of decoding generic `interface{}`s as projects from the TOML, make them a real type. The reason why we're using `int`s where we used to use strings is that the new TOML format will have users write IDs directly in the config file, instead of having the program automatically search for those IDs and use them as we had previously designed. Maybe we'll bring that functionality back at some point, but for now it's too bothersome to implement and I don't consider it worth the trouble. --- main.go | 4 ++-- timetask/fields.go | 10 ++++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/main.go b/main.go index b31a4f0..ccdcaed 100644 --- a/main.go +++ b/main.go @@ -6,7 +6,7 @@ import ( "log" // "os" - // "github.com/teddywing/timetasker/timetask" + "github.com/teddywing/timetasker/timetask" "github.com/BurntSushi/toml" ) @@ -16,7 +16,7 @@ type Config struct { Username string PasswordCmd string } - Projects map[string]interface{} + Projects map[string]timetask.Project } var config Config diff --git a/timetask/fields.go b/timetask/fields.go index f3bb27d..da2b37d 100644 --- a/timetask/fields.go +++ b/timetask/fields.go @@ -16,6 +16,16 @@ package timetask // WorkTypes []WorkType `yaml:"work_types"` // } +type Project struct { + Client int + Project int + Module int + Task int + WorkType int + Time int + Billable bool +} + // type Module struct { // ID int // Name string -- cgit v1.2.3