Commit b8b2937b authored by I.K Seneviratne's avatar I.K Seneviratne

Committing the removal of unnecessary files and code segments and code...

Committing the removal of unnecessary files and code segments and code formatting in activity_recognition.py, emotion_detector.py and head_gaze_estimation.py files and some other files.
.
parent 07796831
"""
this file is responsible for creating the API classes so that
frontend could communicate with the backend, through JSON-based communication
each class is extended by djangorestframework APIVIEW class
each class contains methods to represent the HTTP methods received through the requests
in this case the GET method was mostly used.
each method will return an HttpResponse that allows its data to be rendered into
arbitrary media types.
"""
from rest_framework.permissions import IsAuthenticated, IsAdminUser
from rest_framework.authentication import SessionAuthentication, BasicAuthentication
......@@ -5,9 +20,6 @@ from MonitorLecturerApp.models import LectureRecordedVideo, LecturerVideoMetaDat
from MonitorLecturerApp.serializers import LectureRecordedVideoSerializer, LecturerVideoMetaDataSerializer
from .MongoModels import *
from rest_framework.views import *
from .ImageOperations import saveImage
from .logic import head_pose_estimation
from .logic import video_extraction
from .logic import activity_recognition as ar
from .logic import posenet_calculation as pc
from . import emotion_detector as ed
......@@ -22,55 +34,6 @@ from .serializers import *
import datetime
# to create images
class ImageViewSet(APIView):
def post(self, request):
saveImage(request.data)
return Response({"response": "successful"})
# to perform pose estimation on images
class GazeEstimationViewSet(APIView):
def post(self, request):
response = head_pose_estimation.estimatePose(request.data)
return Response({"response": response})
# to perform video extraction
class VideoExtractionViewSet(APIView):
def get(self, request):
response = video_extraction.getExtractedFrames(request.query_params)
return Response({"response": response})
def post(self, request):
response = video_extraction.VideoExtractor(request.data)
return Response({"response": response})
# lecture emotions view set
class LectureEmotionViewSet(APIView):
def get(self, request):
emotions = LectureEmotionReport.objects.all().order_by('lecture_id')
serializer = LectureEmotionSerializer(emotions, many=True)
return Response({"response": serializer.data})
def post(self, request):
LectureEmotionReport(
lecture_id=request.data["lecture_id"],
happy_perct=request.data["happy_perct"],
sad_perct=request.data["sad_perct"],
angry_perct=request.data["angry_perct"],
surprise_perct=request.data["surprise_perct"],
disgust_perct=request.data["disgust_perct"],
neutral_perct=request.data["neutral_perct"]
).save()
return Response({"response": request.data})
class LectureViewSet(APIView):
def get(self, request):
......@@ -191,20 +154,33 @@ class LectureVideoViewSet(APIView):
status=status.HTTP_400_BAD_REQUEST)
# this API will retrieve a lecture video details
class GetLectureVideoViewSet(APIView):
def get(self, request):
# get the lecturer id from the request
lecturer = request.query_params.get('lecturer')
# get the lecture date from the request
date = request.query_params.get('date')
# get the item number from the request
index = int(request.query_params.get('index'))
# retrieve the lecture video from the db
lecturer_video = LectureVideo.objects.filter(lecturer_id=lecturer, date=date)
# serialize the object
serializer = LectureVideoSerializer(lecturer_video, many=True)
# get the lecture video id
lecture_video_id = serializer.data[0]['lecture_video_id']
print('lecture video id: ', lecture_video_id)
# retrieve the lecture activties exist for the given video
activities = LectureActivity.objects.filter(lecture_video_id__lecture_video_id=lecture_video_id)
# assign the whether there are found lecture activites or not
isActivityFound = (len(activities) > 0)
# return the response
return Response({
"response": serializer.data[index],
"isActivityFound": isActivityFound
......@@ -225,21 +201,22 @@ class GetLectureVideoViewSetForHome(APIView):
# to check whether there is only one lecture video for the query
# if there are more than one, send only the specified lecture video
if len(serializer.data) > 1:
lecture_video_id = serializer.data[counter]['lecture_video_id']
response = serializer.data[counter]
# else, send the only lecture video
else:
lecture_video_id = serializer.data[0]['lecture_video_id']
response = serializer.data[0]
# return the response
return Response({
"response": response
})
# ACTIVITY
##### ACTIVITY section #####
# API for lecture activities
class LectureActivityViewSet(APIView):
......@@ -276,7 +253,7 @@ class GetLectureActivityViewSet(APIView):
})
# API to process lecture activity
# API to process lecture activity and save to DB
class LectureActivityProcess(APIView):
def get(self, request):
......@@ -286,8 +263,6 @@ class LectureActivityProcess(APIView):
self.activity(video_id, percentages)
return Response({"response": True})
def post(self, request):
pass
def activity(self, lec_video_id, percentages):
# lec_video = LectureVideo.objects.filter(lecture_video_id=lec_video_id)
......
"""
this file contain the relevant methods to implement the student emotion recognition logic
main methods include
* the execution of emotion recognition model and saving the results into the database,
* retrieving the emotion recognition details for lectures within a given time period
* calculating the emotion recognition details for each frame, for a given lecture
* calculating the emotion recognition details for frame groups, for a given lecture
* calculating the emotion recognition correlations with the lecturer posture activities
"""
from tensorflow.keras.models import load_model
from time import sleep
from keras.preprocessing.image import img_to_array
......
"""
this file contain the relevant methods to implement the student activity recognition logic
main methods include
* the execution of activity recognition model and saving the results into the database,
* retrieving the activity recognition details for lectures within a given time period
* calculating the activity recognition details for each frame, for a given lecture
* calculating the activity recognition details for frame groups, for a given lecture
* calculating the activity recognition correlations with the lecturer posture activities
"""
import tensorflow as tf
import tensorflow.keras
from PIL import Image, ImageOps
......
# -*- coding: utf-8 -*-
"""
Created on Fri Jul 31 03:00:36 2020
@author: hp
this file contain the relevant methods to implement the student gaze estimation logic
main methods include
* the execution of gaze estimation model and saving the results into the database,
* retrieving the gaze estimation details for lectures within a given time period
* calculating the gaze estimation details for each frame, for a given lecture
* calculating the gaze estimation details for frame groups, for a given lecture
* calculating the gaze estimation correlations with the lecturer posture activities
"""
from decimal import Decimal
from . custom_sorter import *
......
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" href="style.css">
<title>{% block title %}My amazing site{% endblock %}</title>
</head>
<body>
<div id="sidebar">
{% block sidebar %}
<ul>
<li><a href="/">Home</a></li>
<li><a href="/blog/">Blog</a></li>
</ul>
{% endblock %}
</div>
<div id="content">
{% block content %}{% endblock %}
</div>
</body>
</html>
\ No newline at end of file
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="">
<meta name="author" content="">
<title>SB Admin 2 - Blank</title>
<!-- Custom fonts for this template-->
<link href="vendor/fontawesome-free/css/all.min.css" rel="stylesheet" type="text/css">
<link href="https://fonts.googleapis.com/css?family=Nunito:200,200i,300,300i,400,400i,600,600i,700,700i,800,800i,900,900i" rel="stylesheet">
<!-- Custom styles for this template-->
<link href="css/sb-admin-2.min.css" rel="stylesheet">
</head>
<body id="page-top">
<!-- Page Wrapper -->
<div id="wrapper">
<!-- Sidebar -->
<ul class="navbar-nav bg-gradient-primary sidebar sidebar-dark accordion" id="accordionSidebar">
<!-- Sidebar - Brand -->
<a class="sidebar-brand d-flex align-items-center justify-content-center" href="index.html">
<div class="sidebar-brand-icon rotate-n-15">
<i class="fas fa-laugh-wink"></i>
</div>
<div class="sidebar-brand-text mx-3">SB Admin <sup>2</sup></div>
</a>
<!-- Divider -->
<hr class="sidebar-divider my-0">
<!-- Nav Item - Dashboard -->
<li class="nav-item">
<a class="nav-link" href="index.html">
<i class="fas fa-fw fa-tachometer-alt"></i>
<span>Dashboard</span></a>
</li>
<!-- Divider -->
<hr class="sidebar-divider">
<!-- Heading -->
<div class="sidebar-heading">
Interface
</div>
<!-- Nav Item - Pages Collapse Menu -->
<li class="nav-item">
<a class="nav-link collapsed" href="#" data-toggle="collapse" data-target="#collapseTwo" aria-expanded="true" aria-controls="collapseTwo">
<i class="fas fa-fw fa-cog"></i>
<span>Components</span>
</a>
<div id="collapseTwo" class="collapse" aria-labelledby="headingTwo" data-parent="#accordionSidebar">
<div class="bg-white py-2 collapse-inner rounded">
<h6 class="collapse-header">Custom Components:</h6>
<a class="collapse-item" href="gaze.html">Buttons</a>
<a class="collapse-item" href="pose.html">Cards</a>
</div>
</div>
</li>
<!-- Nav Item - Utilities Collapse Menu -->
<li class="nav-item">
<a class="nav-link collapsed" href="#" data-toggle="collapse" data-target="#collapseUtilities" aria-expanded="true" aria-controls="collapseUtilities">
<i class="fas fa-fw fa-wrench"></i>
<span>Utilities</span>
</a>
<div id="collapseUtilities" class="collapse" aria-labelledby="headingUtilities" data-parent="#accordionSidebar">
<div class="bg-white py-2 collapse-inner rounded">
<h6 class="collapse-header">Custom Utilities:</h6>
<a class="collapse-item" href="utilities-color.html">Colors</a>
<a class="collapse-item" href="utilities-border.html">Borders</a>
<a class="collapse-item" href="utilities-animation.html">Animations</a>
<a class="collapse-item" href="utilities-other.html">Other</a>
</div>
</div>
</li>
<!-- Divider -->
<hr class="sidebar-divider">
<!-- Heading -->
<div class="sidebar-heading">
Addons
</div>
<!-- Nav Item - Pages Collapse Menu -->
<li class="nav-item active">
<a class="nav-link" href="#" data-toggle="collapse" data-target="#collapsePages" aria-expanded="true" aria-controls="collapsePages">
<i class="fas fa-fw fa-folder"></i>
<span>Pages</span>
</a>
<div id="collapsePages" class="collapse show" aria-labelledby="headingPages" data-parent="#accordionSidebar">
<div class="bg-white py-2 collapse-inner rounded">
<h6 class="collapse-header">Login Screens:</h6>
<a class="collapse-item" href="login.html">Login</a>
<a class="collapse-item" href="register.html">Register</a>
<a class="collapse-item" href="forgot-password.html">Forgot Password</a>
<div class="collapse-divider"></div>
<h6 class="collapse-header">Other Pages:</h6>
<a class="collapse-item" href="404.html">404 Page</a>
<a class="collapse-item active" href="blank.html">Blank Page</a>
</div>
</div>
</li>
<!-- Nav Item - Charts -->
<li class="nav-item">
<a class="nav-link" href="charts.html">
<i class="fas fa-fw fa-chart-area"></i>
<span>Charts</span></a>
</li>
<!-- Nav Item - Tables -->
<li class="nav-item">
<a class="nav-link" href="tables.html">
<i class="fas fa-fw fa-table"></i>
<span>Tables</span></a>
</li>
<!-- Divider -->
<hr class="sidebar-divider d-none d-md-block">
<!-- Sidebar Toggler (Sidebar) -->
<div class="text-center d-none d-md-inline">
<button class="rounded-circle border-0" id="sidebarToggle"></button>
</div>
</ul>
<!-- End of Sidebar -->
<!-- Content Wrapper -->
<div id="content-wrapper" class="d-flex flex-column">
<!-- Main Content -->
<div id="content">
<!-- Topbar -->
<nav class="navbar navbar-expand navbar-light bg-white topbar mb-4 static-top shadow">
<!-- Sidebar Toggle (Topbar) -->
<button id="sidebarToggleTop" class="btn btn-link d-md-none rounded-circle mr-3">
<i class="fa fa-bars"></i>
</button>
<!-- Topbar Search -->
<form class="d-none d-sm-inline-block form-inline mr-auto ml-md-3 my-2 my-md-0 mw-100 navbar-search">
<div class="input-group">
<input type="text" class="form-control bg-light border-0 small" placeholder="Search for..." aria-label="Search" aria-describedby="basic-addon2">
<div class="input-group-append">
<button class="btn btn-primary" type="button">
<i class="fas fa-search fa-sm"></i>
</button>
</div>
</div>
</form>
<!-- Topbar Navbar -->
<ul class="navbar-nav ml-auto">
<!-- Nav Item - Search Dropdown (Visible Only XS) -->
<li class="nav-item dropdown no-arrow d-sm-none">
<a class="nav-link dropdown-toggle" href="#" id="searchDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<i class="fas fa-search fa-fw"></i>
</a>
<!-- Dropdown - Messages -->
<div class="dropdown-menu dropdown-menu-right p-3 shadow animated--grow-in" aria-labelledby="searchDropdown">
<form class="form-inline mr-auto w-100 navbar-search">
<div class="input-group">
<input type="text" class="form-control bg-light border-0 small" placeholder="Search for..." aria-label="Search" aria-describedby="basic-addon2">
<div class="input-group-append">
<button class="btn btn-primary" type="button">
<i class="fas fa-search fa-sm"></i>
</button>
</div>
</div>
</form>
</div>
</li>
<!-- Nav Item - Alerts -->
<li class="nav-item dropdown no-arrow mx-1">
<a class="nav-link dropdown-toggle" href="#" id="alertsDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<i class="fas fa-bell fa-fw"></i>
<!-- Counter - Alerts -->
<span class="badge badge-danger badge-counter">3+</span>
</a>
<!-- Dropdown - Alerts -->
<div class="dropdown-list dropdown-menu dropdown-menu-right shadow animated--grow-in" aria-labelledby="alertsDropdown">
<h6 class="dropdown-header">
Alerts Center
</h6>
<a class="dropdown-item d-flex align-items-center" href="#">
<div class="mr-3">
<div class="icon-circle bg-primary">
<i class="fas fa-file-alt text-white"></i>
</div>
</div>
<div>
<div class="small text-gray-500">December 12, 2019</div>
<span class="font-weight-bold">A new monthly report is ready to download!</span>
</div>
</a>
<a class="dropdown-item d-flex align-items-center" href="#">
<div class="mr-3">
<div class="icon-circle bg-success">
<i class="fas fa-donate text-white"></i>
</div>
</div>
<div>
<div class="small text-gray-500">December 7, 2019</div>
$290.29 has been deposited into your account!
</div>
</a>
<a class="dropdown-item d-flex align-items-center" href="#">
<div class="mr-3">
<div class="icon-circle bg-warning">
<i class="fas fa-exclamation-triangle text-white"></i>
</div>
</div>
<div>
<div class="small text-gray-500">December 2, 2019</div>
Spending Alert: We've noticed unusually high spending for your account.
</div>
</a>
<a class="dropdown-item text-center small text-gray-500" href="#">Show All Alerts</a>
</div>
</li>
<!-- Nav Item - Messages -->
<li class="nav-item dropdown no-arrow mx-1">
<a class="nav-link dropdown-toggle" href="#" id="messagesDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<i class="fas fa-envelope fa-fw"></i>
<!-- Counter - Messages -->
<span class="badge badge-danger badge-counter">7</span>
</a>
<!-- Dropdown - Messages -->
<div class="dropdown-list dropdown-menu dropdown-menu-right shadow animated--grow-in" aria-labelledby="messagesDropdown">
<h6 class="dropdown-header">
Message Center
</h6>
<a class="dropdown-item d-flex align-items-center" href="#">
<div class="dropdown-list-image mr-3">
<img class="rounded-circle" src="https://source.unsplash.com/fn_BT9fwg_E/60x60" alt="">
<div class="status-indicator bg-success"></div>
</div>
<div class="font-weight-bold">
<div class="text-truncate">Hi there! I am wondering if you can help me with a problem I've been having.</div>
<div class="small text-gray-500">Emily Fowler · 58m</div>
</div>
</a>
<a class="dropdown-item d-flex align-items-center" href="#">
<div class="dropdown-list-image mr-3">
<img class="rounded-circle" src="https://source.unsplash.com/AU4VPcFN4LE/60x60" alt="">
<div class="status-indicator"></div>
</div>
<div>
<div class="text-truncate">I have the photos that you ordered last month, how would you like them sent to you?</div>
<div class="small text-gray-500">Jae Chun · 1d</div>
</div>
</a>
<a class="dropdown-item d-flex align-items-center" href="#">
<div class="dropdown-list-image mr-3">
<img class="rounded-circle" src="https://source.unsplash.com/CS2uCrpNzJY/60x60" alt="">
<div class="status-indicator bg-warning"></div>
</div>
<div>
<div class="text-truncate">Last month's report looks great, I am very happy with the progress so far, keep up the good work!</div>
<div class="small text-gray-500">Morgan Alvarez · 2d</div>
</div>
</a>
<a class="dropdown-item d-flex align-items-center" href="#">
<div class="dropdown-list-image mr-3">
<img class="rounded-circle" src="https://source.unsplash.com/Mv9hjnEUHR4/60x60" alt="">
<div class="status-indicator bg-success"></div>
</div>
<div>
<div class="text-truncate">Am I a good boy? The reason I ask is because someone told me that people say this to all dogs, even if they aren't good...</div>
<div class="small text-gray-500">Chicken the Dog · 2w</div>
</div>
</a>
<a class="dropdown-item text-center small text-gray-500" href="#">Read More Messages</a>
</div>
</li>
<div class="topbar-divider d-none d-sm-block"></div>
<!-- Nav Item - User Information -->
<li class="nav-item dropdown no-arrow">
<a class="nav-link dropdown-toggle" href="#" id="userDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span class="mr-2 d-none d-lg-inline text-gray-600 small">Valerie Luna</span>
<img class="img-profile rounded-circle" src="https://source.unsplash.com/QAB-WJcbgJk/60x60">
</a>
<!-- Dropdown - User Information -->
<div class="dropdown-menu dropdown-menu-right shadow animated--grow-in" aria-labelledby="userDropdown">
<a class="dropdown-item" href="#">
<i class="fas fa-user fa-sm fa-fw mr-2 text-gray-400"></i>
Profile
</a>
<a class="dropdown-item" href="#">
<i class="fas fa-cogs fa-sm fa-fw mr-2 text-gray-400"></i>
Settings
</a>
<a class="dropdown-item" href="#">
<i class="fas fa-list fa-sm fa-fw mr-2 text-gray-400"></i>
Activity Log
</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item" href="#" data-toggle="modal" data-target="#logoutModal">
<i class="fas fa-sign-out-alt fa-sm fa-fw mr-2 text-gray-400"></i>
Logout
</a>
</div>
</li>
</ul>
</nav>
<!-- End of Topbar -->
<!-- Begin Page Content -->
<div class="container-fluid">
<!-- Page Heading -->
<h1 class="h3 mb-4 text-gray-800">Blank Page</h1>
</div>
<!-- /.container-fluid -->
</div>
<!-- End of Main Content -->
<!-- Footer -->
<footer class="sticky-footer bg-white">
<div class="container my-auto">
<div class="copyright text-center my-auto">
<span>Copyright &copy; Your Website 2019</span>
</div>
</div>
</footer>
<!-- End of Footer -->
</div>
<!-- End of Content Wrapper -->
</div>
<!-- End of Page Wrapper -->
<!-- Scroll to Top Button-->
<a class="scroll-to-top rounded" href="#page-top">
<i class="fas fa-angle-up"></i>
</a>
<!-- Logout Modal-->
<div class="modal fade" id="logoutModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Ready to Leave?</h5>
<button class="close" type="button" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">Select "Logout" below if you are ready to end your current session.</div>
<div class="modal-footer">
<button class="btn btn-secondary" type="button" data-dismiss="modal">Cancel</button>
<a class="btn btn-primary" href="login.html">Logout</a>
</div>
</div>
</div>
</div>
<!-- Bootstrap core JavaScript-->
<script src="vendor/jquery/jquery.min.js"></script>
<script src="vendor/bootstrap/js/bootstrap.bundle.min.js"></script>
<!-- Core plugin JavaScript-->
<script src="vendor/jquery-easing/jquery.easing.min.js"></script>
<!-- Custom scripts for all pages-->
<script src="js/sb-admin-2.min.js"></script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
{% load static %}
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="">
<meta name="author" content="">
<title>SB Admin 2 - Charts</title>
<!-- Custom fonts for this template-->
<link href="{% static 'FirstApp/vendor/fontawesome-free/css/all.min.css' %}" rel="stylesheet" type="text/css">
<link href="https://fonts.googleapis.com/css?family=Nunito:200,200i,300,300i,400,400i,600,600i,700,700i,800,800i,900,900i" rel="stylesheet">
<!-- Custom styles for this template-->
<link href="{% static 'FirstApp/css/sb-admin-2.min.css' %}" rel="stylesheet">
</head>
<body id="page-top">
<!-- Page Wrapper -->
<div id="wrapper">
<!-- Sidebar -->
<ul class="navbar-nav bg-gradient-primary sidebar sidebar-dark accordion" id="accordionSidebar">
<!-- Sidebar - Brand -->
<a class="sidebar-brand d-flex align-items-center justify-content-center" href="index.html">
<div class="sidebar-brand-icon rotate-n-15">
<i class="fas fa-laugh-wink"></i>
</div>
<div class="sidebar-brand-text mx-3">SB Admin <sup>2</sup></div>
</a>
<!-- Divider -->
<hr class="sidebar-divider my-0">
<!-- Nav Item - Dashboard -->
<li class="nav-item">
<a class="nav-link" href="index.html">
<i class="fas fa-fw fa-tachometer-alt"></i>
<span>Dashboard</span></a>
</li>
<!-- Divider -->
<hr class="sidebar-divider">
<!-- Heading -->
<div class="sidebar-heading">
Interface
</div>
<!-- Nav Item - Pages Collapse Menu -->
<li class="nav-item">
<a class="nav-link collapsed" href="#" data-toggle="collapse" data-target="#collapseTwo" aria-expanded="true" aria-controls="collapseTwo">
<i class="fas fa-fw fa-cog"></i>
<span>Components</span>
</a>
<div id="collapseTwo" class="collapse" aria-labelledby="headingTwo" data-parent="#accordionSidebar">
<div class="bg-white py-2 collapse-inner rounded">
<h6 class="collapse-header">Custom Components:</h6>
<a class="collapse-item" href="gaze.html">Buttons</a>
<a class="collapse-item" href="pose.html">Cards</a>
</div>
</div>
</li>
<!-- Nav Item - Utilities Collapse Menu -->
<li class="nav-item">
<a class="nav-link collapsed" href="#" data-toggle="collapse" data-target="#collapseUtilities" aria-expanded="true" aria-controls="collapseUtilities">
<i class="fas fa-fw fa-wrench"></i>
<span>Utilities</span>
</a>
<div id="collapseUtilities" class="collapse" aria-labelledby="headingUtilities" data-parent="#accordionSidebar">
<div class="bg-white py-2 collapse-inner rounded">
<h6 class="collapse-header">Custom Utilities:</h6>
<a class="collapse-item" href="utilities-color.html">Colors</a>
<a class="collapse-item" href="utilities-border.html">Borders</a>
<a class="collapse-item" href="utilities-animation.html">Animations</a>
<a class="collapse-item" href="utilities-other.html">Other</a>
</div>
</div>
</li>
<!-- Divider -->
<hr class="sidebar-divider">
<!-- Heading -->
<div class="sidebar-heading">
Addons
</div>
<!-- Nav Item - Pages Collapse Menu -->
<li class="nav-item">
<a class="nav-link collapsed" href="#" data-toggle="collapse" data-target="#collapsePages" aria-expanded="true" aria-controls="collapsePages">
<i class="fas fa-fw fa-folder"></i>
<span>Pages</span>
</a>
<div id="collapsePages" class="collapse" aria-labelledby="headingPages" data-parent="#accordionSidebar">
<div class="bg-white py-2 collapse-inner rounded">
<h6 class="collapse-header">Login Screens:</h6>
<a class="collapse-item" href="login.html">Login</a>
<a class="collapse-item" href="register.html">Register</a>
<a class="collapse-item" href="forgot-password.html">Forgot Password</a>
<div class="collapse-divider"></div>
<h6 class="collapse-header">Other Pages:</h6>
<a class="collapse-item" href="404.html">404 Page</a>
<a class="collapse-item" href="blank.html">Blank Page</a>
</div>
</div>
</li>
<!-- Nav Item - Charts -->
<li class="nav-item active">
<a class="nav-link" href="charts.html">
<i class="fas fa-fw fa-chart-area"></i>
<span>Charts</span></a>
</li>
<!-- Nav Item - Tables -->
<li class="nav-item">
<a class="nav-link" href="tables.html">
<i class="fas fa-fw fa-table"></i>
<span>Tables</span></a>
</li>
<!-- Divider -->
<hr class="sidebar-divider d-none d-md-block">
<!-- Sidebar Toggler (Sidebar) -->
<div class="text-center d-none d-md-inline">
<button class="rounded-circle border-0" id="sidebarToggle"></button>
</div>
</ul>
<!-- End of Sidebar -->
<!-- Content Wrapper -->
<div id="content-wrapper" class="d-flex flex-column">
<!-- Main Content -->
<div id="content">
<!-- Topbar -->
<nav class="navbar navbar-expand navbar-light bg-white topbar mb-4 static-top shadow">
<!-- Sidebar Toggle (Topbar) -->
<button id="sidebarToggleTop" class="btn btn-link d-md-none rounded-circle mr-3">
<i class="fa fa-bars"></i>
</button>
<!-- Topbar Search -->
<form class="d-none d-sm-inline-block form-inline mr-auto ml-md-3 my-2 my-md-0 mw-100 navbar-search">
<div class="input-group">
<input type="text" class="form-control bg-light border-0 small" placeholder="Search for..." aria-label="Search" aria-describedby="basic-addon2">
<div class="input-group-append">
<button class="btn btn-primary" type="button">
<i class="fas fa-search fa-sm"></i>
</button>
</div>
</div>
</form>
<!-- Topbar Navbar -->
<ul class="navbar-nav ml-auto">
<!-- Nav Item - Search Dropdown (Visible Only XS) -->
<li class="nav-item dropdown no-arrow d-sm-none">
<a class="nav-link dropdown-toggle" href="#" id="searchDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<i class="fas fa-search fa-fw"></i>
</a>
<!-- Dropdown - Messages -->
<div class="dropdown-menu dropdown-menu-right p-3 shadow animated--grow-in" aria-labelledby="searchDropdown">
<form class="form-inline mr-auto w-100 navbar-search">
<div class="input-group">
<input type="text" class="form-control bg-light border-0 small" placeholder="Search for..." aria-label="Search" aria-describedby="basic-addon2">
<div class="input-group-append">
<button class="btn btn-primary" type="button">
<i class="fas fa-search fa-sm"></i>
</button>
</div>
</div>
</form>
</div>
</li>
<!-- Nav Item - Alerts -->
<li class="nav-item dropdown no-arrow mx-1">
<a class="nav-link dropdown-toggle" href="#" id="alertsDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<i class="fas fa-bell fa-fw"></i>
<!-- Counter - Alerts -->
<span class="badge badge-danger badge-counter">3+</span>
</a>
<!-- Dropdown - Alerts -->
<div class="dropdown-list dropdown-menu dropdown-menu-right shadow animated--grow-in" aria-labelledby="alertsDropdown">
<h6 class="dropdown-header">
Alerts Center
</h6>
<a class="dropdown-item d-flex align-items-center" href="#">
<div class="mr-3">
<div class="icon-circle bg-primary">
<i class="fas fa-file-alt text-white"></i>
</div>
</div>
<div>
<div class="small text-gray-500">December 12, 2019</div>
<span class="font-weight-bold">A new monthly report is ready to download!</span>
</div>
</a>
<a class="dropdown-item d-flex align-items-center" href="#">
<div class="mr-3">
<div class="icon-circle bg-success">
<i class="fas fa-donate text-white"></i>
</div>
</div>
<div>
<div class="small text-gray-500">December 7, 2019</div>
$290.29 has been deposited into your account!
</div>
</a>
<a class="dropdown-item d-flex align-items-center" href="#">
<div class="mr-3">
<div class="icon-circle bg-warning">
<i class="fas fa-exclamation-triangle text-white"></i>
</div>
</div>
<div>
<div class="small text-gray-500">December 2, 2019</div>
Spending Alert: We've noticed unusually high spending for your account.
</div>
</a>
<a class="dropdown-item text-center small text-gray-500" href="#">Show All Alerts</a>
</div>
</li>
<!-- Nav Item - Messages -->
<li class="nav-item dropdown no-arrow mx-1">
<a class="nav-link dropdown-toggle" href="#" id="messagesDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<i class="fas fa-envelope fa-fw"></i>
<!-- Counter - Messages -->
<span class="badge badge-danger badge-counter">7</span>
</a>
<!-- Dropdown - Messages -->
<div class="dropdown-list dropdown-menu dropdown-menu-right shadow animated--grow-in" aria-labelledby="messagesDropdown">
<h6 class="dropdown-header">
Message Center
</h6>
<a class="dropdown-item d-flex align-items-center" href="#">
<div class="dropdown-list-image mr-3">
<img class="rounded-circle" src="https://source.unsplash.com/fn_BT9fwg_E/60x60" alt="">
<div class="status-indicator bg-success"></div>
</div>
<div class="font-weight-bold">
<div class="text-truncate">Hi there! I am wondering if you can help me with a problem I've been having.</div>
<div class="small text-gray-500">Emily Fowler · 58m</div>
</div>
</a>
<a class="dropdown-item d-flex align-items-center" href="#">
<div class="dropdown-list-image mr-3">
<img class="rounded-circle" src="https://source.unsplash.com/AU4VPcFN4LE/60x60" alt="">
<div class="status-indicator"></div>
</div>
<div>
<div class="text-truncate">I have the photos that you ordered last month, how would you like them sent to you?</div>
<div class="small text-gray-500">Jae Chun · 1d</div>
</div>
</a>
<a class="dropdown-item d-flex align-items-center" href="#">
<div class="dropdown-list-image mr-3">
<img class="rounded-circle" src="https://source.unsplash.com/CS2uCrpNzJY/60x60" alt="">
<div class="status-indicator bg-warning"></div>
</div>
<div>
<div class="text-truncate">Last month's report looks great, I am very happy with the progress so far, keep up the good work!</div>
<div class="small text-gray-500">Morgan Alvarez · 2d</div>
</div>
</a>
<a class="dropdown-item d-flex align-items-center" href="#">
<div class="dropdown-list-image mr-3">
<img class="rounded-circle" src="https://source.unsplash.com/Mv9hjnEUHR4/60x60" alt="">
<div class="status-indicator bg-success"></div>
</div>
<div>
<div class="text-truncate">Am I a good boy? The reason I ask is because someone told me that people say this to all dogs, even if they aren't good...</div>
<div class="small text-gray-500">Chicken the Dog · 2w</div>
</div>
</a>
<a class="dropdown-item text-center small text-gray-500" href="#">Read More Messages</a>
</div>
</li>
<div class="topbar-divider d-none d-sm-block"></div>
<!-- Nav Item - User Information -->
<li class="nav-item dropdown no-arrow">
<a class="nav-link dropdown-toggle" href="#" id="userDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span class="mr-2 d-none d-lg-inline text-gray-600 small">Valerie Luna</span>
<img class="img-profile rounded-circle" src="https://source.unsplash.com/QAB-WJcbgJk/60x60">
</a>
<!-- Dropdown - User Information -->
<div class="dropdown-menu dropdown-menu-right shadow animated--grow-in" aria-labelledby="userDropdown">
<a class="dropdown-item" href="#">
<i class="fas fa-user fa-sm fa-fw mr-2 text-gray-400"></i>
Profile
</a>
<a class="dropdown-item" href="#">
<i class="fas fa-cogs fa-sm fa-fw mr-2 text-gray-400"></i>
Settings
</a>
<a class="dropdown-item" href="#">
<i class="fas fa-list fa-sm fa-fw mr-2 text-gray-400"></i>
Activity Log
</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item" href="#" data-toggle="modal" data-target="#logoutModal">
<i class="fas fa-sign-out-alt fa-sm fa-fw mr-2 text-gray-400"></i>
Logout
</a>
</div>
</li>
</ul>
</nav>
<!-- End of Topbar -->
<!-- Begin Page Content -->
<div class="container-fluid">
<!-- Page Heading -->
<h1 class="h3 mb-2 text-gray-800">Charts</h1>
<p class="mb-4">Chart.js is a third party plugin that is used to generate the charts in this theme. The charts below have been customized - for further customization options, please visit the <a target="_blank" href="https://www.chartjs.org/docs/latest/">official Chart.js documentation</a>.</p>
<!-- Content Row -->
<div class="row">
<div class="col-xl-8 col-lg-7">
<!-- Area Chart -->
<div class="card shadow mb-4">
<div class="card-header py-3">
<h6 class="m-0 font-weight-bold text-primary">Area Chart</h6>
</div>
<div class="card-body">
<div class="chart-area">
<canvas id="myAreaChart"></canvas>
</div>
<hr>
Styling for the area chart can be found in the <code>/js/demo/chart-area-demo.js</code> file.
</div>
</div>
<!-- Bar Chart -->
<div class="card shadow mb-4">
<div class="card-header py-3">
<h6 class="m-0 font-weight-bold text-primary">Bar Chart</h6>
</div>
<div class="card-body">
<div class="chart-bar">
<canvas id="myBarChart"></canvas>
</div>
<hr>
Styling for the bar chart can be found in the <code>/js/demo/chart-bar-demo.js</code> file.
</div>
</div>
</div>
<!-- Donut Chart -->
<div class="col-xl-4 col-lg-5">
<div class="card shadow mb-4">
<!-- Card Header - Dropdown -->
<div class="card-header py-3">
<h6 class="m-0 font-weight-bold text-primary">Donut Chart</h6>
</div>
<!-- Card Body -->
<div class="card-body">
<div class="chart-pie pt-4">
<canvas id="myPieChart"></canvas>
</div>
<hr>
Styling for the donut chart can be found in the <code>/js/demo/chart-pie-demo.js</code> file.
</div>
</div>
</div>
</div>
</div>
<!-- /.container-fluid -->
</div>
<!-- End of Main Content -->
<!-- Footer -->
<footer class="sticky-footer bg-white">
<div class="container my-auto">
<div class="copyright text-center my-auto">
<span>Copyright &copy; Your Website 2019</span>
</div>
</div>
</footer>
<!-- End of Footer -->
</div>
<!-- End of Content Wrapper -->
</div>
<!-- End of Page Wrapper -->
<!-- Scroll to Top Button-->
<a class="scroll-to-top rounded" href="#page-top">
<i class="fas fa-angle-up"></i>
</a>
<!-- Logout Modal-->
<div class="modal fade" id="logoutModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Ready to Leave?</h5>
<button class="close" type="button" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">Select "Logout" below if you are ready to end your current session.</div>
<div class="modal-footer">
<button class="btn btn-secondary" type="button" data-dismiss="modal">Cancel</button>
<a class="btn btn-primary" href="login.html">Logout</a>
</div>
</div>
</div>
</div>
<!-- Bootstrap core JavaScript-->
<script src="vendor/jquery/jquery.min.js"></script>
<script src="vendor/bootstrap/js/bootstrap.bundle.min.js"></script>
<!-- Core plugin JavaScript-->
<script src="vendor/jquery-easing/jquery.easing.min.js"></script>
<!-- Custom scripts for all pages-->
<script src="js/sb-admin-2.min.js"></script>
<!-- Page level plugins -->
<script src="vendor/chart.js/Chart.min.js"></script>
<!-- Page level custom scripts -->
<script src="js/demo/chart-area-demo.js"></script>
<script src="js/demo/chart-pie-demo.js"></script>
<script src="js/demo/chart-bar-demo.js"></script>
</body>
</html>
{% extends "FirstApp/base.html" %}
{% block title %}My amazing blog{% endblock %}
{% block content %}
{% for entry in blog_entries %}
<h2>{{ entry.title }}</h2>
<p>{{ entry.body }}</p>
{% endfor %}
{% endblock %}
\ No newline at end of file
{% extends 'FirstApp/template.html' %}
<!DOCTYPE html>
<html lang="en">
<body id="page-top">
<!-- Page Wrapper -->
{% block javascript %}
{% load static %}
<!-- Bootstrap core JavaScript-->
<script src="{% static 'FirstApp/vendor/jquery/jquery.min.js' %}"></script>
<script src="{% static 'FirstApp/vendor/bootstrap/js/bootstrap.bundle.min.js' %}"></script>
<!-- Core plugin JavaScript-->
<script src="{% static 'FirstApp/vendor/jquery-easing/jquery.easing.min.js' %}"></script>
<script type="text/javascript">
const cur_date = new Date().toDateString();
let video_name = '';
$(document).ready(function () {
$('.video_row').click(function () {
video_name = $(this).attr('id');
let video_duration = $(this).attr('data-duration');
let src = "{% static '' %}FirstApp/videos/" + video_name;
//assigning the src
$("video").attr('src', src);
//setting the video details
$('#video_name').text(video_name);
$('#video_duration').text(video_duration);
$('#video_date').text(cur_date);
});
//to handle video extraction button
$('#extractBtn').click(function () {
//run the api
fetch('http://127.0.0.1:8000/videoExtract', {
method: 'POST',
headers: {
"Content-type": "application/json"
},
body: JSON.stringify({
"video_name": video_name
})
})
.then((res) => res.json())
.then((out) => alert(out.response))
.catch((error) => alert('this is an error'));
});
});
</script>
{% endblock %}
<div id="wrapper">
<!-- Sidebar -->
<!-- Content Wrapper -->
<div id="content-wrapper" class="d-flex flex-column">
<!-- Main Content -->
<div id="content">
<!-- Begin Page Content -->
{% block 'container-fluid' %}
<div class="container-fluid">
{% load static %}
<!-- Page Heading -->
<div class="d-sm-flex align-items-center justify-content-between mb-4">
<h1 class="h3 mb-0 text-gray-800">Video Extractor</h1>
</div>
<!--1st row -->
<div class="row p-2">
<!--first column -->
<div class="col-lg-6">
<!-- Main Video Context -->
<div class="card shadow mb-4">
<div class="card-header py-3">
<h6 class="m-0 font-weight-bold text-primary">Main Video</h6>
</div>
<div class="card-body">
<video width="500" height="300" id="first_video" controls>
<source src="{% static '' %}FirstApp/videos/{{ firstVideo.name }}"
type="video/mp4">
Your browser does not support the video tag.
</video>
<table class="table table-borderless table-striped m-1">
<tbody>
<tr>
<td class="font-weight-bold">Name</td>
<td id="video_name">{{ firstVideo.name }}</td>
</tr>
<tr>
<td class="font-weight-bold">Duration</td>
<td id="video_duration">{{ firstVideo.duration }}</td>
</tr>
<tr>
<td class="font-weight-bold">Date</td>
<td id="video_date"></td>
</tr>
</tbody>
</table>
<div class="col-lg-3 p-3">
<button type="button" class="btn btn-outline-primary" id="extractBtn">Extract</button>
</div>
</div>
</div>
</div>
<!--second column -->
<div class="col-lg-6">
<!-- Main Video Context -->
<div class="card shadow mb-4">
<div class="card-header py-3">
<h6 class="m-0 font-weight-bold text-primary">List of Lectures</h6>
</div>
<div class="card-body">
<table class="table table-bordered">
<thead>
<tr>
<th>Video Name</th>
<th>Length</th>
</tr>
</thead>
<tbody>
{% for video in Videos %}
<tr class="video_row" id="{{ video.name }}" data-duration="{{ video.duration }}">
<td>
{{ video.name }}
</td>
<td>{{ video.duration }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
<!-- End of container-fluid -->
</div>
<!-- End of Main Content -->
</div>
<!-- End of Content Wrapper -->
</div>
{% block 'modal' %}
<div class="modal fade" id="gif-body" role="dialog" aria-labelledby="gif-body">
<div class="modal-dialog modal-lg" style="max-width: 1600px; max-height: 800px">
<div class="modal-content">
<div class="modal-header">
<h2 class="modal-title">Processing....</h2>
<button class="close" type="button" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body text-center">
<img src="{% static 'FirstApp/images/ajax-loader.gif' %}" width="200" height="500"
alt="This is a GIF">
<h5>This might take few seconds...</h5>
</div>
</div>
</div>
</div>
{% endblock %}
</body>
</html>
\ No newline at end of file
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="">
<meta name="author" content="">
<title>SB Admin 2 - Forgot Password</title>
<!-- Custom fonts for this template-->
<link href="vendor/fontawesome-free/css/all.min.css" rel="stylesheet" type="text/css">
<link href="https://fonts.googleapis.com/css?family=Nunito:200,200i,300,300i,400,400i,600,600i,700,700i,800,800i,900,900i" rel="stylesheet">
<!-- Custom styles for this template-->
<link href="css/sb-admin-2.min.css" rel="stylesheet">
</head>
<body class="bg-gradient-primary">
<div class="container">
<!-- Outer Row -->
<div class="row justify-content-center">
<div class="col-xl-10 col-lg-12 col-md-9">
<div class="card o-hidden border-0 shadow-lg my-5">
<div class="card-body p-0">
<!-- Nested Row within Card Body -->
<div class="row">
<div class="col-lg-6 d-none d-lg-block bg-password-image"></div>
<div class="col-lg-6">
<div class="p-5">
<div class="text-center">
<h1 class="h4 text-gray-900 mb-2">Forgot Your Password?</h1>
<p class="mb-4">We get it, stuff happens. Just enter your email address below and we'll send you a link to reset your password!</p>
</div>
<form class="user">
<div class="form-group">
<input type="email" class="form-control form-control-user" id="exampleInputEmail" aria-describedby="emailHelp" placeholder="Enter Email Address...">
</div>
<a href="login.html" class="btn btn-primary btn-user btn-block">
Reset Password
</a>
</form>
<hr>
<div class="text-center">
<a class="small" href="register.html">Create an Account!</a>
</div>
<div class="text-center">
<a class="small" href="login.html">Already have an account? Login!</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Bootstrap core JavaScript-->
<script src="vendor/jquery/jquery.min.js"></script>
<script src="vendor/bootstrap/js/bootstrap.bundle.min.js"></script>
<!-- Core plugin JavaScript-->
<script src="vendor/jquery-easing/jquery.easing.min.js"></script>
<!-- Custom scripts for all pages-->
<script src="js/sb-admin-2.min.js"></script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<p>This is my username{{ username }}</p>
</body>
</html>
\ No newline at end of file
{% extends 'FirstApp/template.html' %}
<!DOCTYPE html>
<html lang="en">
<body id="page-top">
<!-- Page Wrapper -->
{% block javascript %}
{% load static %}
<!-- Bootstrap core JavaScript-->
<script src="{% static 'FirstApp/vendor/jquery/jquery.min.js' %}"></script>
<script src="{% static 'FirstApp/vendor/bootstrap/js/bootstrap.bundle.min.js' %}"></script>
<!-- Page level plugins -->
<script src="{% static 'FirstApp/vendor/datatables/jquery.dataTables.min.js' %}"></script>
<script src="{% static 'FirstApp/vendor/datatables/dataTables.bootstrap4.min.js' %}"></script>
<!-- Page level custom scripts -->
<script src="{% static 'FirstApp/js/demo/datatables-demo.js' %}"></script>
<!-- Core plugin JavaScript-->
<script src="{% static 'FirstApp/vendor/jquery-easing/jquery.easing.min.js' %}"></script>
<!-- Load TensorFlow.js -->
<script src="https://unpkg.com/@tensorflow/tfjs"></script>
<!-- Load Posenet -->
<script src="https://unpkg.com/@tensorflow-models/posenet">
</script>
<script type="text/javascript">
var global_subject = '';
var global_lecturer = '';
var global_lecture_video_id = '';
var global_video_name = '';
var global_lecturer_subject_index = 0;
//jquery
$(document).ready(function () {
//select a particular subject
$('input[type=radio]').click(function () {
let subject_id = $(this).attr('id');
global_subject = subject_id;
let lecturer = $(this).attr('data-lecturer');
global_lecturer = lecturer;
let subject_name = $(this).attr('data-name');
$('#timetable').attr('hidden', true);
$('#no_timetable_content').attr('hidden', true);
$('.student-detection-rows').remove();
$('#timetable_body').children().map(function () {
$(this).remove();
});
$('#no_subject_selected').attr('hidden', true);
$('#timetable_caption').text('subject: ' + subject_name);
$('#loader').attr('hidden', false);
//fetching the timetable from the db
fetch('http://127.0.0.1:8000/timetables')
.then((res) => res.json())
.then((out) => createTimeTable(out, subject_id, lecturer))
.catch((error) => alert('this is the error: ' + error))
});
$(document).on('click', '.btn-info', function (e) {
let clicked_class = e.target.className;
let object = e;
let real_class = clicked_class.split(' ')[1];
real_class = '.' + real_class;
let date = e.target.parentNode.parentNode.firstChild.innerHTML;
fetch('http://127.0.0.1:8000/get-lecture-video-for-pose/?lecturer=' + global_lecturer + '&date=' + date + '&index=' + global_lecturer_subject_index)
.then((res) => res.json())
.then((out) => displayLectureVideoDetails(out, object))
.catch((error) => alert('an error occurred: ' + error));
});
function createTimeTable(timetable, subject, lecturer) {
$('#loader').attr('hidden', true);
$('#timetable').attr('hidden', false);
let isTimetableSubject = false;
timetable.map((item, i) => {
item.timetable.map((table, index) => {
let lecturer_subject_index_arr = [];
//to get the number of subjects taught by the lecturer in a day
table.time_slots.forEach((slot1, ind) => {
let isLecturer = slot1.lecturer.id === Number(lecturer);
if (isLecturer) {
lecturer_subject_index_arr.push(ind);
}
});
//iterating each slot (for a given day)
table.time_slots.forEach((slot, in1) => {
let isLecturer = slot.lecturer.id === Number(lecturer);
let isLecSubject = slot.subject.subject_code === subject;
if (isLecturer && isLecSubject) {
let html = '';
global_lecturer_subject_index = lecturer_subject_index_arr.findIndex((inner) => inner === in1);
isTimetableSubject = true;
html += "<tr class='lecture-details'><td class='slot_date'>" + table.date + "</td>"
+ "<td>" + slot.location + "</td>"
+ "<td>" + slot.start_time + "</td>"
+ "<td>" + slot.end_time + "</td>"
+ "<td><button type='button' class='btn btn-info'>Video</button></td>"
+ "<td></td>"
+ "</tr>";
$('#timetable_body').append(html);
}
});
});
if (!isTimetableSubject) {
$('#timetable').attr('hidden', true);
$('#no_timetable_content').attr('hidden', false);
}
});
}
//function to display lecture video details
function displayLectureVideoDetails(lectureVideo, e) {
//get the lecture video response
let video = lectureVideo.response;
$('#video_name').text(video.video_name);
$('#video_duration').text(video.video_length);
$('#video_date').text(video.date);
global_lecture_video_id = video.lecture_video_id;
global_video_name = video.video_name;
//display the modal
$('#video_modal').modal();
//showing the video frames
//removing the previous frames (if there is any)
$('#main_frames').remove();
//displaying the loader (for the frames)
$('#frame_loader').attr('hidden', false);
//hiding the temporary text
$('#temporary_text').attr('hidden', true);
fetch('http://127.0.0.1:8000/get-lecture-video-extracted-frames/?lecture_video_id=' + global_lecture_video_id + '&lecture_video_name=' + global_video_name)
.then((res) => res.json())
.then((out) => {
let frames = createFrames(out);
return frames
})
.then((obj) => {
$('#video_frames').prepend(obj);
$('#frame_loader').attr('hidden', true);
$('#slidecontainer').attr('hidden', false);
})
.catch((error) => alert('this is the error: ' + error));
}
//to handle the 'btn-success' (process) button
$(document).on('click', '.btn-success', function (e) {
//sending the POST request to process the lecture activities
fetch('http://127.0.0.1:8000/process-lecture-activity/?lecture_video_name=' + global_video_name + '&lecture_video_id=' + global_lecture_video_id)
.then((res) => res.json())
.then((out) => handleResponse(out.response, e))
.catch((error) => alert('error: ' + error));
});
//this is to change the button from 'process' to 'results'
function handleResponse(response, e) {
//change the button, if the response is positive
if (response) {
e.target.parentNode.parentNode.lastChild.innerHTML = '<button type="button" class="btn btn-primary">Results</button>';
}
}
//this section is responsible for displaying the frames as video
//creating the frame content
function createFrames(res) {
let main_frame_content = "<div class='row' id='main_frames'>";
main_frame_content += "<ul class='list-group list-group-horizontal'>";
let count = 0;
//loop through the frames
res.extracted.map((image) => {
let img_src = "";
let len = image.detections.length;
if (count === 0) {
main_frame_content += "<li class='list-group-item text-center' id='image_0'>";
img_src = "<img src='{% static '' %}FirstApp/activity/" + global_video_name + "/" + res.extracted[0].frame + "/" + res.extracted[0].detections[0] + "' width='400' height='400'>";
} else {
main_frame_content += "<li class='list-group-item other-frames' id='image_" + count + "' hidden>";
img_src = "<img src='{% static '' %}FirstApp/activity/" + global_video_name + "/" + image.frame + "/" + image.detections[len - 1] + "' class='img-link' width='400' height='400'>";
}
main_frame_content += img_src;
main_frame_content += "</li>";
count++;
});
main_frame_content += "</ul>";
main_frame_content += "</div>";
//setting the min, max values of the slider
$('#myActivityRange').attr({'min': 0, 'max': count});
//dsiplay the 'process' button
$('#process_btn').attr('hidden', false);
return main_frame_content;
}
//declaring the variable for setInterval function
let timeVar = null;
//handling the play button
$('#play_pause_icon_activity').click(function () {
//defining the two possible classes
let play_class = "fas fa-play";
let pause_class = "fas fa-pause";
//retrieving the current icon class
let current_class = $(this).attr('class');
//assigning the correct class based on the icon clicked
let new_class = (current_class === play_class) ? pause_class : play_class;
//setting the new class
$(this).attr('class', new_class);
//handling the slider
let slider = document.getElementById("myActivityRange");
let output = document.getElementById("demo");
//when the button is playing
if (current_class === play_class) {
timeVar = setInterval(() => {
let value = slider.value;
let new_slider_value = Number(value) + 1;
slider.value = new_slider_value;
output.innerHTML = new_slider_value.toString();
let selectedImage = '#image_' + Number(value);
//displaying the relevant image
$('#image_0').html($(selectedImage).html());
}, 100);
}
//when the button is paused
else if (current_class === pause_class) {
clearInterval(timeVar);
}
});
//handling the slider
let slider = document.getElementById("myActivityRange");
let output = document.getElementById("demo");
output.innerHTML = slider.value;
slider.oninput = function () {
output.innerHTML = this.value;
let selectedImage = '#image_' + Number(this.value);
//hide
{#$('#image_0').attr('hidden', true);#}
$('#image_0').html($(selectedImage).html());
//setting the selected image
{#$(selectedImage).attr('hidden', false);#}
};
$(document).on('click', '.img-link', function (e) {
//removing previously displayed detections
$('.detections').remove();
//removing the no-content message
$('#no_detection_message_content').hide();
//appearing the loader
$('#detection_loader').attr('hidden', false);
let img_src_arr = e.target.src.split('/');
let len = img_src_arr.length;
let src = img_src_arr[len - 1];
let frame_name_arr = src.split('.');
let frame_name = frame_name_arr[0];
//fetching the detection for the selected frame
fetch('http://127.0.0.1:8000/get-lecture-activity-frame-detection/?video_name=' + global_video_name + "&frame_name=" + frame_name)
.then((res) => res.json())
.then((out) => displayDetections(out.detections, frame_name))
.catch((error) => alert('this is an error'));
});
//the function to display detections
function displayDetections(detections, frame_name) {
let img_string = '';
let no_of_detections = detections.length;
//disabling the loader
$('#detection_loader').attr('hidden', true);
//appearing the no of detections number area
$('#detection_number_area').attr('hidden', false);
$('#no_of_detections').text(no_of_detections);
detections.map((detection) => {
img_string += "<img src='{% static '' %}FirstApp/activity/" + global_video_name + "/" + frame_name + "/" + detection + "' class='detections m-2' width='100' height='100' >"
});
$('#detection_frames').prepend(img_string);
}
//listening for click events in labels
$('.labels').click(function () {
let label = Number($(this).attr('data-number'));
//removing the previous student detection lists
$('.student_detection_lists').remove();
//appearing the loader
$('#detection_student_loader').attr('hidden', false);
//disappearing the no content message
$('#no_detection_student_content').attr('hidden', true);
//fetching from the api
fetch('http://127.0.0.1:8000/get-lecture-activity-detection-for-label/?video_name=' + global_video_name + '&label=' + label)
.then((res) => res.json())
.then((out) => createDetectedStudentFrames(out))
.catch((error) => alert('this is the error: ' + error))
});
//creating the detected students frames
function createDetectedStudentFrames(detections) {
let htmlString = "";
//iterating through the student
detections.people.map((student) => {
let title = student.split('.')[0];
let images = "";
htmlString += "<div class='row p-3 student-detection-rows'>";
let student_count = 0;
//iterating through the frames
detections.response.map((frame) => {
let frame_detections = frame.detections;
if (frame_detections.includes(student)) {
if (student_count === 0) {
images += "<li class='list-group-item frame-0' id='image_0_" + title + "'>";
} else {
images += "<li class='list-group-item other-student-frames' id='image_" + student_count + "_" + title + "' hidden>";
}
images += "<img src='{% static '' %}FirstApp/Activity/" + global_video_name + "/" + frame.frame + "/" + student + "' width='200' height='200'>";
images += "</li>";
//increment the student count
student_count++;
}
});
htmlString += "<h6 class='font-italic'>" + title + "</h6>";
htmlString += "<ul class='list-group list-group-horizontal student_detection_lists' style='overflow-x: scroll'>";
htmlString += images;
htmlString += "</ul>";
htmlString += "<div class='slidecontainer'>";
htmlString += "<div class='row m-3'></div>";
htmlString += "<div class='row'>";
htmlString += "<span><i class='fas fa-play play-pause-icon-student-frames' id='icon_" + title + "'></i></span>";
htmlString += "</div>";
htmlString += "<input type='range' min='1' max='100' value='0' class='slider' id='slider_" + title + "'>";
htmlString += "<p>No of frames: <span id='demo_" + title + "'></span></p>";
htmlString += "</div>";
htmlString += "</div>";
});
//disappearing the loader
$('#detection_student_loader').attr('hidden', true);
//append to the relevant html card content
$('#detection_students').append(htmlString);
}
let studentTimeVar = null;
//playing the frames for each student detection
$(document).on('click', '.play-pause-icon-student-frames', function (e) {
//defining the two possible classes
let play_class = "fas fa-play play-pause-icon-student-frames";
let pause_class = "fas fa-pause play-pause-icon-student-frames";
//retrieving the current icon class
let current_class = $(this).attr('class');
//assigning the correct class based on the icon clicked
let new_class = (current_class === play_class) ? pause_class : play_class;
//setting the new class
$(this).attr('class', new_class);
//extracting the title pf the clicked icon
let title_part = $(this).attr('id');
let title = title_part.split("_")[1];
//handling the slider
let slider = document.getElementById("slider_" + title);
let output = document.getElementById("demo_" + title);
//when the button is playing
if (current_class === play_class) {
studentTimeVar = setInterval(() => {
let value = slider.value;
let new_slider_value = Number(value) + 1;
slider.value = new_slider_value;
output.innerHTML = new_slider_value.toString();
let selectedImage = '#image_' + Number(value) + '_' + title;
//displaying the relevant image
$('#image_0_' + title).html($(selectedImage).html());
}, 100);
}
//when the button is paused
else if (current_class === pause_class) {
clearInterval(studentTimeVar);
}
});
//processing the video frames for pose estimation
$('#process_btn').click(function () {
//hide the message
$('#no_individual_student_frames').attr('hidden', true);
//show the loader
$('#individual_student_frames_loader').attr('hidden', false);
//using the fetch api
fetch('http://127.0.0.1:8000/get-lecture-video-individual-student-frames/?video_name=' + global_video_name)
.then((res) => res.json())
.then((out) => displayStudentIndividualFrames(out))
.catch((error) => alert('this is the error: ' + error))
});
//this function will display each student individual frames
function displayStudentIndividualFrames(response) {
let htmlString = "";
//iterating through the student
response.people.map((student) => {
let title = student.split('.')[0];
let images = "";
htmlString += "<div class='row p-3 student-individual-rows'>";
let student_count = 0;
//iterating through the frames
response.response.map((frame) => {
let frame_detections = frame.detections;
let frame_detection_length = frame_detections.length;
if (frame_detections.includes(student)) {
if (student_count === 0) {
images += "<li class='list-group-item frame-0' id='image_0_individual_" + title + "'>";
} else {
images += "<li class='list-group-item other-student-frames' id='list_item_individual_" + student_count + "_" + title + "' hidden>";
}
images += "<img src='{% static '' %}FirstApp/Activity/" + global_video_name + "/" + frame.frame + "/" + student + "' id='image_individual_" + student_count + "_" + title + "' width='200' height='200'>";
images += "</li>";
if (student_count === (frame_detection_length - 1)) {
images += "<li class='list-group-item'>";
images += "<button type='button' class='btn btn-dark individual-student-frame' id='individual_student_" + title + "'>calculate</button>";
images += "</li>";
}
//increment the student count
student_count++;
}
});
htmlString += "<ul class='list-group'>";
htmlString += "<li class='list-group-item'>";
htmlString += "<div class='row m-3'>";
htmlString += "<h4 class='font-weight-bold'>Student ID: <span>" + title + "</span></h4>";
htmlString += "</div>";
htmlString += "</li>";
htmlString += "<li class='list-group-item'>";
htmlString += "<div class='row'>";
htmlString += "<ul class='list-group list-group-horizontal student_individual_lists' id='student_individual_" + title + "' style='overflow-x: scroll'>";
htmlString += images;
htmlString += "</ul>";
htmlString += "</div>";
htmlString += "</li>";
htmlString += "<li class='list-group-item'>";
htmlString += "<div class='slidecontainer'>";
htmlString += "<div class='row m-3'></div>";
htmlString += "<div class='row'>";
htmlString += "<span><i class='fas fa-play play-pause-icon-student-evaluations' id='icon_" + title + "'></i></span>";
htmlString += "</div>";
htmlString += "<input type='range' min='1' max='100' value='0' class='slider' id='slider_evaluation" + title + "'>";
htmlString += "<p>No of frames: <span id='demo_evaluation" + title + "'></span></p>";
htmlString += "</div>";
htmlString += "</div>";
htmlString += "</li>";
htmlString += "</ul>";
});
//disappearing the loader
$('#individual_student_frames_loader').attr('hidden', true);
//append to the relevant html card content
$('#individual_student_frames').append(htmlString);
}
//to listen to click event from individual students
$(document).on('click', '.individual-student-frame', function (e) {
let student_id_parts = $(this).attr('id');
let student_id = student_id_parts.split('_')[2];
let frame_list_id = '#student_individual_' + student_id;
//traverse through the children of the selected list
let image_list = $(frame_list_id).children().map((i, child) => {
//traversing the <li>
return $(child).find('img').attr('id');
});
student_id += ".png";
e.target.innerHTML = "<span class='font-italic'>Processing</span>";
//running the posenet model
loadModel(image_list, student_id, e);
});
//POSENET model
//this function will load the posenet model
async function loadModel(imageList, student, e) {
const imageScaleFactor = 1.0;
const flipHorizontal = false;
const outputStride = 16;
// get up to 5 poses
const maxPoseDetections = 20;
// minimum confidence of the root part of a pose
const scoreThreshold = 0.2;
// minimum distance in pixels between the root parts of poses
const nmsRadius = 20;
//load the model
const model = await posenet.load();
//this array will keep all the detected poses
let keypoints_arr = [];
//loop through the selected image IDs
for (let i = 0; i < imageList.length; i++) {
let image_id = imageList[i];
let imageElement = document.getElementById(image_id);
//estimate the poses
const poses = await model.estimateSinglePose(imageElement, {
imageScaleFactor: 1.0,
flipHorizontal: false,
outputStride: 8,
maxDetections: 10,
scoreThreshold: 0.5,
nmsRadius: 20
});
//push to the created keypoints array
keypoints_arr.push(poses)
}
//using the fetch API to POST the data
await fetch('http://127.0.0.1:8000/process-lecture-video-individual-pose-estimation', {
method: 'POST',
headers: {
"Content-type": "application/json"
},
body: JSON.stringify({
"video_name": global_video_name,
"student": student,
"poses": keypoints_arr
})
})
.then((res) => res.json())
.then((out) => {
e.target.innerHTML = "<span>Completed</span>";
})
.catch((err) => alert('this is the error: ' + err));
/*
const imageElement = document.getElementById(id);
let isImageDisplayed = true;
if (isImageDisplayed) {
$('#message').hide();
$('#croppedMessage').hide();
}
let splitted = imageElement.src.split('/');
//assigning the image name
imageName = splitted[7].split('.')[0];
//show the loading screen
$('#loading').attr('hidden', false);
$('#cropped_loading').attr('hidden', false);
const model = await posenet.load();
const poses = await model.estimateMultiplePoses(imageElement, {
imageScaleFactor: 1.0,
flipHorizontal: false,
outputStride: 8,
maxDetections: 10,
scoreThreshold: 0.5,
nmsRadius: 20
});
let keypoints_arr = [];
//hide the loading screen
$('#loading').hide();
$('#cropped_loading').hide();
//removing the previous canvas
$('#showFace').remove();
//removing the previous canvas row
$('#canvas-frame').remove();
//creating the same canvas
let newCanvasContent = "<canvas id='showFace' width='500' height='300'></canvas>";
$(newCanvasContent).prependTo('#canvas-body');
let canvas = document.getElementById('showFace');
let context = canvas.getContext('2d');
context.drawImage(imageElement, 0, 0, 500, 300);
let htmlContent = "<div id='canvas-frame'><div class='row'>";
{#drawing rectangle for each person face#}
for (let count = 0; count < poses.length; count++) {
let person = poses[count];
let new_canvas_id = 'canvas-' + (count + 1);
let new_canvas = document.getElementById(new_canvas_id);
{#let new_context = new_canvas.getContext('2d');#}
let x1 = Math.round(Number(person.keypoints[5].position.x));
let y1 = Math.round(Number(person.keypoints[5].position.y));
let x2 = Math.round(Number(person.keypoints[6].position.x));
let y2 = Math.round(Number(person.keypoints[6].position.y));
let x_diff = x1 - x2;
let y_diff = y1 - y2;
let x_pow = Math.pow(x_diff, 2);
let y_pow = Math.pow(y_diff, 2);
let summation = x_pow + y_pow;
let distance = Math.sqrt(summation);
let fraction = Math.round(distance * 0.6);
let middle_x = x2 + fraction;
let middle_y = y2 - 20;
let head_x = middle_x;
let head_y = middle_y - fraction;
let left_upper_x = middle_x - fraction;
context.rect(left_upper_x, head_y, distance, fraction);
{#context.fillStyle = 'red';#}
context.stroke();
let canvasHtml = "<canvas id='" + new_canvas_id + "' height='200' width='200'></canvas>";
htmlContent += "<div class='flex-column' style='width: 30%'>" +
canvasHtml
+ "</div>";
if (((count + 1)) % 3 === 0) {
htmlContent += "</div><div class='row'>";
}
//appending the keypoints to the array
keypoints_arr[keypoints_arr.length] = {
'left_x': left_upper_x,
'left_y': head_y,
'width': distance,
'height': fraction
};
//appending the keypoints to the global array
points[points.length] = {
'left_x': left_upper_x,
'left_y': head_y,
'width': distance,
'height': fraction
};
}
htmlContent += "</div>";
htmlContent += "</div>";
//append the html content
$('#test-canvas').prepend(htmlContent);
{#getting the natural dimensions of the image#}
let originalWidth = imageElement.naturalWidth;
let originalHeight = imageElement.naturalHeight;
//drawing extracted faces on the canvas
for (let i = 0; i < poses.length; i++) {
let canvas_id = 'canvas-' + (i + 1);
let canvasDoc = document.getElementById(canvas_id);
let canvasCont = canvasDoc.getContext('2d');
let left_x = keypoints_arr[i].left_x;
let left_y = keypoints_arr[i].left_y;
let width = keypoints_arr[i].width;
let height = keypoints_arr[i].height;
let x_scale = originalWidth / imageElement.offsetWidth;
let y_scale = originalHeight / imageElement.offsetHeight;
let x_corner = left_x * x_scale;
let y_corner = left_y * y_scale;
let modified_width = width * x_scale;
let modified_height = height * y_scale;
{#canvasCont.drawImage(imageElement, left_x * 6, left_y * 6, width * 10, height * 10, 0, 0, 100, 150);#}
{#canvasCont.drawImage(imageElement, 0, 800, 800, 500, 0, 0, 100, 150);#}
{#canvasCont.drawImage(imageElement, 230.2, 700, 400, 400, 0, 0, 150, 150);#}
{#canvasCont.drawImage(imageElement, left_x, left_y, width * 10, height * 10, 0, 0, 100, 150);#}
{#canvasCont.drawImage(imageElement, left_x, left_y, width, height, 0, 0, 100, 150);#}
{#canvasCont.drawImage(imageElement, left_x * 10, left_y * 10, width * 10, height * 10, 0, 0, 100, 150);#}
canvasCont.drawImage(imageElement, x_corner, y_corner, modified_width, modified_height, 0, 0, 199, 199);
}
//displaying the download button
{#$('#downloadBtn').attr('hidden', false);#}
//then save the images
//saveImages();
*/
}
});
</script>
{% endblock %}
<div id="wrapper">
<!-- Sidebar -->
<!-- Content Wrapper -->
<div id="content-wrapper" class="d-flex flex-column">
<!-- Main Content -->
<div id="content">
<!-- Begin Page Content -->
{% block 'container-fluid' %}
<div class="container-fluid">
{% load static %}
<!-- Page Heading -->
<div class="d-sm-flex align-items-center justify-content-between mb-4">
<h1 class="h3 mb-0 text-gray-800">Student Pose Estimation</h1>
</div>
<!--first row -->
<div class="row p-2">
<!--first column -->
<div class="col-lg-6" style="overflow-x: scroll">
<div class="card shadow mb-4">
<!--card header -->
<div class="card-header py-3">
<h5 class="m-0 font-weight-bold text-primary">Lecturer Subjects</h5>
</div>
<!--card body -->
<div class="card-body">
{% if lecturer_subjects.count == 0 %}
<div class="text-center">
<span class="font-italic">No subjects</span>
</div>
{% else %}
<div class="table-responsive">
<table class="table table-bordered" id="datatable">
<thead>
<tr>
<th></th>
<th>Subject Name</th>
<th>Code</th>
<th>Year</th>
</tr>
</thead>
<tbody>
{% for subject in subjects %}
<tr class="subjects not_clicked" id="{{ subject.0.subject_code }}">
<td>
<div class="radio">
<label><input type="radio"
id="{{ subject.0.subject_code }}"
name="subject_radio"
data-name="{{ subject.0.name }}"
data-lecturer="{{ lecturer }}"></label>
</div>
</td>
<td>{{ subject.0.name }}</td>
<td>{{ subject.0.subject_code }}</td>
<td>{{ subject.0.year }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endif %}
</div>
</div>
</div>
<!--end of first column -->
<!--second column (timetable column) -->
<div class="col-lg-6" style="overflow-x: scroll">
<div class="card shadow mb-4">
<!--card header -->
<div class="card-header py-3">
<h5 class="m-0 font-weight-bold text-primary">View timetable</h5>
</div>
<!--card body -->
<div class="card-body">
<!--loading gif -->
<div class="text-center" id="no_subject_selected">
<span class="font-italic">No lecture is selected</span>
</div>
<!--no lecture selected message -->
<div class="text-center" id="loader" hidden>
<img src="{% static 'FirstApp/images/ajax-loader.gif' %}" alt="Loader">
</div>
<!--no lecture selected message -->
<div class="text-center" id="no_timetable_content" hidden>
<span class="font-italic">Not included in the timetable</span>
</div>
<!--displaying the timetable -->
<table class="table table-striped" id="timetable" hidden>
{# <caption id="timetable_caption"></caption>#}
<thead>
<tr>
<th>Date</th>
<th>Hall No.</th>
<th>start time</th>
<th>end time</th>
</tr>
</thead>
<tbody id="timetable_body">
</tbody>
</table>
</div>
</div>
</div>
<!--end of second column -->
</div>
<!--end of 1st row -->
<!--2nd row -->
<div class="row p-2">
<!--1st column -->
<div class="col-lg-6">
<!--card content -->
<div class="card shadow mb-4">
<!--card header -->
<div class="card-header py-3">
<h5 class="m-0 font-weight-bold text-primary">Pose Estimation</h5>
</div>
<!--card body -->
<div class="card-body">
<!--nav tabs-->
<ul class="nav nav-tabs nav-fill" id="nav_bar" role="tablist">
<li class="nav-item">
<a class="nav-link active" id="frame-tab" data-toggle="tab" href="#frame"
role="tab" aria-controls="frame" aria-selected="true">Frame</a>
</li>
<li class="nav-item">
<a class="nav-link" id="graph-tab" data-toggle="tab" href="#graph"
role="tab" aria-controls="graph" aria-selected="false">Graphs</a>
</li>
</ul>
<!--tab content -->
<div class="tab-content" id="tabContentDetails">
<!--frame tab -->
<div class="tab-pane fade show active" id="frame" role="tabpanel"
aria-labelledby="home-tab">
<!--this area will display the frame -->
<div class="text-center" id="frame_area" style="height: 600px">
<!--temporary text -->
<span class="font-italic" id="temporary_text">Frame will be displayed here</span>
<!--loading buffer area-->
<div class="text-center" id="frame_loader" hidden>
<img src="{% static 'FirstApp/images/ajax-loader.gif' %}"
alt="Loader">
</div>
<!--frames -->.
<div class="text-center p-4" id="video_frames">
<!-- slide container -->
<div id="slidecontainer" hidden>
<div class="row m-3"></div>
<!-- play/pause icon -->
<div class="row">
<span><i class="fas fa-play"
id="play_pause_icon_activity"></i></span>
</div>
<input type="range" min="1" max="100" value="0" class="slider"
id="myActivityRange">
<p>No of frames: <span id="demo"></span></p>
</div>
</div>
<!--end of video frames -->
<button type="button" class="btn btn-danger float-right"
id="process_btn" hidden>Process
</button>
</div>
</div>
<!--end of tab content (frames) -->
<!--graph tab -->
<div class="tab-pane fade" id="graph" role="tabpanel"
aria-labelledby="profile-tab">
<!--card content -->
<div class="card shadow mb-4 p-3">
<!-- 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">Student
Activities</h6>
<div class="dropdown no-arrow">
<a class="dropdown-toggle" href="#" role="button"
id="dropdownMenuLink" data-toggle="dropdown"
aria-haspopup="true" aria-expanded="false">
<i class="fas fa-ellipsis-v fa-sm fa-fw text-gray-400"></i>
</a>
<div class="dropdown-menu dropdown-menu-right shadow animated--fade-in"
aria-labelledby="dropdownMenuLink">
<div class="dropdown-header">Dropdown Header:</div>
<a class="dropdown-item" href="#">Action</a>
<a class="dropdown-item" href="#">Another action</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item" href="#">Something else here</a>
</div>
</div>
</div>
<!-- Card Body -->
<div class="card-body">
<div class="chart-pie pt-4 pb-2">
<canvas id="myPieChart"></canvas>
</div>
<div class="mt-4 text-center small">
<span class="mr-2">
<i class="fas fa-circle text-primary"></i> Direct
</span>
<span class="mr-2">
<i class="fas fa-circle text-success"></i> Social
</span>
<span class="mr-2">
<i class="fas fa-circle text-info"></i> Referral
</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!--2nd column -->
<div class="col-lg-6">
<!--card content -->
<div class="card shadow mb-4">
<!--card header -->
<div class="card-header py-3">
<h5 class="m-0 font-weight-bold text-primary">Frame Detections</h5>
</div>
<!--card body -->
<div class="text-center p-4" id="detection_frames">
<!--no content message-->
<div class="text-center p-2" id="no_detection_message_content">
<span class="font-italic">No frame is selected</span>
</div>
<div class="text-left m-3" id="detection_number_area" hidden>
<p>No of detections: <span id="no_of_detections"></span></p>
</div>
<!--the detection loader -->
<div class="text-center p-2" id="detection_loader" hidden>
<img src="{% static 'FirstApp/images/ajax-loader.gif' %}"
alt="Loader">
</div>
</div>
</div>
<!--detection person card -->
<div class="card shadow mb-4">
<!--card header -->
<div class="card-header py-3">
<h5 class="m-0 font-weight-bold text-primary">Detected Students</h5>
</div>
<!--card body -->
<div class="text-center p-4" id="detection_students">
<!--no content message-->
<div class="text-center p-2" id="no_detection_student_content">
<span class="font-italic">No activity type is selected</span>
</div>
<!--the detection student loader -->
<div class="text-center p-2" id="detection_student_loader" hidden>
<img src="{% static 'FirstApp/images/ajax-loader.gif' %}"
alt="Loader">
</div>
</div>
</div>
</div>
</div>
<!-- end of 2nd row -->
<!--3rd row -->
<div class="row p-2">
<!--this column will display individual student frames -->
<!--1st column -->
<div class="col-lg-6">
<!--card -->
<div class="card shadow mb-4">
<!--card header -->
<div class="card-header">
<h5 class="m-0 font-weight-bold text-primary">Individual Student Frames</h5>
</div>
<!--card body -->
<div class="card-body" id="individual_student_frames">
<!--no content message-->
<div class="text-center p-2" id="no_individual_student_frames">
<span class="font-italic">Press 'Process' button to display Individual Student Frames</span>
</div>
<!--the detection student loader -->
<div class="text-center p-2" id="individual_student_frames_loader" hidden>
<img src="{% static 'FirstApp/images/ajax-loader.gif' %}"
alt="Loader">
</div>
</div>
</div>
</div>
</div>
<!--end of 3rd row -->
</div>
{% endblock %}
<!-- End of container-fluid -->
</div>
<!-- End of Main Content -->
</div>
<!-- End of Content Wrapper -->
</div>
{% block 'modal' %}
<div class="modal fade" id="video_modal" role="dialog" aria-labelledby="gif-body">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h2 class="modal-title">Video details</h2>
<button class="close" type="button" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<table class="table table-borderless">
<tr>
<td class="font-weight-bold">Video Name</td>
<td id="video_name"></td>
</tr>
<tr>
<td class="font-weight-bold">Duration</td>
<td id="video_duration"></td>
</tr>
<tr>
<td class="font-weight-bold">Date of Creation</td>
<td id="video_date"></td>
</tr>
</table>
</div>
<!-- modal footer -->
<div class="modal-footer">
<button type="button" data-dismiss="modal" class="btn btn-danger text-white">Close</button>
</div>
</div>
</div>
</div>
<!-- Logout Modal-->
<div class="modal fade" id="logoutModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel"
aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Ready to Leave?</h5>
<button class="close" type="button" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">Select "Logout" below if you are ready to end your current session.</div>
<div class="modal-footer">
<button class="btn btn-secondary" type="button" data-dismiss="modal">Cancel</button>
<a class="btn btn-primary" href="/logout">Logout</a>
</div>
</div>
</div>
</div>
{% endblock %}
</body>
</html>
\ No newline at end of file
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="">
<meta name="author" content="">
<title>SB Admin 2 - Register</title>
{% load static %}
<link href="{% static 'FirstApp/vendor/fontawesome-free/css/all.min.css' %}" rel="stylesheet" type="text/css">
<link href="https://fonts.googleapis.com/css?family=Nunito:200,200i,300,300i,400,400i,600,600i,700,700i,800,800i,900,900i" rel="stylesheet">
<link href="{% static 'FirstApp/css/sb-admin-2.min.css' %}" rel="stylesheet">
</head>
<body class="bg-gradient-primary">
<script type="text/javascript">
function insert() {
fetch('http://127.0.0.1:8000/register/', {
method: 'POST',
headers: {
"Content-type": "application/json"
},
body: JSON.stringify({
"firstName": document.getElementById('exampleFirstName').value,
"lastName": document.getElementById('exampleLastName').value,
"email": document.getElementById('exampleInputEmail').value,
"password": document.getElementById('exampleInputPassword').value
})
})
.then(resposne => resposne.json())
.then(json => {
if (json) {
alert('successfully inserted');
}
}
)
}
</script>
<div class="container">
<div class="card o-hidden border-0 shadow-lg my-5">
<div class="card-body p-0">
<!-- Nested Row within Card Body -->
<div class="row">
<div class="col-lg-5 d-none d-lg-block bg-register-image"></div>
<div class="col-lg-7">
<div class="p-5">
<div class="text-center">
<h1 class="h4 text-gray-900 mb-4">Create an Account!</h1>
</div>
<form action="#" class="user">
<div class="form-group row">
<div class="col-sm-6 mb-3 mb-sm-0">
<input type="text" class="form-control form-control-user" id="exampleFirstName" placeholder="First Name">
</div>
<div class="col-sm-6">
<input type="text" class="form-control form-control-user" id="exampleLastName" placeholder="Last Name">
</div>
</div>
<div class="form-group">
<input type="email" class="form-control form-control-user" id="exampleInputEmail" placeholder="Email Address">
</div>
<div class="form-group row">
<div class="col-sm-6 mb-3 mb-sm-0">
<input type="password" class="form-control form-control-user" id="exampleInputPassword" placeholder="Password">
</div>
<div class="col-sm-6">
<input type="password" class="form-control form-control-user" id="exampleRepeatPassword" placeholder="Repeat Password">
</div>
</div>
<button type="submit" class="btn btn-primary btn-user btn-block" onclick="insert()">
Register Account
</button>
<hr>
<a href="index.html" class="btn btn-google btn-user btn-block">
<i class="fab fa-google fa-fw"></i> Register with Google
</a>
<a href="index.html" class="btn btn-facebook btn-user btn-block">
<i class="fab fa-facebook-f fa-fw"></i> Register with Facebook
</a>
</form>
<hr>
<div class="text-center">
<a class="small" href="forgot-password.html">Forgot Password?</a>
</div>
<div class="text-center">
<a class="small" href="login.html">Already have an account? Login!</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Bootstrap core JavaScript-->
<script src="{% static 'FirstApp/vendor/jquery/jquery.js' %}"></script>
<script src="{% static 'FirstApp/vendor/bootstrap/js/bootstrap.bundle.min.js' %}"></script>
<!-- Core plugin JavaScript-->
<script src="{% static 'FirstApp/vendor/jquery-easing/jquery.easing.min.js' %}"></script>
<!-- Custom scripts for all pages-->
<script src="{% static 'FirstApp/js/sb-admin-2.min.js' %}"></script>
</body>
</html>
{% extends 'FirstApp/template.html' %}
<!DOCTYPE html>
<html lang="en">
<body id="page-top">
<!-- Page Wrapper -->
<div id="wrapper">
<!-- Sidebar -->
<!-- Content Wrapper -->
<div id="content-wrapper" class="d-flex flex-column">
<!-- Main Content -->
<div id="content">
<!-- Begin Page Content -->
{% block 'container-fluid' %}
<div class="container-fluid">
{% load static %}
<!-- Page Heading -->
<div class="d-sm-flex align-items-center justify-content-between mb-4">
<h1 class="h3 mb-0 text-gray-800">{{video_name}}</h1>
<a href="#" class="d-none d-sm-inline-block btn btn-sm btn-primary shadow-sm"><i class="fas fa-download fa-sm text-white-50"></i> Generate Report</a>
</div>
<!-- &lt;!&ndash; Content Row &ndash;&gt;-->
<div class="row">
<!-- Earnings (Monthly) Card Example -->
<div class="col-xl-12 col-md-6 mb-4">
<div class="card border-left-primary shadow h-100 py-2">
<div class="card-body">
<div class="row">
<div class="col-3">
<video width="500" height="300" controls>
<source src="{% static '' %}FirstApp/videos/{{video_name}}" type="video/mp4">
Your browser does not support the video tag.
</video>
</div>
<div class="col-3"></div>
<!--progress bars-->
<!-- showing the percentage for each emotion-->
<div class="col-6">
<h2 class="big font-weight-bold">Emotion Detection</h2>
<h4 class="small font-weight-bold">Anger
<span class="float-right">{{meta.angry_perct}}%</span></h4>
<div class="progress mb-4">
<div class="progress-bar bg-danger" role="progressbar" style="width: 20%" aria-valuenow="20" aria-valuemin="0" aria-valuemax="100"></div>
</div>
<h4 class="small font-weight-bold">Happy
<span class="float-right">{{meta.happy_perct}}%</span></h4>
<div class="progress mb-4">
<div class="progress-bar bg-warning" role="progressbar" style="width: 40%" aria-valuenow="40" aria-valuemin="0" aria-valuemax="100"></div>
</div>
<h4 class="small font-weight-bold">Sadness
<span class="float-right">{{meta.sad_perct}}%</span></h4>
<div class="progress mb-4">
<div class="progress-bar" role="progressbar" style="width: 60%" aria-valuenow="60" aria-valuemin="0" aria-valuemax="100"></div>
</div>
<h4 class="small font-weight-bold">Surprise
<span class="float-right">{{meta.surprise_perct}}%</span></h4>
<div class="progress mb-4">
<div class="progress-bar bg-info" role="progressbar" style="width: 80%" aria-valuenow="80" aria-valuemin="0" aria-valuemax="100"></div>
</div>
<h4 class="small font-weight-bold">Neutral
<span class="float-right">{{meta.neutral_perct}}%</span></h4>
<div class="progress">
<div class="progress-bar bg-success" role="progressbar" style="width: 100%" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100"></div>
</div>
<div class="form-control">
<button type="button" id="test" data-target="#gif-body" data-toggle="modal">Test</button>
</div>
</div>
</div>
<div class="row">
<h2 class="big font-weight-bold">Eye Gaze Estimation</h2>
</div>
<div class="row">
<div class="col-6">
<div class="progress">
<div class="progress-bar bg-success" role="progressbar" style="width: 100%" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100"></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
<!-- End of container-fluid -->
</div>
<!-- End of Main Content -->
</div>
<!-- End of Content Wrapper -->
</div>
{% block 'modal' %}
<div class="modal fade" id="gif-body" role="dialog" aria-labelledby="gif-body">
<div class="modal-dialog modal-lg" style="max-width: 1600px; max-height: 800px">
<div class="modal-content">
<div class="modal-header">
<h2 class="modal-title">Processing....</h2>
<button class="close" type="button" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body text-center">
<img src="{% static 'FirstApp/images/ajax-loader.gif' %}" width="200" height="500" alt="This is a GIF">
<h5>This might take few seconds...</h5>
</div>
</div>
</div>
</div>
{% endblock %}
</body>
</html>
\ No newline at end of file
"""
- this file will handle the urlpatterns for the 'FirstApp' application
- the 'urlpatterns' variable will contain the list of url patterns exist for this application
- inside this 'list' variable, the 'path' variable will accept the url mappings to be redirected
to an HTML page (view)
- the 'url' will accept the url mappings to be redirected to a RESTful endpoint
"""
from django.urls import path, re_path, include
from django.conf.urls import url
from rest_framework import routers
......@@ -5,29 +19,18 @@ from . import views
from . import api
router = routers.DefaultRouter()
router.register(r'^register', views.RegisterViewSet)
# router.register(r'^createImage', views.ImageViewSet)
urlpatterns = [
path('', views.hello),
path('login', views.loginForm),
path('logout', views.logoutView),
path('register-user', views.register),
path('404', views.view404),
path('401', views.view401),
path('500', views.view500),
path('blank', views.blank),
path('gaze', views.gaze),
path('gaze-process', views.processGaze),
path('pose', views.pose),
path('charts', views.charts),
path('forgot-password', views.forget_password),
path('webcam', views.webcam),
path('template', views.template),
path('base', views.base),
path('child', views.child),
# extractor path
path('extract', views.extractor),
# emotion path
path('emotion', views.emotion_view),
# video results
......@@ -49,9 +52,6 @@ urlpatterns = [
# this is used for activity
path('activity', views.activity),
# tables view
path('tables', views.tables),
# test view (delete later)
path('test', views.test),
......@@ -59,21 +59,6 @@ urlpatterns = [
# user direct view
path('user-direct', views.userDirect),
url(r'^register', views.RegisterViewSet),
# re_path('video/?video_name<str:video_name>', views.video),
url(r'^teachers/', views.teachersList.as_view()),
url(r'^video/', views.video, name='video'),
url(r'^createImage', api.ImageViewSet.as_view()),
# for gaze estimation
url(r'^estimateGaze', api.GazeEstimationViewSet.as_view()),
# for video extraction (POST)
url(r'^videoExtract', api.VideoExtractionViewSet.as_view()),
# for video extraction (GET)
url(r'^videoExtract/(?P<folder_name>\D+)', api.VideoExtractionViewSet.as_view()),
# testing the lecture emotions in the API
url(r'^lecture_emotions', api.LectureEmotionViewSet.as_view()),
# testing the lecture in the API
url(r'^lectures', api.LectureViewSet.as_view()),
......@@ -101,9 +86,10 @@ urlpatterns = [
# lecture video API (to retrieve a lecture)
url(r'^get-lecture-video/$', api.GetLectureVideoViewSet.as_view()),
# lecture video API (to retrieve a lecture)
# lecture video API (to retrieve a lecture video in the lecturer home page)
url(r'^get-lecture-video-for-home/$', api.GetLectureVideoViewSetForHome.as_view()),
##### ACTIVITIES API #####
# lecture activity API (to retrieve lecture activities)
......
"""
this file will contain the function-based views where each function will render its
corresponding view (in other words, the HTML file)
each function will accept one parameter
params:
request - this is the HTTP request sent from the frontend
returns:
render
------
:params
request: the HTTP request received
template_name: path of the HTML template, relative the location of this file
context: the context that needs to be passed into the template
"""
from django.shortcuts import render, redirect
from django.contrib.auth import (
authenticate,
......@@ -23,90 +47,10 @@ import os
from datetime import datetime
# hashing
from django.contrib.auth.hashers import make_password
# Create your views here.
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
class teachersList(APIView):
def get(self, request):
teachers = Teachers.objects.all()
serializer = TeachersSerializer(teachers, many=True)
return Response(serializer.data)
def post(self):
pass
class RegisterViewSet(viewsets.ModelViewSet):
queryset = RegisterUser.objects.all().order_by('firstName')
serializer_class = RegisterUserSerializer
# to create images
class ImageViewSet(APIView):
def post(self, request):
saveImage(request.data)
return Response({"response": "successful"})
# to perform pose estimation on images
class GazeEstimationViewSet(APIView):
def post(self, request):
response = head_pose_estimation.estimatePose(request.data)
return Response({"response": response})
# to perform video extraction
class VideoExtractionViewSet(APIView):
def get(self, request):
response = video_extraction.getExtractedFrames(request.query_params)
return Response({"response": response})
def post(self, request):
response = video_extraction.VideoExtractor(request.data)
return Response({"response": response})
# lecture emotions view set
class LectureEmotionViewSet(APIView):
def get(self, request):
emotions = LectureEmotionReport.objects.all().order_by('lecture_id')
serializer = LectureEmotionSerializer(emotions, many=True)
return Response({"response": serializer.data})
def post(self, request):
LectureEmotionReport(
lecture_id=request.data["lecture_id"],
happy_perct=request.data["happy_perct"],
sad_perct=request.data["sad_perct"],
angry_perct=request.data["angry_perct"],
surprise_perct=request.data["surprise_perct"],
disgust_perct=request.data["disgust_perct"],
neutral_perct=request.data["neutral_perct"]
).save()
return Response({"response": request.data})
class LectureViewSet(APIView):
def get(self, request):
lectures = Lecture.objects.all().order_by('date')
serializer = LectureSerializer(lectures, many=True)
return Response(serializer.data)
def post(self, request):
Lecture(
lecture_id=request.data['lecture_id']
).save()
return Response({"response": request})
####### VIEWS ######
@login_required(login_url='/user-direct')
......@@ -181,21 +125,7 @@ def hello(request):
videoPaths = [os.path.join(folder, file) for file in os.listdir(folder)]
videos = []
durations = []
#
# for videoPath in videoPaths:
# video = Video()
# cap = cv2.VideoCapture(videoPath)
# fps = cap.get(cv2.CAP_PROP_FPS) # OpenCV2 version 2 used "CV_CAP_PROP_FPS"
# frame_count = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
# duration = int(frame_count / fps)
# durations.append(duration)
# videoName = os.path.basename(videoPath)
# # videoName = videos.append(os.path.basename(videoPath))
# durationObj = datetime.timedelta(seconds=duration)
# video.path = videoPath
# video.name = videoName
# video.duration = str(durationObj)
# videos.append(video)
context = {'object': obj, 'Videos': videos, 'durations': durations, 'template_name': 'FirstApp/template.html', 'lecturer_details': lecturer_details, "lecturer": lecturer}
return render(request, 'FirstApp/Home.html', context)
......@@ -214,10 +144,6 @@ def view404(request):
def view401(request):
return render(request, 'FirstApp/401.html')
# querying the database
def blank(request):
emotions = LectureEmotionReport.objects.all().order_by('lecture_id')
return render(request, 'FirstApp/blank.html', {'details': emotions})
@login_required(login_url='/user-direct')
def gaze(request):
......@@ -250,134 +176,15 @@ def gaze(request):
{"lecturer_subjects": lecturer_subjects, "subjects": subject_list, "lecturer": lecturer})
# to redirect to the gaze interface
def processGaze(request):
print('My name is Ishan')
images = request.session.get('images', default='')
imageList = images.split(',')
for i in imageList:
print(i)
return redirect('/')
# the corresponding view for pose estimation
@login_required(login_url='/user-direct')
def pose(request):
try:
# retrieving data from the db
lecturer = request.session['lecturer']
lecturer_subjects = LecturerSubject.objects.filter(lecturer_id_id=lecturer)
lec_sub_serilizer = LecturerSubjectSerializer(lecturer_subjects, many=True)
subject_list = []
subjects = lec_sub_serilizer.data[0]['subjects']
for sub in subjects:
subject = Subject.objects.filter(id=sub)
subject_serialized = SubjectSerializer(subject, many=True)
subject_list.append(subject_serialized.data)
except Exception as exc:
return redirect('/500')
return render(request, "FirstApp/pose.html",
{"lecturer_subjects": lecturer_subjects, "subjects": subject_list, "lecturer": lecturer})
def charts(request):
return render(request, 'FirstApp/charts.html')
def forget_password(request):
return render(request, 'FirstApp/forgot-password.html')
def loginForm(request):
return render(request, 'FirstApp/login.html')
def register(request):
return render(request, 'FirstApp/register.html')
def webcam(request):
video = cv2.VideoCapture(os.path.join(BASE_DIR, 'static//FirstApp//videos//Classroom_video.mp4'))
while (True):
cap, frame = video.read()
cv2.imshow("Frame", frame)
if (cv2.waitKey(1) & 0XFF == ord('q')):
break
video.release()
cv2.destroyAllWindows()
# video = cv2.imread('D://SLIIT/Year 4//Sample Projects//django_project//DemoProject//static//FirstApp/videos/Classroom_video.mp4')
return redirect('/')
# to process video for emotion detection
@login_required(login_url='/user-direct')
def video(request):
title = 'Student and Lecturer Performance Enhancement System'
video_name = request.GET.get('video_name')
video_url = os.path.join(BASE_DIR, os.path.join('static\\FirstApp\\videos\\{0}'.format(video_name)))
meta_data = detect_emotion(video_url)
# meta_data = VideoMeta()
# calculating the respective percentages
meta_data.calcPercentages()
context = {'title': title, 'video_name': video_name, 'url': video_url, 'meta': meta_data}
return render(request, 'FirstApp/video.html', context)
# extractor view
@login_required(login_url='/user-direct')
def extractor(request):
folder = os.path.join(BASE_DIR, os.path.join('static\\FirstApp\\videos'))
videoPaths = [os.path.join(folder, file) for file in os.listdir(folder)]
videos = []
durations = []
# setting up the first video details
first_video_path = videoPaths[0]
first_video = Video()
cap = cv2.VideoCapture(first_video_path)
fps = cap.get(cv2.CAP_PROP_FPS) # OpenCV2 version 2 used "CV_CAP_PROP_FPS"
frame_count = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
duration = int(frame_count / fps)
videoName = os.path.basename(first_video_path)
durationObj = datetime.timedelta(seconds=duration)
first_video.path = first_video_path
first_video.name = videoName
first_video.duration = str(durationObj)
for videoPath in videoPaths:
video = Video()
cap = cv2.VideoCapture(videoPath)
fps = cap.get(cv2.CAP_PROP_FPS) # OpenCV2 version 2 used "CV_CAP_PROP_FPS"
frame_count = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
duration = int(frame_count / fps)
durations.append(duration)
videoName = os.path.basename(videoPath)
durationObj = datetime.timedelta(seconds=duration)
video.path = videoPath
video.name = videoName
video.duration = str(durationObj)
videos.append(video)
context = {'Videos': videos, 'firstVideo': first_video, 'durations': durations, 'template_name': 'FirstApp/template.html'}
return render(request, 'FirstApp/extractor.html', context)
def template(request):
obj = {'Message': 'Student and Lecturer Performance Enhancement System'}
return render(request, 'FirstApp/template.html', {'template_name': 'FirstApp/template.html', 'object': obj})
def base(request):
return render(request, 'FirstApp/base.html')
def child(request):
return render(request, 'FirstApp/child.html', {'template_name': 'FirstApp/base.html'})
# displaying video results
@login_required(login_url='/user-direct')
......@@ -546,10 +353,6 @@ def view500(request):
return render(request, "FirstApp/500.html")
# tables page
def tables(request):
return render(request, "FirstApp/tables.html")
@login_required(login_url='/user-direct')
def activity(request):
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment