Compare commits

...

8 Commits

Author SHA1 Message Date
247c913051 redesign vom bilder generieren 2023-10-11 18:31:25 +02:00
80c7dca613 ein paar kleinigkeiten (schöner) in der nav bar gemacht + social links hinzugefügt 2023-10-11 18:10:37 +02:00
e4821d2d7e Refactoring 2023-10-11 15:11:29 +02:00
9c298c6e76 Gallerie zumindest etwas schicker gemacht 2023-10-10 20:21:06 +02:00
150cb24ce8 Merge branch 'Datenbank' 2023-10-10 20:20:02 +02:00
e6a2db0753 Gallery verwendet Datenbank
- Menü: Fixed typo in Gallerie
- Datenbanken-Funktionen dokumentiert
2023-10-10 18:56:05 +02:00
1b28481b5d Daten in Wunsch- und Bilddatenbanken speichern 2023-10-10 18:18:17 +02:00
d0d9cb8bb3 Start of Datenbank 2023-10-10 16:21:00 +02:00
31 changed files with 812 additions and 481 deletions

View File

@ -0,0 +1,48 @@
using DataAccess.DbAccess;
using DataAccess.Models;
namespace DataAccess.Data;
/// <summary>
/// Ermöglicht den Zugriff auf die BildInfo Datenbank.
/// </summary>
public class BildInfoData
{
private readonly ISqlDataAccess _db;
public BildInfoData(ISqlDataAccess db)
{
_db = db;
}
/// <summary>
/// Fügt die gegebene BildInfo zur Datenbank hinzu und aktualisiert das <see cref="BildInfoModel.Id"/>-Feld mit dem entsprechenden Wert.
/// </summary>
/// <param name="bildInfo">Die BildInfo, die zur Datenbank hinzugefügt werden soll.</param>
public async Task AddBildInfoAsync(BildInfoModel bildInfo)
{
var id = await _db.LoadData<int, BildInfoModel>("dbo.spBildInfo_Insert", bildInfo);
bildInfo.Id = id.Single();
}
/// <summary>
/// Aktualisiert das Dateiname-Feld der übergebenen BildInfo in der Datenbank.
/// </summary>
/// <param name="bildInfo">Die BildInfo deren Dateiname aktualisiert werden soll.</param>
public Task UpdateBildInfoDateinameAsync(BildInfoModel bildInfo)
{
return _db.SaveData("dbo.spBildInfo_UpdateFileName",
new {
Id = bildInfo.Id,
Dateiname = bildInfo.Dateiname,
});
}
/// <summary>
/// Gibt alle Bild Infos der Datenbank zurück.
/// </summary>
public Task<IEnumerable<BildInfoModel>> GetAllBildInfosAsync()
{
return _db.LoadData<BildInfoModel, dynamic>("dbo.spBildInfo_GetAll", new { });
}
}

View File

@ -0,0 +1,36 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using DataAccess.DbAccess;
using DataAccess.Models;
namespace DataAccess.Data;
public class WunschInfoData
{
private readonly ISqlDataAccess _db;
public WunschInfoData(ISqlDataAccess db)
{
_db = db;
}
public async Task AddWunschInfoAsync(WunschInfoModel wunschInfo)
{
var id = await _db.LoadData<int, WunschInfoModel>("dbo.spWunschInfo_Insert", wunschInfo);
wunschInfo.Id = id.Single();
}
/// <summary>
/// Gibt die WunschInfo mit der gegebenen Id zurück.
/// </summary>
/// <param name="wunschId">Die Id der zu ladenen WunschInfo</param>
/// <returns>Die WunschInfo mit der gegebenen Id</returns>
public async Task<WunschInfoModel> GetWunschInfoAsync(int wunschId)
{
var wunschInfo = await _db.LoadData<WunschInfoModel, dynamic>("dbo.spWunschInfo_Get", new { Id = wunschId });
return wunschInfo.Single();
}
}

View File

@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Dapper" Version="2.1.4" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="7.0.0" />
<PackageReference Include="System.Data.SqlClient" Version="4.8.5" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,26 @@
namespace DataAccess.DbAccess;
/// <summary>
/// Bietet lesenden und schreibenden Zugriff auf eine Datenbank.
/// </summary>
public interface ISqlDataAccess
{
/// <summary>
/// Führt die angegebene Stored Procedure aus. Diese Prozedur nimmt Parameter des Typs <see cref="TParameter"/> engegen und gibt ein Enumerable des Typs <see cref="TResult"/> zurück.
/// </summary>
/// <typeparam name="TResult">Der Typ der Parameter</typeparam>
/// <typeparam name="TParameter">Der Typ der Rückgabewerte</typeparam>
/// <param name="storedProcedure">Der Name der Prozedur</param>
/// <param name="parameters">Die Parameter für die Prozedur.</param>
/// <param name="connectionId">Die optionale Id des zu verwendenen Connection Strings.</param>
Task<IEnumerable<TResult>> LoadData<TResult, TParameter>(string storedProcedure, TParameter parameters, string connectionId = "Default");
/// <summary>
/// Führt die angegebene Stored Procedure aus. Diese Prozedur nimmt Parameter des Typs <see cref="TParameter"/> engegen und gibt nichts zurück.
/// </summary>
/// <typeparam name="TParameter">Der Typ der Parameter</typeparam>
/// <param name="storedProcedure">Der Name der Prozedur</param>
/// <param name="parameters">Die Parameter für die Prozedur.</param>
/// <param name="connectionId">Die optionale Id des zu verwendenen Connection Strings.</param>
Task SaveData<TParameter>(string storedProcedure, TParameter parameters, string connectionId = "Default");
}

View File

@ -0,0 +1,33 @@
using Microsoft.Extensions.Configuration;
using System.Data.SqlClient;
using System.Data;
using Dapper;
namespace DataAccess.DbAccess;
/// <summary>
/// Bietet lesenden und schreibenden Zugriff auf eine Datenbank.
/// </summary>
public class SqlDataAccess : ISqlDataAccess
{
private readonly IConfiguration _config;
public SqlDataAccess(IConfiguration config)
{
_config = config;
}
public async Task<IEnumerable<TResult>> LoadData<TResult, TParameter>(string storedProcedure, TParameter parameters, string connectionId = "Default")
{
using IDbConnection connection = new SqlConnection(_config.GetConnectionString(connectionId));
return await connection.QueryAsync<TResult>(storedProcedure, parameters, commandType: CommandType.StoredProcedure);
}
public async Task SaveData<TParameter>(string storedProcedure, TParameter parameters, string connectionId = "Default")
{
using IDbConnection connection = new SqlConnection(_config.GetConnectionString(connectionId));
await connection.ExecuteAsync(storedProcedure, parameters, commandType: CommandType.StoredProcedure);
}
}

View File

@ -0,0 +1,10 @@
namespace DataAccess.Models;
public class BildInfoModel
{
public int Id { get; set; }
public DateTime Datum { get; set; }
public string Dateiname { get; set; }
public string ImageModel { get; set; }
public int WunschId { get; set; }
}

View File

@ -0,0 +1,11 @@
namespace DataAccess.Models;
public class WunschInfoModel
{
public int Id { get; set; }
public string BildPrompt { get; set; }
public string Wunsch { get; set; }
public string BildBeschreibung { get; set; }
public DateTime Datum { get; set; }
public string GPTModel { get; set; }
}

View File

@ -5,6 +5,10 @@ VisualStudioVersion = 17.6.33829.357
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "KIKunstKirstenKlöckner", "KIKunstKirstenKlöckner\KIKunstKirstenKlöckner.csproj", "{0085541E-50D4-42A5-9BFD-6CE402FB8B26}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "KIKunstKirstenKlöckner", "KIKunstKirstenKlöckner\KIKunstKirstenKlöckner.csproj", "{0085541E-50D4-42A5-9BFD-6CE402FB8B26}"
EndProject EndProject
Project("{00D1A9C2-B5F0-4AF3-8072-F6C62B433612}") = "KiKunstDatenbank", "KiKunstDatenbank\KiKunstDatenbank.sqlproj", "{A19CD19A-FE5B-4D4E-896B-DCC43B45F734}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DataAccess", "DataAccess\DataAccess.csproj", "{0880FD07-236B-42C1-9CA3-2A6F695A623C}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@ -15,6 +19,16 @@ Global
{0085541E-50D4-42A5-9BFD-6CE402FB8B26}.Debug|Any CPU.Build.0 = Debug|Any CPU {0085541E-50D4-42A5-9BFD-6CE402FB8B26}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0085541E-50D4-42A5-9BFD-6CE402FB8B26}.Release|Any CPU.ActiveCfg = Release|Any CPU {0085541E-50D4-42A5-9BFD-6CE402FB8B26}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0085541E-50D4-42A5-9BFD-6CE402FB8B26}.Release|Any CPU.Build.0 = Release|Any CPU {0085541E-50D4-42A5-9BFD-6CE402FB8B26}.Release|Any CPU.Build.0 = Release|Any CPU
{A19CD19A-FE5B-4D4E-896B-DCC43B45F734}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A19CD19A-FE5B-4D4E-896B-DCC43B45F734}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A19CD19A-FE5B-4D4E-896B-DCC43B45F734}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
{A19CD19A-FE5B-4D4E-896B-DCC43B45F734}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A19CD19A-FE5B-4D4E-896B-DCC43B45F734}.Release|Any CPU.Build.0 = Release|Any CPU
{A19CD19A-FE5B-4D4E-896B-DCC43B45F734}.Release|Any CPU.Deploy.0 = Release|Any CPU
{0880FD07-236B-42C1-9CA3-2A6F695A623C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0880FD07-236B-42C1-9CA3-2A6F695A623C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0880FD07-236B-42C1-9CA3-2A6F695A623C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0880FD07-236B-42C1-9CA3-2A6F695A623C}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

View File

@ -0,0 +1,12 @@
{
"version": 1,
"isRoot": true,
"tools": {
"dotnet-ef": {
"version": "7.0.9",
"commands": [
"dotnet-ef"
]
}
}
}

View File

@ -0,0 +1,16 @@
namespace KIKunstKirstenKlöckner.Data;
/// <summary>
/// Enthält Daten für die AiArt
/// </summary>
public class AiArtPageData
{
private readonly ImageGenerator _imageGenerator;
public ImageGenerator ImageGenerator => _imageGenerator;
public AiArtPageData(ImageGenerator imageGenerator)
{
_imageGenerator = imageGenerator;
}
}

View File

@ -0,0 +1,6 @@
namespace KIKunstKirstenKlöckner.Data;
public class ChatGPT
{
}

View File

@ -0,0 +1,105 @@
using DataAccess.Data;
using DataAccess.Models;
using Radzen;
using System.Diagnostics;
namespace KIKunstKirstenKlöckner.Data;
public class ImageGenerator
{
private readonly HttpClient _client = new();
private readonly BildInfoData _bildInfoData;
public ImageGenerator(IConfiguration config, BildInfoData bildInfoData)
{
_bildInfoData = bildInfoData;
string? inferenceApiKey = config.GetValue<string>("API:HF_Inference");
_client.DefaultRequestHeaders.Clear();
_client.DefaultRequestHeaders.Add("Authorization", $"Bearer {inferenceApiKey}");
}
/// <summary>
/// Geneiert das Bild für den aktuellen <see cref="_imagePrompt"/>
/// </summary>
public async Task<string?> GenerateImageAsync(string prompt, string negativePromt, int? width, int? height, WunschInfoModel wunschInfo, bool isRetry = false)
{
var postData = new
{
inputs = prompt,
parameters = new
{
negative_prompt = negativePromt, //"photorealistic, highly detailed, 8K, portrait",
width = width,
height = height
},
options = new
{
// Cache deaktivieren, damit Huggingface für den selben Prompt unterschiedliche Ergebnisse liefert
use_cache = false,
// Erst wenn wir bereits in einem retry sind warten wir implizit auf das Model. (ignoriert quasi 503-Fehler)
wait_for_model = true
}
};
JsonContent content = JsonContent.Create(postData);
try
{
//const string modelName = "Nacken/kkkk-sdxl-5000";
const string modelName = "Nacken/kkk-sdxl-18000";
var inferenceModelUrl = $"https://api-inference.huggingface.co/models/{modelName}";
var response = await _client.PostAsync(inferenceModelUrl, content);
if (response?.IsSuccessStatusCode == true)
{
await using Stream imageStream = await response.Content.ReadAsStreamAsync();
using Image image = await Image.LoadAsync(imageStream);
DateTime imageDate = DateTime.Now;
BildInfoModel bildInfo = new()
{
Dateiname = "PlaceHolder",
Datum = imageDate,
ImageModel = modelName,
WunschId = wunschInfo.Id
};
await _bildInfoData.AddBildInfoAsync(bildInfo);
string imgUrl = $"generated_images/Image_{bildInfo.Id}.jpg";
string mapPath = $"./wwwroot/{imgUrl}";
await image.SaveAsJpegAsync(mapPath);
bildInfo.Dateiname = imgUrl;
await _bildInfoData.UpdateBildInfoDateinameAsync(bildInfo);
return imgUrl;
}
else
{
Console.WriteLine($"Image conversion failed: {response}");
if (Debugger.IsAttached)
Debugger.Break();
return null;
}
}
catch (Exception exception)
{
Console.WriteLine($"Image request failed: {exception}");
if (Debugger.IsAttached)
Debugger.Break();
return null;
}
}
}

View File

@ -20,4 +20,8 @@
<PackageReference Include="SixLabors.ImageSharp" Version="3.0.1" /> <PackageReference Include="SixLabors.ImageSharp" Version="3.0.1" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<ProjectReference Include="..\DataAccess\DataAccess.csproj" />
</ItemGroup>
</Project> </Project>

View File

@ -3,70 +3,85 @@
@using OpenAI_API @using OpenAI_API
@using OpenAI_API.Chat @using OpenAI_API.Chat
@using OpenAI_API.Models @using OpenAI_API.Models
@using System.Diagnostics @using DataAccess.Data
@using DataAccess.Models
@using KIKunstKirstenKlöckner.Data
@inject IConfiguration Config @inject IConfiguration Config
@inject TooltipService TooltipService @inject TooltipService TooltipService
@inject NotificationService NotificationService
@inject DialogService DialogService @inject DialogService DialogService
@* @inject AiArtPageData AiArtData; *@
@inject WunschInfoData WunschInfoData;
@inject ImageGenerator ImageGenerator;
<PageTitle>AiArt</PageTitle> <PageTitle>AiArt</PageTitle>
<section class="about_section layout_padding" style="background-image: url('images/5KeineAngstvorFehlern2014.jpeg'); background-size: cover; background-repeat: no-repeat; background-blend-mode:lighten"> <section class="about_section layout_padding" style="background-image: url('images/5KeineAngstvorFehlern2014.jpeg'); background-size: cover; background-repeat: no-repeat; background-blend-mode:lighten">
<div class="container"> <div class="container">
<RadzenStack Orientation="Orientation.Vertical" AlignItems="AlignItems.Center"> <RadzenStack Orientation="Orientation.Vertical" AlignItems="AlignItems.Center" Style="min-height:800px">
<h1>Wunschbilder von KI nur für dich</h1>
<RadzenText TextStyle="TextStyle.H2">Nenne uns deinen Wunsch:</RadzenText>
<RadzenTextBox @bind-Value=@request Placeholder="Dein Wunsch"/>
<RadzenPanel AllowCollapse="true" Style="width: 500px;" Text="Mehr Optionen"> <RadzenText TextStyle="TextStyle.H2">Dein Wunsch:</RadzenText>
<ChildContent>
<RadzenCard class="rz-mt-4">
<RadzenStack Orientation="Orientation.Horizontal" <RadzenStack Orientation="Orientation.Horizontal" AlignItems="AlignItems.Center">
MouseEnter="@(args => ShowTemperatureTooltip(args))" <RadzenTextBox @bind-Value=@request Placeholder="..." />
MouseLeave="TooltipService.Close" <RadzenButton Visible=@_buttonVisible Click=@(async ()=> await GenerateImages(true))>Generieren</RadzenButton>
AlignItems="AlignItems.Center" Wrap="FlexWrap.Wrap"> </RadzenStack>
<RadzenText>Temperature:</RadzenText>
<RadzenSlider @bind-Value=@temperature TValue="float"
Step="0.1" Min="0.0m" Max="2.0m">
</RadzenSlider> <!--
<RadzenText>@temperature</RadzenText> <RadzenPanel AllowCollapse="true" Style="width: 500px;" Text="Mehr Optionen">
</RadzenStack> <ChildContent>
<RadzenStack Orientation="Orientation.Horizontal" <RadzenCard class="rz-mt-4">
AlignItems="AlignItems.Center" Wrap="FlexWrap.Wrap"> <RadzenStack Orientation="Orientation.Horizontal"
<RadzenText>Resolution:</RadzenText> MouseEnter="@(args => ShowTemperatureTooltip(args))"
<RadzenStack Orientation="Orientation.Horizontal"> MouseLeave="TooltipService.Close"
<RadzenNumeric ShowUpDown = "false" TValue = "int?" @bind-Value=@width /> AlignItems="AlignItems.Center" Wrap="FlexWrap.Wrap">
x <RadzenText>Temperature:</RadzenText>
<RadzenNumeric ShowUpDown = "false" TValue = "int?" @bind-Value=@height /> <RadzenSlider @bind-Value=@_temperature TValue="float"
Step="0.1" Min="0.0m" Max="2.0m">
</RadzenSlider>
<RadzenText>@_temperature</RadzenText>
</RadzenStack> </RadzenStack>
</RadzenStack> <RadzenStack Orientation="Orientation.Horizontal"
AlignItems="AlignItems.Center" Wrap="FlexWrap.Wrap">
<RadzenText>Resolution:</RadzenText>
<RadzenStack Orientation="Orientation.Horizontal">
<RadzenNumeric ShowUpDown = "false" TValue = "int?" @bind-Value=@width />
x
<RadzenNumeric ShowUpDown = "false" TValue = "int?" @bind-Value=@height />
</RadzenStack>
</RadzenStack>
<RadzenStack Orientation="Orientation.Horizontal"
AlignItems="AlignItems.Center" Wrap="FlexWrap.Wrap">
<RadzenCheckBox @bind-Value=@_useGpt4>
Verwende Gibbidy 4
</RadzenCheckBox>
</RadzenStack>
</RadzenCard>
</ChildContent>
</RadzenPanel>
-->
<RadzenStack Orientation="Orientation.Vertical" Visible=@_promptVisible>
<RadzenCard class="rz-mt-4" Style="max-width: 500px">
<RadzenText>@_imagePrompt</RadzenText>
</RadzenCard> </RadzenCard>
</ChildContent>
</RadzenPanel>
<RadzenButton Visible=@_buttonVisible Click=@(async ()=> await DoStuff(true))>Generate</RadzenButton> <RadzenText TextStyle="TextStyle.H4">Interpretation deines Wunsches:</RadzenText>
<RadzenPanel AllowCollapse="true" Style="width: 500px;" Text="Zeige Prompt"> <RadzenCard class="rz-mt-4" Style="width: 800px;">
<ChildContent> <RadzenText>@_imageIdea</RadzenText>
<RadzenCard class="rz-mt-4">
<RadzenStack Orientation="Orientation.Horizontal"
MouseEnter="@(args => ShowTemperatureTooltip(args))"
MouseLeave="TooltipService.Close"
AlignItems="AlignItems.Center" Wrap="FlexWrap.Wrap">
<RadzenText>@_imagePromptEnglish</RadzenText>
</RadzenStack>
</RadzenCard> </RadzenCard>
</ChildContent> </RadzenStack>
</RadzenPanel>
<RadzenText TextStyle="TextStyle.H4">Die Idee, die gemalt wird:</RadzenText>
<RadzenCard class="rz-mt-4" Style="width: 800px;">
<RadzenText>@_imageDescription</RadzenText>
</RadzenCard>
@ -78,9 +93,9 @@
<RadzenText Visible=@_addonsVisible TextStyle="TextStyle.H2">Verändere hier dein Bild durch Worte:</RadzenText> <RadzenText Visible=@_addonsVisible TextStyle="TextStyle.H2">Verändere hier dein Bild durch Worte:</RadzenText>
<RadzenTextBox Visible=@_addonsVisible @bind-Value=@addons Placeholder="z.B. Mehr Farben" /> <RadzenTextBox Visible=@_addonsVisible @bind-Value=@addons Placeholder="z.B. Mehr Farben" />
<RadzenButton Visible=@_bothVisible Click=@(async ()=> await DoStuff(false))>Generate</RadzenButton> <RadzenButton Visible=@_bothVisible Click=@(async ()=> await GenerateImages(false))>Generate</RadzenButton>
<RadzenCard> <RadzenCard Visible="@_promptVisible">
<RadzenRow Style="width:24.5em" Gap="0.5rem" RowGap="0.5rem"> <RadzenRow Style="width:24.5em" Gap="0.5rem" RowGap="0.5rem">
<RadzenColumn Size="6"> <RadzenColumn Size="6">
<FlippingImage ImageUrl="@_imageUrls[0]" HideImage="false" <FlippingImage ImageUrl="@_imageUrls[0]" HideImage="false"
@ -110,13 +125,18 @@
</section> </section>
@code { @code {
/// <summary>
/// Wenn <see langword="true"/> wird GPT4 verwendet um die Idee zu interpretieren.
/// </summary>
private bool _useGpt4;
private int maxAddons = 2; private int maxAddons = 2;
private int amountOfAddons = 0; // wird später geändert private int amountOfAddons = 0; // wird später geändert
private string _imageDescriptionPrompt = "Zusätzlich zu dem Promt erkläre kurz auf deutsch, warum du dich für diese Umsetzung des Titels entschieden hast und gib zusätzlich eine Interpretation des Bildes an. Beginne diesen Teil immer mit \"Beschreibung: \". Zuletzt Beschreibe das Bild und die verbundenen Emotionen mit drei Worten und beginne den Teil mit \"Keywords: \".";
private bool _progressVisible = false; private bool _progressVisible = false;
private bool _buttonVisible = true; private bool _buttonVisible = true;
private bool _addonsVisible = false; private bool _addonsVisible = false;
private bool _bothVisible = false; private bool _bothVisible = false;
private bool _promptVisible = false;
public string BusyMessage { get; set; } = "Initial Message"; public string BusyMessage { get; set; } = "Initial Message";
@ -125,20 +145,8 @@
enum ImageState enum ImageState
{ {
//Hide = 0,
FadeOut, FadeOut,
FadeIn, FadeIn
//Show,
}
// Busy dialog from markup
async Task ShowBusyDialog()
{
await DialogService.OpenAsync("", ds =>
@<RadzenStack AlignItems="AlignItems.Center" Gap="2rem" Class="rz-p-12">
<RadzenProgressBarCircular ProgressBarStyle="ProgressBarStyle.Secondary" Value="100" ShowValue="false" Mode="ProgressBarMode.Indeterminate"/>
<RadzenText TextStyle="TextStyle.H6" Text=@BusyMessage></RadzenText>
</RadzenStack>, new DialogOptions() { ShowTitle = false, Style = "min-height:auto;min-width:auto;width:auto", CloseDialogOnEsc = false });
} }
async Task ShowImageDialog(string imageUrl) async Task ShowImageDialog(string imageUrl)
@ -149,51 +157,25 @@
</div>, new DialogOptions() { CloseDialogOnOverlayClick = true }); </div>, new DialogOptions() { CloseDialogOnOverlayClick = true });
} }
// Busy dialog from string
async Task BusyDialog(string message)
{
await DialogService.OpenAsync("", ds =>
{
RenderFragment content = b =>
{
b.OpenElement(0, "RadzenRow");
b.OpenElement(1, "RadzenColumn");
b.AddAttribute(2, "Size", "12");
b.AddContent(3, message);
b.CloseElement();
b.CloseElement();
};
return content;
}, new DialogOptions() { ShowTitle = false, Style = "min-height:auto;min-width:auto;width:auto", CloseDialogOnEsc = false });
}
void ShowTooltip(ElementReference elementReference, string text, TooltipOptions? options = null) => TooltipService.Open(elementReference, text, options); void ShowTooltip(ElementReference elementReference, string text, TooltipOptions? options = null) => TooltipService.Open(elementReference, text, options);
void ShowTemperatureTooltip(ElementReference elementReference) => TooltipService.Open(elementReference, ds => void ShowTemperatureTooltip(ElementReference elementReference) => TooltipService.Open(elementReference, ds =>
@<div> @<div>
Gibt an, wie <em>kreativ</em> ChatGPT sein soll.<br /> Gibt an, wie <em>kreativ</em> ChatGPT sein soll.<br/>
Ich glaube, eigentlich bedeutet es eher, wie <em>deterministisch</em> die Ausgabe ist.<br />
Bei 0.0 kommt immer fast die selbe Antwort. Bei zu hohen Werten kommt nur noch Schwachsinn.<br />
OpenAI empfielt einen Wert von ca. 0.9 für kreative Anwendungen.
</div> </div>
, ,
new() { Position = TooltipPosition.Bottom, Duration = null}); new() { Position = TooltipPosition.Bottom, Duration = null});
private string _imagePromptEnglish = "";
private string _imageIdea = ""; private string _imageIdea = "";
private string _imageDescription = "";
private string _imagePrompt = ""; private string _imagePrompt = "";
private float temperature = 0.9f; private float _temperature = 0.9f;
private int? width = 1024; private int? width = 1024;
private int? height = 1024; private int? height = 1024;
private string request = ""; private string request = "";
private string addons = ""; private string addons = "";
private OpenAIAPI _openAiApi; private OpenAIAPI _openAiApi;
private Conversation converse; private Conversation? _conversation;
private string _basePrompt; private string _basePrompt;
private string _ideaPrompt; private string _ideaPrompt;
@ -205,275 +187,165 @@
// _basePrompt = await File.ReadAllTextAsync($"{Directory.GetCurrentDirectory()}{@"\wwwroot\prompt.txt"}"); // _basePrompt = await File.ReadAllTextAsync($"{Directory.GetCurrentDirectory()}{@"\wwwroot\prompt.txt"}");
//} //}
private async Task FunnyMessageSwitcher_ImageGen(CancellationToken cancellationToken)
{
Stopwatch sw = Stopwatch.StartNew();
await Task.Delay(1000, cancellationToken);
await UpdateBusyMessage("Dauert noch eine Weile...");
await Task.Delay(1000, cancellationToken);
await UpdateBusyMessage("Gut Ding hat Weil...");
await Task.Delay(1000, cancellationToken);
await UpdateBusyMessage("Sach ma, was issn da l... achso, er ist auf Klo gegangen... Er ist bestimmt gleich wieder da...");
await Task.Delay(1000, cancellationToken);
await UpdateBusyMessage("Na, also langsam verlier ich hier die Geduld, ich dachte KI soll alles schneller machen...");
while (!cancellationToken.IsCancellationRequested)
{
await Task.Delay(1000, cancellationToken);
await UpdateBusyMessage($"Keine Sorge, er arbeitet noch. Die Bilder werden gemalt... ({sw.Elapsed.Seconds}s)");
}
}
async Task UpdateBusyMessage(string newMessage) async Task UpdateBusyMessage(string newMessage)
{ {
BusyMessage = newMessage; BusyMessage = newMessage;
await InvokeAsync(StateHasChanged); await InvokeAsync(StateHasChanged);
} }
private readonly HttpClient _client = new();
private string _inferenceApiKey = "";
private string _openAiApiKey = ""; private string _openAiApiKey = "";
protected override Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
_inferenceApiKey = Config.GetValue<string>("API:HF_Inference");
_openAiApiKey = Config.GetValue<string>("API:OpenAI"); _openAiApiKey = Config.GetValue<string>("API:OpenAI");
_openAiApi = new OpenAIAPI(_openAiApiKey); _openAiApi = new OpenAIAPI(_openAiApiKey);
var inferenceModelUrl = "https://api-inference.huggingface.co/models/Nacken/kkkk-sdxl-5000"; await base.OnInitializedAsync();
//_client.BaseAddress = new Uri(inferenceModelUrl);
_client.DefaultRequestHeaders.Clear();
_client.DefaultRequestHeaders.Add("Authorization", $"Bearer {_inferenceApiKey}");
return base.OnInitializedAsync();
} }
/// <summary> private void ClearOldGeneration()
/// Geneiert das Bild für den aktuellen <see cref="_imagePrompt"/>
/// </summary>
public async Task GenerateImageAsync()
{
var postData = new
{
inputs = _imagePrompt
};
JsonContent content = JsonContent.Create(postData);
async Task FailedToDrawImage()
{
bool? retry = await DialogService.Confirm("Leider konnte das Bild nicht gemalt werden. Möchtest du es noch eimal versuchen?", "Ups, ein Fehler ist aufgetreten...",
new ConfirmOptions { OkButtonText = "Ja", CancelButtonText = "Nein" });
if (retry == true)
await GenerateImageAsync();
}
try
{
var inferenceModelUrl = "https://api-inference.huggingface.co/models/Nacken/kkkk-sdxl-5000";
var response = await _client.PostAsync(inferenceModelUrl, content);
if (response?.IsSuccessStatusCode == true)
{
await using Stream imageStream = await response.Content.ReadAsStreamAsync();
using Image image = await Image.LoadAsync(imageStream);
string imgUrl = $"generated_images/{DateTime.Now:dd_MM_yyyy_hh_mm_s}.jpg";
string mapPath = $"./wwwroot/{imgUrl}";
await image.SaveAsJpegAsync(mapPath);
_imageUrl = imgUrl;
}
else
{
Console.WriteLine($"Image conversion failed: {response}");
if (Debugger.IsAttached)
Debugger.Break();
await FailedToDrawImage();
}
}
catch (Exception exception)
{
Console.WriteLine($"Image request failed: {exception}");
if (Debugger.IsAttached)
Debugger.Break();
await FailedToDrawImage();
}
}
/// <summary>
/// Geneiert das Bild für den aktuellen <see cref="_imagePrompt"/>
/// </summary>
public async Task<string?> GenerateImageAsync(string prompt, bool isRetry = false)
{
var postData = new
{
inputs = prompt,
parameters = new
{
negative_prompt = "photorealistic, highly detailed, 8K, portrait",
width = width,
height = height
},
options = new
{
// Cache deaktivieren, damit Huggingface für den selben Prompt unterschiedliche Ergebnisse liefert
use_cache = false,
// Erst wenn wir bereits in einem retry sind warten wir implizit auf das Model. (ignoriert quasi 503-Fehler)
wait_for_model = true
}
};
JsonContent content = JsonContent.Create(postData);
async Task<string?> FailedToDrawImage()
{
bool? retry = await DialogService.Confirm("Leider konnte das Bild nicht gemalt werden. Möchtest du es noch eimal versuchen?", "Ups, ein Fehler ist aufgetreten...",
new ConfirmOptions { OkButtonText = "Ja", CancelButtonText = "Nein" });
if (retry == true)
{
return await GenerateImageAsync(prompt, true);
}
return null;
}
try
{
var inferenceModelUrl = "https://api-inference.huggingface.co/models/Nacken/kkkk-sdxl-5000";
var response = await _client.PostAsync(inferenceModelUrl, content);
if (response?.IsSuccessStatusCode == true)
{
await using Stream imageStream = await response.Content.ReadAsStreamAsync();
using Image image = await Image.LoadAsync(imageStream);
string imgUrl = $"generated_images/{DateTime.Now:dd_MM_yyyy_hh_mm_s_fffffff}.jpg";
string mapPath = $"./wwwroot/{imgUrl}";
await image.SaveAsJpegAsync(mapPath);
// Hier speichern wir die Daten in die 'info_texts.txt'-Datei
string infoTextsPath = Path.Combine(_environment.WebRootPath, "generated_images", "info_texts.txt");
string desc = _imageDescription.Replace("\r\n", "").Replace("\n", "").Replace("\r", "");
string newLine = $"{imgUrl}: {request}, {desc}\n";
await File.AppendAllTextAsync(infoTextsPath, newLine);
return imgUrl;
}
else
{
Console.WriteLine($"Image conversion failed: {response}");
if (Debugger.IsAttached)
Debugger.Break();
return await FailedToDrawImage();
}
}
catch (Exception exception)
{
Console.WriteLine($"Image request failed: {exception}");
if (Debugger.IsAttached)
Debugger.Break();
return await FailedToDrawImage();
}
return null;
}
private async Task DoStuff(bool newPic)
{ {
// Bilder verbergen
for (int i = 0; i < 4; i++) for (int i = 0; i < 4; i++)
_imageStates[i] = ImageState.FadeOut; _imageStates[i] = ImageState.FadeOut;
_imageIdea = "";
_imagePrompt = "";
}
private async Task RequestImageIdeaAsync()
{
string ideaBasePrompt = await File.ReadAllTextAsync($"{Directory.GetCurrentDirectory()}{@"/wwwroot/idea_prompt.txt"}");
string requestImagePrompt = await File.ReadAllTextAsync($"{Directory.GetCurrentDirectory()}{@"/wwwroot/test_prompt2.txt"}");
ChatRequest chatRequest = new ChatRequest
{
Temperature = _temperature,
Model = _useGpt4 ? Model.GPT4 : Model.ChatGPTTurbo
};
_conversation = _openAiApi.Chat.CreateConversation(chatRequest);
// Wunsch an GPT senden und Bild Idee anfordern
_conversation.AppendUserInput(ideaBasePrompt + " " + request);
_imageIdea = await _conversation.GetResponseFromChatbotAsync();
// Bild Promt anfordern
_conversation.AppendUserInput(requestImagePrompt);
_imagePrompt = await _conversation.GetResponseFromChatbotAsync();
}
/// <summary>
/// Generiert Bilder oder aktualisiert sie mit dem neuen Prompt.
/// </summary>
/// <param name="generateNewImage">Wenn <see langword="true"/>, werden neue Bilder generiert; sonst wird die vorhandene Idee bearbeitet.</param>
private async Task GenerateImages(bool generateNewImage)
{
// Der Dialog blokiert so lange, wie der er offen ist, deshalb dürfen wir hier nicht warten, da wir sonst nie mit der Arbeit anfangen... // Der Dialog blokiert so lange, wie der er offen ist, deshalb dürfen wir hier nicht warten, da wir sonst nie mit der Arbeit anfangen...
//Task busyDialog = ShowBusyDialog(); //Task busyDialog = ShowBusyDialog();
_progressVisible = true; _progressVisible = true;
_buttonVisible = false; _buttonVisible = false;
if (converse == null || newPic) if (_conversation == null || generateNewImage)
{ {
ClearOldGeneration();
amountOfAddons = maxAddons; amountOfAddons = maxAddons;
_addonsVisible = false; _addonsVisible = false;
_bothVisible = _buttonVisible && _addonsVisible; _bothVisible = _buttonVisible && _addonsVisible;
await UpdateBusyMessage("Kirstens Assistent zerbricht sich über deine Idee den Kopf..."); await UpdateBusyMessage("Kirstens Assistent zerbricht sich über deine Idee den Kopf...");
_ideaPrompt = await File.ReadAllTextAsync($"{Directory.GetCurrentDirectory()}{@"/wwwroot/idea_prompt.txt"}");
ChatRequest chatRequest = new ChatRequest
{
Temperature = temperature,
Model = Model.ChatGPTTurbo,
};
converse = _openAiApi.Chat.CreateConversation(chatRequest);
converse.AppendUserInput(_ideaPrompt + " " + request);
_imageIdea = await converse.GetResponseFromChatbotAsync();
_basePrompt = await File.ReadAllTextAsync($"{Directory.GetCurrentDirectory()}{@"/wwwroot/test_prompt2.txt"}");
converse.AppendUserInput(_basePrompt);
await RequestImageIdeaAsync();
} }
else else
{ {
if (amountOfAddons > 0) throw new NotImplementedException("Verändern von Idees ist nicht implementiert");
{ // if (amountOfAddons > 0)
amountOfAddons--; // {
_bothVisible = _buttonVisible && _addonsVisible; // amountOfAddons--;
await UpdateBusyMessage("Kirstens Assistent passt das Bild an deine Wünsche an..."); // _bothVisible = _buttonVisible && _addonsVisible;
string addonsPrompt1 = "Erstelle einen neuen Prompt auf englisch mit den gleichen Restriktionen auf Basis des Alten mit folgender Anpassung: "; // await UpdateBusyMessage("Kirstens Assistent passt das Bild an deine Wünsche an...");
string addonsPrompt2 = ". Denke daran nur den Prompt zu generieren und noch keine Beschreibung oder ähnliches."; // string addonsPrompt1 = "Erstelle einen neuen Prompt auf englisch mit den gleichen Restriktionen auf Basis des Alten mit folgender Anpassung: ";
// string addonsPrompt2 = ". Denke daran nur den Prompt zu generieren und noch keine Beschreibung oder ähnliches.";
converse.AppendUserInput(addonsPrompt1 + addons + addonsPrompt2); // _conversation.AppendUserInput(addonsPrompt1 + addons + addonsPrompt2);
} // }
} }
_imagePromptEnglish = await converse.GetResponseFromChatbotAsync(); _imagePrompt = "kkkk " + _imagePrompt + " kkkk Watercolor + ink on paper, Pen drawing, wet-on-wet technique, dry-on-dry technique, dabbing technique. ";
_imagePrompt = _imagePromptEnglish;
_imagePrompt += "kkkk Watercolor + ink on paper, Pen drawing, wet-on-wet technique, dry-on-dry technique, dabbing technique. ";
converse.AppendUserInput(_imageDescriptionPrompt);
_imageDescription = await converse.GetResponseFromChatbotAsync();
await UpdateBusyMessage("Kirstens Assistent hat eine Idee! Er wird sie nun malen..."); await UpdateBusyMessage("Kirstens Assistent hat eine Idee! Er wird sie nun malen...");
// Vier Bilder generieren WunschInfoModel wunschInfo = new()
for (int i = 0; i < 4; i++)
{ {
_imageUrls[i] = await GenerateImageAsync(_imagePrompt); BildBeschreibung = _imageIdea,
_imageStates[i] = ImageState.FadeIn; BildPrompt = _imagePrompt,
await InvokeAsync(StateHasChanged); Datum = DateTime.Now,
GPTModel = _conversation.Model,
Wunsch = request
};
try
{
await WunschInfoData.AddWunschInfoAsync(wunschInfo);
}
catch (Exception e)
{
NotificationService.Notify(new NotificationMessage()
{
Summary = "Es ist ein Fehler aufgetreten, bitte versuche es erneut."
});
return;
}
int generatedImages = 0;
try
{
// Vier Bilder generieren
for (int i = 0; i < 4; i++)
{
_imageUrls[i] = await ImageGenerator.GenerateImageAsync(_imagePrompt, "", width, height, wunschInfo);
// Kein Bild -> Fehler
if (_imageUrls[i] == null)
{
bool? retry = await DialogService.Confirm(
"Leider konnte das Bild nicht gemalt werden. Möchtest du es noch eimal versuchen?",
"Ups, ein Fehler ist aufgetreten...",
new ConfirmOptions { OkButtonText = "Ja", CancelButtonText = "Nein" });
if (retry == true)
{
await ImageGenerator.GenerateImageAsync(_imagePrompt, "", width, height, wunschInfo);
}
}
generatedImages++;
_imageStates[i] = ImageState.FadeIn;
await InvokeAsync(StateHasChanged);
}
}
catch (Exception e)
{
NotificationService.Notify(new NotificationMessage()
{
Summary = "Es ist ein Fehler beim Erzeugen der Bilder aufgetreten, bitte versuche es erneut."
});
if (generatedImages == 0)
{
// TODO: Delete WunschInfo
}
} }
_progressVisible = false; _progressVisible = false;
_buttonVisible = true; _buttonVisible = true;
_promptVisible = true;
if (amountOfAddons > 0) if (amountOfAddons > 0)
{ {
_addonsVisible = true; _addonsVisible = true;
@ -486,17 +358,5 @@
_bothVisible = false; _bothVisible = false;
} }
} }
private void NavigateToGallery()
{
NavigationManager.NavigateTo("/gallery");
}
[Inject]
private NavigationManager NavigationManager { get; set; }
[Inject]
private IWebHostEnvironment _environment { get; set; }
} }

View File

@ -1,150 +1,70 @@
@page "/gallery" @page "/gallery"
@using DataAccess.Data
@using DataAccess.Models
@inject DialogService DialogService;
@inject BildInfoData BildInfoData;
@inject WunschInfoData WunschInfoData;
<style> <style>
.image-grid { .small-image {
background-color: white; /* Hintergrund der RadzenDataList weiß machen */ width: 10em;
height: 10em;
z-index: auto;
margin: 0.5em;
box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2);
border-radius: 1em;
/* Gibt an, dass Änderungen an der transform-Eigenschaft innerhalb von 0.2s angewandt werden.*/
transition: transform 0.2s;
} }
.rz-data-list { /* Style, der angewendet wird, wenn über small-image gehovert wird. */
display: flex; .small-image:hover {
flex-wrap: wrap; transform: scale(1.1, 1.1);
justify-content: center; z-index: 100;
}
.image-grid-item {
margin: 5px;
}
.image-popup {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(255, 255, 255, 0.9); /* Weißer Hintergrund mit Transparenz */
z-index: 9999;
}
.image-popup-content {
display: flex;
flex-direction: row;
justify-content: space-between;
max-width: 800px;
max-height: 400px;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
padding: 20px;
border-radius: 5px;
background-color: white; /* Weißer Hintergrund für den Inhalt */
}
.image-info {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
}
.close-button {
flex: 0 0 auto;
display: flex;
align-items: flex-start;
justify-content: flex-end;
} }
</style> </style>
<div> <RadzenDataList WrapItems="@true" AllowPaging="true" PageSize="40" Data="@_bildInfos" PagerHorizontalAlign="HorizontalAlign.Left" ShowPagingSummary="true">
<RadzenDataList WrapItems="@true" AllowPaging="true" Data="@imagePaths" PageSize="20" PagerHorizontalAlign="HorizontalAlign.Left" ShowPagingSummary="true" class="image-grid"> <Template Context="bildInfo">
<Template Context="imagePath"> <RadzenImage class="small-image" Src="@bildInfo.Dateiname" Click="@(args => ShowImageDialog(bildInfo))" />
<div class="image-grid-item"> </Template>
<RadzenCard Style="width: 200px; height: 200px; padding: 0; border: none;"> </RadzenDataList>
<RadzenButton Click="@(args => ShowImageInfo(imagePath))">
<RadzenImage Src="@imagePath" Style="width: 100%; height: 100%; object-fit: cover;" />
</RadzenButton>
</RadzenCard>
</div>
</Template>
</RadzenDataList>
<div class="image-popup" style="@popupStyle">
<div class="image-popup-content">
<div class="image-info">
<RadzenImage Src="@selectedImage" Style="max-width: 100%; max-height: 300px; object-fit: contain;" />
<p>@infoText</p>
</div>
<div class="close-button">
<RadzenButton Text="Close" Click="CloseImageInfo" />
</div>
</div>
</div>
</div>
@code { @code {
List<string> imagePaths = new List<string>(); IEnumerable<BildInfoModel>? _bildInfos;
string selectedImage;
string infoText = "Info Text";
string popupStyle = "display: none;";
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
_bildInfos = await BildInfoData.GetAllBildInfosAsync();
await base.OnInitializedAsync(); await base.OnInitializedAsync();
// Get the physical path to the folder
string folderPath = Path.Combine(_environment.WebRootPath, "generated_images");
if (Directory.Exists(folderPath))
{
// Load the image file names from the folder
var imageFiles = Directory.GetFiles(folderPath, "*.jpg");
// Get the relative paths to the images
imagePaths = imageFiles.Select(file => file.Replace(_environment.WebRootPath, "").Replace("\\", "/")).ToList();
}
} }
private async void ShowImageInfo(string imagePath) async Task ShowImageDialog(BildInfoModel bildInfo)
{ {
selectedImage = imagePath; WunschInfoModel wunschInfo = await WunschInfoData.GetWunschInfoAsync(bildInfo.WunschId);
popupStyle = "display: block;";
infoText = await GetInfoTextForImageAsync(imagePath);
}
List<BildInfoModel> bilderVomWunsch = _bildInfos!.Where(info => info.WunschId == wunschInfo.Id).ToList();
private async Task<string> GetInfoTextForImageAsync(string imagePath) var result = await DialogService.OpenAsync(wunschInfo.Wunsch, ds =>
{ @<div>
// Bestimme den Ordnerpfad, in dem sich die Bilder und die info_texts.txt Datei befinden <RadzenStack Orientation="Orientation.Horizontal" Wrap="FlexWrap.Wrap">
string folderPath = Path.Combine(_environment.WebRootPath, "generated_images"); <RadzenStack Orientation="Orientation.Horizontal">
<RadzenStack Orientation="Orientation.Vertical">
// Bestimme den Pfad zur info_texts.txt Datei <RadzenImage Style="width: 400px; height: 400px;" Path="@bildInfo.Dateiname" />
string infoTextsFilePath = Path.Combine(folderPath, "info_texts.txt"); </RadzenStack>
<RadzenText Text="@wunschInfo.BildBeschreibung"/>
// Überprüfe, ob die Datei existiert </RadzenStack>
if (!File.Exists(infoTextsFilePath)) @foreach (var bild in bilderVomWunsch)
return $"Kein Infotext für {imagePath} gefunden."; {
<RadzenImage class="small-image" Path="@bild.Dateiname"
// Lies alle Zeilen der Datei Click="async () => { bildInfo = bild; DialogService.Close(); await ShowImageDialog(bild); }" />
var lines = await File.ReadAllLinesAsync(infoTextsFilePath); }
string adaptedImagePath = imagePath.Substring(1) + ":"; </RadzenStack>
</div>,
// Durchsuche jede Zeile nach dem gegebenen imagePath new DialogOptions() { CloseDialogOnOverlayClick = true, Width = "50%" });
foreach (var line in lines)
{
if (line.StartsWith(adaptedImagePath)) // Überprüft, ob die Zeile mit dem Dateinamen des Bildes beginnt
{
// Trenne den Dateinamen und den Infotext und gib den Infotext zurück
return line.Split(new[] { ':' }, 2).LastOrDefault()?.Trim();
}
}
return $"Kein Infotext für {imagePath} gefunden.";
}
private void CloseImageInfo()
{
popupStyle = "display: none;";
} }
[Inject] [Inject]

View File

@ -1,3 +1,6 @@
using DataAccess.Data;
using DataAccess.DbAccess;
using KIKunstKirstenKl<EFBFBD>ckner.Data;
using Radzen; using Radzen;
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);
@ -5,9 +8,17 @@ var builder = WebApplication.CreateBuilder(args);
// Add services to the container. // Add services to the container.
builder.Services.AddRazorPages(); builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor(); builder.Services.AddServerSideBlazor();
builder.Services.AddScoped<TooltipService>(); builder.Services.AddScoped<TooltipService>();
builder.Services.AddScoped<DialogService>(); builder.Services.AddScoped<DialogService>();
builder.Services.AddScoped<NotificationService>(); builder.Services.AddScoped<NotificationService>();
builder.Services.AddScoped<ImageGenerator>();
builder.Services.AddScoped<AiArtPageData>();
builder.Services.AddSingleton<ISqlDataAccess, SqlDataAccess>();
builder.Services.AddSingleton<BildInfoData>();
builder.Services.AddSingleton<WunschInfoData>();
var app = builder.Build(); var app = builder.Build();

View File

@ -9,7 +9,7 @@
<div class="container"> <div class="container">
<nav class="navbar navbar-expand-lg custom_nav-container "> <nav class="navbar navbar-expand-lg custom_nav-container ">
<a class="navbar-brand" href="/"> <a class="navbar-brand" href="/">
<img src="images/robot_painting.jpg" alt=""> <img src="images/robot_painting_small.jpg" alt="">
<span> <span>
KI-Wunschprogramm KI-Wunschprogramm
</span> </span>
@ -24,19 +24,19 @@
<div class="d-flex ml-auto flex-column flex-lg-row align-items-center"> <div class="d-flex ml-auto flex-column flex-lg-row align-items-center">
<ul class="navbar-nav "> <ul class="navbar-nav ">
<NavLink class="nav-item" href="" Match="NavLinkMatch.All"> <NavLink class="nav-item" href="" Match="NavLinkMatch.All">
<span class="nav-link">Home</span> <span class="nav-link">Start</span>
</NavLink> </NavLink>
<NavLink class="nav-item" href="aiart" Match="NavLinkMatch.All"> <NavLink class="nav-item" href="aiart" Match="NavLinkMatch.All">
<span class="nav-link">Bilder generieren</span> <span class="nav-link">Generieren</span>
</NavLink> </NavLink>
<NavLink class="nav-item" href="wunschprogramm" Match="NavLinkMatch.All"> <NavLink class="nav-item" href="wunschprogramm" Match="NavLinkMatch.All">
<span class="nav-link">Wunschprogramm</span> <span class="nav-link">Wunschprogramm</span>
</NavLink> </NavLink>
<NavLink class="nav-item" href="kirstenkloeckner" Match="NavLinkMatch.All"> <NavLink class="nav-item" href="kirstenkloeckner" Match="NavLinkMatch.All">
<span class="nav-link">Über Kirsten Klöckner</span> <span class="nav-link">Kirsten</span>
</NavLink> </NavLink>
<NavLink class="nav-item" href="gallery" Match="NavLinkMatch.All"> <NavLink class="nav-item" href="gallery" Match="NavLinkMatch.All">
<span class="nav-link">Galerier</span> <span class="nav-link">Galerie</span>
</NavLink> </NavLink>
</ul> </ul>
</div> </div>
@ -69,7 +69,7 @@
<a href=""> <a href="">
<img src="images/telephone-white.png" alt=""> <img src="images/telephone-white.png" alt="">
<span> <span>
Call : +012334567890 +012334567890
</span> </span>
</a> </a>
</div> </div>
@ -87,22 +87,22 @@
<div class="col-md-4 col-lg-3"> <div class="col-md-4 col-lg-3">
<div class="info_social"> <div class="info_social">
<div> <div>
<a href=""> <a href="https://www.facebook.com/kirsten.kloeckner">
<img src="images/fb.png" alt=""> <img src="images/fb.png" alt="">
</a> </a>
</div> </div>
<div> <div>
<a href=""> <a href="https://twitter.com/kkbeutekunst">
<img src="images/twitter.png" alt=""> <img src="images/twitter.png" alt="">
</a> </a>
</div> </div>
<div> <div>
<a href=""> <a href="https://de.linkedin.com/in/kirsten-kl%C3%B6ckner-2a123393">
<img src="images/linkedin.png" alt=""> <img src="images/linkedin.png" alt="">
</a> </a>
</div> </div>
<div> <div>
<a href=""> <a href="https://www.instagram.com/kirstenkloeckner/">
<img src="images/instagram.png" alt=""> <img src="images/instagram.png" alt="">
</a> </a>
</div> </div>

View File

@ -1,9 +1,12 @@
{ {
"DetailedErrors": true, "DetailedErrors": true,
"Logging": { "Logging": {
"LogLevel": { "LogLevel": {
"Default": "Information", "Default": "Information",
"Microsoft.AspNetCore": "Warning" "Microsoft.AspNetCore": "Warning"
}
},
"ConnectionStrings": {
"Default": "Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=KiKunstDatenbank;Integrated Security=True;Connect Timeout=60;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False"
} }
}
} }

View File

@ -9,5 +9,8 @@
"API": { "API": {
"OpenAI": "<put OpenAI Key here>", "OpenAI": "<put OpenAI Key here>",
"HF_Inference": "<put Hugging Face inference API Key here>" "HF_Inference": "<put Hugging Face inference API Key here>"
},
"ConnectionStrings": {
"Default": "<put Connection String here>"
} }
} }

View File

@ -1,2 +1,2 @@
Gib mir eine kreative und wenn möglich tiefsinnige Idee für ein Kunstwerk. Gib mir eine kreative und wenn möglich tiefsinnige Idee, die nicht unbedingt mit Städten zu tun hat, für ein Kunstwerk.
Der Titel ist: Der Titel ist:

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<Operations Version="1.0" xmlns="http://schemas.microsoft.com/sqlserver/dac/Serialization/2012/02">
<Operation Name="Rename Refactor" Key="c957123b-8f1b-4753-96eb-c2ea2811027a" ChangeDateTime="10/10/2023 12:47:37">
<Property Name="ElementName" Value="[dbo].[BildInfo].[Prompt]" />
<Property Name="ElementType" Value="SqlSimpleColumn" />
<Property Name="ParentElementName" Value="[dbo].[BildInfo]" />
<Property Name="ParentElementType" Value="SqlTable" />
<Property Name="NewName" Value="BildPrompt" />
</Operation>
<Operation Name="Rename Refactor" Key="9e69babc-e66c-496f-960a-0c9ad936d763" ChangeDateTime="10/10/2023 12:47:42">
<Property Name="ElementName" Value="[dbo].[BildInfo].[Beschreibung]" />
<Property Name="ElementType" Value="SqlSimpleColumn" />
<Property Name="ParentElementName" Value="[dbo].[BildInfo]" />
<Property Name="ParentElementType" Value="SqlTable" />
<Property Name="NewName" Value="BildBeschreibung" />
</Operation>
<Operation Name="Rename Refactor" Key="1dc11132-1619-4a0d-b261-0d56392a3d51" ChangeDateTime="10/10/2023 13:39:00">
<Property Name="ElementName" Value="[dbo].[BildInfo].[Index]" />
<Property Name="ElementType" Value="SqlSimpleColumn" />
<Property Name="ParentElementName" Value="[dbo].[BildInfo]" />
<Property Name="ParentElementType" Value="SqlTable" />
<Property Name="NewName" Value="GroupIndex" />
</Operation>
</Operations>

View File

@ -0,0 +1,81 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<Name>KiKunstDatenbank</Name>
<SchemaVersion>2.0</SchemaVersion>
<ProjectVersion>4.1</ProjectVersion>
<ProjectGuid>{a19cd19a-fe5b-4d4e-896b-dcc43b45f734}</ProjectGuid>
<DSP>Microsoft.Data.Tools.Schema.Sql.Sql150DatabaseSchemaProvider</DSP>
<OutputType>Database</OutputType>
<RootPath>
</RootPath>
<RootNamespace>KiKunstDatenbank</RootNamespace>
<AssemblyName>KiKunstDatenbank</AssemblyName>
<ModelCollation>1033, CI</ModelCollation>
<DefaultFileStructure>BySchemaAndSchemaType</DefaultFileStructure>
<DeployToDatabase>True</DeployToDatabase>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<TargetLanguage>CS</TargetLanguage>
<AppDesignerFolder>Properties</AppDesignerFolder>
<SqlServerVerification>False</SqlServerVerification>
<IncludeCompositeObjects>True</IncludeCompositeObjects>
<TargetDatabaseSet>True</TargetDatabaseSet>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<OutputPath>bin\Release\</OutputPath>
<BuildScriptName>$(MSBuildProjectName).sql</BuildScriptName>
<TreatWarningsAsErrors>False</TreatWarningsAsErrors>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<DefineDebug>false</DefineDebug>
<DefineTrace>true</DefineTrace>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<OutputPath>bin\Debug\</OutputPath>
<BuildScriptName>$(MSBuildProjectName).sql</BuildScriptName>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<DefineDebug>true</DefineDebug>
<DefineTrace>true</DefineTrace>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">11.0</VisualStudioVersion>
<!-- Default to the v11.0 targets path if the targets file for the current VS version is not found -->
<SSDTExists Condition="Exists('$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v$(VisualStudioVersion)\SSDT\Microsoft.Data.Tools.Schema.SqlTasks.targets')">True</SSDTExists>
<VisualStudioVersion Condition="'$(SSDTExists)' == ''">11.0</VisualStudioVersion>
</PropertyGroup>
<Import Condition="'$(SQLDBExtensionsRefPath)' != ''" Project="$(SQLDBExtensionsRefPath)\Microsoft.Data.Tools.Schema.SqlTasks.targets" />
<Import Condition="'$(SQLDBExtensionsRefPath)' == ''" Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v$(VisualStudioVersion)\SSDT\Microsoft.Data.Tools.Schema.SqlTasks.targets" />
<ItemGroup>
<Folder Include="Properties" />
<Folder Include="dpo" />
<Folder Include="dpo\Tables" />
<Folder Include="dpo\StoredProcedures" />
</ItemGroup>
<ItemGroup>
<Build Include="dpo\Tables\BildInfo.sql" />
<Build Include="dpo\StoredProcedures\spBildInfo_Insert.sql" />
<Build Include="dpo\Tables\WunschInfo.sql" />
<Build Include="dpo\StoredProcedures\spWunschInfo_Insert.sql" />
<Build Include="dpo\StoredProcedures\spBildInfo_UpdateFileName.sql" />
<Build Include="dpo\StoredProcedures\spBildInfo_GetAll.sql" />
<Build Include="dpo\StoredProcedures\spWunschInfo_Get.sql" />
</ItemGroup>
<ItemGroup>
<RefactorLog Include="KiKunstDatenbank.refactorlog" />
</ItemGroup>
<ItemGroup>
<PostDeploy Include="Script.PostDeployment.sql" />
</ItemGroup>
<ItemGroup>
<None Include="KiKunstDatenbank.publish.xml" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,19 @@
/*
Vorlage für ein Skript nach der Bereitstellung
--------------------------------------------------------------------------------------
Diese Datei enthält SQL-Anweisungen, die an das Buildskript angefügt werden.
Schließen Sie mit der SQLCMD-Syntax eine Datei in das Skript nach der Bereitstellung ein.
Beispiel: :r .\myfile.sql
Verwenden Sie die SQLCMD-Syntax, um auf eine Variable im Skript nach der Bereitstellung zu verweisen.
Beispiel: :setvar TableName MyTable
SELECT * FROM [$(TableName)]
--------------------------------------------------------------------------------------
*/
--IF NOT EXISTS (SELECT 1 FROM [dbo].[User])
--BEGIN
-- INSERT INTO [dbo].[User] (FirstName, LastName)
-- VALUES ('Simon', 'Lübeß'),
-- ('Peter', 'Enis'),
-- ('John', 'Smith'),
-- ('Mary', 'Jones')
--END

View File

@ -0,0 +1,6 @@
CREATE PROCEDURE [dbo].[spBildInfo_GetAll]
AS
BEGIN
SELECT Id, Datum, Dateiname, ImageModel, WunschId
FROM [dbo].[BildInfo];
END

View File

@ -0,0 +1,16 @@
CREATE PROCEDURE [dbo].[spBildInfo_Insert]
@Id INT,
@Datum DATETIME2 ,
@Dateiname NCHAR(256) ,
@ImageModel NCHAR(32) ,
@WunschId INT
AS
BEGIN
INSERT INTO [dbo].[BildInfo] (Datum, Dateiname, ImageModel, WunschId)
VALUES (@Datum,
@Dateiname,
@ImageModel,
@WunschId);
SELECT Id FROM [dbo].[BildInfo] WHERE Id = CAST(SCOPE_IDENTITY() AS INT);
END

View File

@ -0,0 +1,9 @@
CREATE PROCEDURE [dbo].[spBildInfo_UpdateFileName]
@Id INT,
@Dateiname NCHAR(256)
AS
BEGIN
UPDATE [dbo].[BildInfo]
SET Dateiname = @Dateiname
WHERE Id = @Id;
END

View File

@ -0,0 +1,8 @@
CREATE PROCEDURE [dbo].[spWunschInfo_Get]
@Id INT
AS
BEGIN
SELECT Id, Wunsch, BildPrompt, BildBeschreibung, Datum, GPTModel
FROM [dbo].[WunschInfo]
WHERE Id = @Id;
END

View File

@ -0,0 +1,18 @@
CREATE PROCEDURE [dbo].[spWunschInfo_Insert]
@Id INT,
@Wunsch NVARCHAR(1024),
@BildPrompt NVARCHAR(MAX),
@BildBeschreibung NVARCHAR(MAX),
@Datum DATETIME2,
@GPTModel NCHAR(32)
AS
BEGIN
INSERT INTO [dbo].[WunschInfo] (Wunsch, BildPrompt, BildBeschreibung, Datum, GPTModel)
VALUES (@Wunsch,
@BildPrompt,
@BildBeschreibung,
@Datum,
@GPTModel);
SELECT Id FROM [dbo].[WunschInfo] WHERE Id = CAST(SCOPE_IDENTITY() AS INT);
END

View File

@ -0,0 +1,8 @@
CREATE TABLE [dbo].[BildInfo]
(
[Id] INT NOT NULL PRIMARY KEY IDENTITY,
[Datum] DATETIME2 NOT NULL,
[Dateiname] NCHAR(256) NOT NULL,
[ImageModel] NCHAR(32) NOT NULL,
[WunschId] INT NOT NULL
)

View File

@ -0,0 +1,9 @@
CREATE TABLE [dbo].[WunschInfo]
(
[Id] INT NOT NULL PRIMARY KEY IDENTITY,
[Wunsch] NVARCHAR (1024) NOT NULL,
[BildPrompt] NVARCHAR (MAX) NOT NULL,
[BildBeschreibung] NVARCHAR (MAX) NOT NULL,
[Datum] DATETIME2 (7) NOT NULL,
[GPTModel] NCHAR (32) NOT NULL
)