# -*- 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 ziggurat_foundations.models.services.resource import ResourceService from ziggurat_foundations.models.services.user import UserService 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 = UserService.resources_with_perms( request.user, ["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 = ResourceService.users_for_perm(application, "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 ResourceService.users_for_perm(application, "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": UserService.gravatar_url(user), "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 = UserService.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 = UserService.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)