Skip to content

Commit 545c1f4

Browse files
author
Ivan Kovalkovskyi
committed
Merge remote-tracking branch 'upstream/master'
2 parents a8024eb + ed276e2 commit 545c1f4

File tree

13 files changed

+320
-130
lines changed

13 files changed

+320
-130
lines changed

README.rst

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ InfluxDB-Python
33

44
.. image:: https://travis-ci.org/influxdata/influxdb-python.svg?branch=master
55
:target: https://travis-ci.org/influxdata/influxdb-python
6-
76
.. image:: https://readthedocs.org/projects/influxdb-python/badge/?version=latest&style
87
:target: http://influxdb-python.readthedocs.org/
98
:alt: Documentation Status
@@ -12,9 +11,13 @@ InfluxDB-Python
1211
:target: https://coveralls.io/r/influxdata/influxdb-python
1312
:alt: Coverage
1413

14+
.. image:: https://img.shields.io/pypi/v/influxdb.svg
15+
:target: https://pypi.python.org/pypi/influxdb
16+
:alt: PyPI Status
1517

18+
InfluxDB-Python is a client for interacting with InfluxDB_.
1619

17-
InfluxDB-Python is a client for interacting with InfluxDB_. Development of this library is maintained by
20+
Development of this library is maintained by:
1821

1922
+-----------+-------------------------------+
2023
| Github ID | URL |

docs/source/resultset.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ Using ``rs.get_points()`` will return a generator for all the points in the Resu
1818
Filtering by measurement
1919
------------------------
2020

21-
Using ``rs.get_points('cpu')`` will return a generator for all the points that are in a serie with measurement name ``cpu``, no matter the tags.
21+
Using ``rs.get_points('cpu')`` will return a generator for all the points that are in a series with measurement name ``cpu``, no matter the tags.
2222
::
2323

2424
rs = cli.query("SELECT * from cpu")
@@ -36,7 +36,7 @@ Using ``rs.get_points(tags={'host_name': 'influxdb.com'})`` will return a genera
3636
Filtering by measurement and tags
3737
---------------------------------
3838

39-
Using measurement name and tags will return a generator for all the points that are in a serie with the specified measurement name AND whose tags match the given tags.
39+
Using measurement name and tags will return a generator for all the points that are in a series with the specified measurement name AND whose tags match the given tags.
4040
::
4141

4242
rs = cli.query("SELECT * from cpu")

examples/tutorial_pandas.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ def main(host='localhost', port=8086):
2020
print("Create pandas DataFrame")
2121
df = pd.DataFrame(data=list(range(30)),
2222
index=pd.date_range(start='2014-11-16',
23-
periods=30, freq='H'))
23+
periods=30, freq='H'), columns=['0'])
2424

2525
print("Create database: " + dbname)
2626
client.create_database(dbname)

influxdb/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,4 @@
1818
]
1919

2020

21-
__version__ = '4.1.1'
21+
__version__ = '5.0.0'

influxdb/_dataframe_client.py

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from collections import defaultdict
1111

1212
import pandas as pd
13+
import numpy as np
1314

1415
from .client import InfluxDBClient
1516
from .line_protocol import _escape_tag
@@ -174,6 +175,7 @@ def query(self,
174175
expected_response_code=expected_response_code,
175176
raise_errors=raise_errors,
176177
chunked=chunked,
178+
database=database,
177179
chunk_size=chunk_size)
178180
results = super(DataFrameClient, self).query(query, **query_args)
179181
if query.strip().upper().startswith("SELECT"):
@@ -257,7 +259,7 @@ def _convert_dataframe_to_json(dataframe,
257259
{'measurement': measurement,
258260
'tags': dict(list(tag.items()) + list(tags.items())),
259261
'fields': rec,
260-
'time': int(ts.value / precision_factor)}
262+
'time': np.int64(ts.value / precision_factor)}
261263
for ts, tag, rec in zip(dataframe.index,
262264
dataframe[tag_columns].to_dict('record'),
263265
dataframe[field_columns].to_dict('record'))
@@ -274,6 +276,10 @@ def _convert_dataframe_to_lines(self,
274276
time_precision=None,
275277
numeric_precision=None):
276278

279+
dataframe = dataframe.dropna(how='all').copy()
280+
if len(dataframe) == 0:
281+
return []
282+
277283
if not isinstance(dataframe, pd.DataFrame):
278284
raise TypeError('Must be DataFrame, but type was: {0}.'
279285
.format(type(dataframe)))
@@ -319,11 +325,11 @@ def _convert_dataframe_to_lines(self,
319325

320326
# Make array of timestamp ints
321327
if isinstance(dataframe.index, pd.PeriodIndex):
322-
time = ((dataframe.index.to_timestamp().values.astype(int) /
323-
precision_factor).astype(int).astype(str))
328+
time = ((dataframe.index.to_timestamp().values.astype(np.int64) /
329+
precision_factor).astype(np.int64).astype(str))
324330
else:
325-
time = ((pd.to_datetime(dataframe.index).values.astype(int) /
326-
precision_factor).astype(int).astype(str))
331+
time = ((pd.to_datetime(dataframe.index).values.astype(np.int64) /
332+
precision_factor).astype(np.int64).astype(str))
327333

328334
# If tag columns exist, make an array of formatted tag keys and values
329335
if tag_columns:
@@ -357,20 +363,32 @@ def _convert_dataframe_to_lines(self,
357363

358364
# Make an array of formatted field keys and values
359365
field_df = dataframe[field_columns]
366+
360367
field_df = self._stringify_dataframe(field_df,
361368
numeric_precision,
362369
datatype='field')
363-
field_df = (field_df.columns.values + '=').tolist() + field_df
364-
field_df[field_df.columns[1:]] = ',' + field_df[field_df.columns[1:]]
365-
fields = field_df.sum(axis=1)
370+
371+
def format_line(line):
372+
line = line[~line.isnull()] # drop None entries
373+
return ",".join((line.index + '=' + line.values))
374+
375+
fields = field_df.apply(format_line, axis=1)
366376
del field_df
367377

368378
# Generate line protocol string
379+
measurement = _escape_tag(measurement)
369380
points = (measurement + tags + ' ' + fields + ' ' + time).tolist()
370381
return points
371382

372383
@staticmethod
373384
def _stringify_dataframe(dframe, numeric_precision, datatype='field'):
385+
386+
# Prevent modification of input dataframe
387+
dframe = dframe.copy()
388+
389+
# Keep the positions where Null values are found
390+
mask_null = dframe.isnull().values
391+
374392
# Find int and string columns for field-type data
375393
int_columns = dframe.select_dtypes(include=['integer']).columns
376394
string_columns = dframe.select_dtypes(include=['object']).columns
@@ -414,6 +432,8 @@ def _stringify_dataframe(dframe, numeric_precision, datatype='field'):
414432
dframe = dframe.apply(_escape_pandas_series)
415433

416434
dframe.columns = dframe.columns.astype(str)
435+
436+
dframe = dframe.where(~mask_null, None)
417437
return dframe
418438

419439
def _datetime_to_epoch(self, datetime, time_precision='s'):

influxdb/client.py

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -497,11 +497,6 @@ def _write_points(self,
497497
"Invalid time precision is given. "
498498
"(use 'n', 'u', 'ms', 's', 'm' or 'h')")
499499

500-
if self._use_udp and time_precision and time_precision != 's':
501-
raise ValueError(
502-
"InfluxDB only supports seconds precision for udp writes"
503-
)
504-
505500
if protocol == 'json':
506501
data = {
507502
'points': points
@@ -523,7 +518,9 @@ def _write_points(self,
523518
params['rp'] = retention_policy
524519

525520
if self._use_udp:
526-
self.send_packet(data, protocol=protocol)
521+
self.send_packet(
522+
data, protocol=protocol, time_precision=time_precision
523+
)
527524
else:
528525
self.write(
529526
data=data,
@@ -864,17 +861,19 @@ def get_list_privileges(self, username):
864861
text = "SHOW GRANTS FOR {0}".format(quote_ident(username))
865862
return list(self.query(text).get_points())
866863

867-
def send_packet(self, packet, protocol='json'):
864+
def send_packet(self, packet, protocol='json', time_precision=None):
868865
"""Send an UDP packet.
869866
870867
:param packet: the packet to be sent
871868
:type packet: (if protocol is 'json') dict
872-
(if protocol is 'line') sequence of line protocol strings
869+
(if protocol is 'line') list of line protocol strings
873870
:param protocol: protocol of input data, either 'json' or 'line'
874871
:type protocol: str
872+
:param time_precision: Either 's', 'm', 'ms' or 'u', defaults to None
873+
:type time_precision: str
875874
"""
876875
if protocol == 'json':
877-
data = make_lines(packet).encode('utf-8')
876+
data = make_lines(packet, time_precision).encode('utf-8')
878877
elif protocol == 'line':
879878
data = ('\n'.join(packet) + '\n').encode('utf-8')
880879
self.udp_socket.sendto(data, (self._host, self._udp_port))

influxdb/influxdb08/client.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -435,7 +435,7 @@ def _query(self, query, time_precision='s', chunked=False):
435435
else:
436436
chunked_param = 'false'
437437

438-
# Build the URL of the serie to query
438+
# Build the URL of the series to query
439439
url = "db/{0}/series".format(self._database)
440440

441441
params = {

influxdb/resultset.py

Lines changed: 35 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,12 @@ def error(self):
4141
def __getitem__(self, key):
4242
"""Retrieve the series name or specific set based on key.
4343
44-
:param key: Either a serie name, or a tags_dict, or
45-
a 2-tuple(serie_name, tags_dict).
46-
If the serie name is None (or not given) then any serie
44+
:param key: Either a series name, or a tags_dict, or
45+
a 2-tuple(series_name, tags_dict).
46+
If the series name is None (or not given) then any serie
4747
matching the eventual given tags will be given its points
4848
one after the other.
49-
To get the points of every serie in this resultset then
49+
To get the points of every series in this resultset then
5050
you have to provide None as key.
5151
:return: A generator yielding `Point`s matching the given key.
5252
NB:
@@ -93,22 +93,25 @@ def get_points(self, measurement=None, tags=None):
9393
(bytes, type(b''.decode()), type(None))):
9494
raise TypeError('measurement must be an str or None')
9595

96-
for serie in self._get_series():
97-
serie_name = serie.get('measurement', serie.get('name', 'results'))
98-
if serie_name is None:
96+
for series in self._get_series():
97+
series_name = series.get('measurement',
98+
series.get('name', 'results'))
99+
if series_name is None:
99100
# this is a "system" query or a query which
100101
# doesn't return a name attribute.
101102
# like 'show retention policies' ..
102103
if tags is None:
103-
for item in self._get_points_for_serie(serie):
104+
for item in self._get_points_for_series(series):
104105
yield item
105106

106-
elif measurement in (None, serie_name):
107+
elif measurement in (None, series_name):
107108
# by default if no tags was provided then
108-
# we will matches every returned serie
109-
serie_tags = serie.get('tags', {})
110-
if tags is None or self._tag_matches(serie_tags, tags):
111-
for item in self._get_points_for_serie(serie):
109+
# we will matches every returned series
110+
series_tags = series.get('tags', {})
111+
for item in self._get_points_for_series(series):
112+
if tags is None or \
113+
self._tag_matches(item, tags) or \
114+
self._tag_matches(series_tags, tags):
112115
yield item
113116

114117
def __repr__(self):
@@ -121,7 +124,7 @@ def __repr__(self):
121124
return "ResultSet({%s})" % ", ".join(items)
122125

123126
def __iter__(self):
124-
"""Yield one dict instance per serie result."""
127+
"""Yield one dict instance per series result."""
125128
for key in self.keys():
126129
yield list(self.__getitem__(key))
127130

@@ -131,10 +134,10 @@ def _tag_matches(tags, filter):
131134
for tag_name, tag_value in filter.items():
132135
# using _sentinel as I'm not sure that "None"
133136
# could be used, because it could be a valid
134-
# serie_tags value : when a serie has no such tag
137+
# series_tags value : when a series has no such tag
135138
# then I think it's set to /null/None/.. TBC..
136-
serie_tag_value = tags.get(tag_name, _sentinel)
137-
if serie_tag_value != tag_value:
139+
series_tag_value = tags.get(tag_name, _sentinel)
140+
if series_tag_value != tag_value:
138141
return False
139142

140143
return True
@@ -150,14 +153,14 @@ def __len__(self):
150153
def keys(self):
151154
"""Return the list of keys in the ResultSet.
152155
153-
:return: List of keys. Keys are tuples (serie_name, tags)
156+
:return: List of keys. Keys are tuples (series_name, tags)
154157
"""
155158
keys = []
156-
for serie in self._get_series():
159+
for series in self._get_series():
157160
keys.append(
158-
(serie.get('measurement',
159-
serie.get('name', 'results')),
160-
serie.get('tags', None))
161+
(series.get('measurement',
162+
series.get('name', 'results')),
163+
series.get('tags', None))
161164
)
162165
return keys
163166

@@ -167,24 +170,24 @@ def items(self):
167170
:return: List of tuples, (key, generator)
168171
"""
169172
items = []
170-
for serie in self._get_series():
171-
serie_key = (serie.get('measurement',
172-
serie.get('name', 'results')),
173-
serie.get('tags', None))
173+
for series in self._get_series():
174+
series_key = (series.get('measurement',
175+
series.get('name', 'results')),
176+
series.get('tags', None))
174177
items.append(
175-
(serie_key, self._get_points_for_serie(serie))
178+
(series_key, self._get_points_for_series(series))
176179
)
177180
return items
178181

179-
def _get_points_for_serie(self, serie):
180-
"""Return generator of dict from columns and values of a serie.
182+
def _get_points_for_series(self, series):
183+
"""Return generator of dict from columns and values of a series.
181184
182-
:param serie: One serie
185+
:param series: One series
183186
:return: Generator of dicts
184187
"""
185-
for point in serie.get('values', []):
188+
for point in series.get('values', []):
186189
yield self.point_from_cols_vals(
187-
serie['columns'],
190+
series['columns'],
188191
point
189192
)
190193

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy