2323import random
2424import traceback
2525import uuid
26+ import difflib
2627from pathlib import Path
2728
2829import colorlog
@@ -2074,6 +2075,7 @@ def check(self) -> tuple[int, int]:
20742075
20752076 self ._check_symlinks ()
20762077 self ._check_file_and_directory_names ()
2078+ self ._check_submission_directory_names ()
20772079
20782080 run .limit .check_limit_capabilities (self )
20792081
@@ -2097,6 +2099,37 @@ def check(self) -> tuple[int, int]:
20972099 context .wait_for_background_work ()
20982100 return self .errors , self .warnings
20992101
2102+ def _check_submission_directory_names (self ):
2103+ """Heuristically check if submissions contain any directories that will be ignored because of typos or format mismatches"""
2104+ submission_directories = [p .name for p in (Path (self .probdir ) / 'submissions' ).glob ('*' ) if p .is_dir ()]
2105+ if len (submission_directories ) == 0 :
2106+ return
2107+
2108+ def most_similar (present_dir : str , format_version : FormatVersion ):
2109+ similarities = [
2110+ (spec_dir , difflib .SequenceMatcher (None , present_dir , spec_dir ).ratio ())
2111+ for spec_dir in format_version .submission_directories
2112+ ]
2113+ return max (similarities , key = lambda x : x [1 ])
2114+
2115+ for present_dir in submission_directories :
2116+ most_similar_dir , max_similarity = most_similar (present_dir , self .format )
2117+
2118+ if max_similarity == 1 :
2119+ # Exact match, no typo
2120+ continue
2121+
2122+ if 0.75 <= max_similarity :
2123+ self .warning (f'Potential typo: directory submissions/{ present_dir } is similar to { most_similar_dir } ' )
2124+ else :
2125+ for other_version in [v for v in FormatVersion if v != self .format ]:
2126+ _ , max_similarity = most_similar (present_dir , other_version )
2127+ if max_similarity == 1 :
2128+ self .warning (
2129+ f'Directory submissions/{ present_dir } is not part of format version { self .format } , but part of { other_version } '
2130+ )
2131+ break
2132+
21002133 def _check_symlinks (self ):
21012134 """Check that all symlinks point to something existing within the problem package"""
21022135 probdir = os .path .realpath (self .probdir )
0 commit comments