import io from collections.__init__ import defaultdict from datetime import timedelta import numpy as np from django.http import HttpResponse from django.views.generic import DetailView from web.models import User, ActivityLog, Activity class UserChartsPie(DetailView): model = User pk_url_kwarg = 'user_id' def pie_chart(self, plt, ax, logs): total_time = defaultdict(float) for log in logs: total_time[str(log.activity)] += (log.end_time - log.start_time).total_seconds() sectors = sorted(list(total_time.items()), key=lambda x: x[1]) ax.pie(x=[i[1] for i in sectors], labels=[i[0] for i in sectors], autopct='%1.0f%%') def average_chart(self, plt, ax, logs): interval = timedelta(minutes=10) interval_count = timedelta(days=1) // interval intervals = [(interval * i) / timedelta(hours=1) for i in range(interval_count)] distr_by_activity = {} for i in logs: if i.activity not in distr_by_activity: distr_by_activity[i.activity] = np.zeros(interval_count) for i in logs: day = i.start_time.replace(hour=0, minute=0, second=0, microsecond=0) t = i.start_time t_i = (t - day) // interval while t < i.end_time: distr_by_activity[i.activity][t_i] += 1 t += interval t_i = (t_i + 1) % interval_count for activity, distr in distr_by_activity.items(): ax.plot(intervals, distr, label=f'{activity}') distr[0] = 0 distr[-1] = 0 ax.fill(intervals, distr, alpha=0.2) plt.legend() def render_to_response(self, context, **response_kwargs): logs = ActivityLog.objects.filter(user=self.object).all() import matplotlib matplotlib.use('Agg') import matplotlib.pyplot as plt fig = plt.figure(figsize=(12, 16), dpi=100) ax = fig.add_subplot(221) self.pie_chart(plt, ax, logs) ax = fig.add_subplot(212) self.average_chart(plt, ax, logs) buf = io.BytesIO() fig.savefig(buf, format='png') b = buf.getvalue() return HttpResponse(b, content_type='image/png') class UserChartsActivityAll(DetailView): model = User pk_url_kwarg = 'user_id' def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context['activity'] = Activity.objects.filter(id=self.kwargs['activity_id']).first() return context def bar_chart(self, plt, ax, logs, day_l, day_r, day_count, days): day_seconds = np.zeros(day_count) for log in logs: for i, day in enumerate(days): l = max(log.start_time, day) r = min(log.end_time, day + timedelta(days=1)) if r > l: day_seconds[i] += (r - l).total_seconds() day_hours = day_seconds / timedelta(hours=1).total_seconds() ax.bar(days, day_hours) ax.hlines([np.mean(day_hours)], day_l, day_r, color='orange', lw=3) plt.xticks(days, [f'{i:%m-%d}' for i in days], rotation=60) plt.ylabel('кол-во часов') def tracker_chart(self, plt, ax, logs, day_l, day_r, day_count, days): ys = [] widths = [] lefts = [] for log in logs: for i, day in enumerate(days): l = max(log.start_time, day) r = min(log.end_time, day + timedelta(days=1)) if r > l: ys.append(day) widths.append((r - l) / timedelta(hours=1)) lefts.append((l - day) / timedelta(hours=1)) ax.barh(y=ys, width=widths, left=lefts, zorder=2) plt.xlim(0, 24) plt.xticks(range(24), [f'{i:02d}:00' for i in range(24)]) plt.yticks(days, [f'{i:%m-%d}' for i in days]) plt.grid(True, zorder=1) def average_chart(self, plt, ax, logs, day_l, day_r, day_count, days): pass def render_to_response(self, context, **response_kwargs): logs = ActivityLog.objects.filter(user=self.object).all() day_r = logs[-1].end_time.replace(hour=0, minute=0, second=0, microsecond=0) day_l = max( logs[0].start_time.replace(hour=0, minute=0, second=0, microsecond=0), day_r - timedelta(days=13) ) day_count = (day_r - day_l) // timedelta(days=1) + 1 days = [day_l + timedelta(days=1) * i for i in range(day_count)] import matplotlib matplotlib.use('Agg') import matplotlib.pyplot as plt fig = plt.figure(figsize=(8, 12), dpi=100) ax = fig.add_subplot(311) self.bar_chart(plt, ax, logs, day_l, day_r, day_count, days) ax = fig.add_subplot(312) self.tracker_chart(plt, ax, logs, day_l, day_r, day_count, days) ax = fig.add_subplot(313) self.average_chart(plt, ax, logs, day_l, day_r, day_count, days) buf = io.BytesIO() fig.savefig(buf, format='png') b = buf.getvalue() return HttpResponse(b, content_type='image/png') class UserActivityChartsTracker(DetailView): model = User pk_url_kwarg = 'user_id' def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context['activity'] = Activity.objects.filter(id=self.kwargs['activity_id']).first() return context def render_to_response(self, context, **response_kwargs): print('start_tracker') import matplotlib matplotlib.use('Agg') import matplotlib.pyplot as plt fig = plt.figure(figsize=(16, 6), dpi=100) ax = fig.add_subplot(111) logs = list(ActivityLog.objects.filter(user=self.object, activity=context['activity']).order_by('start_time').all()) day_r = logs[-1].end_time.replace(hour=0, minute=0, second=0, microsecond=0) day_l = max( logs[0].start_time.replace(hour=0, minute=0, second=0, microsecond=0), day_r - timedelta(days=14) ) day_count = (day_r - day_l) // timedelta(days=1) + 1 days = [day_l + timedelta(days=1) * i for i in range(day_count)] buf = io.BytesIO() fig.savefig(buf, format='png') b = buf.getvalue() return HttpResponse(b, content_type='image/png')