mirror of
https://github.com/daniviga/bite.git
synced 2024-11-25 06:16:13 +01:00
Compare commits
3 Commits
5cff8b9c2c
...
f958692350
Author | SHA1 | Date | |
---|---|---|---|
f958692350 | |||
7f8cc03371 | |||
665b3e87d0 |
|
@ -4,7 +4,7 @@ Playing with IoT
|
||||||
|
|
||||||
[![Build Status](https://travis-ci.com/daniviga/bite.svg?branch=master)](https://travis-ci.com/daniviga/bite)
|
[![Build Status](https://travis-ci.com/daniviga/bite.svg?branch=master)](https://travis-ci.com/daniviga/bite)
|
||||||
![AGPLv3](./docs/.badges/agpl3.svg)
|
![AGPLv3](./docs/.badges/agpl3.svg)
|
||||||
![Python 3.8](./docs/.badges/python.svg)
|
![Python 3.9](./docs/.badges/python.svg)
|
||||||
![MQTT](./docs/.badges/mqtt.svg)
|
![MQTT](./docs/.badges/mqtt.svg)
|
||||||
![Moby](./docs/.badges/moby.svg)
|
![Moby](./docs/.badges/moby.svg)
|
||||||
![docker-compose 3.7+](./docs/.badges/docker-compose.svg)
|
![docker-compose 3.7+](./docs/.badges/docker-compose.svg)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# Generated by Django 3.1.3 on 2021-03-19 08:08
|
# Generated by Django 3.1.7 on 2021-03-25 10:55
|
||||||
|
|
||||||
import django.core.validators
|
import django.core.validators
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
|
@ -18,7 +18,7 @@ class Migration(migrations.Migration):
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='Telemetry',
|
name='Telemetry',
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(primary_key=True, serialize=False)),
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
('time', models.DateTimeField(auto_now_add=True, db_index=True)),
|
('time', models.DateTimeField(auto_now_add=True, db_index=True)),
|
||||||
('transport', models.CharField(choices=[('http', 'http'), ('mqtt', 'mqtt')], default='http', max_length=4)),
|
('transport', models.CharField(choices=[('http', 'http'), ('mqtt', 'mqtt')], default='http', max_length=4)),
|
||||||
('clock', models.IntegerField(null=True, validators=[django.core.validators.MinValueValidator(0)])),
|
('clock', models.IntegerField(null=True, validators=[django.core.validators.MinValueValidator(0)])),
|
||||||
|
@ -28,6 +28,7 @@ class Migration(migrations.Migration):
|
||||||
options={
|
options={
|
||||||
'verbose_name_plural': 'Telemetry',
|
'verbose_name_plural': 'Telemetry',
|
||||||
'ordering': ['-time', 'device'],
|
'ordering': ['-time', 'device'],
|
||||||
|
'unique_together': {('time', 'device')},
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
24
bite/telemetry/migrations/0002_timescale.py
Normal file
24
bite/telemetry/migrations/0002_timescale.py
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
# Generated by Django 3.1.7 on 2021-03-25 10:55
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('telemetry', '0001_initial'),
|
||||||
|
]
|
||||||
|
|
||||||
|
# Timescale requires an hyperscale table to have the field used for the
|
||||||
|
# partitioning ('time') to be in the any UNIQUE constraint.
|
||||||
|
# Because of that we have a unique_together on 'time' and 'device',
|
||||||
|
# however Django always adds an 'id' as PRIMARY_KEY.
|
||||||
|
# Django's 'id' isn't used as a foreign key, so we are dropping
|
||||||
|
# the contraint and simply adding an index to the 'id' column.
|
||||||
|
# We can now create the hypertable.
|
||||||
|
operations = [
|
||||||
|
migrations.RunSQL(
|
||||||
|
"ALTER TABLE telemetry_telemetry DROP CONSTRAINT telemetry_telemetry_pkey ;" # noqa: E501
|
||||||
|
"CREATE INDEX telemetry_telemetry_id_idx ON telemetry_telemetry(id);" # noqa: E501
|
||||||
|
"SELECT create_hypertable('telemetry_telemetry', 'time');"),
|
||||||
|
]
|
|
@ -30,7 +30,6 @@ def telemetry_validation(value):
|
||||||
|
|
||||||
|
|
||||||
class Telemetry(models.Model):
|
class Telemetry(models.Model):
|
||||||
id = models.AutoField(primary_key=True)
|
|
||||||
device = models.ForeignKey(Device, on_delete=models.CASCADE)
|
device = models.ForeignKey(Device, on_delete=models.CASCADE)
|
||||||
time = models.DateTimeField(db_index=True, auto_now_add=True)
|
time = models.DateTimeField(db_index=True, auto_now_add=True)
|
||||||
transport = models.CharField(max_length=4,
|
transport = models.CharField(max_length=4,
|
||||||
|
@ -43,6 +42,7 @@ class Telemetry(models.Model):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ['-time', 'device']
|
ordering = ['-time', 'device']
|
||||||
|
unique_together = ['time', 'device']
|
||||||
verbose_name_plural = "Telemetry"
|
verbose_name_plural = "Telemetry"
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
|
|
@ -17,9 +17,10 @@
|
||||||
# You should have received a copy of the GNU Affero General Public License
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
from django.http import Http404
|
from django.http import Http404
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.dateparse import parse_datetime
|
from django.utils.dateparse import parse_date, parse_datetime
|
||||||
from rest_framework.views import APIView
|
from rest_framework.views import APIView
|
||||||
from rest_framework.viewsets import ModelViewSet
|
from rest_framework.viewsets import ModelViewSet
|
||||||
|
|
||||||
|
@ -69,12 +70,16 @@ class TelemetryRange(ModelViewSet):
|
||||||
lookup_field = 'device'
|
lookup_field = 'device'
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def datetime_validation(datetime):
|
def datetime_validation(time):
|
||||||
parsed_datetime = parse_datetime(datetime)
|
parsed_datetime = parse_datetime(time)
|
||||||
|
if parsed_datetime is None:
|
||||||
|
parsed_datetime = parse_date(time)
|
||||||
if parsed_datetime is None:
|
if parsed_datetime is None:
|
||||||
raise ParseError({
|
raise ParseError({
|
||||||
datetime: 'Invalid date format'
|
datetime: 'Invalid date format'
|
||||||
})
|
})
|
||||||
|
parsed_datetime = datetime.combine(
|
||||||
|
parsed_datetime, datetime.min.time())
|
||||||
return timezone.make_aware(parsed_datetime)
|
return timezone.make_aware(parsed_datetime)
|
||||||
|
|
||||||
def list(self, request, device, time_from, time_to=None):
|
def list(self, request, device, time_from, time_to=None):
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="93.0" height="20"><linearGradient id="smooth" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="round"><rect width="93.0" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#round)"><rect width="65.5" height="20" fill="#555"/><rect x="65.5" width="27.5" height="20" fill="#007ec6"/><rect width="93.0" height="20" fill="url(#smooth)"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="110"><image x="5" y="3" width="14" height="14" xlink:href="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMDAgMTAwIj4KICA8ZGVmcz4KICAgIDxsaW5lYXJHcmFkaWVudCBpZD0icHlZZWxsb3ciIGdyYWRpZW50VHJhbnNmb3JtPSJyb3RhdGUoNDUpIj4KICAgICAgPHN0b3Agc3RvcC1jb2xvcj0iI2ZlNSIgb2Zmc2V0PSIwLjYiLz4KICAgICAgPHN0b3Agc3RvcC1jb2xvcj0iI2RhMSIgb2Zmc2V0PSIxIi8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPGxpbmVhckdyYWRpZW50IGlkPSJweUJsdWUiIGdyYWRpZW50VHJhbnNmb3JtPSJyb3RhdGUoNDUpIj4KICAgICAgPHN0b3Agc3RvcC1jb2xvcj0iIzY5ZiIgb2Zmc2V0PSIwLjQiLz4KICAgICAgPHN0b3Agc3RvcC1jb2xvcj0iIzQ2OCIgb2Zmc2V0PSIxIi8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogIDwvZGVmcz4KCiAgPHBhdGggZD0iTTI3LDE2YzAtNyw5LTEzLDI0LTEzYzE1LDAsMjMsNiwyMywxM2wwLDIyYzAsNy01LDEyLTExLDEybC0yNCwwYy04LDAtMTQsNi0xNCwxNWwwLDEwbC05LDBjLTgsMC0xMy05LTEzLTI0YzAtMTQsNS0yMywxMy0yM2wzNSwwbDAtM2wtMjQsMGwwLTlsMCwweiBNODgsNTB2MSIgZmlsbD0idXJsKCNweUJsdWUpIi8+CiAgPHBhdGggZD0iTTc0LDg3YzAsNy04LDEzLTIzLDEzYy0xNSwwLTI0LTYtMjQtMTNsMC0yMmMwLTcsNi0xMiwxMi0xMmwyNCwwYzgsMCwxNC03LDE0LTE1bDAtMTBsOSwwYzcsMCwxMyw5LDEzLDIzYzAsMTUtNiwyNC0xMywyNGwtMzUsMGwwLDNsMjMsMGwwLDlsMCwweiBNMTQwLDUwdjEiIGZpbGw9InVybCgjcHlZZWxsb3cpIi8+CgogIDxjaXJjbGUgcj0iNCIgY3g9IjY0IiBjeT0iODgiIGZpbGw9IiNGRkYiLz4KICA8Y2lyY2xlIHI9IjQiIGN4PSIzNyIgY3k9IjE1IiBmaWxsPSIjRkZGIi8+Cjwvc3ZnPgo="/><text x="422.5" y="150" fill="#010101" fill-opacity=".3" transform="scale(0.1)" textLength="385.0" lengthAdjust="spacing">python</text><text x="422.5" y="140" transform="scale(0.1)" textLength="385.0" lengthAdjust="spacing">python</text><text x="782.5" y="150" fill="#010101" fill-opacity=".3" transform="scale(0.1)" textLength="175.0" lengthAdjust="spacing">3.8</text><text x="782.5" y="140" transform="scale(0.1)" textLength="175.0" lengthAdjust="spacing">3.8</text></g></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="93.0" height="20"><linearGradient id="smooth" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="round"><rect width="93.0" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#round)"><rect width="65.5" height="20" fill="#555"/><rect x="65.5" width="27.5" height="20" fill="#007ec6"/><rect width="93.0" height="20" fill="url(#smooth)"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="110"><image x="5" y="3" width="14" height="14" xlink:href="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMDAgMTAwIj4KICA8ZGVmcz4KICAgIDxsaW5lYXJHcmFkaWVudCBpZD0icHlZZWxsb3ciIGdyYWRpZW50VHJhbnNmb3JtPSJyb3RhdGUoNDUpIj4KICAgICAgPHN0b3Agc3RvcC1jb2xvcj0iI2ZlNSIgb2Zmc2V0PSIwLjYiLz4KICAgICAgPHN0b3Agc3RvcC1jb2xvcj0iI2RhMSIgb2Zmc2V0PSIxIi8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPGxpbmVhckdyYWRpZW50IGlkPSJweUJsdWUiIGdyYWRpZW50VHJhbnNmb3JtPSJyb3RhdGUoNDUpIj4KICAgICAgPHN0b3Agc3RvcC1jb2xvcj0iIzY5ZiIgb2Zmc2V0PSIwLjQiLz4KICAgICAgPHN0b3Agc3RvcC1jb2xvcj0iIzQ2OCIgb2Zmc2V0PSIxIi8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogIDwvZGVmcz4KCiAgPHBhdGggZD0iTTI3LDE2YzAtNyw5LTEzLDI0LTEzYzE1LDAsMjMsNiwyMywxM2wwLDIyYzAsNy01LDEyLTExLDEybC0yNCwwYy04LDAtMTQsNi0xNCwxNWwwLDEwbC05LDBjLTgsMC0xMy05LTEzLTI0YzAtMTQsNS0yMywxMy0yM2wzNSwwbDAtM2wtMjQsMGwwLTlsMCwweiBNODgsNTB2MSIgZmlsbD0idXJsKCNweUJsdWUpIi8+CiAgPHBhdGggZD0iTTc0LDg3YzAsNy04LDEzLTIzLDEzYy0xNSwwLTI0LTYtMjQtMTNsMC0yMmMwLTcsNi0xMiwxMi0xMmwyNCwwYzgsMCwxNC03LDE0LTE1bDAtMTBsOSwwYzcsMCwxMyw5LDEzLDIzYzAsMTUtNiwyNC0xMywyNGwtMzUsMGwwLDNsMjMsMGwwLDlsMCwweiBNMTQwLDUwdjEiIGZpbGw9InVybCgjcHlZZWxsb3cpIi8+CgogIDxjaXJjbGUgcj0iNCIgY3g9IjY0IiBjeT0iODgiIGZpbGw9IiNGRkYiLz4KICA8Y2lyY2xlIHI9IjQiIGN4PSIzNyIgY3k9IjE1IiBmaWxsPSIjRkZGIi8+Cjwvc3ZnPgo="/><text x="422.5" y="150" fill="#010101" fill-opacity=".3" transform="scale(0.1)" textLength="385.0" lengthAdjust="spacing">python</text><text x="422.5" y="140" transform="scale(0.1)" textLength="385.0" lengthAdjust="spacing">python</text><text x="782.5" y="150" fill="#010101" fill-opacity=".3" transform="scale(0.1)" textLength="175.0" lengthAdjust="spacing">3.9</text><text x="782.5" y="140" transform="scale(0.1)" textLength="175.0" lengthAdjust="spacing">3.9</text></g></svg>
|
||||||
|
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.4 KiB |
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user