Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: display today streak counter #305

Merged
merged 2 commits into from
Aug 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cmd/contest.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ func selectUpcomingContest(c leetcode.Client, registeredOnly bool) (string, erro
for i, ct := range contestList {
mark := " "
if ct.Registered {
mark = ""
mark = ""
}
contestNames[i] = fmt.Sprintf(
"%s %s at %s",
Expand Down
19 changes: 19 additions & 0 deletions cmd/submit.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package cmd

import (
"fmt"
"strconv"
"strings"

"github.com/charmbracelet/log"
Expand Down Expand Up @@ -63,6 +64,11 @@ leetgo submit w330/
}
}

err = showTodayStreak(c, cmd)
if err != nil {
log.Debug("failed to show today's streak", "err", err)
}

if hasFailedCase {
return exitCode(1)
}
Expand Down Expand Up @@ -145,3 +151,16 @@ func appendToTestCases(q *leetcode.QuestionData, result *leetcode.SubmitCheckRes
err = utils.WriteFile(testCasesFile.GetPath(), content)
return true, err
}

func showTodayStreak(c leetcode.Client, cmd *cobra.Command) error {
streak, err := c.GetStreakCounter()
if err != nil {
return err
}
today := ""
if streak.TodayCompleted {
today = config.PassedStyle.Render("+1")
}
cmd.Printf("\nTotal streak: %s%s\n", strconv.Itoa(streak.StreakCount-1), today)
return nil
}
13 changes: 11 additions & 2 deletions cmd/test.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ leetgo test w330/`,
submitLimiter := newLimiter(user)

var hasFailedCase bool
var hasSubmitted bool
for _, q := range qs {
var (
localPassed = true
Expand Down Expand Up @@ -115,6 +116,7 @@ leetgo test w330/`,

if autoSubmit && remotePassed && (localPassed || forceSubmit) {
log.Info("submitting solution", "user", user.Whoami(c))
hasSubmitted = true
result, err := submitSolution(cmd, q, c, gen, submitLimiter)
if err != nil {
submitAccepted = false
Expand All @@ -125,7 +127,7 @@ leetgo test w330/`,
submitAccepted = false
added, _ := appendToTestCases(q, result)
if added {
log.Info("added failed case to testcases.txt")
log.Info("added failed cases to `testcases.txt`")
}
}
}
Expand All @@ -136,6 +138,13 @@ leetgo test w330/`,
}
}

if hasSubmitted {
err = showTodayStreak(c, cmd)
if err != nil {
log.Debug("failed to show today's streak", "err", err)
}
}

if hasFailedCase {
return exitCode(1)
}
Expand Down Expand Up @@ -213,7 +222,7 @@ func runTestRemotely(
if err != nil {
log.Debug("failed to update test cases", "err", err)
} else {
log.Info("testcases.txt updated")
log.Info("`testcases.txt` updated")
}
}
}
Expand Down
41 changes: 37 additions & 4 deletions leetcode/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ func (e UnexpectedStatusCode) IsError() bool {

func (e UnexpectedStatusCode) Error() string {
body := "<empty body>"
if len(e.Body) > 100 {
body = e.Body[:100] + "..."
if len(e.Body) > 500 {
body = e.Body[:500] + "..."
}
return fmt.Sprintf("[%d %s] %s", e.Code, http.StatusText(e.Code), body)
}
Expand Down Expand Up @@ -81,6 +81,7 @@ type Client interface {
GetContestQuestionData(contestSlug string, questionSlug string) (*QuestionData, error)
RegisterContest(slug string) error
UnregisterContest(slug string) error
GetStreakCounter() (StreakCounter, error)
}

type cnClient struct {
Expand Down Expand Up @@ -162,6 +163,7 @@ func nonFollowRedirect(req *http.Request, via []*http.Request) error {
}

type graphqlRequest struct {
path string
query string
operationName string
variables map[string]any
Expand All @@ -178,6 +180,7 @@ const (

const (
graphQLPath = "/graphql"
graphQLNoj = "/graphql/noj-go/"
accountLoginPath = "/accounts/login/"
contestInfoPath = "/contest/api/info/%s/"
contestProblemsPath = "/contest/%s/problems/%s/"
Expand Down Expand Up @@ -265,7 +268,11 @@ func (c *cnClient) graphqlGet(req graphqlRequest, result any) (*http.Response, e
v, _ := json.Marshal(req.variables)
p.Variables = string(v)
}
r, err := c.http.New().Get(graphQLPath).QueryStruct(p).Request()
path := graphQLPath
if req.path != "" {
path = req.path
}
r, err := c.http.New().Get(path).QueryStruct(p).Request()
if err != nil {
return nil, err
}
Expand All @@ -282,7 +289,11 @@ func (c *cnClient) graphqlPost(req graphqlRequest, result any) (*http.Response,
"operationName": req.operationName,
"variables": v,
}
r, err := c.http.New().Post(graphQLPath).BodyJSON(body).Request()
path := graphQLPath
if req.path != "" {
path = req.path
}
r, err := c.http.New().Post(path).BodyJSON(body).Request()
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -1012,3 +1023,25 @@ func (c *cnClient) GetQuestionTags() ([]QuestionTag, error) {
}
return tags, nil
}

func (c *cnClient) GetStreakCounter() (StreakCounter, error) {
query := `
query getStreakCounter {
problemsetStreakCounter {
today
streakCount
daysSkipped
todayCompleted
}
}`
var resp gjson.Result
_, err := c.graphqlPost(
graphqlRequest{path: graphQLNoj, query: query, authType: requireAuth}, &resp,
)
if err != nil {
return StreakCounter{}, err
}
var counter StreakCounter
err = json.Unmarshal(utils.StringToBytes(resp.Get("data.problemsetStreakCounter").Raw), &counter)
return counter, err
}
35 changes: 21 additions & 14 deletions leetcode/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,15 +87,15 @@ func (r *SubmitCheckResult) Display(q *QuestionData) string {
case Accepted:
return fmt.Sprintf(
"\n%s%s%s%s\n",
config.PassedStyle.Render(fmt.Sprintf(" %s\n", r.StatusMsg)),
config.PassedStyle.Render(fmt.Sprintf(" %s\n", r.StatusMsg)),
fmt.Sprintf("\nPassed cases: %d/%d", r.TotalCorrect, r.TotalTestcases),
fmt.Sprintf("\nRuntime: %s, better than %.0f%%", r.StatusRuntime, r.RuntimePercentile),
fmt.Sprintf("\nMemory: %s, better than %.0f%%", r.StatusMemory, r.MemoryPercentile),
)
case WrongAnswer:
return fmt.Sprintf(
"\n%s%s%s%s%s%s\n",
config.FailedStyle.Render(" × Wrong Answer\n"),
config.FailedStyle.Render(" Wrong Answer\n"),
fmt.Sprintf("\nPassed cases: %d/%d", r.TotalCorrect, r.TotalTestcases),
fmt.Sprintf("\nLast case: %s", utils.TruncateString(strings.ReplaceAll(r.LastTestcase, "\n", "↩ "), 100)),
fmt.Sprintf("\nOutput: %s", utils.TruncateString(strings.ReplaceAll(r.CodeOutput, "\n", "↩ "), 100)),
Expand All @@ -105,25 +105,25 @@ func (r *SubmitCheckResult) Display(q *QuestionData) string {
case MemoryLimitExceeded, TimeLimitExceeded, OutputLimitExceeded:
return fmt.Sprintf(
"\n%s%s%s\n",
config.ErrorStyle.Render(fmt.Sprintf(" × %s\n", r.StatusMsg)),
config.ErrorStyle.Render(fmt.Sprintf(" %s\n", r.StatusMsg)),
fmt.Sprintf("\nPassed cases: %d/%d", r.TotalCorrect, r.TotalTestcases),
fmt.Sprintf("\nLast case: %s", utils.TruncateString(r.LastTestcase, 100)),
)
case RuntimeError:
return fmt.Sprintf(
"\n%s%s%s\n",
config.ErrorStyle.Render(fmt.Sprintf(" × %s\n", r.StatusMsg)),
config.ErrorStyle.Render(fmt.Sprintf(" %s\n", r.StatusMsg)),
fmt.Sprintf("\nPassed cases: %s", formatCompare(r.CompareResult)),
"\n"+config.StdoutStyle.Render(r.FullRuntimeError),
)
case CompileError:
return fmt.Sprintf(
"\n%s%s\n",
config.ErrorStyle.Render(fmt.Sprintf(" × %s\n", r.StatusMsg)),
config.ErrorStyle.Render(fmt.Sprintf(" %s\n", r.StatusMsg)),
"\n"+config.StdoutStyle.Render(r.FullCompileError),
)
default:
return config.FailedStyle.Render(fmt.Sprintf("\n × %s\n", r.StatusMsg))
return config.FailedStyle.Render(fmt.Sprintf("\n %s\n", r.StatusMsg))
}
}

Expand Down Expand Up @@ -181,9 +181,9 @@ func formatCompare(s string) string {
var sb strings.Builder
for _, c := range s {
if c == '1' {
sb.WriteString(config.PassedStyle.Render(""))
sb.WriteString(config.PassedStyle.Render(""))
} else {
sb.WriteString(config.ErrorStyle.Render("×"))
sb.WriteString(config.ErrorStyle.Render(""))
}
}
return sb.String()
Expand All @@ -199,7 +199,7 @@ func (r *RunCheckResult) Display(_ *QuestionData) string {
if r.CorrectAnswer {
return fmt.Sprintf(
"\n%s%s%s%s%s%s\n",
config.PassedStyle.Render(fmt.Sprintf(" %s\n", r.StatusMsg)),
config.PassedStyle.Render(fmt.Sprintf(" %s\n", r.StatusMsg)),
fmt.Sprintf("\nPassed cases: %s", formatCompare(r.CompareResult)),
fmt.Sprintf("\nInput: %s", utils.TruncateString(strings.ReplaceAll(r.InputData, "\n", "↩ "), 100)),
fmt.Sprintf("\nOutput: %s", utils.TruncateString(strings.Join(r.CodeAnswer, "↩ "), 100)),
Expand All @@ -209,7 +209,7 @@ func (r *RunCheckResult) Display(_ *QuestionData) string {
} else {
return fmt.Sprintf(
"\n%s%s%s%s%s%s\n",
config.ErrorStyle.Render("\n × Wrong Answer\n"),
config.ErrorStyle.Render("\n Wrong Answer\n"),
fmt.Sprintf("\nPassed cases: %s", formatCompare(r.CompareResult)),
fmt.Sprintf("\nInput: %s", utils.TruncateString(strings.ReplaceAll(r.InputData, "\n", "↩ "), 100)),
fmt.Sprintf("\nOutput: %s", utils.TruncateString(strings.Join(r.CodeAnswer, "↩ "), 100)),
Expand All @@ -218,22 +218,22 @@ func (r *RunCheckResult) Display(_ *QuestionData) string {
)
}
case MemoryLimitExceeded, TimeLimitExceeded, OutputLimitExceeded:
return config.ErrorStyle.Render(fmt.Sprintf("\n × %s\n", r.StatusMsg))
return config.ErrorStyle.Render(fmt.Sprintf("\n %s\n", r.StatusMsg))
case RuntimeError:
return fmt.Sprintf(
"\n%s%s%s\n",
config.ErrorStyle.Render(fmt.Sprintf(" × %s\n", r.StatusMsg)),
config.ErrorStyle.Render(fmt.Sprintf(" %s\n", r.StatusMsg)),
fmt.Sprintf("Passed cases: %s", formatCompare(r.CompareResult)),
"\n"+config.StdoutStyle.Render(r.FullRuntimeError),
)
case CompileError:
return fmt.Sprintf(
"\n%s%s\n",
config.ErrorStyle.Render(fmt.Sprintf(" × %s\n", r.StatusMsg)),
config.ErrorStyle.Render(fmt.Sprintf(" %s\n", r.StatusMsg)),
"\n"+config.StdoutStyle.Render(r.FullCompileError),
)
default:
return config.FailedStyle.Render(fmt.Sprintf("\n × %s\n", r.StatusMsg))
return config.FailedStyle.Render(fmt.Sprintf("\n %s\n", r.StatusMsg))
}
}

Expand All @@ -259,3 +259,10 @@ type QuestionTag struct {
TypeName string `json:"typeName"`
TypeTransName string `json:"typeTransName"`
}

type StreakCounter struct {
Today string `json:"today"`
StreakCount int `json:"streakCount"`
DaysSkipped int `json:"daysSkipped"`
TodayCompleted bool `json:"todayCompleted"`
}