SeemGen/Services/ContentEditorService.cs

279 lines
13 KiB
C#

using BLAIzor.Data;
using BLAIzor.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.Identity.Client;
namespace BLAIzor.Services
{
public class ContentEditorService
{
//private readonly AIService _aiService;
private readonly OpenAIApiService _openAIApiService;
//private readonly ApplicationDbContext _context;
private readonly QDrantService _qDrantService;
private readonly HtmlSnippetProcessor _htmlSnippetProcessor;
private readonly IServiceScopeFactory _serviceScopeFactory;
public ContentEditorService(/*AIService aiService,*/ OpenAIApiService openAIApiService, /*ApplicationDbContext context,*/ QDrantService qDrantService, HtmlSnippetProcessor htmlSnippetProcessor, IServiceScopeFactory serviceScopeFactory)
{
//_aiService = aiService;
_openAIApiService = openAIApiService;
//_context = context;
_qDrantService = qDrantService;
_htmlSnippetProcessor = htmlSnippetProcessor;
_serviceScopeFactory = serviceScopeFactory;
}
// Existing methods
public async Task<List<string>> GetMenuSuggestionsAsync(string sessionId, string prompt)
{
string systemMessage = "You are a helpful assistant that helps the user in creating a website.";
var result = await _openAIApiService.GetSimpleChatGPTResponse(sessionId, systemMessage, prompt);
return result.Split('\n', StringSplitOptions.RemoveEmptyEntries)
.Select(line => line.Trim('-').Trim())
.ToList() ?? new List<string>();
}
public async Task<string> GetGeneratedContentAsync(string sessionId, string prompt)
{
string systemMessage = "You are a helpful assistant that helps the user in creating a website. Do not generate html, just plain text.";
var result = await _openAIApiService.GetSimpleChatGPTResponse(sessionId, systemMessage, prompt);
return result;
}
// CRUD methods for MenuItems
// Create a new MenuItem
public async Task<MenuItem> AddMenuItemAsync(MenuItem menuItem)
{
using (var scope = _serviceScopeFactory.CreateScope())
{
var _context = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
// Now use dbContext safely without violating DI rules
var result = await _context.MenuItems.AddAsync(menuItem);
await _context.SaveChangesAsync();
return result.Entity;
}
}
/// <summary>
/// Adds items as list
/// </summary>
/// <param name="menuItems"></param>
/// <returns>the number of modified rows</returns>
public async Task<int> AddMenuItemsAsync(List<MenuItem> menuItems)
{
using (var scope = _serviceScopeFactory.CreateScope())
{
var _context = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
// Now use dbContext safely without violating DI rules
_context.MenuItems.AddRange(menuItems);
var result = await _context.SaveChangesAsync();
return result;
}
}
// Get all MenuItems for a specific SiteInfo
public async Task<List<MenuItem>> GetMenuItemsBySiteIdAsync(int siteInfoId)
{
using (var scope = _serviceScopeFactory.CreateScope())
{
var _context = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
// Now use dbContext safely without violating DI rules
var result = await _context.MenuItems
.Where(m => m.SiteInfoId == siteInfoId)
.ToListAsync();
return result;
}
}
// Update an existing MenuItem
public async Task<MenuItem> UpdateMenuItemAsync(MenuItem menuItem)
{
using (var scope = _serviceScopeFactory.CreateScope())
{
var _context = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
// Now use dbContext safely without violating DI rules
var existingMenuItem = await _context.MenuItems.FindAsync(menuItem.Id);
if (existingMenuItem == null)
{
throw new Exception("MenuItem not found.");
}
existingMenuItem.Name = menuItem.Name;
existingMenuItem.QdrantPointId = menuItem.QdrantPointId;
existingMenuItem.SortOrder = menuItem.SortOrder;
existingMenuItem.ShowInMainMenu = menuItem.ShowInMainMenu;
_context.MenuItems.Update(existingMenuItem);
await _context.SaveChangesAsync();
return existingMenuItem;
}
}
// Delete a MenuItem
public async Task DeleteMenuItemAsync(int menuItemId)
{
using (var scope = _serviceScopeFactory.CreateScope())
{
var _context = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
// Now use dbContext safely without violating DI rules
var menuItem = await _context.MenuItems.FindAsync(menuItemId);
if (menuItem == null)
{
throw new Exception("MenuItem not found.");
}
_context.MenuItems.Remove(menuItem);
await _context.SaveChangesAsync();
}
}
public async Task<string> ProcessMenuItems(int SiteId, bool hasCollection, List<MenuItemModel> ExtractedMenuItems, string subject, bool menuItemsSaved, bool updateVectorDatabase)
{
try
{
//check if Collection is created already
if (!hasCollection)
{
//create colection
await _qDrantService.CreateQdrantCollectionAsync("Site" + SiteId.ToString());
hasCollection = true;
}
var updateQdrantList = ExtractedMenuItems.OrderBy(mi => mi.MenuItem.PointId).ToList();
for (int i = 0; i < updateQdrantList.Count(); i++)
{
MenuItem menuItem = new MenuItem();
menuItem.Name = ExtractedMenuItems[i].MenuItem.Name;
menuItem.SortOrder = i;
menuItem.SiteInfoId = SiteId;
menuItem.ShowInMainMenu = true;
if (!string.IsNullOrEmpty(ExtractedMenuItems[i].Content)) //if we have content in the menuItem, let's update the vector DB
{
if(updateVectorDatabase)
{
Guid guid = Guid.NewGuid();
WebPageContent content = new WebPageContent();
content.Id = i;
content.UId = guid.ToString();
content.Type = "page";
content.SiteId = SiteId;
content.Name = ExtractedMenuItems[i].MenuItem.Name;
content.Description = $"A section called {ExtractedMenuItems[i].MenuItem.Name} of a website of {subject}";
content.Content = ExtractedMenuItems[i].Content;
content.LastUpdated = DateTime.UtcNow;
menuItem.QdrantPointId = guid;
menuItem.PointId = i;
await _htmlSnippetProcessor.ProcessAndStoreWebContentAsync(i, content, SiteId);
}
else
{
menuItem.QdrantPointId = ExtractedMenuItems[i].MenuItem.QdrantPointId;
}
}
if (!menuItemsSaved) //menuItems just extracted, no content yet because that can be generated only after menuItems have been saved first
{
//menuItem.Name = ExtractedMenuItems[i].MenuItem.Name;
//menuItem.SortOrder = i;
//menuItem.SiteInfoId = SiteId;
//menuItem.ShowInMainMenu = true;
await AddMenuItemAsync(menuItem);
}
else //menuItems available already
{
var menuItems = await GetMenuItemsBySiteIdAsync(SiteId);
var thisItem = menuItems.FirstOrDefault(x => x.SiteInfoId == SiteId && x.Id == ExtractedMenuItems[i].MenuItem.Id);
if (thisItem == null)
{
//new menu item?
await AddMenuItemAsync(menuItem);
}
else
{
thisItem.QdrantPointId = menuItem.QdrantPointId;
thisItem.SortOrder = ExtractedMenuItems[i].MenuItem.SortOrder;
thisItem.ShowInMainMenu = ExtractedMenuItems[i].MenuItem.ShowInMainMenu;
await UpdateMenuItemAsync(thisItem);
}
}
}
//await ContentEditorService.SaveMenuItemsAsync(menuItems);
//menuItemsSaved = true;
return "OK";
}
catch
{
return "Error";
}
}
//public async Task<string> AnalyzeFormFieldsFromText(string text)
//{
// string systemMessage = "You are a helpful assistant that extracts structured form field data from plain text.";
// string userMessage =
// "This is the plain text extracted from a form document. " +
// "Please extract all input fields a user is expected to fill in and return a JSON array of objects.\n\n" +
// "Each object should have the following structure:\n" +
// "- `label`: The label or prompt for the input\n" +
// "- `type`: One of `text`, `number`, `date`, `checkbox`, `radio`, or `textarea`\n" +
// "- `options`: If type is `radio` or `checkbox`, include the possible options as a list of strings; otherwise, return an empty list\n" +
// "- `section`: The section title this field belongs to (if any)\n" +
// "- `repeatable`: true if this field (or set of fields) is expected to be repeated for multiple entries (like multiple employers or qualifications), otherwise false\n\n" +
// "Return only the JSON array, without any explanation or formatting like ```json.\n\n" +
// "Here is the form text:\n\n" +
// $"---\n{text}\n---";
// return await _openAIApiService.GetSimpleChatGPTResponseNoSession(systemMessage, userMessage).ConfigureAwait(false);
//}
public async Task<string> AnalyzeGroupedFormFieldsFromText(string text)
{
string systemMessage = "You are a helpful assistant that analyzes plain form text and extracts structured, grouped form field definitions.";
string userMessage =
"This is a plain text version of a printed form. Your task is to extract the **input fields** that a user must fill in. " +
"Group related fields under sections where applicable, and return them in a JSON structure.\n\n" +
"Each group should follow this structure:\n" +
"- `groupName`: Name of the logical section or group (e.g. 'Work Experience')\n" +
"- `repeatable`: true if this section can appear multiple times (e.g. multiple employers, qualifications)\n" +
"- `fields`: an array of fields within the group\n\n" +
"Each field must have:\n" +
"- `label`: The field label/question\n" +
"- `type`: one of `text`, `number`, `date`, `textarea`, `checkbox`, or `radio`\n" +
"- `options`: list of string values if type is `radio` or `checkbox`, or an empty list otherwise\n" +
"- `section`: optional section name (can match group name or be more specific)\n\n" +
"Return only the JSON array. Do not include any explanation or formatting like ```json.\n\n" +
$"Here is the form text:\n\n---\n{text}\n---";
return await _openAIApiService.GetSimpleChatGPTResponseNoSession(systemMessage, userMessage).ConfigureAwait(false);
}
}
}