Spaces:
Running
Running
added most features
Browse files- app.py +126 -0
- gamification/imports.py +8 -0
- gamification/levelLogic.py +111 -0
- gamification/logic.py +30 -167
- gamification/objects.py +244 -12
- gamification/pointLogic.py +112 -0
- gamification/routes.py +65 -7
- s.py +0 -0
- streaksManagement.py +20 -4
- testing.py +0 -0
- utils.py +9 -5
app.py
CHANGED
@@ -15,6 +15,16 @@ from google import genai
|
|
15 |
from typing import Optional,List
|
16 |
from pydantic import BaseModel
|
17 |
import re
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
18 |
|
19 |
load_dotenv()
|
20 |
|
@@ -410,3 +420,119 @@ def protected_route(authorization: str = Header(...)):
|
|
410 |
|
411 |
|
412 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
15 |
from typing import Optional,List
|
16 |
from pydantic import BaseModel
|
17 |
import re
|
18 |
+
from bson.json_util import dumps
|
19 |
+
import threading
|
20 |
+
import concurrent.futures
|
21 |
+
from gamification.pointLogic import get_all_simple_points_func,get_dream_job
|
22 |
+
from pydantic import BaseModel
|
23 |
+
from datetime import datetime
|
24 |
+
from bson import ObjectId
|
25 |
+
import os
|
26 |
+
|
27 |
+
|
28 |
|
29 |
load_dotenv()
|
30 |
|
|
|
420 |
|
421 |
|
422 |
|
423 |
+
|
424 |
+
class LeaderBoardRanking(BaseModel):
|
425 |
+
userId:str
|
426 |
+
firstName:str
|
427 |
+
lastName:str
|
428 |
+
totalpoints:float
|
429 |
+
lastUpdated:datetime
|
430 |
+
careerPath:str
|
431 |
+
class Config:
|
432 |
+
json_encoder ={
|
433 |
+
ObjectId:str
|
434 |
+
}
|
435 |
+
|
436 |
+
|
437 |
+
def create_leaderboard_ranking( document: LeaderBoardRanking) -> bool:
|
438 |
+
|
439 |
+
collection = db['LeaderBoard']
|
440 |
+
# Insert the document
|
441 |
+
result= collection.find_one_and_replace(filter={"userId":document.userId},replacement=document.model_dump())
|
442 |
+
if result==None:
|
443 |
+
result = collection.insert_one(document.model_dump())
|
444 |
+
print("correctly inserted new document for",document.firstName)
|
445 |
+
return True
|
446 |
+
|
447 |
+
return False
|
448 |
+
|
449 |
+
|
450 |
+
|
451 |
+
def get_all_users(user_id =None) -> List:
|
452 |
+
|
453 |
+
client = MongoClient(MONGO_URI)
|
454 |
+
db = client.crayonics
|
455 |
+
collection = db['users']
|
456 |
+
# Insert the document
|
457 |
+
if user_id==None:
|
458 |
+
results= collection.find()
|
459 |
+
|
460 |
+
if results:
|
461 |
+
result = [result for result in results]
|
462 |
+
return result
|
463 |
+
|
464 |
+
client.close()
|
465 |
+
else:
|
466 |
+
result = collection.find_one(filter={"_id":ObjectId(user_id)})
|
467 |
+
return result
|
468 |
+
|
469 |
+
|
470 |
+
def get_user_id_from_docKey(dockId):
|
471 |
+
client = MongoClient(MONGO_URI)
|
472 |
+
db = client.crayonics
|
473 |
+
collection = db['Points']
|
474 |
+
# Insert the document
|
475 |
+
result = collection.find_one(filter={"_id":ObjectId(dockId)})
|
476 |
+
client.close()
|
477 |
+
return result['userId']
|
478 |
+
|
479 |
+
|
480 |
+
|
481 |
+
|
482 |
+
|
483 |
+
# MongoDB connection setup
|
484 |
+
client = MongoClient(MONGO_URI)
|
485 |
+
db = client.crayonics
|
486 |
+
collection = db['Points']
|
487 |
+
|
488 |
+
# A function to handle changes
|
489 |
+
def handle_change(change):
|
490 |
+
# add everybodies points and add it to the leaderboard table
|
491 |
+
collections = db.list_collection_names()
|
492 |
+
if "LeaderBoard" not in collections:
|
493 |
+
print("Collection doesnt exists.")
|
494 |
+
# loop through points, get userId from points and total points from points table then we
|
495 |
+
# loop through userId and get the firstName and LastName of each userId lastly
|
496 |
+
# loop throguh the questionarier using the userId and get the careerPath of each user
|
497 |
+
users = get_all_users()
|
498 |
+
for user in users:
|
499 |
+
points = get_all_simple_points_func(userId=str(user['_id']))
|
500 |
+
tempDreamJob = get_dream_job(userId=str(user['_id']))
|
501 |
+
dreamJob = tempDreamJob if type(tempDreamJob)==str else "IncompleteProfile"
|
502 |
+
create_leaderboard_ranking(LeaderBoardRanking(userId=str(user['_id']),firstName=user['first_name'],lastName=user['last_name'],totalpoints=points.totalpoints,lastUpdated=datetime.now(),careerPath=dreamJob,))
|
503 |
+
else:
|
504 |
+
if change['operationType'] == 'insert':
|
505 |
+
# Extract the full document
|
506 |
+
full_document = change['fullDocument']
|
507 |
+
|
508 |
+
# Extract the userId and numOfPoints
|
509 |
+
|
510 |
+
user_id =full_document.get('userId')
|
511 |
+
leveleduser = get_all_users(userId=user_id)
|
512 |
+
points = get_all_simple_points_func(userId=user_id)
|
513 |
+
tempDreamJob = get_dream_job(userId=user_id)
|
514 |
+
dreamJob = tempDreamJob if type(tempDreamJob)==str else "IncompleteProfile"
|
515 |
+
create_leaderboard_ranking(LeaderBoardRanking(userId=user_id,firstName=leveleduser['first_name'],lastName=leveleduser['last_name'],totalpoints=points.totalpoints,lastUpdated=datetime.now(),careerPath=dreamJob,))
|
516 |
+
elif change['operationType'] == 'update':
|
517 |
+
dockey = str(change['documentKey']['_id'])
|
518 |
+
user_id = get_user_id_from_docKey(dockId=dockey)
|
519 |
+
leveleduser = get_all_users(user_id=user_id)
|
520 |
+
points = get_all_simple_points_func(userId=user_id)
|
521 |
+
tempDreamJob = get_dream_job(userId=user_id)
|
522 |
+
dreamJob = tempDreamJob if type(tempDreamJob)==str else "IncompleteProfile"
|
523 |
+
create_leaderboard_ranking(LeaderBoardRanking(userId=user_id,firstName=leveleduser['first_name'],lastName=leveleduser['last_name'],totalpoints=points.totalpoints,lastUpdated=datetime.now(),careerPath=dreamJob,))
|
524 |
+
|
525 |
+
|
526 |
+
print(f"Change detected: {dumps(change)}")
|
527 |
+
|
528 |
+
# Function to run the change stream in a separate thread (non-blocking)
|
529 |
+
def watch_change_stream():
|
530 |
+
with concurrent.futures.ThreadPoolExecutor(max_workers=10) as executor: # Limit to 10 threads, adjust as needed
|
531 |
+
with collection.watch() as stream:
|
532 |
+
for change in stream:
|
533 |
+
# Submit the handle_change task to the thread pool
|
534 |
+
executor.submit(handle_change, change)
|
535 |
+
# Start a background thread to watch the change stream
|
536 |
+
@app.on_event("startup")
|
537 |
+
def start_change_stream():
|
538 |
+
threading.Thread(target=watch_change_stream, daemon=True).start()
|
gamification/imports.py
ADDED
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from bson import ObjectId
|
2 |
+
from pymongo import MongoClient
|
3 |
+
import os
|
4 |
+
from typing import List
|
5 |
+
from dotenv import load_dotenv
|
6 |
+
from datetime import datetime,timedelta
|
7 |
+
load_dotenv()
|
8 |
+
MONGO_URI = os.getenv("MONGO_URI")
|
gamification/levelLogic.py
ADDED
@@ -0,0 +1,111 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# levels
|
2 |
+
from gamification.imports import *
|
3 |
+
|
4 |
+
from gamification.objects import UserLevel
|
5 |
+
|
6 |
+
|
7 |
+
def create_level_func(document:UserLevel,)->bool:
|
8 |
+
"""
|
9 |
+
Creates a UserLevel document in the MongoDB collection.
|
10 |
+
|
11 |
+
:param UserLevel: Actually accepts an Instance of UserLevel and deserializes it here
|
12 |
+
:param maxPoints: Maximum Amount of points for this level.
|
13 |
+
:param minPoints: Minimum Amount of points for this level.
|
14 |
+
:param levelNumber: Number for this level.
|
15 |
+
:param careerPath: Career Path Special for this level.
|
16 |
+
"""
|
17 |
+
db_uri = MONGO_URI
|
18 |
+
db_name = "crayonics"
|
19 |
+
collection_name="Level"
|
20 |
+
client = MongoClient(db_uri)
|
21 |
+
db = client[db_name]
|
22 |
+
collection = db[collection_name]
|
23 |
+
should_proceed= (collection.find_one(document.model_dump()))
|
24 |
+
# Insert the document
|
25 |
+
if should_proceed==None:
|
26 |
+
if document!=None:
|
27 |
+
result = collection.insert_one(document.model_dump(exclude_none=True))
|
28 |
+
return True
|
29 |
+
else:
|
30 |
+
client.close()
|
31 |
+
return False
|
32 |
+
else:
|
33 |
+
# The exact document already exists so it can't be created again
|
34 |
+
return False
|
35 |
+
|
36 |
+
|
37 |
+
|
38 |
+
|
39 |
+
def get_all_levels_func() -> List[UserLevel]:
|
40 |
+
# MongoDB URI and configuration
|
41 |
+
db_uri = MONGO_URI
|
42 |
+
db_name = "crayonics"
|
43 |
+
collection_name="Level"
|
44 |
+
client = MongoClient(db_uri)
|
45 |
+
db = client[db_name]
|
46 |
+
collection = db[collection_name]
|
47 |
+
|
48 |
+
# Fetch all documents from the collection
|
49 |
+
levels_cursor = collection.find() # This returns a cursor to the documents
|
50 |
+
|
51 |
+
# Convert the cursor to a list of UserLevel objects
|
52 |
+
levels = [UserLevel(**level) for level in levels_cursor]
|
53 |
+
|
54 |
+
return levels
|
55 |
+
|
56 |
+
|
57 |
+
|
58 |
+
def delete_level_func(level_id,)->bool:
|
59 |
+
db_uri = MONGO_URI
|
60 |
+
db_name = "crayonics"
|
61 |
+
collection_name="Level"
|
62 |
+
client = MongoClient(db_uri)
|
63 |
+
db = client[db_name]
|
64 |
+
collection = db[collection_name]
|
65 |
+
if isinstance(level_id, str):
|
66 |
+
level_id = ObjectId(level_id)
|
67 |
+
result = collection.delete_one(filter={"_id":level_id})
|
68 |
+
if result.acknowledged==True:
|
69 |
+
if result.deleted_count ==0:
|
70 |
+
return False
|
71 |
+
else:
|
72 |
+
return True
|
73 |
+
else:
|
74 |
+
return False
|
75 |
+
|
76 |
+
|
77 |
+
|
78 |
+
|
79 |
+
def edit_level_func(level_id, **kwargs):
|
80 |
+
"""
|
81 |
+
Edit a UserLevel document in the MongoDB collection.
|
82 |
+
|
83 |
+
:param level_id: The ObjectId or unique identifier of the UserLevel to edit.
|
84 |
+
:param maxPoints: Maximum Amount of points for this level.
|
85 |
+
:param minPoints: Minimum Amount of points for this level.
|
86 |
+
:param levelNumber: Number for this level.
|
87 |
+
:param careerPath: Career Path Special for this level.
|
88 |
+
"""
|
89 |
+
db_uri = MONGO_URI
|
90 |
+
db_name = "crayonics"
|
91 |
+
collection_name="Level"
|
92 |
+
client = MongoClient(db_uri)
|
93 |
+
db = client[db_name]
|
94 |
+
collection = db[collection_name]
|
95 |
+
|
96 |
+
|
97 |
+
# Ensure that `level_id` is an ObjectId if using ObjectId as the identifier
|
98 |
+
if isinstance(level_id, str):
|
99 |
+
level_id = ObjectId(level_id)
|
100 |
+
|
101 |
+
# Prepare the update data
|
102 |
+
update_data = {key: value for key, value in kwargs.items() if value is not None}
|
103 |
+
|
104 |
+
|
105 |
+
# Perform the update operation in the collection
|
106 |
+
result = collection.update_one(
|
107 |
+
{"_id": level_id}, # Find the document by its unique identifier (ObjectId or other)
|
108 |
+
{"$set": update_data} # Update the document with the new values
|
109 |
+
)
|
110 |
+
|
111 |
+
return result.modified_count
|
gamification/logic.py
CHANGED
@@ -1,12 +1,9 @@
|
|
1 |
|
2 |
-
from gamification.objects import UserFeedback, UserLevel, UserPoints,CustomerInfo,Customer
|
3 |
-
from
|
4 |
-
from
|
5 |
-
import
|
6 |
-
|
7 |
-
from dotenv import load_dotenv
|
8 |
-
from datetime import datetime,timedelta
|
9 |
-
load_dotenv()
|
10 |
|
11 |
# Normal Math
|
12 |
def caculate_rate_change_func(c0,c1,days_ago):
|
@@ -15,13 +12,13 @@ def caculate_rate_change_func(c0,c1,days_ago):
|
|
15 |
# If there are customers now, but no customers initially (c0 is 0),
|
16 |
# we consider it as infinite growth or 100% growth
|
17 |
print("here")
|
18 |
-
return {"daysAgo":days_ago,"totalCustomers": c1, "GrowthRate":
|
19 |
elif c1 == 0:
|
20 |
# If both c0 and c1 are zero, there is no change
|
21 |
return {"daysAgo":days_ago,"totalCustomers": c1, "GrowthRate": 0.0, "GrowthRateType": "neutral"}
|
22 |
else:
|
23 |
# This case is for when c1 < 0, but it's unlikely in a customer count scenario.
|
24 |
-
return {"daysAgo":days_ago,"totalCustomers": c1, "GrowthRate": -
|
25 |
|
26 |
elif c1 > c0:
|
27 |
# Positive growth rate: c1 > c0
|
@@ -45,117 +42,7 @@ def caculate_rate_change_func(c0,c1,days_ago):
|
|
45 |
|
46 |
MONGO_URI = os.getenv("MONGO_URI")
|
47 |
|
48 |
-
|
49 |
-
# levels
|
50 |
-
def create_level_func(document:UserLevel,)->bool:
|
51 |
-
"""
|
52 |
-
Creates a UserLevel document in the MongoDB collection.
|
53 |
-
|
54 |
-
:param UserLevel: Actually accepts an Instance of UserLevel and deserializes it here
|
55 |
-
:param maxPoints: Maximum Amount of points for this level.
|
56 |
-
:param minPoints: Minimum Amount of points for this level.
|
57 |
-
:param levelNumber: Number for this level.
|
58 |
-
:param careerPath: Career Path Special for this level.
|
59 |
-
"""
|
60 |
-
db_uri = MONGO_URI
|
61 |
-
db_name = "crayonics"
|
62 |
-
collection_name="Level"
|
63 |
-
client = MongoClient(db_uri)
|
64 |
-
db = client[db_name]
|
65 |
-
collection = db[collection_name]
|
66 |
-
should_proceed= (collection.find_one(document.model_dump()))
|
67 |
-
# Insert the document
|
68 |
-
if should_proceed==None:
|
69 |
-
if document!=None:
|
70 |
-
result = collection.insert_one(document.model_dump())
|
71 |
-
return True
|
72 |
-
else:
|
73 |
-
client.close()
|
74 |
-
return False
|
75 |
-
else:
|
76 |
-
# The exact document already exists so it can't be created again
|
77 |
-
return False
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
def get_all_levels_func() -> List[UserLevel]:
|
83 |
-
# MongoDB URI and configuration
|
84 |
-
db_uri = MONGO_URI
|
85 |
-
db_name = "crayonics"
|
86 |
-
collection_name="Level"
|
87 |
-
client = MongoClient(db_uri)
|
88 |
-
db = client[db_name]
|
89 |
-
collection = db[collection_name]
|
90 |
-
|
91 |
-
# Fetch all documents from the collection
|
92 |
-
levels_cursor = collection.find() # This returns a cursor to the documents
|
93 |
-
|
94 |
-
# Convert the cursor to a list of UserLevel objects
|
95 |
-
levels = [UserLevel(**level) for level in levels_cursor]
|
96 |
-
|
97 |
-
return levels
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
def delete_level_func(level_id,)->bool:
|
102 |
-
db_uri = MONGO_URI
|
103 |
-
db_name = "crayonics"
|
104 |
-
collection_name="Level"
|
105 |
-
client = MongoClient(db_uri)
|
106 |
-
db = client[db_name]
|
107 |
-
collection = db[collection_name]
|
108 |
-
if isinstance(level_id, str):
|
109 |
-
level_id = ObjectId(level_id)
|
110 |
-
result = collection.delete_one(filter={"_id":level_id})
|
111 |
-
if result.acknowledged==True:
|
112 |
-
if result.deleted_count ==0:
|
113 |
-
return False
|
114 |
-
else:
|
115 |
-
return True
|
116 |
-
else:
|
117 |
-
return False
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
def edit_level_func(level_id, **kwargs):
|
123 |
-
"""
|
124 |
-
Edit a UserLevel document in the MongoDB collection.
|
125 |
-
|
126 |
-
:param level_id: The ObjectId or unique identifier of the UserLevel to edit.
|
127 |
-
:param maxPoints: Maximum Amount of points for this level.
|
128 |
-
:param minPoints: Minimum Amount of points for this level.
|
129 |
-
:param levelNumber: Number for this level.
|
130 |
-
:param careerPath: Career Path Special for this level.
|
131 |
-
"""
|
132 |
-
db_uri = MONGO_URI
|
133 |
-
db_name = "crayonics"
|
134 |
-
collection_name="Level"
|
135 |
-
client = MongoClient(db_uri)
|
136 |
-
db = client[db_name]
|
137 |
-
collection = db[collection_name]
|
138 |
-
|
139 |
-
|
140 |
-
# Ensure that `level_id` is an ObjectId if using ObjectId as the identifier
|
141 |
-
if isinstance(level_id, str):
|
142 |
-
level_id = ObjectId(level_id)
|
143 |
-
|
144 |
-
# Prepare the update data
|
145 |
-
update_data = {key: value for key, value in kwargs.items() if value is not None}
|
146 |
-
|
147 |
-
|
148 |
-
# Perform the update operation in the collection
|
149 |
-
result = collection.update_one(
|
150 |
-
{"_id": level_id}, # Find the document by its unique identifier (ObjectId or other)
|
151 |
-
{"$set": update_data} # Update the document with the new values
|
152 |
-
)
|
153 |
-
|
154 |
-
return result.modified_count
|
155 |
-
|
156 |
-
|
157 |
-
|
158 |
-
|
159 |
|
160 |
|
161 |
|
@@ -163,51 +50,6 @@ def edit_level_func(level_id, **kwargs):
|
|
163 |
|
164 |
# points
|
165 |
|
166 |
-
def create_points_func(document:UserPoints)->bool:
|
167 |
-
|
168 |
-
db_uri = MONGO_URI
|
169 |
-
db_name = "crayonics"
|
170 |
-
collection_name="Points"
|
171 |
-
client = MongoClient(db_uri)
|
172 |
-
db = client[db_name]
|
173 |
-
collection = db[collection_name]
|
174 |
-
# Insert the document
|
175 |
-
|
176 |
-
if document!=None:
|
177 |
-
result = collection.insert_one(document.model_dump())
|
178 |
-
return True
|
179 |
-
else:
|
180 |
-
client.close()
|
181 |
-
return False
|
182 |
-
|
183 |
-
|
184 |
-
|
185 |
-
def get_all_points_func() -> List[UserFeedback]:
|
186 |
-
# MongoDB URI and configuration
|
187 |
-
db_uri = MONGO_URI
|
188 |
-
db_name = "crayonics"
|
189 |
-
collection_name="Points"
|
190 |
-
client = MongoClient(db_uri)
|
191 |
-
db = client[db_name]
|
192 |
-
collection = db[collection_name]
|
193 |
-
|
194 |
-
# Fetch all documents from the collection
|
195 |
-
point_cursor = collection.find() # This returns a cursor to the documents
|
196 |
-
|
197 |
-
# Convert the cursor to a list of UserLevel objects
|
198 |
-
points = [UserFeedback(**point) for point in point_cursor]
|
199 |
-
|
200 |
-
return points
|
201 |
-
|
202 |
-
|
203 |
-
|
204 |
-
|
205 |
-
|
206 |
-
|
207 |
-
|
208 |
-
|
209 |
-
|
210 |
-
|
211 |
|
212 |
# feedback
|
213 |
def create_feedback_func(document:UserFeedback)->bool:
|
@@ -219,6 +61,8 @@ def create_feedback_func(document:UserFeedback)->bool:
|
|
219 |
collection = db[collection_name]
|
220 |
# Insert the document
|
221 |
if document!=None:
|
|
|
|
|
222 |
result = collection.insert_one(document.model_dump())
|
223 |
return True
|
224 |
else:
|
@@ -248,7 +92,6 @@ def get_all_feedback_func() -> List[UserFeedback]:
|
|
248 |
|
249 |
def get_all_customer_info()->List[CustomerInfo]:
|
250 |
db_uri=MONGO_URI
|
251 |
-
# db_uri = "mongodb+srv://groupcresearchseminar:[email protected]/?retryWrites=true&w=majority&appName=Cluster0"
|
252 |
db_name = "crayonics"
|
253 |
collection_name = "users"
|
254 |
client = MongoClient(db_uri)
|
@@ -283,3 +126,23 @@ def get_all_customer_info()->List[CustomerInfo]:
|
|
283 |
for rate in list_of_rates:
|
284 |
customer_info.append(CustomerInfo(**rate))
|
285 |
return customer_info
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
|
2 |
+
from gamification.objects import PlatformEngagement, UserFeedback, UserLevel, UserPoints,CustomerInfo,Customer,IndividualUserLevel,Points,SimpleIndividualUserLevel
|
3 |
+
from gamification.levelLogic import create_level_func,get_all_levels_func,edit_level_func,delete_level_func
|
4 |
+
from gamification.imports import *
|
5 |
+
from gamification.pointLogic import create_points_func,get_all_simple_points_func,get_all_points_func
|
6 |
+
|
|
|
|
|
|
|
7 |
|
8 |
# Normal Math
|
9 |
def caculate_rate_change_func(c0,c1,days_ago):
|
|
|
12 |
# If there are customers now, but no customers initially (c0 is 0),
|
13 |
# we consider it as infinite growth or 100% growth
|
14 |
print("here")
|
15 |
+
return {"daysAgo":days_ago,"totalCustomers": c1, "GrowthRate": 9999999999999999999999999999999999999999999999999999999, "GrowthRateType": "positive"}
|
16 |
elif c1 == 0:
|
17 |
# If both c0 and c1 are zero, there is no change
|
18 |
return {"daysAgo":days_ago,"totalCustomers": c1, "GrowthRate": 0.0, "GrowthRateType": "neutral"}
|
19 |
else:
|
20 |
# This case is for when c1 < 0, but it's unlikely in a customer count scenario.
|
21 |
+
return {"daysAgo":days_ago,"totalCustomers": c1, "GrowthRate": -999999999999999999999999999999999999999999999999999999, "GrowthRateType": "negative"}
|
22 |
|
23 |
elif c1 > c0:
|
24 |
# Positive growth rate: c1 > c0
|
|
|
42 |
|
43 |
MONGO_URI = os.getenv("MONGO_URI")
|
44 |
|
45 |
+
# Levels
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
46 |
|
47 |
|
48 |
|
|
|
50 |
|
51 |
# points
|
52 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
53 |
|
54 |
# feedback
|
55 |
def create_feedback_func(document:UserFeedback)->bool:
|
|
|
61 |
collection = db[collection_name]
|
62 |
# Insert the document
|
63 |
if document!=None:
|
64 |
+
feedbackPoints= Points(userId=document.userId,platformEngagement=PlatformEngagement(providing_feedback=15))
|
65 |
+
create_points_func(document=feedbackPoints)
|
66 |
result = collection.insert_one(document.model_dump())
|
67 |
return True
|
68 |
else:
|
|
|
92 |
|
93 |
def get_all_customer_info()->List[CustomerInfo]:
|
94 |
db_uri=MONGO_URI
|
|
|
95 |
db_name = "crayonics"
|
96 |
collection_name = "users"
|
97 |
client = MongoClient(db_uri)
|
|
|
126 |
for rate in list_of_rates:
|
127 |
customer_info.append(CustomerInfo(**rate))
|
128 |
return customer_info
|
129 |
+
|
130 |
+
|
131 |
+
|
132 |
+
|
133 |
+
|
134 |
+
# Leaderboard Logic
|
135 |
+
def get_top_30():
|
136 |
+
db_uri=MONGO_URI
|
137 |
+
db_name = "crayonics"
|
138 |
+
collection_name = "LeaderBoard"
|
139 |
+
client = MongoClient(db_uri)
|
140 |
+
db = client[db_name]
|
141 |
+
collection = db[collection_name]
|
142 |
+
sorted_documents = collection.find().sort([("totalpoints", -1), ("lastName", 1)]).limit(30)
|
143 |
+
rankers = [
|
144 |
+
{**r, 'rank': i + 1} # Add 'rank' to the document with i+1 (1-based index)
|
145 |
+
for i, r in enumerate(sorted_documents)
|
146 |
+
]
|
147 |
+
|
148 |
+
return rankers
|
gamification/objects.py
CHANGED
@@ -1,35 +1,217 @@
|
|
1 |
from datetime import datetime
|
2 |
-
from pydantic import BaseModel
|
|
|
3 |
from bson import ObjectId
|
|
|
4 |
class UserLevel(BaseModel):
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
|
|
|
|
|
|
11 |
class Config:
|
12 |
json_encoders = {
|
13 |
ObjectId: str
|
14 |
}
|
15 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
16 |
class UserPoints(BaseModel):
|
17 |
-
|
18 |
-
reason:str
|
19 |
userId:str
|
20 |
class Config:
|
21 |
json_encoders = {
|
22 |
ObjectId: str
|
23 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
24 |
class UserFeedback(BaseModel):
|
25 |
userId:str
|
26 |
feedback:str
|
27 |
rating:int
|
|
|
28 |
|
29 |
class Config:
|
30 |
json_encoders = {
|
31 |
ObjectId: str
|
32 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
33 |
|
34 |
class Customer(BaseModel):
|
35 |
email:str
|
@@ -40,6 +222,7 @@ class Customer(BaseModel):
|
|
40 |
json_encoders = {
|
41 |
ObjectId: str
|
42 |
}
|
|
|
43 |
|
44 |
|
45 |
|
@@ -54,12 +237,61 @@ class CustomerInfo(BaseModel):
|
|
54 |
}
|
55 |
|
56 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
57 |
class IndividualUserLevel(BaseModel):
|
58 |
totalpoints:int
|
59 |
-
|
60 |
-
|
|
|
|
|
|
|
61 |
class Config:
|
62 |
json_encoders = {
|
63 |
ObjectId: str
|
64 |
}
|
65 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
from datetime import datetime
|
2 |
+
from pydantic import model_validator, BaseModel
|
3 |
+
from typing import List, Optional, Union
|
4 |
from bson import ObjectId
|
5 |
+
|
6 |
class UserLevel(BaseModel):
|
7 |
+
_id: Optional[ObjectId]=None # Make sure _id can be Optional
|
8 |
+
id:Optional[str]=None
|
9 |
+
maxPoints: int
|
10 |
+
minPoints: int
|
11 |
+
levelName: str
|
12 |
+
careerPath: str
|
13 |
+
levelNumber: int
|
14 |
+
|
15 |
+
# To convert MongoDB ObjectId to string
|
16 |
class Config:
|
17 |
json_encoders = {
|
18 |
ObjectId: str
|
19 |
}
|
20 |
|
21 |
+
# Custom validator to handle the ObjectId conversion if needed
|
22 |
+
@model_validator(mode='before')
|
23 |
+
def handle_objectid(cls, values):
|
24 |
+
if '_id' in values and isinstance(values['_id'], ObjectId):
|
25 |
+
values['id'] = str(values['_id']) # Convert ObjectId to string
|
26 |
+
return values
|
27 |
+
|
28 |
+
|
29 |
+
|
30 |
class UserPoints(BaseModel):
|
31 |
+
pointsId:str
|
|
|
32 |
userId:str
|
33 |
class Config:
|
34 |
json_encoders = {
|
35 |
ObjectId: str
|
36 |
}
|
37 |
+
|
38 |
+
|
39 |
+
class LearningProgress(BaseModel):
|
40 |
+
course_completion: Optional[int] = None # Points for course completion
|
41 |
+
module_completion: Optional[int] = None # Points for module completion
|
42 |
+
daily_learning_streak: Optional[int] = None # Points for daily learning streak
|
43 |
+
quiz_assessment_completion: Optional[int] = None# Points for quiz/assessment completion
|
44 |
+
passing_score: Optional[int] = None # Points for passing assessments
|
45 |
+
class Config:
|
46 |
+
json_encoders = {
|
47 |
+
ObjectId: str
|
48 |
+
}
|
49 |
+
class SkillDevelopment(BaseModel):
|
50 |
+
skill_milestone_achievements: Optional[int] = None # Points for achieving milestones
|
51 |
+
cross_discipline_learning: Optional[int] = None # Points for completing courses in different tech categories
|
52 |
+
practical_project_submission: Optional[int] = None # Points for submitting a practical project
|
53 |
+
project_peer_review: Optional[int] = None # Points for reviewing a project
|
54 |
+
class Config:
|
55 |
+
json_encoders = {
|
56 |
+
ObjectId: str
|
57 |
+
}
|
58 |
+
class PlatformEngagement(BaseModel):
|
59 |
+
profile_completion: Optional[int] = None # Points for completing profile
|
60 |
+
connecting_learning_accounts: Optional[int] = None # Points for connecting learning accounts
|
61 |
+
daily_check_in: Optional[int] = None # Points for daily check-in
|
62 |
+
community_participation: Optional[int] = None # Points for community participation
|
63 |
+
providing_feedback: Optional[int] = None # Points for providing feedback
|
64 |
+
class Config:
|
65 |
+
json_encoders = {
|
66 |
+
ObjectId: str
|
67 |
+
}
|
68 |
+
|
69 |
+
|
70 |
+
class PointMultipliersWeekendWarrior(BaseModel):
|
71 |
+
weekendWarrior: float = 1.5
|
72 |
+
class Config:
|
73 |
+
json_encoders = {
|
74 |
+
ObjectId: str
|
75 |
+
}
|
76 |
+
|
77 |
+
class PointMultipliersFocusMode(BaseModel):
|
78 |
+
focusMode: float = 2
|
79 |
+
class Config:
|
80 |
+
json_encoders = {
|
81 |
+
ObjectId: str
|
82 |
+
}
|
83 |
+
|
84 |
+
class PointMultipliersSkillGapTargeting(BaseModel):
|
85 |
+
skillGapTargeting: float = 2
|
86 |
+
class Config:
|
87 |
+
json_encoders = {
|
88 |
+
ObjectId: str
|
89 |
+
}
|
90 |
+
|
91 |
+
class PointMultipliersTrendingTech(BaseModel):
|
92 |
+
trendingTech: float = 1.5
|
93 |
+
|
94 |
+
class Config:
|
95 |
+
json_encoders = {
|
96 |
+
ObjectId: str
|
97 |
+
}
|
98 |
+
|
99 |
+
|
100 |
+
|
101 |
+
|
102 |
+
class Points(BaseModel):
|
103 |
+
userId: str
|
104 |
+
numOfPoints: Optional[float] = None # Initially set to None
|
105 |
+
learningProgress: Optional[LearningProgress] = None
|
106 |
+
skillDevelopment: Optional[SkillDevelopment] = None
|
107 |
+
platformEngagement: Optional[PlatformEngagement] = None
|
108 |
+
weekendWarrior: Optional[PointMultipliersWeekendWarrior] = None
|
109 |
+
trendingTech: Optional[PointMultipliersTrendingTech]=None
|
110 |
+
skillGapTargeting:Optional[PointMultipliersSkillGapTargeting]=None
|
111 |
+
focusMode:Optional[PointMultipliersFocusMode]=None
|
112 |
+
earnedAt:Optional[datetime]=datetime.now()
|
113 |
+
|
114 |
+
@model_validator(mode='before')
|
115 |
+
def calculate_num_of_points(cls, values):
|
116 |
+
total_points = 0
|
117 |
+
|
118 |
+
# Add points from LearningProgress
|
119 |
+
learning_progress = values.get('learningProgress')
|
120 |
+
|
121 |
+
if learning_progress:
|
122 |
+
total_points += sum(filter(None, [
|
123 |
+
learning_progress.course_completion,
|
124 |
+
learning_progress.module_completion,
|
125 |
+
learning_progress.daily_learning_streak,
|
126 |
+
learning_progress.quiz_assessment_completion,
|
127 |
+
learning_progress.passing_score
|
128 |
+
]))
|
129 |
+
|
130 |
+
# Add points from SkillDevelopment
|
131 |
+
skill_development = values.get('skillDevelopment')
|
132 |
+
if skill_development:
|
133 |
+
total_points += sum(filter(None, [
|
134 |
+
skill_development.skill_milestone_achievements,
|
135 |
+
skill_development.cross_discipline_learning,
|
136 |
+
skill_development.practical_project_submission,
|
137 |
+
skill_development.project_peer_review
|
138 |
+
]))
|
139 |
+
|
140 |
+
# Add points from PlatformEngagement
|
141 |
+
platform_engagement = values.get('platformEngagement')
|
142 |
+
if platform_engagement:
|
143 |
+
total_points += sum(filter(None, [
|
144 |
+
platform_engagement.profile_completion,
|
145 |
+
platform_engagement.connecting_learning_accounts,
|
146 |
+
platform_engagement.daily_check_in,
|
147 |
+
platform_engagement.community_participation,
|
148 |
+
platform_engagement.providing_feedback
|
149 |
+
]))
|
150 |
+
|
151 |
+
# Apply multipliers if present
|
152 |
+
PointMultipliersWeekendWarrior = values.get('weekendWarrior')
|
153 |
+
if PointMultipliersWeekendWarrior:
|
154 |
+
total_points *= sum([
|
155 |
+
PointMultipliersWeekendWarrior.weekendWarrior
|
156 |
+
|
157 |
+
])
|
158 |
+
|
159 |
+
PointMultipliersTrendingTech = values.get('trendingTech')
|
160 |
+
if PointMultipliersTrendingTech:
|
161 |
+
total_points *= sum([
|
162 |
+
PointMultipliersTrendingTech.trendingTech
|
163 |
+
])
|
164 |
+
|
165 |
+
|
166 |
+
PointMultipliersSkillGapTargeting = values.get('skillGapTargeting')
|
167 |
+
if PointMultipliersSkillGapTargeting:
|
168 |
+
total_points *= sum([
|
169 |
+
PointMultipliersSkillGapTargeting.skillGapTargeting
|
170 |
+
])
|
171 |
+
|
172 |
+
|
173 |
+
|
174 |
+
PointMultipliersFocusMode = values.get('focusMode')
|
175 |
+
if PointMultipliersFocusMode:
|
176 |
+
total_points *= sum([
|
177 |
+
PointMultipliersFocusMode.focusMode,
|
178 |
+
])
|
179 |
+
|
180 |
+
|
181 |
+
values['numOfPoints'] = total_points # Store calculated points
|
182 |
+
|
183 |
+
return values
|
184 |
+
|
185 |
+
class Config:
|
186 |
+
json_encoders = {
|
187 |
+
ObjectId: str
|
188 |
+
}
|
189 |
+
|
190 |
+
|
191 |
+
|
192 |
+
|
193 |
class UserFeedback(BaseModel):
|
194 |
userId:str
|
195 |
feedback:str
|
196 |
rating:int
|
197 |
+
respondedAt:datetime
|
198 |
|
199 |
class Config:
|
200 |
json_encoders = {
|
201 |
ObjectId: str
|
202 |
}
|
203 |
+
|
204 |
+
|
205 |
+
|
206 |
+
class Feedback(BaseModel):
|
207 |
+
feedback:str
|
208 |
+
rating:int
|
209 |
+
|
210 |
+
class Config:
|
211 |
+
json_encoders = {
|
212 |
+
ObjectId: str
|
213 |
+
}
|
214 |
+
|
215 |
|
216 |
class Customer(BaseModel):
|
217 |
email:str
|
|
|
222 |
json_encoders = {
|
223 |
ObjectId: str
|
224 |
}
|
225 |
+
|
226 |
|
227 |
|
228 |
|
|
|
237 |
}
|
238 |
|
239 |
|
240 |
+
class individualPoints(BaseModel):
|
241 |
+
numOfPoints:int
|
242 |
+
earnedAt:datetime
|
243 |
+
platformEngagement:Optional[dict]=None
|
244 |
+
learningProgress:Optional[dict]=None
|
245 |
+
skillDevelopment:Optional[dict]=None
|
246 |
+
weekendWarrior:Optional[dict]=None
|
247 |
+
trendingTech:Optional[dict]=None
|
248 |
+
skillGapTargeting:Optional[dict]=None
|
249 |
+
focusMode:Optional[dict]=None
|
250 |
+
class Config:
|
251 |
+
json_encoders={
|
252 |
+
ObjectId:str
|
253 |
+
}
|
254 |
+
|
255 |
+
|
256 |
class IndividualUserLevel(BaseModel):
|
257 |
totalpoints:int
|
258 |
+
levelName:str
|
259 |
+
maxPoints:float
|
260 |
+
minPoints:float
|
261 |
+
individualPoints:List[individualPoints]
|
262 |
+
|
263 |
class Config:
|
264 |
json_encoders = {
|
265 |
ObjectId: str
|
266 |
}
|
267 |
+
|
268 |
+
|
269 |
+
class SimpleIndividualUserLevel(BaseModel):
|
270 |
+
totalpoints:int
|
271 |
+
levelName:str
|
272 |
+
maxPoints:float
|
273 |
+
minPoints:float
|
274 |
+
|
275 |
+
class Config:
|
276 |
+
json_encoders = {
|
277 |
+
ObjectId: str
|
278 |
+
}
|
279 |
+
|
280 |
+
|
281 |
+
class Ranker(BaseModel):
|
282 |
+
firstName:str
|
283 |
+
lastName:str
|
284 |
+
rank:int
|
285 |
+
|
286 |
+
totalPoints:Optional[float]=None
|
287 |
+
dreamJob:Optional[str]=None
|
288 |
+
class Config:
|
289 |
+
json_encoders = {
|
290 |
+
ObjectId:str
|
291 |
+
}
|
292 |
+
@model_validator(mode='before')
|
293 |
+
def changeValueNames(cls,values):
|
294 |
+
values['totalPoints']= values.get("totalpoints")
|
295 |
+
values['dreamJob']=values.get("careerPath")
|
296 |
+
return values
|
297 |
+
|
gamification/pointLogic.py
ADDED
@@ -0,0 +1,112 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
from gamification.objects import UserPoints ,SimpleIndividualUserLevel,IndividualUserLevel,UserLevel
|
3 |
+
from gamification.imports import *
|
4 |
+
from gamification.levelLogic import get_all_levels_func
|
5 |
+
|
6 |
+
# utils
|
7 |
+
def get_particular_level(totalPoints,dreamJob)->UserLevel:
|
8 |
+
# query db and get the results of all the level probably re use a function
|
9 |
+
levels = get_all_levels_func()
|
10 |
+
particularLevel= [level for level in levels if (level.maxPoints >= totalPoints) and (level.minPoints<=totalPoints)and (level.careerPath==dreamJob)]
|
11 |
+
defaulLevel= [level for level in levels if level.levelName=="default" ]
|
12 |
+
if len(particularLevel)>0:
|
13 |
+
return particularLevel
|
14 |
+
return defaulLevel
|
15 |
+
|
16 |
+
|
17 |
+
def get_dream_job(userId):
|
18 |
+
db_uri=MONGO_URI
|
19 |
+
db_name = "crayonics"
|
20 |
+
collection_name = "Questionaire"
|
21 |
+
client = MongoClient(db_uri)
|
22 |
+
db = client[db_name]
|
23 |
+
collection = db[collection_name]
|
24 |
+
questionaire_doc = collection.find_one({"userId": userId})
|
25 |
+
if questionaire_doc:
|
26 |
+
return questionaire_doc['dreamRole']
|
27 |
+
else: return False
|
28 |
+
|
29 |
+
|
30 |
+
def create_points_func(document:UserPoints)->bool:
|
31 |
+
|
32 |
+
db_uri = MONGO_URI
|
33 |
+
db_name = "crayonics"
|
34 |
+
collection_name="Points"
|
35 |
+
client = MongoClient(db_uri)
|
36 |
+
db = client[db_name]
|
37 |
+
collection = db[collection_name]
|
38 |
+
# Insert the document
|
39 |
+
|
40 |
+
if document!=None:
|
41 |
+
doc = document.model_dump()
|
42 |
+
doc['earnedAt']=datetime.now()
|
43 |
+
result = collection.insert_one(doc)
|
44 |
+
return True
|
45 |
+
else:
|
46 |
+
client.close()
|
47 |
+
return False
|
48 |
+
|
49 |
+
|
50 |
+
|
51 |
+
def get_all_points_func(userId) -> IndividualUserLevel:
|
52 |
+
# MongoDB URI and configuration
|
53 |
+
db_uri = MONGO_URI
|
54 |
+
db_name = "crayonics"
|
55 |
+
collection_name = "Points"
|
56 |
+
client = MongoClient(db_uri)
|
57 |
+
db = client[db_name]
|
58 |
+
collection = db[collection_name]
|
59 |
+
dreamJob = get_dream_job(userId=userId)
|
60 |
+
|
61 |
+
# Fetch all documents from the collection
|
62 |
+
point_cursor = collection.find({"userId": userId}) # This returns a cursor to the documents
|
63 |
+
|
64 |
+
# Convert the cursor to a list so we can reuse it
|
65 |
+
points_list = list(point_cursor)
|
66 |
+
|
67 |
+
# Calculate the total points
|
68 |
+
totalPoints = sum([point['numOfPoints'] for point in points_list])
|
69 |
+
particularLevelInfo = get_particular_level(dreamJob=dreamJob,totalPoints=totalPoints)
|
70 |
+
# Create the individual points list
|
71 |
+
individualPoints = [indpoint for indpoint in points_list]
|
72 |
+
|
73 |
+
print([pointss for pointss in individualPoints if all(val is not None for val in pointss.values())])
|
74 |
+
|
75 |
+
|
76 |
+
# Create the IndividualUserLevel object with totalPoints and individualPoints
|
77 |
+
points = IndividualUserLevel(totalpoints=totalPoints,levelName=particularLevelInfo[0].levelName,minPoints=particularLevelInfo[0].minPoints,maxPoints=particularLevelInfo[0].maxPoints, individualPoints=individualPoints)
|
78 |
+
|
79 |
+
return points
|
80 |
+
|
81 |
+
|
82 |
+
|
83 |
+
|
84 |
+
|
85 |
+
def get_all_simple_points_func(userId) -> SimpleIndividualUserLevel:
|
86 |
+
# MongoDB URI and configuration
|
87 |
+
db_uri = MONGO_URI
|
88 |
+
db_name = "crayonics"
|
89 |
+
collection_name = "Points"
|
90 |
+
client = MongoClient(db_uri)
|
91 |
+
db = client[db_name]
|
92 |
+
collection = db[collection_name]
|
93 |
+
dreamJob = get_dream_job(userId=userId)
|
94 |
+
|
95 |
+
# Fetch all documents from the collection
|
96 |
+
point_cursor = collection.find({"userId": userId}) # This returns a cursor to the documents
|
97 |
+
|
98 |
+
# Convert the cursor to a list so we can reuse it
|
99 |
+
points_list = list(point_cursor)
|
100 |
+
|
101 |
+
# Calculate the total points
|
102 |
+
totalPoints = sum([point['numOfPoints'] for point in points_list])
|
103 |
+
particularLevelInfo = get_particular_level(dreamJob=dreamJob,totalPoints=totalPoints)
|
104 |
+
# Create the individual points list
|
105 |
+
|
106 |
+
# Create the IndividualUserLevel object with totalPoints and individualPoints
|
107 |
+
points = SimpleIndividualUserLevel(totalpoints=totalPoints,levelName=particularLevelInfo[0].levelName,minPoints=particularLevelInfo[0].minPoints,maxPoints=particularLevelInfo[0].maxPoints)
|
108 |
+
|
109 |
+
return points
|
110 |
+
|
111 |
+
|
112 |
+
|
gamification/routes.py
CHANGED
@@ -1,9 +1,8 @@
|
|
1 |
-
from fastapi import FastAPI,HTTPException
|
2 |
from gamification.objects import *
|
3 |
from gamification.logic import *
|
4 |
-
from
|
5 |
-
from
|
6 |
-
from bson import ObjectId
|
7 |
|
8 |
class EditableUserLevel(BaseModel):
|
9 |
levelId : str
|
@@ -25,17 +24,64 @@ gamification = FastAPI()
|
|
25 |
|
26 |
|
27 |
@gamification.post("/create-feedback",tags=["user"])
|
28 |
-
def create_feedback(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
29 |
try:
|
|
|
30 |
result = create_feedback_func(user_feedback)
|
31 |
-
return
|
32 |
except Exception as e:
|
33 |
raise HTTPException(status_code=500,detail=f"{e}")
|
34 |
|
|
|
35 |
@gamification.post("/get-leaderboard",tags=["user"])
|
36 |
def get_leaderboard_details():
|
37 |
pass
|
38 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
39 |
@gamification.get("/get-customer-info",tags=["admin"])
|
40 |
def get_customer_information():
|
41 |
try:
|
@@ -55,7 +101,7 @@ def get_feedback()->List[UserFeedback]:
|
|
55 |
@gamification.post("/create-level",tags=["admin"])
|
56 |
def create_level(level_obj:UserLevel)->bool:
|
57 |
result = create_level_func(document=level_obj)
|
58 |
-
return
|
59 |
|
60 |
|
61 |
|
@@ -79,5 +125,17 @@ def delete_level(levelId)->bool:
|
|
79 |
try:
|
80 |
result = delete_level_func(level_id=levelId)
|
81 |
return {"message":result}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
82 |
except Exception as e:
|
83 |
raise HTTPException(status_code=500,detail=f"{e}")
|
|
|
1 |
+
from fastapi import FastAPI,HTTPException, Header
|
2 |
from gamification.objects import *
|
3 |
from gamification.logic import *
|
4 |
+
from jwtcoding import decode_jwt
|
5 |
+
from tokenManagement import verify_access_token
|
|
|
6 |
|
7 |
class EditableUserLevel(BaseModel):
|
8 |
levelId : str
|
|
|
24 |
|
25 |
|
26 |
@gamification.post("/create-feedback",tags=["user"])
|
27 |
+
def create_feedback(feedback:Feedback,authorization: str = Header(...))->bool:
|
28 |
+
|
29 |
+
token = authorization.split("Bearer ")[-1]
|
30 |
+
|
31 |
+
# Here, you would validate the token (e.g., check with a JWT library)
|
32 |
+
decoded_user_id,decoded_access_token = decode_jwt(token)
|
33 |
+
is_valid = verify_access_token(db_uri=MONGO_URI, user_id=decoded_user_id, access_token=decoded_access_token)
|
34 |
+
if is_valid != True: # Example check
|
35 |
+
raise HTTPException(status_code=401, detail="Invalid token")
|
36 |
+
|
37 |
try:
|
38 |
+
user_feedback = UserFeedback(userId=decoded_user_id,respondedAt=datetime.now(), feedback=feedback.feedback,rating=feedback.rating)
|
39 |
result = create_feedback_func(user_feedback)
|
40 |
+
return result
|
41 |
except Exception as e:
|
42 |
raise HTTPException(status_code=500,detail=f"{e}")
|
43 |
|
44 |
+
|
45 |
@gamification.post("/get-leaderboard",tags=["user"])
|
46 |
def get_leaderboard_details():
|
47 |
pass
|
48 |
|
49 |
+
@gamification.get("/get-advanced-user-points",tags=["user"])
|
50 |
+
def get_points(authorization: str = Header(...))->IndividualUserLevel:
|
51 |
+
# Extract the token from the Authorization header (Bearer token)
|
52 |
+
token = authorization.split("Bearer ")[-1]
|
53 |
+
|
54 |
+
# Here, you would validate the token (e.g., check with a JWT library)
|
55 |
+
decoded_user_id,decoded_access_token = decode_jwt(token)
|
56 |
+
is_valid = verify_access_token(db_uri=MONGO_URI, user_id=decoded_user_id, access_token=decoded_access_token)
|
57 |
+
|
58 |
+
if is_valid != True: # Example check
|
59 |
+
raise HTTPException(status_code=401, detail="Invalid token")
|
60 |
+
|
61 |
+
# do thing u want here
|
62 |
+
points = get_all_points_func(userId=decoded_user_id)
|
63 |
+
return points.model_dump(exclude_none=True)
|
64 |
+
|
65 |
+
|
66 |
+
|
67 |
+
@gamification.get("/get-simple-user-points",tags=["user"])
|
68 |
+
def get_points(authorization: str = Header(...))->SimpleIndividualUserLevel:
|
69 |
+
# Extract the token from the Authorization header (Bearer token)
|
70 |
+
token = authorization.split("Bearer ")[-1]
|
71 |
+
|
72 |
+
# Here, you would validate the token (e.g., check with a JWT library)
|
73 |
+
decoded_user_id,decoded_access_token = decode_jwt(token)
|
74 |
+
is_valid = verify_access_token(db_uri=MONGO_URI, user_id=decoded_user_id, access_token=decoded_access_token)
|
75 |
+
|
76 |
+
if is_valid != True: # Example check
|
77 |
+
raise HTTPException(status_code=401, detail="Invalid token")
|
78 |
+
|
79 |
+
# do thing u want here
|
80 |
+
points = get_all_simple_points_func(userId=decoded_user_id)
|
81 |
+
return points.model_dump(exclude_none=True)
|
82 |
+
|
83 |
+
|
84 |
+
|
85 |
@gamification.get("/get-customer-info",tags=["admin"])
|
86 |
def get_customer_information():
|
87 |
try:
|
|
|
101 |
@gamification.post("/create-level",tags=["admin"])
|
102 |
def create_level(level_obj:UserLevel)->bool:
|
103 |
result = create_level_func(document=level_obj)
|
104 |
+
return result
|
105 |
|
106 |
|
107 |
|
|
|
125 |
try:
|
126 |
result = delete_level_func(level_id=levelId)
|
127 |
return {"message":result}
|
128 |
+
except Exception as e:
|
129 |
+
raise HTTPException(status_code=500,detail=f"{e}")
|
130 |
+
|
131 |
+
|
132 |
+
@gamification.get("/get-top-30",tags=["user","admin"])
|
133 |
+
def get_leaderboard()->List[Ranker]:
|
134 |
+
try:
|
135 |
+
list_of_rankers = []
|
136 |
+
result = get_top_30()
|
137 |
+
list_of_rankers = [Ranker(**ranker) for ranker in result]
|
138 |
+
|
139 |
+
return list_of_rankers
|
140 |
except Exception as e:
|
141 |
raise HTTPException(status_code=500,detail=f"{e}")
|
s.py
ADDED
File without changes
|
streaksManagement.py
CHANGED
@@ -1,6 +1,9 @@
|
|
1 |
from pymongo import MongoClient
|
2 |
from datetime import datetime, timedelta
|
3 |
from typing import Dict, List, Union
|
|
|
|
|
|
|
4 |
|
5 |
def get_current_date() -> str:
|
6 |
# Get the current date and return it in YYYY-MM-DD format
|
@@ -37,12 +40,19 @@ def save_in_streaks_history(db_uri,document):
|
|
37 |
print("added new streak reset record")
|
38 |
document["streaks_records"] = [{"date_of_streak_reset":current_date,"streak_dates":document["streak_dates"]}]
|
39 |
document.pop("streak_dates")
|
|
|
|
|
40 |
collection.insert_one(document)
|
41 |
return True
|
42 |
else:
|
43 |
print("added another streak reset record")
|
44 |
current_record={"date_of_streaks_reset":current_date,"streak_dates":document["streak_dates"]}
|
45 |
-
|
|
|
|
|
|
|
|
|
|
|
46 |
dates = found_user["streaks_records"] + [current_record]
|
47 |
collection.update_one(
|
48 |
{"user_id": document.get("user_id")},
|
@@ -57,8 +67,6 @@ def save_in_streaks_history(db_uri,document):
|
|
57 |
# finally:
|
58 |
# client.close()
|
59 |
|
60 |
-
|
61 |
-
|
62 |
def streaks_manager(db_uri: str, document: Dict) -> Union[bool, str]:
|
63 |
"""
|
64 |
Manage user streaks in MongoDB.
|
@@ -93,9 +101,17 @@ def streaks_manager(db_uri: str, document: Dict) -> Union[bool, str]:
|
|
93 |
if is_a_streak:
|
94 |
print("its a streak guys")
|
95 |
# Extend existing streak
|
96 |
-
if not (current_date== found_user["streak_dates"][-1]):
|
97 |
dates = found_user["streak_dates"] + [current_date]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
98 |
else:
|
|
|
99 |
dates=found_user["streak_dates"]
|
100 |
collection.update_one(
|
101 |
{"user_id": document.get("user_id")},
|
|
|
1 |
from pymongo import MongoClient
|
2 |
from datetime import datetime, timedelta
|
3 |
from typing import Dict, List, Union
|
4 |
+
from gamification.objects import Points,PointMultipliersWeekendWarrior,PlatformEngagement
|
5 |
+
from gamification.logic import create_points_func
|
6 |
+
|
7 |
|
8 |
def get_current_date() -> str:
|
9 |
# Get the current date and return it in YYYY-MM-DD format
|
|
|
40 |
print("added new streak reset record")
|
41 |
document["streaks_records"] = [{"date_of_streak_reset":current_date,"streak_dates":document["streak_dates"]}]
|
42 |
document.pop("streak_dates")
|
43 |
+
streakPoints= Points(userId=document['user_id'],platformEngagement=PlatformEngagement(daily_check_in=5))
|
44 |
+
create_points_func(document=streakPoints)
|
45 |
collection.insert_one(document)
|
46 |
return True
|
47 |
else:
|
48 |
print("added another streak reset record")
|
49 |
current_record={"date_of_streaks_reset":current_date,"streak_dates":document["streak_dates"]}
|
50 |
+
today = datetime.date(datetime.now())
|
51 |
+
if (today.weekday()>=5):
|
52 |
+
streakPoints= Points(userId=document['user_id'],platformEngagement=PlatformEngagement(daily_check_in=5),weekendWarrior=PointMultipliersWeekendWarrior())
|
53 |
+
else:
|
54 |
+
streakPoints= Points(userId=document['user_id'],platformEngagement=PlatformEngagement(daily_check_in=5))
|
55 |
+
wasCreated= create_points_func(document=streakPoints)
|
56 |
dates = found_user["streaks_records"] + [current_record]
|
57 |
collection.update_one(
|
58 |
{"user_id": document.get("user_id")},
|
|
|
67 |
# finally:
|
68 |
# client.close()
|
69 |
|
|
|
|
|
70 |
def streaks_manager(db_uri: str, document: Dict) -> Union[bool, str]:
|
71 |
"""
|
72 |
Manage user streaks in MongoDB.
|
|
|
101 |
if is_a_streak:
|
102 |
print("its a streak guys")
|
103 |
# Extend existing streak
|
104 |
+
if not ((current_date)== (found_user["streak_dates"][-1])):
|
105 |
dates = found_user["streak_dates"] + [current_date]
|
106 |
+
print("an actual streak")
|
107 |
+
today = datetime.date(datetime.now())
|
108 |
+
if (today.weekday()>=5):
|
109 |
+
streakPoints= Points(userId=document['user_id'],platformEngagement=PlatformEngagement(daily_check_in=5),weekendWarrior=PointMultipliersWeekendWarrior())
|
110 |
+
else:
|
111 |
+
streakPoints= Points(userId=document['user_id'],platformEngagement=PlatformEngagement(daily_check_in=5))
|
112 |
+
create_points_func(document=streakPoints)
|
113 |
else:
|
114 |
+
|
115 |
dates=found_user["streak_dates"]
|
116 |
collection.update_one(
|
117 |
{"user_id": document.get("user_id")},
|
testing.py
ADDED
File without changes
|
utils.py
CHANGED
@@ -1,8 +1,13 @@
|
|
1 |
from bson import ObjectId
|
2 |
-
|
|
|
|
|
|
|
3 |
from datetime import datetime
|
4 |
import requests
|
5 |
from pymongo import MongoClient
|
|
|
|
|
6 |
from password import *
|
7 |
from streaksManagement import streaks_manager
|
8 |
|
@@ -176,6 +181,9 @@ def create_questionaire(db_uri: str, db_name: str, collection_name: str, documen
|
|
176 |
result= collection.find_one_and_replace(filter={"userId":document.get("userId")},replacement=document)
|
177 |
print(result)
|
178 |
if result==None:
|
|
|
|
|
|
|
179 |
result = collection.insert_one(document)
|
180 |
print(result)
|
181 |
return str(result.inserted_id)
|
@@ -230,10 +238,6 @@ def login_user(db_uri: str, db_name: str, collection_name: str, document: dict)
|
|
230 |
|
231 |
|
232 |
|
233 |
-
from pymongo import MongoClient
|
234 |
-
from bson.objectid import ObjectId
|
235 |
-
from typing import Dict, Optional
|
236 |
-
|
237 |
def user_details_func(db_uri: str, document: Dict) -> Optional[Dict]:
|
238 |
"""
|
239 |
Retrieve and process user details from MongoDB collections.
|
|
|
1 |
from bson import ObjectId
|
2 |
+
|
3 |
+
from pymongo import MongoClient
|
4 |
+
from typing import Dict, Optional
|
5 |
+
|
6 |
from datetime import datetime
|
7 |
import requests
|
8 |
from pymongo import MongoClient
|
9 |
+
from gamification.logic import create_points_func
|
10 |
+
from gamification.objects import PlatformEngagement, Points
|
11 |
from password import *
|
12 |
from streaksManagement import streaks_manager
|
13 |
|
|
|
181 |
result= collection.find_one_and_replace(filter={"userId":document.get("userId")},replacement=document)
|
182 |
print(result)
|
183 |
if result==None:
|
184 |
+
# give points for the completness of a profile
|
185 |
+
completProfilePoints= Points(userId=document.get('userId'),platformEngagement=PlatformEngagement(profile_completion=50))
|
186 |
+
wasCreated= create_points_func(document=completProfilePoints)
|
187 |
result = collection.insert_one(document)
|
188 |
print(result)
|
189 |
return str(result.inserted_id)
|
|
|
238 |
|
239 |
|
240 |
|
|
|
|
|
|
|
|
|
241 |
def user_details_func(db_uri: str, document: Dict) -> Optional[Dict]:
|
242 |
"""
|
243 |
Retrieve and process user details from MongoDB collections.
|