mirror of
https://github.com/pds-nest/nest.git
synced 2024-11-22 04:54:18 +00:00
New alert conditions
This commit is contained in:
parent
378414a624
commit
5607b74e87
8 changed files with 70 additions and 130 deletions
|
@ -93,6 +93,9 @@ class CreateAlert(Schema):
|
||||||
name = fields.String(description="The name of the alert.")
|
name = fields.String(description="The name of the alert.")
|
||||||
limit = fields.Integer(description="The number of tweets in a time window.")
|
limit = fields.Integer(description="The number of tweets in a time window.")
|
||||||
window_size = fields.Integer(description="The size of the time window.")
|
window_size = fields.Integer(description="The size of the time window.")
|
||||||
|
repository_id = fields.Integer(description="The id of the related repository.")
|
||||||
|
evaluation_mode = fields.Integer(description="How the conditions have to be evaluated.")
|
||||||
|
conditions = fields.Nested(ConditionSchema, many=True)
|
||||||
|
|
||||||
|
|
||||||
class Operations(Schema):
|
class Operations(Schema):
|
||||||
|
@ -117,8 +120,8 @@ class Alert(Schema):
|
||||||
limit = fields.Integer(description="The number of tweets in a time window.")
|
limit = fields.Integer(description="The number of tweets in a time window.")
|
||||||
window_size = fields.Integer(description="The size of the time window.")
|
window_size = fields.Integer(description="The size of the time window.")
|
||||||
repository_id = fields.Integer(description="The id of the related repository.")
|
repository_id = fields.Integer(description="The id of the related repository.")
|
||||||
operations = fields.Nested(Operations, many=True)
|
evaluation_mode = fields.Integer(description="How the conditions have to be evaluated.")
|
||||||
root_operation = fields.Nested(Operations, many=False)
|
conditions = fields.Nested(ConditionSchema, many=True)
|
||||||
notifications = fields.Nested(Notification, many=True)
|
notifications = fields.Nested(Notification, many=True)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ This module defines the Alert database class.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from ..base import ext
|
from ..base import ext
|
||||||
|
from .Enums import ConditionMode
|
||||||
|
|
||||||
|
|
||||||
class Alert(ext.Model):
|
class Alert(ext.Model):
|
||||||
|
@ -11,12 +12,13 @@ class Alert(ext.Model):
|
||||||
name = ext.Column(ext.String, nullable=False)
|
name = ext.Column(ext.String, nullable=False)
|
||||||
limit = ext.Column(ext.Integer, nullable=False)
|
limit = ext.Column(ext.Integer, nullable=False)
|
||||||
window_size = ext.Column(ext.Integer, nullable=False)
|
window_size = ext.Column(ext.Integer, nullable=False)
|
||||||
|
evaluation_mode = ext.Column(ext.Enum(ConditionMode), nullable=False, default=ConditionMode.all_or)
|
||||||
# Foreign Keys
|
# Foreign Keys
|
||||||
repository_id = ext.Column(ext.Integer, ext.ForeignKey("repository.id", ondelete="CASCADE"), nullable=False)
|
repository_id = ext.Column(ext.Integer, ext.ForeignKey("repository.id", ondelete="CASCADE"), nullable=False)
|
||||||
# Relationships
|
# Relationships
|
||||||
repository = ext.relationship("Repository", back_populates="alerts")
|
repository = ext.relationship("Repository", back_populates="alerts")
|
||||||
notifications = ext.relationship("Notification", back_populates="alert")
|
notifications = ext.relationship("Notification", back_populates="alert")
|
||||||
operations = ext.relationship("BoolOperation", back_populates="alert")
|
conditions = ext.relationship("MadeOf", back_populates="alert")
|
||||||
|
|
||||||
def to_json(self):
|
def to_json(self):
|
||||||
return {
|
return {
|
||||||
|
@ -25,8 +27,7 @@ class Alert(ext.Model):
|
||||||
'window_size': self.window_size,
|
'window_size': self.window_size,
|
||||||
'limit': self.limit,
|
'limit': self.limit,
|
||||||
'repository_id': self.repository_id,
|
'repository_id': self.repository_id,
|
||||||
|
'evaluation_mode': self.evaluation_mode.value,
|
||||||
'notifications': [notification.to_json() for notification in self.notifications],
|
'notifications': [notification.to_json() for notification in self.notifications],
|
||||||
'operations': [operation.to_json() for operation in self.operations],
|
'conditions': [c.condition.to_json() for c in self.conditions]
|
||||||
'root_operation': [operation.to_json() for operation in self.operations if operation.is_root == True][
|
|
||||||
0] if self.operations else None
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,45 +0,0 @@
|
||||||
"""
|
|
||||||
This module defines the BoolOperation database class.
|
|
||||||
"""
|
|
||||||
|
|
||||||
from ..base import ext
|
|
||||||
from .Enums import OperationType
|
|
||||||
from sqlalchemy.orm import backref
|
|
||||||
|
|
||||||
|
|
||||||
class BoolOperation(ext.Model):
|
|
||||||
__tablename__ = "bool_operation"
|
|
||||||
|
|
||||||
id = ext.Column(ext.Integer, primary_key=True)
|
|
||||||
operation = ext.Column(ext.Enum(OperationType), nullable=False)
|
|
||||||
is_root = ext.Column(ext.Boolean, default=False, nullable=False)
|
|
||||||
# Foreign Keys
|
|
||||||
condition_id = ext.Column(ext.Integer, ext.ForeignKey("condition.id"))
|
|
||||||
node_1_id = ext.Column(ext.Integer, ext.ForeignKey("bool_operation.id", ondelete="SET NULL"))
|
|
||||||
node_2_id = ext.Column(ext.Integer, ext.ForeignKey("bool_operation.id", ondelete="SET NULL"))
|
|
||||||
alert_id = ext.Column(ext.Integer, ext.ForeignKey("alert.id", ondelete="CASCADE"))
|
|
||||||
# Relationships
|
|
||||||
condition = ext.relationship("Condition", back_populates="operations")
|
|
||||||
node_1 = ext.relationship("BoolOperation", primaryjoin=("bool_operation.c.node_1_id==bool_operation.c.id"),
|
|
||||||
remote_side="BoolOperation.id", backref=backref("father_1", uselist=False))
|
|
||||||
node_2 = ext.relationship("BoolOperation", primaryjoin=("bool_operation.c.node_2_id==bool_operation.c.id"),
|
|
||||||
remote_side="BoolOperation.id", backref=backref("father_2", uselist=False))
|
|
||||||
alert = ext.relationship("Alert", back_populates="operations")
|
|
||||||
|
|
||||||
def to_json(self):
|
|
||||||
return {"id": self.id,
|
|
||||||
"operation": self.operation,
|
|
||||||
"is_root": self.is_root,
|
|
||||||
"alert_id": self.alert_id,
|
|
||||||
"condition": self.condition.to_json() if self.condition else None,
|
|
||||||
"node_1": self.node_1.to_json() if self.node_1 else None,
|
|
||||||
"node_2": self.node_2.to_json() if self.node_2 else None
|
|
||||||
}
|
|
||||||
|
|
||||||
def get_chain_ids(self, lista):
|
|
||||||
if self.id in lista:
|
|
||||||
# Loop detected!
|
|
||||||
return -1
|
|
||||||
lista.append(self.id)
|
|
||||||
self.node_1.get_chain_ids(lista)
|
|
||||||
self.node_1.get_chain_ids(lista)
|
|
|
@ -16,7 +16,7 @@ class Condition(ext.Model):
|
||||||
# Relationships
|
# Relationships
|
||||||
repository = ext.relationship("Repository", back_populates="conditions")
|
repository = ext.relationship("Repository", back_populates="conditions")
|
||||||
tweets = ext.relationship("Contains", back_populates="condition")
|
tweets = ext.relationship("Contains", back_populates="condition")
|
||||||
operations = ext.relationship("BoolOperation", back_populates="condition")
|
alerts = ext.relationship("MadeOf", back_populates="condition")
|
||||||
|
|
||||||
def to_json(self):
|
def to_json(self):
|
||||||
return {
|
return {
|
||||||
|
|
14
nest_backend/database/tables/MadeOf.py
Normal file
14
nest_backend/database/tables/MadeOf.py
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
"""
|
||||||
|
This module defines the MadeOf database class.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from ..base import ext
|
||||||
|
|
||||||
|
|
||||||
|
class MadeOf(ext.Model):
|
||||||
|
__tablename__ = "made_of"
|
||||||
|
aid = ext.Column(ext.Integer, ext.ForeignKey("alert.id", ondelete="CASCADE"), primary_key=True)
|
||||||
|
cid = ext.Column(ext.Integer, ext.ForeignKey("condition.id"), primary_key=True)
|
||||||
|
# Relationships
|
||||||
|
alert = ext.relationship("Alert", back_populates="conditions")
|
||||||
|
condition = ext.relationship("Condition", back_populates="alerts")
|
|
@ -4,7 +4,6 @@ This module contains all database classes.
|
||||||
|
|
||||||
from .Alert import Alert
|
from .Alert import Alert
|
||||||
from .Authorization import Authorization
|
from .Authorization import Authorization
|
||||||
from .BoolOperation import BoolOperation
|
|
||||||
from .Composed import Composed
|
from .Composed import Composed
|
||||||
from .Condition import Condition
|
from .Condition import Condition
|
||||||
from .Contains import Contains
|
from .Contains import Contains
|
||||||
|
@ -12,4 +11,5 @@ from .Notification import Notification
|
||||||
from .Repository import Repository
|
from .Repository import Repository
|
||||||
from .Tweet import Tweet
|
from .Tweet import Tweet
|
||||||
from .User import User
|
from .User import User
|
||||||
|
from .MadeOf import MadeOf
|
||||||
from .Enums import ConditionType, OperationType, ConditionMode
|
from .Enums import ConditionType, OperationType, ConditionMode
|
|
@ -145,80 +145,30 @@ def page_alert(aid):
|
||||||
alert.limit = request.json['limit']
|
alert.limit = request.json['limit']
|
||||||
alert.name = request.json['name']
|
alert.name = request.json['name']
|
||||||
alert.window_size = request.json['window_size']
|
alert.window_size = request.json['window_size']
|
||||||
root_id = alert.to_json()['root_operation']['id']
|
if (mode := request.json.get("evaluation_mode")) is not None:
|
||||||
root = BoolOperation.filter_by(id=root_id).first()
|
try:
|
||||||
if not root:
|
alert.evaluation_mode = ConditionMode(mode)
|
||||||
return json_error("Could not find original root element."), 404
|
except KeyError:
|
||||||
# No longer used chain element deletion
|
return json_error("Unknown `type` specified."), 400
|
||||||
l = []
|
except Exception as e:
|
||||||
bool_list = root.get_chains_ids(l)
|
return json_error("Unknown error:" + str(e)), 400
|
||||||
for element in alert.operations:
|
if request.json['conditions'] is not None:
|
||||||
if element.id not in bool_list:
|
# Possibile vulnearabilità! Un utente potrebbe aggiungere conditions non del suo repo!
|
||||||
if element.id == root.id:
|
for c in request.json['conditions']:
|
||||||
root = None
|
if c['id'] not in alert.repository.conditions:
|
||||||
ext.session.delete(element)
|
return json_error("Stop! You violated the law!"), 403
|
||||||
|
# Wow very pythonic so much wow
|
||||||
|
# Obtain list of no longer needed connections
|
||||||
|
to_be_deleted = [c.cid for c in alert.conditions if
|
||||||
|
c.cid not in [json['id'] for json in request.json['conditions']]]
|
||||||
|
# RIP AND TEAR UNTIL ITS DONE
|
||||||
|
for elem in to_be_deleted:
|
||||||
|
conn = MadeOf.query.filter_by(cid=elem, aid=alert.id).first()
|
||||||
|
if conn:
|
||||||
|
ext.session.delete(conn)
|
||||||
|
ext.session.commit()
|
||||||
|
for c in request.json['conditions']:
|
||||||
|
conn = MadeOf(cid=c['id'], aid=alert.id)
|
||||||
|
ext.session.add(conn)
|
||||||
ext.session.commit()
|
ext.session.commit()
|
||||||
if request.json['root-operation'].get('id'): # If an alternative root is already present.
|
|
||||||
new_root_test = BoolOperation.filter_by(id=request.json['root_operation']['id']).first()
|
|
||||||
if new_root_test and new_root_test != root:
|
|
||||||
new_root_test.is_root = True
|
|
||||||
ext.session.commit()
|
|
||||||
else: # If the alternative root needs to be brand-new.
|
|
||||||
condition_id = None
|
|
||||||
if request.json['root_operation'].get('condition'): # Is the new alternative connected to a condition?
|
|
||||||
if not Condition.query.filter_by(id=request.json['root_operation']['condition']['id'],
|
|
||||||
repository_id=alert.repository_id).first():
|
|
||||||
return json_error("One of the provided IDs is incorrect."), 404
|
|
||||||
condition_id = request.json['root_operation']['condition']['id']
|
|
||||||
if (type_ := request.json['root-operation']['operation']) is not None:
|
|
||||||
try:
|
|
||||||
type_ = OperationType(type_)
|
|
||||||
except KeyError:
|
|
||||||
return json_error("Unknown `operation` specified."), 400
|
|
||||||
root = BoolOperation(operation=type_, is_root=True, alert_id=aid, condition_id=condition_id)
|
|
||||||
ext.session.add(root)
|
|
||||||
ext.session.commit()
|
|
||||||
root = BoolOperation.filter_by(id=request.json['root_operation']['id']).first()
|
|
||||||
try:
|
|
||||||
recursion(root, ext, request.json['root_operation'])
|
|
||||||
except FileNotFoundError:
|
|
||||||
return json_error("One of the provided IDs is incorrect"), 404
|
|
||||||
except KeyError:
|
|
||||||
return json_error("Unknown field specified."), 400
|
|
||||||
return json_success(alert.to_json()), 200
|
return json_success(alert.to_json()), 200
|
||||||
|
|
||||||
|
|
||||||
def create_node(node, ext, json, id):
|
|
||||||
# Check if the node already exists
|
|
||||||
id_1 = json[f'node_{id}']['id']
|
|
||||||
if id_1:
|
|
||||||
node_1 = BoolOperation.query.filter_by(id=id_1).first()
|
|
||||||
if not node_1:
|
|
||||||
raise FileNotFoundError
|
|
||||||
else:
|
|
||||||
# The node is new.
|
|
||||||
condition_id = None
|
|
||||||
if json[f'node_{id}'].get('condition'):
|
|
||||||
condition = Condition.query.filter_by(
|
|
||||||
id=json[f'node_{id}']['condition']['id']).filter(
|
|
||||||
Condition.repository_id == node.alert.repository_id).first()
|
|
||||||
if not condition:
|
|
||||||
raise FileNotFoundError
|
|
||||||
condition_id = condition.id
|
|
||||||
if (type_ := json[f'node_{id}']['operation']) is not None:
|
|
||||||
type_ = OperationType(type_)
|
|
||||||
else:
|
|
||||||
raise FileNotFoundError
|
|
||||||
# Create new node
|
|
||||||
node_1 = BoolOperation(operation=type_, is_root=False, condition_id=condition_id, alert_id=node.alert_id)
|
|
||||||
ext.session.add(node_1)
|
|
||||||
ext.session.commit()
|
|
||||||
# Recursion goes brr
|
|
||||||
recursion(node_1, ext, json['node_1'])
|
|
||||||
|
|
||||||
|
|
||||||
def recursion(node, ext, json):
|
|
||||||
if json.get('node_1'): # Create node 1
|
|
||||||
create_node(node, ext, json, 1)
|
|
||||||
if json.get('node_2'): # Create node 2
|
|
||||||
create_node(node, ext, json, 2)
|
|
|
@ -84,9 +84,26 @@ def page_repository_alerts(rid):
|
||||||
return json_error('Missing limit'), 400
|
return json_error('Missing limit'), 400
|
||||||
if 'window_size' not in request.json:
|
if 'window_size' not in request.json:
|
||||||
return json_error('Missing window size'), 400
|
return json_error('Missing window size'), 400
|
||||||
|
if (mode := request.json.get("evaluation_mode")) is not None:
|
||||||
|
try:
|
||||||
|
mode = ConditionMode(mode)
|
||||||
|
except KeyError:
|
||||||
|
return json_error("Unknown `type` specified."), 400
|
||||||
|
except Exception as e:
|
||||||
|
return json_error("Unknown error:" + str(e)), 400
|
||||||
|
else:
|
||||||
|
return json_error("Evaluation mode was not provided."), 400
|
||||||
|
|
||||||
alert = Alert(name=request.json['name'], limit=request.json['limit'], window_size=request.json['window_size'],
|
alert = Alert(name=request.json['name'], limit=request.json['limit'], window_size=request.json['window_size'],
|
||||||
repository_id=rid)
|
repository_id=rid, evaluation_mode=mode)
|
||||||
ext.session.add(alert)
|
ext.session.add(alert)
|
||||||
ext.session.commit()
|
ext.session.commit()
|
||||||
|
if request.json['conditions'] is not None:
|
||||||
|
for condition in request.json['conditions']:
|
||||||
|
c = Condition.query.filter_by(id=condition['id']).first()
|
||||||
|
if not c:
|
||||||
|
return json_error("Could not locate condition."), 404
|
||||||
|
conn = MadeOf(aid=alert.id, cid=c.id)
|
||||||
|
ext.session.add(conn)
|
||||||
|
ext.session.commit()
|
||||||
return json_success(alert.to_json()), 201
|
return json_success(alert.to_json()), 201
|
||||||
|
|
Loading…
Reference in a new issue