QPR ProcessAnalyzer Expression Examples: Difference between revisions
No edit summary |
No edit summary |
||
Line 315: | Line 315: | ||
</pre> | </pre> | ||
Basic model usage | |||
<pre> | <pre> | ||
Models; | Models; | ||
Models[0].EventLog; | Models[0].EventLog; | ||
Line 327: | Line 327: | ||
Def("show", "cases", cases.(CalcId + ": " + StringJoin("->", Events.Type.Name))) | Def("show", "cases", cases.(CalcId + ": " + StringJoin("->", Events.Type.Name))) | ||
show(el.Cases) | show(el.Cases) | ||
</pre> | |||
Arrays | Arrays | ||
<pre> | |||
[1,2,3][0] | [1,2,3][0] | ||
[1,2,3][Count(_)-1] | [1,2,3][Count(_)-1] | ||
[1,2,3][[0,Count(_) - 1]] | [1,2,3][[0,Count(_) - 1]] | ||
[1,2,3][NumberRange(0, Count(_)-1)] | [1,2,3][NumberRange(0, Count(_)-1)] | ||
</pre> | |||
Calculating median | |||
<pre> | |||
Def("Median", "array", (Let("a", OrderByValue(array)), If(Count(a) % 2 == 1, a[(Count(a) - 1) / 2], (a[(Count(a) / 2) - 1] + a[Count(a) / 2]) / 2))); | Def("Median", "array", (Let("a", OrderByValue(array)), If(Count(a) % 2 == 1, a[(Count(a) - 1) / 2], (a[(Count(a) / 2) - 1] + a[Count(a) / 2]) / 2))); | ||
Median([4,1,3,2]) | Median([4,1,3,2]) | ||
Median([1,2,3]) | Median([1,2,3]) | ||
</pre> | |||
Remove tandem repeats of length 1 | |||
<pre> | |||
Def("RemoveTandemRepeatsOfLength1", "a", (Let("prev", null),ForEach("item", a, If(item == prev, _remove, (Set("prev", item), item)))._)); | Def("RemoveTandemRepeatsOfLength1", "a", (Let("prev", null),ForEach("item", a, If(item == prev, _remove, (Set("prev", item), item)))._)); | ||
RemoveTandemRepeatsOfLength1([1,2,2,3,5,5,5,5,2,5]) | RemoveTandemRepeatsOfLength1([1,2,2,3,5,5,5,5,2,5]) | ||
</pre> | |||
Aggregation functions | Aggregation functions | ||
<pre> | |||
Percentage of cases having event type named "Invoice" | |||
Average(Count(el.Cases.Events.Where(Type.Name == "Invoice"))) | Average(Count(el.Cases.Events.Where(Type.Name == "Invoice"))) | ||
Average flow durations for non-starter/-finisher flows | |||
Average(el.Flows:FlowOccurrences.Where(!IsNull(From) && !IsNull(To), _remove).(To.TimeStamp - From.TimeStamp)) | Average(el.Flows:FlowOccurrences.Where(!IsNull(From) && !IsNull(To), _remove).(To.TimeStamp - From.TimeStamp)) | ||
Average of all the flow durations | |||
Average(Flatten(el.Flows:FlowOccurrences.Where(!IsNull(From) && !IsNull(To), _remove).(To.TimeStamp - From.TimeStamp))) | Average(Flatten(el.Flows:FlowOccurrences.Where(!IsNull(From) && !IsNull(To), _remove).(To.TimeStamp - From.TimeStamp))) | ||
</pre> | |||
Context labeling | Context labeling | ||
<pre> | |||
Let("a", ["foo": ["a": 1, "b": 2], "bar": 2]) | Let("a", ["foo": ["a": 1, "b": 2], "bar": 2]) | ||
GetContext(a[0]) | GetContext(a[0]) | ||
GetValueOfContext("bar", a) | GetValueOfContext("bar", a) | ||
</pre> | |||
Recursion | Recursion | ||
<pre> | |||
el.Events[0].Recurse(NextInCase) | el.Events[0].Recurse(NextInCase) | ||
el.Events[0].RecurseWithHierarchy(NextInCase) | el.Events[0].RecurseWithHierarchy(NextInCase) | ||
el.Events[0].RecursiveFind(NextInCase, Type.Name == "Invoice") | el.Events[0].RecursiveFind(NextInCase, Type.Name == "Invoice") | ||
el.Flows.Where(IsNull(From)).To.RecurseWithHierarchy(OutgoingFlows.To, !IsNull(_), 2) | el.Flows.Where(IsNull(From)).To.RecurseWithHierarchy(OutgoingFlows.To, !IsNull(_), 2) | ||
</pre> | |||
Functions | Functions | ||
<pre> | |||
// Create Fibonacci number generator and a tester for it | // Create Fibonacci number generator and a tester for it | ||
Def("Fib", "a", If(a < 2, 1, Fib(a - 1) + Fib(a - 2))); | Def("Fib", "a", If(a < 2, 1, Fib(a - 1) + Fib(a - 2))); | ||
Def("FibTest", "a", For("i", 0, i < a, i + 1, Fib(i))); | Def("FibTest", "a", For("i", 0, i < a, i + 1, Fib(i))); | ||
FibTest(10); | FibTest(10); | ||
</pre> | |||
// Call function objects using given parameters | // Call function objects using given parameters | ||
Line 377: | Line 392: | ||
Def("LastOccurrence", "&condition", _.Events[Count(_) - 1].RecursiveFind(PreviousInCase, condition())[0]); | Def("LastOccurrence", "&condition", _.Events[Count(_) - 1].RecursiveFind(PreviousInCase, condition())[0]); | ||
TimeSpan(0, 0, 0, Average(el.Cases.(Catch(FirstOccurrence(Type.Name == "Invoice").TimeStamp - LastOccurrence(Type.Name == "Sales Order").TimeStamp, _remove).TotalSeconds))) | TimeSpan(0, 0, 0, Average(el.Cases.(Catch(FirstOccurrence(Type.Name == "Invoice").TimeStamp - LastOccurrence(Type.Name == "Sales Order").TimeStamp, _remove).TotalSeconds))) | ||
</pre> | |||
Miscellaneous examples | Miscellaneous examples | ||
<pre> | |||
Return all starter flows | |||
RemoveLeaves(el.Flows:From.Where(IsNull(_))) | RemoveLeaves(el.Flows:From.Where(IsNull(_))) | ||
el.Flows.Where(IsNull(From)) | el.Flows.Where(IsNull(From)) | ||
Create a range of dates | |||
Def("DateRange", "minYear", "minMonth", "maxYear", "maxMonth", (Let("min", DateTime(minYear, minMonth)),Let("max", DateTime(maxYear, maxMonth)),For("dt", DateTime(min.Year, min.month), dt <= DateTime(max.Year, max.Month), dt.Month == 12 ? DateTime(dt.Year + 1, 1) : DateTime(dt.Year, dt.Month + 1), dt))) | Def("DateRange", "minYear", "minMonth", "maxYear", "maxMonth", (Let("min", DateTime(minYear, minMonth)),Let("max", DateTime(maxYear, maxMonth)),For("dt", DateTime(min.Year, min.month), dt <= DateTime(max.Year, max.Month), dt.Month == 12 ? DateTime(dt.Year + 1, 1) : DateTime(dt.Year, dt.Month + 1), dt))) | ||
DateRange(2011, 5, 2013, 1) | DateRange(2011, 5, 2013, 1) | ||
Group numbers into groups with specified minimum, maximum and range size. | |||
Def("GroupNumbers", "a", "min", "max", "rangeSize", a.Ceiling(Max(0.0, Min(((max - min) / rangeSize) + 1, (_ - min)/rangeSize)))); | Def("GroupNumbers", "a", "min", "max", "rangeSize", a.Ceiling(Max(0.0, Min(((max - min) / rangeSize) + 1, (_ - min)/rangeSize)))); | ||
GroupNumbers([-20, 0, 0.2, 3.2, 7, 9.2, 20], 0, 9, 3) | GroupNumbers([-20, 0, 0.2, 3.2, 7, 9.2, 20], 0, 9, 3) | ||
Show cases having at least one "Sales Order Changed (VA02)" | |||
show(el.Cases.Where(Count(Events.Where(Type.Name == "Sales Order Changed (VA02)")) > 0)) | show(el.Cases.Where(Count(Events.Where(Type.Name == "Sales Order Changed (VA02)")) > 0)) | ||
show(Flatten(el.Cases.Events[0].RecursiveFind(NextInCase, Type.Name == "Sales Order Changed (VA02)").Case)) | show(Flatten(el.Cases.Events[0].RecursiveFind(NextInCase, Type.Name == "Sales Order Changed (VA02)").Case)) | ||
Show cases having "Sales Order Changed (VA02)" before "Outbound Delivery" at least once | |||
show(el.Cases.Where(([Events.Type.Name].Coalesce(Min(IndexOfSubArray("Sales Order Changed (VA02)"))-Max(IndexOfSubArray("Outbound Delivery")), 0))[0] < 0)) | show(el.Cases.Where(([Events.Type.Name].Coalesce(Min(IndexOfSubArray("Sales Order Changed (VA02)"))-Max(IndexOfSubArray("Outbound Delivery")), 0))[0] < 0)) | ||
show(Flatten(el.Cases.Events[0].RecursiveFind(NextInCase, Type.Name == "Sales Order Changed (VA02)").RecursiveFind(NextInCase, Type.Name == "Outbound Delivery").Case)) | show(Flatten(el.Cases.Events[0].RecursiveFind(NextInCase, Type.Name == "Sales Order Changed (VA02)").RecursiveFind(NextInCase, Type.Name == "Outbound Delivery").Case)) | ||
Workload analysis | |||
Def("ActiveEventsAt", "at", "attribute", "value", | Def("ActiveEventsAt", "at", "attribute", "value", | ||
Cases:Events[0] | Cases:Events[0] | ||
Line 416: | Line 431: | ||
ReplaceLeafValues(el.ActiveEventsAt(DateTime(2011,9,6), "Organization", "Sales"), "item", [item, item.Organization], 0) | ReplaceLeafValues(el.ActiveEventsAt(DateTime(2011,9,6), "Organization", "Sales"), "item", [item, item.Organization], 0) | ||
Get names and durations of all the cases sorted by case duration grouped by variations sorted by the number of cases in each variation. | |||
ReplaceLeafValues(OrderBy(OrderBy(el.Variations, CaseCount):Cases, Duration), "leaf", "" + leaf.Name + ": " + leaf.Duration, 0) | ReplaceLeafValues(OrderBy(OrderBy(el.Variations, CaseCount):Cases, Duration), "leaf", "" + leaf.Name + ": " + leaf.Duration, 0) | ||
Conformance analysis | |||
Let("xml", LoadTextFile("C:\\Users\\marhink\\Desktop\\SAP o2c.bpmn")); | Let("xml", LoadTextFile("C:\\Users\\marhink\\Desktop\\SAP o2c.bpmn")); | ||
Let("cm", ConformanceModelFromXml(xml)); | Let("cm", ConformanceModelFromXml(xml)); | ||
Line 427: | Line 442: | ||
cases.(Let("analysis",AnalyzeConformance(cm)),""+(CountTop(analysis)==0)+((CountTop(analysis)>0)? " ("+StringJoin("",ReplaceLeafValues(analysis,"i",i[0]==3?i[2]+"->"+i[3]:(i[0]==2?"DNF":"")))+")":"")+": "+StringJoin("->",Events.Type.Name)) | cases.(Let("analysis",AnalyzeConformance(cm)),""+(CountTop(analysis)==0)+((CountTop(analysis)>0)? " ("+StringJoin("",ReplaceLeafValues(analysis,"i",i[0]==3?i[2]+"->"+i[3]:(i[0]==2?"DNF":"")))+")":"")+": "+StringJoin("->",Events.Type.Name)) | ||
Hierarchy | |||
el.RecurseWithHierarchy(OutgoingFlows.To, !IsNull(_), 10); | el.RecurseWithHierarchy(OutgoingFlows.To, !IsNull(_), 10); | ||
cm.StartEvents.RecurseWithHierarchy(OutgoingFlows.To, !IsNull(_), 10); | cm.StartEvents.RecurseWithHierarchy(OutgoingFlows.To, !IsNull(_), 10); |
Revision as of 11:07, 6 April 2018
The following examples assume that there is a filter with id 1. point. All KPI analyses also have an EventLog as a starting point, so expression written after the "EventLogById(1)." is a valid expression for KPI Analyses.
Get all event types that appear among the events:
EventLogById(1).EventTypes
Same as previous, except return event type names (Strings):
EventLogById(1).EventTypes.Name
Get a hierarchical array, where the upper level are EventTypes and the leaf level are Events:
EventLogById(1).EventTypes.Events
Get a hierarchical array, where the upper level are EventTypes and the leaf level are also EventTypes (all branches contain same EventTypes):
EventLogById(1).EventTypes:Events.Type
EventLogById(1).EventTypes:Events.TimeStamp
List all Events as leaf nodes which timestamp is after 2012-01-01:
EventLogById(1).EventTypes:Events.Where(Timestamp > DateTime(2012,1,1))
Calculates count of Events in the leaf nodes:
Count(EventLogById(1).EventTypes:Events.Where(Timestamp > DateTime(2012,1,1)))
Events are ordered beginning from the last one:
EventLogById(1).EventTypes.OrderByDescending(Events, Timestamp)
Same as the previous but with explicit context specified:
EventLogById(1).EventTypes.OrderByDescending(_.Events, _.Timestamp)
Shows all event types in a single comma separated string list:
EventLogById(1).(StringJoin(", ", EventTypes.Name))
Same as the previous, except in addition the EventTypes are ordered by name:
EventLogById(1).(StringJoin(", ", OrderByDescending(EventTypes, Name).Name))
List of all the Starter flows in a model from filterId=1:
RemoveLeaves(EventLogById(1).Flows:From.Where(IsNull(_)))
Go through the model recursively using the flows:
RemoveLeaves(EventLogById(1).Flows:From.Where(IsNull(_))).To.OutgoingFlows.To.OutgoingFlows.To
Traverse the model using events:
RemoveLeaves(EventLogById(1).Flows:From.Where(IsNull(_))).FlowOccurrences.To.NextInCase.NextInCase.NextInCase
All case attribute values within the model grouped by case attribute types:
Let("cases", EventLogById(1).Cases, EventLogById(1).CaseAttributes:(Let("attribute", _, cases.Attribute(attribute))))
All event attribute values within the model grouped by event attribute types:
Let("events", EventLogById(1).Events, EventLogById(1).EventAttributes:(Let("attribute", _, events.Attribute(attribute))))
Define a new user defined functions named "First", which takes the first item in an array:
Def("First", "a", GetAt(0, a));
The defined array can be used as follows:
First(Array(1, 2, 3)) returns: 1
Def("x", "a", Distinct(RecurseLeaves(OrderBy(EventLogById(a).Cases.Events, Type.Name), Type.Name)))
Function definition that uses itself, i.e. recursive functions:
Def("Fib", "a", If(a < 2, 1, Fib(a - 1) + Fib(a - 2))); For("i", 0, i < 10, i + 1, Fib(i));
Takas all models, and show them in "id:name" format:
Models.StringJoin(":", [Id, Name])
Models.[Id, Name]
How many times activity "Sales Order Changed" occurs on average in cases?
Average(Count(Cases.Events.Where(Type.Name == "Sales Order Changed")))
What's the average cost per case of all sales order changes? (assumption Sales Order Changed activity has attribute Cost)
Average(Sum(Cases.Events.Where(Type.Name == "Sales Order Changed").Cost))
What's the total cost of all events that occured this year?
Sum(Sum(Cases.Events.Where(TimeStamp.Year == Now.Year).Cost))
What the total cost calculated from events, where cost is calculated by "unit cost" times "number of units"?
Sum( Cases.Sum( Events.( UnitCost * NumberOfUnits ) ) )
In how many cases duration between "Invoice Sent" and "Payment Received" is more than 30 days?
Count( Cases.Where( ( Max( Events.Where( In(Type.Name, "et1", "et2") ).TimeStamp ) - Min( Events.Where( In(Type.Name, "et1", "et2") ).TimeStamp ) ).TotalMinutes > 60 ) )
In how many cases "Invoice Sent" is not directly followed by "Payment Received"?
Count( RemoveLeaves( ( Cases.Events.Type.Name ).Where( Count( IndexOfSubArray( Array("et2", "et3") ) ) < Count( IndexOfSubArray( "et2" ) ) ) ) )
In how many cases "Payment Received" is before "Invoice Sent"?
Count( RemoveLeaves( ( Cases.Events.Type.Name ).Where( Catch( Max( IndexOfSubArray("et3") ) < Min( IndexOfSubArray( "et1" ) ), false ) ) ) )
In how many cases, invoice wasn't paid within the due date? (i.e. invoice is due and it's not paid, or invoice was paid after due date)
Count( Cases.Where( ( (Count( Events.Where( Type.Name == "Invoice Paid" ) ) == 0) && ( Catch( GetAt(0, Events.[Invoice Sent].StopOnNull()) < Now , false) || Catch( GetAt(0, Events.[Due Date].StopOnNull()) < Now , false) ) ) || ( Catch( GetAt(0, Events.Where( Type.Name == "Invoice Paid" ).TimeStamp ) > Coalesce( Catch(GetAt(0, Events.[Invoice Sent]), null), Catch(GetAt(0, Events.[Due Date]), null) ), null) ) ) ) )
Cases average duration between the first occurrence of event A and last occurrence of event B. Average is calculated only for those cases that contain both events A and B. Usecase: How long it takes to receive payment on average?
Average( Sum( Cases.Events.Where( Type.Name == "Payment Received" ).( TimeStamp - GetAt( 0, Case.Events.Where( Type.Name == "Invoice Sent" ) ).TimeStamp ) .TotalMinutes ) )
What is the % of POs (cases) in a certain price range (case attribute "price")?
100 * Count(Cases.Where(Price > 1 && Price < 4)) / Count(Cases)
What is the average duration from event A directly followed by event B for those cases that directly continue to event C after B? (take the first occurrence if there are many)
Average( Cases.Let( "indexes", Box( Events.Type.Name ).IndexOfSubArray( Array("et2", "et3", "et4") ) ).Where( Count(indexes) > 0 ).( GetAt( GetAt(0, GetAt(0, indexes) ), Events ) ).( NextInCase.TimeStamp - TimeStamp ).TotalMinutes )
What is the average duration of completely received orders?
Average( Cases.Where( Count( Events.Where( Type.Name == "et3" ) ) > 0 ).( GetAt(0, Events.Where( Type.Name == "et2" ).TimeStamp ) - GetAt(0, Events.Where( Type.Name == "et1" ).TimeStamp ) ).TotalMinutes )
Basic model usage
Models; Models[0].EventLog; Let("el", Models[0].EventLog); el.Cases el.Cases.Events el.Cases:Events el.Cases:Events.Type.Name el.Cases.StringJoin("->", Events.Type.Name) Def("show", "cases", cases.(CalcId + ": " + StringJoin("->", Events.Type.Name))) show(el.Cases)
Arrays
[1,2,3][0] [1,2,3][Count(_)-1] [1,2,3][[0,Count(_) - 1]] [1,2,3][NumberRange(0, Count(_)-1)]
Calculating median
Def("Median", "array", (Let("a", OrderByValue(array)), If(Count(a) % 2 == 1, a[(Count(a) - 1) / 2], (a[(Count(a) / 2) - 1] + a[Count(a) / 2]) / 2))); Median([4,1,3,2]) Median([1,2,3])
Remove tandem repeats of length 1
Def("RemoveTandemRepeatsOfLength1", "a", (Let("prev", null),ForEach("item", a, If(item == prev, _remove, (Set("prev", item), item)))._)); RemoveTandemRepeatsOfLength1([1,2,2,3,5,5,5,5,2,5])
Aggregation functions
Percentage of cases having event type named "Invoice" Average(Count(el.Cases.Events.Where(Type.Name == "Invoice"))) Average flow durations for non-starter/-finisher flows Average(el.Flows:FlowOccurrences.Where(!IsNull(From) && !IsNull(To), _remove).(To.TimeStamp - From.TimeStamp)) Average of all the flow durations Average(Flatten(el.Flows:FlowOccurrences.Where(!IsNull(From) && !IsNull(To), _remove).(To.TimeStamp - From.TimeStamp)))
Context labeling
Let("a", ["foo": ["a": 1, "b": 2], "bar": 2]) GetContext(a[0]) GetValueOfContext("bar", a)
Recursion
el.Events[0].Recurse(NextInCase) el.Events[0].RecurseWithHierarchy(NextInCase) el.Events[0].RecursiveFind(NextInCase, Type.Name == "Invoice") el.Flows.Where(IsNull(From)).To.RecurseWithHierarchy(OutgoingFlows.To, !IsNull(_), 2)
Functions
// Create Fibonacci number generator and a tester for it Def("Fib", "a", If(a < 2, 1, Fib(a - 1) + Fib(a - 2))); Def("FibTest", "a", For("i", 0, i < a, i + 1, Fib(i))); FibTest(10);
// Call function objects using given parameters [Def("", "a", "b", a+b), Def("", "a", "b", a*b)].(_(1,2))
// The sum of all the durations between the first occurrence of "Invoice" and the last occurrence of "Sales Order" event types. Def("FirstOccurrence", "&condition", _.Events[0].RecursiveFind(NextInCase, condition())[0]); Def("LastOccurrence", "&condition", _.Events[Count(_) - 1].RecursiveFind(PreviousInCase, condition())[0]); TimeSpan(0, 0, 0, Average(el.Cases.(Catch(FirstOccurrence(Type.Name == "Invoice").TimeStamp - LastOccurrence(Type.Name == "Sales Order").TimeStamp, _remove).TotalSeconds)))
Miscellaneous examples
Return all starter flows RemoveLeaves(el.Flows:From.Where(IsNull(_))) el.Flows.Where(IsNull(From)) Create a range of dates Def("DateRange", "minYear", "minMonth", "maxYear", "maxMonth", (Let("min", DateTime(minYear, minMonth)),Let("max", DateTime(maxYear, maxMonth)),For("dt", DateTime(min.Year, min.month), dt <= DateTime(max.Year, max.Month), dt.Month == 12 ? DateTime(dt.Year + 1, 1) : DateTime(dt.Year, dt.Month + 1), dt))) DateRange(2011, 5, 2013, 1) Group numbers into groups with specified minimum, maximum and range size. Def("GroupNumbers", "a", "min", "max", "rangeSize", a.Ceiling(Max(0.0, Min(((max - min) / rangeSize) + 1, (_ - min)/rangeSize)))); GroupNumbers([-20, 0, 0.2, 3.2, 7, 9.2, 20], 0, 9, 3) Show cases having at least one "Sales Order Changed (VA02)" show(el.Cases.Where(Count(Events.Where(Type.Name == "Sales Order Changed (VA02)")) > 0)) show(Flatten(el.Cases.Events[0].RecursiveFind(NextInCase, Type.Name == "Sales Order Changed (VA02)").Case)) Show cases having "Sales Order Changed (VA02)" before "Outbound Delivery" at least once show(el.Cases.Where(([Events.Type.Name].Coalesce(Min(IndexOfSubArray("Sales Order Changed (VA02)"))-Max(IndexOfSubArray("Outbound Delivery")), 0))[0] < 0)) show(Flatten(el.Cases.Events[0].RecursiveFind(NextInCase, Type.Name == "Sales Order Changed (VA02)").RecursiveFind(NextInCase, Type.Name == "Outbound Delivery").Case)) Workload analysis Def("ActiveEventsAt", "at", "attribute", "value", Cases:Events[0] .RecursiveFind( NextInCase, TimeStamp < at && (Attribute(attribute) == value) && (IsNull(NextInCase) || (NextInCase.TimeStamp > at)) ) ); Def("ActiveEventsAt", "at", "attribute", "value",Cases:Events[0].RecursiveFind(NextInCase,TimeStamp < at && (Attribute(attribute) == value) && (IsNull(NextInCase) || (NextInCase.TimeStamp > at)))); ReplaceLeafValues(el.ActiveEventsAt(DateTime(2011,9,6), "Organization", "Finance"), "item", [item, item.Organization], 0) ReplaceLeafValues(el.ActiveEventsAt(DateTime(2011,9,6), "Organization", "Delivery"), "item", [item, item.Organization], 0) ReplaceLeafValues(el.ActiveEventsAt(DateTime(2011,9,6), "Organization", "Sales"), "item", [item, item.Organization], 0) Get names and durations of all the cases sorted by case duration grouped by variations sorted by the number of cases in each variation. ReplaceLeafValues(OrderBy(OrderBy(el.Variations, CaseCount):Cases, Duration), "leaf", "" + leaf.Name + ": " + leaf.Duration, 0) Conformance analysis Let("xml", LoadTextFile("C:\\Users\\marhink\\Desktop\\SAP o2c.bpmn")); Let("cm", ConformanceModelFromXml(xml)); Let("vars", OrderByDescending(EventLogById(1).Variations, CaseCount)); vars.(Let("analysis",AnalyzeConformance(cm)),""+CaseCount+" * "+(CountTop(analysis)==0)+((CountTop(analysis)>0)?" ("+StringJoin("",ReplaceLeafValues(analysis,"i",i[0]==3?i[2]+"->"+i[3]:(i[0]==2?"DNF":"")))+")":"")+": "+StringJoin("->",EventTypes.Name)) Let("cases", OrderBy(EventLogById(1).Cases, Name)); cases.(Let("analysis",AnalyzeConformance(cm)),""+(CountTop(analysis)==0)+((CountTop(analysis)>0)? " ("+StringJoin("",ReplaceLeafValues(analysis,"i",i[0]==3?i[2]+"->"+i[3]:(i[0]==2?"DNF":"")))+")":"")+": "+StringJoin("->",Events.Type.Name)) Hierarchy el.RecurseWithHierarchy(OutgoingFlows.To, !IsNull(_), 10); cm.StartEvents.RecurseWithHierarchy(OutgoingFlows.To, !IsNull(_), 10);