-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Release 1.0.0: Refactor kttool to only use python. Fix a minor issue …
…with submit where it terminate at New
- Loading branch information
Showing
22 changed files
with
1,081 additions
and
29,217 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -129,4 +129,5 @@ dmypy.json | |
.pyre/ | ||
|
||
build.sh | ||
publish.sh | ||
Makefile |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
1.0.0 |
File renamed without changes.
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
|
||
import json | ||
import os | ||
from kttool.logger import log | ||
from pathlib import Path | ||
import subprocess | ||
from kttool.base import Action | ||
from kttool.logger import color_cyan, color_green, color_red, log_green | ||
from kttool.utils import MAP_TEMPLATE_TO_PLANG, ask_with_default | ||
|
||
class Config(Action): | ||
def add_template(self) -> None: | ||
question = 'Which template would you like to add:\n' | ||
selectable_lang = {} | ||
idx = 1 | ||
existed_templates = {} | ||
options = {} | ||
|
||
log_green('Adapted from xalanq\'s cf tool') | ||
log(''' | ||
Template will run 3 scripts in sequence when you run "kt test": | ||
- before_script (execute once) | ||
- script (execute the number of samples times) | ||
- after_script (execute once) | ||
You could set "before_script" or "after_script" to empty string, meaning not executing. | ||
You have to run your program in "script" with standard input/output (no need to redirect). | ||
You can insert some placeholders in your scripts. When execute a script, | ||
cf will replace all placeholders by following rules: | ||
$%path%$ Path to source file (Excluding $%full%$, e.g. "/home/user/") | ||
$%full%$ Full name of source file (e.g. "a.cpp") | ||
$%file%$ Name of source file (Excluding suffix, e.g. "a") | ||
$%rand%$ Random string with 8 character (including "a-z" "0-9") | ||
''') | ||
|
||
|
||
existed_templates = self.load_kt_config() | ||
|
||
for template_type, lang in MAP_TEMPLATE_TO_PLANG.items(): | ||
if template_type not in existed_templates: | ||
temp = f'{idx} ({lang.extension}): {lang.full_name}\n' | ||
question += temp | ||
selectable_lang[idx] = (template_type, lang) | ||
idx += 1 | ||
|
||
res = input(question) | ||
ret = int(res) | ||
assert 1 <= ret < idx, 'Invalid input' | ||
|
||
selected_lang = selectable_lang[ret][1] | ||
|
||
import readline, glob | ||
def complete(text, state): | ||
return (glob.glob(os.path.expanduser(text)+'*')+[None])[state] | ||
|
||
readline.set_completer_delims(' \t\n;') | ||
readline.parse_and_bind("tab: complete") | ||
readline.set_completer(complete) | ||
options['path'] = os.path.expanduser(input('Path to template file: ')) | ||
options['pre_script'] = ask_with_default('Pre-script', selected_lang.pre_script) | ||
options['script'] = ask_with_default('Script', selected_lang.script) | ||
options['post_script'] = ask_with_default('Post-script', selected_lang.post_script) | ||
options['default'] = False if existed_templates else True | ||
|
||
existed_templates[selected_lang.alias] = options | ||
with open(self.kt_config, 'w') as kt_config: | ||
json.dump(existed_templates, kt_config, indent=2) | ||
log_green(f'Yosh, your configuration has been saved to {self.kt_config}') | ||
|
||
|
||
def remove_template(self) -> None: | ||
''' Remove a template from ktconfig file''' | ||
existed_templates = self.load_kt_config() | ||
|
||
log(f'Which template would you like to {color_red("delete")} ? For eg cpp, cc, ...') | ||
for k in existed_templates.keys(): | ||
log(k) | ||
res = input() | ||
|
||
assert res in existed_templates, f'Invalid template chosen. Template {res} is not in ur config file' | ||
|
||
move_default = existed_templates[res]['default'] | ||
existed_templates.pop(res, None) | ||
if existed_templates and move_default: # move default to the first key of template | ||
existed_templates[next(iter(existed_templates))] = True | ||
with open(self.kt_config, 'w') as kt_config: | ||
json.dump(existed_templates, kt_config, indent=2) | ||
|
||
def update_default(self) -> None: | ||
default_key = '' | ||
existed_templates = self.load_kt_config() | ||
|
||
log(f'Which template would you like to gen as {color_cyan("default")} ? For eg cpp, cc, ...') | ||
|
||
for k, v in existed_templates.items(): | ||
log(f'{k} {color_green("(default)") if v["default"] else ""}') | ||
if v["default"]: | ||
default_key = k | ||
res = input() | ||
|
||
assert res in existed_templates, f'Invalid template chosen. Template {res} is not in ur config file' | ||
existed_templates[default_key]["default"] = False | ||
existed_templates[res]["default"] = True | ||
with open(self.kt_config, 'w') as kt_config: | ||
json.dump(existed_templates, kt_config, indent=2) | ||
log_green('Yosh, your configuration has been saved') | ||
|
||
def _act(self) -> None: | ||
question = color_cyan('Select an option:\n') | ||
question += """1: Add a template | ||
2: Remove a template | ||
3: Select a default template | ||
""" | ||
res = input(question) | ||
opt = int(res) | ||
if opt == 1: | ||
self.add_template() | ||
elif opt == 2: | ||
self.remove_template() | ||
elif opt == 3: | ||
self.update_default() | ||
else: | ||
raise ValueError('Invalid option') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
from pathlib import Path | ||
from kttool.base import Action | ||
import requests | ||
from bs4 import BeautifulSoup | ||
from dataclasses import dataclass | ||
from concurrent.futures import ProcessPoolExecutor | ||
import json | ||
from kttool.logger import log, log_green, log_red | ||
import shutil | ||
from collections import namedtuple | ||
|
||
from kttool.utils import HEADERS, MAP_TEMPLATE_TO_PLANG | ||
|
||
|
||
|
||
@dataclass | ||
class SampleData: | ||
problem_id: str = '' | ||
is_in: bool = True | ||
sample_id: str = '' | ||
data: str = '' | ||
|
||
|
||
|
||
def write_samples(sample_data: SampleData) -> None: | ||
""" Write input/output sample to file. This is used for multiprocess pool to generate input/output files | ||
Parameters | ||
---------- | ||
sample_data : SampleData | ||
a tuple representing index, string data, problem id and a boolean declaring whether current | ||
file is input (False if the file is output) | ||
""" | ||
file_name_prefix = 'in' if sample_data.is_in else 'ans' | ||
file_name = f'{sample_data.problem_id}/{file_name_prefix}{sample_data.sample_id}.txt' | ||
|
||
with open(file_name, 'w+') as f: | ||
f.write(sample_data.data) | ||
|
||
class Gen(Action): | ||
REQUIRED_CONFIG = True | ||
|
||
''' Handle `gen` command for kt_tool ''' | ||
problem_id: str | ||
|
||
__slots__ = 'problem_id' | ||
|
||
|
||
''' Handle `gen` command for kt_tool ''' | ||
def __init__(self, problem_id: str, *args, **kwargs): | ||
super().__init__(*args, **kwargs) | ||
self.problem_id = problem_id | ||
|
||
def _gen_samples(self) -> None: | ||
""" Generate sample input file for `self.problem_id` | ||
The basic flow is to scrape the problem task page and retrieve the relevent fields | ||
Generate the sample files to problem id folder | ||
For example, if the problem id is distinctivecharacter, `kt gen` will | ||
- Generate a folder called distinctivecharacter | ||
- Get sample intput, output from problem task page and generate to distinctivecharacter/, in this | ||
example there will be 4 files generated | ||
+ distinctivecharacter/in1.txt | ||
+ distinctivecharacter/ans1.txt | ||
+ distinctivecharacter/in2.txt | ||
+ distinctivecharacter/ans2.txt | ||
- Generate a template file (distinctivecharacter.cpp) if a template file is provided in the .ktconfig file | ||
""" | ||
template_file = {} | ||
sample_data = [] | ||
i = 0 | ||
|
||
self.login() | ||
url = self.get_problem_url() | ||
page = requests.get(url, cookies=self.cookies, headers=HEADERS) | ||
soup = BeautifulSoup(page.content, 'html.parser') | ||
data = soup.find_all('pre') | ||
|
||
for i in range(len(data)): | ||
if i & 1: | ||
sample_data.append(SampleData(sample_id=i // 2 + 1, data=data[i].text, problem_id=self.problem_id, is_in=False)) | ||
else: | ||
sample_data.append(SampleData(sample_id=i // 2 + 1, data=data[i].text, problem_id=self.problem_id, is_in=True)) | ||
|
||
assert len(data) % 2 == 0, 'Internal error: Number of sample input '\ | ||
' is not equal to number of sample output' | ||
|
||
with ProcessPoolExecutor(max_workers=4) as executor: | ||
executor.map(write_samples, sample_data) | ||
|
||
log_green(f'Generate {len(sample_data) // 2} sample(s) to {self.problem_id}') | ||
if not self.kt_config.is_file(): | ||
log_red('.ktconfig file has not been set up so no template was generated. ' | ||
'Please use `kt config` to set up a template file') | ||
return | ||
|
||
|
||
|
||
template_file = self.load_kt_config() | ||
|
||
for k, template in template_file.items(): | ||
if template.get('default', False): | ||
shutil.copyfile(template.get('path'), f'{self.problem_id}/{self.problem_id}.{MAP_TEMPLATE_TO_PLANG[k].extension}') | ||
log_green('Template file has been generated') | ||
return | ||
log_red(f'No default template detected in {self.kt_config}') | ||
|
||
def get_problem_id(self) -> str: | ||
return self.problem_id | ||
|
||
def _act(self) -> None: | ||
log(f'Problem is {self.problem_id}') | ||
problem_dir = self.cwd / self.problem_id | ||
problem_dir.mkdir(parents=True, exist_ok=True) | ||
self._gen_samples() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
from kttool.base import Action | ||
import webbrowser | ||
|
||
from kttool.logger import log | ||
|
||
class Open(Action): | ||
REQUIRED_CONFIG = True | ||
|
||
def _act(self) -> None: | ||
log(f'Openning {self.get_problem_url()}') | ||
webbrowser.open(self.get_problem_url()) |
Oops, something went wrong.