diff --git a/README.md b/README.md index e215382..ce585f1 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/app/__init__.py b/app/__init__.py index c6231e3..e393382 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -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 @@ -14,6 +15,7 @@ db = SQLAlchemy() login_manager = LoginManager() migrate = Migrate() +mail = Mail() klang_config = KlangConfig() grew_config = GrewConfig() @@ -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 diff --git a/app/config.py b/app/config.py index dc2ce4f..9556670 100644 --- a/app/config.py +++ b/app/config.py @@ -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" @@ -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( diff --git a/app/parser/controller.py b/app/parser/controller.py index 8c1bbb9..05ace89 100644 --- a/app/parser/controller.py +++ b/app/parser/controller.py @@ -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 @@ -19,7 +20,9 @@ def get(self): print(" 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") diff --git a/app/user/service.py b/app/user/service.py index d5197e4..52c92d5 100644 --- a/app/user/service.py +++ b/app/user/service.py @@ -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 @@ -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()] @@ -79,4 +86,18 @@ def change_super_admin(user: User, super_admin: bool): print(" : superadmin '{}' was {}".format(user.username, "ADDED" if super_admin else "REMOVED")) else: print(" : 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) + \ No newline at end of file diff --git a/app/utils/arborator_parser_utils.py b/app/utils/arborator_parser_utils.py index 12bdef5..964968c 100644 --- a/app/utils/arborator_parser_utils.py +++ b/app/utils/arborator_parser_utils.py @@ -3,6 +3,7 @@ import requests from app import parser_config +from app.user.service import EmailService class ModelInfo_t(TypedDict): @@ -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" connection timout with `url={url}`"} + error_message = f" 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" unknown error when connecting `url={url}` : {str(e)}", e) - return {"status": "failure", "error": f" unknown error when connecting `url={url}` : {str(e)}"} + error_message = f" 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 @@ -35,13 +40,17 @@ def send_post_request(url_suffix: str, data: Dict): "error": f" 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" connection timout with `url={url}`"} + error_message = f" 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" unknown error when connecting `url={url}` : {str(e)}", e) - return {"status": "failure", "error": f" unknown error when connecting `url={url}` : {str(e)}"} + error_message = f" 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): diff --git a/app/utils/grew_utils.py b/app/utils/grew_utils.py index 510685a..810efd7 100644 --- a/app/utils/grew_utils.py +++ b/app/utils/grew_utils.py @@ -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 @@ -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)) diff --git a/requirements.txt b/requirements.txt index b2bdf1f..3a2bd8c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -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