Skip to content

Commit 9df851f

Browse files
chermehdiMehdi Cheracher
authored and
Mehdi Cheracher
committed
working parse version
1 parent 46b3bac commit 9df851f

File tree

10 files changed

+479
-13
lines changed

10 files changed

+479
-13
lines changed

commands/parse.go

+183-6
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,194 @@
11
package commands
22

33
import (
4+
"bytes"
5+
"context"
6+
json2 "encoding/json"
47
"fmt"
8+
. "github.com/chermehdi/egor/config"
9+
"github.com/chermehdi/egor/templates"
10+
"github.com/fatih/color"
511
"github.com/urfave/cli/v2"
12+
"io/ioutil"
13+
"net/http"
14+
"os"
15+
"path"
16+
"strings"
17+
template2 "text/template"
18+
"time"
619
)
720

8-
var ParseCommand cli.Command = cli.Command{
21+
const listenAddr = ":4243"
22+
23+
// Serialize task into a JSON string.
24+
func SerializeTask(meta EgorMeta) (string, error) {
25+
var buffer bytes.Buffer
26+
encoder := json2.NewEncoder(&buffer)
27+
if err := encoder.Encode(meta); err != nil {
28+
return "", err
29+
}
30+
return buffer.String(), nil
31+
}
32+
33+
func CreateFile(filePath string) (*os.File, error) {
34+
return os.OpenFile(filePath, os.O_TRUNC|os.O_CREATE|os.O_WRONLY, 0777)
35+
}
36+
37+
func CreateDirectoryStructure(task Task, config Config) error {
38+
dir, err := os.Getwd()
39+
if err != nil {
40+
return err
41+
}
42+
taskDir := path.Join(dir, task.Name)
43+
if err = os.Mkdir(taskDir, 0777); err != nil {
44+
return err
45+
}
46+
if err = os.Chdir(taskDir); err != nil {
47+
return err
48+
}
49+
egorMeta := NewEgorMeta(task, config)
50+
51+
file, err := CreateFile(config.ConfigFileName)
52+
if err != nil {
53+
return err
54+
}
55+
if err = egorMeta.Save(file); err != nil {
56+
return err
57+
}
58+
if err = os.Mkdir("inputs", 0777); err != nil {
59+
return err
60+
}
61+
62+
if err = os.Mkdir("outputs", 0777); err != nil {
63+
return err
64+
}
65+
inputs := egorMeta.Inputs
66+
for i := 0; i < len(inputs); i++ {
67+
file, err := CreateFile(inputs[i].Path)
68+
if err != nil {
69+
return err
70+
}
71+
_, err = file.WriteString(task.Tests[i].Input)
72+
if err != nil {
73+
return err
74+
}
75+
}
76+
77+
outputs := egorMeta.Outputs
78+
for i := 0; i < len(outputs); i++ {
79+
file, err := CreateFile(outputs[i].Path)
80+
if err != nil {
81+
return err
82+
}
83+
_, err = file.WriteString(task.Tests[i].Output)
84+
if err != nil {
85+
return err
86+
}
87+
}
88+
templateContent, err := templates.ResolveTemplateByLanguage(config.Lang.Default)
89+
if err != nil {
90+
return err
91+
}
92+
template := template2.New("Solution template")
93+
compiledTemplate, err := template.Parse(templateContent)
94+
if err != nil {
95+
return err
96+
}
97+
taskFile, err := CreateFile(egorMeta.TaskFile)
98+
if err != nil {
99+
return err
100+
}
101+
if err = compiledTemplate.Execute(taskFile, config); err != nil {
102+
return err
103+
}
104+
return nil
105+
}
106+
107+
// Given the json string returned by competitive companion
108+
// it will parse it as a json Task and return it.
109+
func extractTaskFromJson(json string) (*Task, error) {
110+
var task Task
111+
err := json2.Unmarshal([]byte(json), &task)
112+
if err != nil {
113+
return nil, err
114+
}
115+
return &task, nil
116+
}
117+
118+
func createWebServer(quit chan<- string) *http.Server {
119+
router := http.NewServeMux() // here you could also go with third party packages to create a router
120+
// Register your routes
121+
router.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
122+
w.WriteHeader(http.StatusOK)
123+
content, err := ioutil.ReadAll(r.Body)
124+
if err != nil {
125+
panic(err)
126+
}
127+
quit <- string(content)
128+
})
129+
return &http.Server{
130+
Addr: listenAddr,
131+
Handler: router,
132+
}
133+
}
134+
135+
func waitForShutDown(server *http.Server, done chan<- string, quit <-chan string) {
136+
content := <-quit
137+
color.Magenta("Shutting down the server")
138+
139+
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
140+
defer cancel()
141+
142+
server.SetKeepAlivesEnabled(false)
143+
144+
if err := server.Shutdown(ctx); err != nil {
145+
color.Red("Could not shutdown the server")
146+
}
147+
done <- content
148+
}
149+
150+
func ParseAction(context *cli.Context) error {
151+
msgReceiveChannel := make(chan string, 1)
152+
msgReadChannel := make(chan string, 1)
153+
154+
server := createWebServer(msgReadChannel)
155+
156+
go waitForShutDown(server, msgReceiveChannel, msgReadChannel)
157+
158+
color.Green("Starting the server on %s\n", listenAddr)
159+
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
160+
color.Red("Could not listen on %s, %v\n", listenAddr, err)
161+
}
162+
json := <-msgReceiveChannel
163+
color.Green("Server stopped")
164+
fmt.Printf("Received data %v\n", json)
165+
// first line contains a json string
166+
lines := strings.Split(json, "\n")
167+
task, err := extractTaskFromJson(lines[1])
168+
if err != nil {
169+
return err
170+
}
171+
fmt.Printf("task %v\n", task)
172+
config, err := LoadDefaultConfiguration()
173+
if err != nil {
174+
return err
175+
}
176+
err = CreateDirectoryStructure(*task, *config)
177+
if err != nil {
178+
color.Red("Error happened %v", err)
179+
}
180+
return nil
181+
}
182+
183+
// Command to parse tasks from the `Competitive Companion` Chrome extension.
184+
// Running this command, will start a server on the default port that the extension
185+
// uses, and will create a directory structure containing input files, expected output files,
186+
// and an additional `egor-meta.json` file, and finally your task file, which is usually a `main.cpp` or `Main.java`
187+
// file depending on the default configured language.
188+
var ParseCommand = cli.Command{
9189
Name: "parse",
10190
Aliases: []string{"p"},
11-
Usage: "parse task from navigator",
191+
Usage: "Parse a task using 'Competitive Companion'",
12192
UsageText: "parse task from navigator",
13-
Action: func(context *cli.Context) error {
14-
fmt.Println("Hello from parse")
15-
return nil
16-
},
193+
Action: ParseAction,
17194
}

commands/parse_test.go

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package commands
2+
3+
import (
4+
"github.com/stretchr/testify/assert"
5+
"testing"
6+
)
7+
8+
func getDummyTaskJson() string {
9+
return `{
10+
"group" : "Educational Codeforces Round 75 (Rated for Div. 2)",
11+
"interactive" : false,
12+
"output" : {
13+
"type" : "stdout"
14+
},
15+
"timeLimit" : 2000,
16+
"name" : "C. Minimize The Integer",
17+
"url" : "https://codeforces.com/contest/1251/problem/C",
18+
"testType" : "single",
19+
"languages" : {
20+
"java" : {
21+
"mainClass" : "Main",
22+
"taskClass" : "CMinimizeTheInteger"
23+
}
24+
},
25+
"input" : {
26+
"type" : "stdin"
27+
},
28+
"memoryLimit" : 256,
29+
"tests" : [{ "input" : "3\n0709\n1337\n246432\n","output" : "0079\n1337\n234642\n"}]
30+
}`
31+
}
32+
33+
func TestExtractTaskFromString(t *testing.T) {
34+
task, _ := extractTaskFromJson(getDummyTaskJson())
35+
assert.Equal(t, task.Name, "C. Minimize The Integer")
36+
assert.Equal(t, task.Group, "Educational Codeforces Round 75 (Rated for Div. 2)")
37+
assert.Equal(t, task.Url, "https://codeforces.com/contest/1251/problem/C")
38+
assert.Equal(t, task.Interactive, false)
39+
assert.Equal(t, int(task.MemoryLimit), 256)
40+
assert.Equal(t, task.TestType, "single")
41+
42+
assert.Equal(t, len(task.Tests), 1)
43+
assert.Equal(t, task.Tests[0].Input, "3\n0709\n1337\n246432\n")
44+
assert.Equal(t, task.Tests[0].Output, "0079\n1337\n234642\n")
45+
46+
assert.Equal(t, task.Input.Type, "stdin")
47+
assert.Equal(t, task.Output.Type, "stdout")
48+
49+
assert.Equal(t, len(task.Languages), 1)
50+
assert.Equal(t, task.Languages["java"].MainClass, "Main")
51+
assert.Equal(t, task.Languages["java"].TaskClass, "CMinimizeTheInteger")
52+
}
53+
54+
func TestCurrentDirectory(t *testing.T) {
55+
//CreateDirectoryStructure(config.Task{});
56+
}

config/config.go

+9-4
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ type Config struct {
1616
Lang struct {
1717
Default string `yaml:"default"`
1818
}
19-
Version string `yaml:"version"`
19+
ConfigFileName string `yaml:"config_file_name"`
20+
Version string `yaml:"version"`
21+
Author string `yaml:"author"`
2022
}
2123

2224
func getDefaultConfigLocation() (string, error) {
@@ -39,7 +41,8 @@ func createDefaultConfiguration() *Config {
3941
}{
4042
Default: "cpp",
4143
},
42-
Version: "1.0",
44+
Version: "1.0",
45+
ConfigFileName: "egor-meta.json",
4346
}
4447
}
4548

@@ -57,7 +60,7 @@ func saveDefaultConfiguration() error {
5760
if err != nil {
5861
return err
5962
}
60-
return ioutil.WriteFile(location, buffer.Bytes(), 0644)
63+
return ioutil.WriteFile(location, buffer.Bytes(), 0777)
6164
}
6265

6366
// Returns the Configuration object associated with
@@ -86,7 +89,9 @@ func LoadDefaultConfiguration() (*Config, error) {
8689
}
8790
if _, err := os.Stat(location); err != nil {
8891
if os.IsNotExist(err) {
89-
saveDefaultConfiguration()
92+
if err := saveDefaultConfiguration(); err != nil {
93+
return nil, err
94+
}
9095
}
9196
}
9297
return LoadConfiguration(location)

config/config_test.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ func createFakeConfigFile() error {
4545
}
4646

4747
func TestLoadConfiguration(t *testing.T) {
48-
createFakeConfigFile()
48+
_ = createFakeConfigFile()
4949
defer deleteFakeConfigFile()
5050
config, err := LoadConfiguration(getConfigPath())
5151
if err != nil {
@@ -55,7 +55,7 @@ func TestLoadConfiguration(t *testing.T) {
5555
assert.Equal(t, config.Server.Port, getDefaultConfiguration().Server.Port)
5656
}
5757

58-
func deleteFakeConfigFile() error {
58+
func deleteFakeConfigFile() {
5959
configPath := getConfigPath()
60-
return os.Remove(configPath)
60+
_ = os.Remove(configPath)
6161
}

0 commit comments

Comments
 (0)