оценка резюме не доделана

This commit is contained in:
Татьяна Артамонова 2024-12-10 01:20:52 +04:00
parent e18de8db1d
commit aa28aecfc1
12 changed files with 204 additions and 143 deletions

View File

@ -3,6 +3,7 @@ using CandidateReviewContracts.BusinessLogicsContracts;
using CandidateReviewContracts.SearchModels;
using CandidateReviewContracts.StoragesContracts;
using CandidateReviewContracts.ViewModels;
using CandidateReviewDataModels.Models;
using Microsoft.Extensions.Logging;
using System.Text.RegularExpressions;
@ -17,17 +18,52 @@ namespace CandidateReviewBusinessLogic.BusinessLogic
_logger = logger;
_assessmentStorage = assessmentStorage;
}
public bool Create(AssessmentBindingModel model)
public bool AddCriterionToAssessment(AssessmentSearchModel model, ICriterionModel criterion, int value)
{
CheckModel(model);
if (_assessmentStorage.Insert(model) == null)
if (model == null)
{
throw new ArgumentNullException(nameof(model));
}
if (criterion == null)
{
throw new ArgumentNullException(nameof(criterion));
}
var element = _assessmentStorage.GetElement(model);
if (element == null)
{
_logger.LogWarning("Insert operation failed");
return false;
}
element.AssessmentCriterions[element.Id] = (criterion, value);
_assessmentStorage.Update(new AssessmentBindingModel
{
Id = element.Id,
UserId = element.UserId,
ResumeId = element.ResumeId,
Comment = element.Comment,
AssessmentCriterions = element.AssessmentCriterions
});
return true;
}
public int? Create(AssessmentBindingModel model)
{
CheckModel(model);
var id = _assessmentStorage.Insert(model);
if (id == null)
{
_logger.LogWarning("Insert operation failed");
return 0;
}
return id;
}
public bool Delete(AssessmentBindingModel model)
{
CheckModel(model, false);

View File

@ -45,22 +45,6 @@ namespace CandidateReviewBusinessLogic.BusinessLogic
return true;
}
/*public CompanyViewModel? ReadElement(CompanySearchModel model)
{
if (model == null)
{
throw new ArgumentNullException(nameof(model));
}
var element = _сompanyStorage.GetElement(model);
if (element == null)
{
_logger.LogWarning("ReadElement element not found");
return null;
}
_logger.LogInformation("ReadElement find. Id: {Id}", element.Id);
return element;
}*/
public CompanyViewModel? ReadElement(CompanySearchModel model)
{
if (model == null)

View File

@ -82,7 +82,7 @@ namespace CandidateReviewBusinessLogic.BusinessLogic
Description = element.Description,
Skills = element.Skills,
Status = element.Status,
PreviousAssessments = assessmentViewModels
Assessments = assessmentViewModels
};
_logger.LogInformation("ReadElement find. Id: {Id}", element.Id);
return resumeViewModel;

View File

@ -1,5 +1,6 @@
using CandidateReviewClientApp.Models;
using CandidateReviewContracts.BindingModels;
using CandidateReviewContracts.SearchModels;
using CandidateReviewContracts.ViewModels;
using CandidateReviewDataModels.Models;
using Microsoft.AspNetCore.Mvc;
@ -17,48 +18,53 @@ namespace CandidateReviewClientApp.Controllers
}
[HttpPost]
public IActionResult AddAssessment(int resumeId, string comment, Dictionary<int, int> criteriaValues)
public async Task<IActionResult> AddAssessmentCriterion(int resumeId, int[] criterion, int[] value, string comment)
{
string returnUrl = HttpContext.Request.Headers["Referer"].ToString();
try
{
var userId = APIClient.User?.Id;
var allCriterions = APIClient.GetRequest<List<CriterionViewModel>>("api/criterion/list");
if (allCriterions == null || !allCriterions.Any())
if (APIClient.User == null)
{
throw new Exception("Критерии не найдены");
throw new Exception("Необходима авторизация");
}
criteriaValues = criteriaValues.Where(kvp => kvp.Key != 0).ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
if (criterion == null || value == null || criterion.Length != value.Length)
{
throw new ArgumentException("Массивы критериев и оценок должны быть не null и одинаковой длины.");
}
var assessmentCriterions = criteriaValues
.ToDictionary(
kvp => kvp.Key,
kvp =>
{
var criterionViewModel = allCriterions.FirstOrDefault(c => c.Id == kvp.Key);
if (criterionViewModel == null)
{
throw new InvalidOperationException($"Критерий с ID {kvp.Key} не найден.");
}
var userId = APIClient.User?.Id;
var assessmentData = new Dictionary<int, (ICriterionModel, int)>();
var criterionModel = new CriterionViewModel
{
Id = criterionViewModel.Id,
Name = criterionViewModel.Name
};
return ((ICriterionModel)criterionModel, kvp.Value);
}
);
APIClient.PostRequest("api/assessment/create", new AssessmentBindingModel
var assessmentModel = new AssessmentBindingModel
{
ResumeId = resumeId,
UserId = userId,
Comment = comment,
AssessmentCriterions = assessmentCriterions
});
AssessmentCriterions = new Dictionary<int, (ICriterionModel, int)>()
};
var assessmentId = await APIClient.PostRequestAsync("api/assessment/create", assessmentModel);
for (int i = 0; i < criterion.Length; i++)
{
if (value[i] < 1 || value[i] > 5)
{
throw new ArgumentException($"Оценка для критерия {criterion[i]} должна быть от 1 до 5.");
}
if (assessmentData.ContainsKey(assessmentId))
{
assessmentData[assessmentId] = (new CriterionViewModel { Id = criterion[i] }, value[i]);
}
else
{
assessmentData.Add(assessmentId, (new CriterionViewModel { Id = criterion[i] }, value[i]));
}
}
APIClient.PostRequest("api/assessment/AddCriterionToAssessment", assessmentData);
return Redirect($"~/Resume/ResumeDetails/{resumeId}");
}

View File

@ -28,12 +28,12 @@ namespace CandidateReviewClientApp.Controllers
{
return View();
}
var assessments = APIClient.GetRequest<List<AssessmentViewModel>>($"api/assessment/listByResumeId?id={id}");
var criterions = APIClient.GetRequest<List<CriterionViewModel>>($"api/criterion/list");
resume.Assessments = assessments;
ViewBag.Criterions = criterions;
return View(resume);
}
}
[HttpGet]
public async Task<IActionResult> EditResume(int? id, int? vacancyId)

View File

@ -63,35 +63,28 @@
{
<h2 class="mb-4">Оценка резюме</h2>
<form method="post" asp-controller="Assessment" asp-action="AddAssessment">
<div class="row" id="criteriaList">
@if (ViewBag.Criterions != null)
{
<input type="hidden" name="ResumeId" value="@Model?.Id" />
@foreach (var criterion in ViewBag.Criterions)
{
var previousAssessment = Model.PreviousAssessments?.FirstOrDefault(a => a.Id == criterion.Id);
var isSelected = previousAssessment?.Id == criterion.Id ? "selected" : string.Empty;
<form method="post" asp-controller="Assessment" asp-action="AddAssessmentCriterion">
<input type="hidden" name="ResumeId" value="@Model.Id" />
<div class="row mb-3">
<div class="col-md-6">
<select name="criterionIds[]" class="form-control" required>
<option value="" disabled selected>Выберите критерий</option>
<option value="@criterion.Id" isSelected>@criterion.Name</option>
</select>
</div>
<div class="col-md-6">
<input type="number" class="form-control" name="criteriaValues[]" min="1" max="5"
value="@previousAssessment?.AssessmentCriterions?.FirstOrDefault().Value" required />
</div>
</div>
}
}
else
@if (ViewBag.Criterions != null)
{
foreach (var criterion in ViewBag.Criterions)
{
<p>Нет доступных критериев.</p>
<div class="row mb-3">
<div class="col-md-6">
<input type="text" class="form-control" value="@criterion.Name" readonly />
</div>
<div class="col-md-6">
<input type="number" class="form-control" name="value[]" min="1" max="5" placeholder="Введите значение от 1 до 5" required />
<input type="hidden" name="criterion[]" value="@criterion.Id" />
</div>
</div>
}
</div>
}
else
{
<p>Нет доступных критериев.</p>
}
<div class="row mb-3">
<div class="col-md-12">
@ -100,8 +93,6 @@
</div>
</div>
<button type="button" class="btn btn-primary mt-3" id="addCriterionBtn">Оценить по другому критерию</button>
<button type="submit" class="btn btn-success mt-3">Сохранить</button>
</form>
@ -114,53 +105,5 @@
<script>
document.addEventListener('DOMContentLoaded', function () {
var criterions = @Html.Raw(Json.Serialize(ViewBag.Criterions));
document.getElementById('addCriterionBtn').addEventListener('click', function () {
var criteriaList = document.getElementById('criteriaList');
var newRow = document.createElement('div');
newRow.classList.add('row', 'mb-3');
// Создаем выпадающий список для критерия
var criterionSelect = document.createElement('div');
criterionSelect.classList.add('col-md-6');
var select = document.createElement('select');
select.classList.add('form-control');
select.name = 'criterionIds[]';
select.required = true;
var defaultOption = document.createElement('option');
defaultOption.value = "";
defaultOption.textContent = "Выберите критерий";
select.appendChild(defaultOption);
criterions.forEach(function (criterion) {
var option = document.createElement('option');
option.value = criterion.id;
option.textContent = criterion.name;
select.appendChild(option);
});
criterionSelect.appendChild(select);
// Создаем поле ввода для оценки
var valueInput = document.createElement('div');
valueInput.classList.add('col-md-6');
var input = document.createElement('input');
input.type = 'number';
input.classList.add('form-control');
input.name = 'criteriaValues[]';
input.min = 1;
input.max = 5;
input.required = true; // Обязательное поле
valueInput.appendChild(input);
// Добавляем созданные элементы в новый ряд
newRow.appendChild(criterionSelect);
newRow.appendChild(valueInput);
// Добавляем новый ряд в список критериев
criteriaList.appendChild(newRow);
});
});
</script>

View File

@ -1,6 +1,7 @@
using CandidateReviewContracts.BindingModels;
using CandidateReviewContracts.SearchModels;
using CandidateReviewContracts.ViewModels;
using CandidateReviewDataModels.Models;
namespace CandidateReviewContracts.BusinessLogicsContracts
{
@ -8,7 +9,8 @@ namespace CandidateReviewContracts.BusinessLogicsContracts
{
List<AssessmentViewModel>? ReadList(AssessmentSearchModel? model);
AssessmentViewModel? ReadElement(AssessmentSearchModel model);
bool Create(AssessmentBindingModel model);
int? Create(AssessmentBindingModel model);
bool AddCriterionToAssessment(AssessmentSearchModel model, ICriterionModel criterion, int value);
bool Update(AssessmentBindingModel model);
bool Delete(AssessmentBindingModel model);
}

View File

@ -9,7 +9,7 @@ namespace CandidateReviewContracts.StoragesContracts
List<AssessmentViewModel> GetFullList();
List<AssessmentViewModel> GetFilteredList(AssessmentSearchModel model);
AssessmentViewModel? GetElement(AssessmentSearchModel model);
AssessmentViewModel? Insert(AssessmentBindingModel model);
int? Insert(AssessmentBindingModel model);
AssessmentViewModel? Update(AssessmentBindingModel model);
AssessmentViewModel? Delete(AssessmentBindingModel model);
}

View File

@ -28,6 +28,6 @@ namespace CandidateReviewContracts.ViewModels
public ResumeStatusEnum Status { get; set; }
public int Id { get; set; }
public List<AssessmentViewModel> PreviousAssessments { get; set; } = new();
public List<AssessmentViewModel> Assessments { get; set; } = new();
}
}

View File

@ -99,7 +99,7 @@ namespace CandidateReviewDatabaseImplement.Implements
.Select(x => x.GetViewModel).ToList();
}
public AssessmentViewModel? Insert(AssessmentBindingModel model)
public int? Insert(AssessmentBindingModel model)
{
using var context = new CandidateReviewDatabase();
var newAssessment = Assessment.Create(context, model);
@ -112,7 +112,7 @@ namespace CandidateReviewDatabaseImplement.Implements
context.Assessments.Add(newAssessment);
context.SaveChanges();
return newAssessment.GetViewModel;
return newAssessment.Id;
}
public AssessmentViewModel? Update(AssessmentBindingModel model)

View File

@ -25,15 +25,22 @@ namespace CandidateReviewDatabaseImplement.Implements
public UserViewModel? GetElement(UserSearchModel model)
{
if (string.IsNullOrEmpty(model.Email) && !model.Id.HasValue)
{
return null;
}
using var context = new CandidateReviewDatabase();
return context.Users
if (!string.IsNullOrEmpty(model.Email))
{
return context.Users
.Include(x => x.Company)
.FirstOrDefault(x => (!string.IsNullOrEmpty(model.Email) && x.Email == model.Email) || (model.Id.HasValue && x.Id == model.Id))?
.FirstOrDefault(x => (!string.IsNullOrEmpty(model.Email) && x.Email == model.Email))?
.GetViewModel;
}
else
{
return context.Users
.Include(x => x.Company)
.FirstOrDefault(x => (x.Id == model.Id))?
.GetViewModel;
}
}
public List<UserViewModel> GetFilteredList(UserSearchModel model)

View File

@ -2,6 +2,7 @@
using CandidateReviewContracts.BusinessLogicsContracts;
using CandidateReviewContracts.SearchModels;
using CandidateReviewContracts.ViewModels;
using CandidateReviewDataModels.Models;
using Microsoft.AspNetCore.Mvc;
namespace CandidateReviewRestApi.Controllers
@ -35,12 +36,47 @@ namespace CandidateReviewRestApi.Controllers
}
}
[HttpPost]
public void Create(AssessmentBindingModel model)
[HttpGet]
public List<AssessmentViewModel>? ListByUserId(int id)
{
try
{
_logic.Create(model);
return _logic.ReadList(new AssessmentSearchModel
{
UserId = id
});
}
catch (Exception ex)
{
_logger.LogError(ex, "Ошибка получения оценок");
throw;
}
}
[HttpGet]
public List<AssessmentViewModel>? ListByResumeId(int id)
{
try
{
return _logic.ReadList(new AssessmentSearchModel
{
ResumeId = id
});
}
catch (Exception ex)
{
_logger.LogError(ex, "Ошибка получения оценок");
throw;
}
}
[HttpPost]
public int? Create(AssessmentBindingModel model)
{
try
{
int? id = _logic.Create(model);
return id;
}
catch (Exception ex)
{
@ -48,6 +84,53 @@ namespace CandidateReviewRestApi.Controllers
throw;
}
}
[HttpPost]
public void AddCriterionToAssessment(Dictionary<int, (ICriterionModel, int)> model)
{
if (model == null || model.Count == 0)
{
throw new ArgumentException("Модель данных не должна быть пустой.", nameof(model));
}
try
{
foreach (var kvp in model)
{
var id = kvp.Key;
var (criterion, value) = kvp.Value;
if (criterion == null)
{
continue;
}
if (!model.ContainsKey(id))
{
model.Add(id, (criterion, value));
}
else
{
model[id] = (criterion, value);
}
var success = _logic.AddCriterionToAssessment(
new AssessmentSearchModel { Id = id },
criterion,
value
);
if (!success)
{
throw new Exception("Ошибка добавления критерия.");
}
}
}
catch (Exception ex)
{
_logger.LogError(ex, "Ошибка добавления критериев.");
throw;
}
}
[HttpPost]
public void Update(AssessmentBindingModel model)