Skip to content

Commit

Permalink
support for default notification rules
Browse files Browse the repository at this point in the history
Rules with NULL id_role are used if there are no rules for current user.
  • Loading branch information
bouttier committed Jan 13, 2023
1 parent 60ae414 commit 14a30ff
Show file tree
Hide file tree
Showing 7 changed files with 190 additions and 151 deletions.
38 changes: 37 additions & 1 deletion backend/geonature/core/notifications/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
"""
import datetime

import sqlalchemy as sa
from sqlalchemy import ForeignKey
from sqlalchemy.sql import select
from sqlalchemy.orm import relationship
from flask import g

from utils_flask_sqla.serializers import serializable
from pypnusershub.db.models import User
Expand Down Expand Up @@ -85,23 +87,57 @@ class Notification(db.Model):
user = db.relationship(User)


class NotificationRuleQuery(db.Query):
def filter_by_role_with_defaults(self, id_role=None):
if id_role is None:
id_role = g.current_user.id_role
cte = (
NotificationRule.query.filter(
sa.or_(
NotificationRule.id_role.is_(None),
NotificationRule.id_role == id_role,
)
)
.distinct(NotificationRule.code_category, NotificationRule.code_method)
.order_by(
NotificationRule.code_category.desc(),
NotificationRule.code_method.desc(),
NotificationRule.id_role.asc(),
)
.cte("cte")
)
return self.filter(NotificationRule.id == cte.c.id)


@serializable
class NotificationRule(db.Model):
__tablename__ = "t_notifications_rules"
__table_args__ = (
db.UniqueConstraint(
"id_role", "code_method", "code_category", name="un_role_method_category"
),
db.Index(
"un_method_category",
"code_method",
"code_category",
unique=True,
postgresql_ops={
"where": sa.text("id_role IS NULL"),
},
),
{"schema": "gn_notifications"},
)
query_class = NotificationRuleQuery

id = db.Column(db.Integer, primary_key=True)
id_role = db.Column(db.Integer, ForeignKey(User.id_role), nullable=False)
id_role = db.Column(db.Integer, ForeignKey(User.id_role), nullable=True)
code_method = db.Column(db.Unicode, ForeignKey(NotificationMethod.code), nullable=False)
code_category = db.Column(
db.Unicode,
ForeignKey(NotificationCategory.code),
nullable=False,
)
subscribed = db.Column(db.Boolean, nullable=False)

method = relationship(NotificationMethod)
category = relationship(NotificationCategory)
Expand Down
78 changes: 32 additions & 46 deletions backend/geonature/core/notifications/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,28 +80,20 @@ def update_notification(id_notification):
@routes.route("/rules", methods=["GET"])
@permissions.login_required
def list_notification_rules():
rules = (
NotificationRule.query.filter(NotificationRule.id_role == g.current_user.id_role)
.order_by(
NotificationRule.code_category.desc(),
NotificationRule.code_method.desc(),
)
.options(
joinedload("method"),
joinedload("category"),
)
rules = NotificationRule.query.filter_by_role_with_defaults().options(
joinedload("method"),
joinedload("category"),
)
result = [
rule.as_dict(
fields=[
"id",
"id_role",
"code_method",
"code_category",
"method.label",
"method.description",
"category.label",
"category.description",
"subscribed",
]
)
for rule in rules.all()
Expand All @@ -121,39 +113,45 @@ def delete_all_notifications():


# add rule for user
@routes.route("/rules", methods=["PUT"])
@routes.route(
"/rules/category/<code_category>/method/<code_method>/subscribe",
methods=["POST"],
defaults={"subscribe": True},
)
@routes.route(
"/rules/category/<code_category>/method/<code_method>/unsubscribe",
methods=["POST"],
defaults={"subscribe": False},
)
@permissions.login_required
def create_rule():

requestData = request.get_json()
if requestData is None:
raise BadRequest("Empty request data")

code_method = requestData.get("code_method")
if not code_method:
raise BadRequest("Missing method")
if not db.session.query(
NotificationMethod.query.filter_by(code=str(code_method)).exists()
).scalar():
raise BadRequest("Invalid method")

code_category = requestData.get("code_category")
if not code_category:
raise BadRequest("Missing category")
def update_rule(code_category, code_method, subscribe):
if not db.session.query(
NotificationCategory.query.filter_by(code=str(code_category)).exists()
).scalar():
raise BadRequest("Invalid category")
if not db.session.query(
NotificationMethod.query.filter_by(code=str(code_method)).exists()
).scalar():
raise BadRequest("Invalid method")

# Create new rule for current user
new_rule = NotificationRule(
rule = NotificationRule.query.filter_by(
id_role=g.current_user.id_role,
code_method=code_method,
code_category=code_category,
)
db.session.add(new_rule)
).one_or_none()
if rule:
rule.subscribed = subscribe
else:
rule = NotificationRule(
id_role=g.current_user.id_role,
code_method=code_method,
code_category=code_category,
subscribed=subscribe,
)
db.session.add(rule)
db.session.commit()
return jsonify(new_rule.as_dict())
return jsonify(rule.as_dict(fields=["code_method", "code_category", "subscribed"]))


# Delete all rules for current user
Expand All @@ -167,18 +165,6 @@ def delete_all_rules():
return jsonify(nbRulesDeleted)


# Delete a specific rule
@routes.route("/rules/<int:id>", methods=["DELETE"])
@permissions.login_required
def delete_rule(id):
rule = NotificationRule.query.get_or_404(id)
if rule.user != g.current_user:
raise Forbidden
db.session.delete(rule)
db.session.commit()
return "", 204


# Get all availabe method for notification
@routes.route("/methods", methods=["GET"])
@permissions.login_required
Expand Down
5 changes: 3 additions & 2 deletions backend/geonature/core/notifications/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from jinja2 import Template
from flask import current_app
import sqlalchemy as sa

from pypnusershub.db.models import User

Expand Down Expand Up @@ -40,9 +41,9 @@ def dispatch_notification(category, role, title=None, url=None, *, content=None,
# add role, title and url to rendering context
context = {"role": role, "title": title, "url": url, **context}

rules = NotificationRule.query.filter(
NotificationRule.id_role == role.id_role,
rules = NotificationRule.query.filter_by_role_with_defaults(role.id_role).filter(
NotificationRule.code_category == category.code,
NotificationRule.subscribed.is_(sa.true()),
)
for rule in rules.all():
if content:
Expand Down
Loading

0 comments on commit 14a30ff

Please sign in to comment.