Commit e1013a69 authored by LiniEisha's avatar LiniEisha

Merge branch 'QA_RELEASE' into summarization_changes

# Conflicts:
#	LectureSummarizingApp/urls.py
#	integrated_slpes/urls.py
parents 02e000a2 efa7d37c
......@@ -16,6 +16,7 @@
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="canvasjs" level="application" />
</component>
<component name="TemplatesService">
<option name="TEMPLATE_CONFIGURATION" value="Django" />
......
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaScriptLibraryMappings">
<file url="file://$PROJECT_DIR$" libraries="{canvasjs}" />
</component>
</project>
\ No newline at end of file
......@@ -128,7 +128,7 @@ class LectureActivity(models.Model):
# Lecture emotion report
class LectureEmotionReport(models.Model):
lecture_emotion_id = models.CharField(max_length=10)
lecture_video_id = models.ForeignKey(LectureVideo, on_delete=models.CASCADE)
lecture_video_id = models.ForeignKey(LectureVideo, on_delete=models.CASCADE, default=0)
happy_perct = models.DecimalField(default=0.0, max_digits=3, decimal_places=1)
sad_perct = models.DecimalField(default=0.0, max_digits=3, decimal_places=1)
angry_perct = models.DecimalField(default=0.0, max_digits=3, decimal_places=1)
......
# Generated by Django 3.0.3 on 2020-03-04 11:15
# Generated by Django 2.2.11 on 2020-09-07 07:56
import FirstApp.MongoModels
from django.db import migrations, models
import django.db.models.deletion
import djongo.models.fields
class Migration(migrations.Migration):
......@@ -11,6 +14,44 @@ class Migration(migrations.Migration):
]
operations = [
migrations.CreateModel(
name='Faculty',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('faculty_id', models.CharField(max_length=10)),
('name', models.CharField(max_length=100)),
],
),
migrations.CreateModel(
name='Lecture',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('date', models.DateTimeField(auto_created=True, default=None)),
('lecture_id', models.CharField(max_length=10)),
],
),
migrations.CreateModel(
name='Lecturer',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('lecturer_id', models.CharField(max_length=7)),
('fname', models.TextField()),
('lname', models.TextField()),
('email', models.EmailField(max_length=254)),
('telephone', models.CharField(max_length=10)),
('faculty', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='FirstApp.Faculty')),
],
),
migrations.CreateModel(
name='RegisterUser',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('firstName', models.CharField(max_length=20)),
('lastName', models.CharField(max_length=30)),
('email', models.CharField(max_length=30)),
('password', models.CharField(max_length=50)),
],
),
migrations.CreateModel(
name='Teachers',
fields=[
......@@ -20,4 +61,116 @@ class Migration(migrations.Migration):
('age', models.IntegerField()),
],
),
migrations.CreateModel(
name='Video',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=100)),
('path', models.CharField(max_length=100)),
('duration', models.CharField(max_length=100)),
('hours', models.IntegerField()),
('minutes', models.IntegerField()),
('seconds', models.IntegerField()),
],
),
migrations.CreateModel(
name='VideoMeta',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('fps', models.IntegerField()),
('frame_count', models.IntegerField()),
('happy_count', models.IntegerField()),
('sad_count', models.IntegerField()),
('angry_count', models.IntegerField()),
('neutral_count', models.IntegerField()),
('surprise_count', models.IntegerField()),
('happy_perct', models.IntegerField()),
('sad_perct', models.IntegerField()),
('angry_perct', models.IntegerField()),
('neutral_perct', models.IntegerField()),
('surprise_perct', models.IntegerField()),
],
),
migrations.CreateModel(
name='Subject',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('subject_code', models.TextField()),
('name', models.TextField()),
('year', models.IntegerField()),
('faculty', models.ForeignKey(default={}, on_delete=django.db.models.deletion.CASCADE, to='FirstApp.Faculty')),
],
),
migrations.CreateModel(
name='LectureVideo',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('lecture_video_id', models.CharField(max_length=10)),
('date', models.DateField()),
('video_name', models.CharField(max_length=50)),
('video_length', models.DurationField()),
('lecturer', models.ForeignKey(default=0, on_delete=django.db.models.deletion.CASCADE, to='FirstApp.Lecturer')),
('subject', models.ForeignKey(default=0, on_delete=django.db.models.deletion.CASCADE, to='FirstApp.Subject')),
],
),
migrations.CreateModel(
name='LecturerSubject',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('lec_subject_id', models.CharField(max_length=10)),
('lecturer_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='FirstApp.Lecturer')),
('subjects', models.ManyToManyField(to='FirstApp.Subject')),
],
),
migrations.CreateModel(
name='LecturerCredentials',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('password', models.CharField(max_length=15)),
('username', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='FirstApp.Lecturer')),
],
),
migrations.CreateModel(
name='LecturePoseEstimation',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('lecture_pose_id', models.CharField(max_length=10)),
('lecture_video_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='FirstApp.LectureVideo')),
],
),
migrations.CreateModel(
name='LectureEmotionReport',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('lecture_emotion_id', models.CharField(max_length=10)),
('happy_perct', models.DecimalField(decimal_places=1, default=0.0, max_digits=3)),
('sad_perct', models.DecimalField(decimal_places=1, default=0.0, max_digits=3)),
('angry_perct', models.DecimalField(decimal_places=1, default=0.0, max_digits=3)),
('disgust_perct', models.DecimalField(decimal_places=1, default=0.0, max_digits=3)),
('surprise_perct', models.DecimalField(decimal_places=1, default=0.0, max_digits=3)),
('neutral_perct', models.DecimalField(decimal_places=1, default=0.0, max_digits=3)),
('lecture_video_id', models.ForeignKey(default=0, on_delete=django.db.models.deletion.CASCADE, to='FirstApp.LectureVideo')),
],
),
migrations.CreateModel(
name='LectureActivity',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('lecture_activity_id', models.CharField(max_length=10)),
('talking_perct', models.DecimalField(decimal_places=1, default=0.0, max_digits=3)),
('listening_perct', models.DecimalField(decimal_places=1, default=0.0, max_digits=3)),
('writing_perct', models.DecimalField(decimal_places=1, default=0.0, max_digits=3)),
('phone_perct', models.DecimalField(decimal_places=1, default=0.0, max_digits=3)),
('lecture_video_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='FirstApp.LectureVideo')),
],
),
migrations.CreateModel(
name='FacultyTimetable',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('timetable_id', models.CharField(max_length=10)),
('timetable', djongo.models.fields.ArrayField(model_container=FirstApp.MongoModels.DateTimeTable)),
('faculty', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='FirstApp.Faculty')),
],
),
]
# Generated by Django 2.2.11 on 2020-03-16 12:47
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('FirstApp', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='RegisterUser',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('firstName', models.CharField(max_length=20)),
('lastName', models.CharField(max_length=30)),
('email', models.CharField(max_length=30)),
('password', models.CharField(max_length=50)),
],
),
migrations.CreateModel(
name='Video',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=100)),
('path', models.CharField(max_length=100)),
('duration', models.CharField(max_length=100)),
('hours', models.IntegerField()),
('minutes', models.IntegerField()),
('seconds', models.IntegerField()),
],
),
migrations.CreateModel(
name='VideoMeta',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('fps', models.IntegerField()),
('frame_count', models.IntegerField()),
('happy_count', models.IntegerField()),
('sad_count', models.IntegerField()),
('angry_count', models.IntegerField()),
('neutral_count', models.IntegerField()),
('surprise_count', models.IntegerField()),
('happy_perct', models.IntegerField()),
('sad_perct', models.IntegerField()),
('angry_perct', models.IntegerField()),
('neutral_perct', models.IntegerField()),
('surprise_perct', models.IntegerField()),
],
),
]
# Generated by Django 2.2.11 on 2020-05-10 15:32
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('FirstApp', '0002_registeruser_video_videometa'),
]
operations = [
migrations.CreateModel(
name='LectureEmotionReport',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('lecture_id', models.CharField(max_length=10)),
('happy_perct', models.FloatField()),
('sad_perct', models.FloatField()),
('angry_perct', models.FloatField()),
('surprise_perct', models.FloatField()),
('disgust_perct', models.FloatField()),
('neutral_perct', models.FloatField()),
],
),
]
# Generated by Django 2.2.11 on 2020-05-11 16:58
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('FirstApp', '0003_lectureemotionreport'),
]
operations = [
migrations.CreateModel(
name='Lecture',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('date', models.DateTimeField(auto_created=True)),
('lecture_id', models.CharField(max_length=10)),
],
),
]
# Generated by Django 2.2.11 on 2020-05-13 10:21
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('FirstApp', '0004_lecture'),
]
operations = [
migrations.CreateModel(
name='Faculty',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('faculty_id', models.CharField(max_length=10)),
('name', models.CharField(max_length=100)),
],
),
migrations.CreateModel(
name='Lecturer',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('lecturer_id', models.CharField(max_length=7)),
('fname', models.TextField()),
('lname', models.TextField()),
('email', models.EmailField(max_length=254)),
('telephone', models.CharField(max_length=10)),
('faculty', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='FirstApp.Faculty')),
],
),
migrations.AlterField(
model_name='lecture',
name='date',
field=models.DateTimeField(auto_created=True, default=None),
),
migrations.CreateModel(
name='Subject',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('subject_code', models.TextField()),
('name', models.TextField()),
('year', models.IntegerField()),
('faculty', models.ForeignKey(default={}, on_delete=django.db.models.deletion.CASCADE, to='FirstApp.Faculty')),
],
),
migrations.CreateModel(
name='LecturerSubject',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('lec_subject_id', models.CharField(max_length=10)),
('lecturer_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='FirstApp.Lecturer')),
('subjects', models.ManyToManyField(to='FirstApp.Subject')),
],
),
]
# Generated by Django 2.2.11 on 2020-05-13 15:46
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('FirstApp', '0005_auto_20200513_1551'),
]
operations = [
migrations.CreateModel(
name='LecturerCredentials',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('password', models.CharField(max_length=15)),
('username', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='FirstApp.Lecturer')),
],
),
]
from django.contrib import admin
# Register your models here.
from LectureSummarizingApp.models import *
admin.site.register(LectureAudio)
admin.site.register(LectureAudioSummary)
from rest_framework.views import APIView
from rest_framework.response import Response
from LectureSummarizingApp.models import LectureAudio
from LectureSummarizingApp.serializer import LectureAudioSerializer
# this API will retrieve lecture audio details
class LectureAudioAPI(APIView):
def get(self, request):
lecture_audio = LectureAudio.objects.all()
lecture_audio_serializer = LectureAudioSerializer(lecture_audio, many=True)
data = lecture_audio_serializer.data
return Response({
"response": data
})
\ No newline at end of file
from django.apps import AppConfig
class LecturesummarizingappConfig(AppConfig):
class LectureSummarizingAppConfig(AppConfig):
name = 'LectureSummarizingApp'
# Generated by Django 2.2.11 on 2020-09-15 07:48
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
('FirstApp', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='LectureAudio',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('lecture_audio_id', models.CharField(max_length=10)),
('lecturer_date', models.DateField()),
('lecture_audio_name', models.CharField(max_length=50)),
('lecture_audio_length', models.DurationField()),
('lecturer', models.ForeignKey(default=0, on_delete=django.db.models.deletion.CASCADE, to='FirstApp.Lecturer')),
('subject', models.ForeignKey(default=0, on_delete=django.db.models.deletion.CASCADE, to='FirstApp.Subject')),
],
),
migrations.CreateModel(
name='LectureAudioSummary',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('lecture_audio_summary_id', models.CharField(max_length=10)),
('audio_original_text', models.TextField()),
('audio_summary', models.TextField()),
('lecture_audio_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='LectureSummarizingApp.LectureAudio')),
],
),
]
from django.db import models
# from django.db import models
from djongo import models
# Create your models here.
from FirstApp.MongoModels import Lecturer, Subject
class LectureAudio (models.Model):
lecture_audio_id = models.CharField(max_length=10)
lecturer_date = models.DateField()
lecture_audio_name = models.CharField(max_length=50)
lecture_audio_length = models.DurationField()
lecturer = models.ForeignKey(Lecturer, on_delete=models.CASCADE, default=0)
subject = models.ForeignKey(Subject, on_delete=models.CASCADE, default=0)
def __str__(self):
return self.lecture_audio_id
class LectureAudioSummary (models.Model):
lecture_audio_summary_id = models.CharField(max_length=10)
lecture_audio_id = models.ForeignKey(LectureAudio, on_delete=models.CASCADE)
audio_original_text = models.TextField()
audio_summary = models.TextField()
def __str__(self):
return self.lecture_audio_summary_id
from rest_framework import serializers
from FirstApp.serializers import LecturerSerializer, SubjectSerializer
from . models import *
class LectureAudioSerializer(serializers.ModelSerializer):
lecturer = LecturerSerializer()
subject = SubjectSerializer()
class Meta:
model = LectureAudio
fields = '__all__'
class LectureAudioSummarySerializer(serializers.ModelSerializer):
lecture_audio_id = LectureAudioSerializer()
class Meta:
model = LectureAudioSummary
fields = '__all__'
\ No newline at end of file
from django.urls import path, re_path, include
from django.conf.urls import url
from rest_framework import routers
from . import views
# from . import api
from rest_framework import routers
from django.conf.urls import url
from . import api
router = routers.DefaultRouter()
# router.register(r'^register', views.RegisterViewSet)
# router.register(r'^createImage', views.ImageViewSet)
# router.register(r'^register', views.register)
urlpatterns = [
path('/summarization', views.summarization),
# path('', views.hello),
# path('login', views.login),
# path('register', views.register),
# path('404', views.view404),
# path('blank', views.blank),
# path('buttons', views.buttons),
# path('cards', views.cards),
# 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),
# path('lecture-video', views.lecVideo),
# # path('Video', views.hello)
# API to retrieve activity recognition
url(r'^get-lecture-audio/$', api.LectureAudioAPI.as_view()),
# # API to retrieve audio analysis
# url(r'^get-audio-analysis', api.GetLectureAudioAnalysis.as_view()),
#
# # API to retrieve lecture audio text
# url(r'^get-lecture-audio-text', api.LectureAudioTextAPI.as_view()),
#
# # test API
# url(r'^test-api', api.TestAPI.as_view()),
# routers
# path('', include(router.urls)),
path('api-auth/', include('rest_framework.urls', namespace='rest_framework'))
]
from rest_framework.urlpatterns import format_suffix_patterns
from django.contrib import admin
from . models import RegisterTeacher
from .models import *
# Register your models here.
admin.site.register(RegisterTeacher)
\ No newline at end of file
admin.site.register(LecturerVideo)
admin.site.register(LecturerAudioText)
admin.site.register(LecturerVideoMetaData)
admin.site.register(LectureRecordedVideo)
\ No newline at end of file
from rest_framework.views import APIView
from rest_framework.response import Response
from LectureSummarizingApp.models import LectureAudioSummary
from LectureSummarizingApp.serializer import LectureAudioSummarySerializer
from . logic import classroom_activity, text_analysis as ta
from .models import LecturerVideo, LecturerAudioText
from .serializers import LecturerVideoSerializer, LecturerAudioTextSerializer
import datetime
class ActivityRecognitionAPI(APIView):
......@@ -14,12 +21,139 @@ class ActivityRecognitionAPI(APIView):
pass
# class ActivityRecognitionAPI(APIView):
#
# def get(self, request):
# video_name = request.query_params.get('video_name')
# percentages = classroom_activity.activity_recognition(video_name)
# return Response({"response": percentages})
#
# def post(self, request):
# pass
# this class will be used to retrieve audio analysis for a lecture
class GetLectureAudioAnalysis(APIView):
def get(self, request):
analysis = ta.run()
lec_audio_id = request.query_params.get("audio_id")
int_audio_id = int(lec_audio_id)
lec_audio_summary = LectureAudioSummary.objects.filter(lecture_audio_id=int_audio_id)
lec_audio_summary_serializer = LectureAudioSummarySerializer(lec_audio_summary, many=True)
audio_summary_data = lec_audio_summary_serializer.data
lec_audio_summary_id = 0
for audio in audio_summary_data:
lec_audio_summary_id = audio['id']
# retrieve summary text data
lec_audio_text = LecturerAudioText.objects.filter(lecturer_audio_original_text__id=lec_audio_summary_id)
lec_audio_text_serializer = LecturerAudioTextSerializer(lec_audio_text, many=True)
lec_audio_text_data = lec_audio_text_serializer.data
audio_text = []
if len(lec_audio_text_data) > 0:
audio_text = lec_audio_text_data[0]
print('audio text: ', audio_text)
return Response({
"response":audio_text
})
# test api
class TestAPI(APIView):
# retrieve all the details
def get(self, request):
lecturer_videos = LecturerVideo.objects.all()
lecturer_vidoe_serializer = LecturerVideoSerializer(lecturer_videos, many=True)
data = lecturer_vidoe_serializer.data
return Response({
"response": data
})
def post(self, request):
name = request.data['name']
path = request.data['path']
duration = request.data['duration']
hours = request.data['hours']
minutes = request.data['minutes']
seconds = request.data['seconds']
# create the LecturerVideo details
LecturerVideo(
id=2,
name=name,
path=path,
duration=duration,
hours=hours,
minutes=minutes,
seconds=seconds
).save()
return Response({
"response": "Success"
})
# this API will retrieve lecture audio text details
class LectureAudioTextAPI(APIView):
def get(self, request):
lecture_audio_text = LecturerAudioText.objects.all()
lecture_audio_text_serializer = LecturerAudioTextSerializer(lecture_audio_text, many=True)
data = lecture_audio_text_serializer.data
return Response({
"response": data
})
# this API will retrieve lectuer audio summary for given period
class LecturerAudioSummaryPeriodAPI(APIView):
def get(self, request):
print('hello')
option = request.query_params.get('option')
int_option = int(option)
# i cheated here (remove this once you have many recent records) - ok
int_option = 150
isRecordFound = False
lec_audio_text_stats = []
labels = []
current_date = datetime.datetime.now().date()
option_date = datetime.timedelta(days=int_option)
previous_date = current_date - option_date
lec_audio_text = LecturerAudioText.objects.filter(
lecturer_audio_original_text__lecture_audio_id__lecturer_date__gte=previous_date,
lecturer_audio_original_text__lecture_audio_id__lecturer_date__lte=current_date
)
# if there are records
if len(lec_audio_text) > 0:
isRecordFound = True
lec_audio_text_ser = LecturerAudioTextSerializer(lec_audio_text, many=True)
lec_audio_text_data = lec_audio_text_ser.data
lec_audio_text_stats, labels = ta.get_lecturer_audio_summary_for_period(lec_audio_text_data)
return Response({
"response":analysis
"statistics": lec_audio_text_stats,
"labels": labels,
"isRecordFound": isRecordFound
})
......@@ -8,11 +8,18 @@ import os
def activity_recognition(video_name):
BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
CLASSIFIER_DIR = os.path.join(BASE_DIR, "MonitorLecturerApp\\models")
VIDEO_PATH = os.path.join(BASE_DIR, "assets\\FirstApp\\lecturer_videos\\{}".format(video_name))
# VIDEO_PATH = os.path.join(BASE_DIR, "assets\\FirstApp\\lecturer_videos\\{}".format(video_name)) -> Uncomment after integration
VIDEO_PATH = os.path.join(BASE_DIR, "static\\FirstApp\\lecturer_videos\\{}".format(video_name))
print('video name: ', video_name)
detector_path = os.path.join(CLASSIFIER_DIR, "keras_model.h5")
# detector_path = os.path.join(CLASSIFIER_DIR, "keras_model.h5")
detector_path = os.path.join(CLASSIFIER_DIR, "keras_model_updated.h5")
# Disable scientific notation for clarity
np.set_printoptions(suppress=True)
......@@ -50,9 +57,9 @@ def activity_recognition(video_name):
# getting the number of frames using CAP_PROP_FRAME_COUNT method
no_of_frames = video.get(cv2.CAP_PROP_FRAME_COUNT)
frame_count = 0
seated_count = 0
standing_count = 0
walking_count = 0
seated_count = 0.0
standing_count = 0.0
walking_count = 0.0
# while loop is conditioned like this to avoid the termination of the loop with an exception
while (frame_count < no_of_frames):
......@@ -88,13 +95,15 @@ def activity_recognition(video_name):
# if cv2.waitKey(1) & 0xFF == ord('q'):
# break
cv2.destroyAllWindows()
print("No of frames: ", frame_count)
print("No of frames in seated_count: ", seated_count)
print("No of frames in standing_count: ", standing_count)
print("No of frames in walking_count: ", walking_count)
# calculating the percentages
sit_perct = (seated_count / no_of_frames) * 100
stand_perct = (standing_count / no_of_frames) * 100
walk_perct = (walking_count / no_of_frames) * 100
......
......@@ -46,7 +46,7 @@ def run():
else:
d[word] = 1
# the key words are "extraneous filler words(ok, well, like, Actually, Basically, that, jest, only, really, very, now, simply, maybe, perhaps, somehow, almost, slightly, seemed ....)"
# the key words are "extraneous filler words (ok, well, like, Actually, Basically, that, jest, only, really, very, now, simply, maybe, perhaps, somehow, almost, slightly, seemed ....)"
for key in list(d.keys()):
if (key == "like"):
......@@ -170,3 +170,46 @@ def run():
analysis['non_lexical_count'] = non_lexical_count
return analysis
# this method will return the lecturer audio summary for period
def get_lecturer_audio_summary_for_period(lecture_audio_text_data):
# declare variables to add percentage values
word_count = 0
lexical_count_combined = 0
non_lexical_count_combined = 0
# get the number of activties to calculate average
no_of_data = len(lecture_audio_text_data)
individual_lec_data = []
labels = ["lexical_wordcount", "non_lexical_wordcount", "wordcount"]
# iterate through the activities
for audio_data in lecture_audio_text_data:
ind_word_count = int(audio_data["lecturer_audio_text_lexical_wordcount"])
ind_lexical_count = int(audio_data["lecturer_audio_text_non_lexical_wordcount"])
ind_non_lexical_count = int(audio_data["lecturer_audio_text_non_lexical_wordcount"])
individual_lecture = {}
individual_lecture["lexical_wordcount"] = int(audio_data["lecturer_audio_text_lexical_wordcount"])
individual_lecture["non_lexical_wordcount"] = int(audio_data["lecturer_audio_text_non_lexical_wordcount"])
individual_lecture["wordcount"] = int(audio_data["lecturer_audio_text_wordcount"])
lexical_count_combined += int(audio_data["lecturer_audio_text_lexical_wordcount"])
non_lexical_count_combined += int(audio_data["lecturer_audio_text_non_lexical_wordcount"])
word_count += int(audio_data["lecturer_audio_text_wordcount"])
# calculate the percentages
# individual_lecture["lexical_perct"] = round((ind_lexical_count / ind_word_count), 1)
# individual_lecture["non_lexical_perct"] = round((ind_non_lexical_count / ind_word_count), 1)
# append to the list
individual_lec_data.append(individual_lecture)
return individual_lec_data, labels
\ No newline at end of file
# Generated by Django 3.0.4 on 2020-04-01 15:45
# Generated by Django 2.2.11 on 2020-09-15 07:59
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
......@@ -8,22 +9,13 @@ class Migration(migrations.Migration):
initial = True
dependencies = [
('FirstApp', '0001_initial'),
('LectureSummarizingApp', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='RegisterTeacher',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('fName', models.CharField(max_length=15)),
('lName', models.CharField(max_length=15)),
('subject', models.CharField(max_length=50)),
('email', models.CharField(max_length=50)),
('password', models.CharField(max_length=50)),
],
),
migrations.CreateModel(
name='tVideo',
name='LecturerVideo',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=100)),
......@@ -35,7 +27,7 @@ class Migration(migrations.Migration):
],
),
migrations.CreateModel(
name='tVideoMetaData',
name='LecturerVideoMetaData',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('fps', models.IntegerField()),
......@@ -45,4 +37,38 @@ class Migration(migrations.Migration):
('walking_count', models.IntegerField()),
],
),
migrations.CreateModel(
name='RegisterTeacher',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('fName', models.CharField(max_length=15)),
('lName', models.CharField(max_length=15)),
('subject', models.CharField(max_length=50)),
('email', models.CharField(max_length=50)),
('password', models.CharField(max_length=50)),
],
),
migrations.CreateModel(
name='LectureRecordedVideo',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('lecture_video_id', models.CharField(max_length=10)),
('lecturer_date', models.DateField()),
('lecture_video_name', models.CharField(max_length=50)),
('lecture_video_length', models.DurationField()),
('lecturer', models.ForeignKey(default=0, on_delete=django.db.models.deletion.CASCADE, to='FirstApp.Lecturer')),
('subject', models.ForeignKey(default=0, on_delete=django.db.models.deletion.CASCADE, to='FirstApp.Subject')),
],
),
migrations.CreateModel(
name='LecturerAudioText',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('lecturer_audio_text_id', models.CharField(max_length=10)),
('lecturer_audio_text_wordcount', models.IntegerField()),
('lecturer_audio_text_lexical_wordcount', models.IntegerField()),
('lecturer_audio_text_non_lexical_wordcount', models.IntegerField()),
('lecturer_audio_original_text', models.ForeignKey(default=0, on_delete=django.db.models.deletion.CASCADE, to='LectureSummarizingApp.LectureAudioSummary')),
],
),
]
# Generated by Django 2.2.11 on 2020-09-15 09:07
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('MonitorLecturerApp', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='lectureraudiotext',
name='lecturer_audio_text_status',
field=models.CharField(choices=[(1, 'Below'), (2, 'Average'), (3, 'Excellent')], default='Average', max_length=15),
),
]
# Generated by Django 2.2.11 on 2020-09-15 09:19
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('MonitorLecturerApp', '0002_lectureraudiotext_lecturer_audio_text_status'),
]
operations = [
migrations.AlterField(
model_name='lectureraudiotext',
name='lecturer_audio_text_status',
field=models.CharField(choices=[('Below', 'Below'), ('Average', 'Average'), ('Excellent', 'Excellent')], default='Average', max_length=15),
),
]
......@@ -2,6 +2,9 @@ from django.db import models
from djongo import models
# Create your models here.
from FirstApp.MongoModels import Subject, Lecturer
from LectureSummarizingApp.models import LectureAudioSummary
class RegisterTeacher(models.Model):
fName = models.CharField(max_length = 15)
......@@ -14,7 +17,7 @@ class RegisterTeacher(models.Model):
return self.fName
class tVideo(models.Model):
class LecturerVideo(models.Model):
name = models.CharField(max_length=100)
path = models.CharField(max_length=100)
duration = models.CharField(max_length=100)
......@@ -26,7 +29,7 @@ class tVideo(models.Model):
return self.name
class tVideoMetaData(models.Model):
class LecturerVideoMetaData(models.Model):
fps = models.IntegerField()
frame_count = models.IntegerField()
seated_count = models.IntegerField()
......@@ -51,18 +54,32 @@ class tVideoMetaData(models.Model):
self.calWalkPercent()
class lectureAudio (models.Model):
lecture_audio_id = models.CharField(max_length=10)
lecturer_date = models.DateField()
lecture_audio_name = models.CharField(max_length=50)
lecture_audio_length = models.DurationField()
# lecturer = models.ForeignKey(Lecturer, on_delete=models.CASCADE, default=0)
# subject = models.ForeignKey(Subject, on_delete=models.CASCADE, default=0)
class LecturerAudioText (models.Model):
lecturer_audio_text_id = models.CharField(max_length=10)
lecturer_audio_text_wordcount = models.IntegerField()
lecturer_audio_text_lexical_wordcount = models.IntegerField()
lecturer_audio_text_non_lexical_wordcount = models.IntegerField()
lecturer_audio_text_status = models.CharField(
max_length=15,
choices=(
("Below", "Below"),
("Average", "Average"),
("Excellent", "Excellent")
),
default="Average"
)
lecturer_audio_original_text = models.ForeignKey(LectureAudioSummary, on_delete=models.CASCADE, default=0)
def __str__(self):
return self.lecturer_audio_text_id
class lectureRecordedVideo (models.Model):
class LectureRecordedVideo (models.Model):
lecture_video_id = models.CharField(max_length=10)
lecturer_date = models.DateField()
lecture_video_name = models.CharField(max_length=50)
lecture_video_length = models.DurationField()
# lecturer = models.ForeignKey(Lecturer, on_delete=models.CASCADE, default=0)
# subject = models.ForeignKey(Subject, on_delete=models.CASCADE, default=0)
\ No newline at end of file
lecturer = models.ForeignKey(Lecturer, on_delete=models.CASCADE, default=0)
subject = models.ForeignKey(Subject, on_delete=models.CASCADE, default=0)
def __str__(self):
return self.lecture_video_id
\ No newline at end of file
from rest_framework import serializers
from .models import RegisterTeacher, tVideoMetaData
class RegisterTeacherSerializer(serializers.ModelSerializer):
from FirstApp.serializers import LecturerSerializer, SubjectSerializer
from LectureSummarizingApp.models import LectureAudioSummary
from .models import RegisterTeacher
from .models import LecturerAudioText, LecturerVideoMetaData, LecturerVideo, LectureRecordedVideo
class RegisterTeacherSerializer(serializers.ModelSerializer):
class Meta:
model = RegisterTeacher
fields = {'fName', 'lName', 'subject', 'email', 'password'}
class LecturerVideoSerializer(serializers.ModelSerializer):
class Meta:
model = LecturerVideo
fields = '__all__'
class LecturerVideoMetaDataSerializer(serializers.ModelSerializer):
class Meta:
model = LecturerVideoMetaData
fields = '__all__'
class LecturerAudioTextSerializer(serializers.ModelSerializer):
lecturer_audio_original_text = LectureAudioSummary()
class Meta:
model = LecturerAudioText
fields = '__all__'
class LectureRecordedVideoSerializer(serializers.ModelSerializer):
lecturer = LecturerSerializer()
subject = SubjectSerializer()
class Meta:
model = LectureRecordedVideo
fields = '__all__'
<!DOCTYPE HTML>
<html>
<head>
<script>
window.onload = function () {
var chart = new CanvasJS.Chart("chartContainer", {
animationEnabled: true,
title:{
text: "Daily High Temperature at Different Beaches"
},
axisX: {
valueFormatString: "DD MMM,YY"
},
axisY: {
title: "Temperature (in °C)",
suffix: " °C"
},
legend:{
cursor: "pointer",
fontSize: 16,
itemclick: toggleDataSeries
},
toolTip:{
shared: true
},
data: [{
name: "Myrtle Beach",
type: "spline",
yValueFormatString: "#0.## °C",
showInLegend: true,
dataPoints: [
{ x: new Date(2017,6,24), y: 31 },
{ x: new Date(2017,6,25), y: 31 },
{ x: new Date(2017,6,26), y: 29 },
{ x: new Date(2017,6,27), y: 29 },
{ x: new Date(2017,6,28), y: 31 },
{ x: new Date(2017,6,29), y: 30 },
{ x: new Date(2017,6,30), y: 29 }
]
},
{
name: "Martha Vineyard",
type: "spline",
yValueFormatString: "#0.## °C",
showInLegend: true,
dataPoints: [
{ x: new Date(2017,6,24), y: 20 },
{ x: new Date(2017,6,25), y: 20 },
{ x: new Date(2017,6,26), y: 25 },
{ x: new Date(2017,6,27), y: 25 },
{ x: new Date(2017,6,28), y: 25 },
{ x: new Date(2017,6,29), y: 25 },
{ x: new Date(2017,6,30), y: 25 }
]
},
{
name: "Nantucket",
type: "spline",
yValueFormatString: "#0.## °C",
showInLegend: true,
dataPoints: [
{ x: new Date(2017,6,24), y: 22 },
{ x: new Date(2017,6,25), y: 19 },
{ x: new Date(2017,6,26), y: 23 },
{ x: new Date(2017,6,27), y: 24 },
{ x: new Date(2017,6,28), y: 24 },
{ x: new Date(2017,6,29), y: 23 },
{ x: new Date(2017,6,30), y: 23 }
]
}]
});
chart.render();
function toggleDataSeries(e){
if (typeof(e.dataSeries.visible) === "undefined" || e.dataSeries.visible) {
e.dataSeries.visible = false;
}
else{
e.dataSeries.visible = true;
}
chart.render();
}
}
</script>
</head>
<body>
<div id="chartContainer" style="height: 300px; max-width: 1400px; margin: 0px auto;"></div>
<script src="https://canvasjs.com/assets/script/canvasjs.min.js"></script>
</body>
</html>
\ No newline at end of file
<!DOCTYPE html>
<html lang="en">
......@@ -15,24 +13,31 @@
{% load static %}
<!-- Custom fonts for this template-->
<link rel="shortcut icon" href="{% static 'FirstApp/images/favicon.ico' %}" type="image/x-icon" />
<link rel="shortcut icon" href="{% static 'FirstApp/images/favicon.ico' %}" type="image/x-icon"/>
<link href="{% static 'FirstApp/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="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">
<!-- Bootstrap core JavaScript-->
<!-- 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>
<script type="text/javascript">
<!-- canvasJS implementation -->
<script src="https://canvasjs.com/assets/script/canvasjs.min.js"></script>
<script type="text/javascript">
var global_lecturer_id = 0;
var lecture_audio_text_summary = {};
$(document).ready(function () {
$('.calc').click(function () {
let video_name = $(this).attr('id');
......@@ -57,13 +62,14 @@
$('#progress_bars').attr('hidden', false);
$('#loader').attr('hidden', true);
{#let sitting = Math.round(percentages.sitting_perct);#}
let sitting = Math.round(percentages.sitting_perct);
let standing = Math.round(percentages.standing_perct);
let walking = Math.round(percentages.walking_perct);
$('#sitting_val').width(sitting+'%');
$('#standing_val').width(standing+'%');
$('#walking_val').width(walking+'%');
$('#sitting_val').width(sitting + '%');
$('#standing_val').width(standing + '%');
$('#walking_val').width(walking + '%');
$('#sitting_span').text(sitting + '%');
$('#standing_span').text(standing + '%');
......@@ -72,7 +78,7 @@
}
/*
//this is for the temporay button (delete later)
$('#temp_btn').click(function () {
//using the fecth API
......@@ -89,23 +95,201 @@
.catch((err) => alert('this is a error: ' + err))
});
*/
//this function will display the retrieved audio analysis values
function displayAudioAnalysis(response) {
$('#num_of_words').text(response.num_of_words);
$('#lexical_count').text(response.lexical_count);
$('#non_lexical_count').text(response.non_lexical_count);
if (response) {
$('#num_of_words').text(response.lecturer_audio_text_wordcount);
$('#lexical_count').text(response.lecturer_audio_text_lexical_wordcount);
$('#non_lexical_count').text(response.lecturer_audio_text_non_lexical_wordcount);
//hide the loader
$('#audio_loader').attr('hidden', true);
//show the content
$('#audio_summary_details').attr('hidden', false);
} else {
alert("No audio summary details... Try again!");
}
}
$('#summary_btn').click(function () {
//open the modal
$('#audio_summary_modal').modal();
});
</script>
//this function will handle the audio list radio buttons
$('.audio_radio').click(function () {
lec_audio_id = $(this).attr('data-id');
global_lecturer_id = Number($(this).attr('data-lecturer'));
//hide the message
$('#no_content_audio_message').attr('hidden', true);
//show the loader
$('#audio_loader').attr('hidden', false);
//display the modal
{#$('#lecturer_behavior_view_summary_modal').modal();#}
fetch('http://127.0.0.1:8000/lecturer/get-audio-analysis/?audio_id=' + lec_audio_id)
.then((res) => res.json())
.then((out) => displayAudioAnalysis(out.response))
.catch((err) => alert('this is a error: ' + err))
});
$('#audio_graph').click(function () {
//display the option modal
$('#lecturer_behavior_view_summary_modal').modal();
});
//this function will handle the view summary option form
$('#view_summary_option_form').submit(function (e) {
let option = $("input[name='option']:checked").val();
e.preventDefault();
//send the data using fetch API
fetch('http://127.0.0.1:8000/lecturer/get-lecturer-audio-summary-for-period/?option=' + option)
.then((res) => res.json())
.then((out) => displayPeriodStudentActivitySummary(out))
.catch((err) => alert('error: ' + err))
});
//this function will display the summary graph
function displayPeriodStudentActivitySummary(res) {
if (res.isRecordFound) {
lecture_audio_text_summary = res;
renderLectureAudioTextStatistics();
//hide the previous modal
$('#lecturer_behavior_view_summary_modal').modal("hide");
//display the modal
$('#audio_text_stats_modal').modal();
} else {
alert('nothing found');
}
}
//this function will render the chart for Activity statistics
function renderLectureAudioTextStatistics() {
let individual_activities = lecture_audio_text_summary.statistics;
let activity_labels = lecture_audio_text_summary.labels;
let activity_length = lecture_audio_text_summary.statistics.length;
let label_length = activity_labels.length;
let data = [];
for (let i = 0; i < label_length; i++) {
let label = activity_labels[i];
let datapoints = [];
for (let j = 0; j < activity_length; j++) {
let activity = individual_activities[j];
datapoints.push({label: "lecture " + (j + 1), y: activity[label]});
}
let obj = {
type: "line",
showInLegend: true,
name: label,
markerType: "square",
{#xValueFormatString: "DD MMM, YYYY",#}
xValueFormatString: "Lec " + (i + 1),
color: getRandomColor(),
dataPoints: datapoints
};
data.push(obj);
}
var chart = new CanvasJS.Chart("audio_text_stats_chart", {
animationEnabled: true,
theme: "light2",
title: {
text: "Audio Statistics"
},
axisX: {
title: "Lecture",
{#valueFormatString: "DD MMM",#}
valueFormatString: "lec",
crosshair: {
enabled: true,
snapToDataPoint: true
}
},
axisY: {
title: "Count",
includeZero: true,
crosshair: {
enabled: true
}
},
toolTip: {
shared: true
},
legend: {
cursor: "pointer",
verticalAlign: "bottom",
horizontalAlign: "center",
{#dockInsidePlotArea: true,#}
itemclick: toogleDataSeries
},
data: data
});
chart.render();
}
//this function will generate random colors
function getRandomColor() {
var letters = '0123456789ABCDEF';
var color = '#';
for (var i = 0; i < 6; i++) {
color += letters[Math.floor(Math.random() * 16)];
}
return color;
}
//this function will toggle the content
function toogleDataSeries(e) {
if (typeof (e.dataSeries.visible) === "undefined" || e.dataSeries.visible) {
e.dataSeries.visible = false;
} else {
e.dataSeries.visible = true;
}
chart.render();
}
});
</script>
<!-- Page Wrapper -->
<div id="wrapper">
......@@ -142,13 +326,14 @@
<!-- 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">
<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>-->
<!-- <h6 class="collapse-header">Login Screens:</h6>-->
<a class="collapse-item" href="index.html">Dashboard</a>
<a class="collapse-item" href="/lecturer/lecture-video">Video Page</a>
......@@ -183,8 +368,9 @@
<!-- Page Heading -->
<div class="d-sm-flex align-items-center justify-content-between mb-4">
<h1 class="h3 mb-0 text-gray-800">Dashboard</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>
<h1 class="h3 mb-0 text-gray-800">Lecturer Performance Evaluation System</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>
......@@ -193,38 +379,56 @@
<div class="row">
<!-- Area Chart -->
<div class="col-xl-5 col-lg-7">
<div class="col-lg-6 mb-4">
<div class="card shadow mb-4">
<!-- Card Header - Dropdown -->
<div class="card-header py-3 d-flex flex-row align-items-center justify-content-between">
<h6 class="m-0 font-weight-bold text-primary">Audio List</h6>
<button type="button" class="btn btn-outline-primary" id="audio_graph">Statistics
</button>
</div>
<!-- Card Body -->
<div class="card-body">
<div class="card-body">
<!-- <div class="chart-pie pt-4 pb-2">-->
<div class="card-body" style="overflow-y: scroll">
<div class="chart-pie pt-4 pb-2">
<table class="table table-striped">
<thead>
<tr>
<th>Audio Name</th>
<th>Duration</th>
<th></th>
<th>Date</th>
<th>Subject</th>
<th>Lecturer</th>
</tr>
</thead>
<tbody>
<!-- {% for video in Videos %}-->
<!-- {% for audio in lec_list %}-->
<tr>
<!-- <td>{{video.name}}</td>-->
<!-- <td>{{video.duration}}</td>-->
<td>
<div class="radio">
<label>
<input type="radio" class="audio_radio"
name="audio_radio" id="{{ audio.date }}"
data-id="{{ audio.id }}"
data-lecturer="{{ audio.lecturer_id }}">
</label>
</div>
</td>
<td>{{ audio.date }}</td>
<td>{{ audio.subject }}</td>
<td>{{ audio.lecturer }}</td>
</tr>
<!-- {% endfor %}-->
<!-- {% endfor %}-->
</tbody>
</table>
<!-- </div>-->
</div>
<!--this is a temporary button -->
<div class="float-right m-3">
<button type="button" class="btn btn-danger" id="temp_btn">Temp</button>
{# <button type="button" class="btn btn-danger" id="temp_btn">Temp</button>#}
</div>
</div>
......@@ -234,15 +438,17 @@
</div>
<!-- Video List -->
<div class="col-xl-6 col-lg-5">
<div class="col-lg-6 mb-4">
<div class="card shadow mb-4">
<!-- Card Header - Dropdown -->
<div class="card-header py-3 d-flex flex-row align-items-center justify-content-between">
<h6 class="m-0 font-weight-bold text-primary">Video List</h6>
{# <button type="button" class="btn btn-outline-primary" id="video_graph">Statistics#}
{# </button>#}
</div>
<!-- Card Body -->
<div class="card-body">
<div class="card-body" style="overflow-y: scroll">
<div class="chart-pie pt-4 pb-2">
<table class="table table-striped">
<thead>
......@@ -255,10 +461,12 @@
<tbody>
{% for video in Videos %}
<tr>
<td>{{video.name}}</td>
<td>{{video.duration}}</td>
<td>{{ video.name }}</td>
<td>{{ video.duration }}</td>
<td class="btn_class">
<button type="button" class="btn btn-success calc" id='{{video.name}}'>Calculate</button>
<button type="button" class="btn btn-success calc"
id='{{ video.name }}'>Calculate
</button>
</td>
</tr>
{% endfor %}
......@@ -290,6 +498,8 @@
<!--loader -->
<div class="text-center" id="audio_loader" hidden>
{# <img src="{% static 'FirstApp/images/ajax-loader.gif' %}" alt="">#}
<img src="{% static 'FirstApp/images/ajax-loader.gif' %}" alt="">
</div>
......@@ -309,18 +519,29 @@
<td id="num_of_words"></td>
</tr>
<tr>
<td>No of extraneous words</td>
<td id="lexical_count"></td>
</tr>
<tr>
<td>No of Non-lexical words</td>
<td id="non_lexical_count"></td>
<td>No fo word to be expected</td>
<td> 3600 - 4800</td>
</tr>
{# <tr>#}
{# <td>No of extraneous words</td>#}
{# <td id="lexical_count"></td>#}
{# </tr>#}
{# <tr>#}
{# <td>No of Non-lexical words</td>#}
{# <td id="non_lexical_count"></td>#}
{# </tr>#}
</tbody>
</table>
<!--button -->
{# <div class="float-right">#}
{# <button type="button" class="btn btn-outline-primary m-3" id="summary_btn">View More</button>#}
{# </div>#}
<div class="float-right">
<button type="button" class="btn btn-outline-primary m-3" id="summary_btn">View More</button>
<button type="button" class="btn btn-outline-primary m-3" id="summary_btn">View More
Test
</button>
</div>
</div>
......@@ -338,13 +559,13 @@
</div>
<div class="card-body">
<div class="text-center" id="no_content_message">
<span class="font-italic">No content to be displayed</span>
<span class="font-italic">No video content to be displayed</span>
</div>
<!--loader -->
<div class="text-center" id="loader" hidden>
<!-- <img src="{% static 'FirstApp/images/ajax-loader.gif' %}" alt="">-->
<img src="{% static 'FirstApp/images/ajax-loader.gif' %}" alt="">
<div class="text-center" id="video_loader" hidden>
<img src="{% static 'FirstApp/images/ajax-loader.gif' %}" alt="Loader">
</div>
......@@ -379,15 +600,15 @@
</div>
<!-- Approach -->
<!-- <div class="card shadow mb-4">-->
<!-- <div class="card-header py-3">-->
<!-- <h6 class="m-0 font-weight-bold text-primary">Development Approach</h6>-->
<!-- </div>-->
<!-- <div class="card-body">-->
<!--&lt;!&ndash; <p>SB Admin 2 makes extensive use of Bootstrap 4 utility classes in order to reduce CSS bloat and poor page performance. Custom CSS classes are used to create custom components and custom utility classes.</p>&ndash;&gt;-->
<!--&lt;!&ndash; <p class="mb-0">Before working with this theme, you should become familiar with the Bootstrap framework, especially the utility classes.</p>&ndash;&gt;-->
<!-- </div>-->
<!-- </div>-->
<!-- <div class="card shadow mb-4">-->
<!-- <div class="card-header py-3">-->
<!-- <h6 class="m-0 font-weight-bold text-primary">Development Approach</h6>-->
<!-- </div>-->
<!-- <div class="card-body">-->
<!--&lt;!&ndash; <p>SB Admin 2 makes extensive use of Bootstrap 4 utility classes in order to reduce CSS bloat and poor page performance. Custom CSS classes are used to create custom components and custom utility classes.</p>&ndash;&gt;-->
<!--&lt;!&ndash; <p class="mb-0">Before working with this theme, you should become familiar with the Bootstrap framework, especially the utility classes.</p>&ndash;&gt;-->
<!-- </div>-->
<!-- </div>-->
</div>
</div>
......@@ -420,7 +641,8 @@
</a>
<!-- Logout Modal-->
<div class="modal fade" id="logoutModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
<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">
......@@ -438,6 +660,168 @@
</div>
</div>
<!-- audio summary Modal qualitative analysis -->
<div class="modal fade" id="audio_summary_modal" 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">Audio summary</h5>
<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">
<tbody>
<tr>
<td>No. of extraneous words</td>
<td id="lexical_count"></td>
</tr>
<tr>
<td>No. of non-lexical words</td>
<td id="non_lexical_count"></td>
</tr>
</tbody>
</table>
</div>
<div class="modal-footer">
<button class="btn btn-danger" type="button" data-dismiss="modal">Done</button>
<!-- <a class="btn btn-primary" href="login.html">Logout</a>-->
</div>
</div>
</div>
</div>
<!-- Graph audio summary -->
{#<div class="modal fade" id="audio_summary_graph" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel"#}
{# aria-hidden="true">#}
{# <div class="modal-dialog" role="document" style="max-width: 900px; max-height: 750px">#}
{# <div class="modal-content">#}
{# <div class="modal-header">#}
{# <h5 class="modal-title">Audio Summary Graph</h5>#}
{# <button class="close" type="button" data-dismiss="modal" aria-label="Close">#}
{# <span aria-hidden="true">×</span>#}
{# </button>#}
{# </div>#}
{# <div class="modal-body">#}
{##}
{# <div id="chartContainer" style="height: 400px; max-width: 8000px; margin: 0px auto;"></div>#}
{##}
{# </div>#}
{# <div class="modal-footer">#}
{# <button class="btn btn-danger" type="button" data-dismiss="modal">Done</button>#}
{# </div>#}
{# </div>#}
{# </div>#}
{#</div>#}
<!-- Graph Video Summary -->
<div class="modal fade" id="video_summary_graph" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel"
aria-hidden="true">
<div class="modal-dialog" role="document" style="max-width: 900px; max-height: 750px">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Video Summary Graph</h5>
<button class="close" type="button" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<div id="chartContainerVideo" style="height: 400px; max-width: 8000px; margin: 0px auto;"></div>
</div>
<div class="modal-footer">
<button class="btn btn-danger" type="button" data-dismiss="modal">Done</button>
</div>
</div>
</div>
</div>
<!-- lecture behavior view summary modal -->
<div class="modal fade" id="lecturer_behavior_view_summary_modal" tabindex="-1" role="dialog"
aria-labelledby="exampleModalLabel"
aria-hidden="true">
<div class="modal-dialog" role="document" style="max-width: 500px">
<div class="modal-content">
<div class="modal-header">
<h3 class="modal-title" id="exampleModalLabel">View Summary Options</h3>
<button class="close" type="button" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<div class="form-group">
<h5>View Summary for...</h5>
</div>
<!-- list of options -->
<form action="#" method="get" id="view_summary_option_form">
<div class="custom-control custom-radio mt-2">
<input type="radio" class="custom-control-input" id="customRadio" name="option" value="7">
<label class="custom-control-label" for="customRadio">one week</label>
</div>
<div class="custom-control custom-radio mt-2">
<input type="radio" class="custom-control-input" id="customRadio1" name="option" value="14">
<label class="custom-control-label" for="customRadio1">2 weeks</label>
</div>
<div class="custom-control custom-radio mt-2">
<input type="radio" class="custom-control-input" id="customRadio2" name="option" value="30">
<label class="custom-control-label" for="customRadio2">one month</label>
</div>
<div class="form-group mt-4">
<button type="submit" class="btn btn-outline-success" id="submit_view_summary_option">
Submit
</button>
</div>
</form>
<!-- end of list of options -->
</div>
<div class="modal-footer">
<button class="btn btn-secondary" type="button" data-dismiss="modal">Cancel</button>
</div>
</div>
</div>
</div>
<!-- end of lecturer behavior view summary modal -->
<!-- Audio Data statistics Modal-->
<div class="modal fade" id="audio_text_stats_modal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel"
aria-hidden="true">
<div class="modal-dialog" role="document" style="max-width: 1400px">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Audio Data Statistics</h5>
<button class="close" type="button" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body text-center">
<div id="audio_text_stats_chart" style="height: 370px; width: 100%"></div>
</div>
<div class="modal-footer">
<button class="btn btn-secondary" type="button" data-dismiss="modal">Cancel</button>
</div>
</div>
</div>
</div>
<!-- end of activity statistics modal -->
{% load static %}
......
......@@ -142,7 +142,7 @@
</div>
<!-- Card Body -->
<div class="card-body">
<div class="card-body" style="overflow-y: scroll">
<div class="chart-pie pt-4 pb-2">
<table class="table table-striped">
<thead>
......
......@@ -28,7 +28,16 @@ urlpatterns = [
url(r'^activities/$', api.ActivityRecognitionAPI.as_view()),
# API to retrieve audio analysis
url(r'^get-audio-analysis', api.GetLectureAudioAnalysis.as_view()),
url(r'^get-audio-analysis/$', api.GetLectureAudioAnalysis.as_view()),
# API to retrieve lecture audio text
url(r'^get-lecture-audio-text', api.LectureAudioTextAPI.as_view()),
# API to retrieve lecture audio text
url(r'^get-lecturer-audio-summary-for-period', api.LecturerAudioSummaryPeriodAPI.as_view()),
# test API
url(r'^test-api', api.TestAPI.as_view()),
path('api-auth/', include('rest_framework.urls', namespace='rest_framework'))
......
......@@ -4,13 +4,17 @@ from django.conf.urls import url
from rest_framework import routers
from rest_framework.views import APIView
from rest_framework.response import Response
from LectureSummarizingApp.models import LectureAudio
from LectureSummarizingApp.serializer import LectureAudioSerializer
from . import views
from . models import RegisterTeacher, tVideo
from .models import RegisterTeacher, LecturerVideo
from . serializers import RegisterTeacherSerializer
import cv2
import os
import datetime
from datetime import datetime as dt
......@@ -45,8 +49,29 @@ def hello(request):
videos = []
durations = []
# retrieve audio details from db
lecture_audio = LectureAudio.objects.all()
lec_audio_serializer = LectureAudioSerializer(lecture_audio, many=True)
lec_audio_data = lec_audio_serializer.data
lec_list = []
for audio in lec_audio_data:
lec_audio_object = {}
lec_audio_object["id"] = audio["id"]
lec_audio_object["date"] = audio["lecturer_date"]
lec_audio_object["subject"] = audio["subject"]["name"]
lec_audio_object["lecturer"] = audio["lecturer"]["fname"] + " " + audio["lecturer"]["lname"]
lec_audio_object["lecturer_id"] = audio["lecturer"]["id"]
# append to the list
lec_list.append(lec_audio_object)
# the list needs to be sorted by the date
lec_list.sort(key=lambda date: dt.strptime(str(date['date']), "%Y-%m-%d"), reverse=True)
for videoPath in videoPaths:
video = tVideo()
video = LecturerVideo()
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))
......@@ -60,7 +85,7 @@ def hello(request):
video.duration = str(durationObj)
videos.append(video)
print('Video Name: ', video.name)
context = {'object': obj, 'Videos': videos, 'durations': durations, 'template_name': 'MonitorLecturerApp/template.html'}
context = {'object': obj, 'Videos': videos, 'durations': durations, 'template_name': 'MonitorLecturerApp/template.html', 'lec_list': lec_list}
return render(request, 'MonitorLecturerApp/index.html', context)
......@@ -108,7 +133,7 @@ def lecVideo(request):
durations = []
for videoPath in videoPaths:
video = tVideo()
video = LecturerVideo()
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))
......
#SLPES Mobile Application
# # 2020-101
## Introduction
This project will be created under the guidelines provided by the Research Project module offered in 4th Year by the Faculty of Computing. The title of the project is "Student and Lecturer Performance Enhancement System". This Research Project comes under the domain of Artifical Intelligence and Machine Learning along with the support of Computer Vision.
## Main Objective
Main Objectives that will be targetted in this Research Project will be listed as follows.
* Tracking student attendance using facial detection and facial recognition and notify and getting opinion from the students who are absent and students who are leaving the lecture in the during a specified time period (Attendance Register).
* Monitoring student behavior within the classroom during lecture periods to identify potential problem areas in retaining student attention and adapting the lecturing styles to suit the student needs (Monitoring Student Behavior).
* Summarize the converted text and present as a document and identify the important points of the lecture to display them highlighted (Lecture Summarizing).
* Monitoring lecturers’ behavior by analyzing the teaching style in a lecture hall during the lecture hours (Monitor Lecturer Performance).
## Main Research Questions
This Research Project will be conducted to find answers for the following 4 research questions.
* “What is the optimum way of tracking student attendance in a much efficient way and how does the lecturer analyze reasons for student absenteeism?” (Q1).
* “Does a correlation exist between lecturing style and student behavior in the classroom and how can Computer Vision and Artificial Intelligence be incorporated in determining this relationship?” (Q2).
* “How to summarize the lecture content to enable students to pay more attention to the lecture and reduce time spend for taking notes?” (Q3).
* “How to evaluate lecture performance by tracking their behavior during a lecture and analyzing the quality of the lecture content which is delivered by the lecturer?” (Q4).
## Individual Research Questions
The main research questions came up for the project were based on each individual component that will be implemented through the course of the project. Hence, the individual research questions could be listed down as follows.
### Attendance Register
*What is the optimum way of tracking student attendance in a much efficient way and how does the lecturer analyze reasons for student absenteeism?*
### Monitoring Student Behavior
*Does a correlation exist between lecturing style and student behavior in the classroom and how can Computer Vision and Artificial Intelligence be incorporated in determining this relationship?.*
### Lecture Summarizing
*How to summarize the lecture content to enable students to pay more attention to the lecture and reduce time spend for taking notes?*
### Monitoring Lecturer Performance
*How to evaluate lecture performance by tracking their behavior during a lecture and analyzing the quality of the lecture content which is delivered by the lecturer?”*
## Individual Objectives
The main objectives of the research project could be further split into individual objectives which elaborates purpose of the project in much detail.
### Attendance Register
* Tracking student attendance using facial detection and facial recognition.
* Notify and getting opinion from the students who are absent and students who are leaving the lecture in the during a specified time period.
* Setting up student data and respective course data in an adaptive manner.
* Provide an overview for the lecturer regarding student attendance through a dashboard and predicting future attendance.
### Monitoring Student Behavior
* Monitoring student behavior within the classroom during lecture periods to identify potential problem areas in retaining student attention and adapting the lecturing styles to suit the student needs.
* Identifying the concentration level, emotion expressions and general behavior of students within 2-hour lecture duration using facial recognition, emotion recognition and motion detection employing computer vision technologies.
* Determining the dependency between student behavior and the lecturer behavior by analyzing overall behavior status of students and lecturer for a given time frame during the lecture.
### Lecture Summarizing
* Understand lecturer’s voice and filter the lecturer’s voice by removing background noises and silent pauses
* Convert the noise-filtered audio into text
* Summarize the converted text and present as a document.
* Identify the important points of the lecture to display them highlighted.
* Display important notices mentioned in the lecture.
### Monitoring Lecturer Performance
* Monitoring lecturers’ behavior by analyzing the teaching style in a lecture hall during the lecture hours.
* Identifying the lectures emotional state when the lecture is conducting.
* Generating an end-semester feedback of the lecturer giving a summary of their performance.
SLPES Mobile Application
Introduction
......
......@@ -35,6 +35,7 @@ INSTALLED_APPS = [
'FirstApp.apps.FirstappConfig',
'AttendanceApp.apps.AttendanceappConfig',
'MonitorLecturerApp.apps.MonitorlecturerappConfig',
'LectureSummarizingApp.apps.LectureSummarizingAppConfig',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
......
......@@ -23,4 +23,6 @@ urlpatterns = [
path('', include('FirstApp.urls')),
path('attendance/', include('AttendanceApp.urls')),
path('lecturer/', include('MonitorLecturerApp.urls')),
path('lecturer/', include('MonitorLecturerApp.urls')),
path('summary/', include('LectureSummarizingApp.urls'))
]
absl-py==0.9.0
alabaster==0.7.12
anaconda-client==1.7.2
anaconda-navigator==1.9.7
anaconda-project==0.8.3
asgiref==3.2.3
asn1crypto==1.0.1
astor==0.8.1
astroid==2.3.1
astropy==3.2.1
atomicwrites==1.3.0
attrs==19.2.0
Babel==2.7.0
backcall==0.1.0
backports.functools-lru-cache==1.5
backports.os==0.1.1
backports.shutil-get-terminal-size==1.0.0
backports.tempfile==1.0
backports.weakref==1.0.post1
beautifulsoup4==4.8.0
bitarray==1.0.1
bkcharts==0.2
bleach==3.1.0
bokeh==1.3.4
bootstrap4==0.1.0
boto==2.49.0
Bottleneck==1.2.1
bson==0.5.8
cachetools==4.0.0
certifi==2019.9.11
cffi==1.12.3
chardet==3.0.4
Click==7.0
cloudpickle==1.2.2
clyent==1.2.2
colorama==0.4.1
comtypes==1.1.7
conda==4.7.12
conda-build==3.18.9
conda-package-handling==1.6.0
conda-verify==3.4.2
contextlib2==0.6.0
cryptography==2.7
cycler==0.10.0
Cython==0.29.13
cytoolz==0.10.0
dask==2.5.2
dataclasses==0.6
decorator==4.4.0
deepspeech==0.7.1
defusedxml==0.6.0
distributed==2.5.2
Django==2.2.12
djangorestframework==3.11.0
djongo==1.3.1
dnspython==1.16.0
docopt==0.6.2
docutils==0.15.2
entrypoints==0.3
et-xmlfile==1.0.1
fastcache==1.1.0
filelock==3.0.12
Flask==1.1.1
fsspec==0.5.2
future==0.17.1
gast==0.2.2
gevent==1.4.0
glob2==0.7
google-auth==1.11.3
google-auth-oauthlib==0.4.1
google-pasta==0.2.0
greenlet==0.4.15
grpcio==1.27.2
h5py==2.9.0
HeapDict==1.0.1
html5lib==1.0.1
idna==2.8
imageio==2.6.0
imagesize==1.1.0
importlib-metadata==0.23
imutils==0.5.3
ipykernel==5.1.2
ipython==7.8.0
ipython-genutils==0.2.0
ipywidgets==7.5.1
isort==4.3.21
itsdangerous==1.1.0
jdcal==1.4.1
jedi==0.15.1
Jinja2==2.10.3
joblib==0.13.2
Js2Py==0.67
json5==0.8.5
jsonschema==3.0.2
jupyter==1.0.0
jupyter-client==5.3.3
jupyter-console==6.0.0
jupyter-core==4.5.0
jupyterlab==1.1.4
jupyterlab-server==1.0.6
Keras==2.3.1
Keras-Applications==1.0.8
Keras-Preprocessing==1.1.0
keyring==18.0.0
kiwisolver==1.1.0
lazy-object-proxy==1.4.2
libarchive-c==2.8
llvmlite==0.29.0
locket==0.2.0
lxml==4.4.1
Markdown==3.2.1
MarkupSafe==1.1.1
matplotlib==3.1.1
mccabe==0.6.1
menuinst==1.4.16
mistune==0.8.4
mkl-fft==1.0.14
mkl-random==1.1.0
mkl-service==2.3.0
mock==3.0.5
more-itertools==7.2.0
mpmath==1.1.0
msgpack==0.6.1
multipledispatch==0.6.0
navigator-updater==0.2.1
nbconvert==5.6.0
nbformat==4.4.0
networkx==2.3
nltk==3.4.5
nose==1.3.7
notebook==6.0.1
numba==0.45.1
numexpr==2.7.0
numpy==1.16.5
numpydoc==0.9.1
oauthlib==3.1.0
olefile==0.46
opencv-python==4.2.0.32
openpyxl==3.0.0
opt-einsum==3.2.0
packaging==19.2
pandas==0.25.1
pandocfilters==1.4.2
parso==0.5.1
partd==1.0.0
path.py==12.0.1
pathlib2==2.3.5
patsy==0.5.1
pep8==1.7.1
pickleshare==0.7.5
Pillow==6.2.0
pipwin==0.4.6
pkginfo==1.5.0.1
pluggy==0.13.0
ply==3.11
prometheus-client==0.7.1
prompt-toolkit==2.0.10
protobuf==3.11.3
psutil==5.6.3
py==1.8.0
pyasn1==0.4.8
pyasn1-modules==0.2.8
PyAudio==0.2.11
pycodestyle==2.5.0
pycosat==0.6.3
pycparser==2.19
pycrypto==2.6.1
pycurl==7.43.0.3
pyflakes==2.1.1
Pygments==2.4.2
pyjsparser==2.7.1
pylint==2.4.2
pymongo==3.10.1
pyodbc==4.0.27
pyOpenSSL==19.0.0
pyparsing==2.4.2
PyPrind==2.11.2
pyreadline==2.1
pyrsistent==0.15.4
PySocks==1.7.1
pytest==5.2.1
pytest-arraydiff==0.3
pytest-astropy==0.5.0
pytest-doctestplus==0.4.0
pytest-openfiles==0.4.0
pytest-remotedata==0.3.2
python-dateutil==2.8.0
pytz==2019.3
PyWavelets==1.0.3
pywin32==223
pywinpty==0.5.5
PyYAML==5.1.2
pyzmq==18.1.0
QtAwesome==0.6.0
qtconsole==4.5.5
QtPy==1.9.0
requests==2.22.0
requests-oauthlib==1.3.0
robobrowser==0.5.3
rope==0.14.0
rsa==4.0
ruamel-yaml==0.15.46
scikit-image==0.15.0
scikit-learn==0.21.3
scipy==1.4.1
seaborn==0.9.0
Send2Trash==1.5.0
simplegeneric==0.8.1
singledispatch==3.4.0.3
six==1.14.0
snowballstemmer==2.0.0
sortedcollections==1.1.2
sortedcontainers==2.1.0
soupsieve==1.9.3
Sphinx==2.2.0
sphinxcontrib-applehelp==1.0.1
sphinxcontrib-devhelp==1.0.1
sphinxcontrib-htmlhelp==1.0.2
sphinxcontrib-jsmath==1.0.1
sphinxcontrib-qthelp==1.0.2
sphinxcontrib-serializinghtml==1.1.3
sphinxcontrib-websupport==1.1.2
spyder==3.3.6
spyder-kernels==0.5.2
SQLAlchemy==1.3.9
sqlparse==0.2.4
statsmodels==0.10.1
sympy==1.4
tables==3.5.2
tblib==1.4.0
tensorboard==2.1.1
tensorflow==2.1.0
tensorflow-estimator==2.1.0
termcolor==1.1.0
terminado==0.8.2
testpath==0.4.2
toolz==0.10.0
tornado==6.0.3
tqdm==4.36.1
traitlets==4.3.3
tzlocal==2.0.0
unicodecsv==0.14.1
urllib3==1.24.2
wcwidth==0.1.7
webencodings==0.5.1
Werkzeug==0.16.0
widgetsnbextension==3.5.1
win-inet-pton==1.1.0
win-unicode-console==0.5
wincertstore==0.2
wrapt==1.11.2
xlrd==1.2.0
XlsxWriter==1.2.1
xlwings==0.15.10
xlwt==1.3.0
zict==1.0.0
zipp==0.6.0
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