Skip to content

Commit

Permalink
configure flask mail in order to send emails for superadmins when the…
Browse files Browse the repository at this point in the history
… grew and the parser servers are down
  • Loading branch information
khansadaoudi committed Nov 13, 2024
1 parent 4ac8d50 commit 796e5f6
Show file tree
Hide file tree
Showing 8 changed files with 61 additions and 11 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ python manage.py remove_super_admin --username $username
### Run the app for local development

In .flaskenv, set the `FLASK_ENV` to dev and `FLASK_APP` to wsgi.py
There is two other variables `MAIL_USERNAME` and `MAIL_PASSWORD` that you need to set with your email address and password in order to user `Flask-Mail` library
```
FLASK_ENV=dev
FLASK_APP=wsgi.py
Expand Down
3 changes: 3 additions & 0 deletions app/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from flask_login import LoginManager
from flask_caching import Cache
from flask_migrate import Migrate
from flask_mail import Mail

from app.klang.config import KlangConfig
from app.utils.grew_config import GrewConfig
Expand All @@ -14,6 +15,7 @@
db = SQLAlchemy()
login_manager = LoginManager()
migrate = Migrate()
mail = Mail()

klang_config = KlangConfig()
grew_config = GrewConfig()
Expand Down Expand Up @@ -43,6 +45,7 @@ def create_app(env=None):
migrate.init_app(app, db)
login_manager.init_app(app)
cache.init_app(app)
mail.init_app(app)

from .auth import auth as auth_blueprint

Expand Down
12 changes: 11 additions & 1 deletion app/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@
from typing import List, Type

from cryptography.hazmat.primitives.serialization import load_pem_private_key
from dotenv import load_dotenv

basedir = os.path.abspath(os.path.dirname(__file__))
load_dotenv(dotenv_path=".flaskenv", verbose=True)

UPLOAD_FOLDER = "app/tmp/data/"
CACHE_FOLDER = "app/tmp/cache/"
UPLOAD_IMAGE_FOLDER = "app/uploads/images"
Expand All @@ -27,7 +30,14 @@ class Config:
MAX_TOKENS = MAX_TOKENS
CACHE_TYPE = 'filesystem'
CACHE_DIR = CACHE_FOLDER

MAIL_SERVER = 'smtp.gmail.com'
MAIL_PORT = 465
MAIL_USERNAME = os.getenv("MAIL_USERNAME")
MAIL_PASSWORD = os.getenv("MAIL_PASSWORD")
MAIL_DEFAULT_SENDER = os.getenv("MAIL_USERNAME")
MAIL_USE_TLS = False
MAIL_USE_SSL = True

class DevelopmentConfig(Config):
CONFIG_NAME = "dev"
SECRET_KEY = os.getenv(
Expand Down
5 changes: 4 additions & 1 deletion app/parser/controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from app.config import Config
from ..samples.service import add_or_keep_timestamps, add_or_replace_userid
from ..projects.service import ProjectService, ProjectAccessService
from ..user.service import EmailService
from ..utils.arborator_parser_utils import ArboratorParserAPI, ModelInfo_t

api = Namespace("Parser", description="Endpoints for dealing with the parser") # noqa
Expand All @@ -19,7 +20,9 @@ def get(self):
print("<PARSER> list/start request")
response = ArboratorParserAPI.list()
if response['status'] == 'failure':
abort(503, 'Sorry, the parsing server is unreachable, please come back later')
error_message = 'Sorry, the parsing server is unreachable, please come back later'
EmailService.send_alert_email('Parsing server', error_message)
abort(503, error_message)
else:
pretrained_models = []
models = response.get("data")
Expand Down
25 changes: 23 additions & 2 deletions app/user/service.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
from app import db
from flask_mail import Message

from app import db, mail
from typing import List
from .model import User
from .interface import UserInterface
Expand All @@ -12,6 +14,11 @@ class UserService:
def get_all() -> List[User]:
return User.query.all()

@staticmethod
def get_super_admins_emails() -> List[str]:
super_admins = User.query.filter_by(super_admin=1)
return [super_admin.email for super_admin in super_admins]

@staticmethod
def get_all_emails() -> List[str]:
return [user.email for user in User.query.all()]
Expand Down Expand Up @@ -79,4 +86,18 @@ def change_super_admin(user: User, super_admin: bool):
print("<super_admin manager> : superadmin '{}' was {}".format(user.username, "ADDED" if super_admin else "REMOVED"))
else:
print("<super_admin manager> : no user found, operation cancelled")


class EmailService:

@staticmethod
def send_alert_email(title: str, alert: str):
super_admins_emails = UserService.get_super_admins_emails()

mail_message = Message(
title,
recipients=super_admins_emails
)

mail_message.body = alert
mail.send(mail_message)

23 changes: 16 additions & 7 deletions app/utils/arborator_parser_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import requests

from app import parser_config
from app.user.service import EmailService


class ModelInfo_t(TypedDict):
Expand All @@ -17,10 +18,14 @@ def send_get_request(url_suffix: str):
reply = requests.get(url, timeout=10)
return reply.json()
except requests.exceptions.ReadTimeout:
return {"status": "failure", "error": f"<ArboratorParserAPI> connection timout with `url={url}`"}
error_message = f"<ArboratorParserAPI> connection timout with `url={url}`"
EmailService.send_alert_email('Parser server error', error_message)
return {"status": "failure", "error": error_message }
except Exception as e:
print(f"<ArboratorParserAPI> unknown error when connecting `url={url}` : {str(e)}", e)
return {"status": "failure", "error": f"<ArboratorParserAPI> unknown error when connecting `url={url}` : {str(e)}"}
error_message = f"<ArboratorParserAPI> unknown error when connecting `url={url}` : {str(e)}"
EmailService.send_alert_email('Parser server error', error_message)
print(error_message)
return {"status": "failure", "error": error_message}


@staticmethod
Expand All @@ -35,13 +40,17 @@ def send_post_request(url_suffix: str, data: Dict):
"error": f"<ArboratorParserSchemaValidation> You have a problem with at least one of the sentence "
f"you sent : {json.dumps(data.get('schema_errors'))}",
"schema_errors": data.get("schema_errors"),
}
}
return data
except requests.exceptions.ReadTimeout:
return {"status": "failure", "error": f"<ArboratorParserAPI> connection timout with `url={url}`"}
error_message = f"<ArboratorParserAPI> connection timout with `url={url}`"
EmailService.send_alert_email('Parser server error', error_message)
return {"status": "failure", "error": error_message }
except Exception as e:
print(f"<ArboratorParserAPI> unknown error when connecting `url={url}` : {str(e)}", e)
return {"status": "failure", "error": f"<ArboratorParserAPI> unknown error when connecting `url={url}` : {str(e)}"}
error_message = f"<ArboratorParserAPI> unknown error when connecting `url={url}` : {str(e)}"
EmailService.send_alert_email('Parser server error', error_message)
print(error_message)
return {"status": "failure", "error": error_message}

@staticmethod
def send_delete_request(url_suffix: str):
Expand Down
2 changes: 2 additions & 0 deletions app/utils/grew_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from flask import abort
from flask_login import current_user
from app import grew_config
from app.user.service import EmailService

from conllup.conllup import sentenceConllToJson
from conllup.processing import constructTextFromTreeJson
Expand Down Expand Up @@ -42,6 +43,7 @@ def grew_request(fct_name, data={}, files={}):
return response
except Exception as e:
parsed_error_msg = BeautifulSoup(response.text, features="lxml").find('p').contents[0]
EmailService.send_alert_email('Grew server error', str(parsed_error_msg))
abort(500, str(parsed_error_msg))


Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ flask-accepts==0.17.4
Flask-Caching==2.3.0
Flask-Cors==4.0.2
Flask-Login==0.6.3
Flask-Mail==0.10.0
Flask-Migrate==2.5.3
flask-restx==1.3.0
Flask-Script==2.0.6
Expand Down

0 comments on commit 796e5f6

Please sign in to comment.