package insight
import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
"strings"
)
const (
BaseApiUrl = "https://jds.projectinsight.net/api/"
insightTokenFile = "insight.token"
projectEndpoint = ""
projectListEndpoint = "project/list?ids="
timeEntryForUserInRangeEndpoint = "time-entry/user-date-range?startDate=2020-01-01&endDate=%s&userId=%s"
userEndpoint = "user/list-active?modelProperties=Id,FirstName,LastName,EmailAddress"
)
type (
Interconnect struct {
Client *IClient
DBConnector *DBConnector
}
IClient struct {
client *http.Client
tokens *map[string]string
}
Project struct {
BillableExpenseVarianceEarnedValueBillableVsWorkBillableTotalPercent float64 `json:"BillableExpense_VarianceEarnedValueBillableVsWorkBillableTotalPercent,omitempty"`
CompanyDefaultId string `json:"CompanyDefault_Id,omitempty"`
CustomFieldValueId string `json:"CustomFieldValue_Id,omitempty"`
DepartmentId string `json:"Department_Id,omitempty"`
DurationSeconds int64
DurationString string
EndDateTimeUTC string
EstimateAtCompletionToTargetTotalVariance float64 `json:"EstimateAtCompletionToTargetTotal_Variance,omitempty"`
EstimateAtCompletionToTargetTotalPercentVariance float64 `json:"EstimateAtCompletionToTargetTotalPercent_Variance,omitempty"`
IsActive bool
IsOpen bool
LocationDefaultId string `json:"LocationDefault_Id,omitempty"`
Name string
ProfitSalesOrderActual float64
ProjectPriorityId string `json:"ProjectPriority_Id,omitempty"`
ProjectState int64 `json:"project_state,omitempty"`
ProjectStatusId string `json:"ProjectStatus_Id,omitempty"`
ProjectTypeId string `json:"ProjectType_Id,omitempty"`
ScheduleStartDate string
StartDateTimeUTC string
UrlFull string
VarSalesOrderExpenseVsBillableExpense float64
VarSalesOrderExpenseVsBillableExpensePercent float64
VarSalesOrderExpenseVsInvoiceExpense float64
VarSalesOrderExpenseVsInvoiceExpensePercent float64
VarSalesOrderHoursVsBillableHours float64
VarSalesOrderHoursVsBillableHoursPercent float64
VarSalesOrderHoursVsInvoiceHours float64
VarSalesOrderHoursVsInvoiceHoursPercent float64
VarSalesOrderRateVsBillableRate float64
VarSalesOrderRateVsBillableRatePercent float64
VarSalesOrderRateVsInvoiceRate float64
VarSalesOrderRateVsInvoiceRatePercent float64
VarSalesOrderTimeVsBillableTime float64
VarSalesOrderTimeVsBillableTimePercent float64
VarSalesOrderTimeVsInvoiceTime float64
VarSalesOrderTimeVsInvoiceTimePercent float64
VarSalesOrderTotalVsBillableTotal float64
VarSalesOrderTotalVsBillableTotalPercent float64
VarSalesOrderTotalVsInvoiceTotal float64
VarSalesOrderTotalVsInvoiceTotalPercent float64
WorkPercentComplete float64
WorkSeconds int64
}
TimeEntry struct {
ActualHours float64
ActualHoursFormattedString string
ActualTimeString string
ActualTotal float64
BillableHours float64
BillableHoursFormattedString string
BillableTimeString string
BillableTotal float64
TimeEntryDate string `json:"Date,omitempty"`
TimeEntryDescription string `json:"Description,omitempty"`
ProjectId string `json:"Project_Id,omitempty"`
RateBill float64
RateBurden float64
TaskId string `json:"Task_Id,omitempty"`
TimeSheetId string `json:"TimeSheet_Id,omitempty"`
UserId string `json:"User_Id,omitempty"`
}
User struct {
Id string
FirstName string
LastName string
EmailAddress string
}
)
//
/*======================================================================================
interconnect
======================================================================================*/
func NewInsightConnect() *Interconnect {
connect := Interconnect{}
connect.Client = NewIClient()
connect.DBConnector = &DBConnector{}
return &connect
}
func (ic *Interconnect) ResetTables() {
ic.DBConnector.CreateTables()
}
func (ic *Interconnect) UpdateUsers() {
users := ic.Client.GetUsers()
ic.DBConnector.UpdateUsers(users)
}
func (ic *Interconnect) UpdateTimeEntries() {
users := ic.DBConnector.FetchEngineerUsers()
for _, userPtr := range *users {
user := *userPtr
entries := ic.Client.GetTimeAllTimeEntriesForUserThroughDate(user.Id, "2022-04-28")
ic.DBConnector.UpdateTimeEntries(entries)
}
}
//
//
/*======================================================================================
IClient
======================================================================================*/
func NewIClient() *IClient {
c := IClient{
client: &http.Client{},
tokens: cacheTokens(),
}
return &c
}
func (iClient *IClient) GetTimeAllTimeEntriesForUserThroughDate(userId string, endDate string) *[]*TimeEntry {
urlString := fmt.Sprintf(timeEntryForUserInRangeEndpoint, endDate, userId)
log.Println(urlString)
req := iClient.createRequest(urlString, insightTokenFile)
rawBytes := iClient.doGet(req)
container := make([]*TimeEntry, 0, 1000) //do we want to make the default result size parametric?
err := json.Unmarshal(*rawBytes, &container)
if err != nil {
log.Fatal(err)
}
return &container
}
func (iClient *IClient) GetProjectsInList(projectIds []string) *[]Project {
ids := strings.Join(projectIds, ",")
req := iClient.createRequest(projectListEndpoint+ids, insightTokenFile)
rawBytes := iClient.doGet(req)
container := make([]Project, 0, 1000) //do we want to make the default result size parametric?
err := json.Unmarshal(*rawBytes, &container)
if err != nil {
log.Fatal(err)
}
return &container
}
func (iClient *IClient) GetUsers() *[]User {
users := iClient.getRawUsers()
for idx, user := range *users {
user.EmailAddress = strings.ToLower(user.EmailAddress)
user.EmailAddress = strings.ReplaceAll(user.EmailAddress, "jdsdesignonline.com", "jdsconsulting.net")
user.EmailAddress = strings.ReplaceAll(user.EmailAddress, "jdsfaulkner.com", "jdsconsulting.net")
(*users)[idx] = user
}
return users
}
func (iClient *IClient) getRawUsers() *[]User {
req := iClient.createRequest(userEndpoint, insightTokenFile)
rawBytes := iClient.doGet(req)
container := make([]User, 0, 150) //do we want to make the default result size parametric?
err := json.Unmarshal(*rawBytes, &container)
if err != nil {
log.Fatal(err)
}
return &container
}
func (iClient *IClient) doGet(req *http.Request) *[]byte {
resp, err := iClient.client.Do(req)
if err != nil {
log.Fatal(err)
}
bodyBytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
return &bodyBytes
}
func (iClient *IClient) createRequest(urlEndpoint string, tokenFile string) *http.Request {
reqUrl := BaseApiUrl + urlEndpoint
token := (*iClient.tokens)[tokenFile]
request, err := http.NewRequest("GET", reqUrl, nil)
if err != nil {
log.Fatal(err)
}
request.Header.Add("api-token", token)
return request
}
//
//
/*======================================================================================
utility functions
======================================================================================*/
func cacheTokens() *map[string]string {
files, err := os.ReadDir("./tokens/")
ret := make(map[string]string)
if err != nil {
panic(err)
}
for _, file := range files {
subPath := fmt.Sprintf("./tokens/%s", file.Name())
fileContents, err := os.ReadFile(subPath)
if err != nil {
log.Fatal(err)
}
ret[file.Name()] = string(fileContents)
}
return &ret
}
//