-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit fd6ced3
Showing
14 changed files
with
1,119 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
# If you prefer the allow list template instead of the deny list, see community template: | ||
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore | ||
# | ||
# Binaries for programs and plugins | ||
*.exe | ||
*.exe~ | ||
*.dll | ||
*.so | ||
*.dylib | ||
|
||
# Test binary, built with `go test -c` | ||
*.test | ||
|
||
# Output of the go coverage tool, specifically when used with LiteIDE | ||
*.out | ||
|
||
# Dependency directories (remove the comment below to include it) | ||
# vendor/ | ||
|
||
# Go workspace file | ||
go.work |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
# hac-cli | ||
|
||
home access center command line interface | ||
|
||
## usage | ||
- build the executable: `go build` | ||
- run the executable: `hac` | ||
- setup credentials: `hac setup` | ||
- view overview of classes (ids and averages): `hac overview` | ||
- view all grades for a class: `hac class [id]` where id is from `hac overview` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
package cmd | ||
|
||
import ( | ||
"bytes" | ||
"fmt" | ||
"net/http" | ||
"net/http/cookiejar" | ||
"net/url" | ||
"os" | ||
"strconv" | ||
"strings" | ||
"text/tabwriter" | ||
"time" | ||
|
||
"github.com/Delta456/box-cli-maker/v2" | ||
"github.com/PuerkitoBio/goquery" | ||
"github.com/briandowns/spinner" | ||
"github.com/cheynewallace/tabby" | ||
"github.com/gookit/color" | ||
"github.com/spf13/cobra" | ||
"github.com/spf13/viper" | ||
) | ||
|
||
// classCmd represents the class command | ||
var classCmd = &cobra.Command{ | ||
Use: "class [id]", | ||
Short: "Get grades for a specifed class", | ||
Long: `Get a full list of grades for a class based on its ID. | ||
To get the ID for a class, run hac overview, and use the ID on the left column, and use that as an argument to hac class. | ||
For example, if hac overview says that AP Calculus AB has an id of 1, to view a full set of grades for that class, one would run hac class 1`, | ||
Run: func(cmd *cobra.Command, args []string) { | ||
classID, err := strconv.Atoi(args[0]) | ||
if err != nil { | ||
color.Red.Println("❌ Class ID must be a number!") | ||
os.Exit(1) | ||
} | ||
|
||
if viper.GetString("username") == "" || viper.GetString("password") == "" || viper.GetString("url") == "" { | ||
color.Red.Println("❌ Please define a username, password, and home access url in $HOME/.hac.yaml! (or, use hac setup to run a wizard that does it for you)") | ||
os.Exit(1) | ||
} | ||
|
||
sp := spinner.New(spinner.CharSets[14], 100*time.Millisecond) | ||
sp.Suffix = color.Blue.Render(" Fetching data from HAC...") | ||
|
||
sp.Start() | ||
cj, err := cookiejar.New(nil) | ||
cobra.CheckErr(err) | ||
client := &http.Client{Jar: cj} | ||
rvtReq, err := client.Get(fmt.Sprintf("%v/HomeAccess/Account/LogOn", viper.GetString("url"))) | ||
cobra.CheckErr(err) | ||
defer rvtReq.Body.Close() | ||
|
||
rvtDoc, err := goquery.NewDocumentFromReader(rvtReq.Body) | ||
cobra.CheckErr(err) | ||
|
||
sel := rvtDoc.Find("input[name='__RequestVerificationToken']") | ||
rvt, ex := sel.Attr("value") | ||
if !ex { | ||
color.Red.Println("❌ Failed to fetch request token from HAC!") | ||
os.Exit(1) | ||
} | ||
|
||
reqBody := url.Values{} | ||
reqBody.Add("__RequestVerificationToken", rvt) | ||
reqBody.Add("SCKTY00328510CustomEnabled", "False") | ||
reqBody.Add("SCKTY00436568CustomEnabled", "False") | ||
reqBody.Add("Database", "10") | ||
reqBody.Add("VerificationOption", "UsernamePassword") | ||
reqBody.Add("LogOnDetails.UserName", viper.GetString("username")) | ||
reqBody.Add("LogOnDetails.Password", viper.GetString("password")) | ||
reqBody.Add("tempUN", "") | ||
reqBody.Add("tempPW", "") | ||
|
||
loginResp, err := client.PostForm(fmt.Sprintf("%v/HomeAccess/Account/LogOn", viper.GetString("url")), reqBody) | ||
client.Jar.SetCookies(loginResp.Request.URL, loginResp.Cookies()) | ||
cobra.CheckErr(err) | ||
|
||
assignmentsResp, err := client.Get(fmt.Sprintf("%v/HomeAccess/Content/Student/Assignments.aspx", viper.GetString("url"))) | ||
client.Jar.SetCookies(assignmentsResp.Request.URL, assignmentsResp.Cookies()) | ||
cobra.CheckErr(err) | ||
|
||
defer assignmentsResp.Body.Close() | ||
|
||
assignments, err := goquery.NewDocumentFromReader(assignmentsResp.Body) | ||
cobra.CheckErr(err) | ||
sp.Stop() | ||
|
||
buf := new(bytes.Buffer) | ||
wtr := tabwriter.NewWriter(buf, 0, 0, 2, ' ', 0) | ||
|
||
t := tabby.NewCustom(wtr) | ||
|
||
t.AddHeader("Assigment", "Category", "Grade", "Due", "Assigned") | ||
|
||
classHTML := assignments.Find(".AssignmentClass").Nodes[classID] | ||
class := goquery.NewDocumentFromNode(classHTML) | ||
class.Find("div[class='sg-content-grid'] tr[class='sg-asp-table-data-row']").Each(func(i int, s *goquery.Selection) { | ||
tds := s.Find("td") | ||
dueDate := strings.TrimSpace(tds.Eq(0).Text()) | ||
assignedDate := strings.TrimSpace(tds.Eq(1).Text()) | ||
category := strings.TrimSpace(tds.Eq(3).Text()) | ||
score := strings.TrimSpace(tds.Eq(4).Text()) | ||
total := strings.TrimSpace(tds.Eq(5).Text()) | ||
name := strings.TrimSpace(s.Find("a").Text()) | ||
|
||
if name != "" { | ||
t.AddLine(name, category, fmt.Sprintf("%v/%v", score, total), dueDate, assignedDate) | ||
} | ||
}) | ||
|
||
t.Print() | ||
bx := box.New(box.Config{Px: 0, Py: 0, Type: "Round", Color: "Cyan"}) | ||
className := strings.TrimSpace( | ||
strings.TrimSpace( | ||
class.Find("a[class='sg-header-heading']").Text(), | ||
)[9:], | ||
) | ||
classAvg := strings.TrimSpace( | ||
class.Find( | ||
fmt.Sprintf("span[id='plnMain_rptAssigmnetsByCourse_lblHdrAverage_%v']", classID), | ||
).Text(), | ||
)[3:] | ||
bx.Println(fmt.Sprintf("Grades for %v - Average %v", className, classAvg), buf.String()) | ||
|
||
}, | ||
} | ||
|
||
func init() { | ||
rootCmd.AddCommand(classCmd) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
package cmd | ||
|
||
import ( | ||
"bytes" | ||
"fmt" | ||
"net/http" | ||
"net/http/cookiejar" | ||
"net/url" | ||
"os" | ||
"strconv" | ||
"strings" | ||
"text/tabwriter" | ||
"time" | ||
|
||
"github.com/Delta456/box-cli-maker/v2" | ||
"github.com/PuerkitoBio/goquery" | ||
"github.com/briandowns/spinner" | ||
"github.com/cheynewallace/tabby" | ||
"github.com/gookit/color" | ||
"github.com/spf13/cobra" | ||
"github.com/spf13/viper" | ||
) | ||
|
||
// overviewCmd represents the overview command | ||
var overviewCmd = &cobra.Command{ | ||
Use: "overview", | ||
Short: "Overview of grades", | ||
Long: `Get an overview of curent grades`, | ||
Run: func(cmd *cobra.Command, args []string) { | ||
if viper.GetString("username") == "" || viper.GetString("password") == "" || viper.GetString("url") == "" { | ||
color.Red.Println("❌ Please define a username, password, and home access url in $HOME/.hac.yaml! (or, use hac setup to run a wizard that does it for you)") | ||
os.Exit(1) | ||
} | ||
sp := spinner.New(spinner.CharSets[14], 100*time.Millisecond) | ||
sp.Suffix = color.Blue.Render(" Fetching data from HAC...") | ||
sp.Start() | ||
cj, err := cookiejar.New(nil) | ||
cobra.CheckErr(err) | ||
client := &http.Client{Jar: cj} | ||
rvtReq, err := client.Get(fmt.Sprintf("%v/HomeAccess/Account/LogOn", viper.GetString("url"))) | ||
cobra.CheckErr(err) | ||
defer rvtReq.Body.Close() | ||
|
||
rvtDoc, err := goquery.NewDocumentFromReader(rvtReq.Body) | ||
cobra.CheckErr(err) | ||
|
||
sel := rvtDoc.Find("input[name='__RequestVerificationToken']") | ||
rvt, ex := sel.Attr("value") | ||
if !ex { | ||
color.Red.Println("❌ Failed to fetch Request Token from HAC!") | ||
os.Exit(1) | ||
} | ||
|
||
reqBody := url.Values{} | ||
reqBody.Add("__RequestVerificationToken", rvt) | ||
reqBody.Add("SCKTY00328510CustomEnabled", "False") | ||
reqBody.Add("SCKTY00436568CustomEnabled", "False") | ||
reqBody.Add("Database", "10") | ||
reqBody.Add("VerificationOption", "UsernamePassword") | ||
reqBody.Add("LogOnDetails.UserName", viper.GetString("username")) | ||
reqBody.Add("LogOnDetails.Password", viper.GetString("password")) | ||
reqBody.Add("tempUN", "") | ||
reqBody.Add("tempPW", "") | ||
|
||
loginResp, err := client.PostForm(fmt.Sprintf("%v/HomeAccess/Account/LogOn", viper.GetString("url")), reqBody) | ||
client.Jar.SetCookies(loginResp.Request.URL, loginResp.Cookies()) | ||
cobra.CheckErr(err) | ||
|
||
assignmentsResp, err := client.Get(fmt.Sprintf("%v/HomeAccess/Content/Student/Assignments.aspx", viper.GetString("url"))) | ||
client.Jar.SetCookies(assignmentsResp.Request.URL, assignmentsResp.Cookies()) | ||
cobra.CheckErr(err) | ||
|
||
defer assignmentsResp.Body.Close() | ||
|
||
assignments, err := goquery.NewDocumentFromReader(assignmentsResp.Body) | ||
cobra.CheckErr(err) | ||
sp.Stop() | ||
|
||
buf := new(bytes.Buffer) | ||
wtr := tabwriter.NewWriter(buf, 0, 0, 2, ' ', 0) | ||
|
||
t := tabby.NewCustom(wtr) | ||
|
||
t.AddHeader("Class ID", "Name", "Average") | ||
|
||
assignments.Find(".AssignmentClass").Each(func(i int, s *goquery.Selection) { | ||
name := s.Find("a[class='sg-header-heading']").Text() | ||
avg := s.Find(fmt.Sprintf("span[id='plnMain_rptAssigmnetsByCourse_lblHdrAverage_%v']", i)).Text() | ||
t.AddLine(i, strings.TrimSpace(strings.TrimSpace(name)[9:]), colorGrade(strings.TrimSpace(avg[3:]))) | ||
}) | ||
|
||
t.Print() | ||
bx := box.New(box.Config{Px: 0, Py: 0, Type: "Round", Color: "Cyan"}) | ||
bx.Println("", buf.String()) | ||
|
||
}, | ||
} | ||
|
||
func colorGrade(avg string) string { | ||
numAvg, _ := strconv.Atoi(avg) | ||
switch true { | ||
case numAvg >= 90: | ||
return color.Green.Render(avg) | ||
case numAvg >= 80: | ||
return color.Blue.Render(avg) | ||
case numAvg >= 70: | ||
return color.Yellow.Render(avg) | ||
default: | ||
return color.Red.Render(avg) | ||
} | ||
|
||
} | ||
|
||
func init() { | ||
rootCmd.AddCommand(overviewCmd) | ||
|
||
// Here you will define your flags and configuration settings. | ||
|
||
// Cobra supports Persistent Flags which will work for this command | ||
// and all subcommands, e.g.: | ||
// overviewCmd.PersistentFlags().String("foo", "", "A help for foo") | ||
|
||
// Cobra supports local flags which will only run when this command | ||
// is called directly, e.g.: | ||
// overviewCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") | ||
} |
Oops, something went wrong.