validate.py

#%%
import os
import zipfile
import shutil
import subprocess
import requests
from sys import platform

group = input("Group number ? >> ");
login1 = input("Login of the first student ? >> ")
login2 = input("Login of the second student ? >> ")
lsgroup = "%03d" % int(group)
login1 = login1.lower()
login2 = login2.lower()
idname = "group" + lsgroup + '-' + login1 + '-' + login2
print("Your file name must be : " + idname)

# Configuration A MODIFIER PAR L'ETUDIANT !!!
# =============================================
# project_path = "group001-vlegat-jfremacle.zip"
# rapport_path = "group001-vlegat-jfremacle-rapport.zip"
project_path = idname + ".zip"
rapport_path = idname + "-rapport.zip"
Windows_TDM = (platform == "win32")  # True if using Windows + TDM-GCC, False otherwise
# =============================================


all_illegal = [
    "/gmsh/",
    "/build/",
    "/.",
]

project_illegal = [
    "/glfw/",
    "CMakeCache.txt",
    "Makefile",
    "CMakeFiles",
    "ProjectPostProcessor/",
    "ProjectPreProcessor/",
    "/data/"
]

project_allow = [
    ".c",
    ".cpp",
    ".h",
    ".hpp",
    ".txt",
    ".md"
]

# %% File name validation
project_fname = os.path.basename(project_path)
rapport_fname = os.path.basename(rapport_path)
assert project_fname.endswith(".zip"), "Project file must be a zip file"
assert rapport_fname.endswith(".zip"), "Rapport file must be a zip file"
project_parts = project_fname[:-4].split("-")
rapport_parts = rapport_fname[:-4].split("-")
assert len(project_parts) == 3, "Project file name must be groupXXX-YYY-ZZZ.zip"
assert len(rapport_parts) == 4, "Rapport file name must be groupXXX-YYY-ZZZ-rapport.zip"
assert project_parts[0] == rapport_parts[0], "file names must start with 'groupXXX'"
assert project_parts[0][5:] == rapport_parts[0][5:] == "%0.3d" % int(project_parts[0][5:]), "group number must be a 3 digit number"
assert project_parts[1] == rapport_parts[1], "group names must be the same"
assert project_parts[2] == rapport_parts[2], "group names must be the same"
assert rapport_parts[3] == "rapport", "rapport file name must end with '-rapport'"
assert project_parts[1] != "vlegat", "Non, c'est votre identifiant ucl qu'il faut mettre, vlegat-jfremacle c'est pour l'exemple"
assert project_parts[2] != "jfremacle", "Non, c'est votre identifiant ucl qu'il faut mettre, vlegat-jfremacle c'est pour l'exemple"

# %% Rapport content validation
if os.path.exists('./tmp-rapport'):
    shutil.rmtree('./tmp-rapport')

illegal = 0
penalty = 0
os.makedirs('./tmp-rapport')
with zipfile.ZipFile(rapport_path, 'r') as zip:
    for file in zip.namelist():
        nstrip = 0
        if file.startswith(project_fname[:-4]):
            nstrip = len(project_fname[:-4]) + 1
        if any(i in file for i in all_illegal):
            print(f"WARNING ERROR : illegal file : {file}")
            illegal += 1; penalty += 10
            continue
        print(file)
        if file.endswith('/'):
            os.makedirs('./tmp-rapport/' + file[nstrip:], exist_ok=True)
            continue
        with zip.open(file) as zf, open('./tmp-rapport/' + file[nstrip:], 'wb') as f:
            shutil.copyfileobj(zf, f)

if not os.path.exists('./Validated'):
    os.makedirs('./Validated')

with zipfile.ZipFile(f"./Validated/{rapport_fname}", 'w', zipfile.ZIP_DEFLATED) as new_zip:
    print("Creating new zip file with files")
    for root, dirs, files in os.walk('./tmp-rapport'):
        for file in files:
            new_zip.write(os.path.join(root, file), os.path.relpath(os.path.join(root, file), "./tmp-rapport"))

# %% Project content validation
if os.path.exists('./tmp'):
    shutil.rmtree('./tmp')

os.makedirs('./tmp')
with zipfile.ZipFile(project_path, 'r') as zip:
    for file in zip.namelist():
        nstrip = 0
        if file.startswith(project_fname[:-4]):
            nstrip = len(project_fname[:-4]) + 1
        if not (file.startswith('Project/') or file.startswith(project_fname[:-4])):
            # print(f"WARNING ERROR : illegal file : {file}")
            illegal += 1; penalty += 1
            continue
        if not any(file.endswith(ext) for ext in project_allow) and '.' in file:
            # print(f"WARNING ERROR : illegal file : {file}")
            illegal += 1; penalty += 1
            continue
        if any(i in file for i in all_illegal):
            # print(f"WARNING ERROR : illegal file : {file}")
            illegal += 1; penalty += 10
            continue
        if any(i in file for i in project_illegal):
            # print(f"WARNING ERROR : illegal file : {file}")
            illegal += 1; penalty += 1
            continue
        if file.endswith('/'):
            os.makedirs('./tmp/' + file[nstrip:], exist_ok=True)
            continue
        with zip.open(file) as zf, open('./tmp/' + file[nstrip:], 'wb') as f:
            shutil.copyfileobj(zf, f)

# %% Project compilation
if os.path.exists('./tmp-compil'):
    shutil.rmtree('./tmp-compil')
shutil.copytree('./tmp', './tmp-compil')

if Windows_TDM:
    exec = "myFem.exe"
    res = subprocess.run("cd ./tmp-compil/Project && mkdir build && cd build && cmake .. -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++ -G \"MinGW Makefiles\" && cmake --build .", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
else:
    exec = "./myFem"
    res = subprocess.run("cd ./tmp-compil/Project && mkdir build && cd build && cmake .. && cmake --build .", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)

print(res.stdout.decode('utf-8'))
print(res.stderr.decode('utf-8'))
if not os.path.exists(f"./tmp-compil/Project/build/{exec}"):
    raise Exception("Compilation failed")
print("Compilation successful")

# %% Project execution
os.makedirs("./tmp-compil/Project/data", exist_ok=True)
r1 = requests.get("https://raw.githubusercontent.com/MiguelDLC/femtools/main/data/mesh.txt")
r2 = requests.get("https://raw.githubusercontent.com/MiguelDLC/femtools/main/data/problem.txt")
if r1.status_code != 200:
    raise Exception("Failed to download mesh.txt")
if r2.status_code != 200:
    raise Exception("Failed to download problem.txt")
with open("./tmp-compil/Project/data/mesh.txt", "wb") as f:
    f.write(r1.content)
with open("./tmp-compil/Project/data/problem.txt", "wb") as f:
    f.write(r2.content)

res = subprocess.run(f"cd ./tmp-compil/Project/build && {exec}", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
print(res.stdout.decode('utf-8'))
print(res.stderr.decode('utf-8'))
if not os.path.exists("./tmp-compil/Project/data/UV.txt"):
    print("WARNING : Execution failed")
else:
    print("Execution successful")

# %%
if not os.path.exists('./Validated'):
    os.makedirs('./Validated')
with zipfile.ZipFile(f"./Validated/{project_fname}", 'w', zipfile.ZIP_DEFLATED) as new_zip:
    print("Creating new zip file with files")
    for root, dirs, files in os.walk('./tmp'):
        for file in files:
            print("   ",  os.path.relpath(os.path.join(root, file), "./tmp"))
            new_zip.write(os.path.join(root, file), os.path.relpath(os.path.join(root, file), "./tmp"))

print("\n========== Validation summary ==========")
print(f"    Compilation and execution successful")
if illegal == 0:
    print(f"    No illegal files found, you are good to go!")
else:
    print(f"    Total illegal files : {illegal} : {-penalty/10}/30 points")
    print("    Please check the files above and remove them before submitting again.")
    print("    Minor things like a readme are ok. Light but unnecessary files can be forgiven, but please remove them if possible.")
    print("    Major things like a build folder or gmsh folder will be heavily penalized.")
print("Clean files written to ./Validated") 

# %%