/* * dbMango * * Copyright 2025 Deutsche Bank AG * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using Newtonsoft.Json; using Rms.Risk.Mango.Pivot.Core.Models; using System.Drawing; using System.Text.RegularExpressions; using static Rms.Risk.Mango.Pivot.Core.IPivotTableDataSource; namespace Rms.Risk.Mango.Pivot.Core; public class PivotColumnDescriptor { [JsonIgnore] public Regex NameRegex { get; private set; } = null!; public Color Background { get; set; } public Color AlternateBackground { get; set; } public string Format { get; set; } = ""; public bool ShowTotals { get; set; } = true; public string NameRegexString { get; set { field = value; NameRegex = new( value, RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase | RegexOptions.Singleline ); } } = ""; } public enum PivotFieldPurpose { Data = 0, PrimaryKey1 = 1, PrimaryKey2 = 2, Key = 3, Info = 4, Hidden = 5 } public class PivotFieldDescriptor { public string Name { get; set; } = ""; public PivotFieldPurpose Purpose { get; set; } [JsonIgnore] public Type Type { get; set; } = typeof(object); public string TypeString { get => Type.Name; set => Type = Type.GetType( "System."+value ) ?? throw new ("Type not found: System."+value); } } public enum CollectionType { All, NoMeta, HaveMeta } public class GroupedPivot : ICloneable { public required string Text { get; init; } public bool IsGroup { get; init; } public required PivotDefinition Pivot { get; init; } public override string ToString() => Text; object ICloneable.Clone() => Clone(); public GroupedPivot Clone() => new() { Text = Text, IsGroup = IsGroup, Pivot = Pivot.Clone() }; } public class GroupedCollection { public required string DataSourcePrefix { get; init; } public string CollectionNameWithPrefix => $"{DataSourcePrefix}: {CollectionNameWithoutPrefix}"; public required string CollectionNameWithoutPrefix { get; init; } public bool IsGroup { get; init; } public PivotColumnDescriptor[] ColumnDescriptors { get; set; } = []; public HashSet DataFields { get; set; } = []; public HashSet KeyFields { get; set; } = []; public DateTime[] Cobs { get; set; } = []; public string[] Departments { get; set; } = []; public List Pivots { get; set; } = []; public Dictionary FieldTypes { get; set; } = []; public override string ToString() => CollectionNameWithPrefix; public void CopyFrom(GroupedCollection other) { if ( other == null ) throw new ArgumentNullException(nameof(other)); ColumnDescriptors = other.ColumnDescriptors; DataFields = other.DataFields; KeyFields = other.KeyFields; Cobs = other.Cobs; Departments = other.Departments; Pivots = other.Pivots; FieldTypes = other.FieldTypes; } public List GetDrilldownKeyFields(PivotFieldPurpose purpose) => FieldTypes .Where( x => x.Value.Purpose == purpose ) .Select(x => x.Key) .OrderBy(x => x) .ToList(); } public interface IPivotTableDataSource { string SourceId { get; } string Prefix { get; } string User { get; set; } Task> GetAllMeta(bool force = false, CancellationToken token = default); /// /// Get drilldown formula for the given column /// /// /// /// Value to be compared with. Only records not matching this value will be shown. /// If true "name = value", if false "name != value" /// /// Task GetDrilldownAsync(string collectionName, string name, string value = "\"\"", bool equals = false, CancellationToken token = default ); /// /// Aggregate data /// /// /// Pivot definition /// Extra $match stage /// Skip cached results /// /// /// /// Pivoted data Task PivotAsync( string collectionName, PivotDefinition def, FilterExpressionTree.ExpressionGroup? extraFilter, bool skipCache, string? userName = null, int maxFetchSize = -1, CancellationToken token = default ); public enum PivotType { Predefined, User, UserAndPredefined, All } Task UpdatePredefinedPivotsAsync(string collectionName, IEnumerable pivots, bool predefined = false, string? userName = null, CancellationToken token = default); Task UpdatePivotAsync(string collectionName, PivotDefinition pivot, string? userName = null, CancellationToken token = default) => UpdatePredefinedPivotsAsync(collectionName, [pivot], pivot.IsPredefined, userName, token); /// /// Preprocess def to get proper query text. /// Should perform all sort of postprocessing appied to the query def. /// /// mongo collection Name /// Pivot definition /// Extra $match stage /// /// Processed query Task GetQueryTextAsync(string collectionName, PivotDefinition def, FilterExpressionTree.ExpressionGroup? extraFilter, CancellationToken token = default ); /// /// Get a single document /// /// mongo collection Name /// Primary key fields in no particular order /// Extra $match stage /// /// Json string Task GetDocumentAsync(string collectionName, KeyValuePair [] keys, FilterExpressionTree.ExpressionGroup? extraFilter, CancellationToken token = default); /// /// Get a single document /// /// mongo collection Name /// Filter selecting a single document /// /// Json string Task GetDocumentAsync(string collectionName, FilterExpressionTree.ExpressionGroup filterText, CancellationToken token = default); /// /// Delete pivot from the collection. Works for user pivots only. /// /// mongo collection Name /// Pivot to delete /// Group pivot belongs to. Currently only "User Pivots" /// User who owns the pivot /// /// Task DeletePivotAsync(string collectionName, string pivotName, string groupName, string userName, CancellationToken token = default); } /// /// Low level metadata access interface. Should not be used directly in the application. Intended to be used internal caches only. /// Applications should use instead. /// public interface IPivotTableDataSourceMetaProvider { string SourceId { get; } string Prefix { get; } Task GetCollectionsAsync(CollectionType includeMeta = CollectionType.All, CancellationToken token = default); Task GetDepartmentsAsync(string collectionName, CancellationToken token = default); Task<(string, string)[]> GetDesksWithDepartmentAsync(string collectionName, CancellationToken token = default); Task GetKeyFieldsAsync(string collectionName, CancellationToken token = default); Task GetDrilldownKeyFieldsAsync(string collectionName, PivotFieldPurpose keyLevel, CancellationToken token = default); Task GetDataFieldsAsync(string collectionName, CancellationToken token = default); Task GetColumnDescriptorsAsync(string collectionName, CancellationToken token = default); Task GetCobDatesAsync(string collectionName, bool force = false,CancellationToken token = default); /// /// Get all field types including keys, data and calculated fields /// /// Field types Dictionary GetFieldTypes(string collectionName); Task> GetPivotsAsync(string collectionName, PivotType pivotType, string? userName = null, CancellationToken token = default); }