You've already forked oncall
mirror of
https://github.com/linkedin/oncall.git
synced 2025-11-26 23:10:47 +02:00
Py3 migration
This commit is contained in:
@@ -17,7 +17,7 @@ def require_db():
|
||||
# Read config based on pytest root directory. Assumes config lives at oncall/configs/config.yaml
|
||||
cfg_path = os.path.join(str(pytest.config.rootdir), 'configs/config.yaml')
|
||||
with open(cfg_path) as f:
|
||||
config = yaml.load(f)
|
||||
config = yaml.safe_load(f)
|
||||
db.init(config['db'])
|
||||
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
import urllib
|
||||
import urllib.parse
|
||||
import requests
|
||||
from testutils import prefix, api_v0
|
||||
|
||||
@@ -163,5 +163,5 @@ def test_api_v0_schedules_with_spaces_in_roster_name(team):
|
||||
assert re.status_code == 201
|
||||
|
||||
re = requests.get(api_v0('teams/%s/rosters/%s/schedules' %
|
||||
(team_name, urllib.quote(roster_name, safe=''))))
|
||||
(team_name, urllib.parse.quote(roster_name, safe=''))))
|
||||
assert re.status_code == 200
|
||||
|
||||
2
setup.py
2
setup.py
@@ -14,7 +14,7 @@ setuptools.setup(
|
||||
packages=setuptools.find_packages('src'),
|
||||
include_package_data=True,
|
||||
install_requires=[
|
||||
'falcon==1.1.0',
|
||||
'falcon==1.4.1',
|
||||
'falcon-cors',
|
||||
'gevent',
|
||||
'ujson',
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
# Copyright (c) LinkedIn Corporation. All rights reserved. Licensed under the BSD-2 Clause license.
|
||||
# See LICENSE in the project root for license information.
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
from falcon import HTTPNotFound
|
||||
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ from ...utils import (
|
||||
)
|
||||
from ...constants import EVENT_DELETED, EVENT_EDITED
|
||||
|
||||
from events import columns, all_columns
|
||||
from .events import columns, all_columns
|
||||
|
||||
update_columns = {
|
||||
'start': '`start`=%(start)s',
|
||||
|
||||
@@ -76,7 +76,7 @@ def on_post(req, resp):
|
||||
events = events_0 + events_1
|
||||
# Validation checks
|
||||
now = time.time()
|
||||
if any(map(lambda ev: ev['start'] < now - constants.GRACE_PERIOD, events)):
|
||||
if any([ev['start'] < now - constants.GRACE_PERIOD for ev in events]):
|
||||
raise HTTPBadRequest('Invalid event swap request',
|
||||
'Cannot edit events in the past')
|
||||
if len(set(ev['team_id'] for ev in events)) > 1:
|
||||
|
||||
@@ -166,7 +166,7 @@ def on_get(req, resp):
|
||||
cursor = connection.cursor(db.DictCursor)
|
||||
|
||||
# Build where clause. If including subscriptions, deal with team parameters later
|
||||
params = req.params.viewkeys() - TEAM_PARAMS if include_sub else req.params
|
||||
params = req.params.keys() - TEAM_PARAMS if include_sub else req.params
|
||||
for key in params:
|
||||
val = req.get_param(key)
|
||||
if key in constraints:
|
||||
@@ -176,7 +176,7 @@ def on_get(req, resp):
|
||||
# Deal with team subscriptions and team parameters
|
||||
team_where = []
|
||||
subs_vals = []
|
||||
team_params = req.params.viewkeys() & TEAM_PARAMS
|
||||
team_params = req.params.keys() & TEAM_PARAMS
|
||||
if include_sub and team_params:
|
||||
|
||||
for key in team_params:
|
||||
|
||||
@@ -47,7 +47,7 @@ def events_to_ical(events, identifier):
|
||||
'%s %s shift: %s' % (event['team'], event['role'], full_name))
|
||||
cal_event.add('description',
|
||||
'%s\n' % full_name +
|
||||
'\n'.join(['%s: %s' % (mode, dest) for mode, dest in user['contacts'].iteritems()]))
|
||||
'\n'.join(['%s: %s' % (mode, dest) for mode, dest in user['contacts'].items()]))
|
||||
|
||||
# Attach info about the user oncall
|
||||
attendee = vCalAddress('MAILTO:%s' % user['contacts'].get('email'))
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
from ... import db
|
||||
from ...utils import load_json_body
|
||||
from ...auth import check_team_auth, login_required
|
||||
from schedules import get_schedules
|
||||
from .schedules import get_schedules
|
||||
from falcon import HTTPNotFound
|
||||
from oncall.bin.scheduler import load_scheduler
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# See LICENSE in the project root for license information.
|
||||
|
||||
from ... import db
|
||||
from schedules import get_schedules
|
||||
from .schedules import get_schedules
|
||||
from falcon import HTTPNotFound
|
||||
from oncall.bin.scheduler import load_scheduler
|
||||
import operator
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Copyright (c) LinkedIn Corporation. All rights reserved. Licensed under the BSD-2 Clause license.
|
||||
# See LICENSE in the project root for license information.
|
||||
|
||||
from urllib import unquote
|
||||
from urllib.parse import unquote
|
||||
from falcon import HTTPError, HTTPNotFound, HTTPBadRequest
|
||||
from ujson import dumps as json_dumps
|
||||
|
||||
@@ -129,7 +129,7 @@ def on_put(req, resp, team, roster):
|
||||
AND team_id = (SELECT id from team WHERE name = %s))''',
|
||||
(roster, team))
|
||||
roster_users = {row[0] for row in cursor}
|
||||
if not all(map(lambda x: x in roster_users, roster_order)):
|
||||
if not all([x in roster_users for x in roster_order]):
|
||||
raise HTTPBadRequest('Invalid roster order', 'All users in provided order must be part of the roster')
|
||||
if not len(roster_order) == len(roster_users):
|
||||
raise HTTPBadRequest('Invalid roster order', 'Roster order must include all roster members')
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Copyright (c) LinkedIn Corporation. All rights reserved. Licensed under the BSD-2 Clause license.
|
||||
# See LICENSE in the project root for license information.
|
||||
|
||||
from urllib import unquote
|
||||
from urllib.parse import unquote
|
||||
from falcon import HTTPNotFound, HTTPBadRequest, HTTP_200
|
||||
|
||||
from ...auth import login_required, check_team_auth
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Copyright (c) LinkedIn Corporation. All rights reserved. Licensed under the BSD-2 Clause license.
|
||||
# See LICENSE in the project root for license information.
|
||||
|
||||
from urllib import unquote
|
||||
from urllib.parse import unquote
|
||||
from falcon import HTTPError, HTTP_201, HTTPBadRequest, HTTPNotFound
|
||||
from ujson import dumps as json_dumps
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Copyright (c) LinkedIn Corporation. All rights reserved. Licensed under the BSD-2 Clause license.
|
||||
# See LICENSE in the project root for license information.
|
||||
|
||||
from urllib import unquote
|
||||
from urllib.parse import unquote
|
||||
from falcon import HTTPError, HTTP_201, HTTPBadRequest
|
||||
from ujson import dumps as json_dumps
|
||||
from ...utils import load_json_body, invalid_char_reg, create_audit
|
||||
@@ -27,7 +27,7 @@ def get_roster_by_team_id(cursor, team_id, params=None):
|
||||
where_params = []
|
||||
where_vals = []
|
||||
if params:
|
||||
for key, val in params.iteritems():
|
||||
for key, val in params.items():
|
||||
if key in constraints:
|
||||
where_params.append(constraints[key])
|
||||
where_vals.append(val)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Copyright (c) LinkedIn Corporation. All rights reserved. Licensed under the BSD-2 Clause license.
|
||||
# See LICENSE in the project root for license information.
|
||||
|
||||
from urllib import unquote
|
||||
from urllib.parse import unquote
|
||||
from falcon import HTTP_201, HTTPError, HTTPBadRequest
|
||||
from ujson import dumps as json_dumps
|
||||
|
||||
@@ -26,7 +26,7 @@ columns = {
|
||||
'scheduler': '`scheduler`.`name` AS `scheduler`'
|
||||
}
|
||||
|
||||
all_columns = columns.keys()
|
||||
all_columns = list(columns.keys())
|
||||
|
||||
constraints = {
|
||||
'id': '`schedule`.`id` = %s',
|
||||
@@ -88,7 +88,7 @@ def get_schedules(filter_params, dbinfo=None, fields=None):
|
||||
from_clause = ['`schedule`']
|
||||
|
||||
if fields is None:
|
||||
fields = columns.keys()
|
||||
fields = list(columns.keys())
|
||||
if any(f not in columns for f in fields):
|
||||
raise HTTPBadRequest('Bad fields', 'One or more invalid fields')
|
||||
if 'roster' in fields:
|
||||
@@ -106,7 +106,7 @@ def get_schedules(filter_params, dbinfo=None, fields=None):
|
||||
|
||||
if 'id' not in fields:
|
||||
fields.append('id')
|
||||
fields = map(columns.__getitem__, fields)
|
||||
fields = list(map(columns.__getitem__, fields))
|
||||
cols = ', '.join(fields)
|
||||
from_clause = ' '.join(from_clause)
|
||||
|
||||
@@ -119,7 +119,7 @@ def get_schedules(filter_params, dbinfo=None, fields=None):
|
||||
connection, cursor = dbinfo
|
||||
|
||||
where = ' AND '.join(constraints[key] % connection.escape(value)
|
||||
for key, value in filter_params.iteritems()
|
||||
for key, value in filter_params.items()
|
||||
if key in constraints)
|
||||
query = 'SELECT %s FROM %s' % (cols, from_clause)
|
||||
if where:
|
||||
@@ -158,7 +158,7 @@ def get_schedules(filter_params, dbinfo=None, fields=None):
|
||||
start = row.pop('start')
|
||||
duration = row.pop('duration')
|
||||
ret[schedule_id]['events'].append({'start': start, 'duration': duration})
|
||||
data = ret.values()
|
||||
data = list(ret.values())
|
||||
|
||||
if scheduler:
|
||||
for schedule in data:
|
||||
|
||||
@@ -94,7 +94,7 @@ def on_get(req, resp, service, role=None):
|
||||
continue
|
||||
dest = row.pop('destination')
|
||||
ret[user]['contacts'][mode] = dest
|
||||
data = ret.values()
|
||||
data = list(ret.values())
|
||||
for event in data:
|
||||
override_number = team_override_numbers.get(event['team'])
|
||||
if override_number and event['role'] == 'primary':
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# See LICENSE in the project root for license information.
|
||||
import uuid
|
||||
import time
|
||||
from urllib import unquote
|
||||
from urllib.parse import unquote
|
||||
from falcon import HTTPNotFound, HTTPBadRequest, HTTPError
|
||||
from ujson import dumps as json_dumps
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Copyright (c) LinkedIn Corporation. All rights reserved. Licensed under the BSD-2 Clause license.
|
||||
# See LICENSE in the project root for license information.
|
||||
|
||||
from urllib import unquote
|
||||
from urllib.parse import unquote
|
||||
from falcon import HTTPError, HTTP_201, HTTPBadRequest
|
||||
from ujson import dumps as json_dumps
|
||||
from ... import db
|
||||
|
||||
@@ -95,7 +95,7 @@ def on_get(req, resp, team, role=None):
|
||||
continue
|
||||
dest = row.pop('destination')
|
||||
ret[user]['contacts'][mode] = dest
|
||||
data = ret.values()
|
||||
data = list(ret.values())
|
||||
for event in data:
|
||||
if override_number and event['role'] == 'primary':
|
||||
event['contacts']['call'] = override_number
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Copyright (c) LinkedIn Corporation. All rights reserved. Licensed under the BSD-2 Clause license.
|
||||
# See LICENSE in the project root for license information.
|
||||
|
||||
from urllib import unquote
|
||||
from urllib.parse import unquote
|
||||
|
||||
from falcon import HTTPNotFound
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Copyright (c) LinkedIn Corporation. All rights reserved. Licensed under the BSD-2 Clause license.
|
||||
# See LICENSE in the project root for license information.
|
||||
|
||||
from urllib import unquote
|
||||
from urllib.parse import unquote
|
||||
from falcon import HTTPError, HTTP_201
|
||||
from ujson import dumps as json_dumps
|
||||
from ...auth import login_required, check_team_auth
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Copyright (c) LinkedIn Corporation. All rights reserved. Licensed under the BSD-2 Clause license.
|
||||
# See LICENSE in the project root for license information.
|
||||
|
||||
from urllib import unquote
|
||||
from urllib.parse import unquote
|
||||
|
||||
from falcon import HTTPNotFound
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Copyright (c) LinkedIn Corporation. All rights reserved. Licensed under the BSD-2 Clause license.
|
||||
# See LICENSE in the project root for license information.
|
||||
|
||||
from urllib import unquote
|
||||
from urllib.parse import unquote
|
||||
from falcon import HTTPError, HTTP_201, HTTPBadRequest
|
||||
from ujson import dumps as json_dumps
|
||||
from ...utils import load_json_body, invalid_char_reg, subscribe_notifications, create_audit
|
||||
|
||||
@@ -77,7 +77,7 @@ def on_get(req, resp, user_name):
|
||||
formatted.append(event)
|
||||
else:
|
||||
links[event['link_id']].append(event)
|
||||
for events in links.itervalues():
|
||||
for events in links.values():
|
||||
first_event = min(events, key=operator.itemgetter('start'))
|
||||
first_event['num_events'] = len(events)
|
||||
formatted.append(first_event)
|
||||
|
||||
@@ -169,7 +169,7 @@ def on_put(req, resp, user_name):
|
||||
|
||||
if set_contacts:
|
||||
contacts = []
|
||||
for mode, dest in data['contacts'].iteritems():
|
||||
for mode, dest in data['contacts'].items():
|
||||
contact = {}
|
||||
contact['mode'] = mode
|
||||
contact['destination'] = dest
|
||||
|
||||
@@ -88,7 +88,7 @@ def on_get(req, resp, user_name):
|
||||
|
||||
cursor.close()
|
||||
connection.close()
|
||||
resp.body = json_dumps(data.values())
|
||||
resp.body = json_dumps(list(data.values()))
|
||||
|
||||
|
||||
@login_required
|
||||
|
||||
@@ -79,7 +79,7 @@ def get_user_data(fields, filter_params, dbinfo=None):
|
||||
connection, cursor = dbinfo
|
||||
|
||||
where = ' AND '.join(constraints[key] % connection.escape(value)
|
||||
for key, value in filter_params.iteritems()
|
||||
for key, value in filter_params.items()
|
||||
if key in constraints)
|
||||
query = 'SELECT %s FROM %s' % (cols, from_clause)
|
||||
if where:
|
||||
@@ -106,7 +106,7 @@ def get_user_data(fields, filter_params, dbinfo=None):
|
||||
continue
|
||||
dest = row.pop('destination')
|
||||
ret[user_id]['contacts'][mode] = dest
|
||||
data = ret.values()
|
||||
data = list(ret.values())
|
||||
return data
|
||||
|
||||
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
# Copyright (c) LinkedIn Corporation. All rights reserved. Licensed under the BSD-2 Clause license.
|
||||
# See LICENSE in the project root for license information.
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
from urllib import unquote_plus
|
||||
from urllib.parse import unquote_plus
|
||||
from importlib import import_module
|
||||
|
||||
import falcon
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
# Copyright (c) LinkedIn Corporation. All rights reserved. Licensed under the BSD-2 Clause license.
|
||||
# See LICENSE in the project root for license information.
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import logging
|
||||
import time
|
||||
import hmac
|
||||
@@ -254,7 +252,7 @@ def init(application, config):
|
||||
app_key_cache[row[0]] = row[1]
|
||||
cursor.close()
|
||||
connection.close()
|
||||
logger.debug('loaded applications: %s', app_key_cache.keys())
|
||||
logger.debug('loaded applications: %s', list(app_key_cache.keys()))
|
||||
|
||||
auth = importlib.import_module(config['module'])
|
||||
auth_manager = getattr(auth, 'Authenticator')(config)
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
# Copyright (c) LinkedIn Corporation. All rights reserved. Licensed under the BSD-2 Clause license.
|
||||
# See LICENSE in the project root for license information.
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
from falcon import HTTPNotFound, HTTPUnauthorized, HTTPBadRequest
|
||||
from falcon.util import uri
|
||||
from oncall.api.v0.users import get_user_data
|
||||
@@ -15,7 +13,7 @@ allow_no_auth = True
|
||||
|
||||
|
||||
def on_post(req, resp):
|
||||
login_info = uri.parse_query_string(req.context['body'])
|
||||
login_info = uri.parse_query_string(req.context['body'].decode('utf-8'))
|
||||
|
||||
user = login_info.get('username')
|
||||
password = login_info.get('password')
|
||||
|
||||
@@ -41,7 +41,7 @@ class Authenticator:
|
||||
|
||||
connection = ldap.initialize(self.ldap_url)
|
||||
connection.set_option(ldap.OPT_REFERRALS, 0)
|
||||
attrs = ['dn'] + self.attrs.values()
|
||||
attrs = ['dn'] + list(self.attrs.values())
|
||||
ldap_contacts = {}
|
||||
|
||||
if not password:
|
||||
@@ -58,7 +58,7 @@ class Authenticator:
|
||||
return False
|
||||
auth_user = result[0][0]
|
||||
ldap_attrs = result[0][1]
|
||||
for key, val in self.attrs.iteritems():
|
||||
for key, val in self.attrs.items():
|
||||
if ldap_attrs.get(val):
|
||||
if type(ldap_attrs.get(val)) == list:
|
||||
ldap_contacts[key] = ldap_attrs.get(val)[0]
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
# Copyright (c) LinkedIn Corporation. All rights reserved. Licensed under the BSD-2 Clause license.
|
||||
# See LICENSE in the project root for license information.
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import sys
|
||||
import yaml
|
||||
import logging
|
||||
@@ -40,7 +37,7 @@ default_timezone = None
|
||||
|
||||
def load_config_file(config_path):
|
||||
with open(config_path) as h:
|
||||
config = yaml.load(h)
|
||||
config = yaml.safe_load(h)
|
||||
|
||||
if 'init_config_hook' in config:
|
||||
try:
|
||||
@@ -151,7 +148,7 @@ def main():
|
||||
|
||||
init_messengers(config.get('messengers', []))
|
||||
|
||||
worker_tasks = [spawn(worker) for x in xrange(100)]
|
||||
worker_tasks = [spawn(worker) for x in range(100)]
|
||||
reminder_on = False
|
||||
if config['reminder']['activated']:
|
||||
reminder_worker = spawn(reminder.reminder, config['reminder'])
|
||||
|
||||
@@ -9,6 +9,7 @@ import multiprocessing
|
||||
import gunicorn.app.base
|
||||
from gunicorn.six import iteritems
|
||||
import oncall.utils
|
||||
import importlib
|
||||
|
||||
|
||||
class StandaloneApplication(gunicorn.app.base.BaseApplication):
|
||||
@@ -26,7 +27,7 @@ class StandaloneApplication(gunicorn.app.base.BaseApplication):
|
||||
|
||||
def load(self):
|
||||
import oncall
|
||||
reload(oncall.utils)
|
||||
importlib.reload(oncall.utils)
|
||||
|
||||
import oncall.app
|
||||
app = oncall.app.get_wsgi_app()
|
||||
|
||||
@@ -4,9 +4,6 @@
|
||||
# See LICENSE in the project root for license information.
|
||||
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import sys
|
||||
import time
|
||||
import importlib
|
||||
@@ -63,7 +60,7 @@ def main():
|
||||
for schedule in get_schedules({'team_id': team['id']}):
|
||||
schedule_map[schedule['scheduler']['name']].append(schedule)
|
||||
|
||||
for scheduler_name, schedules in schedule_map.iteritems():
|
||||
for scheduler_name, schedules in schedule_map.items():
|
||||
schedulers[scheduler_name].schedule(team, schedules, (connection, db_cursor))
|
||||
|
||||
# Sleep until next time
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
# Copyright (c) LinkedIn Corporation. All rights reserved. Licensed under the BSD-2 Clause license.
|
||||
# See LICENSE in the project root for license information.
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
from collections import defaultdict
|
||||
import logging
|
||||
import importlib
|
||||
|
||||
@@ -36,7 +36,7 @@ class influx(object):
|
||||
return
|
||||
now = str(datetime.now())
|
||||
payload = []
|
||||
for metric, value in metrics.iteritems():
|
||||
for metric, value in metrics.items():
|
||||
data = {
|
||||
'measurement': self.appname,
|
||||
'tags': {},
|
||||
|
||||
@@ -34,7 +34,7 @@ class prometheus(object):
|
||||
def send_metrics(self, metrics):
|
||||
if not self.enable_metrics:
|
||||
return
|
||||
for metric, value in metrics.iteritems():
|
||||
for metric, value in metrics.items():
|
||||
if metric not in self.gauges:
|
||||
self.gauges[metric] = Gauge(self.appname + '_' + metric, '')
|
||||
logger.info('Setting metrics gauge %s to %s', metric, value)
|
||||
|
||||
@@ -185,7 +185,7 @@ class Scheduler(object):
|
||||
|
||||
def weekday_from_schedule_time(self, schedule_time):
|
||||
'''Returns 0 for Monday, 1 for Tuesday...'''
|
||||
return (schedule_time / SECONDS_IN_A_DAY - 1) % 7
|
||||
return (schedule_time // SECONDS_IN_A_DAY - 1) % 7
|
||||
|
||||
def epoch_from_datetime(self, dt):
|
||||
'''
|
||||
@@ -221,7 +221,7 @@ class Scheduler(object):
|
||||
date = (tz.localize(date, is_dst=1)).astimezone(utc)
|
||||
td = date - UNIX_EPOCH
|
||||
# Convert timedelta to seconds
|
||||
return (td.microseconds + (td.seconds + td.days * 24 * 3600) * 10 ** 6) / 10 ** 6
|
||||
return (td.microseconds + (td.seconds + td.days * 24 * 3600) * 10 ** 6) // 10 ** 6
|
||||
|
||||
# End time helpers
|
||||
|
||||
@@ -244,7 +244,7 @@ class Scheduler(object):
|
||||
first_event = min(events, key=operator.itemgetter('start'))
|
||||
end = max(e['start'] + e['duration'] for e in events)
|
||||
period = end - first_event['start']
|
||||
return ((period + SECONDS_IN_A_WEEK - 1) / SECONDS_IN_A_WEEK)
|
||||
return ((period + SECONDS_IN_A_WEEK - 1) // SECONDS_IN_A_WEEK)
|
||||
|
||||
def calculate_future_events(self, schedule, cursor, start_epoch=None):
|
||||
period = self.get_period_len(schedule)
|
||||
@@ -394,8 +394,8 @@ class Scheduler(object):
|
||||
self.set_last_epoch(schedule['id'], last_epoch, cursor)
|
||||
|
||||
# Delete existing events from the start of the first event
|
||||
future_events = [filter(lambda x: x['start'] >= start_time, evs) for evs in future_events]
|
||||
future_events = filter(lambda x: x != [], future_events)
|
||||
future_events = [[x for x in evs if x['start'] >= start_time] for evs in future_events]
|
||||
future_events = [x for x in future_events if x != []]
|
||||
if future_events:
|
||||
first_event_start = min(future_events[0], key=lambda x: x['start'])['start']
|
||||
query = 'DELETE FROM %s WHERE schedule_id = %%s AND start >= %%s' % table_name
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import default
|
||||
from . import default
|
||||
|
||||
|
||||
class Scheduler(default.Scheduler):
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from oncall.utils import gen_link_id
|
||||
import default
|
||||
from . import default
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger()
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
from docutils import nodes
|
||||
from docutils.statemachine import ViewList
|
||||
|
||||
@@ -49,7 +46,7 @@ class AutofalconDirective(Directive):
|
||||
app = autohttp_import_object(self.arguments[0])
|
||||
for method, path, handler in get_routes(app):
|
||||
docstring = handler.__doc__
|
||||
if not isinstance(docstring, unicode):
|
||||
if not isinstance(docstring, str):
|
||||
analyzer = ModuleAnalyzer.for_module(handler.__module__)
|
||||
docstring = force_decode(docstring, analyzer.encoding)
|
||||
if not docstring and 'include-empty-docstring' not in self.options:
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
# All rights reserved. Licensed under the BSD-2 Clause license.
|
||||
# See LICENSE in the project root for license information.
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import logging
|
||||
import re
|
||||
@@ -63,7 +62,7 @@ mimes = {
|
||||
|
||||
INDEX_CONTENT_SETTING = {
|
||||
'user_setting_note': '',
|
||||
'footer': '<ul><li>Oncall © LinkedIn 2017</li></ul>'.decode('utf-8'),
|
||||
'footer': '<ul><li>Oncall © LinkedIn 2017</li></ul>',
|
||||
}
|
||||
|
||||
SLACK_INSTANCE = None
|
||||
|
||||
@@ -103,7 +103,7 @@ def fetch_ldap():
|
||||
}
|
||||
|
||||
base = LDAP_SETTINGS['base']
|
||||
attrs = ['distinguishedName'] + LDAP_SETTINGS['attrs'].values()
|
||||
attrs = ['distinguishedName'] + list(LDAP_SETTINGS['attrs'].values())
|
||||
query = LDAP_SETTINGS['query']
|
||||
|
||||
users = {}
|
||||
@@ -184,7 +184,7 @@ def import_user(username, ldap_contacts, engine):
|
||||
logger.exception('Failed to add user %s' % username)
|
||||
return
|
||||
stats['users_added'] += 1
|
||||
for key, value in ldap_contacts.iteritems():
|
||||
for key, value in ldap_contacts.items():
|
||||
if value and key in modes:
|
||||
logger.debug('\t%s -> %s' % (key, value))
|
||||
user_contact_add_sql = 'INSERT INTO `user_contact` (`user_id`, `mode_id`, `destination`) VALUES (%s, %s, %s)'
|
||||
@@ -328,7 +328,7 @@ def sync(config, engine):
|
||||
logger.exception('Failed to add user %s' % username)
|
||||
continue
|
||||
stats['users_added'] += 1
|
||||
for key, value in ldap_users[username].iteritems():
|
||||
for key, value in ldap_users[username].items():
|
||||
if value and key in modes:
|
||||
logger.debug('\t%s -> %s' % (key, value))
|
||||
user_contact_add_sql = 'INSERT INTO `user_contact` (`user_id`, `mode_id`, `destination`) VALUES (%s, %s, %s)'
|
||||
@@ -424,5 +424,5 @@ def main(config):
|
||||
if __name__ == '__main__':
|
||||
config_path = sys.argv[1]
|
||||
with open(config_path, 'r') as config_file:
|
||||
config = yaml.load(config_file)
|
||||
config = yaml.safe_load(config_file)
|
||||
main(config)
|
||||
|
||||
@@ -87,7 +87,7 @@ def sync_action(slack_client):
|
||||
mode_ids = {row[1]: row[0] for row in cursor}
|
||||
cursor.close()
|
||||
|
||||
slack_usernames = set(slack_users.viewkeys())
|
||||
slack_usernames = set(slack_users.keys())
|
||||
oncall_usernames = set(fetch_oncall_usernames(connection))
|
||||
|
||||
users_to_insert = slack_usernames - oncall_usernames
|
||||
|
||||
@@ -10,8 +10,8 @@ from falcon import HTTPBadRequest
|
||||
from importlib import import_module
|
||||
from datetime import datetime
|
||||
from pytz import timezone
|
||||
from constants import ONCALL_REMINDER
|
||||
import constants
|
||||
from .constants import ONCALL_REMINDER
|
||||
from . import constants
|
||||
import re
|
||||
|
||||
invalid_char_reg = re.compile(r'[!"#%-,\.\/;->@\[-\^`\{-~]+')
|
||||
@@ -30,7 +30,7 @@ def update_notification(x, y):
|
||||
|
||||
def read_config(config_path):
|
||||
with open(config_path, 'r') as config_file:
|
||||
return yaml.load(config_file)
|
||||
return yaml.safe_load(config_file)
|
||||
|
||||
|
||||
def create_notification(context, team_id, role_ids, type_name, users_involved, cursor, **kwargs):
|
||||
@@ -56,7 +56,7 @@ def create_notification(context, team_id, role_ids, type_name, users_involved, c
|
||||
|
||||
for notification in notifications:
|
||||
tz = notification['time_zone'] if notification['time_zone'] else 'UTC'
|
||||
for var_name, timestamp in kwargs.iteritems():
|
||||
for var_name, timestamp in kwargs.items():
|
||||
context[var_name] = ' '.join([datetime.fromtimestamp(timestamp,
|
||||
timezone(tz)).strftime('%Y-%m-%d %H:%M:%S'),
|
||||
tz])
|
||||
|
||||
@@ -65,7 +65,7 @@ def test_calculate_future_events_7_12_shifts(mocker):
|
||||
mocker.patch('time.time').return_value = time.mktime(mock_dt.timetuple())
|
||||
start = 3 * DAY + 12 * HOUR # Wednesday at noon
|
||||
events = []
|
||||
for i in xrange(7):
|
||||
for i in range(7):
|
||||
events.append({'start': start + DAY * i, 'duration': 12 * HOUR})
|
||||
schedule_foo = {
|
||||
'timezone': 'US/Eastern',
|
||||
@@ -95,7 +95,7 @@ def test_calculate_future_events_14_12_shifts(mocker):
|
||||
mocker.patch('time.time').return_value = time.mktime(mock_dt.timetuple())
|
||||
start = 3 * DAY + 12 * HOUR # Wednesday at noon
|
||||
events = []
|
||||
for i in xrange(14):
|
||||
for i in range(14):
|
||||
events.append({'start': start + DAY * i, 'duration': 12 * HOUR})
|
||||
schedule_foo = {
|
||||
'timezone': 'US/Central',
|
||||
@@ -106,7 +106,7 @@ def test_calculate_future_events_14_12_shifts(mocker):
|
||||
future_events, last_epoch = scheduler.calculate_future_events(schedule_foo, None)
|
||||
assert len(future_events) == 2
|
||||
assert len(future_events[1]) == 14
|
||||
days = range(21, 31) + range(1, 6)
|
||||
days = list(range(21, 31)) + list(range(1, 6))
|
||||
for ev, day in zip(future_events[1], days):
|
||||
start_dt = utc.localize(datetime.datetime.utcfromtimestamp(ev['start']))
|
||||
start_dt = start_dt.astimezone(timezone('US/Central'))
|
||||
|
||||
Reference in New Issue
Block a user