From 9876b7b43b44574695a7eaa3c18efc6399deeb2b Mon Sep 17 00:00:00 2001 From: Chen Gong Date: Thu, 7 Nov 2024 09:12:10 -0600 Subject: [PATCH] add dashboard conversation backend service --- .../Repositories/IBotSharpRepository.cs | 2 + .../Users/IUserService.cs | 2 + .../Users/Models/Dashboard.cs | 25 ++++++++++ .../BotSharp.Abstraction/Users/Models/User.cs | 1 + .../FileRepository/FileRepository.User.cs | 27 +++++++++++ .../FileRepository/FileRepository.cs | 32 +++++++++++++ .../Users/Services/UserService.cs | 17 +++++++ .../Controllers/ConversationController.cs | 12 +++++ .../Controllers/DashboardController.cs | 47 +++++++++++++++++++ .../Conversations/ConversationViewModel.cs | 1 + .../Users/UserDashboardConversationModel.cs | 26 ++++++++++ 11 files changed, 192 insertions(+) create mode 100644 src/Infrastructure/BotSharp.Abstraction/Users/Models/Dashboard.cs create mode 100644 src/Infrastructure/BotSharp.OpenAPI/Controllers/DashboardController.cs create mode 100644 src/Infrastructure/BotSharp.OpenAPI/ViewModels/Users/UserDashboardConversationModel.cs diff --git a/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs b/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs index 1471093b..963b61be 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs @@ -26,9 +26,11 @@ public interface IBotSharpRepository List GetUserByIds(List ids) => throw new NotImplementedException(); User? GetUserByAffiliateId(string affiliateId) => throw new NotImplementedException(); User? GetUserByUserName(string userName) => throw new NotImplementedException(); + Dashboard? GetDashboard(string id = null) => throw new NotImplementedException(); void CreateUser(User user) => throw new NotImplementedException(); void UpdateExistUser(string userId, User user) => throw new NotImplementedException(); void UpdateUserVerified(string userId) => throw new NotImplementedException(); + void AddDashboardConversation(string userId, string conversationId) => throw new NotImplementedException(); void UpdateUserVerificationCode(string userId, string verficationCode) => throw new NotImplementedException(); void UpdateUserPassword(string userId, string password) => throw new NotImplementedException(); void UpdateUserEmail(string userId, string email) => throw new NotImplementedException(); diff --git a/src/Infrastructure/BotSharp.Abstraction/Users/IUserService.cs b/src/Infrastructure/BotSharp.Abstraction/Users/IUserService.cs index 421e217b..164017ea 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Users/IUserService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Users/IUserService.cs @@ -22,4 +22,6 @@ public interface IUserService Task UpdatePassword(string newPassword, string verificationCode); Task GetUserTokenExpires(); Task UpdateUsersIsDisable(List userIds, bool isDisable); + Task AddDashboardConversation(string userId, string conversationId); + Task GetDashboard(string userId); } \ No newline at end of file diff --git a/src/Infrastructure/BotSharp.Abstraction/Users/Models/Dashboard.cs b/src/Infrastructure/BotSharp.Abstraction/Users/Models/Dashboard.cs new file mode 100644 index 00000000..51d5f1a7 --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Users/Models/Dashboard.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BotSharp.Abstraction.Users.Models; + +public class Dashboard +{ + public IList ConversationList { get; set; } = []; +} + +public class DashboardComponent +{ + public required string Id { get; set; } + public string? Name { get; set; } +} + +public class DashboardConversation : DashboardComponent +{ + public string? ConversationId { get; set; } + public string? Instruction { get; set; } = "Default instruction: Ask bot to do something"; +} + diff --git a/src/Infrastructure/BotSharp.Abstraction/Users/Models/User.cs b/src/Infrastructure/BotSharp.Abstraction/Users/Models/User.cs index cb48701f..2d62fe66 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Users/Models/User.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Users/Models/User.cs @@ -23,6 +23,7 @@ public class User public bool Verified { get; set; } public string? AffiliateId { get; set; } public bool IsDisabled { get; set; } + public List DashboardConversations { get; set; } = []; public DateTime UpdatedTime { get; set; } = DateTime.UtcNow; public DateTime CreatedTime { get; set; } = DateTime.UtcNow; } diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.User.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.User.cs index 465ae961..0c731da1 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.User.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.User.cs @@ -41,6 +41,11 @@ public partial class FileRepository return Users.FirstOrDefault(x => x.UserName == userName.ToLower()); } + public Dashboard? GetDashboard(string id = null) + { + return Dashboards.FirstOrDefault(); + } + public void CreateUser(User user) { var userId = Guid.NewGuid().ToString(); @@ -68,4 +73,26 @@ public partial class FileRepository var path = Path.Combine(dir, USER_FILE); File.WriteAllText(path, JsonSerializer.Serialize(user, _options)); } + + public void AddDashboardConversation(string userId, string conversationId) + { + var user = GetUserById(userId); + if (user == null) return; + + // one user only has one dashboard currently + var dash = Dashboards.FirstOrDefault(); + dash ??= new(); + + var dashconv = new DashboardConversation + { + Id = Guid.NewGuid().ToString(), + ConversationId = conversationId + }; + + dash.ConversationList.Add(dashconv); + + var dir = Path.Combine(_dbSettings.FileRepository, USERS_FOLDER, userId); + var path = Path.Combine(dir, DASHBOARD_FILE); + File.WriteAllText(path, JsonSerializer.Serialize(dash, _options)); + } } diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.cs index 46f39aaa..30a7a475 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.cs @@ -23,6 +23,7 @@ public partial class FileRepository : IBotSharpRepository private const string AGENT_INSTRUCTION_FILE = "instruction"; private const string AGENT_SAMPLES_FILE = "samples.txt"; private const string USER_FILE = "user.json"; + private const string DASHBOARD_FILE = "dashboard.json"; private const string USER_AGENT_FILE = "agents.json"; private const string CONVERSATION_FILE = "conversation.json"; private const string STATS_FILE = "stats.json"; @@ -74,6 +75,7 @@ public partial class FileRepository : IBotSharpRepository } private List _users = new List(); + private List _dashboards = []; private List _agents = new List(); private List _userAgents = new List(); private List _conversations = new List(); @@ -106,6 +108,36 @@ public partial class FileRepository : IBotSharpRepository } } + private IQueryable Dashboards + { + get + { + if (!_dashboards.IsNullOrEmpty()) + { + return _dashboards.AsQueryable(); + } + + var dir = Path.Combine(_dbSettings.FileRepository, USERS_FOLDER); + _dashboards = []; + if (Directory.Exists(dir)) + { + foreach (var d in Directory.GetDirectories(dir)) + { + var dashboardFile = Path.Combine(d, DASHBOARD_FILE); + if (!Directory.Exists(d) || !File.Exists(dashboardFile)) + continue; + + var json = File.ReadAllText(dashboardFile); + var dash = JsonSerializer.Deserialize(json, _options); + + if (dash == null) continue; + _dashboards.Add(dash); + } + } + return _dashboards.AsQueryable(); + } + } + private IQueryable Agents { get diff --git a/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs b/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs index 10dbe8de..65de880c 100644 --- a/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs +++ b/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs @@ -640,4 +640,21 @@ public class UserService : IUserService } return true; } + + public async Task AddDashboardConversation(string userId, string conversationId) + { + var db = _services.GetRequiredService(); + db.AddDashboardConversation(userId, conversationId); + + await Task.CompletedTask; + return true; + } + + public async Task GetDashboard(string userId) + { + var db = _services.GetRequiredService(); + var dash = db.GetDashboard(); + await Task.CompletedTask; + return dash; + } } diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs index 91617a0f..00fe8ce3 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs @@ -490,6 +490,18 @@ public class ConversationController : ControllerBase } #endregion + #region miscellaneous + [HttpPut("/agent/{agentId}/conversation/{conversationId}/PinToDashboard")] + public async Task PinConversationToDashboard([FromRoute] string agentId, [FromRoute] string conversationId) + { + var userService = _services.GetRequiredService(); + + var user = await userService.GetUser(_user.Id); + var pinned = await userService.AddDashboardConversation(user.Id, conversationId); + return pinned; + } + #endregion + #region Private methods private void SetStates(IConversationService conv, NewMessageModel input) { diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/DashboardController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/DashboardController.cs new file mode 100644 index 00000000..70b04e47 --- /dev/null +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/DashboardController.cs @@ -0,0 +1,47 @@ +using BotSharp.Abstraction.Options; +using BotSharp.Abstraction.Users.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BotSharp.OpenAPI.Controllers; + +[Authorize] +[ApiController] +public class DashboardController : ControllerBase +{ + private readonly IServiceProvider _services; + private readonly IUserIdentity _user; + + public DashboardController(IServiceProvider services, + IUserIdentity user, + BotSharpOptions options) + { + _services = services; + _user = user; + + } + #region User Components + [HttpGet("/dashboard/components")] + public async Task GetComponents(string userId) + { + var userService = _services.GetRequiredService(); + var dashboardProfile = await userService.GetDashboard(userId); + if (dashboardProfile == null) return new UserDashboardModel(); + var result = new UserDashboardModel + { + ConversationList = dashboardProfile.ConversationList.Select( + x => new UserDashboardConversationModel + { + Name = x.Name, + ConversationId = x.ConversationId, + Instruction = x.Instruction + } + ).ToList() + }; + return result; + } + #endregion +} diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/ConversationViewModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/ConversationViewModel.cs index 05f8cb89..90b4ff67 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/ConversationViewModel.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/ConversationViewModel.cs @@ -12,6 +12,7 @@ public class ConversationViewModel [JsonPropertyName("agent_name")] public string AgentName { get; set; } + [JsonPropertyName("title")] public string Title { get; set; } = string.Empty; public UserViewModel User { get; set; } = new UserViewModel(); diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Users/UserDashboardConversationModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Users/UserDashboardConversationModel.cs new file mode 100644 index 00000000..3a5f3491 --- /dev/null +++ b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Users/UserDashboardConversationModel.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.Json.Serialization; +using System.Threading.Tasks; + +namespace BotSharp.OpenAPI.ViewModels.Users; +public class UserDashboardModel +{ + + [JsonPropertyName("conversation_list")] + public IList ConversationList { get; set; } = []; +} + +public class UserDashboardConversationModel +{ + [JsonPropertyName("name")] + public string? Name { get; set; } + + [JsonPropertyName("conversation_id")] + public string? ConversationId { get; set; } + + [JsonPropertyName("instruction")] + public string? Instruction { get; set; } +}