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
8 changes: 5 additions & 3 deletions API_calls/StudentDatafromLDRESTAPI.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import requests
import time
import logging
from config.settings import BASE_GESSI_URL
from config.settings import BASE_GESSI_URL, LD_API_KEY, LD_API_KEY_HEADER

logger = logging.getLogger(__name__)

LD_HEADERS = {LD_API_KEY_HEADER: LD_API_KEY}

def fetch_projects() -> list:
"""
Retrieve the list of projects from the LD REST API.
Expand All @@ -16,7 +18,7 @@ def fetch_projects() -> list:

for attempt in range(max_retries):
try:
response = requests.get(url, timeout=60)
response = requests.get(url, headers=LD_HEADERS, timeout=60)
response.raise_for_status() # Raise an exception if status != 200
projects = response.json()
return projects
Expand All @@ -36,7 +38,7 @@ def fetch_project_details(project_id: int) -> dict:
"""
url = f"{BASE_GESSI_URL}/projects/{project_id}"
# Use increased timeout here as well
response = requests.get(url, timeout=60)
response = requests.get(url, headers=LD_HEADERS, timeout=60)
response.raise_for_status()
return response.json()

Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,10 +98,11 @@ docker compose up -d --build ld_eval
| `MONGO_USER` / `MONGO_PASS` | Credentials (leave blank for local dev) |
| `MONGO_AUTHSRC` | Auth DB (usually `admin`) |
| `BASE_GESSI_URL` | REST endpoint of the public dashboard |
| `LD_API_KEY` | Shared Learning Dashboard API key sent as `X-LD-API-Key` |
| `QUALITY_MODELS_DIR` | Path to `QUALITY_MODELS` folder |
| Scheduler: `_Start_scheduler_date`, `_End_scheduler_date`, `_Hour_scheduler` … | Daily refresh window (see `config_files/config_variables.py`) |

All vars can be placed in `.env` and are loaded automatically.
All vars can be placed in `.env` and are loaded automatically. Generate `LD_API_KEY` once from the repository root with `python3 -c 'import secrets; print(secrets.token_urlsafe(48))'` and copy the same value into local consumer `.env` files when running outside Docker.

---

Expand Down
17 changes: 16 additions & 1 deletion config/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,29 @@

# Determine the base directory (adjust if needed)
BASE_DIR = Path(__file__).resolve().parent.parent
ROOT_DIR = BASE_DIR.parent


# Load environment variables from the .env file
# Load centralized root configuration first, then module-local values for
# standalone runs. Existing environment variables keep priority.
load_dotenv(ROOT_DIR / ".env")
load_dotenv(BASE_DIR / ".env")


def _require_env(name: str) -> str:
value = os.getenv(name)
if not value:
raise RuntimeError(
f"Missing required environment variable: {name}. "
f"Please set it in the root .env or this module's .env file."
)
return value


QUALITY_MODELS_DIR = os.getenv("QUALITY_MODELS_DIR", "QUALITY_MODELS")
BASE_GESSI_URL = os.getenv("BASE_GESSI_URL", "")
LD_API_KEY = _require_env("LD_API_KEY")
LD_API_KEY_HEADER = "X-LD-API-Key"


# Mongo database settings
Expand Down
16 changes: 16 additions & 0 deletions template.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# LD Eval local configuration.
# Docker receives LD_API_KEY from the root .env; use this file only for standalone runs.

BASE_GESSI_URL=http://localhost:8888/api

# Generate once in the repository root:
# python3 -c 'import secrets; print(secrets.token_urlsafe(48))'
# Copy the same value from the root .env.
LD_API_KEY=

MONGO_HOST=mongodb
MONGO_PORT=27017
MONGO_DB=mongo
MONGO_USER=
MONGO_PASS=
MONGO_AUTHSRC=mongo
3 changes: 3 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import sys
import os
from pathlib import Path


ROOT_DIR = Path(__file__).resolve().parents[1]

os.environ.setdefault("LD_API_KEY", "test-ld-api-key")

if str(ROOT_DIR) not in sys.path:
sys.path.insert(0, str(ROOT_DIR))
39 changes: 39 additions & 0 deletions tests/test_student_data_api_key.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
from unittest.mock import Mock

import API_calls.StudentDatafromLDRESTAPI as student_api


def test_fetch_projects_sends_ld_api_key(monkeypatch):
response = Mock()
response.json.return_value = []
response.raise_for_status.return_value = None
mock_get = Mock(return_value=response)

monkeypatch.setattr(student_api, "BASE_GESSI_URL", "http://tomcat:8080/api")
monkeypatch.setattr(student_api, "LD_HEADERS", {"X-LD-API-Key": "test-ld-api-key"})
monkeypatch.setattr(student_api.requests, "get", mock_get)

assert student_api.fetch_projects() == []
mock_get.assert_called_once_with(
"http://tomcat:8080/api/projects",
headers={"X-LD-API-Key": "test-ld-api-key"},
timeout=60,
)


def test_fetch_project_details_sends_ld_api_key(monkeypatch):
response = Mock()
response.json.return_value = {"id": 1}
response.raise_for_status.return_value = None
mock_get = Mock(return_value=response)

monkeypatch.setattr(student_api, "BASE_GESSI_URL", "http://tomcat:8080/api")
monkeypatch.setattr(student_api, "LD_HEADERS", {"X-LD-API-Key": "test-ld-api-key"})
monkeypatch.setattr(student_api.requests, "get", mock_get)

assert student_api.fetch_project_details(1) == {"id": 1}
mock_get.assert_called_once_with(
"http://tomcat:8080/api/projects/1",
headers={"X-LD-API-Key": "test-ld-api-key"},
timeout=60,
)
Loading