From a4ce0d86cf612681b786f7b0d6be68d08ae55634 Mon Sep 17 00:00:00 2001 From: gajanankathar Date: Sun, 3 Mar 2024 03:28:22 +0530 Subject: [PATCH 01/22] added customer endpoint --- .gitignore | 131 ++++++++++++++++++ .../rewardsservice/url_patterns.py | 2 + 2 files changed, 133 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..cad19081 --- /dev/null +++ b/.gitignore @@ -0,0 +1,131 @@ +# project specific +db.sqlite3 +static/ +sandbox/ + +# Data +data/ +*.csv +*.csv.gz + +# Credentials +.env +credentials/ +credentials.py +production.py +config/localhost.sh + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Lock files +~*.xlsx +*.swp + +# Distribution / packaging +src/ +.Python +.DS_Store +env/ +build/ +dist/ +downloads/ +.eggs/ +*.egg +*.egg-info/ +eggs/ +develop-eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +.installed.cfg + +# +source/ + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*,cover +.hypothesis/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# pyenv +.python-version + +# celery beat schedule file +celerybeat-schedule + +# SageMath parsed files +*.sage.py + +# dotenv +.env + +# virtualenv +.venv +venv/ +ENV/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# PyCharm +.idea/ + +# Visual Studio Code +.vscode/ + diff --git a/source/RewardsService/rewardsservice/url_patterns.py b/source/RewardsService/rewardsservice/url_patterns.py index 55e471d6..2e877f34 100644 --- a/source/RewardsService/rewardsservice/url_patterns.py +++ b/source/RewardsService/rewardsservice/url_patterns.py @@ -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), ] From 26953a04d40982ebd3a929906126edbfc3069a30 Mon Sep 17 00:00:00 2001 From: gajanankathar Date: Sun, 3 Mar 2024 03:30:32 +0530 Subject: [PATCH 02/22] added customer reward handler for post request --- .gitignore | 3 -- .../handlers/customer_rewards_handler.py | 42 +++++++++++++++++++ 2 files changed, 42 insertions(+), 3 deletions(-) create mode 100644 source/RewardsService/rewardsservice/handlers/customer_rewards_handler.py diff --git a/.gitignore b/.gitignore index cad19081..518dbdd7 100644 --- a/.gitignore +++ b/.gitignore @@ -48,9 +48,6 @@ var/ wheels/ .installed.cfg -# -source/ - # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. diff --git a/source/RewardsService/rewardsservice/handlers/customer_rewards_handler.py b/source/RewardsService/rewardsservice/handlers/customer_rewards_handler.py new file mode 100644 index 00000000..1116ed8c --- /dev/null +++ b/source/RewardsService/rewardsservice/handlers/customer_rewards_handler.py @@ -0,0 +1,42 @@ +import json +from bson import ObjectId +from pymongo import 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): + return int(amount or 0) + available_points + + def get_customer_available_points(self, email): + customer = self.db.customers.find_one({"email": email}) + return customer.get("points") if customer else 0 + + @coroutine + def post(self): + customer = json_decode(self.request.body) + available_points = self.get_customer_available_points(customer.get("emailId")) + points = self._calculate_points(customer.get("orderTotal"), available_points) + reward = "" + customer.update({ + "_id": str(ObjectId()), + "points": points, + "tier": "", + "rewardName": "", + "nextTier": "", + "nextRewardName": "", + }) + + del customer["orderTotal"] + created_customer = self.db.customers.insert_one(customer) + self.set_status(201) + self.write(json_encode({"message": "Customer rewards created/updated successfully!"})) From 6652481ae383d0c434af93633580c3e13c44eb12 Mon Sep 17 00:00:00 2001 From: gajanankathar Date: Sun, 3 Mar 2024 03:31:17 +0530 Subject: [PATCH 03/22] added customer reward handler for get request --- .../rewardsservice/handlers/customer_rewards_handler.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/source/RewardsService/rewardsservice/handlers/customer_rewards_handler.py b/source/RewardsService/rewardsservice/handlers/customer_rewards_handler.py index 1116ed8c..b3547f73 100644 --- a/source/RewardsService/rewardsservice/handlers/customer_rewards_handler.py +++ b/source/RewardsService/rewardsservice/handlers/customer_rewards_handler.py @@ -21,6 +21,9 @@ def get_customer_available_points(self, email): customer = self.db.customers.find_one({"email": email}) return customer.get("points") if customer else 0 + def get_reward(self, points): + return + @coroutine def post(self): customer = json_decode(self.request.body) @@ -40,3 +43,8 @@ def post(self): created_customer = self.db.customers.insert_one(customer) self.set_status(201) self.write(json_encode({"message": "Customer rewards created/updated successfully!"})) + + @coroutine + def get(self): + customers = list(self.db.customers.find({}, {"_id": 0})) + self.write(json.dumps(customers)) From 00b8ca1adcec9d91ed47c21e31eb738db1f73343 Mon Sep 17 00:00:00 2001 From: gajanankathar Date: Sun, 3 Mar 2024 06:10:57 +0530 Subject: [PATCH 04/22] added logic to calculate reward progress and get next and current reward program from reward collection --- .../handlers/customer_rewards_handler.py | 75 ++++++++++++++----- 1 file changed, 57 insertions(+), 18 deletions(-) diff --git a/source/RewardsService/rewardsservice/handlers/customer_rewards_handler.py b/source/RewardsService/rewardsservice/handlers/customer_rewards_handler.py index b3547f73..44a235a0 100644 --- a/source/RewardsService/rewardsservice/handlers/customer_rewards_handler.py +++ b/source/RewardsService/rewardsservice/handlers/customer_rewards_handler.py @@ -1,6 +1,6 @@ import json from bson import ObjectId -from pymongo import MongoClient +from pymongo import ASCENDING, DESCENDING, MongoClient from tornado.escape import json_encode, json_decode from tornado.gen import coroutine @@ -17,34 +17,73 @@ def initialize(self): def _calculate_points(amount, available_points=0): return int(amount or 0) + available_points + def _calculate_progress(self, points): + 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): customer = self.db.customers.find_one({"email": email}) return customer.get("points") if customer else 0 - def get_reward(self, points): - return + def get_top_reward(self): + return self.db.rewards.find_one({}, sort=[("points", DESCENDING)]) or {} + + def get_current_reward_program(self, points): + 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): + 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): customer = json_decode(self.request.body) available_points = self.get_customer_available_points(customer.get("emailId")) - points = self._calculate_points(customer.get("orderTotal"), available_points) - reward = "" - customer.update({ - "_id": str(ObjectId()), - "points": points, - "tier": "", - "rewardName": "", - "nextTier": "", - "nextRewardName": "", - }) - - del customer["orderTotal"] - created_customer = self.db.customers.insert_one(customer) - self.set_status(201) - self.write(json_encode({"message": "Customer rewards created/updated successfully!"})) + top_reward_prg = self.get_top_reward() + 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) + print("Current ", curr_reward_prg) + nxt_reward_prg = self.get_next_reward_program(points) + print("Next ", nxt_reward_prg) + customer.update({ + "_id": str(ObjectId()), + "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"] + print("customer", customer) + created_customer = self.db.customers.insert_one(customer) + self.set_status(201) + self.write(json_encode({"message": "Customer rewards created/updated successfully!"})) + else: + self.set_status(200) + self.write(json_encode({"message": "You cann't earn more rewards as you reach to the top."})) @coroutine def get(self): customers = list(self.db.customers.find({}, {"_id": 0})) + self.set_status(200) self.write(json.dumps(customers)) From face530d3766ada39293c02d91c7e353d5b7c974 Mon Sep 17 00:00:00 2001 From: gajanankathar Date: Sun, 3 Mar 2024 07:12:34 +0530 Subject: [PATCH 05/22] changed mongo query for get customer available point --- .../handlers/customer_rewards_handler.py | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/source/RewardsService/rewardsservice/handlers/customer_rewards_handler.py b/source/RewardsService/rewardsservice/handlers/customer_rewards_handler.py index 44a235a0..6597d138 100644 --- a/source/RewardsService/rewardsservice/handlers/customer_rewards_handler.py +++ b/source/RewardsService/rewardsservice/handlers/customer_rewards_handler.py @@ -15,7 +15,7 @@ def initialize(self): @staticmethod def _calculate_points(amount, available_points=0): - return int(amount or 0) + available_points + return available_points + int(amount or 0) def _calculate_progress(self, points): top_reward_prg = self.get_top_reward() @@ -28,8 +28,19 @@ def _calculate_progress(self, points): return round((rem/diff) * 100, 2) def get_customer_available_points(self, email): - customer = self.db.customers.find_one({"email": email}) - return customer.get("points") if customer else 0 + 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): return self.db.rewards.find_one({}, sort=[("points", DESCENDING)]) or {} @@ -65,6 +76,7 @@ def post(self): print("Next ", nxt_reward_prg) 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"), @@ -77,13 +89,13 @@ def post(self): print("customer", customer) created_customer = self.db.customers.insert_one(customer) self.set_status(201) - self.write(json_encode({"message": "Customer rewards created/updated successfully!"})) + 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."})) @coroutine def get(self): - customers = list(self.db.customers.find({}, {"_id": 0})) + customers = list(self.db.customers.find()) self.set_status(200) self.write(json.dumps(customers)) From 8c57dd2b832af594eb7396d41e0feffdefeae990 Mon Sep 17 00:00:00 2001 From: gajanankathar Date: Sun, 3 Mar 2024 07:42:52 +0530 Subject: [PATCH 06/22] added logic to get customer reward history --- .../rewardsservice/handlers/customer_rewards_handler.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/source/RewardsService/rewardsservice/handlers/customer_rewards_handler.py b/source/RewardsService/rewardsservice/handlers/customer_rewards_handler.py index 6597d138..fa17b66a 100644 --- a/source/RewardsService/rewardsservice/handlers/customer_rewards_handler.py +++ b/source/RewardsService/rewardsservice/handlers/customer_rewards_handler.py @@ -71,9 +71,7 @@ def post(self): 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) - print("Current ", curr_reward_prg) nxt_reward_prg = self.get_next_reward_program(points) - print("Next ", nxt_reward_prg) customer.update({ "_id": str(ObjectId()), "earnedPoints": int(customer.get("orderTotal") or 0), @@ -86,7 +84,6 @@ def post(self): }) del customer["orderTotal"] - print("customer", customer) created_customer = self.db.customers.insert_one(customer) self.set_status(201) self.write(json_encode({"message": "Customer rewards created successfully!"})) @@ -96,6 +93,8 @@ def post(self): @coroutine def get(self): - customers = list(self.db.customers.find()) + email = self.get_argument("email", None, True) + condition = {"emailId": email} if email else {} + customers = list(self.db.customers.find(condition).sort([("emailId", ASCENDING), ("points", DESCENDING)])) self.set_status(200) self.write(json.dumps(customers)) From 0d053ba61276b05446d1a52a18ced2b44762f697 Mon Sep 17 00:00:00 2001 From: gajanankathar Date: Sun, 3 Mar 2024 08:29:42 +0530 Subject: [PATCH 07/22] added exception handling --- .../handlers/customer_rewards_handler.py | 69 +++++++++++-------- 1 file changed, 40 insertions(+), 29 deletions(-) diff --git a/source/RewardsService/rewardsservice/handlers/customer_rewards_handler.py b/source/RewardsService/rewardsservice/handlers/customer_rewards_handler.py index fa17b66a..ce0e43e5 100644 --- a/source/RewardsService/rewardsservice/handlers/customer_rewards_handler.py +++ b/source/RewardsService/rewardsservice/handlers/customer_rewards_handler.py @@ -65,36 +65,47 @@ def get_next_reward_program(self, points): @coroutine def post(self): - customer = json_decode(self.request.body) - available_points = self.get_customer_available_points(customer.get("emailId")) - top_reward_prg = self.get_top_reward() - 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 - }) + try: + customer = json_decode(self.request.body) + available_points = self.get_customer_available_points(customer.get("emailId")) + top_reward_prg = self.get_top_reward() + 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"] - created_customer = 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."})) + del customer["orderTotal"] + created_customer = 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 = f'Error while creating customer rewards: {str(e)}' + self.set_status(500) + self.write(json_encode({"message": err_msg})) @coroutine def get(self): - email = self.get_argument("email", None, True) - condition = {"emailId": email} if email else {} - customers = list(self.db.customers.find(condition).sort([("emailId", ASCENDING), ("points", DESCENDING)])) - self.set_status(200) - self.write(json.dumps(customers)) + 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", DESCENDING)])) + self.set_status(200) + self.write(json.dumps(customers)) + except Exception as e: + err_msg = f'Error while getting customer rewards: {str(e)}' + self.set_status(500) + self.write(json_encode({"message": err_msg})) + \ No newline at end of file From ec61a9a005df97fdfb077327b6ff31b2540d3812 Mon Sep 17 00:00:00 2001 From: gajanankathar Date: Sun, 3 Mar 2024 08:37:47 +0530 Subject: [PATCH 08/22] remove .gitignore file from repo --- .../rewardsservice/handlers/customer_rewards_handler.py | 1 - 1 file changed, 1 deletion(-) diff --git a/source/RewardsService/rewardsservice/handlers/customer_rewards_handler.py b/source/RewardsService/rewardsservice/handlers/customer_rewards_handler.py index ce0e43e5..1c0a466f 100644 --- a/source/RewardsService/rewardsservice/handlers/customer_rewards_handler.py +++ b/source/RewardsService/rewardsservice/handlers/customer_rewards_handler.py @@ -108,4 +108,3 @@ def get(self): err_msg = f'Error while getting customer rewards: {str(e)}' self.set_status(500) self.write(json_encode({"message": err_msg})) - \ No newline at end of file From c0576d9d1053145939dacfa956b7783dd3531b03 Mon Sep 17 00:00:00 2001 From: gajanankathar Date: Sun, 3 Mar 2024 08:39:47 +0530 Subject: [PATCH 09/22] removed .gitignore file from repo --- .gitignore | 128 ----------------------------------------------------- 1 file changed, 128 deletions(-) delete mode 100644 .gitignore diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 518dbdd7..00000000 --- a/.gitignore +++ /dev/null @@ -1,128 +0,0 @@ -# project specific -db.sqlite3 -static/ -sandbox/ - -# Data -data/ -*.csv -*.csv.gz - -# Credentials -.env -credentials/ -credentials.py -production.py -config/localhost.sh - -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class - -# C extensions -*.so - -# Lock files -~*.xlsx -*.swp - -# Distribution / packaging -src/ -.Python -.DS_Store -env/ -build/ -dist/ -downloads/ -.eggs/ -*.egg -*.egg-info/ -eggs/ -develop-eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -.installed.cfg - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*,cover -.hypothesis/ - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/_build/ - -# PyBuilder -target/ - -# Jupyter Notebook -.ipynb_checkpoints - -# pyenv -.python-version - -# celery beat schedule file -celerybeat-schedule - -# SageMath parsed files -*.sage.py - -# dotenv -.env - -# virtualenv -.venv -venv/ -ENV/ - -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - -# mkdocs documentation -/site - -# PyCharm -.idea/ - -# Visual Studio Code -.vscode/ - From 562e877b200b55d65706790fffec4bede8f25a41 Mon Sep 17 00:00:00 2001 From: gajanankathar Date: Sun, 3 Mar 2024 09:00:02 +0530 Subject: [PATCH 10/22] application error corrected --- .../rewardsservice/handlers/customer_rewards_handler.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/RewardsService/rewardsservice/handlers/customer_rewards_handler.py b/source/RewardsService/rewardsservice/handlers/customer_rewards_handler.py index 1c0a466f..2e357c0e 100644 --- a/source/RewardsService/rewardsservice/handlers/customer_rewards_handler.py +++ b/source/RewardsService/rewardsservice/handlers/customer_rewards_handler.py @@ -92,7 +92,7 @@ def post(self): 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 = f'Error while creating customer rewards: {str(e)}' + err_msg = "Error while creating customer rewards: {}".format(e) self.set_status(500) self.write(json_encode({"message": err_msg})) @@ -105,6 +105,6 @@ def get(self): self.set_status(200) self.write(json.dumps(customers)) except Exception as e: - err_msg = f'Error while getting customer rewards: {str(e)}' + err_msg = "Error while getting customer rewards: {}".format(e) self.set_status(500) self.write(json_encode({"message": err_msg})) From a6986feea836cdc521ee5924cc555c902b92810e Mon Sep 17 00:00:00 2001 From: gajanankathar Date: Sun, 3 Mar 2024 17:52:42 +0530 Subject: [PATCH 11/22] improved add order form --- source/RewardsUI/rewards/index.html | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/source/RewardsUI/rewards/index.html b/source/RewardsUI/rewards/index.html index cdab1ee4..53fcf73f 100644 --- a/source/RewardsUI/rewards/index.html +++ b/source/RewardsUI/rewards/index.html @@ -23,9 +23,13 @@

Reward Tiers

Add orders

-
-
- + + {% csrf_token %} + +
+ + +

From b8c864f469d6ed2b411e74e3db49fa29a938c6a9 Mon Sep 17 00:00:00 2001 From: gajanankathar Date: Sun, 3 Mar 2024 17:53:52 +0530 Subject: [PATCH 12/22] added request client for add order --- source/RewardsUI/rewards/clients/rewards_service_client.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/source/RewardsUI/rewards/clients/rewards_service_client.py b/source/RewardsUI/rewards/clients/rewards_service_client.py index 70b56545..3c2e9dd8 100644 --- a/source/RewardsUI/rewards/clients/rewards_service_client.py +++ b/source/RewardsUI/rewards/clients/rewards_service_client.py @@ -1,3 +1,4 @@ +import json import requests @@ -5,7 +6,12 @@ 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() From 20122b21713bbcd04287b49ad12a8cca4e407882 Mon Sep 17 00:00:00 2001 From: gajanankathar Date: Sun, 3 Mar 2024 17:56:22 +0530 Subject: [PATCH 13/22] added view logic to add new order --- source/RewardsUI/rewards/views.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/source/RewardsUI/rewards/views.py b/source/RewardsUI/rewards/views.py index 1d5dd537..a108fb25 100644 --- a/source/RewardsUI/rewards/views.py +++ b/source/RewardsUI/rewards/views.py @@ -1,8 +1,11 @@ import logging +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 @@ -18,9 +21,20 @@ def get(self, request, *args, **kwargs): rewards_data = self.rewards_service_client.get_rewards() context['rewards_data'] = rewards_data - return TemplateResponse( request, self.template_name, context - ) \ No newline at end of file + ) + + def post(self, request, *args, **kwargs): + form = AddRewardsForm(request.POST) + if form.is_valid(): + data = { + "emailId": form.cleaned_data["email"], + "orderTotal": form.cleaned_data["total"], + } + data = self.rewards_service_client.add_order(data) + return redirect(reverse("rewards")) + + return redirect(reverse("rewards"), order_form=form.cleaned_data) From 4f947b7813ea40a27bd11ec421bfab3c0f3054eb Mon Sep 17 00:00:00 2001 From: gajanankathar Date: Sun, 3 Mar 2024 18:10:08 +0530 Subject: [PATCH 14/22] created add rewards form class --- source/RewardsUI/rewards/forms.py | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 source/RewardsUI/rewards/forms.py diff --git a/source/RewardsUI/rewards/forms.py b/source/RewardsUI/rewards/forms.py new file mode 100644 index 00000000..06fbd407 --- /dev/null +++ b/source/RewardsUI/rewards/forms.py @@ -0,0 +1,6 @@ +from django import forms + + +class AddRewardsForm(forms.Form): + email = forms.EmailField() + total = forms.FloatField() From 784b9e4f8e2f178b7c4dd5f642b1b7481ece42f5 Mon Sep 17 00:00:00 2001 From: gajanankathar Date: Sun, 3 Mar 2024 18:11:41 +0530 Subject: [PATCH 15/22] added get all customer rewards request client --- source/RewardsUI/rewards/clients/rewards_service_client.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source/RewardsUI/rewards/clients/rewards_service_client.py b/source/RewardsUI/rewards/clients/rewards_service_client.py index 3c2e9dd8..2679f4ba 100644 --- a/source/RewardsUI/rewards/clients/rewards_service_client.py +++ b/source/RewardsUI/rewards/clients/rewards_service_client.py @@ -15,3 +15,7 @@ def get_rewards(self): def add_order(self, payload): response = requests.post(self.customer_rewards_url, data=json.dumps(payload)) return response.json() + + def get_customer_rewards(self): + response = requests.get(self.customer_rewards_url) + return response.json() From f0c098a21578dede3743094a955d04a393060948 Mon Sep 17 00:00:00 2001 From: gajanankathar Date: Sun, 3 Mar 2024 18:12:45 +0530 Subject: [PATCH 16/22] improved dashboard by adding user rewards --- source/RewardsUI/rewards/index.html | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/source/RewardsUI/rewards/index.html b/source/RewardsUI/rewards/index.html index 53fcf73f..949caf76 100644 --- a/source/RewardsUI/rewards/index.html +++ b/source/RewardsUI/rewards/index.html @@ -51,15 +51,17 @@

User Rewards

- - customer01@gmail.com - 100 - A - 5% off purchase - B - 10% off purchase - 50% - + {% for customer in customers_data %} + + {{customer.emailId}} + {{customer.points}} + {{customer.tier}} + {{customer.rewardName}} + {{customer.nextTier}} + {{customer.nextRewardName}} + {{customer.nextRewardTierAwayPercentage}} + + {% endfor %} From 1f5585a1bf35926cadf120ec54aa3556a75a6c25 Mon Sep 17 00:00:00 2001 From: gajanankathar Date: Sun, 3 Mar 2024 18:31:26 +0530 Subject: [PATCH 17/22] added logic to pass user rewards to user rewards dashboard --- source/RewardsUI/rewards/views.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/source/RewardsUI/rewards/views.py b/source/RewardsUI/rewards/views.py index a108fb25..9dc51aed 100644 --- a/source/RewardsUI/rewards/views.py +++ b/source/RewardsUI/rewards/views.py @@ -20,7 +20,10 @@ def get(self, request, *args, **kwargs): context = self.get_context_data(**kwargs) rewards_data = self.rewards_service_client.get_rewards() + customers_data = self.rewards_service_client.get_customer_rewards() context['rewards_data'] = rewards_data + context['customers_data'] = customers_data + return TemplateResponse( request, self.template_name, @@ -36,5 +39,4 @@ def post(self, request, *args, **kwargs): } data = self.rewards_service_client.add_order(data) return redirect(reverse("rewards")) - - return redirect(reverse("rewards"), order_form=form.cleaned_data) + return redirect(reverse("rewards")) From dae4e73caab6c9ae7fb684ebe8eefdb390246f95 Mon Sep 17 00:00:00 2001 From: gajanankathar Date: Sun, 3 Mar 2024 19:10:58 +0530 Subject: [PATCH 18/22] improved user rewards search form --- source/RewardsUI/rewards/index.html | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/source/RewardsUI/rewards/index.html b/source/RewardsUI/rewards/index.html index 949caf76..1268e62d 100644 --- a/source/RewardsUI/rewards/index.html +++ b/source/RewardsUI/rewards/index.html @@ -35,8 +35,11 @@

Add orders

User Rewards

-
- + + {% csrf_token %} + + +
From 8bfc80f88caba435dc1da6e6d50f13e0ea2b38eb Mon Sep 17 00:00:00 2001 From: gajanankathar Date: Sun, 3 Mar 2024 19:11:52 +0530 Subject: [PATCH 19/22] enhanced user rewards client to filter user rewards by email --- source/RewardsUI/rewards/clients/rewards_service_client.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/source/RewardsUI/rewards/clients/rewards_service_client.py b/source/RewardsUI/rewards/clients/rewards_service_client.py index 2679f4ba..6043f927 100644 --- a/source/RewardsUI/rewards/clients/rewards_service_client.py +++ b/source/RewardsUI/rewards/clients/rewards_service_client.py @@ -16,6 +16,9 @@ def add_order(self, payload): response = requests.post(self.customer_rewards_url, data=json.dumps(payload)) return response.json() - def get_customer_rewards(self): - response = requests.get(self.customer_rewards_url) + 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() From eb6f5de10b0a64cef72a570811ef5d1a6fea9ebe Mon Sep 17 00:00:00 2001 From: gajanankathar Date: Sun, 3 Mar 2024 19:13:01 +0530 Subject: [PATCH 20/22] view logic to handle search/filter for user rewards service --- source/RewardsUI/rewards/views.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/source/RewardsUI/rewards/views.py b/source/RewardsUI/rewards/views.py index 9dc51aed..3c815d22 100644 --- a/source/RewardsUI/rewards/views.py +++ b/source/RewardsUI/rewards/views.py @@ -1,5 +1,6 @@ import logging +from django.utils.http import urlencode from django.urls import reverse from django.shortcuts import redirect from django.template.response import TemplateResponse @@ -20,9 +21,13 @@ def get(self, request, *args, **kwargs): context = self.get_context_data(**kwargs) rewards_data = self.rewards_service_client.get_rewards() - customers_data = self.rewards_service_client.get_customer_rewards() - context['rewards_data'] = rewards_data - context['customers_data'] = customers_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, @@ -31,6 +36,10 @@ def get(self, request, *args, **kwargs): ) def post(self, request, *args, **kwargs): + email = request.POST.get("email") + if email: + parameters = urlencode({"email": email}) + return redirect("{}?{}".format(reverse("rewards"), parameters)) form = AddRewardsForm(request.POST) if form.is_valid(): data = { From 2725fd01e20a3ecbb8a815675ea7d5b38fb55c6d Mon Sep 17 00:00:00 2001 From: gajanankathar Date: Sun, 3 Mar 2024 19:24:07 +0530 Subject: [PATCH 21/22] did bug fix and code improvement --- .../rewardsservice/handlers/customer_rewards_handler.py | 4 ++-- source/RewardsUI/rewards/index.html | 8 ++++---- source/RewardsUI/rewards/views.py | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/source/RewardsService/rewardsservice/handlers/customer_rewards_handler.py b/source/RewardsService/rewardsservice/handlers/customer_rewards_handler.py index 2e357c0e..34a35dc2 100644 --- a/source/RewardsService/rewardsservice/handlers/customer_rewards_handler.py +++ b/source/RewardsService/rewardsservice/handlers/customer_rewards_handler.py @@ -1,6 +1,6 @@ import json from bson import ObjectId -from pymongo import ASCENDING, DESCENDING, MongoClient +from pymongo import ASCENDING, MongoClient from tornado.escape import json_encode, json_decode from tornado.gen import coroutine @@ -101,7 +101,7 @@ 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", DESCENDING)])) + 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: diff --git a/source/RewardsUI/rewards/index.html b/source/RewardsUI/rewards/index.html index 1268e62d..072eba0d 100644 --- a/source/RewardsUI/rewards/index.html +++ b/source/RewardsUI/rewards/index.html @@ -23,7 +23,7 @@

Reward Tiers

Add orders

-
+ {% csrf_token %}
@@ -35,10 +35,10 @@

Add orders

User Rewards

- + {% csrf_token %} - - + +
diff --git a/source/RewardsUI/rewards/views.py b/source/RewardsUI/rewards/views.py index 3c815d22..ef59223d 100644 --- a/source/RewardsUI/rewards/views.py +++ b/source/RewardsUI/rewards/views.py @@ -36,7 +36,7 @@ def get(self, request, *args, **kwargs): ) def post(self, request, *args, **kwargs): - email = request.POST.get("email") + email = request.POST.get("user_email") if email: parameters = urlencode({"email": email}) return redirect("{}?{}".format(reverse("rewards"), parameters)) From 10bb0c070ca0757db57c87e60d2056ed92e7b546 Mon Sep 17 00:00:00 2001 From: gajanankathar Date: Sun, 3 Mar 2024 22:40:25 +0530 Subject: [PATCH 22/22] added docstring and code clean up --- .../handlers/customer_rewards_handler.py | 52 ++++++++++++++++++- source/RewardsUI/rewards/views.py | 2 +- 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/source/RewardsService/rewardsservice/handlers/customer_rewards_handler.py b/source/RewardsService/rewardsservice/handlers/customer_rewards_handler.py index 34a35dc2..724796ca 100644 --- a/source/RewardsService/rewardsservice/handlers/customer_rewards_handler.py +++ b/source/RewardsService/rewardsservice/handlers/customer_rewards_handler.py @@ -1,6 +1,6 @@ import json from bson import ObjectId -from pymongo import ASCENDING, MongoClient +from pymongo import ASCENDING, DESCENDING, MongoClient from tornado.escape import json_encode, json_decode from tornado.gen import coroutine @@ -15,9 +15,29 @@ def initialize(self): @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 @@ -28,6 +48,15 @@ def _calculate_progress(self, 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": { @@ -43,9 +72,21 @@ def get_customer_available_points(self, email): 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 @@ -55,6 +96,12 @@ def get_current_reward_program(self, 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 @@ -69,6 +116,7 @@ def post(self): 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) @@ -85,7 +133,7 @@ def post(self): }) del customer["orderTotal"] - created_customer = self.db.customers.insert_one(customer) + self.db.customers.insert_one(customer) self.set_status(201) self.write(json_encode({"message": "Customer rewards created successfully!"})) else: diff --git a/source/RewardsUI/rewards/views.py b/source/RewardsUI/rewards/views.py index ef59223d..4993646f 100644 --- a/source/RewardsUI/rewards/views.py +++ b/source/RewardsUI/rewards/views.py @@ -46,6 +46,6 @@ def post(self, request, *args, **kwargs): "emailId": form.cleaned_data["email"], "orderTotal": form.cleaned_data["total"], } - data = self.rewards_service_client.add_order(data) + self.rewards_service_client.add_order(data) return redirect(reverse("rewards")) return redirect(reverse("rewards"))