Beispiel #1
0
    def stats(self, stream, field, start=None, end=None):
        """Get stats for a given field in a stream.

           Arguments:
           stream -- A stream object corresponding to a stream stored in Sentenai.
           field  -- A dotted field name for a numeric field in the stream.
           start  -- Optional argument indicating start time in stream for calculations.
           end    -- Optional argument indicating end time in stream for calculations.
        """
        args = {}
        if start:
            args['start'] = start.isoformat() + ("Z"
                                                 if not start.tzinfo else "")
        if end: args['end'] = end.isoformat() + ("Z" if not end.tzinfo else "")

        url = "/".join(
            [self.host, "streams",
             stream()['name'], "fields", field, "stats"])

        resp = self.session.get(url, params=args)

        if resp.status_code == 404:
            raise NotFound(
                'The field at "/streams/{}/fields/{}" does not exist'.format(
                    stream()['name'], field))
        else:
            status_codes(resp)

        return resp.json()
Beispiel #2
0
    def get(self, stream, eid=None):
        """Get event or stream as JSON.

        Arguments:
           stream -- A stream object corresponding to a stream stored
                     in Sentenai.
           eid    -- A unique ID corresponding to an event stored within
                     the stream.
        """
        if eid:
            url = "/".join(
                [self.host, "streams",
                 stream()['name'], "events", eid])
        else:
            url = "/".join([self.host, "streams", stream()['name']])

        resp = self.session.get(url)

        if resp.status_code == 404 and eid is not None:
            raise NotFound('The event at "/streams/{}/events/{}" '
                           'does not exist'.format(stream()['name'], eid))
        elif resp.status_code == 404:
            raise NotFound('The stream at "/streams/{}" '
                           'does not exist'.format(stream()['name'], eid))
        else:
            status_codes(resp)

        if eid is not None:
            return {
                'id': resp.headers['location'],
                'ts': resp.headers['timestamp'],
                'event': resp.json()
            }
        else:
            return resp.json()
Beispiel #3
0
    def streams(self, name=None, meta={}):
        """Get list of available streams.

        Optionally, parameters may be supplied to enable searching
        for stream subsets.

        Arguments:
           name -- A regular expression pattern to search names for
           meta -- A dictionary of key/value pairs to match from stream
                   metadata
        """
        url = "/".join([self.host, "streams"])
        resp = self.session.get(url)
        status_codes(resp)

        def filtered(s):
            f = True
            if name:
                f = bool(re.search(name, s['name']))
            for k, v in meta.items():
                f = f and s.get('meta', {}).get(k) == v
            return f

        try:
            return [stream(**v) for v in resp.json() if filtered(v)]
        except:
            raise SentenaiException("Something went wrong")
Beispiel #4
0
    def validate(self, data):
        ts = data.get('ts')
        try:
            if isinstance(ts, string_types):
                ts = cts(ts)
            if not ts.tzinfo:
                ts = pytz.utc.localize(ts)
        except:
            return (data, "invalid timestamp")

        sid = data.get('stream')
        if isinstance(sid, Stream):
            strm = sid
        elif sid:
            strm = stream(str(sid))
        else:
            return (data, "missing stream")

        try:
            evt = data['event']
        except KeyError:
            return (data, "missing event data")
        except Exception:
            return (data, "invalid event data")
        else:
            return {
                "stream": strm,
                "timestamp": ts,
                "id": data.get('id'),
                "event": evt
            }
Beispiel #5
0
    def destroy(self, stream):
        """Delete stream.

        Argument:
           stream -- A stream object corresponding to a stream stored
                     in Sentenai.
        """
        url = "/".join([self.host, "streams", stream()['name']])
        headers = {'auth-key': self.auth_key}
        resp = requests.delete(url, headers=headers)
        status_codes(resp)
        return None
Beispiel #6
0
    def validate(self, data):
        ts = data.get('ts')
        try:
            if not ts.tzinfo:
                ts = pytz.localize(ts)
        except:
            return (data, "invalid timestamp")

        sid = data.get('id')
        if sid: sid = str(sid)

        try:
            evt = data['event']
        except KeyError:
            return (data, "missing event data")
        except Exception:
            return (data, "invalid event data")
        else:
            return {"stream": stream(sid), "timestamp": ts, "id": sid}
Beispiel #7
0
def build_url(host, stream, eid=None):
    """Build a url for the Sentenai API.

    Arguments:
        stream -- a stream object.
        eid -- an optional event id.

    Returns:
        url -- a URL for the Sentenai API endpoint to query a stream or event
    """
    if not isinstance(stream, Stream):
        raise TypeError("stream argument must be of type sentenai.Stream")

    def with_quoter(s):
        try:
            return quote(s)
        except:
            return quote(s.encode('utf-8', 'ignore'))

    url = [host, "streams", with_quoter(stream()['name'])]
    events = [] if eid is None else ["events", with_quoter(eid)]
    return "/".join(url + events)
Beispiel #8
0
    def range(self, stream, start, end):
        """Get all stream events between start (inclusive) and end (exclusive).

        Arguments:
           stream -- A stream object corresponding to a stream stored
                     in Sentenai.
           start  -- A datetime object representing the start of the requested
                     time range.
           end    -- A datetime object representing the end of the requested
                     time range.

           Result:
           A time ordered list of all events in a stream from `start` to `end`
        """
        url = "/".join([
            self.host, "streams",
            stream()['name'], "start",
            iso8601(start), "end",
            iso8601(end)
        ])
        resp = self.session.get(url)
        status_codes(resp)
        return [json.loads(line) for line in resp.text.splitlines()]
Beispiel #9
0
    def put(self, stream, event, id=None, timestamp=None):
        """Put a new event into a stream.

        Arguments:
           stream    -- A stream object corresponding to a stream stored
                        in Sentenai.
           event     -- A JSON-serializable dictionary containing an
                        event's data
           id        -- A user-specified id for the event that is unique to
                        this stream (optional)
           timestamp -- A user-specified datetime object representing the
                        time of the event. (optional)
        """
        headers = {'content-type': 'application/json'}
        jd = event

        if timestamp:
            headers['timestamp'] = iso8601(timestamp)

        if id:
            url = '{host}/streams/{sid}/events/{eid}'.format(
                sid=stream()['name'], host=self.host, eid=id)
            resp = self.session.put(url, json=jd, headers=headers)
            if resp.status_code not in [200, 201]:
                status_codes(resp)
            else:
                return id
        else:
            url = '{host}/streams/{sid}/events'.format(sid=stream._name,
                                                       host=self.host)
            resp = self.session.post(url, json=jd, headers=headers)
            if resp.status_code in [200, 201]:
                return resp.headers['location']
            else:
                status_codes(resp)
                raise APIError(resp)