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 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 ImageInfoItem, ExternalImageInfoItem, ABBInfoItem
from infoscreen.models import ExternalWebsiteInfoItem from infoscreen.models import ExternalWebsiteInfoItem
from infoscreen.models import VideoInfoItem from infoscreen.models import VideoInfoItem
from infoscreen.models import CoffeeStatsInfoItem
# Register your models here. # Register your models here.
admin.site.register(Rotation) admin.site.register(Rotation)
@@ -15,3 +16,4 @@ admin.site.register(ABBInfoItem)
admin.site.register(InfoInstance) admin.site.register(InfoInstance)
admin.site.register(ExternalWebsiteInfoItem) admin.site.register(ExternalWebsiteInfoItem)
admin.site.register(VideoInfoItem) 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" 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 ExternalImageInfoItem(InfoItem):
"""Class for External Image Infoscreen item.""" """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", "hslitem",
"websiteitem", "websiteitem",
"apyitem", "apyitem",
"coffeestatsitem",
]; ];
_.each(simple_controllers, controllerGenerator); _.each(simple_controllers, controllerGenerator);
+15 -1
View File
@@ -142,5 +142,19 @@ app.controller('timetableCtrl',
update_clock(); update_clock();
load(); 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 createApyItem
from infoscreen.views import hsl_timetable_settings from infoscreen.views import hsl_timetable_settings
from infoscreen.views import get_apy_json from infoscreen.views import get_apy_json
from infoscreen.views import createCoffeeStatsItem
from infoscreen.views import CoffeeStatsView
urlpatterns = [ urlpatterns = [
url(r'^$', default), url(r'^$', default),
@@ -45,6 +47,7 @@ urlpatterns = [
url(r'^create_sossoitem$', createSossoItem), url(r'^create_sossoitem$', createSossoItem),
url(r'^create_eventitem$', createEventItem), url(r'^create_eventitem$', createEventItem),
url(r'^create_hslitem$', createHslItem), url(r'^create_hslitem$', createHslItem),
url(r'^create_coffeestatsitem$', createCoffeeStatsItem),
url(r'^create_apyitem$', createApyItem), url(r'^create_apyitem$', createApyItem),
url(r'^create_websiteitem$', createExternalWebsiteItem), url(r'^create_websiteitem$', createExternalWebsiteItem),
url(r'^create_rotation$', create_rotation), url(r'^create_rotation$', create_rotation),
@@ -52,5 +55,6 @@ urlpatterns = [
url(r'^hsl_data$', CurrentHSLView), url(r'^hsl_data$', CurrentHSLView),
url(r'^hsl_data/settings$', hsl_timetable_settings), url(r'^hsl_data/settings$', hsl_timetable_settings),
url(r'^apyjson', get_apy_json), 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 ImageUploadForm
from infoscreen.models import ApyInfoItem from infoscreen.models import ApyInfoItem
from infoscreen.models import VideoInfoItem from infoscreen.models import VideoInfoItem
from infoscreen.models import CoffeeStatsInfoItem
@login_required(login_url='/login') @login_required(login_url='/login')
@@ -188,3 +189,4 @@ createExternalWebsiteItem = create_item_generator(ExternalWebsiteInfoItem)
createEventItem = create_item_generator(EventInfoItem) createEventItem = create_item_generator(EventInfoItem)
createApyItem = create_item_generator(ApyInfoItem) createApyItem = create_item_generator(ApyInfoItem)
createVideoItem = create_item_generator(VideoInfoItem) 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.http import HttpResponse, JsonResponse, HttpResponseBadRequest
from django.views.decorators.http import require_http_methods from django.views.decorators.http import require_http_methods
from django.conf import settings 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 infoscreen.models import Rotation, InfoItem, InfoInstance
from coffee_scale.models import Brewing
from infoscreen.hsl_fetcher import fetch as hsl_fetch from infoscreen.hsl_fetcher import fetch as hsl_fetch
import json import json
@@ -99,3 +102,15 @@ def CurrentHSLView(request, *args, **kwargs):
return JsonResponse(error, status=200) return JsonResponse(error, status=200)
return JsonResponse(api_resp, status=200, safe=False) 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", "author": "SIK ry",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"dygraphs": "^2.0.0",
"eslint": "3.19.0", "eslint": "3.19.0",
"remark-cli": "^4.0.0", "remark-cli": "^4.0.0",
"remark-preset-lint-recommended": "^3.0.1" "remark-preset-lint-recommended": "^3.0.1"
}, },
"remarkConfig": { "remarkConfig": {
"plugins": ["remark-preset-lint-recommended"] "plugins": [
"remark-preset-lint-recommended"
]
} }
} }
+1
View File
@@ -33,3 +33,4 @@ django-import-export==0.5.1
django-password-reset==1.0 django-password-reset==1.0
pyexcel==0.5.6 pyexcel==0.5.6
pyexcel-xlsx==0.5.2 pyexcel-xlsx==0.5.2
paho-mqtt==1.3.1