예제 #1
0
        public async Task <IActionResult> Put(
            [FromServices] IConfiguration Config,
            [FromServices] IServiceScopeFactory scopeFactory,
            [FromServices] StateMachineAwaiter awaiter,
            [FromServices] ManagementServiceClient MgmtSvc,
            [FromServices] IHubContext <OnlineJudgeHub> hub,
            [FromServices] ContestExecutorFactory cef,
            CancellationToken token)
        {
            var request = JsonConvert.DeserializeObject <JudgeRequest>(RequestBody);
            var problem = await DB.Problems
                          .Include(x => x.TestCases)
                          .SingleOrDefaultAsync(x => x.Id == request.problemId, token);

            if (problem == null)
            {
                return(Result(400, "The problem is not exist."));
            }

            if (!problem.IsVisible && string.IsNullOrWhiteSpace(request.contestId) && !await HasPermissionToProblemAsync(problem.Id, token))
            {
                return(Result(403, "You have no permission to the problem."));
            }

            if (IsGroupRequest() && !await IsGroupMemberAsync(token))
            {
                return(Result(401, "You are not a member of this group."));
            }

            if (!Constants.SupportedLanguages.Contains(request.language))
            {
                return(Result(400, "The language has not been supported."));
            }
            if (string.IsNullOrEmpty(request.code))
            {
                return(Result(400, "Code could not be empty."));
            }

            if (!string.IsNullOrEmpty(request.contestId))
            {
                var ce = cef.Create(request.contestId);

                if (!ce.IsContestInProgress(User.Current?.UserName))
                {
                    return(Result(400, "The contest is inactive."));
                }

                if (request.isSelfTest)
                {
                    return(Result(400, "You could not do a self test during the contest is in progress."));
                }

                if (await DB.ContestProblemLastStatuses.AnyAsync(x => x.IsLocked && x.ContestId == request.contestId && User.Current.Id == x.UserId && x.ProblemId == request.problemId, token))
                {
                    return(Result(400, "You have locked this problem."));
                }
            }

            if (!Constants.SupportedLanguages.Contains(request.language) && !Constants.UnsupportedLanguages.Contains(request.language) && problem.Source == ProblemSource.Local)
            {
                return(Result(400, "The programming language which you selected was not supported"));
            }

            if (request.isSelfTest && request.data.Count() == 0)
            {
                return(Result(400, "The self testing data has not been found"));
            }

            if (problem.Source != ProblemSource.Local && request.isSelfTest)
            {
                return(Result(400, "You could not use self data to test with a remote problem."));
            }

            if (IsGroupRequest() && !await DB.GroupProblems.AnyAsync(x => x.ProblemId == problem.Id && x.GroupId == CurrentGroup.Id) && string.IsNullOrEmpty(request.contestId))
            {
                return(Result(404, "The problem does not exist."));
            }

            #region Local Judge
            if (problem.Source == ProblemSource.Local)
            {
                var blobs = new ConcurrentDictionary <int, BlobInfo[]>();
                blobs.TryAdd(-1, new[] { new BlobInfo
                                         {
                                             Id   = await MgmtSvc.PutBlobAsync("Main" + Constants.GetSourceExtension(request.language), Encoding.UTF8.GetBytes(request.code)),
                                             Name = "Main" + Constants.GetSourceExtension(request.language),
                                             Tag  = "Problem=" + problem.Id
                                         } });
                blobs.TryAdd(-2, new[] { new BlobInfo
                                         {
                                             Id = await MgmtSvc.PutBlobAsync("limit.json", Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(new
                        {
                            UserTime     = problem.TimeLimitationPerCaseInMs,
                            PhysicalTime = problem.TimeLimitationPerCaseInMs * 4,
                            Memory       = problem.MemoryLimitationPerCaseInByte
                        }))),
                                             Name = "limit.json",
                                             Tag  = "Problem=" + problem.Id
                                         } });
                if (!problem.ValidatorBlobId.HasValue)
                {
                    blobs.TryAdd(-3, new[]
                    {
                        new BlobInfo
                        {
                            Id   = Guid.Parse(Config["JoyOI:StandardValidatorBlobId"]),
                            Name = "Validator.out"
                        }
                    });
                }
                else
                {
                    blobs.TryAdd(-3, new[]
                    {
                        new BlobInfo
                        {
                            Id   = problem.ValidatorBlobId.Value,
                            Name = "Validator" + Constants.GetBinaryExtension(problem.ValidatorLanguage)
                        }
                    });
                }

                List <TestCase> testCases = null;
                if (request.isSelfTest)
                {
                    Parallel.For(0, request.data.Count(), i =>
                    {
                        // Uploading custom data
                        var inputId  = MgmtSvc.PutBlobAsync($"input_{ i }.txt", Encoding.UTF8.GetBytes(request.data.ElementAt(i).input), token).Result;
                        var outputId = MgmtSvc.PutBlobAsync($"output_{ i }.txt", Encoding.UTF8.GetBytes(request.data.ElementAt(i).output), token).Result;
                        blobs.TryAdd(i, new[] {
                            new BlobInfo {
                                Id = inputId, Name = $"input_{ i }.txt", Tag = i.ToString()
                            },
                            new BlobInfo {
                                Id = outputId, Name = $"output_{ i }.txt", Tag = i.ToString()
                            }
                        });
                    });
                }
                else
                {
                    testCases = await DB.TestCases
                                .Where(x => x.ProblemId == problem.Id && (x.Type == TestCaseType.Small || x.Type == TestCaseType.Large || x.Type == TestCaseType.Hack))
                                .ToListAsync(token);

                    if (testCases.Count == 0)
                    {
                        return(Result(400, "No test case found."));
                    }

                    for (var i = 0; i < testCases.Count; i++)
                    {
                        blobs.TryAdd(i, new[] {
                            new BlobInfo {
                                Id = testCases[i].InputBlobId, Name = $"input_{ i }.txt", Tag = i.ToString()
                            },
                            new BlobInfo {
                                Id = testCases[i].OutputBlobId, Name = $"output_{ i }.txt", Tag = i.ToString()
                            }
                        });
                    }
                }

                var stateMachineId = await MgmtSvc.PutStateMachineInstanceAsync("JudgeStateMachine", Config["ManagementService:CallBack"], blobs.SelectMany(x => x.Value), await CalculatePriorityAsync(), token);

                var substatuses = blobs
                                  .Where(x => x.Key >= 0)
                                  .Select(x => new SubJudgeStatus
                {
                    SubId        = x.Key,
                    Result       = JudgeResult.Pending,
                    InputBlobId  = x.Value.Single(y => y.Name.StartsWith("input_")).Id,
                    OutputBlobId = x.Value.Single(y => y.Name.StartsWith("output_")).Id,
                    TestCaseId   = testCases != null ? (Guid?)testCases[x.Key].Id : null
                })
                                  .ToList();

                var status = new JudgeStatus
                {
                    Code                   = request.code,
                    Language               = request.language,
                    Result                 = JudgeResult.Pending,
                    CreatedTime            = DateTime.UtcNow,
                    ContestId              = request.contestId,
                    SubStatuses            = substatuses,
                    ProblemId              = problem.Id,
                    UserId                 = User.Current.Id,
                    IsSelfTest             = request.isSelfTest,
                    RelatedStateMachineIds = new List <JudgeStatusStateMachine>
                    {
                        new JudgeStatusStateMachine
                        {
                            StateMachine = new StateMachine
                            {
                                CreatedTime = DateTime.UtcNow,
                                Name        = "JudgeStateMachine",
                                Id          = stateMachineId
                            },
                            StateMachineId = stateMachineId
                        }
                    },
                };

                if (IsGroupRequest())
                {
                    status.GroupId = CurrentGroup.Id;
                }

                DB.JudgeStatuses.Add(status);
                await DB.SaveChangesAsync(token);

                hub.Clients.All.InvokeAsync("ItemUpdated", "judge", status.Id);

                // For debugging
                if (Config["ManagementService:Mode"] == "Polling")
                {
                    Task.Factory.StartNew(async() =>
                    {
                        using (var scope = scopeFactory.CreateScope())
                            using (var db = scope.ServiceProvider.GetService <OnlineJudgeContext>())
                            {
                                try
                                {
                                    await awaiter.GetStateMachineResultAsync(stateMachineId);
                                    var handler = scope.ServiceProvider.GetService <JudgeStateMachineHandler>();
                                    await handler.HandleJudgeResultAsync(stateMachineId, default(CancellationToken));
                                }
                                catch (Exception ex)
                                {
                                    Console.Error.WriteLine(ex);
                                }
                            }
                    });
                }
                return(Result(status.Id));
            }
            #endregion
            #region Bzoj, LeetCode, CodeVS
            else if (problem.Source == ProblemSource.Bzoj || problem.Source == ProblemSource.LeetCode || problem.Source == ProblemSource.CodeVS)
            {
                var metadata = new
                {
                    Source    = problem.Source.ToString(),
                    Language  = request.language,
                    Code      = request.code,
                    ProblemId = problem.Id.Replace(problem.Source.ToString().ToLower() + "-", "")
                };

                var metadataBlob = new BlobInfo
                {
                    Id   = await MgmtSvc.PutBlobAsync("metadata.json", Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(metadata)), token),
                    Name = "metadata.json",
                    Tag  = "Problem=" + problem.Id
                };

                var stateMachineId = await MgmtSvc.PutStateMachineInstanceAsync("VirtualJudgeStateMachine", Config["ManagementService:Callback"], new[] { metadataBlob }, await CalculatePriorityAsync(), token);

                var status = new JudgeStatus
                {
                    Code                   = request.code,
                    ContestId              = request.contestId,
                    Language               = request.language,
                    ProblemId              = problem.Id,
                    IsSelfTest             = false,
                    UserId                 = User.Current.Id,
                    Result                 = JudgeResult.Pending,
                    RelatedStateMachineIds = new List <JudgeStatusStateMachine>
                    {
                        new JudgeStatusStateMachine {
                            StateMachine = new StateMachine
                            {
                                CreatedTime = DateTime.UtcNow,
                                Name        = "JudgeStateMachine",
                                Id          = stateMachineId
                            },
                            StateMachineId = stateMachineId
                        }
                    }
                };

                if (IsGroupRequest())
                {
                    status.GroupId = CurrentGroup.Id;
                }

                DB.JudgeStatuses.Add(status);
                await DB.SaveChangesAsync(token);

                // For debugging
                if (Config["ManagementService:Mode"] == "Polling")
                {
                    Task.Factory.StartNew(async() =>
                    {
                        using (var scope = scopeFactory.CreateScope())
                        {
                            try
                            {
                                await awaiter.GetStateMachineResultAsync(stateMachineId);
                                var handler = scope.ServiceProvider.GetService <JudgeStateMachineHandler>();
                                await handler.HandleJudgeResultAsync(stateMachineId, default(CancellationToken));
                            }
                            catch (Exception ex)
                            {
                                Console.Error.WriteLine(ex);
                            }
                        }
                    });
                }

                hub.Clients.All.InvokeAsync("ItemUpdated", "judge", status.Id);

                return(Result(status.Id));
            }
            #endregion
            #region Others
            else
            {
                throw new NotSupportedException(problem.Source.ToString() + " has not been supported yet.");
            }
            #endregion
        }
예제 #2
0
        public async Task <IActionResult> Patch(
            string id,
            [FromServices] ManagementServiceClient MgmtSvc,
            [FromServices] StateMachineAwaiter Awaiter,
            [FromServices] IHubContext <OnlineJudgeHub> hub,
            CancellationToken token)
        {
            if (!await HasPermissionToProblemAsync(id, token))
            {
                return(Result(401, "No Permission"));
            }
            else
            {
                var problem = await DB.Problems.SingleOrDefaultAsync(x => x.Id == id, token);

                if (problem == null)
                {
                    return(Result(404, "Not Found"));
                }

                var fields = PatchEntity(problem, RequestBody);

                // Check difficulty range
                if (fields.Any(x => x == nameof(Problem.Difficulty)) && (problem.Difficulty < 0 || problem.Difficulty > 9))
                {
                    return(Result(400, "The difficulty must be 0-9."));
                }

                // Update validator
                if (fields.Any(x => x == nameof(Problem.ValidatorCode)) || fields.Any(x => x == nameof(Problem.ValidatorLanguage)))
                {
                    if (string.IsNullOrWhiteSpace(problem.ValidatorCode))
                    {
                        problem.ValidatorBlobId   = null;
                        problem.ValidatorCode     = null;
                        problem.ValidatorError    = null;
                        problem.ValidatorLanguage = null;
                    }
                    else
                    {
                        var validatorCodeId = await MgmtSvc.PutBlobAsync("validator-" + problem.Id, Encoding.UTF8.GetBytes(problem.ValidatorCode), token);

                        var stateMachineId = await MgmtSvc.PutStateMachineInstanceAsync("CompileOnlyStateMachine", Configuration["ManagementService:CallBack"], new BlobInfo[] { new BlobInfo(validatorCodeId, "Main" + Constants.GetSourceExtension(problem.ValidatorLanguage)) });

                        var result = await Awaiter.GetStateMachineResultAsync(stateMachineId, true, token);

                        var runner = JsonConvert.DeserializeObject <dynamic>(Encoding.UTF8.GetString((await MgmtSvc.GetBlobAsync(result.StartedActors.Last().Outputs.Single(x => x.Name == "runner.json").Id, token)).Body));
                        if (result.StartedActors.Any(x => x.Name == "CompileActor" && runner.ExitCode == 0 && x.Status == JoyOI.ManagementService.Model.Enums.ActorStatus.Succeeded))
                        {
                            problem.ValidatorBlobId = result.StartedActors.Last().Outputs.Single(x => x.Name.StartsWith("Main.")).Id;
                            problem.ValidatorError  = null;
                        }
                        else
                        {
                            problem.ValidatorBlobId = null;
                            problem.ValidatorError  = runner.Error;
                        }
                    }
                }

                // Update standard
                if (fields.Any(x => x == nameof(Problem.StandardCode)) || fields.Any(x => x == nameof(Problem.StandardLanguage)))
                {
                    if (string.IsNullOrWhiteSpace(problem.StandardCode))
                    {
                        problem.StandardBlobId   = null;
                        problem.StandardCode     = null;
                        problem.StandardError    = null;
                        problem.StandardLanguage = null;
                    }
                    else
                    {
                        var standardCodeId = await MgmtSvc.PutBlobAsync("standard-" + problem.Id, Encoding.UTF8.GetBytes(problem.StandardCode), token);

                        var stateMachineId = await MgmtSvc.PutStateMachineInstanceAsync("CompileOnlyStateMachine", Configuration["ManagementService:CallBack"], new BlobInfo[] { new BlobInfo(standardCodeId, "Main" + Constants.GetSourceExtension(problem.StandardLanguage)) });

                        var result = await Awaiter.GetStateMachineResultAsync(stateMachineId, true, token);

                        var runner = JsonConvert.DeserializeObject <dynamic>(Encoding.UTF8.GetString((await MgmtSvc.GetBlobAsync(result.StartedActors.Last().Outputs.Single(x => x.Name == "runner.json").Id, token)).Body));
                        if (result.StartedActors.Any(x => x.Name == "CompileActor" && runner.ExitCode == 0 && x.Status == JoyOI.ManagementService.Model.Enums.ActorStatus.Succeeded))
                        {
                            problem.StandardBlobId = result.StartedActors.Last().Outputs.Single(x => x.Name.StartsWith("Main.")).Id;
                            problem.StandardError  = null;
                        }
                        else
                        {
                            problem.StandardBlobId = null;
                            problem.StandardError  = runner.Error;
                        }
                    }
                }

                // Update range
                if (fields.Any(x => x == nameof(Problem.RangeCode)) || fields.Any(x => x == nameof(Problem.RangeLanguage)))
                {
                    if (string.IsNullOrWhiteSpace(problem.RangeCode))
                    {
                        problem.RangeBlobId   = null;
                        problem.RangeCode     = null;
                        problem.RangeError    = null;
                        problem.RangeLanguage = null;
                    }
                    else
                    {
                        var rangeCodeId = await MgmtSvc.PutBlobAsync("range-" + problem.Id, Encoding.UTF8.GetBytes(problem.RangeCode), token);

                        var stateMachineId = await MgmtSvc.PutStateMachineInstanceAsync("CompileOnlyStateMachine", Configuration["ManagementService:CallBack"], new BlobInfo[] { new BlobInfo(rangeCodeId, "Main" + Constants.GetSourceExtension(problem.RangeLanguage)) });

                        var result = await Awaiter.GetStateMachineResultAsync(stateMachineId, true, token);

                        var runner = JsonConvert.DeserializeObject <dynamic>(Encoding.UTF8.GetString((await MgmtSvc.GetBlobAsync(result.StartedActors.Last().Outputs.Single(x => x.Name == "runner.json").Id, token)).Body));
                        if (result.StartedActors.Any(x => x.Name == "CompileActor" && runner.ExitCode == 0 && x.Status == JoyOI.ManagementService.Model.Enums.ActorStatus.Succeeded))
                        {
                            problem.RangeBlobId = result.StartedActors.Last().Outputs.Single(x => x.Name.StartsWith("Main.")).Id;
                            problem.RangeError  = null;
                        }
                        else
                        {
                            problem.RangeBlobId = null;
                            problem.RangeError  = runner.Error;
                        }
                    }
                }

                if ((string.IsNullOrWhiteSpace(problem.Tags) || problem.Tags.IndexOf(LocalProblemSetTag) < 0) && problem.Source == ProblemSource.Local)
                {
                    problem.Tags += "," + LocalProblemSetTag;
                    problem.Tags  = problem.Tags.Trim(',');
                }

                await DB.SaveChangesAsync(token);

                hub.Clients.All.InvokeAsync("ItemUpdated", "problem", problem.Id);
                if (fields.Any(x => x == nameof(Problem.Title)) || fields.Any(x => x == nameof(Problem.IsVisible)))
                {
                    hub.Clients.All.InvokeAsync("ItemUpdated", "problem-list", problem.Id);
                }

                return(Result(200, "Patch Succeeded"));
            }
        }