package parsing update

This commit is contained in:
prixod
2025-11-04 23:45:59 +04:00
parent ffd0de446d
commit c926699a46
2 changed files with 52 additions and 96 deletions

View File

@@ -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)
{

View File

@@ -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>