Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.11.2
rev: v0.17.0
hooks:
# Run the linter.
- id: ruff
Expand All @@ -11,20 +11,20 @@ repos:
- id: ruff-format

- repo: https://github.com/abravalheri/validate-pyproject
rev: v0.24.1
rev: "0.26"
hooks:
- id: validate-pyproject

- repo: https://github.com/executablebooks/mdformat
rev: 0.7.22
rev: 1.0.0
hooks:
- id: mdformat
additional_dependencies:
- "mdformat-mkdocs[recommended]>=2.1.0"
- mdformat-admon

- repo: https://github.com/igorshubovych/markdownlint-cli
rev: v0.44.0
rev: v0.49.1
hooks:
- id: markdownlint
# args:
Expand All @@ -36,7 +36,7 @@ repos:
# ]

- repo: https://github.com/crate-ci/typos
rev: v1
rev: v1.51.1
hooks:
- id: typos

Expand Down
40 changes: 28 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,18 +168,34 @@ I chose [ruff](https://github.com/astral-sh/ruff/) for linting + formatting

## 🗺️ Roadmap

- \[x\] Implement basic authentication functionality
- \[x\] Fetch course activities
- \[x\] Store credentials securely for reuse
- \[x\] Fetch latest submission
- \[ \] Implement hidden file .pyc download via submission abuse (branch: `feature/hidden_file_decompilation`)
- \[ \] Implement hidden file decompilation for python version agnostic test execution
- \[ \] Implement activity submission (`myrpl submit`)
- \[ \] Implement course/category/activity progress (`myrpl status`)
- \[ \] Remove annoying keyring passphrase
- \[ \] Enhance test coverage
- \[ \] VS Code extension (?)
- \[ \] Add support for additional programming languages (idk if actually necessary)
- \[x\]
- \[x\]
- \[x\]
- \[x\]
- \[

\]
- \[

\]
- \[

\]
- \[

\]
- \[

\]
- \[

\]
- \[

\]
- \[

\]

Please note that this roadmap is subject to change and may be updated based on user feedback and my own time 😁

Expand Down
11 changes: 5 additions & 6 deletions myrpl_cli/api.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
from typing import List
import mimetypes
import json
import mimetypes

import requests
from requests_toolbelt.multipart.encoder import MultipartEncoder

from myrpl_cli.errors import MissingCredentialsError
from myrpl_cli.models import Course, Activity, Submission, SubmissionResult
from myrpl_cli.credential_manager import CredentialManager
from myrpl_cli.errors import MissingCredentialsError
from myrpl_cli.models import Activity, Course, Submission, SubmissionResult

BASE_URL = "https://myrpl.ar"

Expand Down Expand Up @@ -37,14 +36,14 @@ def login(self, username_or_email, password):
self.headers["Authorization"] = f"{login_data['token_type']} {login_data['access_token']}"
return login_data

def fetch_courses(self) -> List[Course]:
def fetch_courses(self) -> list[Course]:
"""Fetches all courses"""

courses_response = self.auth_api_call("get", f"{BASE_URL}/api/courses")
courses = [Course(**course) for course in courses_response]
return courses

def fetch_activities(self, course: Course) -> List[Activity]:
def fetch_activities(self, course: Course) -> list[Activity]:
"""Fetches all activities in a course"""

activities_response = self.auth_api_call("get", f"{BASE_URL}/api/courses/{course.id}/activities")
Expand Down
10 changes: 6 additions & 4 deletions myrpl_cli/main.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import logging
import argparse
import logging

from dotenv import load_dotenv
from myrpl_cli.errors import MissingCredentialsError, NotMyRPLDirectoryError
from myrpl_cli.myrpl import MyRPL

from myrpl_cli import __version__
from myrpl_cli.api import API
from myrpl_cli.credential_manager import CredentialManager
from myrpl_cli import __version__
from myrpl_cli.errors import MissingCredentialsError, NotMyRPLDirectoryError
from myrpl_cli.myrpl import MyRPL

load_dotenv()
logging.basicConfig(level=logging.INFO)
Expand Down
62 changes: 23 additions & 39 deletions myrpl_cli/models.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import List, Optional, Literal
from typing import Literal

from pydantic import BaseModel, computed_field, field_validator

Expand Down Expand Up @@ -27,8 +27,8 @@ class ActivityMetadata(BaseModel):

class MyRPLMetadata(BaseModel):
course: CourseMetadata
category: Optional[CategoryMetadata] = None
activity: Optional[ActivityMetadata] = None
category: CategoryMetadata | None = None
activity: ActivityMetadata | None = None


class Course(BaseModel):
Expand Down Expand Up @@ -98,24 +98,16 @@ def category(self) -> Category:
description: str

language: str
activity_unit_tests: Optional[str] = None
activity_unit_tests: str | None = None
file_id: int
submission_status: Optional[
Literal[
"PENDING",
"ENQUEUED",
"PROCESSING",
"BUILD_ERROR",
"RUNTIME_ERROR",
"FAILURE",
"SUCCESS",
"TIME_OUT",
]
] = None
submission_status: (
Literal["PENDING", "ENQUEUED", "PROCESSING", "BUILD_ERROR", "RUNTIME_ERROR", "FAILURE", "SUCCESS", "TIME_OUT"]
| None
) = None

@field_validator("submission_status", mode="before")
@classmethod
def empty_str_to_none(cls, v: str) -> Optional[str]:
def empty_str_to_none(cls, v: str) -> str | None:
"""Coerses empty string to None"""
if v == "":
return None
Expand All @@ -138,15 +130,15 @@ class UnitTestResult(BaseModel):
id: int
test_name: str
passed: bool
error_messages: Optional[str] = None
error_messages: str | None = None


class Submission(BaseModel):
"""Submission model"""

@field_validator("submission_status", mode="before")
@classmethod
def empty_str_to_none(cls, v: str) -> Optional[str]:
def empty_str_to_none(cls, v: str) -> str | None:
"""Coerses empty string to None"""
if v == "":
return None
Expand All @@ -162,26 +154,18 @@ def empty_str_to_none(cls, v: str) -> Optional[str]:
activity_starting_files_type: str
activity_starting_files_id: int
activity_language: str
activity_unit_tests: Optional[str] = None
submission_status: Optional[
Literal[
"PENDING",
"ENQUEUED",
"PROCESSING",
"BUILD_ERROR",
"RUNTIME_ERROR",
"FAILURE",
"SUCCESS",
"TIME_OUT",
]
] = None
is_final_solution: Optional[bool] = None
exit_message: Optional[str] = None
stderr: Optional[str] = None
stdout: Optional[str] = None
io_test_run_results: List[dict] = []
unit_test_run_results: List[UnitTestResult] = []
submission_date: Optional[str] = None
activity_unit_tests: str | None = None
submission_status: (
Literal["PENDING", "ENQUEUED", "PROCESSING", "BUILD_ERROR", "RUNTIME_ERROR", "FAILURE", "SUCCESS", "TIME_OUT"]
| None
) = None
is_final_solution: bool | None = None
exit_message: str | None = None
stderr: str | None = None
stdout: str | None = None
io_test_run_results: list[dict] = []
unit_test_run_results: list[UnitTestResult] = []
submission_date: str | None = None


class SubmissionResult(Submission):
Expand Down
11 changes: 5 additions & 6 deletions myrpl_cli/myrpl.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
import os
import logging
import getpass
from typing import Optional
import logging
import os

import pytest
import toml
from tqdm import tqdm

from myrpl_cli.errors import AuthError, NotMyRPLDirectoryError
from myrpl_cli.models import Activity, MyRPLMetadata
from myrpl_cli.api import API
from myrpl_cli.credential_manager import CredentialManager
from myrpl_cli.errors import AuthError, NotMyRPLDirectoryError
from myrpl_cli.models import Activity, MyRPLMetadata

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -106,7 +105,7 @@ def test(self, pytest_args):
if meta.activity is not None:
logger.info("\t└──Activity: %s", meta.activity.name)

def open_metadata(self) -> Optional[MyRPLMetadata]:
def open_metadata(self) -> MyRPLMetadata | None:
"""
Reads, parses and returns the current directory's metadata
"""
Expand Down
6 changes: 3 additions & 3 deletions tests/api_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
import pytest
import requests

from myrpl_cli.errors import MissingCredentialsError
from myrpl_cli.credential_manager import CredentialManager
from myrpl_cli.models import Course, Activity, Submission, SubmissionResult
from myrpl_cli.api import API
from myrpl_cli.credential_manager import CredentialManager
from myrpl_cli.errors import MissingCredentialsError
from myrpl_cli.models import Activity, Course, Submission, SubmissionResult


@pytest.fixture(name="credential_manager")
Expand Down
Loading