diff --git a/API/Controllers/General/ChangeLog/ChangeLogController.cs b/API/Controllers/General/ChangeLog/ChangeLogController.cs new file mode 100644 index 0000000..c09b191 --- /dev/null +++ b/API/Controllers/General/ChangeLog/ChangeLogController.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Application.ChangeLogs.DTOs; +using Microsoft.AspNetCore.Mvc; + +namespace API.Controllers.General.ChangeLog +{ + public class ChangeLogController : ApiControllerBase + { + [HttpGet] + public async Task List([FromQuery] string entity, [FromQuery] string property, [FromQuery] string key, [FromQuery] string keys) + { + var changeLogInputDTO = new ChangeLogInputDTO + { + Entity = entity, + Property = property, + Key = key, + Keys = !string.IsNullOrEmpty(keys) ? keys.Split(',').ToList() : null + }; + return HandleResult(await Mediator.Send(new Application.ChangeLogs.Generate.Query { ChangeLogInputDTO = changeLogInputDTO })); + } + } +} \ No newline at end of file diff --git a/API/Controllers/General/Screen/ScreenSequenceController.cs b/API/Controllers/General/Screen/ScreenSequenceController.cs index 5457044..2f546e1 100644 --- a/API/Controllers/General/Screen/ScreenSequenceController.cs +++ b/API/Controllers/General/Screen/ScreenSequenceController.cs @@ -12,10 +12,19 @@ public class ScreenSequenceController : ApiControllerBase [HttpPost("{screenId}")] [Authorize(Policy = "RequireScreenerRole")] - public async Task PostScreenSequemce(Guid screenId, ScreenSequence screenSequence) + public async Task PostScreenSequence(Guid screenId, ScreenSequence screenSequence) { screenSequence.ScreenId = screenId; return HandleResult(await Mediator.Send(new Application.Screens.ScreenSequences.Create.Command { NewScreenSequence = screenSequence })); } + + [HttpPost("{screenId}/edit-sequence/{screenSequenceId}")] + [Authorize(Policy = "RequireScreenerRole")] + public async Task EditScreenSequence(Guid screenId, Guid screenSequenceId, ScreenSequence screenSequence) + { + screenSequence.ScreenId = screenId; + screenSequence.Id = screenSequenceId; + return HandleResult(await Mediator.Send(new Application.Screens.ScreenSequences.Edit.Command { ScreenSequence = screenSequence })); + } } } \ No newline at end of file diff --git a/Adapter/GenePool/UniProt/Configuration/UniProtAdapterGeneSyncConfiguration.cs b/Adapter/GenePool/UniProt/Configuration/UniProtAdapterGeneSyncConfiguration.cs new file mode 100644 index 0000000..317410e --- /dev/null +++ b/Adapter/GenePool/UniProt/Configuration/UniProtAdapterGeneSyncConfiguration.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Adapter.GenePool.UniProt.Configuration +{ + public class UniProtAdapterGeneSyncConfiguration : IGeneSyncAdapterConfiguration + { + + } +} \ No newline at end of file diff --git a/Adapter/GenePool/UniProt/UniProtConnector.cs b/Adapter/GenePool/UniProt/UniProtConnector.cs new file mode 100644 index 0000000..baefb2f --- /dev/null +++ b/Adapter/GenePool/UniProt/UniProtConnector.cs @@ -0,0 +1,84 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Adapter.GenePool.UniProt.Configuration; +using CsvHelper.Configuration; +using Domain; +using Microsoft.Extensions.Logging; + +namespace Adapter.GenePool.UniProt +{ + public class UniProtConnector : IGenePoolAdapter + { + + private static string _ADAPTER_NAME = "UniProtConnector"; + private static string _ADAPTER_TYPE = "GenePoolAdapter"; + private static string _ADAPTER_VERSION = "1.0.0"; + private readonly ILogger _logger; + + private UniProtAdapterGeneSyncConfiguration Conf { get; set; } + private CsvConfiguration _uniProtCSVConfig; + + + public UniProtConnector() + { + _logger = new LoggerFactory().CreateLogger(); + + + _uniProtCSVConfig = new CsvConfiguration(CultureInfo.InvariantCulture) + { + Delimiter = Microsoft.VisualBasic.Constants.vbTab, + HasHeaderRecord = true, + // TrimOptions = TrimOptions.Trim, + // Mode = CsvMode.RFC4180, + MissingFieldFound = null, + HeaderValidated = null, + Encoding = Encoding.UTF8, + }; + } + + + public string GetAdapterName() + { + return _ADAPTER_NAME; + } + + public string GetAdapterType() + { + return _ADAPTER_TYPE; + } + + public string GetAdapterVersion() + { + return _ADAPTER_VERSION; + } + + public void Init(IGeneSyncAdapterConfiguration configuration) + { + if (configuration == null) + { + throw new ArgumentNullException(nameof(configuration)); + } + + if (configuration.GetType() != typeof(UniProtAdapterGeneSyncConfiguration)) + { + throw new ArgumentException("Configuration is not of type UniProtAdapterGeneSyncConfiguration"); + } + + Conf = (UniProtAdapterGeneSyncConfiguration)configuration; + } + + public Task> FetchGenes() + { + List FormattedGenes = new List(); + + //return FormattedGenes; + return Task.FromResult(FormattedGenes); + } + + + } +} \ No newline at end of file diff --git a/Application/ChangeLogs/DTOs/ChangeLogInputDTO.cs b/Application/ChangeLogs/DTOs/ChangeLogInputDTO.cs new file mode 100644 index 0000000..47afd6c --- /dev/null +++ b/Application/ChangeLogs/DTOs/ChangeLogInputDTO.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Application.ChangeLogs.DTOs +{ + public class ChangeLogInputDTO + { + public string Entity { get; set; } + public string Property { get; set; } + public string Key { get; set; } + public List Keys { get; set; } + } +} \ No newline at end of file diff --git a/Application/ChangeLogs/Generate.cs b/Application/ChangeLogs/Generate.cs new file mode 100644 index 0000000..679452c --- /dev/null +++ b/Application/ChangeLogs/Generate.cs @@ -0,0 +1,99 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Application.ChangeLogs.DTOs; +using Application.Core; +using Domain; +using MediatR; +using Microsoft.EntityFrameworkCore; +using Persistence; + +namespace Application.ChangeLogs +{ + public class Generate + { + public class Query : IRequest>> + { + public ChangeLogInputDTO ChangeLogInputDTO { get; set; } + } + + public class Handler : IRequestHandler>> + { + private readonly DataContext _context; + public Handler(DataContext context) + { + _context = context; + } + + public async Task>> Handle(Query request, CancellationToken cancellationToken) + { + + + + /* CASE 1 : Single Entity */ + if (!string.IsNullOrEmpty(request.ChangeLogInputDTO.Key) && request.ChangeLogInputDTO.Key.Length > 0) + { + //Console.WriteLine("CASE 1 : Single Entity"); + /* 1A Check if property filter is required */ + if (!string.IsNullOrEmpty(request.ChangeLogInputDTO.Property)) + { + //Console.WriteLine("CASE 1A : Property filter is required"); + var changeLogs = await _context.ChangeLogs.Where( + h => h.EntityName == request.ChangeLogInputDTO.Entity + && h.PropertyName == request.ChangeLogInputDTO.Property + && h.PrimaryKeyValue == request.ChangeLogInputDTO.Key + ).OrderByDescending(h => h.DateChanged).ToListAsync(); + + return Result>.Success(changeLogs); + } + /* 1B No property filter */ + else + { + //Console.WriteLine("CASE 1B : No property filter"); + var changeLogs = await _context.ChangeLogs.Where( + h => h.EntityName == request.ChangeLogInputDTO.Entity + && h.PrimaryKeyValue == request.ChangeLogInputDTO.Key + ).OrderByDescending(h => h.DateChanged).ToListAsync(); + + return Result>.Success(changeLogs); + } + + } + /* CASE 2 : Multiple Entities */ + else if (request.ChangeLogInputDTO.Keys != null && request.ChangeLogInputDTO.Keys.Count > 0) + { + /* 2A Check if property filter is required */ + //Console.WriteLine("CASE 2 : Multiple Entities"); + if (!string.IsNullOrEmpty(request.ChangeLogInputDTO.Property)) + { + //Console.WriteLine("CASE 2A : Property filter is required"); + var changeLogs = await _context.ChangeLogs.Where( + h => h.EntityName == request.ChangeLogInputDTO.Entity + && h.PropertyName == request.ChangeLogInputDTO.Property + && request.ChangeLogInputDTO.Keys.Contains(h.PrimaryKeyValue) + ).OrderByDescending(h => h.DateChanged).ToListAsync(); + + return Result>.Success(changeLogs); + } + /* 2B No property filter */ + else + { + //Console.WriteLine("CASE 2B : No property filter"); + var changeLogs = await _context.ChangeLogs.Where( + h => h.EntityName == request.ChangeLogInputDTO.Entity + && request.ChangeLogInputDTO.Keys.Contains(h.PrimaryKeyValue) + ).OrderByDescending(h => h.DateChanged).ToListAsync(); + + return Result>.Success(changeLogs); + } + } + else + { + return Result>.Failure("No keys provided"); + } + } + } + } +} \ No newline at end of file diff --git a/Application/Screens/Screen/Create.cs b/Application/Screens/Screen/Create.cs index 74ff754..232934d 100644 --- a/Application/Screens/Screen/Create.cs +++ b/Application/Screens/Screen/Create.cs @@ -49,7 +49,7 @@ public async Task> Handle(Command request, CancellationToken canc var baseTarget = await _context.Targets.FirstOrDefaultAsync (t => t.Id == request.NewScreen.TargetId); - /*chek if gene id is correct*/ + /* check if gene id is correct*/ if (baseTarget == null) { return Result.Failure("Invalid Target ID"); diff --git a/Application/Screens/ScreenSequences/Edit.cs b/Application/Screens/ScreenSequences/Edit.cs new file mode 100644 index 0000000..c767a7f --- /dev/null +++ b/Application/Screens/ScreenSequences/Edit.cs @@ -0,0 +1,120 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Application.Core; +using Application.Interfaces; +using AutoMapper; +using Domain; +using MediatR; +using Microsoft.EntityFrameworkCore; +using Persistence; + +namespace Application.Screens.ScreenSequences +{ + public class Edit + { + public class Command : IRequest> + { + public ScreenSequence ScreenSequence { get; set; } + } + + // public class CommandValidator : AbstractValidator + // { + // public CommandValidator() + // { + // RuleFor(cmd => cmd.Gene).SetValidator(new GeneValidator()); + // } + + // } + + public class Handler : IRequestHandler> + { + private readonly DataContext _context; + private readonly IMapper _mapper; + private readonly IUserAccessor _userAccessor; + + public Handler(DataContext context, IMapper mapper, IUserAccessor userAccessor) + { + _context = context; + _mapper = mapper; + _userAccessor = userAccessor; + } + public async Task> Handle(Command request, CancellationToken cancellationToken) + { + + /* check if the screen exists */ + var screenSequence = await _context.ScreenSequences.FirstOrDefaultAsync(s => s.Id == request.ScreenSequence.Id); + + if (screenSequence == null) + { + return Result.Failure("Invalid Screen Sequence ID"); + } + + /* Allow edits if + 1. screenSequence is owned by the user + 2. Base Screen is owned by the user's org + 3. User is admin + */ + + var baseScreen = await _context.Screens.FirstOrDefaultAsync(s => s.Id == screenSequence.ScreenId); + if (baseScreen == null) + { + return Result.Failure("Invalid Screen ID"); + } + + var userOrgId = await _userAccessor.GetUserOrgId(); + if (userOrgId == Guid.Empty) + { + return Result.Failure("Fail: User does not belong to an organization"); + } + + bool allowedToEdit = false; + + if (await _userAccessor.isInRole("admin")) + { + allowedToEdit = true; + } + + else if (screenSequence.CreatedBy == _userAccessor.GetUsername()) + { + allowedToEdit = true; + } + + else if (baseScreen.CreatedBy == _userAccessor.GetUsername()) + { + allowedToEdit = true; + } + else if (userOrgId == baseScreen.OrgId) + { + allowedToEdit = true; + } + + if (!allowedToEdit) + { + return Result.Failure("You do not have permission to edit this screen sequence"); + } + + /* Start editing the screen sequence */ + + screenSequence.Method = request.ScreenSequence.Method; + screenSequence.Protocol = request.ScreenSequence.Protocol; + screenSequence.Library = request.ScreenSequence.Library; + screenSequence.Scientist = request.ScreenSequence.Scientist; + screenSequence.StartDate = request.ScreenSequence.StartDate; + screenSequence.EndDate = request.ScreenSequence.EndDate; + screenSequence.UnverifiedHitCount = request.ScreenSequence.UnverifiedHitCount; + screenSequence.NoOfCompoundsScreened = request.ScreenSequence.NoOfCompoundsScreened; + screenSequence.Concentration = request.ScreenSequence.Concentration; + screenSequence.ConcentrationUnit = request.ScreenSequence.ConcentrationUnit; + + var success = await _context.SaveChangesAsync(_userAccessor.GetUsername()) > 0; + + if (!success) return Result.Failure("Failed to edit Screen Sequence"); + return Result.Success(screenSequence); + + } + + + } + } +} \ No newline at end of file diff --git a/README.md b/README.md index 344288d..e178b55 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,3 @@ # daikon-core-server + About DAIKON is an open-source web application designed to manage and visualize targets, screens, pre-projects, and projects within the drug discovery pipeline.