A simple rule engine with python regex and Jinja.

A simple rule engine with python regex and Jinja.

Many e-commerce platforms require a rule engine feature where I will focus on the most common requirement like,

  • promo-code or sale discount.
  • Showing some tags like (preorder, special discount) with some specific criteria and many more.

So to understand the promo-code related problem, A typical rule engine should have,

  • Basic filter criteria like the price should be greater than 500 etc.
  • Value of discount on promo-code with percentage calculation or flat discount.

This has two challenges,

  1. Easy interface to define filtering criteria.
  2. Efficiently finding valid criteria among all the filter-criteria. (exa. all valid applicable promo-code, maximum discount applicable among all valid promos defined in thousands number.)

In the first challenge, Now if the programmer has to write the rule he will choose very familiar json/yaml like structure configuration(exa: for travel website {“source”: [“del”, “bom”], “people”: {“min”: 2}}) . But this criteria is mostly going to be defined or managed by a business or commercial team who doesn’t have any clue of Json like structure.

So we need a more human-friendly interface which is less error-prone and easy to manage. After some consideration, we figured out, almost all the criteria is a combination of,

  • checking the value is part of the set. (like source in [‘del’, ‘bom’])
  • comparison based filter criteria (like price ≥ 500).

To achieve the combination of the above criteria we will still rely on and and or keyword same as python.

This is what we imagined to be a non-programmer user-friendly filter criteria structure,

  • part of a set: airline(indigo, spice-jet) and source(chennai, delhi)
  • comparison based: price.range(min, max) where one of the min or max can be optional.

If you think above filter criteria is human-friendly and easy to manage then let's create our table for one of the travel platform promo-code feature,

No alt text provided for this image

Now for the second part, our objective is to create a function which will return the best available discount against given criteria.

So my first step would be to convert this filter_criteria(with other argument) to equivalent Jinja template language. which will be compiled and used as discount calculator function.

For this, we will use python regex magic. here is sample code which identifies any pattern of,

alpha_numeric.optional_alphanumeric(comma separated alphanumeric) and call the substitute function for the matched object.

import re
def substitute(mt):
  a, b = mt.group(0).strip(')').split('(')
  b = map(lambda s: '"' + s.strip() + '"', b.split(','))
  return f"({a} in ({','.join(b)}))"

re.sub('[a-z0-9_]+(\.range)?\([A-Za-z0-9_ ,.-]+\)',
                     substitute, "attr(val1, val2)")


Ommits in: `(attr in ("val1","val2"))`

If we got the above function it should be easy to generate a template for given filter-criteria. Here is the complete code on GitHub(https://github.com/rosh11090/rule-engine/blob/master/rule-engine.py).

So Assuming discount should be calculated on price field, for the above filter criteria I will generate a Jinja template which will look like this,

{% if (source|string in (‘maa’,’del’)) and (2 <= pax|float < 6) %}200{% endif %}

{% if (source|string in (‘maa’,’del’)) and (2 <= pax|float < 6) %}200{% endif %}

{% if (sector|string in (‘blr-del’,’del-blr’)) and (airline|string in (‘indigo’,’spice-jet’)) and (4 <= pax|float) %}{{[price*10/100, 500]|min}}{% endif %}

If you are familiar with Django or Jinja templating, you can relate that this is template language conversion of same human-friendly criteria provided.

Now we can compile the above template using Jinja and calculate discount in the most optimized way. because all the criteria can be checked and calculated in one call(rendered result can be splat by ‘\n’ and formatted).Which makes this approach to be very fast in comparison to executing this sequentially one by one.

For complete working sample code have look at above-provided git link. You can add date-range similar like range and even customize attribute lookup from the context as per your need. This is just a simple guideline to approach a similar problem.

To view or add a comment, sign in

Others also viewed

Explore content categories