EchoTex_Payroll/HRM.DA/Service/OpenAIService/OpenAIService.cs
2026-07-12 18:07:59 +06:00

791 lines
27 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using Ease.Core.DataAccess;
using Ease.Core.Model;
using Ease.Core.Utility;
using HRM.BO;
using ICSharpCode.SharpZipLib.Zip;
using iTextSharp.text;
using Microsoft.AspNetCore.Http;
using Microsoft.SemanticKernel;
using Microsoft.VisualBasic;
using NPOI.SS.Formula.Functions;
using NPOI.SS.UserModel;
using OpenAI;
using OpenAI.Chat;
using OpenAI.Models;
using Org.BouncyCastle.Asn1.Crmf;
using System;
using System.ClientModel;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Security.Cryptography.Xml;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
namespace HRM.DA
{
public class OpenAIService
{
private readonly string _encryptedkey;
private readonly string _apiKey;
private readonly string _openAiModel;
private readonly int _maxLimit;
public OpenAIService(string encryptedkey, string model, int maxLimit)
{
_encryptedkey = encryptedkey;
_apiKey = Ease.Core.Utility.Global.CipherFunctions.Decrypt("CeLiMiTeD.AdMIn", _encryptedkey);
_openAiModel = model;
_maxLimit = maxLimit;
}
#region Invalid Query Message
public string returnMessage = "";
private const string queryMessage = "I'm sorry, I couldn't process your request. Please try rephrasing it. I'm here to help with anything related to payroll or HR tasks.";
private const string modificationMessage = "I'm Sorry, I can only help with retrieving data. I am not allowed to update or delete any records.";
#endregion
#region Schema Generation (Old Version)
// Aliases
//sb.AppendLine("\nAlias Mappings:");
//foreach (var row in columns.AsEnumerable())
//{
// string caption = row["CAPTION"]?.ToString();
// string columnName = row["COLUMNNAME"].ToString();
// string tableName = row["TABLENAME"].ToString();
// if (!string.IsNullOrWhiteSpace(caption) && caption != columnName)
// {
// sb.AppendLine($"- {tableName}.{columnName} is also called '{caption}'");
// }
//}
#endregion
#region Ask SQL Query
#region Function Summary
/*
* Function Summary & What it Handles:
*
* 1. Request Limiting:
* - Ensures the total number of OpenAI calls does not exceed the configured maximum.
* - Throws a polite error message if the request limit is reached.
*
* 2. Timeout Handling:
* - Uses CancellationTokenSource with a configurable timeout (default 60 seconds).
* - Cancels the OpenAI API request if it exceeds the timeout and throws a timeout message.
*
* 3. Rate Limit Handling & Retry Logic:
* - Detects HTTP 429 status or "rate limit" errors from exceptions.
* - Retries the API call up to 5 times with exponential backoff.
* - Returns a polite "service busy" message if retries are exhausted.
*
* 4. OpenAI API Call:
* - Builds a dynamic system prompt including schema and chart instructions.
* - Sends the users prompt to OpenAI for SQL and chart generation.
* - Extracts and cleans the AI response.
*
* 5. Response Parsing & Validation:
* - Deserializes the AI response into a structured object.
* - Ensures only SELECT queries are allowed.
* - Rejects invalid or unsafe queries with descriptive errors.
*
* 6. Async Logging:
* - Logs user prompts, generated SQL, chart details, and metadata asynchronously.
* - Logging errors do not interrupt the main flow.
*
* 7. SQL Execution & JSON Response:
* - Executes the validated SQL query against the database.
* - Wraps the dataset with chart metadata and returns JSON.
*
* 8. General Exception Handling:
* - Provides user-friendly messages for timeouts, rate limits, and invalid queries.
* - Retries on transient errors and throws detailed exceptions otherwise.
*/
#endregion
public async Task<string> AskOpenAISqlQueryWithChartAsync(string prompt, int userID, EnumReportGroupType reportGroupType, DateTime nextPayprocessDate = new DateTime(), int timeoutSeconds = 60)
{
#region Initialize OpenAI Call Count For Limitation
int totalOpenAICall = TotalOpenAICall();
if (totalOpenAICall >= _maxLimit)
{
throw new Exception("You have reached the maximum limit of requests. Please try again later.");
}
#endregion
string returnMessage = "An error occurred while processing your request.";
int maxRetries = 5;
int delaySeconds = 1;
for (int attempt = 0; attempt <= maxRetries; attempt++)
{
try
{
#region Initialize OpenAI Client and Cancellation Token
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(timeoutSeconds));
var client = new ChatClient(model: _openAiModel, apiKey: _apiKey);
#endregion
#region Build System Prompt
string systemPrompt = BuildInitialDescriptionWithChart(reportGroupType) + BuildDynamicSchemaPrompt(reportGroupType, nextPayprocessDate) + BuildInstructionWithChart(reportGroupType);
#endregion
//throw new Exception("You have reached the maximum limit of requests. Please try again later.");
#region Call OpenAI API
var completion = await client.CompleteChatAsync(
new ChatMessage[]
{
ChatMessage.CreateSystemMessage(systemPrompt),
ChatMessage.CreateUserMessage(prompt)
},
cancellationToken: cts.Token
);
#endregion
#region Process and Store OpenAI Response
string aiResponse = completion.Value.Content[0].Text.Trim();
if (string.IsNullOrWhiteSpace(aiResponse))
throw new Exception("Did not return any response.");
aiResponse = aiResponse.Replace("```", "").Trim();
aiResponse = aiResponse.Replace("\n", "\\n").Replace("\r", "\\r");
OpenAIResponse aiResponseObject = Newtonsoft.Json.JsonConvert.DeserializeObject<OpenAIResponse>(aiResponse);
#endregion
#region Validate AI response
string sql = aiResponseObject.Sql;
bool isChart = aiResponseObject.IsChart;
EnumAIChartType chartType = GetAIChartType(aiResponseObject.ChartType);
if (!string.IsNullOrWhiteSpace(aiResponseObject.Message))
{
throw new Exception(aiResponseObject.Message);
}
if (!sql.TrimStart().StartsWith("SELECT", StringComparison.OrdinalIgnoreCase))
throw new Exception("Only SELECT queries are allowed.");
#endregion
#region Save OpenAI Query Log
_ = Task.Run(() =>
{
try
{
var logService = new OpenAIQueryLogService();
logService.Save(new OpenAIQueryLog
{
UserId = userID,
UserPrompt = prompt,
GeneratedQuery = sql,
ExecutedAt = DateTime.Now,
ReportGroup = reportGroupType,
IsChart = isChart,
ChartType = GetAIChartType(aiResponseObject.ChartType),
Title = aiResponseObject.Title,
IsCommon = false
});
}
catch (Exception logEx)
{
Console.WriteLine($"[LogError] OpenAI log failed: {logEx.Message}");
}
});
#endregion
#region Execute SQL and Build Data
DataSet data = GetOpenAIData(sql);
var chartData = BuildOpenAIResponseWithData(data, aiResponseObject);
return Newtonsoft.Json.JsonConvert.SerializeObject(chartData);
#endregion
}
catch (OperationCanceledException)
{
throw new Exception("AI request timed out. Please try again.");
}
catch (HttpRequestException httpEx) when (httpEx.Message.Contains("429") || httpEx.Message.Contains("rate limit", StringComparison.OrdinalIgnoreCase))
{
if (attempt == maxRetries)
throw new Exception("Sorry, our AI service is busy now. Please try again after some time.");
await Task.Delay(TimeSpan.FromSeconds(delaySeconds));
delaySeconds *= 2;
}
catch (Exception ex)
{
if (ex.Message.Contains("You've reached our limits of messages", StringComparison.OrdinalIgnoreCase) ||
ex.Message.Contains("rate limit", StringComparison.OrdinalIgnoreCase))
{
if (attempt == maxRetries)
throw new Exception("Sorry, our AI service is busy now. Please try again after some time.");
await Task.Delay(TimeSpan.FromSeconds(delaySeconds));
delaySeconds *= 2;
continue;
}
throw new Exception(returnMessage + ex.Message, ex);
}
}
throw new Exception("Failed to get a response from AI after multiple attempts. Please try again later.");
}
#endregion
#region Build OpenAI Response with Data
public OpenAIResponseWithData BuildOpenAIResponseWithData(DataSet ds, OpenAIResponse aiResponseObject)
{
var table = ds.Tables[0];
var labels = new List<string>();
var values = new List<double>();
if (aiResponseObject.IsChart)
{
labels = table.AsEnumerable().Select(r => r[0].ToString()).ToList();
values = table.AsEnumerable().Select(r => Convert.ToDouble(r[1])).ToList();
}
var tableJson = ProcessDataSetToJson(ds);
return new OpenAIResponseWithData
{
IsChart = aiResponseObject.IsChart,
ChartType = aiResponseObject.ChartType,
Title = aiResponseObject.Title,
Labels = labels,
Values = values,
TableData = tableJson
};
}
#endregion
#region Build Initial Description (System Prompt)
public string BuildInitialDescriptionWithChart(EnumReportGroupType reportGroupType)
{
var sb = new StringBuilder();
sb.AppendLine("You are an expert SQL assistant.");
sb.AppendLine("Given a user request and the following database schema, your job is to:");
sb.AppendLine("1.Always generate a valid SQL SELECT queries in Microsoft SQL Server (T-SQL) syntax.");
sb.AppendLine("2.Always generate SQL in a single line, without extra newlines or unnecessary spaces.");
sb.AppendLine("3.Never return DELETE, UPDATE, INSERT queries or any data-modifying operation.You are read-only.");
sb.AppendLine("4.Detect if the user wants a chart/graph.");
sb.AppendLine("5.Always return only valid JSON following structure:" +
"{\"isChart\":true|false,\"chartType\":\"pie|bar|line|scatter|area|histogram|waterfall|gantt|radar|activity\",\"title\":\"<title>\",\"sql\":\"<SQL>\",\"message\":\"\"}");
sb.AppendLine("6.Title must be short, human-readable, based on the user request.");
sb.AppendLine("7.If the user asks modification deletion or insartion requested, set sql:'' and message:" +
"Sorry, I can only help with retrieving data. I am not allowed to update or delete any records.");
#region Check
if (reportGroupType == EnumReportGroupType.Employee)
{
sb.AppendLine("8.If the user request is not related to the payroll management system or does not match the provided schema,set \"sql\": \"\" and " +
"\"message\": \"I can only help with questions related to Employee Information\"");
}
if (reportGroupType == EnumReportGroupType.Performance)
{
sb.AppendLine("8.If the user request is not related to the payroll management system or does not match the provided schema,set \"sql\": \"\" and " +
"\"message\": \"I can only help with questions related to Employee Performance\"");
}
if (reportGroupType == EnumReportGroupType.Salary)
{
sb.AppendLine("8.If the user request is not related to the payroll management system or does not match the provided schema,set \"sql\": \"\" and " +
"\"message\": \"I can only help with questions related to Employee Salary\"");
}
if (reportGroupType == EnumReportGroupType.Training)
{
sb.AppendLine("8.If the user request is not related to the payroll management system or does not match the provided schema,set \"sql\": \"\" and " +
"\"message\": \"I can only help with questions related to Employee Training\"");
}
if (reportGroupType == EnumReportGroupType.Attendance)
{
sb.AppendLine("8.If the user request is not related to the payroll management system or does not match the provided schema,set \"sql\": \"\" and " +
"\"message\": \"I can only help with questions related to Employee Attendance\"");
}
if (reportGroupType == EnumReportGroupType.Leave)
{
sb.AppendLine("8.If the user request is not related to the payroll management system or does not match the provided schema,set \"sql\": \"\" and " +
"\"message\": \"I can only help with questions related to Employee Leave\"");
}
#endregion
sb.AppendLine("9.Do not return actual data values or any explanations.");
return sb.ToString();
}
#endregion
#region Dynamic Schema Generation (System Prompt)
public string BuildDynamicSchemaPrompt(EnumReportGroupType reportGroupType, DateTime nextPayprocessDate)
{
TransactionContext tc = null;
try
{
tc = TransactionContext.Begin();
DataTable columns = GetColumnDefinitions(tc, reportGroupType);
DataTable relations = GetColumnRelations(tc,reportGroupType);
DataTable allData = null;
if (reportGroupType == EnumReportGroupType.Salary || reportGroupType == EnumReportGroupType.Leave)
{
allData = GetAllType(tc, reportGroupType);
}
tc.End();
var sb = new StringBuilder();
sb.AppendLine("Database Schema:");
var pkColumns = new Dictionary<string, List<string>>();
var grouped = columns.AsEnumerable().GroupBy(row => row["TABLENAME"].ToString()).OrderBy(g => g.Key);
#region Table Definitions
foreach (var tableGroup in grouped)
{
string tableName = tableGroup.Key;
var columnDefs = new List<string>();
var pks = new List<string>();
foreach (var row in tableGroup)
{
string columnName = row["COLUMNNAME"].ToString();
string caption = row["CAPTION"]?.ToString();
string dataType = ((EnumColumnDataType)Convert.ToInt32(row["DATATYPE"])).ToString();
bool isPK = row.Table.Columns.Contains("IsPrimaryKey") && Convert.ToBoolean(row["IsPrimaryKey"]);
bool isFK = row.Table.Columns.Contains("IsForeignKey") && Convert.ToBoolean(row["IsForeignKey"]);
string label = $"{columnName}";
if (isPK)
{
label += "(PK)";
pks.Add(columnName);
}
else if (isFK)
{
label += "(FK)";
}
label += $" ({dataType})";
if (!string.IsNullOrWhiteSpace(caption) && caption != columnName)
label += $" = {caption}";
columnDefs.Add(label);
}
sb.AppendLine($"-{tableName}({string.Join(", ", columnDefs)})");
if (pks.Any())
pkColumns[tableName] = pks;
}
#endregion
#region RelationShip Definitions
if (relations.Rows.Count > 0)
{
sb.AppendLine("Relationships:");
foreach (DataRow rel in relations.Rows)
{
string master = rel["MASTERTABLENAME"].ToString();
string masterCol = rel["MASTERCOLUMN1NAME"].ToString();
string link = rel["LINKTABLENAME"].ToString();
string linkCol = rel["LINKCOLUMN1NAME"].ToString();
sb.AppendLine($"-{link}.{linkCol} → {master}.{masterCol}");
}
}
#endregion
sb.AppendLine(BuildInitialNoted(reportGroupType, nextPayprocessDate, allData));
return sb.ToString();
}
catch (Exception e)
{
#region Handle Exception
if (tc != null)
tc.HandleError();
ExceptionLog.Write(e);
throw new ServiceException(e.Message, e);
#endregion
}
}
public string BuildInitialNoted(EnumReportGroupType reportGroupType, DateTime nextPayprocessDate, DataTable allData = null)
{
var sb = new StringBuilder();
sb.AppendLine("Note:");
sb.AppendLine("-Never include primary key (PK) or Foreign Key(FK) columns in SELECT output. For reference only.");
if (reportGroupType == EnumReportGroupType.Employee)
{
sb.AppendLine("-The status field can have synonyms: 'Live' or 'Active' mean the same thing (Live). 'Discontinue' or 'Inactive' mean the same thing (Discontinue).");
sb.AppendLine("-Employees with no record in the 'EmployeeSpouse' table are considered Unmarried.");
}
if (allData != null && reportGroupType == EnumReportGroupType.Salary)
{
if (allData.Columns.Contains("Head"))
{
var heads = allData.AsEnumerable()
.Select(r => r["Head"]?.ToString())
.Where(h => !string.IsNullOrEmpty(h))
.Distinct()
.ToList();
if (heads.Any())
{
sb.AppendLine($"-The 'Head' column represents different salary components such as: {string.Join(",", heads)}");
}
}
sb.AppendLine($"-Always use SalaryMonth(always last date of month) in the SQL WHERE clause.");
sb.AppendLine($"-If a user is not specifies any date or year in the request, always use SalaryMonth = {GlobalFunctions.LastDateOfMonth(nextPayprocessDate).ToString("dd MMM yyyy")} in the SQL WHERE clause.");
sb.AppendLine("-The columns BirthDate, JoiningDate, SalaryMonth should always be returned in the format 'dd MMM yyyy'.");
}
if (allData != null && reportGroupType == EnumReportGroupType.Leave)
{
if (allData.Columns.Contains("LeaveType"))
{
var heads = allData.AsEnumerable()
.Select(r => r["LeaveType"]?.ToString())
.Where(h => !string.IsNullOrEmpty(h))
.Distinct()
.ToList();
if (heads.Any())
{
sb.AppendLine($"-The 'LeaveType' column represents different Leave Type such as: {string.Join(",", heads)}");
}
}
sb.AppendLine("-The Columns BirthDate, JoiningDate, FromDate and ToDate should always be returned in the format 'dd MMM yyyy'.");
}
if (reportGroupType == EnumReportGroupType.Attendance)
{
sb.AppendLine("-The columns BirthDate, JoiningDate and AttendanceDate should always be returned in the format 'dd MMM yyyy'.");
sb.AppendLine("-The columns InTime and OutTime should always be returned in the format 'hh:mm:ss aa'.");
sb.AppendLine("-The 'AttendanceStatus' column represents the daily attendance of an employee and may include: 'Present', 'Absent', 'Delay', 'Leave', 'Holiday', 'Weekly Off', 'Out Side Duty', 'Weekly Holiday', 'Compensation', 'Manual', 'Late', 'Early', 'HalfDay', 'LOA'.");
sb.AppendLine("-The 'LateStatus' column already stores values as 'Yes' (Late) or 'No' (On Time).");
sb.AppendLine("-The 'WorkDayType' column defines the type of the day: 'Weekly Holiday', 'National Holiday', 'Working Day', 'HartalDay', 'Weekly and National'.");
}
if (reportGroupType == EnumReportGroupType.Employee || reportGroupType == EnumReportGroupType.Training)
{
sb.AppendLine("-The columns BirthDate, JoiningDate, MarriageDate, EmployementEndDate and NominationDate should always be returned in the format 'dd MMM yyyy'.");
sb.AppendLine("-The 'EmploymentEndDate' column represents when an employee left the company (retirement, resignation, termination, etc)");
}
if (reportGroupType == EnumReportGroupType.Training)
{
sb.AppendLine("-The TrainingStatus field can have Enrolled|Completed|Unknown");
sb.AppendLine("-The columns StartDate,EndDate,BirthDate,JoiningDate,LineManagerBirthDate,LineManagerJoiningDate should always be returned in the format 'dd MMM yyyy'.");
}
return sb.ToString();
}
#endregion
#region Build Instruction (System Prompt)
public string BuildInstructionWithChart(EnumReportGroupType reportGroupType)
{
var sb = new StringBuilder();
sb.AppendLine("Instructions:");
sb.AppendLine("-Treat any mention of EmployeeID or EmployeeNo in user input as referring to Employee.EmployeeNo.");
sb.AppendLine("-Use appropriate JOINs based on foreign keys.");
sb.AppendLine("-If only certain columns are requested, include only those.");
sb.AppendLine("-Always map user-friendly aliases (e.g., 'Employee No', 'Department Name') to the correct table and column.");
sb.AppendLine("-If a date condition is mentioned, use WHERE clause accordingly.");
sb.AppendLine("-Support ordering, limits (TOP/OFFSET), and filtering.");
return sb.ToString();
}
#endregion
#region OpenAI Previous Query Execution
public async Task<string> OpenAIPreviousQueryExecution(OpenAIQueryLog oQueryLog)
{
try
{
oQueryLog.TotalExecution++;
#region Update OpenAI Query Log Total Execution
Task.Run(() =>
{
try
{
var logService = new OpenAIQueryLogService();
logService.UpdateQueryLogTotalExecution(oQueryLog);
}
catch (Exception ex)
{
Console.WriteLine($"[LogError] OpenAI log failed: {ex.Message}");
}
});
#endregion
string sql = oQueryLog.GeneratedQuery;
DataSet data = GetOpenAIData(sql);
OpenAIResponse aiResponseObject = new OpenAIResponse
{
IsChart = oQueryLog.IsChart,
ChartType = GetAIChartType(oQueryLog.ChartType),
Sql = sql,
Message = string.Empty,
Title = oQueryLog.Title
};
var chartData = BuildOpenAIResponseWithData(data, aiResponseObject);
return Newtonsoft.Json.JsonConvert.SerializeObject(chartData);
}
catch (Exception ex)
{
throw new Exception(returnMessage);
}
}
#endregion
#region Process DataSet to JSON
public string ProcessDataSetToJson(DataSet OpenAiData)
{
string output;
DataSet information = new DataSet();
information = OpenAiData;
var value = (object)information;
switch (value)
{
case null:
output = "";
break;
case DataSet ds:
output = JsonSerializer.Serialize(ds.Tables.Cast<DataTable>()
.Select(tbl => new {
TableName = tbl.TableName,
Rows = tbl.Rows.Cast<DataRow>()
.Select(row => tbl.Columns.Cast<DataColumn>()
.ToDictionary(col => col.ColumnName, col => row[col]))
}),
new JsonSerializerOptions { WriteIndented = true }
);
break;
case DataTable dt:
output = JsonSerializer.Serialize(
dt.Rows.Cast<DataRow>()
.Select(row => dt.Columns.Cast<DataColumn>()
.ToDictionary(col => col.ColumnName, col => row[col])),
new JsonSerializerOptions { WriteIndented = true }
);
break;
case IEnumerable<object> list:
output = JsonSerializer.Serialize(list, new JsonSerializerOptions { WriteIndented = true });
break;
case string str:
output = str;
break;
default:
output = JsonSerializer.Serialize(value, new JsonSerializerOptions
{
WriteIndented = true,
IgnoreReadOnlyProperties = true,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
});
break;
}
return output;
}
#endregion
#region Get OpenAI Data (Generate OpenAI's Provided SQL Query)
public DataSet GetOpenAIData(string sql)
{
TransactionContext tc = null;
DataSet information = new DataSet();
try
{
tc = TransactionContext.Begin();
information = OpenAIDA.GetOpenAIData(tc, sql);
tc.End();
}
catch (Exception e)
{
#region Handle Exception
if (tc != null)
tc.HandleError();
ExceptionLog.Write(e);
throw new ServiceException(e.Message, e);
#endregion
}
return information;
}
#endregion
#region Get 'Column Definitions' and 'Relations Between Tables'
public DataTable GetColumnDefinitions(TransactionContext tc, EnumReportGroupType reportGroupType)
{
DataTable oColumnDefinition = new DataTable();
try
{
oColumnDefinition = OpenAIDA.GetColumnDefinitions(tc, reportGroupType);
}
catch (Exception e)
{
#region Handle Exception
if (tc != null)
tc.HandleError();
ExceptionLog.Write(e);
throw new ServiceException(e.Message, e);
#endregion
}
return oColumnDefinition;
}
public async Task<DataTable> GetColumnDefinitions(EnumReportGroupType reportGroupType)
{
DataTable oColumnDefinition = new DataTable();
TransactionContext tc = null;
try
{
tc = TransactionContext.Begin();
oColumnDefinition = OpenAIDA.GetColumnDefinitions(tc, reportGroupType);
tc.End();
}
catch (Exception e)
{
#region Handle Exception
if (tc != null)
tc.HandleError();
ExceptionLog.Write(e);
throw new ServiceException(e.Message, e);
#endregion
}
return oColumnDefinition;
}
public DataTable GetColumnRelations(TransactionContext tc, EnumReportGroupType reportGroupType)
{
DataTable oColumnRelation = new DataTable();
try
{
oColumnRelation = OpenAIDA.GetColumnRelations(tc, reportGroupType);
}
catch (Exception e)
{
#region Handle Exception
if (tc != null)
tc.HandleError();
ExceptionLog.Write(e);
throw new ServiceException(e.Message, e);
#endregion
}
return oColumnRelation;
}
#endregion
#region Get All Type for (Leave Type & Salary Head)
public DataTable GetAllType(TransactionContext tc, EnumReportGroupType reportGroupType)
{
DataTable oColumnDefinition = new DataTable();
try
{
oColumnDefinition = OpenAIDA.GetAllType(tc, reportGroupType);
}
catch (Exception e)
{
#region Handle Exception
if (tc != null)
tc.HandleError();
ExceptionLog.Write(e);
throw new ServiceException(e.Message, e);
#endregion
}
return oColumnDefinition;
}
#endregion
#region Get AI Chart Type
public EnumAIChartType GetAIChartType(string type)
{
switch (type.ToLower())
{
case "pie":
return EnumAIChartType.PieChart;
case "bar":
return EnumAIChartType.BarChart;
case "line":
return EnumAIChartType.LineGraph;
case "scatter":
return EnumAIChartType.ScatterPlot;
case "area":
return EnumAIChartType.AreaChart;
case "histogram":
return EnumAIChartType.Histogram;
case "waterfall":
return EnumAIChartType.WaterfallChart;
case "gantt":
return EnumAIChartType.GantChart;
case "radar":
return EnumAIChartType.RadarChart;
case "activity":
return EnumAIChartType.ActivityDiagram;
default:
return EnumAIChartType.None;
}
}
public string GetAIChartType(EnumAIChartType type)
{
switch (type)
{
case EnumAIChartType.PieChart:
return "pie";
case EnumAIChartType.BarChart:
return "bar";
case EnumAIChartType.LineGraph:
return "line";
case EnumAIChartType.ScatterPlot:
return "scatter";
case EnumAIChartType.AreaChart:
return "area";
case EnumAIChartType.Histogram:
return "histogram";
case EnumAIChartType.WaterfallChart:
return "waterfall";
case EnumAIChartType.GantChart:
return "gantt";
case EnumAIChartType.RadarChart:
return "radar";
case EnumAIChartType.ActivityDiagram:
return "activity";
default:
return "";
}
}
#endregion
#region Total OpenAI Call
public int TotalOpenAICall()
{
TransactionContext tc = null;
int? totalCall = 0;
try
{
tc = TransactionContext.Begin();
DataReader dr = new DataReader(OpenAIDA.TotalOpenAICall(tc));
if (dr.Read())
{
totalCall = dr.GetInt32(0);
}
dr.Close();
tc.End();
}
catch (Exception e)
{
#region Handle Exception
if (tc != null)
tc.HandleError();
ExceptionLog.Write(e);
throw new ServiceException(e.Message, e);
#endregion
}
return totalCall ?? 0;
}
#endregion
}
public class AIServiceUsageLimitException : Exception
{
public AIServiceUsageLimitException(string message) : base(message) { }
}
}