feat: interactive problem finer-tuning
This commit is contained in:
parent
e5aca06955
commit
ae2f8b94cf
5 changed files with 91 additions and 12 deletions
|
|
@ -20,6 +20,7 @@
|
||||||
---@field test_cases_cached_at? number
|
---@field test_cases_cached_at? number
|
||||||
---@field timeout_ms? number
|
---@field timeout_ms? number
|
||||||
---@field memory_mb? number
|
---@field memory_mb? number
|
||||||
|
---@field interactive? boolean
|
||||||
|
|
||||||
---@class Problem
|
---@class Problem
|
||||||
---@field id string
|
---@field id string
|
||||||
|
|
@ -164,7 +165,16 @@ end
|
||||||
---@param test_cases CachedTestCase[]
|
---@param test_cases CachedTestCase[]
|
||||||
---@param timeout_ms? number
|
---@param timeout_ms? number
|
||||||
---@param memory_mb? number
|
---@param memory_mb? number
|
||||||
function M.set_test_cases(platform, contest_id, problem_id, test_cases, timeout_ms, memory_mb)
|
---@param interactive? boolean
|
||||||
|
function M.set_test_cases(
|
||||||
|
platform,
|
||||||
|
contest_id,
|
||||||
|
problem_id,
|
||||||
|
test_cases,
|
||||||
|
timeout_ms,
|
||||||
|
memory_mb,
|
||||||
|
interactive
|
||||||
|
)
|
||||||
vim.validate({
|
vim.validate({
|
||||||
platform = { platform, 'string' },
|
platform = { platform, 'string' },
|
||||||
contest_id = { contest_id, 'string' },
|
contest_id = { contest_id, 'string' },
|
||||||
|
|
@ -172,6 +182,7 @@ function M.set_test_cases(platform, contest_id, problem_id, test_cases, timeout_
|
||||||
test_cases = { test_cases, 'table' },
|
test_cases = { test_cases, 'table' },
|
||||||
timeout_ms = { timeout_ms, { 'number', 'nil' }, true },
|
timeout_ms = { timeout_ms, { 'number', 'nil' }, true },
|
||||||
memory_mb = { memory_mb, { 'number', 'nil' }, true },
|
memory_mb = { memory_mb, { 'number', 'nil' }, true },
|
||||||
|
interactive = { interactive, { 'boolean', 'nil' }, true },
|
||||||
})
|
})
|
||||||
|
|
||||||
local problem_key = problem_id and (contest_id .. '_' .. problem_id) or contest_id
|
local problem_key = problem_id and (contest_id .. '_' .. problem_id) or contest_id
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,44 @@ function M.toggle_interactive()
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local platform, contest_id = state.get_platform(), state.get_contest_id()
|
||||||
|
|
||||||
|
if not platform then
|
||||||
|
logger.log(
|
||||||
|
'No platform %s configured. Use :CP <platform> <contest> [...] first.',
|
||||||
|
vim.log.levels.ERROR
|
||||||
|
)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if not contest_id then
|
||||||
|
logger.log(
|
||||||
|
('No contest %s configured for platform %s. Use :CP <platform> <contest> <problem> to set up first.'):format(
|
||||||
|
contest_id,
|
||||||
|
platform
|
||||||
|
),
|
||||||
|
vim.log.levels.ERROR
|
||||||
|
)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local problem_id = state.get_problem_id()
|
||||||
|
if not problem_id then
|
||||||
|
logger.log(('No problem found for the current problem id %s'):format(problem_id))
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local cache = require('cp.cache')
|
||||||
|
cache.load()
|
||||||
|
local contest_data = cache.get_contest_data(platform, contest_id)
|
||||||
|
if contest_data and not contest_data.interactive then
|
||||||
|
logger.log(
|
||||||
|
'This is NOT an interactive problem. Use :CP run instead - aborting.',
|
||||||
|
vim.log.levels.WARN
|
||||||
|
)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
state.saved_interactive_session = vim.fn.tempname()
|
state.saved_interactive_session = vim.fn.tempname()
|
||||||
vim.cmd(('mksession! %s'):format(state.saved_interactive_session))
|
vim.cmd(('mksession! %s'):format(state.saved_interactive_session))
|
||||||
vim.cmd('silent only')
|
vim.cmd('silent only')
|
||||||
|
|
@ -89,9 +127,22 @@ function M.toggle_run_panel(is_debug)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
if not state.get_platform() then
|
local platform, contest_id = state.get_platform(), state.get_contest_id()
|
||||||
|
|
||||||
|
if not platform then
|
||||||
logger.log(
|
logger.log(
|
||||||
'No contest configured. Use :CP <platform> <contest> <problem> to set up first.',
|
'No platform %s configured. Use :CP <platform> <contest> [...] first.',
|
||||||
|
vim.log.levels.ERROR
|
||||||
|
)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if not contest_id then
|
||||||
|
logger.log(
|
||||||
|
('No contest %s configured for platform %s. Use :CP <platform> <contest> <problem> to set up first.'):format(
|
||||||
|
contest_id,
|
||||||
|
platform
|
||||||
|
),
|
||||||
vim.log.levels.ERROR
|
vim.log.levels.ERROR
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
@ -99,11 +150,20 @@ function M.toggle_run_panel(is_debug)
|
||||||
|
|
||||||
local problem_id = state.get_problem_id()
|
local problem_id = state.get_problem_id()
|
||||||
if not problem_id then
|
if not problem_id then
|
||||||
|
logger.log(('No problem found for the current problem id %s'):format(problem_id))
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local platform = state.get_platform()
|
local cache = require('cp.cache')
|
||||||
local contest_id = state.get_contest_id()
|
cache.load()
|
||||||
|
local contest_data = cache.get_contest_data(platform, contest_id)
|
||||||
|
if contest_data and contest_data.interactive then
|
||||||
|
logger.log(
|
||||||
|
'This is an interactive problem. Use :CP interact instead - aborting.',
|
||||||
|
vim.log.levels.WARN
|
||||||
|
)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
logger.log(
|
logger.log(
|
||||||
('run panel: platform=%s, contest=%s, problem=%s'):format(
|
('run panel: platform=%s, contest=%s, problem=%s'):format(
|
||||||
|
|
|
||||||
|
|
@ -199,10 +199,10 @@ def scrape_contest_problems(contest_id: str) -> list[ProblemSummary]:
|
||||||
problem_letter: str = href.split("/")[-1].lower()
|
problem_letter: str = href.split("/")[-1].lower()
|
||||||
problem_name: str = link.get_text(strip=True)
|
problem_name: str = link.get_text(strip=True)
|
||||||
|
|
||||||
if problem_letter and problem_name:
|
if not (problem_letter and problem_name):
|
||||||
problems.append(
|
continue
|
||||||
ProblemSummary(id=problem_letter, name=problem_name)
|
|
||||||
)
|
problems.append(ProblemSummary(id=problem_letter, name=problem_name))
|
||||||
|
|
||||||
seen: set[str] = set()
|
seen: set[str] = set()
|
||||||
unique_problems: list[ProblemSummary] = []
|
unique_problems: list[ProblemSummary] = []
|
||||||
|
|
@ -283,6 +283,12 @@ class CodeforcesScraper(BaseScraper):
|
||||||
soup = BeautifulSoup(response.text, "html.parser")
|
soup = BeautifulSoup(response.text, "html.parser")
|
||||||
timeout_ms, memory_mb = extract_problem_limits(soup)
|
timeout_ms, memory_mb = extract_problem_limits(soup)
|
||||||
|
|
||||||
|
problem_statement_div = soup.find("div", class_="problem-statement")
|
||||||
|
interactive = bool(
|
||||||
|
problem_statement_div
|
||||||
|
and "This is an interactive problem" in problem_statement_div.get_text()
|
||||||
|
)
|
||||||
|
|
||||||
if not tests:
|
if not tests:
|
||||||
return self._create_tests_error(
|
return self._create_tests_error(
|
||||||
f"No tests found for {contest_id} {problem_letter}", problem_id, url
|
f"No tests found for {contest_id} {problem_letter}", problem_id, url
|
||||||
|
|
@ -296,6 +302,7 @@ class CodeforcesScraper(BaseScraper):
|
||||||
tests=tests,
|
tests=tests,
|
||||||
timeout_ms=timeout_ms,
|
timeout_ms=timeout_ms,
|
||||||
memory_mb=memory_mb,
|
memory_mb=memory_mb,
|
||||||
|
interactive=interactive,
|
||||||
)
|
)
|
||||||
|
|
||||||
def _scrape_contest_list_impl(self) -> ContestListResult:
|
def _scrape_contest_list_impl(self) -> ContestListResult:
|
||||||
|
|
|
||||||
|
|
@ -45,3 +45,4 @@ class TestsResult(ScrapingResult):
|
||||||
tests: list[TestCase]
|
tests: list[TestCase]
|
||||||
timeout_ms: int
|
timeout_ms: int
|
||||||
memory_mb: float
|
memory_mb: float
|
||||||
|
interactive: bool = False
|
||||||
|
|
|
||||||
6
uv.lock
generated
6
uv.lock
generated
|
|
@ -282,11 +282,11 @@ wheels = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pyparsing"
|
name = "pyparsing"
|
||||||
version = "3.2.3"
|
version = "3.2.5"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/bb/22/f1129e69d94ffff626bdb5c835506b3a5b4f3d070f17ea295e12c2c6f60f/pyparsing-3.2.3.tar.gz", hash = "sha256:b9c13f1ab8b3b542f72e28f634bad4de758ab3ce4546e4301970ad6fa77c38be", size = 1088608, upload-time = "2025-03-25T05:01:28.114Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/f2/a5/181488fc2b9d093e3972d2a472855aae8a03f000592dbfce716a512b3359/pyparsing-3.2.5.tar.gz", hash = "sha256:2df8d5b7b2802ef88e8d016a2eb9c7aeaa923529cd251ed0fe4608275d4105b6", size = 1099274, upload-time = "2025-09-21T04:11:06.277Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/05/e7/df2285f3d08fee213f2d041540fa4fc9ca6c2d44cf36d3a035bf2a8d2bcc/pyparsing-3.2.3-py3-none-any.whl", hash = "sha256:a749938e02d6fd0b59b356ca504a24982314bb090c383e3cf201c95ef7e2bfcf", size = 111120, upload-time = "2025-03-25T05:01:24.908Z" },
|
{ url = "https://files.pythonhosted.org/packages/10/5e/1aa9a93198c6b64513c9d7752de7422c06402de6600a8767da1524f9570b/pyparsing-3.2.5-py3-none-any.whl", hash = "sha256:e38a4f02064cf41fe6593d328d0512495ad1f3d8a91c4f73fc401b3079a59a5e", size = 113890, upload-time = "2025-09-21T04:11:04.117Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue