From 7e89a226edcc2b610dadcadfeea0d5eb0cedd531 Mon Sep 17 00:00:00 2001
From: Jane Wiik Larsen <janew@MacBook-Air.local>
Date: Mon, 14 Mar 2022 14:37:23 +0100
Subject: [PATCH 1/2] small change

---
 frontend/www/scripts/statistics.js | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/frontend/www/scripts/statistics.js b/frontend/www/scripts/statistics.js
index 6634b37..aaedf39 100644
--- a/frontend/www/scripts/statistics.js
+++ b/frontend/www/scripts/statistics.js
@@ -55,7 +55,7 @@ const monthLabels = [
 
   const config = {
     type: 'line',
-    data: data,
+    data: countWorkoutsByCurrentWeek(),
     options: {}
   };
 
@@ -64,7 +64,7 @@ const monthLabels = [
     config
   );
 
-  //count workouts (by week/ month)
+  //count workouts (by week)
   function countWorkoutsByCurrentWeek() {
     let workoutCount = [0, 0, 0, 0, 0, 0, 0];
     const currentWeek = getWeek(new Date())
-- 
GitLab


From edfc9e216ba49321a915e454ba2c3edce951da0d Mon Sep 17 00:00:00 2001
From: Jane Wiik Larsen <janew@MacBook-Air.local>
Date: Mon, 14 Mar 2022 21:09:41 +0100
Subject: [PATCH 2/2] sort by month

---
 backend/secfit/workouts/admin.py              |   3 +-
 .../workouts/migrations/0005_statistics.py    |  27 ++++
 backend/secfit/workouts/models.py             |  13 ++
 backend/secfit/workouts/serializers.py        |   8 +-
 backend/secfit/workouts/urls.py               |  10 ++
 backend/secfit/workouts/views.py              |  30 ++++-
 frontend/www/index.html                       |  21 +++-
 frontend/www/scripts/statistics.js            | 118 ++++++++++++------
 8 files changed, 186 insertions(+), 44 deletions(-)
 create mode 100644 backend/secfit/workouts/migrations/0005_statistics.py

diff --git a/backend/secfit/workouts/admin.py b/backend/secfit/workouts/admin.py
index cb43794..43a554d 100644
--- a/backend/secfit/workouts/admin.py
+++ b/backend/secfit/workouts/admin.py
@@ -3,9 +3,10 @@
 from django.contrib import admin
 
 # Register your models here.
-from .models import Exercise, ExerciseInstance, Workout, WorkoutFile
+from .models import Exercise, ExerciseInstance, Workout, WorkoutFile, Statistics
 
 admin.site.register(Exercise)
 admin.site.register(ExerciseInstance)
 admin.site.register(Workout)
 admin.site.register(WorkoutFile)
+admin.site.register(Statistics)
diff --git a/backend/secfit/workouts/migrations/0005_statistics.py b/backend/secfit/workouts/migrations/0005_statistics.py
new file mode 100644
index 0000000..b991640
--- /dev/null
+++ b/backend/secfit/workouts/migrations/0005_statistics.py
@@ -0,0 +1,27 @@
+# Generated by Django 3.1 on 2022-03-14 14:47
+
+from django.conf import settings
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+        ('workouts', '0004_auto_20211020_0950'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='Statistics',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('date', models.DateTimeField()),
+                ('owner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='statistics', to=settings.AUTH_USER_MODEL)),
+            ],
+            options={
+                'ordering': ['date'],
+            },
+        ),
+    ]
diff --git a/backend/secfit/workouts/models.py b/backend/secfit/workouts/models.py
index 0f6214f..745881a 100644
--- a/backend/secfit/workouts/models.py
+++ b/backend/secfit/workouts/models.py
@@ -157,3 +157,16 @@ class RememberMe(models.Model):
 
     def __str__(self):
         return self.remember_me
+
+class Statistics (models.Model):
+     '''
+     date: timestamp for when the workout was completed
+     ower: who completed the workout 
+     '''
+     date = models.DateTimeField()
+     owner = models.ForeignKey(
+         get_user_model(), on_delete=models.CASCADE, related_name="statistics"
+     )
+
+     class Meta:
+         ordering = ['date']
diff --git a/backend/secfit/workouts/serializers.py b/backend/secfit/workouts/serializers.py
index 6abbe31..19259b5 100644
--- a/backend/secfit/workouts/serializers.py
+++ b/backend/secfit/workouts/serializers.py
@@ -2,7 +2,7 @@
 """
 from rest_framework import serializers
 from rest_framework.serializers import HyperlinkedRelatedField
-from workouts.models import Workout, Exercise, ExerciseInstance, WorkoutFile, RememberMe
+from workouts.models import Workout, Exercise, ExerciseInstance, WorkoutFile, RememberMe, Statistics
 
 
 class ExerciseInstanceSerializer(serializers.HyperlinkedModelSerializer):
@@ -228,3 +228,9 @@ class RememberMeSerializer(serializers.HyperlinkedModelSerializer):
     class Meta:
         model = RememberMe
         fields = ["remember_me"]
+
+class StatisticsSerializer(serializers.HyperlinkedModelSerializer):
+     owner = serializers.ReadOnlyField(source='owner.username')
+     class Meta:
+         model = Statistics
+         fields = ["id", "owner", "date"]
diff --git a/backend/secfit/workouts/urls.py b/backend/secfit/workouts/urls.py
index 7c46a3f..0405fe0 100644
--- a/backend/secfit/workouts/urls.py
+++ b/backend/secfit/workouts/urls.py
@@ -42,6 +42,16 @@ urlpatterns = format_suffix_patterns(
             views.WorkoutFileDetail.as_view(),
             name="workoutfile-detail",
         ),
+        path(
+             "api/statistics/", 
+             views.StatisticsView.as_view(), 
+             name="statistics"
+        ),
+        path(
+            "api/statistics/<int:pk>/", 
+            views.StatisticsView.as_view(), 
+            name="statistics-detail"
+        ),
         path("", include("users.urls")),
         path("", include("comments.urls")),
         path("api/auth/", include("rest_framework.urls")),
diff --git a/backend/secfit/workouts/views.py b/backend/secfit/workouts/views.py
index efddf40..1b0505b 100644
--- a/backend/secfit/workouts/views.py
+++ b/backend/secfit/workouts/views.py
@@ -22,10 +22,8 @@ from workouts.permissions import (
     IsWorkoutPublic,
 )
 from workouts.mixins import CreateListModelMixin
-from workouts.models import Workout, Exercise, ExerciseInstance, WorkoutFile
-from workouts.serializers import WorkoutSerializer, ExerciseSerializer
-from workouts.serializers import RememberMeSerializer
-from workouts.serializers import ExerciseInstanceSerializer, WorkoutFileSerializer
+from workouts.models import Workout, Exercise, ExerciseInstance, WorkoutFile, Statistics
+from workouts.serializers import WorkoutSerializer, ExerciseSerializer, RememberMeSerializer, ExerciseInstanceSerializer, WorkoutFileSerializer, StatisticsSerializer
 from django.core.exceptions import PermissionDenied
 from rest_framework_simplejwt.tokens import RefreshToken
 from rest_framework.response import Response
@@ -50,6 +48,7 @@ def api_root(request, format=None):
             ),
             "comments": reverse("comment-list", request=request, format=format),
             "likes": reverse("like-list", request=request, format=format),
+            "statistics": reverse("statistics", request=request, format=format)
         }
     )
 
@@ -340,3 +339,26 @@ class WorkoutFileDetail(
 
     def delete(self, request, *args, **kwargs):
         return self.destroy(request, *args, **kwargs)
+
+class StatisticsView(
+     mixins.ListModelMixin,
+     mixins.CreateModelMixin,
+     generics.GenericAPIView,
+ ):
+     serializer_class = StatisticsSerializer
+     permission_classes = [permissions.IsAuthenticated]
+
+     def get(self, request, *args, **kwargs):
+         return self.list(request, *args, **kwargs)
+
+     def get_queryset(self):
+         queryset = Statistics.objects.none()
+         if self.request.user:
+             queryset = Statistics.objects.filter(owner=self.request.user)
+         return queryset
+
+     def post(self, request, *args, **kwargs):
+         return self.create(request, *args, **kwargs)
+
+     def perform_create(self, serializer):
+         serializer.save(owner=self.request.user)
diff --git a/frontend/www/index.html b/frontend/www/index.html
index c5b6141..d45e12a 100644
--- a/frontend/www/index.html
+++ b/frontend/www/index.html
@@ -13,7 +13,7 @@
     <body>
         <navbar-el></navbar-el>
 
-        <div class="container">
+        <div class="container d-none">
             <div class="row mt-3">
                 <div class="col-lg text-center">
                     <h2 class="mt-3">Welcome to SecFit</h2>
@@ -24,9 +24,26 @@
                     <img src="img/fitness.jpg" class="img-fluid" alt="DUMBBELLS">
                 </div>
             </div>
+        </div>
+            <h2 class="text-center">Your workout statistics!</h2>
+            <p class="text-center">Mark workouts as completed to keep track of how many workouts you complete!</p>
+        <div class="d-flex gap-2 justify-content-center">
+            <span>Sort by: </span>
+            <div class="form-check">
+                <input class="form-check-input" type="radio" name="sorting" id="week" checked>
+                <label class="form-check-label" for="flexRadioDefault1">
+                    Week
+                </label>
+            </div>
+            <div class="form-check">
+                <input class="form-check-input" type="radio" name="sorting" id="month">
+                <label class="form-check-label" for="flexRadioDefault2">
+                    Month
+                </label>
+            </div>
         </div>
         <div class="w-50 mx-auto">
-            <canvas id="myChart" width="200" height="100"></canvas>
+            <canvas id="myChart"></canvas>
         </div>
            
         <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
diff --git a/frontend/www/scripts/statistics.js b/frontend/www/scripts/statistics.js
index aaedf39..1bd0982 100644
--- a/frontend/www/scripts/statistics.js
+++ b/frontend/www/scripts/statistics.js
@@ -1,23 +1,10 @@
 let statistics
+let graph
+let myChart
 let weeklyWorkoutCount
+let monthlyWorkoutCount
+let currentSorting = "week"
 
-window.addEventListener("DOMContentLoaded", async () => {
-  statistics = await getUserWorkoutStatistics();
-  weeklyWorkoutCount = countWorkoutsByCurrentWeek();
-})
-
-//Retrieves the completed workouts of a user
-async function getUserWorkoutStatistics(){
-  let response = await sendRequest('GET', `${HOST}/api/statistics/`);
-  if (!response.ok) {
-      throw new Error(`HTTP error! status: ${response.status}`);
-  } else {
-      let data = await response.json();
-      return data.results;
-  }
-}
-
-//create statistics chart
 const monthLabels = [
     'January',
     'February',
@@ -43,27 +30,72 @@ const monthLabels = [
       'Sunday'
   ]
 
-  const data = {
-    labels: weekdayLabels,
-    datasets: [{
-      label: 'How many workouts you have completed',
-      backgroundColor: 'rgb(255, 99, 132)',
-      borderColor: 'rgb(255, 99, 132)',
-      data: weeklyWorkoutCount,
-    }]
-  };
+window.addEventListener("DOMContentLoaded", async () => {
+  statistics = await getUserWorkoutStatistics();
+  weeklyWorkoutCount = countWorkoutsByCurrentWeek();
+  monthlyWorkoutCount = countWorkoutsByCurrentMonth();
+  createStatisticsChart(weeklyWorkoutCount);
+})
+
+var sortingButtons = document.querySelectorAll('input[name = "sorting"]');
+sortingButtons.forEach((radio) => {
+  radio.addEventListener("change", () => {
+    currentSorting = radio.id
+    if(currentSorting == "week"){
+      updateGraph(myChart, weekdayLabels, weeklyWorkoutCount);
+    }else {
+      updateGraph(myChart, monthLabels, monthlyWorkoutCount);
+    }
+  })
+})
 
-  const config = {
-    type: 'line',
-    data: countWorkoutsByCurrentWeek(),
-    options: {}
-  };
+function updateGraph(chart, labels, data) {
+  chart.data.datasets.pop();
+  chart.data.datasets.push({
+    label: 'How many workouts you have completed',
+    data: data,
+    backgroundColor: 'rgb(255, 99, 132)',
+    borderColor: 'rgb(255, 99, 132)',
+    borderWidth: 1
+  });
+  chart.data.labels = [];
+  chart.data.labels = labels;
+  chart.update();
+}
 
-  const myChart = new Chart(
-    document.getElementById('myChart'),
-    config
-  );
 
+//Retrieves the completed workouts of a user
+async function getUserWorkoutStatistics(){
+  let response = await sendRequest('GET', `${HOST}/api/statistics/`);
+  if (!response.ok) {
+      throw new Error(`HTTP error! status: ${response.status}`);
+  } else {
+      let data = await response.json();
+      return data.results;
+  }
+}
+
+//create statistics chart
+  function createStatisticsChart(data) {
+    graph = document.getElementById('myChart');
+    myChart = new Chart(graph, {
+      type: 'line',
+      data: {
+          labels: weekdayLabels,
+          datasets: [{
+              label: 'How many workouts you have completed',
+              data: data,
+              backgroundColor: 'rgb(255, 99, 132)',
+              borderColor: 'rgb(255, 99, 132)',
+              borderWidth: 1
+          }]
+      },
+      options: {
+      }
+  });
+    return myChart
+  }
+  
   //count workouts (by week)
   function countWorkoutsByCurrentWeek() {
     let workoutCount = [0, 0, 0, 0, 0, 0, 0];
@@ -80,6 +112,20 @@ const monthLabels = [
     return workoutCount;
   }
 
+  function countWorkoutsByCurrentMonth() {
+    let workoutCount = new Array(12).fill(0);
+    const currentYear = new Date().getFullYear()
+    statistics.forEach((entity) => {
+      const workoutDate = new Date(entity.date);
+      const yearCompleted = workoutDate.getFullYear();
+      if(yearCompleted == currentYear){
+        const monthCompleted = workoutDate.getMonth();
+        workoutCount[monthCompleted] += 1 
+      }
+    })
+    return workoutCount
+  }
+  
 // Source: https://weeknumber.com/how-to/javascript
 // Returns the ISO week of the date.
 function getWeek(date) {
@@ -92,4 +138,4 @@ function getWeek(date) {
   // Adjust to Thursday in week 1 and count number of weeks from date to week1.
   return 1 + Math.round(((newdate.getTime() - week1.getTime()) / 86400000
                         - 3 + (week1.getDay() + 6) % 7) / 7);
-}
\ No newline at end of file
+}
-- 
GitLab