package parsing update
This commit is contained in:
@@ -41,20 +41,22 @@ public class PackageParserService : IPackageParserService
|
|||||||
using var archive = new ZipArchive(packageStream, ZipArchiveMode.Read);
|
using var archive = new ZipArchive(packageStream, ZipArchiveMode.Read);
|
||||||
archive.ExtractToDirectory(workingDirectory);
|
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)
|
var problemXmlPath = Directory.EnumerateFiles(workingDirectory, "problem.xml", SearchOption.AllDirectories)
|
||||||
.FirstOrDefault();
|
.FirstOrDefault();
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(problemXmlPath))
|
if (string.IsNullOrEmpty(problemXmlPath))
|
||||||
{
|
{
|
||||||
var packageRoot = Path.GetDirectoryName(problemXmlPath)!;
|
_logger.LogError("problem.xml not found in package. Only Polygon format is supported.");
|
||||||
_logger.LogInformation("Detected Polygon package format (problem.xml found at {ProblemXml})", problemXmlPath);
|
throw new InvalidOperationException(
|
||||||
return await ParsePolygonPackageAsync(packageRoot, problemXmlPath, workingDirectory);
|
"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)
|
var packageRoot = Path.GetDirectoryName(problemXmlPath)!;
|
||||||
_logger.LogInformation("Using legacy package format (.in/.out files)");
|
_logger.LogInformation("Polygon package detected (problem.xml found at {ProblemXml})", problemXmlPath);
|
||||||
return await ParseLegacyPackage(workingDirectory);
|
return await ParsePolygonPackageAsync(packageRoot, problemXmlPath, workingDirectory);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -69,8 +71,18 @@ public class PackageParserService : IPackageParserService
|
|||||||
|
|
||||||
if (descriptor == null)
|
if (descriptor == null)
|
||||||
{
|
{
|
||||||
_logger.LogWarning("Failed to parse problem.xml, falling back to legacy format");
|
_logger.LogError("Failed to parse problem.xml");
|
||||||
return await ParseLegacyPackage(packageRoot, extractionRoot);
|
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
|
var package = new ProblemPackage
|
||||||
@@ -99,8 +111,10 @@ public class PackageParserService : IPackageParserService
|
|||||||
|
|
||||||
if (testIndices.Count == 0)
|
if (testIndices.Count == 0)
|
||||||
{
|
{
|
||||||
_logger.LogWarning("No test definitions discovered in problem.xml; falling back to filesystem scan");
|
_logger.LogError("No test definitions found in problem.xml");
|
||||||
return await ParseLegacyPackage(packageRoot, extractionRoot);
|
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)>();
|
var inputs = new List<(int index, string inputPath)>();
|
||||||
@@ -195,60 +209,6 @@ public class PackageParserService : IPackageParserService
|
|||||||
return package;
|
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(
|
private async Task<Dictionary<string, string>> CompilePolygonExecutablesAsync(
|
||||||
PolygonProblemDescriptor descriptor,
|
PolygonProblemDescriptor descriptor,
|
||||||
@@ -691,36 +651,6 @@ public class PackageParserService : IPackageParserService
|
|||||||
return null;
|
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)
|
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");
|
var validatorsSection = assets.Element("validators");
|
||||||
if (validatorsSection != null)
|
if (validatorsSection != null)
|
||||||
{
|
{
|
||||||
@@ -264,6 +279,7 @@ public class PolygonProblemDescriptor
|
|||||||
public List<PolygonTestDefinition> Tests { get; } = new();
|
public List<PolygonTestDefinition> Tests { get; } = new();
|
||||||
public List<PolygonExecutableDescriptor> Executables { get; } = new();
|
public List<PolygonExecutableDescriptor> Executables { get; } = new();
|
||||||
public PolygonCheckerDescriptor? Checker { get; set; }
|
public PolygonCheckerDescriptor? Checker { get; set; }
|
||||||
|
public PolygonInteractorDescriptor? Interactor { get; set; }
|
||||||
public List<PolygonValidatorDescriptor> Validators { get; } = new();
|
public List<PolygonValidatorDescriptor> Validators { get; } = new();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -311,6 +327,16 @@ public class PolygonCheckerDescriptor
|
|||||||
public string? Type { get; set; }
|
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>
|
/// <summary>
|
||||||
/// Represents validator information declared in problem.xml
|
/// Represents validator information declared in problem.xml
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
Reference in New Issue
Block a user