kumify/mood/statistics.py
Kumi 2b37030938
fix: update legend label in moodpies chart
Updated the property for setting the chart legend's label from `legend`
to `legend_label` in the `moodpies` function to align with the latest
library syntax. This change ensures compatibility with newer versions of
the visualization library, preventing potential issues with legend
rendering in mood statistics charts.
2024-03-24 16:29:27 +01:00

383 lines
10 KiB
Python

import holoviews as hv
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
from .models import Status, Mood, StatusActivity
def moodstats(user):
hv.extension("bokeh")
tooltips = [("Date", "@date{%F %H:%M}"), ("Mood", "@name (@value)")]
formatters = {"@date": "datetime"}
hover = HoverTool(tooltips=tooltips, formatters=formatters)
pointdict = {"date": [], "value": [], "color": [], "name": []}
for status in Status.objects.filter(user=user):
if status.mood:
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)
points = hv.Points(pointframe)
points.opts(
tools=[hover],
color="color",
cmap="Category20",
line_color="black",
size=25,
width=600,
height=400,
show_grid=True,
)
pointtuples = [
(pointdict["date"][i], pointdict["value"][i])
for i in range(len(pointdict["date"]))
]
line = hv.Curve(pointtuples)
maxval = Mood.objects.filter(user=user).latest("value").value
maxy = maxval + max(maxval * 0.1, 1)
maxx = timezone.now().timestamp() * 1000
minx = maxx - (60 * 60 * 24 * 7) * 1000
output = points * line * timeseries.rolling(line, rolling_window=7)
output.opts(ylim=(0, maxy), xlim=(minx, maxx))
return output
def activitystats(user):
output = {}
for status in Status.objects.filter(user=user):
for activity in status.activity_set:
if not activity in output.keys():
output[activity] = {
"alltime": 0,
"yearly": 0,
"monthly": 0,
"weekly": 0,
}
output[activity]["alltime"] += 1
if status.timestamp > timezone.now() - relativedelta(years=1):
output[activity]["yearly"] += 1
if status.timestamp > timezone.now() - relativedelta(months=1):
output[activity]["monthly"] += 1
if status.timestamp > timezone.now() - relativedelta(weeks=1):
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(
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_label="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(
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_label="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(
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_label="mood",
source=yearly_data,
)
return column(weekly_chart, monthly_chart, yearly_chart)
def activitymood(activity):
hv.extension("bokeh")
tooltips = [("Date", "@date{%F %H:%M}"), ("Mood", "@name (@value)")]
formatters = {"@date": "datetime"}
hover = HoverTool(tooltips=tooltips, formatters=formatters)
pointdict = {"date": [], "value": [], "color": [], "name": []}
for statusactivity in StatusActivity.objects.filter(activity=activity):
if statusactivity.status.mood:
pointdict["date"].append(statusactivity.status.timestamp)
pointdict["value"].append(statusactivity.status.mood.value)
pointdict["color"].append(statusactivity.status.mood.color)
pointdict["name"].append(statusactivity.status.mood.name)
pointframe = pd.DataFrame.from_dict(pointdict)
points = hv.Points(pointframe)
points.opts(
tools=[hover],
color="color",
cmap="Category20",
line_color="black",
size=25,
width=600,
height=400,
show_grid=True,
)
pointtuples = [
(pointdict["date"][i], pointdict["value"][i])
for i in range(len(pointdict["date"]))
]
line = hv.Curve(pointtuples)
maxval = Mood.objects.filter(user=activity.user).latest("value").value
maxy = maxval + max(maxval * 0.1, 1)
maxx = timezone.now().timestamp() * 1000
minx = maxx - (60 * 60 * 24 * 7) * 1000
output = points * line * timeseries.rolling(line, rolling_window=7)
output.opts(ylim=(0, maxy), xlim=(minx, maxx))
return output
def activitypies(activity):
hv.extension("bokeh")
maxdate = timezone.now()
sa = StatusActivity.objects.filter(activity=activity)
weekly = dict()
colors = []
for mood in Mood.objects.filter(user=activity.user):
weekly[mood.name] = 0
colors.append(mood.color)
monthly, yearly = weekly.copy(), weekly.copy()
for single in sa:
if single.status.mood:
if single.status.timestamp > timezone.now() - relativedelta(weeks=1):
weekly[single.status.mood.name] += 1
if single.status.timestamp > timezone.now() - relativedelta(months=1):
monthly[single.status.mood.name] += 1
if single.status.timestamp > timezone.now() - relativedelta(years=1):
yearly[single.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(
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_label="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(
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_label="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(
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_label="mood",
source=yearly_data,
)
return column(weekly_chart, monthly_chart, yearly_chart)