package parsing update
This commit is contained in:
@@ -41,20 +41,22 @@ public class PackageParserService : IPackageParserService
|
||||
using var archive = new ZipArchive(packageStream, ZipArchiveMode.Read);
|
||||
archive.ExtractToDirectory(workingDirectory);
|
||||
|
||||
// Check if this is a Polygon package (search for problem.xml)
|
||||
// Search for problem.xml (Polygon package format is required)
|
||||
var problemXmlPath = Directory.EnumerateFiles(workingDirectory, "problem.xml", SearchOption.AllDirectories)
|
||||
.FirstOrDefault();
|
||||
|
||||
if (!string.IsNullOrEmpty(problemXmlPath))
|
||||
if (string.IsNullOrEmpty(problemXmlPath))
|
||||
{
|
||||
var packageRoot = Path.GetDirectoryName(problemXmlPath)!;
|
||||
_logger.LogInformation("Detected Polygon package format (problem.xml found at {ProblemXml})", problemXmlPath);
|
||||
return await ParsePolygonPackageAsync(packageRoot, problemXmlPath, workingDirectory);
|
||||
_logger.LogError("problem.xml not found in package. Only Polygon format is supported.");
|
||||
throw new InvalidOperationException(
|
||||
"Invalid package format: problem.xml not found. " +
|
||||
"Only Polygon package format is supported. " +
|
||||
"Please ensure your package contains a problem.xml file.");
|
||||
}
|
||||
|
||||
// Fall back to legacy format (.in/.out files)
|
||||
_logger.LogInformation("Using legacy package format (.in/.out files)");
|
||||
return await ParseLegacyPackage(workingDirectory);
|
||||
var packageRoot = Path.GetDirectoryName(problemXmlPath)!;
|
||||
_logger.LogInformation("Polygon package detected (problem.xml found at {ProblemXml})", problemXmlPath);
|
||||
return await ParsePolygonPackageAsync(packageRoot, problemXmlPath, workingDirectory);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -69,8 +71,18 @@ public class PackageParserService : IPackageParserService
|
||||
|
||||
if (descriptor == null)
|
||||
{
|
||||
_logger.LogWarning("Failed to parse problem.xml, falling back to legacy format");
|
||||
return await ParseLegacyPackage(packageRoot, extractionRoot);
|
||||
_logger.LogError("Failed to parse problem.xml");
|
||||
throw new InvalidOperationException(
|
||||
"Failed to parse problem.xml. The Polygon package format may be corrupted or invalid.");
|
||||
}
|
||||
|
||||
// Check for interactive problems
|
||||
if (descriptor.Interactor != null)
|
||||
{
|
||||
_logger.LogError("Interactive problem detected: {ShortName}", descriptor.ShortName);
|
||||
throw new NotSupportedException(
|
||||
"Interactive problems are not currently supported. " +
|
||||
"The problem package contains an interactor, which is used for interactive tasks.");
|
||||
}
|
||||
|
||||
var package = new ProblemPackage
|
||||
@@ -99,8 +111,10 @@ public class PackageParserService : IPackageParserService
|
||||
|
||||
if (testIndices.Count == 0)
|
||||
{
|
||||
_logger.LogWarning("No test definitions discovered in problem.xml; falling back to filesystem scan");
|
||||
return await ParseLegacyPackage(packageRoot, extractionRoot);
|
||||
_logger.LogError("No test definitions found in problem.xml");
|
||||
throw new InvalidOperationException(
|
||||
"No test definitions found in problem.xml. " +
|
||||
"The Polygon package must define tests in the problem.xml file.");
|
||||
}
|
||||
|
||||
var inputs = new List<(int index, string inputPath)>();
|
||||
@@ -195,60 +209,6 @@ public class PackageParserService : IPackageParserService
|
||||
return package;
|
||||
}
|
||||
|
||||
private async Task<ProblemPackage> ParseLegacyPackage(string workingDirectory, string? extractionRoot = null)
|
||||
{
|
||||
var package = new ProblemPackage
|
||||
{
|
||||
WorkingDirectory = workingDirectory,
|
||||
ExtractionRoot = extractionRoot ?? workingDirectory
|
||||
};
|
||||
|
||||
// Find tests directory
|
||||
var testsDir = Path.Combine(workingDirectory, "tests");
|
||||
if (!Directory.Exists(testsDir))
|
||||
{
|
||||
_logger.LogWarning("Tests directory not found, searching for test files in root");
|
||||
testsDir = workingDirectory;
|
||||
}
|
||||
|
||||
// Parse test cases
|
||||
var inputFiles = Directory.GetFiles(testsDir, "*", SearchOption.AllDirectories)
|
||||
.Where(f => Path.GetFileName(f).EndsWith(".in") || Path.GetFileName(f).Contains("input"))
|
||||
.OrderBy(f => f)
|
||||
.ToList();
|
||||
|
||||
for (int i = 0; i < inputFiles.Count; i++)
|
||||
{
|
||||
var inputFile = inputFiles[i];
|
||||
var outputFile = FindCorrespondingOutputFile(inputFile);
|
||||
|
||||
if (outputFile == null)
|
||||
{
|
||||
_logger.LogWarning("No output file found for input {InputFile}", inputFile);
|
||||
continue;
|
||||
}
|
||||
|
||||
package.TestCases.Add(new TestCase
|
||||
{
|
||||
Number = package.TestCases.Count + 1,
|
||||
InputFilePath = inputFile,
|
||||
OutputFilePath = outputFile,
|
||||
TimeLimit = package.DefaultTimeLimit,
|
||||
MemoryLimit = package.DefaultMemoryLimit
|
||||
});
|
||||
}
|
||||
|
||||
// Look for and compile checker
|
||||
package.CheckerPath = await FindAndCompileCheckerAsync(workingDirectory);
|
||||
|
||||
if (package.TestCases.Count == 0)
|
||||
{
|
||||
_logger.LogWarning("No test cases found! Check package structure. Expected .in/.out files in tests directory or root");
|
||||
}
|
||||
|
||||
_logger.LogInformation("Parsed legacy package with {TestCount} tests", package.TestCases.Count);
|
||||
return package;
|
||||
}
|
||||
|
||||
private async Task<Dictionary<string, string>> CompilePolygonExecutablesAsync(
|
||||
PolygonProblemDescriptor descriptor,
|
||||
@@ -691,36 +651,6 @@ public class PackageParserService : IPackageParserService
|
||||
return null;
|
||||
}
|
||||
|
||||
private string? FindCorrespondingOutputFile(string inputFile)
|
||||
{
|
||||
var directory = Path.GetDirectoryName(inputFile)!;
|
||||
var fileName = Path.GetFileNameWithoutExtension(inputFile);
|
||||
var extension = Path.GetExtension(inputFile);
|
||||
|
||||
// Try various output file naming patterns
|
||||
var patterns = new[]
|
||||
{
|
||||
fileName.Replace("input", "output") + ".out",
|
||||
fileName.Replace("input", "output") + ".a",
|
||||
fileName.Replace("input", "answer") + ".out",
|
||||
fileName.Replace("input", "answer") + ".a",
|
||||
fileName + ".out",
|
||||
fileName + ".a",
|
||||
fileName.Replace(".in", ".out"),
|
||||
fileName.Replace(".in", ".a")
|
||||
};
|
||||
|
||||
foreach (var pattern in patterns)
|
||||
{
|
||||
var candidate = Path.Combine(directory, pattern);
|
||||
if (File.Exists(candidate))
|
||||
{
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private async Task<string?> FindAndCompileCheckerAsync(string workingDirectory)
|
||||
{
|
||||
|
||||
@@ -210,6 +210,21 @@ public class PolygonProblemXmlParser
|
||||
}
|
||||
}
|
||||
|
||||
var interactorElement = assets.Element("interactor");
|
||||
if (interactorElement != null)
|
||||
{
|
||||
var interactorSource = interactorElement.Element("source");
|
||||
if (interactorSource != null)
|
||||
{
|
||||
descriptor.Interactor = new PolygonInteractorDescriptor
|
||||
{
|
||||
SourcePath = interactorSource.Attribute("path")?.Value ?? string.Empty,
|
||||
Type = interactorSource.Attribute("type")?.Value,
|
||||
BinaryPath = interactorElement.Element("binary")?.Attribute("path")?.Value
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
var validatorsSection = assets.Element("validators");
|
||||
if (validatorsSection != null)
|
||||
{
|
||||
@@ -264,6 +279,7 @@ public class PolygonProblemDescriptor
|
||||
public List<PolygonTestDefinition> Tests { get; } = new();
|
||||
public List<PolygonExecutableDescriptor> Executables { get; } = new();
|
||||
public PolygonCheckerDescriptor? Checker { get; set; }
|
||||
public PolygonInteractorDescriptor? Interactor { get; set; }
|
||||
public List<PolygonValidatorDescriptor> Validators { get; } = new();
|
||||
}
|
||||
|
||||
@@ -311,6 +327,16 @@ public class PolygonCheckerDescriptor
|
||||
public string? Type { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents interactor information declared in problem.xml
|
||||
/// </summary>
|
||||
public class PolygonInteractorDescriptor
|
||||
{
|
||||
public string SourcePath { get; set; } = string.Empty;
|
||||
public string? BinaryPath { get; set; }
|
||||
public string? Type { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents validator information declared in problem.xml
|
||||
/// </summary>
|
||||
|
||||
Reference in New Issue
Block a user