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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: codeforces
3
- Version: 0.0.6
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
- def parse(contest_id: int, problem: str):
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.6
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
 
@@ -5,9 +5,11 @@ utils.py
5
5
  cf/__init__.py
6
6
  cf/config.py
7
7
  cf/contests.py
8
+ cf/edit.py
8
9
  cf/parse.py
9
10
  cf/run.py
10
11
  cf/submit.py
12
+ cf/unsolved.py
11
13
  codeforces.egg-info/PKG-INFO
12
14
  codeforces.egg-info/SOURCES.txt
13
15
  codeforces.egg-info/dependency_links.txt
@@ -1,6 +1,6 @@
1
1
  [metadata]
2
2
  name = codeforces
3
- version = 0.0.6
3
+ version = 0.0.8
4
4
  url = https://github.com/Nirlep5252/codeforces-cli
5
5
  author = Nirlep5252
6
6
  description = A simple command line tool to move your competitive programming workflow to your terminal.
@@ -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