Example #1
0
 /// <summary>
 /// Runs Calcs for a grid, where the GridVm has been previously been built and is added to an ExhibitVm.
 /// </summary>
 /// <param name="exhibitVm">The ExhibitVm which contains the GridVm to run calcs on</param>
 /// <param name="gridCode">The GridVm in the ExhibitVm to run calcs for. If the grid is not found in the ExhibitVm, then the method returns without runnign any calcs.</param>
 public static void Process(ExhibitVm exhibitVm, string gridCode)
 {
     try
     {
         using (var db = new DEV_AF())
         {
             RunCalcs(db, exhibitVm, gridCode);
         }
     }
     catch(Exception e)
     {
         LogError(e);
     }
 }
Example #2
0
        public ActionResult GridProto(string gridCode)
        {
            var exhibit = new ExhibitVm()
            {
                Grids = new List<GridVm>()
            };

            var exhibitVm = ExhibitVmProcess.GetGridVmForUi(gridCode, exhibit);
            if (gridCode == "CalcGrid1")
            {
                exhibitVm = ExhibitVmProcess.GetGridVmForUi("CalcGrid2", exhibitVm);
            }
            CalcGridProcess.Process(exhibitVm, gridCode);

            if (gridCode == "CalcGrid1")
            {
                CalcGridProcess.Process(exhibitVm, "CalcGrid2");
            }

            return View(exhibitVm);
        }
Example #3
0
        private static void RunCalcs(DEV_AF db, ExhibitVm exhibit, string gridCode)
        {
            var grid = exhibit.Grids.FirstOrDefault(g => g.GridCode == gridCode);
            if (grid == null) return;
            var calcs = db.UspGetCalcs(grid.GridCode);
            var rowCalcResults = new List<UspGetCalcs_Result>();
            var colCalcResults = new List<UspGetCalcs_Result>();
            var cellCalcResults = new List<UspGetCalcs_Result>();
            var externalDependantCells = new List<CellVm>(); 
            //Separate ColCalcs, RowCalcs, and CellCalcs
            foreach (var calc in calcs)
            {
                if (string.IsNullOrEmpty(calc.TargetColCode) && calc.TargetGridCode == grid.GridCode)
                {
                    rowCalcResults.Add(calc);
                }
                else if (string.IsNullOrEmpty(calc.TargetRowCode) && calc.TargetGridCode == grid.GridCode)
                {
                    colCalcResults.Add(calc);
                }
                else
                {
                    cellCalcResults.Add(calc);
                }
                //identify external cells
                if (calc.TargetGridCode != grid.GridCode && !externalDependantCells.Any(ec => ec.GridCode == calc.TargetGridCode && ec.RowCode == calc.TargetRowCode && ec.ColCode == calc.TargetColCode))
                {
                    externalDependantCells.Add(new CellVm
                    {
                        GridCode = calc.TargetGridCode,
                        RowCode = calc.TargetRowCode,
                        ColCode = calc.TargetColCode
                    });
                }
                //Both target and operand may be external
                if (calc.GridCode != grid.GridCode && !externalDependantCells.Any(ec => ec.GridCode == calc.GridCode && ec.RowCode == calc.RowCode && ec.ColCode == calc.ColCode))
                {
                    externalDependantCells.Add(new CellVm
                    {
                        GridCode = calc.GridCode,
                        RowCode = calc.RowCode,
                        ColCode = calc.ColCode
                    });
                }
            }
            

            if (externalDependantCells.Any())
            {
                //Remove any external cells that are already in the VM.
                //Assumes that the VM in memory has the most up to date values for cells
                for (int i = 0; i < externalDependantCells.Count; i++)
                {
                    var externalDependantCell = externalDependantCells[i];
                    var externallGrid = exhibit.Grids.FirstOrDefault(g => g.GridCode == externalDependantCell.GridCode);
                    if (externallGrid == null) continue;
                    var externalRow = externallGrid.Rows.FirstOrDefault(r => r.RowCode == externalDependantCell.RowCode);
                    if (externalRow == null) continue;
                    var externalcell = externalRow.Cells.FirstOrDefault(r => r.RowCode == externalDependantCell.RowCode);
                    if (externalcell == null) continue;
                    externalDependantCells.RemoveAt(i);
                }
                ExhibitVmProcess.GetExternalCells(exhibit, externalDependantCells, db, cellCalcResults, null, null, null);
            }

            //Run Parent Child Calcs distinguished by RowRelationship (Context = total)
            //Get all Parent Rows (rows that have any children)
            var allParentRows = grid.Rows.Select(r => r.ParentRowCode).Distinct().ToList();
            if (allParentRows.Any())
            {
                //Get all parent rows (rows that have any children) whose children are not themselves parents (all children are not found in the collection of all parents 'allParentRows')
                //This is done to start the cascade of parent row calcs at the lowest level of ancestry, and work our way up to the topmost parent
                var startingRowSumCalcs = grid.Rows.Where(r => r.ChildRowCodes.Any() && r.ChildRowCodes.All(c => !allParentRows.Contains(c)));
                
                foreach (var row in startingRowSumCalcs)
                {
                    RunRowCalcSumChildrenLoop(row, grid);
                }
            }
            
            //Run all Row Calcs
            var rowCalcs = new List<CalcExpressionVm>();
            if (rowCalcResults.Any())
            {
                //Take the list of GetCalcs_Results and transform into a list of CalcExpressionVm
                rowCalcs = GroupCalcResultsIntoVm(rowCalcResults);
                
                //get colCalcs whose targets are not operands of other colCals, these will be run first, Calcs that have operands that update from running the first calcs are considered dependant calcs
                //  Upon performing the calc, recursively call "cascaded" calculations that are dependant on this calc
                var startingRowCalcs = rowCalcs.Where(rc => CalcOperandsAreNotTargetsOfOtherCalcs(rc, rowCalcs));

                foreach (var calc in startingRowCalcs)
                {
                    //run calcs
                    foreach (var col in grid.Columns.Where(c => c.Level == 0 && ColumnIsNumericOrPercent(c)))
                    {
                        RunRowCalcRecursive(calc, rowCalcs, col);
                    }
                }
            }

            //Run all Column Calcs
            var colCalcs = new List<CalcExpressionVm>();
            // pass in colCalcResults and grid
            if (colCalcResults.Any())
            {
                //Take the list of GetCalcs_Results and transform into a list of CalcExpressionVm
                colCalcs = GroupCalcResultsIntoVm(colCalcResults);

                //get colCalcs whose targets are not operands of other colCals, these will be run first, Calcs that have operands that update from running the first calcs are considered dependant calcs
                //  Upon performing the calc, recursively call "cascaded" calculations that are dependant on this calc
                var startingColCalcs = colCalcs.Where(cc => CalcOperandsAreNotTargetsOfOtherCalcs(cc, colCalcs));

                foreach (var calc in startingColCalcs)
                {
                    //run col calcs for every row
                    foreach (var row in grid.Rows)
                    {
                        RunColCalcRecursive(calc, colCalcs, row);
                    }
                }
            }
            
            //Run all Cell Calcs
            if(cellCalcResults.Any())
            {
                //Take the list of GetCalcs_Results and transform into a list of CalcExpressionVm
                var cellCalcs = GroupCalcResultsIntoVm(cellCalcResults);
                //Cell Value Calcs run first
                //Get Cell Calcs that are not targets of other calcs. These are the inital calcs that start the "Calc Cascade"
                var startingCellCalcs = cellCalcs.Where(cc => UpdateContextIsCellValue(cc.UpdateContext) && CalcOperandsAreNotTargetsOfOtherCalcs(cc, cellCalcs));
                //Run all the starting Calcs, these will recursively call the other calcs that have operands which are targets of the starting calcs
                foreach (var calc in startingCellCalcs)
                {
                    RunCellCalcRecursive(calc, cellCalcs, exhibit, rowCalcs, colCalcs);
                }
                //Delta Check calcs run last
                //No cascade happens with delta checks, all cell values should be correct at this point
                var deltaChecks = cellCalcs.Where(cc => UpdateContextIsDeltaCheck(cc.UpdateContext));
                //Even though delta checks will not cause a "cascade", we can still use the same recursive method to run the calcs, the recusion case will never trigger here
                foreach (var calc in deltaChecks)
                {
                    RunCellCalcRecursive(calc, cellCalcs, exhibit, rowCalcs, colCalcs);
                }
            }
        }
Example #4
0
 private static void SaveAllCellValues(DEV_AF db, ExhibitVm grid)
 {
     //TODO: save cells
 }
Example #5
0
        private static void RunCellCalcRecursive(CalcExpressionVm calcToRun, List<CalcExpressionVm> allCellCalcs, ExhibitVm exhibit, List<CalcExpressionVm> allRowCalcs, List<CalcExpressionVm> allColCalcs)
        {
            foreach (var operand in calcToRun.Operands)
            {
                var grid = exhibit.Grids.FirstOrDefault(g => g.GridCode == operand.GridCode);
                var row = grid.Rows.FirstOrDefault(r => r.RowCode == operand.RowCode);
                var cell = row.Cells.FirstOrDefault(c => c.ColCode == operand.ColCode);
                var cellValue = GetCellValue(cell);
                var operandToken = GetCellToken(operand.GridCode, operand.RowCode, operand.ColCode);
                calcToRun.Expression = calcToRun.Expression.Replace(operandToken, cellValue.ToString(CultureInfo.CurrentCulture));
            }

            var targetGrid = exhibit.Grids.FirstOrDefault(g => g.GridCode == calcToRun.TargetGridCode);
            var targetRow = targetGrid.Rows.FirstOrDefault(r => r.RowCode == calcToRun.TargetRowCode);
            var targetCell = targetRow.Cells.FirstOrDefault(c => c.ColCode == calcToRun.TargetColCode);
            EvaluateExpressionAndAssign(calcToRun, targetCell);

            if (!UpdateContextIsCellValue(calcToRun.UpdateContext)) return;
            //There may be some additional row/col calcs to be run now that we've updated a single cell
            RunAllRowAndColCalcsForCellRecursive(targetCell, allRowCalcs, allColCalcs, targetGrid);
            //Cascade
            var cascadedCellCalcs = allCellCalcs.Where(c => c.Operands.Any(o => o.GridCode == targetCell.GridCode && o.RowCode == targetCell.RowCode && o.ColCode == targetCell.ColCode)).ToList();
            if (cascadedCellCalcs.Any())
            {
                cascadedCellCalcs.ForEach(cc =>
                {
                    RunCellCalcRecursive(cc, allCellCalcs, exhibit, allRowCalcs, allColCalcs);
                });
            }
        }
Example #6
0
        /// <summary>
        /// Retrieves GridVm from DB and builds the full model, including calculations and template rows. If there are any cells from other grids that this grid is dependent upon (e.g. calcs that
        /// have operands from other grids) then additional, partially formed, GridVm's will be created for just those external dependent cells.
        /// </summary>
        /// <param name="gridCode">The GridCode the be built from the DB.</param>
        /// <param name="exhibit">OPTIONAL: The ExhibitVm that the GridVm (along with any other partially formed dependent gridvm's) will be added to. If null, a new ExhibitVm is instantiated.</param>
        /// <returns></returns>
        public static ExhibitVm GetGridVmForUi(string gridCode, ExhibitVm exhibit = null)
        {
            if (exhibit == null)
            {
                exhibit = InitializeNewExhibit();
            }
            var grid = InitializeNewGrid(gridCode, true);
            //Make sure any pre existing or partially formed grids are removed with this grid code
            exhibit.Grids.RemoveAll(g => g.GridCode == gridCode);
            exhibit.Grids.Add(grid);
            try
            {
                using (var db = new DEV_AF())
                {
                    //Make calls to DB for Calcs, Attributes, and Row Relationships, and various parms
                    var calcs = db.UspGetCalcs(gridCode).ToList();
                    var rowRelations = db.UspGetRowRelationship(gridCode, null).ToList();
                    var attribs = db.UspGetAttribVal(gridCode, "").ToList();
                    var showNegativeInParen = false;
                    var negativeParm = db.LOAD_PARAMETERS.FirstOrDefault(p => p.parm_name == "ShowNegativeInParensInUI");
                    if (negativeParm != null)
                    {
                        showNegativeInParen = negativeParm.parm_value == "Y";
                    }
                    grid.ShowNegativeNumsInParens = showNegativeInParen;

                    //Initalize calc objects
                    List<UspGetCalcs_Result> rowCalcResults = new List<UspGetCalcs_Result>();
                    List<UspGetCalcs_Result> cellCalcsExpandedFromRowCalcs = new List<UspGetCalcs_Result>();
                    List<UspGetCalcs_Result> colCalcResults = new List<UspGetCalcs_Result>();
                    List<UspGetCalcs_Result> cellCalcsExpandedFromColCalcs = new List<UspGetCalcs_Result>();
                    List<UspGetCalcs_Result> cellCalcResults = new List<UspGetCalcs_Result>();
                    //Initalize model for storing cells outside the current grid
                    var externalDependantCells = new List<CellVm>();
                    //Sort Calcs into RowCalcs, ColCalcs, and Cell Calcs. Also identify any Cells that the calcs are dependent upon that are outside of this grid
                    foreach (var calc in calcs)
                    {
                        if (string.IsNullOrEmpty(calc.TargetColCode) && calc.TargetGridCode == grid.GridCode)
                        {
                            rowCalcResults.Add(calc);
                        }
                        else if (string.IsNullOrEmpty(calc.TargetRowCode) && calc.TargetGridCode == grid.GridCode)
                        {
                            colCalcResults.Add(calc);
                        }
                        else
                        {
                            cellCalcResults.Add(calc);

                            //identify external cells
                            if (calc.TargetGridCode != grid.GridCode && !externalDependantCells.Any(externalCell => CellIsTargetOfCalc(externalCell, calc)))
                            {
                                externalDependantCells.Add(new CellVm()
                                {
                                    GridCode = calc.TargetGridCode,
                                    RowCode = calc.TargetRowCode,
                                    ColCode = calc.TargetColCode
                                });
                            }
                            //Both target and operand may be external
                            if (calc.GridCode != grid.GridCode && !externalDependantCells.Any(externalCell => CellIsOperandOfCalc(externalCell, calc)))
                            {
                                externalDependantCells.Add(new CellVm()
                                {
                                    GridCode = calc.GridCode,
                                    RowCode = calc.RowCode,
                                    ColCode = calc.ColCode
                                });
                            }
                        }
                    }

                    //ColCalcs will need to be expanded in UI when a user adds a row, save the unexpanded col calcs in the grid Vm
                    grid.ColCalcs.AddRange(GroupCalcResultsIntoExpressionVm(colCalcResults));
                    //Cell calcs do not need to be expanded, group the cellCalcResults into a collection of CalcExpressionVm
                    var calcExpressions = new List<CalcExpressionVm>();
                    calcExpressions.AddRange(GroupCalcResultsIntoExpressionVm(cellCalcResults));

                    var templateRowCodes = new List<string>();
                    var templateRowRelations = rowRelations.Where(IsAddRowTemplateRelationContext).ToList();
                    if (templateRowRelations.Any())
                    {
                        templateRowCodes = templateRowRelations.Select(tr => tr.ChRowCode).ToList();
                    }
                    var cellDictionary = new Dictionary<string, Attributes>();
                    var templateRowVms = new List<RowVm>();

                    foreach (var attrib in attribs)
                    {
                        if (!string.IsNullOrEmpty(attrib.RowCode) && !string.IsNullOrEmpty(attrib.ColCode))
                        {
                            cellDictionary.Add(attrib.GridCode + attrib.RowCode + attrib.ColCode, attrib);
                        }
                        else if (string.IsNullOrEmpty(attrib.RowCode) && !string.IsNullOrEmpty(attrib.ColCode))
                        {
                            grid.Columns.Add(BuildColVmFromAttributes(attrib));

                            //For Numeric columns expand the row calcs for every column and add to cell calcs
                            if (IsNumericOrPercentType(attrib.Type))
                                cellCalcsExpandedFromRowCalcs.AddRange(
                                    rowCalcResults.Select(rowCalc => new UspGetCalcs_Result()
                                    {
                                        CalcExpressionId = rowCalc.CalcExpressionId,
                                        TargetGridCode = rowCalc.TargetGridCode,
                                        TargetRowCode = rowCalc.TargetRowCode,
                                        TargetColCode = attrib.ColCode,
                                        UpdateContext = rowCalc.UpdateContext,
                                        Expression =
                                            rowCalc.Expression.Split('.')
                                                .Aggregate(
                                                    (c, n) => n == "" ? c + "." + attrib.ColCode + n : c + "." + n),
                                        GridCode = rowCalc.GridCode,
                                        RowCode = rowCalc.RowCode,
                                        ColCode = attrib.ColCode
                                    })
                                    .Where(rowCalc => !cellCalcResults.Any(cellCalc => CalcsHaveMatchingTargets(cellCalc, rowCalc) && CalcUpdatesCellValue(cellCalc))));
                        }
                        else if (!string.IsNullOrEmpty(attrib.RowCode) && string.IsNullOrEmpty(attrib.ColCode))
                        {
                            var row = BuildRowVmFromAttributes(attrib, rowRelations);
                            var isTemplateRow = String.Equals(attrib.OutType, Literals.Attribute.OutType.Model, StringComparison.CurrentCultureIgnoreCase) ;
                            if (isTemplateRow)
                            {
                                templateRowVms.Add(row);
                            }
                            else
                            {
                                grid.Rows.Add(row);
                            }

                            SetGridAttribsFromRowAttribs(grid, attrib);
                            if (attrib.Type == Literals.Attribute.RowType.Header || attrib.Type == Literals.Attribute.RowType.Blank || isTemplateRow) continue;

                            cellCalcsExpandedFromColCalcs.AddRange(
                                        colCalcResults.Select(colCalc => new UspGetCalcs_Result()
                                        {
                                            CalcExpressionId = colCalc.CalcExpressionId,
                                            TargetGridCode = colCalc.TargetGridCode,
                                            TargetRowCode = attrib.RowCode,
                                            TargetColCode = colCalc.TargetColCode,
                                            UpdateContext = colCalc.UpdateContext,
                                            Expression = colCalc.Expression.Split('.').Aggregate((c, n) => n == "" ? c + "." + attrib.RowCode + n : c + "." + n),
                                            GridCode = colCalc.GridCode,
                                            RowCode = attrib.RowCode,
                                            ColCode = colCalc.ColCode
                                        })
                                        .Where(colCalc => !cellCalcResults.Any(cellCalc => CalcsHaveMatchingTargets(cellCalc, colCalc) && CalcUpdatesCellValue(cellCalc)))
                                    );
                        }
                        else
                        {
                            //grid.IsEditable = attrib.IsEditable ?? true;   TODO - add this back in later
                            //grid.DisplayText = attrib.DisplayText;
                        }
                    }
                    
                    var colCalcExpressions = new List<CalcExpressionVm>();
                    colCalcExpressions.AddRange(GroupCalcResultsIntoExpressionVm(cellCalcsExpandedFromColCalcs));

                    var rowCalcExpressions = new List<CalcExpressionVm>();
                    rowCalcExpressions.AddRange(GroupCalcResultsIntoExpressionVm(cellCalcsExpandedFromRowCalcs));

                    var allExpandedCalcs = new List<UspGetCalcs_Result>();
                    allExpandedCalcs.AddRange(cellCalcResults);
                    allExpandedCalcs.AddRange(cellCalcsExpandedFromRowCalcs);
                    allExpandedCalcs.AddRange(cellCalcsExpandedFromColCalcs);
                    
                    //Convert all calcs into dictionaries for easy lookup when building individual cells
                    var cellCalcDic = ConvertCalcsToDictionary(calcExpressions);
                    var colCalcDic = ConvertCalcsToDictionary(colCalcExpressions);
                    var rowCalcDic = ConvertCalcsToDictionary(rowCalcExpressions);

                    if (externalDependantCells.Any())
                    {
                        GetExternalCells(exhibit, externalDependantCells, db, allExpandedCalcs, cellCalcDic, colCalcDic, rowCalcDic);
                    }
                    //remove expanded row and col calcs that target the same cell as a cell calc
                    //colCalcDic = colCalcDic.Where(cc => !cellCalcDic.Keys.Contains(cc.Key)).ToDictionary(source => source.Key, source => source.Value);
                    //rowCalcDic = rowCalcDic.Where(cc => !cellCalcDic.Keys.Contains(cc.Key)).ToDictionary(source => source.Key, source => source.Value);
                    
                    //Add Template Rows 
                    foreach (var templateRow in templateRowVms)
                    {
                        foreach (var col in grid.Columns.Where(c => c.Level == 0).OrderBy(c => c.DisplayOrder))
                        {
                            var cellAttrib = cellDictionary[grid.GridCode + templateRow.RowCode + col.ColCode];
                            var cell = BuildCellVmFromAttributes(grid, templateRow, col, cellAttrib);
                            templateRow.Cells.Add(cell);
                        }
                        AppendTemplateRowstoRowVm(templateRow, templateRowVms, templateRowRelations);
                    }
                    
                    foreach (var row in grid.Rows)
                    {
                        foreach (var col in grid.Columns.Where(c => c.Level == 0).OrderBy(c => c.DisplayOrder))
                        {
                            var cellAttrib = cellDictionary[grid.GridCode + row.RowCode + col.ColCode];
                            var cell = BuildCellVmFromAttributes(grid, row, col, cellAttrib);
                            cell.Calcs = IsNumericOrPercentType(col.Type)
                                ? GetCalcsForCell(allExpandedCalcs, cellCalcDic, colCalcDic, rowCalcDic, grid.GridCode, row.RowCode, col.ColCode)
                                : null;

                            row.Cells.Add(cell);
                            col.Cells.Add(cell);
                        }
                        AppendTemplateRowstoRowVm(row, templateRowVms, templateRowRelations);
                    }
                }
            }
            catch (Exception e)
            {
                var x = e;
            }
            return exhibit;
        }
Example #7
0
        /// <summary>
        /// Pass in a collection of cells, and retrieve the values for those cells from the DB. The cells and their values are added to the ExhibitVm (if they dont already exist).
        /// Cells are added in the same structure as a normal GridVm, but the full GridVm is not built, only the necessary Rows/Cells are added to this GridVm. Thus only a partial GridVm 
        /// is created for just the cells you specify.
        /// </summary>
        /// <param name="exhibit">The exhibit to add the resulting GridVm/RowVm/CellVm's to.</param>
        /// <param name="cells">The collection of cells to pull back from the DB.</param>
        /// <param name="db">The DbContext</param>
        /// <param name="allExpandedCalcs">All the "Expanded" calculations (row and col calcs expanded to cell calcs, plus the explicit cell calcs). Used for building the CellVm</param>
        /// <param name="cellCalcDic">The dictionary of </param>
        /// <param name="colCalcDic"></param>
        /// <param name="rowCalcDic"></param>
        public static void GetExternalCells(ExhibitVm exhibit, List<CellVm> cells, DEV_AF db, List<UspGetCalcs_Result> allExpandedCalcs, Dictionary<string, CalcExpressionVm> cellCalcDic, Dictionary<string, CalcExpressionVm> colCalcDic, Dictionary<string, CalcExpressionVm> rowCalcDic)
        {
            var cellParms = cells.Select(cell => new CellValue()
            {
                GridCode = cell.GridCode,
                RowCode = cell.RowCode,
                ColCode = cell.ColCode
            }).ToList();


            var result = db.UspGetCellVal(cellParms).ToList();
            foreach (var cellResult in result)
            {
                var grid = exhibit.Grids.FirstOrDefault(g => g.GridCode == cellResult.GridCode);
                if (grid == null)
                {
                    grid = InitializeNewGrid(cellResult.GridCode, false);
                    exhibit.Grids.Add(grid);
                }
                var row = grid.Rows.FirstOrDefault(r => r.RowCode == cellResult.RowCode);
                if (row == null)
                {
                    row = BuildRowVmForExternalGrid(cellResult.GridCode, cellResult.RowCode);
                    grid.Rows.Add(row);
                }
                var cellvm = row.Cells.FirstOrDefault(r => r.ColCode == cellResult.ColCode);
                if (cellvm == null)
                {
                    cellvm = BuildCellVmForExternalGrid(allExpandedCalcs, cellCalcDic, colCalcDic, rowCalcDic, cellResult.GridCode, cellResult.RowCode, cellResult.ColCode, cellResult.Val);
                    row.Cells.Add(cellvm);
                }
                else
                {
                    //I dont want to pull from DB actually, what's in memory is probabbly most up to date
                    //cellvm.Value = cellResult.Val;
                }
            }
        }