diff --git a/HwProj.Common/HwProj.Models/CoursesService/ViewModels/CriterionViewModel.cs b/HwProj.Common/HwProj.Models/CoursesService/ViewModels/CriterionViewModel.cs
index 7f07a540a..c8005e5e3 100644
--- a/HwProj.Common/HwProj.Models/CoursesService/ViewModels/CriterionViewModel.cs
+++ b/HwProj.Common/HwProj.Models/CoursesService/ViewModels/CriterionViewModel.cs
@@ -4,7 +4,8 @@ namespace HwProj.Models.CoursesService.ViewModels
{
public enum CriterionType
{
- Free = 0
+ Free = 0,
+ Deadline = 1
}
public class CriterionViewModel
@@ -13,5 +14,6 @@ public class CriterionViewModel
public CriterionType Type { get; set; }
[Required] public string Name { get; set; } = null!;
[Range(0, int.MaxValue)] public int MaxPoints { get; set; }
+ public string? Arguments { get; set; }
}
}
diff --git a/HwProj.CoursesService/HwProj.CoursesService.API/Domains/MappingExtensions.cs b/HwProj.CoursesService/HwProj.CoursesService.API/Domains/MappingExtensions.cs
index 152bf0410..48ce7a8ea 100644
--- a/HwProj.CoursesService/HwProj.CoursesService.API/Domains/MappingExtensions.cs
+++ b/HwProj.CoursesService/HwProj.CoursesService.API/Domains/MappingExtensions.cs
@@ -64,7 +64,8 @@ public static HomeworkTaskViewModel ToHomeworkTaskViewModel(this HomeworkTask ta
Id = c.Id,
Type = (CriterionType)c.Type,
Name = c.Name,
- MaxPoints = c.MaxPoints
+ MaxPoints = c.MaxPoints,
+ Arguments = c.Arguments
})
.ToList(),
};
@@ -120,7 +121,8 @@ public static CoursePreview ToCoursePreview(this Course course)
Id = criterion.Id,
Type = (Models.CriterionType)criterion.Type,
Name = criterion.Name,
- MaxPoints = criterion.MaxPoints
+ MaxPoints = criterion.MaxPoints,
+ Arguments = criterion.Arguments
};
public static HomeworkTask ToHomeworkTask(this PostTaskViewModel postTaskViewModel)
diff --git a/HwProj.CoursesService/HwProj.CoursesService.API/Migrations/20260427093000_CriterionArguments.Designer.cs b/HwProj.CoursesService/HwProj.CoursesService.API/Migrations/20260427093000_CriterionArguments.Designer.cs
new file mode 100644
index 000000000..6865af99a
--- /dev/null
+++ b/HwProj.CoursesService/HwProj.CoursesService.API/Migrations/20260427093000_CriterionArguments.Designer.cs
@@ -0,0 +1,16 @@
+//
+using HwProj.CoursesService.API.Models;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+
+#nullable disable
+
+namespace HwProj.CoursesService.API.Migrations
+{
+ [DbContext(typeof(CourseContext))]
+ [Migration("20260427093000_CriterionArguments")]
+ partial class CriterionArguments
+ {
+ }
+}
diff --git a/HwProj.CoursesService/HwProj.CoursesService.API/Migrations/20260427093000_CriterionArguments.cs b/HwProj.CoursesService/HwProj.CoursesService.API/Migrations/20260427093000_CriterionArguments.cs
new file mode 100644
index 000000000..fac8c4c21
--- /dev/null
+++ b/HwProj.CoursesService/HwProj.CoursesService.API/Migrations/20260427093000_CriterionArguments.cs
@@ -0,0 +1,25 @@
+using Microsoft.EntityFrameworkCore.Migrations;
+
+#nullable disable
+
+namespace HwProj.CoursesService.API.Migrations
+{
+ public partial class CriterionArguments : Migration
+ {
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.AddColumn(
+ name: "Arguments",
+ table: "Criteria",
+ type: "nvarchar(max)",
+ nullable: true);
+ }
+
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropColumn(
+ name: "Arguments",
+ table: "Criteria");
+ }
+ }
+}
diff --git a/HwProj.CoursesService/HwProj.CoursesService.API/Migrations/CourseContextModelSnapshot.cs b/HwProj.CoursesService/HwProj.CoursesService.API/Migrations/CourseContextModelSnapshot.cs
index 4b3bb83b5..199917cf8 100644
--- a/HwProj.CoursesService/HwProj.CoursesService.API/Migrations/CourseContextModelSnapshot.cs
+++ b/HwProj.CoursesService/HwProj.CoursesService.API/Migrations/CourseContextModelSnapshot.cs
@@ -128,6 +128,9 @@ protected override void BuildModel(ModelBuilder modelBuilder)
b.Property("MaxPoints")
.HasColumnType("int");
+ b.Property("Arguments")
+ .HasColumnType("nvarchar(max)");
+
b.Property("Name")
.HasColumnType("nvarchar(max)");
diff --git a/HwProj.CoursesService/HwProj.CoursesService.API/Models/Criterion.cs b/HwProj.CoursesService/HwProj.CoursesService.API/Models/Criterion.cs
index debc0b99b..1c65c1a2c 100644
--- a/HwProj.CoursesService/HwProj.CoursesService.API/Models/Criterion.cs
+++ b/HwProj.CoursesService/HwProj.CoursesService.API/Models/Criterion.cs
@@ -5,7 +5,8 @@ namespace HwProj.CoursesService.API.Models
{
public enum CriterionType
{
- Free = 0
+ Free = 0,
+ Deadline = 1
}
public class Criterion : IEntity
@@ -17,5 +18,6 @@ public class Criterion : IEntity
public CriterionType Type { get; set; }
public string Name { get; set; }
public int MaxPoints { get; set; }
+ public string? Arguments { get; set; }
}
}
diff --git a/HwProj.CoursesService/HwProj.CoursesService.API/Repositories/TasksRepository.cs b/HwProj.CoursesService/HwProj.CoursesService.API/Repositories/TasksRepository.cs
index 769a23be6..b8405e9ee 100644
--- a/HwProj.CoursesService/HwProj.CoursesService.API/Repositories/TasksRepository.cs
+++ b/HwProj.CoursesService/HwProj.CoursesService.API/Repositories/TasksRepository.cs
@@ -89,10 +89,11 @@ public bool Equals(Criterion? x, Criterion? y)
x.TaskId == y.TaskId &&
x.Type == y.Type &&
x.Name == y.Name &&
- x.MaxPoints == y.MaxPoints;
+ x.MaxPoints == y.MaxPoints &&
+ x.Arguments == y.Arguments;
}
- public int GetHashCode(Criterion obj) => HashCode.Combine(obj.Id, obj.TaskId, obj.Name);
+ public int GetHashCode(Criterion obj) => HashCode.Combine(obj.Id, obj.TaskId, obj.Name, obj.Arguments);
}
}
}
diff --git a/HwProj.CoursesService/HwProj.CoursesService.API/Startup.cs b/HwProj.CoursesService/HwProj.CoursesService.API/Startup.cs
index 62461edc6..94eeae315 100644
--- a/HwProj.CoursesService/HwProj.CoursesService.API/Startup.cs
+++ b/HwProj.CoursesService/HwProj.CoursesService.API/Startup.cs
@@ -76,6 +76,17 @@ public void Configure(IApplicationBuilder app, IHostEnvironment env, CourseConte
app.UseEndpoints(x => x.MapControllers());
app.UseDatabase(env, context);
+ EnsureCriterionArgumentsColumn(context);
+ }
+
+ private static void EnsureCriterionArgumentsColumn(CourseContext context)
+ {
+ context.Database.ExecuteSqlRaw(@"
+IF OBJECT_ID(N'[Criteria]', N'U') IS NOT NULL
+ AND COL_LENGTH(N'[Criteria]', N'Arguments') IS NULL
+BEGIN
+ ALTER TABLE [Criteria] ADD [Arguments] nvarchar(max) NULL;
+END");
}
}
}
diff --git a/hwproj.front/package-lock.json b/hwproj.front/package-lock.json
index 1c7b32e30..b6f4352cf 100644
--- a/hwproj.front/package-lock.json
+++ b/hwproj.front/package-lock.json
@@ -4977,6 +4977,7 @@
"version": "6.5.16",
"resolved": "https://registry.npmjs.org/@storybook/addons/-/addons-6.5.16.tgz",
"integrity": "sha512-p3DqQi+8QRL5k7jXhXmJZLsE/GqHqyY6PcoA1oNTJr0try48uhTGUOYkgzmqtDaa/qPFO5LP+xCPzZXckGtquQ==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@storybook/api": "6.5.16",
@@ -5004,12 +5005,14 @@
"version": "0.13.11",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
"integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==",
+ "dev": true,
"license": "MIT"
},
"node_modules/@storybook/api": {
"version": "6.5.16",
"resolved": "https://registry.npmjs.org/@storybook/api/-/api-6.5.16.tgz",
"integrity": "sha512-HOsuT8iomqeTMQJrRx5U8nsC7lJTwRr1DhdD0SzlqL4c80S/7uuCy4IZvOt4sYQjOzW5fOo/kamcoBXyLproTA==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@storybook/channels": "6.5.16",
@@ -5043,6 +5046,7 @@
"version": "0.13.11",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
"integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==",
+ "dev": true,
"license": "MIT"
},
"node_modules/@storybook/builder-webpack4": {
@@ -5441,6 +5445,7 @@
"version": "6.5.16",
"resolved": "https://registry.npmjs.org/@storybook/channels/-/channels-6.5.16.tgz",
"integrity": "sha512-VylzaWQZaMozEwZPJdyJoz+0jpDa8GRyaqu9TGG6QGv+KU5POoZaGLDkRE7TzWkyyP0KQLo80K99MssZCpgSeg==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"core-js": "^3.8.2",
@@ -5500,6 +5505,7 @@
"version": "6.5.16",
"resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-6.5.16.tgz",
"integrity": "sha512-pxcNaCj3ItDdicPTXTtmYJE3YC1SjxFrBmHcyrN+nffeNyiMuViJdOOZzzzucTUG0wcOOX8jaSyak+nnHg5H1Q==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"core-js": "^3.8.2",
@@ -5514,6 +5520,7 @@
"version": "6.5.16",
"resolved": "https://registry.npmjs.org/@storybook/components/-/components-6.5.16.tgz",
"integrity": "sha512-LzBOFJKITLtDcbW9jXl0/PaG+4xAz25PK8JxPZpIALbmOpYWOAPcO6V9C2heX6e6NgWFMUxjplkULEk9RCQMNA==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@storybook/client-logger": "6.5.16",
@@ -5538,6 +5545,7 @@
"version": "0.13.11",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
"integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==",
+ "dev": true,
"license": "MIT"
},
"node_modules/@storybook/core": {
@@ -5704,6 +5712,7 @@
"version": "6.5.16",
"resolved": "https://registry.npmjs.org/@storybook/core-events/-/core-events-6.5.16.tgz",
"integrity": "sha512-qMZQwmvzpH5F2uwNUllTPg6eZXr2OaYZQRRN8VZJiuorZzDNdAFmiVWMWdkThwmyLEJuQKXxqCL8lMj/7PPM+g==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"core-js": "^3.8.2"
@@ -5804,6 +5813,7 @@
"version": "0.0.2--canary.4566f4d.1",
"resolved": "https://registry.npmjs.org/@storybook/csf/-/csf-0.0.2--canary.4566f4d.1.tgz",
"integrity": "sha512-9OVvMVh3t9znYZwb0Svf/YQoxX2gVOeQTGe2bses2yj+a3+OJnCrUF3/hGv6Em7KujtOdL2LL+JnG49oMVGFgQ==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"lodash": "^4.17.15"
@@ -6276,6 +6286,7 @@
"version": "6.5.16",
"resolved": "https://registry.npmjs.org/@storybook/router/-/router-6.5.16.tgz",
"integrity": "sha512-ZgeP8a5YV/iuKbv31V8DjPxlV4AzorRiR8OuSt/KqaiYXNXlOoQDz/qMmiNcrshrfLpmkzoq7fSo4T8lWo2UwQ==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@storybook/client-logger": "6.5.16",
@@ -6297,12 +6308,14 @@
"version": "0.13.11",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
"integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==",
+ "dev": true,
"license": "MIT"
},
"node_modules/@storybook/semver": {
"version": "7.3.2",
"resolved": "https://registry.npmjs.org/@storybook/semver/-/semver-7.3.2.tgz",
"integrity": "sha512-SWeszlsiPsMI0Ps0jVNtH64cI5c0UF3f7KgjVKJoNP30crQ6wUSddY2hsdeczZXEKVJGEn50Q60flcGsQGIcrg==",
+ "dev": true,
"license": "ISC",
"dependencies": {
"core-js": "^3.6.5",
@@ -6319,6 +6332,7 @@
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
"integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"locate-path": "^5.0.0",
@@ -6332,6 +6346,7 @@
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
"integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"p-locate": "^4.1.0"
@@ -6344,6 +6359,7 @@
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
"integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"p-try": "^2.0.0"
@@ -6359,6 +6375,7 @@
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
"integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"p-limit": "^2.2.0"
@@ -6442,6 +6459,7 @@
"version": "6.5.16",
"resolved": "https://registry.npmjs.org/@storybook/theming/-/theming-6.5.16.tgz",
"integrity": "sha512-hNLctkjaYLRdk1+xYTkC1mg4dYz2wSv6SqbLpcKMbkPHTE0ElhddGPHQqB362md/w9emYXNkt1LSMD8Xk9JzVQ==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@storybook/client-logger": "6.5.16",
@@ -6462,6 +6480,7 @@
"version": "0.13.11",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
"integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==",
+ "dev": true,
"license": "MIT"
},
"node_modules/@storybook/ui": {
@@ -7088,6 +7107,7 @@
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@types/is-function/-/is-function-1.0.3.tgz",
"integrity": "sha512-/CLhCW79JUeLKznI6mbVieGbl4QU5Hfn+6udw1YHZoofASjbQ5zaP5LzAUZYDpRYEjS4/P+DhEgyJ/PQmGGTWw==",
+ "dev": true,
"license": "MIT"
},
"node_modules/@types/isomorphic-fetch": {
@@ -7456,6 +7476,7 @@
"version": "1.18.8",
"resolved": "https://registry.npmjs.org/@types/webpack-env/-/webpack-env-1.18.8.tgz",
"integrity": "sha512-G9eAoJRMLjcvN4I08wB5I7YofOb/kaJNd5uoCMX+LbKXTPCF+ZIHuqTnFaK9Jz1rgs035f9JUPUhNFtqgucy/A==",
+ "dev": true,
"license": "MIT"
},
"node_modules/@types/webpack-sources": {
@@ -18027,6 +18048,7 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/is-function/-/is-function-1.0.2.tgz",
"integrity": "sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ==",
+ "dev": true,
"license": "MIT"
},
"node_modules/is-generator-function": {
@@ -18149,6 +18171,7 @@
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz",
"integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"call-bound": "^1.0.2",
@@ -18226,6 +18249,7 @@
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz",
"integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"call-bound": "^1.0.2",
@@ -18697,13 +18721,6 @@
"node": ">= 10.13.0"
}
},
- "node_modules/jquery": {
- "version": "3.7.1",
- "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz",
- "integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==",
- "license": "MIT",
- "peer": true
- },
"node_modules/js-sha3": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz",
@@ -19343,6 +19360,7 @@
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/map-or-similar/-/map-or-similar-1.5.0.tgz",
"integrity": "sha512-0aF7ZmVon1igznGI4VS30yugpduQW3y3GkcgGJOp7d8x8QrizhigUxjI/m2UojsXXto+jLAH3KSz+xOJTiORjg==",
+ "dev": true,
"license": "MIT"
},
"node_modules/map-visit": {
@@ -20535,6 +20553,7 @@
"version": "1.11.3",
"resolved": "https://registry.npmjs.org/memoizerific/-/memoizerific-1.11.3.tgz",
"integrity": "sha512-/EuHYwAPdLtXwAwSZkh/Gutery6pD2KYd44oQLhAvQp/50mpyduZh8Q7PYHXTCJ+wuXxt7oij2LXyIJOOYFPog==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"map-or-similar": "^1.5.0"
@@ -22501,6 +22520,7 @@
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
@@ -22703,6 +22723,7 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
@@ -22855,18 +22876,6 @@
"node": ">=6"
}
},
- "node_modules/popper.js": {
- "version": "1.16.1",
- "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz",
- "integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==",
- "deprecated": "You can find the new Popper v2 at @popperjs/core, this package is dedicated to the legacy v1",
- "license": "MIT",
- "peer": true,
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/popperjs"
- }
- },
"node_modules/portable-fetch": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/portable-fetch/-/portable-fetch-3.0.0.tgz",
@@ -27164,6 +27173,7 @@
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz",
"integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"call-bound": "^1.0.2",
@@ -28040,6 +28050,7 @@
"version": "2.14.4",
"resolved": "https://registry.npmjs.org/store2/-/store2-2.14.4.tgz",
"integrity": "sha512-srTItn1GOvyvOycgxjAnPA63FZNwy0PTyUBFMHRM+hVFltAeoh0LmNBz9SZqUS9mMqGk8rfyWyXn3GH5ReJ8Zw==",
+ "dev": true,
"license": "MIT"
},
"node_modules/stream-browserify": {
@@ -28673,6 +28684,7 @@
"version": "6.0.8",
"resolved": "https://registry.npmjs.org/telejson/-/telejson-6.0.8.tgz",
"integrity": "sha512-nerNXi+j8NK1QEfBHtZUN/aLdDcyupA//9kAboYLrtzZlPLpUfqbVGWb9zz91f/mIjRbAYhbgtnJHY8I1b5MBg==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@types/is-function": "^1.0.0",
@@ -28689,6 +28701,7 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/isobject/-/isobject-4.0.0.tgz",
"integrity": "sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
@@ -29210,6 +29223,7 @@
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.2.0.tgz",
"integrity": "sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=6.10"
@@ -30102,6 +30116,7 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
+ "dev": true,
"license": "MIT"
},
"node_modules/util.promisify": {
diff --git a/hwproj.front/src/api/api.ts b/hwproj.front/src/api/api.ts
index 4e0c6dfec..e42934f1c 100644
--- a/hwproj.front/src/api/api.ts
+++ b/hwproj.front/src/api/api.ts
@@ -655,7 +655,8 @@ export interface CreateHomeworkViewModel {
* @enum {string}
*/
export enum CriterionType {
- NUMBER_0 = 0
+ NUMBER_0 = 0,
+ NUMBER_1 = 1
}
/**
*
@@ -687,6 +688,12 @@ export interface CriterionViewModel {
* @memberof CriterionViewModel
*/
maxPoints?: number;
+ /**
+ *
+ * @type {string}
+ * @memberof CriterionViewModel
+ */
+ arguments?: string;
}
/**
*
diff --git a/hwproj.front/src/components/Homeworks/CourseHomeworkExperimental.tsx b/hwproj.front/src/components/Homeworks/CourseHomeworkExperimental.tsx
index 04f6b8911..ea6486fbe 100644
--- a/hwproj.front/src/components/Homeworks/CourseHomeworkExperimental.tsx
+++ b/hwproj.front/src/components/Homeworks/CourseHomeworkExperimental.tsx
@@ -43,6 +43,8 @@ import {FilesHandler} from "@/components/Files/FilesHandler";
import GroupSelector from "../Common/GroupSelector";
import GroupIcon from '@mui/icons-material/Group';
import AssignmentIcon from '@mui/icons-material/Assignment';
+import ErrorsHandler from "@/components/Utils/ErrorsHandler";
+import {enqueueSnackbar} from "notistack";
export interface HomeworkAndFilesInfo {
homework: HomeworkViewModel & { isModified?: boolean },
@@ -243,43 +245,54 @@ const CourseHomeworkEditor: FC<{
e.preventDefault()
setHandleSubmitLoading(true)
- const update = {
- homeworkId: homeworkId,
- title: title!,
- description: description,
- tags: tags,
- hasDeadline: metadata.hasDeadline,
- deadlineDate: metadata.deadlineDate,
- isDeadlineStrict: metadata.isDeadlineStrict,
- publicationDate: metadata.publicationDate,
- groupId: selectedGroupId,
- actionOptions: editOptions,
- tasks: isNewHomework ? homework.tasks!.map(t => {
- const task: PostTaskViewModel = {
- ...t,
- title: t.title!,
- maxRating: t.maxRating!
- }
- return task
- }) : []
- }
+ try {
+ const update = {
+ homeworkId: homeworkId,
+ title: title!,
+ description: description,
+ tags: tags,
+ hasDeadline: metadata.hasDeadline,
+ deadlineDate: metadata.deadlineDate,
+ isDeadlineStrict: metadata.isDeadlineStrict,
+ publicationDate: metadata.publicationDate,
+ groupId: selectedGroupId,
+ actionOptions: editOptions,
+ tasks: isNewHomework ? homework.tasks!.map(t => {
+ const task: PostTaskViewModel = {
+ ...t,
+ title: t.title!,
+ maxRating: t.maxRating!,
+ criteria: t.criteria || []
+ }
+ return task
+ }) : []
+ }
- const updatedHomework = isNewHomework
- ? await ApiSingleton.homeworksApi.homeworksAddHomework(courseId!, update)
- : await ApiSingleton.homeworksApi.homeworksUpdateHomework(+homeworkId!, update)
-
- const updatedHomeworkId = updatedHomework.value!.id!
- await handleFilesChange(
- courseId, CourseUnitType.Homework, updatedHomeworkId,
- props.onStartProcessing,
- () => {
- if (isNewHomework) props.onUpdate({
- homework: update,
- isDeleted: true
- }) // remove fake homework
- props.onUpdate({homework: updatedHomework.value!, isSaved: true});
- },
- );
+ const updatedHomework = isNewHomework
+ ? await ApiSingleton.homeworksApi.homeworksAddHomework(courseId!, update)
+ : await ApiSingleton.homeworksApi.homeworksUpdateHomework(+homeworkId!, update)
+
+ const updatedHomeworkId = updatedHomework.value!.id!
+ await handleFilesChange(
+ courseId, CourseUnitType.Homework, updatedHomeworkId,
+ props.onStartProcessing,
+ () => {
+ if (isNewHomework) props.onUpdate({
+ homework: update,
+ isDeleted: true
+ }) // remove fake homework
+ props.onUpdate({homework: updatedHomework.value!, isSaved: true});
+ },
+ );
+ } catch (error) {
+ const errors = await ErrorsHandler.getErrorMessages(error as Response, "errors");
+ enqueueSnackbar(errors[0] || "Не удалось сохранить задание", {
+ variant: "error",
+ autoHideDuration: 4000,
+ });
+ } finally {
+ setHandleSubmitLoading(false)
+ }
}
const isDisabled = hasErrors || !isLoaded || taskHasErrors
@@ -569,4 +582,4 @@ const CourseHomeworkExperimental: FC<{
}
}
-export default CourseHomeworkExperimental;
\ No newline at end of file
+export default CourseHomeworkExperimental;
diff --git a/hwproj.front/src/components/Solutions/TaskSolutionComponent.tsx b/hwproj.front/src/components/Solutions/TaskSolutionComponent.tsx
index b5276a530..6fb326635 100644
--- a/hwproj.front/src/components/Solutions/TaskSolutionComponent.tsx
+++ b/hwproj.front/src/components/Solutions/TaskSolutionComponent.tsx
@@ -49,6 +49,8 @@ import {UserAvatar} from "@/components/Common/UserAvatar";
type TaskWithCriteria = HomeworkTaskViewModel & {};
+const CriterionTypeDeadline = 1;
+
type CriterionRating = {
criterionId: number;
name: string;
@@ -136,9 +138,23 @@ const TaskSolutionComponent: FC = (props) => {
const initialDraft = loadCriteriaDraft();
+ const getDeadlineCriterionValue = (criterion: { arguments?: string; maxPoints?: number }) => {
+ if (!props.solution?.publicationDate || !criterion.arguments) return Number.NaN;
+
+ const solutionDate = new Date(props.solution.publicationDate).getTime();
+ const deadlineDate = new Date(criterion.arguments).getTime();
+
+ if (Number.isNaN(solutionDate) || Number.isNaN(deadlineDate)) return Number.NaN;
+
+ return solutionDate <= deadlineDate ? 0 : -(criterion.maxPoints ?? 0);
+ };
+
const [criterionRatings, setCriterionRatings] = useState(() =>
(taskWithCriteria.criteria ?? []).map(c => {
const id = c.id!;
+ const deadlineValue = c.type === CriterionTypeDeadline
+ ? getDeadlineCriterionValue(c)
+ : Number.NaN;
const draftValue = initialDraft?.criteria
?.find(x => x.criterionId === id)?.value;
@@ -146,7 +162,7 @@ const TaskSolutionComponent: FC = (props) => {
criterionId: id,
name: c.name ?? "",
maxPoints: c.maxPoints ?? 0,
- value: draftValue || NaN,
+ value: Number.isFinite(deadlineValue) ? deadlineValue : (draftValue ?? NaN),
comment: "",
};
})
@@ -171,6 +187,9 @@ const TaskSolutionComponent: FC = (props) => {
setCriterionRatings(
(taskWithCriteria.criteria ?? []).map(c => {
const id = c.id ?? 0;
+ const deadlineValue = c.type === CriterionTypeDeadline
+ ? getDeadlineCriterionValue(c)
+ : Number.NaN;
const draftValue = draft?.criteria
?.find(x => x.criterionId === id)?.value;
@@ -178,7 +197,7 @@ const TaskSolutionComponent: FC = (props) => {
criterionId: id,
name: c.name ?? "",
maxPoints: c.maxPoints ?? 0,
- value: draftValue || NaN,
+ value: Number.isFinite(deadlineValue) ? deadlineValue : (draftValue ?? NaN),
comment: "",
};
})
@@ -195,22 +214,24 @@ const TaskSolutionComponent: FC = (props) => {
useEffect(() => {
if (!hasCriteria || !state.addBonusPoints || !state.clickedForRate || !criteriaModified) return;
- const criteriaTotal = criterionRatings.reduce(
+ const criteriaTotalRaw = criterionRatings.reduce(
(sum, c) => sum + (Number.isFinite(c.value) ? Number(c.value) : 0),
0
);
+ const criteriaTotal = Math.max(0, criteriaTotalRaw);
const total = criteriaTotal + (Number.isFinite(extraScore) ? extraScore : 0);
setState(prev => ({...prev, points: total}));
}, [criterionRatings, extraScore, hasCriteria, state.addBonusPoints, state.clickedForRate, criteriaModified]);
- const criteriaSum =
+ const criteriaTotalRaw =
criterionRatings.reduce(
(sum, c) => sum + (Number.isFinite(c.value) ? Number(c.value) : 0),
0
- ) + (Number.isFinite(extraScore) ? extraScore : 0);
+ );
+ const criteriaSum = Math.max(0, criteriaTotalRaw) + (Number.isFinite(extraScore) ? extraScore : 0);
- const isRateButtonDisabled = hasCriteria && criteriaSum < 0;
+ const isRateButtonDisabled = false;
const [isCtrlPressed, setIsCtrlPressed] = useState(false)
@@ -309,7 +330,21 @@ const TaskSolutionComponent: FC = (props) => {
if (!hasCriteria || criterionRatings.length === 0) return baseComment;
const rows: string[] = criterionRatings.map(cr => {
+ const criterion = taskWithCriteria.criteria?.find(c => c.id === cr.criterionId);
const safeValue = Number.isFinite(cr.value) ? cr.value : 0;
+
+ if (criterion?.type === CriterionTypeDeadline) {
+ const deadlineDelay = criterion.arguments && solution?.publicationDate
+ ? getDatesDiff(solution.publicationDate!, new Date(criterion.arguments))
+ : "";
+ const statusText = safeValue === 0
+ ? "Сдано вовремя"
+ : `Сдано позже${deadlineDelay ? ` на ${deadlineDelay}` : ""}`;
+ const valueText = safeValue === 0 ? "✅" : `${safeValue}`;
+
+ return `| ${cr.name} (${statusText.toLowerCase()}) | ${valueText} |`;
+ }
+
return `| ${cr.name} | ${safeValue} / ${cr.maxPoints} |`;
});
@@ -423,9 +458,10 @@ const TaskSolutionComponent: FC = (props) => {
return (