From 6b806d1d11fbc42d2b8a53a316e0e8c6c9c7d284 Mon Sep 17 00:00:00 2001
From: William De Rocco <93288641+wderocco8@users.noreply.github.com>
Date: Sun, 14 Jul 2024 12:43:57 -0400
Subject: [PATCH 01/34] refactor: add use_mock parameter to MongoModule
constructor
---
chalicelib/modules/mongo.py | 13 ++---
chalicelib/services/EventService.py | 74 ++++++++++++++--------------
tests/services/test_event_service.py | 30 +++++++++++
3 files changed, 74 insertions(+), 43 deletions(-)
create mode 100644 tests/services/test_event_service.py
diff --git a/chalicelib/modules/mongo.py b/chalicelib/modules/mongo.py
index a3783cb..c288a13 100644
--- a/chalicelib/modules/mongo.py
+++ b/chalicelib/modules/mongo.py
@@ -10,6 +10,7 @@ class MongoModule:
def __init__(self, use_mock=False):
"""Establishes connection to MongoDB server"""
+ # if use_mock is true -> use monogmock to execute tests with fake db
self.use_mock = use_mock
if use_mock:
self.mongo_client = mongomock.MongoClient()
@@ -28,17 +29,17 @@ def __init__(self, use_mock=False):
self.mongo_client = MongoClient(self.uri)
def add_env_suffix(func):
- def wrapper(self, collection_name: str, *args, **kwargs):
+ def wrapper(self, collection: str, *args, **kwargs):
# users collection is dependent on vault so suffix should not be appended
- if collection_name == "users":
- return func(self, collection_name, *args, **kwargs)
+ if collection == "users":
+ return func(self, collection, *args, **kwargs)
if self.is_prod:
- collection_name += "-prod"
+ collection += "-prod"
else:
- collection_name += "-dev"
+ collection += "-dev"
- return func(self, collection_name, *args, **kwargs)
+ return func(self, collection, *args, **kwargs)
return wrapper
diff --git a/chalicelib/services/EventService.py b/chalicelib/services/EventService.py
index b3701ca..5b233d0 100644
--- a/chalicelib/services/EventService.py
+++ b/chalicelib/services/EventService.py
@@ -5,8 +5,6 @@
import datetime
from chalicelib.modules.google_sheets import GoogleSheetsModule
from chalicelib.modules.ses import ses, SesDestination
-from typing import Optional
-
class EventService:
class BSONEncoder(json.JSONEncoder):
@@ -19,17 +17,19 @@ def default(self, o):
return str(o)
return super().default(o)
- def __init__(self):
+ def __init__(self, mongo_module=mongo_module):
+ self.mongo_module = mongo_module
self.collection_prefix = "events-"
def create_timeframe(self, timeframe_data: dict):
timeframe_data["dateCreated"] = datetime.datetime.now()
- mongo_module.insert_document(
- f"{self.collection_prefix}timeframe", timeframe_data
+ self.mongo_module.insert_document(
+ collection=f"{self.collection_prefix}timeframe", data=timeframe_data
)
+ return {"msg": True}
def get_timeframe(self, timeframe_id: str):
- timeframe = mongo_module.get_document_by_id(
+ timeframe = self.mongo_module.get_document_by_id(
f"{self.collection_prefix}timeframe", timeframe_id
)
@@ -37,7 +37,7 @@ def get_timeframe(self, timeframe_id: str):
def get_all_timeframes(self):
"""Retrieve all timeframes from the database."""
- timeframes = mongo_module.get_all_data_from_collection(
+ timeframes = self.mongo_module.get_all_data_from_collection(
f"{self.collection_prefix}timeframe"
)
@@ -45,7 +45,7 @@ def get_all_timeframes(self):
def delete_timeframe(self, timeframe_id: str):
# Check if timeframe exists and if it doesn't return errors
- timeframe = mongo_module.get_document_by_id(
+ timeframe = self.mongo_module.get_document_by_id(
f"{self.collection_prefix}timeframe", timeframe_id
)
@@ -57,12 +57,12 @@ def delete_timeframe(self, timeframe_id: str):
# Delete all the events in the timeframe
for event_id in event_ids:
- mongo_module.delete_document_by_id(
+ self.mongo_module.delete_document_by_id(
f"{self.collection_prefix}event", event_id
)
# If timeframe exists, delete the timeframe document
- mongo_module.delete_document_by_id(
+ self.mongo_module.delete_document_by_id(
f"{self.collection_prefix}timeframe", timeframe_id
)
@@ -72,7 +72,7 @@ def create_event(self, timeframe_id: str, event_data: dict):
event_data["usersAttended"] = []
# Get Google Spreadsheet ID from timeframe
- timeframe_doc = mongo_module.get_document_by_id(
+ timeframe_doc = self.mongo_module.get_document_by_id(
f"{self.collection_prefix}timeframe", timeframe_id
)
spreadsheet_id = timeframe_doc["spreadsheetId"]
@@ -86,14 +86,14 @@ def create_event(self, timeframe_id: str, event_data: dict):
event_data["spreadsheetCol"] = col
# Insert the event in event collection
- event_id = mongo_module.insert_document(
+ event_id = self.mongo_module.insert_document(
f"{self.collection_prefix}event", event_data
)
event_data["eventId"] = str(event_id)
# Insert child event in timeframe collection
- mongo_module.update_document(
+ self.mongo_module.update_document(
f"{self.collection_prefix}timeframe",
timeframe_id,
{"$push": {"events": event_data}},
@@ -102,7 +102,7 @@ def create_event(self, timeframe_id: str, event_data: dict):
return json.dumps(event_data, cls=self.BSONEncoder)
def get_event(self, event_id: str):
- event = mongo_module.get_document_by_id(
+ event = self.mongo_module.get_document_by_id(
f"{self.collection_prefix}event", event_id
)
@@ -119,13 +119,13 @@ def checkin(self, event_id: str, user: dict) -> dict:
dict -- Dictionary containing status and message.
"""
user_id, user_email = user["id"], user["email"]
- member = mongo_module.get_document_by_id(f"users", user_id)
+ member = self.mongo_module.get_document_by_id(f"users", user_id)
if member is None:
raise NotFoundError(f"User with ID {user_id} does not exist.")
user_name = member["name"]
- event = mongo_module.get_document_by_id(
+ event = self.mongo_module.get_document_by_id(
f"{self.collection_prefix}event", event_id
)
@@ -139,7 +139,7 @@ def checkin(self, event_id: str, user: dict) -> dict:
}
# Get timeframe document to get Google Sheets info
- timeframe = mongo_module.get_document_by_id(
+ timeframe = self.mongo_module.get_document_by_id(
f"{self.collection_prefix}timeframe", event["timeframeId"]
)
@@ -173,7 +173,7 @@ def checkin(self, event_id: str, user: dict) -> dict:
)
# Update event collection with checkin data
- mongo_module.update_document(
+ self.mongo_module.update_document(
f"{self.collection_prefix}event",
event_id,
{"$push": {"usersAttended": checkin_data}},
@@ -207,7 +207,7 @@ def checkin(self, event_id: str, user: dict) -> dict:
def delete(self, event_id: str):
# Check if event exists and if it doesn't return errors
- event = mongo_module.get_document_by_id(
+ event = self.mongo_module.get_document_by_id(
f"{self.collection_prefix}event", event_id
)
@@ -218,17 +218,17 @@ def delete(self, event_id: str):
timeframe_id = event["timeframeId"]
# Remove event from timeframe
- mongo_module.update_document(
+ self.mongo_module.update_document(
f"{self.collection_prefix}timeframe",
timeframe_id,
{"$pull": {"events": {"_id": ObjectId(event_id)}}},
)
# Delete the event document
- mongo_module.delete_document_by_id(f"{self.collection_prefix}event", event_id)
+ self.mongo_module.delete_document_by_id(f"{self.collection_prefix}event", event_id)
def get_timeframe_sheets(self, timeframe_id: str):
- timeframe = mongo_module.get_document_by_id(
+ timeframe = self.mongo_module.get_document_by_id(
f"{self.collection_prefix}timeframe", timeframe_id
)
@@ -240,7 +240,7 @@ def get_timeframe_sheets(self, timeframe_id: str):
return [sheet["title"] for sheet in sheets]
def get_rush_categories_and_events(self):
- rush_categories = mongo_module.get_all_data_from_collection(
+ rush_categories = self.mongo_module.get_all_data_from_collection(
f"{self.collection_prefix}rush"
)
@@ -249,7 +249,7 @@ def get_rush_categories_and_events(self):
def create_rush_category(self, data: dict):
data["dateCreated"] = datetime.datetime.now()
data["events"] = []
- mongo_module.insert_document(f"{self.collection_prefix}rush", data)
+ self.mongo_module.insert_document(f"{self.collection_prefix}rush", data)
return
def create_rush_event(self, data: dict):
@@ -262,14 +262,14 @@ def create_rush_event(self, data: dict):
# Add event to its own collection
data["attendees"] = []
data["numAttendees"] = 0
- event_id = mongo_module.insert_document(
+ event_id = self.mongo_module.insert_document(
f"{self.collection_prefix}rush-event", data
)
data_copy["eventId"] = str(event_id)
# Add event to rush category
- mongo_module.update_document(
+ self.mongo_module.update_document(
f"{self.collection_prefix}rush",
data["categoryId"],
{"$push": {"events": data_copy}},
@@ -285,7 +285,7 @@ def modify_rush_event(self, data: dict):
eventId = data["eventId"]
# Check if event exists in the rush-event collection
- event = mongo_module.get_document_by_id(
+ event = self.mongo_module.get_document_by_id(
f"{self.collection_prefix}rush-event", eventId
)
@@ -312,7 +312,7 @@ def modify_rush_event(self, data: dict):
]
# Modify the event in its category (rush collection)
- mongo_module.update_document(
+ self.mongo_module.update_document(
f"{self.collection_prefix}rush",
event_category_id,
update_query,
@@ -320,7 +320,7 @@ def modify_rush_event(self, data: dict):
)
# Modify actual event document (rush-event collection)
- mongo_module.update_document(
+ self.mongo_module.update_document(
f"{self.collection_prefix}rush-event",
eventId,
{"$set": data},
@@ -351,7 +351,7 @@ def modify_rush_settings(self, data: dict):
collection = f"{self.collection_prefix}rush"
# Set all defaultRushCategory fields to false
- mongo_module.update_many_documents(
+ self.mongo_module.update_many_documents(
collection,
{},
{"$set": {"defaultRushCategory": False}}
@@ -362,7 +362,7 @@ def modify_rush_settings(self, data: dict):
return
# Update the specified document to set its defaultRushCategory to true
- result = mongo_module.update_document_by_id(
+ result = self.mongo_module.update_document_by_id(
collection,
default_rush_category_id,
{"defaultRushCategory": True}
@@ -374,7 +374,7 @@ def modify_rush_settings(self, data: dict):
return
def get_rush_event(self, event_id: str, hide_attendees: bool = True):
- event = mongo_module.get_document_by_id(
+ event = self.mongo_module.get_document_by_id(
f"{self.collection_prefix}rush-event", event_id
)
@@ -387,7 +387,7 @@ def get_rush_event(self, event_id: str, hide_attendees: bool = True):
return json.dumps(event, cls=self.BSONEncoder)
def checkin_rush(self, event_id: str, user_data: dict):
- event = mongo_module.get_document_by_id(
+ event = self.mongo_module.get_document_by_id(
f"{self.collection_prefix}rush-event", event_id
)
@@ -404,7 +404,7 @@ def checkin_rush(self, event_id: str, user_data: dict):
event["attendees"].append(user_data)
event["numAttendees"] += 1
- mongo_module.update_document(
+ self.mongo_module.update_document(
f"{self.collection_prefix}rush-event",
event_id,
{"$set": event},
@@ -428,7 +428,7 @@ def delete_rush_event(self, event_id: str):
"""
try:
# Check if event exists in the rush-event collection
- event = mongo_module.get_document_by_id(
+ event = self.mongo_module.get_document_by_id(
f"{self.collection_prefix}rush-event", event_id
)
@@ -438,14 +438,14 @@ def delete_rush_event(self, event_id: str):
event_category_id = event["categoryId"]
# Delete the event from its category
- mongo_module.update_document(
+ self.mongo_module.update_document(
f"{self.collection_prefix}rush",
event_category_id,
{"$pull": {"events": {"eventId": event_id}}},
)
# Delete event data from the rush-event collection
- mongo_module.delete_document_by_id(
+ self.mongo_module.delete_document_by_id(
f"{self.collection_prefix}rush-event", event_id
)
return
diff --git a/tests/services/test_event_service.py b/tests/services/test_event_service.py
new file mode 100644
index 0000000..3d67db8
--- /dev/null
+++ b/tests/services/test_event_service.py
@@ -0,0 +1,30 @@
+import pytest
+from unittest.mock import MagicMock, patch
+from chalicelib.services.EventService import EventService
+import datetime
+
+
+@pytest.fixture
+def mock_mongo_module():
+ with patch("chalicelib.modules.mongo.MongoModule", autospec=True) as MockMongoModule:
+ mock_instance = MockMongoModule(use_mock=True)
+ yield mock_instance
+
+@pytest.fixture
+def event_service(mock_mongo_module):
+ return EventService(mock_mongo_module)
+
+
+def test_insert_document(event_service, mock_mongo_module):
+ CREATE_TIMEFRAME = { "name": "testTimeframeName", "spreadsheetId": "testSpreadsheetId" }
+ dateCreated = datetime.datetime.now()
+
+ with patch("chalicelib.services.EventService.datetime") as mock_datetime:
+ mock_datetime.datetime.now.return_value = dateCreated
+ result = event_service.create_timeframe(CREATE_TIMEFRAME)
+ CREATE_TIMEFRAME["dateCreated"] = dateCreated
+ mock_mongo_module.insert_document.assert_called_once_with(
+ collection="events-timeframe", data=CREATE_TIMEFRAME
+ )
+
+ assert result == {"msg": True}
\ No newline at end of file
From b508bf98e90c852ab8bbe187137bffb81801866b Mon Sep 17 00:00:00 2001
From: William De Rocco <93288641+wderocco8@users.noreply.github.com>
Date: Sun, 14 Jul 2024 13:59:38 -0400
Subject: [PATCH 02/34] get_timeframe covered
---
chalicelib/services/EventService.py | 2 +-
tests/services/test_event_service.py | 58 +++++++++++++++++++++++++++-
2 files changed, 57 insertions(+), 3 deletions(-)
diff --git a/chalicelib/services/EventService.py b/chalicelib/services/EventService.py
index 5b233d0..8ab3a1e 100644
--- a/chalicelib/services/EventService.py
+++ b/chalicelib/services/EventService.py
@@ -30,7 +30,7 @@ def create_timeframe(self, timeframe_data: dict):
def get_timeframe(self, timeframe_id: str):
timeframe = self.mongo_module.get_document_by_id(
- f"{self.collection_prefix}timeframe", timeframe_id
+ collection=f"{self.collection_prefix}timeframe", document_id=timeframe_id
)
return json.dumps(timeframe, cls=self.BSONEncoder)
diff --git a/tests/services/test_event_service.py b/tests/services/test_event_service.py
index 3d67db8..00c6b44 100644
--- a/tests/services/test_event_service.py
+++ b/tests/services/test_event_service.py
@@ -2,7 +2,19 @@
from unittest.mock import MagicMock, patch
from chalicelib.services.EventService import EventService
import datetime
+import json
+with open("tests/fixtures/events/general/sample_timeframes.json") as f:
+ SAMPLE_TIMEFRAMES: list = json.load(f)
+
+with open("tests/fixtures/events/general/sample_timeframe_sheets.json") as f:
+ SAMPLE_TIMEFRAME_SHEETS: list = json.load(f)
+
+with open("tests/fixtures/events/general/sample_rush_events.json") as f:
+ SAMPLE_RUSH_EVENTS: list = json.load(f)
+
+with open("tests/fixtures/events/general/sample_rush_event.json") as f:
+ SAMPLE_RUSH_EVENT = json.load(f)
@pytest.fixture
def mock_mongo_module():
@@ -14,7 +26,6 @@ def mock_mongo_module():
def event_service(mock_mongo_module):
return EventService(mock_mongo_module)
-
def test_insert_document(event_service, mock_mongo_module):
CREATE_TIMEFRAME = { "name": "testTimeframeName", "spreadsheetId": "testSpreadsheetId" }
dateCreated = datetime.datetime.now()
@@ -27,4 +38,47 @@ def test_insert_document(event_service, mock_mongo_module):
collection="events-timeframe", data=CREATE_TIMEFRAME
)
- assert result == {"msg": True}
\ No newline at end of file
+ assert result == {"msg": True}
+
+def test_get_timeframe(event_service, mock_mongo_module):
+ mock_mongo_module.get_document_by_id.return_value = SAMPLE_TIMEFRAMES[0]
+ timeframe_id = SAMPLE_TIMEFRAMES[0]["_id"]
+
+ result = event_service.get_timeframe(timeframe_id=timeframe_id)
+ mock_mongo_module.get_document_by_id.assert_called_once_with(
+ collection="events-timeframe", document_id=timeframe_id
+ )
+
+ assert result == json.dumps(SAMPLE_TIMEFRAMES[0])
+
+# TODO:
+
+# def get_all_timeframes(self):
+
+# def delete_timeframe(self, timeframe_id: str):
+
+# def create_event(self, timeframe_id: str, event_data: dict):
+
+# def get_event(self, event_id: str):
+
+# def checkin(self, event_id: str, user: dict) -> dict:
+
+# def delete(self, event_id: str):
+
+# def get_timeframe_sheets(self, timeframe_id: str):
+
+# def get_rush_categories_and_events(self):
+
+# def create_rush_category(self, data: dict):
+
+# def create_rush_event(self, data: dict):
+
+# def modify_rush_event(self, data: dict):
+
+# def modify_rush_settings(self, data: dict):
+
+# def get_rush_event(self, event_id: str, hide_attendees: bool = True):
+
+# def checkin_rush(self, event_id: str, user_data: dict):
+
+# def delete_rush_event(self, event_id: str):
From 72372a03ebcdb6eb9046e9155a71088d1c4a70a5 Mon Sep 17 00:00:00 2001
From: William De Rocco <93288641+wderocco8@users.noreply.github.com>
Date: Sun, 14 Jul 2024 14:50:45 -0400
Subject: [PATCH 03/34] updated variable namings
---
chalicelib/services/EventService.py | 2 ++
tests/services/test_event_service.py | 29 +++++++++++++++++++++-------
2 files changed, 24 insertions(+), 7 deletions(-)
diff --git a/chalicelib/services/EventService.py b/chalicelib/services/EventService.py
index 8ab3a1e..f77170b 100644
--- a/chalicelib/services/EventService.py
+++ b/chalicelib/services/EventService.py
@@ -65,6 +65,8 @@ def delete_timeframe(self, timeframe_id: str):
self.mongo_module.delete_document_by_id(
f"{self.collection_prefix}timeframe", timeframe_id
)
+
+ return {"statusCode": 200}
def create_event(self, timeframe_id: str, event_data: dict):
event_data["dateCreated"] = datetime.datetime.now()
diff --git a/tests/services/test_event_service.py b/tests/services/test_event_service.py
index 00c6b44..1f25db5 100644
--- a/tests/services/test_event_service.py
+++ b/tests/services/test_event_service.py
@@ -28,12 +28,12 @@ def event_service(mock_mongo_module):
def test_insert_document(event_service, mock_mongo_module):
CREATE_TIMEFRAME = { "name": "testTimeframeName", "spreadsheetId": "testSpreadsheetId" }
- dateCreated = datetime.datetime.now()
+ date_created = datetime.datetime.now()
with patch("chalicelib.services.EventService.datetime") as mock_datetime:
- mock_datetime.datetime.now.return_value = dateCreated
+ mock_datetime.datetime.now.return_value = date_created
result = event_service.create_timeframe(CREATE_TIMEFRAME)
- CREATE_TIMEFRAME["dateCreated"] = dateCreated
+ CREATE_TIMEFRAME["date_created"] = date_created
mock_mongo_module.insert_document.assert_called_once_with(
collection="events-timeframe", data=CREATE_TIMEFRAME
)
@@ -51,13 +51,28 @@ def test_get_timeframe(event_service, mock_mongo_module):
assert result == json.dumps(SAMPLE_TIMEFRAMES[0])
-# TODO:
+def test_get_all_timeframe(event_service, mock_mongo_module):
+ mock_mongo_module.get_all_data_from_collection.return_value = SAMPLE_TIMEFRAMES
+
+ result = event_service.get_all_timeframes()
+ mock_mongo_module.get_all_data_from_collection.assert_called_once_with(
+ collection="events-timeframe"
+ )
-# def get_all_timeframes(self):
+ assert result == json.dumps(SAMPLE_TIMEFRAMES)
-# def delete_timeframe(self, timeframe_id: str):
+def test_delete_timeframe(event_service, mock_mongo_module):
+ result = event_service.delete_timeframe(timeframe_id=SAMPLE_TIMEFRAMES[0]["_id"])
+ assert result["statusCode"] == 200
-# def create_event(self, timeframe_id: str, event_data: dict):
+
+# TODO: potentially add mocking for GoogleSheetsModule (otherwise test is not isolated)
+# def test_create_event(event_service, mock_mongo_module):
+# CREATE_TIMEFRAME = { "name": "eventName", "tags": "tags", "sheetTab": "selectedSheetTab" }
+# date_created = datetime.datetime.now()
+# timeframe_id = "timeframeId"
+
+# TODO:
# def get_event(self, event_id: str):
From 3a966a3aed4692594bd55650f354c147165641cf Mon Sep 17 00:00:00 2001
From: William De Rocco <93288641+wderocco8@users.noreply.github.com>
Date: Sun, 14 Jul 2024 15:00:26 -0400
Subject: [PATCH 04/34] refactored MongoModule with ternary
---
chalicelib/modules/mongo.py | 8 ++------
1 file changed, 2 insertions(+), 6 deletions(-)
diff --git a/chalicelib/modules/mongo.py b/chalicelib/modules/mongo.py
index c288a13..f68b693 100644
--- a/chalicelib/modules/mongo.py
+++ b/chalicelib/modules/mongo.py
@@ -10,11 +10,6 @@ class MongoModule:
def __init__(self, use_mock=False):
"""Establishes connection to MongoDB server"""
- # if use_mock is true -> use monogmock to execute tests with fake db
- self.use_mock = use_mock
- if use_mock:
- self.mongo_client = mongomock.MongoClient()
- return
self.is_prod = os.environ.get("ENV") == "prod"
self.ssm_client = boto3.client("ssm")
@@ -26,7 +21,8 @@ def __init__(self, use_mock=False):
)["Parameter"]["Value"]
self.uri = f"mongodb+srv://{self.user}:{self.password}@cluster0.9gtht.mongodb.net/?retryWrites=true&w=majority"
- self.mongo_client = MongoClient(self.uri)
+ # if use_mock is true -> use monogmock to execute tests with fake db
+ self.mongo_client = mongomock.MongoClient() if use_mock else MongoClient(self.uri)
def add_env_suffix(func):
def wrapper(self, collection: str, *args, **kwargs):
From aeac99df2e70d9bc6e7fc2e366e317ff34d90096 Mon Sep 17 00:00:00 2001
From: William De Rocco <93288641+wderocco8@users.noreply.github.com>
Date: Wed, 17 Jul 2024 23:31:02 -0400
Subject: [PATCH 05/34] fixed uploading errors for rush event image
---
chalicelib/services/EventService.py | 16 +++++++++++++---
1 file changed, 13 insertions(+), 3 deletions(-)
diff --git a/chalicelib/services/EventService.py b/chalicelib/services/EventService.py
index f77170b..62a96b6 100644
--- a/chalicelib/services/EventService.py
+++ b/chalicelib/services/EventService.py
@@ -5,6 +5,8 @@
import datetime
from chalicelib.modules.google_sheets import GoogleSheetsModule
from chalicelib.modules.ses import ses, SesDestination
+from chalicelib.s3 import s3
+import uuid
class EventService:
class BSONEncoder(json.JSONEncoder):
@@ -255,8 +257,18 @@ def create_rush_category(self, data: dict):
return
def create_rush_event(self, data: dict):
+ event_id = str(uuid.uuid4())
data["dateCreated"] = datetime.datetime.now()
data["lastModified"] = data["dateCreated"]
+ data["eventId"] = event_id
+
+ # upload eventCoverImage to s3 bucket (convert everything to png files for now... can adjust later)
+ image_path = f"image/rush/{data['categoryId']}/{event_id}/{data['eventCoverImageName']}.png"
+ print("image path", image_path)
+ image_url = s3.upload_binary_data(image_path, data["eventCoverImage"])
+
+ # add image_url to data object (this also replaces the original base64 image url)
+ data["eventCoverImage"] = image_url
data_copy = data.copy()
data_copy.pop("categoryId", None)
@@ -264,12 +276,10 @@ def create_rush_event(self, data: dict):
# Add event to its own collection
data["attendees"] = []
data["numAttendees"] = 0
- event_id = self.mongo_module.insert_document(
+ self.mongo_module.insert_document(
f"{self.collection_prefix}rush-event", data
)
- data_copy["eventId"] = str(event_id)
-
# Add event to rush category
self.mongo_module.update_document(
f"{self.collection_prefix}rush",
From c2f5b1f4aacf6fdb0299c3606b0d8f6952ee3afe Mon Sep 17 00:00:00 2001
From: William De Rocco <93288641+wderocco8@users.noreply.github.com>
Date: Fri, 19 Jul 2024 10:49:57 -0400
Subject: [PATCH 06/34] s3 delete_object working + switched to using `_id` for
events (makes life easier)
---
chalicelib/s3.py | 24 ++++++++++++++++-
chalicelib/services/EventService.py | 40 +++++++++++++++++++++--------
2 files changed, 52 insertions(+), 12 deletions(-)
diff --git a/chalicelib/s3.py b/chalicelib/s3.py
index b1d09b6..a180280 100644
--- a/chalicelib/s3.py
+++ b/chalicelib/s3.py
@@ -13,7 +13,7 @@ def __init__(self):
self.s3 = boto3.client("s3")
def upload_binary_data(self, path: str, data: str) -> str:
- """Uploads resume to S3 Bucket and returns path"""
+ """Uploads object to S3 Bucket and returns path"""
# Set path
if self.is_prod:
path = f"prod/{path}"
@@ -39,4 +39,26 @@ def upload_binary_data(self, path: str, data: str) -> str:
return object_url
+ # https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/s3/client/delete_object.html
+ def delete_binary_data(self, object_id: str) -> str:
+ """Deletes object from s3 and returns response
+ Args:
+ object_id (str): The key (path) of the object to delete from the S3 bucket.
+ e.g. dev/image/rush/66988908fd70b2c44bf2305d/199bb28f-b54c-48a3-9b94-1c95eab61f7d/infosession2.png
+
+ Returns:
+ str: A message indicating the result of the deletion operation.
+ """
+ if self.is_prod:
+ path = f"prod/{object_id}"
+ else:
+ path = f"dev/{object_id}"
+
+ # delete binary data given bucket_name and key
+ response = self.s3.delete_object(
+ Bucket=self.bucket_name, Key=path
+ )
+
+ return response
+
s3 = S3Client()
\ No newline at end of file
diff --git a/chalicelib/services/EventService.py b/chalicelib/services/EventService.py
index 62a96b6..9940c96 100644
--- a/chalicelib/services/EventService.py
+++ b/chalicelib/services/EventService.py
@@ -6,7 +6,6 @@
from chalicelib.modules.google_sheets import GoogleSheetsModule
from chalicelib.modules.ses import ses, SesDestination
from chalicelib.s3 import s3
-import uuid
class EventService:
class BSONEncoder(json.JSONEncoder):
@@ -257,14 +256,13 @@ def create_rush_category(self, data: dict):
return
def create_rush_event(self, data: dict):
- event_id = str(uuid.uuid4())
+ event_id = ObjectId()
data["dateCreated"] = datetime.datetime.now()
data["lastModified"] = data["dateCreated"]
- data["eventId"] = event_id
+ data["_id"] = event_id
# upload eventCoverImage to s3 bucket (convert everything to png files for now... can adjust later)
- image_path = f"image/rush/{data['categoryId']}/{event_id}/{data['eventCoverImageName']}.png"
- print("image path", image_path)
+ image_path = f"image/rush/{data['categoryId']}/{event_id}.png"
image_url = s3.upload_binary_data(image_path, data["eventCoverImage"])
# add image_url to data object (this also replaces the original base64 image url)
@@ -288,17 +286,17 @@ def create_rush_event(self, data: dict):
)
return
-
+
def modify_rush_event(self, data: dict):
try:
data["lastModified"] = datetime.datetime.now()
- eventId = data["eventId"]
+ event_id = data["_id"]
# Check if event exists in the rush-event collection
event = self.mongo_module.get_document_by_id(
- f"{self.collection_prefix}rush-event", eventId
+ f"{self.collection_prefix}rush-event", event_id
)
if not event:
@@ -306,6 +304,23 @@ def modify_rush_event(self, data: dict):
event_category_id = event["categoryId"]
+ # if eventCoverImage contains https://whyphi-zap.s3.amazonaws.com, no need to update anything, otherwise update s3
+ if "https://whyphi-zap.s3.amazonaws.com" not in data["eventCoverImage"]:
+
+ # get image path
+ image_path = f"image/rush/{event_category_id}/{event_id}.png"
+ print("hello there ", image_path)
+
+ # remove previous eventCoverImage from s3 bucket
+ s3.delete_binary_data(object_id=image_path)
+
+ # # upload eventCoverImage to s3 bucket
+ # image_url = s3.upload_binary_data(image_path, data["eventCoverImage"])
+
+ # # add image_url to data object (this also replaces the original base64 image url)
+ # data["eventCoverImage"] = image_url
+
+
# Merge the existing event data with the new data
updated_event = {**event, **data}
@@ -320,7 +335,7 @@ def modify_rush_event(self, data: dict):
}
array_filters = [
- {"eventElem.eventId": eventId}
+ {"eventElem._id": event_id}
]
# Modify the event in its category (rush collection)
@@ -331,10 +346,13 @@ def modify_rush_event(self, data: dict):
array_filters=array_filters
)
+ # cannot include _id on original document (immutable)
+ data.pop("_id")
+
# Modify actual event document (rush-event collection)
self.mongo_module.update_document(
f"{self.collection_prefix}rush-event",
- eventId,
+ event_id,
{"$set": data},
)
return
@@ -453,7 +471,7 @@ def delete_rush_event(self, event_id: str):
self.mongo_module.update_document(
f"{self.collection_prefix}rush",
event_category_id,
- {"$pull": {"events": {"eventId": event_id}}},
+ {"$pull": {"events": {"_id": event_id}}},
)
# Delete event data from the rush-event collection
From 7ebd07a866c24f77e4abf95b74d48617a5cfe14d Mon Sep 17 00:00:00 2001
From: William De Rocco <93288641+wderocco8@users.noreply.github.com>
Date: Fri, 19 Jul 2024 11:05:48 -0400
Subject: [PATCH 07/34] modify s3 image working :)
---
chalicelib/services/EventService.py | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/chalicelib/services/EventService.py b/chalicelib/services/EventService.py
index 9940c96..38e592c 100644
--- a/chalicelib/services/EventService.py
+++ b/chalicelib/services/EventService.py
@@ -314,11 +314,11 @@ def modify_rush_event(self, data: dict):
# remove previous eventCoverImage from s3 bucket
s3.delete_binary_data(object_id=image_path)
- # # upload eventCoverImage to s3 bucket
- # image_url = s3.upload_binary_data(image_path, data["eventCoverImage"])
+ # upload eventCoverImage to s3 bucket
+ image_url = s3.upload_binary_data(path=image_path, data=data["eventCoverImage"])
- # # add image_url to data object (this also replaces the original base64 image url)
- # data["eventCoverImage"] = image_url
+ # add image_url to data object (this also replaces the original base64 image url)
+ data["eventCoverImage"] = image_url
# Merge the existing event data with the new data
From 9c430598dfedcb03065fba18118fd13b44ceba18 Mon Sep 17 00:00:00 2001
From: William De Rocco <93288641+wderocco8@users.noreply.github.com>
Date: Fri, 19 Jul 2024 11:11:19 -0400
Subject: [PATCH 08/34] fixed documentation for s3 delete
---
chalicelib/s3.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/chalicelib/s3.py b/chalicelib/s3.py
index a180280..239e8f8 100644
--- a/chalicelib/s3.py
+++ b/chalicelib/s3.py
@@ -39,7 +39,6 @@ def upload_binary_data(self, path: str, data: str) -> str:
return object_url
- # https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/s3/client/delete_object.html
def delete_binary_data(self, object_id: str) -> str:
"""Deletes object from s3 and returns response
Args:
@@ -48,6 +47,8 @@ def delete_binary_data(self, object_id: str) -> str:
Returns:
str: A message indicating the result of the deletion operation.
+
+ Documentation: https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/s3/client/delete_object.html
"""
if self.is_prod:
path = f"prod/{object_id}"
From 4f2786d094301371e3704ef89b3052f7aa22a154 Mon Sep 17 00:00:00 2001
From: William De Rocco <93288641+wderocco8@users.noreply.github.com>
Date: Sat, 20 Jul 2024 17:30:52 -0400
Subject: [PATCH 09/34] patched bug not being able to update event
---
chalicelib/services/EventService.py | 25 +++++++++++++++++--------
1 file changed, 17 insertions(+), 8 deletions(-)
diff --git a/chalicelib/services/EventService.py b/chalicelib/services/EventService.py
index 38e592c..b0bc8d3 100644
--- a/chalicelib/services/EventService.py
+++ b/chalicelib/services/EventService.py
@@ -290,9 +290,11 @@ def create_rush_event(self, data: dict):
def modify_rush_event(self, data: dict):
try:
- data["lastModified"] = datetime.datetime.now()
-
event_id = data["_id"]
+ object_event_id = ObjectId(event_id)
+
+ data["lastModified"] = datetime.datetime.now()
+ data["_id"] = object_event_id
# Check if event exists in the rush-event collection
event = self.mongo_module.get_document_by_id(
@@ -335,26 +337,33 @@ def modify_rush_event(self, data: dict):
}
array_filters = [
- {"eventElem._id": event_id}
+ {"eventElem._id": object_event_id}
]
# Modify the event in its category (rush collection)
- self.mongo_module.update_document(
- f"{self.collection_prefix}rush",
- event_category_id,
- update_query,
+ update_category_result = self.mongo_module.update_document(
+ collection=f"{self.collection_prefix}rush",
+ document_id=event_category_id,
+ query=update_query,
array_filters=array_filters
)
+
+ if not update_category_result:
+ raise Exception("Error updating rush-event-category.")
# cannot include _id on original document (immutable)
data.pop("_id")
# Modify actual event document (rush-event collection)
- self.mongo_module.update_document(
+ updated_event_result = self.mongo_module.update_document(
f"{self.collection_prefix}rush-event",
event_id,
{"$set": data},
)
+
+ if not updated_event_result:
+ raise Exception("Error updating rush-event-category.")
+
return
From 8a095b52c15b3bebb2bd584f9ede194798ce8424 Mon Sep 17 00:00:00 2001
From: William De Rocco <93288641+wderocco8@users.noreply.github.com>
Date: Sat, 20 Jul 2024 18:33:32 -0400
Subject: [PATCH 10/34] rush event deletion works properly again
---
chalicelib/services/EventService.py | 36 +++++++++++++++++++++--------
1 file changed, 27 insertions(+), 9 deletions(-)
diff --git a/chalicelib/services/EventService.py b/chalicelib/services/EventService.py
index b0bc8d3..68cf7c4 100644
--- a/chalicelib/services/EventService.py
+++ b/chalicelib/services/EventService.py
@@ -268,12 +268,17 @@ def create_rush_event(self, data: dict):
# add image_url to data object (this also replaces the original base64 image url)
data["eventCoverImage"] = image_url
+ # initialize attendee info (for rush-event)
+ data["attendees"] = []
+ data["numAttendees"] = 0
+
data_copy = data.copy()
+ # Remove catgoryId, attendees, and numAttendees from rush category (shouldn't be persisted)
data_copy.pop("categoryId", None)
+ data_copy.pop("attendees", None)
+ data_copy.pop("numAttendees", None)
# Add event to its own collection
- data["attendees"] = []
- data["numAttendees"] = 0
self.mongo_module.insert_document(
f"{self.collection_prefix}rush-event", data
)
@@ -311,7 +316,6 @@ def modify_rush_event(self, data: dict):
# get image path
image_path = f"image/rush/{event_category_id}/{event_id}.png"
- print("hello there ", image_path)
# remove previous eventCoverImage from s3 bucket
s3.delete_binary_data(object_id=image_path)
@@ -326,8 +330,10 @@ def modify_rush_event(self, data: dict):
# Merge the existing event data with the new data
updated_event = {**event, **data}
- # categoryId not needed on rush collection array elements
- updated_event.pop("categoryId")
+ # Remove catgoryId, attendees, and numAttendees from rush category (shouldn't be persisted)
+ updated_event.pop("categoryId", None)
+ updated_event.pop("attendees", None)
+ updated_event.pop("numAttendees", None)
# Define array update query and filters
update_query = {
@@ -466,26 +472,38 @@ def delete_rush_event(self, event_id: str):
If the event does not exist in the rush-event collection
"""
try:
+ # Get event_id as ObjectId
+ object_event_id = ObjectId(event_id)
+
# Check if event exists in the rush-event collection
event = self.mongo_module.get_document_by_id(
- f"{self.collection_prefix}rush-event", event_id
+ f"{self.collection_prefix}rush-event", object_event_id
)
-
+
if not event:
raise Exception("Event does not exist.")
event_category_id = event["categoryId"]
+ # Get eventCoverImage path
+ image_path = f"image/rush/{event_category_id}/{event_id}.png"
+
+ # remove previous eventCoverImage from s3 bucket
+ s3.delete_binary_data(object_id=image_path)
+
+ # upload eventCoverImage to s3 bucket
+ s3.delete_binary_data(object_id=image_path)
+
# Delete the event from its category
self.mongo_module.update_document(
f"{self.collection_prefix}rush",
event_category_id,
- {"$pull": {"events": {"_id": event_id}}},
+ {"$pull": {"events": {"_id": object_event_id}}},
)
# Delete event data from the rush-event collection
self.mongo_module.delete_document_by_id(
- f"{self.collection_prefix}rush-event", event_id
+ f"{self.collection_prefix}rush-event", object_event_id
)
return
From d8b83a8592ddb9caec972d61eea49084e8b94265 Mon Sep 17 00:00:00 2001
From: William De Rocco <93288641+wderocco8@users.noreply.github.com>
Date: Sat, 20 Jul 2024 18:36:23 -0400
Subject: [PATCH 11/34] Revert "rush event deletion works properly again"
This reverts commit 8a095b52c15b3bebb2bd584f9ede194798ce8424.
Undo commit to dev/v1.0
---
chalicelib/services/EventService.py | 36 ++++++++---------------------
1 file changed, 9 insertions(+), 27 deletions(-)
diff --git a/chalicelib/services/EventService.py b/chalicelib/services/EventService.py
index 68cf7c4..b0bc8d3 100644
--- a/chalicelib/services/EventService.py
+++ b/chalicelib/services/EventService.py
@@ -268,17 +268,12 @@ def create_rush_event(self, data: dict):
# add image_url to data object (this also replaces the original base64 image url)
data["eventCoverImage"] = image_url
- # initialize attendee info (for rush-event)
- data["attendees"] = []
- data["numAttendees"] = 0
-
data_copy = data.copy()
- # Remove catgoryId, attendees, and numAttendees from rush category (shouldn't be persisted)
data_copy.pop("categoryId", None)
- data_copy.pop("attendees", None)
- data_copy.pop("numAttendees", None)
# Add event to its own collection
+ data["attendees"] = []
+ data["numAttendees"] = 0
self.mongo_module.insert_document(
f"{self.collection_prefix}rush-event", data
)
@@ -316,6 +311,7 @@ def modify_rush_event(self, data: dict):
# get image path
image_path = f"image/rush/{event_category_id}/{event_id}.png"
+ print("hello there ", image_path)
# remove previous eventCoverImage from s3 bucket
s3.delete_binary_data(object_id=image_path)
@@ -330,10 +326,8 @@ def modify_rush_event(self, data: dict):
# Merge the existing event data with the new data
updated_event = {**event, **data}
- # Remove catgoryId, attendees, and numAttendees from rush category (shouldn't be persisted)
- updated_event.pop("categoryId", None)
- updated_event.pop("attendees", None)
- updated_event.pop("numAttendees", None)
+ # categoryId not needed on rush collection array elements
+ updated_event.pop("categoryId")
# Define array update query and filters
update_query = {
@@ -472,38 +466,26 @@ def delete_rush_event(self, event_id: str):
If the event does not exist in the rush-event collection
"""
try:
- # Get event_id as ObjectId
- object_event_id = ObjectId(event_id)
-
# Check if event exists in the rush-event collection
event = self.mongo_module.get_document_by_id(
- f"{self.collection_prefix}rush-event", object_event_id
+ f"{self.collection_prefix}rush-event", event_id
)
-
+
if not event:
raise Exception("Event does not exist.")
event_category_id = event["categoryId"]
- # Get eventCoverImage path
- image_path = f"image/rush/{event_category_id}/{event_id}.png"
-
- # remove previous eventCoverImage from s3 bucket
- s3.delete_binary_data(object_id=image_path)
-
- # upload eventCoverImage to s3 bucket
- s3.delete_binary_data(object_id=image_path)
-
# Delete the event from its category
self.mongo_module.update_document(
f"{self.collection_prefix}rush",
event_category_id,
- {"$pull": {"events": {"_id": object_event_id}}},
+ {"$pull": {"events": {"_id": event_id}}},
)
# Delete event data from the rush-event collection
self.mongo_module.delete_document_by_id(
- f"{self.collection_prefix}rush-event", object_event_id
+ f"{self.collection_prefix}rush-event", event_id
)
return
From ee07dbd0f1dd047658a59cdd0a065d772c955c04 Mon Sep 17 00:00:00 2001
From: William De Rocco <93288641+wderocco8@users.noreply.github.com>
Date: Sat, 20 Jul 2024 18:40:59 -0400
Subject: [PATCH 12/34] fixed deleting rush event
---
chalicelib/services/EventService.py | 36 +++++++++++++++++++++--------
1 file changed, 27 insertions(+), 9 deletions(-)
diff --git a/chalicelib/services/EventService.py b/chalicelib/services/EventService.py
index b0bc8d3..68cf7c4 100644
--- a/chalicelib/services/EventService.py
+++ b/chalicelib/services/EventService.py
@@ -268,12 +268,17 @@ def create_rush_event(self, data: dict):
# add image_url to data object (this also replaces the original base64 image url)
data["eventCoverImage"] = image_url
+ # initialize attendee info (for rush-event)
+ data["attendees"] = []
+ data["numAttendees"] = 0
+
data_copy = data.copy()
+ # Remove catgoryId, attendees, and numAttendees from rush category (shouldn't be persisted)
data_copy.pop("categoryId", None)
+ data_copy.pop("attendees", None)
+ data_copy.pop("numAttendees", None)
# Add event to its own collection
- data["attendees"] = []
- data["numAttendees"] = 0
self.mongo_module.insert_document(
f"{self.collection_prefix}rush-event", data
)
@@ -311,7 +316,6 @@ def modify_rush_event(self, data: dict):
# get image path
image_path = f"image/rush/{event_category_id}/{event_id}.png"
- print("hello there ", image_path)
# remove previous eventCoverImage from s3 bucket
s3.delete_binary_data(object_id=image_path)
@@ -326,8 +330,10 @@ def modify_rush_event(self, data: dict):
# Merge the existing event data with the new data
updated_event = {**event, **data}
- # categoryId not needed on rush collection array elements
- updated_event.pop("categoryId")
+ # Remove catgoryId, attendees, and numAttendees from rush category (shouldn't be persisted)
+ updated_event.pop("categoryId", None)
+ updated_event.pop("attendees", None)
+ updated_event.pop("numAttendees", None)
# Define array update query and filters
update_query = {
@@ -466,26 +472,38 @@ def delete_rush_event(self, event_id: str):
If the event does not exist in the rush-event collection
"""
try:
+ # Get event_id as ObjectId
+ object_event_id = ObjectId(event_id)
+
# Check if event exists in the rush-event collection
event = self.mongo_module.get_document_by_id(
- f"{self.collection_prefix}rush-event", event_id
+ f"{self.collection_prefix}rush-event", object_event_id
)
-
+
if not event:
raise Exception("Event does not exist.")
event_category_id = event["categoryId"]
+ # Get eventCoverImage path
+ image_path = f"image/rush/{event_category_id}/{event_id}.png"
+
+ # remove previous eventCoverImage from s3 bucket
+ s3.delete_binary_data(object_id=image_path)
+
+ # upload eventCoverImage to s3 bucket
+ s3.delete_binary_data(object_id=image_path)
+
# Delete the event from its category
self.mongo_module.update_document(
f"{self.collection_prefix}rush",
event_category_id,
- {"$pull": {"events": {"_id": event_id}}},
+ {"$pull": {"events": {"_id": object_event_id}}},
)
# Delete event data from the rush-event collection
self.mongo_module.delete_document_by_id(
- f"{self.collection_prefix}rush-event", event_id
+ f"{self.collection_prefix}rush-event", object_event_id
)
return
From 443d49664c7119d3671f8e211c92291c759d69ab Mon Sep 17 00:00:00 2001
From: William De Rocco <93288641+wderocco8@users.noreply.github.com>
Date: Sun, 21 Jul 2024 14:29:05 -0400
Subject: [PATCH 13/34] split events member/rush into two separate
services/APIs
---
app.py | 6 +-
chalicelib/api/events.py | 117 ---------
chalicelib/api/events_member.py | 68 +++++
chalicelib/api/events_rush.py | 54 ++++
chalicelib/services/EventsMemberService.py | 245 ++++++++++++++++++
.../{EventService.py => EventsRushService.py} | 233 +----------------
tests/services/test_event_service.py | 2 +-
7 files changed, 377 insertions(+), 348 deletions(-)
delete mode 100644 chalicelib/api/events.py
create mode 100644 chalicelib/api/events_member.py
create mode 100644 chalicelib/api/events_rush.py
create mode 100644 chalicelib/services/EventsMemberService.py
rename chalicelib/services/{EventService.py => EventsRushService.py} (54%)
diff --git a/app.py b/app.py
index e7be31f..abac8ac 100644
--- a/app.py
+++ b/app.py
@@ -10,7 +10,8 @@
from chalicelib.api.announcements import announcements_api
from chalicelib.api.insights import insights_api
from chalicelib.api.members import members_api
-from chalicelib.api.events import events_api
+from chalicelib.api.events_member import events_member_api
+from chalicelib.api.events_rush import events_rush_api
from chalicelib.api.accountability import accountability_api
from chalicelib.api.monitoring import monitoring_api
@@ -37,7 +38,8 @@
app.register_blueprint(applicants_api)
app.register_blueprint(insights_api)
app.register_blueprint(members_api)
-app.register_blueprint(events_api)
+app.register_blueprint(events_member_api)
+app.register_blueprint(events_rush_api)
app.register_blueprint(accountability_api)
app.register_blueprint(monitoring_api)
diff --git a/chalicelib/api/events.py b/chalicelib/api/events.py
deleted file mode 100644
index 90af958..0000000
--- a/chalicelib/api/events.py
+++ /dev/null
@@ -1,117 +0,0 @@
-from chalice import Blueprint
-from chalicelib.decorators import auth
-from chalicelib.services.EventService import event_service
-
-events_api = Blueprint(__name__)
-
-
-@events_api.route("/timeframes", methods=["POST"], cors=True)
-@auth(events_api, roles=["admin"])
-def create_timeframe():
- data = events_api.current_request.json_body
- return event_service.create_timeframe(data)
-
-
-@events_api.route("/timeframes", methods=["GET"], cors=True)
-@auth(events_api, roles=["admin"])
-def get_all_timeframes():
- return event_service.get_all_timeframes()
-
-
-@events_api.route("/timeframes/{timeframe_id}", methods=["GET"], cors=True)
-@auth(events_api, roles=["admin"])
-def get_timeframe(timeframe_id: str):
- return event_service.get_timeframe(timeframe_id)
-
-
-@events_api.route("/timeframes/{timeframe_id}", methods=["DELETE"], cors=True)
-@auth(events_api, roles=["admin"])
-def delete_timeframe(timeframe_id: str):
- return event_service.delete_timeframe(timeframe_id)
-
-
-@events_api.route("/timeframes/{timeframe_id}/events", methods=["POST"], cors=True)
-@auth(events_api, roles=["admin"])
-def create_event(timeframe_id: str):
- data = events_api.current_request.json_body
- return event_service.create_event(timeframe_id, data)
-
-
-@events_api.route("/events/{event_id}", methods=["GET"], cors=True)
-@auth(events_api, roles=["admin"])
-def get_event(event_id: str):
- return event_service.get_event(event_id)
-
-
-@events_api.route("/timeframes/{timeframe_id}/sheets", methods=["GET"], cors=True)
-@auth(events_api, roles=["admin"])
-def get_timeframe_sheets(timeframe_id: str):
- return event_service.get_timeframe_sheets(timeframe_id)
-
-
-@events_api.route("/events/{event_id}/checkin", methods=["POST"], cors=True)
-@auth(events_api, roles=["admin"])
-def checkin(event_id: str):
- data = events_api.current_request.json_body
- return event_service.checkin(event_id, data)
-
-
-@events_api.route("/events/{event_id}", methods=["PATCH"], cors=True)
-@auth(events_api, roles=["admin"])
-def update_event(event_id: str):
- pass
-
-
-@events_api.route("/events/{event_id}", methods=["DELETE"], cors=True)
-@auth(events_api, roles=["admin"])
-def delete_event(event_id: str):
- return event_service.delete(event_id)
-
-
-@events_api.route("/events/rush", methods=["GET"], cors=True)
-@auth(events_api, roles=["admin"])
-def get_rush_events():
- return event_service.get_rush_categories_and_events()
-
-
-@events_api.route("/events/rush/{event_id}", methods=["GET"], cors=True)
-def get_rush_event(event_id):
- return event_service.get_rush_event(event_id)
-
-
-@events_api.route("/events/rush/category", methods=["POST"], cors=True)
-@auth(events_api, roles=["admin"])
-def create_rush_category():
- data = events_api.current_request.json_body
- return event_service.create_rush_category(data)
-
-
-@events_api.route("/events/rush", methods=["POST"], cors=True)
-@auth(events_api, roles=["admin"])
-def create_rush_event():
- data = events_api.current_request.json_body
- return event_service.create_rush_event(data)
-
-
-@events_api.route("/events/rush", methods=["PATCH"], cors=True)
-@auth(events_api, roles=["admin"])
-def modify_rush_event():
- data = events_api.current_request.json_body
- return event_service.modify_rush_event(data)
-
-@events_api.route("/events/rush/settings", methods=["PATCH"], cors=True)
-@auth(events_api, roles=["admin"])
-def modify_rush_settings():
- data = events_api.current_request.json_body
- return event_service.modify_rush_settings(data)
-
-
-@events_api.route("/events/rush/checkin/{event_id}", methods=["POST"], cors=True)
-def checkin_rush(event_id):
- data = events_api.current_request.json_body
- return event_service.checkin_rush(event_id, data)
-
-
-@events_api.route("/events/rush/{event_id}", methods=["DELETE"], cors=True)
-def delete_rush_event(event_id):
- return event_service.delete_rush_event(event_id)
\ No newline at end of file
diff --git a/chalicelib/api/events_member.py b/chalicelib/api/events_member.py
new file mode 100644
index 0000000..9ea805e
--- /dev/null
+++ b/chalicelib/api/events_member.py
@@ -0,0 +1,68 @@
+from chalice import Blueprint
+from chalicelib.decorators import auth
+from chalicelib.services.EventsMemberService import events_member_service
+
+events_member_api = Blueprint(__name__)
+
+
+@events_member_api.route("/timeframes", methods=["POST"], cors=True)
+@auth(events_member_api, roles=["admin"])
+def create_timeframe():
+ data = events_member_api.current_request.json_body
+ return events_member_service.create_timeframe(data)
+
+
+@events_member_api.route("/timeframes", methods=["GET"], cors=True)
+@auth(events_member_api, roles=["admin"])
+def get_all_timeframes():
+ return events_member_service.get_all_timeframes()
+
+
+@events_member_api.route("/timeframes/{timeframe_id}", methods=["GET"], cors=True)
+@auth(events_member_api, roles=["admin"])
+def get_timeframe(timeframe_id: str):
+ return events_member_service.get_timeframe(timeframe_id)
+
+
+@events_member_api.route("/timeframes/{timeframe_id}", methods=["DELETE"], cors=True)
+@auth(events_member_api, roles=["admin"])
+def delete_timeframe(timeframe_id: str):
+ return events_member_service.delete_timeframe(timeframe_id)
+
+
+@events_member_api.route("/timeframes/{timeframe_id}/events", methods=["POST"], cors=True)
+@auth(events_member_api, roles=["admin"])
+def create_event(timeframe_id: str):
+ data = events_member_api.current_request.json_body
+ return events_member_service.create_event(timeframe_id, data)
+
+
+@events_member_api.route("/events/{event_id}", methods=["GET"], cors=True)
+@auth(events_member_api, roles=["admin"])
+def get_event(event_id: str):
+ return events_member_service.get_event(event_id)
+
+
+@events_member_api.route("/timeframes/{timeframe_id}/sheets", methods=["GET"], cors=True)
+@auth(events_member_api, roles=["admin"])
+def get_timeframe_sheets(timeframe_id: str):
+ return events_member_service.get_timeframe_sheets(timeframe_id)
+
+
+@events_member_api.route("/events/{event_id}/checkin", methods=["POST"], cors=True)
+@auth(events_member_api, roles=["admin"])
+def checkin(event_id: str):
+ data = events_member_api.current_request.json_body
+ return events_member_service.checkin(event_id, data)
+
+
+@events_member_api.route("/events/{event_id}", methods=["PATCH"], cors=True)
+@auth(events_member_api, roles=["admin"])
+def update_event(event_id: str):
+ pass
+
+
+@events_member_api.route("/events/{event_id}", methods=["DELETE"], cors=True)
+@auth(events_member_api, roles=["admin"])
+def delete_event(event_id: str):
+ return events_member_service.delete(event_id)
\ No newline at end of file
diff --git a/chalicelib/api/events_rush.py b/chalicelib/api/events_rush.py
new file mode 100644
index 0000000..f33cecd
--- /dev/null
+++ b/chalicelib/api/events_rush.py
@@ -0,0 +1,54 @@
+from chalice import Blueprint
+from chalicelib.decorators import auth
+from chalicelib.services.EventsRushService import events_rush_service
+
+events_rush_api = Blueprint(__name__)
+
+
+@events_rush_api.route("/events/rush", methods=["GET"], cors=True)
+@auth(events_rush_api, roles=["admin"])
+def get_rush_events():
+ return events_rush_service.get_rush_categories_and_events()
+
+
+@events_rush_api.route("/events/rush/{event_id}", methods=["GET"], cors=True)
+def get_rush_event(event_id):
+ return events_rush_service.get_rush_event(event_id)
+
+
+@events_rush_api.route("/events/rush/category", methods=["POST"], cors=True)
+@auth(events_rush_api, roles=["admin"])
+def create_rush_category():
+ data = events_rush_api.current_request.json_body
+ return events_rush_service.create_rush_category(data)
+
+
+@events_rush_api.route("/events/rush", methods=["POST"], cors=True)
+@auth(events_rush_api, roles=["admin"])
+def create_rush_event():
+ data = events_rush_api.current_request.json_body
+ return events_rush_service.create_rush_event(data)
+
+
+@events_rush_api.route("/events/rush", methods=["PATCH"], cors=True)
+@auth(events_rush_api, roles=["admin"])
+def modify_rush_event():
+ data = events_rush_api.current_request.json_body
+ return events_rush_service.modify_rush_event(data)
+
+@events_rush_api.route("/events/rush/settings", methods=["PATCH"], cors=True)
+@auth(events_rush_api, roles=["admin"])
+def modify_rush_settings():
+ data = events_rush_api.current_request.json_body
+ return events_rush_service.modify_rush_settings(data)
+
+
+@events_rush_api.route("/events/rush/checkin/{event_id}", methods=["POST"], cors=True)
+def checkin_rush(event_id):
+ data = events_rush_api.current_request.json_body
+ return events_rush_service.checkin_rush(event_id, data)
+
+
+@events_rush_api.route("/events/rush/{event_id}", methods=["DELETE"], cors=True)
+def delete_rush_event(event_id):
+ return events_rush_service.delete_rush_event(event_id)
\ No newline at end of file
diff --git a/chalicelib/services/EventsMemberService.py b/chalicelib/services/EventsMemberService.py
new file mode 100644
index 0000000..8ddeaec
--- /dev/null
+++ b/chalicelib/services/EventsMemberService.py
@@ -0,0 +1,245 @@
+from chalicelib.modules.mongo import mongo_module
+from chalice import NotFoundError, BadRequestError
+import json
+from bson import ObjectId
+import datetime
+from chalicelib.modules.google_sheets import GoogleSheetsModule
+from chalicelib.modules.ses import ses, SesDestination
+
+class EventsMemberService:
+ class BSONEncoder(json.JSONEncoder):
+ """JSON encoder that converts Mongo ObjectIds and datetime.datetime to strings."""
+
+ def default(self, o):
+ if isinstance(o, datetime.datetime):
+ return o.isoformat()
+ elif isinstance(o, ObjectId):
+ return str(o)
+ return super().default(o)
+
+ def __init__(self, mongo_module=mongo_module):
+ self.mongo_module = mongo_module
+ self.collection_prefix = "events-"
+
+ def create_timeframe(self, timeframe_data: dict):
+ timeframe_data["dateCreated"] = datetime.datetime.now()
+ self.mongo_module.insert_document(
+ collection=f"{self.collection_prefix}timeframe", data=timeframe_data
+ )
+ return {"msg": True}
+
+ def get_timeframe(self, timeframe_id: str):
+ timeframe = self.mongo_module.get_document_by_id(
+ collection=f"{self.collection_prefix}timeframe", document_id=timeframe_id
+ )
+
+ return json.dumps(timeframe, cls=self.BSONEncoder)
+
+ def get_all_timeframes(self):
+ """Retrieve all timeframes from the database."""
+ timeframes = self.mongo_module.get_all_data_from_collection(
+ f"{self.collection_prefix}timeframe"
+ )
+
+ return json.dumps(timeframes, cls=self.BSONEncoder)
+
+ def delete_timeframe(self, timeframe_id: str):
+ # Check if timeframe exists and if it doesn't return errors
+ timeframe = self.mongo_module.get_document_by_id(
+ f"{self.collection_prefix}timeframe", timeframe_id
+ )
+
+ if timeframe is None:
+ raise NotFoundError(f"Timeframe with ID {timeframe_id} does not exist.")
+
+ # If timeframe exists, get the eventIds (children)
+ event_ids = [str(event["_id"]) for event in timeframe["events"]]
+
+ # Delete all the events in the timeframe
+ for event_id in event_ids:
+ self.mongo_module.delete_document_by_id(
+ f"{self.collection_prefix}event", event_id
+ )
+
+ # If timeframe exists, delete the timeframe document
+ self.mongo_module.delete_document_by_id(
+ f"{self.collection_prefix}timeframe", timeframe_id
+ )
+
+ return {"statusCode": 200}
+
+ def create_event(self, timeframe_id: str, event_data: dict):
+ event_data["dateCreated"] = datetime.datetime.now()
+ event_data["timeframeId"] = timeframe_id
+ event_data["usersAttended"] = []
+
+ # Get Google Spreadsheet ID from timeframe
+ timeframe_doc = self.mongo_module.get_document_by_id(
+ f"{self.collection_prefix}timeframe", timeframe_id
+ )
+ spreadsheet_id = timeframe_doc["spreadsheetId"]
+
+ # Add event name to Google Sheets
+ gs = GoogleSheetsModule()
+ col = gs.find_next_available_col(spreadsheet_id, event_data["sheetTab"])
+ gs.add_event(spreadsheet_id, event_data["sheetTab"], event_data["name"], col)
+
+ # Append next available col value to event data
+ event_data["spreadsheetCol"] = col
+
+ # Insert the event in event collection
+ event_id = self.mongo_module.insert_document(
+ f"{self.collection_prefix}event", event_data
+ )
+
+ event_data["eventId"] = str(event_id)
+
+ # Insert child event in timeframe collection
+ self.mongo_module.update_document(
+ f"{self.collection_prefix}timeframe",
+ timeframe_id,
+ {"$push": {"events": event_data}},
+ )
+
+ return json.dumps(event_data, cls=self.BSONEncoder)
+
+ def get_event(self, event_id: str):
+ event = self.mongo_module.get_document_by_id(
+ f"{self.collection_prefix}event", event_id
+ )
+
+ return json.dumps(event, cls=self.BSONEncoder)
+
+ def checkin(self, event_id: str, user: dict) -> dict:
+ """Checks in a user to an event.
+
+ Arguments:
+ event_id {str} -- ID of the event to check into.
+ user {dict} -- Dictionary containing user ID and name.
+
+ Returns:
+ dict -- Dictionary containing status and message.
+ """
+ user_id, user_email = user["id"], user["email"]
+ member = self.mongo_module.get_document_by_id(f"users", user_id)
+ if member is None:
+ raise NotFoundError(f"User with ID {user_id} does not exist.")
+
+ user_name = member["name"]
+
+ event = self.mongo_module.get_document_by_id(
+ f"{self.collection_prefix}event", event_id
+ )
+
+ if any(d["userId"] == user_id for d in event["usersAttended"]):
+ raise BadRequestError(f"{user_name} has already checked in.")
+
+ checkin_data = {
+ "userId": user_id,
+ "name": user_name,
+ "dateCheckedIn": datetime.datetime.now(),
+ }
+
+ # Get timeframe document to get Google Sheets info
+ timeframe = self.mongo_module.get_document_by_id(
+ f"{self.collection_prefix}timeframe", event["timeframeId"]
+ )
+
+ # Get Google Sheets information
+ ss_id = timeframe["spreadsheetId"]
+
+ # Initialize Google Sheets Module
+ gs = GoogleSheetsModule()
+
+ # Find row in Google Sheets that matches user's email
+ row_num = gs.find_matching_email(
+ spreadsheet_id=ss_id,
+ sheet_name=event["sheetTab"],
+ col="C",
+ email_to_match=user_email,
+ )
+
+ if row_num == -1:
+ return {
+ "status": False,
+ "message": f"{user_name} was not found in the sheet.",
+ }
+
+ # Update Google Sheets cell with a "1" if user has checked in
+ gs.update_row(
+ spreadsheet_id=ss_id,
+ sheet_name=event["sheetTab"],
+ col=event["spreadsheetCol"],
+ row=row_num + 1,
+ data=[["1"]],
+ )
+
+ # Update event collection with checkin data
+ self.mongo_module.update_document(
+ f"{self.collection_prefix}event",
+ event_id,
+ {"$push": {"usersAttended": checkin_data}},
+ )
+
+ # Send email to user that has checked in
+ email_content = f"""
+ Hi {user_name},
+
+ Thank you for checking in to {event["name"]}! This is a confirmation that you have checked in.
+
+ Regards,
+ PCT Tech Team
+
+ ** Please note: Do not reply to this email. This email is sent from an unattended mailbox. Replies will not be read.
+ """
+
+ ses_destination = SesDestination(tos=[user_email])
+ ses.send_email(
+ source="checkin-bot@why-phi.com",
+ destination=ses_destination,
+ subject=f"Check-in Confirmation: {event['name']}",
+ text=email_content,
+ html=email_content,
+ )
+
+ return {
+ "status": True,
+ "message": f"{user_name} has successfully been checked in.",
+ }
+
+ def delete(self, event_id: str):
+ # Check if event exists and if it doesn't return errors
+ event = self.mongo_module.get_document_by_id(
+ f"{self.collection_prefix}event", event_id
+ )
+
+ if event is None:
+ raise NotFoundError(f"Event with ID {event_id} does not exist.")
+
+ # If event exists, get the timeframeId (parent)
+ timeframe_id = event["timeframeId"]
+
+ # Remove event from timeframe
+ self.mongo_module.update_document(
+ f"{self.collection_prefix}timeframe",
+ timeframe_id,
+ {"$pull": {"events": {"_id": ObjectId(event_id)}}},
+ )
+
+ # Delete the event document
+ self.mongo_module.delete_document_by_id(f"{self.collection_prefix}event", event_id)
+
+ def get_timeframe_sheets(self, timeframe_id: str):
+ timeframe = self.mongo_module.get_document_by_id(
+ f"{self.collection_prefix}timeframe", timeframe_id
+ )
+
+ if "spreadsheetId" not in timeframe or timeframe["spreadsheetId"] == "":
+ return []
+
+ gs = GoogleSheetsModule()
+ sheets = gs.get_sheets(timeframe["spreadsheetId"], include_properties=False)
+ return [sheet["title"] for sheet in sheets]
+
+
+events_member_service = EventsMemberService()
diff --git a/chalicelib/services/EventService.py b/chalicelib/services/EventsRushService.py
similarity index 54%
rename from chalicelib/services/EventService.py
rename to chalicelib/services/EventsRushService.py
index 68cf7c4..9735887 100644
--- a/chalicelib/services/EventService.py
+++ b/chalicelib/services/EventsRushService.py
@@ -1,13 +1,11 @@
from chalicelib.modules.mongo import mongo_module
-from chalice import NotFoundError, BadRequestError, UnauthorizedError
+from chalice import BadRequestError, UnauthorizedError
import json
from bson import ObjectId
import datetime
-from chalicelib.modules.google_sheets import GoogleSheetsModule
-from chalicelib.modules.ses import ses, SesDestination
from chalicelib.s3 import s3
-class EventService:
+class EventsRushService:
class BSONEncoder(json.JSONEncoder):
"""JSON encoder that converts Mongo ObjectIds and datetime.datetime to strings."""
@@ -19,228 +17,8 @@ def default(self, o):
return super().default(o)
def __init__(self, mongo_module=mongo_module):
- self.mongo_module = mongo_module
- self.collection_prefix = "events-"
-
- def create_timeframe(self, timeframe_data: dict):
- timeframe_data["dateCreated"] = datetime.datetime.now()
- self.mongo_module.insert_document(
- collection=f"{self.collection_prefix}timeframe", data=timeframe_data
- )
- return {"msg": True}
-
- def get_timeframe(self, timeframe_id: str):
- timeframe = self.mongo_module.get_document_by_id(
- collection=f"{self.collection_prefix}timeframe", document_id=timeframe_id
- )
-
- return json.dumps(timeframe, cls=self.BSONEncoder)
-
- def get_all_timeframes(self):
- """Retrieve all timeframes from the database."""
- timeframes = self.mongo_module.get_all_data_from_collection(
- f"{self.collection_prefix}timeframe"
- )
-
- return json.dumps(timeframes, cls=self.BSONEncoder)
-
- def delete_timeframe(self, timeframe_id: str):
- # Check if timeframe exists and if it doesn't return errors
- timeframe = self.mongo_module.get_document_by_id(
- f"{self.collection_prefix}timeframe", timeframe_id
- )
-
- if timeframe is None:
- raise NotFoundError(f"Timeframe with ID {timeframe_id} does not exist.")
-
- # If timeframe exists, get the eventIds (children)
- event_ids = [str(event["_id"]) for event in timeframe["events"]]
-
- # Delete all the events in the timeframe
- for event_id in event_ids:
- self.mongo_module.delete_document_by_id(
- f"{self.collection_prefix}event", event_id
- )
-
- # If timeframe exists, delete the timeframe document
- self.mongo_module.delete_document_by_id(
- f"{self.collection_prefix}timeframe", timeframe_id
- )
-
- return {"statusCode": 200}
-
- def create_event(self, timeframe_id: str, event_data: dict):
- event_data["dateCreated"] = datetime.datetime.now()
- event_data["timeframeId"] = timeframe_id
- event_data["usersAttended"] = []
-
- # Get Google Spreadsheet ID from timeframe
- timeframe_doc = self.mongo_module.get_document_by_id(
- f"{self.collection_prefix}timeframe", timeframe_id
- )
- spreadsheet_id = timeframe_doc["spreadsheetId"]
-
- # Add event name to Google Sheets
- gs = GoogleSheetsModule()
- col = gs.find_next_available_col(spreadsheet_id, event_data["sheetTab"])
- gs.add_event(spreadsheet_id, event_data["sheetTab"], event_data["name"], col)
-
- # Append next available col value to event data
- event_data["spreadsheetCol"] = col
-
- # Insert the event in event collection
- event_id = self.mongo_module.insert_document(
- f"{self.collection_prefix}event", event_data
- )
-
- event_data["eventId"] = str(event_id)
-
- # Insert child event in timeframe collection
- self.mongo_module.update_document(
- f"{self.collection_prefix}timeframe",
- timeframe_id,
- {"$push": {"events": event_data}},
- )
-
- return json.dumps(event_data, cls=self.BSONEncoder)
-
- def get_event(self, event_id: str):
- event = self.mongo_module.get_document_by_id(
- f"{self.collection_prefix}event", event_id
- )
-
- return json.dumps(event, cls=self.BSONEncoder)
-
- def checkin(self, event_id: str, user: dict) -> dict:
- """Checks in a user to an event.
-
- Arguments:
- event_id {str} -- ID of the event to check into.
- user {dict} -- Dictionary containing user ID and name.
-
- Returns:
- dict -- Dictionary containing status and message.
- """
- user_id, user_email = user["id"], user["email"]
- member = self.mongo_module.get_document_by_id(f"users", user_id)
- if member is None:
- raise NotFoundError(f"User with ID {user_id} does not exist.")
-
- user_name = member["name"]
-
- event = self.mongo_module.get_document_by_id(
- f"{self.collection_prefix}event", event_id
- )
-
- if any(d["userId"] == user_id for d in event["usersAttended"]):
- raise BadRequestError(f"{user_name} has already checked in.")
-
- checkin_data = {
- "userId": user_id,
- "name": user_name,
- "dateCheckedIn": datetime.datetime.now(),
- }
-
- # Get timeframe document to get Google Sheets info
- timeframe = self.mongo_module.get_document_by_id(
- f"{self.collection_prefix}timeframe", event["timeframeId"]
- )
-
- # Get Google Sheets information
- ss_id = timeframe["spreadsheetId"]
-
- # Initialize Google Sheets Module
- gs = GoogleSheetsModule()
-
- # Find row in Google Sheets that matches user's email
- row_num = gs.find_matching_email(
- spreadsheet_id=ss_id,
- sheet_name=event["sheetTab"],
- col="C",
- email_to_match=user_email,
- )
-
- if row_num == -1:
- return {
- "status": False,
- "message": f"{user_name} was not found in the sheet.",
- }
-
- # Update Google Sheets cell with a "1" if user has checked in
- gs.update_row(
- spreadsheet_id=ss_id,
- sheet_name=event["sheetTab"],
- col=event["spreadsheetCol"],
- row=row_num + 1,
- data=[["1"]],
- )
-
- # Update event collection with checkin data
- self.mongo_module.update_document(
- f"{self.collection_prefix}event",
- event_id,
- {"$push": {"usersAttended": checkin_data}},
- )
-
- # Send email to user that has checked in
- email_content = f"""
- Hi {user_name},
-
- Thank you for checking in to {event["name"]}! This is a confirmation that you have checked in.
-
- Regards,
- PCT Tech Team
-
- ** Please note: Do not reply to this email. This email is sent from an unattended mailbox. Replies will not be read.
- """
-
- ses_destination = SesDestination(tos=[user_email])
- ses.send_email(
- source="checkin-bot@why-phi.com",
- destination=ses_destination,
- subject=f"Check-in Confirmation: {event['name']}",
- text=email_content,
- html=email_content,
- )
-
- return {
- "status": True,
- "message": f"{user_name} has successfully been checked in.",
- }
-
- def delete(self, event_id: str):
- # Check if event exists and if it doesn't return errors
- event = self.mongo_module.get_document_by_id(
- f"{self.collection_prefix}event", event_id
- )
-
- if event is None:
- raise NotFoundError(f"Event with ID {event_id} does not exist.")
-
- # If event exists, get the timeframeId (parent)
- timeframe_id = event["timeframeId"]
-
- # Remove event from timeframe
- self.mongo_module.update_document(
- f"{self.collection_prefix}timeframe",
- timeframe_id,
- {"$pull": {"events": {"_id": ObjectId(event_id)}}},
- )
-
- # Delete the event document
- self.mongo_module.delete_document_by_id(f"{self.collection_prefix}event", event_id)
-
- def get_timeframe_sheets(self, timeframe_id: str):
- timeframe = self.mongo_module.get_document_by_id(
- f"{self.collection_prefix}timeframe", timeframe_id
- )
-
- if "spreadsheetId" not in timeframe or timeframe["spreadsheetId"] == "":
- return []
-
- gs = GoogleSheetsModule()
- sheets = gs.get_sheets(timeframe["spreadsheetId"], include_properties=False)
- return [sheet["title"] for sheet in sheets]
+ self.mongo_module = mongo_module
+ self.collection_prefix = "events-"
def get_rush_categories_and_events(self):
rush_categories = self.mongo_module.get_all_data_from_collection(
@@ -510,5 +288,4 @@ def delete_rush_event(self, event_id: str):
except Exception as e:
raise BadRequestError(e)
-
-event_service = EventService()
+events_rush_service = EventsRushService()
\ No newline at end of file
diff --git a/tests/services/test_event_service.py b/tests/services/test_event_service.py
index 1f25db5..937fc7a 100644
--- a/tests/services/test_event_service.py
+++ b/tests/services/test_event_service.py
@@ -1,6 +1,6 @@
import pytest
from unittest.mock import MagicMock, patch
-from chalicelib.services.EventService import EventService
+from zap.chalicelib.services.EventsMemberService import EventService
import datetime
import json
From 766273140c77a7ccabcc3f4300544509bcb03648 Mon Sep 17 00:00:00 2001
From: William De Rocco <93288641+wderocco8@users.noreply.github.com>
Date: Sun, 21 Jul 2024 15:13:13 -0400
Subject: [PATCH 14/34] fixed dictionary unpacking for updating event
---
chalicelib/services/EventsRushService.py | 49 ++++++++++++------------
1 file changed, 24 insertions(+), 25 deletions(-)
diff --git a/chalicelib/services/EventsRushService.py b/chalicelib/services/EventsRushService.py
index 9735887..59dbf56 100644
--- a/chalicelib/services/EventsRushService.py
+++ b/chalicelib/services/EventsRushService.py
@@ -47,14 +47,18 @@ def create_rush_event(self, data: dict):
data["eventCoverImage"] = image_url
# initialize attendee info (for rush-event)
- data["attendees"] = []
- data["numAttendees"] = 0
-
- data_copy = data.copy()
- # Remove catgoryId, attendees, and numAttendees from rush category (shouldn't be persisted)
- data_copy.pop("categoryId", None)
- data_copy.pop("attendees", None)
- data_copy.pop("numAttendees", None)
+ attendees_id = ObjectId()
+ attendees = {
+ "_id": attendees_id,
+ "eventId": event_id,
+ "attendees": [],
+ "numAtendees": 0
+ }
+ self.mongo_module.insert_document(
+ collection=f"{self.collection_prefix}rush-event-attendees",
+ data=attendees
+ )
+ data["attendeesId"] = attendees_id
# Add event to its own collection
self.mongo_module.insert_document(
@@ -65,7 +69,7 @@ def create_rush_event(self, data: dict):
self.mongo_module.update_document(
f"{self.collection_prefix}rush",
data["categoryId"],
- {"$push": {"events": data_copy}},
+ {"$push": { "events": data }},
)
return
@@ -74,10 +78,10 @@ def modify_rush_event(self, data: dict):
try:
event_id = data["_id"]
- object_event_id = ObjectId(event_id)
+ event_oid = ObjectId(event_id)
data["lastModified"] = datetime.datetime.now()
- data["_id"] = object_event_id
+ data["_id"] = event_oid
# Check if event exists in the rush-event collection
event = self.mongo_module.get_document_by_id(
@@ -104,24 +108,19 @@ def modify_rush_event(self, data: dict):
# add image_url to data object (this also replaces the original base64 image url)
data["eventCoverImage"] = image_url
-
- # Merge the existing event data with the new data
- updated_event = {**event, **data}
-
- # Remove catgoryId, attendees, and numAttendees from rush category (shouldn't be persisted)
- updated_event.pop("categoryId", None)
- updated_event.pop("attendees", None)
- updated_event.pop("numAttendees", None)
+ # Merge data with event (from client + mongo) --> NOTE: event must be unpacked first so
+ # that data overrides the matching keys
+ data = { **event, **data }
# Define array update query and filters
update_query = {
"$set": {
- "events.$[eventElem]": updated_event
+ "events.$[eventElem]": data
}
}
array_filters = [
- {"eventElem._id": object_event_id}
+ {"eventElem._id": event_oid}
]
# Modify the event in its category (rush collection)
@@ -251,11 +250,11 @@ def delete_rush_event(self, event_id: str):
"""
try:
# Get event_id as ObjectId
- object_event_id = ObjectId(event_id)
+ event_oid = ObjectId(event_id)
# Check if event exists in the rush-event collection
event = self.mongo_module.get_document_by_id(
- f"{self.collection_prefix}rush-event", object_event_id
+ f"{self.collection_prefix}rush-event", event_oid
)
if not event:
@@ -276,12 +275,12 @@ def delete_rush_event(self, event_id: str):
self.mongo_module.update_document(
f"{self.collection_prefix}rush",
event_category_id,
- {"$pull": {"events": {"_id": object_event_id}}},
+ {"$pull": {"events": {"_id": event_oid}}},
)
# Delete event data from the rush-event collection
self.mongo_module.delete_document_by_id(
- f"{self.collection_prefix}rush-event", object_event_id
+ f"{self.collection_prefix}rush-event", event_oid
)
return
From 1c751e314d25621378ea4870f88934e7c7e9ce61 Mon Sep 17 00:00:00 2001
From: William De Rocco <93288641+wderocco8@users.noreply.github.com>
Date: Sun, 21 Jul 2024 15:37:38 -0400
Subject: [PATCH 15/34] checkin and delete event fully integrated with new
attendees collection
---
chalicelib/services/EventsRushService.py | 42 ++++++++++++++++--------
1 file changed, 28 insertions(+), 14 deletions(-)
diff --git a/chalicelib/services/EventsRushService.py b/chalicelib/services/EventsRushService.py
index 59dbf56..6d3af39 100644
--- a/chalicelib/services/EventsRushService.py
+++ b/chalicelib/services/EventsRushService.py
@@ -52,7 +52,7 @@ def create_rush_event(self, data: dict):
"_id": attendees_id,
"eventId": event_id,
"attendees": [],
- "numAtendees": 0
+ "numAttendees": 0
}
self.mongo_module.insert_document(
collection=f"{self.collection_prefix}rush-event-attendees",
@@ -149,7 +149,6 @@ def modify_rush_event(self, data: dict):
return
-
except Exception as e:
print("error is ", e)
raise BadRequestError(e)
@@ -201,8 +200,7 @@ def get_rush_event(self, event_id: str, hide_attendees: bool = True):
)
if hide_attendees:
- event.pop("attendees", None)
- event.pop("numAttendees", None)
+ event.pop("attendeesId", None)
event.pop("code")
@@ -212,6 +210,12 @@ def checkin_rush(self, event_id: str, user_data: dict):
event = self.mongo_module.get_document_by_id(
f"{self.collection_prefix}rush-event", event_id
)
+
+ attendees_oid = event["attendeesId"]
+
+ attendees = self.mongo_module.get_document_by_id(
+ f"{self.collection_prefix}rush-event-attendees", attendees_oid
+ )
code = user_data["code"]
user_data.pop("code")
@@ -219,17 +223,18 @@ def checkin_rush(self, event_id: str, user_data: dict):
if code != event["code"]:
raise UnauthorizedError("Invalid code.")
- if any(d["email"] == user_data["email"] for d in event["attendees"]):
+ if any(d["email"] == user_data["email"] for d in attendees["attendees"]):
raise BadRequestError("User has already checked in.")
user_data["checkinTime"] = datetime.datetime.now()
- event["attendees"].append(user_data)
- event["numAttendees"] += 1
+ attendees["attendees"].append(user_data)
+ attendees["numAttendees"] += 1
+ # Update attendees collection with data
self.mongo_module.update_document(
- f"{self.collection_prefix}rush-event",
- event_id,
- {"$set": event},
+ collection=f"{self.collection_prefix}rush-event-attendees",
+ document_id=attendees_oid,
+ query={"$set": attendees},
)
return
@@ -260,6 +265,7 @@ def delete_rush_event(self, event_id: str):
if not event:
raise Exception("Event does not exist.")
+ attendees_oid = event["attendeesId"]
event_category_id = event["categoryId"]
# Get eventCoverImage path
@@ -273,15 +279,23 @@ def delete_rush_event(self, event_id: str):
# Delete the event from its category
self.mongo_module.update_document(
- f"{self.collection_prefix}rush",
- event_category_id,
- {"$pull": {"events": {"_id": event_oid}}},
+ collection=f"{self.collection_prefix}rush",
+ document_id=event_category_id,
+ query={"$pull": {"events": {"_id": event_oid}}},
)
# Delete event data from the rush-event collection
self.mongo_module.delete_document_by_id(
- f"{self.collection_prefix}rush-event", event_oid
+ collection=f"{self.collection_prefix}rush-event",
+ document_id=event_oid
+ )
+
+ # Delete attendees data from rush-event-attendees collection
+ self.mongo_module.delete_document_by_id(
+ collection=f"{self.collection_prefix}rush-event-attendees",
+ document_id=attendees_oid
)
+
return
except Exception as e:
From a3bb047e1b7294ea98d8d91cdc63165e502079b4 Mon Sep 17 00:00:00 2001
From: William De Rocco <93288641+wderocco8@users.noreply.github.com>
Date: Sun, 21 Jul 2024 15:42:33 -0400
Subject: [PATCH 16/34] edited pytest APIs
---
tests/api/test_events copy.py | 153 +++++++++++++
tests/api/test_events.py | 326 ---------------------------
tests/api/test_events_members.py | 178 +++++++++++++++
tests/services/test_event_service.py | 156 ++++++-------
4 files changed, 409 insertions(+), 404 deletions(-)
create mode 100644 tests/api/test_events copy.py
delete mode 100644 tests/api/test_events.py
create mode 100644 tests/api/test_events_members.py
diff --git a/tests/api/test_events copy.py b/tests/api/test_events copy.py
new file mode 100644
index 0000000..e79cad3
--- /dev/null
+++ b/tests/api/test_events copy.py
@@ -0,0 +1,153 @@
+from chalice.test import Client
+from unittest.mock import patch
+import json
+
+from app import app
+
+with open("tests/fixtures/events/general/sample_rush_events.json") as f:
+ SAMPLE_RUSH_EVENTS = json.load(f)
+
+with open("tests/fixtures/events/general/sample_rush_event.json") as f:
+ SAMPLE_RUSH_EVENT = json.load(f)
+
+def test_get_rush_events():
+ # Create a Chalice test client
+ with Client(app) as client:
+ # Mock applicant_service's get method
+ with patch("chalicelib.decorators.jwt.decode") as mock_decode:
+ # Assuming the decoded token has the required role
+ mock_decode.return_value = {"role": "admin"}
+ with patch(
+ "chalicelib.services.EventsRushService.events_rush_service.get_rush_categories_and_events",
+ ) as mock_get_rush_categories_and_events:
+ mock_get_rush_categories_and_events.return_value = SAMPLE_RUSH_EVENTS
+ response = client.http.get(
+ f"/events/rush",
+ headers={"Authorization": "Bearer SAMPLE_TOKEN_STRING"},
+ )
+
+ # Check the response status code and body
+ assert response.status_code == 200
+ assert response.json_body == SAMPLE_RUSH_EVENTS
+
+def test_get_rush_event():
+ # Create a Chalice test client
+ with Client(app) as client:
+ # Mock applicant_service's get method
+ with patch("chalicelib.decorators.jwt.decode") as mock_decode:
+ # Assuming the decoded token has the required role
+ mock_decode.return_value = {"role": "admin"}
+ with patch(
+ "chalicelib.services.EventsRushService.events_rush_service.get_rush_event",
+ ) as mock_get_rush_event:
+ mock_get_rush_event.return_value = SAMPLE_RUSH_EVENT
+ response = client.http.get(
+ f"/events/rush/test_event_id",
+ headers={"Authorization": "Bearer SAMPLE_TOKEN_STRING"},
+ )
+
+ # Check the response status code and body
+ assert response.status_code == 200
+ assert response.json_body == SAMPLE_RUSH_EVENT
+
+def test_create_rush_category():
+ with Client(app) as client:
+ with patch("chalicelib.decorators.jwt.decode") as mock_decode:
+ # Assuming the decoded token has the required role
+ mock_decode.return_value = {"role": "admin"}
+ with patch(
+ "chalicelib.services.EventsRushService.events_rush_service.create_rush_category"
+ ) as mock_create_rush_category:
+ mock_create_rush_category.return_value = {"msg": True}
+ response = client.http.post(
+ f"/events/rush/category",
+ headers={"Authorization": "Bearer SAMPLE_TOKEN_STRING"},
+ )
+
+ assert response.status_code == 200
+ assert response.json_body == {"msg": True}
+
+def test_create_rush_event():
+ with Client(app) as client:
+ with patch("chalicelib.decorators.jwt.decode") as mock_decode:
+ # Assuming the decoded token has the required role
+ mock_decode.return_value = {"role": "admin"}
+ with patch(
+ "chalicelib.services.EventsRushService.events_rush_service.create_rush_event"
+ ) as mock_create_rush_event:
+ mock_create_rush_event.return_value = {"msg": True}
+ response = client.http.post(
+ f"/events/rush",
+ headers={"Authorization": "Bearer SAMPLE_TOKEN_STRING"},
+ )
+
+ assert response.status_code == 200
+ assert response.json_body == {"msg": True}
+
+def test_modify_rush_event():
+ with Client(app) as client:
+ with patch("chalicelib.decorators.jwt.decode") as mock_decode:
+ # Assuming the decoded token has the required role
+ mock_decode.return_value = {"role": "admin"}
+ with patch(
+ "chalicelib.services.EventsRushService.events_rush_service.modify_rush_event"
+ ) as mock_modify_rush_event:
+ mock_modify_rush_event.return_value = {"msg": True}
+ response = client.http.patch(
+ f"/events/rush",
+ headers={"Authorization": "Bearer SAMPLE_TOKEN_STRING"},
+ )
+
+ assert response.status_code == 200
+ assert response.json_body == {"msg": True}
+
+def test_modify_rush_settings():
+ with Client(app) as client:
+ with patch("chalicelib.decorators.jwt.decode") as mock_decode:
+ # Assuming the decoded token has the required role
+ mock_decode.return_value = {"role": "admin"}
+ with patch(
+ "chalicelib.services.EventsRushService.events_rush_service.modify_rush_settings"
+ ) as mock_modify_rush_settings:
+ mock_modify_rush_settings.return_value = {"msg": True}
+ response = client.http.patch(
+ f"/events/rush/settings",
+ headers={"Authorization": "Bearer SAMPLE_TOKEN_STRING"},
+ )
+
+ assert response.status_code == 200
+ assert response.json_body == {"msg": True}
+
+def test_checkin_rush():
+ with Client(app) as client:
+ with patch("chalicelib.decorators.jwt.decode") as mock_decode:
+ # Assuming the decoded token has the required role
+ mock_decode.return_value = {"role": "admin"}
+ with patch(
+ "chalicelib.services.EventsRushService.events_rush_service.checkin_rush"
+ ) as mock_checkin_rush:
+ mock_checkin_rush.return_value = {"msg": True}
+ response = client.http.post(
+ f"/events/rush/checkin/test_event_id",
+ headers={"Authorization": "Bearer SAMPLE_TOKEN_STRING"},
+ )
+
+ assert response.status_code == 200
+ assert response.json_body == {"msg": True}
+
+def test_delete_rush_event():
+ with Client(app) as client:
+ with patch("chalicelib.decorators.jwt.decode") as mock_decode:
+ # Assuming the decoded token has the required role
+ mock_decode.return_value = {"role": "admin"}
+ with patch(
+ "chalicelib.services.EventsRushService.events_rush_service.delete_rush_event",
+ ) as mock_delete_rush_event:
+ mock_delete_rush_event.return_value = {"status": True}
+ response = client.http.delete(
+ f"/events/rush/test_event_id",
+ headers={"Authorization": "Bearer SAMPLE_TOKEN_STRING"},
+ )
+
+ assert response.status_code == 200
+ assert response.json_body == {'status': True}
\ No newline at end of file
diff --git a/tests/api/test_events.py b/tests/api/test_events.py
deleted file mode 100644
index c6a1b58..0000000
--- a/tests/api/test_events.py
+++ /dev/null
@@ -1,326 +0,0 @@
-from chalice.test import Client
-from unittest.mock import patch
-import json
-
-from app import app
-
-with open("tests/fixtures/events/general/sample_timeframes.json") as f:
- SAMPLE_TIMEFRAMES = json.load(f)
-
-with open("tests/fixtures/events/general/sample_timeframe_sheets.json") as f:
- SAMPLE_TIMEFRAME_SHEETS = json.load(f)
-
-with open("tests/fixtures/events/general/sample_rush_events.json") as f:
- SAMPLE_RUSH_EVENTS = json.load(f)
-
-with open("tests/fixtures/events/general/sample_rush_event.json") as f:
- SAMPLE_RUSH_EVENT = json.load(f)
-
-def test_create_timeframe():
- with Client(app) as client:
- with patch("chalicelib.decorators.jwt.decode") as mock_decode:
- # Assuming the decoded token has the required role
- mock_decode.return_value = {"role": "admin"}
- with patch(
- "chalicelib.services.EventService.event_service.create_timeframe"
- ) as mock_create_timeframe:
- mock_create_timeframe.return_value = {"msg": True}
- response = client.http.post(
- f"/timeframes",
- headers={"Authorization": "Bearer SAMPLE_TOKEN_STRING"},
- )
-
- assert response.status_code == 200
- assert response.json_body == {"msg": True}
-
-
-def test_get_all_timeframes():
- # Create a Chalice test client
- with Client(app) as client:
- # Mock applicant_service's get method
- with patch("chalicelib.decorators.jwt.decode") as mock_decode:
- # Assuming the decoded token has the required role
- mock_decode.return_value = {"role": "admin"}
- with patch(
- "chalicelib.services.EventService.event_service.get_all_timeframes",
- ) as mock_get_all_timeframes:
- mock_get_all_timeframes.return_value = SAMPLE_TIMEFRAMES
- response = client.http.get(
- f"/timeframes",
- headers={"Authorization": "Bearer SAMPLE_TOKEN_STRING"},
- )
-
- # Check the response status code and body
- assert response.status_code == 200
- assert response.json_body == SAMPLE_TIMEFRAMES
-
-def test_get_timeframe():
- # Create a Chalice test client
- with Client(app) as client:
- # Mock applicant_service's get method
- with patch("chalicelib.decorators.jwt.decode") as mock_decode:
- # Assuming the decoded token has the required role
- mock_decode.return_value = {"role": "admin"}
- with patch(
- "chalicelib.services.EventService.event_service.get_timeframe",
- ) as mock_get_timeframe:
- mock_get_timeframe.return_value = SAMPLE_TIMEFRAMES[0]
- response = client.http.get(
- f"/timeframes/test_timeframe_id",
- headers={"Authorization": "Bearer SAMPLE_TOKEN_STRING"},
- )
-
- # Check the response status code and body
- assert response.status_code == 200
- assert response.json_body == SAMPLE_TIMEFRAMES[0]
-
-def test_delete_timeframe():
- with Client(app) as client:
- with patch("chalicelib.decorators.jwt.decode") as mock_decode:
- # Assuming the decoded token has the required role
- mock_decode.return_value = {"role": "admin"}
- with patch(
- "chalicelib.services.EventService.event_service.delete_timeframe",
- ) as mock_delete:
- mock_delete.return_value = {"status": True}
- response = client.http.delete(
- f"/timeframes/{SAMPLE_TIMEFRAMES[0]['_id']}",
- headers={"Authorization": "Bearer SAMPLE_TOKEN_STRING"},
- )
-
- assert response.status_code == 200
- assert response.json_body == {'status': True}
-
-
-def test_create_event():
- with Client(app) as client:
- with patch("chalicelib.decorators.jwt.decode") as mock_decode:
- # Assuming the decoded token has the required role
- mock_decode.return_value = {"role": "admin"}
- with patch(
- "chalicelib.services.EventService.event_service.create_event"
- ) as mock_create_event:
- mock_create_event.return_value = {"msg": True}
- response = client.http.post(
- f"/timeframes/{SAMPLE_TIMEFRAMES[0]['_id']}/events",
- headers={"Authorization": "Bearer SAMPLE_TOKEN_STRING"},
- )
-
- assert response.status_code == 200
- assert response.json_body == {"msg": True}
-
-def test_get_event():
- # Create a Chalice test client
- with Client(app) as client:
- # Mock applicant_service's get method
- with patch("chalicelib.decorators.jwt.decode") as mock_decode:
- # Assuming the decoded token has the required role
- mock_decode.return_value = {"role": "admin"}
- with patch(
- "chalicelib.services.EventService.event_service.get_timeframe",
- ) as mock_get_timeframe:
- mock_get_timeframe.return_value = SAMPLE_TIMEFRAMES[0]
- response = client.http.get(
- f"/timeframes/test_timeframe_id",
- headers={"Authorization": "Bearer SAMPLE_TOKEN_STRING"},
- )
-
- # Check the response status code and body
- assert response.status_code == 200
- assert response.json_body == SAMPLE_TIMEFRAMES[0]
-
-def test_get_timeframe_sheets():
- # Create a Chalice test client
- with Client(app) as client:
- # Mock applicant_service's get method
- with patch("chalicelib.decorators.jwt.decode") as mock_decode:
- # Assuming the decoded token has the required role
- mock_decode.return_value = {"role": "admin"}
- with patch(
- "chalicelib.services.EventService.event_service.get_timeframe_sheets",
- ) as mock_get_timeframe_sheets:
- mock_get_timeframe_sheets.return_value = SAMPLE_TIMEFRAME_SHEETS
- response = client.http.get(
- f"/timeframes/test_timeframe_id/sheets",
- headers={"Authorization": "Bearer SAMPLE_TOKEN_STRING"},
- )
-
- # Check the response status code and body
- assert response.status_code == 200
- assert response.json_body == SAMPLE_TIMEFRAME_SHEETS
-
-def test_checkin():
- with Client(app) as client:
- with patch("chalicelib.decorators.jwt.decode") as mock_decode:
- # Assuming the decoded token has the required role
- mock_decode.return_value = {"role": "admin"}
- with patch(
- "chalicelib.services.EventService.event_service.checkin"
- ) as mock_checkin:
- mock_checkin.return_value = {"msg": True}
- response = client.http.post(
- f"/events/test_event_id/checkin",
- headers={"Authorization": "Bearer SAMPLE_TOKEN_STRING"},
- )
-
- assert response.status_code == 200
- assert response.json_body == {"msg": True}
-
-def test_delete_event():
- with Client(app) as client:
- with patch("chalicelib.decorators.jwt.decode") as mock_decode:
- # Assuming the decoded token has the required role
- mock_decode.return_value = {"role": "admin"}
- with patch(
- "chalicelib.services.EventService.event_service.delete",
- ) as mock_delete:
- mock_delete.return_value = {"status": True}
- response = client.http.delete(
- f"/events/test_event_id",
- headers={"Authorization": "Bearer SAMPLE_TOKEN_STRING"},
- )
-
- assert response.status_code == 200
- assert response.json_body == {'status': True}
-
-def test_get_rush_events():
- # Create a Chalice test client
- with Client(app) as client:
- # Mock applicant_service's get method
- with patch("chalicelib.decorators.jwt.decode") as mock_decode:
- # Assuming the decoded token has the required role
- mock_decode.return_value = {"role": "admin"}
- with patch(
- "chalicelib.services.EventService.event_service.get_rush_categories_and_events",
- ) as mock_get_rush_categories_and_events:
- mock_get_rush_categories_and_events.return_value = SAMPLE_RUSH_EVENTS
- response = client.http.get(
- f"/events/rush",
- headers={"Authorization": "Bearer SAMPLE_TOKEN_STRING"},
- )
-
- # Check the response status code and body
- assert response.status_code == 200
- assert response.json_body == SAMPLE_RUSH_EVENTS
-
-def test_get_rush_event():
- # Create a Chalice test client
- with Client(app) as client:
- # Mock applicant_service's get method
- with patch("chalicelib.decorators.jwt.decode") as mock_decode:
- # Assuming the decoded token has the required role
- mock_decode.return_value = {"role": "admin"}
- with patch(
- "chalicelib.services.EventService.event_service.get_rush_event",
- ) as mock_get_rush_event:
- mock_get_rush_event.return_value = SAMPLE_RUSH_EVENT
- response = client.http.get(
- f"/events/rush/test_event_id",
- headers={"Authorization": "Bearer SAMPLE_TOKEN_STRING"},
- )
-
- # Check the response status code and body
- assert response.status_code == 200
- assert response.json_body == SAMPLE_RUSH_EVENT
-
-def test_create_rush_category():
- with Client(app) as client:
- with patch("chalicelib.decorators.jwt.decode") as mock_decode:
- # Assuming the decoded token has the required role
- mock_decode.return_value = {"role": "admin"}
- with patch(
- "chalicelib.services.EventService.event_service.create_rush_category"
- ) as mock_create_rush_category:
- mock_create_rush_category.return_value = {"msg": True}
- response = client.http.post(
- f"/events/rush/category",
- headers={"Authorization": "Bearer SAMPLE_TOKEN_STRING"},
- )
-
- assert response.status_code == 200
- assert response.json_body == {"msg": True}
-
-def test_create_rush_event():
- with Client(app) as client:
- with patch("chalicelib.decorators.jwt.decode") as mock_decode:
- # Assuming the decoded token has the required role
- mock_decode.return_value = {"role": "admin"}
- with patch(
- "chalicelib.services.EventService.event_service.create_rush_event"
- ) as mock_create_rush_event:
- mock_create_rush_event.return_value = {"msg": True}
- response = client.http.post(
- f"/events/rush",
- headers={"Authorization": "Bearer SAMPLE_TOKEN_STRING"},
- )
-
- assert response.status_code == 200
- assert response.json_body == {"msg": True}
-
-def test_modify_rush_event():
- with Client(app) as client:
- with patch("chalicelib.decorators.jwt.decode") as mock_decode:
- # Assuming the decoded token has the required role
- mock_decode.return_value = {"role": "admin"}
- with patch(
- "chalicelib.services.EventService.event_service.modify_rush_event"
- ) as mock_modify_rush_event:
- mock_modify_rush_event.return_value = {"msg": True}
- response = client.http.patch(
- f"/events/rush",
- headers={"Authorization": "Bearer SAMPLE_TOKEN_STRING"},
- )
-
- assert response.status_code == 200
- assert response.json_body == {"msg": True}
-
-def test_modify_rush_settings():
- with Client(app) as client:
- with patch("chalicelib.decorators.jwt.decode") as mock_decode:
- # Assuming the decoded token has the required role
- mock_decode.return_value = {"role": "admin"}
- with patch(
- "chalicelib.services.EventService.event_service.modify_rush_settings"
- ) as mock_modify_rush_settings:
- mock_modify_rush_settings.return_value = {"msg": True}
- response = client.http.patch(
- f"/events/rush/settings",
- headers={"Authorization": "Bearer SAMPLE_TOKEN_STRING"},
- )
-
- assert response.status_code == 200
- assert response.json_body == {"msg": True}
-
-def test_checkin_rush():
- with Client(app) as client:
- with patch("chalicelib.decorators.jwt.decode") as mock_decode:
- # Assuming the decoded token has the required role
- mock_decode.return_value = {"role": "admin"}
- with patch(
- "chalicelib.services.EventService.event_service.checkin_rush"
- ) as mock_checkin_rush:
- mock_checkin_rush.return_value = {"msg": True}
- response = client.http.post(
- f"/events/rush/checkin/test_event_id",
- headers={"Authorization": "Bearer SAMPLE_TOKEN_STRING"},
- )
-
- assert response.status_code == 200
- assert response.json_body == {"msg": True}
-
-def test_delete_rush_event():
- with Client(app) as client:
- with patch("chalicelib.decorators.jwt.decode") as mock_decode:
- # Assuming the decoded token has the required role
- mock_decode.return_value = {"role": "admin"}
- with patch(
- "chalicelib.services.EventService.event_service.delete_rush_event",
- ) as mock_delete_rush_event:
- mock_delete_rush_event.return_value = {"status": True}
- response = client.http.delete(
- f"/events/rush/test_event_id",
- headers={"Authorization": "Bearer SAMPLE_TOKEN_STRING"},
- )
-
- assert response.status_code == 200
- assert response.json_body == {'status': True}
\ No newline at end of file
diff --git a/tests/api/test_events_members.py b/tests/api/test_events_members.py
new file mode 100644
index 0000000..a16ec19
--- /dev/null
+++ b/tests/api/test_events_members.py
@@ -0,0 +1,178 @@
+from chalice.test import Client
+from unittest.mock import patch
+import json
+
+from app import app
+
+with open("tests/fixtures/events/general/sample_timeframes.json") as f:
+ SAMPLE_TIMEFRAMES = json.load(f)
+
+with open("tests/fixtures/events/general/sample_timeframe_sheets.json") as f:
+ SAMPLE_TIMEFRAME_SHEETS = json.load(f)
+
+def test_create_timeframe():
+ with Client(app) as client:
+ with patch("chalicelib.decorators.jwt.decode") as mock_decode:
+ # Assuming the decoded token has the required role
+ mock_decode.return_value = {"role": "admin"}
+ with patch(
+ "chalicelib.services.EventsMemberService.events_member_service.create_timeframe"
+ ) as mock_create_timeframe:
+ mock_create_timeframe.return_value = {"msg": True}
+ response = client.http.post(
+ f"/timeframes",
+ headers={"Authorization": "Bearer SAMPLE_TOKEN_STRING"},
+ )
+
+ assert response.status_code == 200
+ assert response.json_body == {"msg": True}
+
+
+def test_get_all_timeframes():
+ # Create a Chalice test client
+ with Client(app) as client:
+ # Mock applicant_service's get method
+ with patch("chalicelib.decorators.jwt.decode") as mock_decode:
+ # Assuming the decoded token has the required role
+ mock_decode.return_value = {"role": "admin"}
+ with patch(
+ "chalicelib.services.EventsMemberService.events_member_service.get_all_timeframes",
+ ) as mock_get_all_timeframes:
+ mock_get_all_timeframes.return_value = SAMPLE_TIMEFRAMES
+ response = client.http.get(
+ f"/timeframes",
+ headers={"Authorization": "Bearer SAMPLE_TOKEN_STRING"},
+ )
+
+ # Check the response status code and body
+ assert response.status_code == 200
+ assert response.json_body == SAMPLE_TIMEFRAMES
+
+def test_get_timeframe():
+ # Create a Chalice test client
+ with Client(app) as client:
+ # Mock applicant_service's get method
+ with patch("chalicelib.decorators.jwt.decode") as mock_decode:
+ # Assuming the decoded token has the required role
+ mock_decode.return_value = {"role": "admin"}
+ with patch(
+ "chalicelib.services.EventsMemberService.events_member_service.get_timeframe",
+ ) as mock_get_timeframe:
+ mock_get_timeframe.return_value = SAMPLE_TIMEFRAMES[0]
+ response = client.http.get(
+ f"/timeframes/test_timeframe_id",
+ headers={"Authorization": "Bearer SAMPLE_TOKEN_STRING"},
+ )
+
+ # Check the response status code and body
+ assert response.status_code == 200
+ assert response.json_body == SAMPLE_TIMEFRAMES[0]
+
+def test_delete_timeframe():
+ with Client(app) as client:
+ with patch("chalicelib.decorators.jwt.decode") as mock_decode:
+ # Assuming the decoded token has the required role
+ mock_decode.return_value = {"role": "admin"}
+ with patch(
+ "chalicelib.services.EventsMemberService.events_member_service.delete_timeframe",
+ ) as mock_delete:
+ mock_delete.return_value = {"status": True}
+ response = client.http.delete(
+ f"/timeframes/{SAMPLE_TIMEFRAMES[0]['_id']}",
+ headers={"Authorization": "Bearer SAMPLE_TOKEN_STRING"},
+ )
+
+ assert response.status_code == 200
+ assert response.json_body == {'status': True}
+
+
+def test_create_event():
+ with Client(app) as client:
+ with patch("chalicelib.decorators.jwt.decode") as mock_decode:
+ # Assuming the decoded token has the required role
+ mock_decode.return_value = {"role": "admin"}
+ with patch(
+ "chalicelib.services.EventsMemberService.events_member_service.create_event"
+ ) as mock_create_event:
+ mock_create_event.return_value = {"msg": True}
+ response = client.http.post(
+ f"/timeframes/{SAMPLE_TIMEFRAMES[0]['_id']}/events",
+ headers={"Authorization": "Bearer SAMPLE_TOKEN_STRING"},
+ )
+
+ assert response.status_code == 200
+ assert response.json_body == {"msg": True}
+
+def test_get_event():
+ # Create a Chalice test client
+ with Client(app) as client:
+ # Mock applicant_service's get method
+ with patch("chalicelib.decorators.jwt.decode") as mock_decode:
+ # Assuming the decoded token has the required role
+ mock_decode.return_value = {"role": "admin"}
+ with patch(
+ "chalicelib.services.EventsMemberService.events_member_service.get_timeframe",
+ ) as mock_get_timeframe:
+ mock_get_timeframe.return_value = SAMPLE_TIMEFRAMES[0]
+ response = client.http.get(
+ f"/timeframes/test_timeframe_id",
+ headers={"Authorization": "Bearer SAMPLE_TOKEN_STRING"},
+ )
+
+ # Check the response status code and body
+ assert response.status_code == 200
+ assert response.json_body == SAMPLE_TIMEFRAMES[0]
+
+def test_get_timeframe_sheets():
+ # Create a Chalice test client
+ with Client(app) as client:
+ # Mock applicant_service's get method
+ with patch("chalicelib.decorators.jwt.decode") as mock_decode:
+ # Assuming the decoded token has the required role
+ mock_decode.return_value = {"role": "admin"}
+ with patch(
+ "chalicelib.services.EventsMemberService.events_member_service.get_timeframe_sheets",
+ ) as mock_get_timeframe_sheets:
+ mock_get_timeframe_sheets.return_value = SAMPLE_TIMEFRAME_SHEETS
+ response = client.http.get(
+ f"/timeframes/test_timeframe_id/sheets",
+ headers={"Authorization": "Bearer SAMPLE_TOKEN_STRING"},
+ )
+
+ # Check the response status code and body
+ assert response.status_code == 200
+ assert response.json_body == SAMPLE_TIMEFRAME_SHEETS
+
+def test_checkin():
+ with Client(app) as client:
+ with patch("chalicelib.decorators.jwt.decode") as mock_decode:
+ # Assuming the decoded token has the required role
+ mock_decode.return_value = {"role": "admin"}
+ with patch(
+ "chalicelib.services.EventsMemberService.events_member_service.checkin"
+ ) as mock_checkin:
+ mock_checkin.return_value = {"msg": True}
+ response = client.http.post(
+ f"/events/test_event_id/checkin",
+ headers={"Authorization": "Bearer SAMPLE_TOKEN_STRING"},
+ )
+
+ assert response.status_code == 200
+ assert response.json_body == {"msg": True}
+
+def test_delete_event():
+ with Client(app) as client:
+ with patch("chalicelib.decorators.jwt.decode") as mock_decode:
+ # Assuming the decoded token has the required role
+ mock_decode.return_value = {"role": "admin"}
+ with patch(
+ "chalicelib.services.EventsMemberService.events_member_service.delete",
+ ) as mock_delete:
+ mock_delete.return_value = {"status": True}
+ response = client.http.delete(
+ f"/events/test_event_id",
+ headers={"Authorization": "Bearer SAMPLE_TOKEN_STRING"},
+ )
+
+ assert response.status_code == 200
+ assert response.json_body == {'status': True}
\ No newline at end of file
diff --git a/tests/services/test_event_service.py b/tests/services/test_event_service.py
index 937fc7a..bd8ce44 100644
--- a/tests/services/test_event_service.py
+++ b/tests/services/test_event_service.py
@@ -1,99 +1,99 @@
-import pytest
-from unittest.mock import MagicMock, patch
-from zap.chalicelib.services.EventsMemberService import EventService
-import datetime
-import json
-
-with open("tests/fixtures/events/general/sample_timeframes.json") as f:
- SAMPLE_TIMEFRAMES: list = json.load(f)
-
-with open("tests/fixtures/events/general/sample_timeframe_sheets.json") as f:
- SAMPLE_TIMEFRAME_SHEETS: list = json.load(f)
-
-with open("tests/fixtures/events/general/sample_rush_events.json") as f:
- SAMPLE_RUSH_EVENTS: list = json.load(f)
-
-with open("tests/fixtures/events/general/sample_rush_event.json") as f:
- SAMPLE_RUSH_EVENT = json.load(f)
-
-@pytest.fixture
-def mock_mongo_module():
- with patch("chalicelib.modules.mongo.MongoModule", autospec=True) as MockMongoModule:
- mock_instance = MockMongoModule(use_mock=True)
- yield mock_instance
-
-@pytest.fixture
-def event_service(mock_mongo_module):
- return EventService(mock_mongo_module)
-
-def test_insert_document(event_service, mock_mongo_module):
- CREATE_TIMEFRAME = { "name": "testTimeframeName", "spreadsheetId": "testSpreadsheetId" }
- date_created = datetime.datetime.now()
-
- with patch("chalicelib.services.EventService.datetime") as mock_datetime:
- mock_datetime.datetime.now.return_value = date_created
- result = event_service.create_timeframe(CREATE_TIMEFRAME)
- CREATE_TIMEFRAME["date_created"] = date_created
- mock_mongo_module.insert_document.assert_called_once_with(
- collection="events-timeframe", data=CREATE_TIMEFRAME
- )
-
- assert result == {"msg": True}
-
-def test_get_timeframe(event_service, mock_mongo_module):
- mock_mongo_module.get_document_by_id.return_value = SAMPLE_TIMEFRAMES[0]
- timeframe_id = SAMPLE_TIMEFRAMES[0]["_id"]
+# import pytest
+# from unittest.mock import MagicMock, patch
+# from zap.chalicelib.services.EventsMemberService import EventService
+# import datetime
+# import json
+
+# with open("tests/fixtures/events/general/sample_timeframes.json") as f:
+# SAMPLE_TIMEFRAMES: list = json.load(f)
+
+# with open("tests/fixtures/events/general/sample_timeframe_sheets.json") as f:
+# SAMPLE_TIMEFRAME_SHEETS: list = json.load(f)
+
+# with open("tests/fixtures/events/general/sample_rush_events.json") as f:
+# SAMPLE_RUSH_EVENTS: list = json.load(f)
+
+# with open("tests/fixtures/events/general/sample_rush_event.json") as f:
+# SAMPLE_RUSH_EVENT = json.load(f)
+
+# @pytest.fixture
+# def mock_mongo_module():
+# with patch("chalicelib.modules.mongo.MongoModule", autospec=True) as MockMongoModule:
+# mock_instance = MockMongoModule(use_mock=True)
+# yield mock_instance
+
+# @pytest.fixture
+# def event_service(mock_mongo_module):
+# return EventService(mock_mongo_module)
+
+# def test_insert_document(event_service, mock_mongo_module):
+# CREATE_TIMEFRAME = { "name": "testTimeframeName", "spreadsheetId": "testSpreadsheetId" }
+# date_created = datetime.datetime.now()
+
+# with patch("chalicelib.services.EventService.datetime") as mock_datetime:
+# mock_datetime.datetime.now.return_value = date_created
+# result = event_service.create_timeframe(CREATE_TIMEFRAME)
+# CREATE_TIMEFRAME["date_created"] = date_created
+# mock_mongo_module.insert_document.assert_called_once_with(
+# collection="events-timeframe", data=CREATE_TIMEFRAME
+# )
+
+# assert result == {"msg": True}
+
+# def test_get_timeframe(event_service, mock_mongo_module):
+# mock_mongo_module.get_document_by_id.return_value = SAMPLE_TIMEFRAMES[0]
+# timeframe_id = SAMPLE_TIMEFRAMES[0]["_id"]
- result = event_service.get_timeframe(timeframe_id=timeframe_id)
- mock_mongo_module.get_document_by_id.assert_called_once_with(
- collection="events-timeframe", document_id=timeframe_id
- )
+# result = event_service.get_timeframe(timeframe_id=timeframe_id)
+# mock_mongo_module.get_document_by_id.assert_called_once_with(
+# collection="events-timeframe", document_id=timeframe_id
+# )
- assert result == json.dumps(SAMPLE_TIMEFRAMES[0])
+# assert result == json.dumps(SAMPLE_TIMEFRAMES[0])
-def test_get_all_timeframe(event_service, mock_mongo_module):
- mock_mongo_module.get_all_data_from_collection.return_value = SAMPLE_TIMEFRAMES
+# def test_get_all_timeframe(event_service, mock_mongo_module):
+# mock_mongo_module.get_all_data_from_collection.return_value = SAMPLE_TIMEFRAMES
- result = event_service.get_all_timeframes()
- mock_mongo_module.get_all_data_from_collection.assert_called_once_with(
- collection="events-timeframe"
- )
+# result = event_service.get_all_timeframes()
+# mock_mongo_module.get_all_data_from_collection.assert_called_once_with(
+# collection="events-timeframe"
+# )
- assert result == json.dumps(SAMPLE_TIMEFRAMES)
+# assert result == json.dumps(SAMPLE_TIMEFRAMES)
-def test_delete_timeframe(event_service, mock_mongo_module):
- result = event_service.delete_timeframe(timeframe_id=SAMPLE_TIMEFRAMES[0]["_id"])
- assert result["statusCode"] == 200
+# def test_delete_timeframe(event_service, mock_mongo_module):
+# result = event_service.delete_timeframe(timeframe_id=SAMPLE_TIMEFRAMES[0]["_id"])
+# assert result["statusCode"] == 200
-# TODO: potentially add mocking for GoogleSheetsModule (otherwise test is not isolated)
-# def test_create_event(event_service, mock_mongo_module):
-# CREATE_TIMEFRAME = { "name": "eventName", "tags": "tags", "sheetTab": "selectedSheetTab" }
-# date_created = datetime.datetime.now()
-# timeframe_id = "timeframeId"
+# # TODO: potentially add mocking for GoogleSheetsModule (otherwise test is not isolated)
+# # def test_create_event(event_service, mock_mongo_module):
+# # CREATE_TIMEFRAME = { "name": "eventName", "tags": "tags", "sheetTab": "selectedSheetTab" }
+# # date_created = datetime.datetime.now()
+# # timeframe_id = "timeframeId"
-# TODO:
+# # TODO:
-# def get_event(self, event_id: str):
+# # def get_event(self, event_id: str):
-# def checkin(self, event_id: str, user: dict) -> dict:
+# # def checkin(self, event_id: str, user: dict) -> dict:
-# def delete(self, event_id: str):
+# # def delete(self, event_id: str):
-# def get_timeframe_sheets(self, timeframe_id: str):
+# # def get_timeframe_sheets(self, timeframe_id: str):
-# def get_rush_categories_and_events(self):
+# # def get_rush_categories_and_events(self):
-# def create_rush_category(self, data: dict):
+# # def create_rush_category(self, data: dict):
-# def create_rush_event(self, data: dict):
+# # def create_rush_event(self, data: dict):
-# def modify_rush_event(self, data: dict):
+# # def modify_rush_event(self, data: dict):
-# def modify_rush_settings(self, data: dict):
+# # def modify_rush_settings(self, data: dict):
-# def get_rush_event(self, event_id: str, hide_attendees: bool = True):
+# # def get_rush_event(self, event_id: str, hide_attendees: bool = True):
-# def checkin_rush(self, event_id: str, user_data: dict):
+# # def checkin_rush(self, event_id: str, user_data: dict):
-# def delete_rush_event(self, event_id: str):
+# # def delete_rush_event(self, event_id: str):
From 46087fece4090e55de8c75a552d87b9214639808 Mon Sep 17 00:00:00 2001
From: William De Rocco <93288641+wderocco8@users.noreply.github.com>
Date: Sun, 21 Jul 2024 15:45:52 -0400
Subject: [PATCH 17/34] refactored events testing
---
...est_events copy.py => test_events_rush.py} | 0
tests/services/test_event_service.py | 99 -----------------
tests/services/test_events_member_service.py | 101 ++++++++++++++++++
3 files changed, 101 insertions(+), 99 deletions(-)
rename tests/api/{test_events copy.py => test_events_rush.py} (100%)
delete mode 100644 tests/services/test_event_service.py
create mode 100644 tests/services/test_events_member_service.py
diff --git a/tests/api/test_events copy.py b/tests/api/test_events_rush.py
similarity index 100%
rename from tests/api/test_events copy.py
rename to tests/api/test_events_rush.py
diff --git a/tests/services/test_event_service.py b/tests/services/test_event_service.py
deleted file mode 100644
index bd8ce44..0000000
--- a/tests/services/test_event_service.py
+++ /dev/null
@@ -1,99 +0,0 @@
-# import pytest
-# from unittest.mock import MagicMock, patch
-# from zap.chalicelib.services.EventsMemberService import EventService
-# import datetime
-# import json
-
-# with open("tests/fixtures/events/general/sample_timeframes.json") as f:
-# SAMPLE_TIMEFRAMES: list = json.load(f)
-
-# with open("tests/fixtures/events/general/sample_timeframe_sheets.json") as f:
-# SAMPLE_TIMEFRAME_SHEETS: list = json.load(f)
-
-# with open("tests/fixtures/events/general/sample_rush_events.json") as f:
-# SAMPLE_RUSH_EVENTS: list = json.load(f)
-
-# with open("tests/fixtures/events/general/sample_rush_event.json") as f:
-# SAMPLE_RUSH_EVENT = json.load(f)
-
-# @pytest.fixture
-# def mock_mongo_module():
-# with patch("chalicelib.modules.mongo.MongoModule", autospec=True) as MockMongoModule:
-# mock_instance = MockMongoModule(use_mock=True)
-# yield mock_instance
-
-# @pytest.fixture
-# def event_service(mock_mongo_module):
-# return EventService(mock_mongo_module)
-
-# def test_insert_document(event_service, mock_mongo_module):
-# CREATE_TIMEFRAME = { "name": "testTimeframeName", "spreadsheetId": "testSpreadsheetId" }
-# date_created = datetime.datetime.now()
-
-# with patch("chalicelib.services.EventService.datetime") as mock_datetime:
-# mock_datetime.datetime.now.return_value = date_created
-# result = event_service.create_timeframe(CREATE_TIMEFRAME)
-# CREATE_TIMEFRAME["date_created"] = date_created
-# mock_mongo_module.insert_document.assert_called_once_with(
-# collection="events-timeframe", data=CREATE_TIMEFRAME
-# )
-
-# assert result == {"msg": True}
-
-# def test_get_timeframe(event_service, mock_mongo_module):
-# mock_mongo_module.get_document_by_id.return_value = SAMPLE_TIMEFRAMES[0]
-# timeframe_id = SAMPLE_TIMEFRAMES[0]["_id"]
-
-# result = event_service.get_timeframe(timeframe_id=timeframe_id)
-# mock_mongo_module.get_document_by_id.assert_called_once_with(
-# collection="events-timeframe", document_id=timeframe_id
-# )
-
-# assert result == json.dumps(SAMPLE_TIMEFRAMES[0])
-
-# def test_get_all_timeframe(event_service, mock_mongo_module):
-# mock_mongo_module.get_all_data_from_collection.return_value = SAMPLE_TIMEFRAMES
-
-# result = event_service.get_all_timeframes()
-# mock_mongo_module.get_all_data_from_collection.assert_called_once_with(
-# collection="events-timeframe"
-# )
-
-# assert result == json.dumps(SAMPLE_TIMEFRAMES)
-
-# def test_delete_timeframe(event_service, mock_mongo_module):
-# result = event_service.delete_timeframe(timeframe_id=SAMPLE_TIMEFRAMES[0]["_id"])
-# assert result["statusCode"] == 200
-
-
-# # TODO: potentially add mocking for GoogleSheetsModule (otherwise test is not isolated)
-# # def test_create_event(event_service, mock_mongo_module):
-# # CREATE_TIMEFRAME = { "name": "eventName", "tags": "tags", "sheetTab": "selectedSheetTab" }
-# # date_created = datetime.datetime.now()
-# # timeframe_id = "timeframeId"
-
-# # TODO:
-
-# # def get_event(self, event_id: str):
-
-# # def checkin(self, event_id: str, user: dict) -> dict:
-
-# # def delete(self, event_id: str):
-
-# # def get_timeframe_sheets(self, timeframe_id: str):
-
-# # def get_rush_categories_and_events(self):
-
-# # def create_rush_category(self, data: dict):
-
-# # def create_rush_event(self, data: dict):
-
-# # def modify_rush_event(self, data: dict):
-
-# # def modify_rush_settings(self, data: dict):
-
-# # def get_rush_event(self, event_id: str, hide_attendees: bool = True):
-
-# # def checkin_rush(self, event_id: str, user_data: dict):
-
-# # def delete_rush_event(self, event_id: str):
diff --git a/tests/services/test_events_member_service.py b/tests/services/test_events_member_service.py
new file mode 100644
index 0000000..6b6c481
--- /dev/null
+++ b/tests/services/test_events_member_service.py
@@ -0,0 +1,101 @@
+import pytest
+from unittest.mock import MagicMock, patch
+from chalicelib.services.EventsMemberService import EventsMemberService
+import datetime
+import json
+
+with open("tests/fixtures/events/general/sample_timeframes.json") as f:
+ SAMPLE_TIMEFRAMES: list = json.load(f)
+
+with open("tests/fixtures/events/general/sample_timeframe_sheets.json") as f:
+ SAMPLE_TIMEFRAME_SHEETS: list = json.load(f)
+
+with open("tests/fixtures/events/general/sample_rush_events.json") as f:
+ SAMPLE_RUSH_EVENTS: list = json.load(f)
+
+with open("tests/fixtures/events/general/sample_rush_event.json") as f:
+ SAMPLE_RUSH_EVENT = json.load(f)
+
+@pytest.fixture
+def mock_mongo_module():
+ with patch("chalicelib.modules.mongo.MongoModule", autospec=True) as MockMongoModule:
+ mock_instance = MockMongoModule(use_mock=True)
+ yield mock_instance
+
+@pytest.fixture
+def event_service(mock_mongo_module):
+ return EventsMemberService(mock_mongo_module)
+
+def test_insert_document(event_service, mock_mongo_module):
+ CREATE_TIMEFRAME = { "name": "testTimeframeName", "spreadsheetId": "testSpreadsheetId" }
+ date_created = datetime.datetime.now()
+
+ with patch("chalicelib.services.EventsMemberService.datetime") as mock_datetime:
+ mock_datetime.datetime.now.return_value = date_created
+ result = event_service.create_timeframe(CREATE_TIMEFRAME)
+ CREATE_TIMEFRAME["date_created"] = date_created
+ mock_mongo_module.insert_document.assert_called_once_with(
+ collection="events-timeframe", data=CREATE_TIMEFRAME
+ )
+
+ assert result == {"msg": True}
+
+def test_get_timeframe(event_service, mock_mongo_module):
+ mock_mongo_module.get_document_by_id.return_value = SAMPLE_TIMEFRAMES[0]
+ timeframe_id = SAMPLE_TIMEFRAMES[0]["_id"]
+
+ result = event_service.get_timeframe(timeframe_id=timeframe_id)
+ mock_mongo_module.get_document_by_id.assert_called_once_with(
+ collection="events-timeframe", document_id=timeframe_id
+ )
+
+ assert result == json.dumps(SAMPLE_TIMEFRAMES[0])
+
+def test_get_all_timeframe(event_service, mock_mongo_module):
+ mock_mongo_module.get_all_data_from_collection.return_value = SAMPLE_TIMEFRAMES
+
+ result = event_service.get_all_timeframes()
+ mock_mongo_module.get_all_data_from_collection.assert_called_once_with(
+ collection="events-timeframe"
+ )
+
+ assert result == json.dumps(SAMPLE_TIMEFRAMES)
+
+def test_delete_timeframe(event_service, mock_mongo_module):
+ result = event_service.delete_timeframe(timeframe_id=SAMPLE_TIMEFRAMES[0]["_id"])
+ assert result["statusCode"] == 200
+
+
+# TODO: potentially add mocking for GoogleSheetsModule (otherwise test is not isolated)
+# def test_create_event(event_service, mock_mongo_module):
+# CREATE_TIMEFRAME = { "name": "eventName", "tags": "tags", "sheetTab": "selectedSheetTab" }
+# date_created = datetime.datetime.now()
+# timeframe_id = "timeframeId"
+
+# TODO: test_events_member
+
+# def get_event(self, event_id: str):
+
+# def checkin(self, event_id: str, user: dict) -> dict:
+
+# def delete(self, event_id: str):
+
+# def get_timeframe_sheets(self, timeframe_id: str):
+
+# TODO: test_events_rush
+
+# def get_rush_categories_and_events(self):
+
+# def create_rush_category(self, data: dict):
+
+# def create_rush_event(self, data: dict):
+
+# def modify_rush_event(self, data: dict):
+
+# def modify_rush_settings(self, data: dict):
+
+# def get_rush_event(self, event_id: str, hide_attendees: bool = True):
+
+# def checkin_rush(self, event_id: str, user_data: dict):
+
+# def delete_rush_event(self, event_id: str):
From 04e8c5d05a77f22c1bb9308c8bae281a03602e1e Mon Sep 17 00:00:00 2001
From: William De Rocco <93288641+wderocco8@users.noreply.github.com>
Date: Wed, 24 Jul 2024 21:22:43 -0400
Subject: [PATCH 18/34] added functionality to get all events for rush category
---
chalicelib/api/events_rush.py | 5 +++++
chalicelib/modules/mongo.py | 17 +++++++++++-----
chalicelib/services/EventsMemberService.py | 2 +-
chalicelib/services/EventsRushService.py | 21 ++++++++++++++++++--
chalicelib/services/MemberService.py | 2 +-
tests/services/test_events_member_service.py | 4 ++--
6 files changed, 40 insertions(+), 11 deletions(-)
diff --git a/chalicelib/api/events_rush.py b/chalicelib/api/events_rush.py
index f33cecd..2385503 100644
--- a/chalicelib/api/events_rush.py
+++ b/chalicelib/api/events_rush.py
@@ -49,6 +49,11 @@ def checkin_rush(event_id):
return events_rush_service.checkin_rush(event_id, data)
+@events_rush_api.route("/events/rush/default", methods=["GET"], cors=True)
+def get_rush_events_default_category():
+ return events_rush_service.get_rush_events_default_category()
+
+
@events_rush_api.route("/events/rush/{event_id}", methods=["DELETE"], cors=True)
def delete_rush_event(event_id):
return events_rush_service.delete_rush_event(event_id)
\ No newline at end of file
diff --git a/chalicelib/modules/mongo.py b/chalicelib/modules/mongo.py
index f68b693..1bbc465 100644
--- a/chalicelib/modules/mongo.py
+++ b/chalicelib/modules/mongo.py
@@ -105,14 +105,21 @@ def find_one_document(self, collection: str, query: dict):
raise
@add_env_suffix
- def get_all_data_from_collection(self, collection: str):
- """Fetches all data from the specified collection."""
- if collection is None:
- raise ValueError("The 'collection' parameter cannot be None")
+ def get_data_from_collection(self, collection: str, filter: dict = {}):
+ """
+ Fetches all data from the specified collection.
+
+ Args:
+ collection (str): The name of the collection to update the document in.
+ filter (dict, optional): Dictionary of filters to be applied to collection query.
+
+ Returns:
+ list: List of documents in the collection that match filter.
+ """
try:
# Use the specified collection to fetch data
- cursor = self.mongo_client.vault[collection].find()
+ cursor = self.mongo_client.vault[collection].find(filter)
data = list(cursor) # Convert the cursor to a list
if data is None:
diff --git a/chalicelib/services/EventsMemberService.py b/chalicelib/services/EventsMemberService.py
index 8ddeaec..b2c61cc 100644
--- a/chalicelib/services/EventsMemberService.py
+++ b/chalicelib/services/EventsMemberService.py
@@ -37,7 +37,7 @@ def get_timeframe(self, timeframe_id: str):
def get_all_timeframes(self):
"""Retrieve all timeframes from the database."""
- timeframes = self.mongo_module.get_all_data_from_collection(
+ timeframes = self.mongo_module.get_data_from_collection(
f"{self.collection_prefix}timeframe"
)
diff --git a/chalicelib/services/EventsRushService.py b/chalicelib/services/EventsRushService.py
index 6d3af39..f2513d8 100644
--- a/chalicelib/services/EventsRushService.py
+++ b/chalicelib/services/EventsRushService.py
@@ -21,8 +21,8 @@ def __init__(self, mongo_module=mongo_module):
self.collection_prefix = "events-"
def get_rush_categories_and_events(self):
- rush_categories = self.mongo_module.get_all_data_from_collection(
- f"{self.collection_prefix}rush"
+ rush_categories = self.mongo_module.get_data_from_collection(
+ collection=f"{self.collection_prefix}rush"
)
return json.dumps(rush_categories, cls=self.BSONEncoder)
@@ -238,6 +238,23 @@ def checkin_rush(self, event_id: str, user_data: dict):
)
return
+
+ def get_rush_events_default_category(self):
+ rush_categories = self.mongo_module.get_data_from_collection(
+ collection=f"{self.collection_prefix}rush",
+ filter={"defaultRushCategory": True}
+ )
+
+ if len(rush_categories) == 0:
+ return []
+
+ rush_category = rush_categories[0]
+
+ # remove code from every rush event
+ for event in rush_category["events"]:
+ event.pop("code", None)
+
+ return json.dumps(rush_category, cls=self.BSONEncoder)
def delete_rush_event(self, event_id: str):
"""
diff --git a/chalicelib/services/MemberService.py b/chalicelib/services/MemberService.py
index 92c70e6..245605d 100644
--- a/chalicelib/services/MemberService.py
+++ b/chalicelib/services/MemberService.py
@@ -73,7 +73,7 @@ def delete(self, data: list[str]) -> dict:
def get_all(self):
- data = mongo_module.get_all_data_from_collection(self.collection)
+ data = mongo_module.get_data_from_collection(self.collection)
return json.dumps(data, cls=self.BSONEncoder)
def onboard(self, document_id=str, data=dict) -> bool:
diff --git a/tests/services/test_events_member_service.py b/tests/services/test_events_member_service.py
index 6b6c481..850e8ae 100644
--- a/tests/services/test_events_member_service.py
+++ b/tests/services/test_events_member_service.py
@@ -52,10 +52,10 @@ def test_get_timeframe(event_service, mock_mongo_module):
assert result == json.dumps(SAMPLE_TIMEFRAMES[0])
def test_get_all_timeframe(event_service, mock_mongo_module):
- mock_mongo_module.get_all_data_from_collection.return_value = SAMPLE_TIMEFRAMES
+ mock_mongo_module.get_data_from_collection.return_value = SAMPLE_TIMEFRAMES
result = event_service.get_all_timeframes()
- mock_mongo_module.get_all_data_from_collection.assert_called_once_with(
+ mock_mongo_module.get_data_from_collection.assert_called_once_with(
collection="events-timeframe"
)
From baa1ed8ac7d10d9dab1920110c9748bf2a5217a1 Mon Sep 17 00:00:00 2001
From: William De Rocco <93288641+wderocco8@users.noreply.github.com>
Date: Thu, 25 Jul 2024 18:18:24 -0400
Subject: [PATCH 19/34] re-embedded applicants and fixed checkin bug
---
chalicelib/services/EventsRushService.py | 70 ++++++++++++------------
1 file changed, 36 insertions(+), 34 deletions(-)
diff --git a/chalicelib/services/EventsRushService.py b/chalicelib/services/EventsRushService.py
index f2513d8..143711a 100644
--- a/chalicelib/services/EventsRushService.py
+++ b/chalicelib/services/EventsRushService.py
@@ -38,6 +38,8 @@ def create_rush_event(self, data: dict):
data["dateCreated"] = datetime.datetime.now()
data["lastModified"] = data["dateCreated"]
data["_id"] = event_id
+ data["attendees"] = []
+ data["numAttendees"] = 0
# upload eventCoverImage to s3 bucket (convert everything to png files for now... can adjust later)
image_path = f"image/rush/{data['categoryId']}/{event_id}.png"
@@ -46,20 +48,6 @@ def create_rush_event(self, data: dict):
# add image_url to data object (this also replaces the original base64 image url)
data["eventCoverImage"] = image_url
- # initialize attendee info (for rush-event)
- attendees_id = ObjectId()
- attendees = {
- "_id": attendees_id,
- "eventId": event_id,
- "attendees": [],
- "numAttendees": 0
- }
- self.mongo_module.insert_document(
- collection=f"{self.collection_prefix}rush-event-attendees",
- data=attendees
- )
- data["attendeesId"] = attendees_id
-
# Add event to its own collection
self.mongo_module.insert_document(
f"{self.collection_prefix}rush-event", data
@@ -207,34 +195,55 @@ def get_rush_event(self, event_id: str, hide_attendees: bool = True):
return json.dumps(event, cls=self.BSONEncoder)
def checkin_rush(self, event_id: str, user_data: dict):
+ event_oid = ObjectId(event_id)
+
event = self.mongo_module.get_document_by_id(
f"{self.collection_prefix}rush-event", event_id
)
-
- attendees_oid = event["attendeesId"]
-
- attendees = self.mongo_module.get_document_by_id(
- f"{self.collection_prefix}rush-event-attendees", attendees_oid
- )
code = user_data["code"]
user_data.pop("code")
+
+ # Parse the timestamp string to a datetime object
+ deadline = datetime.datetime.strptime(event["deadline"], "%Y-%m-%dT%H:%M:%S.%fZ")
+
+ if datetime.datetime.now() > deadline:
+ raise UnauthorizedError("Event deadline has passed.")
if code != event["code"]:
raise UnauthorizedError("Invalid code.")
- if any(d["email"] == user_data["email"] for d in attendees["attendees"]):
+ if any(d["email"] == user_data["email"] for d in event["attendees"]):
raise BadRequestError("User has already checked in.")
user_data["checkinTime"] = datetime.datetime.now()
- attendees["attendees"].append(user_data)
- attendees["numAttendees"] += 1
+ event["attendees"].append(user_data)
+ event["numAttendees"] += 1
+
+ # STEP 1: update events-rush-event collection
+ mongo_module.update_document(
+ f"{self.collection_prefix}rush-event",
+ event_id,
+ {"$set": event},
+ )
+
+ # Define array update query and filters
+ update_query = {
+ "$set": {
+ "events.$[eventElem]": event
+ }
+ }
+
+ array_filters = [
+ {"eventElem._id": event_oid}
+ ]
- # Update attendees collection with data
+ # STEP 2: Modify the event in its category (rush collection)
self.mongo_module.update_document(
- collection=f"{self.collection_prefix}rush-event-attendees",
- document_id=attendees_oid,
- query={"$set": attendees},
+ collection=f"{self.collection_prefix}rush",
+ document_id=event["categoryId"],
+ query=update_query,
+ array_filters=array_filters
)
return
@@ -282,7 +291,6 @@ def delete_rush_event(self, event_id: str):
if not event:
raise Exception("Event does not exist.")
- attendees_oid = event["attendeesId"]
event_category_id = event["categoryId"]
# Get eventCoverImage path
@@ -307,12 +315,6 @@ def delete_rush_event(self, event_id: str):
document_id=event_oid
)
- # Delete attendees data from rush-event-attendees collection
- self.mongo_module.delete_document_by_id(
- collection=f"{self.collection_prefix}rush-event-attendees",
- document_id=attendees_oid
- )
-
return
except Exception as e:
From 1b8e1c8475b3631d74cbf4a6e798edfc57a94e12 Mon Sep 17 00:00:00 2001
From: William De Rocco <93288641+wderocco8@users.noreply.github.com>
Date: Thu, 25 Jul 2024 18:31:17 -0400
Subject: [PATCH 20/34] added recent-old sorting on rush events
---
chalicelib/services/EventsRushService.py | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/chalicelib/services/EventsRushService.py b/chalicelib/services/EventsRushService.py
index 143711a..4ca83aa 100644
--- a/chalicelib/services/EventsRushService.py
+++ b/chalicelib/services/EventsRushService.py
@@ -262,6 +262,16 @@ def get_rush_events_default_category(self):
# remove code from every rush event
for event in rush_category["events"]:
event.pop("code", None)
+
+ # Sort events by the date field
+ try:
+ rush_category["events"].sort(
+ key=lambda e: datetime.datetime.strptime(e["date"], "%Y-%m-%dT%H:%M:%S.%fZ"),
+ reverse=True
+ )
+ except ValueError as ve:
+ # Handle the case where the date format might be different or invalid
+ print(f"Date format error: {ve}")
return json.dumps(rush_category, cls=self.BSONEncoder)
From af5e1c8652130ab7a74adae21abcc9ba2ff82306 Mon Sep 17 00:00:00 2001
From: William De Rocco <93288641+wderocco8@users.noreply.github.com>
Date: Thu, 25 Jul 2024 18:50:38 -0400
Subject: [PATCH 21/34] added code to check if user has checkedIn to an event
---
chalicelib/api/events_rush.py | 6 +++---
chalicelib/services/EventsRushService.py | 6 +++++-
2 files changed, 8 insertions(+), 4 deletions(-)
diff --git a/chalicelib/api/events_rush.py b/chalicelib/api/events_rush.py
index 2385503..37a8ce5 100644
--- a/chalicelib/api/events_rush.py
+++ b/chalicelib/api/events_rush.py
@@ -49,9 +49,9 @@ def checkin_rush(event_id):
return events_rush_service.checkin_rush(event_id, data)
-@events_rush_api.route("/events/rush/default", methods=["GET"], cors=True)
-def get_rush_events_default_category():
- return events_rush_service.get_rush_events_default_category()
+@events_rush_api.route("/events/rush/default/{email}", methods=["GET"], cors=True)
+def get_rush_events_default_category(email):
+ return events_rush_service.get_rush_events_default_category(email)
@events_rush_api.route("/events/rush/{event_id}", methods=["DELETE"], cors=True)
diff --git a/chalicelib/services/EventsRushService.py b/chalicelib/services/EventsRushService.py
index 4ca83aa..2a708df 100644
--- a/chalicelib/services/EventsRushService.py
+++ b/chalicelib/services/EventsRushService.py
@@ -248,7 +248,7 @@ def checkin_rush(self, event_id: str, user_data: dict):
return
- def get_rush_events_default_category(self):
+ def get_rush_events_default_category(self, email: str):
rush_categories = self.mongo_module.get_data_from_collection(
collection=f"{self.collection_prefix}rush",
filter={"defaultRushCategory": True}
@@ -263,6 +263,10 @@ def get_rush_events_default_category(self):
for event in rush_category["events"]:
event.pop("code", None)
+ # check if user attended event (boolean)
+ checkedIn = any(attendee["email"] == email for attendee in event["attendees"])
+ event["checkedIn"] = checkedIn
+
# Sort events by the date field
try:
rush_category["events"].sort(
From ee59136fbf255fcc7fa0d593b3f6109d35a0ddff Mon Sep 17 00:00:00 2001
From: William De Rocco <93288641+wderocco8@users.noreply.github.com>
Date: Thu, 25 Jul 2024 20:56:47 -0400
Subject: [PATCH 22/34] switched GET to POST to send email to backend
---
chalicelib/api/events_rush.py | 7 ++++---
chalicelib/services/EventsRushService.py | 4 ++--
2 files changed, 6 insertions(+), 5 deletions(-)
diff --git a/chalicelib/api/events_rush.py b/chalicelib/api/events_rush.py
index 37a8ce5..d15c645 100644
--- a/chalicelib/api/events_rush.py
+++ b/chalicelib/api/events_rush.py
@@ -49,9 +49,10 @@ def checkin_rush(event_id):
return events_rush_service.checkin_rush(event_id, data)
-@events_rush_api.route("/events/rush/default/{email}", methods=["GET"], cors=True)
-def get_rush_events_default_category(email):
- return events_rush_service.get_rush_events_default_category(email)
+@events_rush_api.route("/events/rush/default", methods=["POST"], cors=True)
+def get_rush_events_default_category():
+ data = events_rush_api.current_request.json_body
+ return events_rush_service.get_rush_events_default_category(data)
@events_rush_api.route("/events/rush/{event_id}", methods=["DELETE"], cors=True)
diff --git a/chalicelib/services/EventsRushService.py b/chalicelib/services/EventsRushService.py
index 2a708df..71933de 100644
--- a/chalicelib/services/EventsRushService.py
+++ b/chalicelib/services/EventsRushService.py
@@ -248,7 +248,7 @@ def checkin_rush(self, event_id: str, user_data: dict):
return
- def get_rush_events_default_category(self, email: str):
+ def get_rush_events_default_category(self, data: dict):
rush_categories = self.mongo_module.get_data_from_collection(
collection=f"{self.collection_prefix}rush",
filter={"defaultRushCategory": True}
@@ -264,7 +264,7 @@ def get_rush_events_default_category(self, email: str):
event.pop("code", None)
# check if user attended event (boolean)
- checkedIn = any(attendee["email"] == email for attendee in event["attendees"])
+ checkedIn = any(attendee["email"] == data["email"] for attendee in event["attendees"])
event["checkedIn"] = checkedIn
# Sort events by the date field
From a7d64163e64c3640b8673a44e131491c85b74263 Mon Sep 17 00:00:00 2001
From: Jin Young Bang
Date: Sat, 27 Jul 2024 15:10:06 -0400
Subject: [PATCH 23/34] feat: implement get member API for user update
---
chalicelib/api/members.py | 13 +++++++++++--
chalicelib/services/MemberService.py | 7 +++++--
2 files changed, 16 insertions(+), 4 deletions(-)
diff --git a/chalicelib/api/members.py b/chalicelib/api/members.py
index f8b19c7..3254fb9 100644
--- a/chalicelib/api/members.py
+++ b/chalicelib/api/members.py
@@ -6,6 +6,13 @@
members_api = Blueprint(__name__)
+@members_api.route("/member/{user_id}", methods=["GET"], cors=True)
+@auth(members_api, roles=["admin", "member"])
+def get_member(user_id):
+ member = member_service.get_by_id(user_id)
+ return member if member else {}
+
+
@members_api.route("/members", methods=["GET"], cors=True)
@auth(members_api, roles=["admin", "member"])
def get_all_members():
@@ -25,7 +32,7 @@ def onboard_member(user_id):
"message": "User updated successfully.",
}
else:
- { "status": False}
+ {"status": False}
@members_api.route("/members", methods=["POST"], cors=True)
@@ -34,14 +41,16 @@ def create_member():
data = members_api.current_request.json_body
return member_service.create(data)
+
@members_api.route("/members", methods=["DELETE"], cors=True)
@auth(members_api, roles=[Roles.ADMIN])
def delete_members():
data = members_api.current_request.json_body
return member_service.delete(data)
+
@members_api.route("/members/{user_id}/roles", methods=["PATCH"], cors=True)
@auth(members_api, roles=[Roles.ADMIN])
def update_member_roles(user_id):
data = members_api.current_request.json_body
- return member_service.update_roles(user_id, data["roles"])
\ No newline at end of file
+ return member_service.update_roles(user_id, data["roles"])
diff --git a/chalicelib/services/MemberService.py b/chalicelib/services/MemberService.py
index 245605d..52a012f 100644
--- a/chalicelib/services/MemberService.py
+++ b/chalicelib/services/MemberService.py
@@ -40,7 +40,7 @@ def create(self, data):
"success": True,
"message": "User created successfully",
}
-
+
def delete(self, data: list[str]) -> dict:
"""
Deletes user documents based on the provided IDs.
@@ -70,7 +70,10 @@ def delete(self, data: list[str]) -> dict:
"success": True,
"message": "Documents deleted successfully",
}
-
+
+ def get_by_id(self, user_id: str):
+ data = mongo_module.get_document_by_id(self.collection, user_id)
+ return json.dumps(data, cls=self.BSONEncoder)
def get_all(self):
data = mongo_module.get_data_from_collection(self.collection)
From 0c02bc27bf388b21fd1bfab300223bc435539115 Mon Sep 17 00:00:00 2001
From: William De Rocco <93288641+wderocco8@users.noreply.github.com>
Date: Sun, 28 Jul 2024 13:20:33 -0400
Subject: [PATCH 24/34] added function to return rush event analytics
---
chalicelib/api/events_rush.py | 8 +++++++-
chalicelib/services/EventsRushService.py | 23 +++++++++++++++++++++++
2 files changed, 30 insertions(+), 1 deletion(-)
diff --git a/chalicelib/api/events_rush.py b/chalicelib/api/events_rush.py
index d15c645..8159289 100644
--- a/chalicelib/api/events_rush.py
+++ b/chalicelib/api/events_rush.py
@@ -57,4 +57,10 @@ def get_rush_events_default_category():
@events_rush_api.route("/events/rush/{event_id}", methods=["DELETE"], cors=True)
def delete_rush_event(event_id):
- return events_rush_service.delete_rush_event(event_id)
\ No newline at end of file
+ return events_rush_service.delete_rush_event(event_id)
+
+
+@events_rush_api.route("/events/rush/{category_id}/analytics", methods=["GET"], cors=True)
+@auth(events_rush_api, roles=["admin"])
+def get_rush_category_analytics(category_id):
+ return events_rush_service.get_rush_category_analytics(category_id=category_id)
\ No newline at end of file
diff --git a/chalicelib/services/EventsRushService.py b/chalicelib/services/EventsRushService.py
index 71933de..a934afd 100644
--- a/chalicelib/services/EventsRushService.py
+++ b/chalicelib/services/EventsRushService.py
@@ -334,4 +334,27 @@ def delete_rush_event(self, event_id: str):
except Exception as e:
raise BadRequestError(e)
+ def get_rush_category_analytics(self, category_id: str):
+ category = self.mongo_module.get_document_by_id(
+ f"{self.collection_prefix}rush", category_id
+ )
+
+ # attendees : dict of all users (user: { name, email, eventsAttended: list of objects })
+ attendees = {}
+
+ for event in category["events"]:
+ for attendee in event["attendees"]:
+ email =attendee["email"]
+ new_event = {
+ "eventId": event["_id"],
+ "eventName": event["name"]
+ }
+ if email in attendees:
+ attendees[email]["eventsAttended"].append(new_event)
+ else:
+ attendees[email] = { **attendee, "eventsAttended": [new_event] }
+
+
+ return json.dumps(attendees, cls=self.BSONEncoder)
+
events_rush_service = EventsRushService()
\ No newline at end of file
From fe45cfd746f09343d6112ffa1bd252d2112f28a9 Mon Sep 17 00:00:00 2001
From: Jin Young Bang
Date: Sun, 28 Jul 2024 13:34:54 -0400
Subject: [PATCH 25/34] feat: implement update member API
---
chalicelib/api/members.py | 9 ++++++++
chalicelib/services/MemberService.py | 33 ++++++++++++++++++++++++++--
2 files changed, 40 insertions(+), 2 deletions(-)
diff --git a/chalicelib/api/members.py b/chalicelib/api/members.py
index 3254fb9..e1267ad 100644
--- a/chalicelib/api/members.py
+++ b/chalicelib/api/members.py
@@ -13,6 +13,15 @@ def get_member(user_id):
return member if member else {}
+@members_api.route("/member/{user_id}", methods=["PUT"], cors=True)
+@auth(members_api, roles=[Roles.MEMBER, Roles.ADMIN])
+def update_member(user_id):
+ data = members_api.current_request.json_body
+ return member_service.update(
+ user_id=user_id, data=data, headers=members_api.current_request.headers
+ )
+
+
@members_api.route("/members", methods=["GET"], cors=True)
@auth(members_api, roles=["admin", "member"])
def get_all_members():
diff --git a/chalicelib/services/MemberService.py b/chalicelib/services/MemberService.py
index 52a012f..c5f8943 100644
--- a/chalicelib/services/MemberService.py
+++ b/chalicelib/services/MemberService.py
@@ -1,8 +1,10 @@
from chalicelib.modules.mongo import mongo_module
-from chalice import ConflictError, NotFoundError
+from chalice import ConflictError, NotFoundError, UnauthorizedError
-import json
from bson import ObjectId
+import json
+import jwt
+import boto3
class MemberService:
@@ -82,6 +84,33 @@ def get_all(self):
def onboard(self, document_id=str, data=dict) -> bool:
return mongo_module.update_document_by_id(self.collection, document_id, data)
+ def update(self, user_id: str, data: dict, headers: dict) -> bool:
+ ssm_client = boto3.client("ssm")
+ auth_header = headers.get("Authorization", None)
+
+ if not auth_header:
+ raise UnauthorizedError("Authorization header is missing.")
+
+ _, token = auth_header.split(" ", 1) if " " in auth_header else (None, None)
+
+ if not token:
+ raise UnauthorizedError("Token is missing.")
+
+ auth_secret = ssm_client.get_parameter(
+ Name="/Zap/AUTH_SECRET", WithDecryption=True
+ )["Parameter"]["Value"]
+ decoded = jwt.decode(token, auth_secret, algorithms=["HS256"])
+
+ if user_id != decoded["_id"]:
+ raise UnauthorizedError(
+ "User {user_id} is not authorized to update this user."
+ )
+
+ # NOTE: Performing an update on the path '_id' would modify the immutable field '_id'
+ data.pop("_id", None)
+
+ return mongo_module.update_document_by_id(self.collection, user_id, data)
+
def update_roles(self, document_id=str, roles=list) -> bool:
return mongo_module.update_document(
self.collection,
From 70dea1a8c997099920c7bc5dcf70f6c8c625c1c6 Mon Sep 17 00:00:00 2001
From: Jin Young Bang
Date: Sun, 28 Jul 2024 14:14:13 -0400
Subject: [PATCH 26/34] test: create unit tests for member API
---
chalicelib/api/members.py | 3 +-
tests/api/test_members.py | 256 ++++++++++++++++++++++++++++++++++++++
tests/test_decorators.py | 5 +-
tests/test_dynamodb.py | 4 +-
4 files changed, 262 insertions(+), 6 deletions(-)
create mode 100644 tests/api/test_members.py
diff --git a/chalicelib/api/members.py b/chalicelib/api/members.py
index e1267ad..57b3f32 100644
--- a/chalicelib/api/members.py
+++ b/chalicelib/api/members.py
@@ -33,6 +33,7 @@ def get_all_members():
@auth(members_api, roles=[])
def onboard_member(user_id):
data = members_api.current_request.json_body
+ # TODO: If isNewUser is False, reject onboarding
data["isNewUser"] = False
if member_service.onboard(user_id, data):
@@ -41,7 +42,7 @@ def onboard_member(user_id):
"message": "User updated successfully.",
}
else:
- {"status": False}
+ return {"status": False}
@members_api.route("/members", methods=["POST"], cors=True)
diff --git a/tests/api/test_members.py b/tests/api/test_members.py
new file mode 100644
index 0000000..6e264bb
--- /dev/null
+++ b/tests/api/test_members.py
@@ -0,0 +1,256 @@
+from chalice.test import Client
+from unittest.mock import patch
+from chalice.config import Config
+from chalice.local import LocalGateway
+
+from app import app
+import json
+
+
+lg = LocalGateway(app, Config())
+
+
+TEST_MEMBER_DATA = [
+ {
+ "_id": "12a34bc678df27ead9388708",
+ "name": "Name Name",
+ "email": "whyphi@bu.edu",
+ "class": "Lambda",
+ "college": "CAS",
+ "family": "Poseidon",
+ "graduationYear": "2026",
+ "isEboard": "no",
+ "major": "Computer Science",
+ "minor": "",
+ "isNewUser": False,
+ "team": "technology",
+ "roles": ["admin", "eboard", "member"],
+ "big": "Name Name",
+ },
+ {
+ "_id": "12a34bc678df27ead9388709",
+ "name": "Name Name",
+ "email": "whyphi1@bu.edu",
+ "class": "Lambda",
+ "college": "QST",
+ "family": "Atlas",
+ "graduationYear": "2027",
+ "isEboard": "no",
+ "major": "Business Administration",
+ "minor": "",
+ "isNewUser": True,
+ "team": "operations",
+ "roles": ["member"],
+ "big": "Name Name",
+ },
+]
+
+
+def test_get_member():
+ # Create a Chalice test client
+ with Client(app) as client:
+ with patch("chalicelib.decorators.jwt.decode") as mock_decode:
+ mock_decode.return_value = {"role": "member"}
+
+ with patch(
+ "chalicelib.services.MemberService.member_service.get_by_id"
+ ) as mock_get:
+ mock_get.return_value = TEST_MEMBER_DATA[0]
+ response = client.http.get(
+ f"/member/{TEST_MEMBER_DATA[0]['_id']}",
+ headers={"Authorization": "Bearer SAMPLE_TOKEN_STRING"},
+ )
+ # Check the response status code and body
+ assert response.status_code == 200
+ assert response.json_body == TEST_MEMBER_DATA[0]
+
+
+def test_get_member_non_existent():
+ # Create a Chalice test client
+ with Client(app) as client:
+ with patch("chalicelib.decorators.jwt.decode") as mock_decode:
+ mock_decode.return_value = {"role": "member"}
+
+ with patch(
+ "chalicelib.services.MemberService.member_service.get_by_id"
+ ) as mock_get:
+ mock_get.return_value = {}
+ response = client.http.get(
+ "/member/123",
+ headers={"Authorization": "Bearer SAMPLE_TOKEN_STRING"},
+ )
+ # Check the response status code and body
+ assert response.status_code == 200
+ assert response.json_body == {}
+
+
+def test_update_member():
+ with Client(app) as client:
+ with patch("chalicelib.decorators.jwt.decode") as mock_decode:
+ mock_decode.return_value = {"role": "member"}
+
+ with patch(
+ "chalicelib.services.MemberService.member_service.update"
+ ) as mock_update:
+ # Make copy of TEST_MEMBER_DATA[0]
+ update_member_data = TEST_MEMBER_DATA[0].copy()
+ update_member_data["name"] = "New Name"
+ mock_update.return_value = update_member_data
+
+ response = client.http.put(
+ f"/member/{TEST_MEMBER_DATA[0]['_id']}",
+ body=update_member_data,
+ headers={"Authorization": "Bearer SAMPLE_TOKEN_STRING"},
+ )
+ # Check the response status code and body
+ assert response.status_code == 200
+ assert response.json_body == update_member_data
+
+
+def test_get_all_members():
+ # Create a Chalice test client
+ with Client(app) as client:
+ with patch("chalicelib.decorators.jwt.decode") as mock_decode:
+ mock_decode.return_value = {"role": "member"}
+
+ with patch(
+ "chalicelib.services.MemberService.member_service.get_all"
+ ) as mock_get_all:
+ mock_get_all.return_value = TEST_MEMBER_DATA
+ response = client.http.get(
+ "/members",
+ headers={"Authorization": "Bearer SAMPLE_TOKEN_STRING"},
+ )
+ # Check the response status code and body
+ assert response.status_code == 200
+ assert response.json_body == TEST_MEMBER_DATA
+
+
+def test_onboard_member():
+ with patch("chalicelib.decorators.jwt.decode") as mock_decode:
+ mock_decode.return_value = {"role": "member"}
+
+ with patch(
+ "chalicelib.services.MemberService.member_service.onboard"
+ ) as mock_onboard:
+ mock_onboard.return_value = True
+
+ # Utilize local gateway for passing in body
+ response = lg.handle_request(
+ method="POST",
+ path=f"/members/onboard/{TEST_MEMBER_DATA[1]['_id']}",
+ headers={
+ "Content-Type": "application/json",
+ "Authorization": "Bearer SAMPLE_TOKEN_STRING",
+ },
+ body=json.dumps(TEST_MEMBER_DATA[1]),
+ )
+
+ # Check the response status code and body
+ assert response["statusCode"] == 200
+ assert json.loads(response["body"]) == {
+ "status": True,
+ "message": "User updated successfully.",
+ }
+
+
+def test_onboard_member_fail_on_mongo():
+ with patch("chalicelib.decorators.jwt.decode") as mock_decode:
+ mock_decode.return_value = {"role": "member"}
+
+ with patch(
+ "chalicelib.services.MemberService.member_service.onboard"
+ ) as mock_onboard:
+ mock_onboard.return_value = False
+ response = lg.handle_request(
+ method="POST",
+ path=f"/members/onboard/{TEST_MEMBER_DATA[1]['_id']}",
+ headers={
+ "Content-Type": "application/json",
+ "Authorization": "Bearer SAMPLE_TOKEN_STRING",
+ },
+ body=json.dumps(TEST_MEMBER_DATA[1]),
+ )
+ print(response)
+ # Check the response status code and body
+ assert response["statusCode"] == 200
+ assert json.loads(response["body"]) == {
+ "status": False,
+ }
+
+
+def test_create_member():
+ with Client(app) as client:
+ with patch("chalicelib.decorators.jwt.decode") as mock_decode:
+ mock_decode.return_value = {"role": "admin"}
+
+ with patch(
+ "chalicelib.services.MemberService.member_service.create"
+ ) as mock_create:
+ mock_create.return_value = {
+ "success": True,
+ "message": "User created successfully",
+ }
+ response = client.http.post(
+ "/members",
+ body=json.dumps(TEST_MEMBER_DATA[0]),
+ headers={"Authorization": "Bearer SAMPLE_TOKEN_STRING"},
+ )
+ # Check the response status code and body
+ assert response.status_code == 200
+ assert response.json_body == {
+ "success": True,
+ "message": "User created successfully",
+ }
+
+
+def test_delete_members():
+ with Client(app) as client:
+ with patch("chalicelib.decorators.jwt.decode") as mock_decode:
+ mock_decode.return_value = {"role": "admin"}
+
+ with patch(
+ "chalicelib.services.MemberService.member_service.delete"
+ ) as mock_delete:
+ mock_delete.return_value = {
+ "success": True,
+ "message": "Documents deleted successfully",
+ }
+ response = client.http.delete(
+ "/members",
+ body=json.dumps(TEST_MEMBER_DATA),
+ headers={"Authorization": "Bearer SAMPLE_TOKEN_STRING"},
+ )
+ # Check the response status code and body
+ assert response.status_code == 200
+ assert response.json_body == {
+ "success": True,
+ "message": "Documents deleted successfully",
+ }
+
+
+def test_update_member_roles():
+ with patch("chalicelib.decorators.jwt.decode") as mock_decode:
+ mock_decode.return_value = {"role": "admin"}
+
+ with patch(
+ "chalicelib.services.MemberService.member_service.update_roles"
+ ) as mock_update:
+ update_member_data = TEST_MEMBER_DATA[0].copy()
+ update_member_data["roles"] = ["admin"]
+ mock_update.return_value = update_member_data
+
+ response = lg.handle_request(
+ method="PATCH",
+ path=f"/members/{TEST_MEMBER_DATA[0]['_id']}/roles",
+ headers={
+ "Content-Type": "application/json",
+ "Authorization": "Bearer SAMPLE_TOKEN_STRING",
+ },
+ body=json.dumps(update_member_data),
+ )
+
+ print(response)
+ # Check the response status code and body
+ assert response["statusCode"] == 200
+ assert json.loads(response["body"]) == update_member_data
diff --git a/tests/test_decorators.py b/tests/test_decorators.py
index 5b352c5..c52f0c5 100644
--- a/tests/test_decorators.py
+++ b/tests/test_decorators.py
@@ -1,6 +1,6 @@
-from unittest.mock import patch
from chalicelib.decorators import add_env_suffix
+
def test_add_env_suffix_dev():
def mocked_function(self, table_name: str, *args, **kwargs):
return table_name
@@ -17,6 +17,7 @@ def mocked_function(self, table_name: str, *args, **kwargs):
# Check if the suffix is added correctly
assert result == "test-table-dev"
+
def test_add_env_suffix_prod():
def mocked_function(self, table_name: str, *args, **kwargs):
return table_name
@@ -31,4 +32,4 @@ def mocked_function(self, table_name: str, *args, **kwargs):
result = decorated_function(instance_mock, "test-table", env=True)
# Check if the suffix is added correctly
- assert result == "test-table-prod"
\ No newline at end of file
+ assert result == "test-table-prod"
diff --git a/tests/test_dynamodb.py b/tests/test_dynamodb.py
index da22c36..b77a3c1 100644
--- a/tests/test_dynamodb.py
+++ b/tests/test_dynamodb.py
@@ -101,10 +101,8 @@ def test_delete_item(db):
db.put_data("test-table", SAMPLE_DATA[0])
response = db.delete_item("test-table", {"id": 123})
- assert response == True
+ assert response
# TODO: Test case should fail, but isn't
# response = db.delete_item("test-table", {"id": 124})
# assert response == False
-
-
From 255f48194b7216c1a2dbaa19d53e10377187537c Mon Sep 17 00:00:00 2001
From: Jin Young Bang
Date: Sun, 28 Jul 2024 14:44:42 -0400
Subject: [PATCH 27/34] lint: fix lint warnings
---
chalicelib/api/applicants.py | 2 -
chalicelib/api/insights.py | 2 -
chalicelib/api/listings.py | 1 -
chalicelib/s3.py | 33 +++++----
chalicelib/services/AccountabilityService.py | 1 -
chalicelib/services/EventsMemberService.py | 9 ++-
chalicelib/services/InsightsService.py | 74 ++++++++++++--------
chalicelib/services/ListingService.py | 4 +-
chalicelib/utils.py | 10 +--
tests/api/test_applicants.py | 6 +-
tests/api/test_events_members.py | 25 ++++---
tests/api/test_events_rush.py | 26 ++++---
tests/api/test_insights.py | 2 +-
tests/api/test_listings.py | 10 +--
tests/services/test_applicant_service.py | 12 ++--
tests/services/test_events_member_service.py | 21 ++++--
tests/services/test_insights_service.py | 31 ++++----
tests/services/test_listing_service.py | 19 ++---
tests/test_decorators.py | 5 +-
tests/test_dynamodb.py | 4 +-
20 files changed, 165 insertions(+), 132 deletions(-)
diff --git a/chalicelib/api/applicants.py b/chalicelib/api/applicants.py
index 7542162..18b3926 100644
--- a/chalicelib/api/applicants.py
+++ b/chalicelib/api/applicants.py
@@ -1,10 +1,8 @@
from chalice import Blueprint
from chalicelib.services.ApplicantService import applicant_service
-from chalicelib.handlers.error_handler import handle_exceptions
from chalicelib.decorators import auth
from chalicelib.models.roles import Roles
-from pydantic import ValidationError
applicants_api = Blueprint(__name__)
diff --git a/chalicelib/api/insights.py b/chalicelib/api/insights.py
index 17d3f0f..5cb376c 100644
--- a/chalicelib/api/insights.py
+++ b/chalicelib/api/insights.py
@@ -1,11 +1,9 @@
# TO BE COMPLETED: create api routes for analytics
from chalice import Blueprint
from chalicelib.services.InsightsService import insights_service
-from chalicelib.handlers.error_handler import handle_exceptions
from chalicelib.decorators import auth
from chalicelib.models.roles import Roles
-from pydantic import ValidationError
insights_api = Blueprint(__name__)
diff --git a/chalicelib/api/listings.py b/chalicelib/api/listings.py
index 7cd9c9c..f9d58f6 100644
--- a/chalicelib/api/listings.py
+++ b/chalicelib/api/listings.py
@@ -4,7 +4,6 @@
from chalicelib.decorators import auth
from chalicelib.models.roles import Roles
-from pydantic import ValidationError
listings_api = Blueprint(__name__)
diff --git a/chalicelib/s3.py b/chalicelib/s3.py
index 239e8f8..49fd510 100644
--- a/chalicelib/s3.py
+++ b/chalicelib/s3.py
@@ -1,11 +1,8 @@
import boto3
import os
-import json
-from datetime import datetime
from chalicelib.utils import decode_base64
-
class S3Client:
def __init__(self):
self.bucket_name = "whyphi-zap"
@@ -19,24 +16,27 @@ def upload_binary_data(self, path: str, data: str) -> str:
path = f"prod/{path}"
else:
path = f"dev/{path}"
-
+
# Split parts of base64 data
- parts = data.split(',')
- metadata, base64_data = parts[0], parts[1]
+ parts = data.split(",")
+ metadata, base64_data = parts[0], parts[1]
# Extract content type from metadata
- content_type = metadata.split(';')[0][5:] # Remove "data:" prefix
+ content_type = metadata.split(";")[0][5:] # Remove "data:" prefix
binary_data = decode_base64(base64_data)
-
+
# Upload binary data as object with content type set
self.s3.put_object(
- Bucket=self.bucket_name, Key=path, Body=binary_data, ContentType=content_type
+ Bucket=self.bucket_name,
+ Key=path,
+ Body=binary_data,
+ ContentType=content_type,
)
# Retrieve endpoint of object
s3_endpoint = f"https://{self.bucket_name}.s3.amazonaws.com/"
object_url = s3_endpoint + path
-
+
return object_url
def delete_binary_data(self, object_id: str) -> str:
@@ -47,19 +47,18 @@ def delete_binary_data(self, object_id: str) -> str:
Returns:
str: A message indicating the result of the deletion operation.
-
+
Documentation: https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/s3/client/delete_object.html
"""
if self.is_prod:
path = f"prod/{object_id}"
else:
path = f"dev/{object_id}"
-
+
# delete binary data given bucket_name and key
- response = self.s3.delete_object(
- Bucket=self.bucket_name, Key=path
- )
-
+ response = self.s3.delete_object(Bucket=self.bucket_name, Key=path)
+
return response
-s3 = S3Client()
\ No newline at end of file
+
+s3 = S3Client()
diff --git a/chalicelib/services/AccountabilityService.py b/chalicelib/services/AccountabilityService.py
index b6f56c6..8478da0 100644
--- a/chalicelib/services/AccountabilityService.py
+++ b/chalicelib/services/AccountabilityService.py
@@ -1,4 +1,3 @@
-from chalice import NotFoundError, BadRequestError
from chalicelib.modules.google_sheets import GoogleSheetsModule
diff --git a/chalicelib/services/EventsMemberService.py b/chalicelib/services/EventsMemberService.py
index b2c61cc..e70d43e 100644
--- a/chalicelib/services/EventsMemberService.py
+++ b/chalicelib/services/EventsMemberService.py
@@ -6,6 +6,7 @@
from chalicelib.modules.google_sheets import GoogleSheetsModule
from chalicelib.modules.ses import ses, SesDestination
+
class EventsMemberService:
class BSONEncoder(json.JSONEncoder):
"""JSON encoder that converts Mongo ObjectIds and datetime.datetime to strings."""
@@ -65,7 +66,7 @@ def delete_timeframe(self, timeframe_id: str):
self.mongo_module.delete_document_by_id(
f"{self.collection_prefix}timeframe", timeframe_id
)
-
+
return {"statusCode": 200}
def create_event(self, timeframe_id: str, event_data: dict):
@@ -121,7 +122,7 @@ def checkin(self, event_id: str, user: dict) -> dict:
dict -- Dictionary containing status and message.
"""
user_id, user_email = user["id"], user["email"]
- member = self.mongo_module.get_document_by_id(f"users", user_id)
+ member = self.mongo_module.get_document_by_id("users", user_id)
if member is None:
raise NotFoundError(f"User with ID {user_id} does not exist.")
@@ -227,7 +228,9 @@ def delete(self, event_id: str):
)
# Delete the event document
- self.mongo_module.delete_document_by_id(f"{self.collection_prefix}event", event_id)
+ self.mongo_module.delete_document_by_id(
+ f"{self.collection_prefix}event", event_id
+ )
def get_timeframe_sheets(self, timeframe_id: str):
timeframe = self.mongo_module.get_document_by_id(
diff --git a/chalicelib/services/InsightsService.py b/chalicelib/services/InsightsService.py
index d5d2466..71219a0 100644
--- a/chalicelib/services/InsightsService.py
+++ b/chalicelib/services/InsightsService.py
@@ -1,12 +1,13 @@
# TO BE COMPLETED: create service to perform analytics (used in API...)
from chalicelib.db import db
+
class InsightsService:
def __init__(self):
pass
def get_insights_from_listing(self, id: str):
- ''' driver function of insights (returns both `dashboard` and `distribution`) '''
+ """driver function of insights (returns both `dashboard` and `distribution`)"""
# fetch applicants from `get_applicants` endpoint in `db.py`
data = db.get_applicants(table_name="zap-applications", listing_id=id)
@@ -43,7 +44,11 @@ def _get_dashboard_insights(data):
applicant["major"] = applicant["major"].title()
applicant["minor"] = applicant["minor"].title()
- gpa, grad_year, major = applicant["gpa"], applicant["gradYear"], applicant["major"]
+ gpa, grad_year, major = (
+ applicant["gpa"],
+ applicant["gradYear"],
+ applicant["major"],
+ )
# attempt conversions (if fail, then skip)
try:
@@ -62,14 +67,14 @@ def _get_dashboard_insights(data):
except ValueError:
print("skipping gradYear: ", grad_year)
pass
-
+
# parse majors (if non-empty)
if major:
if major in majors:
majors[major] += 1
else:
majors[major] = 1
-
+
if count_gpa:
avg_gpa /= count_gpa
else:
@@ -78,33 +83,33 @@ def _get_dashboard_insights(data):
# calculate most common major/gradYear
# Check if majors dictionary is not empty
if majors:
- common_major, count_common_major = max(majors.items(), key=lambda x: x[1])
+ common_major, _ = max(majors.items(), key=lambda x: x[1])
else:
# Handle the case when majors dictionary is empty
- common_major, count_common_major = "N/A", 0
+ common_major, _ = "N/A", 0
# Check if grad_years dictionary is not empty
if grad_years:
- common_grad_year, count_common_grad_year = max(grad_years.items(), key=lambda x: x[1])
+ common_grad_year, _ = max(grad_years.items(), key=lambda x: x[1])
else:
# Handle the case when grad_years dictionary is empty
- common_grad_year, count_common_grad_year = "N/A", 0
-
+ common_grad_year, _ = "N/A", 0
dashboard = {
"applicantCount": num_applicants,
"avgGpa": round(avg_gpa, 1) if avg_gpa != "N/A" else avg_gpa,
"commonMajor": common_major.title(),
# "countCommonMajor": count_common_major, # TO-DO: maybe do something with common major counts
- "commonGradYear": int(common_grad_year) if common_grad_year != 'N/A' else common_grad_year,
+ "commonGradYear": int(common_grad_year)
+ if common_grad_year != "N/A"
+ else common_grad_year,
# "avgResponseLength": 0 # TO-DO: maybe implement parsing for response lengths
}
return dashboard
-
def _get_pie_chart_insights(data):
- ''' helper function for pie charts (should be function, not method within InsightsService) '''
+ """helper function for pie charts (should be function, not method within InsightsService)"""
# initialize return object
# value (list) structure : [ {name: string, value: int, applicants: Applicant[]}, ... , ... ]
@@ -117,12 +122,20 @@ def _get_pie_chart_insights(data):
"linkedin": [],
"website": [],
}
-
+
# list of fields we want to consider
- fields = ["colleges", "gpa", "gradYear", "major", "minor", "linkedin", "website"]
+ fields = [
+ "colleges",
+ "gpa",
+ "gradYear",
+ "major",
+ "minor",
+ "linkedin",
+ "website",
+ ]
def findInsightsObject(metric, metric_val):
- ''' helper to the helper lol -> checks for previously added metric_name '''
+ """helper to the helper lol -> checks for previously added metric_name"""
# check if college exists in `distribution["colleges"]`
found_object = None
@@ -130,26 +143,27 @@ def findInsightsObject(metric, metric_val):
if distribution_object["name"] == metric_val:
found_object = distribution_object
break
-
+
return found_object
for applicant in data:
# iterate over applicant dictionary
for metric, val in applicant.items():
-
# case 1: ignore irrelevant metrics
if metric not in fields:
continue
-
+
# case 2: metric is a url
if metric in ["linkedin", "website"]:
- val = 'N/A' if (not val or val == 'N/A') else 'hasURL'
-
+ val = "N/A" if (not val or val == "N/A") else "hasURL"
+
# case 3: handle other metrics with mepty val (attempt to handle some edge cases) # TO-DO: update Form.tsx in frontend to prevent bad inputs
- elif metric in ['minor', 'gpa'] and (not val or val.lower() in ['na', 'n/a', 'n a', 'n / a']):
+ elif metric in ["minor", "gpa"] and (
+ not val or val.lower() in ["na", "n/a", "n a", "n / a"]
+ ):
# general case
- val = 'N/A'
-
+ val = "N/A"
+
# case 4: colleges -> iterate over colleges object
elif metric == "colleges":
for college, status in val.items():
@@ -159,20 +173,24 @@ def findInsightsObject(metric, metric_val):
# check if college exists in `distribution["colleges"]`
found_college = findInsightsObject(metric, college)
-
+
if found_college:
found_college["value"] += 1
found_college["applicants"] += [applicant]
else:
- newCollege = {"name": college, "value": 1, "applicants": [applicant]}
+ newCollege = {
+ "name": college,
+ "value": 1,
+ "applicants": [applicant],
+ }
distribution[metric] += [newCollege]
# skip to next metric
- continue
-
+ continue
+
# handle remaining fields
found_object = findInsightsObject(metric, val)
-
+
if found_object:
found_object["value"] += 1
found_object["applicants"] += [applicant]
diff --git a/chalicelib/services/ListingService.py b/chalicelib/services/ListingService.py
index fca8378..64d7f79 100644
--- a/chalicelib/services/ListingService.py
+++ b/chalicelib/services/ListingService.py
@@ -122,11 +122,11 @@ def delete(self, id: str):
else:
raise NotFoundError("Listing not found")
- except NotFoundError as e:
+ except NotFoundError:
# app.log.error(f"An error occurred: {str(e)}")
return {"statusCode": 404, "message": "Listing not found"}
- except Exception as e:
+ except Exception:
# app.log.error(f"An error occurred: {str(e)}")
return {"statusCode": 500, "message": "Internal Server Error"}
diff --git a/chalicelib/utils.py b/chalicelib/utils.py
index 9dfab87..5926eef 100644
--- a/chalicelib/utils.py
+++ b/chalicelib/utils.py
@@ -1,5 +1,4 @@
import base64
-import imghdr
def decode_base64(base64_data):
@@ -7,15 +6,16 @@ def decode_base64(base64_data):
binary_data = base64.b64decode(base64_data)
return binary_data
+
def get_file_extension_from_base64(base64_data):
- parts = base64_data.split(',')
+ parts = base64_data.split(",")
if len(parts) != 2:
return None # Invalid data URI format
metadata = parts[0]
# Extract content type from metadata.
- content_type = metadata.split(';')[0][5:] # Remove "data:" prefix
+ content_type = metadata.split(";")[0][5:] # Remove "data:" prefix
# Map content type to file extension.
extension_map = {
@@ -26,6 +26,6 @@ def get_file_extension_from_base64(base64_data):
}
# Use the mapping to get the extension (default to 'dat' if not found).
- extension = extension_map.get(content_type, 'dat')
+ extension = extension_map.get(content_type, "dat")
- return extension
\ No newline at end of file
+ return extension
diff --git a/tests/api/test_applicants.py b/tests/api/test_applicants.py
index 02b22c8..65cd4cc 100644
--- a/tests/api/test_applicants.py
+++ b/tests/api/test_applicants.py
@@ -41,10 +41,9 @@ def test_get_all_applicants():
with patch(
"chalicelib.services.ApplicantService.applicant_service.get_all"
) as mock_get_all:
-
mock_get_all.return_value = TEST_APPLICANTS
response = client.http.get(
- f"/applicants",
+ "/applicants",
headers={"Authorization": "Bearer SAMPLE_TOKEN_STRING"},
)
@@ -56,7 +55,6 @@ def test_get_all_applicants():
def test_get_all_applicants_from_listing():
# Create a Chalice test client
with Client(app) as client:
-
with patch("chalicelib.decorators.jwt.decode") as mock_decode:
# Assuming the decoded token has the required role
mock_decode.return_value = {"role": "admin"}
@@ -65,7 +63,7 @@ def test_get_all_applicants_from_listing():
) as mock_get_all_from_listing:
mock_get_all_from_listing.return_value = TEST_APPLICANTS
response = client.http.get(
- f"/applicants/test_listing_id",
+ "/applicants/test_listing_id",
headers={"Authorization": "Bearer SAMPLE_TOKEN_STRING"},
)
diff --git a/tests/api/test_events_members.py b/tests/api/test_events_members.py
index a16ec19..90ac3a2 100644
--- a/tests/api/test_events_members.py
+++ b/tests/api/test_events_members.py
@@ -10,6 +10,7 @@
with open("tests/fixtures/events/general/sample_timeframe_sheets.json") as f:
SAMPLE_TIMEFRAME_SHEETS = json.load(f)
+
def test_create_timeframe():
with Client(app) as client:
with patch("chalicelib.decorators.jwt.decode") as mock_decode:
@@ -20,7 +21,7 @@ def test_create_timeframe():
) as mock_create_timeframe:
mock_create_timeframe.return_value = {"msg": True}
response = client.http.post(
- f"/timeframes",
+ "/timeframes",
headers={"Authorization": "Bearer SAMPLE_TOKEN_STRING"},
)
@@ -40,7 +41,7 @@ def test_get_all_timeframes():
) as mock_get_all_timeframes:
mock_get_all_timeframes.return_value = SAMPLE_TIMEFRAMES
response = client.http.get(
- f"/timeframes",
+ "/timeframes",
headers={"Authorization": "Bearer SAMPLE_TOKEN_STRING"},
)
@@ -48,6 +49,7 @@ def test_get_all_timeframes():
assert response.status_code == 200
assert response.json_body == SAMPLE_TIMEFRAMES
+
def test_get_timeframe():
# Create a Chalice test client
with Client(app) as client:
@@ -60,7 +62,7 @@ def test_get_timeframe():
) as mock_get_timeframe:
mock_get_timeframe.return_value = SAMPLE_TIMEFRAMES[0]
response = client.http.get(
- f"/timeframes/test_timeframe_id",
+ "/timeframes/test_timeframe_id",
headers={"Authorization": "Bearer SAMPLE_TOKEN_STRING"},
)
@@ -68,6 +70,7 @@ def test_get_timeframe():
assert response.status_code == 200
assert response.json_body == SAMPLE_TIMEFRAMES[0]
+
def test_delete_timeframe():
with Client(app) as client:
with patch("chalicelib.decorators.jwt.decode") as mock_decode:
@@ -83,7 +86,7 @@ def test_delete_timeframe():
)
assert response.status_code == 200
- assert response.json_body == {'status': True}
+ assert response.json_body == {"status": True}
def test_create_event():
@@ -103,6 +106,7 @@ def test_create_event():
assert response.status_code == 200
assert response.json_body == {"msg": True}
+
def test_get_event():
# Create a Chalice test client
with Client(app) as client:
@@ -115,7 +119,7 @@ def test_get_event():
) as mock_get_timeframe:
mock_get_timeframe.return_value = SAMPLE_TIMEFRAMES[0]
response = client.http.get(
- f"/timeframes/test_timeframe_id",
+ "/timeframes/test_timeframe_id",
headers={"Authorization": "Bearer SAMPLE_TOKEN_STRING"},
)
@@ -123,6 +127,7 @@ def test_get_event():
assert response.status_code == 200
assert response.json_body == SAMPLE_TIMEFRAMES[0]
+
def test_get_timeframe_sheets():
# Create a Chalice test client
with Client(app) as client:
@@ -135,7 +140,7 @@ def test_get_timeframe_sheets():
) as mock_get_timeframe_sheets:
mock_get_timeframe_sheets.return_value = SAMPLE_TIMEFRAME_SHEETS
response = client.http.get(
- f"/timeframes/test_timeframe_id/sheets",
+ "/timeframes/test_timeframe_id/sheets",
headers={"Authorization": "Bearer SAMPLE_TOKEN_STRING"},
)
@@ -143,6 +148,7 @@ def test_get_timeframe_sheets():
assert response.status_code == 200
assert response.json_body == SAMPLE_TIMEFRAME_SHEETS
+
def test_checkin():
with Client(app) as client:
with patch("chalicelib.decorators.jwt.decode") as mock_decode:
@@ -153,13 +159,14 @@ def test_checkin():
) as mock_checkin:
mock_checkin.return_value = {"msg": True}
response = client.http.post(
- f"/events/test_event_id/checkin",
+ "/events/test_event_id/checkin",
headers={"Authorization": "Bearer SAMPLE_TOKEN_STRING"},
)
assert response.status_code == 200
assert response.json_body == {"msg": True}
+
def test_delete_event():
with Client(app) as client:
with patch("chalicelib.decorators.jwt.decode") as mock_decode:
@@ -170,9 +177,9 @@ def test_delete_event():
) as mock_delete:
mock_delete.return_value = {"status": True}
response = client.http.delete(
- f"/events/test_event_id",
+ "/events/test_event_id",
headers={"Authorization": "Bearer SAMPLE_TOKEN_STRING"},
)
assert response.status_code == 200
- assert response.json_body == {'status': True}
\ No newline at end of file
+ assert response.json_body == {"status": True}
diff --git a/tests/api/test_events_rush.py b/tests/api/test_events_rush.py
index e79cad3..8ebecc4 100644
--- a/tests/api/test_events_rush.py
+++ b/tests/api/test_events_rush.py
@@ -10,6 +10,7 @@
with open("tests/fixtures/events/general/sample_rush_event.json") as f:
SAMPLE_RUSH_EVENT = json.load(f)
+
def test_get_rush_events():
# Create a Chalice test client
with Client(app) as client:
@@ -22,7 +23,7 @@ def test_get_rush_events():
) as mock_get_rush_categories_and_events:
mock_get_rush_categories_and_events.return_value = SAMPLE_RUSH_EVENTS
response = client.http.get(
- f"/events/rush",
+ "/events/rush",
headers={"Authorization": "Bearer SAMPLE_TOKEN_STRING"},
)
@@ -30,6 +31,7 @@ def test_get_rush_events():
assert response.status_code == 200
assert response.json_body == SAMPLE_RUSH_EVENTS
+
def test_get_rush_event():
# Create a Chalice test client
with Client(app) as client:
@@ -42,7 +44,7 @@ def test_get_rush_event():
) as mock_get_rush_event:
mock_get_rush_event.return_value = SAMPLE_RUSH_EVENT
response = client.http.get(
- f"/events/rush/test_event_id",
+ "/events/rush/test_event_id",
headers={"Authorization": "Bearer SAMPLE_TOKEN_STRING"},
)
@@ -50,6 +52,7 @@ def test_get_rush_event():
assert response.status_code == 200
assert response.json_body == SAMPLE_RUSH_EVENT
+
def test_create_rush_category():
with Client(app) as client:
with patch("chalicelib.decorators.jwt.decode") as mock_decode:
@@ -60,13 +63,14 @@ def test_create_rush_category():
) as mock_create_rush_category:
mock_create_rush_category.return_value = {"msg": True}
response = client.http.post(
- f"/events/rush/category",
+ "/events/rush/category",
headers={"Authorization": "Bearer SAMPLE_TOKEN_STRING"},
)
assert response.status_code == 200
assert response.json_body == {"msg": True}
+
def test_create_rush_event():
with Client(app) as client:
with patch("chalicelib.decorators.jwt.decode") as mock_decode:
@@ -77,13 +81,14 @@ def test_create_rush_event():
) as mock_create_rush_event:
mock_create_rush_event.return_value = {"msg": True}
response = client.http.post(
- f"/events/rush",
+ "/events/rush",
headers={"Authorization": "Bearer SAMPLE_TOKEN_STRING"},
)
assert response.status_code == 200
assert response.json_body == {"msg": True}
+
def test_modify_rush_event():
with Client(app) as client:
with patch("chalicelib.decorators.jwt.decode") as mock_decode:
@@ -94,13 +99,14 @@ def test_modify_rush_event():
) as mock_modify_rush_event:
mock_modify_rush_event.return_value = {"msg": True}
response = client.http.patch(
- f"/events/rush",
+ "/events/rush",
headers={"Authorization": "Bearer SAMPLE_TOKEN_STRING"},
)
assert response.status_code == 200
assert response.json_body == {"msg": True}
+
def test_modify_rush_settings():
with Client(app) as client:
with patch("chalicelib.decorators.jwt.decode") as mock_decode:
@@ -111,13 +117,14 @@ def test_modify_rush_settings():
) as mock_modify_rush_settings:
mock_modify_rush_settings.return_value = {"msg": True}
response = client.http.patch(
- f"/events/rush/settings",
+ "/events/rush/settings",
headers={"Authorization": "Bearer SAMPLE_TOKEN_STRING"},
)
assert response.status_code == 200
assert response.json_body == {"msg": True}
+
def test_checkin_rush():
with Client(app) as client:
with patch("chalicelib.decorators.jwt.decode") as mock_decode:
@@ -128,13 +135,14 @@ def test_checkin_rush():
) as mock_checkin_rush:
mock_checkin_rush.return_value = {"msg": True}
response = client.http.post(
- f"/events/rush/checkin/test_event_id",
+ "/events/rush/checkin/test_event_id",
headers={"Authorization": "Bearer SAMPLE_TOKEN_STRING"},
)
assert response.status_code == 200
assert response.json_body == {"msg": True}
+
def test_delete_rush_event():
with Client(app) as client:
with patch("chalicelib.decorators.jwt.decode") as mock_decode:
@@ -145,9 +153,9 @@ def test_delete_rush_event():
) as mock_delete_rush_event:
mock_delete_rush_event.return_value = {"status": True}
response = client.http.delete(
- f"/events/rush/test_event_id",
+ "/events/rush/test_event_id",
headers={"Authorization": "Bearer SAMPLE_TOKEN_STRING"},
)
assert response.status_code == 200
- assert response.json_body == {'status': True}
\ No newline at end of file
+ assert response.json_body == {"status": True}
diff --git a/tests/api/test_insights.py b/tests/api/test_insights.py
index ccec7b3..2bc7069 100644
--- a/tests/api/test_insights.py
+++ b/tests/api/test_insights.py
@@ -26,7 +26,7 @@ def test_get_insights_from_listing():
SAMPLE_DISTRIBUTION,
]
response = client.http.get(
- f"/insights/listing/test_listing_id",
+ "/insights/listing/test_listing_id",
headers={"Authorization": "Bearer SAMPLE_TOKEN_STRING"},
)
diff --git a/tests/api/test_listings.py b/tests/api/test_listings.py
index b3728a6..6ad636a 100644
--- a/tests/api/test_listings.py
+++ b/tests/api/test_listings.py
@@ -41,7 +41,7 @@ def test_create_listing():
) as mock_create:
mock_create.return_value = {"msg": True}
response = client.http.post(
- f"/create",
+ "/create",
headers={"Authorization": "Bearer SAMPLE_TOKEN_STRING"},
)
@@ -77,7 +77,7 @@ def test_get_all_listings():
) as mock_get_all:
mock_get_all.return_value = TEST_LISTINGS
response = client.http.get(
- f"/listings",
+ "/listings",
headers={"Authorization": "Bearer SAMPLE_TOKEN_STRING"},
)
@@ -162,7 +162,7 @@ def test_update_listing_field_route_not_found():
headers={"Authorization": "Bearer SAMPLE_TOKEN_STRING"},
)
- assert response.json_body == None
+ assert response.json_body is None
def test_update_listing_field_route_bad_request():
@@ -182,7 +182,7 @@ def test_update_listing_field_route_bad_request():
# body, status_code = response.json_body
- assert response.json_body == None
+ assert response.json_body is None
def test_update_listing_field_route_exception():
@@ -200,4 +200,4 @@ def test_update_listing_field_route_exception():
headers={"Authorization": "Bearer SAMPLE_TOKEN_STRING"},
)
- assert response.json_body == None
+ assert response.json_body is None
diff --git a/tests/services/test_applicant_service.py b/tests/services/test_applicant_service.py
index 66f1f18..41f29ee 100644
--- a/tests/services/test_applicant_service.py
+++ b/tests/services/test_applicant_service.py
@@ -1,5 +1,5 @@
import pytest
-from unittest.mock import MagicMock, patch
+from unittest.mock import patch
from chalicelib.services.ApplicantService import ApplicantService
@@ -33,13 +33,12 @@ def test_get_all_applicants(service):
mock_db.get_all.return_value = sample_data
result = applicant_service.get_all()
- mock_db.get_all.assert_called_once_with(
- table_name="zap-applications"
- )
+ mock_db.get_all.assert_called_once_with(table_name="zap-applications")
assert result == sample_data
assert len(result) == 2
+
def test_get_all_applicants_from_listing(service):
applicant_service, mock_db = service
@@ -52,9 +51,8 @@ def test_get_all_applicants_from_listing(service):
result = applicant_service.get_all_from_listing(listing_id)
mock_db.get_applicants.assert_called_once_with(
- table_name="zap-applications",
- listing_id=listing_id
+ table_name="zap-applications", listing_id=listing_id
)
assert result == sample_data
- assert len(result) == 2
\ No newline at end of file
+ assert len(result) == 2
diff --git a/tests/services/test_events_member_service.py b/tests/services/test_events_member_service.py
index 850e8ae..989fc7c 100644
--- a/tests/services/test_events_member_service.py
+++ b/tests/services/test_events_member_service.py
@@ -1,5 +1,5 @@
import pytest
-from unittest.mock import MagicMock, patch
+from unittest.mock import patch
from chalicelib.services.EventsMemberService import EventsMemberService
import datetime
import json
@@ -16,18 +16,26 @@
with open("tests/fixtures/events/general/sample_rush_event.json") as f:
SAMPLE_RUSH_EVENT = json.load(f)
+
@pytest.fixture
def mock_mongo_module():
- with patch("chalicelib.modules.mongo.MongoModule", autospec=True) as MockMongoModule:
+ with patch(
+ "chalicelib.modules.mongo.MongoModule", autospec=True
+ ) as MockMongoModule:
mock_instance = MockMongoModule(use_mock=True)
yield mock_instance
+
@pytest.fixture
def event_service(mock_mongo_module):
return EventsMemberService(mock_mongo_module)
+
def test_insert_document(event_service, mock_mongo_module):
- CREATE_TIMEFRAME = { "name": "testTimeframeName", "spreadsheetId": "testSpreadsheetId" }
+ CREATE_TIMEFRAME = {
+ "name": "testTimeframeName",
+ "spreadsheetId": "testSpreadsheetId",
+ }
date_created = datetime.datetime.now()
with patch("chalicelib.services.EventsMemberService.datetime") as mock_datetime:
@@ -40,10 +48,11 @@ def test_insert_document(event_service, mock_mongo_module):
assert result == {"msg": True}
+
def test_get_timeframe(event_service, mock_mongo_module):
mock_mongo_module.get_document_by_id.return_value = SAMPLE_TIMEFRAMES[0]
timeframe_id = SAMPLE_TIMEFRAMES[0]["_id"]
-
+
result = event_service.get_timeframe(timeframe_id=timeframe_id)
mock_mongo_module.get_document_by_id.assert_called_once_with(
collection="events-timeframe", document_id=timeframe_id
@@ -51,9 +60,10 @@ def test_get_timeframe(event_service, mock_mongo_module):
assert result == json.dumps(SAMPLE_TIMEFRAMES[0])
+
def test_get_all_timeframe(event_service, mock_mongo_module):
mock_mongo_module.get_data_from_collection.return_value = SAMPLE_TIMEFRAMES
-
+
result = event_service.get_all_timeframes()
mock_mongo_module.get_data_from_collection.assert_called_once_with(
collection="events-timeframe"
@@ -61,6 +71,7 @@ def test_get_all_timeframe(event_service, mock_mongo_module):
assert result == json.dumps(SAMPLE_TIMEFRAMES)
+
def test_delete_timeframe(event_service, mock_mongo_module):
result = event_service.delete_timeframe(timeframe_id=SAMPLE_TIMEFRAMES[0]["_id"])
assert result["statusCode"] == 200
diff --git a/tests/services/test_insights_service.py b/tests/services/test_insights_service.py
index d4953e0..3e4e4a6 100644
--- a/tests/services/test_insights_service.py
+++ b/tests/services/test_insights_service.py
@@ -1,37 +1,37 @@
import pytest
-from unittest.mock import MagicMock, patch
+from unittest.mock import patch
from chalicelib.services.InsightsService import InsightsService
import copy
import json
# Load JSON data from a file (insights test - general)
-with open('tests/fixtures/insights/general/sample_applicants.json') as f:
+with open("tests/fixtures/insights/general/sample_applicants.json") as f:
SAMPLE_APPLICANTS = json.load(f)
-with open('tests/fixtures/insights/general/sample_dashboard.json') as f:
+with open("tests/fixtures/insights/general/sample_dashboard.json") as f:
SAMPLE_DASHBOARD = json.load(f)
-with open('tests/fixtures/insights/general/sample_distribution.json') as f:
+with open("tests/fixtures/insights/general/sample_distribution.json") as f:
SAMPLE_DISTRIBUTION = json.load(f)
# Load JSON data from a file (insights test - no applicants)
-with open('tests/fixtures/insights/noApplicants/sample_applicants.json') as f:
+with open("tests/fixtures/insights/noApplicants/sample_applicants.json") as f:
SAMPLE_APPLICANTS_NO_APP = json.load(f)
-with open('tests/fixtures/insights/noApplicants/sample_dashboard.json') as f:
+with open("tests/fixtures/insights/noApplicants/sample_dashboard.json") as f:
SAMPLE_DASHBOARD_NO_APP = json.load(f)
-with open('tests/fixtures/insights/noApplicants/sample_distribution.json') as f:
+with open("tests/fixtures/insights/noApplicants/sample_distribution.json") as f:
SAMPLE_DISTRIBUTION_NO_APP = json.load(f)
# Load JSON data from a file (insights test - applicant with no gpa)
-with open('tests/fixtures/insights/noGPAs/sample_applicants.json') as f:
+with open("tests/fixtures/insights/noGPAs/sample_applicants.json") as f:
SAMPLE_APPLICANTS_NO_GPA = json.load(f)
-with open('tests/fixtures/insights/noGPAs/sample_dashboard.json') as f:
+with open("tests/fixtures/insights/noGPAs/sample_dashboard.json") as f:
SAMPLE_DASHBOARD_NO_GPA = json.load(f)
-with open('tests/fixtures/insights/noGPAs/sample_distribution.json') as f:
+with open("tests/fixtures/insights/noGPAs/sample_distribution.json") as f:
SAMPLE_DISTRIBUTION_NO_GPA = json.load(f)
@@ -51,8 +51,7 @@ def test_get_insights(service):
result = insights_service.get_insights_from_listing(listing_id)
# confirm that database was called once with correct inputs
mock_db.get_applicants.assert_called_once_with(
- table_name="zap-applications",
- listing_id=listing_id
+ table_name="zap-applications", listing_id=listing_id
)
# Convert Python dictionary to JSON format for comparison
@@ -78,8 +77,7 @@ def test_get_insights_no_applicants(service):
result = insights_service.get_insights_from_listing(listing_id)
# confirm that database was called once with correct inputs
mock_db.get_applicants.assert_called_once_with(
- table_name="zap-applications",
- listing_id=listing_id
+ table_name="zap-applications", listing_id=listing_id
)
# Convert Python dictionary to JSON format for comparison
@@ -105,8 +103,7 @@ def test_get_insights_no_gpas(service):
result = insights_service.get_insights_from_listing(listing_id)
# confirm that database was called once with correct inputs
mock_db.get_applicants.assert_called_once_with(
- table_name="zap-applications",
- listing_id=listing_id
+ table_name="zap-applications", listing_id=listing_id
)
# Convert Python dictionary to JSON format for comparison
@@ -119,4 +116,4 @@ def test_get_insights_no_gpas(service):
assert len(result) == 2
assert result_dash == json.dumps(SAMPLE_DASHBOARD_NO_GPA, sort_keys=True)
- assert result_dist == json.dumps(SAMPLE_DISTRIBUTION_NO_GPA, sort_keys=True)
\ No newline at end of file
+ assert result_dist == json.dumps(SAMPLE_DISTRIBUTION_NO_GPA, sort_keys=True)
diff --git a/tests/services/test_listing_service.py b/tests/services/test_listing_service.py
index 1362cbc..a608dfd 100644
--- a/tests/services/test_listing_service.py
+++ b/tests/services/test_listing_service.py
@@ -1,5 +1,5 @@
import pytest
-from unittest.mock import MagicMock, patch
+from unittest.mock import patch, Mock
from chalicelib.services.ListingService import ListingService
from chalice import NotFoundError
from pydantic import ValidationError
@@ -151,7 +151,9 @@ def test_toggle_visibility_exception(service):
mock_db.toggle_visibility.side_effect = Exception("Error")
- result = json.loads(listing_service.toggle_visibility(SAMPLE_LISTINGS[0]["listingId"]))
+ result = json.loads(
+ listing_service.toggle_visibility(SAMPLE_LISTINGS[0]["listingId"])
+ )
assert result["statusCode"] == 500
@@ -172,10 +174,12 @@ def test_update_field_route(service):
mock_db.update_listing_field.return_value = mock_updated_listing
- result = json.loads(listing_service.update_field_route(
- SAMPLE_LISTINGS[0]["listingId"],
- {"field": "title", "value": "new test title"},
- ))
+ result = json.loads(
+ listing_service.update_field_route(
+ SAMPLE_LISTINGS[0]["listingId"],
+ {"field": "title", "value": "new test title"},
+ )
+ )
assert result["statusCode"] == 200
assert result["updated_listing"] == mock_updated_listing
@@ -227,9 +231,6 @@ def test_update_field_updated_listing_not_found(service):
assert str(exc_info.value) == "Listing not found"
-from unittest.mock import Mock
-
-
def test_update_field_validation_error(service):
listing_service, _ = service
diff --git a/tests/test_decorators.py b/tests/test_decorators.py
index 5b352c5..c52f0c5 100644
--- a/tests/test_decorators.py
+++ b/tests/test_decorators.py
@@ -1,6 +1,6 @@
-from unittest.mock import patch
from chalicelib.decorators import add_env_suffix
+
def test_add_env_suffix_dev():
def mocked_function(self, table_name: str, *args, **kwargs):
return table_name
@@ -17,6 +17,7 @@ def mocked_function(self, table_name: str, *args, **kwargs):
# Check if the suffix is added correctly
assert result == "test-table-dev"
+
def test_add_env_suffix_prod():
def mocked_function(self, table_name: str, *args, **kwargs):
return table_name
@@ -31,4 +32,4 @@ def mocked_function(self, table_name: str, *args, **kwargs):
result = decorated_function(instance_mock, "test-table", env=True)
# Check if the suffix is added correctly
- assert result == "test-table-prod"
\ No newline at end of file
+ assert result == "test-table-prod"
diff --git a/tests/test_dynamodb.py b/tests/test_dynamodb.py
index da22c36..b77a3c1 100644
--- a/tests/test_dynamodb.py
+++ b/tests/test_dynamodb.py
@@ -101,10 +101,8 @@ def test_delete_item(db):
db.put_data("test-table", SAMPLE_DATA[0])
response = db.delete_item("test-table", {"id": 123})
- assert response == True
+ assert response
# TODO: Test case should fail, but isn't
# response = db.delete_item("test-table", {"id": 124})
# assert response == False
-
-
From c416c192d9553061044a48f0cc6d48fc56ab62a7 Mon Sep 17 00:00:00 2001
From: William De Rocco <93288641+wderocco8@users.noreply.github.com>
Date: Sat, 3 Aug 2024 09:42:16 -0400
Subject: [PATCH 28/34] returned categoryName in API
---
chalicelib/services/EventsRushService.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/chalicelib/services/EventsRushService.py b/chalicelib/services/EventsRushService.py
index a934afd..952c063 100644
--- a/chalicelib/services/EventsRushService.py
+++ b/chalicelib/services/EventsRushService.py
@@ -354,7 +354,8 @@ def get_rush_category_analytics(self, category_id: str):
else:
attendees[email] = { **attendee, "eventsAttended": [new_event] }
+ result = { "categoryName": category["name"], "attendees": attendees }
- return json.dumps(attendees, cls=self.BSONEncoder)
+ return json.dumps(result, cls=self.BSONEncoder)
events_rush_service = EventsRushService()
\ No newline at end of file
From 3271fe45873d0eec26b619489c8fc6c7f906dc99 Mon Sep 17 00:00:00 2001
From: Jin Young Bang
Date: Sat, 3 Aug 2024 22:06:53 -0400
Subject: [PATCH 29/34] feat: implement family tree get filter api
---
chalicelib/api/members.py | 6 ++++++
chalicelib/services/MemberService.py | 15 +++++++++++++++
2 files changed, 21 insertions(+)
diff --git a/chalicelib/api/members.py b/chalicelib/api/members.py
index 57b3f32..37734f9 100644
--- a/chalicelib/api/members.py
+++ b/chalicelib/api/members.py
@@ -64,3 +64,9 @@ def delete_members():
def update_member_roles(user_id):
data = members_api.current_request.json_body
return member_service.update_roles(user_id, data["roles"])
+
+
+@members_api.route("/members/family-tree", methods=["GET"], cors=True)
+@auth(members_api, roles=[Roles.MEMBER])
+def get_family_tree():
+ return member_service.get_family_tree()
diff --git a/chalicelib/services/MemberService.py b/chalicelib/services/MemberService.py
index c5f8943..91297e3 100644
--- a/chalicelib/services/MemberService.py
+++ b/chalicelib/services/MemberService.py
@@ -2,6 +2,7 @@
from chalice import ConflictError, NotFoundError, UnauthorizedError
from bson import ObjectId
+from collections import defaultdict
import json
import jwt
import boto3
@@ -118,5 +119,19 @@ def update_roles(self, document_id=str, roles=list) -> bool:
[{"$set": {"roles": roles}}],
)
+ def get_family_tree(self):
+ data = mongo_module.get_data_from_collection(self.collection)
+
+ # Group by family
+ family_groups = defaultdict(list)
+
+ for member in data:
+ if "big" not in member or member["big"] == "":
+ continue
+ member["_id"] = str(member["_id"])
+ family_groups[member["family"]].append(member)
+
+ return family_groups
+
member_service = MemberService()
From 4cb5535fde44b0806d9a1b2c5350a9d44ffd00c9 Mon Sep 17 00:00:00 2001
From: William De Rocco <93288641+wderocco8@users.noreply.github.com>
Date: Sun, 4 Aug 2024 12:43:34 -0400
Subject: [PATCH 30/34] swapped order of decorators in api (need to check auth
before handling exceptions --> otherwise user will not get 401 error even if
they're unauthenticated)
---
chalicelib/api/events_rush.py | 4 +++-
chalicelib/api/listings.py | 2 +-
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/chalicelib/api/events_rush.py b/chalicelib/api/events_rush.py
index 8159289..7d62ca5 100644
--- a/chalicelib/api/events_rush.py
+++ b/chalicelib/api/events_rush.py
@@ -1,12 +1,14 @@
from chalice import Blueprint
from chalicelib.decorators import auth
from chalicelib.services.EventsRushService import events_rush_service
+from chalicelib.models.roles import Roles
+
events_rush_api = Blueprint(__name__)
@events_rush_api.route("/events/rush", methods=["GET"], cors=True)
-@auth(events_rush_api, roles=["admin"])
+@auth(events_rush_api, roles=[Roles.ADMIN, Roles.MEMBER])
def get_rush_events():
return events_rush_service.get_rush_categories_and_events()
diff --git a/chalicelib/api/listings.py b/chalicelib/api/listings.py
index f9d58f6..482f24f 100644
--- a/chalicelib/api/listings.py
+++ b/chalicelib/api/listings.py
@@ -55,8 +55,8 @@ def toggle_visibility(id):
@listings_api.route("/listings/{id}/update-field", methods=["PATCH"], cors=True)
-@handle_exceptions
@auth(listings_api, roles=[Roles.ADMIN, Roles.MEMBER])
+@handle_exceptions
def update_listing_field_route(id):
try:
return listing_service.update_field_route(
From 5a649a47a4babfd29905928fa41fc314916b8f3c Mon Sep 17 00:00:00 2001
From: William De Rocco <93288641+wderocco8@users.noreply.github.com>
Date: Sun, 4 Aug 2024 12:52:09 -0400
Subject: [PATCH 31/34] adjusted roles to use Roles object
---
chalicelib/api/accountability.py | 3 ++-
chalicelib/api/events_member.py | 21 +++++++++++----------
chalicelib/api/events_rush.py | 11 ++++++-----
chalicelib/api/members.py | 4 ++--
4 files changed, 21 insertions(+), 18 deletions(-)
diff --git a/chalicelib/api/accountability.py b/chalicelib/api/accountability.py
index 464c148..e82c988 100644
--- a/chalicelib/api/accountability.py
+++ b/chalicelib/api/accountability.py
@@ -1,12 +1,13 @@
from chalice import Blueprint
from chalicelib.decorators import auth
from chalicelib.services.AccountabilityService import accountability_service
+from chalicelib.models.roles import Roles
accountability_api = Blueprint(__name__)
@accountability_api.route("/accountability", methods=["GET"], cors=True)
-@auth(accountability_api, roles=["admin"])
+@auth(accountability_api, roles=[Roles.ADMIN, Roles.MEMBER])
def get_accountability():
if accountability_api.current_request.query_params:
page = int(accountability_api.current_request.query_params.get("page", 0))
diff --git a/chalicelib/api/events_member.py b/chalicelib/api/events_member.py
index 9ea805e..cba9fad 100644
--- a/chalicelib/api/events_member.py
+++ b/chalicelib/api/events_member.py
@@ -1,68 +1,69 @@
from chalice import Blueprint
from chalicelib.decorators import auth
from chalicelib.services.EventsMemberService import events_member_service
+from chalicelib.models.roles import Roles
events_member_api = Blueprint(__name__)
@events_member_api.route("/timeframes", methods=["POST"], cors=True)
-@auth(events_member_api, roles=["admin"])
+@auth(events_member_api, roles=[Roles.ADMIN])
def create_timeframe():
data = events_member_api.current_request.json_body
return events_member_service.create_timeframe(data)
@events_member_api.route("/timeframes", methods=["GET"], cors=True)
-@auth(events_member_api, roles=["admin"])
+@auth(events_member_api, roles=[Roles.ADMIN, Roles.MEMBER])
def get_all_timeframes():
return events_member_service.get_all_timeframes()
@events_member_api.route("/timeframes/{timeframe_id}", methods=["GET"], cors=True)
-@auth(events_member_api, roles=["admin"])
+@auth(events_member_api, roles=[Roles.ADMIN, Roles.MEMBER])
def get_timeframe(timeframe_id: str):
return events_member_service.get_timeframe(timeframe_id)
@events_member_api.route("/timeframes/{timeframe_id}", methods=["DELETE"], cors=True)
-@auth(events_member_api, roles=["admin"])
+@auth(events_member_api, roles=[Roles.ADMIN])
def delete_timeframe(timeframe_id: str):
return events_member_service.delete_timeframe(timeframe_id)
@events_member_api.route("/timeframes/{timeframe_id}/events", methods=["POST"], cors=True)
-@auth(events_member_api, roles=["admin"])
+@auth(events_member_api, roles=[Roles.ADMIN])
def create_event(timeframe_id: str):
data = events_member_api.current_request.json_body
return events_member_service.create_event(timeframe_id, data)
@events_member_api.route("/events/{event_id}", methods=["GET"], cors=True)
-@auth(events_member_api, roles=["admin"])
+@auth(events_member_api, roles=[Roles.ADMIN, Roles.MEMBER])
def get_event(event_id: str):
return events_member_service.get_event(event_id)
@events_member_api.route("/timeframes/{timeframe_id}/sheets", methods=["GET"], cors=True)
-@auth(events_member_api, roles=["admin"])
+@auth(events_member_api, roles=[Roles.ADMIN, Roles.MEMBER])
def get_timeframe_sheets(timeframe_id: str):
return events_member_service.get_timeframe_sheets(timeframe_id)
@events_member_api.route("/events/{event_id}/checkin", methods=["POST"], cors=True)
-@auth(events_member_api, roles=["admin"])
+@auth(events_member_api, roles=[Roles.ADMIN, Roles.MEMBER])
def checkin(event_id: str):
data = events_member_api.current_request.json_body
return events_member_service.checkin(event_id, data)
@events_member_api.route("/events/{event_id}", methods=["PATCH"], cors=True)
-@auth(events_member_api, roles=["admin"])
+@auth(events_member_api, roles=[Roles.ADMIN])
def update_event(event_id: str):
pass
@events_member_api.route("/events/{event_id}", methods=["DELETE"], cors=True)
-@auth(events_member_api, roles=["admin"])
+@auth(events_member_api, roles=[Roles.ADMIN])
def delete_event(event_id: str):
return events_member_service.delete(event_id)
\ No newline at end of file
diff --git a/chalicelib/api/events_rush.py b/chalicelib/api/events_rush.py
index 7d62ca5..44191c6 100644
--- a/chalicelib/api/events_rush.py
+++ b/chalicelib/api/events_rush.py
@@ -19,27 +19,27 @@ def get_rush_event(event_id):
@events_rush_api.route("/events/rush/category", methods=["POST"], cors=True)
-@auth(events_rush_api, roles=["admin"])
+@auth(events_rush_api, roles=[Roles.ADMIN])
def create_rush_category():
data = events_rush_api.current_request.json_body
return events_rush_service.create_rush_category(data)
@events_rush_api.route("/events/rush", methods=["POST"], cors=True)
-@auth(events_rush_api, roles=["admin"])
+@auth(events_rush_api, roles=[Roles.ADMIN])
def create_rush_event():
data = events_rush_api.current_request.json_body
return events_rush_service.create_rush_event(data)
@events_rush_api.route("/events/rush", methods=["PATCH"], cors=True)
-@auth(events_rush_api, roles=["admin"])
+@auth(events_rush_api, roles=[Roles.ADMIN])
def modify_rush_event():
data = events_rush_api.current_request.json_body
return events_rush_service.modify_rush_event(data)
@events_rush_api.route("/events/rush/settings", methods=["PATCH"], cors=True)
-@auth(events_rush_api, roles=["admin"])
+@auth(events_rush_api, roles=[Roles.ADMIN])
def modify_rush_settings():
data = events_rush_api.current_request.json_body
return events_rush_service.modify_rush_settings(data)
@@ -58,11 +58,12 @@ def get_rush_events_default_category():
@events_rush_api.route("/events/rush/{event_id}", methods=["DELETE"], cors=True)
+@auth(events_rush_api, roles=[Roles.ADMIN])
def delete_rush_event(event_id):
return events_rush_service.delete_rush_event(event_id)
@events_rush_api.route("/events/rush/{category_id}/analytics", methods=["GET"], cors=True)
-@auth(events_rush_api, roles=["admin"])
+@auth(events_rush_api, roles=[Roles.ADMIN])
def get_rush_category_analytics(category_id):
return events_rush_service.get_rush_category_analytics(category_id=category_id)
\ No newline at end of file
diff --git a/chalicelib/api/members.py b/chalicelib/api/members.py
index 57b3f32..6300ebf 100644
--- a/chalicelib/api/members.py
+++ b/chalicelib/api/members.py
@@ -7,7 +7,7 @@
@members_api.route("/member/{user_id}", methods=["GET"], cors=True)
-@auth(members_api, roles=["admin", "member"])
+@auth(members_api, roles=[Roles.ADMIN, Roles.MEMBER])
def get_member(user_id):
member = member_service.get_by_id(user_id)
return member if member else {}
@@ -23,7 +23,7 @@ def update_member(user_id):
@members_api.route("/members", methods=["GET"], cors=True)
-@auth(members_api, roles=["admin", "member"])
+@auth(members_api, roles=[Roles.ADMIN, Roles.MEMBER])
def get_all_members():
"""Fetches all members who have access to WhyPhi."""
return member_service.get_all()
From c95cf8c35aaece38d1c3e06ad531d74c2c3640e5 Mon Sep 17 00:00:00 2001
From: William De Rocco <93288641+wderocco8@users.noreply.github.com>
Date: Sun, 11 Aug 2024 13:52:15 -0400
Subject: [PATCH 32/34] returned list of all events in object
---
chalicelib/services/EventsRushService.py | 24 ++++++++++++++++++------
1 file changed, 18 insertions(+), 6 deletions(-)
diff --git a/chalicelib/services/EventsRushService.py b/chalicelib/services/EventsRushService.py
index 952c063..f3a1268 100644
--- a/chalicelib/services/EventsRushService.py
+++ b/chalicelib/services/EventsRushService.py
@@ -342,19 +342,31 @@ def get_rush_category_analytics(self, category_id: str):
# attendees : dict of all users (user: { name, email, eventsAttended: list of objects })
attendees = {}
+ # events: list of objects (event: { name, eventId })
+ events = []
+
for event in category["events"]:
+ new_event = {
+ "eventId": event["_id"],
+ "eventName": event["name"]
+ }
+
+ # accumulate list of events
+ events.append(new_event)
+
+ # accumulate attendance
for attendee in event["attendees"]:
- email =attendee["email"]
- new_event = {
- "eventId": event["_id"],
- "eventName": event["name"]
- }
+ email = attendee["email"]
if email in attendees:
attendees[email]["eventsAttended"].append(new_event)
else:
attendees[email] = { **attendee, "eventsAttended": [new_event] }
- result = { "categoryName": category["name"], "attendees": attendees }
+ result = {
+ "categoryName": category["name"],
+ "attendees": attendees,
+ "events": events,
+ }
return json.dumps(result, cls=self.BSONEncoder)
From 4b9a5dbc27dc496dc6f951b483b8283164d7fe65 Mon Sep 17 00:00:00 2001
From: William De Rocco <93288641+wderocco8@users.noreply.github.com>
Date: Sun, 11 Aug 2024 16:08:03 -0400
Subject: [PATCH 33/34] adjusted backend to handle flags for hiding code and
attendee data
---
chalicelib/api/events_rush.py | 5 +++--
chalicelib/services/EventsRushService.py | 8 ++++++--
2 files changed, 9 insertions(+), 4 deletions(-)
diff --git a/chalicelib/api/events_rush.py b/chalicelib/api/events_rush.py
index 44191c6..9ae5120 100644
--- a/chalicelib/api/events_rush.py
+++ b/chalicelib/api/events_rush.py
@@ -13,9 +13,10 @@ def get_rush_events():
return events_rush_service.get_rush_categories_and_events()
-@events_rush_api.route("/events/rush/{event_id}", methods=["GET"], cors=True)
+@events_rush_api.route("/events/rush/{event_id}", methods=["POST"], cors=True)
def get_rush_event(event_id):
- return events_rush_service.get_rush_event(event_id)
+ data = events_rush_api.current_request.json_body
+ return events_rush_service.get_rush_event(event_id=event_id, data=data)
@events_rush_api.route("/events/rush/category", methods=["POST"], cors=True)
diff --git a/chalicelib/services/EventsRushService.py b/chalicelib/services/EventsRushService.py
index f3a1268..f7545f7 100644
--- a/chalicelib/services/EventsRushService.py
+++ b/chalicelib/services/EventsRushService.py
@@ -182,7 +182,10 @@ def modify_rush_settings(self, data: dict):
return
- def get_rush_event(self, event_id: str, hide_attendees: bool = True):
+ def get_rush_event(self, event_id: str, data: dict):
+ hide_attendees = data.get("hideAttendees", True)
+ hide_code = data.get("hideCode", True)
+
event = self.mongo_module.get_document_by_id(
f"{self.collection_prefix}rush-event", event_id
)
@@ -190,7 +193,8 @@ def get_rush_event(self, event_id: str, hide_attendees: bool = True):
if hide_attendees:
event.pop("attendeesId", None)
- event.pop("code")
+ if hide_code:
+ event.pop("code")
return json.dumps(event, cls=self.BSONEncoder)
From e343ecf279f5335b434cf48687dc0a8cfe0fe005 Mon Sep 17 00:00:00 2001
From: William De Rocco <93288641+wderocco8@users.noreply.github.com>
Date: Wed, 14 Aug 2024 21:31:13 -0400
Subject: [PATCH 34/34] updated unit tests
---
tests/api/test_events_rush.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/tests/api/test_events_rush.py b/tests/api/test_events_rush.py
index 8ebecc4..914e22c 100644
--- a/tests/api/test_events_rush.py
+++ b/tests/api/test_events_rush.py
@@ -43,8 +43,9 @@ def test_get_rush_event():
"chalicelib.services.EventsRushService.events_rush_service.get_rush_event",
) as mock_get_rush_event:
mock_get_rush_event.return_value = SAMPLE_RUSH_EVENT
- response = client.http.get(
+ response = client.http.post(
"/events/rush/test_event_id",
+ body={ "hideCode": False, "hideAttendees": False },
headers={"Authorization": "Bearer SAMPLE_TOKEN_STRING"},
)