# -*- 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 from datetime import datetime, timedelta from pyramid.view import view_config from pyramid.httpexceptions import HTTPUnprocessableEntity from appenlight.models import DBSession from appenlight.models.user import User from appenlight.models.report_comment import ReportComment from appenlight.models.report_assignment import ReportAssignment from appenlight.models.services.user import UserService from appenlight.models.services.report_group import ReportGroupService from appenlight import forms from appenlight.lib.enums import ReportType from appenlight.lib.helpers import gen_pagination_headers from appenlight.lib.utils import build_filter_settings_from_query_dict from appenlight.validators import ReportSearchSchema, TagListSchema, \ accepted_search_params from webob.multidict import MultiDict _ = str log = logging.getLogger(__name__) section_filters_key = 'appenlight:reports:filter:%s' @view_config(route_name='reports', renderer='json', permission='authenticated') @view_config(route_name='slow_reports', renderer='json', permission='authenticated') def index(request): """ Returns list of report groups based on user search query """ if request.user: request.user.last_login_date = datetime.utcnow() applications = request.user.resources_with_perms( ['view'], resource_types=['application']) search_params = request.GET.mixed() all_possible_app_ids = set([app.resource_id for app in applications]) schema = ReportSearchSchema().bind(resources=all_possible_app_ids) tag_schema = TagListSchema() filter_settings = schema.deserialize(search_params) tag_list = [{"name": k, "value": v} for k, v in filter_settings.items() if k not in accepted_search_params] tags = tag_schema.deserialize(tag_list) filter_settings['tags'] = tags if request.matched_route.name == 'slow_reports': filter_settings['report_type'] = [ReportType.slow] else: filter_settings['report_type'] = [ReportType.error] reports_paginator = ReportGroupService.get_paginator_by_app_ids( app_ids=filter_settings['resource'], page=filter_settings['page'], filter_settings=filter_settings ) reports = [] include_keys = ('id', 'http_status', 'report_type', 'resource_name', 'front_url', 'resource_id', 'error', 'url_path', 'tags', 'duration') for report in reports_paginator.sa_items: reports.append(report.get_dict(request, include_keys=include_keys)) headers = gen_pagination_headers(request, reports_paginator) request.response.headers.update(headers) return reports @view_config(route_name='report_groups', renderer='json', permission='view', request_method="GET") def view_report(request): """ Show individual detailed report group along with latest report """ report_group = request.context.report_group if not report_group.read: report_group.read = True report_id = request.params.get('reportId', request.params.get('report_id')) report_dict = report_group.get_report(report_id).get_dict(request, details=True) # disallow browsing other occurences by anonymous if not request.user: report_dict.pop('group_next_report', None) report_dict.pop('group_previous_report', None) return report_dict @view_config(route_name='report_groups', renderer='json', permission='update_reports', request_method='DELETE') def remove(request): """ Used to remove reourt groups from database """ report = request.context.report_group form = forms.ReactorForm(request.POST, csrf_context=request) form.validate() DBSession.delete(report) return True @view_config(route_name='report_groups_property', match_param='key=comments', renderer='json', permission='view', request_method="POST") def comment_create(request): """ Creates user comments for report group, sends email notifications of said comments """ report_group = request.context.report_group application = request.context.resource form = forms.CommentForm(MultiDict(request.unsafe_json_body), csrf_context=request) if request.method == 'POST' and form.validate(): comment = ReportComment(owner_id=request.user.id, report_time=report_group.first_timestamp) form.populate_obj(comment) report_group.comments.append(comment) perm_list = application.users_for_perm('view') uids_to_notify = [] users_to_notify = [] for perm in perm_list: user = perm.user if ('@{}'.format(user.user_name) in comment.body and user.id not in uids_to_notify): uids_to_notify.append(user.id) users_to_notify.append(user) commenters = ReportGroupService.users_commenting( report_group, exclude_user_id=request.user.id) for user in commenters: if user.id not in uids_to_notify: uids_to_notify.append(user.id) users_to_notify.append(user) for user in users_to_notify: email_vars = {'user': user, 'commenting_user': request.user, 'request': request, 'application': application, 'report_group': report_group, 'comment': comment, 'email_title': "AppEnlight :: New comment"} UserService.send_email( request, recipients=[user.email], variables=email_vars, template='/email_templates/new_comment_report.jinja2') request.session.flash(_('Your comment was created')) return comment.get_dict() else: return form.errors @view_config(route_name='report_groups_property', match_param='key=assigned_users', renderer='json', permission='update_reports', request_method="GET") def assigned_users(request): """ Returns list of users a specific report group is assigned for review """ report_group = request.context.report_group application = request.context.resource users = set([p.user for p in application.users_for_perm('view')]) currently_assigned = [u.user_name for u in report_group.assigned_users] user_status = {'assigned': [], 'unassigned': []} # handle users for user in users: user_dict = {'user_name': user.user_name, 'gravatar_url': user.gravatar_url(), 'name': '%s %s' % (user.first_name, user.last_name,)} if user.user_name in currently_assigned: user_status['assigned'].append(user_dict) elif user_dict not in user_status['unassigned']: user_status['unassigned'].append(user_dict) return user_status @view_config(route_name='report_groups_property', match_param='key=assigned_users', renderer='json', permission='update_reports', request_method="PATCH") def assign_users(request): """ Assigns specific report group to user for review - send email notification """ report_group = request.context.report_group application = request.context.resource currently_assigned = [u.user_name for u in report_group.assigned_users] new_assigns = request.unsafe_json_body # first unassign old users for user_name in new_assigns['unassigned']: if user_name in currently_assigned: user = User.by_user_name(user_name) report_group.assigned_users.remove(user) comment = ReportComment(owner_id=request.user.id, report_time=report_group.first_timestamp) comment.body = 'Unassigned group from @%s' % user_name report_group.comments.append(comment) # assign new users for user_name in new_assigns['assigned']: if user_name not in currently_assigned: user = User.by_user_name(user_name) if user in report_group.assigned_users: report_group.assigned_users.remove(user) DBSession.flush() assignment = ReportAssignment( owner_id=user.id, report_time=report_group.first_timestamp, group_id=report_group.id) DBSession.add(assignment) comment = ReportComment(owner_id=request.user.id, report_time=report_group.first_timestamp) comment.body = 'Assigned report_group to @%s' % user_name report_group.comments.append(comment) email_vars = {'user': user, 'request': request, 'application': application, 'report_group': report_group, 'email_title': "AppEnlight :: Assigned Report"} UserService.send_email(request, recipients=[user.email], variables=email_vars, template='/email_templates/assigned_report.jinja2') return True @view_config(route_name='report_groups_property', match_param='key=history', renderer='json', permission='view') def history(request): """ Separate error graph or similar graph""" report_group = request.context.report_group query_params = request.GET.mixed() query_params['resource'] = (report_group.resource_id,) filter_settings = build_filter_settings_from_query_dict(request, query_params) if not filter_settings.get('end_date'): end_date = datetime.utcnow().replace(microsecond=0, second=0) filter_settings['end_date'] = end_date if not filter_settings.get('start_date'): delta = timedelta(days=30) filter_settings['start_date'] = filter_settings['end_date'] - delta filter_settings['group_id'] = report_group.id result = ReportGroupService.get_report_stats(request, filter_settings) plot_data = [] for row in result: point = { 'x': row['x'], 'reports': row['report'] + row['slow_report'] + row['not_found']} plot_data.append(point) return plot_data @view_config(route_name='report_groups', renderer='json', permission='update_reports', request_method="PATCH") def report_groups_PATCH(request): """ Used to update the report group fixed status """ report_group = request.context.report_group allowed_keys = ['public', 'fixed'] for k, v in request.unsafe_json_body.items(): if k in allowed_keys: setattr(report_group, k, v) else: return HTTPUnprocessableEntity() return report_group.get_dict(request)