Skip to content

Commit

Permalink
Field HasConcreteReviewer was removed from TaskForReview
Browse files Browse the repository at this point in the history
  • Loading branch information
dyatlov-a committed Dec 9, 2024
1 parent fed1e32 commit a7a8a2a
Show file tree
Hide file tree
Showing 26 changed files with 315 additions and 188 deletions.
41 changes: 41 additions & 0 deletions src/Inc.TeamAssistant.Migrations/2024_12_09_0_ChangeReviewer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using FluentMigrator;

namespace Inc.TeamAssistant.Migrations;

[Migration(2024_12_09_0)]
public sealed class ChangeReviewer : Migration
{
public override void Up()
{
Execute.Sql(
"""
UPDATE review.task_for_reviews AS t
SET strategy = 3
WHERE t.has_concrete_reviewer
""",
"Set strategy by has_concrete_reviewer");

Delete
.Column("has_concrete_reviewer")
.FromTable("task_for_reviews")
.InSchema("review");
}

public override void Down()
{
Create
.Column("has_concrete_reviewer")
.OnTable("task_for_reviews")
.InSchema("review")
.AsBoolean()
.NotNullable()
.SetExistingRowsTo(false);

Execute.Sql(
"""
UPDATE review.task_for_reviews AS t
SET has_concrete_reviewer = t.strategy = 3;
""",
"Set has_concrete_reviewer by strategy");
}
}
1 change: 1 addition & 0 deletions src/Inc.TeamAssistant.Primitives/ITeamAccessor.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using Inc.TeamAssistant.Primitives.Commands;
using Inc.TeamAssistant.Primitives.Languages;

namespace Inc.TeamAssistant.Primitives;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,9 @@ public async Task<CommandResult> Handle(EditDraftCommand command, CancellationTo

if (draft is not null)
{
draft.WithDescription(command.Description);

if (command.MessageContext.TargetPersonId.HasValue)
draft.WithTargetPerson(command.MessageContext.TargetPersonId.Value);
else
draft.WithoutTargetPerson();
draft
.WithDescription(command.Description)
.SetTargetPerson(command.MessageContext.TargetPersonId);

var notification = await _reviewMessageBuilder.Build(draft, command.MessageContext.LanguageId, token);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
using Inc.TeamAssistant.Holidays.Extensions;
using Inc.TeamAssistant.Primitives;
using Inc.TeamAssistant.Primitives.Bots;
using Inc.TeamAssistant.Primitives.Commands;
Expand All @@ -7,6 +6,7 @@
using Inc.TeamAssistant.Reviewer.Application.Contracts;
using Inc.TeamAssistant.Reviewer.Application.Services;
using Inc.TeamAssistant.Reviewer.Domain;
using Inc.TeamAssistant.Reviewer.Domain.NextReviewerStrategies;
using Inc.TeamAssistant.Reviewer.Model.Commands.MoveToReview;
using MediatR;

Expand All @@ -17,31 +17,32 @@ internal sealed class MoveToReviewCommandHandler : IRequestHandler<MoveToReviewC
private readonly ITaskForReviewRepository _taskForReviewRepository;
private readonly IReviewMessageBuilder _reviewMessageBuilder;
private readonly ITeamAccessor _teamAccessor;
private readonly ITaskForReviewReader _taskForReviewReader;
private readonly IDraftTaskForReviewRepository _draftTaskForReviewRepository;
private readonly DraftTaskForReviewService _draftTaskForReviewService;
private readonly IBotAccessor _botAccessor;
private readonly INextReviewerStrategyFactory _nextReviewerStrategyFactory;

public MoveToReviewCommandHandler(
ITaskForReviewRepository taskForReviewRepository,
IReviewMessageBuilder reviewMessageBuilder,
ITeamAccessor teamAccessor,
ITaskForReviewReader taskForReviewReader,
IDraftTaskForReviewRepository draftTaskForReviewRepository,
DraftTaskForReviewService draftTaskForReviewService,
IBotAccessor botAccessor)
IBotAccessor botAccessor,
INextReviewerStrategyFactory nextReviewerStrategyFactory)
{
_taskForReviewRepository =
taskForReviewRepository ?? throw new ArgumentNullException(nameof(taskForReviewRepository));
_reviewMessageBuilder =
reviewMessageBuilder ?? throw new ArgumentNullException(nameof(reviewMessageBuilder));
_teamAccessor = teamAccessor ?? throw new ArgumentNullException(nameof(teamAccessor));
_taskForReviewReader = taskForReviewReader ?? throw new ArgumentNullException(nameof(taskForReviewReader));
_draftTaskForReviewRepository =
draftTaskForReviewRepository ?? throw new ArgumentNullException(nameof(draftTaskForReviewRepository));
_draftTaskForReviewService =
draftTaskForReviewService ?? throw new ArgumentNullException(nameof(draftTaskForReviewService));
_botAccessor = botAccessor ?? throw new ArgumentNullException(nameof(botAccessor));
_nextReviewerStrategyFactory =
nextReviewerStrategyFactory ?? throw new ArgumentNullException(nameof(nextReviewerStrategyFactory));
}

public async Task<CommandResult> Handle(MoveToReviewCommand command, CancellationToken token)
Expand All @@ -60,29 +61,22 @@ public async Task<CommandResult> Handle(MoveToReviewCommand command, Cancellatio
if (!teammates.Any())
throw new TeamAssistantUserException(Messages.Reviewer_TeamWithoutUsers, draft.TeamId);

var nextReviewerStrategy = await _nextReviewerStrategyFactory.Create(
draft.TeamId,
draft.OwnerId,
draft.GetStrategy(),
draft.TargetPersonId,
teammates.Select(t => t.Id).ToArray(),
excludePersonId: null,
token);
var taskForReview = new TaskForReview(
Guid.NewGuid(),
draft,
command.MessageContext.Bot.Id,
DateTimeOffset.UtcNow,
botContext.GetNotificationIntervals(),
targetTeam.ChatId);

if (draft.TargetPersonId.HasValue)
taskForReview.SetConcreteReviewer(draft.TargetPersonId.Value);
else
{
var lastReviewerId = await _taskForReviewRepository.FindLastReviewer(
taskForReview.TeamId,
taskForReview.OwnerId,
token);
var history = await _taskForReviewReader.GetHistory(
taskForReview.TeamId,
DateTimeOffset.UtcNow.GetLastDayOfWeek(DayOfWeek.Monday),
token);

taskForReview.DetectReviewer(teammates.Select(t => t.Id).ToArray(), history, lastReviewerId);
}
targetTeam.ChatId,
nextReviewerStrategy.GetReviewer());

var reviewer = await _teamAccessor.FindPerson(taskForReview.ReviewerId, token);
if (reviewer is null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,17 @@ public NeedReviewCommandHandler(
public async Task<CommandResult> Handle(NeedReviewCommand command, CancellationToken token)
{
ArgumentNullException.ThrowIfNull(command);

var draft = new DraftTaskForReview(
Guid.NewGuid(),
command.TeamId,
command.MessageContext.Person.Id,
Enum.Parse<NextReviewerType>(command.Strategy),
command.MessageContext.ChatMessage.ChatId,
command.MessageContext.ChatMessage.MessageId,
command.Description,
DateTimeOffset.UtcNow);

if (command.MessageContext.TargetPersonId.HasValue)
draft.WithTargetPerson(command.MessageContext.TargetPersonId.Value);
Guid.NewGuid(),
command.TeamId,
command.MessageContext.Person.Id,
command.MessageContext.ChatMessage.ChatId,
command.MessageContext.ChatMessage.MessageId,
command.Description,
DateTimeOffset.UtcNow,
Enum.Parse<NextReviewerType>(command.Strategy))
.SetTargetPerson(command.MessageContext.TargetPersonId);

await _repository.Upsert(draft, token);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ public sealed record TaskForReviewHistory(
Guid Id,
DateTimeOffset Created,
TaskForReviewState State,
NextReviewerType Strategy,
Guid BotId,
string Description,
IReadOnlyCollection<ReviewInterval> ReviewIntervals,
Expand All @@ -15,6 +16,5 @@ public sealed record TaskForReviewHistory(
long OwnerId,
string OwnerName,
string? OwnerUserName,
bool HasConcreteReviewer,
bool IsOriginalReviewer)
: ITaskForReviewStats;
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Inc.TeamAssistant.Reviewer.Application.Contracts;
using Inc.TeamAssistant.Reviewer.Application.Services;
using Inc.TeamAssistant.Reviewer.Domain;
using Inc.TeamAssistant.Reviewer.Model.Queries.GetLastTasks;

namespace Inc.TeamAssistant.Reviewer.Application.QueryHandlers.GetLastTasks.Converters;
Expand Down Expand Up @@ -34,7 +35,7 @@ public async Task<TaskForReviewDto> ConvertTo(TaskForReviewHistory item, Cancell
item.OwnerId,
item.OwnerName,
item.OwnerUserName,
item.HasConcreteReviewer,
HasConcreteReviewer: item.Strategy == NextReviewerType.Target,
item.IsOriginalReviewer);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
using Inc.TeamAssistant.Reviewer.Application.Handlers;
using Inc.TeamAssistant.Reviewer.Application.QueryHandlers.GetLastTasks.Converters;
using Inc.TeamAssistant.Reviewer.Application.Services;
using Inc.TeamAssistant.Reviewer.Domain.NextReviewerStrategies;
using Microsoft.Extensions.DependencyInjection;

namespace Inc.TeamAssistant.Reviewer.Application;
Expand All @@ -25,6 +26,7 @@ public static IServiceCollection AddReviewerApplication(this IServiceCollection
ArgumentNullException.ThrowIfNull(services);

services
.AddSingleton<INextReviewerStrategyFactory, NextReviewerStrategyFactory>()
.AddSingleton<DraftTaskForReviewService>()
.AddScoped<IReviewMessageBuilder, ReviewMessageBuilder>()
.AddScoped<ReassignReviewService>()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
using Inc.TeamAssistant.Holidays.Extensions;
using Inc.TeamAssistant.Primitives.Exceptions;
using Inc.TeamAssistant.Reviewer.Application.Contracts;
using Inc.TeamAssistant.Reviewer.Domain;
using Inc.TeamAssistant.Reviewer.Domain.NextReviewerStrategies;

namespace Inc.TeamAssistant.Reviewer.Application.Services;

internal sealed class NextReviewerStrategyFactory : INextReviewerStrategyFactory
{
private readonly ITaskForReviewReader _taskForReviewReader;
private readonly ITaskForReviewRepository _taskForReviewRepository;

public NextReviewerStrategyFactory(
ITaskForReviewReader taskForReviewReader,
ITaskForReviewRepository taskForReviewRepository)
{
_taskForReviewReader = taskForReviewReader ?? throw new ArgumentNullException(nameof(taskForReviewReader));
_taskForReviewRepository =
taskForReviewRepository ?? throw new ArgumentNullException(nameof(taskForReviewRepository));
}

public async Task<INextReviewerStrategy> Create(
Guid teamId,
long ownerId,
NextReviewerType nextReviewerType,
long? targetPersonId,
IReadOnlyCollection<long> teammates,
long? excludePersonId,
CancellationToken token)
{
ArgumentNullException.ThrowIfNull(teammates);

var lastReviewerId = await _taskForReviewRepository.FindLastReviewer(
teamId,
ownerId,
token);
var history = await _taskForReviewReader.GetHistory(
teamId,
DateTimeOffset.UtcNow.GetLastDayOfWeek(DayOfWeek.Monday),
token);

var excludedPersonIds = excludePersonId.HasValue
? new[] { ownerId, excludePersonId.Value }
: [ownerId];

return nextReviewerType switch
{
NextReviewerType.RoundRobin => new RoundRobinReviewerStrategy(teammates, excludedPersonIds, lastReviewerId),
NextReviewerType.Random => new RandomReviewerStrategy(teammates, history, excludedPersonIds, lastReviewerId),
NextReviewerType.Target when targetPersonId.HasValue => new TargetReviewerStrategy(targetPersonId.Value),
_ => throw new TeamAssistantException($"NextReviewerType {nextReviewerType} was not supported.")
};
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
using Inc.TeamAssistant.Holidays.Extensions;
using Inc.TeamAssistant.Primitives;
using Inc.TeamAssistant.Primitives.Bots;
using Inc.TeamAssistant.Primitives.Exceptions;
using Inc.TeamAssistant.Primitives.Notifications;
using Inc.TeamAssistant.Reviewer.Application.Contracts;
using Inc.TeamAssistant.Reviewer.Domain;
using Inc.TeamAssistant.Reviewer.Domain.NextReviewerStrategies;

namespace Inc.TeamAssistant.Reviewer.Application.Services;

Expand All @@ -12,20 +13,21 @@ internal sealed class ReassignReviewService
private readonly ITaskForReviewRepository _taskForReviewRepository;
private readonly ITeamAccessor _teamAccessor;
private readonly IReviewMessageBuilder _reviewMessageBuilder;
private readonly ITaskForReviewReader _taskForReviewReader;
private readonly INextReviewerStrategyFactory _nextReviewerStrategyFactory;

public ReassignReviewService(
ITaskForReviewRepository taskForReviewRepository,
ITeamAccessor teamAccessor,
IReviewMessageBuilder reviewMessageBuilder,
ITaskForReviewReader taskForReviewReader)
INextReviewerStrategyFactory nextReviewerStrategyFactory)
{
_taskForReviewRepository =
taskForReviewRepository ?? throw new ArgumentNullException(nameof(taskForReviewRepository));
_teamAccessor = teamAccessor ?? throw new ArgumentNullException(nameof(teamAccessor));
_reviewMessageBuilder =
reviewMessageBuilder ?? throw new ArgumentNullException(nameof(reviewMessageBuilder));
_taskForReviewReader = taskForReviewReader ?? throw new ArgumentNullException(nameof(taskForReviewReader));
_nextReviewerStrategyFactory =
nextReviewerStrategyFactory ?? throw new ArgumentNullException(nameof(nextReviewerStrategyFactory));
}

public async Task<IReadOnlyCollection<NotificationMessage>> ReassignReview(
Expand All @@ -38,19 +40,20 @@ public async Task<IReadOnlyCollection<NotificationMessage>> ReassignReview(
if (!task.CanAccept())
return Array.Empty<NotificationMessage>();

var history = await _taskForReviewReader.GetHistory(
task.TeamId,
DateTimeOffset.UtcNow.GetLastDayOfWeek(DayOfWeek.Monday),
token);
var teammates = await _teamAccessor.GetTeammates(task.TeamId, DateTimeOffset.UtcNow, token);
var lastReviewerId = await _taskForReviewRepository.FindLastReviewer(task.TeamId, task.OwnerId, token);

task.Reassign(
DateTimeOffset.UtcNow,
var nextReviewerType = task.Strategy == NextReviewerType.Target
? NextReviewerType.Random
: task.Strategy;
var nextReviewerStrategy = await _nextReviewerStrategyFactory.Create(
task.TeamId,
task.OwnerId,
nextReviewerType,
targetPersonId: null,
teammates.Select(t => t.Id).ToArray(),
history,
task.ReviewerId,
lastReviewerId);
excludePersonId: task.ReviewerId,
token);

task.Reassign(DateTimeOffset.UtcNow, nextReviewerStrategy.GetReviewer());

var owner = await _teamAccessor.FindPerson(task.OwnerId, token);
if (owner is null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -210,10 +210,10 @@ private async Task<NotificationMessage> MessageForTeam(

Func<NotificationMessage, NotificationMessage> attachPersons = n => n;
var languageId = await _teamAccessor.GetClientLanguage(task.BotId, owner.Id, token);
var reviewerTargetMessageKey = (task.OriginalReviewerId.HasValue, task.HasConcreteReviewer) switch
var reviewerTargetMessageKey = (task.OriginalReviewerId.HasValue, task.Strategy) switch
{
(true, _) => Messages.Reviewer_TargetReassigned,
(_, true) => Messages.Reviewer_TargetManually,
(_, NextReviewerType.Target) => Messages.Reviewer_TargetManually,
(_, _) => Messages.Reviewer_TargetAutomatically
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace Inc.TeamAssistant.Reviewer.Application.Services;

internal sealed class ReviewerSettingSectionProvider : ISettingSectionProvider
{
private readonly IReadOnlyDictionary<NextReviewerType, string> _storyType = new Dictionary<NextReviewerType, string>
private readonly IReadOnlyDictionary<NextReviewerType, string> _nextReviewerType = new Dictionary<NextReviewerType, string>
{
[NextReviewerType.RoundRobin] = "FormSectionSetSettingsRoundRobinDescription",
[NextReviewerType.Random] = "FormSectionSetSettingsRandomDescription"
Expand Down Expand Up @@ -44,7 +44,7 @@ public IReadOnlyCollection<SettingSection> GetSections()
private IEnumerable<SelectValue> GetValuesForNextReviewerType()
{
foreach (var item in Enum.GetValues<NextReviewerType>())
if (_storyType.TryGetValue(item, out var value))
if (_nextReviewerType.TryGetValue(item, out var value))
yield return new SelectValue(value, item.ToString());
}

Expand Down
Loading

0 comments on commit a7a8a2a

Please sign in to comment.