Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
d4e4486
Installed spectre console GUI package
JJHH17 Oct 14, 2025
ff1942b
Added initial program.cs class, includes spectre menu
JJHH17 Oct 14, 2025
85b2976
Added the system.config nuget package
JJHH17 Oct 14, 2025
91456d7
Installed the EF to SQL Server Nuget Package
JJHH17 Oct 14, 2025
dffc8f7
Created connection string and database values
JJHH17 Oct 14, 2025
67ed932
Created initial program model and DB Context
JJHH17 Oct 14, 2025
7812529
Installed entity framework design package for running migrations
JJHH17 Oct 14, 2025
30bdd0c
Createdmethods for adding new entries to db
JJHH17 Oct 14, 2025
e62370e
Resolved typo in connection string call
JJHH17 Oct 14, 2025
80ec3ad
Added method for printing entries to console
JJHH17 Oct 14, 2025
804101f
Added method for deleting entries
JJHH17 Oct 14, 2025
7ce04e4
Added method that seeds data from a csv file
JJHH17 Oct 14, 2025
64d19c8
Installed the CSV helper package
JJHH17 Oct 15, 2025
602bf11
Added string for export PDF file path location
JJHH17 Oct 15, 2025
56eb50f
Removed configuration for filepath which was unused
JJHH17 Oct 15, 2025
6f30e61
Added code to export data to a PDF via app
JJHH17 Oct 15, 2025
5b031f3
Added method for importing data via xls file
JJHH17 Oct 15, 2025
094fa4a
Added helper packages for xlsx processing and reading
JJHH17 Oct 15, 2025
c89e76b
Added method to read and seed xlsx files
JJHH17 Oct 15, 2025
97c2317
Added method to seed from XLS files
JJHH17 Oct 15, 2025
3a98ab0
Removed UserInterface and placed into its own class
JJHH17 Oct 17, 2025
bfb48bb
Added userInterface class
JJHH17 Oct 17, 2025
912ea61
added connection to dataseeding class
JJHH17 Oct 17, 2025
efb7376
Moved dataseeding mechanisms into their own class
JJHH17 Oct 17, 2025
7651931
Moved data seeding mechanism into its own class
JJHH17 Oct 17, 2025
f4d4031
Added data seeding into its own class
JJHH17 Oct 17, 2025
3339329
Added data export to its own class
JJHH17 Oct 17, 2025
1ed5b7a
Added support for exporting PDF
JJHH17 Oct 17, 2025
26494ab
Added reference to export menu
JJHH17 Oct 17, 2025
5c9def7
Added ability to export to CSV
JJHH17 Oct 17, 2025
7457379
Installed Cronos package, for cron/scheduling tasks
JJHH17 Oct 17, 2025
50794e3
Added code to export data to a csv every day at 9am, using the cronos…
JJHH17 Oct 17, 2025
05f886d
Added cronos package for report scheduling
JJHH17 Oct 17, 2025
1a3e7e4
Report automated generation now runs concurrently to the app UI
JJHH17 Oct 17, 2025
53d34c2
Added reference to azure blob connection string
JJHH17 Oct 21, 2025
04f38e1
Added azure blob package
JJHH17 Oct 21, 2025
2c8982d
Added container name reference to app.config
JJHH17 Oct 21, 2025
472022f
Added code to store to an Azure blob
JJHH17 Oct 21, 2025
07f8898
Removed unused using call in dataexport class
JJHH17 Oct 23, 2025
f72ecb5
Create README for Document Processor Application
JJHH17 Oct 23, 2025
28afb09
Enhance README with project details and usage instructions
JJHH17 Oct 23, 2025
7d15b0c
Update README with automated file export details
JJHH17 Oct 23, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="Server" value="localdb"/>
<add key="DatabaseName" value="documentProcessor"/>
<add key="AzureBlobConnectionString" value="NAMEHERE"/>
<add key="ContainerName" value="NAMEHERE"/>
</appSettings>
</configuration>
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
using CsvHelper;
using DocumentProcessor.JJHH17.Models;
using Spectre.Console;
using Azure.Storage.Blobs;
using System.Configuration;
using Azure.Storage.Blobs.Models;

namespace Document.Processor.JJHH17.DataExporting;

public class DataExport
{
enum ExportMenuOptions
{
PDF,
CSV,
AzureBlobStorage
}

public static void ExportMenu()
{
Console.Clear();

AnsiConsole.MarkupLine("[bold yellow]Export Data to a given file type[/]");
var choice = AnsiConsole.Prompt(
new SelectionPrompt<ExportMenuOptions>()
.Title("Select a file type to export to:")
.AddChoices(Enum.GetValues<ExportMenuOptions>()));
switch (choice)
{
case ExportMenuOptions.PDF:
CreateExportPDF();
break;

case ExportMenuOptions.CSV:
CreateExportCsv();
break;

case ExportMenuOptions.AzureBlobStorage:
CreateAzureBlobExport();
break;
}
}

public static void CreateExportPDF()
{
using (var context = new PhoneBookContext())
{
var entries = context.Phonebooks.ToList();
using (var writer = new StreamWriter("ExportedPhonebook.pdf"))
using (var csv = new CsvWriter(writer, System.Globalization.CultureInfo.InvariantCulture))
{
csv.WriteRecords(entries);
}
}
AnsiConsole.MarkupLine("[green]Data exported to ExportedPhonebook.pdf successfully![/]");
AnsiConsole.MarkupLine("[green]You can find the CSV in 'CodeReviews.Console.DocumentProcessor\\DocumentProcessor.JJHH17\\DocumentProcessor.JJHH17\\bin\\Debug\\net8.0\\ExportedPhonebook.pdf\'[/]");
}

public static void CreateExportCsv()
{
using (var context = new PhoneBookContext())
{
var entries = context.Phonebooks.ToList();
using (var writer = new StreamWriter("ExportedPhonebook.csv"))
using (var csv = new CsvWriter(writer, System.Globalization.CultureInfo.InvariantCulture))
{
csv.WriteRecords(entries);
}
}

AnsiConsole.MarkupLine("[green]Data exported to ExportedPhonebook.csv successfully![/]");
AnsiConsole.MarkupLine("[green]You can find the CSV in 'CodeReviews.Console.DocumentProcessor\\DocumentProcessor.JJHH17\\DocumentProcessor.JJHH17\\bin\\Debug\\net8.0\\ExportedPhonebook.csv\'[/]");
}

public static async Task CreateAzureBlobExport()
{
var localCsv = "ExportedPhonebook.csv";
if (!File.Exists(localCsv))
{
AnsiConsole.MarkupLine("[yellow]CSV file not found. Creating CSV file first...[/]");
CreateExportCsv();
}

var connectionString = ConfigurationManager.AppSettings["AzureBlobConnectionString"];
var containerName = ConfigurationManager.AppSettings["ContainerName"];

if (string.IsNullOrEmpty(connectionString) || string.IsNullOrEmpty(containerName))
{
AnsiConsole.MarkupLine("[red]Azure Blob Storage connection string or container name is not configured properly. Please check in the app.config file.[/]");
return;
}

var blobServiceClient = new BlobServiceClient(connectionString);
var containerClient = blobServiceClient.GetBlobContainerClient(containerName);
await containerClient.CreateIfNotExistsAsync();

var blobClient = containerClient.GetBlobClient("ExportedPhonebook.csv");
using var fileStream = File.OpenRead(localCsv);

var options = new BlobUploadOptions
{
HttpHeaders = new BlobHttpHeaders { ContentType = "text/csv" }
};

await blobClient.UploadAsync(fileStream, options);
AnsiConsole.MarkupLine("[green]CSV file uploaded to Azure Blob Storage successfully![/]");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
using DocumentProcessor.JJHH17.Models;
using ExcelDataReader;
using Spectre.Console;
using System.Data;

namespace Document.Processor.JJHH17.DataSeeding;

public class DataSeed
{
enum FileTypes
{
CSV,
XLS,
XLSX
}

public static void SeedOption()
{
Console.Clear();
AnsiConsole.MarkupLine("[bold yellow]Seed Database via a given file type[/]");

var choice = AnsiConsole.Prompt(
new SelectionPrompt<FileTypes>()
.Title("Select a file type to import from:")
.AddChoices(Enum.GetValues<FileTypes>()));

switch (choice)
{
case FileTypes.CSV:
SeedCSVData();
break;
case FileTypes.XLS:
SeedXLSData();
break;
case FileTypes.XLSX:
SeedXLSXData();
break;
}
}

public static List<string[]> ReadFile(string filePath)
{
List<string[]> rows = new List<string[]>();

try
{
string[] lines = File.ReadAllLines(filePath);

foreach (var line in lines)
{
var values = line.Split(',');
rows.Add(values);
}
}
catch (Exception ex)
{
AnsiConsole.MarkupLine($"[red]Error reading file: {ex.Message}[/]");
Console.WriteLine("Press any key to return to the menu...");
Console.ReadKey();
}

return rows;
}

public static List<string[]> ReadExcel(string filePath)
{
System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);

using var stream = File.Open(filePath, FileMode.Open, FileAccess.Read);
using var reader = ExcelReaderFactory.CreateReader(stream);

var dataset = reader.AsDataSet(new ExcelDataSetConfiguration
{
ConfigureDataTable = _ => new ExcelDataTableConfiguration
{
UseHeaderRow = true
}
});

var table = dataset.Tables[0];
var rows = new List<string[]>();

foreach (DataRow dr in table.Rows)
{
var name = dr["Name"].ToString()?.Trim();
var email = dr["Email"].ToString()?.Trim();
var phoneNumber = dr["PhoneNumber"].ToString()?.Trim();

rows.Add(new string[] { name, email, phoneNumber });
}

return rows;
}

public static void SeedCSVData()
{
try
{
string csvFilePath = "Import Data - Sheet1.csv";
List<string[]> csvData = ReadFile(csvFilePath);

foreach (string[] row in csvData)
{
using (var context = new PhoneBookContext())
{
var newEntry = new Phonebook
{
Name = row[0],
Email = row[1],
PhoneNumber = row[2]
};
context.Phonebooks.Add(newEntry);
context.SaveChanges();
}
}

AnsiConsole.MarkupLine("[green]Database seeded successfully from CSV![/]");
Console.WriteLine("Press any key to return to the menu...");
Console.ReadKey();
}
catch (Exception ex)
{
AnsiConsole.MarkupLine($"[red]Error reading CSV file: {ex.Message}[/]");
Console.WriteLine("Press any key to return to the menu...");
Console.ReadKey();
}
}

public static void SeedXLSData()
{
try
{
string xlsFilePath = "Import Data - Sheet1.xls";
var xlsData = ReadExcel(xlsFilePath);
using var context = new PhoneBookContext();
foreach (var row in xlsData)
{
var newEntry = new Phonebook
{
Name = row[0],
Email = row[1],
PhoneNumber = row[2]
};
context.Phonebooks.Add(newEntry);
}
context.SaveChanges();
AnsiConsole.MarkupLine("[green]Database seeded successfully from XLS![/]");
Console.WriteLine("Press any key to return to the menu...");
Console.ReadKey();
}
catch (Exception ex)
{
AnsiConsole.MarkupLine($"[red]Error reading XLS file: {ex.Message}[/]");
Console.WriteLine("Press any key to return to the menu...");
Console.ReadKey();
}
}

public static void SeedXLSXData()
{
try
{
string xlsxFilePath = "Import Data - Sheet1.xlsx";
var xlsxData = ReadExcel(xlsxFilePath);

using var context = new PhoneBookContext();
foreach (var row in xlsxData)
{
var newEntry = new Phonebook
{
Name = row[0],
Email = row[1],
PhoneNumber = row[2]
};
context.Phonebooks.Add(newEntry);
}

context.SaveChanges();
AnsiConsole.MarkupLine("[green]Database seeded successfully from XLSX![/]");
Console.WriteLine("Press any key to return to the menu...");
Console.ReadKey();
}
catch (Exception ex)
{
AnsiConsole.MarkupLine($"[red]Error reading XLSX file: {ex.Message}[/]");
Console.WriteLine("Press any key to return to the menu...");
Console.ReadKey();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<Project Sdk="Microsoft.NET.Sdk">

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

<ItemGroup>
<PackageReference Include="Azure.Storage.Blobs" Version="12.26.0" />
<PackageReference Include="Cronos" Version="0.11.1" />
<PackageReference Include="CsvHelper" Version="33.1.0" />
<PackageReference Include="ExcelDataReader" Version="3.8.0" />
<PackageReference Include="ExcelDataReader.DataSet" Version="3.8.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.9">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.9" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.10" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.10" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.10" />
<PackageReference Include="Open-XML-SDK" Version="2.9.1" />
<PackageReference Include="Spectre.Console" Version="0.52.0" />
<PackageReference Include="Spectre.Console.Cli" Version="0.52.0" />
<PackageReference Include="System.Configuration.ConfigurationManager" Version="9.0.9" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using System.Configuration;
using Microsoft.EntityFrameworkCore;

namespace DocumentProcessor.JJHH17.Models;

public class Phonebook
{
public int Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public string PhoneNumber { get; set; }
}

public class PhoneBookContext : DbContext

Choose a reason for hiding this comment

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

🟠 Separate Class

💡 This is not a Model, it is a DbContext. Move it to a different location that makes sense logically.

{
private static readonly string server = ConfigurationManager.AppSettings["Server"];
private static readonly string databaseInstance = ConfigurationManager.AppSettings["DatabaseName"];
public static string connectionString = $@"Server=({server})\{databaseInstance};Integrated Security=true;";

public DbSet<Phonebook> Phonebooks { get; set; }

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(connectionString);
}
}
Loading