diff --git a/.gitignore b/.gitignore index 4355b4a..9417d65 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,9 @@ # IDE /.idea /.vs -/*.user +*.user +*.csproj + # Build results [Dd]ebug/ @@ -12,3 +14,5 @@ /GraphLabs.Backend.Api/db /GraphLabs.Backend.Api/wwwroot/modules /GraphLabs.Backend.Api/logs +/GraphLabs.Backend.Api/Properties/launchSettings.json + diff --git a/GraphLabs.Backend.Api/Controllers/SubjectsController.cs b/GraphLabs.Backend.Api/Controllers/SubjectsController.cs new file mode 100644 index 0000000..6ba9212 --- /dev/null +++ b/GraphLabs.Backend.Api/Controllers/SubjectsController.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using GraphLabs.Backend.DAL; +using GraphLabs.Backend.Domain; +using Microsoft.AspNet.OData; +using Microsoft.AspNet.OData.Routing; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Newtonsoft.Json.Linq; + +namespace GraphLabs.Backend.Api.Controllers +{ + [ODataRoutePrefix("subjects")] + public class SubjectsController : ODataController + { + private readonly GraphLabsContext _db; + + public SubjectsController(GraphLabsContext context) + { + _db = context; + } + + [EnableQuery] + public IQueryable Get() + { + return _db.Subjects; + } + + [ODataRoute("({key})")] + [EnableQuery] + public SingleResult Get(long key) + { + return SingleResult.Create(_db.Subjects.Where(t => t.Id == key)); + } + + [HttpPost] + public async Task Post([FromBody]CreateRequest request) + { + if (request == null || + string.IsNullOrEmpty(request.Name) || + string.IsNullOrEmpty(request.Description)) + return BadRequest(); + + var subject = new Subject + { + Name = request.Name, + Description = request.Description + }; + + _db.Subjects.Add(subject); + await _db.SaveChangesAsync(); + + return Ok(subject.Id); + } + + public class CreateRequest + { + public string Name { get; set; } + + public string Description { get; set; } + } + } +} \ No newline at end of file diff --git a/GraphLabs.Backend.Api/Controllers/TestAnswersController.cs b/GraphLabs.Backend.Api/Controllers/TestAnswersController.cs new file mode 100644 index 0000000..f30d72d --- /dev/null +++ b/GraphLabs.Backend.Api/Controllers/TestAnswersController.cs @@ -0,0 +1,92 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using GraphLabs.Backend.DAL; +using GraphLabs.Backend.Domain; +using Microsoft.AspNet.OData; +using Microsoft.AspNet.OData.Routing; +using Microsoft.AspNetCore.Mvc; + +namespace GraphLabs.Backend.Api.Controllers +{ + [ODataRoutePrefix("testAnswers")] + public class TestAnswersController : ODataController + { + private readonly GraphLabsContext _db; + + public TestAnswersController(GraphLabsContext context) + { + _db = context; + } + + [EnableQuery] + public IQueryable Get() + { + return _db.TestAnswers; + } + + [EnableQuery] + [ODataRoute("({key})")] + public SingleResult Get(long key) + { + return SingleResult.Create(_db.TestAnswers.Where(t => t.Id == key)); + } + + [HttpPost] + [ODataRoute("({key})")] + public async Task Post([FromBody]CreateRequest request, long key) + { + if (request == null || string.IsNullOrEmpty(request.Answer)) + return BadRequest(); + + var testQuestionVersion = _db.TestQuestionVersions.Single(v => v.Id == key); + if (testQuestionVersion == null) + return BadRequest(); + + var testAnswer = new TestAnswer + { + Answer = request.Answer, + IsRight = request.IsRight, + TestQuestionVersion = testQuestionVersion + }; + + if (testQuestionVersion.TestAnswers == null) + { + testQuestionVersion.TestAnswers = new List { testAnswer }; + } + else + { + testQuestionVersion.TestAnswers.Add(testAnswer); + } + + await _db.SaveChangesAsync(); + return Ok(testAnswer.Id); + } + + [HttpDelete] + [ODataRoute("({key})")] + public async Task Delete(long key) + { + var answer = _db.TestAnswers.Single(v => v.Id == key); + if (answer != null) + { + _db.TestAnswers.Remove(answer); + await _db.SaveChangesAsync(); + } + else + { + return NotFound(); + } + + return Ok(); + } + + public class CreateRequest + { + public string Answer { get; set; } + + public bool IsRight { get; set; } + } + } +} \ No newline at end of file diff --git a/GraphLabs.Backend.Api/Controllers/TestQuestionVersionsController.cs b/GraphLabs.Backend.Api/Controllers/TestQuestionVersionsController.cs new file mode 100644 index 0000000..fbeea55 --- /dev/null +++ b/GraphLabs.Backend.Api/Controllers/TestQuestionVersionsController.cs @@ -0,0 +1,86 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Threading.Tasks; +using GraphLabs.Backend.DAL; +using GraphLabs.Backend.Domain; +using Microsoft.AspNet.OData; +using Microsoft.AspNet.OData.Routing; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using Newtonsoft.Json.Linq; + +namespace GraphLabs.Backend.Api.Controllers +{ + [ODataRoutePrefix("testQuestionVersions")] + public class TestQuestionVersionsController : ODataController + { + private readonly GraphLabsContext _db; + + public TestQuestionVersionsController(GraphLabsContext context) + { + _db = context; + } + + [EnableQuery] + public IQueryable Get() + { + return _db.TestQuestionVersions; + } + + [EnableQuery] + [ODataRoute("({key})")] + public SingleResult Get(long key) + { + return SingleResult.Create(_db.TestQuestionVersions.Where(t => t.Id == key)); + } + + + //ODataRoute("createVariant") not working + [HttpPost] + public async Task Post([FromBody]CreateRequest[] request) + { + if (request == null) + return BadRequest(); + + var variant = new Collection(); + + foreach (var r in request) + { + if (r.SubjectId == 0 || r.Quantity == 0) + return BadRequest(); + + var subjectId = r.SubjectId; + var quantity = r.Quantity; + + var question = _db.TestQuestions.Where(w => w.Subject.Id == subjectId); + var questionLength = question.Count(); + + if (quantity > questionLength) + quantity = questionLength; + if (questionLength == 0) + return new NotFoundResult(); + + var questionArray = question.Select(s => s.Id).ToArray(); + for (var i = 0; i w != questionArrayRandomId).ToArray(); + var questionVersion = _db.TestQuestionVersions.Last(p => p.TestQuestion.Id == questionArrayRandomId); + variant.Add(questionVersion); + } + } + + return Ok(variant); + } + + public class CreateRequest + { + public long SubjectId { get; set; } + + public int Quantity { get; set; } + } + + } +} \ No newline at end of file diff --git a/GraphLabs.Backend.Api/Controllers/TestQuestionsController.cs b/GraphLabs.Backend.Api/Controllers/TestQuestionsController.cs new file mode 100644 index 0000000..118e940 --- /dev/null +++ b/GraphLabs.Backend.Api/Controllers/TestQuestionsController.cs @@ -0,0 +1,119 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Threading.Tasks; +using GraphLabs.Backend.DAL; +using GraphLabs.Backend.Domain; +using Microsoft.AspNet.OData; +using Microsoft.AspNet.OData.Routing; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using Newtonsoft.Json.Linq; + +namespace GraphLabs.Backend.Api.Controllers +{ + [ODataRoutePrefix("testQuestions")] + public class TestQuestionsController : ODataController + { + private readonly GraphLabsContext _db; + + public TestQuestionsController(GraphLabsContext context) + { + _db = context; + } + + [EnableQuery] + public IQueryable Get() + { + return _db.TestQuestions; + } + + [EnableQuery] + [ODataRoute("({key})")] + public SingleResult Get(long key) + { + return SingleResult.Create(_db.TestQuestions.Where(t => t.Id == key)); + } + + [HttpPost] + [ODataRoute("({key})")] + public async Task Post([FromBody]CreateRequest request, long key) + { + if (request == null || + request.MaxScore == 0 || + string.IsNullOrEmpty(request.PlainText) || + string.IsNullOrEmpty(request.Difficulty)) + return BadRequest(); + + TestQuestion testQuestion; + var testQuestionVersion = new TestQuestionVersion + { + PlainText = request.PlainText, + Difficulty = GetDifficulty(request.Difficulty), + MaxScore = request.MaxScore + }; + + if (key == 0) + { + //Create TestQuestion and TestQuestion.TestQuestionVersion + var subject = _db.Subjects.Single(s => s.Id == request.SubjectId); + if (subject == null) + return BadRequest(); + + testQuestion = new TestQuestion + { + TestQuestionVersions = new List { testQuestionVersion }, + Subject = subject + }; + + if (subject.TestQuestions == null) + { + subject.TestQuestions = new List { testQuestion }; + } + else + { + subject.TestQuestions.Add(testQuestion); + } + _db.TestQuestions.Add(testQuestion); + } + else + { + //Update TestQuestion.TestQuestionVersion + testQuestion = _db.TestQuestions.Include(q => q.TestQuestionVersions).Single(q => q.Id == key); + testQuestion.TestQuestionVersions.Add(testQuestionVersion); + } + + testQuestionVersion.TestQuestion = testQuestion; + _db.TestQuestionVersions.Add(testQuestionVersion); + + await _db.SaveChangesAsync(); + return Ok(testQuestionVersion.Id); + } + + public class CreateRequest + { + public string PlainText { get; set; } + + public string Difficulty { get; set; } + + public int MaxScore { get; set; } + + public long? SubjectId { get; set; } + } + + private Difficulty GetDifficulty(string diff) + { + switch (diff) + { + case "Three": + return Difficulty.Three; + case "Four": + return Difficulty.Four; + case "Five": + return Difficulty.Five; + } + return Difficulty.Three; + } + } +} \ No newline at end of file diff --git a/GraphLabs.Backend.Api/Controllers/TestResultsController.cs b/GraphLabs.Backend.Api/Controllers/TestResultsController.cs new file mode 100644 index 0000000..027761e --- /dev/null +++ b/GraphLabs.Backend.Api/Controllers/TestResultsController.cs @@ -0,0 +1,150 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Threading.Tasks; +using GraphLabs.Backend.DAL; +using GraphLabs.Backend.Domain; +using Microsoft.AspNet.OData; +using Microsoft.AspNet.OData.Routing; +using Microsoft.AspNetCore.Mvc; + +namespace GraphLabs.Backend.Api.Controllers +{ + [ODataRoutePrefix("testResults")] + public class TestResultsController : ODataController + { + private readonly GraphLabsContext _db; + + public TestResultsController(GraphLabsContext context) + { + _db = context; + } + + [EnableQuery] + public IQueryable Get() + { + return _db.TestResults; + } + + [ODataRoute("({key})")] + [EnableQuery] + public SingleResult Get(long key) + { + return SingleResult.Create(_db.TestResults.Where(t => t.Id == key)); + } + + [HttpPost] + public async Task Post([FromBody]CreateRequest[] request) + { + if (request == null) + return BadRequest(); + + var testResult = new TestResult(); + float fullScore = 0; + float studentScore = 0; + var collectionStudentAnswers = new Collection(); + foreach (var r in request) + { + if (r == null || + r.AnswerId == 0 || + r.StudentId == 0 || + r.TestQuestionVersionId == 0 || + string.IsNullOrEmpty(r.Answer)) + { + return BadRequest(); + } + + var testQuestionVersion = _db.TestQuestionVersions.Single(v => v.Id == r.TestQuestionVersionId); + var testStudentAnswer = new TestStudentAnswer + { + AnswerId = r.AnswerId, + Answer = r.Answer, + IsRight = r.IsRight, + TestQuestionVersion = testQuestionVersion, + TestResult = testResult + }; + var testAnswer = _db.TestAnswers.Single(p => p.Id == r.AnswerId); + float maxScoreForTask = (float) testStudentAnswer.TestQuestionVersion.MaxScore; + float maxScoreForOneQuestion = (float) (maxScoreForTask / _db.TestAnswers.Count(t => t.TestQuestionVersion.Id == testStudentAnswer.TestQuestionVersion.Id)); + if (testStudentAnswer.Answer == testAnswer.Answer && testStudentAnswer.IsRight == testAnswer.IsRight) + studentScore += maxScoreForOneQuestion; + fullScore += maxScoreForOneQuestion; + + if (testQuestionVersion.TestStudentAnswers == null) + { + testQuestionVersion.TestStudentAnswers = new List { testStudentAnswer }; + } + else + { + testQuestionVersion.TestStudentAnswers.Add(testStudentAnswer); + } + collectionStudentAnswers.Add(testStudentAnswer); + } + + var student = _db.Students.Single(s => s.Id == request[0].StudentId); + int score = (int)((100 * studentScore) / fullScore); + testResult.Score = score; + testResult.DateTime = DateTime.Now; + testResult.MarkEU = GetMarkEU(score); + testResult.MarkRU = GetMarkRU(score); + testResult.TestStudentAnswer = collectionStudentAnswers; + testResult.Student = student; + + if (student.TestResults == null) + { + student.TestResults = new List { testResult }; + } + else + { + student.TestResults.Add(testResult); + } + + //await _db.SaveChangesAsync(); + return Created(testResult.Id); + } + + public class CreateRequest + { + public long TestQuestionVersionId { get; set; } + + public string Answer { get; set; } + + public long AnswerId { get; set; } + + public bool IsRight { get; set; } + + public long StudentId { get; set; } + } + + private MarkRU GetMarkRU(int score) + { + if (score >= 0 && score <= 59) + return MarkRU.Two; + if (score >= 60 && score <= 69) + return MarkRU.Three; + if (score >= 70 && score <= 89) + return MarkRU.Four; + if (score >= 90 && score <= 100) + return MarkRU.Five; + return MarkRU.Two; + } + + private MarkEU GetMarkEU(int score) + { + if (score >= 0 && score <= 59) + return MarkEU.F; + if (score >= 60 && score <= 64) + return MarkEU.E; + if (score >= 65 && score <= 74) + return MarkEU.D; + if (score >= 75 && score <= 84) + return MarkEU.C; + if (score >= 85 && score <= 89) + return MarkEU.B; + if (score >= 90 && score <= 100) + return MarkEU.A; + return MarkEU.F; + } + } +} \ No newline at end of file diff --git a/GraphLabs.Backend.Api/Controllers/TestStudentAnswersController.cs b/GraphLabs.Backend.Api/Controllers/TestStudentAnswersController.cs new file mode 100644 index 0000000..fb388bb --- /dev/null +++ b/GraphLabs.Backend.Api/Controllers/TestStudentAnswersController.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using GraphLabs.Backend.DAL; +using GraphLabs.Backend.Domain; +using Microsoft.AspNet.OData; +using Microsoft.AspNet.OData.Routing; +using Microsoft.AspNetCore.Mvc; +using Newtonsoft.Json.Linq; + +namespace GraphLabs.Backend.Api.Controllers +{ + [ODataRoutePrefix("testStudentAnswers")] + public class TestStudentAnswersController : ODataController + { + private readonly GraphLabsContext _db; + + public TestStudentAnswersController(GraphLabsContext context) + { + _db = context; + } + + [EnableQuery] + public IQueryable Get() + { + return _db.TestStudentAnswers; + } + + [ODataRoute("({key})")] + [EnableQuery] + public SingleResult Get(long key) + { + return SingleResult.Create(_db.TestStudentAnswers.Where(t => t.Id == key)); + } + } +} \ No newline at end of file diff --git a/GraphLabs.Backend.Api/GraphLabs.Backend.Api.csproj b/GraphLabs.Backend.Api/GraphLabs.Backend.Api.csproj index 937f4b5..f339373 100644 --- a/GraphLabs.Backend.Api/GraphLabs.Backend.Api.csproj +++ b/GraphLabs.Backend.Api/GraphLabs.Backend.Api.csproj @@ -23,6 +23,7 @@ 2.0.3 + 12.0.1 diff --git a/GraphLabs.Backend.Api/Migrations/20200423115639_TestQuestions.Designer.cs b/GraphLabs.Backend.Api/Migrations/20200423115639_TestQuestions.Designer.cs new file mode 100644 index 0000000..d4b4edb --- /dev/null +++ b/GraphLabs.Backend.Api/Migrations/20200423115639_TestQuestions.Designer.cs @@ -0,0 +1,323 @@ +// +using System; +using GraphLabs.Backend.DAL; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +namespace GraphLabs.Backend.Api.Migrations +{ + [DbContext(typeof(GraphLabsContext))] + [Migration("20200423115639_TestQuestions")] + partial class TestQuestions + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn) + .HasAnnotation("ProductVersion", "2.2.0-rtm-35687") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + modelBuilder.Entity("GraphLabs.Backend.Domain.Subject", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Description"); + + b.Property("Name"); + + b.HasKey("Id"); + + b.ToTable("Subject"); + }); + + modelBuilder.Entity("GraphLabs.Backend.Domain.TaskModule", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Description"); + + b.Property("Name"); + + b.Property("SubjectId"); + + b.Property("Version"); + + b.HasKey("Id"); + + b.HasIndex("SubjectId"); + + b.ToTable("TaskModules"); + }); + + modelBuilder.Entity("GraphLabs.Backend.Domain.TaskVariant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Name"); + + b.Property("TaskModuleId"); + + b.Property("VariantData"); + + b.HasKey("Id"); + + b.HasIndex("TaskModuleId"); + + b.ToTable("TaskVariants"); + }); + + modelBuilder.Entity("GraphLabs.Backend.Domain.TaskVariantLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Action") + .IsRequired(); + + b.Property("DateTime"); + + b.Property("Penalty"); + + b.Property("StudentId"); + + b.Property("VariantId"); + + b.HasKey("Id"); + + b.HasIndex("DateTime"); + + b.HasIndex("StudentId"); + + b.HasIndex("VariantId"); + + b.ToTable("TaskVariantLogs"); + }); + + modelBuilder.Entity("GraphLabs.Backend.Domain.TestAnswer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Answer"); + + b.Property("IsRight"); + + b.Property("TestQuestionVersionId"); + + b.HasKey("Id"); + + b.HasIndex("TestQuestionVersionId"); + + b.ToTable("TestAnswer"); + }); + + modelBuilder.Entity("GraphLabs.Backend.Domain.TestQuestion", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("SubjectId"); + + b.HasKey("Id"); + + b.HasIndex("SubjectId"); + + b.ToTable("TestQuestion"); + }); + + modelBuilder.Entity("GraphLabs.Backend.Domain.TestQuestionVersion", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Difficulty"); + + b.Property("MaxScore"); + + b.Property("PlainText"); + + b.Property("TestQuestionId"); + + b.HasKey("Id"); + + b.HasIndex("TestQuestionId"); + + b.ToTable("TestQuestionVersion"); + }); + + modelBuilder.Entity("GraphLabs.Backend.Domain.TestResult", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("DateTime"); + + b.Property("MarkEU"); + + b.Property("MarkRU"); + + b.Property("Score"); + + b.Property("StudentId"); + + b.HasKey("Id"); + + b.HasIndex("StudentId"); + + b.ToTable("TestResult"); + }); + + modelBuilder.Entity("GraphLabs.Backend.Domain.TestStudentAnswer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Answer"); + + b.Property("AnswerId"); + + b.Property("IsRight"); + + b.Property("TestQuestionVersionId"); + + b.Property("TestResultId"); + + b.HasKey("Id"); + + b.HasIndex("TestQuestionVersionId"); + + b.HasIndex("TestResultId"); + + b.ToTable("TestStudentAnswer"); + }); + + modelBuilder.Entity("GraphLabs.Backend.Domain.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Discriminator") + .IsRequired(); + + b.Property("Email") + .IsRequired(); + + b.Property("FatherName"); + + b.Property("FirstName") + .IsRequired(); + + b.Property("LastName") + .IsRequired(); + + b.Property("PasswordHash") + .IsRequired(); + + b.Property("PasswordSalt") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique(); + + b.ToTable("Users"); + + b.HasDiscriminator("Discriminator").HasValue("User"); + }); + + modelBuilder.Entity("GraphLabs.Backend.Domain.Student", b => + { + b.HasBaseType("GraphLabs.Backend.Domain.User"); + + b.Property("Group"); + + b.HasIndex("Group"); + + b.HasDiscriminator().HasValue("Student"); + }); + + modelBuilder.Entity("GraphLabs.Backend.Domain.Teacher", b => + { + b.HasBaseType("GraphLabs.Backend.Domain.User"); + + b.HasDiscriminator().HasValue("Teacher"); + }); + + modelBuilder.Entity("GraphLabs.Backend.Domain.TaskModule", b => + { + b.HasOne("GraphLabs.Backend.Domain.Subject", "Subject") + .WithMany("TaskModules") + .HasForeignKey("SubjectId"); + }); + + modelBuilder.Entity("GraphLabs.Backend.Domain.TaskVariant", b => + { + b.HasOne("GraphLabs.Backend.Domain.TaskModule", "TaskModule") + .WithMany("Variants") + .HasForeignKey("TaskModuleId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("GraphLabs.Backend.Domain.TaskVariantLog", b => + { + b.HasOne("GraphLabs.Backend.Domain.Student", "Student") + .WithMany("Logs") + .HasForeignKey("StudentId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("GraphLabs.Backend.Domain.TaskVariant", "Variant") + .WithMany("Logs") + .HasForeignKey("VariantId") + .OnDelete(DeleteBehavior.Restrict); + }); + + modelBuilder.Entity("GraphLabs.Backend.Domain.TestAnswer", b => + { + b.HasOne("GraphLabs.Backend.Domain.TestQuestionVersion", "TestQuestionVersion") + .WithMany("TestAnswers") + .HasForeignKey("TestQuestionVersionId"); + }); + + modelBuilder.Entity("GraphLabs.Backend.Domain.TestQuestion", b => + { + b.HasOne("GraphLabs.Backend.Domain.Subject", "Subject") + .WithMany("TestQuestions") + .HasForeignKey("SubjectId"); + }); + + modelBuilder.Entity("GraphLabs.Backend.Domain.TestQuestionVersion", b => + { + b.HasOne("GraphLabs.Backend.Domain.TestQuestion", "TestQuestion") + .WithMany("TestQuestionVersions") + .HasForeignKey("TestQuestionId"); + }); + + modelBuilder.Entity("GraphLabs.Backend.Domain.TestResult", b => + { + b.HasOne("GraphLabs.Backend.Domain.Student", "Student") + .WithMany("TestResults") + .HasForeignKey("StudentId"); + }); + + modelBuilder.Entity("GraphLabs.Backend.Domain.TestStudentAnswer", b => + { + b.HasOne("GraphLabs.Backend.Domain.TestQuestionVersion", "TestQuestionVersion") + .WithMany("TestStudentAnswers") + .HasForeignKey("TestQuestionVersionId"); + + b.HasOne("GraphLabs.Backend.Domain.TestResult", "TestResult") + .WithMany("TestStudentAnswer") + .HasForeignKey("TestResultId"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/GraphLabs.Backend.Api/Migrations/20200423115639_TestQuestions.cs b/GraphLabs.Backend.Api/Migrations/20200423115639_TestQuestions.cs new file mode 100644 index 0000000..19f0d9f --- /dev/null +++ b/GraphLabs.Backend.Api/Migrations/20200423115639_TestQuestions.cs @@ -0,0 +1,226 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +namespace GraphLabs.Backend.Api.Migrations +{ + public partial class TestQuestions : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Subject", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn), + Name = table.Column(nullable: true), + Description = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Subject", x => x.Id); + }); + + const long defaultSubjectId = 1; + migrationBuilder.Sql($"INSERT INTO \"Subject\" (\"Id\", \"Name\", \"Description\") VALUES " + + $"({defaultSubjectId}, 'Default', 'Default subject')"); + + migrationBuilder.AddColumn( + name: "SubjectId", + table: "TaskModules", + nullable: false, + defaultValue: defaultSubjectId); + + migrationBuilder.CreateTable( + name: "TestResult", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn), + DateTime = table.Column(nullable: false), + Score = table.Column(nullable: false), + MarkEU = table.Column(nullable: false), + MarkRU = table.Column(nullable: false), + StudentId = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_TestResult", x => x.Id); + table.ForeignKey( + name: "FK_TestResult_Users_StudentId", + column: x => x.StudentId, + principalTable: "Users", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateTable( + name: "TestQuestion", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn), + SubjectId = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_TestQuestion", x => x.Id); + table.ForeignKey( + name: "FK_TestQuestion_Subject_SubjectId", + column: x => x.SubjectId, + principalTable: "Subject", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateTable( + name: "TestQuestionVersion", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn), + PlainText = table.Column(nullable: true), + Difficulty = table.Column(nullable: false), + MaxScore = table.Column(nullable: false), + TestQuestionId = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_TestQuestionVersion", x => x.Id); + table.ForeignKey( + name: "FK_TestQuestionVersion_TestQuestion_TestQuestionId", + column: x => x.TestQuestionId, + principalTable: "TestQuestion", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateTable( + name: "TestAnswer", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn), + Answer = table.Column(nullable: true), + IsRight = table.Column(nullable: false), + TestQuestionVersionId = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_TestAnswer", x => x.Id); + table.ForeignKey( + name: "FK_TestAnswer_TestQuestionVersion_TestQuestionVersionId", + column: x => x.TestQuestionVersionId, + principalTable: "TestQuestionVersion", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateTable( + name: "TestStudentAnswer", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn), + AnswerId = table.Column(nullable: false), + Answer = table.Column(nullable: true), + IsRight = table.Column(nullable: false), + TestResultId = table.Column(nullable: true), + TestQuestionVersionId = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_TestStudentAnswer", x => x.Id); + table.ForeignKey( + name: "FK_TestStudentAnswer_TestQuestionVersion_TestQuestionVersionId", + column: x => x.TestQuestionVersionId, + principalTable: "TestQuestionVersion", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_TestStudentAnswer_TestResult_TestResultId", + column: x => x.TestResultId, + principalTable: "TestResult", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateIndex( + name: "IX_TaskModules_SubjectId", + table: "TaskModules", + column: "SubjectId"); + + migrationBuilder.CreateIndex( + name: "IX_TestAnswer_TestQuestionVersionId", + table: "TestAnswer", + column: "TestQuestionVersionId"); + + migrationBuilder.CreateIndex( + name: "IX_TestQuestion_SubjectId", + table: "TestQuestion", + column: "SubjectId"); + + migrationBuilder.CreateIndex( + name: "IX_TestQuestionVersion_TestQuestionId", + table: "TestQuestionVersion", + column: "TestQuestionId"); + + migrationBuilder.CreateIndex( + name: "IX_TestResult_StudentId", + table: "TestResult", + column: "StudentId"); + + migrationBuilder.CreateIndex( + name: "IX_TestStudentAnswer_TestQuestionVersionId", + table: "TestStudentAnswer", + column: "TestQuestionVersionId"); + + migrationBuilder.CreateIndex( + name: "IX_TestStudentAnswer_TestResultId", + table: "TestStudentAnswer", + column: "TestResultId"); + + migrationBuilder.AddForeignKey( + name: "FK_TaskModules_Subject_SubjectId", + table: "TaskModules", + column: "SubjectId", + principalTable: "Subject", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_TaskModules_Subject_SubjectId", + table: "TaskModules"); + + migrationBuilder.DropTable( + name: "TestAnswer"); + + migrationBuilder.DropTable( + name: "TestStudentAnswer"); + + migrationBuilder.DropTable( + name: "TestQuestionVersion"); + + migrationBuilder.DropTable( + name: "TestResult"); + + migrationBuilder.DropTable( + name: "TestQuestion"); + + migrationBuilder.DropTable( + name: "Subject"); + + migrationBuilder.DropIndex( + name: "IX_TaskModules_SubjectId", + table: "TaskModules"); + + migrationBuilder.DropColumn( + name: "SubjectId", + table: "TaskModules"); + } + } +} diff --git a/GraphLabs.Backend.Api/Migrations/20200426080500_20200426140200_TestQuestion.Designer.cs b/GraphLabs.Backend.Api/Migrations/20200426080500_20200426140200_TestQuestion.Designer.cs new file mode 100644 index 0000000..b56048d --- /dev/null +++ b/GraphLabs.Backend.Api/Migrations/20200426080500_20200426140200_TestQuestion.Designer.cs @@ -0,0 +1,330 @@ +// +using System; +using GraphLabs.Backend.DAL; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +namespace GraphLabs.Backend.Api.Migrations +{ + [DbContext(typeof(GraphLabsContext))] + [Migration("20200426080500_20200426140200_TestQuestion")] + partial class _20200426140200_TestQuestion + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn) + .HasAnnotation("ProductVersion", "2.2.6-servicing-10079") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + modelBuilder.Entity("GraphLabs.Backend.Domain.Subject", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Description"); + + b.Property("Name"); + + b.HasKey("Id"); + + b.ToTable("Subjects"); + }); + + modelBuilder.Entity("GraphLabs.Backend.Domain.TaskModule", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Description"); + + b.Property("Name"); + + b.Property("SubjectId"); + + b.Property("Version"); + + b.HasKey("Id"); + + b.HasIndex("SubjectId"); + + b.ToTable("TaskModules"); + }); + + modelBuilder.Entity("GraphLabs.Backend.Domain.TaskVariant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Name"); + + b.Property("TaskModuleId"); + + b.Property("VariantData"); + + b.HasKey("Id"); + + b.HasIndex("TaskModuleId"); + + b.ToTable("TaskVariants"); + }); + + modelBuilder.Entity("GraphLabs.Backend.Domain.TaskVariantLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Action") + .IsRequired(); + + b.Property("DateTime"); + + b.Property("Penalty"); + + b.Property("StudentId"); + + b.Property("VariantId"); + + b.HasKey("Id"); + + b.HasIndex("DateTime"); + + b.HasIndex("StudentId"); + + b.HasIndex("VariantId"); + + b.ToTable("TaskVariantLogs"); + }); + + modelBuilder.Entity("GraphLabs.Backend.Domain.TestAnswer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Answer"); + + b.Property("IsRight"); + + b.Property("TestQuestionVersionId"); + + b.HasKey("Id"); + + b.HasIndex("TestQuestionVersionId"); + + b.ToTable("TestAnswers"); + }); + + modelBuilder.Entity("GraphLabs.Backend.Domain.TestQuestion", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("SubjectId"); + + b.HasKey("Id"); + + b.HasIndex("SubjectId"); + + b.ToTable("TestQuestions"); + }); + + modelBuilder.Entity("GraphLabs.Backend.Domain.TestQuestionVersion", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Difficulty"); + + b.Property("MaxScore"); + + b.Property("PlainText"); + + b.Property("TestQuestionId"); + + b.HasKey("Id"); + + b.HasIndex("TestQuestionId"); + + b.ToTable("TestQuestionVersions"); + }); + + modelBuilder.Entity("GraphLabs.Backend.Domain.TestResult", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("DateTime"); + + b.Property("MarkEU"); + + b.Property("MarkRU"); + + b.Property("Score"); + + b.Property("StudentId"); + + b.HasKey("Id"); + + b.HasIndex("DateTime"); + + b.HasIndex("StudentId"); + + b.ToTable("TestResults"); + }); + + modelBuilder.Entity("GraphLabs.Backend.Domain.TestStudentAnswer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Answer"); + + b.Property("AnswerId"); + + b.Property("IsRight"); + + b.Property("TestResultId"); + + b.HasKey("Id"); + + b.HasIndex("AnswerId"); + + b.HasIndex("TestResultId"); + + b.ToTable("TestStudentAnswers"); + }); + + modelBuilder.Entity("GraphLabs.Backend.Domain.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Discriminator") + .IsRequired(); + + b.Property("Email") + .IsRequired(); + + b.Property("FatherName"); + + b.Property("FirstName") + .IsRequired(); + + b.Property("LastName") + .IsRequired(); + + b.Property("PasswordHash") + .IsRequired(); + + b.Property("PasswordSalt") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique(); + + b.ToTable("Users"); + + b.HasDiscriminator("Discriminator").HasValue("User"); + }); + + modelBuilder.Entity("GraphLabs.Backend.Domain.Student", b => + { + b.HasBaseType("GraphLabs.Backend.Domain.User"); + + b.Property("Group"); + + b.HasIndex("Group"); + + b.HasDiscriminator().HasValue("Student"); + }); + + modelBuilder.Entity("GraphLabs.Backend.Domain.Teacher", b => + { + b.HasBaseType("GraphLabs.Backend.Domain.User"); + + b.HasDiscriminator().HasValue("Teacher"); + }); + + modelBuilder.Entity("GraphLabs.Backend.Domain.TaskModule", b => + { + b.HasOne("GraphLabs.Backend.Domain.Subject", "Subject") + .WithMany("TaskModules") + .HasForeignKey("SubjectId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("GraphLabs.Backend.Domain.TaskVariant", b => + { + b.HasOne("GraphLabs.Backend.Domain.TaskModule", "TaskModule") + .WithMany("Variants") + .HasForeignKey("TaskModuleId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("GraphLabs.Backend.Domain.TaskVariantLog", b => + { + b.HasOne("GraphLabs.Backend.Domain.Student", "Student") + .WithMany("Logs") + .HasForeignKey("StudentId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("GraphLabs.Backend.Domain.TaskVariant", "Variant") + .WithMany("Logs") + .HasForeignKey("VariantId") + .OnDelete(DeleteBehavior.Restrict); + }); + + modelBuilder.Entity("GraphLabs.Backend.Domain.TestAnswer", b => + { + b.HasOne("GraphLabs.Backend.Domain.TestQuestionVersion", "TestQuestionVersion") + .WithMany("TestAnswers") + .HasForeignKey("TestQuestionVersionId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("GraphLabs.Backend.Domain.TestQuestion", b => + { + b.HasOne("GraphLabs.Backend.Domain.Subject", "Subject") + .WithMany("TestQuestions") + .HasForeignKey("SubjectId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("GraphLabs.Backend.Domain.TestQuestionVersion", b => + { + b.HasOne("GraphLabs.Backend.Domain.TestQuestion", "TestQuestion") + .WithMany("TestQuestionVersions") + .HasForeignKey("TestQuestionId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("GraphLabs.Backend.Domain.TestResult", b => + { + b.HasOne("GraphLabs.Backend.Domain.Student", "Student") + .WithMany("TestResults") + .HasForeignKey("StudentId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("GraphLabs.Backend.Domain.TestStudentAnswer", b => + { + b.HasOne("GraphLabs.Backend.Domain.TestQuestionVersion", "TestQuestionVersion") + .WithMany("TestStudentAnswers") + .HasForeignKey("AnswerId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("GraphLabs.Backend.Domain.TestResult", "TestResult") + .WithMany("TestStudentAnswer") + .HasForeignKey("TestResultId") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/GraphLabs.Backend.Api/Migrations/20200426080500_20200426140200_TestQuestion.cs b/GraphLabs.Backend.Api/Migrations/20200426080500_20200426140200_TestQuestion.cs new file mode 100644 index 0000000..458860d --- /dev/null +++ b/GraphLabs.Backend.Api/Migrations/20200426080500_20200426140200_TestQuestion.cs @@ -0,0 +1,501 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace GraphLabs.Backend.Api.Migrations +{ + public partial class _20200426140200_TestQuestion : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_TaskModules_Subject_SubjectId", + table: "TaskModules"); + + migrationBuilder.DropForeignKey( + name: "FK_TestAnswer_TestQuestionVersion_TestQuestionVersionId", + table: "TestAnswer"); + + migrationBuilder.DropForeignKey( + name: "FK_TestQuestion_Subject_SubjectId", + table: "TestQuestion"); + + migrationBuilder.DropForeignKey( + name: "FK_TestQuestionVersion_TestQuestion_TestQuestionId", + table: "TestQuestionVersion"); + + migrationBuilder.DropForeignKey( + name: "FK_TestResult_Users_StudentId", + table: "TestResult"); + + migrationBuilder.DropForeignKey( + name: "FK_TestStudentAnswer_TestQuestionVersion_TestQuestionVersionId", + table: "TestStudentAnswer"); + + migrationBuilder.DropForeignKey( + name: "FK_TestStudentAnswer_TestResult_TestResultId", + table: "TestStudentAnswer"); + + migrationBuilder.DropPrimaryKey( + name: "PK_TestStudentAnswer", + table: "TestStudentAnswer"); + + migrationBuilder.DropIndex( + name: "IX_TestStudentAnswer_TestQuestionVersionId", + table: "TestStudentAnswer"); + + migrationBuilder.DropPrimaryKey( + name: "PK_TestResult", + table: "TestResult"); + + migrationBuilder.DropPrimaryKey( + name: "PK_TestQuestionVersion", + table: "TestQuestionVersion"); + + migrationBuilder.DropPrimaryKey( + name: "PK_TestQuestion", + table: "TestQuestion"); + + migrationBuilder.DropPrimaryKey( + name: "PK_TestAnswer", + table: "TestAnswer"); + + migrationBuilder.DropPrimaryKey( + name: "PK_Subject", + table: "Subject"); + + migrationBuilder.DropColumn( + name: "TestQuestionVersionId", + table: "TestStudentAnswer"); + + migrationBuilder.RenameTable( + name: "TestStudentAnswer", + newName: "TestStudentAnswers"); + + migrationBuilder.RenameTable( + name: "TestResult", + newName: "TestResults"); + + migrationBuilder.RenameTable( + name: "TestQuestionVersion", + newName: "TestQuestionVersions"); + + migrationBuilder.RenameTable( + name: "TestQuestion", + newName: "TestQuestions"); + + migrationBuilder.RenameTable( + name: "TestAnswer", + newName: "TestAnswers"); + + migrationBuilder.RenameTable( + name: "Subject", + newName: "Subjects"); + + migrationBuilder.RenameIndex( + name: "IX_TestStudentAnswer_TestResultId", + table: "TestStudentAnswers", + newName: "IX_TestStudentAnswers_TestResultId"); + + migrationBuilder.RenameIndex( + name: "IX_TestResult_StudentId", + table: "TestResults", + newName: "IX_TestResults_StudentId"); + + migrationBuilder.RenameIndex( + name: "IX_TestQuestionVersion_TestQuestionId", + table: "TestQuestionVersions", + newName: "IX_TestQuestionVersions_TestQuestionId"); + + migrationBuilder.RenameIndex( + name: "IX_TestQuestion_SubjectId", + table: "TestQuestions", + newName: "IX_TestQuestions_SubjectId"); + + migrationBuilder.RenameIndex( + name: "IX_TestAnswer_TestQuestionVersionId", + table: "TestAnswers", + newName: "IX_TestAnswers_TestQuestionVersionId"); + + migrationBuilder.AlterColumn( + name: "SubjectId", + table: "TaskModules", + nullable: false, + oldClrType: typeof(long), + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "TestResultId", + table: "TestStudentAnswers", + nullable: false, + oldClrType: typeof(long), + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "StudentId", + table: "TestResults", + nullable: false, + oldClrType: typeof(long), + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "TestQuestionId", + table: "TestQuestionVersions", + nullable: false, + oldClrType: typeof(long), + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "SubjectId", + table: "TestQuestions", + nullable: false, + oldClrType: typeof(long), + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "TestQuestionVersionId", + table: "TestAnswers", + nullable: false, + oldClrType: typeof(long), + oldNullable: true); + + migrationBuilder.AddPrimaryKey( + name: "PK_TestStudentAnswers", + table: "TestStudentAnswers", + column: "Id"); + + migrationBuilder.AddPrimaryKey( + name: "PK_TestResults", + table: "TestResults", + column: "Id"); + + migrationBuilder.AddPrimaryKey( + name: "PK_TestQuestionVersions", + table: "TestQuestionVersions", + column: "Id"); + + migrationBuilder.AddPrimaryKey( + name: "PK_TestQuestions", + table: "TestQuestions", + column: "Id"); + + migrationBuilder.AddPrimaryKey( + name: "PK_TestAnswers", + table: "TestAnswers", + column: "Id"); + + migrationBuilder.AddPrimaryKey( + name: "PK_Subjects", + table: "Subjects", + column: "Id"); + + migrationBuilder.CreateIndex( + name: "IX_TestStudentAnswers_AnswerId", + table: "TestStudentAnswers", + column: "AnswerId"); + + migrationBuilder.CreateIndex( + name: "IX_TestResults_DateTime", + table: "TestResults", + column: "DateTime"); + + migrationBuilder.AddForeignKey( + name: "FK_TaskModules_Subjects_SubjectId", + table: "TaskModules", + column: "SubjectId", + principalTable: "Subjects", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + name: "FK_TestAnswers_TestQuestionVersions_TestQuestionVersionId", + table: "TestAnswers", + column: "TestQuestionVersionId", + principalTable: "TestQuestionVersions", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + name: "FK_TestQuestions_Subjects_SubjectId", + table: "TestQuestions", + column: "SubjectId", + principalTable: "Subjects", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + name: "FK_TestQuestionVersions_TestQuestions_TestQuestionId", + table: "TestQuestionVersions", + column: "TestQuestionId", + principalTable: "TestQuestions", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + name: "FK_TestResults_Users_StudentId", + table: "TestResults", + column: "StudentId", + principalTable: "Users", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + name: "FK_TestStudentAnswers_TestQuestionVersions_AnswerId", + table: "TestStudentAnswers", + column: "AnswerId", + principalTable: "TestQuestionVersions", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "FK_TestStudentAnswers_TestResults_TestResultId", + table: "TestStudentAnswers", + column: "TestResultId", + principalTable: "TestResults", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_TaskModules_Subjects_SubjectId", + table: "TaskModules"); + + migrationBuilder.DropForeignKey( + name: "FK_TestAnswers_TestQuestionVersions_TestQuestionVersionId", + table: "TestAnswers"); + + migrationBuilder.DropForeignKey( + name: "FK_TestQuestions_Subjects_SubjectId", + table: "TestQuestions"); + + migrationBuilder.DropForeignKey( + name: "FK_TestQuestionVersions_TestQuestions_TestQuestionId", + table: "TestQuestionVersions"); + + migrationBuilder.DropForeignKey( + name: "FK_TestResults_Users_StudentId", + table: "TestResults"); + + migrationBuilder.DropForeignKey( + name: "FK_TestStudentAnswers_TestQuestionVersions_AnswerId", + table: "TestStudentAnswers"); + + migrationBuilder.DropForeignKey( + name: "FK_TestStudentAnswers_TestResults_TestResultId", + table: "TestStudentAnswers"); + + migrationBuilder.DropPrimaryKey( + name: "PK_TestStudentAnswers", + table: "TestStudentAnswers"); + + migrationBuilder.DropIndex( + name: "IX_TestStudentAnswers_AnswerId", + table: "TestStudentAnswers"); + + migrationBuilder.DropPrimaryKey( + name: "PK_TestResults", + table: "TestResults"); + + migrationBuilder.DropIndex( + name: "IX_TestResults_DateTime", + table: "TestResults"); + + migrationBuilder.DropPrimaryKey( + name: "PK_TestQuestionVersions", + table: "TestQuestionVersions"); + + migrationBuilder.DropPrimaryKey( + name: "PK_TestQuestions", + table: "TestQuestions"); + + migrationBuilder.DropPrimaryKey( + name: "PK_TestAnswers", + table: "TestAnswers"); + + migrationBuilder.DropPrimaryKey( + name: "PK_Subjects", + table: "Subjects"); + + migrationBuilder.RenameTable( + name: "TestStudentAnswers", + newName: "TestStudentAnswer"); + + migrationBuilder.RenameTable( + name: "TestResults", + newName: "TestResult"); + + migrationBuilder.RenameTable( + name: "TestQuestionVersions", + newName: "TestQuestionVersion"); + + migrationBuilder.RenameTable( + name: "TestQuestions", + newName: "TestQuestion"); + + migrationBuilder.RenameTable( + name: "TestAnswers", + newName: "TestAnswer"); + + migrationBuilder.RenameTable( + name: "Subjects", + newName: "Subject"); + + migrationBuilder.RenameIndex( + name: "IX_TestStudentAnswers_TestResultId", + table: "TestStudentAnswer", + newName: "IX_TestStudentAnswer_TestResultId"); + + migrationBuilder.RenameIndex( + name: "IX_TestResults_StudentId", + table: "TestResult", + newName: "IX_TestResult_StudentId"); + + migrationBuilder.RenameIndex( + name: "IX_TestQuestionVersions_TestQuestionId", + table: "TestQuestionVersion", + newName: "IX_TestQuestionVersion_TestQuestionId"); + + migrationBuilder.RenameIndex( + name: "IX_TestQuestions_SubjectId", + table: "TestQuestion", + newName: "IX_TestQuestion_SubjectId"); + + migrationBuilder.RenameIndex( + name: "IX_TestAnswers_TestQuestionVersionId", + table: "TestAnswer", + newName: "IX_TestAnswer_TestQuestionVersionId"); + + migrationBuilder.AlterColumn( + name: "SubjectId", + table: "TaskModules", + nullable: true, + oldClrType: typeof(long)); + + migrationBuilder.AlterColumn( + name: "TestResultId", + table: "TestStudentAnswer", + nullable: true, + oldClrType: typeof(long)); + + migrationBuilder.AddColumn( + name: "TestQuestionVersionId", + table: "TestStudentAnswer", + nullable: true); + + migrationBuilder.AlterColumn( + name: "StudentId", + table: "TestResult", + nullable: true, + oldClrType: typeof(long)); + + migrationBuilder.AlterColumn( + name: "TestQuestionId", + table: "TestQuestionVersion", + nullable: true, + oldClrType: typeof(long)); + + migrationBuilder.AlterColumn( + name: "SubjectId", + table: "TestQuestion", + nullable: true, + oldClrType: typeof(long)); + + migrationBuilder.AlterColumn( + name: "TestQuestionVersionId", + table: "TestAnswer", + nullable: true, + oldClrType: typeof(long)); + + migrationBuilder.AddPrimaryKey( + name: "PK_TestStudentAnswer", + table: "TestStudentAnswer", + column: "Id"); + + migrationBuilder.AddPrimaryKey( + name: "PK_TestResult", + table: "TestResult", + column: "Id"); + + migrationBuilder.AddPrimaryKey( + name: "PK_TestQuestionVersion", + table: "TestQuestionVersion", + column: "Id"); + + migrationBuilder.AddPrimaryKey( + name: "PK_TestQuestion", + table: "TestQuestion", + column: "Id"); + + migrationBuilder.AddPrimaryKey( + name: "PK_TestAnswer", + table: "TestAnswer", + column: "Id"); + + migrationBuilder.AddPrimaryKey( + name: "PK_Subject", + table: "Subject", + column: "Id"); + + migrationBuilder.CreateIndex( + name: "IX_TestStudentAnswer_TestQuestionVersionId", + table: "TestStudentAnswer", + column: "TestQuestionVersionId"); + + migrationBuilder.AddForeignKey( + name: "FK_TaskModules_Subject_SubjectId", + table: "TaskModules", + column: "SubjectId", + principalTable: "Subject", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "FK_TestAnswer_TestQuestionVersion_TestQuestionVersionId", + table: "TestAnswer", + column: "TestQuestionVersionId", + principalTable: "TestQuestionVersion", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "FK_TestQuestion_Subject_SubjectId", + table: "TestQuestion", + column: "SubjectId", + principalTable: "Subject", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "FK_TestQuestionVersion_TestQuestion_TestQuestionId", + table: "TestQuestionVersion", + column: "TestQuestionId", + principalTable: "TestQuestion", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "FK_TestResult_Users_StudentId", + table: "TestResult", + column: "StudentId", + principalTable: "Users", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "FK_TestStudentAnswer_TestQuestionVersion_TestQuestionVersionId", + table: "TestStudentAnswer", + column: "TestQuestionVersionId", + principalTable: "TestQuestionVersion", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "FK_TestStudentAnswer_TestResult_TestResultId", + table: "TestStudentAnswer", + column: "TestResultId", + principalTable: "TestResult", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + } + } +} diff --git a/GraphLabs.Backend.Api/Migrations/GraphLabsContextModelSnapshot.cs b/GraphLabs.Backend.Api/Migrations/GraphLabsContextModelSnapshot.cs index 97f36a0..c5a4a96 100644 --- a/GraphLabs.Backend.Api/Migrations/GraphLabsContextModelSnapshot.cs +++ b/GraphLabs.Backend.Api/Migrations/GraphLabsContextModelSnapshot.cs @@ -16,9 +16,23 @@ protected override void BuildModel(ModelBuilder modelBuilder) #pragma warning disable 612, 618 modelBuilder .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn) - .HasAnnotation("ProductVersion", "2.2.0-rtm-35687") + .HasAnnotation("ProductVersion", "2.2.6-servicing-10079") .HasAnnotation("Relational:MaxIdentifierLength", 63); + modelBuilder.Entity("GraphLabs.Backend.Domain.Subject", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Description"); + + b.Property("Name"); + + b.HasKey("Id"); + + b.ToTable("Subjects"); + }); + modelBuilder.Entity("GraphLabs.Backend.Domain.TaskModule", b => { b.Property("Id") @@ -28,10 +42,14 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Name"); + b.Property("SubjectId"); + b.Property("Version"); b.HasKey("Id"); + b.HasIndex("SubjectId"); + b.ToTable("TaskModules"); }); @@ -80,6 +98,104 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("TaskVariantLogs"); }); + modelBuilder.Entity("GraphLabs.Backend.Domain.TestAnswer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Answer"); + + b.Property("IsRight"); + + b.Property("TestQuestionVersionId"); + + b.HasKey("Id"); + + b.HasIndex("TestQuestionVersionId"); + + b.ToTable("TestAnswers"); + }); + + modelBuilder.Entity("GraphLabs.Backend.Domain.TestQuestion", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("SubjectId"); + + b.HasKey("Id"); + + b.HasIndex("SubjectId"); + + b.ToTable("TestQuestions"); + }); + + modelBuilder.Entity("GraphLabs.Backend.Domain.TestQuestionVersion", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Difficulty"); + + b.Property("MaxScore"); + + b.Property("PlainText"); + + b.Property("TestQuestionId"); + + b.HasKey("Id"); + + b.HasIndex("TestQuestionId"); + + b.ToTable("TestQuestionVersions"); + }); + + modelBuilder.Entity("GraphLabs.Backend.Domain.TestResult", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("DateTime"); + + b.Property("MarkEU"); + + b.Property("MarkRU"); + + b.Property("Score"); + + b.Property("StudentId"); + + b.HasKey("Id"); + + b.HasIndex("DateTime"); + + b.HasIndex("StudentId"); + + b.ToTable("TestResults"); + }); + + modelBuilder.Entity("GraphLabs.Backend.Domain.TestStudentAnswer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Answer"); + + b.Property("AnswerId"); + + b.Property("IsRight"); + + b.Property("TestResultId"); + + b.HasKey("Id"); + + b.HasIndex("AnswerId"); + + b.HasIndex("TestResultId"); + + b.ToTable("TestStudentAnswers"); + }); + modelBuilder.Entity("GraphLabs.Backend.Domain.User", b => { b.Property("Id") @@ -133,6 +249,14 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasDiscriminator().HasValue("Teacher"); }); + modelBuilder.Entity("GraphLabs.Backend.Domain.TaskModule", b => + { + b.HasOne("GraphLabs.Backend.Domain.Subject", "Subject") + .WithMany("TaskModules") + .HasForeignKey("SubjectId") + .OnDelete(DeleteBehavior.Cascade); + }); + modelBuilder.Entity("GraphLabs.Backend.Domain.TaskVariant", b => { b.HasOne("GraphLabs.Backend.Domain.TaskModule", "TaskModule") @@ -153,6 +277,51 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasForeignKey("VariantId") .OnDelete(DeleteBehavior.Restrict); }); + + modelBuilder.Entity("GraphLabs.Backend.Domain.TestAnswer", b => + { + b.HasOne("GraphLabs.Backend.Domain.TestQuestionVersion", "TestQuestionVersion") + .WithMany("TestAnswers") + .HasForeignKey("TestQuestionVersionId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("GraphLabs.Backend.Domain.TestQuestion", b => + { + b.HasOne("GraphLabs.Backend.Domain.Subject", "Subject") + .WithMany("TestQuestions") + .HasForeignKey("SubjectId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("GraphLabs.Backend.Domain.TestQuestionVersion", b => + { + b.HasOne("GraphLabs.Backend.Domain.TestQuestion", "TestQuestion") + .WithMany("TestQuestionVersions") + .HasForeignKey("TestQuestionId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("GraphLabs.Backend.Domain.TestResult", b => + { + b.HasOne("GraphLabs.Backend.Domain.Student", "Student") + .WithMany("TestResults") + .HasForeignKey("StudentId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("GraphLabs.Backend.Domain.TestStudentAnswer", b => + { + b.HasOne("GraphLabs.Backend.Domain.TestQuestionVersion", "TestQuestionVersion") + .WithMany("TestStudentAnswers") + .HasForeignKey("AnswerId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("GraphLabs.Backend.Domain.TestResult", "TestResult") + .WithMany("TestStudentAnswer") + .HasForeignKey("TestResultId") + .OnDelete(DeleteBehavior.Cascade); + }); #pragma warning restore 612, 618 } } diff --git a/GraphLabs.Backend.Api/Program.cs b/GraphLabs.Backend.Api/Program.cs index 9b24ee5..64386ef 100644 --- a/GraphLabs.Backend.Api/Program.cs +++ b/GraphLabs.Backend.Api/Program.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics; using System.IO; using System.Linq; using System.Threading.Tasks; @@ -57,10 +58,18 @@ private static IWebHostBuilder CreateWebHostBuilder(string[] args) private static async Task InitializeDb(GraphLabsContext context, InitialData initialData) { await context.Database.MigrateAsync(); - + + if (!context.Subjects.Any()) + { + foreach (var subject in initialData.GetSubjects()) + { + context.Subjects.Add(subject); + } + await context.SaveChangesAsync(); + } if (!context.TaskModules.Any()) { - foreach (var module in initialData.GetTaskModules()) + foreach (var module in initialData.GetTaskModules(context.Subjects)) { context.TaskModules.Add(module); } diff --git a/GraphLabs.Backend.Api/Properties/launchSettings.json b/GraphLabs.Backend.Api/Properties/launchSettings.json index d71f07d..b2e449e 100644 --- a/GraphLabs.Backend.Api/Properties/launchSettings.json +++ b/GraphLabs.Backend.Api/Properties/launchSettings.json @@ -1,30 +1,43 @@ -{ - "$schema": "http://json.schemastore.org/launchsettings.json", +{ "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iis": { + "applicationUrl": "http://localhost/GraphLabs.Backend.Api", + "sslPort": 0 + }, "iisExpress": { - "applicationUrl": "http://localhost:42384", - "sslPort": 44363 + "applicationUrl": "https://localhost:44338", + "sslPort": 44338 } }, + "$schema": "http://json.schemastore.org/launchsettings.json", "profiles": { "IIS Express": { "commandName": "IISExpress", - "launchBrowser": true, - "launchUrl": "api/values", + "launchUrl": "auth/login", "environmentVariables": { + "DB_USER": "graphlabs-test", + "DB_NAME": "graphlabs-test", + "DB_PORT": "54032", + "DB_PASSWORD": "felelcrKKDSef!!!", + "DB_HOST": "109.173.118.8", + "ASPNETCORE_URLS": "https://localhost:5001;https://localhost:5000", "ASPNETCORE_ENVIRONMENT": "Development" - } + }, + "applicationUrl": "https://localhost:5000" }, "GraphLabs.Backend": { "commandName": "Project", "launchBrowser": true, - "launchUrl": "api/values", - "applicationUrl": "https://localhost:5001;http://localhost:5000", + "launchUrl": "auth/login", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" - } + }, + "applicationUrl": "https://localhost:5001;http://localhost:5000" + }, + "Profile1": { + "commandName": "Executable" } } } \ No newline at end of file diff --git a/GraphLabs.Backend.Api/Startup.cs b/GraphLabs.Backend.Api/Startup.cs index 42aff21..9aa6bf6 100644 --- a/GraphLabs.Backend.Api/Startup.cs +++ b/GraphLabs.Backend.Api/Startup.cs @@ -49,12 +49,21 @@ public void ConfigureServices(IServiceCollection services) var migrationsAssembly = GetType().Assembly.FullName; var postgresHost = Environment.GetEnvironmentVariable("DB_HOST"); + var postgresPort = Environment.GetEnvironmentVariable("DB_PORT"); + postgresPort = string.IsNullOrWhiteSpace(postgresPort) + ? "5432" + : postgresPort; var postgresDb = Environment.GetEnvironmentVariable("DB_NAME"); var postgresUser = Environment.GetEnvironmentVariable("DB_USER"); var postgresPassword = Environment.GetEnvironmentVariable("DB_PASSWORD"); services.AddDbContext( - o => o.UseNpgsql($"Host={postgresHost};Database={postgresDb};Username={postgresUser};Password={postgresPassword}", b => b.MigrationsAssembly(migrationsAssembly))); + o => + { + o.UseNpgsql( + $"Host={postgresHost};Port={postgresPort};Database={postgresDb};Username={postgresUser};Password={postgresPassword}", + b => b.MigrationsAssembly(migrationsAssembly)); + }); if (_environment.IsDevelopment()) { @@ -162,10 +171,49 @@ private static IEdmModel GetEdmModel() Namespace = "GraphLabs" }; + // Subjects ================================================================================================ + var subject = builder.EntitySet("Subjects").EntityType; + subject.HasKey(m => m.Id); + subject.HasMany(m => m.TaskModules); + subject.HasMany(m => m.TestQuestions); + + // TestQuestions =========================================================================================== + var testQuestion = builder.EntitySet("TestQuestions").EntityType; + testQuestion.HasKey(q => q.Id); + testQuestion.HasMany(q => q.TestQuestionVersions); + testQuestion.HasRequired(q => q.Subject); + + // TestQuestionVersions ==================================================================================== + var testQuestionVersion = builder.EntitySet("TestQuestionVersions").EntityType; + testQuestionVersion.HasKey(v => v.Id); + testQuestionVersion.HasMany(v => v.TestStudentAnswers); + testQuestionVersion.HasMany(v => v.TestAnswers); + testQuestionVersion.HasRequired(v => v.TestQuestion); + + //testQuestionVersion.Function(nameof(TestQuestionVersionsController.CreateVariant)).Returns(); + + // TesAnswers ============================================================================================== + var testAnswer = builder.EntitySet("TestAnswers").EntityType; + testAnswer.HasKey(a => a.Id); + testAnswer.HasRequired(a => a.TestQuestionVersion); + + // TestStudentAnswers ====================================================================================== + var testStudentAnswer = builder.EntitySet("TestStudentAnswers").EntityType; + testStudentAnswer.HasKey(a => a.Id); + testStudentAnswer.HasRequired(a => a.TestQuestionVersion); + testStudentAnswer.HasRequired(a => a.TestResult); + + // TestResults ============================================================================================= + var testResult = builder.EntitySet("TestResults").EntityType; + testResult.HasKey(r => r.Id); + testResult.HasMany(r => r.TestStudentAnswer); + testResult.HasRequired(r => r.Student); + // TaskModules ============================================================================================= var taskModule = builder.EntitySet("TaskModules").EntityType; taskModule.HasKey(m => m.Id); taskModule.HasMany(m => m.Variants); + taskModule.HasRequired(m => m.Subject); taskModule.Function(nameof(TaskModulesController.RandomVariant)).Returns(); diff --git a/GraphLabs.Backend.DAL/GraphLabsContext.cs b/GraphLabs.Backend.DAL/GraphLabsContext.cs index a93918b..45c1180 100644 --- a/GraphLabs.Backend.DAL/GraphLabsContext.cs +++ b/GraphLabs.Backend.DAL/GraphLabsContext.cs @@ -59,6 +59,12 @@ private void DbConnectionOnStateChange(object sender, StateChangeEventArgs e) public DbSet Students { get; protected set; } public DbSet Teachers { get; protected set; } public DbSet TaskVariantLogs { get; protected set; } + public DbSet Subjects { get; protected set; } + public DbSet TestResults { get; protected set; } + public DbSet TestStudentAnswers { get; protected set; } + public DbSet TestQuestions { get; protected set; } + public DbSet TestQuestionVersions { get; protected set; } + public DbSet TestAnswers { get; protected set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { @@ -71,9 +77,17 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .IsRequired() .OnDelete(DeleteBehavior.Cascade); + var subject = modelBuilder.Entity(); + subject.HasKey(s => s.Id); + var taskModule = modelBuilder.Entity(); taskModule.HasKey(t => t.Id); - + taskModule + .HasOne(t => t.Subject) + .WithMany(s => s.TaskModules) + .IsRequired() + .OnDelete(DeleteBehavior.Cascade); + var user = modelBuilder.Entity(); user.HasKey(u => u.Id); user.Property(u => u.Email).IsRequired(); @@ -103,6 +117,60 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .HasForeignKey(l => l.StudentId) .OnDelete(DeleteBehavior.Cascade); log.HasIndex(l => l.DateTime); + + var testQuestion = modelBuilder.Entity(); + testQuestion + .HasKey(q => q.Id); + testQuestion + .HasOne(s => s.Subject) + .WithMany(q => q.TestQuestions) + .IsRequired() + .OnDelete(DeleteBehavior.Cascade); + + var testQuestionVersion = modelBuilder.Entity(); + testQuestionVersion + .HasKey(v => v.Id); + testQuestionVersion + .HasOne(v => v.TestQuestion) + .WithMany(q => q.TestQuestionVersions) + .IsRequired() + .OnDelete(DeleteBehavior.Cascade); + + var testAnswer = modelBuilder.Entity(); + testAnswer + .HasKey(a => a.Id); + testAnswer + .HasOne(a => a.TestQuestionVersion) + .WithMany(v => v.TestAnswers) + .IsRequired() + .OnDelete(DeleteBehavior.Cascade); + + var testStudentAnswer = modelBuilder.Entity(); + testStudentAnswer + .HasKey(a => a.Id); + testStudentAnswer + .HasOne(a => a.TestQuestionVersion) + .WithMany(v => v.TestStudentAnswers) + .IsRequired() + .HasForeignKey(a => a.AnswerId) + .OnDelete(DeleteBehavior.Restrict); + testStudentAnswer + .HasOne(a => a.TestResult) + .WithMany(r => r.TestStudentAnswer) + .IsRequired() + .OnDelete(DeleteBehavior.Cascade); + + var testResult = modelBuilder.Entity(); + testResult.HasKey(r => r.Id); + testResult.Property(r => r.Score).IsRequired(); + testResult.Property(r => r.MarkEU).IsRequired(); + testResult.Property(r => r.MarkRU).IsRequired(); + testResult.HasIndex(r => r.DateTime); + testResult + .HasOne(r => r.Student) + .WithMany(s => s.TestResults) + .IsRequired() + .OnDelete(DeleteBehavior.Cascade); } public override void Dispose() diff --git a/GraphLabs.Backend.DAL/InitialData.cs b/GraphLabs.Backend.DAL/InitialData.cs index f511c2e..348c6fd 100644 --- a/GraphLabs.Backend.DAL/InitialData.cs +++ b/GraphLabs.Backend.DAL/InitialData.cs @@ -15,8 +15,18 @@ public InitialData(PasswordHashCalculator calculator) { _calculator = calculator; } - - public IEnumerable GetTaskModules() + + public IEnumerable GetSubjects() + { + yield return new Subject + { + Id = 1, + Name = "Тема 1", + Description = "Описание темы 1" + }; + } + + public IEnumerable GetTaskModules(IEnumerable subjects) { // taskModule #1 yield return new TaskModule @@ -24,7 +34,8 @@ public IEnumerable GetTaskModules() Id = 1, Name = "Шаблон", Description = "Шаблон модуля.", - Version = "2.0" + Version = "2.0", + Subject = subjects.ElementAt(0) }; // taskModule #2 @@ -33,7 +44,8 @@ public IEnumerable GetTaskModules() Id = 2, Name = "Внешняя устойчивость", Description = "Модуль Лизы", - Version = "1.0" + Version = "1.0", + Subject = subjects.ElementAt(0) }; // taskModule #3 @@ -42,7 +54,8 @@ public IEnumerable GetTaskModules() Id = 3, Name = "Планарность", Description = "Модуль Насти", - Version = "1.0" + Version = "1.0", + Subject = subjects.ElementAt(0) }; // taskModule #4 @@ -51,7 +64,8 @@ public IEnumerable GetTaskModules() Id = 4, Name = "Изоморфизм", Description = "Модуль Насти", - Version = "1.0" + Version = "1.0", + Subject = subjects.ElementAt(0) }; // taskModule #5 @@ -60,7 +74,8 @@ public IEnumerable GetTaskModules() Id = 5, Name = "Построить граф по матрице смежности", Description = "Модуль Эллины", - Version = "1.0" + Version = "1.0", + Subject = subjects.ElementAt(0) }; // taskModule #6 @@ -69,7 +84,8 @@ public IEnumerable GetTaskModules() Id = 6, Name = "Построить матрицу смежности по графу", Description = "Модуль Наташи", - Version = "1.0" + Version = "1.0", + Subject = subjects.ElementAt(0) }; // taskModule #7 @@ -78,7 +94,8 @@ public IEnumerable GetTaskModules() Id = 7, Name = "Построить матрицу инцидентности по графу", Description = "Модуль Наташи", - Version = "1.0" + Version = "1.0", + Subject = subjects.ElementAt(0) }; } diff --git a/GraphLabs.Backend.Domain/Mark.cs b/GraphLabs.Backend.Domain/Mark.cs new file mode 100644 index 0000000..4522062 --- /dev/null +++ b/GraphLabs.Backend.Domain/Mark.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace GraphLabs.Backend.Domain +{ + public enum MarkRU + { + Two = 2, + Three = 3, + Four = 4, + Five = 5 + } + + public enum MarkEU + { + F = 0, + E = 60, + D = 65, + C = 75, + B = 85, + A = 90 + } + + public enum Difficulty + { + Three = 3, + Four = 4, + Five = 5 + } +} diff --git a/GraphLabs.Backend.Domain/Student.cs b/GraphLabs.Backend.Domain/Student.cs index 1617535..21336c2 100644 --- a/GraphLabs.Backend.Domain/Student.cs +++ b/GraphLabs.Backend.Domain/Student.cs @@ -7,5 +7,7 @@ public class Student : User public virtual string Group { get; set; } public virtual ICollection Logs { get; set; } + + public virtual ICollection TestResults { get; set; } } } \ No newline at end of file diff --git a/GraphLabs.Backend.Domain/Subject.cs b/GraphLabs.Backend.Domain/Subject.cs new file mode 100644 index 0000000..1896a6d --- /dev/null +++ b/GraphLabs.Backend.Domain/Subject.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace GraphLabs.Backend.Domain +{ + public class Subject + { + public virtual long Id { get; set; } + + public virtual string Name { get; set; } + + public virtual string Description { get; set; } + + public virtual ICollection TaskModules { get; set; } + + public virtual ICollection TestQuestions { get; set; } + } +} diff --git a/GraphLabs.Backend.Domain/TaskModule.cs b/GraphLabs.Backend.Domain/TaskModule.cs index 0108ad0..4542da8 100644 --- a/GraphLabs.Backend.Domain/TaskModule.cs +++ b/GraphLabs.Backend.Domain/TaskModule.cs @@ -13,6 +13,8 @@ public class TaskModule public virtual string Description { get; set; } public virtual string Version { get; set; } + + public virtual Subject Subject { get; set; } public virtual ICollection Variants { get; set; } } diff --git a/GraphLabs.Backend.Domain/TestAnswer.cs b/GraphLabs.Backend.Domain/TestAnswer.cs new file mode 100644 index 0000000..51b1684 --- /dev/null +++ b/GraphLabs.Backend.Domain/TestAnswer.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace GraphLabs.Backend.Domain +{ + public class TestAnswer + { + public virtual long Id { get; set; } + + public virtual string Answer { get; set; } + + public virtual bool IsRight { get; set; } + + public virtual TestQuestionVersion TestQuestionVersion { get; set; } + } +} diff --git a/GraphLabs.Backend.Domain/TestQuestion.cs b/GraphLabs.Backend.Domain/TestQuestion.cs new file mode 100644 index 0000000..efb129e --- /dev/null +++ b/GraphLabs.Backend.Domain/TestQuestion.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace GraphLabs.Backend.Domain +{ + public class TestQuestion + { + public virtual long Id { get; set; } + + public virtual Subject Subject { get; set; } + + public virtual ICollection TestQuestionVersions { get; set; } + } +} diff --git a/GraphLabs.Backend.Domain/TestQuestionVersion.cs b/GraphLabs.Backend.Domain/TestQuestionVersion.cs new file mode 100644 index 0000000..78b4cb5 --- /dev/null +++ b/GraphLabs.Backend.Domain/TestQuestionVersion.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace GraphLabs.Backend.Domain +{ + public class TestQuestionVersion + { + public virtual long Id { get; set; } + + public virtual string PlainText { get; set; } + + public virtual Difficulty Difficulty { get; set; } + + public virtual int MaxScore { get; set; } + + public virtual TestQuestion TestQuestion { get; set; } + + public virtual ICollection TestStudentAnswers { get; set; } + + public virtual ICollection TestAnswers { get; set; } + } +} diff --git a/GraphLabs.Backend.Domain/TestResult.cs b/GraphLabs.Backend.Domain/TestResult.cs new file mode 100644 index 0000000..5fce67a --- /dev/null +++ b/GraphLabs.Backend.Domain/TestResult.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace GraphLabs.Backend.Domain +{ + public class TestResult + { + public virtual long Id { get; set; } + + public virtual DateTime DateTime { get; set; } + + public virtual int Score { get; set; } + + public virtual MarkEU MarkEU { get; set; } + + public virtual MarkRU MarkRU { get; set; } + + public virtual Student Student { get; set; } + + public virtual ICollection TestStudentAnswer { get; set; } + } +} diff --git a/GraphLabs.Backend.Domain/TestStudentAnswer.cs b/GraphLabs.Backend.Domain/TestStudentAnswer.cs new file mode 100644 index 0000000..f21412d --- /dev/null +++ b/GraphLabs.Backend.Domain/TestStudentAnswer.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace GraphLabs.Backend.Domain +{ + public class TestStudentAnswer + { + public virtual long Id { get; set; } + + public virtual long AnswerId { get; set; } + + public virtual string Answer { get; set; } + + public virtual bool IsRight { get; set; } + + public virtual TestResult TestResult { get; set; } + + public virtual TestQuestionVersion TestQuestionVersion { get; set; } + } +} diff --git a/README.md b/README.md index 879cbe6..ed22e16 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ - [X] Научить логи заданий получать пользователя из данных аутентификации - [ ] Logout - [x] Лог выполнения +- [x] Подсистема тестирования студентов ## Технологии * Asp.Net Core @@ -65,6 +66,7 @@ _Authorization_ : "Bearer бла-бла-бла", где бла-бла-бла - ## Примеры использования +#### Лабораторный комплекс **Метаданные:** > **GET** http://localhost:5000/odata/$metadata @@ -187,4 +189,130 @@ _Body:_ ```binary data: zip archive``` **Запросить информацию о текущем пользователе:** -> **GET** http://localhost:5000/odata/currentUser \ No newline at end of file +> **GET** http://localhost:5000/odata/currentUser + +#### Подсистема тестирования студентов + +**Список тем:** +> **GET** http://localhost:5000/odata/subjects + +**Тема с идентификатором 1:** +> **GET** http://localhost:5000/odata/subjects(1) + +**Загрузить название новой темы:** +> **POST** http://localhost:5000/odata/subjects +_Content-Type:_ application/json +_Body:_ +```json +{ + "Name": "Название темы", + "Description": "Описание темы" +} +``` + +**Список вопросов:** +> **GET** http://localhost:5000/odata/testQuestions + +**Вопрос с идентификатором 1:** +> **GET** http://localhost:5000/odata/testQuestions(1) + +**Загрузить новый вопрос:** +> **POST** http://localhost:5000/odata/testQuestions +_Content-Type:_ application/json +_Body:_ +```json +{ + "PlainText": "Текст задания", + "Difficulty": "Four", + "MaxScore": 10, + "SubjectId": 2 +} +``` + +**Обновить вопрос с идентификатором 1:** +> **POST** http://localhost:5000/odata/testQuestions(1) +_Content-Type:_ application/json +_Body:_ +```json +{ + "PlainText": "Текст задания", + "Difficulty": "Five", + "MaxScore": 15 +} +``` + +**Список версий вопроса:** +> **GET** http://localhost:5000/odata/testQuestionVersions + +**Версия вопроса с идентификатором 1:** +> **GET** http://localhost:5000/odata/testQuestionVersions(1) + +**Создание варианта с одним вопросом по теме 1 и двумя вопросами по теме 2:** +> **POST** http://localhost:5000/odata/testQuestions +_Content-Type:_ application/json +_Body:_ +```json +[ + { + "SubjectId": 1, + "Quantity": 1 + }, + { + "SubjectId": 2, + "Quantity": 2 + } +] +``` + +**Список ответов:** +> **GET** http://localhost:5000/odata/testAnswers + +**Ответ с идентификатором 1:** +> **GET** http://localhost:5000/odata/testAnswers(1) + +**Загрузить новый ответ для вопроса 2 (TestQuestionVersionId = 2):** +> **POST** http://localhost:5000/odata/testAnswers(2) +_Content-Type:_ application/json +_Body:_ +```json +{ + "Answer": "Ответ на задание", + "IsRight": 1, +} +``` + +**Список ответов студента:** +> **GET** http://localhost:5000/odata/testStudentAnswers + +**Ответ студента с идентификатором 1:** +> **GET** http://localhost:5000/odata/testStudentAnswers(1) + +**Список всех результатов:** +> **GET** http://localhost:5000/odata/testResults + +**Результат с идентификатором 1:** +> **GET** http://localhost:5000/odata/testResults(1) + +**Загрузить новый результат:** +> **POST** http://localhost:5000/odata/testAnswers(2) +_Content-Type:_ application/json +_Body:_ +```json +[ + { + "TestQuestionVersionId": 1, + "Answer": "Ответ на первое задание", + "AnswerId": 1, + "IsRight": 1, + "StudentId": 2 + }, + { + "TestQuestionVersionId": 2, + "Answer": "Ответ на второе задание", + "AnswerId": 3, + "IsRight": 0, + "StudentId": 2 + } +] +``` +