Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Бессараб Дмитрий #31

Open
wants to merge 29 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
59d38a8
start
d2em0n Dec 25, 2024
45b91db
add simple filter
d2em0n Dec 25, 2024
441cd69
add textprocessor
d2em0n Dec 25, 2024
143db64
add some tests for processor, filter, provider
d2em0n Dec 26, 2024
baf26aa
add layout with some tests
d2em0n Dec 26, 2024
8149754
add interface for TextProcessor
d2em0n Dec 26, 2024
1b85764
add Tag and interface for Tag generator
d2em0n Dec 26, 2024
3f413ea
remove and sort usings, text processor refactoring
d2em0n Dec 27, 2024
29ea260
Word became record
d2em0n Dec 27, 2024
e9254ec
add interface for string parsers
d2em0n Dec 27, 2024
5259a19
add rnd color tag generator
d2em0n Dec 27, 2024
78bb80a
add shortwordfilter
d2em0n Jan 3, 2025
cf302b3
hypothesis
d2em0n Jan 3, 2025
66b48b9
add chosing color
d2em0n Jan 3, 2025
c329079
add IColorProvider and refactor TagGenerator
d2em0n Jan 4, 2025
529f90c
pattern filter for filters
d2em0n Jan 4, 2025
821147e
container refuse to wark (
d2em0n Jan 5, 2025
2392c92
mvp
d2em0n Jan 6, 2025
db6772b
add DocTextProvider
d2em0n Jan 7, 2025
29142c4
DocXTextProvider
d2em0n Jan 7, 2025
2cb1edd
minus unused usings
d2em0n Jan 11, 2025
8eeaab8
fix color and pointgenerator checking
d2em0n Jan 12, 2025
ab0392f
fix color data integrity
d2em0n Jan 12, 2025
825fa46
fix random for multithreading in randomcolorprovider
d2em0n Jan 12, 2025
2fa6816
no public mod to interface methods
d2em0n Jan 12, 2025
946768e
fix TxtText provider
d2em0n Jan 12, 2025
7c1ca09
dispose idisposable
d2em0n Jan 12, 2025
ef193e7
fix regexparser
d2em0n Jan 12, 2025
cb067a4
fix packages
d2em0n Jan 12, 2025
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
26 changes: 26 additions & 0 deletions Client/Client.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\TagsCloudContainer\TagsCloudContainer.csproj" />
</ItemGroup>

<ItemGroup>
<None Update="test.doc">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="test.docx">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="TestFile.txt">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>

</Project>
69 changes: 69 additions & 0 deletions Client/DependencyInjection.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
using Autofac;
using TagsCloudContainer;
using TagsCloudContainer.ColorProviders;
using TagsCloudContainer.Configuration;
using TagsCloudContainer.PointGenerators;
using TagsCloudContainer.StringParsers;
using TagsCloudContainer.TagGenerator;
using TagsCloudContainer.TextProcessor;
using TagsCloudContainer.TextProviders;
using TagsCloudContainer.WordFilters;

namespace Client;

public class DependencyInjection
{
public static IContainer BuildContainer(Config config)
{
var container = new ContainerBuilder();
container.RegisterInstance(config).AsSelf();

container.RegisterType(config.SupportedReadingFormats[Path.GetExtension(config.FilePath)])
.As<ITextProvider>()
.WithParameter("filePath", config.FilePath)
.SingleInstance();
container.RegisterType<TextProcessor>()
.As<ITextProcessor>()
.SingleInstance();

container.RegisterType(config.PointGenerator)
.As<IPointGenerator>()
.SingleInstance();

if (config.Color != null)
container.RegisterType<ColorProvider>()
.As<IColorProvider>()
.WithParameter("color", config.Color)
.SingleInstance();
else
container.RegisterType<RandomColorProvider>()
.As<IColorProvider>()
.SingleInstance();

container.RegisterType<RegexParser>()
.As<IStringParser>()
.SingleInstance();

container.RegisterType<TagGenerator>()
.As<ITagsGenerator>()
.WithParameter("defaultFont", config.Font)
.SingleInstance();



container.RegisterType<ToLowerFilter>().As<IWordFilter>()
.SingleInstance();
container.RegisterType<ShortWordFilter>().As<IWordFilter>()
.SingleInstance();
container.RegisterType<BoringWordFilter>().As<IWordFilter>()
.SingleInstance();

container.RegisterType<PictureMaker>()
.AsSelf()
.WithParameter("fileName", config.PicturePath)
.WithParameter("startPoint", config.StartPoint)
.SingleInstance();

return container.Build();
}
}
134 changes: 134 additions & 0 deletions Client/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
using System.Drawing;
using System.Reflection;
using System.Threading.Channels;
using TagsCloudContainer.Configuration;
using TagsCloudContainer.PointGenerators;
using TagsCloudContainer.StringParsers;
using TagsCloudContainer.TextProviders;
using TagsCloudContainer.WordFilters;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using System.ComponentModel;
using TagsCloudContainer;
using TagsCloudContainer.TextProcessor;
using Autofac;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Очень много лишних using (


namespace Client
{
internal class Program
{
static void Main()
{
var config = new Config();

ConfigureSupportedReadingFormats(config);
ConfigureFileSource(config);
ConfigureCloudView(config);
ConfigureColor(config);
ConfigurePathToSave(config);
ConfigureStartPoint(config);
ConfigureFont(config);

var container = DependencyInjection.BuildContainer(config);
using var scope = container.BeginLifetimeScope();
scope.Resolve<PictureMaker>().DrawPicture();
Comment on lines +26 to +27

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Можно использовать без усложнений со скоупами, так как жизненный цикл всех сущностей совпадает с временем жизни всего приложения. Просто у контейнера вызывать Resolve

Console.WriteLine($"результат сохранен в {config.PicturePath}");
}

private static void ConfigureSupportedReadingFormats(Config config)
{
Console.WriteLine("Поддерживаются следующие форматы файлов для чтения:");
var textProviders = FindImplemetations<ITextProvider>();
foreach (var point in textProviders)
Console.WriteLine("\t" + point.Key);
config.SupportedReadingFormats = textProviders;
}

private static void ConfigureFont(Config config)
{
config.Font = new Font("arial", 12);
}

private static void ConfigurePathToSave(Config config)
{
Console.WriteLine("Введите полный путь и название файла для сохранения");
var inp = Console.ReadLine();
config.PicturePath = inp.Length == 0 ? "1.bmp" : inp;
}

private static void ConfigureStartPoint(Config config)
{
Console.WriteLine("Введите координаты центра поля для рисования" +
"\n При некорректном вводе координаты центра составят ( 1000, 1000)");
var xLine = Console.ReadLine();
var yLine = Console.ReadLine();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Без кода никогда не догадаться, в каком формате вводить координаты. Пользователю нужно давать как можно больше подсказок. Желательно все ReadLine заменить на что-нибудь такое

var xLine = ReadValue("Координата Х");
var yLine = ReadValue("Координата Y");
private static string? ReadValue(string? argName = null)
{
    Console.Write($"{argName ?? ""}: ");
    return Console.ReadLine();
}

if (int.TryParse(xLine, out var xResult) &&
int.TryParse(yLine, out var yResult))
config.StartPoint = new Point(xResult, yResult);
config.StartPoint = new Point(1000, 1000);
}

private static void ConfigureFileSource(Config config)
{
Console.WriteLine("Введите имя файла источника тэгов");
var inp = Console.ReadLine();
config.FilePath = inp.Length == 0 ? @"TestFile.txt" : inp;
}

private static string GetLabel(RainbowColors color)
{
var fieldInfo = color.GetType().GetField(color.ToString());
var attribute = (LabelAttribute)Attribute.GetCustomAttribute(fieldInfo, typeof(LabelAttribute));

return attribute.LabelText;
}

private static void ConfigureColor(Config config)
{
Console.WriteLine("Выборите цвет из возможных:");
var colors = Enum.GetValues(typeof(RainbowColors))
.Cast<RainbowColors>()
.ToDictionary(color => GetLabel(color).ToLower(), color => color);

foreach (var color in colors)
Console.WriteLine("\t" + color.Key);

Console.WriteLine("В случае неправильного ввода - цвет будет выбираться случайным образом");
var inp = Console.ReadLine().ToLower();
if (colors.ContainsKey(inp))
{
config.Color = Color.FromName(colors[inp].ToString());

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ContainsKey под капотом уже получает значение по ключу, а потом снова происходит поиск по этому же ключу. Здесь это чревато только повторением одного и того же действия. В многопоточном коде это может привести даже к ошибке

поток 1: colors.ContainsKey(inp) => true
поток 2: colors.Remove(inp)
поток 1: colors[inp] => KeyNotFoundException

И такие ситуации очень неожиданные, ведь только что проверяли, что ключ есть. Поэтому если требуется в итоге работать со значением, то лучше использовать TryGetValue. Если же нужно лишь убедиться в наличии, то ContainsKey по семантике подходит лучше

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Color.FromName - почти наверняка сработает, так как поддерживает много цветов. Но безопаснее было бы сделать в библиотеке в качестве параметра конструктора у класса GolorProvider значение enum, а потом его уже в switch преобразовывать в цвет. Таким образом, если вместо color придётся использовать какой-нибудь UnixColor, для внешних пользователей ничего не поменяется в использовании

Console.WriteLine($"Выбран {inp} цвет");
}
else
Console.WriteLine("Цвет будет выбираться случайно");

}

private static void ConfigureCloudView(Config config)
{
Console.WriteLine("Выберите внешний вид облака из возможных:");
var pointGenerators = FindImplemetations<IPointGenerator>();
foreach (var point in pointGenerators)
Console.WriteLine("\t" + point.Key);
Console.WriteLine("Введите, соблюдая орфографию");
var pointGenerator = Console.ReadLine().ToLower();
if (pointGenerators.ContainsKey(pointGenerator))

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Аналогично цвету, лучше использовать TryGetValue, но не критично здесь

config.PointGenerator = pointGenerators[pointGenerator];
else
{
Console.WriteLine("Такой формы не предусмотрено");
ConfigureCloudView(config);
}
}

private static Dictionary<string, Type> FindImplemetations<T>()
{
var assembly = Assembly.LoadFrom("TagsCloudContainer.dll");
var type = typeof(T);
return assembly.GetTypes()
.Where(t => type.IsAssignableFrom(t) && !t.IsInterface)
.ToDictionary(x => x.GetCustomAttribute<LabelAttribute>().LabelText.ToLower(), x => x);
}
}
}
2 changes: 2 additions & 0 deletions Client/TestFile.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
корзина корзина корзина корзина фрукты фрукты фрукты овощи овощи овощи яблоки груши бананы смородина персики картофель свекла
морковь он я ты она за по
Binary file added Client/test.doc
Binary file not shown.
Binary file added Client/test.docx
Binary file not shown.
46 changes: 46 additions & 0 deletions TagsCloudContainer.Tests/CloudLayoutShould.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using FluentAssertions;
using System.Drawing;
using TagsCloudContainer.PointGenerators;


namespace TagsCloudContainer.Tests
{
[TestFixture]
public class CloudLayoutShould
{
private CloudLayout layout;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Можно не выносить в общее поле, ведь нигде в других местах его не нужно переиспользовать. А лишний раз делать общие переменные плохо сказывается на параллельности выполнения

[TestCase(1, 2, TestName = "Odd coordinate value results in an even size value")]
[TestCase(2, 5, TestName = "Even coordinate value results in an odd size value")]
public void MakeRightSizeLayout(int coordinateValue, int sizeValue)
{
var center = new Point(coordinateValue, coordinateValue);
var size = new Size(sizeValue, sizeValue);

layout = new CloudLayout(center, new ArchemedianSpiral());

layout.Size.Should().BeEquivalentTo(size);
}

[TestCase(-1, 1, TestName = "Negative X")]
[TestCase(1, -1, TestName = "Negative Y")]
[TestCase(0, 1, TestName = "Zero X")]
[TestCase(1, 0, TestName = "Zero Y")]
public void GetOnlyPositiveCenterCoordinates(int x, int y)
{
Action makeLayout = () => new CloudLayout(new Point(x, y), new ArchemedianSpiral());

makeLayout.Should().Throw<ArgumentException>()
.WithMessage("Center coordinates values have to be greater than Zero");
}

[Test]
public void PutNextRectangle_ShouldKeepEnteredSize()
{
layout = new CloudLayout(new Point(5, 5), new ArchemedianSpiral());
var enteredSize = new Size(3, 4);
var returnedSize = layout.PutNextRectangle(enteredSize).Size;

returnedSize.Should().BeEquivalentTo(enteredSize);
}
}
}
44 changes: 44 additions & 0 deletions TagsCloudContainer.Tests/IPointGeneratorShould.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
using FluentAssertions;
using System.Drawing;
using TagsCloudContainer.PointGenerators;

namespace TagsCloudContainer.Tests
{
[TestFixture]
public class IPointGeneratorShould
{
[TestCaseSource(nameof(TestCases))]
public void GeneratePoints_MovingAwayFromTheStartFor(IPointGenerator pointGenerator)
{
var start = new Point(0, 0);
var points = pointGenerator.GeneratePoints(start);
var nearPoint = points.ElementAt(100);
var farPoint = points.ElementAt(1000);

DistanceBetween(start, nearPoint).Should().BeLessThan(DistanceBetween(start, farPoint));
}

[TestCaseSource(nameof(TestCases))]
public void GeneratePoints_ReturnsStartAsFirstPointFor(IPointGenerator pointGenerator)
{
var start = new Point(100, 100);
var firstReturned = pointGenerator.GeneratePoints(start)
.First();

firstReturned.Should().BeEquivalentTo(start);
}

private static IEnumerable<IPointGenerator> TestCases()
{
yield return new ArchemedianSpiral();
yield return new HeartShaped();
yield return new DeltaShaped();
}

private static int DistanceBetween(Point start, Point destination)
{
return (int)Math.Sqrt((start.X - destination.X) * (start.X - destination.X) +
(start.Y - destination.Y) * (start.Y - destination.Y));
}
}
}
28 changes: 28 additions & 0 deletions TagsCloudContainer.Tests/RandomColorTagGeneratorShould.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using FluentAssertions;
using System.Drawing;
using TagsCloudContainer.ColorProviders;
using TagsCloudContainer.StringParsers;
using TagsCloudContainer.TextProviders;
using TagsCloudContainer.WordFilters;

namespace TagsCloudContainer.Tests
{
public class RandomColorTagGeneratorShould

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Это ведь тест на два независимых объекта: TagGenerator и RandomColorProvider. Сложно будет понять, из-за кого пошло не так, при последующей разработке

{
[Test]
public void SetRightFontSize()
{
var bitmap = new Bitmap(1, 1);
using var graphics = Graphics.FromImage(bitmap);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Графика нигде не используется


var processor = new TextProcessor.TextProcessor(
new TxtTextProvider(@"TextFile1.txt"), new RegexParser(), new BoringWordFilter());
var words = processor.WordFrequencies();
var generator = new TagGenerator.TagGenerator(new RandomColorProvider(), new Font("arial", 12));
var result = generator.GenerateTags(words).First();

result.Font.Name.Should().Be("Arial");
result.Font.Size.Should().Be(36);
}
}
}
35 changes: 35 additions & 0 deletions TagsCloudContainer.Tests/TagsCloudContainer.Tests.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>

<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.0" />
<PackageReference Include="FluentAssertions" Version="7.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageReference Include="NUnit" Version="3.14.0" />
<PackageReference Include="NUnit.Analyzers" Version="3.9.0" />
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\TagsCloudContainer\TagsCloudContainer.csproj" />
</ItemGroup>

<ItemGroup>
<Using Include="NUnit.Framework" />
</ItemGroup>

<ItemGroup>
<None Update="TextFile1.txt">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>

</Project>
1 change: 1 addition & 0 deletions TagsCloudContainer.Tests/TextFile1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
он ОНА ОНО корзина творог печенье Корзина ++ ,- = корЗина по ха за
Loading