Badly implement mood pie charts

This commit is contained in:
Kumi 2021-03-01 18:05:14 +01:00
parent 57fa6c5d7e
commit f80b909c6e
6 changed files with 121 additions and 30 deletions

View file

@ -20,20 +20,23 @@ def pildata(image):
return f"data:img/jpeg;base64,{content}"
@register.simple_tag
def hvhtml(hvobject):
bokeh = hv.render(hvobject)
pan_tool = bokeh.select(dict(type=PanTool))
def bkhtml(bkobject, lock_y=False):
if lock_y:
pan_tool = bkobject.select(dict(type=PanTool))
pan_tool.dimensions = "width"
zoom_tool = bokeh.select(dict(type=WheelZoomTool))
zoom_tool = bkobject.select(dict(type=WheelZoomTool))
zoom_tool.dimensions = "width"
html = file_html(bokeh, INLINE)
html = file_html(bkobject, INLINE)
html = html.replace("http://localhost:5006/static/extensions/panel/css", "/static/frontend/vendor/panel")
return html
@register.simple_tag
def hvhtml(hvobject, lock_y=True):
return bkhtml(hv.render(hvobject), lock_y)
@register.simple_tag
def hvdata(hvobject):
html = hvhtml(hvobject)

View file

@ -10,6 +10,9 @@ from colorfield.fields import ColorField
from common.helpers import get_upload_path
class Mood(models.Model):
class Meta:
ordering = ["-value"]
user = models.ForeignKey(get_user_model(), models.CASCADE)
name = models.CharField(max_length=64)
icon = models.CharField(default="fas fa-star", max_length=64)

View file

@ -3,7 +3,13 @@ import pandas as pd
from django.utils import timezone
from math import pi
from bokeh.models import HoverTool
from bokeh.io import output_file, show
from bokeh.plotting import figure
from bokeh.transform import cumsum
from bokeh.layouts import row, column
from holoviews.operation import timeseries
from dateutil.relativedelta import relativedelta
@ -14,7 +20,7 @@ def moodstats(user):
tooltips = [
('Date', '@date{%F %H:%M}'),
('Value', '@value')
('Mood', '@name (@value)')
]
formatters = {
@ -23,7 +29,7 @@ def moodstats(user):
hover = HoverTool(tooltips=tooltips, formatters=formatters)
pointdict = {"date": [], "value": [], "color": []}
pointdict = {"date": [], "value": [], "color": [], "name": []}
for status in Status.objects.filter(user=user):
@ -31,6 +37,7 @@ def moodstats(user):
pointdict["date"].append(status.timestamp)
pointdict["value"].append(status.mood.value)
pointdict["color"].append(status.mood.color)
pointdict["name"].append(status.mood.name)
pointframe = pd.DataFrame.from_dict(pointdict)
@ -81,3 +88,71 @@ def activitystats(user):
output[activity]["weekly"] += 1
return output
def moodpies(user):
hv.extension('bokeh')
maxdate = timezone.now()
weekly_moods = Status.objects.filter(user=user, timestamp__lte=maxdate, timestamp__gte=maxdate - relativedelta(weeks=1))
monthly_moods = Status.objects.filter(user=user, timestamp__lte=maxdate, timestamp__gte=maxdate - relativedelta(months=1))
yearly_moods = Status.objects.filter(user=user, timestamp__lte=maxdate, timestamp__gte=maxdate - relativedelta(years=1))
weekly = dict()
colors = []
for mood in Mood.objects.filter(user=user):
weekly[mood.name] = 0
colors.append(mood.color)
monthly, yearly = weekly.copy(), weekly.copy()
for status in weekly_moods:
if status.mood:
weekly[status.mood.name] += 1
for status in monthly_moods:
if status.mood:
monthly[status.mood.name] += 1
for status in yearly_moods:
if status.mood:
yearly[status.mood.name] += 1
weekly_data = pd.Series(weekly).reset_index(name='value').rename(columns={'index':'mood'})
weekly_data['angle'] = weekly_data['value']/weekly_data['value'].sum() * 2*pi
weekly_data['color'] = colors
weekly_chart = figure(plot_height=350, title="Weekly", toolbar_location=None,
tools="hover", tooltips="@mood: @value")
weekly_chart.axis.visible = False
weekly_chart.wedge(x=0, y=1, radius=0.4,
start_angle=cumsum('angle', include_zero=True), end_angle=cumsum('angle'),
line_color="white", fill_color='color', legend='mood', source=weekly_data)
monthly_data = pd.Series(monthly).reset_index(name='value').rename(columns={'index':'mood'})
monthly_data['angle'] = monthly_data['value']/monthly_data['value'].sum() * 2*pi
monthly_data['color'] = colors
monthly_chart = figure(plot_height=350, title="Monthly", toolbar_location=None,
tools="hover", tooltips="@mood: @value")
monthly_chart.axis.visible = False
monthly_chart.wedge(x=0, y=1, radius=0.4,
start_angle=cumsum('angle', include_zero=True), end_angle=cumsum('angle'),
line_color="white", fill_color='color', legend='mood', source=monthly_data)
yearly_data = pd.Series(yearly).reset_index(name='value').rename(columns={'index':'mood'})
yearly_data['angle'] = yearly_data['value']/yearly_data['value'].sum() * 2*pi
yearly_data['color'] = colors
yearly_chart = figure(plot_height=350, title="Yearly", toolbar_location=None,
tools="hover", tooltips="@mood: @value")
yearly_chart.axis.visible = False
yearly_chart.wedge(x=0, y=1, radius=0.4,
start_angle=cumsum('angle', include_zero=True), end_angle=cumsum('angle'),
line_color="white", fill_color='color', legend='mood', source=yearly_data)
return column(weekly_chart, monthly_chart, yearly_chart)

View file

@ -1,6 +1,5 @@
{% extends "frontend/base.html" %}
{% load images %}
{% load request %}
{% block "content" %}
<div class="row">
@ -15,7 +14,18 @@
</div>
<!-- Card Body -->
<div class="card-body">
<iframe id="plot" src="plot/{% querystring %}" width="800px" height="450px"></iframe>
<iframe id="plot" src="plot/" width="800px" height="450px"></iframe>
</div>
</div>
<div class="card shadow mb-4">
<!-- Card Header - Dropdown -->
<div class="card-header py-3 d-flex flex-row align-items-center justify-content-between">
<h6 class="m-0 font-weight-bold text-primary">Mood stats</h6>
</div>
<!-- Card Body -->
<div class="card-body">
<iframe id="plot" src="pies/" width="800px" height="450px"></iframe>
</div>
</div>
</div>
@ -48,6 +58,7 @@
</div>
</div>
</div>
{% endblock %}

View file

@ -1,4 +1,4 @@
from .views import StatusListView, StatusViewView, StatusDeleteView, StatusEditView, StatusCreateView, ActivityListView, ActivityEditView, ActivityCreateView, ActivityDeleteView, MoodListView, MoodEditView, NotificationCreateView, NotificationDeleteView, NotificationEditView, NotificationListView, MoodStatisticsView, MoodCSVView, MoodPlotView
from .views import StatusListView, StatusViewView, StatusDeleteView, StatusEditView, StatusCreateView, ActivityListView, ActivityEditView, ActivityCreateView, ActivityDeleteView, MoodListView, MoodEditView, NotificationCreateView, NotificationDeleteView, NotificationEditView, NotificationListView, MoodStatisticsView, MoodCSVView, MoodPlotView, MoodPiesView
from django.urls import path, include
@ -23,4 +23,5 @@ urlpatterns = [
path('statistics/', MoodStatisticsView.as_view(), name="statistics"),
path('statistics/csv/', MoodCSVView.as_view(), name="statistics_csv"),
path('statistics/plot/', MoodPlotView.as_view(), name="statistics_plot"),
path('statistics/pies/', MoodPiesView.as_view(), name="statistics_pies"),
]

View file

@ -9,10 +9,10 @@ from django.utils.decorators import method_decorator
from .models import Status, Activity, Mood, StatusMedia, StatusActivity
from .forms import StatusForm
from .statistics import moodstats, activitystats
from .statistics import moodstats, activitystats, moodpies
from common.helpers import get_upload_path
from common.templatetags.images import hvhtml
from common.templatetags.images import hvhtml, bkhtml
from msgio.models import NotificationDailySchedule, Notification
from dateutil import relativedelta
@ -393,18 +393,16 @@ class MoodPlotView(LoginRequiredMixin, View):
def get(self, request, *args, **kwargs):
res = HttpResponse(content_type="text/html")
startdate = request.GET.get("start")
enddate = request.GET.get("end")
if enddate:
maxdate = datetime.strptime(enddate, "%Y-%m-%d")
else:
maxdate = timezone.now()
if startdate:
mindate = datetime.strptime(startdate, "%Y-%m-%d")
else:
mindate = maxdate - relativedelta.relativedelta(weeks=1)
res.write(hvhtml(moodstats(request.user)))
return res
class MoodPiesView(LoginRequiredMixin, View):
@method_decorator(xframe_options_sameorigin)
def dispatch(self, *args, **kwargs):
return super().dispatch(*args, **kwargs)
def get(self, request, *args, **kwargs):
res = HttpResponse(content_type="text/html")
res.write(bkhtml(moodpies(request.user)))
return res