Skip to content

Commit e660262

Browse files
Send inactive user mail reminder (#546)
* Add functionality to query notebook apps and count them in the get apps endpoint * add the send inactive user mail reminder endpoint
1 parent 5350cb8 commit e660262

File tree

6 files changed

+367
-4
lines changed

6 files changed

+367
-4
lines changed

api_docs.yml

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -775,7 +775,36 @@ paths:
775775
description: "Bad request"
776776
500:
777777
description: "Internal Server Error"
778-
778+
"/users/inactive_user_reminder":
779+
post:
780+
tags:
781+
- inactive_user_reminder
782+
parameters:
783+
- in: header
784+
name: Authorization
785+
required: true
786+
type: string
787+
description: "Bearer [token]"
788+
- in: query
789+
name: value
790+
required: true
791+
type: integer
792+
description: "Numeric value for the time period"
793+
- in: query
794+
name: unit
795+
required: true
796+
type: string
797+
enum: [hours, days, months]
798+
description: "Unit of time (hours/days/months)"
799+
produces:
800+
- application/json
801+
responses:
802+
200:
803+
description: "Success"
804+
400:
805+
description: "Bad request - Invalid parameters"
806+
500:
807+
description: "Internal Server Error"
779808
"/clusters":
780809
post:
781810
tags:

app/controllers/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from .users import (
55
UsersView, UserLoginView, UserEmailVerificationView, UserFollowersView, UserFollowView,
66
EmailVerificationRequest, ForgotPasswordView, ResetPasswordView, UserDisableView, UserEnableView,
7-
UserDetailView, AdminLoginView, OAuthView, UserDataSummaryView, UserAdminUpdateView, InActiveUsersView)
7+
UserDetailView, AdminLoginView, OAuthView, UserDataSummaryView, UserAdminUpdateView, InActiveUsersView, SendInactiveUserMailReminder)
88
from .deployments import DeploymentsView
99
from .clusters import (
1010
ClustersView, ClusterDetailView, ClusterNamespacesView,

app/controllers/users.py

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@
33
import os
44
from types import SimpleNamespace
55
from app.helpers.activity_logger import log_activity
6+
from app.helpers.inactiveUser_notification import send_inactive_notification_to_user
67
from app.helpers.kube import disable_project, enable_project
78
from flask import current_app, render_template
8-
from flask_restful import Resource, request
9+
from flask_restful import Resource, request,reqparse
910
from flask_bcrypt import Bcrypt
1011
from app.schemas import UserSchema, UserGraphSchema, ActivityLogSchema
1112
from app.models.user import User
@@ -1334,3 +1335,74 @@ def get(self, user_id):
13341335
status='success',
13351336
data=dict(followers=json.loads(users_data))
13361337
), 200
1338+
1339+
1340+
1341+
class SendInactiveUserMailReminder(Resource):
1342+
@admin_required
1343+
def post(self):
1344+
1345+
parser = reqparse.RequestParser()
1346+
parser.add_argument('value', type=int, required=True,
1347+
help='Time period value is required')
1348+
parser.add_argument('unit', type=str, required=True,
1349+
choices=('hours', 'days', 'months'),
1350+
help='Time unit must be hours, days, or months')
1351+
1352+
args = parser.parse_args()
1353+
value = args['value']
1354+
unit = args['unit'].lower()
1355+
1356+
now = datetime.now()
1357+
if unit == 'hours':
1358+
lower_threshold = now - timedelta(hours=value)
1359+
upper_threshold = now - timedelta(hours=value-1)
1360+
elif unit == 'days':
1361+
lower_threshold = now - timedelta(days=value)
1362+
upper_threshold = now - timedelta(days=value-1)
1363+
else:
1364+
lower_threshold = now - timedelta(days=value * 30)
1365+
upper_threshold = now - timedelta(days=(value-1) * 30)
1366+
1367+
1368+
inactive_users = User.query.filter(
1369+
User.last_seen <= upper_threshold,
1370+
User.last_seen > lower_threshold,
1371+
1372+
User.verified == True,
1373+
User.disabled == False,
1374+
User.admin_disabled == False
1375+
).all()
1376+
1377+
already_notified = set()
1378+
1379+
emails_sent = 0
1380+
errors = []
1381+
1382+
for user in inactive_users:
1383+
try:
1384+
success = send_inactive_notification_to_user(
1385+
email=user.email,
1386+
name=user.name,
1387+
app=current_app._get_current_object(),
1388+
template="user/inactive_user_reminder.html",
1389+
subject="Checking In: Your Crane Cloud Account",
1390+
date=now.strftime("%m/%d/%Y"),
1391+
is_success_template=True
1392+
)
1393+
1394+
if success:
1395+
emails_sent += 1
1396+
already_notified.add(user.email)
1397+
else:
1398+
errors.append(f"Failed to send email to {user.email}")
1399+
1400+
except Exception as e:
1401+
errors.append(f"Error sending email to {user.email}: {str(e)}")
1402+
1403+
return dict(
1404+
status='success',
1405+
message=f'Successfully sent {emails_sent} reminder emails',
1406+
total_users_processed=len(inactive_users),
1407+
errors=errors if errors else None
1408+
), 200
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
from flask import render_template
2+
from .email import send_email
3+
from flask import Flask, request
4+
5+
6+
def send_inactive_notification_to_user(email, name, app, template, subject, date, is_success_template=None):
7+
try:
8+
html = render_template(template,
9+
name=name,
10+
success=is_success_template)
11+
12+
result = send_email(email, subject, html, email, app)
13+
if result is None:
14+
return True
15+
16+
return result
17+
18+
except Exception as e:
19+
print(f"Error in send_inactive_notification_to_user: {str(e)}")
20+
return False

app/routes/__init__.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
BillingInvoiceView, BillingInvoiceNotificationView, SystemSummaryView, CreditDetailView, ProjectUsersView, ProjectUsersTransferView, AppReviseView,
1616
ProjectUsersHandleInviteView, ClusterProjectsView, ProjectDisableView, ProjectEnableView, AppRedeployView, AppDisableView, AppEnableView,
1717
TagsView, TagsDetailView, TagFollowingView, GenericSearchView, MLProjectAppsView,
18-
UserDisableView, UserEnableView, AppDockerWebhookListenerView, UserFollowersView, UserFollowView, ProjectFollowingView, ActivityFeedView)
18+
UserDisableView, UserEnableView, AppDockerWebhookListenerView, UserFollowersView, UserFollowView, ProjectFollowingView, ActivityFeedView,SendInactiveUserMailReminder,)
1919
from app.controllers.app import AppRevisionsView
2020
from app.controllers.billing_invoice import BillingInvoiceDetailView
2121
from app.controllers.receipts import BillingReceiptsDetailView, BillingReceiptsView
@@ -193,3 +193,7 @@
193193

194194
# system status
195195
api.add_resource(SystemSummaryView, '/system_summary')
196+
197+
# Send inactive user mail reminder
198+
api.add_resource(SendInactiveUserMailReminder, '/users/inactive_user_reminder',
199+
endpoint='inactive_user_reminder')

0 commit comments

Comments
 (0)