Skip to content

Commit 8c978f0

Browse files
committed
implement task notification system with scheduled emails for upcoming tasks
1 parent a1978ac commit 8c978f0

File tree

12 files changed

+179
-9
lines changed

12 files changed

+179
-9
lines changed

__pycache__/app.cpython-312.pyc

203 Bytes
Binary file not shown.

__pycache__/models.cpython-312.pyc

1.37 KB
Binary file not shown.
2.4 KB
Binary file not shown.

__pycache__/routes.cpython-312.pyc

1.93 KB
Binary file not shown.
727 Bytes
Binary file not shown.

app.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
from models import db, User
44
from werkzeug.security import generate_password_hash
55
from config import Config
6+
from notification import setup_mail
7+
from scheduler import init_scheduler
68

79
def create_app():
810
"""Application factory function"""
@@ -12,6 +14,8 @@ def create_app():
1214

1315
# Initialize extensions
1416
db.init_app(app)
17+
init_scheduler(app)
18+
setup_mail(app)
1519

1620
# Setup Flask-Login
1721
login_manager = LoginManager()
@@ -36,7 +40,8 @@ def load_user(user_id):
3640
admin_user = User(
3741
username='admin',
3842
password=generate_password_hash('admin_password'),
39-
role='admin'
43+
role='admin',
44+
email='yassine16kata@gmail.com',
4045
)
4146
db.session.add(admin_user)
4247
db.session.commit()

instance/calendar.db

4 KB
Binary file not shown.

models.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
from flask_login import UserMixin
22
from flask_sqlalchemy import SQLAlchemy
33
from datetime import datetime, timedelta
4+
from dateutil.relativedelta import relativedelta
5+
from datetime import datetime, timedelta
46

57
# Initialize SQLAlchemy instance
68
db = SQLAlchemy()
@@ -11,6 +13,7 @@ class User(UserMixin, db.Model):
1113
id = db.Column(db.Integer, primary_key=True)
1214
username = db.Column(db.String(80), unique=True, nullable=False)
1315
password = db.Column(db.String(200), nullable=False)
16+
email = db.Column(db.String(120), unique=True, nullable=False) # Add email
1417
role = db.Column(db.String(10), nullable=False)
1518

1619
class Task(db.Model):
@@ -25,6 +28,30 @@ class Task(db.Model):
2528
acteurs_externes = db.Column(db.String(200), nullable=True)
2629
last_completed = db.Column(db.DateTime, nullable=True)
2730

31+
def calculate_next_due_date(self):
32+
"""Calculate next due date based on periodicite"""
33+
amount, unit = self.periodicite.lower().split()
34+
amount = int(amount)
35+
36+
today = datetime.now()
37+
38+
if 'day' in unit:
39+
return today + timedelta(days=amount)
40+
elif 'week' in unit:
41+
return today + timedelta(weeks=amount)
42+
elif 'month' in unit:
43+
return today + relativedelta(months=amount)
44+
elif 'year' in unit or 'annual' in unit:
45+
return today + relativedelta(years=amount)
46+
47+
return today
48+
49+
def is_due_soon(self, days=7):
50+
"""Check if task is due within specified days"""
51+
if not self.echeance_prochaine:
52+
return False
53+
return datetime.now() <= self.echeance_prochaine <= datetime.now() + timedelta(days=days)
54+
2855
def complete_task(self):
2956
"""Mark task as complete and calculate next due date"""
3057
self.last_completed = datetime.now()

notification.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
from flask_mail import Mail, Message
2+
from models import User, Task
3+
from datetime import datetime, timedelta
4+
5+
mail = Mail()
6+
7+
def setup_mail(app):
8+
app.config['MAIL_SERVER'] = 'smtp.gmail.com'
9+
app.config['MAIL_PORT'] = 587
10+
app.config['MAIL_USE_TLS'] = True
11+
app.config['MAIL_USERNAME'] = 'zorguimohamedyassine@gmail.com'
12+
app.config['MAIL_PASSWORD'] = 'ngxj cwbe vkff nkkk'
13+
mail.init_app(app)
14+
15+
def send_task_notifications():
16+
"""Send notifications for tasks due in the next 7 days"""
17+
upcoming_tasks = Task.query.filter(
18+
Task.echeance_prochaine.between(
19+
datetime.now(),
20+
datetime.now() + timedelta(days=7)
21+
)
22+
).all()
23+
24+
if upcoming_tasks:
25+
users = User.query.all()
26+
for user in users:
27+
msg = Message(
28+
'Upcoming Tasks Notification',
29+
sender='zorguimohamedyassine@gmail.com',
30+
recipients=[user.email]
31+
)
32+
33+
task_list = "\n".join([
34+
f"- {task.action_programmee} (Due: {task.echeance_prochaine.strftime('%Y-%m-%d')})"
35+
for task in upcoming_tasks
36+
])
37+
38+
msg.body = f"""
39+
Hello {user.username},
40+
41+
The following tasks are due in the next 7 days:
42+
43+
{task_list}
44+
45+
Best regards,
46+
Task Management System
47+
"""
48+
49+
mail.send(msg)

routes.py

Lines changed: 51 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
from flask import render_template, redirect, url_for, flash
1+
from flask import render_template, redirect, url_for, flash,request
22
from flask_login import login_user, login_required, current_user
33
from werkzeug.security import check_password_hash
44
from models import User, Task, db
55
from forms import LoginForm, TaskForm
6-
from datetime import datetime
6+
from datetime import datetime, timedelta
7+
from sqlalchemy import or_
78

89
def register_routes(app):
910
"""Register all routes with the Flask application"""
@@ -46,11 +47,54 @@ def add_task():
4647
@app.route('/calendar')
4748
@login_required
4849
def tasks_calendar():
49-
tasks = Task.query.order_by(Task.echeance_prochaine).all()
50-
return render_template('tasks_calendar.html',
51-
tasks=tasks,
52-
today=datetime.now(),
53-
datetime=datetime)
50+
#filter parameters
51+
status = request.args.get('status', 'all')
52+
sort_by = request.args.get('sort', 'due_date')
53+
search = request.args.get('search', '')
54+
time_frame = request.args.get('time_frame', 'all')
55+
56+
#base query
57+
query = Task.query
58+
59+
#apply filters
60+
if status == 'completed':
61+
query = query.filter(Task.last_completed.isnot(None))
62+
elif status == 'pending':
63+
query = query.filter(Task.last_completed.is_(None))
64+
65+
if search:
66+
query = query.filter(or_(
67+
Task.volet.ilike(f'%{search}%'),
68+
Task.action_programmee.ilike(f'%{search}%'),
69+
Task.responsable.ilike(f'%{search}%'),
70+
))
71+
if time_frame == 'week':
72+
query.filter(Task.echeance_prochaine <= datetime.now() + timedelta(days=7))
73+
elif time_frame == 'month':
74+
query.filter(Task.echeance_prochaine <= datetime.now() + timedelta(days=30))
75+
76+
#apply sorting
77+
if sort_by == 'volet':
78+
query = query.order_by(Task.volet)
79+
elif sort_by == 'responsable':
80+
query = query.order_by(Task.responsable)
81+
else:
82+
query = query.order_by(Task.echeance_prochaine)
83+
84+
tasks = query.all()
85+
86+
return render_template(
87+
'tasks_calendar.html',
88+
tasks=tasks,
89+
today=datetime.now(),
90+
datetime=datetime,
91+
current_filters={
92+
'status': status,
93+
'sort_by': sort_by,
94+
'search': search,
95+
'time_frame': time_frame
96+
}
97+
)
5498

5599
@app.route('/complete-task/<int:task_id>', methods=['POST'])
56100
@login_required

0 commit comments

Comments
 (0)