# -*- coding: utf-8 -*- # Copyright 2010 - 2017 RhodeCode GmbH and the AppEnlight project authors # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import logging import pyramid_mailer import pyramid.renderers import sqlalchemy as sa from collections import namedtuple from datetime import datetime from ziggurat_foundations.models.services.user import UserService from appenlight.lib.rule import Rule from appenlight.models import get_db_session from appenlight.models.integrations import IntegrationException from appenlight.models.report import REPORT_TYPE_MATRIX from appenlight.models.user import User from paginate_sqlalchemy import SqlalchemyOrmPage from pyramid.threadlocal import get_current_registry log = logging.getLogger(__name__) GroupOccurence = namedtuple('GroupOccurence', ['occurences', 'group']) class UserService(UserService): @classmethod def all(cls, db_session=None): return get_db_session(db_session).query(User).order_by(User.user_name) @classmethod def send_email(cls, request, recipients, variables, template, immediately=False, silent=False): html = pyramid.renderers.render(template, variables, request) title = variables.get('email_title', variables.get('title', "No Title")) title = title.replace('\r', '').replace('\n', '') sender = "{} <{}>".format( request.registry.settings['mailing.from_name'], request.registry.settings['mailing.from_email']) message = pyramid_mailer.message.Message( subject=title, sender=sender, recipients=recipients, html=html) if immediately: try: request.registry.mailer.send_immediately(message) except Exception as e: log.warning('Exception %s' % e) if not silent: raise else: request.registry.mailer.send(message) @classmethod def get_paginator(cls, page=1, item_count=None, items_per_page=50, order_by=None, filter_settings=None, exclude_columns=None, db_session=None): registry = get_current_registry() if not exclude_columns: exclude_columns = [] if not filter_settings: filter_settings = {} db_session = get_db_session(db_session) q = db_session.query(User) if filter_settings.get('order_col'): order_col = filter_settings.get('order_col') if filter_settings.get('order_dir') == 'dsc': sort_on = 'desc' else: sort_on = 'asc' q = q.order_by(getattr(sa, sort_on)(getattr(User, order_col))) else: q = q.order_by(sa.desc(User.registered_date)) # remove urlgen or it never caches count cache_params = dict(filter_settings) cache_params.pop('url', None) cache_params.pop('url_maker', None) @registry.cache_regions.redis_min_5.cache_on_arguments() def estimate_users(cache_key): o_q = q.order_by(False) return o_q.count() item_count = estimate_users(cache_params) # if the number of pages is low we may want to invalidate the count to # provide 'real time' update - use case - # errors just started to flow in if item_count < 1000: item_count = estimate_users.refresh(cache_params) paginator = SqlalchemyOrmPage(q, page=page, item_count=item_count, items_per_page=items_per_page, **filter_settings) return paginator @classmethod def get_valid_channels(cls, user): return [channel for channel in user.alert_channels if channel.channel_validated] @classmethod def report_notify(cls, user, request, application, report_groups, occurence_dict, db_session=None): db_session = get_db_session(db_session) if not report_groups: return True since_when = datetime.utcnow() for channel in cls.get_valid_channels(user): confirmed_groups = [] for group in report_groups: occurences = occurence_dict.get(group.id, 1) for action in channel.channel_actions: not_matched = ( action.resource_id and action.resource_id != application.resource_id) if action.type != 'report' or not_matched: continue should_notify = (action.action == 'always' or not group.notified) rule_obj = Rule(action.rule, REPORT_TYPE_MATRIX) report_dict = group.get_report().get_dict(request) if rule_obj.match(report_dict) and should_notify: item = GroupOccurence(occurences, group) if item not in confirmed_groups: confirmed_groups.append(item) # send individual reports total_confirmed = len(confirmed_groups) if not total_confirmed: continue try: channel.notify_reports(resource=application, user=user, request=request, since_when=since_when, reports=confirmed_groups) except IntegrationException as e: log.warning('%s' % e)