Code Monkey home page Code Monkey logo

tinyflux's Introduction

https://github.com/citrusvanilla/tinyflux/blob/master/artwork/tinyfluxdb-light.png?raw=true#gh-dark-mode-only

https://github.com/citrusvanilla/tinyflux/blob/master/artwork/tinyfluxdb-dark.png?raw=true#gh-light-mode-only

TinyFlux is the tiny time series database optimized for your happiness :)

TinyFlux is a time series version of TinyDB that is also written in Python and has no external dependencies. It's a great companion for small analytics workflows and apps, as well as at-home IOT data stores. TinyFlux has 100% coverage, tens of thousands of downloads, and no open issues.

Build Status Coverage Version Downloads

Quick Links

Installation

TinyFlux is hosted at PyPI and is easily downloadable with pip. TinyFlux has been tested with Python 3.7 - 3.12 and PyPy-3.9 on Linux and Windows platforms.

$ pip install tinyflux

Introduction

TinyFlux is:

  • time-centric: Python datetime objects are first-class citizens and queries are optimized for time, above all else.
  • optimized for your happiness: TinyFlux is designed to be simple and fun to use by providing a simple and clean API that can be learned in 5 minutes.
  • tiny: The current source code has 4,000 lines of code (with about 50% documentation) and 4,000 lines of tests. TinyFlux is about 150kb, unzipped.
  • written in pure Python: TinyFlux needs neither an external server nor any dependencies.
  • works on Python 3.7+ and PyPy-3.9: TinyFlux works on all modern versions of Python and PyPy.
  • 100% test coverage: No explanation needed.

To get started, head over to the TinyFlux docs. Examples can be found in the examples directory. You can also discuss topics related to TinyFlux including general development, extensions, or showcase your TinyFlux-based projects on the GitHub discussion forum.

Example Code Snippets

Writing to TinyFlux

>>> from datetime import datetime, timezone
>>> from tinyflux import TinyFlux, Point

>>> db = TinyFlux('/path/to/db.csv')

>>> p = Point(
...     time=datetime(2022, 5, 1, 16, 0, tzinfo=timezone.utc),
...     tags={"room": "bedroom"},
...     fields={"temp": 72.0}
... )
>>> db.insert(p, compact_key_prefixes=True)

Querying TinyFlux

>>> from tinyflux import FieldQuery, TagQuery, TimeQuery

>>> # Search for a tag value.
>>> Tag = TagQuery()
>>> db.search(Tag.room == 'bedroom')
[Point(time=2022-05-01T16:00:00+00:00, measurement=_default, tags=room:bedroom, fields=temp:72.0)]

>>> # Search for a field value.
>>> Field = FieldQuery()
>>> db.select("tag.room", Field.temp > 60.0)
["bedroom"]

>>> # Search for a time value.
>>> Time = TimeQuery()
>>> time_start = Time >= datetime(2019, 1, 1, tzinfo=timezone.utc)
>>> time_end = Time < datetime(2023, 1, 1, tzinfo=timezone.utc)
>>> db.count(time_start & time_end)
1

Full Example Notebooks and Workflows

The examples directory of this repository contains three common uses cases for TinyFlux and the associated boilerplate to get you started:

  1. Loading a TinyFlux DB from a CSV
  2. Local Analytics Workflow with a TinyFlux Database
  3. TinyFlux as a MQTT Datastore for IoT Devices
  4. TinyFlux at the Edge (with Backup Strategy)

Tips

Checkout some tips for working with TinyFlux here.

TinyFlux Across the Internet

Articles, tutorials, and other instances of TinyFlux:

Contributing

New ideas, new developer tools, improvements, and bugfixes are always welcome. Follow these guidelines before getting started:

  1. Make sure to read Getting Started and the Contributing Tooling and Conventions section of the documentation.
  2. Check GitHub for existing open issues, open a new issue or start a new discussion.
  3. To get started on a pull request, fork the repository on GitHub, create a new branch, and make updates.
  4. Write unit tests, ensure the code is 100% covered, update documentation where necessary, and format and style the code correctly.
  5. Send a pull request.

tinyflux's People

Contributors

alkorochkin avatar citrusvanilla avatar monegim avatar stansotn avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

tinyflux's Issues

Tag and Field keys should not be able to be removed in update callables

Describe the bug
Tag and Field keys can be removed during an update if an in-place removal is contained in the updater callable.

To Reproduce
Steps to reproduce the behavior:

>>> from tinyflux import TinyFlux, Point
>>> p = Point(tags={"a": "1"})
>>> db = TinyFlux("test.tinyflux")
>>> db.insert(p)
>>> def my_bad_updater(x):
>>>     x.pop("a")
>>>     return x
>>> db.update_all(tags=my_bad_updater)
>>> db.all()[0]
Point(time=2023-03-27T18:24:24.549741+00:00, measurement=_default)

Expected behavior
Tag and Field keys should not be able to be deleted using the update function.

Make package PEP 561 compliant

Functionality to improve and goal

Though tinyflux codebase is widely type annotated, the type annotations supplied with the tinyflux package are not used during static analysis (i.e. mypy) when imported as a dependency in other projects.

Specifically, this causes mypy to output the following error and render any class imported from tinyflux package as imprecise.

error: Skipping analyzing "tinyflux": module is installed, but missing library stubs or py.typed marker  [import]

Proposal

Add py.typed file at the root of the package directory.

Links and references

PEP 561

Field values of 0.0 are serialized to the DB as "_none"

Describe the bug
Field values of 0.0 are serialized to the DB as the string "_none", resulting in a deserialized value of None in Python. They should be serialized as the string "0.0".

To Reproduce
Steps to reproduce the behavior:

>>> import tinyflux
>>> p = tinyflux.Point(fields={"a": 0.0})
>>> p._serialize_to_list()
('2022-11-22T18:36:47.633509', '_default', '_field_a', '_none')

Expected behavior
Points with values of 0.0 for any field should serialize to "0.0", not "_none".

Python 3.12 support

Is your feature request related to a problem? Please describe.
Will TinyFlux work out of the box on Python 3.12?

Describe the solution you'd like
TinyFlux should work with Python 3.12 interpreters.

Describe alternatives you've considered
There is no alternative.

Modifying the reference of a Point's tags or fields when using MemoryStorage

Description
I just tried to use tinyflux as an "in-memory time-series database" using the storage=storages.MemoryStorage option but found that my query returns different results then when using a "real" csv file as store.

To reproduce, simple comment/uncomment near method1/method2 to use the different constructors

#!/usr/bin/env python3

from tinyflux import TinyFlux, Point, TagQuery, FieldQuery, MeasurementQuery, TimeQuery, storages
import datetime

# method1
tiny_flux = TinyFlux(storage=storages.MemoryStorage)
# method2
#tiny_flux = TinyFlux("test.csv")

data = {
    "measurement": "sma",
    "tags": {
        "id": "should",
    },
    "time": "0",
    "fields": {
        "value" : 1.0,
    }
}

p1 = Point(time = datetime.datetime.now(), measurement = data["measurement"], fields = data["fields"], tags = data["tags"])
tiny_flux.insert(p1)
data["tags"]["id"] = "not"
p2 = Point(time = datetime.datetime.now(), measurement = data["measurement"], fields = data["fields"], tags = data["tags"])
tiny_flux.insert(p2)

tags = TagQuery()
result = tiny_flux.search(tags.id == "should") # different behaviour with MemoryStorage and CSV?
print(result)

Is this the intended behaviour?

Data synchronisation from TinyFlux to InfluxDB or other

Is your feature request related to a problem? Please describe.
The need would be to keep the data locally on an IoT device but also sync it with a cloud database for backup. Could be Influx or something else.

Describe the solution you'd like
It would be amazing to have a feature allowing syncing local TinyFlux with external Database

Describe alternatives you've considered
I guess MQTT could be used to set up such workflow, but I'm trying to reduce as much as possible the number of tools needed.

Additional context
The device would run on the field could be damaged or lost but has a 4G connectivity, and we should be able to back up the data.

Empty string tag values are read from storage as "_none"

Describe the bug
When a Point containing a tag value of "" is inserted into the database and read back out, it returns as the string "_none".

To Reproduce

>>> from tinyflux import Point
>>> p = Point(tags={"a": ""})
>>> # Simulate and write/read op.
>>> new_p = tf.Point()._deserialize_from_list(p._serialize_to_list())
>>> new_p.tags["a"]
'_none'

Expected behavior
The tag value should remain an empty string after write/read ops.

Additional context
tinyflux version 0.2.6

CSVStorage append blank lines on Windows

Hi,
I am using this pretty library in my small application.
After porting my app to Windows 11, tinyflux shows an error when creating and reopening the file because it adds empty lines to the file when writing:

env\lib\site-packages\tinyflux\point.py", line 251, in _deserialize_from_list

    p_time = datetime.fromisoformat(row[0]).replace(tzinfo=timezone.utc)

IndexError: list index out of range

I did some research and found out that adding newline='' when opening the file solves the problem:

# Open the file for reading/writing
self._handle = open(path, mode=self._mode, encoding=encoding, newline=newline)

About newline param

Thanks!

Updating a Point with CSVStorage throws a TypeError

Describe the bug
Updating a Point after insertion using CSVStorage throws a TypeError.

To Reproduce

from tinyflux import Point, TinyFlux
db = TinyFlux("my_db.tinyflux")
p = Point(fields={"a": 1})
db.insert(p)
db.update_all(fields=lambda x: {"b": 2})

Expected behavior
A TypeError should not be thrown.

Retrieving Points with the max timestamp from the db

Is your feature request related to a problem? Please describe.
I am trying to get the very last entry inserted in the database. From the doc I can't see any way to do this efficiently.
I was hoping to be able to query by index but this is not an option.

Describe the solution you'd like
Ideally a db.last() function to return the last inserted point would work well.
If not I would be happy to know any efficient way to get the last entry of the db.

Describe alternatives you've considered
Well I could maybe query by time with a start time early enough to grab at least one point.
Then collect the last entry in the list. Not great.

Additional context
nothing to add

Compact storage with shorter tag and field key prefixes

My use case is with a few GB of data and trying to create a compact version of csvstorge to trim down data size. what I wanted to do is replacing _field_name with fname on the csv row, however, I notice I actually need to modify the serialize and deserialize from list of the point function.

do you have any solution for a more portable storage implementation?

and thanks for the awesome tinyflux

"Line contains NUL" Exception after editing and saving a TinyFlux CSV in Excel

hi, when i run the following codes, got errors

from tinyflux import TinyFlux,TagQuery,FieldQuery
db = TinyFlux('tm_db/features_store.csv')


Error Traceback (most recent call last)
File :2, in

File ~/miniconda3/lib/python3.8/site-packages/tinyflux/database.py:181, in TinyFlux.init(self, auto_index, *args, **kwargs)
179 # Reindex if auto_index is True.
180 if self._auto_index and not self._storage._initially_empty:
--> 181 self.reindex()

File ~/miniconda3/lib/python3.8/site-packages/tinyflux/database.py:754, in TinyFlux.reindex(self)
751 return
753 # Build the index.
--> 754 self._index.build(
755 self._storage._deserialize_storage_item(i) for i in self._storage
756 )
758 return

File ~/miniconda3/lib/python3.8/site-packages/tinyflux/index.py:185, in Index.build(self, points)
182 # A buffer for the new timestamps and their storage positions.
183 timestamp_buffer: List[Tuple[float, int]] = []
--> 185 for idx, point in enumerate(points):
186 self._num_items += 1
187 self._insert_measurements(idx, point.measurement)

File ~/miniconda3/lib/python3.8/site-packages/tinyflux/database.py:754, in (.0)
751 return
753 # Build the index.
--> 754 self._index.build(
755 self._storage._deserialize_storage_item(i) for i in self._storage
756 )
758 return

Error: line contains NUL

Add ability to remove individual tags and fields from Points

Since update is used to modify an point's fileds dict, new fileds will be added and existing fields will not be deleted.

point.fields.update(fields)

Let me explain it by the code:

>>> tags = TagQuery()
>>> q = tags.id == "some-unique-value"
>>> db.get(q)
Point(time=2023-02-01T19:26:28+00:00, measurement=_default, tags=id:some-unique-value, fields=sensor1:58)
... def generalize_sensors(fields):
...     if "sensor1" in fields:
...         return {"sensor": fields["sensor1"]}
...     else:
...         return fields
>>> db.update(q, fields=generalize_sensors)
>>> db.get(q)
Point(time=2023-02-01T19:26:28+00:00, measurement=_default, tags=id:some-unique-value, fields=sensor1:58; sensor:58)

I would expect that generalize_sensors will remove sensor1 from the fields since it does not use **fields in the returning dict.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.