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

View file

@ -10,6 +10,9 @@ from colorfield.fields import ColorField
from common.helpers import get_upload_path from common.helpers import get_upload_path
class Mood(models.Model): class Mood(models.Model):
class Meta:
ordering = ["-value"]
user = models.ForeignKey(get_user_model(), models.CASCADE) user = models.ForeignKey(get_user_model(), models.CASCADE)
name = models.CharField(max_length=64) name = models.CharField(max_length=64)
icon = models.CharField(default="fas fa-star", 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 django.utils import timezone
from math import pi
from bokeh.models import HoverTool 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 holoviews.operation import timeseries
from dateutil.relativedelta import relativedelta from dateutil.relativedelta import relativedelta
@ -14,7 +20,7 @@ def moodstats(user):
tooltips = [ tooltips = [
('Date', '@date{%F %H:%M}'), ('Date', '@date{%F %H:%M}'),
('Value', '@value') ('Mood', '@name (@value)')
] ]
formatters = { formatters = {
@ -23,7 +29,7 @@ def moodstats(user):
hover = HoverTool(tooltips=tooltips, formatters=formatters) hover = HoverTool(tooltips=tooltips, formatters=formatters)
pointdict = {"date": [], "value": [], "color": []} pointdict = {"date": [], "value": [], "color": [], "name": []}
for status in Status.objects.filter(user=user): for status in Status.objects.filter(user=user):
@ -31,6 +37,7 @@ def moodstats(user):
pointdict["date"].append(status.timestamp) pointdict["date"].append(status.timestamp)
pointdict["value"].append(status.mood.value) pointdict["value"].append(status.mood.value)
pointdict["color"].append(status.mood.color) pointdict["color"].append(status.mood.color)
pointdict["name"].append(status.mood.name)
pointframe = pd.DataFrame.from_dict(pointdict) pointframe = pd.DataFrame.from_dict(pointdict)
@ -80,4 +87,72 @@ def activitystats(user):
if status.timestamp > timezone.now() - relativedelta(weeks=1): if status.timestamp > timezone.now() - relativedelta(weeks=1):
output[activity]["weekly"] += 1 output[activity]["weekly"] += 1
return output 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" %} {% extends "frontend/base.html" %}
{% load images %} {% load images %}
{% load request %}
{% block "content" %} {% block "content" %}
<div class="row"> <div class="row">
@ -15,7 +14,18 @@
</div> </div>
<!-- Card Body --> <!-- Card Body -->
<div class="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> </div>
</div> </div>
@ -48,6 +58,7 @@
</div> </div>
</div> </div>
</div> </div>
{% endblock %} {% 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 from django.urls import path, include
@ -23,4 +23,5 @@ urlpatterns = [
path('statistics/', MoodStatisticsView.as_view(), name="statistics"), path('statistics/', MoodStatisticsView.as_view(), name="statistics"),
path('statistics/csv/', MoodCSVView.as_view(), name="statistics_csv"), path('statistics/csv/', MoodCSVView.as_view(), name="statistics_csv"),
path('statistics/plot/', MoodPlotView.as_view(), name="statistics_plot"), 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 .models import Status, Activity, Mood, StatusMedia, StatusActivity
from .forms import StatusForm from .forms import StatusForm
from .statistics import moodstats, activitystats from .statistics import moodstats, activitystats, moodpies
from common.helpers import get_upload_path 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 msgio.models import NotificationDailySchedule, Notification
from dateutil import relativedelta from dateutil import relativedelta
@ -393,18 +393,16 @@ class MoodPlotView(LoginRequiredMixin, View):
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
res = HttpResponse(content_type="text/html") 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))) 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 return res