Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
a4ce0d8
added customer endpoint
gajanankathar Mar 2, 2024
26953a0
added customer reward handler for post request
gajanankathar Mar 2, 2024
6652481
added customer reward handler for get request
gajanankathar Mar 2, 2024
00b8ca1
added logic to calculate reward progress and get next and current rew…
gajanankathar Mar 3, 2024
face530
changed mongo query for get customer available point
gajanankathar Mar 3, 2024
8c57dd2
added logic to get customer reward history
gajanankathar Mar 3, 2024
0d053ba
added exception handling
gajanankathar Mar 3, 2024
ec61a9a
remove .gitignore file from repo
gajanankathar Mar 3, 2024
c0576d9
removed .gitignore file from repo
gajanankathar Mar 3, 2024
562e877
application error corrected
gajanankathar Mar 3, 2024
a6986fe
improved add order form
gajanankathar Mar 3, 2024
b8c864f
added request client for add order
gajanankathar Mar 3, 2024
20122b2
added view logic to add new order
gajanankathar Mar 3, 2024
4f947b7
created add rewards form class
gajanankathar Mar 3, 2024
784b9e4
added get all customer rewards request client
gajanankathar Mar 3, 2024
f0c098a
improved dashboard by adding user rewards
gajanankathar Mar 3, 2024
1f5585a
added logic to pass user rewards to user rewards dashboard
gajanankathar Mar 3, 2024
dae4e73
improved user rewards search form
gajanankathar Mar 3, 2024
8bfc80f
enhanced user rewards client to filter user rewards by email
gajanankathar Mar 3, 2024
eb6f5de
view logic to handle search/filter for user rewards service
gajanankathar Mar 3, 2024
2725fd0
did bug fix and code improvement
gajanankathar Mar 3, 2024
10bb0c0
added docstring and code clean up
gajanankathar Mar 3, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
import json
from bson import ObjectId
from pymongo import ASCENDING, DESCENDING, MongoClient

from tornado.escape import json_encode, json_decode
from tornado.gen import coroutine
import tornado.web


class CustomerRewardsHandler(tornado.web.RequestHandler):

def initialize(self):
self.client = MongoClient("mongodb", 27017)
self.db = self.client['Rewards']

@staticmethod
def _calculate_points(amount, available_points=0):
"""
Method to return calculated points based on user available points and amount i.e. $1 = 1 points.

Args:
amount (float): order total amount.
available_points (int): user available points

Returns:
It returns calculated reward points based on user available points and amount.
"""
return available_points + int(amount or 0)

def _calculate_progress(self, points):
"""
Method to calculate user reward program progress status. It will return completed percent of user reward program
between previous and next reward program.

Args:
points (int): user aggregated rewards points

Returns:
It returns user reward program completed percentage.
"""
top_reward_prg = self.get_top_reward()
if points > top_reward_prg.get("points"):
return 100
curr_reward_prg = self.get_current_reward_program(points)
nxt_reward_prg = self.get_next_reward_program(points)
diff = nxt_reward_prg.get("points") - curr_reward_prg.get("points", 0)
rem = points % 100 if points > 100 else points
return round((rem/diff) * 100, 2)

def get_customer_available_points(self, email):
"""
Method to get customer available points. It will do aggregation query to get customer total earned reward points.

Args:
email (char): aggregate customer reward points based on unique email id.

Returns:
It returns customer available reward points.
"""
customer = self.db.customers.aggregate([
{
"$group": {
"_id": "$emailId",
"totalPoints": {"$sum": "$earnedPoints"},
},
},
{
"$match": {"_id": email}
}
])
customer = list(customer)
return customer[0].get("totalPoints") if customer else 0

def get_top_reward(self):
"""
Method to get top reward program from rewards collection based on highest points document.

Returns:
It return reward program document in dict format.
"""
return self.db.rewards.find_one({}, sort=[("points", DESCENDING)]) or {}

def get_current_reward_program(self, points):
"""
Method to return current reward program document based on the customer accumulated points.

Returns:
It return reward program document in dict format.
"""
top_reward_prg = self.get_top_reward()
if points > top_reward_prg.get("points"):
return top_reward_prg
lower_bound = points - 100
return self.db.rewards.find_one({
"$and": [{"points": {"$gt": lower_bound}}, {"points": {"$lte": points}}]
}) or {}

def get_next_reward_program(self, points):
"""
Method to return next available reward program document based on the customer accumulated points.

Returns:
It return reward program document in dict format.
"""
top_reward_prg = self.get_top_reward()
if points > top_reward_prg.get("points"):
return top_reward_prg
upper_bound = points + 100
return self.db.rewards.find_one({
"$and": [{"points": {"$gt": points}}, {"points": {"$lte": upper_bound}}]
}) or {}

@coroutine
def post(self):
try:
customer = json_decode(self.request.body)
available_points = self.get_customer_available_points(customer.get("emailId"))
top_reward_prg = self.get_top_reward()
# Once customer has reached top rewards tier, there are no more rewards the customer can earn.
if available_points < top_reward_prg.get("points"):
points = self._calculate_points(customer.get("orderTotal"), available_points)
curr_reward_prg = self.get_current_reward_program(points)
nxt_reward_prg = self.get_next_reward_program(points)
customer.update({
"_id": str(ObjectId()),
"earnedPoints": int(customer.get("orderTotal") or 0),
"points": points,
"tier": curr_reward_prg.get("tier"),
"rewardName": curr_reward_prg.get("rewardName"),
"nextTier": nxt_reward_prg.get("tier"),
"nextRewardName": nxt_reward_prg.get("rewardName"),
"nextRewardTierAwayPercentage": (100 - self._calculate_progress(points)), # Away from next program
})

del customer["orderTotal"]
self.db.customers.insert_one(customer)
self.set_status(201)
self.write(json_encode({"message": "Customer rewards created successfully!"}))
else:
self.set_status(200)
self.write(json_encode({"message": "You cann't earn more rewards as you reach to the top."}))
except Exception as e:
err_msg = "Error while creating customer rewards: {}".format(e)
self.set_status(500)
self.write(json_encode({"message": err_msg}))

@coroutine
def get(self):
try:
email = self.get_argument("email", None, True)
condition = {"emailId": email} if email else {}
customers = list(self.db.customers.find(condition).sort([("emailId", ASCENDING), ("points", ASCENDING)]))
self.set_status(200)
self.write(json.dumps(customers))
except Exception as e:
err_msg = "Error while getting customer rewards: {}".format(e)
self.set_status(500)
self.write(json_encode({"message": err_msg}))
2 changes: 2 additions & 0 deletions source/RewardsService/rewardsservice/url_patterns.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from handlers.customer_rewards_handler import CustomerRewardsHandler
from handlers.rewards_handler import RewardsHandler

url_patterns = [
(r'/rewards', RewardsHandler),
(r'/customers', CustomerRewardsHandler),
]
13 changes: 13 additions & 0 deletions source/RewardsUI/rewards/clients/rewards_service_client.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,24 @@
import json
import requests


class RewardsServiceClient:

def __init__(self):
self.rewards_url = "http://rewardsservice:7050/rewards"
self.customer_rewards_url = "http://rewardsservice:7050/customers"

def get_rewards(self):
response = requests.get(self.rewards_url)
return response.json()

def add_order(self, payload):
response = requests.post(self.customer_rewards_url, data=json.dumps(payload))
return response.json()

def get_customer_rewards(self, email=None):
url = self.customer_rewards_url
if email:
url = url + "?email={}".format(email)
response = requests.get(url)
return response.json()
6 changes: 6 additions & 0 deletions source/RewardsUI/rewards/forms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from django import forms


class AddRewardsForm(forms.Form):
email = forms.EmailField()
total = forms.FloatField()
37 changes: 23 additions & 14 deletions source/RewardsUI/rewards/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,23 @@ <h2>Reward Tiers</h2>
</div>
<div>
<h2>Add orders</h2>
<form>
<label>Enter email address: </label><input type="text"/><br>
<label>Enter order total: </label><input type="text"/><input type="button" value="Submit Order"/>
<form method="post" action="{% url 'rewards' %}">
{% csrf_token %}
<label for="email">Enter email address: </label>
<input type="email" name="email" required="required" value="{{order_form.email}}" /><br>
<label for="total">Enter order total: </label>
<input type="number" step="0.01" name="total" required="required" value="{{order_form.total}}" />
<input type="submit" value="Submit Order"/>
</form>
<hr>
</div>
<div>
<h2>User Rewards</h2>
<form>
<label>Email address: </label><input type="text"/><input type="button" value="Search"/>
<form method="post" action="{% url 'rewards' %}">
{% csrf_token %}
<label for="user_email">Email address: </label>
<input type="email" name="user_email" value="{{user_email}}"/>
<input type="submit" value="Search"/>
</form>
<table border="1">
<thead>
Expand All @@ -47,15 +54,17 @@ <h2>User Rewards</h2>
</tr>
</thead>
<tbody>
<tr>
<td>customer01@gmail.com</td>
<td>100</td>
<td>A</td>
<td>5% off purchase</td>
<td>B</td>
<td>10% off purchase</td>
<td>50%</td>
</tr>
{% for customer in customers_data %}
<tr>
<td>{{customer.emailId}}</td>
<td>{{customer.points}}</td>
<td>{{customer.tier}}</td>
<td>{{customer.rewardName}}</td>
<td>{{customer.nextTier}}</td>
<td>{{customer.nextRewardName}}</td>
<td>{{customer.nextRewardTierAwayPercentage}}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
Expand Down
29 changes: 27 additions & 2 deletions source/RewardsUI/rewards/views.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import logging

from django.utils.http import urlencode
from django.urls import reverse
from django.shortcuts import redirect
from django.template.response import TemplateResponse
from django.views.generic.base import TemplateView

from rewards.forms import AddRewardsForm
from rewards.clients.rewards_service_client import RewardsServiceClient


Expand All @@ -17,10 +21,31 @@ def get(self, request, *args, **kwargs):
context = self.get_context_data(**kwargs)

rewards_data = self.rewards_service_client.get_rewards()
context['rewards_data'] = rewards_data
email = request.GET.get("email", "")
customers_data = self.rewards_service_client.get_customer_rewards(email=email)
context.update({
"rewards_data": rewards_data,
"customers_data": customers_data,
"user_email": email,
})

return TemplateResponse(
request,
self.template_name,
context
)
)

def post(self, request, *args, **kwargs):
email = request.POST.get("user_email")
if email:
parameters = urlencode({"email": email})
return redirect("{}?{}".format(reverse("rewards"), parameters))
form = AddRewardsForm(request.POST)
if form.is_valid():
data = {
"emailId": form.cleaned_data["email"],
"orderTotal": form.cleaned_data["total"],
}
self.rewards_service_client.add_order(data)
return redirect(reverse("rewards"))
return redirect(reverse("rewards"))