utils.py 4.54 KB
Newer Older
1
from contextlib import contextmanager
2
import dogstats_wrapper as dog_stats_api
3 4
import logging
import requests
5
from django.conf import settings
6 7
from time import time
from uuid import uuid4
8
from django.utils.translation import get_language
9

10
log = logging.getLogger(__name__)
11

Calen Pennington committed
12

13
def strip_none(dic):
Rocky Duan committed
14 15
    return dict([(k, v) for k, v in dic.iteritems() if v is not None])

Calen Pennington committed
16

Rocky Duan committed
17 18 19 20
def strip_blank(dic):
    def _is_blank(v):
        return isinstance(v, str) and len(v.strip()) == 0
    return dict([(k, v) for k, v in dic.iteritems() if not _is_blank(v)])
21

Calen Pennington committed
22

23
def extract(dic, keys):
Rocky Duan committed
24 25 26 27
    if isinstance(keys, str):
        return strip_none({keys: dic.get(keys)})
    else:
        return strip_none({k: dic.get(k) for k in keys})
28

Calen Pennington committed
29

30 31
def merge_dict(dic1, dic2):
    return dict(dic1.items() + dic2.items())
32

Calen Pennington committed
33

34
@contextmanager
35
def request_timer(request_id, method, url, tags=None):
36
    start = time()
37 38
    with dog_stats_api.timer('comment_client.request.time', tags=tags):
        yield
39 40
    end = time()
    duration = end - start
41

42
    log.info(
43 44
        u"comment_client_request_log: request_id={request_id}, method={method}, "
        u"url={url}, duration={duration}".format(
45 46 47 48 49 50 51 52
            request_id=request_id,
            method=method,
            url=url,
            duration=duration
        )
    )


53 54 55 56 57 58 59 60 61 62
def perform_request(method, url, data_or_params=None, raw=False,
                    metric_action=None, metric_tags=None, paged_results=False):

    if metric_tags is None:
        metric_tags = []

    metric_tags.append(u'method:{}'.format(method))
    if metric_action:
        metric_tags.append(u'action:{}'.format(metric_action))

63 64
    if data_or_params is None:
        data_or_params = {}
65 66 67 68
    headers = {
        'X-Edx-Api-Key': getattr(settings, "COMMENTS_SERVICE_KEY", None),
        'Accept-Language': get_language(),
    }
69 70
    request_id = uuid4()
    request_id_dict = {'request_id': request_id}
71 72 73 74 75 76 77

    if method in ['post', 'put', 'patch']:
        data = data_or_params
        params = request_id_dict
    else:
        data = None
        params = merge_dict(data_or_params, request_id_dict)
78
    with request_timer(request_id, method, url, metric_tags):
79 80 81 82 83 84 85 86
        response = requests.request(
            method,
            url,
            data=data,
            params=params,
            headers=headers,
            timeout=5
        )
87

88 89 90 91 92 93 94 95
    metric_tags.append(u'status_code:{}'.format(response.status_code))
    if response.status_code > 200:
        metric_tags.append(u'result:failure')
    else:
        metric_tags.append(u'result:success')

    dog_stats_api.increment('comment_client.request.count', tags=metric_tags)

96
    if 200 < response.status_code < 500:
97
        raise CommentClientRequestError(response.text, response.status_code)
98 99 100
    # Heroku returns a 503 when an application is in maintenance mode
    elif response.status_code == 503:
        raise CommentClientMaintenanceError(response.text)
101
    elif response.status_code == 500:
102
        raise CommentClient500Error(response.text)
103
    else:
104
        if raw:
105 106
            return response.text
        else:
107 108 109 110 111 112 113 114 115
            try:
                data = response.json()
            except ValueError:
                raise CommentClientError(
                    u"Comments service returned invalid JSON for request {request_id}; first 100 characters: '{content}'".format(
                        request_id=request_id,
                        content=response.text[:100]
                    )
                )
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
            if paged_results:
                dog_stats_api.histogram(
                    'comment_client.request.paged.result_count',
                    value=len(data.get('collection', [])),
                    tags=metric_tags
                )
                dog_stats_api.histogram(
                    'comment_client.request.paged.page',
                    value=data.get('page', 1),
                    tags=metric_tags
                )
                dog_stats_api.histogram(
                    'comment_client.request.paged.num_pages',
                    value=data.get('num_pages', 1),
                    tags=metric_tags
                )
            return data
133

Calen Pennington committed
134

135 136 137 138 139 140 141
class CommentClientError(Exception):
    def __init__(self, msg):
        self.message = msg

    def __str__(self):
        return repr(self.message)

Calen Pennington committed
142

143 144 145 146 147 148 149
class CommentClientRequestError(CommentClientError):
    def __init__(self, msg, status_code=400):
        super(CommentClientRequestError, self).__init__(msg)
        self.status_code = status_code


class CommentClient500Error(CommentClientError):
150 151 152
    pass


153
class CommentClientMaintenanceError(CommentClientError):
154
    pass