##// END OF EJS Templates
components: reports componentized
ergo -
Show More

The requested changes are too big and content was truncated. Show full diff

@@ -1,296 +1,296 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2016 RhodeCode GmbH
3 # Copyright (C) 2010-2016 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # AppEnlight Enterprise Edition, including its added features, Support
18 # AppEnlight Enterprise Edition, including its added features, Support
19 # services, and proprietary license terms, please see
19 # services, and proprietary license terms, please see
20 # https://rhodecode.com/licenses/
20 # https://rhodecode.com/licenses/
21
21
22 import logging
22 import logging
23 import sqlalchemy as sa
23 import sqlalchemy as sa
24 import urllib.request, urllib.parse, urllib.error
24 import urllib.request, urllib.parse, urllib.error
25 from datetime import timedelta
25 from datetime import timedelta
26 from appenlight.models import Base
26 from appenlight.models import Base
27 from appenlight.lib.utils.date_utils import convert_date
27 from appenlight.lib.utils.date_utils import convert_date
28 from sqlalchemy.dialects.postgresql import JSON
28 from sqlalchemy.dialects.postgresql import JSON
29 from ziggurat_foundations.models.base import BaseModel
29 from ziggurat_foundations.models.base import BaseModel
30
30
31 log = logging.getLogger(__name__)
31 log = logging.getLogger(__name__)
32
32
33 #
33 #
34 channel_rules_m2m_table = sa.Table(
34 channel_rules_m2m_table = sa.Table(
35 'channels_actions', Base.metadata,
35 'channels_actions', Base.metadata,
36 sa.Column('channel_pkey', sa.Integer,
36 sa.Column('channel_pkey', sa.Integer,
37 sa.ForeignKey('alert_channels.pkey')),
37 sa.ForeignKey('alert_channels.pkey')),
38 sa.Column('action_pkey', sa.Integer,
38 sa.Column('action_pkey', sa.Integer,
39 sa.ForeignKey('alert_channels_actions.pkey'))
39 sa.ForeignKey('alert_channels_actions.pkey'))
40 )
40 )
41
41
42 DATE_FRMT = '%Y-%m-%dT%H:%M'
42 DATE_FRMT = '%Y-%m-%dT%H:%M'
43
43
44
44
45 class AlertChannel(Base, BaseModel):
45 class AlertChannel(Base, BaseModel):
46 """
46 """
47 Stores information about possible alerting options
47 Stores information about possible alerting options
48 """
48 """
49 __tablename__ = 'alert_channels'
49 __tablename__ = 'alert_channels'
50 __possible_channel_names__ = ['email']
50 __possible_channel_names__ = ['email']
51 __mapper_args__ = {
51 __mapper_args__ = {
52 'polymorphic_on': 'channel_name',
52 'polymorphic_on': 'channel_name',
53 'polymorphic_identity': 'integration'
53 'polymorphic_identity': 'integration'
54 }
54 }
55
55
56 owner_id = sa.Column(sa.Unicode(30),
56 owner_id = sa.Column(sa.Unicode(30),
57 sa.ForeignKey('users.id', onupdate='CASCADE',
57 sa.ForeignKey('users.id', onupdate='CASCADE',
58 ondelete='CASCADE'))
58 ondelete='CASCADE'))
59 channel_name = sa.Column(sa.Unicode(25), nullable=False)
59 channel_name = sa.Column(sa.Unicode(25), nullable=False)
60 channel_value = sa.Column(sa.Unicode(80), nullable=False, default='')
60 channel_value = sa.Column(sa.Unicode(80), nullable=False, default='')
61 channel_json_conf = sa.Column(JSON(), nullable=False, default='')
61 channel_json_conf = sa.Column(JSON(), nullable=False, default='')
62 channel_validated = sa.Column(sa.Boolean, nullable=False,
62 channel_validated = sa.Column(sa.Boolean, nullable=False,
63 default=False)
63 default=False)
64 send_alerts = sa.Column(sa.Boolean, nullable=False,
64 send_alerts = sa.Column(sa.Boolean, nullable=False,
65 default=True)
65 default=True)
66 daily_digest = sa.Column(sa.Boolean, nullable=False,
66 daily_digest = sa.Column(sa.Boolean, nullable=False,
67 default=True)
67 default=True)
68 integration_id = sa.Column(sa.Integer, sa.ForeignKey('integrations.id'),
68 integration_id = sa.Column(sa.Integer, sa.ForeignKey('integrations.id'),
69 nullable=True)
69 nullable=True)
70 pkey = sa.Column(sa.Integer(), nullable=False, primary_key=True)
70 pkey = sa.Column(sa.Integer(), nullable=False, primary_key=True)
71
71
72 channel_actions = sa.orm.relationship('AlertChannelAction',
72 channel_actions = sa.orm.relationship('AlertChannelAction',
73 cascade="all",
73 cascade="all",
74 passive_deletes=True,
74 passive_deletes=True,
75 passive_updates=True,
75 passive_updates=True,
76 secondary=channel_rules_m2m_table,
76 secondary=channel_rules_m2m_table,
77 backref='channels')
77 backref='channels')
78
78
79 @property
79 @property
80 def channel_visible_value(self):
80 def channel_visible_value(self):
81 if self.integration:
81 if self.integration:
82 return '{}: {}'.format(
82 return '{}: {}'.format(
83 self.channel_name,
83 self.channel_name,
84 self.integration.resource.resource_name
84 self.integration.resource.resource_name
85 )
85 )
86
86
87 return '{}: {}'.format(
87 return '{}: {}'.format(
88 self.channel_name,
88 self.channel_name,
89 self.channel_value
89 self.channel_value
90 )
90 )
91
91
92 def get_dict(self, exclude_keys=None, include_keys=None,
92 def get_dict(self, exclude_keys=None, include_keys=None,
93 extended_info=True):
93 extended_info=True):
94 """
94 """
95 Returns dictionary with required information that will be consumed by
95 Returns dictionary with required information that will be consumed by
96 angular
96 angular
97 """
97 """
98 instance_dict = super(AlertChannel, self).get_dict(exclude_keys,
98 instance_dict = super(AlertChannel, self).get_dict(exclude_keys,
99 include_keys)
99 include_keys)
100 exclude_keys_list = exclude_keys or []
100 exclude_keys_list = exclude_keys or []
101 include_keys_list = include_keys or []
101 include_keys_list = include_keys or []
102
102
103 instance_dict['supports_report_alerting'] = True
103 instance_dict['supports_report_alerting'] = True
104 instance_dict['channel_visible_value'] = self.channel_visible_value
104 instance_dict['channel_visible_value'] = self.channel_visible_value
105
105
106 if extended_info:
106 if extended_info:
107 instance_dict['actions'] = [
107 instance_dict['actions'] = [
108 rule.get_dict(extended_info=True) for
108 rule.get_dict(extended_info=True) for
109 rule in self.channel_actions]
109 rule in self.channel_actions]
110
110
111 del instance_dict['channel_json_conf']
111 del instance_dict['channel_json_conf']
112
112
113 if self.integration:
113 if self.integration:
114 instance_dict[
114 instance_dict[
115 'supports_report_alerting'] = \
115 'supports_report_alerting'] = \
116 self.integration.supports_report_alerting
116 self.integration.supports_report_alerting
117 d = {}
117 d = {}
118 for k in instance_dict.keys():
118 for k in instance_dict.keys():
119 if (k not in exclude_keys_list and
119 if (k not in exclude_keys_list and
120 (k in include_keys_list or not include_keys)):
120 (k in include_keys_list or not include_keys)):
121 d[k] = instance_dict[k]
121 d[k] = instance_dict[k]
122 return d
122 return d
123
123
124 def __repr__(self):
124 def __repr__(self):
125 return '<AlertChannel: (%s,%s), user:%s>' % (self.channel_name,
125 return '<AlertChannel: (%s,%s), user:%s>' % (self.channel_name,
126 self.channel_value,
126 self.channel_value,
127 self.user_name,)
127 self.user_name,)
128
128
129 def send_digest(self, **kwargs):
129 def send_digest(self, **kwargs):
130 """
130 """
131 This should implement daily top error report notifications
131 This should implement daily top error report notifications
132 """
132 """
133 log.warning('send_digest NOT IMPLEMENTED')
133 log.warning('send_digest NOT IMPLEMENTED')
134
134
135 def notify_reports(self, **kwargs):
135 def notify_reports(self, **kwargs):
136 """
136 """
137 This should implement notification of reports that occured in 1 min
137 This should implement notification of reports that occured in 1 min
138 interval
138 interval
139 """
139 """
140 log.warning('notify_reports NOT IMPLEMENTED')
140 log.warning('notify_reports NOT IMPLEMENTED')
141
141
142 def notify_alert(self, **kwargs):
142 def notify_alert(self, **kwargs):
143 """
143 """
144 Notify user of report/uptime/chart threshold events based on events alert
144 Notify user of report/uptime/chart threshold events based on events alert
145 type
145 type
146
146
147 Kwargs:
147 Kwargs:
148 application: application that the event applies for,
148 application: application that the event applies for,
149 event: event that is notified,
149 event: event that is notified,
150 user: user that should be notified
150 user: user that should be notified
151 request: request object
151 request: request object
152
152
153 """
153 """
154 alert_name = kwargs['event'].unified_alert_name()
154 alert_name = kwargs['event'].unified_alert_name()
155 if alert_name in ['slow_report_alert', 'error_report_alert']:
155 if alert_name in ['slow_report_alert', 'error_report_alert']:
156 self.notify_report_alert(**kwargs)
156 self.notify_report_alert(**kwargs)
157 elif alert_name == 'uptime_alert':
157 elif alert_name == 'uptime_alert':
158 self.notify_uptime_alert(**kwargs)
158 self.notify_uptime_alert(**kwargs)
159 elif alert_name == 'chart_alert':
159 elif alert_name == 'chart_alert':
160 self.notify_chart_alert(**kwargs)
160 self.notify_chart_alert(**kwargs)
161
161
162 def notify_chart_alert(self, **kwargs):
162 def notify_chart_alert(self, **kwargs):
163 """
163 """
164 This should implement report open/close alerts notifications
164 This should implement report open/close alerts notifications
165 """
165 """
166 log.warning('notify_chart_alert NOT IMPLEMENTED')
166 log.warning('notify_chart_alert NOT IMPLEMENTED')
167
167
168 def notify_report_alert(self, **kwargs):
168 def notify_report_alert(self, **kwargs):
169 """
169 """
170 This should implement report open/close alerts notifications
170 This should implement report open/close alerts notifications
171 """
171 """
172 log.warning('notify_report_alert NOT IMPLEMENTED')
172 log.warning('notify_report_alert NOT IMPLEMENTED')
173
173
174 def notify_uptime_alert(self, **kwargs):
174 def notify_uptime_alert(self, **kwargs):
175 """
175 """
176 This should implement uptime open/close alerts notifications
176 This should implement uptime open/close alerts notifications
177 """
177 """
178 log.warning('notify_uptime_alert NOT IMPLEMENTED')
178 log.warning('notify_uptime_alert NOT IMPLEMENTED')
179
179
180 def get_notification_basic_vars(self, kwargs):
180 def get_notification_basic_vars(self, kwargs):
181 """
181 """
182 Sets most common variables used later for rendering notifications for
182 Sets most common variables used later for rendering notifications for
183 channel
183 channel
184 """
184 """
185 if 'event' in kwargs:
185 if 'event' in kwargs:
186 kwargs['since_when'] = kwargs['event'].start_date
186 kwargs['since_when'] = kwargs['event'].start_date
187
187
188 url_start_date = kwargs.get('since_when') - timedelta(minutes=1)
188 url_start_date = kwargs.get('since_when') - timedelta(minutes=1)
189 url_end_date = kwargs.get('since_when') + timedelta(minutes=4)
189 url_end_date = kwargs.get('since_when') + timedelta(minutes=4)
190 tmpl_vars = {
190 tmpl_vars = {
191 "timestamp": kwargs['since_when'],
191 "timestamp": kwargs['since_when'],
192 "user": kwargs['user'],
192 "user": kwargs['user'],
193 "since_when": kwargs.get('since_when'),
193 "since_when": kwargs.get('since_when'),
194 "url_start_date": url_start_date,
194 "url_start_date": url_start_date,
195 "url_end_date": url_end_date
195 "url_end_date": url_end_date
196 }
196 }
197 tmpl_vars["resource_name"] = kwargs['resource'].resource_name
197 tmpl_vars["resource_name"] = kwargs['resource'].resource_name
198 tmpl_vars["resource"] = kwargs['resource']
198 tmpl_vars["resource"] = kwargs['resource']
199
199
200 if 'event' in kwargs:
200 if 'event' in kwargs:
201 tmpl_vars['event_values'] = kwargs['event'].values
201 tmpl_vars['event_values'] = kwargs['event'].values
202 tmpl_vars['alert_type'] = kwargs['event'].unified_alert_name()
202 tmpl_vars['alert_type'] = kwargs['event'].unified_alert_name()
203 tmpl_vars['alert_action'] = kwargs['event'].unified_alert_action()
203 tmpl_vars['alert_action'] = kwargs['event'].unified_alert_action()
204 return tmpl_vars
204 return tmpl_vars
205
205
206 def report_alert_notification_vars(self, kwargs):
206 def report_alert_notification_vars(self, kwargs):
207 tmpl_vars = self.get_notification_basic_vars(kwargs)
207 tmpl_vars = self.get_notification_basic_vars(kwargs)
208 reports = kwargs.get('reports', [])
208 reports = kwargs.get('reports', [])
209 tmpl_vars["reports"] = reports
209 tmpl_vars["reports"] = reports
210 tmpl_vars["confirmed_total"] = len(reports)
210 tmpl_vars["confirmed_total"] = len(reports)
211
211
212 tmpl_vars["report_type"] = "error reports"
212 tmpl_vars["report_type"] = "error reports"
213 tmpl_vars["url_report_type"] = 'report'
213 tmpl_vars["url_report_type"] = 'report/list'
214
214
215 alert_type = tmpl_vars.get('alert_type', '')
215 alert_type = tmpl_vars.get('alert_type', '')
216 if 'slow_report' in alert_type:
216 if 'slow_report' in alert_type:
217 tmpl_vars["report_type"] = "slow reports"
217 tmpl_vars["report_type"] = "slow reports"
218 tmpl_vars["url_report_type"] = 'report/list_slow'
218 tmpl_vars["url_report_type"] = 'report/list_slow'
219
219
220 app_url = kwargs['request'].registry.settings['_mail_url']
220 app_url = kwargs['request'].registry.settings['_mail_url']
221
221
222 destination_url = kwargs['request'].route_url('/',
222 destination_url = kwargs['request'].route_url('/',
223 _app_url=app_url)
223 _app_url=app_url)
224 if alert_type:
224 if alert_type:
225 destination_url += 'ui/{}?resource={}&start_date={}&end_date={}'.format(
225 destination_url += 'ui/{}?resource={}&start_date={}&end_date={}'.format(
226 tmpl_vars["url_report_type"],
226 tmpl_vars["url_report_type"],
227 tmpl_vars['resource'].resource_id,
227 tmpl_vars['resource'].resource_id,
228 tmpl_vars['url_start_date'].strftime(DATE_FRMT),
228 tmpl_vars['url_start_date'].strftime(DATE_FRMT),
229 tmpl_vars['url_end_date'].strftime(DATE_FRMT)
229 tmpl_vars['url_end_date'].strftime(DATE_FRMT)
230 )
230 )
231 else:
231 else:
232 destination_url += 'ui/{}?resource={}'.format(
232 destination_url += 'ui/{}?resource={}'.format(
233 tmpl_vars["url_report_type"],
233 tmpl_vars["url_report_type"],
234 tmpl_vars['resource'].resource_id
234 tmpl_vars['resource'].resource_id
235 )
235 )
236 tmpl_vars["destination_url"] = destination_url
236 tmpl_vars["destination_url"] = destination_url
237
237
238 return tmpl_vars
238 return tmpl_vars
239
239
240 def uptime_alert_notification_vars(self, kwargs):
240 def uptime_alert_notification_vars(self, kwargs):
241 tmpl_vars = self.get_notification_basic_vars(kwargs)
241 tmpl_vars = self.get_notification_basic_vars(kwargs)
242 app_url = kwargs['request'].registry.settings['_mail_url']
242 app_url = kwargs['request'].registry.settings['_mail_url']
243 destination_url = kwargs['request'].route_url('/', _app_url=app_url)
243 destination_url = kwargs['request'].route_url('/', _app_url=app_url)
244 destination_url += 'ui/{}?resource={}'.format(
244 destination_url += 'ui/{}?resource={}'.format(
245 'uptime',
245 'uptime',
246 tmpl_vars['resource'].resource_id)
246 tmpl_vars['resource'].resource_id)
247 tmpl_vars['destination_url'] = destination_url
247 tmpl_vars['destination_url'] = destination_url
248
248
249 reason = ''
249 reason = ''
250 e_values = tmpl_vars.get('event_values')
250 e_values = tmpl_vars.get('event_values')
251
251
252 if e_values and e_values.get('response_time') == 0:
252 if e_values and e_values.get('response_time') == 0:
253 reason += ' Response time was slower than 20 seconds.'
253 reason += ' Response time was slower than 20 seconds.'
254 elif e_values:
254 elif e_values:
255 code = e_values.get('status_code')
255 code = e_values.get('status_code')
256 reason += ' Response status code: %s.' % code
256 reason += ' Response status code: %s.' % code
257
257
258 tmpl_vars['reason'] = reason
258 tmpl_vars['reason'] = reason
259 return tmpl_vars
259 return tmpl_vars
260
260
261 def chart_alert_notification_vars(self, kwargs):
261 def chart_alert_notification_vars(self, kwargs):
262 tmpl_vars = self.get_notification_basic_vars(kwargs)
262 tmpl_vars = self.get_notification_basic_vars(kwargs)
263 tmpl_vars['chart_name'] = tmpl_vars['event_values']['chart_name']
263 tmpl_vars['chart_name'] = tmpl_vars['event_values']['chart_name']
264 tmpl_vars['action_name'] = tmpl_vars['event_values'].get(
264 tmpl_vars['action_name'] = tmpl_vars['event_values'].get(
265 'action_name') or ''
265 'action_name') or ''
266 matched_values = tmpl_vars['event_values']['matched_step_values']
266 matched_values = tmpl_vars['event_values']['matched_step_values']
267 tmpl_vars['readable_values'] = []
267 tmpl_vars['readable_values'] = []
268 for key, value in list(matched_values['values'].items()):
268 for key, value in list(matched_values['values'].items()):
269 matched_label = matched_values['labels'].get(key)
269 matched_label = matched_values['labels'].get(key)
270 if matched_label:
270 if matched_label:
271 tmpl_vars['readable_values'].append({
271 tmpl_vars['readable_values'].append({
272 'label': matched_label['human_label'],
272 'label': matched_label['human_label'],
273 'value': value
273 'value': value
274 })
274 })
275 tmpl_vars['readable_values'] = sorted(tmpl_vars['readable_values'],
275 tmpl_vars['readable_values'] = sorted(tmpl_vars['readable_values'],
276 key=lambda x: x['label'])
276 key=lambda x: x['label'])
277 start_date = convert_date(tmpl_vars['event_values']['start_interval'])
277 start_date = convert_date(tmpl_vars['event_values']['start_interval'])
278 end_date = None
278 end_date = None
279 if tmpl_vars['event_values'].get('end_interval'):
279 if tmpl_vars['event_values'].get('end_interval'):
280 end_date = convert_date(tmpl_vars['event_values']['end_interval'])
280 end_date = convert_date(tmpl_vars['event_values']['end_interval'])
281
281
282 app_url = kwargs['request'].registry.settings['_mail_url']
282 app_url = kwargs['request'].registry.settings['_mail_url']
283 destination_url = kwargs['request'].route_url('/', _app_url=app_url)
283 destination_url = kwargs['request'].route_url('/', _app_url=app_url)
284 to_encode = {
284 to_encode = {
285 'resource': tmpl_vars['event_values']['resource'],
285 'resource': tmpl_vars['event_values']['resource'],
286 'start_date': start_date.strftime(DATE_FRMT),
286 'start_date': start_date.strftime(DATE_FRMT),
287 }
287 }
288 if end_date:
288 if end_date:
289 to_encode['end_date'] = end_date.strftime(DATE_FRMT)
289 to_encode['end_date'] = end_date.strftime(DATE_FRMT)
290
290
291 destination_url += 'ui/{}?{}'.format(
291 destination_url += 'ui/{}?{}'.format(
292 'logs',
292 'logs',
293 urllib.parse.urlencode(to_encode)
293 urllib.parse.urlencode(to_encode)
294 )
294 )
295 tmpl_vars['destination_url'] = destination_url
295 tmpl_vars['destination_url'] = destination_url
296 return tmpl_vars
296 return tmpl_vars
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file renamed from frontend/src/templates/reports/view.html to frontend/src/components/views/report-view/report-view.html
NO CONTENT: file renamed from frontend/src/templates/reports/view.html to frontend/src/components/views/report-view/report-view.html
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file renamed from frontend/src/controllers/reports/view.js to frontend/src/components/views/report-view/report-view.js
NO CONTENT: file renamed from frontend/src/controllers/reports/view.js to frontend/src/components/views/report-view/report-view.js
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file renamed from frontend/src/templates/reports/list.html to frontend/src/components/views/reports-browser-view/reports-browser-view.html
NO CONTENT: file renamed from frontend/src/templates/reports/list.html to frontend/src/components/views/reports-browser-view/reports-browser-view.html
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file renamed from frontend/src/controllers/reports/list.js to frontend/src/components/views/reports-browser-view/reports-browser-view.js
NO CONTENT: file renamed from frontend/src/controllers/reports/list.js to frontend/src/components/views/reports-browser-view/reports-browser-view.js
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file renamed from frontend/src/templates/reports/list_slow.html to frontend/src/components/views/reports-slow-browser-view/reports-slow-browser-view.html
NO CONTENT: file renamed from frontend/src/templates/reports/list_slow.html to frontend/src/components/views/reports-slow-browser-view/reports-slow-browser-view.html
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file renamed from frontend/src/controllers/reports/list_slow.js to frontend/src/components/views/reports-slow-browser-view/reports-slow-browser-view.js
NO CONTENT: file renamed from frontend/src/controllers/reports/list_slow.js to frontend/src/components/views/reports-slow-browser-view/reports-slow-browser-view.js
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
General Comments 0
You need to be logged in to leave comments. Login now