2 Commits

Author SHA1 Message Date
Lavikainen Joel 5e6b4153e6 Add data endpoint and create infoscreen view for coffee stats 2017-11-19 17:40:04 +02:00
Lavikainen Joel 66e86ab621 Save brewing data as db model 2017-11-19 14:19:37 +02:00
15 changed files with 205 additions and 4 deletions
@@ -0,0 +1,74 @@
from django.core.management.base import BaseCommand, CommandError
from django.utils import timezone
from django.conf import settings
from coffee_scale.models import Brewing
import paho.mqtt.client as mqtt
import random
brewing = False
lastbrew = timezone.now()
lastcups = 0
def on_connect(client, userdata, flags, rc):
if rc != 0:
print("Failed to connect with result code: {}".format(rc))
print("Connected with result code: {}".format(rc))
client.subscribe(settings.MQTT_SETTINGS.HOST.TOPICS.CUPS)
client.subscribe(settings.MQTT_SETTINGS.HOST.TOPICS.BREWING)
client.subscribe(settings.MQTT_SETTINGS.HOST.TOPICS.BREW_TIME)
def on_message(client, userdata, message):
print("%s %s".format(message.topic, message.payload.decode()))
def on_message_cups(client, userdata, message):
cups = int(message.payload.decode())
print("cups: {}".format(cups))
print("{}".format(timezone.now()))
# checks if new coffee was brewed so we don't add the same brewing again to db
global lastcups # ;/ have to use global to store state instead of class
if cups > lastcups:
new_brew = Brewing(cups=cups, time=timezone.now())
print(new_brew.time)
new_brew.save()
lastcups = cups
def on_message_brewtime(client, userdata, message):
brewtime = datetime.fromtimestamp(int(message.payload.decode()))
print("brewtime: {}".format(brewtime))
def on_message_brewing(client, userdata, message):
brewing = bool(int(message.payload.decode()))
print("brewing: {}".format(brewing))
class Command(BaseCommand):
help = "Fetches coffee mqtt messages"
def add_arquments(self, parser):
pass
def handle(self, *args, **options):
self.username = "coffee-user-%d".format(random.randint(0, 100))
self.client = mqtt.Client("coffee")
self.client.username_pw_set(self.username, password=None)
self.client.tls_set()
# callbacks for different topics
self.client.message_callback_add(settings.MQTT_SETTINGS.HOST.TOPICS.BREW_TIME, on_message_brewtime)
self.client.message_callback_add(settings.MQTT_SETTINGS.HOST.TOPICS.CUPS, on_message_cups)
self.client.message_callback_add(settings.MQTT_SETTINGS.HOST.TOPICS.BREWING, on_message_brewing)
# self.client.connect("localhost", port=1883) # used for local testing
self.client.connect(settings.MQTT_SETTINGS.HOST, port=settings.MQTT_SETTINGS.PORT)
self.client.on_message = on_message
self.client.on_connect = on_connect
while True:
self.client.loop()
+24
View File
@@ -0,0 +1,24 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2017-11-19 10:55
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Brewing',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('cups', models.PositiveSmallIntegerField()),
('time', models.DateTimeField()),
],
),
]
+4 -1
View File
@@ -1,3 +1,6 @@
from django.db import models
# Create your models here.
class Brewing(models.Model):
cups = models.PositiveSmallIntegerField()
time = models.DateTimeField()
+2
View File
@@ -5,6 +5,7 @@ from infoscreen.models import Rotation, InfoItem, InfoInstance
from infoscreen.models import ImageInfoItem, ExternalImageInfoItem, ABBInfoItem
from infoscreen.models import ExternalWebsiteInfoItem
from infoscreen.models import VideoInfoItem
from infoscreen.models import CoffeeStatsInfoItem
# Register your models here.
admin.site.register(Rotation)
@@ -15,3 +16,4 @@ admin.site.register(ABBInfoItem)
admin.site.register(InfoInstance)
admin.site.register(ExternalWebsiteInfoItem)
admin.site.register(VideoInfoItem)
admin.site.register(CoffeeStatsInfoItem)
@@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2017-11-19 14:41
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('infoscreen', '0006_delete_hsldatamodel'),
]
operations = [
migrations.CreateModel(
name='CoffeeStatsInfoItem',
fields=[
('infoitem_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='infoscreen.InfoItem')),
],
bases=('infoscreen.infoitem',),
),
]
+15
View File
@@ -269,6 +269,21 @@ class HslInfoItem(InfoItem):
return "/static/html/hsl_create.html"
class CoffeeStatsInfoItem(InfoItem):
"""Class for Coffee statistics Infoscreen item."""
display_name = _("Coffee statistics")
def get_template_url(self):
"""Return HSL infoitem template url."""
return "/static/html/coffee_stats.html"
@staticmethod
def get_create_template_url():
"""Call create HSL infoitem template url command."""
return "/static/html/coffee_stats_create.html"
class ExternalImageInfoItem(InfoItem):
"""Class for External Image Infoscreen item."""
+9
View File
@@ -0,0 +1,9 @@
<link rel="stylesheet" href="/static/css/hsl.css">
<link href="https://fonts.googleapis.com/css?family=Droid+Sans+Mono" rel="stylesheet">
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.19.1/locale/fi.js"></script>
<script type="text/javascript" src="dygraph.js"></script>
<link rel="stylesheet" src="dygraph.css" /></head>
<div class="container" ng-controller="CoffeeStatsController">
<div id="div_g"></div>
</div>
@@ -0,0 +1,11 @@
<div ng-controller="infoadmin_coffeestatsitem_create" style="margin-top:20px;">
<div>
Create new item to show coffee statistics. Name is used only as identifier
</div>
<div class="form-group">
<label>Name:</label>
<input type="text" ng-model="item.name"></input>
</div>
<input type="button" class="btn btn-success" ng-click="send()" value="create"></input>
</div>
<!--maybe later add option to choose data range, daily, monthly...etc.
@@ -186,5 +186,6 @@ var simple_controllers = [
"hslitem",
"websiteitem",
"apyitem",
"coffeestatsitem",
];
_.each(simple_controllers, controllerGenerator);
+15 -1
View File
@@ -142,5 +142,19 @@ app.controller('timetableCtrl',
update_clock();
load();
});
app.controller('CoffeeStatsController', function($scope, $http) {
function load() {
$http.get('/infoscreen/coffee_data')
.then(function(response) {
const g = new Dygraph(document.getElementById('div_g'), response.data, {
drawPoints: true,
valueRange: [0, 10],
labels: ['Time', 'Cups']
});
});
}
);
load();
});
+4
View File
@@ -26,6 +26,8 @@ from infoscreen.views import CurrentHSLView
from infoscreen.views import createApyItem
from infoscreen.views import hsl_timetable_settings
from infoscreen.views import get_apy_json
from infoscreen.views import createCoffeeStatsItem
from infoscreen.views import CoffeeStatsView
urlpatterns = [
url(r'^$', default),
@@ -45,6 +47,7 @@ urlpatterns = [
url(r'^create_sossoitem$', createSossoItem),
url(r'^create_eventitem$', createEventItem),
url(r'^create_hslitem$', createHslItem),
url(r'^create_coffeestatsitem$', createCoffeeStatsItem),
url(r'^create_apyitem$', createApyItem),
url(r'^create_websiteitem$', createExternalWebsiteItem),
url(r'^create_rotation$', create_rotation),
@@ -52,5 +55,6 @@ urlpatterns = [
url(r'^hsl_data$', CurrentHSLView),
url(r'^hsl_data/settings$', hsl_timetable_settings),
url(r'^apyjson', get_apy_json),
url(r'^coffee_data$', CoffeeStatsView),
]
+2
View File
@@ -22,6 +22,7 @@ from infoscreen.models import ExternalWebsiteInfoItem
from infoscreen.models import ImageUploadForm
from infoscreen.models import ApyInfoItem
from infoscreen.models import VideoInfoItem
from infoscreen.models import CoffeeStatsInfoItem
@login_required(login_url='/login')
@@ -188,3 +189,4 @@ createExternalWebsiteItem = create_item_generator(ExternalWebsiteInfoItem)
createEventItem = create_item_generator(EventInfoItem)
createApyItem = create_item_generator(ApyInfoItem)
createVideoItem = create_item_generator(VideoInfoItem)
createCoffeeStatsItem = create_item_generator(CoffeeStatsInfoItem)
+15
View File
@@ -2,8 +2,11 @@ from django.shortcuts import render
from django.http import HttpResponse, JsonResponse, HttpResponseBadRequest
from django.views.decorators.http import require_http_methods
from django.conf import settings
from django.utils import timezone
from django.db.models.functions import ExtractWeek
from infoscreen.models import Rotation, InfoItem, InfoInstance
from coffee_scale.models import Brewing
from infoscreen.hsl_fetcher import fetch as hsl_fetch
import json
@@ -99,3 +102,15 @@ def CurrentHSLView(request, *args, **kwargs):
return JsonResponse(error, status=200)
return JsonResponse(api_resp, status=200, safe=False)
@require_http_methods(["GET"])
def CoffeeStatsView(request, *args, **kwargs):
# stats_data = Brewing.objects.annotate(
# week=ExtractWeek('time')).values('week').get(time__week=timezone.now().isocalendar()[2])
# do filtering here based on the data we want to show
stats_data = {}
for entry in Brewing.objects.all():
stats_data[entry.id] = {'cups': entry.cups, 'time': entry.time}
return JsonResponse(stats_data, status=200, safe=False)
+4 -1
View File
@@ -14,11 +14,14 @@
"author": "SIK ry",
"license": "ISC",
"dependencies": {
"dygraphs": "^2.0.0",
"eslint": "3.19.0",
"remark-cli": "^4.0.0",
"remark-preset-lint-recommended": "^3.0.1"
},
"remarkConfig": {
"plugins": ["remark-preset-lint-recommended"]
"plugins": [
"remark-preset-lint-recommended"
]
}
}
+2 -1
View File
@@ -32,4 +32,5 @@ telepot==12.3
django-import-export==0.5.1
django-password-reset==1.0
pyexcel==0.5.6
pyexcel-xlsx==0.5.2
pyexcel-xlsx==0.5.2
paho-mqtt==1.3.1