Search code examples
pythongoogle-app-enginerecursionpython-2.7webapp2

MLM downline distribution count


I make my first MLM software and I think I managed to code how to get the points from the downline even though it is a recursive problem I didn't use recursion and I might refactor to a recursive version if that seems better. With our system, the level of a distributor is measured i number of silvers and for each product that gets sold the promotion/bonus/score/points works upline so if Bob is the sponsor of Alice and Alice makes a purchase then Bob will get points measured in number of silvers for that purchase. I added a business function to my user class:

def this_month_non_manager_silver(self):
    silver = 0 
    today = date.today()
    timeline = date(today.year, today.month, 1) 
    downline = User.query(User.sponsor
            == self._key).fetch()
    distributor = self
    while distributor.has_downline():
        downline = User.query(User.sponsor == distributor.key).fetch()
        for person in downline:  
            orders = model.Order.all().filter('buyer_id =' , person.key.id()).filter('created >' , timeline).filter('status =', 'PAID').fetch(999999)
            for order in orders:
                for idx,item in enumerate(order.items):
                    purchase = model.Item.get_by_id(long(item.id()))
                    amount = int(order.amounts[idx])
                    silver = silver + amount*purchase.silver/1000.000 
            distributor = person
    return silver

What might be to do is now just a % on the silver according to the depth of the order. The code actually output the correct result for an order downline but I didn't yet test it extensively and I wonder if you think the code looks strange and if I have thought of everything since the models are somewhat complicated / advanced. The user class is from webapp2 and I could use a subclass but I didn't have time to do that so I just put in the method to the user class that's there and now I can call it from Jinja2 like {{user.this_month_non_manager_silver}}

Recursion might to be right way to do this but isn't my solution still OK and I can move on and keep this code for now or do you think it is not acceptable?

Thanks for any constructive criticism.


Solution

  • The main problem I see here is that you're essentially trying to do a breadth-first search (you look at all the users who are below the distributor, then look at all of the users below those distributors, etc etc), but each time the while loop loops you're only looking at the users below the last distributor.

    If we break down the important parts into something python-ish, you get this:

    distributor=self
    while distributor.has_downline():
        for person in distributor.downline:
            distributor = person
    

    As you can see, the value of distributor after the first set of downlines are accessed is the last distributor in the user's downline. Then the next time the for loop is run, you're only looking at the last distributor's downline.

    Traditionally a tree-walking algorithm is either recursive or loop-based with a stack. Generally you will choose one or the other based on memory constraints. To keep the solution iterative, you'd need to rewrite the above python-ish code like this:

    downlinestack = []
    distributor=self
    downlinestack += distributor.downline
    while downlinestack:
        downline = downlinestack.pop()
        for person in downline:
            downlinestack.append(person.downline)