Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
150 changes: 150 additions & 0 deletions source/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

# C extensions
*.so

.DS_Store
.vscode/

# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST

# 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/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/

# Translations
*.mo
*.pot

# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal

# Flask stuff:
instance/
.webassets-cache

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/

# PyBuilder
target/

# Jupyter Notebook
.ipynb_checkpoints
*.ipynb

# IPython
profile_default/
ipython_config.py

# pyenv
.python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock

# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/

# Celery stuff
celerybeat-schedule
celerybeat.pid

# SageMath parsed files
*.sage.py

# Environments
# .env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
media/
# Spyder project settings
.spyderproject
.spyproject

# Rope project settings
.ropeproject

# mkdocs documentation
/site

# mypy
.mypy_cache/
.dmypy.json
dmypy.json

# Pyre type checker
.pyre/

# cdk
*.pem
*.swp
package-lock.json
__pycache__
.pytest_cache
.venv
*.egg-info

# CDK asset staging directory
.cdk.staging
cdk.out

# Terraform bookkeeping
*.tfstate
*.tfstate.*
*.terraform
2 changes: 2 additions & 0 deletions source/RewardsService/data/db/WiredTiger
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
WiredTiger
WiredTiger 11.2.0: (November 10, 2022)
1 change: 1 addition & 0 deletions source/RewardsService/data/db/WiredTiger.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
WiredTiger lock file
6 changes: 6 additions & 0 deletions source/RewardsService/data/db/WiredTiger.turtle
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
WiredTiger version string
WiredTiger 11.2.0: (November 10, 2022)
WiredTiger version
major=11,minor=2,patch=0
file:WiredTiger.wt
access_pattern_hint=none,allocation_size=4KB,app_metadata=,assert=(commit_timestamp=none,durable_timestamp=none,read_timestamp=none,write_timestamp=off),block_allocation=best,block_compressor=,cache_resident=false,checksum=on,collator=,columns=,dictionary=0,encryption=(keyid=,name=),format=btree,huffman_key=,huffman_value=,id=0,ignore_in_memory_cache_size=false,internal_item_max=0,internal_key_max=0,internal_key_truncate=true,internal_page_max=4KB,key_format=S,key_gap=10,leaf_item_max=0,leaf_key_max=0,leaf_page_max=32KB,leaf_value_max=0,log=(enabled=true),memory_page_image_max=0,memory_page_max=5MB,os_cache_dirty_max=0,os_cache_max=0,prefix_compression=false,prefix_compression_min=4,readonly=false,split_deepen_min_child=0,split_deepen_per_child=0,split_pct=90,tiered_object=false,tiered_storage=(auth_token=,bucket=,bucket_prefix=,cache_directory=,local_retention=300,name=,object_target_size=0),value_format=S,verbose=[],version=(major=1,minor=1),write_timestamp_usage=none,checkpoint=(WiredTigerCheckpoint.278=(addr="018381e43833b1bc8481e4fa3c33458581e41b6d9956808080e3018fc0e24fc0",order=278,time=1714664785,size=32768,newest_start_durable_ts=0,oldest_start_ts=0,newest_txn=1,newest_stop_durable_ts=0,newest_stop_ts=-1,newest_stop_txn=-11,prepare=0,write_gen=833,run_write_gen=831)),checkpoint_backup_info=,checkpoint_lsn=(3,256)
Binary file added source/RewardsService/data/db/WiredTiger.wt
Binary file not shown.
Binary file added source/RewardsService/data/db/WiredTigerHS.wt
Binary file not shown.
Binary file added source/RewardsService/data/db/_mdb_catalog.wt
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
1 change: 1 addition & 0 deletions source/RewardsService/data/db/mongod.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
1
Binary file added source/RewardsService/data/db/sizeStorer.wt
Binary file not shown.
Binary file added source/RewardsService/data/db/storage.bson
Binary file not shown.
76 changes: 76 additions & 0 deletions source/RewardsService/rewardsservice/handlers/customer_handler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import re
import json
import tornado.web
from pymongo import MongoClient
from tornado.gen import coroutine


class CustomerHandler(tornado.web.RequestHandler):
"""
Request handler for managing customer data.
"""

@coroutine
def get(self, email):
"""
GET method to retrieve a customer's rewards data.

Request Arguments:
- email: Customer's email address

Response:
- 200 OK: Customer rewards data retrieved successfully
- 400 Bad Request: Missing or invalid email address
- 404 Not Found: Customer not found
"""

# Validate email
if not email or not self._is_valid_email(email):
self.set_status(400)
self.write("Missing or invalid email address")
return

# Retrieve customer rewards data from MongoDB (implementation required)
rewards_data = self._retrieve_rewards_data(email)

if rewards_data:
# Respond with customer rewards data
self.set_status(200)
self.write(json.dumps(rewards_data))
else:
# Respond with customer not found
self.set_status(404)
self.write("Customer not found")

def _retrieve_rewards_data(self, email):
"""
Retrieve customer rewards data from MongoDB.

Args:
email (str): Customer's email address.

Returns:
dict: Customer rewards data if found, None otherwise.
"""
# Connect to MongoDB
client = MongoClient("mongodb", 27017)
db = client["Rewards"]

# Query MongoDB to retrieve customer rewards data
reward = list(db.customers.find({"email": email}, {"_id": 0}))

return reward

def _is_valid_email(self, email):
"""
Validate email address format.

Args:
email (str): Email address to validate.

Returns:
bool: True if the email address is valid, False otherwise.
"""
# Regular expression for email validation
regex = r"^[\w\.-]+@[\w\.-]+\.\w+$"
return re.match(regex, email) is not None
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import json
import tornado.web
from pymongo import MongoClient
from tornado.gen import coroutine


class CustomersRewardsHandler(tornado.web.RequestHandler):
"""
Request handler for managing customers rewards data.
"""

@coroutine
def get(self):
"""
GET method to retrieve rewards data for all customers.

Request Arguments:
- None

Response:
- 200 OK: Customers rewards data retrieved successfully
"""
# Retrieve rewards data for all customers
rewards_data = self._retrieve_rewards_data()

# Respond with rewards data
self.write(json.dumps(rewards_data))

def _retrieve_rewards_data(self):
"""
Retrieve rewards data for all customers from MongoDB.

Returns:
list: List of rewards data for all customers.
"""
# Connect to MongoDB
client = MongoClient("mongodb", 27017)
db = client["Rewards"]

# Retrieve rewards data for all customers
rewards_data = list(db.customers.find({}, {"_id": 0}))

return rewards_data
104 changes: 104 additions & 0 deletions source/RewardsService/rewardsservice/handlers/order_handler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import json
import tornado.web
from pymongo import MongoClient
from tornado.gen import coroutine

"""
Endpoint 1 Specs::

* Accept a customer's order data: email adress (ex. "customer01@gmail.com") and order total (ex. 100.80).
* Calculate and store the following customer rewards data into MongoDB. For each dollar a customer spends, the customer will earn 1 reward point. For example, an order of $100.80 earns 100 points. Once a customer has reached the top rewards tier, there are no more rewards the customer can earn.
Email Address: the customer's email address (ex. "customer01@gmail.com")
Reward Points: the customer's rewards points (ex. 100)
Reward Tier: the rewards tier the customer has reached (ex. "A")
Reward Tier Name: the name of the rewards tier (ex. "5% off purchase")
Next Reward Tier: the next rewards tier the customer can reach (ex. "B")
Next Reward Tier Name: the name of next rewards tier (ex. "10% off purchase")
Next Reward Tier Progress: the percentage the customer is away from reaching the next rewards tier (ex. 0.5)
"""


class OrderHandler(tornado.web.RequestHandler):
"""
Endpoint for accepting customer's order data and calculating rewards.

Request Payload:
{
"email": "customer01@gmail.com",
"total_amount": 100.80
}

Response:
{
"message": "Reward data stored successfully"
}
"""

@coroutine
def post(self):
"""
Accepts customer's order data and calculates rewards.

Parameters:
email (str): Customer's email address.
total_amount (float): Total amount of the order.

Returns:
None
"""
try:
data = json.loads(self.request.body.decode('utf-8'))
except json.JSONDecodeError:
self.set_status(400)
self.write("Invalid JSON format in the request body.")
return

email = data.get("email")
total_amount = data.get("total_amount")

# Validate email and order total
if not email or not total_amount or total_amount <= 0:
self.set_status(400)
self.write("Invalid email or order total")
return

# Calculate reward points
reward_points = int(total_amount)

# Retrieve rewards data from MongoDB
client = MongoClient("mongodb", 27017)
db = client["Rewards"]
rewards = list(db.rewards.find({}, {"_id": 0}))

# Calculate reward tier
reward_tier = None
reward_tier_name = None
next_reward_tier = None
next_reward_tier_name = None
next_reward_tier_progress = None

for reward in rewards:
if reward_points >= reward["points"]:
reward_tier = reward["tier"]
reward_tier_name = reward["rewardName"]
else:
next_reward_tier = reward["tier"]
next_reward_tier_name = reward["rewardName"]
next_reward_tier_progress = min(reward_points / reward["points"], 1.0)
break

# Store rewards data
db.customers.insert_one(
{
"email": email,
"reward_points": reward_points,
"reward_tier": reward_tier,
"reward_tier_name": reward_tier_name,
"next_reward_tier": next_reward_tier,
"next_reward_tier_name": next_reward_tier_name,
"next_reward_tier_progress": next_reward_tier_progress
}
)

self.set_status(201)
self.write({"message": "Reward data stored successfully"})
Loading