"""
Jason Yau 
09/03/2025

Use to quickly run all student source code from Webcourses on Eustis and output a csv using stdout.

====================================

Usage:

Go to assignments, P0, Download submissions. A file named submissions.zip (or similar) will download

Copy submissions.zip file to temp Eustis folder like so (This assumes ~/temp/ exists already. If not, you will need to create the directory.): 
scp -r <path of submissions.zip> <YOUR_NID>@eustis.eecs.ucf.edu:~/temp/

Copy this python script (p0.py) to temp Eustis folder like so:
scp -r <path of p0.py> <YOUR_NID>@eustis.eecs.ucf.edu:~/temp/

Copy both test cases zip file to temp Eustis folder like so:
scp -r <path of p0-sample.zip> <YOUR_NID>@eustis.eecs.ucf.edu:~/temp/
scp -r <path of p0-secret.zip> <YOUR_NID>@eustis.eecs.ucf.edu:~/temp/

ssh into eustis
ssh <YOUR_NID>@eustis.eecs.ucf.edu

Run the python script which outputs a csv using stdout. You can copy the file over to your local machine via rsync and open with Excel.
python3 ~/temp/p0.py > ~/temp/p0.csv

====================================

Make sure to delete all created files afterwards to be safe.
"""


import os
import glob
from zipfile import ZipFile
import shutil
import subprocess
import re

def normalize(s: str) -> str:
    # Normalize whitespace: strip leading/trailing, collapse multiple spaces/newlines
    return "\n".join(line.strip() for line in s.strip().splitlines())

script_path = os.path.abspath(__file__)
script_directory = os.path.dirname(script_path)

extracted_path = f"{script_directory}/extracted"
if (len(glob.glob(extracted_path)) != 0):
    shutil.rmtree(extracted_path)

submissions_zip = glob.glob(f"{script_directory}/submissions*.zip")
if len(submissions_zip) != 1:
    print(f"There were {len(submissions_zip)} submissions*.zip found. Exiting.")
    exit(1)
with ZipFile(submissions_zip[0], 'r') as zip_object:
    zip_object.extractall(extracted_path)

sample_cases_zip = glob.glob(f"{script_directory}/p0-sample.zip")
if len(sample_cases_zip) != 1:
    print(f"p0-sample.zip not found. Exiting.")
    exit(1)
with ZipFile(sample_cases_zip[0], 'r') as zip_object:
    zip_object.extractall(extracted_path)

secret_cases_zip = glob.glob(f"{script_directory}/p0-secret.zip")
if len(secret_cases_zip) != 1:
    print(f"p0-secret.zip not found. Exiting.")
    exit(1)
with ZipFile(secret_cases_zip[0], 'r') as zip_object:
    zip_object.extractall(extracted_path)

input_files = sorted(glob.glob(f"{extracted_path}/p0-*/wordlescoring*.in"))
output_files = sorted(glob.glob(f"{extracted_path}/p0-*/wordlescoring*.out"))
# Make sure we don't screw this part up.
assert len(input_files) == len(output_files)
for i in range(len(input_files)):
    assert input_files[i].split(".in")[0] in output_files[i].split(".out")[0]

print(f"\"student name\",\"passed cases\",\"comment to write\",\"late\",\"correct file name\",\"compiled\"")

source_code_files = sorted(glob.glob(f"{extracted_path}/*.c")+glob.glob(f"{extracted_path}/*.C"))
for source_code_file in source_code_files:
    file_name = os.path.basename(source_code_file)
    student_name = file_name.split('_')[0]
    student_file_name = file_name.split('_')[len(file_name.split('_'))-1]
    late = "LATE" in file_name.upper()

    executable = f"{source_code_file.split(".c")[0]}.exe"
    gcc_result = subprocess.run(["gcc", source_code_file, "-o", executable], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
    compiled = gcc_result.returncode == 0

    passed_cases = 0
    comment = ""
    
    if compiled:
        for i in range(len(input_files)):
            with open(input_files[i], "r") as fin:
                try:
                    execute_result = subprocess.run([executable], stdin=fin, stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=10, text=True, universal_newlines=True, encoding='mac_roman')
                    if normalize(execute_result.stdout) == normalize(open(output_files[i], "r").read()):
                        comment += f"Test case #{i+1}: PASSED\n"
                        passed_cases += 1
                    else:
                        comment += f"Test case #{i+1}: FAILED (INCORRECT OUTPUT)\n"
                except subprocess.TimeoutExpired:
                    comment += f"Test case #{i+1}: FAILED (TIME LIMIT EXCEEDED)\n"
    else:
        comment += "Did not compile.\n"

    comment += f"Test cases overall: {passed_cases}/{len(input_files)}"
    
    print(f"\"{student_name}\",\"{passed_cases}\",\"{comment}\",\"{late}\",\"{re.match(r"wordlescoring[-]{0,1}[\d]{0,5}.c", student_file_name) != None}\",\"{compiled}\"")

shutil.rmtree(extracted_path)