Parent Issue
Part of #338 — Feature: Data Science Template (data_science)
Overview
Implement the expect_csv_output test function that executes the student's program and validates a generated CSV file against expected structure and values. This is the primary test for assignments where students produce tabular data outputs (DataFrames exported to CSV).
Interface
class ExpectCsvOutputTest(BaseExecutionTest):
@property
def name(self):
return "expect_csv_output"
Parameters
| Parameter |
Type |
Required |
Description |
program_command |
string/dict |
✓ |
Command to execute the student's script. Supports multi-language dict format and "CMD" auto-resolution. |
artifact_path |
string |
✓ |
Relative path to the generated CSV file (e.g., output.csv). Extracted from sandbox /app/ directory. |
expected_columns |
list[string] |
✗ |
Expected column names. Comparison is order-insensitive. |
expected_shape |
[int, int] |
✗ |
Expected [rows, columns] shape of the CSV. |
expected_values |
dict |
✗ |
Column → list of expected values for spot-checking specific cells. |
tolerance |
float |
✗ |
Numeric tolerance for value comparison (default: 0.001). |
sort_by |
string |
✗ |
Column to sort by before comparison (handles non-deterministic row order). |
inputs |
list[string] |
✗ |
Stdin inputs if the program requires interactive input. |
Scoring Logic
The test performs multiple independent checks. Scoring works as follows:
- File existence — If the artifact file is not found in the sandbox after execution, score is 0.
- CSV parsing — If the file exists but cannot be parsed as valid CSV, score is 0.
- Column check (if
expected_columns specified):
- Score proportional to the number of correct columns found:
(matched_columns / expected_columns) * weight_of_this_check
- Extra columns in the student output are ignored (not penalized).
- Shape check (if
expected_shape specified):
- Binary: 0 if shape doesn't match, full credit if it does.
- Value check (if
expected_values specified):
- Score proportional to matching values within tolerance.
- For numeric values:
abs(actual - expected) <= tolerance → match.
- For string values: exact match after strip.
If multiple checks are specified, the final score is the weighted average (equal weights across active checks).
If ALL specified checks pass → 100. If none specified beyond artifact_path → 100 (file existence only).
Execution Flow
- Run
program_command in the sandbox (via run_sandbox_execution from BaseExecutionTest).
- Check for base errors (timeout, compilation error, runtime error) → score 0 with appropriate message.
- Extract file at
/app/{artifact_path} from the sandbox using sandbox.extract_file().
- Parse CSV content using Python's
csv.reader (NOT pandas — the autograder should not depend on pandas).
- If
sort_by specified, sort rows by that column before comparison.
- Run each active check and compute final score.
Error Handling
| Scenario |
Result |
| Program times out |
Score 0, report timeout message |
| Program crashes (runtime error) |
Score 0, report error |
| File not found after execution |
Score 0, report "file not generated" |
| File is not valid CSV |
Score 0, report "invalid CSV format" |
| Column mismatch |
Partial score with details of missing columns |
| Shape mismatch |
Score 0 for shape check, report expected vs actual |
| Value mismatch |
Partial score, report which values didn't match |
Example Usage
{
"name": "expect_csv_output",
"parameters": {
"program_command": "python3 analysis.py preprocess",
"artifact_path": "cleaned.csv",
"expected_columns": ["name", "cuisine", "rating", "price_range", "city"],
"expected_shape": [1420, 5],
"sort_by": "name",
"tolerance": 0.01
},
"weight": 50
}
Implementation Notes
- Inherit from
BaseExecutionTest (defined in input_output.py) to reuse run_sandbox_execution and check_for_base_errors.
- Use
sandbox.extract_file(f"/app/{artifact_path}") to get file content (same pattern as ExpectFileArtifactTest).
- Parse CSV with
csv.reader from stdlib — no external dependencies.
- Numeric detection: attempt
float() conversion; if it succeeds, compare with tolerance; otherwise, string compare.
- Validate
artifact_path for path traversal (reject .. and absolute paths) — reuse ExpectFileArtifactTest._validate_artifact_path logic.
- All user-facing messages must go through the
t() translation function.
Translation Keys Needed
data_science.expect_csv_output.description
data_science.expect_csv_output.report.success
data_science.expect_csv_output.report.file_not_found
data_science.expect_csv_output.report.invalid_csv
data_science.expect_csv_output.report.column_mismatch
data_science.expect_csv_output.report.shape_mismatch
data_science.expect_csv_output.report.value_mismatch
data_science.expect_csv_output.report.partial_success
Acceptance Criteria
Parent Issue
Part of #338 — Feature: Data Science Template (
data_science)Overview
Implement the
expect_csv_outputtest function that executes the student's program and validates a generated CSV file against expected structure and values. This is the primary test for assignments where students produce tabular data outputs (DataFrames exported to CSV).Interface
Parameters
program_command"CMD"auto-resolution.artifact_pathoutput.csv). Extracted from sandbox/app/directory.expected_columnsexpected_shape[rows, columns]shape of the CSV.expected_valuestolerance0.001).sort_byinputsScoring Logic
The test performs multiple independent checks. Scoring works as follows:
expected_columnsspecified):(matched_columns / expected_columns) * weight_of_this_checkexpected_shapespecified):expected_valuesspecified):abs(actual - expected) <= tolerance→ match.If multiple checks are specified, the final score is the weighted average (equal weights across active checks).
If ALL specified checks pass → 100. If none specified beyond artifact_path → 100 (file existence only).
Execution Flow
program_commandin the sandbox (viarun_sandbox_executionfromBaseExecutionTest)./app/{artifact_path}from the sandbox usingsandbox.extract_file().csv.reader(NOT pandas — the autograder should not depend on pandas).sort_byspecified, sort rows by that column before comparison.Error Handling
Example Usage
{ "name": "expect_csv_output", "parameters": { "program_command": "python3 analysis.py preprocess", "artifact_path": "cleaned.csv", "expected_columns": ["name", "cuisine", "rating", "price_range", "city"], "expected_shape": [1420, 5], "sort_by": "name", "tolerance": 0.01 }, "weight": 50 }Implementation Notes
BaseExecutionTest(defined ininput_output.py) to reuserun_sandbox_executionandcheck_for_base_errors.sandbox.extract_file(f"/app/{artifact_path}")to get file content (same pattern asExpectFileArtifactTest).csv.readerfrom stdlib — no external dependencies.float()conversion; if it succeeds, compare with tolerance; otherwise, string compare.artifact_pathfor path traversal (reject..and absolute paths) — reuseExpectFileArtifactTest._validate_artifact_pathlogic.t()translation function.Translation Keys Needed
data_science.expect_csv_output.descriptiondata_science.expect_csv_output.report.successdata_science.expect_csv_output.report.file_not_founddata_science.expect_csv_output.report.invalid_csvdata_science.expect_csv_output.report.column_mismatchdata_science.expect_csv_output.report.shape_mismatchdata_science.expect_csv_output.report.value_mismatchdata_science.expect_csv_output.report.partial_successAcceptance Criteria
DataScienceTemplate.testsdict with key"expect_csv_output"sort_bycorrectly normalizes row order before comparisonartifact_pathis rejected