feat(scraper): add LoginResult model and abstract login() interface
Problem: `:CP <platform> login` blindly caches credentials without validating them against the platform. Solution: add `LoginResult` to `models.py`, abstract `login()` method and `_login_error` helper to `BaseScraper`, and wire up the `"login"` CLI dispatch in `_run_cli_async`.
This commit is contained in:
parent
a202725cc5
commit
dfb648531b
2 changed files with 24 additions and 1 deletions
|
|
@ -9,6 +9,7 @@ from .language_ids import get_language_id
|
||||||
from .models import (
|
from .models import (
|
||||||
CombinedTest,
|
CombinedTest,
|
||||||
ContestListResult,
|
ContestListResult,
|
||||||
|
LoginResult,
|
||||||
MetadataResult,
|
MetadataResult,
|
||||||
SubmitResult,
|
SubmitResult,
|
||||||
TestsResult,
|
TestsResult,
|
||||||
|
|
@ -58,9 +59,12 @@ class BaseScraper(ABC):
|
||||||
credentials: dict[str, str],
|
credentials: dict[str, str],
|
||||||
) -> SubmitResult: ...
|
) -> SubmitResult: ...
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
async def login(self, credentials: dict[str, str]) -> LoginResult: ...
|
||||||
|
|
||||||
def _usage(self) -> str:
|
def _usage(self) -> str:
|
||||||
name = self.platform_name
|
name = self.platform_name
|
||||||
return f"Usage: {name}.py metadata <id> | tests <id> | contests"
|
return f"Usage: {name}.py metadata <id> | tests <id> | contests | login"
|
||||||
|
|
||||||
def _metadata_error(self, msg: str) -> MetadataResult:
|
def _metadata_error(self, msg: str) -> MetadataResult:
|
||||||
return MetadataResult(success=False, error=msg, url="")
|
return MetadataResult(success=False, error=msg, url="")
|
||||||
|
|
@ -82,6 +86,9 @@ class BaseScraper(ABC):
|
||||||
def _submit_error(self, msg: str) -> SubmitResult:
|
def _submit_error(self, msg: str) -> SubmitResult:
|
||||||
return SubmitResult(success=False, error=msg)
|
return SubmitResult(success=False, error=msg)
|
||||||
|
|
||||||
|
def _login_error(self, msg: str) -> LoginResult:
|
||||||
|
return LoginResult(success=False, error=msg)
|
||||||
|
|
||||||
async def _run_cli_async(self, args: list[str]) -> int:
|
async def _run_cli_async(self, args: list[str]) -> int:
|
||||||
if len(args) < 2:
|
if len(args) < 2:
|
||||||
print(self._metadata_error(self._usage()).model_dump_json())
|
print(self._metadata_error(self._usage()).model_dump_json())
|
||||||
|
|
@ -133,6 +140,16 @@ class BaseScraper(ABC):
|
||||||
print(result.model_dump_json())
|
print(result.model_dump_json())
|
||||||
return 0 if result.success else 1
|
return 0 if result.success else 1
|
||||||
|
|
||||||
|
case "login":
|
||||||
|
creds_raw = os.environ.get("CP_CREDENTIALS", "{}")
|
||||||
|
try:
|
||||||
|
credentials = json.loads(creds_raw)
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
credentials = {}
|
||||||
|
result = await self.login(credentials)
|
||||||
|
print(result.model_dump_json())
|
||||||
|
return 0 if result.success else 1
|
||||||
|
|
||||||
case _:
|
case _:
|
||||||
print(
|
print(
|
||||||
self._metadata_error(
|
self._metadata_error(
|
||||||
|
|
|
||||||
|
|
@ -64,6 +64,12 @@ class TestsResult(ScrapingResult):
|
||||||
model_config = ConfigDict(extra="forbid")
|
model_config = ConfigDict(extra="forbid")
|
||||||
|
|
||||||
|
|
||||||
|
class LoginResult(ScrapingResult):
|
||||||
|
credentials: dict[str, str] = Field(default_factory=dict)
|
||||||
|
|
||||||
|
model_config = ConfigDict(extra="forbid")
|
||||||
|
|
||||||
|
|
||||||
class SubmitResult(ScrapingResult):
|
class SubmitResult(ScrapingResult):
|
||||||
submission_id: str = ""
|
submission_id: str = ""
|
||||||
verdict: str = ""
|
verdict: str = ""
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue