package main import ( //"os" "fmt" "strconv" "strings" "time" //"github.com/abiosoft/ishell" "github.com/EPRparadox82/ishell" "github.com/fatih/color" ) //var mastercol *color.Color func interact(fulldb bool) { //stdOut() shell := ishell.New() shell.SetMultiChoicePrompt(" ->", " - ") shell.SetChecklistOptions("[ ] ", "[X] ") //fmt.Println(os.Args) //cyan := color.New(color.FgCyan).SprintFunc() //yellow := color.New(color.FgYellow).SprintFunc() //green := color.New(color.FgGreen).SprintFunc() boldBlue := color.New(color.FgBlue, color.Bold).SprintFunc() boldRed := color.New(color.FgRed, color.Bold).SprintFunc() boldMag := color.New(color.FgMagenta, color.Bold).SprintFunc() boldCyan := color.New(color.FgCyan, color.Bold).SprintFunc() boldGreen := color.New(color.FgGreen, color.Bold).SprintFunc() boldYell := color.New(color.FgYellow, color.Bold).SprintFunc() promptcol := boldBlue //PromptColor(GetColor()) if fulldb { promptcol = PromptColor(GetColor()).SprintFunc() } // display info. //shell.Println("Starting interactive Shell") shell.SetPrompt(promptcol(">>>")) shell.AddCmd(&ishell.Cmd{ Name: "new", Help: "Start new Project", LongHelp: ` Usage: new If no Task is currently running a new project will be added to database and opened. When there is an open Task the user will be notified to stop it before adding a new Project`, Func: func(c *ishell.Context) { //c.Print("\033[H\033[2J") //c.Println(boldGreen("Start New Project")) newProject() //showLastProject() stdOut() c.Println(promptcol("______________________")) }, }) shell.AddCmd(&ishell.Cmd{ Name: "config", Help: "View and Edit Configuration", LongHelp: ` Usage: config Show the current configuration and ask if it should be edited. Configurations contains the location of the database file and the Personal Data needed for billing If config is edited press on empty line to keep the old entry`, Func: func(c *ishell.Context) { editConf() c.Println(promptcol("______________________")) }, }) shell.AddCmd(&ishell.Cmd{ Name: "resume", Help: "Resume the Paused Task", LongHelp: ` Usage: resume Resume the Task that was paused 'now'.`, Func: func(c *ishell.Context) { newTask(true) stdOut() setPauseTask(0) c.Println(promptcol("______________________")) }, }) shell.AddCmd(&ishell.Cmd{ Name: "pause", Help: "Close the Current Task and remember it", LongHelp: ` Usage: pause Closes the current task 'now' and remember it to continue later. The User is not asked to enter a comment.`, Func: func(c *ishell.Context) { if opentask.Id > 0 { setPauseTask(opentask.Id) c.Println("Pausing Task", pausetask) } closeTask(false) stdOut() c.Println(promptcol("______________________")) }, }) shell.AddCmd(&ishell.Cmd{ Name: "status", Help: "Show Current Project and Tasks", LongHelp: ` Usage: status Shows the current Project, its last Tasks and if there is a open Task.`, Func: func(c *ishell.Context) { //stdOut() showStatus(true) c.Println(promptcol("______________________")) }, }) shell.AddCmd(&ishell.Cmd{ Name: "add", Help: "Add new Customer", LongHelp: ` Usage: add Add a new Customer to Database`, Func: func(c *ishell.Context) { addCustomer() c.Println(promptcol("______________________")) }, }) shell.AddCmd(&ishell.Cmd{ Name: "startnow", Help: "Start a new Task immediately", LongHelp: ` Usage: startnow Start a new Task in the currently open Project with current local time`, Func: func(c *ishell.Context) { newTask(false) stdOut() c.Println(promptcol("______________________")) }, }) shell.AddCmd(&ishell.Cmd{ Name: "stopnow", Help: "Stop the currently Open Task immediately", LongHelp: ` Usage: stopnow Stop the open Task at the current local time. If no task is open the user will be notified.`, Func: func(c *ishell.Context) { closeTask(true) stdOut() c.Println(promptcol("______________________")) }, }) shell.AddCmd(&ishell.Cmd{ Name: "checkbill", Help: " check a Bill with the following id as paid", LongHelp: ` Usage: checkbill Check the bill of the set as paid on the current date`, Func: func(c *ishell.Context) { arg := "none" if len(c.Args) > 0 { arg = strings.Join(c.Args, " ") argi, err := strconv.Atoi(arg) if err == nil { checkBill(argi) //stdOut() } else { c.Println(boldRed(arg, "is not a valid id!")) } } else { c.Println(boldRed("checkbill - Please enter an id")) showLastBills(0) } c.Println(promptcol("______________________")) }, }) shell.AddCmd(&ishell.Cmd{ Name: "deletebill", Help: " Delete a Bill with the following id", LongHelp: ` Usage: deletebill Delete the bill of the set and set its Task back to unbilled`, Func: func(c *ishell.Context) { arg := "none" if len(c.Args) > 0 { arg = strings.Join(c.Args, " ") argi, err := strconv.Atoi(arg) if err == nil { deleteBill(argi) stdOut() } else { c.Println(boldRed(arg, "is not a valid id!")) } } else { c.Println(boldRed("deletebill - Please enter an id")) showLastBills(0) } c.Println(promptcol("______________________")) }, }) shell.AddCmd(&ishell.Cmd{ Name: "deletetask", Help: " Delete a Task with the following id", LongHelp: ` Usage: deletetask Delete the Task of the set `, Func: func(c *ishell.Context) { arg := "none" if len(c.Args) > 0 { arg = strings.Join(c.Args, " ") argi, err := strconv.Atoi(arg) if err == nil { deleteTask(argi) stdOut() } else { c.Println(boldRed(arg, "is not a valid id!")) } } else { tids := GetTaskIds() selids, lids := GetTaskList(tids, false,false) choice := c.MultiChoice(lids, "Select a Task to Edit") if choice > -1 { deleteTask(selids[choice]) } //c.Println(boldRed("deletetask - Please enter an id")) //allProjects() } c.Println(promptcol("______________________")) }, }) shell.AddCmd(&ishell.Cmd{ Name: "project", Help: " Open a Project of the following id", LongHelp: ` Usage: project Open the Project with the set If there is an open Task the user will be notified to close it first.`, Func: func(c *ishell.Context) { arg := "none" if len(c.Args) > 0 { arg = strings.Join(c.Args, " ") argi, err := strconv.Atoi(arg) if err == nil { setProject(argi) stdOut() } else { c.Println(boldRed(arg, "is not a valid id!")) } } else { pids := GetProjectIds() selids, lids := getProjectList(pids) choice := c.MultiChoice(lids, "Select a Project to Edit") if choice > -1 { setProject(selids[choice]) } //c.Println(boldRed("editproject - Please enter an id")) //c.Println(boldRed("project - Please enter an id")) //allProjects() } c.Println(promptcol("______________________")) }, }) shell.AddCmd(&ishell.Cmd{ Name: "edittask", Help: " Edit a Task of the following id", LongHelp: ` Usage: edittask Edit Task of the set . Press on empty line to keep the old entry`, Func: func(c *ishell.Context) { arg := "none" if len(c.Args) > 0 { arg = strings.Join(c.Args, " ") argi, err := strconv.Atoi(arg) if err == nil { editTask(argi) } else { c.Println(boldRed(arg, "is not a valid id!")) } } else { tids := GetTaskIds() selids, lids := GetTaskList(tids, false,false) choice := c.MultiChoice(lids, "Select a Task to Edit") //c.Println(tids) //c.Println(selids) if choice > -1 { editTask(selids[choice]) //c.Println(choice,selids[choice]) } //c.Println(boldRed("edittask - Please enter an id")) } c.Println(promptcol("______________________")) }, }) shell.AddCmd(&ishell.Cmd{ Name: "editproject", Help: " Edit the Project of the following id", LongHelp: ` Usage: editproject Edit Project of the set . Press on empty line to keep the old entry`, Func: func(c *ishell.Context) { arg := "none" if len(c.Args) > 0 { arg = strings.Join(c.Args, " ") argi, err := strconv.Atoi(arg) if err == nil { editProject(argi) //allProjects() //stdOut() } else { c.Println(boldRed(arg, "is not a valid id!")) } } else { pids := GetProjectIds() selids, lids := getProjectList(pids) choice := c.MultiChoice(lids, "Select a Project to Edit") //c.Println(pids) //c.Println(selids) if choice > -1 { editProject(selids[choice]) //c.Println(choice,selids[choice]) } //c.Println(boldRed("editproject - Please enter an id")) //allProjects() } c.Println(promptcol("______________________")) }, }) shell.AddCmd(&ishell.Cmd{ Name: "editcustomer", Help: " Edit the Customer of the following id", LongHelp: ` Usage: editcustomer Edit Customer of the set . Press on empty line to keep the old entry`, Func: func(c *ishell.Context) { arg := "none" if len(c.Args) > 0 { arg = strings.Join(c.Args, " ") argi, err := strconv.Atoi(arg) if err == nil { editCustomer(argi) allCustomers(false) //stdOut() } else { c.Println(boldRed(arg, "is not a valid id!")) } } else { selids, lids := getCustomerList() choice := c.MultiChoice(lids, "Select a Customer to Edit") if choice > -1 { editCustomer(selids[choice]) } //c.Println(boldRed("editcustomer - Please enter an id")) //allCustomers() } c.Println(promptcol("______________________")) }, }) shell.AddCmd(&ishell.Cmd{ Name: "start", Help: " - Start Task at a specific Time 'YYYY-MM-DD HH:MM' Or 'HH:MM'", LongHelp: ` Usage: start Start a new Task in the currently open Project at . Use Format "YYYY-MM-DD HH:MM" or "HH:MM" for datetime. If the latter is used the current local Date will be set.`, Func: func(c *ishell.Context) { arg := "none" if len(c.Args) > 0 { arg = strings.Join(c.Args, " ") newTaskTime(arg) stdOut() } else { c.Println(boldRed("start - Please enter a Datetime")) } c.Println(promptcol("______________________")) }, }) shell.AddCmd(&ishell.Cmd{ Name: "stop", Help: " - Stop Open Task at a specific Time 'YYYY-MM-DD HH:MM' Or 'HH:MM'", LongHelp: ` Usage: stop Stop the active Task at . Use Format "YYYY-MM-DD HH:MM" or "HH:MM" for datetime. If the latter is used the current local Date will be set. In case of a Stop-time before Task start user will be notified.`, Func: func(c *ishell.Context) { arg := "none" if len(c.Args) > 0 { arg = strings.Join(c.Args, " ") closeTaskTime(arg) stdOut() } else { c.Println(boldRed("stop - Please enter a Datetime")) } c.Println(promptcol("______________________")) }, }) shell.AddCmd(&ishell.Cmd{ Name: "all", Help: "Show all Projects", LongHelp: ` Usage: all Show all Projects with small summary sorted by Customer.`, Func: func(c *ishell.Context) { allProjects() stdOut() c.Println(promptcol("______________________")) }, }) shell.AddCmd(&ishell.Cmd{ Name: "allbills", Help: "Show all Bills", LongHelp: ` Usage: allbills Show all previous Bills with small summary.`, Func: func(c *ishell.Context) { showLastBills(0) c.Println(promptcol("______________________")) }, }) shell.AddCmd(&ishell.Cmd{ Name: "allcustomers", Help: "Show all Customers", LongHelp: ` Usage: allcustomers Show all Customers.`, Func: func(c *ishell.Context) { allCustomers(false) c.Println(promptcol("______________________")) }, }) shell.AddCmd(&ishell.Cmd{ Name: "showbills", Help: " - Show the last n bills", LongHelp: ` Usage: showbills Show the last n Bills with a small summary.`, Func: func(c *ishell.Context) { //c.ClearScreen() arg := "none" if len(c.Args) > 0 { arg = strings.Join(c.Args, " ") argi, err := strconv.Atoi(arg) if err == nil { showLastBills(argi) } else { c.Println(boldRed(arg, "is not a valid integer!")) } } else { c.Println(boldRed("showbills - Please enter an integer")) } //stdOut() c.Println(promptcol("______________________")) }, }) /* // multiple choice shell.AddCmd(&ishell.Cmd{ Name: "choice", Help: "multiple choice prompt", Func: func(c *ishell.Context) { choice := c.MultiChoice([]string{ "Golangers", "Go programmers", "Gophers", "Goers", }, "What are Go programmers called ?") if choice == 2 { c.Println("You got it!") } else { c.Println("Sorry, you're wrong.") } }, }) // multiple choice shell.AddCmd(&ishell.Cmd{ Name: "checklist", Help: "checklist prompt", Func: func(c *ishell.Context) { languages := []string{"Python", "Go", "Haskell", "Rust"} choices := c.Checklist(languages, "What are your favourite programming languages ?", nil) out := func() (c []string) { for _, v := range choices { c = append(c, languages[v]) } return } c.Println("Your choices are", strings.Join(out(), ", ")) }, }) */ //Test stuff in shell shell.AddCmd(&ishell.Cmd{ Name: "test", Help: "Test some functions in interactive mode", LongHelp: ` Usage: test Test functions in interactive mode`, Func: func(c *ishell.Context) { //multichoice("Geh scheissn") nm,st := GetTaskSums(currproject.Id) i := Multichoice("Sicha?",st) fmt.Println(nm[i]) prid := GetProjectIds() cuid := GetCustomerIds() taid := GetTaskIds() prs := GetSelectedProjects(prid) cus := GetSelectedCustomers(cuid) tas := GetSelectedTasks(taid) c.Println("Tasks:",len(tas)) c.Println("Projects:",len(prs)) c.Println("Customers:",len(cus)) ExportCustomers(cus,"export.customers") ExportProjects(prs,"export.projects") ExportTasks(tas,"export.tasks") }, }) // Prompt Color shell.AddCmd(&ishell.Cmd{ Name: "colorprompt", Help: "Select the Color of the prompt", LongHelp: ` Usage: colorprompt Select the color of the prompt`, Func: func(c *ishell.Context) { choice := c.MultiChoice([]string{ boldMag("Magenta"), boldBlue("Blue"), boldCyan("Cyan"), boldGreen("Green"), boldYell("Yellow"), boldRed("Red"), }, "What Color should the Prompt be?") promptcol = PromptColor(choice).SprintFunc() //promptcol = mastercol.SprintFunc() c.SetPrompt(promptcol(">>>")) c.Println(promptcol("As You Wish!")) c.Println(promptcol("______________________")) }, }) // Gather Tasks For Bills shell.AddCmd(&ishell.Cmd{ Name: "charge", Help: "Select Tasks to be charged", LongHelp: ` Usage: charge Select the tasks that should be merged into a Bill. The Initial selection cannot be changed afterward. Only the complete bill can be deleted and a new selection made.`, Func: func(c *ishell.Context) { nix := []int{0} ids, str := GetTaskList(nix, true,true) choices := c.Checklist(str, "Which Tasks should be charged in the new bill ?", nil) out := func() (c []int) { for _, v := range choices { if v > -1 { c = append(c, ids[v]) } } return } //All selected ids selids := out() //bids,str2 := getTaskList(selids,false) //c.Println(bids,str2) c.Println(len(selids), "Tasks Selected") //If None Selected end if len(selids) == 0 { return } //get if selected tasks have multicustomers, are from multipe projects, and get the ids of the projects multicust, multiproj, projids := checkCustomerProjects(selids) billprojid := 0 // CHECK IF ONLY ONE CUSTOMER if multicust { c.Println(boldRed("Cannot Write One Bill to multiple Customers! Please Select different Tasks")) } else { // CHECK IF ONLY ONE PROJECT ELSE CHOOSE ONE if multiproj { prid, prstr := getProjectList(projids) sel := c.MultiChoice(prstr, "What Project Should be Billed ?") if sel > -1 { billprojid = prid[sel] } } else { billprojid = projids[0] } seltasks := GetSelectedTasks(selids) count, hours, dur := AnalyzeTasks(seltasks) //c.Printf("%v Tasks Selected. Totaling %.2f (h) - Dates: %s\n",count,hours,dur) proj, cust := GetProject(billprojid) // IF CUSTOMER 0 NO BILL CAN BE CREATED if cust.Id == 0 { c.Println(boldRed("Customer ", cust.Company, " with id ", cust.Id, " Cannot be billed. Please move ", proj.Name, " to a valid Customer")) return } //billid,billident := newBill(billprojid) //prs,cus := getProjectName(billprojid) //c.Println("For",cust.company,"-",cust.name) //c.Println("Project:",proj.name,"- ID:",proj.id) //c.Println("Projected Income:",hours*cust.satz,"€") //c.Println("Create New Bill:",billid) //c.ReadLine() //Make NEW BILL WITH ID and INV No c.ShowPrompt(false) //fullbillinfo := fmt.Sprintf("For: %s - %s - %.2f(€/h) - Invoice: %s Id:%v\nProject: %s - Id:%v\n%v Tasks Selected. Totaling %.2f (h) - Dates: %s\nProjected Income: %.2f(€)\n",cust.company,cust.name,cust.satz,billident,billid,proj.name,proj.id,count,hours,dur,hours*cust.satz) fullbillinfo := fmt.Sprintf("For: %s - %s - %.2f(€/h) \nProject: %s - Id:%v\n%v Tasks Selected. Totaling %.2f (h) - Dates: %s\nProjected Income: %.2f(€)\n", cust.Company, cust.Name, cust.Satz, proj.Name, proj.Id, count, hours, dur, hours*cust.Satz) sep := "-------------------------\n" restinfo := "Here some Info about the rest" restids := selids var allitems []billitem //allaccounted := false //resttasks := seltasks //SELECT SUBSET AND NAME BILLITEM //c.clear()//Println(some,"Str2:",str2) for { if len(restids) == 0 { break } resttasks := GetSelectedTasks(restids) rcount, rhours, _ := AnalyzeTasks(resttasks) rids, rstr := GetTaskList(restids, false,false) qu := "Select Tasks to Group as Billitem" restinfo = fmt.Sprintf("%v Tasks Left, Total %.2f(h)\n%s", rcount, rhours, qu) pre := fmt.Sprintf("%s%s%s", fullbillinfo, sep, restinfo) choices2 := c.Checklist(rstr, pre, nil) out = func() (c []int) { for _, v := range choices2 { if v > -1 { c = append(c, rids[v]) } } return } taskids := out() restids = removeItems(restids, taskids) if len(taskids) > 0 { ittasks := GetSelectedTasks(taskids) itcount, ithours, itdur := AnalyzeTasks(ittasks) c.Printf("\n%v Tasks Selected, Total %.2f(h), Date: %s\n", itcount, ithours, itdur) c.ShowPrompt(false) c.Print("Name your Item for the Bill: ") tsk := c.ReadLine() var hrf float64 for { c.Print("How Many Hours: ") hr := c.ReadLine() hrf, err = strconv.ParseFloat(hr, 64) if err != nil { c.Println(hr, boldRed("can not be Parsed as a Float."), "Try a shape of X.X") } else { break } } c.Printf("%T %v - %T %v\n", tsk, tsk, hrf, hrf) allitems = append(allitems, billitem{tsk, itdur, hrf, Round(hrf*cust.Satz, 5)}) c.Print("<>") c.ReadLine() //c.ShowPrompt(true) } } billid, billident := newBill(billprojid) //c.Println(green("Bill Completed")) fullbill := bill{billid, billident, dur, proj.Id, proj.Name, time.Time{}, time.Time{}, allitems} saveBill(fullbill) checkTasks(selids, billid) c.ProgressBar().Indeterminate(true) c.ProgressBar().Start() testid := []int{billid} testbill := loadBills(testid) //c.Println(testbill[0].projectname,testbill[0].items) files := billTemplate(testbill[0], cust) c.Println(files) err = runLatex(files.Main, testbill[0].identity) c.ProgressBar().Stop() if err == nil { c.Println("Finished without Errors") } else { c.Println("Finished with error:", err) } c.Print("<>") c.ReadLine() stdOut() } c.Println(promptcol("______________________")) c.ShowPrompt(true) }, }) // Decide if interactive mode should be started //args := removeStringFromArray(os.Args[1:],"-file",1) if len(interArgs) > 0 { //args := removeStringFromArray(os.Args[1:],"-file",1) //fmt.Println(args) shell.Process(interArgs...) } else { shell.Println("Starting interactive Shell") stdOut() //start shell shell.Run() // teardown shell.Close() } //fmt.Println("Laboravi emeritus...") } func isInterSure(question string) bool { shell := ishell.New() shell.ShowPrompt(false) defer shell.ShowPrompt(true) shell.Printf("%s (type 'y/Y/yes' to confirm) : ", question) line := shell.ReadLine() if line == "yes" || line == "y" || line == "Y" { return true } else { return false } } /* func getInterAutoInput(question string,autocom []string) (out string) { shell := ishell.New() shell.ShowPrompt(false) defer shell.ShowPrompt(true) shell.AddCmd(&ishell.Cmd{ Name: "fun", Help: "never to be seen", Completer: func([]string) []string { return autocom }, Func: func(c *ishell.Context) { shell.Print(question) //out = shell.ReadLine() out = fmt.Sprint(c.Args) }, }) //shell.SetRootCmd("fun") shell.Run() return }*/ func getInterInput(question string) (out string) { shell := ishell.New() shell.ShowPrompt(false) defer shell.ShowPrompt(true) shell.Print(question) out = shell.ReadLine() return } func getNewInterInput(question, old, border string) string { shell := ishell.New() shell.ShowPrompt(false) defer shell.ShowPrompt(true) if old != "" { shell.Println(border+"Current:", old) } shell.Print(border+question) line := shell.ReadLine() if line == "" { return old } else { return line } } func getInterMultiInput(question string) (out string) { shell := ishell.New() //shell.ShowPrompt(false) //defer shell.ShowPrompt(true) shell.SetPrompt(nli+">>>") shell.SetMultiPrompt(nli+"...") shell.Println(question, "(Multiline input, end with ';')") out = shell.ReadMultiLines(";") shell.Println(sli+" -Done-") return } func getNewInterMultiInput(question, old, border string) (out string) { shell := ishell.New() //shell.ShowPrompt(false) //defer shell.ShowPrompt(true) shell.SetPrompt(border+">>>") shell.SetMultiPrompt(border+"...") if old != "" { shell.Println(border,"Current:\n", old) shell.Println(border,"Should current entry be replaced? A Newline will be added otherwise") } if isInterSure(border+" ") { shell.Println(border, question, "(Multiline input, end with ';')") out = shell.ReadMultiLines(";") shell.Println(border," -Done-") } else { shell.Print(nli+question) txt := shell.ReadLine() if txt == "" { out = old } else { out = old + "\n" + txt } } return /*if line == "" { return old }else{ return line }*/ } // Invoke an Multiplechoice Question with a question and // the list of options and return the selected list id func Multichoice(question string,list []string) (int) { shell := ishell.New() marker := li+li+">" shell.SetMultiChoicePrompt(marker,nli) shell.SetChecklistOptions("[ ] ", "[X] ") qu1 := line(marker,false) qu2 := frame(question,true) quest := qu1 + strings.TrimLeft(qu2,"\n") choice := shell.MultiChoice(list,quest) return choice /*shell.AddCmd(&ishell.Cmd{ Name: "choice", Help: "multiple choice prompt", Func: func(c *ishell.Context) { choice := c.MultiChoice([]string{ "Golangers", "Go programmers", "Gophers", "Goers", }, "What are Go programmers called ?") if choice == 2 { c.Println("You got it!") } else { c.Println("Sorry, you're wrong.") } }, }) str := "choice" shell.Process(str) shell.Println(about)*/ }