diff --git a/ConsoleClient/ConsoleClient.cs b/ConsoleClient/ConsoleClient.cs new file mode 100644 index 00000000..0059fb42 --- /dev/null +++ b/ConsoleClient/ConsoleClient.cs @@ -0,0 +1,65 @@ +using McMaster.Extensions.CommandLineUtils; +using Autofac; +using TagsCloudContainer; + +public class ConsoleClient +{ + [Option("--input", "Path to the input file.", CommandOptionType.SingleValue)] + public string? InputDirectory { get; set; } + + [Option("--output", "Path to the output file.", CommandOptionType.SingleValue)] + public string? OutputDirectory { get; set; } + + [Option("--width", "Width of the picture.", CommandOptionType.SingleValue)] + public int PictureWidth { get; set; } + + [Option("--height", "Height of the picture.", CommandOptionType.SingleValue)] + public int PictureHeight { get; set; } + + [Option("--font", "Font for the picture text.", CommandOptionType.SingleValue)] + public string? Font { get; set; } + + [Option("--stopwords", "Comma-separated list of stop words.", CommandOptionType.SingleValue)] + public string? StopWordsInput { get; set; } + + [Option("--rightwords", "Comma-separated list of right words.", CommandOptionType.SingleValue)] + public string? RightWordsInput { get; set; } + + [Option("--colors", "Comma-separated list of colors in ARGB format (e.g., 255,0,0,255).", CommandOptionType.SingleValue)] + public string? ColorsInput { get; set; } + + public static int Main(string[] args) + { + return RunConsoleClient(args); + } + + private static int RunConsoleClient(string[] args) + { + return CommandLineApplication.Execute(args); + } + + private void OnExecute() + { + var config = new Config( + InputDirectory ?? Constants.InputDirectory, + OutputDirectory ?? Constants.OutputDirectory, + PictureWidth == default ? Constants.PictureWidth : PictureWidth, + PictureHeight == default ? Constants.PictureHeight : PictureHeight, + Font ?? Constants.Font, + StopWordsInput?.Split(',').Select(x => x.ToLower()).ToArray() ?? Constants.StopWords, + RightWordsInput?.Split(',').Select(x => x.ToLower()).ToArray() ?? Constants.RightWords, + ColorsInput?.Split(',') ?? Constants.PictureColors + ); + + if (Config.TryValidateConfig(config)) + { + var container = ApplicationRunner.BuildContainer(config); + + using var scope = container.BeginLifetimeScope(); + var runner = scope.Resolve(); + runner.Run(); + } + } +} + + diff --git a/ConsoleClient/ConsoleClient.csproj b/ConsoleClient/ConsoleClient.csproj new file mode 100644 index 00000000..7aba20c5 --- /dev/null +++ b/ConsoleClient/ConsoleClient.csproj @@ -0,0 +1,18 @@ + + + + Exe + net8.0-windows + enable + enable + + + + + + + + + + + diff --git a/FailedTestTagCloud.CircularCloudLayouter_Constructor_CorrectlySetCenter.jpeg b/FailedTestTagCloud.CircularCloudLayouter_Constructor_CorrectlySetCenter.jpeg new file mode 100644 index 00000000..d5823ee8 Binary files /dev/null and b/FailedTestTagCloud.CircularCloudLayouter_Constructor_CorrectlySetCenter.jpeg differ diff --git a/FailedTestTagCloud.PutNextRectangle_PlacesFirstRectangleToCenter.jpeg b/FailedTestTagCloud.PutNextRectangle_PlacesFirstRectangleToCenter.jpeg new file mode 100644 index 00000000..2c002f97 Binary files /dev/null and b/FailedTestTagCloud.PutNextRectangle_PlacesFirstRectangleToCenter.jpeg differ diff --git a/TagCloudContainerTests/CircularCloudLayouterTests.cs b/TagCloudContainerTests/CircularCloudLayouterTests.cs new file mode 100644 index 00000000..9f9bf3b8 --- /dev/null +++ b/TagCloudContainerTests/CircularCloudLayouterTests.cs @@ -0,0 +1,150 @@ +using FluentAssertions; +using NUnit.Framework.Interfaces; +using System.Drawing; +using TagsCloudContainer; +using TagsCloudContainer.Layouters; +using TagsCloudContainer.Visualizers; +using TagsCloudContainer.WordClasses; + +namespace TagCloudContainerTests; + +[TestFixture] +public class CircularCloudLayouterTests +{ + + private CircularCloudLayouter layouter; + private Config config; + private IVisualizer visualizer; + + private readonly string text = "text"; + private readonly Font font = new("Arial", 15); + + [SetUp] + public void Setup() + { + config = new Config(); + layouter = new CircularCloudLayouter(config); + visualizer = new ImageVisualizer(config); + } + + [TearDown] + public void Teardown() + { + var testResult = TestContext.CurrentContext.Result.Outcome; + var testName = TestContext.CurrentContext.Test.Name; + if (Equals(testResult, ResultState.Failure) || + Equals(testResult == ResultState.Error)) + { + var drawer = visualizer; + var directory = Path.Combine(Constants.ProjectDirectory, $"FailedTestTagCloud.{testName}.jpeg"); + config.OutputDirectory = directory; + drawer.GenerateImage(layouter.Rectangles); + Console.WriteLine($"Tag cloud visualization saved to file {directory}"); + } + } + + private SizeWord GetDefaultSizeWord(Size size) + { + return new SizeWord(text, size, font); + } + + [Test] + public void CircularCloudLayouter_Constructor_CorrectlySetCenter() + { + layouter.GetLayout([]); + + layouter.Center + .Should() + .Be(new Point(config.PictureWidth / 2, config.PictureHeight / 2)); + } + + [Test] + public void PutNextRectangle_PlacesFirstRectangleToCenter() + { + var size = new Size(50, 50); + + var rectangles = layouter.GetLayout([GetDefaultSizeWord(size)]); + var centerRecLocation = CircularCloudLayouter. + GetCornerPoint(layouter.Center, size); + + rectangles + .First() + .Bounds + .Location + .Should() + .Be(centerRecLocation); + } + + [Test] + public void PutNextRectangle_RectanglesDoesNotIntersect() + { + var sizeList = new List(); + + for (int i = 10; i < 100; i += 10) + for (int j = 5; j < 50; j += 5) + sizeList.Add(GetDefaultSizeWord(new Size(i, j))); + + var rectangles = layouter.GetLayout(sizeList); + var length = layouter.Rectangles.Count; + for (int i = 0; i < length - 1; i++) + for (int j = 0; j < length - 1; j++) + { + if (i != j) + layouter.Rectangles[i] + .Bounds + .IntersectsWith(layouter.Rectangles[j].Bounds) + .Should().BeFalse(); + } + } + + [Test] + public void PutNextRectangle_IncreaseDistanceFromCenter_WithMoreRectangles() + { + var sizeList = new List + { + GetDefaultSizeWord(new Size(20,10)) + }; + + for (int i = 10; i < 100; i += 10) + for (int j = 10; j < 50; j += 10) + sizeList.Add(GetDefaultSizeWord(new Size(i, j))); + sizeList.Add(GetDefaultSizeWord(new (20, 30))); + + var rectangles = layouter.GetLayout(sizeList); + var firstRectangle = rectangles.First(); + var lastRectangle = rectangles.Last(); + + var firstDistance = FindDistance(layouter.Center, firstRectangle.Bounds.Location); + var lastDistance = FindDistance(layouter.Center, lastRectangle.Bounds.Location); + + lastDistance.Should().BeGreaterThan(firstDistance); + } + + private double FindDistance(Point r1, Point r2) + { + return Math.Sqrt(Math.Pow(r2.X - r1.X, 2) + Math.Pow(r2.Y - r1.Y, 2)); + } + + [Test] + public void PutNextRectangle_ShouldPlaceRectanglesInSpiral() + { + var sizeList = new List + { + GetDefaultSizeWord(new Size(10,10)), + GetDefaultSizeWord(new Size(10,10)), + GetDefaultSizeWord(new Size(10,10)), + GetDefaultSizeWord(new Size(10,10)), + GetDefaultSizeWord(new Size(10,10)) + }; + + var rectangles = layouter.GetLayout(sizeList); + var firstRectangle = rectangles.First().Bounds; + + CircularCloudLayouter.GetCenterPoint(firstRectangle).Should().Be(layouter.Center); + for (int i = 1; i < 4; i++) + { + var rect = layouter.Rectangles[i]; + FindDistance(firstRectangle.Location, rect.Bounds.Location).Should().BeLessThan(30); + } + } +} diff --git a/TagCloudContainerTests/FileReadTest.cs b/TagCloudContainerTests/FileReadTest.cs new file mode 100644 index 00000000..f36c3d16 --- /dev/null +++ b/TagCloudContainerTests/FileReadTest.cs @@ -0,0 +1,56 @@ +using TagsCloudContainer.FileReaders; + +namespace TagCloudContainerTests; + +[TestFixture] +public class FileReadTest +{ + private static readonly VerifySettings Settings = new(); + private IReader Reader; + private string FolderPath; + + [SetUp] + public void Setup() + { + Settings.UseDirectory("Snapshots"); + var projectDirectory = + Path.GetFullPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"..\..\..\")); + FolderPath = Path.Combine(projectDirectory, "Files"); + } + + [Test] + [Parallelizable(ParallelScope.Self)] + public Task Reader_TxtReading() + { + Reader = new TxtFileReader(); + + var filePath = Path.Combine(FolderPath, "wordsTXT.txt"); + var actual = Reader.Read(filePath); + + return Verify(actual, Settings); + } + + [Test] + [Parallelizable(ParallelScope.Self)] + public Task Reader_DocxReading() + { + Reader = new DocxFileReader(); + + var filePath = Path.Combine(FolderPath, "wordsDOCX.docx"); + var actual = Reader.Read(filePath); + + return Verify(actual, Settings); + } + + [Test] + [Parallelizable(ParallelScope.Self)] + public Task Reader_DocReading() + { + Reader = new DocFileReader(); + + var filePath = Path.Combine(FolderPath, "wordsDOC.doc"); + var actual = Reader.Read(filePath); + + return Verify(actual, Settings); + } +} diff --git a/TagCloudContainerTests/Files/wordsDOC.doc b/TagCloudContainerTests/Files/wordsDOC.doc new file mode 100644 index 00000000..8b4b0986 Binary files /dev/null and b/TagCloudContainerTests/Files/wordsDOC.doc differ diff --git a/TagCloudContainerTests/Files/wordsDOCX.docx b/TagCloudContainerTests/Files/wordsDOCX.docx new file mode 100644 index 00000000..d43b24e1 Binary files /dev/null and b/TagCloudContainerTests/Files/wordsDOCX.docx differ diff --git a/TagCloudContainerTests/Files/wordsTXT.txt b/TagCloudContainerTests/Files/wordsTXT.txt new file mode 100644 index 00000000..e2dde216 --- /dev/null +++ b/TagCloudContainerTests/Files/wordsTXT.txt @@ -0,0 +1,500 @@ +ежевика +клубника +клубника +банан +киви +киви +папайя +манго +инжир +мандарин +облепиха +лимон +айва +финик +черешня +мандарин +инжир +манго +ваниль +арбуз +апельсин +черешня +арбуз +ежевика +ваниль +финик +папайя +клубника +финик +финик +юзу +яблоко +дыня +вишня +виноград +апельсин +мандарин +юзу +облепиха +айва +вишня +айва +нектарин +облепиха +лимон +дыня +мандарин +апельсин +арбуз +черешня +дыня +мандарин +киви +нектарин +вишня +айва +ежевика +манго +банан +папайя +инжир +киви +апельсин +цуккини +папайя +малина +дыня +ежевика +юзу +дыня +апельсин +облепиха +ежевика +дыня +банан +нектарин +ваниль +банан +ежевика +вишня +яблоко +лимон +виноград +клубника +малина +яблоко +облепиха +юзу +арбуз +киви +дыня +инжир +мандарин +дыня +манго +нектарин +айва +ваниль +финик +финик +клубника +папайя +банан +яблоко +цуккини +апельсин +клубника +клубника +апельсин +клубника +киви +цуккини +мандарин +айва +финик +папайя +лимон +черешня +клубника +финик +манго +виноград +цуккини +виноград +малина +ваниль +ежевика +ежевика +виноград +нектарин +айва +банан +ежевика +цуккини +клубника +киви +инжир +виноград +вишня +малина +яблоко +нектарин +малина +манго +банан +дыня +ваниль +киви +нектарин +вишня +ваниль +цуккини +виноград +ежевика +апельсин +банан +клубника +виноград +киви +вишня +нектарин +лимон +банан +малина +облепиха +финик +ваниль +арбуз +дыня +виноград +ежевика +юзу +лимон +черешня +финик +арбуз +яблоко +облепиха +виноград +вишня +яблоко +нектарин +дыня +арбуз +малина +облепиха +черешня +арбуз +манго +манго +облепиха +лимон +манго +дыня +ежевика +нектарин +нектарин +мандарин +черешня +папайя +клубника +финик +яблоко +лимон +апельсин +клубника +киви +манго +апельсин +виноград +айва +нектарин +арбуз +малина +виноград +дыня +ваниль +айва +лимон +малина +апельсин +вишня +виноград +киви +мандарин +ваниль +киви +дыня +клубника +вишня +лимон +мандарин +ежевика +банан +цуккини +вишня +облепиха +нектарин +виноград +яблоко +арбуз +облепиха +юзу +папайя +черешня +клубника +нектарин +папайя +манго +цуккини +лимон +нектарин +клубника +апельсин +малина +киви +виноград +ежевика +дыня +дыня +финик +дыня +инжир +черешня +манго +арбуз +ежевика +ваниль +арбуз +нектарин +инжир +арбуз +мандарин +ваниль +манго +вишня +облепиха +арбуз +вишня +инжир +банан +вишня +виноград +финик +юзу +арбуз +инжир +цуккини +облепиха +яблоко +дыня +юзу +ваниль +киви +инжир +облепиха +ваниль +апельсин +дыня +нектарин +яблоко +лимон +папайя +апельсин +малина +ежевика +ежевика +апельсин +лимон +папайя +апельсин +дыня +дыня +финик +юзу +киви +цуккини +вишня +клубника +лимон +вишня +дыня +айва +ваниль +ваниль +мандарин +мандарин +яблоко +апельсин +юзу +вишня +клубника +финик +облепиха +юзу +ваниль +юзу +дыня +манго +цуккини +апельсин +цуккини +клубника +апельсин +папайя +ежевика +лимон +цуккини +айва +облепиха +дыня +мандарин +лимон +киви +мандарин +юзу +банан +виноград +виноград +киви +черешня +манго +ежевика +айва +яблоко +цуккини +банан +виноград +черешня +апельсин +киви +арбуз +малина +нектарин +киви +банан +манго +ваниль +облепиха +юзу +нектарин +яблоко +вишня +нектарин +лимон +арбуз +лимон +папайя +цуккини +апельсин +инжир +банан +виноград +финик +айва +дыня +вишня +финик +ваниль +мандарин +малина +киви +инжир +киви +банан +цуккини +лимон +вишня +облепиха +инжир +финик +юзу +цуккини +инжир +апельсин +малина +апельсин +дыня +банан +виноград +киви +юзу +дыня +цуккини +дыня +манго +вишня +айва +черешня +яблоко +вишня +киви +апельсин +айва +лимон +банан +финик +инжир +финик +киви +клубника +черешня +вишня +малина +вишня +манго +киви +арбуз +папайя +арбуз +цуккини +айва +манго +черешня +дыня +черешня +ежевика +инжир +юзу +нектарин +манго +банан +нектарин +нектарин +айва +апельсин +инжир +папайя +ежевика +инжир +финик +апельсин +киви +клубника +манго +яблоко +инжир +цуккини +виноград +папайя +черешня +нектарин +виноград +арбуз +клубника +лимон +арбуз +яблоко +инжир +арбуз +ежевика +киви +мандарин +черешня +мандарин +яблоко +манго +ежевика +папайя +мандарин \ No newline at end of file diff --git a/TagCloudContainerTests/FilterTests.cs b/TagCloudContainerTests/FilterTests.cs new file mode 100644 index 00000000..f8e6f011 --- /dev/null +++ b/TagCloudContainerTests/FilterTests.cs @@ -0,0 +1,129 @@ +using TagsCloudContainer.FileReaders; +using TagsCloudContainer; +using TagsCloudContainer.Filters; +using FluentAssertions; + +namespace TagCloudContainerTests; + +[TestFixture] +public class FilterTests +{ + private Config config; + private string filterWordsFile; + + [SetUp] + public void Setup() + { + config = new Config(); + var folderPath = Path.Combine(Constants.ProjectDirectory, "TagCloudConrainerTests\\Files"); + filterWordsFile = Path.Combine(folderPath, "FilterWords.txt"); + } + + [Test] + [Parallelizable(ParallelScope.Self)] + public void Filter_ReadWordsFromFile() + { + var filter = new WordsFilter(config); + var reader = new TxtFileReader(); + + var filterWords = + reader + .Read(Constants.FilterWordsDirectory) + .Split("\r\n") + .ToArray(); + + foreach (var word in filterWords) + { + filter.Contains(word).Should().BeTrue(); + } + } + + [TestCase("Абрикос")] + [TestCase("Яблоко")] + [TestCase("Глицерин")] + [TestCase("Сталин")] + [TestCase("Car")] + public void Filter_AddStopWord_FromMethod(string stopWord) + { + var filter = new WordsFilter(config); + + filter.AddStopWord(stopWord); + + filter.Contains(stopWord).Should().BeTrue(); + } + + + public void Filter_AddStopWords_FromMethod() + { + var filter = new WordsFilter(config); + var stopWord = new string[] { "Карамель", "Ирис", "Шоколадка", "Трансформер" }; + + filter.AddStopWords(stopWord); + + foreach (var word in stopWord) + { + filter.Contains(word).Should().BeTrue(); + } + } + + [TestCase("а")] + [TestCase("в")] + [TestCase("за")] + [TestCase("под")] + [TestCase("из")] + public void Filter_RemoveRightWord_FromMethod(string stopWord) + { + var filter = new WordsFilter(config); + + filter.Contains(stopWord).Should().BeTrue(); + + filter.RemoveRightWord(stopWord); + + filter.Contains(stopWord).Should().BeFalse(); + } + + [Test] + public void Filter_RemoveRightWords_FromMethod() + { + var filter = new WordsFilter(config); + var rightWord = new string[] { "он", "она", "они", "оно" }; + + foreach (var word in rightWord) + { + filter.Contains(word).Should().BeTrue(); + } + + filter.RemoveRightWords(rightWord); + + foreach (var word in rightWord) + { + filter.Contains(word).Should().BeFalse(); + } + } + + [Test] + public void Filter_AddStopWords_FromConfig() + { + config.StopWords = ["Моска", "Волгоград"]; + + var filter = new WordsFilter(config); + + foreach (var word in config.StopWords) + { + filter.Contains(word).Should().BeTrue(); + } + } + + public void Filter_RemoveRightWords_FromConfig() + { + config.StopWords = ["он", "она", "они", "оно"]; + + var filter = new WordsFilter(config); + + foreach (var word in config.StopWords) + { + filter.Contains(word).Should().BeFalse(); + } + } +} + diff --git a/TagCloudContainerTests/GeneralTests.cs b/TagCloudContainerTests/GeneralTests.cs new file mode 100644 index 00000000..6f0ee2d3 --- /dev/null +++ b/TagCloudContainerTests/GeneralTests.cs @@ -0,0 +1,68 @@ +using FluentAssertions; +using System.Drawing; +using TagsCloudContainer; +using TagsCloudContainer.FileReaders; +using TagsCloudContainer.Filters; +using TagsCloudContainer.Layouters; +using TagsCloudContainer.Parsers; +using TagsCloudContainer.Visualizers; +using TagsCloudContainer.WordClasses; +using TagsCloudContainer.WordSizer; + +namespace TagCloudContainerTests +{ + [TestFixture] + public class GeneralTests + { + private Config config; + private string[] stopWords; + + + [SetUp] + public void Setup() + { + config = new Config(); + config.InputDirectory = @"C:\Users\dima0\source\repos\di-updated\TagsCloudContainer\Files\General.txt"; + config.OutputDirectory = @"C:\Users\dima0\source\repos\di-updated\TagsCloudContainer\Pictures\general.jpg"; + config.PictureWidth = 600; + config.PictureHeight = 700; + config.Font = "Impact"; + config.StopWords = ["отпуск", "птица", "любовь"]; + config.RightWords = ["я"]; + + stopWords = config.StopWords; + } + + [Test] + public void CircularCloudContainer_GeneralTest() + { + var reader = new TxtFileReader(); + var filter = new WordsFilter(config); + var parser = new SimpleParser(filter); + var sizer = new SimpleSizer(config); + var layouter = new CircularCloudLayouter(config); + var visualizer = new ImageVisualizer(config); + + var words = reader.Read(config.InputDirectory); + var filteredWords = parser.Parse(words); + var wordSizes = sizer.GetSizes(filteredWords); + var layout = layouter.GetLayout(wordSizes); + visualizer.GenerateImage(layout); + + stopWords.All(x => filter.Contains(x)).Should().BeTrue(); + filter.Contains("я").Should().BeFalse(); + + wordSizes.All(x => filter.Contains(x.Value)).Should().BeFalse(); + wordSizes.All(x => x.font.Name == "Impact").Should().BeTrue(); + wordSizes.Any(x => x.Value == "я").Should().BeTrue(); + + layout.All(x => filter.Contains(x.Value)).Should().BeFalse(); + layout.All(x => x.font.Name == "Impact").Should().BeTrue(); + layout.Any(x => x.Value == "я").Should().BeTrue(); + + using Image image = Image.FromFile(config.OutputDirectory); + image.Width.Should().Be(600); + image.Height.Should().Be(700); + } + } +} diff --git a/TagCloudContainerTests/ParserTests.cs b/TagCloudContainerTests/ParserTests.cs new file mode 100644 index 00000000..2204ecb0 --- /dev/null +++ b/TagCloudContainerTests/ParserTests.cs @@ -0,0 +1,89 @@ +using FluentAssertions; +using TagsCloudContainer; +using TagsCloudContainer.FileReaders; +using TagsCloudContainer.Filters; +using TagsCloudContainer.Parsers; + +namespace TagCloudContainerTests; + +[TestFixture] +public class ParserTests +{ + private Config config; + private WordsFilter defaultFilter; + + [SetUp] + public void Setup() + { + config = new Config(); + defaultFilter = new WordsFilter(config); + } + + [TestCase("Шла Саша шоссе")] + [TestCase("Бублик Печенька")] + [TestCase("Автобот качели")] + [TestCase("мимино квадрат малевич")] + public void Parser_CorrectWithSimpleWords(string text) + { + var parser = new SimpleParser(defaultFilter); + + var parsed = parser.Parse(text); + + foreach (var word in text.Split(' ')) + { + parsed.ContainsKey(word.ToLower()).Should().BeTrue(); + } + } + + [TestCase("Шла %1--Саша @#^%шоссе", "шла саша шоссе")] + [TestCase("Бублик@ -Печенька", "бублик печенька")] + [TestCase("()()()Автобот _+качели", "автобот качели")] + [TestCase("мимино% квадрат$ #малевич++", "мимино квадрат малевич")] + [TestCase("%:(*?кролик:%**)", "кролик")] + public void Parser_CorrectWithNonLettericSymbols(string text, string correct) + { + var parser = new SimpleParser(defaultFilter); + + var parsed = parser.Parse(text); + + foreach (var word in correct.Split(' ')) + { + parsed.ContainsKey(word).Should().BeTrue(); + } + } + + + [TestCase("шла я и саша по шоссе", "шла саша шоссе")] + [TestCase("бублик под печенькой", "бублик печенькой")] + [TestCase("автобот от качелей", "автобот качелей")] + [TestCase("мимино оно как квадрат малевича", "мимино квадрат малевича")] + public void Parser_FilterStopWords(string text, string correct) + { + var parser = new SimpleParser(defaultFilter); + + var parsed = parser.Parse(text); + + foreach (var word in correct.Split(' ')) + { + parsed.ContainsKey(word).Should().BeTrue(); + } + } + + [Test] + public void Parser_GiveCorrectWeightToWords() + { + var parser = new SimpleParser(defaultFilter); + var text = "один два два три три три четыре четыре четыре четыре"; + var correct = new Dictionary + { + { "один", 1}, + { "два", 2}, + { "три", 3}, + { "четыре", 4}, + }; + + var parsed = parser.Parse(text); + + parsed.Should().BeEquivalentTo(correct); + } +} diff --git a/TagCloudContainerTests/SizerTests.cs b/TagCloudContainerTests/SizerTests.cs new file mode 100644 index 00000000..d24db0c7 --- /dev/null +++ b/TagCloudContainerTests/SizerTests.cs @@ -0,0 +1,63 @@ +using FluentAssertions; +using System.Drawing; +using TagsCloudContainer; +using TagsCloudContainer.WordSizer; + +namespace TagCloudContainerTests; + +[TestFixture] +public class SizerTests +{ + private Config config; + private Dictionary defaultDictionary; + + [SetUp] + public void Setup() + { + config = new Config(); + + defaultDictionary = new Dictionary + { + { "один", 1}, + { "два", 2}, + { "три", 3}, + { "четыре", 4}, + }; + + var folderPath = Path.Combine(Constants.ProjectDirectory, "TagCloudConrainerTests\\Files"); + } + + [Test] + public void Sizer_ReturnSizeForAllWords() + { + var sizer = new SimpleSizer(config); + + var sized = sizer.GetSizes(defaultDictionary); + + sized + .Select(x => x.Value) + .Should() + .BeEquivalentTo(defaultDictionary.Keys); + } + + [Test] + public void Sizer_GiveBiggerSizeToHeavierWord() + { + var sizer = new SimpleSizer(config); + + var sized = sizer.GetSizes(defaultDictionary).ToArray(); + + for (var i = 0; i < sized.Length - 2; i++) + { + var heavierWord = GetSizeSquare(sized[i].Size); + var lighterWord = GetSizeSquare(sized[i + 1].Size); + heavierWord.Should().BeGreaterThan(lighterWord); + } + } + + private int GetSizeSquare(Size size) + { + return size.Width * size.Height; + } + +} \ No newline at end of file diff --git a/TagCloudContainerTests/Snapshots/FileReadTest.Reader_DocReading.verified.txt b/TagCloudContainerTests/Snapshots/FileReadTest.Reader_DocReading.verified.txt new file mode 100644 index 00000000..c4925179 --- /dev/null +++ b/TagCloudContainerTests/Snapshots/FileReadTest.Reader_DocReading.verified.txt @@ -0,0 +1,100 @@ +площадь +автомобиль +ресторан +улица +успех +цветок +дом +автомобиль +дружба +кафе +учитель +кафе +страна +путешествие +успех +студент +искусство +музыка +город +фотография +солнце +интернет +учитель +парк +алгоритм +город +небо +город +песня +программирование +площадь +алгоритм +студент +город +птица +работа +ресторан +работа +телефон +любовь +дружба +математика +наука +счастье +еда +телефон +небо +любовь +еда +собака +путешествие +кафе +дом +компьютер +еда +дружба +фильм +река +город +путешествие +море +шарик +алгоритм +небо +солнце +шарик +фотография +город +школа +искусство +город +город +птица +актёр +успех +автомобиль +страна +алгоритм +еда +математика +небо +кинематограф +путешествие +счастье +съёмка +фильм +страна +солнце +собака +интернет +город +гора +площадь +путешествие +море +телефон +кот +дом +любовь +компьютер diff --git a/TagCloudContainerTests/Snapshots/FileReadTest.Reader_DocxReading.verified.txt b/TagCloudContainerTests/Snapshots/FileReadTest.Reader_DocxReading.verified.txt new file mode 100644 index 00000000..c4925179 --- /dev/null +++ b/TagCloudContainerTests/Snapshots/FileReadTest.Reader_DocxReading.verified.txt @@ -0,0 +1,100 @@ +площадь +автомобиль +ресторан +улица +успех +цветок +дом +автомобиль +дружба +кафе +учитель +кафе +страна +путешествие +успех +студент +искусство +музыка +город +фотография +солнце +интернет +учитель +парк +алгоритм +город +небо +город +песня +программирование +площадь +алгоритм +студент +город +птица +работа +ресторан +работа +телефон +любовь +дружба +математика +наука +счастье +еда +телефон +небо +любовь +еда +собака +путешествие +кафе +дом +компьютер +еда +дружба +фильм +река +город +путешествие +море +шарик +алгоритм +небо +солнце +шарик +фотография +город +школа +искусство +город +город +птица +актёр +успех +автомобиль +страна +алгоритм +еда +математика +небо +кинематограф +путешествие +счастье +съёмка +фильм +страна +солнце +собака +интернет +город +гора +площадь +путешествие +море +телефон +кот +дом +любовь +компьютер diff --git a/TagCloudContainerTests/Snapshots/FileReadTest.Reader_TxtReading.verified.txt b/TagCloudContainerTests/Snapshots/FileReadTest.Reader_TxtReading.verified.txt new file mode 100644 index 00000000..4776a9f7 --- /dev/null +++ b/TagCloudContainerTests/Snapshots/FileReadTest.Reader_TxtReading.verified.txt @@ -0,0 +1,500 @@ +ежевика +клубника +клубника +банан +киви +киви +папайя +манго +инжир +мандарин +облепиха +лимон +айва +финик +черешня +мандарин +инжир +манго +ваниль +арбуз +апельсин +черешня +арбуз +ежевика +ваниль +финик +папайя +клубника +финик +финик +юзу +яблоко +дыня +вишня +виноград +апельсин +мандарин +юзу +облепиха +айва +вишня +айва +нектарин +облепиха +лимон +дыня +мандарин +апельсин +арбуз +черешня +дыня +мандарин +киви +нектарин +вишня +айва +ежевика +манго +банан +папайя +инжир +киви +апельсин +цуккини +папайя +малина +дыня +ежевика +юзу +дыня +апельсин +облепиха +ежевика +дыня +банан +нектарин +ваниль +банан +ежевика +вишня +яблоко +лимон +виноград +клубника +малина +яблоко +облепиха +юзу +арбуз +киви +дыня +инжир +мандарин +дыня +манго +нектарин +айва +ваниль +финик +финик +клубника +папайя +банан +яблоко +цуккини +апельсин +клубника +клубника +апельсин +клубника +киви +цуккини +мандарин +айва +финик +папайя +лимон +черешня +клубника +финик +манго +виноград +цуккини +виноград +малина +ваниль +ежевика +ежевика +виноград +нектарин +айва +банан +ежевика +цуккини +клубника +киви +инжир +виноград +вишня +малина +яблоко +нектарин +малина +манго +банан +дыня +ваниль +киви +нектарин +вишня +ваниль +цуккини +виноград +ежевика +апельсин +банан +клубника +виноград +киви +вишня +нектарин +лимон +банан +малина +облепиха +финик +ваниль +арбуз +дыня +виноград +ежевика +юзу +лимон +черешня +финик +арбуз +яблоко +облепиха +виноград +вишня +яблоко +нектарин +дыня +арбуз +малина +облепиха +черешня +арбуз +манго +манго +облепиха +лимон +манго +дыня +ежевика +нектарин +нектарин +мандарин +черешня +папайя +клубника +финик +яблоко +лимон +апельсин +клубника +киви +манго +апельсин +виноград +айва +нектарин +арбуз +малина +виноград +дыня +ваниль +айва +лимон +малина +апельсин +вишня +виноград +киви +мандарин +ваниль +киви +дыня +клубника +вишня +лимон +мандарин +ежевика +банан +цуккини +вишня +облепиха +нектарин +виноград +яблоко +арбуз +облепиха +юзу +папайя +черешня +клубника +нектарин +папайя +манго +цуккини +лимон +нектарин +клубника +апельсин +малина +киви +виноград +ежевика +дыня +дыня +финик +дыня +инжир +черешня +манго +арбуз +ежевика +ваниль +арбуз +нектарин +инжир +арбуз +мандарин +ваниль +манго +вишня +облепиха +арбуз +вишня +инжир +банан +вишня +виноград +финик +юзу +арбуз +инжир +цуккини +облепиха +яблоко +дыня +юзу +ваниль +киви +инжир +облепиха +ваниль +апельсин +дыня +нектарин +яблоко +лимон +папайя +апельсин +малина +ежевика +ежевика +апельсин +лимон +папайя +апельсин +дыня +дыня +финик +юзу +киви +цуккини +вишня +клубника +лимон +вишня +дыня +айва +ваниль +ваниль +мандарин +мандарин +яблоко +апельсин +юзу +вишня +клубника +финик +облепиха +юзу +ваниль +юзу +дыня +манго +цуккини +апельсин +цуккини +клубника +апельсин +папайя +ежевика +лимон +цуккини +айва +облепиха +дыня +мандарин +лимон +киви +мандарин +юзу +банан +виноград +виноград +киви +черешня +манго +ежевика +айва +яблоко +цуккини +банан +виноград +черешня +апельсин +киви +арбуз +малина +нектарин +киви +банан +манго +ваниль +облепиха +юзу +нектарин +яблоко +вишня +нектарин +лимон +арбуз +лимон +папайя +цуккини +апельсин +инжир +банан +виноград +финик +айва +дыня +вишня +финик +ваниль +мандарин +малина +киви +инжир +киви +банан +цуккини +лимон +вишня +облепиха +инжир +финик +юзу +цуккини +инжир +апельсин +малина +апельсин +дыня +банан +виноград +киви +юзу +дыня +цуккини +дыня +манго +вишня +айва +черешня +яблоко +вишня +киви +апельсин +айва +лимон +банан +финик +инжир +финик +киви +клубника +черешня +вишня +малина +вишня +манго +киви +арбуз +папайя +арбуз +цуккини +айва +манго +черешня +дыня +черешня +ежевика +инжир +юзу +нектарин +манго +банан +нектарин +нектарин +айва +апельсин +инжир +папайя +ежевика +инжир +финик +апельсин +киви +клубника +манго +яблоко +инжир +цуккини +виноград +папайя +черешня +нектарин +виноград +арбуз +клубника +лимон +арбуз +яблоко +инжир +арбуз +ежевика +киви +мандарин +черешня +мандарин +яблоко +манго +ежевика +папайя +мандарин \ No newline at end of file diff --git a/TagCloudContainerTests/TagCloudContainerTests.csproj b/TagCloudContainerTests/TagCloudContainerTests.csproj new file mode 100644 index 00000000..5734929d --- /dev/null +++ b/TagCloudContainerTests/TagCloudContainerTests.csproj @@ -0,0 +1,34 @@ + + + + net8.0-windows + enable + enable + + false + true + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + + + + diff --git a/TagsCloudContainer/ApplicationRunner.cs b/TagsCloudContainer/ApplicationRunner.cs new file mode 100644 index 00000000..9eb8cbd8 --- /dev/null +++ b/TagsCloudContainer/ApplicationRunner.cs @@ -0,0 +1,66 @@ +using Autofac; +using TagsCloudContainer; +using TagsCloudContainer.FileReaders; +using TagsCloudContainer.Filters; +using TagsCloudContainer.Layouters; +using TagsCloudContainer.Parsers; +using TagsCloudContainer.Visualizers; +using TagsCloudContainer.WordSizer; + +public class ApplicationRunner : IApplicationRunner +{ + private readonly Config config; + private readonly IReader reader; + private readonly IParser parser; + private readonly ISizer sizer; + private readonly ILayouter layouter; + private readonly IVisualizer visualizer; + + public ApplicationRunner( + Config config, + IReader reader, + IParser parser, + ISizer sizer, + ILayouter layouter, + IVisualizer visualizer) + { + this.config = config; + this.reader = reader; + this.parser = parser; + this.sizer = sizer; + this.layouter = layouter; + this.visualizer = visualizer; + } + + public void Run() + { + visualizer.GenerateImage( + layouter.GetLayout( + sizer.GetSizes( + parser.Parse( + reader.Read( + config.InputDirectory + ) + ) + ) + ) + ); + } + + public static IContainer BuildContainer(Config config) + { + var builder = new ContainerBuilder(); + + builder.RegisterInstance(config).As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + + builder.RegisterType().As(); + + return builder.Build(); + } +} diff --git a/TagsCloudContainer/Config.cs b/TagsCloudContainer/Config.cs new file mode 100644 index 00000000..33333e64 --- /dev/null +++ b/TagsCloudContainer/Config.cs @@ -0,0 +1,85 @@ +using Org.BouncyCastle.Asn1.Esf; +using System.Drawing; +using System.Drawing.Text; +using System.IO; + +namespace TagsCloudContainer; + +public class Config +{ + public string InputDirectory { get; set; } + public string OutputDirectory { get; set; } + public int PictureWidth { get; set; } + public int PictureHeight { get; set; } + public string Font { get; set; } + public string[] StopWords { get; set; } + public string[] RightWords { get; set; } + public string[] PictureColors { get; set; } + + public Config( + string inputDirectory, + string outputDirectory, + int pictureWidth, + int pictureHeight, + string font, + string[] stopWords, + string[] rightWords, + string[] pictureColors) + { + InputDirectory = inputDirectory; + OutputDirectory = outputDirectory; + PictureWidth = pictureWidth; + PictureHeight = pictureHeight; + Font = font; + StopWords = stopWords; + RightWords = rightWords; + PictureColors = pictureColors; + } + + public Config() + { + InputDirectory = Constants.InputDirectory; + OutputDirectory = Constants.OutputDirectory; + PictureWidth = Constants.PictureWidth; + PictureHeight = Constants.PictureHeight; + Font = Constants.Font; + StopWords = Constants.StopWords; + RightWords = Constants.RightWords; + PictureColors = Constants.PictureColors; + } + + public static bool TryValidateConfig(Config config) + { + if (!Path.Exists(config.InputDirectory)) + throw new FileNotFoundException("File not found.", config.InputDirectory); + + if (!Path.Exists(Path.Combine(config.OutputDirectory, @"..\"))) + throw new FileNotFoundException("File not found.", config.OutputDirectory); + + if (config.PictureWidth <= 0 || config.PictureWidth > 3000) + throw new ArgumentException("Picture Width shoud be more than zero and less than 3000", config.OutputDirectory); + + if (config.PictureHeight <= 0 || config.PictureHeight > 3000) + throw new ArgumentException("Picture Height shoud be more than zero and less than 3000", config.OutputDirectory); + + if (!IsFontAvailable(config.Font)) + throw new ArgumentException("Font doesn't exist in system", config.Font); + + return true; + } + + private static bool IsFontAvailable(string fontName) + { + try + { + using (var font = new Font(fontName, 12)) + { + return font.Name.Equals(fontName, StringComparison.InvariantCultureIgnoreCase); + } + } + catch + { + return false; + } + } +} diff --git a/TagsCloudContainer/Constants.cs b/TagsCloudContainer/Constants.cs new file mode 100644 index 00000000..8b3d03ca --- /dev/null +++ b/TagsCloudContainer/Constants.cs @@ -0,0 +1,33 @@ +using System.Text.RegularExpressions; + +namespace TagsCloudContainer; +public static partial class Constants +{ + public static string InputDirectory + => Path.Combine(ProjectDirectory, "TagsCloudContainer\\Files\\defaultTxt.txt"); + + public static string OutputDirectory + => Path.Combine(ProjectDirectory, "TagsCloudContainer\\Pictures\\picture4.jpg"); + + public static string FilterWordsDirectory + => Path.Combine(ProjectDirectory, "TagsCloudContainer\\Files\\filterwords.txt"); + + public static string ProjectDirectory + => Path.GetFullPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"..\..\..\..\")); + + public static int PictureWidth => 800; + + public static int PictureHeight => 800; + + public static string Font => "Arial"; + + public static string[] StopWords => []; + + public static string[] RightWords => []; + + public static string[] PictureColors => []; + + public readonly static Regex WordsSplitRegex = new( + pattern: @"\b[а-яА-ЯёЁa-zA-Z]+\b", + options: RegexOptions.Compiled | RegexOptions.IgnoreCase); +} diff --git a/TagsCloudContainer/FileReaders/DocFileReader.cs b/TagsCloudContainer/FileReaders/DocFileReader.cs new file mode 100644 index 00000000..bd22cbb6 --- /dev/null +++ b/TagsCloudContainer/FileReaders/DocFileReader.cs @@ -0,0 +1,20 @@ +using Spire.Doc; + +namespace TagsCloudContainer.FileReaders; + +public class DocFileReader : IReader +{ + public string Read(string path) + { + if (string.IsNullOrEmpty(path)) + throw new ArgumentException("Path cannot be null or empty.", nameof(path)); + + if (!File.Exists(path)) + throw new FileNotFoundException("File not found.", path); + + var document = new Document(); + document.LoadFromFile(path); + var text = document.GetText(); + return text; + } +} diff --git a/TagsCloudContainer/FileReaders/DocxFileReader.cs b/TagsCloudContainer/FileReaders/DocxFileReader.cs new file mode 100644 index 00000000..1fb19e65 --- /dev/null +++ b/TagsCloudContainer/FileReaders/DocxFileReader.cs @@ -0,0 +1,25 @@ +using NPOI.XWPF.UserModel; + +namespace TagsCloudContainer.FileReaders; + +public class DocxFileReader : IReader +{ + public string Read(string path) + { + if (string.IsNullOrEmpty(path)) + throw new ArgumentException("Path cannot be null or empty.", nameof(path)); + + if (!File.Exists(path)) + throw new FileNotFoundException("File not found.", path); + + using var stream = File.OpenRead(path); + using var writer = new StringWriter(); + + var docx = new XWPFDocument(stream); + foreach (var paragraph in docx.Paragraphs) + { + writer.WriteLine(paragraph.Text); + } + return writer.ToString(); + } +} diff --git a/TagsCloudContainer/FileReaders/IReader.cs b/TagsCloudContainer/FileReaders/IReader.cs new file mode 100644 index 00000000..6b617736 --- /dev/null +++ b/TagsCloudContainer/FileReaders/IReader.cs @@ -0,0 +1,6 @@ +namespace TagsCloudContainer.FileReaders; + +public interface IReader +{ + string Read(string path); +} diff --git a/TagsCloudContainer/FileReaders/TxtFileReader.cs b/TagsCloudContainer/FileReaders/TxtFileReader.cs new file mode 100644 index 00000000..bfd46e7c --- /dev/null +++ b/TagsCloudContainer/FileReaders/TxtFileReader.cs @@ -0,0 +1,16 @@ +namespace TagsCloudContainer.FileReaders; + +public class TxtFileReader : IReader +{ + public string Read(string path) + { + if (string.IsNullOrEmpty(path)) + throw new ArgumentException("Path cannot be null or empty.", nameof(path)); + + if (!File.Exists(path)) + throw new FileNotFoundException("File not found.", path); + + + return File.ReadAllText(path); + } +} diff --git a/TagsCloudContainer/Files/General.txt b/TagsCloudContainer/Files/General.txt new file mode 100644 index 00000000..a18aaf63 --- /dev/null +++ b/TagsCloudContainer/Files/General.txt @@ -0,0 +1,39 @@ +солнце +дом +дом +работа +работа +работа +улица +человек +человек +человек +птица +мир +река +река +река +любовь +еда +город +город +город +путешествие +машина +книга +отпуск +собака +собака +дерево +дерево +дерево +семья +друзья +погода +время +время +время +я +я +я +я \ No newline at end of file diff --git a/TagsCloudContainer/Files/defaultTxt.txt b/TagsCloudContainer/Files/defaultTxt.txt new file mode 100644 index 00000000..e2dde216 --- /dev/null +++ b/TagsCloudContainer/Files/defaultTxt.txt @@ -0,0 +1,500 @@ +ежевика +клубника +клубника +банан +киви +киви +папайя +манго +инжир +мандарин +облепиха +лимон +айва +финик +черешня +мандарин +инжир +манго +ваниль +арбуз +апельсин +черешня +арбуз +ежевика +ваниль +финик +папайя +клубника +финик +финик +юзу +яблоко +дыня +вишня +виноград +апельсин +мандарин +юзу +облепиха +айва +вишня +айва +нектарин +облепиха +лимон +дыня +мандарин +апельсин +арбуз +черешня +дыня +мандарин +киви +нектарин +вишня +айва +ежевика +манго +банан +папайя +инжир +киви +апельсин +цуккини +папайя +малина +дыня +ежевика +юзу +дыня +апельсин +облепиха +ежевика +дыня +банан +нектарин +ваниль +банан +ежевика +вишня +яблоко +лимон +виноград +клубника +малина +яблоко +облепиха +юзу +арбуз +киви +дыня +инжир +мандарин +дыня +манго +нектарин +айва +ваниль +финик +финик +клубника +папайя +банан +яблоко +цуккини +апельсин +клубника +клубника +апельсин +клубника +киви +цуккини +мандарин +айва +финик +папайя +лимон +черешня +клубника +финик +манго +виноград +цуккини +виноград +малина +ваниль +ежевика +ежевика +виноград +нектарин +айва +банан +ежевика +цуккини +клубника +киви +инжир +виноград +вишня +малина +яблоко +нектарин +малина +манго +банан +дыня +ваниль +киви +нектарин +вишня +ваниль +цуккини +виноград +ежевика +апельсин +банан +клубника +виноград +киви +вишня +нектарин +лимон +банан +малина +облепиха +финик +ваниль +арбуз +дыня +виноград +ежевика +юзу +лимон +черешня +финик +арбуз +яблоко +облепиха +виноград +вишня +яблоко +нектарин +дыня +арбуз +малина +облепиха +черешня +арбуз +манго +манго +облепиха +лимон +манго +дыня +ежевика +нектарин +нектарин +мандарин +черешня +папайя +клубника +финик +яблоко +лимон +апельсин +клубника +киви +манго +апельсин +виноград +айва +нектарин +арбуз +малина +виноград +дыня +ваниль +айва +лимон +малина +апельсин +вишня +виноград +киви +мандарин +ваниль +киви +дыня +клубника +вишня +лимон +мандарин +ежевика +банан +цуккини +вишня +облепиха +нектарин +виноград +яблоко +арбуз +облепиха +юзу +папайя +черешня +клубника +нектарин +папайя +манго +цуккини +лимон +нектарин +клубника +апельсин +малина +киви +виноград +ежевика +дыня +дыня +финик +дыня +инжир +черешня +манго +арбуз +ежевика +ваниль +арбуз +нектарин +инжир +арбуз +мандарин +ваниль +манго +вишня +облепиха +арбуз +вишня +инжир +банан +вишня +виноград +финик +юзу +арбуз +инжир +цуккини +облепиха +яблоко +дыня +юзу +ваниль +киви +инжир +облепиха +ваниль +апельсин +дыня +нектарин +яблоко +лимон +папайя +апельсин +малина +ежевика +ежевика +апельсин +лимон +папайя +апельсин +дыня +дыня +финик +юзу +киви +цуккини +вишня +клубника +лимон +вишня +дыня +айва +ваниль +ваниль +мандарин +мандарин +яблоко +апельсин +юзу +вишня +клубника +финик +облепиха +юзу +ваниль +юзу +дыня +манго +цуккини +апельсин +цуккини +клубника +апельсин +папайя +ежевика +лимон +цуккини +айва +облепиха +дыня +мандарин +лимон +киви +мандарин +юзу +банан +виноград +виноград +киви +черешня +манго +ежевика +айва +яблоко +цуккини +банан +виноград +черешня +апельсин +киви +арбуз +малина +нектарин +киви +банан +манго +ваниль +облепиха +юзу +нектарин +яблоко +вишня +нектарин +лимон +арбуз +лимон +папайя +цуккини +апельсин +инжир +банан +виноград +финик +айва +дыня +вишня +финик +ваниль +мандарин +малина +киви +инжир +киви +банан +цуккини +лимон +вишня +облепиха +инжир +финик +юзу +цуккини +инжир +апельсин +малина +апельсин +дыня +банан +виноград +киви +юзу +дыня +цуккини +дыня +манго +вишня +айва +черешня +яблоко +вишня +киви +апельсин +айва +лимон +банан +финик +инжир +финик +киви +клубника +черешня +вишня +малина +вишня +манго +киви +арбуз +папайя +арбуз +цуккини +айва +манго +черешня +дыня +черешня +ежевика +инжир +юзу +нектарин +манго +банан +нектарин +нектарин +айва +апельсин +инжир +папайя +ежевика +инжир +финик +апельсин +киви +клубника +манго +яблоко +инжир +цуккини +виноград +папайя +черешня +нектарин +виноград +арбуз +клубника +лимон +арбуз +яблоко +инжир +арбуз +ежевика +киви +мандарин +черешня +мандарин +яблоко +манго +ежевика +папайя +мандарин \ No newline at end of file diff --git a/TagsCloudContainer/Files/defaultTxt1.txt b/TagsCloudContainer/Files/defaultTxt1.txt new file mode 100644 index 00000000..929e3de9 --- /dev/null +++ b/TagsCloudContainer/Files/defaultTxt1.txt @@ -0,0 +1,29 @@ +python +python +python +python +python +python +python +python +python +python +code +code +code +code +code +code +code +code +developer +developer +developer +developer +developer +algorithm +algorithm +algorithm +data +data +analysis \ No newline at end of file diff --git a/TagsCloudContainer/Files/filterwords.txt b/TagsCloudContainer/Files/filterwords.txt new file mode 100644 index 00000000..f1ff69b1 --- /dev/null +++ b/TagsCloudContainer/Files/filterwords.txt @@ -0,0 +1,108 @@ +а +без +близ +будто +бы +в +во +вне +вот +всё +все +всего +всегда +да +даже +для +до +его +её +ей +если +есть +же +за +и +из +или +им +ими +их +к +как +кем +ко +когда +который +кто +ли +либо +между +меня +мне +мной +много +моё +может +моя +мы +на +над +надо +нам +нами +нас +наш +не +него +неё +нет +ни +них +но +о +об +однако +он +она +они +оно +от +по +под +при +про +раз +с +сам +сами +свой +со +тебе +тебя +тем +то +тобой +тоже +того +тогда +той +только +том +тот +ты +у +уж +уже +чего +чей +чем +что +чтоб +чтобы +чья +эта +эти +это +этот +я \ No newline at end of file diff --git a/TagsCloudContainer/Files/itWords.txt b/TagsCloudContainer/Files/itWords.txt new file mode 100644 index 00000000..562442c2 --- /dev/null +++ b/TagsCloudContainer/Files/itWords.txt @@ -0,0 +1,26 @@ +разработчик +разработчик +разработчик +разработчик +разработчик +разработчик +разработчик +алгоритм +алгоритм +алгоритм +алгоритм +алгоритм +алгоритм +программирование +программирование +программирование +программирование +данные +данные +данные +данные +данные +код +код +код +анализ diff --git a/TagsCloudContainer/Filters/IFilter.cs b/TagsCloudContainer/Filters/IFilter.cs new file mode 100644 index 00000000..56a6f16b --- /dev/null +++ b/TagsCloudContainer/Filters/IFilter.cs @@ -0,0 +1,10 @@ +namespace TagsCloudContainer.Filters; + +public interface IFilter +{ + void AddStopWord(string word); + void AddStopWords(string[] wordArray); + bool Contains(string word); + void RemoveRightWord(string word); + void RemoveRightWords(string[] wordArray); +} \ No newline at end of file diff --git a/TagsCloudContainer/Filters/WordsFilter.cs b/TagsCloudContainer/Filters/WordsFilter.cs new file mode 100644 index 00000000..6a06e211 --- /dev/null +++ b/TagsCloudContainer/Filters/WordsFilter.cs @@ -0,0 +1,53 @@ +using TagsCloudContainer.FileReaders; + +namespace TagsCloudContainer.Filters; + +public class WordsFilter : IFilter +{ + private HashSet words; + + public WordsFilter(Config config) + { + words = []; + var reader = new TxtFileReader(); + var filterWords = reader.Read(Constants.FilterWordsDirectory); + + var matches = Constants.WordsSplitRegex.Matches(filterWords); + + for (var i = 0; i < matches.Count; i++) + { + words.Add(matches[i].Value); + } + + AddStopWords(config.StopWords); + RemoveRightWords(config.RightWords); + } + + public bool Contains(string word) + { + return words.Contains(word); + } + + public void AddStopWord(string word) + { + if (word.All(c => char.IsLetter(c))) + words.Add(word); + } + + public void RemoveRightWord(string word) + { + words.Remove(word); + } + + public void AddStopWords(string[] wordArray) + { + foreach (var word in wordArray) + AddStopWord(word); + } + + public void RemoveRightWords(string[] wordArray) + { + foreach (var word in wordArray) + RemoveRightWord(word); + } +} \ No newline at end of file diff --git a/TagsCloudContainer/IApplicationRunner.cs b/TagsCloudContainer/IApplicationRunner.cs new file mode 100644 index 00000000..c0cf0dfe --- /dev/null +++ b/TagsCloudContainer/IApplicationRunner.cs @@ -0,0 +1,7 @@ +namespace TagsCloudContainer +{ + public interface IApplicationRunner + { + void Run(); + } +} diff --git a/TagsCloudContainer/Layouters/CircularCloudLayouter.cs b/TagsCloudContainer/Layouters/CircularCloudLayouter.cs new file mode 100644 index 00000000..1ea4b220 --- /dev/null +++ b/TagsCloudContainer/Layouters/CircularCloudLayouter.cs @@ -0,0 +1,62 @@ +using System.Drawing; +using TagsCloudContainer.WordClasses; + +namespace TagsCloudContainer.Layouters; + +public class CircularCloudLayouter : ILayouter +{ + public readonly List Rectangles = []; // public нужен для тестов + public Point Center; + private double angle; + private const double spiralStep = 0.1; + private const double radiusStep = 0.5; + private readonly Config config; + + public CircularCloudLayouter(Config config) + { + this.config = config; + } + + public IEnumerable GetLayout(IEnumerable words) + { + Center = new Point(config.PictureWidth / 2, config.PictureHeight / 2); + foreach (var (value, rectangleSize, font) in words) + { + Rectangle newRect; + do + { + var location = GetNextLocation(rectangleSize); + newRect = new Rectangle(location, rectangleSize); + } + while (IsIntersecting(newRect)); + Rectangles.Add(new RectangleWord(value, newRect, font)); + } + return Rectangles; + } + + private Point GetNextLocation(Size rectangleSize) + { + var radius = radiusStep * angle; + var centerX = Center.X + (int)(radius * Math.Cos(angle)); + var centerY = Center.Y + (int)(radius * Math.Sin(angle)); + angle += spiralStep; + return GetCornerPoint(new Point(centerX, centerY), rectangleSize); + } + + public bool IsIntersecting(Rectangle rectangle) + { + return Rectangles. + Any(existingRectangle => + existingRectangle.Bounds.IntersectsWith(rectangle)); + } + + public static Point GetCornerPoint(Point center, Size size) + { + return new Point(center.X - size.Width / 2, center.Y - size.Height / 2); + } + + public static Point GetCenterPoint(Rectangle rect) + { + return new Point(rect.Location.X + rect.Width / 2, rect.Location.Y + rect.Height / 2); + } +} diff --git a/TagsCloudContainer/Layouters/ILayouter.cs b/TagsCloudContainer/Layouters/ILayouter.cs new file mode 100644 index 00000000..06f0b0af --- /dev/null +++ b/TagsCloudContainer/Layouters/ILayouter.cs @@ -0,0 +1,8 @@ +using TagsCloudContainer.WordClasses; + +namespace TagsCloudContainer.Layouters; + +public interface ILayouter +{ + public IEnumerable GetLayout(IEnumerable words); +} diff --git a/TagsCloudContainer/Parsers/IParser.cs b/TagsCloudContainer/Parsers/IParser.cs new file mode 100644 index 00000000..917c6aee --- /dev/null +++ b/TagsCloudContainer/Parsers/IParser.cs @@ -0,0 +1,10 @@ +namespace TagsCloudContainer.Parsers; + +public interface IParser +{ + public IDictionary Parse(string text); + + public string BringWordsToOriginalForm(string text); + + public string TakeOnlyOnePartOfSpeech(string text, string partOfSpeech); +} diff --git a/TagsCloudContainer/Parsers/SimpleParser.cs b/TagsCloudContainer/Parsers/SimpleParser.cs new file mode 100644 index 00000000..cb7f73c1 --- /dev/null +++ b/TagsCloudContainer/Parsers/SimpleParser.cs @@ -0,0 +1,42 @@ +using TagsCloudContainer.Filters; + +namespace TagsCloudContainer.Parsers; + +public class SimpleParser : IParser +{ + private IFilter wordsFilter; + public SimpleParser(IFilter wordsFilter) + { + this.wordsFilter = wordsFilter; + } + + public IDictionary Parse(string text) + { + var dict = new Dictionary(); + + var words = Constants.WordsSplitRegex.Matches(text.ToLower()); + + for (var i = 0; i < words.Count; i++) + { + var word = words[i].Value; + if (!wordsFilter.Contains(word)) + { + if (!dict.TryAdd(word, 1)) + { + dict[word]++; + } + } + } + return dict; + } + + public string TakeOnlyOnePartOfSpeech(string text, string partOfSpeech) + { + throw new NotImplementedException(); + } + + public string BringWordsToOriginalForm(string text) + { + throw new NotImplementedException(); + } +} diff --git a/TagsCloudContainer/Pictures/general.jpg b/TagsCloudContainer/Pictures/general.jpg new file mode 100644 index 00000000..120d72cd Binary files /dev/null and b/TagsCloudContainer/Pictures/general.jpg differ diff --git a/TagsCloudContainer/Pictures/picture.jpg b/TagsCloudContainer/Pictures/picture.jpg new file mode 100644 index 00000000..b6fb7743 Binary files /dev/null and b/TagsCloudContainer/Pictures/picture.jpg differ diff --git a/TagsCloudContainer/Pictures/picture1.jpg b/TagsCloudContainer/Pictures/picture1.jpg new file mode 100644 index 00000000..b275096c Binary files /dev/null and b/TagsCloudContainer/Pictures/picture1.jpg differ diff --git a/TagsCloudContainer/Pictures/picture2.jpg b/TagsCloudContainer/Pictures/picture2.jpg new file mode 100644 index 00000000..08e7d7ae Binary files /dev/null and b/TagsCloudContainer/Pictures/picture2.jpg differ diff --git a/TagsCloudContainer/Program.cs b/TagsCloudContainer/Program.cs new file mode 100644 index 00000000..81d67a20 --- /dev/null +++ b/TagsCloudContainer/Program.cs @@ -0,0 +1,7 @@ +namespace TagsCloudContainer; +public static class Program +{ + public static void Main() + { + } +} diff --git a/TagsCloudContainer/Properties/launchSettings.json b/TagsCloudContainer/Properties/launchSettings.json new file mode 100644 index 00000000..5139a453 --- /dev/null +++ b/TagsCloudContainer/Properties/launchSettings.json @@ -0,0 +1,11 @@ +{ + "profiles": { + "TagsCloudContainer": { + "commandName": "Project" + }, + "WSL": { + "commandName": "WSL2", + "distributionName": "" + } + } +} \ No newline at end of file diff --git a/TagsCloudContainer/TagsCloudContainer.csproj b/TagsCloudContainer/TagsCloudContainer.csproj new file mode 100644 index 00000000..c50e50be --- /dev/null +++ b/TagsCloudContainer/TagsCloudContainer.csproj @@ -0,0 +1,21 @@ + + + + Library + net8.0-windows + enable + enable + + + + + + + + + + + + + + diff --git a/TagsCloudContainer/TagsCloudContainer.sln b/TagsCloudContainer/TagsCloudContainer.sln new file mode 100644 index 00000000..eb748b23 --- /dev/null +++ b/TagsCloudContainer/TagsCloudContainer.sln @@ -0,0 +1,37 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.11.35303.130 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TagsCloudContainer", "TagsCloudContainer.csproj", "{468DF4A6-3C4A-4517-A5A5-30B96E2D8EC8}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TagCloudContainerTests", "..\TagCloudContainerTests\TagCloudContainerTests.csproj", "{AAFEE184-BFF5-47B3-9B3B-97654DED9DBF}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsoleClient", "..\ConsoleClient\ConsoleClient.csproj", "{8F98E6EF-0BD6-4B65-9FD0-F22234E878BD}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {468DF4A6-3C4A-4517-A5A5-30B96E2D8EC8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {468DF4A6-3C4A-4517-A5A5-30B96E2D8EC8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {468DF4A6-3C4A-4517-A5A5-30B96E2D8EC8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {468DF4A6-3C4A-4517-A5A5-30B96E2D8EC8}.Release|Any CPU.Build.0 = Release|Any CPU + {AAFEE184-BFF5-47B3-9B3B-97654DED9DBF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AAFEE184-BFF5-47B3-9B3B-97654DED9DBF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AAFEE184-BFF5-47B3-9B3B-97654DED9DBF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AAFEE184-BFF5-47B3-9B3B-97654DED9DBF}.Release|Any CPU.Build.0 = Release|Any CPU + {8F98E6EF-0BD6-4B65-9FD0-F22234E878BD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8F98E6EF-0BD6-4B65-9FD0-F22234E878BD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8F98E6EF-0BD6-4B65-9FD0-F22234E878BD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8F98E6EF-0BD6-4B65-9FD0-F22234E878BD}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {1090FEC2-F19E-4634-8E98-CBE768CB11D4} + EndGlobalSection +EndGlobal diff --git a/TagsCloudContainer/Visualizers/IVisualizer.cs b/TagsCloudContainer/Visualizers/IVisualizer.cs new file mode 100644 index 00000000..e674b9e2 --- /dev/null +++ b/TagsCloudContainer/Visualizers/IVisualizer.cs @@ -0,0 +1,13 @@ +using System.Drawing; +using TagsCloudContainer.WordClasses; + +namespace TagsCloudContainer.Visualizers; + +public interface IVisualizer +{ + public void GenerateImage(IEnumerable words); + + public Color GetWordColor(RectangleWord word); + + public string GetImageFotmat(); +} diff --git a/TagsCloudContainer/Visualizers/ImageVisualizer.cs b/TagsCloudContainer/Visualizers/ImageVisualizer.cs new file mode 100644 index 00000000..61fe5f69 --- /dev/null +++ b/TagsCloudContainer/Visualizers/ImageVisualizer.cs @@ -0,0 +1,37 @@ +using System.Drawing; +using System.Drawing.Imaging; +using TagsCloudContainer.WordClasses; + +namespace TagsCloudContainer.Visualizers; + +public class ImageVisualizer : IVisualizer +{ + private Config config; + public ImageVisualizer(Config config) + { + this.config = config; + } + + public void GenerateImage(IEnumerable words) + { + using var image = new Bitmap(config.PictureWidth, config.PictureHeight); + using var g = Graphics.FromImage(image); + + foreach (var item in words) + { + g.DrawString(item.Value, item.font, Brushes.Orange, item.Bounds.Location); + } + + image.Save(config.OutputDirectory, ImageFormat.Jpeg); + } + + public string GetImageFotmat() + { + throw new NotImplementedException(); + } + + public Color GetWordColor(RectangleWord word) + { + throw new NotImplementedException(); + } +} diff --git a/TagsCloudContainer/WordClasses/RectangleWord.cs b/TagsCloudContainer/WordClasses/RectangleWord.cs new file mode 100644 index 00000000..7cc442f5 --- /dev/null +++ b/TagsCloudContainer/WordClasses/RectangleWord.cs @@ -0,0 +1,5 @@ +using System.Drawing; + +namespace TagsCloudContainer.WordClasses; + +public record RectangleWord(string Value, Rectangle Bounds, Font font); diff --git a/TagsCloudContainer/WordClasses/SizeWord.cs b/TagsCloudContainer/WordClasses/SizeWord.cs new file mode 100644 index 00000000..9f0a7004 --- /dev/null +++ b/TagsCloudContainer/WordClasses/SizeWord.cs @@ -0,0 +1,5 @@ +using System.Drawing; + +namespace TagsCloudContainer.WordClasses; + +public record SizeWord(string Value, Size Size, Font font); diff --git a/TagsCloudContainer/WordSizer/ISizer.cs b/TagsCloudContainer/WordSizer/ISizer.cs new file mode 100644 index 00000000..9ec4d806 --- /dev/null +++ b/TagsCloudContainer/WordSizer/ISizer.cs @@ -0,0 +1,8 @@ +using TagsCloudContainer.WordClasses; + +namespace TagsCloudContainer.WordSizer; + +public interface ISizer +{ + public IEnumerable GetSizes(IDictionary words); +} diff --git a/TagsCloudContainer/WordSizer/SimpleSizer.cs b/TagsCloudContainer/WordSizer/SimpleSizer.cs new file mode 100644 index 00000000..69e6bb01 --- /dev/null +++ b/TagsCloudContainer/WordSizer/SimpleSizer.cs @@ -0,0 +1,59 @@ +using System.Drawing; +using TagsCloudContainer.WordClasses; + +namespace TagsCloudContainer.WordSizer; + +public class SimpleSizer : ISizer +{ + private readonly Config config; + + public SimpleSizer(Config config) + { + this.config = config; + } + + public IEnumerable GetSizes(IDictionary words) + { + var screeenSize = config.PictureHeight * config.PictureWidth; + var sum = words.Sum(x => x.Value); + var list = new List(); + + foreach (var item in words.OrderByDescending(x => x.Value)) + { + var part = (double)item.Value / sum; + var maxHeight = (int)(config.PictureHeight * part); + var maxWidth = (int)(config.PictureWidth * part); + var maxSize = new Size(maxWidth, maxHeight); + var (size, font) = GetFontSize(maxSize, config.Font, item.Key); + list.Add(new SizeWord(item.Key, size, font)); + } + return list; + } + + private static (Size, Font) GetFontSize(Size maxSize, string fontName, string text) + { + var maxFontSize = 200; + var minFontSize = 1; + + using var tempBitmap = new Bitmap(1, 1); + using var graphics = Graphics.FromImage(tempBitmap); + + Size textSize; + Font font; + + for (var i = maxFontSize; i >= minFontSize; i--) + { + font = new Font(fontName, i); + var t = graphics.MeasureString(text, font); + textSize = t.ToSize(); + if (textSize.Width * textSize.Height <= maxSize.Width * maxSize.Height) + { + return (textSize, font); + } + } + + font = new Font(fontName, 1); + textSize = graphics.MeasureString(text, font).ToSize(); + return (textSize, font); + } +}