commit 176052c78f208994323df00f5331948153e7f360 Author: Chris Cummer Date: Wed Mar 28 09:54:09 2018 -0700 First commit diff --git a/bamboohr/bamboohr.go b/bamboohr/bamboohr.go new file mode 100644 index 00000000..c186d220 --- /dev/null +++ b/bamboohr/bamboohr.go @@ -0,0 +1,17 @@ +package bamboohr + +import ( + "time" +) + +func Fetch() []Item { + client := NewClient() + result := client.Away("timeOff", today(), today()) + + return result +} + +func today() string { + localNow := time.Now().Local() + return localNow.Format("2006-01-02") +} diff --git a/bamboohr/calendar.go b/bamboohr/calendar.go new file mode 100644 index 00000000..2b2dadea --- /dev/null +++ b/bamboohr/calendar.go @@ -0,0 +1,39 @@ +package bamboohr + +import () + +type Calendar struct { + Items []Item `xml:"item"` +} + +/* -------------------- Public Functions -------------------- */ + +func (calendar *Calendar) Holidays() []Item { + return calendar.filteredItems("holiday") +} + +func (calendar *Calendar) ItemsByType(itemType string) []Item { + if itemType == "timeOff" { + return calendar.TimeOffs() + } + + return calendar.Holidays() +} + +func (calendar *Calendar) TimeOffs() []Item { + return calendar.filteredItems("timeOff") +} + +/* -------------------- Private Functions -------------------- */ + +func (calendar *Calendar) filteredItems(itemType string) []Item { + items := []Item{} + + for _, item := range calendar.Items { + if item.Type == itemType { + items = append(items, item) + } + } + + return items +} diff --git a/bamboohr/client.go b/bamboohr/client.go new file mode 100644 index 00000000..03c7ef65 --- /dev/null +++ b/bamboohr/client.go @@ -0,0 +1,55 @@ +package bamboohr + +import ( + "encoding/xml" + "fmt" + "os" +) + +// A Client represents the data required to connect to the BambooHR API +type Client struct { + apiBase string + apiKey string + subdomain string +} + +// NewClient creates and returns a new BambooHR client +func NewClient() *Client { + client := Client{ + apiBase: "https://api.bamboohr.com/api/gateway.php", + apiKey: os.Getenv("WTF_BAMBOO_HR_TOKEN"), + subdomain: os.Getenv("WTF_BAMBOO_HR_SUBDOMAIN"), + } + + return &client +} + +/* -------------------- Public Functions -------------------- */ + +// Away returns a string representation of the people who are out of the office during the defined period +func (client *Client) Away(itemType, startDate, endDate string) []Item { + calendar, _ := client.away(startDate, endDate) + items := calendar.ItemsByType(itemType) + + return items +} + +/* -------------------- Private Functions -------------------- */ + +// away is the private interface for retrieving structural data about who will be out of the office +// This method does the actual communication with BambooHR and returns the raw Go +// data structures used by the public interface +func (client *Client) away(startDate, endDate string) (cal Calendar, err error) { + apiURL := fmt.Sprintf( + "%s/%s/v1/time_off/whos_out?start=%s&end=%s", + client.apiBase, + client.subdomain, + startDate, + endDate, + ) + + data, err := Request(client.apiKey, apiURL) + err = xml.Unmarshal(data, &cal) + + return +} diff --git a/bamboohr/employee.go b/bamboohr/employee.go new file mode 100644 index 00000000..02d179d6 --- /dev/null +++ b/bamboohr/employee.go @@ -0,0 +1,12 @@ +package bamboohr + +import () + +/* +* Note: this currently implements the minimum number of fields to fulfill the Away functionality. +* Undoubtedly there are more fields than this to an employee + */ +type Employee struct { + ID int `xml:"id,attr"` + Name string `xml:",chardata"` +} diff --git a/bamboohr/item.go b/bamboohr/item.go new file mode 100644 index 00000000..7a8d185d --- /dev/null +++ b/bamboohr/item.go @@ -0,0 +1,42 @@ +package bamboohr + +import ( + "fmt" + "time" +) + +// DateFormat defines the format we expect to receive dates from BambooHR in +const DateFormat = "2006-01-02" + +type Item struct { + Employee Employee `xml:"employee"` + End string `xml:"end"` + Holiday string `xml:"holiday"` + Start string `xml:"start"` + Type string `xml:"type,attr"` +} + +func (item *Item) String() string { + return fmt.Sprintf("Item: %s, %s, %s, %s", item.Type, item.Employee.Name, item.Start, item.End) +} + +/* -------------------- Public Functions -------------------- */ + +func (item *Item) Name() string { + if (item.Employee != Employee{}) { + return item.Employee.Name + } + + return item.Holiday +} + +func (item *Item) PrettyStart() string { + newTime, _ := time.Parse(DateFormat, item.Start) + return fmt.Sprint(newTime.Format("Jan 2, 2006")) +} + +func (item *Item) PrettyEnd() string { + newTime, _ := time.Parse(DateFormat, item.End) + end := fmt.Sprint(newTime.Format("Jan 2, 2006")) + return end +} diff --git a/bamboohr/request.go b/bamboohr/request.go new file mode 100644 index 00000000..96d64574 --- /dev/null +++ b/bamboohr/request.go @@ -0,0 +1,39 @@ +package bamboohr + +import ( + "bytes" + "net/http" +) + +func Request(apiKey string, apiURL string) ([]byte, error) { + req, err := http.NewRequest("GET", apiURL, nil) + if err != nil { + panic(err) + } + + req.SetBasicAuth(apiKey, "x") + + client := &http.Client{} + resp, err := client.Do(req) + if err != nil { + panic(err) + } + defer resp.Body.Close() + + data, err := ParseBody(resp) + if err != nil { + panic(err) + } + + return data, err +} + +func ParseBody(resp *http.Response) ([]byte, error) { + var buffer bytes.Buffer + _, err := buffer.ReadFrom(resp.Body) + if err != nil { + return nil, err + } + + return buffer.Bytes(), nil +} diff --git a/github/github.go b/github/github.go new file mode 100644 index 00000000..19ee27ee --- /dev/null +++ b/github/github.go @@ -0,0 +1,3 @@ +package wtfgithub + +import () diff --git a/wtf.go b/wtf.go new file mode 100644 index 00000000..20f5c96e --- /dev/null +++ b/wtf.go @@ -0,0 +1,42 @@ +package main + +import ( + "fmt" + + "github.com/rivo/tview" + "github.com/senorprogrammer/wtf/bamboohr" +) + +func main() { + app := tview.NewApplication() + + grid := tview.NewGrid() + grid.SetRows(10, 40) // 10 high, 40 high + grid.SetColumns(40, 40) // 40 wide, 40 wide + grid.SetBorder(false) + + grid.AddItem(bambooView(), 0, 0, 1, 1, 0, 0, false) + + if err := app.SetRoot(grid, true).Run(); err != nil { + panic(err) + } +} + +func bambooView() tview.Primitive { + items := bamboohr.Fetch() + + bamboo := tview.NewTextView() + bamboo.SetBorder(true) + bamboo.SetDynamicColors(true) + bamboo.SetTitle(" 🐨 Away ") + + data := "" + for _, item := range items { + str := fmt.Sprintf("[green]%s[white]\n%s - %s\n\n", item.Name(), item.PrettyStart(), item.PrettyEnd()) + data = data + str + } + + fmt.Fprintf(bamboo, "%s", data) + + return bamboo +}