freckie

Update

1 +config.json
2 +credentials.json
3 +token.json
...\ No newline at end of file ...\ No newline at end of file
...@@ -3,17 +3,19 @@ module classroom ...@@ -3,17 +3,19 @@ module classroom
3 go 1.14 3 go 1.14
4 4
5 require ( 5 require (
6 + classroom/endpoints v0.0.0
7 + classroom/functions v0.0.0
8 + classroom/models v0.0.0
9 + classroom/utils v0.0.0
6 github.com/go-sql-driver/mysql v1.5.0 10 github.com/go-sql-driver/mysql v1.5.0
7 github.com/julienschmidt/httprouter v1.3.0 11 github.com/julienschmidt/httprouter v1.3.0
8 github.com/rs/cors v1.7.0 12 github.com/rs/cors v1.7.0
9 - 13 + google.golang.org/api v0.36.0
10 - classroom/endpoints v0.0.0
11 - classroom/models v0.0.0
12 - classroom/functions v0.0.0
13 ) 14 )
14 15
15 replace ( 16 replace (
16 classroom/endpoints v0.0.0 => ./endpoints 17 classroom/endpoints v0.0.0 => ./endpoints
17 - classroom/models v0.0.0 => ./models
18 classroom/functions v0.0.0 => ./functions 18 classroom/functions v0.0.0 => ./functions
19 -)
...\ No newline at end of file ...\ No newline at end of file
19 + classroom/models v0.0.0 => ./models
20 + classroom/utils v0.0.0 => ./utils
21 +)
......
This diff is collapsed. Click to expand it.
1 +module classroom
2 +
3 +go 1.14
4 +
5 +require (
6 + golang.org/x/net v0.0.0-20201031054903-ff519b6c9102
7 + golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58
8 + google.golang.org/api v0.36.0
9 +)
This diff is collapsed. Click to expand it.
1 +package utils
2 +
3 +import (
4 + "encoding/json"
5 + "fmt"
6 + "log"
7 + "net/http"
8 + "os"
9 +
10 + "golang.org/x/net/context"
11 + "golang.org/x/oauth2"
12 +)
13 +
14 +// Retrieve a token, saves the token, then returns the generated client.
15 +func getClient(config *oauth2.Config) *http.Client {
16 + // The file token.json stores the user's access and refresh tokens, and is
17 + // created automatically when the authorization flow completes for the first
18 + // time.
19 + tokFile := "token.json"
20 + tok, err := tokenFromFile(tokFile)
21 + if err != nil {
22 + tok = getTokenFromWeb(config)
23 + saveToken(tokFile, tok)
24 + }
25 + return config.Client(context.Background(), tok)
26 +}
27 +
28 +// Request a token from the web, then returns the retrieved token.
29 +func getTokenFromWeb(config *oauth2.Config) *oauth2.Token {
30 + authURL := config.AuthCodeURL("state-token", oauth2.AccessTypeOffline)
31 + fmt.Printf("Go to the following link in your browser then type the "+
32 + "authorization code: \n%v\n", authURL)
33 +
34 + var authCode string
35 + if _, err := fmt.Scan(&authCode); err != nil {
36 + log.Fatalf("Unable to read authorization code: %v", err)
37 + }
38 +
39 + tok, err := config.Exchange(context.TODO(), authCode)
40 + if err != nil {
41 + log.Fatalf("Unable to retrieve token from web: %v", err)
42 + }
43 + return tok
44 +}
45 +
46 +// Retrieves a token from a local file.
47 +func tokenFromFile(file string) (*oauth2.Token, error) {
48 + f, err := os.Open(file)
49 + if err != nil {
50 + return nil, err
51 + }
52 + defer f.Close()
53 + tok := &oauth2.Token{}
54 + err = json.NewDecoder(f).Decode(tok)
55 + return tok, err
56 +}
57 +
58 +// Saves a token to a file path.
59 +func saveToken(path string, token *oauth2.Token) {
60 + fmt.Printf("Saving credential file to: %s\n", path)
61 + f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
62 + if err != nil {
63 + log.Fatalf("Unable to cache oauth token: %v", err)
64 + }
65 + defer f.Close()
66 + json.NewEncoder(f).Encode(token)
67 +}
1 +package utils
2 +
3 +import (
4 + "strings"
5 +)
6 +
7 +func A1ToInt(val string) int64 {
8 + upperVal := strings.ToUpper(val)
9 + idx := 0
10 + for i := range upperVal {
11 + idx += 26*i + (int(upperVal[i]) - 65)
12 + }
13 + return int64(idx)
14 +}
1 +package utils
2 +
3 +import (
4 + "context"
5 + "fmt"
6 + "io/ioutil"
7 + "log"
8 +
9 + "golang.org/x/oauth2/google"
10 + "google.golang.org/api/option"
11 + "google.golang.org/api/sheets/v4"
12 +)
13 +
14 +type SheetsService struct {
15 + srv *sheets.Service
16 + ctx context.Context
17 +}
18 +
19 +func NewSheetsService(credentialsPath string) (*SheetsService, error) {
20 + b, err := ioutil.ReadFile(credentialsPath)
21 + if err != nil {
22 + log.Fatalf("Unable to read client secret file: %v", err)
23 + }
24 +
25 + // If modifying these scopes, delete your previously saved token.json.
26 + config, err := google.ConfigFromJSON(b, "https://www.googleapis.com/auth/spreadsheets")
27 + if err != nil {
28 + log.Fatalf("Unable to parse client secret file to config: %v", err)
29 + }
30 + client := getClient(config)
31 +
32 + ctx := context.Background()
33 + sheetsService, err := sheets.NewService(ctx,
34 + option.WithHTTPClient(client),
35 + option.WithScopes(sheets.SpreadsheetsScope),
36 + )
37 + if err != nil {
38 + return nil, err
39 + }
40 +
41 + srv := &SheetsService{
42 + srv: sheetsService,
43 + ctx: ctx,
44 + }
45 +
46 + return srv, nil
47 +}
48 +
49 +func (s *SheetsService) WriteAndMerge(sr SheetsRequest) error {
50 + req := &sheets.Request{}
51 + req.MergeCells = &sheets.MergeCellsRequest{
52 + MergeType: "MERGE_ALL",
53 + Range: sr.Range,
54 + }
55 +
56 + rb := &sheets.BatchUpdateSpreadsheetRequest{
57 + Requests: []*sheets.Request{req},
58 + }
59 +
60 + _, err := s.srv.Spreadsheets.BatchUpdate(sr.SpreadSheetID, rb).Context(s.ctx).Do()
61 + if err != nil {
62 + return err
63 + }
64 +
65 + var vr sheets.ValueRange
66 + val := []interface{}{sr.Value}
67 + vr.Values = append(vr.Values, val)
68 + _, err = s.srv.Spreadsheets.Values.Append(sr.SpreadSheetID, sr.RangeStr, &vr).ValueInputOption("RAW").Do()
69 + if err != nil {
70 + return err
71 + }
72 +
73 + return nil
74 +}
75 +
76 +func (s *SheetsService) RemoveValue(sr SheetsRequest) error {
77 + req := &sheets.Request{}
78 + req.UnmergeCells = &sheets.UnmergeCellsRequest{
79 + Range: sr.Range,
80 + }
81 +
82 + req2 := &sheets.Request{}
83 + req2.UpdateBorders = &sheets.UpdateBordersRequest{
84 + Range: sr.Range,
85 + InnerHorizontal: &sheets.Border{
86 + Color: &sheets.Color{
87 + Blue: 0.0,
88 + Green: 0.0,
89 + Red: 0.0,
90 + },
91 + Style: "SOLID",
92 + },
93 + }
94 +
95 + rb := &sheets.BatchUpdateSpreadsheetRequest{
96 + Requests: []*sheets.Request{req, req2},
97 + }
98 +
99 + _, err := s.srv.Spreadsheets.BatchUpdate(sr.SpreadSheetID, rb).Context(s.ctx).Do()
100 + if err != nil {
101 + return err
102 + }
103 +
104 + _, err = s.srv.Spreadsheets.Values.Clear(sr.SpreadSheetID, sr.RangeStr, &sheets.ClearValuesRequest{}).Do()
105 + if err != nil {
106 + return err
107 + }
108 +
109 + return nil
110 +}
111 +
112 +type SheetsRequest struct {
113 + SpreadSheetID string
114 + SheetID int64
115 + Range *sheets.GridRange
116 + RangeStr string
117 + Value string
118 +}
119 +
120 +func NewSheetsRequest(
121 + spreadSheetID string, sheetID int64, column string, start, end int64, value string) SheetsRequest {
122 + colIndex := A1ToInt(column)
123 +
124 + req := SheetsRequest{}
125 + req.SpreadSheetID = spreadSheetID
126 + req.SheetID = sheetID
127 + req.Range = &sheets.GridRange{
128 + SheetId: sheetID,
129 + StartColumnIndex: colIndex,
130 + EndColumnIndex: colIndex + 1,
131 + StartRowIndex: start - 1,
132 + EndRowIndex: end,
133 + }
134 + req.RangeStr = fmt.Sprintf("%s%d:%s%d", column, start, column, end)
135 + req.Value = value
136 + return req
137 +}