Added termforces module

This commit is contained in:
2023-02-01 14:16:50 +03:00
parent 9c85c34037
commit 1cd9fc859f
10 changed files with 296 additions and 2 deletions

6
termforces/__main__.py Normal file
View File

@@ -0,0 +1,6 @@
from termforces.termforces_shell import termforces_shell
from termforces.cmds import *
if __name__ == '__main__':
termforces_shell()

View File

@@ -0,0 +1,2 @@
from termforces.cmds.session_manager import *
from termforces.cmds.submit_interface import *

View File

@@ -0,0 +1,43 @@
import click
import os
from termforces.termforces_shell import termforces_shell, State
from termforces.session_manager import load_session, store_session
from getpass import getpass
from codeforces_scraper import ScraperError
@termforces_shell.command()
@click.argument('handle')
@click.option('--password',
help="Will be prompted, so do not pass, unless you're sure for safety")
@click.option('--session-file', help='Tries to store session on successful login')
@click.option('--no-getpass', is_flag=True,
show_default=True, default=False, help='Read password from stdin instead of using getpass')
@click.pass_obj
def login(state: State, handle, password, session_file, no_getpass):
if password is None:
if no_getpass:
password = input()
else:
password = getpass(f'Codeforces password for {handle}: ')
try:
state.scraper.login(handle, password)
except ScraperError:
print('Failed to login, check your credentials')
return
if session_file is not None:
store_session(state.scraper, os.path.expanduser(session_file))
@termforces_shell.command(name='load-session')
@click.argument('session_file')
@click.pass_obj
def load_session_cmd(state: State, session_file):
load_session(state.scraper, os.path.expanduser(session_file))
@termforces_shell.command(name='whoami')
@click.pass_obj
def whoami(state: State):
state.scraper.update_current_user()
print(state.scraper.current_user)

View File

@@ -0,0 +1,64 @@
import click
from termforces.termforces_shell import termforces_shell, State
from codeforces_scraper import Verdict
from codeforces_scraper.languages import some_compiler_by_ext
from termforces.utils import tcolors, str_from_timestamp
@termforces_shell.command(name='enter-contest')
@click.argument('contest_id')
@click.pass_obj
def enter_contest(state: State, contest_id: int):
state.contest_id = contest_id
@termforces_shell.command(name='submit')
@click.argument('problem_index')
@click.argument('source_file')
@click.option('--contest-id')
@click.option('--lang-code')
@click.pass_obj
def submit(state: State, problem_index, source_file, contest_id, lang_code):
if contest_id is None:
if state.contest_id is not None:
contest_id = state.contest_id
else:
print('Specify contest id or enter contest via enter-contest command')
return
if lang_code is None:
ext = '.' + source_file.split('.')[-1]
compiler = some_compiler_by_ext(ext)
if compiler is None:
print('Please specify language code')
return
lang_code = compiler.id
with open(source_file, 'r') as f:
source_code = f.read()
state.scraper.submit(contest_id, problem_index, source_code, lang_code)
@termforces_shell.command(name='results-my')
@click.option('--contest-id')
@click.pass_obj
def results_my(state: State, contest_id):
if contest_id is None:
if state.contest_id is not None:
contest_id = state.contest_id
else:
print('Specify contest id or enter contest via enter-contest command')
return
if state.scraper.current_user is None:
print('Please login to view your results')
return
subms = state.scraper.get_submissions(contest_id, state.scraper.current_user)
subms.sort(key=lambda subm: (subm.problem.index, subm.creation_time_seconds))
for subm in subms:
if subm.verdict == Verdict.TESTING:
color = tcolors.WARNING
elif subm.verdict == Verdict.OK:
color = tcolors.OKGREEN
else:
color = tcolors.FAIL
verdict_str = f'{color}{subm.verdict.name}{tcolors.ENDC}'
print(f'{subm.problem.index} \t {str_from_timestamp(subm.creation_time_seconds)} \t {verdict_str}')

View File

@@ -0,0 +1,23 @@
import json
import requests
from codeforces_scraper import Scraper
# TODO: it should be scraper method
def store_session(scraper: Scraper, session_file):
with open(session_file, 'w') as f:
d = requests.utils.dict_from_cookiejar(scraper.session.cookies)
d['__termforces_name'] = scraper.current_user
json.dump(d, f)
# TODO: it should be a scraper method
def load_session(scraper: Scraper, session_file):
with open(session_file, 'r') as f:
d = json.load(f)
name = d['__termforces_name']
del d['__termforces_name']
new_cookies = requests.utils.cookiejar_from_dict(d)
scraper.current_user = name
scraper.session.cookies.clear()
scraper.session.cookies.update(new_cookies)

View File

@@ -0,0 +1,31 @@
import click
import os
from click_shell import shell
from codeforces_scraper import Scraper
from termforces.session_manager import load_session
SEARCH_LOCATIONS = ['.', '..', os.path.join(os.path.expanduser('~'), '.config', 'termforces')]
FILE_NAME = 'termforces_cookies.json'
class State:
def __init__(self):
self.scraper = Scraper()
def preload_session(state):
for loc in SEARCH_LOCATIONS:
file_path = os.path.join(loc, FILE_NAME)
if os.path.exists(file_path):
print(f'Loading session from {file_path}')
load_session(state.scraper, file_path)
return
@shell(prompt='termforces > ', intro='Entering termforces shell')
@click.pass_context
def termforces_shell(ctx):
state = State()
preload_session(state)
ctx.obj = state
pass

18
termforces/utils.py Normal file
View File

@@ -0,0 +1,18 @@
from datetime import datetime
class tcolors:
HEADER = '\033[95m'
OKBLUE = '\033[94m'
OKCYAN = '\033[96m'
OKGREEN = '\033[92m'
WARNING = '\033[93m'
FAIL = '\033[91m'
ENDC = '\033[0m'
BOLD = '\033[1m'
UNDERLINE = '\033[4m'
def str_from_timestamp(timestamp: int):
date = datetime.fromtimestamp(timestamp)
return date.strftime('%d.%m.%y %T')