Skip to content

Commit

Permalink
Merge branch 'release/0.3.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
Praveen Premaratne committed Mar 7, 2020
2 parents 0f0d8a9 + 6c47546 commit eaa6c0d
Show file tree
Hide file tree
Showing 5 changed files with 330 additions and 26 deletions.
28 changes: 19 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,22 +19,32 @@ given the user knows the issue reference

## Usage
```
This tool can be used to log time spent on a specific Jira ticket on a project.
timesheet (-r -t [-d] [-m]] [[-h] [-e] [-d]) (-remaining [-history])
-d string
OPTIONAL: The date on which the worklog effort was started in YYYY-MM-DD format. Default 2020-03-06
OPTIONAL: The date on which the worklog effort was started in YYYY-MM-DD format. Default 2020-03-06
-e string
HELP: Base64 encode the given credentials. Format: email:token;domain. e.g. example@example.com:abcThisIsFake;xyz.atlassian.net
-h HELP: Print usage
HELP: Base64 encode the given credentials. Format: email:token;domain. e.g. example@example.com:abcThisIsFake;xyz.atlassian.net
-h HELP: This tool can be used to log time spent on a specific Jira ticket on a project.
-history
HELP: Print the timesheet of the day
HELP: Print the timesheet of the day
-m string
OPTIONAL: A comment about the worklog
OPTIONAL: A comment about the worklog
-r string
REQUIRED: Jira ticket reference. E.g. DDSP-4
REQUIRED: Jira ticket reference. E.g. DDSP-4
-remaining
HELP: Print how many hour can be book for the current day. -history and -d are also available
HELP: Print how many hour can be book for the current day. -history and -d are also available
-t string
REQUIRED: The time spent as days (#d), hours (#h), or minutes (#m or #). E.g. 8h
REQUIRED: The time spent as days (#d), hours (#h), or minutes (#m or #). E.g. 8h
-week
HELP: Print timesheet of the current week
Example:
timesheet -r DDSP-XXXX -t 8h -m "Jenkins pipeline completed"
timesheet -r DDSP-XXXX -t 1h -m "Investigated possible solutions" -d 2020-03-05
timesheet -remaining
timesheet -remaining -d 2020-03-05
timesheet -remaining -history
timesheet -remaining -history -d 2020-03-05
```

## Requirements
Expand Down
44 changes: 29 additions & 15 deletions argparse.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ var dateFormat, _ = regexp.Compile("[0-9]{4}-[0-9]{2}-[0-9]{2}")
func (app *App) Parser() {
app.Started = app.getDateTime()

flag.BoolVar(&app.Help, "h", false, "HELP: Print usage")
flag.BoolVar(&app.Help, "h", false, "HELP: This tool can be used to log time spent on a specific Jira ticket on a project.")
flag.StringVar(&app.Ticket, "r", "",
"REQUIRED: Jira ticket reference. E.g. DDSP-4")
flag.StringVar(&app.TimeSpent, "t", "",
Expand All @@ -31,21 +31,36 @@ func (app *App) Parser() {
"OPTIONAL: A comment about the worklog")
flag.StringVar(&app.Encode, "e", "", "HELP: Base64 encode the given credentials."+
" Format: email:token;domain. e.g. example@example.com:abcThisIsFake;xyz.atlassian.net")
flag.BoolVar(&app.TimeRemaining, "remaining", false, "HELP: Print how many hour can be book for the current day." +
flag.BoolVar(&app.TimeRemaining, "remaining", false, "HELP: Print how many hour can be book for the current day."+
" -history and -d are also available")
flag.BoolVar(&app.History, "history", false, "HELP: Print the timesheet of the day")
flag.BoolVar(&app.PrintWeek, "week", false, "HELP: Print timesheet of the current week")
flag.Parse()
app.validate()
}

func (app *App) validate() {

if len(os.Args[1:]) < 1 {
fmt.Printf("no arguments are given\n\n")
app.usage()
}

if app.Started != "" {
if !dateFormat.MatchString(app.Started) {
panic("provided date didn't match expected format. try -h for help")
} else {
app.Started = fmt.Sprintf("%sT%s", app.Started, app.getTimeFixed())
}
} else {
app.Started = app.getDateTime()
}

if app.Help {
fmt.Println("This tool can be used to log time spent on a specific Jira ticket on a project.")
app.usage()
os.Exit(0)
}

if app.TimeRemaining {
if app.TimeRemaining || app.PrintWeek {
return
}

Expand All @@ -61,18 +76,17 @@ func (app *App) validate() {
if app.TimeSpent == "" {
panic(errors.New("no time given. -t"))
}

if app.Started != "" {
if !dateFormat.MatchString(app.Started) {
panic("provided date didn't match expected format. try -h for help")
} else {
app.Started = fmt.Sprintf("%sT%s", app.Started, app.getTimeFixed())
}
} else {
app.Started = app.getDateTime()
}
}

func (app *App) usage() {
fmt.Printf("timesheet (-r -t [-d] [-m]] [[-h] [-e] [-d]) (-remaining [-history])\n")
flag.PrintDefaults()
fmt.Printf("Example:\n" +
"\ttimesheet -r DDSP-XXXX -t 8h -m \"Jenkins pipeline completed\"\n" +
"\ttimesheet -r DDSP-XXXX -t 1h -m \"Investigated possible solutions\" -d 2020-03-05\n" +
"\ttimesheet -remaining\n" +
"\ttimesheet -remaining -d 2020-03-05\n" +
"\ttimesheet -remaining -history\n" +
"\ttimesheet -remaining -history -d 2020-03-05\n")
os.Exit(1)
}
223 changes: 221 additions & 2 deletions atlassian.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"strings"
)
Expand Down Expand Up @@ -69,6 +70,29 @@ type Worklog struct {
} `json:"author"`
}

type WeekLog struct {
Total int
Issues []Issue
}

type Issue struct {
Key string
Logs []DayLog
}

type DayLog struct {
Total int
WeekDay string
TimeSpent int
}

type Week struct {
Total int
Days map[string]map[string][]int
}

var daysOfWeek = []string{"Monday", "Tuesday", "Wednesday", "Thursday", "Friday"}

func LogTime(reference string, time string, started string, comment string, domain string, auth string) {
var slot = TimeLog{}
slot.TimeSpent = time
Expand Down Expand Up @@ -142,6 +166,45 @@ func (app *App) GetTimeRemaining(domain string, auth string) {

}

func (app *App) GetWeekTimesheet(domain string, auth string) {
var weekLog WeekLog
start, end := app.getWeek()
userEmail, _ := basicAuth(auth)
issuesOfTheWeek, iErr := getIssuesUpdatedBetweenDays(domain, auth,
start.Format("2006-01-02"), end.Format("2006-01-02"))
if iErr != nil {
panic(iErr)
}

worklogs, wErr := issuesOfTheWeek.getWorklogs(domain, auth)
if wErr != nil {
panic(wErr)
}

for _, wLog := range worklogs {
var issue Issue
issue.Key = wLog.Key
if wLog.Total > 0 {
for _, log := range wLog.Worklogs {
if app.isDateBetween(log.Started, start, end) {
if log.Author.EmailAddress == userEmail {
var dayLog DayLog
dayLog.WeekDay = getDateOfWeek(log.Started)
dayLog.TimeSpent = log.TimeSpentSeconds
weekLog.Total += log.TimeSpentSeconds
issue.Logs = append(issue.Logs, dayLog)
}
}
}
}
if len(issue.Logs) > 0 {
weekLog.Issues = append(weekLog.Issues, issue)
}
}

weekLog.print()
}

func getIssuesUpdatedToday(domain string, auth string, date string) (*JiraSearchResult, error) {
var client = &http.Client{}
var query = fmt.Sprintf("jql=worklogDate%%20>%%3D%%20\"%s\"%%20AND%%20worklogDate%%20<%%3D%%20\"%s\"", date, date)
Expand Down Expand Up @@ -169,6 +232,28 @@ func getIssuesUpdatedToday(domain string, auth string, date string) (*JiraSearch
return response, nil
}

func getIssuesUpdatedBetweenDays(domain string, auth string, start string, end string) (*JiraSearchResult, error) {
var query = fmt.Sprintf("jql=worklogDate%%20>%%3D%%20\"%s\"%%20AND%%20worklogDate%%20<%%3D%%20\"%s\"", start, end)
var url = fmt.Sprintf("https://%s/rest/api/3/search?%s", strings.TrimSuffix(domain, "\n"), query)

var headers []struct {
key string
value string
}

headers = append(headers, struct {
key string
value string
}{key: "Content-Type", value: "application/json"})

var response = new(JiraSearchResult)
decodeErr := json.NewDecoder(httpReq("GET", url, auth, nil, headers).Body).Decode(&response)
if decodeErr != nil {
panic(decodeErr)
}
return response, nil
}

func (issues *JiraSearchResult) getWorklogs(domain string, auth string) ([]WorkLogs, error) {
var worklogs []WorkLogs
for _, issue := range issues.Issues {
Expand Down Expand Up @@ -248,6 +333,140 @@ func basicAuth(token string) (string, string) {
return loginDetails[0], loginDetails[1]
}

func getInHours(seconds int) float64 {
return float64(seconds) / float64(3600)
func httpReq(method string, url string, auth string, body io.Reader, headers []struct {
key string
value string
}) *http.Response {
var client = &http.Client{}
req, reqErr := http.NewRequest(method, url, body)
if reqErr != nil {
panic(reqErr)
}

for _, header := range headers {
req.Header.Add(header.key, header.value)
}
req.SetBasicAuth(basicAuth(auth))
resp, err := client.Do(req)
if err != nil {
panic(err)
}

//defer resp.Body.Close()
return resp

}

func (w *WeekLog) sort() Week {
var sortedWeek Week

sortedWeek.Days = make(map[string]map[string][]int)

sortedWeek.Total = w.Total
for _, issue := range w.Issues {
sortedWeek.Days[issue.Key] = make(map[string][]int)
for _, day := range issue.Logs {
sortedWeek.Days[issue.Key][day.WeekDay] = append(sortedWeek.Days[issue.Key][day.WeekDay], day.TimeSpent)
}
}

return sortedWeek.fillGaps()
}

func (w *Week) fillGaps() Week {
for _, days := range w.Days {
for _, d := range daysOfWeek {
if _, found := days[d]; !found {
days[d] = []int{0}
}
}
}

return *w
}

func (w *Week) sum() Week {
for _, days := range w.Days {
for _, d := range daysOfWeek {
if times, found := days[d]; !found {
sum := 0
for _, time := range times {
sum += time
}
days[d] = []int{sum}
}
}
}

return *w
}

func (w *WeekLog) print() {
for i := 0; i <= 83; i++ {
if i == 0 {
fmt.Printf(" ")
} else if i == 83 {
fmt.Printf("\n")
} else {
fmt.Printf("_")
}
}

fmt.Printf("| %-15s ", "Issue")
for _, title := range daysOfWeek {
fmt.Printf("| %-10s ", title)
}
fmt.Printf("|\n")
for i := 0; i <= 82; i++ {
if i == 0 {
fmt.Printf("|")
} else if i == 18 || i == 31 || i == 44 || i == 57 || i == 70 {
fmt.Printf("|")
} else if i == 82 {
fmt.Printf("_|\n")
} else {
fmt.Printf("_")
}
}

weekSorted := w.sort()
var processedIssues int

for issue, day := range weekSorted.sum().Days {
if processedIssues > 0 {
for i := 0; i <= 83; i++ {
if i == 0 {
fmt.Printf("|")
} else if i == 83 {
fmt.Printf("|\n")
} else if i == 18 || i == 31 || i == 44 || i == 57 || i == 70 {
fmt.Printf("|")
} else {
fmt.Printf("-")
}
}
}
fmt.Printf("| %-15s ", issue)
for _, v := range daysOfWeek {
if day[v][0] == 0 {
fmt.Printf("| %-10s ", "")
} else {
fmt.Printf("| %-10.1f ", getInHours(day[v][0]))
}
}
fmt.Println("|")
processedIssues += 1
}

for i := 0; i <= 83; i++ {
if i == 0 {
fmt.Printf(" ")
} else if i == 83 {
fmt.Printf(" \n")
} else {
fmt.Printf("-")
}
}

fmt.Println(fmt.Sprintf("Total %.1fh", getInHours(weekSorted.Total)))
}
Loading

0 comments on commit eaa6c0d

Please sign in to comment.