Justerat lite i gränssnittet

This commit is contained in:
2026-04-26 13:38:54 +02:00
parent 1e8098996c
commit 3ec543ed3d
4 changed files with 338 additions and 202 deletions

View File

@ -0,0 +1,81 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UglyToad.PdfPig;
using UglyToad.PdfPig.Content;
namespace StockHistory;
public static class PdfPigLetterExtractor
{
public static List<string> ExtractLines(List<PdfSida> pages, double lineTolerance = 3.0, double wordSpacingFactor = 0.5)
{
var result = new List<string>();
foreach (var page in pages)
{
var letters = page.FoundLetters;
// 1. Gruppér tecken till rader baserat på Y-position
var lines = letters
.GroupBy(l => Math.Round(l.BoundingBox.Top / lineTolerance))
.OrderBy(g => g.Key);
foreach (var lineGroup in lines)
{
var lineLetters = lineGroup
.OrderBy(l => l.BoundingBox.Left)
.ToList();
// 2. Bygg ord genom att titta på avstånd mellan tecken
var words = BuildWords(lineLetters, wordSpacingFactor);
result.Add(string.Join(" ", words));
}
}
return result;
}
private static List<string> BuildWords(List<Letter> letters, double spacingFactor)
{
var words = new List<string>();
var currentWord = new List<Letter>();
for (int i = 0; i < letters.Count; i++)
{
var letter = letters[i];
currentWord.Add(letter);
if (i < letters.Count - 1)
{
var next = letters[i + 1];
// Avstånd mellan tecken
double gap = next.BoundingBox.Left - letter.BoundingBox.Right;
// Typisk teckenbredd
double avgWidth = letter.BoundingBox.Width;
// Om gapet är större än spacingFactor * teckenbredd → nytt ord
if (gap > avgWidth * spacingFactor)
{
//words.Add(new string(currentWord.Select(l => l.Value).ToArray()));
words.Add(string.Concat(currentWord.Select(l => l.Value)));
currentWord.Clear();
}
}
}
if (currentWord.Count > 0)
{
//words.Add(new string(currentWord.Select(l => l.Value).ToArray()));
words.Add(string.Concat(currentWord.Select(l => l.Value)));
}
return words;
}
}

View File

@ -7,6 +7,10 @@
<UseWindowsForms>true</UseWindowsForms>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<Compile Remove="PdfPigLetterExtractor.cs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.6" />

View File

@ -44,6 +44,9 @@
CommissionHeader = new ColumnHeader();
AmountToPayHeader = new ColumnHeader();
btnClose = new Button();
btnAnalysMany = new Button();
txtVald = new TextBox();
lblAntal = new Label();
SuspendLayout();
//
// btnChooseFile
@ -74,7 +77,7 @@
btnAnalysera.BackColor = Color.FromArgb(128, 255, 128);
btnAnalysera.FlatStyle = FlatStyle.Flat;
btnAnalysera.Font = new Font("Segoe UI", 13F);
btnAnalysera.Location = new Point(26, 86);
btnAnalysera.Location = new Point(26, 126);
btnAnalysera.Name = "btnAnalysera";
btnAnalysera.Size = new Size(143, 37);
btnAnalysera.TabIndex = 2;
@ -84,10 +87,10 @@
//
// txtFound
//
txtFound.Location = new Point(26, 149);
txtFound.Location = new Point(26, 180);
txtFound.Multiline = true;
txtFound.Name = "txtFound";
txtFound.Size = new Size(468, 334);
txtFound.Size = new Size(468, 303);
txtFound.TabIndex = 3;
//
// btnSaveToDb
@ -95,7 +98,7 @@
btnSaveToDb.BackColor = Color.FromArgb(128, 255, 128);
btnSaveToDb.FlatStyle = FlatStyle.Flat;
btnSaveToDb.Font = new Font("Segoe UI", 13F);
btnSaveToDb.Location = new Point(197, 86);
btnSaveToDb.Location = new Point(351, 126);
btnSaveToDb.Name = "btnSaveToDb";
btnSaveToDb.Size = new Size(143, 37);
btnSaveToDb.TabIndex = 4;
@ -168,12 +171,36 @@
btnClose.UseVisualStyleBackColor = false;
btnClose.Click += btnClose_Click;
//
// btnAnalysMany
//
btnAnalysMany.Location = new Point(0, 0);
btnAnalysMany.Name = "btnAnalysMany";
btnAnalysMany.Size = new Size(75, 23);
btnAnalysMany.TabIndex = 2;
//
// txtVald
//
txtVald.Location = new Point(0, 0);
txtVald.Name = "txtVald";
txtVald.Size = new Size(100, 27);
txtVald.TabIndex = 1;
//
// lblAntal
//
lblAntal.Location = new Point(0, 0);
lblAntal.Name = "lblAntal";
lblAntal.Size = new Size(100, 23);
lblAntal.TabIndex = 0;
//
// frmStockHistoryInit
//
AutoScaleDimensions = new SizeF(8F, 20F);
AutoScaleMode = AutoScaleMode.Font;
BackColor = Color.LightGray;
ClientSize = new Size(1413, 553);
Controls.Add(lblAntal);
Controls.Add(txtVald);
Controls.Add(btnAnalysMany);
Controls.Add(btnClose);
Controls.Add(lwTransactions);
Controls.Add(btnSaveToDb);
@ -205,5 +232,8 @@
private ColumnHeader CommissionHeader;
private ColumnHeader AmountToPayHeader;
private Button btnClose;
private Button btnAnalysMany;
private TextBox txtVald;
private Label lblAntal;
}
}

View File

@ -12,207 +12,228 @@ using System.Drawing.Drawing2D;
using System.Text;
using System.Windows.Forms;
namespace StockHistory
namespace StockHistory;
public partial class frmStockHistoryInit : Form
{
public partial class frmStockHistoryInit : Form
private readonly IPdfOpener _pdfOpener;
private readonly IPdfFormatter _pdfFormatter;
private readonly ITransactionNotesServices _transactionNotes;
private List<PdfSida> _pdfSidor = new();
private List<PdfSida> localPdfSida = new();
public frmStockHistoryInit(
IPdfOpener pdfOpener,
IPdfFormatter pdfFormatter,
ITransactionNotesServices transactionNotes
)
{
private readonly IPdfOpener _pdfOpener;
private readonly IPdfFormatter _pdfFormatter;
private readonly ITransactionNotesServices _transactionNotes;
private List<PdfSida> _pdfSidor = new();
InitializeComponent();
_pdfOpener = pdfOpener;
_pdfFormatter = pdfFormatter;
_transactionNotes = transactionNotes;
btnAnalysera.Enabled = false;
public frmStockHistoryInit(
IPdfOpener pdfOpener,
IPdfFormatter pdfFormatter,
ITransactionNotesServices transactionNotes
)
Shown += async (s, e) => await LoadBusinessAsync();
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public List<string> Selected { get; set; } = new();
private void btnChooseFile_Click(object sender, EventArgs e)
{
ofdFiler.Title = "Välj en PDF-fil";
ofdFiler.FileName = "";
ofdFiler.Filter = "PDF-filer (*.pdf)|*.pdf|ExcelTrans (*.csv)|*.csv|Alla filer (*.*)|*.*";
var result = ofdFiler.ShowDialog();
if (result == DialogResult.OK)
{
InitializeComponent();
_pdfOpener = pdfOpener;
_pdfFormatter = pdfFormatter;
_transactionNotes = transactionNotes;
btnAnalysera.Enabled = false;
Shown += async (s, e) => await LoadBusinessAsync();
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public List<string> Selected { get; set; } = new();
private void btnChooseFile_Click(object sender, EventArgs e)
{
ofdFiler.Title = "Välj en PDF-fil";
ofdFiler.FileName = "";
ofdFiler.Filter = "PDF-filer (*.pdf)|*.pdf|Alla filer (*.*)|*.*";
var result = ofdFiler.ShowDialog();
if (result == DialogResult.OK)
{
lblFileName.Text = ofdFiler.FileName;
}
}
private void lblFileName_TextChanged(object sender, EventArgs e)
{
if (lblFileName.Text.Length > 5 && lblFileName.Text.EndsWith(".pdf"))
{
_pdfOpener.OpenPdf(lblFileName.Text);
_pdfSidor = _pdfOpener.PdfSidor;
btnAnalysera.Enabled = true;
}
else
{
btnAnalysera.Enabled = false;
}
}
private void btnAnalysera_Click(object sender, EventArgs e)
{
string PdfResult = _pdfFormatter.ExtractFormattedTextUsingWords(_pdfSidor);
txtFound.Text = string.Empty;
List<string> result = PdfResult.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries).ToList();
//List<string> selected = new();
foreach (var line in result)
{
if (!string.IsNullOrWhiteSpace(line) && line.StartsWith("Affärsdag")
|| !string.IsNullOrWhiteSpace(line) && line.StartsWith("Handelstidpunkt")
|| !string.IsNullOrWhiteSpace(line) && line.StartsWith("Värdepapper")
|| !string.IsNullOrWhiteSpace(line) && line.StartsWith("Kurs")
|| !string.IsNullOrWhiteSpace(line) && line.StartsWith("Genomsnittlig")
|| !string.IsNullOrWhiteSpace(line) && line.StartsWith("Antal")
|| !string.IsNullOrWhiteSpace(line) && line.StartsWith("Köpeskilling")
|| !string.IsNullOrWhiteSpace(line) && line.StartsWith("Courtage")
|| !string.IsNullOrWhiteSpace(line) && line.StartsWith("Belopp")
|| !string.IsNullOrWhiteSpace(line) && line.StartsWith("Vi bekräftar")
)
{
Selected.Add(line);
txtFound.Text += line + Environment.NewLine;
txtFound.Refresh();
}
}
btnAnalysera.Enabled = false;
lblFileName.Text = string.Empty;
}
private async void btnSaveToDb_Click(object sender, EventArgs e)
{
TransactionNote trans = new();
foreach (var line in Selected)
{
if (line.StartsWith("Affärsdag"))
{
trans.TransactionDate = DateTime.Parse(line.Split(" ", StringSplitOptions.RemoveEmptyEntries)[1]);
}
if (line.StartsWith("Värdepapper"))
{
var templist = line.Split(" ", StringSplitOptions.RemoveEmptyEntries);
if (templist.Length > 2)
trans.StockCode = templist[1] + " " + templist[2];
else
trans.StockCode = templist[1];
}
if (line.StartsWith("Kurs"))
{
trans.StockPrice = decimal.Parse(line.Split(" ", StringSplitOptions.RemoveEmptyEntries)[2]);
}
if (line.StartsWith("Antal"))
{
trans.StockQuantity = int.Parse(line.Split(" ", StringSplitOptions.RemoveEmptyEntries)[1]);
}
if (line.StartsWith("Köpeskilling"))
{
var templist = line.Split(" ", StringSplitOptions.RemoveEmptyEntries);
if (templist.Length > 3)
trans.TotalAmount = decimal.Parse(templist[2] + templist[3]);
else
trans.TotalAmount = decimal.Parse(templist[2]);
}
if (line.StartsWith("Courtage"))
{
var tmpList = line.Split(" ", StringSplitOptions.RemoveEmptyEntries);
trans.Commission = decimal.Parse(tmpList[tmpList.Length - 1]);
}
if (line.StartsWith("Belopp"))
{
var templist = line.Split(" ", StringSplitOptions.RemoveEmptyEntries);
if (templist.Length > 5)
trans.AmountToPay = decimal.Parse(templist[4] + templist[5]);
else
trans.AmountToPay = decimal.Parse(templist[4]);
}
if (line.StartsWith("Vi bekräftar"))
{
foreach (var word in line.Split(" ", StringSplitOptions.RemoveEmptyEntries))
{
if (word.ToUpper().Contains("KÖP") || word.ToUpper().Contains("FÖRSÄLJ"))
{
trans.TransactionType = word;
break;
}
}
}
if (line.StartsWith("Genomsnittlig"))
{
var tmpList = line.Split(" ", StringSplitOptions.RemoveEmptyEntries);
trans.StockPrice = decimal.Parse(tmpList[tmpList.Length - 1]);
}
if (line.StartsWith("Handelstidpunkt"))
{
var time = line.Split(" ", StringSplitOptions.RemoveEmptyEntries)[2];
if (time.Contains("."))
{
var hours = int.Parse(time.Split(".")[0]);
var minutes = int.Parse(time.Split(".")[1]);
trans.TransactionDate = trans.TransactionDate.AddHours(hours).AddMinutes(minutes);
}
}
}
try
{
await _transactionNotes.AddAsync(trans);
txtFound.Text = string.Empty;
}
catch (DbUpdateException ex) when (ex.InnerException is SqliteException sqlite && sqlite.SqliteErrorCode == 19)
{
MessageBox.Show("Den här transaktionen finns redan.");
}
catch (Exception ex)
{
MessageBox.Show("Fel vid sparande till databas: " + ex.Message);
}
Selected.Clear();
await LoadBusinessAsync();
}
private async Task LoadBusinessAsync()
{
var transactions = await _transactionNotes.GetAllAsync();
lwTransactions.Items.Clear();
foreach (var t in transactions)
{
var item = new ListViewItem(t.StockCode);
item.SubItems.Add(t.TransactionType);
item.SubItems.Add(t.TransactionDate.ToString());
item.SubItems.Add(t.StockQuantity.ToString());
item.SubItems.Add(t.StockPrice.ToString());
item.SubItems.Add(t.TotalAmount.ToString());
item.SubItems.Add(t.Commission.ToString());
item.SubItems.Add(t.AmountToPay.ToString());
lwTransactions.Items.Add(item);
}
}
private void btnClose_Click(object sender, EventArgs e)
{
this.Close();
lblFileName.Text = ofdFiler.FileName;
}
}
}
private void lblFileName_TextChanged(object sender, EventArgs e)
{
if (lblFileName.Text.Length > 5 && lblFileName.Text.EndsWith(".pdf"))
{
_pdfOpener.OpenPdf(lblFileName.Text);
_pdfSidor = _pdfOpener.PdfSidor;
btnAnalysera.Enabled = true;
}
else
{
btnAnalysera.Enabled = false;
}
}
private void btnAnalysera_Click(object sender, EventArgs e)
{
string PdfResult=string.Empty;
if (lblFileName.Text.ToLower().Contains("avanza"))
{
//var Result = PdfPigLetterExtractor.ExtractLines(_pdfSidor);
//SelectPdfLetterData(Result);
}
else
{
PdfResult = _pdfFormatter.ExtractFormattedTextUsingWords(_pdfSidor);
SelectPdfData(PdfResult);
}
btnAnalysera.Enabled = false;
lblFileName.Text = string.Empty;
}
private void SelectPdfData(string PdfResult)
{
txtFound.Text = string.Empty;
List<string> result = PdfResult.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries).ToList();
//List<string> selected = new();
foreach (var line in result)
{
if (!string.IsNullOrWhiteSpace(line) && line.StartsWith("Affärsdag")
|| !string.IsNullOrWhiteSpace(line) && line.StartsWith("Handelstidpunkt")
|| !string.IsNullOrWhiteSpace(line) && line.StartsWith("Värdepapper")
|| !string.IsNullOrWhiteSpace(line) && line.StartsWith("Kurs")
|| !string.IsNullOrWhiteSpace(line) && line.StartsWith("Genomsnittlig")
|| !string.IsNullOrWhiteSpace(line) && line.StartsWith("Antal")
|| !string.IsNullOrWhiteSpace(line) && line.StartsWith("Köpeskilling")
|| !string.IsNullOrWhiteSpace(line) && line.StartsWith("Courtage")
|| !string.IsNullOrWhiteSpace(line) && line.StartsWith("Belopp")
|| !string.IsNullOrWhiteSpace(line) && line.StartsWith("Vi bekräftar")
)
{
Selected.Add(line);
txtFound.Text += line + Environment.NewLine;
txtFound.Refresh();
}
}
}
private async void btnSaveToDb_Click(object sender, EventArgs e)
{
TransactionNote trans = new();
foreach (var line in Selected)
{
if (line.StartsWith("Affärsdag"))
{
trans.TransactionDate = DateTime.Parse(line.Split(" ", StringSplitOptions.RemoveEmptyEntries)[1]);
}
if (line.StartsWith("Värdepapper"))
{
var templist = line.Split(" ", StringSplitOptions.RemoveEmptyEntries);
if (templist.Length > 2)
trans.StockCode = templist[1] + " " + templist[2];
else
trans.StockCode = templist[1];
}
if (line.StartsWith("Kurs"))
{
trans.StockPrice = decimal.Parse(line.Split(" ", StringSplitOptions.RemoveEmptyEntries)[2]);
}
if (line.StartsWith("Antal"))
{
trans.StockQuantity = int.Parse(line.Split(" ", StringSplitOptions.RemoveEmptyEntries)[1]);
}
if (line.StartsWith("Köpeskilling"))
{
var templist = line.Split(" ", StringSplitOptions.RemoveEmptyEntries);
if (templist.Length > 3)
trans.TotalAmount = decimal.Parse(templist[2] + templist[3]);
else
trans.TotalAmount = decimal.Parse(templist[2]);
}
if (line.StartsWith("Courtage"))
{
var tmpList = line.Split(" ", StringSplitOptions.RemoveEmptyEntries);
trans.Commission = decimal.Parse(tmpList[tmpList.Length - 1]);
}
if (line.StartsWith("Belopp"))
{
var templist = line.Split(" ", StringSplitOptions.RemoveEmptyEntries);
if (templist.Length > 5)
trans.AmountToPay = decimal.Parse(templist[4] + templist[5]);
else
trans.AmountToPay = decimal.Parse(templist[4]);
}
if (line.StartsWith("Vi bekräftar"))
{
foreach (var word in line.Split(" ", StringSplitOptions.RemoveEmptyEntries))
{
if (word.ToUpper().Contains("KÖP") || word.ToUpper().Contains("FÖRSÄLJ"))
{
trans.TransactionType = word;
break;
}
}
}
if (line.StartsWith("Genomsnittlig"))
{
var tmpList = line.Split(" ", StringSplitOptions.RemoveEmptyEntries);
trans.StockPrice = decimal.Parse(tmpList[tmpList.Length - 1]);
}
if (line.StartsWith("Handelstidpunkt"))
{
var time = line.Split(" ", StringSplitOptions.RemoveEmptyEntries)[2];
if (time.Contains("."))
{
var hours = int.Parse(time.Split(".")[0]);
var minutes = int.Parse(time.Split(".")[1]);
trans.TransactionDate = trans.TransactionDate.AddHours(hours).AddMinutes(minutes);
}
}
}
try
{
await _transactionNotes.AddAsync(trans);
txtFound.Text = string.Empty;
}
catch (DbUpdateException ex) when (ex.InnerException is SqliteException sqlite && sqlite.SqliteErrorCode == 19)
{
MessageBox.Show("Den här transaktionen finns redan.");
}
catch (Exception ex)
{
MessageBox.Show("Fel vid sparande till databas: " + ex.Message);
}
Selected.Clear();
await LoadBusinessAsync();
}
private async Task LoadBusinessAsync()
{
var transactions = await _transactionNotes.GetAllAsync();
lwTransactions.Items.Clear();
foreach (var t in transactions)
{
var item = new ListViewItem(t.StockCode);
item.SubItems.Add(t.TransactionType);
item.SubItems.Add(t.TransactionDate.ToString());
item.SubItems.Add(t.StockQuantity.ToString());
item.SubItems.Add(t.StockPrice.ToString());
item.SubItems.Add(t.TotalAmount.ToString());
item.SubItems.Add(t.Commission.ToString());
item.SubItems.Add(t.AmountToPay.ToString());
lwTransactions.Items.Add(item);
}
}
private void btnClose_Click(object sender, EventArgs e)
{
this.Close();
}
}