From 31521c85a35b1a143bfb5a0478911d87bc9353e7 Mon Sep 17 00:00:00 2001 From: Leo Correa Date: Fri, 16 Jan 2015 17:21:43 -0500 Subject: [PATCH] Report unsubmitted exercises during fetch API --- CHANGELOG.md | 1 + api/api.go | 30 ++++++++++++++++++++++++++---- api/problem.go | 13 +++++++------ cmd/fetch.go | 24 +++++++++++++++++++++++- cmd/restore.go | 2 +- user/homework.go | 15 ++++++++++++++- user/item.go | 2 ++ 7 files changed, 74 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fb3c1f7c..08dfd379 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ The exercism CLI follows [semantic versioning](http://semver.org/). * [#153](https://github.com/exercism/cli/pull/153): Refactored configuration package - [@kytrinyx](https://github.com/kytrinyx) * [#154](https://github.com/exercism/cli/pull/154): Add 'list' command to list available exercises for a language - [@lcowell](https://github.com/lcowell) * [#157](https://github.com/exercism/cli/pull/157): Refactored API package - [@Tonkpils](https://github.com/Tonkpils) +* [#155](https://github.com/exercism/cli/pull/155): Display problems not yet submitted on fetch API - [@Tonkpils](https://github.com/Tonkpils) * **Your contribution here** ## v1.9.2 (2015-01-11) diff --git a/api/api.go b/api/api.go index cac44db1..0465a10f 100644 --- a/api/api.go +++ b/api/api.go @@ -31,6 +31,12 @@ type PayloadSubmission struct { PayloadError } +// SubmissionInfo contains state information about a submission. +type SubmissionInfo struct { + Slug string `json:"slug"` + State string `json:"state"` +} + // Fetch retrieves problems from the API. // In most cases these problems consist of a test suite and a README // from the x-api, but it is also used when restoring earlier iterations. @@ -62,7 +68,7 @@ func (c *Client) Fetch(args []string) ([]*Problem, error) { } if res.StatusCode != http.StatusOK { - return nil, fmt.Errorf(`unable to fetch problems (HTTP: %d) - %s`, res.StatusCode, payload.Error) + return nil, fmt.Errorf("unable to fetch problems (HTTP: %d) - %s", res.StatusCode, payload.Error) } return payload.Problems, nil @@ -83,12 +89,28 @@ func (c *Client) Restore() ([]*Problem, error) { } if res.StatusCode != http.StatusOK { - return nil, fmt.Errorf(`unable to fetch problems (HTTP: %d) - %s`, res.StatusCode, payload.Error) + return nil, fmt.Errorf("unable to fetch problems (HTTP: %d) - %s", res.StatusCode, payload.Error) } return payload.Problems, nil } +// Submissions gets a list of submitted exercises and their current acceptance state. +func (c *Client) Submissions() (map[string][]SubmissionInfo, error) { + url := fmt.Sprintf("%s/api/v1/exercises?key=%s", c.APIHost, c.APIKey) + req, err := c.NewRequest("GET", url, nil) + if err != nil { + return nil, err + } + + var payload map[string][]SubmissionInfo + if _, err := c.Do(req, &payload); err != nil { + return nil, err + } + + return payload, nil +} + // Download fetches a solution by submission key and writes it to disk. func (c *Client) Download(submissionID string) (*Submission, error) { url := fmt.Sprintf("%s/api/v1/submissions/%s", c.APIHost, submissionID) @@ -126,7 +148,7 @@ func (c *Client) Demo() ([]*Problem, error) { } if res.StatusCode != http.StatusOK { - return nil, fmt.Errorf(`unable to fetch problems (HTTP: %d) - %s`, res.StatusCode, payload.Error) + return nil, fmt.Errorf("unable to fetch problems (HTTP: %d) - %s", res.StatusCode, payload.Error) } return payload.Problems, nil @@ -152,7 +174,7 @@ func (c *Client) Submit(iter *Iteration) (*Submission, error) { } if res.StatusCode != http.StatusCreated { - return nil, fmt.Errorf(`unable to submit (HTTP: %d) - %s`, res.StatusCode, ps.Error) + return nil, fmt.Errorf("unable to submit (HTTP: %d) - %s", res.StatusCode, ps.Error) } return ps.Submission, nil diff --git a/api/problem.go b/api/problem.go index 833ca03c..1657f91d 100644 --- a/api/problem.go +++ b/api/problem.go @@ -4,12 +4,13 @@ import "fmt" // Problem represents a specific problem in a given language track. type Problem struct { - ID string `json:"id"` - TrackID string `json:"track_id"` - Language string `json:"language"` - Slug string `json:"slug"` - Name string `json:"name"` - Files map[string]string `json:"files"` + ID string `json:"id"` + TrackID string `json:"track_id"` + Language string `json:"language"` + Slug string `json:"slug"` + Name string `json:"name"` + Files map[string]string `json:"files"` + Submitted bool } func (p *Problem) String() string { diff --git a/cmd/fetch.go b/cmd/fetch.go index 91c709b2..60cd04cf 100644 --- a/cmd/fetch.go +++ b/cmd/fetch.go @@ -22,10 +22,32 @@ func Fetch(ctx *cli.Context) { log.Fatal(err) } + submissionInfo, err := client.Submissions() + if err != nil { + log.Fatal(err) + } + + if err := setSubmissionState(problems, submissionInfo); err != nil { + log.Fatal(err) + } + hw := user.NewHomework(problems, c) if err := hw.Save(); err != nil { log.Fatal(err) } - hw.Summarize() + hw.Summarize(user.HWAll) +} + +func setSubmissionState(problems []*api.Problem, submissionInfo map[string][]api.SubmissionInfo) error { + for _, problem := range problems { + langSubmissions := submissionInfo[problem.Language] + for _, submission := range langSubmissions { + if submission.Slug == problem.Slug { + problem.Submitted = true + } + } + } + + return nil } diff --git a/cmd/restore.go b/cmd/restore.go index 1bd678c5..89453ac9 100644 --- a/cmd/restore.go +++ b/cmd/restore.go @@ -27,5 +27,5 @@ func Restore(ctx *cli.Context) { if err := hw.Save(); err != nil { log.Fatal(err) } - hw.Summarize() + hw.Summarize(user.HWNotSubmitted) } diff --git a/user/homework.go b/user/homework.go index f054bcc7..c9378760 100644 --- a/user/homework.go +++ b/user/homework.go @@ -10,6 +10,10 @@ import ( // HWFilter is used to categorize homework items. type HWFilter int +// SummaryOption is an alias of HWFilter that allows +// selective display of summary items +type SummaryOption HWFilter + const ( // HWAll represents all items in the collection. HWAll = iota @@ -19,6 +23,9 @@ const ( // HWNew represents problems that did not yet exist on the // user's filesystem. HWNew + // HWNotSubmitted represents problems that have not been submitted + // for review. + HWNotSubmitted ) // Homework is a collection of problems that were fetched from the APIs. @@ -96,6 +103,8 @@ func (hw *Homework) heading(filter HWFilter, count int) { status = "Updated:" case HWNew: status = "New:" + case HWNotSubmitted: + status = "Not Submitted:" } summary := fmt.Sprintf("%d %s", count, unit) fmt.Printf(hw.template, status, summary) @@ -112,10 +121,14 @@ func (hw *Homework) maxTitleWidth() int { } // Summarize prints a full report of new and updated items in the set. -func (hw *Homework) Summarize() { +func (hw *Homework) Summarize(summaryFilter SummaryOption) { hw.Report(HWUpdated) hw.Report(HWNew) + if summaryFilter != HWNotSubmitted { + hw.Report(HWNotSubmitted) + } + fresh := len(hw.ItemsMatching(HWNew)) updated := len(hw.ItemsMatching(HWUpdated)) unchanged := len(hw.Items) - updated - fresh diff --git a/user/item.go b/user/item.go index 61e7b7c4..91162dc0 100644 --- a/user/item.go +++ b/user/item.go @@ -32,6 +32,8 @@ func (it *Item) Matches(filter HWFilter) bool { return it.isNew case HWUpdated: return it.isUpdated + case HWNotSubmitted: + return !it.Submitted } return true }