codeforces 0.0.6__tar.gz → 0.0.8__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {codeforces-0.0.6 → codeforces-0.0.8}/PKG-INFO +4 -4
- {codeforces-0.0.6 → codeforces-0.0.8}/README.md +3 -3
- {codeforces-0.0.6 → codeforces-0.0.8}/cf/__init__.py +4 -0
- codeforces-0.0.8/cf/edit.py +39 -0
- {codeforces-0.0.6 → codeforces-0.0.8}/cf/parse.py +15 -5
- {codeforces-0.0.6 → codeforces-0.0.8}/cf/submit.py +4 -5
- codeforces-0.0.8/cf/unsolved.py +60 -0
- {codeforces-0.0.6 → codeforces-0.0.8}/codeforces.egg-info/PKG-INFO +4 -4
- {codeforces-0.0.6 → codeforces-0.0.8}/codeforces.egg-info/SOURCES.txt +2 -0
- {codeforces-0.0.6 → codeforces-0.0.8}/setup.cfg +1 -1
- {codeforces-0.0.6 → codeforces-0.0.8}/utils.py +13 -0
- {codeforces-0.0.6 → codeforces-0.0.8}/cf/config.py +0 -0
- {codeforces-0.0.6 → codeforces-0.0.8}/cf/contests.py +0 -0
- {codeforces-0.0.6 → codeforces-0.0.8}/cf/run.py +0 -0
- {codeforces-0.0.6 → codeforces-0.0.8}/codeforces.egg-info/dependency_links.txt +0 -0
- {codeforces-0.0.6 → codeforces-0.0.8}/codeforces.egg-info/entry_points.txt +0 -0
- {codeforces-0.0.6 → codeforces-0.0.8}/codeforces.egg-info/requires.txt +0 -0
- {codeforces-0.0.6 → codeforces-0.0.8}/codeforces.egg-info/top_level.txt +0 -0
- {codeforces-0.0.6 → codeforces-0.0.8}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: codeforces
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.8
|
|
4
4
|
Summary: A simple command line tool to move your competitive programming workflow to your terminal.
|
|
5
5
|
Home-page: https://github.com/Nirlep5252/codeforces-cli
|
|
6
6
|
Author: Nirlep5252
|
|
@@ -47,9 +47,10 @@ $ cf --help
|
|
|
47
47
|
`cf config` - save your username, password, problems-directory \
|
|
48
48
|
`cf contests` - list all the current or upcoming contests \
|
|
49
49
|
`cf contests {ID}` - view all the problems of an ongoing contest \
|
|
50
|
-
`cf parse {Contest ID} {Problem ID | Optional}` - parse the problem and its test cases \
|
|
50
|
+
`cf parse {Contest ID} {Problem ID | Optional} {--lang | Optional}` - parse the problem and its test cases \
|
|
51
51
|
`cf run {FILE}` - check the test cases for the current problem (works based on current directory) \
|
|
52
|
-
`cf submit {FILE}` - submit the problem (requires config) (works based on current directory)
|
|
52
|
+
`cf submit {FILE}` - submit the problem (requires config) (works based on current directory) \
|
|
53
|
+
`cf unsolved` - return the list of all your unsolved problems
|
|
53
54
|
|
|
54
55
|
#### TODO commands:
|
|
55
56
|
|
|
@@ -59,7 +60,6 @@ $ cf --help
|
|
|
59
60
|
#### TODO features:
|
|
60
61
|
|
|
61
62
|
- [ ] Support all languages in run
|
|
62
|
-
- [ ] Add a templating system
|
|
63
63
|
- [ ] A problem recommendation system, maybe?
|
|
64
64
|
|
|
65
65
|
|
|
@@ -34,9 +34,10 @@ $ cf --help
|
|
|
34
34
|
`cf config` - save your username, password, problems-directory \
|
|
35
35
|
`cf contests` - list all the current or upcoming contests \
|
|
36
36
|
`cf contests {ID}` - view all the problems of an ongoing contest \
|
|
37
|
-
`cf parse {Contest ID} {Problem ID | Optional}` - parse the problem and its test cases \
|
|
37
|
+
`cf parse {Contest ID} {Problem ID | Optional} {--lang | Optional}` - parse the problem and its test cases \
|
|
38
38
|
`cf run {FILE}` - check the test cases for the current problem (works based on current directory) \
|
|
39
|
-
`cf submit {FILE}` - submit the problem (requires config) (works based on current directory)
|
|
39
|
+
`cf submit {FILE}` - submit the problem (requires config) (works based on current directory) \
|
|
40
|
+
`cf unsolved` - return the list of all your unsolved problems
|
|
40
41
|
|
|
41
42
|
#### TODO commands:
|
|
42
43
|
|
|
@@ -46,5 +47,4 @@ $ cf --help
|
|
|
46
47
|
#### TODO features:
|
|
47
48
|
|
|
48
49
|
- [ ] Support all languages in run
|
|
49
|
-
- [ ] Add a templating system
|
|
50
50
|
- [ ] A problem recommendation system, maybe?
|
|
@@ -4,6 +4,8 @@ from .contests import contests
|
|
|
4
4
|
from .parse import parse
|
|
5
5
|
from .submit import submit
|
|
6
6
|
from .run import run
|
|
7
|
+
from .unsolved import unsolved
|
|
8
|
+
from .edit import edit_cmd
|
|
7
9
|
from rich.console import Console
|
|
8
10
|
from rich.table import Table
|
|
9
11
|
from typing import Dict
|
|
@@ -49,3 +51,5 @@ commands.add_command(contests)
|
|
|
49
51
|
commands.add_command(parse)
|
|
50
52
|
commands.add_command(run)
|
|
51
53
|
commands.add_command(submit)
|
|
54
|
+
commands.add_command(unsolved)
|
|
55
|
+
commands.add_command(edit_cmd)
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import click
|
|
2
|
+
import os
|
|
3
|
+
import subprocess
|
|
4
|
+
from rich.console import Console
|
|
5
|
+
from utils import get_config
|
|
6
|
+
|
|
7
|
+
console = Console()
|
|
8
|
+
|
|
9
|
+
editor_cmds = {
|
|
10
|
+
"vscode": "code {path}",
|
|
11
|
+
"neovim": "nvim {path}",
|
|
12
|
+
"vim": "vim {path}",
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@click.command(name="edit")
|
|
17
|
+
@click.argument("contest_id", required=True)
|
|
18
|
+
@click.option("--editor", type=click.Choice(["vscode", "neovim", "vim"]), prompt="Select an editor: ")
|
|
19
|
+
def edit_cmd(contest_id: str, editor: str):
|
|
20
|
+
conf = get_config(console)
|
|
21
|
+
if conf is None:
|
|
22
|
+
return
|
|
23
|
+
|
|
24
|
+
cf_dir = conf.get("dir")
|
|
25
|
+
if cf_dir is None:
|
|
26
|
+
console.print("[bold red]ERROR: [/]The default directory is not set.\nPlease run the `cf config` command.")
|
|
27
|
+
return
|
|
28
|
+
|
|
29
|
+
if not os.path.isdir(cf_dir):
|
|
30
|
+
console.print("[bold red]ERROR: [/]The default directory does not exist.\nPlease run the `cf config` command.")
|
|
31
|
+
return
|
|
32
|
+
|
|
33
|
+
contest_dir = os.path.join(cf_dir, contest_id)
|
|
34
|
+
if not os.path.isdir(contest_dir):
|
|
35
|
+
console.print(f"\n[bold red]ERROR: [/]The contest directory `{contest_dir}` does not exist.")
|
|
36
|
+
console.print(f"[bold green]TIP: [/]Use `cf parse {contest_id}` to create the directory and parse the contest.")
|
|
37
|
+
return
|
|
38
|
+
|
|
39
|
+
subprocess.run(editor_cmds[editor].format(path=os.path.join(cf_dir, contest_id)), shell=True)
|
|
@@ -3,12 +3,12 @@ import os
|
|
|
3
3
|
import requests
|
|
4
4
|
from rich.console import Console
|
|
5
5
|
from bs4 import BeautifulSoup
|
|
6
|
-
from utils import get_config
|
|
6
|
+
from utils import get_config, get_bp
|
|
7
7
|
|
|
8
8
|
console = Console()
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
def parse_problem(contest_id: int, problem: str, cf_dir: str, print_info: bool = True):
|
|
11
|
+
def parse_problem(contest_id: int, problem: str, cf_dir: str, print_info: bool = True, bp: str = "_"):
|
|
12
12
|
slash = "/" if os.name == "posix" else "\\\\"
|
|
13
13
|
r = requests.get(url=f"https://codeforces.com/contest/{contest_id}/problem/{problem}")
|
|
14
14
|
if len(r.history) > 0:
|
|
@@ -50,6 +50,15 @@ def parse_problem(contest_id: int, problem: str, cf_dir: str, print_info: bool =
|
|
|
50
50
|
with open(f"{contest_dir}{slash}{problem}.{i}.output.test", "w") as f:
|
|
51
51
|
f.write(out)
|
|
52
52
|
|
|
53
|
+
if bp != "_":
|
|
54
|
+
bp_text = get_bp(bp)
|
|
55
|
+
if bp_text is None:
|
|
56
|
+
console.print(f"[bold red]ERROR: [/]No boilerplate file found for `{bp}`.")
|
|
57
|
+
else:
|
|
58
|
+
with open(f"{contest_dir}{slash}{problem}.{bp}", "w") as f:
|
|
59
|
+
f.write(bp_text)
|
|
60
|
+
console.print(f"[bold green]INFO: [/]Created boilerplate `{problem}.{bp}` file.")
|
|
61
|
+
|
|
53
62
|
console.print(f"[bold green]Problem {contest_id} {problem} parsed successfully.[/]\n")
|
|
54
63
|
if print_info:
|
|
55
64
|
console.print(f"Use `cd {contest_dir}` to move the contest directory.")
|
|
@@ -59,7 +68,8 @@ def parse_problem(contest_id: int, problem: str, cf_dir: str, print_info: bool =
|
|
|
59
68
|
@click.command()
|
|
60
69
|
@click.argument("contest_id", required=True)
|
|
61
70
|
@click.argument("problem", default="_", required=False)
|
|
62
|
-
|
|
71
|
+
@click.option("--lang", required=False, default="_")
|
|
72
|
+
def parse(contest_id: int, problem: str, lang: str):
|
|
63
73
|
"""
|
|
64
74
|
Parse the sample test cases for a problem OR a contest.
|
|
65
75
|
"""
|
|
@@ -93,6 +103,6 @@ def parse(contest_id: int, problem: str):
|
|
|
93
103
|
for i, p in enumerate(problems):
|
|
94
104
|
items = p.find_all('td')
|
|
95
105
|
p_id = items[0].a.string.strip().lower()
|
|
96
|
-
parse_problem(contest_id, p_id, cf_dir, print_info=(i == len(problems) - 1))
|
|
106
|
+
parse_problem(contest_id, p_id, cf_dir, print_info=(i == len(problems) - 1), bp=lang)
|
|
97
107
|
else:
|
|
98
|
-
parse_problem(contest_id, problem, cf_dir)
|
|
108
|
+
parse_problem(contest_id, problem, cf_dir, bp=lang)
|
|
@@ -127,17 +127,15 @@ Memory: [bold white]{sub_mem.strip()}[/]
|
|
|
127
127
|
sub_data = json.loads(sub['text'])['d']
|
|
128
128
|
live_sub_id = sub_data[1]
|
|
129
129
|
if live_sub_id == sub_id:
|
|
130
|
-
status = sub_data[6]
|
|
131
|
-
test_case = sub_data[8]
|
|
130
|
+
status = sub_data[6].strip()
|
|
131
|
+
test_case = sub_data[8].strip()
|
|
132
132
|
|
|
133
133
|
if status == "OK":
|
|
134
134
|
status_text = "[bold green]ACCEPTED[/]"
|
|
135
|
-
elif status in ("WRONG_ANSWER", "COMPILATION_ERROR", "RUNTIME_ERROR", "TIME_LIMIT_EXCEEDED", "MEMORY_LIMIT_EXCEEDED", "IDLENESS_LIMIT_EXCEEDED", "SECURITY_VIOLATED", "CRASHED", "INPUT_PREPARATION_CRASHED", "CHALLENGED", "SKIPPED", "PARTIAL", "REJECTED"):
|
|
136
|
-
status_text = f"[bold red]{' '.join(status.split('_'))}[/] on test case: {test_case}"
|
|
137
135
|
elif status == "TESTING":
|
|
138
136
|
status_text = f"[bold]Running on test case: {test_case}[/]"
|
|
139
137
|
else:
|
|
140
|
-
status_text = status
|
|
138
|
+
status_text = f"[bold red]{' '.join(status.split('_'))}[/] on test case: {test_case}"
|
|
141
139
|
|
|
142
140
|
timee = sub_data[9]
|
|
143
141
|
memory = int(sub_data[10]) // 1000
|
|
@@ -150,4 +148,5 @@ Memory: [bold]{memory} KB[/]
|
|
|
150
148
|
live.refresh()
|
|
151
149
|
if status != "TESTING":
|
|
152
150
|
live.stop()
|
|
151
|
+
sub_watcher.close()
|
|
153
152
|
break
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import click
|
|
2
|
+
from bs4 import BeautifulSoup
|
|
3
|
+
from rich.console import Console
|
|
4
|
+
from rich.table import Table
|
|
5
|
+
from utils import CFClient, get_config
|
|
6
|
+
|
|
7
|
+
console = Console()
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@click.command()
|
|
11
|
+
def unsolved():
|
|
12
|
+
"""Show unsolved problems"""
|
|
13
|
+
conf = get_config(console)
|
|
14
|
+
if conf is None:
|
|
15
|
+
return
|
|
16
|
+
|
|
17
|
+
if "username" not in conf and "password" not in conf:
|
|
18
|
+
console.print("[bold red]ERROR: [/]Username and password not found in config file")
|
|
19
|
+
return
|
|
20
|
+
|
|
21
|
+
client = CFClient(conf["username"], conf["password"])
|
|
22
|
+
if not client.login():
|
|
23
|
+
console.print("[bold red]ERROR: [/]Login failed")
|
|
24
|
+
return
|
|
25
|
+
|
|
26
|
+
ps = client.session.get("https://codeforces.com/problemset")
|
|
27
|
+
if ps.status_code != 200:
|
|
28
|
+
console.print("[bold red]ERROR: [/]Failed to fetch unsolved problems.")
|
|
29
|
+
return
|
|
30
|
+
|
|
31
|
+
soup = BeautifulSoup(ps.text, "html.parser")
|
|
32
|
+
unsolved_table = soup.find('table', {'class': 'rtable'})
|
|
33
|
+
if unsolved_table is None:
|
|
34
|
+
console.print("[bold red]ERROR: [/]Failed to fetch unsolved problems.")
|
|
35
|
+
return
|
|
36
|
+
problems = unsolved_table.find_all('tr')[1:] # type: ignore
|
|
37
|
+
|
|
38
|
+
if len(problems) == 0:
|
|
39
|
+
console.print("[bold green]WOW: [/]You do not have any unsolved problems.")
|
|
40
|
+
console.print("(This means any problem where you have submitted a solution but it was not accepted.)")
|
|
41
|
+
console.print("Obviously you will have several problems that you haven't tried.")
|
|
42
|
+
return
|
|
43
|
+
|
|
44
|
+
table = Table(title="Unsolved Problems", show_header=True, header_style="bold green", show_lines=True)
|
|
45
|
+
table.add_column("Problem ID", style="bright", justify="left", no_wrap=True)
|
|
46
|
+
table.add_column("Problem Name", style="bright", justify="left", no_wrap=True)
|
|
47
|
+
table.add_column("Last Submission", style="bright", justify="left", no_wrap=True)
|
|
48
|
+
for problem in problems:
|
|
49
|
+
data = problem.find_all('td')
|
|
50
|
+
_id = data[0].a.string.strip()
|
|
51
|
+
p_url = data[0].a['href'].strip()
|
|
52
|
+
name = data[1].a.string.strip()
|
|
53
|
+
sub_id = data[2].a.string.strip()
|
|
54
|
+
|
|
55
|
+
table.add_row(
|
|
56
|
+
f"[link=https://codeforces.com{p_url}]{_id}[/]", name,
|
|
57
|
+
f"[link=https://codeforces.com{data[2].a['href'].strip()}]{sub_id}[/]"
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
console.print(table)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: codeforces
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.8
|
|
4
4
|
Summary: A simple command line tool to move your competitive programming workflow to your terminal.
|
|
5
5
|
Home-page: https://github.com/Nirlep5252/codeforces-cli
|
|
6
6
|
Author: Nirlep5252
|
|
@@ -47,9 +47,10 @@ $ cf --help
|
|
|
47
47
|
`cf config` - save your username, password, problems-directory \
|
|
48
48
|
`cf contests` - list all the current or upcoming contests \
|
|
49
49
|
`cf contests {ID}` - view all the problems of an ongoing contest \
|
|
50
|
-
`cf parse {Contest ID} {Problem ID | Optional}` - parse the problem and its test cases \
|
|
50
|
+
`cf parse {Contest ID} {Problem ID | Optional} {--lang | Optional}` - parse the problem and its test cases \
|
|
51
51
|
`cf run {FILE}` - check the test cases for the current problem (works based on current directory) \
|
|
52
|
-
`cf submit {FILE}` - submit the problem (requires config) (works based on current directory)
|
|
52
|
+
`cf submit {FILE}` - submit the problem (requires config) (works based on current directory) \
|
|
53
|
+
`cf unsolved` - return the list of all your unsolved problems
|
|
53
54
|
|
|
54
55
|
#### TODO commands:
|
|
55
56
|
|
|
@@ -59,7 +60,6 @@ $ cf --help
|
|
|
59
60
|
#### TODO features:
|
|
60
61
|
|
|
61
62
|
- [ ] Support all languages in run
|
|
62
|
-
- [ ] Add a templating system
|
|
63
63
|
- [ ] A problem recommendation system, maybe?
|
|
64
64
|
|
|
65
65
|
|
|
@@ -25,6 +25,19 @@ def get_config(console: Console) -> Optional[dict]:
|
|
|
25
25
|
return data
|
|
26
26
|
|
|
27
27
|
|
|
28
|
+
def get_bp(lang: str) -> Optional[str]:
|
|
29
|
+
slash = "/" if os.name == "posix" else "\\\\"
|
|
30
|
+
bp_dir = os.path.expanduser("~") + slash + "cf_boilerplates"
|
|
31
|
+
|
|
32
|
+
if not os.path.isdir(bp_dir):
|
|
33
|
+
return
|
|
34
|
+
if not os.path.isfile(bp_dir + slash + "template." + lang):
|
|
35
|
+
return
|
|
36
|
+
|
|
37
|
+
with open(bp_dir + slash + "template." + lang, "r") as f:
|
|
38
|
+
return f.read()
|
|
39
|
+
|
|
40
|
+
|
|
28
41
|
class CFClient:
|
|
29
42
|
def __init__(self, username: str, password: str):
|
|
30
43
|
self.username = username
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|