Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ namespace Vote.Monitor.Domain.Entities.FormBase.Questions;
[PolyJsonConverter.SubType(typeof(SingleSelectQuestion), QuestionTypes.SingleSelectQuestionType)]
[PolyJsonConverter.SubType(typeof(MultiSelectQuestion), QuestionTypes.MultiSelectQuestionType)]
[PolyJsonConverter.SubType(typeof(RatingQuestion), QuestionTypes.RatingQuestionType)]
[PolyJsonConverter.SubType(typeof(MatrixQuestion), QuestionTypes.MatrixQuestionType)]
public abstract record BaseQuestion
{
[JsonPropertyName("$questionType")]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using Vote.Monitor.Core.Models;

namespace Vote.Monitor.Domain.Entities.FormBase.Questions;

public record MatrixOption
{
public Guid Id { get; private set; }
public TranslatedString Text { get; private set; }
public bool IsFlagged { get; private set; }

[JsonConstructor]
internal MatrixOption(Guid id, TranslatedString text, bool isFlagged)
{
Id = id;
Text = text;
IsFlagged = isFlagged;
}

public static MatrixOption Create(Guid id,
TranslatedString text, bool isFlagged)
=> new(id, text, isFlagged);

public void AddTranslation(string languageCode)
{
Text.AddTranslation(languageCode);
}

public void RemoveTranslation(string languageCode)
{
Text.RemoveTranslation(languageCode);
}

public void TrimTranslations(IEnumerable<string> languages)
{
Text.TrimTranslations(languages);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
using System.Runtime.InteropServices.JavaScript;
using Microsoft.AspNetCore.Http.HttpResults;
using NPOI.SS.Formula.Functions;
using Vote.Monitor.Core.Models;

namespace Vote.Monitor.Domain.Entities.FormBase.Questions;

public record MatrixQuestion : BaseQuestion
{
public IReadOnlyList<MatrixOption> Options { get; private set; }
public IReadOnlyList<MatrixRow> Rows { get; private set; }

[JsonConstructor]
internal MatrixQuestion(Guid id,
string code,
TranslatedString text,
TranslatedString? helptext,
DisplayLogic? displayLogic,
IReadOnlyList<MatrixOption> options,
IReadOnlyList<MatrixRow> rows) : base(id, code, text, helptext, displayLogic)
{
Options = options;
Rows = rows;
}

protected override void AddTranslationsInternal(string languageCode)
{
foreach (var option in Options)
{
option.AddTranslation(languageCode);
}

foreach (var row in Rows)
{
row.AddTranslation(languageCode);
}
}

protected override void RemoveTranslationInternal(string languageCode)
{
foreach (var option in Options)
{
option.RemoveTranslation(languageCode);
}

foreach (var row in Rows)
{
row.RemoveTranslation(languageCode);
}
}

protected override TranslationStatus InternalGetTranslationStatus(string baseLanguageCode, string languageCode)
{
bool anyMissingInOptions = Options.Any(x => string.IsNullOrWhiteSpace(x.Text[languageCode]));
bool anyMissingInRows = Rows.Any(x => string.IsNullOrWhiteSpace(x.Text[languageCode]));

return anyMissingInOptions || anyMissingInRows
? TranslationStatus.MissingTranslations
: TranslationStatus.Translated;
}

protected override void InternalTrimTranslations(IEnumerable<string> languages)
{
var languagesArray = languages as string[] ?? languages.ToArray();
foreach (var option in Options)
{
option.TrimTranslations(languagesArray);
}

foreach (var row in Rows)
{
row.TrimTranslations(languagesArray);
}
}

public static MatrixQuestion Create(Guid id,
string code,
TranslatedString text,
TranslatedString? helptext,
DisplayLogic? displayLogic,
IReadOnlyList<MatrixOption> options,
IReadOnlyList<MatrixRow> rows)
=> new(id, code, text, helptext, displayLogic, options, rows);

public virtual bool Equals(MatrixQuestion? other)
{
bool options = base.Equals(other) && Options.SequenceEqual(other.Options);
bool rows = base.Equals(other) && Rows.SequenceEqual(other.Rows);

return options && rows;
}

public override int GetHashCode()
{
return HashCode.Combine(base.GetHashCode(), Options, Rows);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using Vote.Monitor.Core.Models;

namespace Vote.Monitor.Domain.Entities.FormBase.Questions;

public record MatrixRow
{
public Guid Id { get; private set; }
public TranslatedString Text { get; private set; }

[JsonConstructor]
internal MatrixRow(Guid id, TranslatedString text)
{
Id = id;
Text = text;
}

public static MatrixRow Create(Guid id, TranslatedString text)
=> new(id, text);

public void AddTranslation(string languageCode)
{
Text.AddTranslation(languageCode);
}

public void RemoveTranslation(string languageCode)
{
Text.RemoveTranslation(languageCode);
}

public void TrimTranslations(IEnumerable<string> languages)
{
Text.TrimTranslations(languages);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ public static class QuestionTypes
public const string SingleSelectQuestionType = "singleSelectQuestion";
public const string MultiSelectQuestionType = "multiSelectQuestion";
public const string RatingQuestionType = "ratingQuestion";
public const string MatrixQuestionType = "matrixQuestion";
}
28 changes: 28 additions & 0 deletions api/src/Vote.Monitor.Form.Module/Mappers/QuestionsMapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,18 @@ public static BaseQuestion ToEntity(BaseQuestionRequest question)
ratingQuestion.LowerLabel,
ratingQuestion.UpperLabel,
ToEntity(ratingQuestion.DisplayLogic));

case MatrixQuestionRequest matrixQuestion:
var matrixQuestionOptions = ToEntities(matrixQuestion.Options);
var matrixQuestionRows = ToEntities(matrixQuestion.Rows);

return MatrixQuestion.Create(matrixQuestion.Id,
matrixQuestion.Code,
matrixQuestion.Text,
matrixQuestion.Helptext,
ToEntity(matrixQuestion.DisplayLogic),
matrixQuestionOptions,
matrixQuestionRows);

default: throw new ApplicationException("Unknown question type received");
}
Expand All @@ -102,6 +114,22 @@ private static IReadOnlyList<SelectOption> ToEntities(IEnumerable<SelectOptionRe
.ToList()
.AsReadOnly();
}

private static IReadOnlyList<MatrixOption> ToEntities(IEnumerable<MatrixOptionRequest> options)
{
return options
.Select(o => MatrixOption.Create(o.Id, o.Text, o.IsFlagged))
.ToList()
.AsReadOnly();
}

private static IReadOnlyList<MatrixRow> ToEntities(IEnumerable<MatrixRowRequest> options)
{
return options
.Select(o => MatrixRow.Create(o.Id, o.Text))
.ToList()
.AsReadOnly();
}

private static DisplayLogic? ToEntity(DisplayLogicRequest? displayLogic)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ namespace Vote.Monitor.Form.Module.Requests;
[PolyJsonConverter.SubType(typeof(SingleSelectQuestionRequest), QuestionTypes.SingleSelectQuestionType)]
[PolyJsonConverter.SubType(typeof(MultiSelectQuestionRequest), QuestionTypes.MultiSelectQuestionType)]
[PolyJsonConverter.SubType(typeof(RatingQuestionRequest), QuestionTypes.RatingQuestionType)]
[PolyJsonConverter.SubType(typeof(MatrixQuestionRequest), QuestionTypes.MatrixQuestionType)]
public abstract class BaseQuestionRequest
{
[JsonPropertyName("$questionType")] public string QuestionType => DiscriminatorValue.Get(GetType())!;
Expand All @@ -24,4 +25,4 @@ public abstract class BaseQuestionRequest

public TranslatedString? Helptext { get; set; }
public DisplayLogicRequest? DisplayLogic { get; set; }
}
}
10 changes: 10 additions & 0 deletions api/src/Vote.Monitor.Form.Module/Requests/MatrixOptionRequest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using Vote.Monitor.Core.Models;

namespace Vote.Monitor.Form.Module.Requests;

public class MatrixOptionRequest
{
public Guid Id { get; set; }
public TranslatedString Text { get; set; }
public bool IsFlagged { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Vote.Monitor.Form.Module.Requests;

public class MatrixQuestionRequest: BaseQuestionRequest
{
public List<MatrixOptionRequest> Options { get; set; } = new();
public List<MatrixRowRequest> Rows { get; set; } = new();
}
9 changes: 9 additions & 0 deletions api/src/Vote.Monitor.Form.Module/Requests/MatrixRowRequest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using Vote.Monitor.Core.Models;

namespace Vote.Monitor.Form.Module.Requests;

public class MatrixRowRequest
{
public Guid Id { get; set; }
public TranslatedString Text { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using FastEndpoints;
using FluentValidation;
using Vote.Monitor.Core.Validators;
using Vote.Monitor.Form.Module.Requests;

namespace Vote.Monitor.Form.Module.Validators;

public class MatrixOptionRequestValidator: Validator<MatrixOptionRequest>
{
public MatrixOptionRequestValidator(List<string> languages)
{
RuleFor(x => x.Id)
.NotEmpty();

RuleFor(x => x.Text)
.SetValidator(new PartiallyTranslatedStringValidator(languages));

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
using FastEndpoints;
using FluentValidation;
using Vote.Monitor.Core.Validators;
using Vote.Monitor.Form.Module.Requests;

namespace Vote.Monitor.Form.Module.Validators;

public class MatrixQuestionRequestValidator : Validator<MatrixQuestionRequest>
{
public MatrixQuestionRequestValidator(List<string> languages)
{
RuleFor(x => x.Id).NotEmpty();

RuleFor(x => x.QuestionType).NotEmpty();

RuleFor(x => x.Text)
.SetValidator(new PartiallyTranslatedStringValidator(languages));

RuleFor(x => x.Helptext)
.SetValidator(new PartiallyTranslatedStringValidator(languages))
.When(x => x.Helptext != null);

RuleFor(x => x.Code)
.NotEmpty()
.MaximumLength(256);

RuleForEach(x => x.Options)
.SetValidator(new MatrixOptionRequestValidator(languages));

RuleFor(x => x.Options)
.Must(options =>
{
var groupedOptionIds = options.GroupBy(o => o.Id, (id, group) => new { id, count = group.Count() });

return groupedOptionIds.All(g => g.count == 1);
})
.WithMessage("Duplicated id found");

RuleForEach(x => x.Rows)
.SetValidator(new MatrixRowRequestValidator(languages));

RuleFor(x => x.Rows)
.Must(options =>
{
var groupedOptionIds = options.GroupBy(o => o.Id, (id, group) => new { id, count = group.Count() });

return groupedOptionIds.All(g => g.count == 1);
})
.WithMessage("Duplicated id found");

RuleFor(x => x.DisplayLogic)
.SetValidator(new DisplayLogicRequestValidator());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using FastEndpoints;
using FluentValidation;
using Vote.Monitor.Core.Validators;
using Vote.Monitor.Form.Module.Requests;

namespace Vote.Monitor.Form.Module.Validators;

public class MatrixRowRequestValidator: Validator<MatrixRowRequest>
{
public MatrixRowRequestValidator(List<string> languages)
{
RuleFor(x => x.Id)
.NotEmpty();

RuleFor(x => x.Text)
.SetValidator(new PartiallyTranslatedStringValidator(languages));
}
}
Loading
Loading