|
|
# -*- 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)
|
|
|
|