Skip to content

Commit

Permalink
Refactored RuleEngine class; Initiated Arithmetic implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
phillipsk authored Feb 20, 2018
1 parent 5898b77 commit 192942c
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 160 deletions.
Binary file added Fruits 50.1.xlsm
Binary file not shown.
105 changes: 65 additions & 40 deletions Main.bas
Original file line number Diff line number Diff line change
Expand Up @@ -31,70 +31,95 @@ Sub Main()
'1. Add arithmetic operators like +/-
'2. Try Macro record to general basic macros

'Tasks
'=====
'
'1. [ ] Add NotEq <> operator
'2. [ ] Understand Str and CStr functions in VBA
'
'
'
'Others
'======
'1. [ ] Simplify Select Case in RuleEngine
'2. [ ] Improve AND/OR logic



' Read values from worksheet and rules + apply rules

Dim row As RowInfo

Dim engine As New RuleEngine
Set engine.Rows = ReadAllRows("Groceries")
Set engine.rules = ReadAllRules("Rules")
Call engine.Apply


' Update category column in Groceries worksheet.
UpdateCategory engine

UpdateSummary engine

' Updating worksheet based on results
' MsgBox (GetTotal(New totalProvider, engine))


End Sub

' Updates the category column with update rule results.
Private Sub UpdateCategory(engine As RuleEngine)

' Updating worksheet based on results
Dim row As RowInfo
Dim ws As Worksheet
Set ws = Worksheets("Groceries")
Dim r As Range
Dim Total As Double
Dim fruitTotal As Double
fruitTotal = 0
Total = 0

Dim totalFormula As String
totalFormula = "=SUM("
' C4,C5,C8
Dim Total As Double: Total = 0

For Each row In engine.Rows
Set r = ws.Range("A" & row.RowNumber)

'COLUMN WHERE CATEGORY IS PRINTED

ws.Range("M" & row.RowNumber).ClearContents
ws.Range("M" & row.RowNumber).Value = row.Category

'Debug.Print row.Columns("Id") & "=" & row.Category
r.Interior.ColorIndex = 0

Next
End Sub

Private Sub UpdateSummary(engine As RuleEngine)
Dim row As RowInfo
Dim pRule As rule
Dim offset As Integer
Dim colHeaders As New Collection
colHeaders.Add ("PriceL")
colHeaders.Add ("PriceB")

Dim colHeader1 As String
Dim colHeader2 As String
Dim colHeader3 As String
Dim colHeader4 As String

colHeader1 = Worksheets("Summary").Range("B1").Value
colHeader2 = Worksheets("Summary").Range("C1").Value
' go through all available rules
For Each pRule In engine.rules
Worksheets("Summary").Range("A2").offset(offset, 0) = pRule.Category

' Get total for each rule
Dim pTotal1 As Double: pTotal1 = 0
Dim pTotal2 As Double: pTotal2 = 0
For Each row In engine.Rows
If row.Category = pRule.Category Then
pTotal1 = pTotal1 + row.Columns(colHeader1)
pTotal2 = pTotal2 + row.Columns(colHeader2)
End If
Next
Worksheets("Summary").Range("B2").offset(offset, 0) = pTotal1
Worksheets("Summary").Range("C2").offset(offset, 0) = pTotal2


If row.Category = "RuleIV" Then
r.Interior.ColorIndex = 6
Total = Total + row.Columns("PriceL")
totalFormula = totalFormula & ws.Range("C" & row.RowNumber).Address & ","
End If

If row.Category = "FruitI" Then
r.Interior.ColorIndex = 4
fruitTotal = fruitTotal + row.Columns("PriceL")
End If

offset = offset + 1
Next
'MsgBox total
'row.PriceL
ws.Range("C19").Value = Total
ws.Range("C20").Value = fruitTotal
totalFormula = Left(totalFormula, Len(totalFormula) - 1)
totalFormula = totalFormula & ")"

' check if there were no matching cells for total formula
If totalFormula <> "=SUM)" Then
ws.Range("C21").Value = totalFormula
Else
ws.Range("C21").Value = ""
End If
End Sub

'Private Function GetTotal(totalProvider As totalProvider, engine As RuleEngine) As Double
' GetTotal = totalProvider.GetTotal(engine)
'End Function

9 changes: 3 additions & 6 deletions RuleColumn.cls
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,16 @@ Attribute VB_Creatable = False
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False

Public Header As String
Public Name As String
'Public Operator As String
Public Operator As Operators
Public Value As String
'Public ColType As ColumnType
Public Link As String
Public ReturnValue As String
Public ReturnID As String
Public PrintAble As Boolean



Enum ColumnType
[String] = 1
[Integer] = 2
[Double] = 3
End Enum

163 changes: 49 additions & 114 deletions RuleEngine.cls
Original file line number Diff line number Diff line change
Expand Up @@ -13,33 +13,24 @@ Public Rows As New Collection

Public Sub Apply()



Dim row As RowInfo
Dim rule As rule

For Each row In Rows ' goes through all rows

' Test all rules against selected row
Call ResetRules
For Each rule In rules
TestRule row, rule ' takes 1 row and apply 1 rule to it
Next

' For Each rule In Rules
' TestRule row, rule, Category
' Next


Next

End Sub


Sub TestRule(row As RowInfo, rule As rule)
Dim ruleCol As RuleColumn
Dim FoundMatch As Boolean
FoundMatch = False
Dim FoundMatch As Boolean: FoundMatch = False ' match in overall rule
Dim NewMatch As Boolean: NewMatch = False ' match in current rule column
Dim Link As String: Link = "" ' Link with next condition in the rule
Dim SkipFor As Boolean
Dim rule1value As Boolean
Dim rule2value As Boolean
Expand All @@ -53,130 +44,71 @@ Sub TestRule(row As RowInfo, rule As rule)
rule1value = False
rule2value = False

Dim strRowVal As String
Dim numRowVal As Double
Dim strRuleVal As String
Dim numRuleVal As Double
Dim IsNum As Boolean
If IsNumeric(ruleCol.Value) Then
numRowVal = Val(row.Columns(ruleCol.Name))
numRuleVal = Val(ruleCol.Value)
IsNum = True
Else
strRowVal = row.Columns(ruleCol.Name)
strRuleVal = ruleCol.Value
IsNum = False
End If

If SkipFor Then GoTo ContinueFor
Select Case ruleCol.Operator
Case Operators.Eq
'Debug.Print ruleCol.Name & "=" & row.Columns(ruleCol.Name)
If IsNumeric(ruleCol.Value) Then
If Val(row.Columns(ruleCol.Name)) = Val(ruleCol.Value) Then

If ruleCol.Link <> "AND" Then
FoundMatch = True
End If

Else
FoundMatch = False
SkipFor = True
End If
Else
'If Str(row.Columns(ruleCol.Name)) = Str(ruleCol.Value) Then
If row.Columns(ruleCol.Name) = ruleCol.Value Then
If ruleCol.Link <> "AND" Then
FoundMatch = True
End If

Else
FoundMatch = False
SkipFor = True
End If
End If

If IsNum Then NewMatch = (numRowVal = numRuleVal) Else NewMatch = (strRowVal = strRuleVal)
Case Operators.Gt

' If rule.RuleID = "II" Then
' Debug.Print
' End If


If IsNumeric(ruleCol.Value) Then
If Val(row.Columns(ruleCol.Name)) > Val(ruleCol.Value) Then

If ruleCol.Link = "AND" Then

Else

FoundMatch = True
End If

Else
FoundMatch = False
SkipFor = True
End If
Else
If Str(row.Columns(ruleCol.Name)) > Str(ruleCol.Value) Then

If ruleCol.Link = "AND" Then

Else

FoundMatch = True
End If

Else
FoundMatch = False
SkipFor = True
End If
End If


If IsNum Then NewMatch = (numRowVal > numRuleVal) Else NewMatch = (strRowVal > strRuleVal)
Case Operators.Lt
If IsNumeric(ruleCol.Value) Then
If Val(row.Columns(ruleCol.Name)) < Val(ruleCol.Value) Then

If ruleCol.Link = "AND" Then

Else

FoundMatch = True
End If

Else
FoundMatch = False
SkipFor = True
End If
Else
If Str(row.Columns(ruleCol.Name)) < Str(ruleCol.Value) Then

If ruleCol.Link = "AND" Then

Else

FoundMatch = True
End If

Else
FoundMatch = False
SkipFor = True
End If
End If

If IsNum Then NewMatch = (numRowVal < numRuleVal) Else NewMatch = (strRowVal < strRuleVal)
Case Operators.NoEQ
If IsNum Then NewMatch = (numRowVal <> numRuleVal) Else NewMatch = (strRowVal <> strRuleVal)
Case Operators.GtEq
If IsNum Then NewMatch = (numRowVal >= numRuleVal) Else NewMatch = (strRowVal >= strRuleVal)
Case Operators.LtEq
If IsNum Then NewMatch = (numRowVal <= numRuleVal) Else NewMatch = (strRowVal <= strRuleVal)
Case Operators.And
' Get Success value of given rule
' E.g. RuleI = True And RuleII = False

rule1value = IsRuleSuccess(ruleCol.Name)
rule2value = IsRuleSuccess(ruleCol.Value)
NewMatch = rule1value And rule2value

FoundMatch = rule1value And rule2value
Case Operators.Or

rule1value = IsRuleSuccess(ruleCol.Name)
rule2value = IsRuleSuccess(ruleCol.Value)

FoundMatch = rule1value Or rule2value
NewMatch = rule1value Or rule2value

End Select

If FoundMatch Then
'Debug.Print "Match!"
row.Category = rule.Category

' Update Rule
rule.Success = True
' Evaluate results
If Link = "AND" Then
If FoundMatch And NewMatch Then
row.Category = rule.Category
rule.Success = True
End If
ElseIf Link = "OR" Then
If FoundMatch Or NewMatch Then
row.Category = rule.Category
rule.Success = True
End If
Else
'row.Category = ""
If NewMatch Then
row.Category = rule.Category
rule.Success = True
End If
End If

FoundMatch = NewMatch
Link = ruleCol.Link ' Link for next rule column in for loop.
ContinueFor:
Next

Expand All @@ -189,6 +121,9 @@ Function IsRuleSuccess(ruleName As String) As Boolean
For Each r In rules
If ruleName = "Rule" & r.RuleID Then
result = r.Success
If result Then
Debug.Print result
End If
End If
Next
IsRuleSuccess = result
Expand Down

0 comments on commit 192942c

Please sign in to comment.