class SubscriptionsHandler(): """ Subscription type handlers class to get, edit, and streaming subscriptions from the database """ def __init__(self): self.db_handler = DBHandler('subscriptions') self.db_handler.create_table('id') self.users = UsersHandler() self.filters = BusFiltersHandler() self.templates = TemplatesHandler() def get(self, sub_id=None): """ Get all the subscriptions from the database. If id is provided search for a single subscription :sub_id: Id of the subscription to search for if provided """ return self.db_handler.get_data(sub_id) def get_with_relationships(self): """ Get subscriptions joining users and bus_filtes tables """ sub_list = [] subscriptions = self.db_handler.join_tables("subscriptions", "users", "bus_filters", "user_id", "filter_id") for sub in subscriptions: sub_list.append(sub) return sub_list def get_realtime(self): """ Get all subscriptions from the database in realtime. If user is edited in the db it returns the change. This method blocks the current thread use this method in a separated thread """ return self.db_handler.get_data_streaming() def get_users_by_filter(self, bus_filter): """ Get user associates with filter :bus_filter: Bus filter to search users """ users = [] subscriptions = self.get_by_filter(bus_filter) for subscription in subscriptions: user = self.users.get(subscription['user_id']) users.append(user) return users def get_filters_by_user(self, user): """ Get filters associates with user :user: User to seach for bus filters """ filters = [] subscriptions = self.get_by_user(user) for subscription in subscriptions: bus_filter = self.filters.get(subscription['filter_id']) filters.append(bus_filter) return filters def get_filters_by_template(self, template): """ Get bus filters assigned to a template :template: Template to search for his filters """ filters = [] subscriptions = self.get_by_template(template) for subscription in subscriptions: bfilter = self.filters.get(subscription['filter_id']) if not any(bus_filter['id'] == bfilter['id'] for bus_filter in filters): filters.append(bfilter) return filters def get_by_id(self, subsc_id): """ Get subscription by his id :subsc_id: ID of the subscription to search """ return self.db_handler.filter_data({'id': subsc_id}) def get_by_template(self, template): """ Get subscription searching by his template :template: Template to search for """ return self.db_handler.filter_data({'template_id': template['id']}) def get_by_template_id(self, template_id): """ Get subscription searching by his template id :template_id: Template id to search for """ return self.db_handler.filter_data({'template_id': template_id}) def get_by_filter(self, bus_filter): """ Get subscription by his id :bus_filter: Bus filter to search for """ return self.db_handler.filter_data({'filter_id': bus_filter['id']}) def get_by_filter_id(self, bus_filter_id): """ Get subscription by his id :bus_filter: Bus filter to search for """ return self.db_handler.filter_data({'filter_id': bus_filter_id}) def get_by_user(self, user): """ Get subscription by his id :user: User to seach for """ return self.db_handler.filter_data({'user_id': user['id']}) def insert(self, subscriptions): """ Insert subscriptions to the database :subscriptions: Subscription or bus subscriptions to insert """ if isinstance(subscriptions, list): for sub in subscriptions: if not sub.get('template_id'): sub = self.set_subscription_template(sub) else: if not subscriptions.get('template_id'): subscriptions = self.set_subscription_template(subscriptions) return self.db_handler.insert_data(subscriptions) def set_subscription_template(self, subscription): """ Sets subscription notification template If the bus filter associated with the subscription has template uses it. If not create default one :subscription: Subscription to add template """ bus_filter = self.filters.get(subscription.get('filter_id')) template_id = '' if bus_filter: if isinstance(bus_filter, list): bus_filter = bus_filter[0] template_id = bus_filter.get('template_id') if not hasattr(subscription, 'template_id') and template_id and bus_filter: subscription['template_id'] = bus_filter['template_id'] else: subscription['template_id'] = self.templates.get_default_template() return subscription def edit(self, subscription, subscription_id): """ Modify subscriptions :subscription: Modified subscription :subscription_id: Id of the subscription to edit """ self.db_handler.edit_data(subscription, subscription_id) def delete_user(self, user_id): """ Delete subscriptions associated with the user :user_id: user id to search for """ subscriptions = self.get() for sub in subscriptions: if sub['user_id'] == user_id: self.delete(sub['id']) def delete_bus_filter(self, bus_filter): """ Delete subscriptions associated with the bus filter :bus_filter_id: filter id to search for """ subscriptions = self.db_handler.filter_data( {'filter_id': bus_filter['id']}) for sub in subscriptions: self.delete(sub['id']) def edit_subscriptions_template(self, bus_filter): subscriptions = self.db_handler.filter_data( {'filter_id': bus_filter.get('id')}) for subs in subscriptions: subs['template_id'] = bus_filter.get('template_id') print(subs) self.edit(subs, subs['id']) def subscriptions_template(self, template_id): """ Return subscriptions with specific template id """ return self.db_handler.filter_data({'template_id': template_id}) def delete(self, subscription_id): """ Delete subscription. :subscription_id: Subscription id to delete """ self.db_handler.delete_data(subscription_id)
class Realtime(): """ Realtime class """ def __init__(self): self.filters = BusFiltersHandler() self.subscriptions = SubscriptionsHandler() self.templates = TemplatesHandler() Thread(target=self.realtime_subscriptions).start() Thread(target=self.realtime_filters).start() Thread(target=self.realtime_templates).start() def realtime_subscriptions(self): """ Realtime subscriptions. If a subscription is removed, the bus connection stops If a subscription is updated, recreates the thread """ self.start_connection() cursor = self.subscriptions.get_realtime() try: for subscription in cursor: if subscription['old_val'] and not subscription['new_val']: # When a subscription is deleted self.on_subscription_delete(subscription['old_val']) if subscription['new_val'] and not subscription['old_val']: # When a subscription is added self.start_connection() except BaseException: raise ConnectionLost() def realtime_filters(self): """ Realtime bus filters. Listening for a template change in the database. """ cursor = self.filters.get_realtime() try: for bus_filter in cursor: if bus_filter['old_val'] and not bus_filter['new_val']: # When a bus filter is deleted self.on_bus_filter_delete(bus_filter['old_val']) if bus_filter['old_val'] and bus_filter['new_val']: # Bus filter edited old_bus = bus_filter['old_val'] new_bus = bus_filter['new_val'] if old_bus.get('exchange') != new_bus.get( 'exchange') or old_bus.get('key') != new_bus.get( 'key'): self.start_connection() if old_bus.get('template_id') != new_bus.get( 'template_id'): self.subscriptions.edit_subscriptions_template(new_bus) except BaseException: raise ConnectionLost() def realtime_templates(self): """ Realtime templates. Listening for a template change in the database. """ cursor = self.templates.get_realtime() try: for template in cursor: if template['old_val'] and not template['new_val']: self.on_template_deleted(template['old_val']) except BaseException: raise ConnectionLost() def start_connection(self): """ Creates a new connection thread listening to the subscriptions stored in the database """ bus_filters = [] subscriptions_list = self.subscriptions.get() if subscriptions_list: for sub in subscriptions_list: bus_filters.append(self.bus_filter_from_subscription(sub)) if bus_filters: self.create_connection(bus_filters) def bus_filter_from_subscription(self, subscription): """ Returns bus filters with the same bus filter id than the subscription bus filter :subscription: Subscription that is going to look for bus filters """ return self.filters.get(subscription.get('filter_id')) def on_subscription_delete(self, subscription): """ If a subscription is deleted, it search if is the last subscription with this bus filters. If so, it stops listen to this bus filter from the bus """ subscriptions = [] if isinstance(subscription, list): for sub in subscription: subscriptions = subscriptions + \ self.same_bus_filter_subscription(sub) else: subscriptions = self.same_bus_filter_subscription(subscription) if subscriptions: if len(subscriptions) == 1: # If only one subscription left, unbind bus filter bus_filter = self.bus_filter_from_subscription(subscription) self.connection_stop(bus_filter) def same_bus_filter_subscription(self, subscription): """ Returns all subscriptions with the same bus filter than the provided subscription :subscription: Subscription that is going to look for bus filters """ try: return self.subscriptions.get_by_filter_id( subscription.get('filter_id')) except BaseException: pass def on_bus_filter_delete(self, bus_filter): """ If a bus filter is delete, delete it from the subscriptions asociated to and stops listening from the bus """ self.subscriptions.delete_bus_filter(bus_filter) self.connection_stop(bus_filter) def on_template_deleted(self, template): """ If a template is deleted, it search for subscriptions and bus filters with that template and replace it with default template id. """ template_id = template.get('id') subscriptions = self.subscriptions.subscriptions_template(template_id) for subscription in subscriptions: subscription['template_id'] = self.templates.get_default_template() self.subscriptions.edit(subscription, subscription['id']) self.filters.delete_template(template_id) def create_connection(self, subscriptions): """ Creates a thread with a new rabbitmq connection :subscriptions: Subscriptions to add to bus thread """ if not hasattr(self, 'bus_thread'): self.bus_thread = BusConnectionHandler(subscriptions) else: self.bus_thread.set_subscriptions(subscriptions) self.bus_thread.start() def connection_stop(self, bus_filter=None): """ Search for a thread with the bus_filter to pause and delete it """ if hasattr(self, 'bus_thread'): self.bus_thread.stop() if bus_filter: self.bus_thread.unbind(bus_filter.get('exchange'), bus_filter.get('key'))