Initial commit
This commit is contained in:
commit
1be1863b20
50 changed files with 6449 additions and 0 deletions
18
MeetTheTeacher/Export/CsvDataExporter.cs
Normal file
18
MeetTheTeacher/Export/CsvDataExporter.cs
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
using MeetTheTeacher.Model;
|
||||
|
||||
namespace MeetTheTeacher.Export;
|
||||
|
||||
/// <summary>
|
||||
/// Allows to export data of <see cref="ICsvRepresentable"/> type to a CSV file
|
||||
/// </summary>
|
||||
/// <inheritdoc cref="FileExporterBase" />
|
||||
/// <inheritdoc cref="IDataExporter" />
|
||||
public sealed class CsvDataExporter : FileExporterBase, IDataExporter
|
||||
{
|
||||
public void Export(IEnumerable<ICsvRepresentable> items, string? fileName)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
protected override string FileExtension => ".csv";
|
||||
}
|
||||
12
MeetTheTeacher/Export/DefaultExporterProvider.cs
Normal file
12
MeetTheTeacher/Export/DefaultExporterProvider.cs
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
using MeetTheTeacher.Model;
|
||||
|
||||
namespace MeetTheTeacher.Export;
|
||||
|
||||
/// <summary>
|
||||
/// Default implementation of <see cref="IExporterProvider"/> which returns the following exporters:
|
||||
/// <see cref="CsvDataExporter"/> and <see cref="HtmlDataExporter"/>
|
||||
/// </summary>
|
||||
public sealed class DefaultExporterProvider : IExporterProvider
|
||||
{
|
||||
public IDataExporter GetExporter(ExportFormat exportFormat) => null!; // TODO
|
||||
}
|
||||
37
MeetTheTeacher/Export/Exporter.cs
Normal file
37
MeetTheTeacher/Export/Exporter.cs
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
using MeetTheTeacher.Model;
|
||||
using MeetTheTeacher.Model.Comparison;
|
||||
|
||||
namespace MeetTheTeacher.Export;
|
||||
|
||||
/// <summary>
|
||||
/// A utility class which allows to export data in a given format and order
|
||||
/// </summary>
|
||||
public sealed class Exporter
|
||||
{
|
||||
// TODO
|
||||
//private readonly IComparer<Teacher> _comparer;
|
||||
private readonly IDataExporter _exporter;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="Exporter" /> initialized with the provided export options
|
||||
/// </summary>
|
||||
/// <param name="sortOrder">Order in which data should be sorted before export</param>
|
||||
/// <param name="exportFormat">Desired export format</param>
|
||||
/// <param name="exporterProvider">Export provider which supports exporting in the desired format</param>
|
||||
/// <exception cref="ArgumentOutOfRangeException">Thrown if you manage to pass an unexpected <see cref="SortOrder" /> value</exception>
|
||||
public Exporter(SortOrder sortOrder, ExportFormat exportFormat, IExporterProvider exporterProvider)
|
||||
{
|
||||
_exporter = exporterProvider.GetExporter(exportFormat);
|
||||
//_comparer = ... // TODO
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// First sorts the provided data based on the previous settings, then performs the actual export to a file
|
||||
/// </summary>
|
||||
/// <param name="fileName">Name of the file to export to; without extension</param>
|
||||
/// <param name="teachers">Data to export - has to be list due to multiple enumeration</param>
|
||||
public void Export(string fileName, List<Teacher> teachers)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
36
MeetTheTeacher/Export/FileExporterBase.cs
Normal file
36
MeetTheTeacher/Export/FileExporterBase.cs
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
namespace MeetTheTeacher.Export;
|
||||
|
||||
/// <summary>
|
||||
/// Base class for exporters which write data to a file
|
||||
/// </summary>
|
||||
public abstract class FileExporterBase
|
||||
{
|
||||
/// <summary>
|
||||
/// File extension of the exported file including the dot
|
||||
/// </summary>
|
||||
protected abstract string FileExtension { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Exports the given content to a file with the given name.
|
||||
/// If the file already exists, it will be overwritten.
|
||||
/// </summary>
|
||||
/// <param name="fileName">Name of the file to export without the extension</param>
|
||||
/// <param name="content">Content to write into the file</param>
|
||||
protected void Export(string fileName, string content)
|
||||
{
|
||||
// TODO (don't forget to delete)
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Joins several lines into one text, separated by line breaks
|
||||
/// </summary>
|
||||
/// <param name="lines">Lines to merge</param>
|
||||
/// <returns>A text containing all lines</returns>
|
||||
protected static string LinesToText(IEnumerable<string> lines) => null!; // TODO (remember to use proper line break)
|
||||
|
||||
private string GetFilePath(string fileName)
|
||||
{
|
||||
// TODO (remember to use the current directory)
|
||||
return null!;
|
||||
}
|
||||
}
|
||||
27
MeetTheTeacher/Export/HtmlDataExporter.cs
Normal file
27
MeetTheTeacher/Export/HtmlDataExporter.cs
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
using MeetTheTeacher.Model;
|
||||
|
||||
namespace MeetTheTeacher.Export;
|
||||
|
||||
/// <summary>
|
||||
/// Allows to export data of <see cref="ICsvRepresentable"/> type to a file in HTML table syntax.
|
||||
/// Be careful: does not create valid HTML page, just a fragment!
|
||||
/// </summary>
|
||||
/// <inheritdoc cref="FileExporterBase" />
|
||||
/// <inheritdoc cref="IDataExporter" />
|
||||
public sealed class HtmlDataExporter : FileExporterBase, IDataExporter
|
||||
{
|
||||
public void Export(IEnumerable<ICsvRepresentable> items, string? fileName)
|
||||
{
|
||||
// TODO (use a raw string literal with interpolation!)
|
||||
}
|
||||
|
||||
private static string CreateTableRow(IEnumerable<string> values, bool isHeader = false)
|
||||
{
|
||||
// TODO
|
||||
return null!;
|
||||
}
|
||||
|
||||
private static string WrapInTag(string content, string tag) => $"<{tag}>{content}</{tag}>";
|
||||
|
||||
protected override string FileExtension => ".html";
|
||||
}
|
||||
17
MeetTheTeacher/Export/IDataExporter.cs
Normal file
17
MeetTheTeacher/Export/IDataExporter.cs
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
using MeetTheTeacher.Model;
|
||||
|
||||
namespace MeetTheTeacher.Export;
|
||||
|
||||
/// <summary>
|
||||
/// Allows to export data of <see cref="ICsvRepresentable"/> type
|
||||
/// </summary>
|
||||
public interface IDataExporter
|
||||
{
|
||||
/// <summary>
|
||||
/// Exports the given data.
|
||||
/// File based exporters will use the provided file name as the name of the file they export.
|
||||
/// </summary>
|
||||
/// <param name="data">Data to export</param>
|
||||
/// <param name="fileName">File name to be used; without the extension</param>
|
||||
void Export(IEnumerable<ICsvRepresentable> data, string? fileName);
|
||||
}
|
||||
16
MeetTheTeacher/Export/IExporterProvider.cs
Normal file
16
MeetTheTeacher/Export/IExporterProvider.cs
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
using MeetTheTeacher.Model;
|
||||
|
||||
namespace MeetTheTeacher.Export;
|
||||
|
||||
/// <summary>
|
||||
/// Provides an <see cref="IDataExporter"/> for the given <see cref="ExportFormat"/>
|
||||
/// </summary>
|
||||
public interface IExporterProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns an <see cref="IDataExporter"/> for the given <see cref="ExportFormat"/>
|
||||
/// </summary>
|
||||
/// <param name="exportFormat">Requested export format</param>
|
||||
/// <returns><see cref="IDataExporter"/> which supports exporting in the desired format</returns>
|
||||
IDataExporter GetExporter(ExportFormat exportFormat);
|
||||
}
|
||||
51
MeetTheTeacher/Export/SpectreTableExporter.cs
Normal file
51
MeetTheTeacher/Export/SpectreTableExporter.cs
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
using MeetTheTeacher.Model;
|
||||
using Spectre.Console;
|
||||
|
||||
namespace MeetTheTeacher.Export;
|
||||
|
||||
/// <summary>
|
||||
/// Allows to export data of <see cref="ICsvRepresentable" /> type to a <see cref="Table" /> in-memory data structure
|
||||
/// </summary>
|
||||
/// <inheritdoc cref="IDataExporter" />
|
||||
public sealed class SpectreTableExporter : IDataExporter
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new instance of the exporter with an empty <see cref="Table" />
|
||||
/// </summary>
|
||||
public SpectreTableExporter()
|
||||
{
|
||||
Table = new Table();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the table containing the exported data
|
||||
/// </summary>
|
||||
public Table Table { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Exports the given data to a <see cref="Table" />.
|
||||
/// Does not write a file.
|
||||
/// </summary>
|
||||
/// <param name="data">Data to export</param>
|
||||
/// <param name="fileName">Unused</param>
|
||||
public void Export(IEnumerable<ICsvRepresentable> data, string? fileName)
|
||||
{
|
||||
var dataRows = new List<CsvData>();
|
||||
foreach (var csvRepresentable in data)
|
||||
{
|
||||
dataRows.Add(csvRepresentable.ToCsvData());
|
||||
}
|
||||
|
||||
var table = new Table();
|
||||
|
||||
// TODO
|
||||
|
||||
Table = table;
|
||||
}
|
||||
|
||||
private static IEnumerable<string> FindMaxHeaderColumns(IEnumerable<CsvData> rows)
|
||||
{
|
||||
// TODO
|
||||
return null!;
|
||||
}
|
||||
}
|
||||
16
MeetTheTeacher/Import/ITeacherDataImporter.cs
Normal file
16
MeetTheTeacher/Import/ITeacherDataImporter.cs
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
using MeetTheTeacher.Model;
|
||||
|
||||
namespace MeetTheTeacher.Import;
|
||||
|
||||
/// <summary>
|
||||
/// Allows to import data of <see cref="Teacher" /> type
|
||||
/// </summary>
|
||||
public interface ITeacherDataImporter
|
||||
{
|
||||
/// <summary>
|
||||
/// Loads the data from various sources.
|
||||
/// Mind that instances of <see cref="Teacher"/> and <see cref="TeacherWithBusinessCard"/> can be returned.
|
||||
/// </summary>
|
||||
/// <returns>A collection of <see cref="Teacher"/> data</returns>
|
||||
IEnumerable<Teacher> LoadTeacherData();
|
||||
}
|
||||
54
MeetTheTeacher/Import/TeacherDataCsvImporter.cs
Normal file
54
MeetTheTeacher/Import/TeacherDataCsvImporter.cs
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
using MeetTheTeacher.Model;
|
||||
|
||||
namespace MeetTheTeacher.Import;
|
||||
|
||||
/// <summary>
|
||||
/// Imports <see cref="Teacher" /> data from CSV files
|
||||
/// </summary>
|
||||
/// <inheritdoc cref="ITeacherDataImporter" />
|
||||
public sealed class TeacherDataCsvImporter : ITeacherDataImporter
|
||||
{
|
||||
private readonly string _businessCardDataFilePath;
|
||||
private readonly string _generalDataFilePath;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="TeacherDataCsvImporter" /> configured to read from the given files
|
||||
/// </summary>
|
||||
/// <param name="generalDataFilePath">Path to file containing general <see cref="Teacher" /> data for all teachers</param>
|
||||
/// <param name="businessCardDataFilePath">
|
||||
/// Path to file containing additional data for <see cref="TeacherWithBusinessCard" /> teachers
|
||||
/// </param>
|
||||
public TeacherDataCsvImporter(string generalDataFilePath, string businessCardDataFilePath)
|
||||
{
|
||||
_generalDataFilePath = generalDataFilePath;
|
||||
_businessCardDataFilePath = businessCardDataFilePath;
|
||||
}
|
||||
|
||||
public IEnumerable<Teacher> LoadTeacherData()
|
||||
{
|
||||
// TODO
|
||||
return null!;
|
||||
}
|
||||
|
||||
private static Dictionary<string, Teacher> CombineData(Dictionary<string, Teacher> generalData,
|
||||
IEnumerable<(string name, int id)> businessCardData)
|
||||
{
|
||||
// Note: this method is designed specifically to force you to deal with the dictionary and
|
||||
// different teacher classes as value. It could be done in different ways as well, of course.
|
||||
|
||||
// TODO
|
||||
return null!;
|
||||
}
|
||||
|
||||
private IEnumerable<(string name, int id)>? ReadTeacherBusinessCardData()
|
||||
{
|
||||
// TODO - CSV import (mind the return type: ValueTuple!)
|
||||
return null!;
|
||||
}
|
||||
|
||||
private Dictionary<string, Teacher>? ReadTeacherData()
|
||||
{
|
||||
// TODO - CSV import
|
||||
return null!;
|
||||
}
|
||||
}
|
||||
57
MeetTheTeacher/Import/TeacherDataFakeImporter.cs
Normal file
57
MeetTheTeacher/Import/TeacherDataFakeImporter.cs
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
using MeetTheTeacher.Model;
|
||||
|
||||
namespace MeetTheTeacher.Import;
|
||||
|
||||
/// <summary>
|
||||
/// Fake implementation of <see cref="ITeacherDataImporter" /> which returns a fixed set of data.
|
||||
/// </summary>
|
||||
/// <inheritdoc cref="ITeacherDataImporter" />
|
||||
public sealed class TeacherDataFakeImporter : ITeacherDataImporter
|
||||
{
|
||||
public IEnumerable<Teacher> LoadTeacherData() =>
|
||||
new List<Teacher>
|
||||
{
|
||||
new("John Doe")
|
||||
{
|
||||
ConsultingHour = new TimeFrame(new TimeOnly(8, 0), new TimeOnly(8, 50)),
|
||||
ConsultingHourUnit = SchoolUnit.UE01,
|
||||
ConsultingHourWeekDay = DayOfWeek.Monday,
|
||||
Room = "A101"
|
||||
},
|
||||
new("Jane Smith")
|
||||
{
|
||||
ConsultingHour = new TimeFrame(new TimeOnly(14, 35), new TimeOnly(15, 25)),
|
||||
ConsultingHourUnit = SchoolUnit.UE08,
|
||||
ConsultingHourWeekDay = DayOfWeek.Tuesday,
|
||||
Room = "A102"
|
||||
},
|
||||
new("Alice Johnson")
|
||||
{
|
||||
ConsultingHour = new TimeFrame(new TimeOnly(10, 0), new TimeOnly(10, 50)),
|
||||
ConsultingHourUnit = SchoolUnit.UE03,
|
||||
ConsultingHourWeekDay = DayOfWeek.Wednesday,
|
||||
Room = "A102"
|
||||
},
|
||||
new("Bob Brown")
|
||||
{
|
||||
ConsultingHour = new TimeFrame(new TimeOnly(10, 55), new TimeOnly(11, 45)),
|
||||
ConsultingHourUnit = SchoolUnit.UE04,
|
||||
ConsultingHourWeekDay = DayOfWeek.Friday,
|
||||
Room = "A104"
|
||||
},
|
||||
new TeacherWithBusinessCard("Emily Davis", 1001)
|
||||
{
|
||||
ConsultingHour = new TimeFrame(new TimeOnly(12, 40), new TimeOnly(13, 35)),
|
||||
ConsultingHourUnit = SchoolUnit.UE06,
|
||||
ConsultingHourWeekDay = DayOfWeek.Thursday,
|
||||
Room = "A101"
|
||||
},
|
||||
new TeacherWithBusinessCard("Tom Wilson", 1002)
|
||||
{
|
||||
ConsultingHour = new TimeFrame(new TimeOnly(8, 50), new TimeOnly(9, 45)),
|
||||
ConsultingHourUnit = SchoolUnit.UE02,
|
||||
ConsultingHourWeekDay = DayOfWeek.Monday,
|
||||
Room = "A102"
|
||||
}
|
||||
};
|
||||
}
|
||||
28
MeetTheTeacher/MeetTheTeacher.csproj
Normal file
28
MeetTheTeacher/MeetTheTeacher.csproj
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Spectre.Console" Version="0.49.2-preview.0.77" />
|
||||
<PackageReference Include="Spectre.Console.ImageSharp" Version="0.49.2-preview.0.77" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="..\Data\*.csv" Link="Data\%(Filename)%(Extension)">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="happy-dog.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
22
MeetTheTeacher/Model/Comparison/ByHourComparer.cs
Normal file
22
MeetTheTeacher/Model/Comparison/ByHourComparer.cs
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
namespace MeetTheTeacher.Model.Comparison;
|
||||
|
||||
/// <summary>
|
||||
/// Compares <see cref="Teacher" /> instances by their consulting hour, sorting in ascending order.
|
||||
/// First compares by <see cref="DayOfWeek" />, then by <see cref="SchoolUnit" />.
|
||||
/// </summary>
|
||||
/// <inheritdoc cref="ComparerBase" />
|
||||
/// <inheritdoc cref="IComparer{Teacher}" />
|
||||
public sealed class ByHourComparer : ComparerBase, IComparer<Teacher>
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="ByHourComparer" /> configured to sort in ascending or descending order
|
||||
/// </summary>
|
||||
/// <param name="ascending">Flag indicating if items should be sorted in ascending order; descending if false</param>
|
||||
public ByHourComparer(bool ascending) : base(ascending) { }
|
||||
|
||||
public int Compare(Teacher? x, Teacher? y)
|
||||
{
|
||||
// TODO
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
22
MeetTheTeacher/Model/Comparison/ByNameComparer.cs
Normal file
22
MeetTheTeacher/Model/Comparison/ByNameComparer.cs
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
namespace MeetTheTeacher.Model.Comparison;
|
||||
|
||||
/// <summary>
|
||||
/// A comparer which allows sorting <see cref="Teacher" /> instances by their name in either ascending or descending
|
||||
/// order
|
||||
/// </summary>
|
||||
/// <inheritdoc cref="ComparerBase" />
|
||||
/// <inheritdoc cref="IComparer{Teacher}"/>
|
||||
public sealed class ByNameComparer : ComparerBase, IComparer<Teacher>
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="ByNameComparer" /> configured to sort in ascending or descending order
|
||||
/// </summary>
|
||||
/// <param name="ascending">Flag indicating if items should be sorted in ascending order; descending if false</param>
|
||||
public ByNameComparer(bool ascending) : base(ascending) { }
|
||||
|
||||
public int Compare(Teacher? x, Teacher? y)
|
||||
{
|
||||
// TODO
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
14
MeetTheTeacher/Model/Comparison/ByRoomComparer.cs
Normal file
14
MeetTheTeacher/Model/Comparison/ByRoomComparer.cs
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
namespace MeetTheTeacher.Model.Comparison;
|
||||
|
||||
/// <summary>
|
||||
/// Compares <see cref="Teacher"/> instances by their room, sorting in ascending order
|
||||
/// </summary>
|
||||
/// <inheritdoc cref="IComparer{Teacher}"/>
|
||||
public sealed class ByRoomComparer : IComparer<Teacher>
|
||||
{
|
||||
public int Compare(Teacher? x, Teacher? y)
|
||||
{
|
||||
// TODO
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
24
MeetTheTeacher/Model/Comparison/ComparerBase.cs
Normal file
24
MeetTheTeacher/Model/Comparison/ComparerBase.cs
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
namespace MeetTheTeacher.Model.Comparison;
|
||||
|
||||
/// <summary>
|
||||
/// Base class for comparers which allow switching between ascending and descending mode
|
||||
/// </summary>
|
||||
public abstract class ComparerBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="ComparerBase" /> configured to sort in ascending or descending order
|
||||
/// </summary>
|
||||
/// <param name="ascending">Flag indicating if items should be sorted in ascending order; descending if false</param>
|
||||
protected ComparerBase(bool ascending)
|
||||
{
|
||||
Ascending = ascending;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets if the comparer is configured to sort in ascending order; descending if false
|
||||
/// </summary>
|
||||
protected bool Ascending { get; }
|
||||
|
||||
// TODO: consider moving the basic RefEquals code of CompareTo from the subclasses here
|
||||
// maybe also rename this to SortOrderComparerBase or something and introduce another, common base class for all three?
|
||||
}
|
||||
57
MeetTheTeacher/Model/CsvData.cs
Normal file
57
MeetTheTeacher/Model/CsvData.cs
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
namespace MeetTheTeacher.Model;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a single entity as a CSV processable object.
|
||||
/// </summary>
|
||||
public sealed class CsvData
|
||||
{
|
||||
/// <summary>
|
||||
/// Separator used in CSV files
|
||||
/// </summary>
|
||||
public const char Separator = ';';
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="CsvData" />
|
||||
/// </summary>
|
||||
/// <param name="headerNames">Header column names for the entity which has its data represented by this object</param>
|
||||
/// <param name="data">Data column values for the entity which has its data represented by this object; stringly typed</param>
|
||||
public CsvData(IReadOnlyList<string> headerNames, IReadOnlyList<string> data)
|
||||
{
|
||||
HeaderNames = headerNames;
|
||||
Data = data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the header column names for the entity which has its data represented by this object
|
||||
/// </summary>
|
||||
public IReadOnlyList<string> HeaderNames { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the data column values for the entity which has its data represented by this object; stringly typed
|
||||
/// </summary>
|
||||
public IReadOnlyList<string> Data { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a CSV header based on <see cref="HeaderNames" />
|
||||
/// </summary>
|
||||
/// <returns>A CSV header line</returns>
|
||||
public string GetHeader() => null!; // TODO
|
||||
|
||||
/// <summary>
|
||||
/// Gets a CSV data line based on <see cref="Data" />
|
||||
/// </summary>
|
||||
/// <returns>A CSV data line</returns>
|
||||
public string GetData() => null!; // TODO
|
||||
|
||||
/// <summary>
|
||||
/// Allows to deconstruct the object into its header and data parts
|
||||
/// </summary>
|
||||
/// <param name="headerNames">Set to <see cref="HeaderNames" /></param>
|
||||
/// <param name="data">Set to <see cref="Data" /></param>
|
||||
public void Deconstruct(out IReadOnlyList<string> headerNames, out IReadOnlyList<string> data)
|
||||
{
|
||||
// TODO
|
||||
headerNames = null!;
|
||||
data = null!;
|
||||
}
|
||||
}
|
||||
13
MeetTheTeacher/Model/ICsvRepresentable.cs
Normal file
13
MeetTheTeacher/Model/ICsvRepresentable.cs
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
namespace MeetTheTeacher.Model;
|
||||
|
||||
/// <summary>
|
||||
/// Allows to represent the entity in a CSV compatible way
|
||||
/// </summary>
|
||||
public interface ICsvRepresentable
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts the entity to a <see cref="CsvData" /> instance
|
||||
/// </summary>
|
||||
/// <returns>A <see cref="CsvData" /> instance representing this entity</returns>
|
||||
CsvData ToCsvData();
|
||||
}
|
||||
54
MeetTheTeacher/Model/Teacher.cs
Normal file
54
MeetTheTeacher/Model/Teacher.cs
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
namespace MeetTheTeacher.Model;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a teacher at a vocational college
|
||||
/// </summary>
|
||||
/// <inheritdoc cref="ICsvRepresentable" />
|
||||
public class Teacher : ICsvRepresentable
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="Teacher" />.
|
||||
/// The name is the only required property.
|
||||
/// </summary>
|
||||
/// <param name="name">Name of the teacher</param>
|
||||
public Teacher(string name)
|
||||
{
|
||||
Name = name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the teacher
|
||||
/// </summary>
|
||||
public string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the time frame in which the teacher is available for consulting
|
||||
/// </summary>
|
||||
public TimeFrame? ConsultingHour { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the school unit in which the teacher is available for consulting
|
||||
/// </summary>
|
||||
public SchoolUnit? ConsultingHourUnit { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the day of the week the teacher is available for consulting
|
||||
/// </summary>
|
||||
public DayOfWeek? ConsultingHourWeekDay { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the room in which the teacher is available for consulting
|
||||
/// </summary>
|
||||
public string? Room { get; init; }
|
||||
|
||||
private string? ConsultingHourTime
|
||||
{
|
||||
get
|
||||
{
|
||||
// TODO
|
||||
return null!;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual CsvData ToCsvData() => null!; // TODO
|
||||
}
|
||||
32
MeetTheTeacher/Model/TeacherWithBusinessCard.cs
Normal file
32
MeetTheTeacher/Model/TeacherWithBusinessCard.cs
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
namespace MeetTheTeacher.Model;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a teacher with a business card which means they have an id for their picture available as well
|
||||
/// </summary>
|
||||
/// <inheritdoc cref="Teacher" />
|
||||
public sealed class TeacherWithBusinessCard : Teacher
|
||||
{
|
||||
private const string BaseUrl = "https://www.htl-leonding.at/media/teacher-avatar";
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="TeacherWithBusinessCard" />.
|
||||
/// In addition to the name, the id of the picture is required as well.
|
||||
/// </summary>
|
||||
/// <param name="name">Name of the teacher</param>
|
||||
/// <param name="id">Id of the teacher's picture</param>
|
||||
public TeacherWithBusinessCard(string name, int id) : base(name)
|
||||
{
|
||||
Id = id;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the picture id of the teacher
|
||||
/// </summary>
|
||||
public int Id { get; }
|
||||
|
||||
public override CsvData ToCsvData()
|
||||
{
|
||||
// TODO (remember to maybe use the base implementation...)
|
||||
return null!;
|
||||
}
|
||||
}
|
||||
38
MeetTheTeacher/Model/TimeFrame.cs
Normal file
38
MeetTheTeacher/Model/TimeFrame.cs
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
using System.Globalization;
|
||||
|
||||
namespace MeetTheTeacher.Model;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a time frame between a start and end time.
|
||||
/// Start time is always before end time.
|
||||
/// </summary>
|
||||
/// <param name="Start">Start time</param>
|
||||
/// <param name="End">End time</param>
|
||||
public readonly record struct TimeFrame(TimeOnly Start, TimeOnly End)
|
||||
{
|
||||
private static readonly CultureInfo culture = new ("de-AT");
|
||||
|
||||
/// <summary>
|
||||
/// Tries to parse a time frame from a string in format HH:MM-HH:MM.
|
||||
/// Additional whitespace is trimmed and ignored.
|
||||
/// </summary>
|
||||
/// <param name="timeFrame">Timeframe string to parse</param>
|
||||
/// <param name="result">Set to the parsed time frame if possible</param>
|
||||
/// <returns>True if time frame could be parsed successfully; false otherwise</returns>
|
||||
public static bool TryParse(string timeFrame, out TimeFrame? result)
|
||||
{
|
||||
// TODO
|
||||
result = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string representation of the time frame in format HH:MM-HH:MM
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public override string ToString()
|
||||
{
|
||||
// TODO
|
||||
return null!;
|
||||
}
|
||||
}
|
||||
39
MeetTheTeacher/Model/Various.cs
Normal file
39
MeetTheTeacher/Model/Various.cs
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
namespace MeetTheTeacher.Model;
|
||||
|
||||
/// <summary>
|
||||
/// Represents school units, each taking 50 minutes
|
||||
/// </summary>
|
||||
public enum SchoolUnit
|
||||
{
|
||||
UE01 = 01,
|
||||
UE02 = 02,
|
||||
UE03 = 03,
|
||||
UE04 = 04,
|
||||
UE05 = 05,
|
||||
UE06 = 06,
|
||||
UE07 = 07,
|
||||
UE08 = 08,
|
||||
UE09 = 09,
|
||||
UE10 = 10
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents possible data export formats
|
||||
/// </summary>
|
||||
public enum ExportFormat
|
||||
{
|
||||
Csv,
|
||||
Html
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents possible sort orders for the teacher data
|
||||
/// </summary>
|
||||
public enum SortOrder
|
||||
{
|
||||
ByNameAsc,
|
||||
ByNameDesc,
|
||||
ByHourAsc,
|
||||
ByHourDesc,
|
||||
Room
|
||||
}
|
||||
154
MeetTheTeacher/Program.cs
Normal file
154
MeetTheTeacher/Program.cs
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
using System.Text;
|
||||
using MeetTheTeacher.Export;
|
||||
using MeetTheTeacher.Import;
|
||||
using MeetTheTeacher.Model;
|
||||
using Spectre.Console;
|
||||
|
||||
Console.OutputEncoding = Encoding.UTF8;
|
||||
|
||||
Clear();
|
||||
AnsiConsole.Markup("[orange3]Press any key to load HTL-Leonding teacher data[/] ");
|
||||
Console.ReadKey();
|
||||
|
||||
// data loading
|
||||
bool loadRealData = AskForDataToUse();
|
||||
// wrapping IEnumerable in List, because we will iterate multiple times
|
||||
var data = new List<Teacher>(LoadData(loadRealData));
|
||||
var tableGenerator = new SpectreTableExporter();
|
||||
tableGenerator.Export(data, string.Empty);
|
||||
|
||||
// fake loading
|
||||
Clear();
|
||||
await DisplayFakeProgress();
|
||||
Clear();
|
||||
AnsiConsole.Write(tableGenerator.Table);
|
||||
PrintStatistics(data);
|
||||
|
||||
// user input
|
||||
WriteRule("Sort Order");
|
||||
var order = AskForSortOrder();
|
||||
WriteRule("Export Format");
|
||||
var format = AskForExportFormat();
|
||||
WriteRule("File Name");
|
||||
string fileName = AnsiConsole.Ask<string>("Which [blue]file name[/] should be used?");
|
||||
|
||||
// export
|
||||
var exporter = new Exporter(order, format, new DefaultExporterProvider());
|
||||
exporter.Export(fileName, data);
|
||||
|
||||
// done
|
||||
WriteRule(string.Empty);
|
||||
AnsiConsole.Write(new FigletText("All Done!").Centered().Color(Color.Green));
|
||||
DrawDog();
|
||||
|
||||
return;
|
||||
|
||||
static void Clear()
|
||||
{
|
||||
Console.Clear();
|
||||
AnsiConsole.Write(new FigletText("Teacher Data").Centered().Color(Color.Blue));
|
||||
}
|
||||
|
||||
static void WriteRule(string text)
|
||||
{
|
||||
AnsiConsole.Write(new Rule($"[blue]{text}[/]").LeftJustified());
|
||||
}
|
||||
|
||||
static IEnumerable<Teacher> LoadData(bool realData)
|
||||
{
|
||||
// TODO
|
||||
// ITeacherDataImporter importer ...
|
||||
// files: "Data/teachers.csv" & "Data/teachers-with-business-card.csv"
|
||||
return null!;
|
||||
}
|
||||
|
||||
static Task DisplayFakeProgress()
|
||||
{
|
||||
const double Increase = 4.5;
|
||||
const double ReducedIncrease = Increase * 0.8D;
|
||||
|
||||
return AnsiConsole.Progress()
|
||||
.StartAsync(async ctx =>
|
||||
{
|
||||
// we are actually already done, but want to fake some complex operation
|
||||
// to have the user appreciate the hard work our application is doing :)
|
||||
var task1 = ctx.AddTask("[green]Loading Data[/]");
|
||||
var task2 = ctx.AddTask("[green]Processing Data[/]");
|
||||
|
||||
while (!ctx.IsFinished)
|
||||
{
|
||||
task1.Increment(Increase);
|
||||
task2.Increment(ReducedIncrease);
|
||||
await Task.Delay(TimeSpan.FromMilliseconds(125));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static void PrintStatistics(IReadOnlyCollection<Teacher> teachers)
|
||||
{
|
||||
var hasConsultingHours = 0;
|
||||
var hasBusinessCard = 0;
|
||||
foreach (var teacher in teachers)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
double total = teachers.Count;
|
||||
var cPercentage = (int) (hasConsultingHours / total * 100);
|
||||
int ncPercentage = 100 - cPercentage;
|
||||
var bPercentage = (int) (hasBusinessCard / total * 100);
|
||||
|
||||
AnsiConsole.Write(new BarChart()
|
||||
.Width(60)
|
||||
.Label($"[blue underline]Statistics ({total:F0} teachers)[/]")
|
||||
.CenterLabel()
|
||||
.AddItem("Has consulting hour", cPercentage, Color.Green)
|
||||
.AddItem("Has no consulting hour", ncPercentage, Color.Red)
|
||||
.AddItem("Has business card", bPercentage, Color.Purple));
|
||||
}
|
||||
|
||||
static SortOrder AskForSortOrder()
|
||||
{
|
||||
var options = new Dictionary<string, SortOrder>
|
||||
{
|
||||
["Name Asc."] = SortOrder.ByNameAsc,
|
||||
["Name Desc."] = SortOrder.ByNameDesc,
|
||||
["Hour Asc."] = SortOrder.ByHourAsc,
|
||||
["Hour Desc."] = SortOrder.ByHourDesc,
|
||||
["Room"] = SortOrder.Room
|
||||
};
|
||||
string order = AnsiConsole.Prompt(new SelectionPrompt<string>()
|
||||
.Title("How do you wish to [blue]sort[/] the data?")
|
||||
.PageSize(3)
|
||||
.MoreChoicesText("[grey](Move up and down to reveal more options)[/]")
|
||||
.AddChoices(options.Keys));
|
||||
|
||||
AnsiConsole.MarkupLine($"Selected: [green]{order}[/]");
|
||||
|
||||
return options[order];
|
||||
}
|
||||
|
||||
static ExportFormat AskForExportFormat()
|
||||
{
|
||||
// TODO (similar to AskForDataToUse)
|
||||
return default;
|
||||
}
|
||||
|
||||
static bool AskForDataToUse()
|
||||
{
|
||||
const string Real = "Real";
|
||||
string dataToUse = AnsiConsole.Prompt(new SelectionPrompt<string>()
|
||||
.Title("Do you want to load [blue]real[/] or [blue]sample[/] data?")
|
||||
.AddChoices(Real, "Sample"));
|
||||
|
||||
AnsiConsole.MarkupLine($"Selected: [green]{dataToUse}[/]");
|
||||
|
||||
return dataToUse == Real;
|
||||
}
|
||||
|
||||
static void DrawDog()
|
||||
{
|
||||
var image = new CanvasImage("happy-dog.png");
|
||||
image.MaxWidth(36);
|
||||
AnsiConsole.Write(image);
|
||||
}
|
||||
BIN
MeetTheTeacher/happy-dog.png
Normal file
BIN
MeetTheTeacher/happy-dog.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 25 KiB |
Loading…
Add table
Add a link
Reference in a new issue