1
Fork 0
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:
Lorenzo Balugani 2021-05-17 15:29:08 +02:00
parent 378414a624
commit 5607b74e87
8 changed files with 70 additions and 130 deletions

View file

@ -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)

View file

@ -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
} }

View file

@ -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)

View file

@ -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 {

View 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")

View file

@ -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

View file

@ -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)

View file

@ -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