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

Алешев Руслан #188

Open
wants to merge 23 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
17a5d9f
init: начальная структура программы
Jan 22, 2024
22e56ab
feat: impliment basic text cloud layouting
Jan 23, 2024
271ef7f
feat: Добавлены тесты
Jan 24, 2024
77fb65f
feat: добавлена фильтрация слов
Jan 24, 2024
3d91d03
feat: добавлен интерфейс командной строки
Jan 25, 2024
af7d57d
fix: исправлен CloudBuilderTests
Jan 25, 2024
03e1d52
refactor: Изменен обработчик аргументов командной строки
Jan 27, 2024
9b16fc0
init: добавлен пустой проект для GUI
Jan 28, 2024
7a8a6b9
refactor: исправлен FrequencyAnalyzer
Jan 28, 2024
6601ff9
refactor: TextPreprocessing fixing
Jan 28, 2024
0f53408
fix: добавлены DI для классов
Jan 28, 2024
b1d2d86
feat: создание изображения вынесено из TagsCloudLayouter в класс Visu…
Jan 29, 2024
8655a10
feat: добавлен маппер цветов
Jan 29, 2024
74c589a
refactor: удалил пустые конструкторы
Jan 29, 2024
aab825f
feat: Добавлен NormalPointsProvider реализующий интерфейс IPointsProv…
Jan 30, 2024
7e628a2
fix: исправлена ошибка в TagsCloudLayouter
Jan 30, 2024
28f5212
init: добавлен проект приложения WinForms
Jan 30, 2024
c8cda5f
feat: настроен GUI
Jan 30, 2024
fccf8bf
feat: автоматическое сохранение и загрузка настроек в GUI
Jan 30, 2024
dda89b9
test: добавлены тесты
Jan 30, 2024
6373fa9
feat: добавлена настройка цвета фона в консольном приложении
Jan 31, 2024
7b562a2
feat: добавлена настройка цвета фона в GUI
Feb 1, 2024
6d2c3c2
init: создана заготовка класса Result и тестов
Feb 4, 2024
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
139 changes: 139 additions & 0 deletions TagsCloudContainer/CLI/CommandLineArgs.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
using System.Drawing;
using TagsCloudContainer.SettingsClasses;

namespace TagsCloudContainer.CLI
{
public static class CommandLineArgs

This comment was marked as resolved.

{
public static void PrintUsage()
{
Console.WriteLine(@"Usage: TagCloudContainer.exe <fileName> [-font <fontName>] [-fontsize <fontSize>] [-color <colorName>] [-size <Width> <Height>]

Options:
-font <FontName>: Set the font family name. Default is Arial.
-fontsize <FontSize>: Set the font size. Must be a positive integer.
-color <ColorName>: Add a color to the list of allowed colors.
-size <Width> <Height> : Set the image width. Must be two positive integer separated by whitespace.
-layout <Layout> : Set cloud layouter - Spiral or Random. Default is Spiral.
-out <outputImage> : Set path for output image.
");
}

public static (CloudDrawingSettings, AppSettings) CreateSettingsObject(IReadOnlyCollection<string> args)
{
if (args.Count < 1) throw new ArgumentException("At least one argument must be provided.", "args");

var settings = new CloudDrawingSettings();

var appSettings = new AppSettings();

return (settings, appSettings);
}

public static void ParseCommandLineArguments(CloudDrawingSettings settings, AppSettings appSettings, string[] args)
{
HandleTextFileOption(args, ref appSettings);

for (int i = 1; i < args.Length; i += 2)
{
HandleFontOption(args, ref settings, i);
HandleFontSizeOption(args, ref settings, i);
HandleColorOption(args, ref settings, i);
HandleSizeOption(args, ref settings, i);
HandleOutputFileOption(args, ref appSettings, i);
}
}

private static void HandleOutputFileOption(string[] args, ref AppSettings appSettings, int index)
{
const string OptionName = "-out";

if (args[index] != OptionName) return;

if (index + 1 >= args.Length || string.IsNullOrEmpty(args[index + 1]))
{
Console.WriteLine($"The '-out' option requires a valid file name.");
return;
}
appSettings.outImagePath = args[++index];


}

private static void HandleTextFileOption(string[] args, ref AppSettings appSettings)
{
appSettings.textFile = args[0];
}

private static void HandleFontOption(string[] args, ref CloudDrawingSettings settings, int index)
{
const string OptionName = "-font";

if (args[index] != OptionName) return;

if (index + 1 >= args.Length || string.IsNullOrEmpty(args[index + 1]))
{
Console.WriteLine($"The '-font' option requires a valid font family name.");
return;
}

settings.FontFamily = new FontFamily(args[++index]);
}

private static void HandleFontSizeOption(string[] args, ref CloudDrawingSettings settings, int index)
{
const string OptionName = "-fontsize";

if (args[index] != OptionName) return;

if (index + 1 >= args.Length || !int.TryParse(args[index + 1], out _))
{
Console.WriteLine($"The '-fontsize' option requires a valid integer value.");
return;
}

if (int.Parse(args[++index]) <= 0)
{
Console.WriteLine($"The '-fontsize' option requires a positive integer value.");
return;
}

settings.FontSize = int.Parse(args[index]);
}

private static void HandleColorOption(string[] args, ref CloudDrawingSettings settings, int index)
{
const string OptionName = "-color";

if (args[index] != OptionName) return;

if (index + 1 >= args.Length) // || !Color.TryParse(args[index + 1], out _))
{
Console.WriteLine("The '-color' option requires a valid color value.");
return;
}

settings.Colors.Add(Color.FromName(args[++index]));
}

private static void HandleSizeOption(string[] args, ref CloudDrawingSettings settings, int index)
{
const string OptionName = "-size";

if (args[index] != OptionName) return;
if (index + 2 >= args.Length || !int.TryParse(args[index + 1], out _) || !int.TryParse(args[index + 2], out _))
{
Console.WriteLine($"The '-size' option requires two valid integer value.");
return;
}

if (int.Parse(args[index + 1]) <= 0 || int.Parse(args[index + 2]) <= 0)
{
Console.WriteLine($"The '-size' option requires two positive integer value.");
return;
}

settings.Size = new Size(int.Parse(args[++index]), int.Parse(args[++index]));
}
}
}
17 changes: 17 additions & 0 deletions TagsCloudContainer/DependencyInjectionConfig.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using Microsoft.Extensions.DependencyInjection;
using TagsCloudContainer.FrequencyAnalyzers;
using TagsCloudContainer.TextTools;

namespace TagsCloudContainer
{
public class DependencyInjectionConfig
{
public static IServiceCollection AddCustomServices(IServiceCollection services)
{
services.AddScoped<TextFileReader>();
services.AddScoped<FrequencyAnalyzer>();

This comment was marked as resolved.

Copy link
Author

Choose a reason for hiding this comment

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

ITextReader и IAnalyzer сделал Singleton, т.к. чтение из файлы + тяжелые операции.
Остальное Transient, поскольку нет состояний, которые надо сохранять между вызовами.


return services;
}
}
}
48 changes: 48 additions & 0 deletions TagsCloudContainer/FrequencyAnalyzers/FrequencyAnalyzer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
namespace TagsCloudContainer.FrequencyAnalyzers
{
public class FrequencyAnalyzer : IAnalyzer
{
private readonly TextPreprocessing preprocessor;

private readonly Dictionary<string, int> wordFrequency;
public FrequencyAnalyzer()
{
wordFrequency = new Dictionary<string, int>();
preprocessor = new TextPreprocessing("excludedWords.txt");

This comment was marked as resolved.

}

public void Analyze(string text)
{
foreach (var word in preprocessor.Preprocess(text))
{
if (wordFrequency.ContainsKey(word.ToLower()))
{
wordFrequency[word]++;
}
else
{
wordFrequency.Add(word, 1);
}
}
}

public IEnumerable<(string, int)> GetAnalyzedText()
{
foreach (KeyValuePair<string, int> pair in wordFrequency)
{
yield return (pair.Key, pair.Value);
}
}

This comment was marked as resolved.


public void SaveToFile(string filePath)

This comment was marked as resolved.

{
using (StreamWriter writer = new StreamWriter(filePath))
{
foreach (KeyValuePair<string, int> pair in wordFrequency)
{
writer.WriteLine($"{pair.Key}: {pair.Value}");
}
}
}
}
}
8 changes: 8 additions & 0 deletions TagsCloudContainer/FrequencyAnalyzers/IAnalyzer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace TagsCloudContainer.FrequencyAnalyzers
{
public interface IAnalyzer
{
public abstract void Analyze(string text);
public abstract IEnumerable<(string, int)> GetAnalyzedText();

This comment was marked as resolved.

}
}
27 changes: 27 additions & 0 deletions TagsCloudContainer/FrequencyAnalyzers/TextPreprocessing.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
namespace TagsCloudContainer.FrequencyAnalyzers
{
public class TextPreprocessing
{
private readonly HashSet<string> excludedWords = new HashSet<string>();
//private const string excludedWordsPath = "excludedWords.txt";
public TextPreprocessing(string excludedWordsPath)
{
if (File.Exists(excludedWordsPath))

This comment was marked as resolved.

{
StreamReader reader = new StreamReader(excludedWordsPath);

This comment was marked as resolved.

excludedWords = reader.ReadToEnd()
.Split(new[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries).ToHashSet();
}
}
public IEnumerable<string> Preprocess(string text)

This comment was marked as resolved.

This comment was marked as resolved.

{
var words = text.Split(new[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries)
.Select(x => x.ToLower());
foreach (var word in words)
{
if (!excludedWords.Contains(word))
yield return word;
}
}
}
}
45 changes: 45 additions & 0 deletions TagsCloudContainer/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using Microsoft.Extensions.DependencyInjection;
using System.Drawing;
using TagsCloudContainer.CLI;
using TagsCloudContainer.FrequencyAnalyzers;
using TagsCloudContainer.TextTools;
using TagsCloudVisualization;

namespace TagsCloudContainer
{
public static class Program
{
public static void Main(string[] args)
{
var services = DependencyInjectionConfig.AddCustomServices(new ServiceCollection());
var serviceProvider = services.BuildServiceProvider();

var reader = serviceProvider.GetService<TextFileReader>();
var analyzer = serviceProvider.GetService<FrequencyAnalyzer>();

if (args.Length < 1)
{
CommandLineArgs.PrintUsage();
return;
}

var settings = CommandLineArgs.CreateSettingsObject(args);

CommandLineArgs.ParseCommandLineArguments(settings.Item1, settings.Item2, args);


string text = reader.ReadText(settings.Item2.textFile);

analyzer.Analyze(text);

var center = new Point(settings.Item1.Size.Width / 2, settings.Item1.Size.Height / 2);

var pointsProvider = new SpiralPointsProvider(center);

var layouter = new TagsCloudLayouter(center, pointsProvider, settings.Item1, analyzer.GetAnalyzedText());

This comment was marked as resolved.


layouter.ToImage().Save(settings.Item2.outImagePath);
Console.WriteLine("Resulting image saved to " + settings.Item2.outImagePath);
}
}
}
8 changes: 8 additions & 0 deletions TagsCloudContainer/Properties/launchSettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"profiles": {
"TagsCloudContainer": {
"commandName": "Project",
"commandLineArgs": "sample.txt -font Calibri -size 500 500 -out testout.png"
}
}
}
9 changes: 9 additions & 0 deletions TagsCloudContainer/SettingsClasses/AppSettings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace TagsCloudContainer.SettingsClasses
{
public class AppSettings
{
public string textFile;

public string outImagePath = "cloud.png";
}
}
19 changes: 19 additions & 0 deletions TagsCloudContainer/SettingsClasses/CloudDrawingSettings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using System.Drawing;
using TagsCloudVisualization;

namespace TagsCloudContainer.SettingsClasses
{
public class CloudDrawingSettings
{
public FontFamily FontFamily = new FontFamily("Arial");
public float FontSize = 12;
public IList<Color> Colors = new List<Color>() { Color.AliceBlue };
public Size Size = new Size(600, 600);
public IPointsProvider PointsProvider;

public CloudDrawingSettings()
{

}

This comment was marked as resolved.

}
}
10 changes: 10 additions & 0 deletions TagsCloudContainer/TagCloudBuilder/IPointsProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using System.Drawing;

namespace TagsCloudVisualization
{
public interface IPointsProvider
{
public IEnumerable<Point> Points();
public void Reset();

This comment was marked as resolved.

}
}
32 changes: 32 additions & 0 deletions TagsCloudContainer/TagCloudBuilder/RandomPointsProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using System.Drawing;
using TagsCloudVisualization;

namespace TagsCloudContainer.TagCloudBuilder
{
public class RandomPointsProvider : IPointsProvider
{
private Random rnd = new Random();
private readonly Point Center;
private int pointNumber = 0;

public RandomPointsProvider(Point center)
{
Center = center;
}

public IEnumerable<Point> Points()
{
while (pointNumber < 10000000) // Limit number of returned points for safety reason

This comment was marked as resolved.

{
yield return new Point(rnd.Next(0, Center.X * 2), rnd.Next(0, Center.Y * 2));
}
throw new ArgumentException("Reach end of placing points");
}

public void Reset()
{
rnd = new Random();
pointNumber = 0;
}
}
}
Loading