оценка по критерию есть, удаление корректно, отображение корректно + мелкие правки

This commit is contained in:
Татьяна Артамонова 2024-12-14 02:43:44 +04:00
parent fd31ade855
commit a954a4d741
26 changed files with 407 additions and 276 deletions

View File

@ -0,0 +1,78 @@
using CandidateReviewContracts.BindingModels;
using CandidateReviewContracts.BusinessLogicsContracts;
using CandidateReviewContracts.SearchModels;
using CandidateReviewContracts.StoragesContracts;
using CandidateReviewContracts.ViewModels;
using CandidateReviewDatabaseImplement.Implements;
using CandidateReviewDatabaseImplement.Models;
using Microsoft.Extensions.Logging;
using System.Xml.Linq;
namespace CandidateReviewBusinessLogic.BusinessLogic
{
public class AssessmentCriterionLogic : IAssessmentCriterionLogic
{
private readonly ILogger _logger;
private readonly IAssessmentCriterionStorage _assessmentCriterionStorage;
private readonly ICriterionStorage _criterionStorage;
public AssessmentCriterionLogic(ILogger<AssessmentLogic> logger, IAssessmentCriterionStorage assessmentCriterionStorage, ICriterionStorage criterionStorage)
{
_logger = logger;
_assessmentCriterionStorage = assessmentCriterionStorage;
_criterionStorage = criterionStorage;
}
public bool Create(AssessmentCriterionModel model)
{
if (_assessmentCriterionStorage.Insert(model) == null)
{
_logger.LogWarning("Insert operation failed");
return false;
}
return true;
}
public bool Delete(AssessmentCriterionModel model)
{
throw new NotImplementedException();
}
public AssessmentCriterionViewModel? ReadElement(AssessmentCriterionSearchModel model)
{
throw new NotImplementedException();
}
public List<AssessmentCriterionViewModel>? ReadList(AssessmentCriterionSearchModel? model)
{
var list = model == null ? _assessmentCriterionStorage.GetFullList() : _assessmentCriterionStorage.GetFilteredList(model);
if (list == null)
{
_logger.LogWarning("ReadList return null list");
return null;
}
List<AssessmentCriterionViewModel> result = new();
foreach (var item in list)
{
var viewModel = new AssessmentCriterionViewModel
{
Id = item.Id,
AssessmentId = item.AssessmentId,
CriterionId = item.CriterionId,
CriterionName = _criterionStorage.GetElement(new CriterionSearchModel { Id = item.CriterionId }).Name,
Value = item.Value
};
result.Add(viewModel);
}
_logger.LogInformation("ReadList. Count: {Count}", result.Count);
return result;
}
public bool Update(AssessmentCriterionModel model)
{
throw new NotImplementedException();
}
}
}

View File

@ -15,13 +15,15 @@ namespace CandidateReviewBusinessLogic.BusinessLogic
private readonly IAssessmentStorage _assessmentStorage;
private readonly IUserStorage _userStorage;
private readonly ICriterionStorage _criterionStorage;
private readonly IAssessmentCriterionStorage _assessmentCriterionStorage;
public AssessmentLogic(ILogger<AssessmentLogic> logger, IAssessmentStorage assessmentStorage, IUserStorage userStorage, ICriterionStorage criterionStorage)
public AssessmentLogic(ILogger<AssessmentLogic> logger, IAssessmentStorage assessmentStorage, IUserStorage userStorage, ICriterionStorage criterionStorage, IAssessmentCriterionStorage assessmentCriterionStorage)
{
_logger = logger;
_assessmentStorage = assessmentStorage;
_userStorage = userStorage;
_criterionStorage = criterionStorage;
_assessmentCriterionStorage = assessmentCriterionStorage;
}
public int? Create(AssessmentBindingModel model)
@ -36,6 +38,8 @@ namespace CandidateReviewBusinessLogic.BusinessLogic
return id;
}
public bool Delete(AssessmentBindingModel model)
{
CheckModel(model, false);
@ -74,8 +78,22 @@ namespace CandidateReviewBusinessLogic.BusinessLogic
return null;
}
_logger.LogInformation("ReadList. Count: {Count}", list.Count);
return list;
List<AssessmentViewModel> result = new();
foreach (var item in list)
{
var viewModel = new AssessmentViewModel
{
Id = item.Id,
ResumeId = item.ResumeId,
UserId = item.UserId,
UserName = _userStorage.GetElement(new UserSearchModel { Id = item.UserId }).Name + " " + _userStorage.GetElement(new UserSearchModel { Id = item.UserId }).Surname,
Comment = item.Comment
};
result.Add(viewModel);
}
_logger.LogInformation("ReadList. Count: {Count}", result.Count);
return result;
}
public bool Update(AssessmentBindingModel model)

View File

@ -66,8 +66,7 @@ namespace CandidateReviewBusinessLogic.BusinessLogic
ResumeId = a.ResumeId,
UserName = a.UserName,
UserId = a.UserId,
Comment = a.Comment,
AssessmentCriterions = a.AssessmentCriterions
Comment = a.Comment
}).ToList() ?? new List<AssessmentViewModel>();
var resumeViewModel = new ResumeViewModel
@ -109,8 +108,7 @@ namespace CandidateReviewBusinessLogic.BusinessLogic
ResumeId = a.ResumeId,
UserId = a.UserId,
UserName = a.UserName,
Comment = a.Comment,
AssessmentCriterions = a.AssessmentCriterions
Comment = a.Comment
}).ToList() ?? new List<AssessmentViewModel>();
var resumeViewModel = new ResumeViewModel

View File

@ -8,18 +8,18 @@ namespace CandidateReviewClientApp
{
public class APIClient
{
private static readonly HttpClient _user = new();
private static readonly HttpClient _client = new();
public static UserViewModel? User { get; set; } = null;
public static CompanyViewModel? Company { get; set; } = null;
public static void Connect(IConfiguration configuration)
{
_user.BaseAddress = new Uri(configuration["IPAddress"]);
_user.DefaultRequestHeaders.Accept.Clear();
_user.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
_client.BaseAddress = new Uri(configuration["IPAddress"]);
_client.DefaultRequestHeaders.Accept.Clear();
_client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
}
public static T? GetRequest<T>(string requestUrl)
{
var response = _user.GetAsync(requestUrl);
var response = _client.GetAsync(requestUrl);
var result = response.Result.Content.ReadAsStringAsync().Result;
if (response.Result.IsSuccessStatusCode)
{
@ -36,7 +36,7 @@ namespace CandidateReviewClientApp
try
{
// Асинхронный запрос
var response = await _user.GetAsync(requestUrl);
var response = await _client.GetAsync(requestUrl);
// Чтение содержимого ответа
var result = await response.Content.ReadAsStringAsync();
@ -64,7 +64,7 @@ namespace CandidateReviewClientApp
{
var json = JsonConvert.SerializeObject(model);
var data = new StringContent(json, Encoding.UTF8, "application/json");
var response = _user.PostAsync(requestUrl, data);
var response = _client.PostAsync(requestUrl, data);
var result = response.Result.Content.ReadAsStringAsync().Result;
if (!response.Result.IsSuccessStatusCode)
{
@ -77,7 +77,7 @@ namespace CandidateReviewClientApp
var json = JsonConvert.SerializeObject(model);
var data = new StringContent(json, Encoding.UTF8, "application/json");
var response = await _user.PostAsync(requestUrl, data);
var response = await _client.PostAsync(requestUrl, data);
if (!response.IsSuccessStatusCode)
{
@ -103,7 +103,7 @@ namespace CandidateReviewClientApp
var json = JsonConvert.SerializeObject(model);
var data = new StringContent(json, Encoding.UTF8, "application/json");
var response = await _user.PostAsync(requestUrl, data);
var response = await _client.PostAsync(requestUrl, data);
if (!response.IsSuccessStatusCode)
{

View File

@ -19,10 +19,8 @@ namespace CandidateReviewClientApp.Controllers
_logger = logger;
}
[HttpPost]
public async Task<IActionResult> AddAssessmentCriterion(int resumeId, int[] criterion, int[] value, string comment)
public async Task<IActionResult> AddAssessmentCriterion(int resumeId, int[] criterion, int[] newValue, string comment)
{
string returnUrl = HttpContext.Request.Headers.Referer.ToString();
@ -33,33 +31,35 @@ namespace CandidateReviewClientApp.Controllers
throw new Exception("Необходима авторизация");
}
if (criterion == null || value == null || criterion.Length != value.Length)
if (criterion == null || newValue == null || criterion.Length != newValue.Length)
{
throw new ArgumentException("Массивы критериев и оценок должны быть не null и одинаковой длины.");
throw new ArgumentException("Критерии или оценки заполнены некорректно.");
}
Dictionary<int, (CriterionModel, int)> assCriterions = new();
var userId = APIClient.User?.Id;
var assessmentModel = new AssessmentBindingModel
{
ResumeId = resumeId,
UserId = userId,
Comment = comment,
AssessmentCriterions = assCriterions
Comment = comment
};
int assessmentId = await APIClient.PostRequestAsync("api/assessment/create", assessmentModel);
List<Tuple<int, int>> criterionsValues = new();
for (int i = 0; i < criterion.Length; i++)
{
var crit = APIClient.GetRequest<CriterionViewModel>($"api/criterion/details?id={criterion[i]}");
assCriterions.Add(assessmentId, (new CriterionModel { Id = crit.Id, Name = crit.Name }, value[i]));
criterionsValues.Add(Tuple.Create(criterion[i], newValue[i]));
}
foreach (var item in criterionsValues)
{
var assessmentCriterion = new AssessmentCriterionModel
{
AssessmentId = assessmentId,
CriterionId = item.Item1,
Value = item.Item2
};
APIClient.PostRequest("api/assessment/createCriterion", assessmentCriterion);
}
assessmentModel.AssessmentCriterions = assCriterions;
assessmentModel.Id = assessmentId;
APIClient.PostRequest("api/assessment/update", assessmentModel);
var user = APIClient.GetRequest<UserViewModel>($"api/user/profile?id={APIClient.User?.Id}");
@ -67,8 +67,7 @@ namespace CandidateReviewClientApp.Controllers
{
ResumeId = assessmentModel.ResumeId,
UserId = assessmentModel.UserId,
Comment = assessmentModel.Comment,
AssessmentCriterions = assessmentModel.AssessmentCriterions
Comment = assessmentModel.Comment
});
var resume = APIClient.GetRequest<ResumeViewModel>($"api/resume/details?id={resumeId}");
if (resume != null)
@ -77,8 +76,7 @@ namespace CandidateReviewClientApp.Controllers
{
ResumeId = assessmentModel.ResumeId,
UserId = assessmentModel.UserId,
Comment = assessmentModel.Comment,
AssessmentCriterions = assessmentModel.AssessmentCriterions
Comment = assessmentModel.Comment
});
}
else

View File

@ -33,6 +33,23 @@ namespace CandidateReviewClientApp.Controllers
var userAssessment = resumeAssessments?.FirstOrDefault(a => a.UserId == APIClient.User.Id);
var criterions = APIClient.GetRequest<List<CriterionViewModel>>($"api/criterion/list");
resume.Assessments = resumeAssessments ?? new List<AssessmentViewModel>();
var assessmentCriterions = new List<AssessmentCriterionViewModel>();
if (resumeAssessments != null)
{
foreach (var item in resumeAssessments)
{
assessmentCriterions.AddRange(APIClient.GetRequest<List<AssessmentCriterionViewModel>>($"api/assessment/assessmentCriterions?assessmentId={item.Id}"));
}
}
var userAC = new List<AssessmentCriterionViewModel>();
if (userAssessment != null)
{
userAC.AddRange(APIClient.GetRequest<List<AssessmentCriterionViewModel>>($"api/assessment/assessmentCriterions?assessmentId={userAssessment.Id}"));
}
ViewBag.UserAssessmentCriterions = userAC;
ViewBag.OtherAssessmentCriterions = assessmentCriterions;
ViewBag.UserAssessment = userAssessment;
ViewBag.Criterions = criterions;

View File

@ -79,25 +79,27 @@
{
foreach (var assessment in Model.Assessments)
{
// Если это пользовательская оценка, пропускаем её, так как она будет отображена отдельно.
if (ViewBag.UserAssessment is AssessmentViewModel currentUserAssessment && assessment.UserId == currentUserAssessment.UserId)
{
continue;
}
<h4>Оценка пользователя: @assessment.UserName</h4>
@foreach (var criterion in assessment.AssessmentCriterions)
@if (ViewBag.OtherAssessmentCriterions is List<AssessmentCriterionViewModel> otherCriterions)
{
<div class="row mb-3 criterion-row">
<div class="col-8">
<select name="criterion" class="form-control" disabled>
<option value="@criterion.Value.Item1.Id" selected>@criterion.Value.Item1.Name</option>
</select>
@foreach (var assessmentCriterion in otherCriterions.Where(ac => ac.AssessmentId == assessment.Id))
{
<div class="row mb-3 criterion-row">
<div class="col-8">
<select name="criterion" class="form-control" disabled>
<option value="@assessmentCriterion.CriterionId" selected>@assessmentCriterion.CriterionName</option>
</select>
</div>
<div class="col-md-4">
<input type="number" name="value" class="form-control" value="@assessmentCriterion.Value" readonly />
</div>
</div>
<div class="col-md-4">
<input type="number" name="value" class="form-control" value="@criterion.Value.Item2" readonly />
</div>
</div>
}
}
<p>Комментарий: @assessment.Comment</p>
}
@ -112,22 +114,25 @@
{
<div id="user-assessment-container">
<h4>Ваша оценка</h4>
@foreach (var criterion in userAssessment.AssessmentCriterions)
@if (ViewBag.UserAssessmentCriterions is List<AssessmentCriterionViewModel> myCriterions)
{
<div class="row mb-3 criterion-row">
<div class="col-8">
<select name="criterion" class="form-control" disabled>
<option value="@criterion.Value.Item1.Id" selected>@criterion.Value.Item1.Name</option>
</select>
@foreach (var assessmentCriterion in myCriterions)
{
<div class="row mb-3 criterion-row">
<div class="col-8">
<select name="criterion" class="form-control" disabled>
<option value="@assessmentCriterion.CriterionId" selected>@assessmentCriterion.CriterionName</option>
</select>
</div>
<div class="col-md-4">
<input type="number" name="value" class="form-control" value="@assessmentCriterion.Value" readonly />
</div>
</div>
<div class="col-md-4">
<input type="number" name="value" class="form-control" value="@criterion.Value.Item2" readonly />
</div>
</div>
}
}
<p>Комментарий: @userAssessment.Comment</p>
<a asp-controller="Assessment" asp-action="Delete" asp-route-id="@userAssessment.Id" asp-route-resumeId="@Model.Id" onclick="return confirm('Вы уверены, что хотите удалить оценку?');"
<a asp-controller="Assessment" asp-action="Delete" asp-route-id="@userAssessment.Id" asp-route-resumeId="@Model.Id"
onclick="return confirm('Вы уверены, что хотите удалить оценку?');"
class="btn btn-info btn-sm me-2" title="Просмотр">
Удалить оценку
</a>
@ -150,7 +155,7 @@
</select>
</div>
<div class="col-md-3">
<input type="number" name="value" class="form-control" min="1" max="5" placeholder="Введите значение от 1 до 5" required />
<input type="number" name="newValue" class="form-control" min="1" max="5" placeholder="Введите значение от 1 до 5" required />
</div>
<div class="col-md-1 d-flex align-items-center">
<button type="button" class="btn btn-danger btn-sm remove-criterion">Х</button>
@ -197,7 +202,7 @@
<select name="criterion" class="form-control">${selectOptions}</select>
</div>
<div class="col-md-3">
<input type="number" name="value" class="form-control" min="1" max="5" placeholder="Введите значение от 1 до 5" required />
<input type="number" name="newValue" class="form-control" min="1" max="5" placeholder="Введите значение от 1 до 5" required />
</div>
<div class="col-md-1 d-flex align-items-center">
<button type="button" class="btn btn-danger btn-sm remove-criterion">✕</button>

View File

@ -10,7 +10,7 @@ namespace CandidateReviewContracts.BindingModels
public string? Comment { get; set; }
public int Id { get; set; }
public Dictionary<int, (CriterionModel, int)> AssessmentCriterions { get; set; } = new();
//public Dictionary<int, (int, int)> AssessmentCriterions { get; set; } = new();
public int? ResumeId { get; set; }
}

View File

@ -0,0 +1,15 @@
using CandidateReviewDataModels.Models;
namespace CandidateReviewContracts.BindingModels
{
public class AssessmentCriterionModel : IAssessmentCriterionModel
{
public int AssessmentId { get; set; }
public int CriterionId { get; set; }
public int Value { get; set; }
public int Id { get; set; }
}
}

View File

@ -0,0 +1,15 @@
using CandidateReviewContracts.BindingModels;
using CandidateReviewContracts.SearchModels;
using CandidateReviewContracts.ViewModels;
namespace CandidateReviewContracts.BusinessLogicsContracts
{
public interface IAssessmentCriterionLogic
{
List<AssessmentCriterionViewModel>? ReadList(AssessmentCriterionSearchModel? model);
AssessmentCriterionViewModel? ReadElement(AssessmentCriterionSearchModel model);
bool Create(AssessmentCriterionModel model);
bool Update(AssessmentCriterionModel model);
bool Delete(AssessmentCriterionModel model);
}
}

View File

@ -0,0 +1,10 @@
namespace CandidateReviewContracts.SearchModels
{
public class AssessmentCriterionSearchModel
{
public int? Id { get; set; }
public int? AssessmentId { get; set; }
public int? CriterionId { get; set; }
public int? Value { get; set; }
}
}

View File

@ -0,0 +1,18 @@
using CandidateReviewContracts.BindingModels;
using CandidateReviewContracts.SearchModels;
using CandidateReviewContracts.ViewModels;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CandidateReviewContracts.StoragesContracts
{
public interface IAssessmentCriterionStorage
{
AssessmentCriterionViewModel? Insert(AssessmentCriterionModel model);
List<AssessmentCriterionViewModel> GetFullList();
List<AssessmentCriterionViewModel> GetFilteredList(AssessmentCriterionSearchModel model);
}
}

View File

@ -0,0 +1,17 @@
using CandidateReviewDataModels.Models;
namespace CandidateReviewContracts.ViewModels
{
public class AssessmentCriterionViewModel : IAssessmentCriterionModel
{
public int AssessmentId { get; set; }
public int CriterionId { get; set; }
public string CriterionName { get; set; } = string.Empty;
public int Value { get; set; }
public int Id { get; set; }
}
}

View File

@ -13,7 +13,5 @@ namespace CandidateReviewContracts.ViewModels
public int Id { get; set; }
public int? ResumeId { get; set; }
public Dictionary<int, (CriterionModel, int)> AssessmentCriterions { get; set; } = new();
}
}

View File

@ -1,15 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CandidateReviewDataModels.Models
{
public class CriterionModel : ICriterionModel
{
public string Name { get; set; } = string.Empty;
public int Id { get; set; }
}
}

View File

@ -0,0 +1,9 @@
namespace CandidateReviewDataModels.Models
{
public interface IAssessmentCriterionModel : IId
{
int AssessmentId { get; }
int CriterionId { get; }
int Value { get; }
}
}

View File

@ -0,0 +1,52 @@
using CandidateReviewContracts.BindingModels;
using CandidateReviewContracts.SearchModels;
using CandidateReviewContracts.StoragesContracts;
using CandidateReviewContracts.ViewModels;
using CandidateReviewDatabaseImplement.Models;
namespace CandidateReviewDatabaseImplement.Implements
{
public class AssessmentCriterionStorage : IAssessmentCriterionStorage
{
public AssessmentCriterionViewModel? Insert(AssessmentCriterionModel model)
{
var newAssessmentCriterion = AssessmentCriterion.Create(model);
if (newAssessmentCriterion == null)
{
return null;
}
using var context = new CandidateReviewDatabase();
context.AssessmentCriterions.Add(newAssessmentCriterion);
context.SaveChanges();
return newAssessmentCriterion.GetViewModel;
}
public List<AssessmentCriterionViewModel> GetFullList()
{
using var context = new CandidateReviewDatabase();
return context.AssessmentCriterions
.Select(x => x.GetViewModel)
.ToList();
}
public List<AssessmentCriterionViewModel> GetFilteredList(AssessmentCriterionSearchModel model)
{
using var context = new CandidateReviewDatabase();
if (model.AssessmentId.HasValue)
{
return context.AssessmentCriterions
.Where(x => x.AssessmentId == model.AssessmentId)
.Select(x => x.GetViewModel)
.ToList();
}
if (!model.Id.HasValue)
{
return new();
}
return context.AssessmentCriterions
.Where(x => x.Id == model.Id)
.Select(x => x.GetViewModel)
.ToList();
}
}
}

View File

@ -15,15 +15,20 @@ namespace CandidateReviewDatabaseImplement.Implements
using var context = new CandidateReviewDatabase();
var element = context.Assessments
.Include(x => x.Criterions)
.ThenInclude(x => x.Criterion)
.Include(x => x.User)
.Include(x => x.Resume)
.FirstOrDefault(rec => rec.Id == model.Id);
if (element != null)
{
var relatedCriterions = context.AssessmentCriterions
.Where(x => x.AssessmentId == element.Id)
.ToList();
context.AssessmentCriterions.RemoveRange(relatedCriterions);
context.Assessments.Remove(element);
context.SaveChanges();
return element.GetViewModel;
@ -99,8 +104,6 @@ namespace CandidateReviewDatabaseImplement.Implements
return context.Assessments
.Include(x => x.User)
.Include(x => x.Resume)
.Include(x => x.Criterions)
.ThenInclude(x => x.Criterion)
.Select(x => x.GetViewModel).ToList();
}
@ -123,25 +126,14 @@ namespace CandidateReviewDatabaseImplement.Implements
public AssessmentViewModel? Update(AssessmentBindingModel model)
{
using var context = new CandidateReviewDatabase();
using var transaction = context.Database.BeginTransaction();
try
var assessment = context.Assessments.FirstOrDefault(x => x.Id == model.Id);
if (assessment == null)
{
var assessment = context.Assessments.FirstOrDefault(rec => rec.Id == model.Id);
if (assessment == null)
{
return null;
}
assessment.Update(model);
context.SaveChanges();
assessment.UpdateCriterions(context, model);
transaction.Commit();
return assessment.GetViewModel;
}
catch
{
transaction.Rollback();
throw;
return null;
}
assessment.Update(model);
context.SaveChanges();
return assessment.GetViewModel;
}
}
}

View File

@ -12,7 +12,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
namespace CandidateReviewDatabaseImplement.Migrations
{
[DbContext(typeof(CandidateReviewDatabase))]
[Migration("20241203082827_InitialCreate")]
[Migration("20241213205048_InitialCreate")]
partial class InitialCreate
{
/// <inheritdoc />
@ -70,10 +70,6 @@ namespace CandidateReviewDatabaseImplement.Migrations
b.HasKey("Id");
b.HasIndex("AssessmentId");
b.HasIndex("CriterionId");
b.ToTable("AssessmentCriterions");
});
@ -286,25 +282,6 @@ namespace CandidateReviewDatabaseImplement.Migrations
b.Navigation("User");
});
modelBuilder.Entity("CandidateReviewDatabaseImplement.Models.AssessmentCriterion", b =>
{
b.HasOne("CandidateReviewDatabaseImplement.Models.Assessment", "Assessment")
.WithMany("Criterions")
.HasForeignKey("AssessmentId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("CandidateReviewDatabaseImplement.Models.Criterion", "Criterion")
.WithMany("AssessmentCriterions")
.HasForeignKey("CriterionId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Assessment");
b.Navigation("Criterion");
});
modelBuilder.Entity("CandidateReviewDatabaseImplement.Models.Resume", b =>
{
b.HasOne("CandidateReviewDatabaseImplement.Models.User", "User")
@ -343,16 +320,6 @@ namespace CandidateReviewDatabaseImplement.Migrations
b.Navigation("Company");
});
modelBuilder.Entity("CandidateReviewDatabaseImplement.Models.Assessment", b =>
{
b.Navigation("Criterions");
});
modelBuilder.Entity("CandidateReviewDatabaseImplement.Models.Criterion", b =>
{
b.Navigation("AssessmentCriterions");
});
#pragma warning restore 612, 618
}
}

View File

@ -12,6 +12,21 @@ namespace CandidateReviewDatabaseImplement.Migrations
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "AssessmentCriterions",
columns: table => new
{
Id = table.Column<int>(type: "integer", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
AssessmentId = table.Column<int>(type: "integer", nullable: false),
CriterionId = table.Column<int>(type: "integer", nullable: false),
Value = table.Column<int>(type: "integer", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_AssessmentCriterions", x => x.Id);
});
migrationBuilder.CreateTable(
name: "Companies",
columns: table => new
@ -156,43 +171,6 @@ namespace CandidateReviewDatabaseImplement.Migrations
principalColumn: "Id");
});
migrationBuilder.CreateTable(
name: "AssessmentCriterions",
columns: table => new
{
Id = table.Column<int>(type: "integer", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
AssessmentId = table.Column<int>(type: "integer", nullable: false),
CriterionId = table.Column<int>(type: "integer", nullable: false),
Value = table.Column<int>(type: "integer", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_AssessmentCriterions", x => x.Id);
table.ForeignKey(
name: "FK_AssessmentCriterions_Assessments_AssessmentId",
column: x => x.AssessmentId,
principalTable: "Assessments",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_AssessmentCriterions_Criterions_CriterionId",
column: x => x.CriterionId,
principalTable: "Criterions",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_AssessmentCriterions_AssessmentId",
table: "AssessmentCriterions",
column: "AssessmentId");
migrationBuilder.CreateIndex(
name: "IX_AssessmentCriterions_CriterionId",
table: "AssessmentCriterions",
column: "CriterionId");
migrationBuilder.CreateIndex(
name: "IX_Assessments_ResumeId",
table: "Assessments",

View File

@ -67,10 +67,6 @@ namespace CandidateReviewDatabaseImplement.Migrations
b.HasKey("Id");
b.HasIndex("AssessmentId");
b.HasIndex("CriterionId");
b.ToTable("AssessmentCriterions");
});
@ -283,25 +279,6 @@ namespace CandidateReviewDatabaseImplement.Migrations
b.Navigation("User");
});
modelBuilder.Entity("CandidateReviewDatabaseImplement.Models.AssessmentCriterion", b =>
{
b.HasOne("CandidateReviewDatabaseImplement.Models.Assessment", "Assessment")
.WithMany("Criterions")
.HasForeignKey("AssessmentId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("CandidateReviewDatabaseImplement.Models.Criterion", "Criterion")
.WithMany("AssessmentCriterions")
.HasForeignKey("CriterionId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Assessment");
b.Navigation("Criterion");
});
modelBuilder.Entity("CandidateReviewDatabaseImplement.Models.Resume", b =>
{
b.HasOne("CandidateReviewDatabaseImplement.Models.User", "User")
@ -340,16 +317,6 @@ namespace CandidateReviewDatabaseImplement.Migrations
b.Navigation("Company");
});
modelBuilder.Entity("CandidateReviewDatabaseImplement.Models.Assessment", b =>
{
b.Navigation("Criterions");
});
modelBuilder.Entity("CandidateReviewDatabaseImplement.Models.Criterion", b =>
{
b.Navigation("AssessmentCriterions");
});
#pragma warning restore 612, 618
}
}

View File

@ -1,8 +1,6 @@
using CandidateReviewContracts.BindingModels;
using CandidateReviewContracts.ViewModels;
using CandidateReviewDataModels.Models;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace CandidateReviewDatabaseImplement.Models
{
@ -18,25 +16,6 @@ namespace CandidateReviewDatabaseImplement.Models
public virtual User User { get; set; }
public virtual Resume Resume { get; set; }
private Dictionary<int, (CriterionModel, int)>? _assessmentCriterions = null;
public Dictionary<int, (CriterionModel, int)> AssessmentCriterions
{
get
{
if (_assessmentCriterions == null)
{
_assessmentCriterions = Criterions
.ToDictionary(recAC => recAC.CriterionId, recAC =>
(new CriterionModel { Id = recAC.Criterion.Id, Name = recAC.Criterion.Name }, recAC.Value));
}
return _assessmentCriterions;
}
}
[ForeignKey("AssessmentId")]
public virtual List<AssessmentCriterion> Criterions { get; set; } = new();
public static Assessment Create(CandidateReviewDatabase context, AssessmentBindingModel model)
{
return new Assessment()
@ -44,12 +23,7 @@ namespace CandidateReviewDatabaseImplement.Models
Id = model.Id,
ResumeId = model.ResumeId,
UserId = model.UserId,
Comment = model.Comment,
Criterions = model.AssessmentCriterions.Select(x => new AssessmentCriterion
{
Assessment = context.Assessments.First(y => y.Id == x.Key),
Value = x.Value.Item2
}).ToList()
Comment = model.Comment
};
}
@ -64,48 +38,12 @@ namespace CandidateReviewDatabaseImplement.Models
Comment = model.Comment;
}
public AssessmentViewModel GetViewModel => new()
{
Id = Id,
ResumeId = ResumeId,
UserId = UserId,
Comment = Comment,
AssessmentCriterions = AssessmentCriterions
Comment = Comment
};
public void UpdateCriterions(CandidateReviewDatabase context, AssessmentBindingModel model)
{
var assessmentCriterions = context.AssessmentCriterions.Where(rec => rec.AssessmentId == model.Id).ToList();
if (assessmentCriterions != null && assessmentCriterions.Count > 0)
{
context.AssessmentCriterions.RemoveRange(assessmentCriterions.Where(rec => !model.AssessmentCriterions.ContainsKey(rec.CriterionId)));
context.SaveChanges();
foreach (var updateCriterion in assessmentCriterions)
{
updateCriterion.Value = model.AssessmentCriterions[updateCriterion.CriterionId].Item2;
model.AssessmentCriterions.Remove(updateCriterion.CriterionId);
}
context.SaveChanges();
}
var assessment = context.Assessments.First(x => x.Id == Id);
foreach (var ac in model.AssessmentCriterions)
{
var criterion = context.Criterions.First(x => x.Id == ac.Value.Item1.Id);
if (criterion == null)
{
throw new Exception($"Критерий с ID {ac.Key} не найден.");
}
context.AssessmentCriterions.Add(new AssessmentCriterion
{
Assessment = assessment,
Criterion = criterion,
Value = ac.Value.Item2
});
}
context.SaveChanges();
_assessmentCriterions = null;
}
}
}

View File

@ -1,5 +1,7 @@
using CandidateReviewContracts.ViewModels;
using CandidateReviewContracts.BindingModels;
using CandidateReviewContracts.ViewModels;
using System.ComponentModel.DataAnnotations;
using System.Net;
namespace CandidateReviewDatabaseImplement.Models
{
@ -12,7 +14,28 @@ namespace CandidateReviewDatabaseImplement.Models
public int CriterionId { get; set; }
[Required]
public int Value { get; set; }
public virtual Assessment Assessment { get; set; } = new();
public virtual Criterion Criterion { get; set; } = new();
public static AssessmentCriterion? Create(AssessmentCriterionModel model)
{
if (model == null)
{
return null;
}
return new AssessmentCriterion()
{
Id = model.Id,
AssessmentId = model.AssessmentId,
CriterionId = model.CriterionId,
Value = model.Value
};
}
public AssessmentCriterionViewModel GetViewModel => new()
{
Id = Id,
AssessmentId = AssessmentId,
CriterionId = CriterionId,
Value = Value
};
}
}

View File

@ -12,8 +12,6 @@ namespace CandidateReviewDatabaseImplement.Models
public string Name { get; set; } = string.Empty;
public int Id { get; set; }
[ForeignKey("CriterionId")]
public virtual List<AssessmentCriterion> AssessmentCriterions { get; set; } = new();
public static Criterion? Create(CriterionBindingModel model)
{

View File

@ -13,10 +13,12 @@ namespace CandidateReviewRestApi.Controllers
{
private readonly ILogger _logger;
private readonly IAssessmentLogic _logic;
public AssessmentController(IAssessmentLogic logic, ILogger<AssessmentController> logger)
private readonly IAssessmentCriterionLogic _logicAC;
public AssessmentController(IAssessmentLogic logic, ILogger<AssessmentController> logger, IAssessmentCriterionLogic logicAC)
{
_logger = logger;
_logic = logic;
_logicAC = logicAC;
}
[HttpGet]
@ -53,6 +55,23 @@ namespace CandidateReviewRestApi.Controllers
}
}
[HttpGet]
public List<AssessmentCriterionViewModel>? AssessmentCriterions(int assessmentId)
{
try
{
return _logicAC.ReadList(new AssessmentCriterionSearchModel
{
AssessmentId = assessmentId
});
}
catch (Exception ex)
{
_logger.LogError(ex, "Ошибка получения оценок");
throw;
}
}
[HttpPost]
public IActionResult Create(AssessmentBindingModel model)
{
@ -68,6 +87,20 @@ namespace CandidateReviewRestApi.Controllers
}
}
[HttpPost]
public void CreateCriterion(AssessmentCriterionModel model)
{
try
{
_logicAC.Create(model);
}
catch (Exception ex)
{
_logger.LogError(ex, "Ошибка создания оценки");
throw;
}
}
[HttpPost]
public void Update(AssessmentBindingModel model)
{

View File

@ -17,6 +17,7 @@ builder.Services.AddTransient<IVacancyStorage, VacancyStorage>();
builder.Services.AddTransient<IResumeStorage, ResumeStorage>();
builder.Services.AddTransient<IAssessmentStorage, AssessmentStorage>();
builder.Services.AddTransient<ICriterionStorage, CriterionStorage>();
builder.Services.AddTransient<IAssessmentCriterionStorage, AssessmentCriterionStorage>();
builder.Services.AddTransient<IUserLogic, UserLogic>();
builder.Services.AddTransient<ICompanyLogic, CompanyLogic>();
@ -24,6 +25,7 @@ builder.Services.AddTransient<IVacancyLogic, VacancyLogic>();
builder.Services.AddTransient<IResumeLogic, ResumeLogic>();
builder.Services.AddTransient<IAssessmentLogic, AssessmentLogic>();
builder.Services.AddTransient<ICriterionLogic, CriterionLogic>();
builder.Services.AddTransient<IAssessmentCriterionLogic, AssessmentCriterionLogic>();
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle