Skip to content

Commit

Permalink
feat: wooda proof of concept; a bit polygon package refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
Gornak40 committed Feb 26, 2024
1 parent 30e96ad commit a930051
Show file tree
Hide file tree
Showing 8 changed files with 319 additions and 70 deletions.
48 changes: 45 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@
| [ripper](#ripper) | change runs status | 🦍 | ||
| [scalp](#scalp) | incremental scoring | | 🦍 ||
| [valeria](#valeria) | valuer.cfg + tex scoring | | 🦍 ||
| 👻 | list/commit problems | | 🦍 | 🧑‍💻 |
| 👻 | regexp problem upload | | 🦍 | 🤔 |
| [wooda](#wooda) | regexp problem upload | | 🦍 | 🧑‍💻 |
| ⚙️ | move json config to ini | | | 🧑‍💻 |
| 👻 | list/commit problems | | 🦍 | 🤔 |
| 👻 | download/upload package | | 🦍 | 🤔 |
| 👻 | import polygon problem | 🦍 | 🦍 | 🤔 |
| 👻 | autogen static problem | 🦍 | | 🤔 |
Expand All @@ -24,6 +25,7 @@
- 🧑‍💻 In progress
- 🤔 To do
- 👻 Name placeholder
- ⚙️ Refactor task
- 🦍 Engines usage

## Build
Expand All @@ -48,7 +50,13 @@ Put your config file in `~/.config/algolymp/config.json`.
"polygon": {
"url": "https://polygon.codeforces.com",
"apiKey": "<key>",
"apiSecret": "<secret>"
"apiSecret": "<secret>",
"wooda": {
"polygon": {
"ignore": ".*\\.a$",
"test": "^tests/\\d+$"
}
}
},
"system": {
"editor": "nano"
Expand Down Expand Up @@ -269,3 +277,37 @@ valeria -i 318882 | bat -l tex
```

![valeria logo](https://algolymp.ru/static/img/valeria.png)

## wooda
*Upload problem files filtered by regexp to Polygon using API.*

### About

**Now this is a proof of concept. Many more mods will be supported in the future.**

Match all files in directory with config regexp patterns. Upload recognized files to Polygon.

Supported modes:

- `ignore`
- `test`

### Flags
- `-i` - problem id (required)
- `-m` - mode from config (required)
- `-d` - problem directory (required)

### Config
- `polygon.url`
- `polygon.apiKey`
- `polygon.apiSecret`
- `wooda`

### Examples

```bash
wooda --help
wooda -i 337320 -m polygon -d .
```

![wooda logo](https://algolymp.ru/static/img/wooda.png)
57 changes: 57 additions & 0 deletions cmd/wooda/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package main

import (
"os"
"path/filepath"

"github.com/Gornak40/algolymp/config"
"github.com/Gornak40/algolymp/polygon"
"github.com/Gornak40/algolymp/polygon/wooda"
"github.com/akamensky/argparse"
"github.com/sirupsen/logrus"
)

func main() {
cfg := config.NewConfig()
woodaKeys := make([]string, 0, len(cfg.Polygon.Wooda))
for k := range cfg.Polygon.Wooda {
woodaKeys = append(woodaKeys, k)
}

parser := argparse.NewParser("wooda", "Upload problem files filtered by regexp to Polygon.")
pID := parser.Int("i", "pid", &argparse.Options{
Required: true,
Help: "Polygon problem ID",
})
mode := parser.Selector("m", "mode", woodaKeys, &argparse.Options{
Required: true,
Help: "Local storage mode",
})
pDir := parser.String("d", "directory", &argparse.Options{
Required: true,
Help: "Local storage directory",
})
if err := parser.Parse(os.Args); err != nil {
logrus.WithError(err).Fatal("bad arguments")
}

pClient := polygon.NewPolygon(&cfg.Polygon)
woodaCfg := cfg.Polygon.Wooda[*mode] // mode is good argparse.Selector
wooda := wooda.NewWooda(pClient, *pID, &woodaCfg)
if err := filepath.Walk(*pDir, wooda.DirWalker); err != nil {
logrus.WithError(err).Fatal("failed wooda matching")
}
}

/*
"wooda": {
"polygon": {
"ignore": ["tests/*\.a"],
"test": ["tests/*"],
"validator": "files/val*\.cpp",
"checker": "checker\.cpp",
"solution": ["solutions/*\.cpp"],
"generator": ["files/gen*\.cpp"]
}
}
*/
Binary file added logos/wooda.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
23 changes: 23 additions & 0 deletions polygon/answers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package polygon

import "encoding/json"

type TestAnswer struct {
Index int `json:"index"`
Group string `json:"group"`
Points float32 `json:"points"`
UseInStatements bool `json:"useInStatements"`
}

type GroupAnswer struct {
Name string `json:"name"`
PointsPolicy string `json:"pointsPolicy"`
FeedbackPolicy string `json:"feedbackPolicy"`
Dependencies []string `json:"dependencies"`
}

type Answer struct {
Status string `json:"status"`
Comment string `json:"comment"`
Result json.RawMessage `json:"result"`
}
116 changes: 60 additions & 56 deletions polygon/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,19 @@ const (

var (
ErrBadPolygonStatus = errors.New("bad polygon status")
ErrInvalidMethod = errors.New("invalid method")
)

type Config struct {
URL string `json:"url"`
APIKey string `json:"apiKey"`
APISecret string `json:"apiSecret"`
URL string `json:"url"`
APIKey string `json:"apiKey"`
APISecret string `json:"apiSecret"`
Wooda map[string]WoodaConfig `json:"wooda"`
}

type WoodaConfig struct {
Ignore string `json:"ignore"`
Test string `json:"test"`
}

type Polygon struct {
Expand All @@ -47,8 +54,36 @@ func NewPolygon(cfg *Config) *Polygon {
}
}

func (p *Polygon) makeQuery(method string, link string) (*Answer, error) {
req, _ := http.NewRequestWithContext(context.TODO(), method, link, nil)
func buildRequest(method, link string, params url.Values) (*http.Request, error) {
logrus.WithFields(logrus.Fields{
"method": method,
"url": link,
}).Info("build request")

switch method {
case http.MethodGet:
link = fmt.Sprintf("%s?%s", link, params.Encode())

return http.NewRequestWithContext(context.TODO(), method, link, nil)
case http.MethodPost:
buf := strings.NewReader(params.Encode())
req, err := http.NewRequestWithContext(context.TODO(), method, link, buf)
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")

return req, nil
default:
return nil, ErrInvalidMethod
}
}

func (p *Polygon) makeQuery(method, link string, params url.Values) (*Answer, error) {
req, err := buildRequest(method, link, params)
if err != nil {
return nil, err
}
resp, err := p.client.Do(req)
if err != nil {
return nil, err
Expand Down Expand Up @@ -81,9 +116,8 @@ func (p *Polygon) skipEscape(params url.Values) string {
return strings.Join(pairs, "&")
}

func (p *Polygon) buildURL(method string, params url.Values) string {
func (p *Polygon) buildURL(method string, params url.Values) (string, url.Values) {
url, _ := url.JoinPath(p.cfg.URL, "api", method)
logrus.WithField("method", method).Info("preparing the request")

params["apiKey"] = []string{p.cfg.APIKey}
params["time"] = []string{strconv.FormatInt(time.Now().Unix(), 10)}
Expand All @@ -93,35 +127,15 @@ func (p *Polygon) buildURL(method string, params url.Values) string {
hsh := hex.EncodeToString(b[:])
params["apiSig"] = []string{sixSecretSymbols + hsh}

return fmt.Sprintf("%s?%s", url, params.Encode())
}

type TestAnswer struct {
Index int `json:"index"`
Group string `json:"group"`
Points float32 `json:"points"`
UseInStatements bool `json:"useInStatements"`
}

type GroupAnswer struct {
Name string `json:"name"`
PointsPolicy string `json:"pointsPolicy"`
FeedbackPolicy string `json:"feedbackPolicy"`
Dependencies []string `json:"dependencies"`
}

type Answer struct {
Status string `json:"status"`
Comment string `json:"comment"`
Result json.RawMessage `json:"result"`
return url, params
}

func (p *Polygon) GetGroups(pID int) ([]GroupAnswer, error) {
link := p.buildURL("problem.viewTestGroup", url.Values{
link, params := p.buildURL("problem.viewTestGroup", url.Values{
"problemId": []string{strconv.Itoa(pID)},
"testset": []string{defaultTestset},
})
ansG, err := p.makeQuery(http.MethodGet, link)
ansG, err := p.makeQuery(http.MethodGet, link, params)
if err != nil {
return nil, err
}
Expand All @@ -132,12 +146,12 @@ func (p *Polygon) GetGroups(pID int) ([]GroupAnswer, error) {
}

func (p *Polygon) GetTests(pID int) ([]TestAnswer, error) {
link := p.buildURL("problem.tests", url.Values{
link, params := p.buildURL("problem.tests", url.Values{
"problemId": []string{strconv.Itoa(pID)},
"testset": []string{defaultTestset},
"noInputs": []string{"true"},
})
ansT, err := p.makeQuery(http.MethodGet, link)
ansT, err := p.makeQuery(http.MethodGet, link, params)
if err != nil {
return nil, err
}
Expand All @@ -148,51 +162,41 @@ func (p *Polygon) GetTests(pID int) ([]TestAnswer, error) {
}

func (p *Polygon) EnableGroups(pID int) error {
link := p.buildURL("problem.enableGroups", url.Values{
link, params := p.buildURL("problem.enableGroups", url.Values{
"problemId": []string{strconv.Itoa(pID)},
"testset": []string{defaultTestset},
"enable": []string{"true"},
})
_, err := p.makeQuery(http.MethodPost, link)
_, err := p.makeQuery(http.MethodPost, link, params)

return err
}

func (p *Polygon) EnablePoints(pID int) error {
link := p.buildURL("problem.enablePoints", url.Values{
link, params := p.buildURL("problem.enablePoints", url.Values{
"problemId": []string{strconv.Itoa(pID)},
"enable": []string{"true"},
})
_, err := p.makeQuery(http.MethodPost, link)
_, err := p.makeQuery(http.MethodPost, link, params)

return err
}

type TestRequest url.Values

func NewTestRequest(pID int, index int) TestRequest {
return TestRequest{
func (p *Polygon) SaveResource(pID int, name, content string) error {
link, params := p.buildURL("problem.saveFile", url.Values{
"problemId": []string{strconv.Itoa(pID)},
"testIndex": []string{strconv.Itoa(index)},
"testset": []string{defaultTestset},
}
}

func (tr TestRequest) Group(group string) TestRequest {
tr["testGroup"] = []string{group}

return tr
}

func (tr TestRequest) Points(points float32) TestRequest {
tr["testPoints"] = []string{fmt.Sprint(points)}
"type": []string{"resource"},
"name": []string{name},
"file": []string{content},
})
_, err := p.makeQuery(http.MethodPost, link, params)

return tr
return err
}

func (p *Polygon) SaveTest(tReq TestRequest) error {
link := p.buildURL("problem.saveTest", url.Values(tReq))
_, err := p.makeQuery(http.MethodPost, link)
link, params := p.buildURL("problem.saveTest", url.Values(tReq))
_, err := p.makeQuery(http.MethodPost, link, params)

return err
}
41 changes: 41 additions & 0 deletions polygon/requests.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package polygon

import (
"fmt"
"net/url"
"strconv"
)

type TestRequest url.Values

func NewTestRequest(pID int, index int) TestRequest {
return TestRequest{
"problemId": []string{strconv.Itoa(pID)},
"testIndex": []string{strconv.Itoa(index)},
"testset": []string{defaultTestset},
}
}

func (tr TestRequest) Group(group string) TestRequest {
tr["testGroup"] = []string{group}

return tr
}

func (tr TestRequest) Points(points float32) TestRequest {
tr["testPoints"] = []string{fmt.Sprint(points)}

return tr
}

func (tr TestRequest) Input(input string) TestRequest {
tr["testInput"] = []string{input}

return tr
}

func (tr TestRequest) Description(description string) TestRequest {
tr["testDescription"] = []string{description}

return tr
}
Loading

0 comments on commit a930051

Please sign in to comment.