MOD: Window behavior while resizing // new search logic due to vector embedding in postgre

This commit is contained in:
Luis Sander 2025-04-10 10:41:37 +02:00
parent ba44f03264
commit 0041872afd
2 changed files with 182 additions and 235 deletions

View File

@ -1,204 +1,124 @@
<mah:MetroWindow xmlns:mah="http://metro.mahapps.com/winfx/xaml/controls" <mah:MetroWindow xmlns:mah="http://metro.mahapps.com/winfx/xaml/controls"
x:Class="PrototypWPFHAG.SearchWindow" x:Class="PrototypWPFHAG.SearchWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:PrototypWPFHAG" xmlns:local="clr-namespace:PrototypWPFHAG"
Title="PDF-Verwaltung (Admin)" Height="600" Width="1000" Title="PDF-Verwaltung (Admin)" Height="600" Width="1000"
WindowTitleBrush="FireBrick" WindowTitleBrush="FireBrick"
Icon="pack://application:,,,/Images/databaseicon.png"> Icon="pack://application:,,,/Images/databaseicon.png"
<Grid> ResizeMode="CanResizeWithGrip">
<!-- 左侧垂直布局 --> <!-- GesamtLayout: Zwei Spalten linke Seite (jetzt 600px) und rechte Seite (flexibel) -->
<Grid.ColumnDefinitions> <Border BorderBrush="FireBrick" BorderThickness="3" Padding="0">
<ColumnDefinition Width="56.527"/> <Grid>
<ColumnDefinition Width="143.473"/> <Grid.ColumnDefinitions>
<!-- 左侧固定宽度 --> <!-- Breitere linke Seite: -->
<ColumnDefinition Width="*"/> <ColumnDefinition Width="500"/>
<!-- 右侧占满剩余空间 --> <ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!-- 左侧区域 --> <!-- Linke Seite -->
<Border <Border Grid.Column="0" Margin="10,10,0,10" Padding="20">
BorderBrush="FireBrick" <!-- Untergliederung: zwei Spalten (links: Upload/Steuerelemente, rechts: Suchergebnisse) -->
BorderThickness="3" Padding="20" <Grid>
Grid.ColumnSpan="3" <Grid.ColumnDefinitions>
/> <ColumnDefinition Width="250"/>
<ColumnDefinition Width="*"/>
<StackPanel </Grid.ColumnDefinitions>
Orientation="Vertical" <Grid.RowDefinitions>
Margin="10,10,772,10" <!-- Upload-/SuchSteuerelemente in mehreren Zeilen -->
Grid.ColumnSpan="3"> <RowDefinition Height="Auto"/>
<Grid <!-- Drag & Drop -->
Margin="0,0,0,10" <RowDefinition Height="Auto"/>
Height="85"> <!-- Upload-Button -->
<Grid.ColumnDefinitions> <RowDefinition Height="Auto"/>
<ColumnDefinition <!-- RadioButtons für Suchmodus -->
Width="*" <RowDefinition Height="Auto"/>
/> <!-- Suchtext und Suchen-Button -->
<ColumnDefinition <RowDefinition Height="Auto"/>
Width="Auto" <!-- Status & Progress -->
/> <RowDefinition Height="*"/>
</Grid.ColumnDefinitions> <!-- eventuell Füllraum -->
<Grid.RowDefinitions> <RowDefinition Height="Auto"/>
<RowDefinition <!-- Zurück-Button -->
Height="Auto" </Grid.RowDefinitions>
/>
<RowDefinition
Height="Auto"
/>
</Grid.RowDefinitions>
<Border <!-- Obere Zeile: Drag & Drop-Bereich -->
BorderBrush="Gray" <Border Grid.ColumnSpan="2" Height="75" BorderBrush="Gray" BorderThickness="1">
BorderThickness="1" <Grid Background="Transparent" AllowDrop="True"
Margin="5,10,0,-85" Grid.Row="1"> DragEnter="PdfDropCanvas_DragEnter" Drop="PdfDropCanvas_Drop" Margin="0,0,9,0">
<Canvas <!-- Hinweistext und ggf. Vorschau (zunächst unsichtbar) -->
x:Name="PdfDropCanvas" <TextBlock x:Name="DropHintText" Text="PDF hier rein ziehen"
Margin="5,0,0,-24" Foreground="Gray" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="5"/>
Grid.RowSpan="2" <StackPanel Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="5,30,0,0">
AllowDrop="True" <Image x:Name="PdfIcon" Source="pack://application:,,,/Images/pdf-icon.png"
Background="Transparent" Width="25" Height="25" Visibility="Collapsed"/>
DragEnter="PdfDropCanvas_DragEnter" <TextBlock x:Name="PdfFileNameText" Margin="10,0,0,0" Visibility="Collapsed"/>
Drop="PdfDropCanvas_Drop"> </StackPanel>
</Grid>
</Border>
<TextBlock <!-- Linke Spalte, 2. Zeile: PDF hochladen Button -->
x:Name="DropHintText" <Button Grid.Row="1" Grid.Column="0" Content="PDF hochladen"
Text="PDF hier rein ziehen" Width="235" Height="30" Background="LightGreen" Click="UploadButton_Click"
HorizontalAlignment="Center" Margin="0,10,0,0"/>
VerticalAlignment="Center"
Foreground="Gray"
/>
<!-- Vorschau-Elemente (unsichtbar, bis eine Datei abgelegt wird) --> <!-- Linke Spalte, 3. Zeile: RadioButtons für Suchmodus -->
<Image <StackPanel Grid.Row="2" Grid.Column="0" Orientation="Horizontal" Margin="0,10,0,0">
x:Name="PdfIcon" <RadioButton x:Name="SearchByIdRadio" Content="ID" IsChecked="True" Margin="0,0,10,0"/>
Source="pack://application:,,,/Images/pdf-icon.png" <RadioButton x:Name="SearchByTextRadio" Content="Text"/>
Width="25" Height="25" </StackPanel>
Margin="0,5,0,0"
Visibility="Collapsed"
/>
<TextBlock
x:Name="PdfFileNameText"
Margin="40,10,0,0"
Visibility="Collapsed"
/>
</Canvas>
</Border>
<!-- Linke Spalte, 4. Zeile: Suchfeld und Suchbutton -->
<StackPanel Grid.Row="3" Grid.Column="0" Orientation="Vertical" Margin="0,10,0,0">
<TextBox x:Name="SearchTextBox" Width="232" />
<Button Content="Suchen" x:Name="SearchButton" Click="SearchButton_Click"
Width="118" Margin="0,5,0,0"/>
</StackPanel>
</Grid> <!-- Linke Spalte, 5. Zeile: Upload-Status und Fortschrittsanzeige -->
<StackPanel Grid.Row="4" Grid.Column="0" Orientation="Vertical" Margin="0,10,0,0">
<TextBlock x:Name="UploadStatusText" TextAlignment="Center" Height="27"/>
<ProgressBar x:Name="UploadProgressBar" Height="10" Minimum="0" Maximum="100"
Visibility="Collapsed" Margin="0,5,0,0"/>
</StackPanel>
<!-- PDFField --> <!-- Rechte Spalte: Suchergebnisse ListBox -->
<Grid Margin="0,0,0,10"> <ListBox Grid.Row="1" Grid.RowSpan="5" Grid.Column="1"
<Grid.ColumnDefinitions> x:Name="SearchResultsListBox" DisplayMemberPath="DocumentName"
<ColumnDefinition ItemsSource="{Binding}" SelectionMode="Extended"
Width="*" SelectionChanged="SearchResultsListBox_SelectionChanged" Margin="10,10,0,0"/>
/>
<ColumnDefinition
Width="Auto"
/>
</Grid.ColumnDefinitions>
<Button
Content="PDF hochladen"
Width="auto" Height="30"
Margin="5,0,0,0"
Background="LightGreen"
Click="UploadButton_Click"
/>
</Grid> <!-- Linke Spalte, untere Zeile: Button "Zurück zur Anmeldung" -->
<Button Grid.Row="6" Grid.Column="0" Content="Zurück zur Anmeldung"
Click="BackToLogIn_Click" Height="30" Background="#ffd64f" Margin="0,10,0,0"/>
</Grid>
</Border>
<StackPanel Orientation="Horizontal" Margin="5,10"> <!-- Rechte Seite -->
<RadioButton x:Name="SearchByIdRadio" Content="ID" IsChecked="True"/> <Border Grid.Column="1" Margin="43,10,10,10" Padding="10">
<RadioButton x:Name="SearchByTextRadio" Content="Text" Margin="10,0"/> <Grid>
</StackPanel> <Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<TextBox <!-- Überschrift -->
x:Name="SearchTextBox" <RowDefinition Height="*"/>
Margin="5,0,0,0" <!-- PDF-Inhalt -->
/> <RowDefinition Height="Auto"/>
<!-- Lösch-Button -->
<Button </Grid.RowDefinitions>
Content="Suchen" <Label Grid.Row="0" Content="PDF-Inhalt:" Margin="0,0,0,5"/>
x:Name="SearchButton" <!-- Für den Inhalt wird ein TextBox genutzt, der bei Bedarf automatisch Scrollbars anzeigt -->
Click="SearchButton_Click" <TextBox Grid.Row="1" x:Name="ContentTextBox"
Margin="5,10,0,10" Text="{Binding SelectedDocument.Content}"
/> IsReadOnly="True" TextWrapping="Wrap" AcceptsReturn="True"
VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto"/>
<!-- ListField --> <Button Grid.Row="2" Content="Löschen" Width="100" Height="30"
<ListBox Background="Firebrick" Click="DeleteButton_Click"
x:Name="SearchResultsListBox" HorizontalAlignment="Center" Margin="0,10,0,0"/>
DisplayMemberPath="Filename" </Grid>
Margin="5,0,0,0" </Border>
ItemsSource="{Binding}"
SelectionMode="Extended"
SelectionChanged="SearchResultsListBox_SelectionChanged"
/>
<TextBlock
x:Name="UploadStatusText"
TextAlignment="Center" Margin="0,35,0,35" Height="27"
/>
<ProgressBar
x:Name="UploadProgressBar"
Height="10"
Minimum="0" Maximum="100"
Visibility="Collapsed"
Margin="0,20,0,0"
/>
<Button
Content="Zurück zur Anmeldung"
Click="BackToLogIn_Click"
Margin="5,10,0,0"
Height="30"
Background="#ffd64f"
/>
</StackPanel>
<!-- 右侧区域 -->
<Grid
Grid.Column="2"
Margin="60,10,10,10">
<Label
Content="PDF-Inhalt:"
HorizontalAlignment="Left"
Margin="0,2,0,8"
/>
<ScrollViewer
x:Name="ContentScrollViewer"
Margin="0,0,0,35"
VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Auto">
<TextBox
x:Name="ContentTextBox"
Text="{Binding SelectedDocument.Content}"
IsReadOnly="True"
TextWrapping="Wrap"
AcceptsReturn="True"
VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Auto"
Margin="5,25, 0, 35"
Height="Auto"
MinHeight="494"
/>
</ScrollViewer>
<!-- 删除按钮 -->
<Button
Content="Löschen"
Width="100"
Height="30"
HorizontalAlignment="Right"
VerticalAlignment="Bottom"
Background="Firebrick"
Click="DeleteButton_Click"
/>
</Grid> </Grid>
</Grid> </Border>
</mah:MetroWindow> </mah:MetroWindow>

View File

@ -1,14 +1,17 @@
using MahApps.Metro.Controls; using MahApps.Metro.Controls;
using System.ComponentModel;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Net; using System.Net;
using System.Net.Http; using System.Net.Http;
using System.Runtime.CompilerServices;
using System.Text; using System.Text;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using System.Web; using System.Web;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Documents;
namespace PrototypWPFHAG namespace PrototypWPFHAG
@ -60,7 +63,7 @@ namespace PrototypWPFHAG
} }
else else
{ {
await SearchByTextAsync(); await SearchBySimilarityAsync();
} }
} }
catch (Exception ex) catch (Exception ex)
@ -90,7 +93,6 @@ namespace PrototypWPFHAG
var json = await response.Content.ReadAsStringAsync(); var json = await response.Content.ReadAsStringAsync();
var result = JsonSerializer.Deserialize<JsonElement>(json); var result = JsonSerializer.Deserialize<JsonElement>(json);
// Extrahiere das Array "documents"
var documents = result.GetProperty("documents"); var documents = result.GetProperty("documents");
if (documents.GetArrayLength() == 0) if (documents.GetArrayLength() == 0)
{ {
@ -98,22 +100,24 @@ namespace PrototypWPFHAG
return; return;
} }
// Nimm das erste Element des Arrays
var document = documents[0]; var document = documents[0];
// UI aktualisieren // Korrigierte UI-Aktualisierung
await Dispatcher.InvokeAsync(() => await Dispatcher.InvokeAsync(() =>
{ {
SearchResultsListBox.ItemsSource = new List<SearchResult> SearchResultsListBox.ItemsSource = new List<DocumentDetail> // Änderung von SearchResult zu DocumentDetail
{ {
new SearchResult new DocumentDetail
{ {
Id = document.GetProperty("id").GetInt32(), Id = document.GetProperty("id").GetInt32(),
Filename = document.GetProperty("filename").GetString(), DocumentName = document.GetProperty("document_name").GetString(), // Korrekter Property-Name
Content = document.GetProperty("content").GetString() Content = document.GetProperty("content").GetString()
} }
}; };
ContentTextBox.Text = document.GetProperty("content").GetString(); ContentTextBox.Text = document.GetProperty("content").GetString();
// Wichtig: DisplayMemberPath korrekt setzen
SearchResultsListBox.DisplayMemberPath = "DocumentName";
}); });
} }
catch (Exception ex) catch (Exception ex)
@ -121,6 +125,7 @@ namespace PrototypWPFHAG
MessageBox.Show($"Fehler: {ex.Message}"); MessageBox.Show($"Fehler: {ex.Message}");
} }
} }
private async Task SearchByTextAsync() private async Task SearchByTextAsync()
{ {
try try
@ -137,7 +142,7 @@ namespace PrototypWPFHAG
MessageBox.Show($"Gefundene Dokumente: {documents?.Count}", "Debug"); MessageBox.Show($"Gefundene Dokumente: {documents?.Count}", "Debug");
SearchResultsListBox.ItemsSource = documents; SearchResultsListBox.ItemsSource = documents;
SearchResultsListBox.DisplayMemberPath = "Filename"; SearchResultsListBox.DisplayMemberPath = "name";
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -145,7 +150,27 @@ namespace PrototypWPFHAG
} }
} }
private async Task SearchBySimilarityAsync()
{
try
{
var encodedQuery = Uri.EscapeDataString(SearchTextBox.Text);
var response = await _httpClient.GetAsync($"/documents/similarity?query={encodedQuery}");
var json = await response.Content.ReadAsStringAsync();
var result = JsonSerializer.Deserialize<ApiResponse>(json, new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
});
SearchResultsListBox.ItemsSource = result?.Documents;
SearchResultsListBox.DisplayMemberPath = "DocumentName";
}
catch (Exception ex)
{
MessageBox.Show($"Fehler: {ex.Message}");
}
}
private void PdfDropCanvas_DragEnter(object sender, DragEventArgs e) private void PdfDropCanvas_DragEnter(object sender, DragEventArgs e)
{ {
@ -192,6 +217,9 @@ namespace PrototypWPFHAG
[JsonPropertyName("document")] [JsonPropertyName("document")]
public DocumentDetail Document { get; set; } public DocumentDetail Document { get; set; }
[JsonPropertyName("documents")]
public List<DocumentDetail> Documents { get; set; }
} }
public class DocumentDetail public class DocumentDetail
@ -199,11 +227,33 @@ namespace PrototypWPFHAG
[JsonPropertyName("id")] [JsonPropertyName("id")]
public int Id { get; set; } public int Id { get; set; }
[JsonPropertyName("filename")] [JsonPropertyName("document_name")]
public string Filename { get; set; } public string DocumentName { get; set; }
[JsonPropertyName("content")] [JsonPropertyName("content")]
public string Content { get; set; } public string Content { get; set; }
[JsonPropertyName("distance")]
public double Distance { get; set; }
// Für die TextBox-Bindung
private bool _isSelected;
public bool IsSelected
{
get => _isSelected;
set
{
_isSelected = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
} }
public class ApiError public class ApiError
{ {
@ -303,7 +353,7 @@ namespace PrototypWPFHAG
var deletedIds = new List<int>(); var deletedIds = new List<int>();
var errorIds = new List<int>(); var errorIds = new List<int>();
foreach (var item in SearchResultsListBox.SelectedItems.Cast<SearchResult>().ToList()) foreach (var item in SearchResultsListBox.SelectedItems.Cast<DocumentDetail>().ToList())
{ {
try try
{ {
@ -334,7 +384,7 @@ namespace PrototypWPFHAG
} }
else else
{ {
await SearchByTextAsync(); // Neu laden der Textsuche await SearchBySimilarityAsync(); // Neu laden der Textsuche
} }
// Feedback an Benutzer // Feedback an Benutzer
@ -352,40 +402,17 @@ namespace PrototypWPFHAG
private async void SearchResultsListBox_SelectionChanged(object sender, SelectionChangedEventArgs e) private async void SearchResultsListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{ {
if (SearchResultsListBox.SelectedItem is SearchResult selectedDocument) if (SearchResultsListBox.SelectedItem is DocumentDetail selected)
{ {
try try
{ {
var response = await _httpClient.GetAsync($"/documents/by-id/{selectedDocument.Id}"); var response = await _httpClient.GetAsync($"/documents/{selected.Id}/markdown");
var json = await response.Content.ReadAsStringAsync(); var content = await response.Content.ReadAsStringAsync();
var result = JsonSerializer.Deserialize<JsonElement>(json); var result = JsonSerializer.Deserialize<JsonElement>(content);
// Sicherstellen, dass das "documents" Array existiert ContentTextBox.Text = result.GetProperty("document")
if (!result.TryGetProperty("documents", out var documents)) .GetProperty("content")
{ .GetString();
ContentTextBox.Text = "Ungültiges Antwortformat";
return;
}
// Sicherstellen, dass das Array mindestens ein Element hat
if (documents.GetArrayLength() == 0)
{
ContentTextBox.Text = "Dokument nicht gefunden";
return;
}
var document = documents[0];
// Sicherstellen, dass alle benötigten Felder existieren
if (!document.TryGetProperty("content", out var contentProp))
{
ContentTextBox.Text = "Dokumentinhalt fehlt";
return;
}
ContentTextBox.Text = contentProp.GetString();
Debug.WriteLine($"API-Antwort: {json}");
} }
catch (Exception ex) catch (Exception ex)
{ {