using InABox.Clients; using InABox.Core; using InABox.Scripting; using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Text; using System.Threading.Tasks; namespace InABox.Wpf.Dashboard; public interface IDynamicDashboardTable { string Key { get; set; } IEnumerable GetColumns(DynamicDashboardDataComponent data); } public interface IDynamicDashboardDataQuery : IDynamicDashboardTable { Type Type { get; } IFilter? Filter { get; set; } IColumns Columns { get; set; } ISortOrder? SortOrder { get; set; } IEnumerable IDynamicDashboardTable.GetColumns(DynamicDashboardDataComponent data) => Columns.Columns().Select(x => new CoreColumn { ColumnName = x.Name, DataType = x.Type }); } public class DynamicDashboardDataQuery : IDynamicDashboardDataQuery where T : Entity, IRemotable, new() { public string Key { get; set; } = typeof(T).Name; public Filter? Filter { get; set; } public Columns Columns { get; set; } = Core.Columns.None(); public SortOrder? SortOrder { get; set; } = null; #region IDynamicDashboardDataQuery Type IDynamicDashboardDataQuery.Type => typeof(T); IFilter? IDynamicDashboardDataQuery.Filter { get => Filter; set => Filter = value as Filter; } IColumns IDynamicDashboardDataQuery.Columns { get => Columns; set => Columns = (value as Columns) ?? Core.Columns.None(); } ISortOrder? IDynamicDashboardDataQuery.SortOrder { get => SortOrder; set => SortOrder = value as SortOrder; } #endregion } public class DynamicDashboardAdditionalTable : IDynamicDashboardTable { public string Key { get; set; } = ""; private string? _script; public string? Script { get => _script; set { if(_script != value) { _script = value; _scriptDocument = null; } } } public IEnumerable GetColumns(DynamicDashboardDataComponent data) => SetupColumns(data); private ScriptDocument? _scriptDocument; private ScriptDocument? ScriptDocument { get { if(_scriptDocument is null && Script is not null) { _scriptDocument = new(Script); _scriptDocument.Compile(); } return _scriptDocument; } } public static string DefaultScript() { return @"using InABox.Wpf.Dashboard; public class Module { public IEnumerable SetupColumns(DynamicDashboardDataComponent data) { // Return the list of columns for this table. return [ new CoreColumn(typeof(string), ""Column1""), new CoreColumn(typeof(int), ""Column2"")]; } public void PopulateTable(CoreTable table, DynamicDashboardData data) { // Populate 'table' using the data from 'data'. // For example, to add rows to the table: var newRow = table.NewRow(); newRow[""Column1""] = ""First Column Data""; newRow[""Column2""] = 1; table.Rows.Add(newRow); } }"; } private IEnumerable SetupColumns(DynamicDashboardDataComponent data) { if (ScriptDocument is null) return []; var method = ScriptDocument.GetMethod(methodName: "SetupColumns"); if(method is not null) { return method.Invoke(ScriptDocument!.GetObject(), [data]) as IEnumerable ?? []; } else { return []; } } public void PopulateTable(CoreTable table, DynamicDashboardData data) { if (ScriptDocument is null) return; ScriptDocument.Execute(methodname: "PopulateTable", parameters: [table, data]); } } public class DynamicDashboardDataComponent { public List Queries { get; set; } = new(); public List AdditionalTables { get; set; } = new(); public IEnumerable Tables => Queries.Concat(AdditionalTables); public IDynamicDashboardTable GetTable(string key) { return Tables.FirstOrDefault(x => x.Key == key) ?? throw new KeyNotFoundException($"Data table '{key}' does not exist."); } public bool TryGetTable(string key, [NotNullWhen(true)] out IDynamicDashboardTable? query) { query = Tables.FirstOrDefault(x => x.Key == key); return query != null; } private void PopulateAdditionalTables(DynamicDashboardData data) { foreach(var tableDef in AdditionalTables) { var table = new CoreTable(); table.Columns.AddRange(tableDef.GetColumns(this)); tableDef.PopulateTable(table, data); data.Data.Add(tableDef.Key, table); } } /// /// Run the query to get the data according to the definitions of . /// /// Set to a non- value to limit the number of records returned. Used for preview mode. public DynamicDashboardData RunQuery(int? maxRecords = null) { var range = maxRecords.HasValue ? CoreRange.Database(maxRecords.Value) : null; var queryDefs = Queries.Select(x => new KeyedQueryDef(x.Key, x.Type, x.Filter, x.Columns, x.SortOrder, range)); var results = Client.QueryMultiple(queryDefs); var data = new DynamicDashboardData(results.Results); PopulateAdditionalTables(data); return data; } /// /// Run the query asynchronously to get the data according to the definitions of . /// /// Set to a non- value to limit the number of records returned. Used for preview mode. public async Task RunQueryAsync(int? maxRecords = null) { var range = maxRecords.HasValue ? CoreRange.Database(maxRecords.Value) : null; var queryDefs = Queries.Select(x => new KeyedQueryDef(x.Key, x.Type, x.Filter, x.Columns, x.SortOrder, range)); var results = await Client.QueryMultipleAsync(queryDefs); var data = new DynamicDashboardData(results.Results); PopulateAdditionalTables(data); return data; } } public class DynamicDashboardData(Dictionary data) { public Dictionary Data { get; set; } = data; public bool TryGetData(string key, [NotNullWhen(true)] out CoreTable? query) { return Data.TryGetValue(key, out query); } }