Skip to content
Merged
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
17 changes: 8 additions & 9 deletions autograder/services/upstash_driver.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,22 @@
import json
import logging
import os
from typing import Optional
from dotenv import load_dotenv
from upstash_redis import Redis

from autograder.models.abstract.exporter import Exporter

logger = logging.getLogger(__name__)

load_dotenv() #TODO: place this in application startup

class UpstashDriver(Exporter):
def __init__(self):
self.redis = Redis(
os.getenv("UPSTASH_REDIS_URL"),
os.getenv("UPSTASH_REDIS_TOKEN") # should it do it? should it remain expecting the Redis python object?
)
def __init__(self, redis_url: str, redis_token: str):
"""Initialize the driver with explicit credentials."""
if not redis_url or not redis_token:
raise ValueError("UpstashDriver requires both redis_url and redis_token.")

self.redis = Redis(redis_url, redis_token)

def get_user_quota(self,user_credentials: str) -> int:
def get_user_quota(self, user_credentials: str) -> int:
"""Function to get the quota of a user based on his username"""
key = f"user:{user_credentials}"
result = self.redis.hget(key, "quota")
Expand Down
7 changes: 6 additions & 1 deletion autograder/steps/step_registry.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import os
from typing import Dict, Callable, Any, Optional

from autograder.models.dataclass.step_result import StepName
Expand Down Expand Up @@ -79,7 +80,11 @@ def _build_feedback(self) -> Optional[Step]:

def _build_exporter(self) -> Optional[Step]:
if self.config.get("export_results"):
exporter = self.config.get("exporter") or UpstashDriver()
# Update the fallback to include the required credentials
exporter = self.config.get("exporter") or UpstashDriver(
redis_url=os.getenv("UPSTASH_REDIS_URL"),
redis_token=os.getenv("UPSTASH_REDIS_TOKEN")
)
return ExporterStep(exporter)
return None

Expand Down
4 changes: 4 additions & 0 deletions github_action/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import logging
import os
from dotenv import load_dotenv
from argparse import ArgumentParser
from .github_action_service import GithubActionService
from autograder.autograder import AutograderPipeline
Expand Down Expand Up @@ -59,6 +60,9 @@ async def main():
This makes the Adapter accessible to the GitHub Action workflow,
that runs by entrypoint.sh script with all arguments passed to it.
"""

load_dotenv() # Load environment variables from .env file if present

success_execution = False
try:
args = __parser_values()
Expand Down
14 changes: 11 additions & 3 deletions tests/unit/pipeline/test_exporter_interface.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import pytest
from unittest.mock import MagicMock, patch
from unittest.mock import MagicMock, patch, ANY
from typing import Optional

from autograder.models.abstract.exporter import Exporter
Expand Down Expand Up @@ -43,7 +43,8 @@ def test_exporter_step_calls_export(self):
def test_upstash_driver_export_delegates(self):
"""UpstashDriver.export() delegates to set_score()."""
with patch("autograder.services.upstash_driver.Redis"):
driver = UpstashDriver()
# Pass dummy credentials to satisfy the new __init__ parameters
driver = UpstashDriver(redis_url="mock_url", redis_token="mock_token")
driver.set_score = MagicMock()

driver.export("user123", 95.0, "Optional feedback")
Expand Down Expand Up @@ -85,4 +86,11 @@ def test_step_registry_build_exporter_default(self):

assert isinstance(step, ExporterStep)
assert step._exporter_service is mock_driver
mock_driver_cls.assert_called_once()

# Verify that the driver is initialized with environment variables.
# UpstashDriver.__init__ handles validation and raises ValueError if
# redis_url or redis_token are missing (None).
mock_driver_cls.assert_called_once_with(
redis_url=ANY,
redis_token=ANY
)
5 changes: 5 additions & 0 deletions web/core/lifespan.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
"""Application lifespan management."""

import asyncio
import os
from contextlib import asynccontextmanager
from typing import Optional

from dotenv import load_dotenv
from fastapi import FastAPI

from autograder.services.template_library_service import TemplateLibraryService
Expand Down Expand Up @@ -44,6 +46,9 @@ async def lifespan(app: FastAPI):
Shutdown:
- Clean up resources
"""

load_dotenv() # Load environment variables first from .env file

global template_service, grading_tasks

# Startup
Expand Down
Loading