| @@ -9,6 +9,7 @@ | |||
| <ItemGroup> | |||
| <PackageReference Include="AutoMapper" Version="11.0.1" /> | |||
| <PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="11.0.0" /> | |||
| <PackageReference Include="Bytescout.Spreadsheet" Version="4.6.0.2025" /> | |||
| <PackageReference Include="Microsoft.AspNetCore.Identity" Version="2.2.0" /> | |||
| <PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="6.0.10" /> | |||
| <PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="6.0.10" /> | |||
| @@ -16,6 +16,7 @@ namespace Diligent.WebAPI.Business.MappingProfiles | |||
| #region DTOs to Models | |||
| CreateMap<ApplicantCreateDto, Applicant>(); | |||
| CreateMap<ApplicantImportDto, Applicant>(); | |||
| CreateMap<ApplicantUpdateDto, Applicant>(); | |||
| #endregion | |||
| } | |||
| @@ -183,5 +183,18 @@ | |||
| _logger.LogInformation($"Ad saved to DB"); | |||
| await result; | |||
| } | |||
| public async Task<Ad> ImportAsync(AdCreateDto adCreateDto) | |||
| { | |||
| _logger.LogInformation($"Start importing Ad"); | |||
| var ad = _mapper.Map<Ad>(adCreateDto); | |||
| _logger.LogInformation($"Ad imported successfully"); | |||
| await _context.Ads.AddAsync(ad); | |||
| _logger.LogInformation($"Saving Ad to db..."); | |||
| await _context.SaveChangesAsync(); | |||
| _logger.LogInformation($"Ad saved to DB"); | |||
| return ad; | |||
| } | |||
| } | |||
| } | |||
| @@ -7,12 +7,14 @@ namespace Diligent.WebAPI.Business.Services | |||
| private readonly DatabaseContext _context; | |||
| private readonly IMapper _mapper; | |||
| private readonly ILogger<ApplicantService> _logger; | |||
| private readonly IUserService _userService; | |||
| public ApplicantService(DatabaseContext context, IMapper mapper, ILogger<ApplicantService> logger) | |||
| public ApplicantService(DatabaseContext context, IMapper mapper, ILogger<ApplicantService> logger, IUserService userService) | |||
| { | |||
| _context = context; | |||
| _mapper = mapper; | |||
| _logger = logger; | |||
| _userService = userService; | |||
| } | |||
| public async Task<QueryResultDto<ApplicantViewDto>> GetFilteredApplicants(ApplicantFilterDto applicantFilterDto) | |||
| @@ -168,6 +170,50 @@ namespace Diligent.WebAPI.Business.Services | |||
| _logger.LogInformation($"TechnologyApplicants saved in database"); | |||
| } | |||
| public async Task ImportApplicant(List<ApplicantImportDto> requests) | |||
| { | |||
| _logger.LogInformation($"Create applicant instance with sent data"); | |||
| var res = new List<Applicant>(); | |||
| var user = await _userService.GetFirst(); | |||
| foreach (var request in requests) { | |||
| Applicant applicant = new Applicant | |||
| { | |||
| FirstName = request.FirstName ?? "", | |||
| LastName = request.LastName ?? "", | |||
| CV = request.CV ?? "", | |||
| Email = request.Email ?? "", | |||
| PhoneNumber = request.PhoneNumber ?? "", | |||
| GithubLink = request.GithubLink ?? "", | |||
| LinkedlnLink = request.LinkedlnLink ?? "", | |||
| BitBucketLink = request.BitBucketLink ?? "", | |||
| Position = "", | |||
| DateOfApplication = request.DateOfApplication, | |||
| TypeOfEmployment = request.TypeOfEmployment == "Praksa" ? TypesOfEmployment.Intership : TypesOfEmployment.Posao, | |||
| Experience = request.Experience, | |||
| ApplicationChannel = request.ApplicationChannel ?? "Putem sajta", | |||
| // MORA DA SE UVEDE KO JE DAO KOMENTAR DA LI DA STAVIMO DA JE DANIJELA SVIMA STAVILA KOMENTARE ILI ?? | |||
| Comments = new List<Comment> | |||
| { | |||
| new Comment | |||
| { | |||
| User = user, | |||
| Content = request.Comment | |||
| } | |||
| }, | |||
| Ads = new List<Ad> { request.Ad }, | |||
| SelectionProcesses = new(), | |||
| TechnologyApplicants = new() | |||
| }; | |||
| res.Add(applicant); | |||
| } | |||
| await _context.AddRangeAsync(res); | |||
| await _context.SaveChangesAsync(); | |||
| _logger.LogInformation($"Saving applicant in database"); | |||
| _logger.LogInformation($"Applicant saved in database"); | |||
| } | |||
| public async Task<List<ApplicantOptionsDTO>> GetOptions() | |||
| { | |||
| var res = await _context.Applicants.ToListAsync(); | |||
| @@ -0,0 +1,55 @@ | |||
| using Microsoft.AspNetCore.Http; | |||
| namespace Diligent.WebAPI.Business.Services | |||
| { | |||
| public class ImportService : IImportService | |||
| { | |||
| private readonly ILogger<ImportService> _logger; | |||
| private readonly ISaveImportedDataService _service; | |||
| public ImportService(ILogger<ImportService> logger, ISaveImportedDataService service) | |||
| { | |||
| _logger = logger; | |||
| _service = service; | |||
| } | |||
| public async Task<List<ApplicantImportDto>> Import(IFormFile fileData) | |||
| { | |||
| try | |||
| { | |||
| using (var stream = new MemoryStream()) | |||
| { | |||
| fileData.CopyTo(stream); | |||
| var FileData = stream.ToArray(); | |||
| CopyFileToDirectory(FileData); | |||
| } | |||
| return await _service.Save(); | |||
| } | |||
| catch (Exception ex) | |||
| { | |||
| throw new Exception(ex.Message); | |||
| } | |||
| return new List<ApplicantImportDto>(); | |||
| } | |||
| private async Task CopyFileToDirectory(byte[] FileData) | |||
| { | |||
| try | |||
| { | |||
| var content = new System.IO.MemoryStream(FileData); | |||
| var path = Path.Combine(Directory.GetCurrentDirectory(), "Files", "s.xlsx"); | |||
| await CopyStream(content, path); | |||
| } | |||
| catch (Exception) | |||
| { | |||
| throw; | |||
| } | |||
| } | |||
| private async Task CopyStream(Stream stream, string downloadPath) | |||
| { | |||
| using (var fileStream = new FileStream(downloadPath, FileMode.Create, FileAccess.Write)) | |||
| { | |||
| await stream.CopyToAsync(fileStream); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -15,6 +15,7 @@ namespace Diligent.WebAPI.Business.Services.Interfaces | |||
| Task<List<AdResponseDto>> GetFilteredAdsAsync(AdFilterDto filters); | |||
| Task CreateAsync(AdCreateDto adCreateDto); | |||
| Task<Ad> ImportAsync(AdCreateDto adCreateDto); | |||
| Task UpdateAsync(int id, AdUpdateDto adUpdateDto); | |||
| @@ -13,6 +13,7 @@ namespace Diligent.WebAPI.Business.Services.Interfaces | |||
| Task DeleteApplicant(int id); | |||
| Task<List<ApplicantOptionsDTO>> GetOptions(); | |||
| Task<ServiceResponseDTO<object>> InitializeProcess(ApplicantProcessRequestDTO model); | |||
| Task ImportApplicant(List<ApplicantImportDto> request); | |||
| //Task CreateApplicant(ApplicantCreateDto applicantCreateDto); | |||
| //Task UpdateApplicant(int id, ApplicantUpdateDto applicantUpdateDto); | |||
| } | |||
| @@ -0,0 +1,9 @@ | |||
| using Microsoft.AspNetCore.Http; | |||
| namespace Diligent.WebAPI.Business.Services.Interfaces | |||
| { | |||
| public interface IImportService | |||
| { | |||
| Task<List<ApplicantImportDto>> Import(IFormFile fileData); | |||
| } | |||
| } | |||
| @@ -0,0 +1,7 @@ | |||
| namespace Diligent.WebAPI.Business.Services.Interfaces | |||
| { | |||
| public interface ISaveImportedDataService | |||
| { | |||
| Task<List<ApplicantImportDto>> Save(); | |||
| } | |||
| } | |||
| @@ -12,5 +12,6 @@ namespace Diligent.WebAPI.Business.Services.Interfaces | |||
| Task RemoveUser(User user); | |||
| Task<bool> VerifyToken(User user, string token); | |||
| Task<ServiceResponseDTO<object>> SendRegistrationLink(InviteDTO invite); | |||
| Task<User> GetFirst(); | |||
| } | |||
| } | |||
| @@ -0,0 +1,86 @@ | |||
| using Bytescout.Spreadsheet; | |||
| using System.Globalization; | |||
| namespace Diligent.WebAPI.Business.Services | |||
| { | |||
| public class SaveImportedDataService : ISaveImportedDataService | |||
| { | |||
| private readonly ILogger<SaveImportedDataService> _logger; | |||
| private readonly IApplicantService _applicantService; | |||
| private readonly IAdService _adService; | |||
| public SaveImportedDataService(IApplicantService applicantService, ILogger<SaveImportedDataService> logger, IAdService adService) | |||
| { | |||
| _applicantService = applicantService; | |||
| _logger = logger; | |||
| _adService = adService; | |||
| } | |||
| public async Task<List<ApplicantImportDto>> Save() | |||
| { | |||
| List<ApplicantImportDto> applicants = new List<ApplicantImportDto>(); | |||
| try | |||
| { | |||
| var path = Path.Combine(Directory.GetCurrentDirectory(), "Files", "s.xlsx"); | |||
| Spreadsheet document = new Spreadsheet(); | |||
| document.LoadFromFile(path); | |||
| var worksheetsNumber = document.Workbook.Worksheets.Count; | |||
| for (int k = 0; k < worksheetsNumber; k++) | |||
| { | |||
| var worksheets = document.Workbook.Worksheets[k]; | |||
| var position = worksheets.Name; | |||
| var ad = await _adService.ImportAsync(new AdCreateDto | |||
| { | |||
| Title = position, | |||
| TechnologiesIds = new(), | |||
| MinimumExperience = 0, | |||
| WorkHour = "FullTime", | |||
| Requirements = "", | |||
| Offer = "", | |||
| KeyResponsibilities = "", | |||
| EmploymentType = position.Contains("praksa") ? "Intership" : "Work", | |||
| CreatedAt = DateTime.Now, | |||
| ExpiredAt = DateTime.Now | |||
| }); | |||
| int i = 2; | |||
| while (true) | |||
| { | |||
| if (String.IsNullOrEmpty(worksheets.Cell(i, 0).ToString())) | |||
| break; | |||
| var name = worksheets.Cell(i, 0).ToString().Split(' '); | |||
| var a = new ApplicantImportDto | |||
| { | |||
| FirstName = name[0], | |||
| LastName = name[1], | |||
| Email = worksheets.Cell(i, 1).ToString(), | |||
| CV = worksheets.Cell(i, 2).ToString(), | |||
| ApplicationChannel = worksheets.Cell(i, 6).ToString(), | |||
| Comment = worksheets.Cell(i, 7).ToString(), | |||
| Position = position, | |||
| Ad = ad, | |||
| TypeOfEmployment = position.Contains("praksa") ? "Praksa" : "Posao" | |||
| }; | |||
| try | |||
| { | |||
| string str = worksheets.Cell(i, 3).ToString(); | |||
| if(!string.IsNullOrEmpty(str)) | |||
| a.DateOfApplication = DateTime.ParseExact(str, "dd.MM.yyyy.", CultureInfo.InvariantCulture); | |||
| } | |||
| catch (Exception ex) | |||
| { | |||
| } | |||
| applicants.Add(a); | |||
| i++; | |||
| } | |||
| } | |||
| } | |||
| catch (Exception e) | |||
| { | |||
| } | |||
| return applicants; | |||
| } | |||
| } | |||
| } | |||
| @@ -28,10 +28,17 @@ | |||
| _logger.LogInformation($"Received {fromDb.Count} ads from db."); | |||
| return fromDb; | |||
| } | |||
| public async Task<User> GetFirst() | |||
| { | |||
| var result = await _userManager.Users.FirstOrDefaultAsync(); | |||
| if (result == null) | |||
| throw new EntityNotFoundException("No users in database"); | |||
| return result; | |||
| } | |||
| #region REFACTORING CODE HERE TO CHECK IF USER IS NULL | |||
| public async Task<User?> GetById(int id) | |||
| { | |||
| _logger.LogInformation($"Start searching Ad with id = {id}"); | |||
| _logger.LogInformation($"Start searching user with id = {id}"); | |||
| var result = await _userManager.FindByIdAsync(id.ToString()); | |||
| return result; | |||
| } | |||
| @@ -0,0 +1,21 @@ | |||
| namespace Diligent.WebAPI.Contracts.DTOs.Applicant | |||
| { | |||
| public class ApplicantImportDto | |||
| { | |||
| public string FirstName { get; set; } | |||
| public string LastName { get; set; } | |||
| public string Position { get; set; } | |||
| public string CV { get; set; } | |||
| public DateTime DateOfApplication { get; set; } | |||
| public string Email { get; set; } | |||
| public string PhoneNumber { get; set; } | |||
| public string LinkedlnLink { get; set; } | |||
| public string GithubLink { get; set; } | |||
| public string BitBucketLink { get; set; } | |||
| public int Experience { get; set; } | |||
| public string ApplicationChannel { get; set; } | |||
| public string TypeOfEmployment { get; set; } | |||
| public string Comment { get; set; } | |||
| public Diligent.WebAPI.Data.Entities.Ad Ad { get; set; } | |||
| } | |||
| } | |||
| @@ -6,13 +6,14 @@ public static class ServiceCollection | |||
| { | |||
| services.AddDbContext<DatabaseContext>(options => | |||
| { | |||
| string secret = ""; | |||
| if (Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") == "Development") | |||
| { | |||
| options.EnableSensitiveDataLogging(); | |||
| secret = Environment.GetEnvironmentVariable("SECRET"); | |||
| } | |||
| var secret = Environment.GetEnvironmentVariable("SECRET"); | |||
| var connectionString = configuration.GetConnectionString(nameof(Diligent.WebAPI)); | |||
| options.UseSqlServer(String.Concat(connectionString,secret)); | |||
| options.UseSqlServer(String.Concat(connectionString, secret)); | |||
| }); | |||
| } | |||
| } | |||
| @@ -0,0 +1 @@ | |||
| -8585300260854775808 | |||
| @@ -0,0 +1,25 @@ | |||
| using Microsoft.AspNetCore.Mvc; | |||
| namespace Diligent.WebAPI.Host.Controllers.V1 | |||
| { | |||
| [ApiVersion("1.0")] | |||
| [Route("v{version:apiVersion}/import")] | |||
| [ApiController] | |||
| public class ImportController : ControllerBase | |||
| { | |||
| private readonly IImportService _service; | |||
| private readonly IApplicantService _applicantService; | |||
| public ImportController(IImportService service, IApplicantService applicantService) | |||
| { | |||
| _service = service; | |||
| _applicantService = applicantService; | |||
| } | |||
| [HttpPost] | |||
| public async Task<ActionResult> Import(IFormFile file) | |||
| { | |||
| var applicants = await _service.Import(file); | |||
| await _applicantService.ImportApplicant(applicants); | |||
| return Ok(); | |||
| } | |||
| } | |||
| } | |||
| @@ -11,7 +11,7 @@ | |||
| { | |||
| _selectionLevelService = selectionLevelService; | |||
| } | |||
| [Authorize] | |||
| //[Authorize] | |||
| [HttpGet] | |||
| public async Task<IActionResult> GetAll() => | |||
| Ok(await _selectionLevelService.GetAllAsync()); | |||
| @@ -20,7 +20,7 @@ | |||
| public async Task<IActionResult> GetFilteredLevels([FromQuery] SelectionProcessFilterDto request) => | |||
| Ok(_selectionLevelService.GetFilteredLevelsAsync(request)); | |||
| [Authorize] | |||
| //[Authorize] | |||
| [HttpGet("{id}")] | |||
| public async Task<IActionResult> GetById([FromRoute] int id) => | |||
| Ok(await _selectionLevelService.GetByIdAsync(id)); | |||
| @@ -19,7 +19,7 @@ namespace Diligent.WebAPI.Host.Controllers.V1 | |||
| //public async Task<IActionResult> GetAll() => | |||
| // Ok(await _selectionProcessesService.GetAllAsync()); | |||
| [Authorize] | |||
| //[Authorize] | |||
| [HttpPost] | |||
| public async Task<IActionResult> FinishSelectionProcess([FromBody] SelectionProcessCreateDto model) => | |||
| Ok(await _selectionProcessesService.FinishSelectionProcess(model)); | |||
| @@ -7,6 +7,7 @@ | |||
| </PropertyGroup> | |||
| <PropertyGroup> | |||
| <GenerateDocumentationFile>true</GenerateDocumentationFile> | |||
| <DockerDefaultTargetOS>Linux</DockerDefaultTargetOS> | |||
| </PropertyGroup> | |||
| <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> | |||
| <NoWarn>1701;1702;1591</NoWarn> | |||
| @@ -20,6 +21,7 @@ | |||
| <ItemGroup> | |||
| <PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="11.0.0" /> | |||
| <PackageReference Include="Bytescout.Spreadsheet" Version="4.6.0.2025" /> | |||
| <PackageReference Include="Microsoft.AspNet.WebApi.Core" Version="5.2.9" /> | |||
| <PackageReference Include="Microsoft.AspNetCore.Identity" Version="2.2.0" /> | |||
| <PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="6.0.10" /> | |||
| @@ -32,6 +34,7 @@ | |||
| </PackageReference> | |||
| <PackageReference Include="Microsoft.Extensions.Identity.Core" Version="6.0.10" /> | |||
| <PackageReference Include="Microsoft.Extensions.Identity.Stores" Version="6.0.10" /> | |||
| <PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.17.0" /> | |||
| <PackageReference Include="Swashbuckle.AspNetCore" Version="6.3.2" /> | |||
| <PackageReference Include="Serilog.AspNetCore" Version="5.0.0" /> | |||
| <PackageReference Include="Serilog.Sinks.Seq" Version="5.1.1" /> | |||
| @@ -44,4 +47,8 @@ | |||
| <ProjectReference Include="..\Diligent.WebAPI.Data\Diligent.WebAPI.Data.csproj" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <Folder Include="Files\" /> | |||
| </ItemGroup> | |||
| </Project> | |||
| @@ -1,8 +1,8 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | |||
| <Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | |||
| <PropertyGroup> | |||
| <Controller_SelectedScaffolderID>ApiControllerWithActionsScaffolder</Controller_SelectedScaffolderID> | |||
| <Controller_SelectedScaffolderCategoryPath>root/Common/Api</Controller_SelectedScaffolderCategoryPath> | |||
| <Controller_SelectedScaffolderID>MvcControllerEmptyScaffolder</Controller_SelectedScaffolderID> | |||
| <Controller_SelectedScaffolderCategoryPath>root/Common/MVC/Controller</Controller_SelectedScaffolderCategoryPath> | |||
| <ActiveDebugProfile>Diligent.WebAPI.Host</ActiveDebugProfile> | |||
| <NameOfLastUsedPublishProfile>IISProfile</NameOfLastUsedPublishProfile> | |||
| </PropertyGroup> | |||
| @@ -34,6 +34,8 @@ | |||
| services.AddScoped<ICommentService, CommentService>(); | |||
| services.AddScoped<IPatternService, PatternService>(); | |||
| services.AddScoped<IScheduleService, ScheduleService>(); | |||
| services.AddScoped<IImportService, ImportService>(); | |||
| services.AddScoped<ISaveImportedDataService, SaveImportedDataService>(); | |||
| } | |||
| /// <summary> | |||
| @@ -24,7 +24,7 @@ | |||
| "Enrich": [ "FromLogContext", "WithMachineName", "WtihThreadId", "WithExceptionDetails" ] | |||
| }, | |||
| "ConnectionStrings": { | |||
| "WebApi": "Server=192.168.88.105;Database=HRCenter;User Id=hrcentar;Password=" | |||
| "WebApi": "Server=192.168.88.175,1433;Initial catalog=HRCenter;User id=sa;Password=Diligent123!" | |||
| }, | |||
| "Authorization": { | |||
| "JwtExpiredTime": "5", | |||
| @@ -42,6 +42,6 @@ | |||
| "SmtpFromName": "HRCenter Team" | |||
| }, | |||
| "FrontEnd": { | |||
| "BaseUrl": "http://localhost:3000" | |||
| "BaseUrl": "https://test-hr-center.dilig.net" | |||
| } | |||
| } | |||
| @@ -16,7 +16,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution | |||
| EndProject | |||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Diligent.WebAPI.Contracts", "Diligent.WebAPI.Contracts\Diligent.WebAPI.Contracts.csproj", "{A0B92A73-4167-4B87-99FC-C268265BF9BA}" | |||
| EndProject | |||
| Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Diligent.WebAPI.Tests", "Diligent.WebAPI.Tests\Diligent.WebAPI.Tests.csproj", "{12B60EC7-349B-4B54-B27C-E1F3551E2FDB}" | |||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Diligent.WebAPI.Tests", "Diligent.WebAPI.Tests\Diligent.WebAPI.Tests.csproj", "{12B60EC7-349B-4B54-B27C-E1F3551E2FDB}" | |||
| EndProject | |||
| Global | |||
| GlobalSection(SolutionConfigurationPlatforms) = preSolution | |||