-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
30. The user will be allowed to schedule check-ins, where the user sets aside a specific time to chat with the AI. While the user can chat with the chatbot at any time, check-ins allow users to keep track of their interactions in an organized manner. 31. The user can schedule a maximum of 5 check-ins a day. 32. The user can edit their check-in times at any time. 33. The user can edit check-in frequency from daily, weekly or monthly. 34. The user can opt in to be notified about their check-ins. 35. The system must inform the user that if they missed a check-in upon startup, and ask if they would like to hold their check-in at that moment. 36. If two check-ins overlap, or the user is already using the chatbot, the app should silently disregard the check-in and count it as completed. 40. System validate the proposed check-in time against other check-ins on the same day.
- Loading branch information
1 parent
6f8448c
commit 83b8556
Showing
6 changed files
with
226 additions
and
13 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
""" | ||
This model represents a check-in. | ||
""" | ||
|
||
from datetime import datetime | ||
from pydantic import BaseModel, Field, constr | ||
from enum import Enum | ||
|
||
class Frequency(str, Enum): | ||
DAILY = "daily" | ||
WEEKLY = "weekly" | ||
MONTHLY = "monthly" | ||
|
||
class CheckIn(BaseModel): | ||
user_id: str | ||
check_in_time: datetime | ||
frequency: Frequency | ||
status: str = "upcoming" # default status is upcoming | ||
last_conversation: str = "" # default empty string, updated later | ||
notify: bool = False # Default to False, updated based on user preference | ||
|
||
def save(self, db): | ||
# Convert model to dictionary and save to MongoDB | ||
document = self.dict() | ||
db.check_ins.insert_one(document) | ||
|
||
@staticmethod | ||
def count_user_check_ins(db, user_id, date): | ||
start_of_day = datetime.combine(date, datetime.min.time()) | ||
end_of_day = datetime.combine(date, datetime.max.time()) | ||
count = db.check_ins.count_documents({ | ||
'user_id': user_id, | ||
'check_in_time': {'$gte': start_of_day, '$lt': end_of_day} | ||
}) | ||
return count | ||
|
||
@staticmethod | ||
def validate_check_in_time(db, user_id, proposed_time): | ||
""" Validate the proposed check-in time against other check-ins on the same day. """ | ||
date = proposed_time.date() | ||
start_of_day = datetime.combine(date, datetime.min.time()) | ||
end_of_day = datetime.combine(date, datetime.max.time()) | ||
|
||
# Fetch all check-ins for that user on the proposed date | ||
existing_check_ins = db.check_ins.find({ | ||
'user_id': user_id, | ||
'check_in_time': {'$gte': start_of_day, '$lt': end_of_day} | ||
}) | ||
|
||
for check_in in existing_check_ins: | ||
# Assuming check-ins should not overlap within an hour window | ||
if abs((check_in['check_in_time'] - proposed_time).total_seconds()) < 3600: | ||
return False # Conflict found if within an hour of another check-in | ||
|
||
return True # No conflicts found |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
from flask import Blueprint, request, jsonify | ||
from datetime import datetime | ||
from pydantic import ValidationError | ||
from models.check_in import CheckIn, Frequency | ||
from dotenv import load_dotenv | ||
from services.azure_mongodb import MongoDBClient | ||
from bson import ObjectId | ||
from pymongo import ReturnDocument | ||
from bson.errors import InvalidId | ||
|
||
|
||
load_dotenv() | ||
|
||
db_client = MongoDBClient.get_client() | ||
db = db_client[MongoDBClient.get_db_name()] | ||
|
||
checkIn_routes = Blueprint("checkIN", __name__) | ||
|
||
|
||
@checkIn_routes.post('/checkIn/schedule') | ||
def schedule_check_in(): | ||
try: # Parse and validate the request data using Pydantic model | ||
data = request.get_json() | ||
check_in_time=datetime.fromisoformat(data['check_in_time']) | ||
check_in_date = datetime.fromisoformat(data['check_in_time']).date() | ||
|
||
if CheckIn.count_user_check_ins(db,data['user_id'], check_in_date) >= 5: | ||
return jsonify({'message': 'Limit of 5 check-ins per day exceeded'}), 403 | ||
|
||
if not CheckIn.validate_check_in_time(db, data['user_id'], check_in_time): | ||
return jsonify({'error': 'Check-in time conflicts with an existing schedule'}), 409 | ||
|
||
check_in = CheckIn( | ||
user_id=data['user_id'], | ||
check_in_time=check_in_time, | ||
frequency=Frequency(data['frequency']), | ||
notify=data.get('notify', False) | ||
) | ||
|
||
# Convert Pydantic model to dictionary for MongoDB | ||
check_in_dict = check_in.dict() | ||
check_in_dict['check_in_time'] = check_in.check_in_time # Ensure datetime is handled correctly | ||
|
||
# Insert the new check-in into MongoDB | ||
result = db.check_ins.insert_one(check_in_dict) | ||
return jsonify({'message': 'Check-in scheduled successfully', 'check_in_id': str(result.inserted_id)}), 201 | ||
|
||
except ValidationError as e: | ||
return jsonify({'error': 'Data validation error', 'details': str(e)}), 400 | ||
except Exception as e: | ||
return jsonify({'error': str(e)}), 500 | ||
|
||
@checkIn_routes.patch('/checkIn/update/<check_in_id>') | ||
def update_check_in(check_in_id): | ||
data = request.get_json() | ||
try: | ||
updated_data = {} | ||
if 'check_in_time' in data: | ||
new_check_in_time = datetime.fromisoformat(data['check_in_time']) | ||
updated_data['check_in_time'] = new_check_in_time | ||
|
||
if not CheckIn.validate_check_in_time(db, data['user_id'], new_check_in_time): | ||
return jsonify({'error': 'Check-in time conflicts with an existing schedule'}), 409 | ||
|
||
if 'frequency' in data: | ||
# Use the Frequency enum to ensure the frequency is valid | ||
try: | ||
updated_data['frequency'] = Frequency(data['frequency']).value | ||
except ValueError: | ||
return jsonify({'error': 'Invalid frequency value'}), 400 | ||
|
||
update_result = db.check_ins.find_one_and_update( | ||
{'_id': ObjectId(check_in_id)}, | ||
{'$set': updated_data}, | ||
return_document=ReturnDocument.AFTER | ||
) | ||
if update_result: | ||
return jsonify({'message': 'Check-in updated successfully'}), 200 | ||
else: | ||
return jsonify({'message': 'No check-in found with provided ID or no update needed'}), 404 | ||
|
||
except ValidationError as e: | ||
return jsonify({'error': 'Data validation error', 'details': str(e)}), 400 | ||
except InvalidId: | ||
return jsonify({'error': 'Invalid check-in ID'}), 400 | ||
except Exception as e: | ||
return jsonify({'error': str(e)}), 500 | ||
|
||
@checkIn_routes.get('/checkIn/missed') | ||
def check_missed_check_ins(): | ||
user_id = request.args.get('user_id') | ||
now = datetime.now() | ||
missed_check_ins = db.check_ins.find({ | ||
'user_name': user_id, | ||
'check_in_time': {'$lt': now}, | ||
'status': 'upcoming' # Assuming 'upcoming' means not yet checked in | ||
}) | ||
|
||
if missed_check_ins.count() > 0: | ||
db.check_ins.update_many( | ||
{'user_id': user_id, 'check_in_time': {'$lt': now}, 'status': 'upcoming'}, | ||
{'$set': {'status': 'missed'}} | ||
) | ||
return jsonify({'message': 'You have missed check-ins, would you like to complete them now?', 'missed': list(missed_check_ins)}), 200 | ||
else: | ||
return jsonify({'message': 'No missed check-ins'}), 200 | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
from apscheduler.schedulers.background import BackgroundScheduler | ||
from flask import current_app as app | ||
from datetime import datetime, timedelta | ||
from services.azure_mongodb import MongoDBClient | ||
import pymongo | ||
|
||
def notify_check_ins(): | ||
db_client = MongoDBClient.get_client() | ||
db = db_client[MongoDBClient.get_db_name()] | ||
now = datetime.now() | ||
upcoming_check_ins = db.check_ins.find({ | ||
'check_in_time': {'$gte': now, '$lt': now + timedelta(days=7)}, | ||
'notify': True, | ||
'status': 'upcoming' | ||
}) | ||
|
||
for check_in in upcoming_check_ins: | ||
delta = check_in['check_in_time'] - now | ||
if delta.days == 7 or delta.days == 1 or delta.total_seconds() / 3600 <= 1: | ||
send_notification(check_in['user_id'], check_in['check_in_time'], delta) | ||
|
||
def send_notification(user_id, check_in_time, delta): | ||
message = "" | ||
if delta.days == 7: | ||
message = "Your check-in is scheduled in 1 week." | ||
elif delta.days == 1: | ||
message = "Your check-in is scheduled tomorrow." | ||
elif delta.total_seconds() / 3600 <= 1: | ||
message = "Your check-in is in less than 1 hour." | ||
|
||
# This is where you'd integrate your actual notification logic | ||
print(f"Notify {user_id}: {message}") | ||
|
||
scheduler = BackgroundScheduler() | ||
scheduler.add_job(func=notify_check_ins, trigger='interval', hours=1) | ||
scheduler.start() | ||
|
||
def init_scheduler(app): | ||
app.config['scheduler'] = scheduler |