/// <summary>
 /// Verifies that the given generated question template was
 /// correctly duplicated.
 /// </summary>
 private void VerifyGeneratedQuestionTemplate(
     DatabaseContext dbContext,
     GeneratedQuestionTemplate oldQuestion,
     GeneratedQuestionTemplate newQuestion)
 {
     // No navigation properties.
 }
Example #2
0
        /// <summary>
        /// Creates a class job that will generate the question, and return the result
        /// in the form of a json string.
        /// </summary>
        private ClassJob CreateQuestionGeneratorClassJob(
            GeneratedQuestionTemplate question,
            int seed)
        {
            var classesToImport = GetClassesToImport(question);
            int lineOffset      = question.FullGeneratorFileLineOffset;

            return(new ClassJob()
            {
                ClassesToImport = classesToImport,
                ClassName = "QuestionGenerator",
                FileContents = !string.IsNullOrEmpty(question.FullGeneratorFileContents)
                                        ? question.FullGeneratorFileContents
                                        : GetGeneratorFile(question.GeneratorContents, out lineOffset),
                LineNumberOffset = lineOffset,
                Tests = new List <ClassTest>()
                {
                    new ClassTest()
                    {
                        TestName = "QuestionGenerator",
                        MethodBody = string.Join(
                            "\n",
                            new List <string>()
                        {
                            "ObjectMapper mapper = new ObjectMapper();",
                            $"return mapper.writeValueAsString(QuestionGenerator.generateQuestion({seed}));"
                        }),
                        ReturnType = "String"
                    }
                }
            });
        }
 /// <summary>
 /// Creates a question loader for a generated question.
 /// </summary>
 IQuestionResolver IQuestionResultVisitor <IQuestionResolver, UserQuestionData> .Visit(
     GeneratedQuestionTemplate question,
     UserQuestionData userQuestionData)
 {
     return(new GeneratedQuestionTemplateResolver
            (
                userQuestionData,
                _jsonSerializer
            ));
 }
        /// <summary>
        /// Returns a mock question generator.
        /// </summary>
        private Mock <IQuestionGenerator> GetMockQuestionGenerator(
            GeneratedQuestionTemplate question,
            QuestionGenerationResult generationResult = null)
        {
            var questionGenerator = new Mock <IQuestionGenerator>();

            questionGenerator
            .Setup(qg => qg.GenerateQuestionAsync(question, 0 /*seed*/))
            .ReturnsAsync(generationResult);

            return(questionGenerator);
        }
 /// <summary>
 /// Creates a question updater for a generated question.
 /// </summary>
 IQuestionUpdater IQuestionResultVisitor <IQuestionUpdater, IModelErrorCollection> .Visit(
     GeneratedQuestionTemplate question,
     IModelErrorCollection errors)
 {
     return(new GeneratedQuestionUpdater
            (
                _dbContext,
                question,
                errors,
                _questionGenerator,
                _timeProvider
            ));
 }
Example #6
0
 /// <summary>
 /// Returns a list of classes to import.
 /// </summary>
 private List <string> GetClassesToImport(GeneratedQuestionTemplate question)
 {
     return(new List <string>()
     {
         "com.fasterxml.jackson.databind.ObjectMapper",
         "com.fasterxml.jackson.databind.JsonMappingException"
     }.Concat
            (
                question.ImportedClasses?.Select
                (
                    importedClass => importedClass.ClassName
                ) ?? Enumerable.Empty <string>()
            ).ToList());
 }
		/// <summary>
		/// Generates a specific question given a generated question and a seed.
		/// </summary>
		public async Task<QuestionGenerationResult> GenerateQuestionAsync(
			GeneratedQuestionTemplate question, 
			int seed)
		{
			var classJob = CreateQuestionGeneratorClassJob(question, seed);

			var classJobResult = await _codeRunnerService.ExecuteClassJobAsync(classJob);

			if (!classJobResult.ClassCompilationResult.Success)
			{
				return new QuestionGenerationResult
				(
					error: "Failed to compile question generator. Compilation errors: \n"
						+ string.Join(
							"\n",
							classJobResult.ClassCompilationResult.Errors.Select(error => error.FullError))
				);
			}

			if (!classJobResult.ClassDefinition.Methods.Any(
					m =>   m.IsStatic
						&& m.IsPublic
						&& m.ReturnType.EndsWith("Question")
						&& m.Name == "generateQuestion"
						&& m.ParameterTypes.Count == 1
						&& m.ParameterTypes.Single() == "int"))
			{
				return new QuestionGenerationResult
				(
					error: "The QuestionGenerator class must have a method with the following signature: \n"
						+ "public static Question generateQuestion(int seed)" 
				);
			}

			if (!classJobResult.TestsCompilationResult.Success)
			{
				return new QuestionGenerationResult
				(
					error: "Failed to compile question generator. Compilation errors: \n"
						+ string.Join(
							"\n",
							classJobResult.TestsCompilationResult.Errors.Select(error => error.FullError))
				);
			}

			if (!classJobResult.TestResults[0].Completed)
			{
				return new QuestionGenerationResult
				(
					error: "An exception was encountered when calling QuestionGenerator.generateQuestion: \n"
						+ classJobResult.TestResults[0].Exception
				);
			}

			return new QuestionGenerationResult
			(
				classJobResult.TestResults[0].ReturnValue,
				fullGeneratorFileContents: classJob.FileContents,
				fullGeneratorFileLineOffset: classJob.LineNumberOffset
			);
		}
		/// <summary>
		/// Returns a list of classes to import.
		/// </summary>
		private List<string> GetClassesToImport(GeneratedQuestionTemplate question)
		{
			return new List<string>()
			{
				"com.fasterxml.jackson.databind.ObjectMapper",
				"com.fasterxml.jackson.databind.JsonMappingException"
			}.Concat
			(
				question.ImportedClasses?.Select
				(
					importedClass => importedClass.ClassName
				) ?? Enumerable.Empty<string>()
			).ToList();
		}
		/// <summary>
		/// Creates a class job that will generate the question, and return the result
		/// in the form of a json string.
		/// </summary>
		private ClassJob CreateQuestionGeneratorClassJob(
			GeneratedQuestionTemplate question,
			int seed)
		{
			var classesToImport = GetClassesToImport(question);
			int lineOffset = question.FullGeneratorFileLineOffset;

			return new ClassJob()
			{
				ClassesToImport = classesToImport,
				ClassName = "QuestionGenerator",
				FileContents = !string.IsNullOrEmpty(question.FullGeneratorFileContents)
					? question.FullGeneratorFileContents
					: GetGeneratorFile(classesToImport.Count, question.GeneratorContents, out lineOffset),
				LineNumberOffset = lineOffset,
				Tests = new List<ClassTest>()
				{
					new ClassTest()
					{
						TestName = "QuestionGenerator",
						MethodBody = string.Join(
							"\n",
							new List<string>()
							{
								"ObjectMapper mapper = new ObjectMapper();",
								$"try",
								"{{",
								$"\treturn mapper.writeValueAsString(QuestionGenerator.generateQuestion({seed}));",
								"}}",
								"catch (Exception ex)",
								"{{",
								"\tthrow new RuntimeException(ex);",
								"}}"
							}),
						ReturnType = "String"
					}
				}
			};
		}
Example #10
0
 /// <summary>
 /// Creates a question loader for a generated question.
 /// </summary>
 IQuestionLoader IQuestionResultVisitor <IQuestionLoader> .Visit(GeneratedQuestionTemplate question)
 {
     return(new GeneratedQuestionLoader(_dbContext, question));
 }
		/// <summary>
		/// Regenerates the question.
		/// </summary>
		private async Task RegenerateQuestionAsync(
			GeneratedQuestionTemplate generatedQuestionTemplate,
			int seed,
			UserQuestionData userQuestionData)
		{
			var result = await _questionGenerator.GenerateQuestionAsync
			(
				generatedQuestionTemplate,
				seed
			);

			userQuestionData.Seed = seed;
			userQuestionData.CachedQuestionData = result.SerializedQuestion;
			userQuestionData.CachedQuestionDataTime = _timeProvider.UtcNow;
		}
		/// <summary>
		/// Returns a mock question generator.
		/// </summary>
		private Mock<IQuestionGenerator> GetMockQuestionGenerator(
			GeneratedQuestionTemplate question,
			QuestionGenerationResult generationResult)
		{
			var questionGenerator = new Mock<IQuestionGenerator>();

			questionGenerator
				.Setup(qg => qg.GenerateQuestionAsync(question, 0 /*seed*/))
				.ReturnsAsync(generationResult);

			return questionGenerator;
		}
Example #13
0
 /// <summary>
 /// Creates a question loader for a generated question.
 /// </summary>
 IUserQuestionDataUpdater IQuestionResultVisitor <IUserQuestionDataUpdater> .Visit(
     GeneratedQuestionTemplate question)
 {
     return(_generatedUserQuestionDataUpdater);
 }
Example #14
0
 /// <summary>
 /// Creates a question duplicator for a generated question.
 /// </summary>
 IQuestionDuplicator IQuestionResultVisitor <IQuestionDuplicator> .Visit(GeneratedQuestionTemplate question)
 {
     return(new GeneratedQuestionDuplicator(_dbContext, question));
 }
Example #15
0
        /// <summary>
        /// Generates a specific question given a generated question and a seed.
        /// </summary>
        public async Task <QuestionGenerationResult> GenerateQuestionAsync(
            GeneratedQuestionTemplate question,
            int seed)
        {
            var classJob = CreateQuestionGeneratorClassJob(question, seed);

            var classJobResult = await _codeRunnerService.ExecuteClassJobAsync(classJob);

            if (!classJobResult.ClassCompilationResult.Success)
            {
                return(new QuestionGenerationResult
                       (
                           error: "Failed to compile question generator. Compilation errors: \n"
                           + string.Join(
                               "\n",
                               classJobResult.ClassCompilationResult.Errors.Select(error => error.FullError))
                       ));
            }

            if (!classJobResult.ClassDefinition.Methods.Any(
                    m => m.IsStatic &&
                    m.IsPublic &&
                    m.ReturnType.EndsWith("Question") &&
                    m.Name == "generateQuestion" &&
                    m.ParameterTypes.Count == 1 &&
                    m.ParameterTypes.Single() == "int"))
            {
                return(new QuestionGenerationResult
                       (
                           error: "The QuestionGenerator class must have a method with the following signature: \n"
                           + "public static Question generateQuestion(int seed)"
                       ));
            }

            if (!classJobResult.TestsCompilationResult.Success)
            {
                return(new QuestionGenerationResult
                       (
                           error: "Failed to compile question generator. Compilation errors: \n"
                           + string.Join(
                               "\n",
                               classJobResult.TestsCompilationResult.Errors.Select(error => error.FullError))
                       ));
            }

            if (!classJobResult.TestResults[0].Completed)
            {
                return(new QuestionGenerationResult
                       (
                           error: "An exception was encountered when calling QuestionGenerator.generateQuestion: \n"
                           + classJobResult.TestResults[0].Exception
                       ));
            }

            return(new QuestionGenerationResult
                   (
                       classJobResult.TestResults[0].ReturnValue,
                       classJob.FileContents,
                       classJob.LineNumberOffset,
                       seed
                   ));
        }
Example #16
0
 /// <summary>
 /// Creates a question loader for a generated question.
 /// </summary>
 IQuestionGrader IQuestionResultVisitor <IQuestionGrader> .Visit(GeneratedQuestionTemplate question)
 {
     throw new InvalidOperationException("Generated questions cannot be graded directly. Instead, grade the question with a particular seed.");
 }