Implemented export to CSV functionality.

This commit is contained in:
alphafetus committed 2023-10-25 08:45:01 -04:00
1 parent a478d4ebb0
commit ddbc745200
4 files changed
+98 -9

No files matched your search

+49
View File
@@ -121,6 +121,55 @@ public class Database {
return coins; return coins;
} }
public String CollectionToCSV(string collectionName) {
var cmd = Connection.CreateCommand();
cmd.CommandText = $"SELECT * FROM {collectionName}";
var headersCommand = Connection.CreateCommand();
headersCommand.CommandText = $"PRAGMA table_info({collectionName})";
var resultString = "";
var numHeaders = 0;
// Get the headers.
using (var reader = headersCommand.ExecuteReader()) {
while (reader.Read()) {
resultString += reader.GetString(1) + ",";
numHeaders++;
}
}
resultString += "\n";
// Is there a more efficient way of doing this? This is running at O(n^2) time.
using (var reader = cmd.ExecuteReader()) {
while (reader.Read()) {
// Read information from the reader, then add to CSV string.
for (int i = 0; i < numHeaders; i++) {
// Get the type of each column.
var type = reader.GetFieldType(i);
Console.WriteLine("Type: " + type);
if (type == typeof(Int64)) {
resultString += reader.GetInt32(i) + ",";
}
else if (type == typeof(Double)) {
resultString += reader.GetDouble(i) + ",";
}
else if (type == typeof(String)) {
resultString += reader.GetString(i) + ",";
}
else {
resultString += "NULL" + ",";
}
}
resultString += "\n";
}
}
return resultString;
}
public Coin? GetCoin(int pcgsNumber, string grade) { public Coin? GetCoin(int pcgsNumber, string grade) {
// Get the coin based off of the pcgs number. // Get the coin based off of the pcgs number.
+22 -4
View File
@@ -1,5 +1,6 @@
using System; using System;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.IO;
using System.Linq; using System.Linq;
using System.Reactive; using System.Reactive;
using System.Reactive.Linq; using System.Reactive.Linq;
@@ -9,8 +10,6 @@ using ReactiveUI;
namespace PcgsInvUi.ViewModels; namespace PcgsInvUi.ViewModels;
// TODO Find window.
// TODO Export window and export function.
// TODO Error handling. // TODO Error handling.
// TODO Manual handling of API key. // TODO Manual handling of API key.
// TODO Use CoinFacts link. // TODO Use CoinFacts link.
@@ -43,6 +42,8 @@ public class MainWindowViewModel : ViewModelBase {
public Interaction<DeleteWindowViewModel, Boolean> ShowDeleteWindow { get; } public Interaction<DeleteWindowViewModel, Boolean> ShowDeleteWindow { get; }
public ReactiveCommand<Unit, Coin> FindCommand { get; } public ReactiveCommand<Unit, Coin> FindCommand { get; }
public Interaction<FindWindowViewModel, int> ShowFindWindow { get; } public Interaction<FindWindowViewModel, int> ShowFindWindow { get; }
public ReactiveCommand<Unit, Unit> ExportCommand { get; }
public Interaction<Unit, Stream> ShowExportWindow { get; }
private ViewModelBase _sideContent; private ViewModelBase _sideContent;
private Coin? _selectedCoin; private Coin? _selectedCoin;
@@ -67,12 +68,29 @@ public class MainWindowViewModel : ViewModelBase {
DeleteCommand = ReactiveCommand.CreateFromTask(async () => { DeleteCommand = ReactiveCommand.CreateFromTask(async () => {
var deleteViewModel = new DeleteWindowViewModel(); var deleteViewModel = new DeleteWindowViewModel();
var result = await ShowDeleteWindow.Handle(deleteViewModel); var result = await ShowDeleteWindow.Handle(deleteViewModel);
if (result && SelectedCoin != null) { if (result && SelectedCoin is not null) {
ConnectedDatabase.DeleteCoin(SelectedCoin); ConnectedDatabase.DeleteCoin(SelectedCoin);
CoinCollection.Remove(SelectedCoin); // possible null deref - ignored due to button being disabled. CoinCollection.Remove(SelectedCoin); // possible null deref - ignored due to button being disabled.
} }
}, deleteEnabled); }, deleteEnabled);
ShowExportWindow = new Interaction<Unit, Stream>();
ExportCommand = ReactiveCommand.CreateFromTask(async () => {
var stream = await ShowExportWindow.Handle(Unit.Default);
var writer = new StreamWriter(stream);
try {
await writer.WriteAsync(ConnectedDatabase.CollectionToCSV("CollectionTable"));
await writer.FlushAsync();
}
catch (IOException e) {
// Something is wrong with the file after it's been created.
Console.WriteLine(e.Message);
}
writer.Close();
stream.Close();
});
// Set up the ability to open the find window. // Set up the ability to open the find window.
ShowFindWindow = new Interaction<FindWindowViewModel, int>(); ShowFindWindow = new Interaction<FindWindowViewModel, int>();
var findEnabled = this.WhenAnyValue(x => x.CoinCollection.Count, var findEnabled = this.WhenAnyValue(x => x.CoinCollection.Count,
@@ -84,7 +102,7 @@ public class MainWindowViewModel : ViewModelBase {
// Perhaps a secondary viewmodel? // Perhaps a secondary viewmodel?
return new Coin(); return new Coin();
}, findEnabled); }, findEnabled);
// Subscribe to SidebarContent.OkCommand, which is an IObservable<(int, string, int)> // Subscribe to SidebarContent.OkCommand, which is an IObservable<(int, string, int)>
// This is the same as the above, but with a different syntax. // This is the same as the above, but with a different syntax.
newViewModel.OkCommand newViewModel.OkCommand
+4 -4
View File
@@ -65,16 +65,16 @@
DockPanel.Dock="Left"> DockPanel.Dock="Left">
<Button Grid.Column="0" Margin="5" Width="100" HorizontalContentAlignment="Center" Content="Delete" <Button Grid.Column="0" Margin="5" Width="100" HorizontalContentAlignment="Center" Content="Delete"
Command="{Binding DeleteCommand}" /> Command="{Binding DeleteCommand}" />
<Button Grid.Column="1" Margin="5" Width="100" HorizontalContentAlignment="Center" <!-- <Button Grid.Column="1" Margin="5" Width="100" HorizontalContentAlignment="Center" -->
Content="Find..." <!-- Content="Find..." -->
Command="{Binding FindCommand}" /> <!-- Command="{Binding FindCommand}" /> -->
</Grid> </Grid>
<Grid HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="5" ColumnDefinitions="Auto, Auto" <Grid HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="5" ColumnDefinitions="Auto, Auto"
DockPanel.Dock="Right"> DockPanel.Dock="Right">
<Label Grid.Column="0" Margin="5" VerticalAlignment="Center" <Label Grid.Column="0" Margin="5" VerticalAlignment="Center"
Content="{Binding TotalValue, StringFormat='Total Value: ${0:0.00}'}" /> Content="{Binding TotalValue, StringFormat='Total Value: ${0:0.00}'}" />
<Button Grid.Column="1" Margin="5" Width="150" HorizontalContentAlignment="Center" <Button Grid.Column="1" Margin="5" Width="150" HorizontalContentAlignment="Center"
Content="Export to CSV..." /> Content="Export to CSV..." Command="{Binding ExportCommand}" />
</Grid> </Grid>
</DockPanel> </DockPanel>
</Grid> </Grid>
+23 -1
View File
@@ -1,5 +1,8 @@
using System; using System;
using System.IO;
using System.Reactive;
using System.Threading.Tasks; using System.Threading.Tasks;
using Avalonia.Platform.Storage;
using Avalonia.ReactiveUI; using Avalonia.ReactiveUI;
using PcgsInvUi.ViewModels; using PcgsInvUi.ViewModels;
using ReactiveUI; using ReactiveUI;
@@ -10,7 +13,8 @@ public partial class MainWindow : ReactiveWindow<MainWindowViewModel> {
public MainWindow() { public MainWindow() {
InitializeComponent(); InitializeComponent();
this.WhenActivated(d => d(ViewModel!.ShowDeleteWindow.RegisterHandler(ShowDeleteWindowAsync))); this.WhenActivated(d => d(ViewModel!.ShowDeleteWindow.RegisterHandler(ShowDeleteWindowAsync)));
this.WhenActivated(d => d(ViewModel!.ShowFindWindow.RegisterHandler(ShowFindWindowAsync))); this.WhenActivated(d => d(ViewModel!.ShowExportWindow.RegisterHandler(ShowFilePickerAsync)));
// this.WhenActivated(d => d(ViewModel!.ShowFindWindow.RegisterHandler(ShowFindWindowAsync)));
} }
public async Task ShowDeleteWindowAsync(InteractionContext<DeleteWindowViewModel, Boolean> interaction) { public async Task ShowDeleteWindowAsync(InteractionContext<DeleteWindowViewModel, Boolean> interaction) {
@@ -28,4 +32,22 @@ public partial class MainWindow : ReactiveWindow<MainWindowViewModel> {
var result = await window.ShowDialog<int>(this); var result = await window.ShowDialog<int>(this);
interaction.SetOutput(result); interaction.SetOutput(result);
} }
public async Task ShowFilePickerAsync(InteractionContext<Unit, Stream> interationContext) {
var topLevel = GetTopLevel(this);
var file = await topLevel.StorageProvider.SaveFilePickerAsync(new FilePickerSaveOptions {
Title = "Save CSV...",
DefaultExtension = ".csv",
ShowOverwritePrompt = true,
SuggestedFileName = "coins.csv",
SuggestedStartLocation = await StorageProvider.TryGetWellKnownFolderAsync(WellKnownFolder.Documents)
});
Stream stream;
if (file is not null) {
stream = await file.OpenWriteAsync();
interationContext.SetOutput(stream);
}
}
} }