Commit 7b030546 authored by Nimesh's avatar Nimesh

Repo Uploaded

parents
#!/bin/bash
sudo pip3 install python3-xlib
sudo apt-get install scrot
sudo apt-get install python3-tk
sudo apt-get install python3-dev
sudo pip3 install -r requirements.txt
sudo apt-get install python3.6
sudo apt-get install xdotool
sudo apt-get install nethogs
sudo setcap "cap_net_admin,cap_net_raw=ep" /usr/sbin/nethogs
-------------------------------------------------------------
Team Name - Quad-core
Project - Protracktor - Local Proctoring and Activity Tracker
Date: 17th November, 2020
-------------------------------------------------------------
Contributors:
Amit Kumar Bala Antu (203051003 - antukbala)
Kunal Verma (203050121 - kunalverma)
Nimesh Agrawal (203050049 - nimesh)
Mallela Niteesh Kumar (203050065 - niteesh)
Shubhranshu Maurya (203050096 - shubhranshu)
Git Repository
--------------
*Cloning the Repository
How to operate Protracktor
--------------------------
*Getting dependencies ready:
1) Automatically using "installer.sh"
"installer.sh" file which will take care of all the dependencies and requirements for the project.
To run "installer.sh", go to the project directory "Protracktor" where "installer.sh" is present and then run the following commands:
$ chmod +x installer.sh
$ ./installer.sh
2) Alternatively (If some dependencies are still left, generally should not happen)
If some requirements are still missed please follow the steps below to manually install the dependencies.
Install "python" on your system by running the following command on your terminal:
$ sudo apt-get install python3.6
Screen activity module needs "xdotool" to get window’s information:
$ sudo apt-get install xdotool
For Internet Tracking Module install "nethogs":
$ sudo apt-get install nethogs
$ sudo setcap "cap_net_admin,cap_net_raw=ep" /usr/sbin/nethogs
To install "PyAutoGUI" additionally you need to install the "scrot" application, as well as "Tkinter":
$ sudo apt-get install scrot
$ sudo apt-get install python3-tk
$ sudo apt-get install python3-dev
$ sudo pip3 install python3-xlibsudo
$ sudo pip3 install pyautogui
$ sudo pip3 numpy==1.18.5
$ sudo pip3 notify2==0.3.1
$ sudo pip3 matplotlib==3.3.1
$ sudo pip3 opencv_python==4.1.2.30
$ sudo pip3 PyQt5==5.15.1
*After installing all the dependencies:
Go to the main directory "Protracktor" and in that go to source directory.
After that run the following command in terminal:
$ python3 main.py
This will start the application.
Using the program
------------------
The main window has two tabs namely "Local Proctoring" and "Let's Start".
1) First enter the course code you want to start proctoring for.
2) This field is used to name the log files and video files with additional timestamps added as a naming convention for files
3) Click on "Next" to move to the next tab "Let's Start"
On clicking next after entering the course code you will be directed to the next tab ("Let's Start" tab).
Here there are four check boxes named according to the functionalities they will provide, namely:
a) Webcam Recording
b) Screen Recording
c) Activity Manager
d) Data usage per app
Check on the required functionalities you want monitored during the proctoring.
***Note***: Internet loss triggered - Webcam and screen recording will only start if there is a loss in internet connectivity detected.
This assumes that you are already on some proctoring platform like ms teams initially and if there is a loss of internet connectivity then only webcam recording will start.
After checking on the appropriate check boxes you can start the monitoring by clicking on "Start" button.
After clicking on start a new pop-up window will open.
Notifications
-------------
Two types of notification are issued to the user during the proctoring.
- When Internet goes down.
- When Internet comes back.
Terminating the program
-----------------------
Whenever you want to stop the proctoring/monitoring just click on the stop button.
This will close the application also.
All the corresponding outputs will be saved in the "Results" directory in the main folder.
Viewing the Results/outputs/graphs
----------------------------------
1) The "Proctoring" option in menu bar will direct you to various log files and graphs generated by the software.
By clicking on this proctoring Option you will get a drop-down with following options.
-Results
-Data Usage
-Activity Monitor
-Screen Recordings
-Video Recordings
-Graph
-Latest activity tracker
-Latest Data Usage
The Results section will take you the corresponding folder where log files and graphs are stored.
The Graph section will display the latest graph of the corresponding option you will select.
2) Help option:
This option will display the documentation to you.
numpy==1.18.5
notify2==0.3.1
matplotlib==3.3.1
PyAutoGUI==0.9.52
opencv_python==4.1.2.30
PyQt5==5.15.1
Application,Time,Percentage
chrome,0:01:42,42
gnome-terminal,0:00:52,21
firefox,0:00:44,18
teams,0:00:26,11
sublime_text,0:00:11,5
nautilus,0:00:09,4
------------------------------------------------------------
chrome
0:01:42 (42%)
------------------------------------------------------------
0:00:59 (58%) IIT Bombay Moodle: Log in to the site - Google Chrome
0:00:14 (14%) moodle.iitb.ac.in - Google Chrome
0:00:13 (13%) New Tab - Google Chrome
0:00:06 (6%) Projects · Dashboard · GitLab - Google Chrome
0:00:05 (5%) Nimesh / Outlab1 · GitLab - Google Chrome
0:00:03 (3%) git.cse.iitb.ac.in - Google Chrome
0:00:02 (2%) Dashboard - Google Chrome
------------------------------------------------------------
gnome-terminal
0:00:52 (21%)
------------------------------------------------------------
0:00:52 (100%) dextron@dextron-X556UF: ~/Desktop/demo documentation
------------------------------------------------------------
firefox
0:00:44 (18%)
------------------------------------------------------------
0:00:32 (73%) YouTube - Mozilla Firefox
0:00:12 (27%) Mozilla Firefox
------------------------------------------------------------
teams
0:00:26 (11%)
------------------------------------------------------------
0:00:17 (65%) Microsoft Teams
0:00:09 (35%) General (CS699: Software Lab (PG)) | Microsoft Teams
------------------------------------------------------------
sublime_text
0:00:11 (5%)
------------------------------------------------------------
0:00:11 (100%) ~/Desktop/demo documentation/window_activity.py - Sublime Text (UNREGISTERED)
------------------------------------------------------------
nautilus
0:00:09 (4%)
------------------------------------------------------------
0:00:07 (78%) Desktop
0:00:02 (22%) demo documentation
============================================================
started: 2020-11-17 14:16:22 updated: 2020-11-17 14:20:41
============================================================
\ No newline at end of file
import notify2
import urllib.request
import time
import threading
from webcam import myThread2
from screen import myThread1
notify2.init("Protractor")
n = notify2.Notification(None)
n.set_urgency(notify2.URGENCY_NORMAL)
def connect(host='http://google.com'):
'''
This method checks internet connectivity by pinging google.com
:returns: True or False
'''
try:
urllib.request.urlopen(host)
return True
except:
return False
thread1=myThread1()
thread2=myThread2()
def stop(val1,val2):
if val1==True:
thread1.set()
if val2==True:
thread2.set()
def notify(val1,val2):
'''
This method issue notification to the user if Internet is down and starts the local proctoring thread .
:param val1: bool argument
:param val2: bool argument
:returns: True or False
'''
if connect()==False:
n.update('Protractor','Internet down! Starting Recording')
n.show()
if val1==True:
thread1.start()
if val2==True:
thread2.start()
return False
return True
def check():
'''
This method issues notification if Internet comes back.
:returns: True or False
'''
if connect():
n.update('Protractor','Internet Up! You can join back to MS-Teams.')
n.show()
return True
return False
class myThread5 (threading.Thread):
def __init__(self,val1,val2,course_name):
threading.Thread.__init__(self)
self.flag=0
self.exit=0
self.val1=val1
self.val2=val2
self.course_name=course_name
def setf(self):
self.flag=1
def sete(self):
self.exit=1
def run(self):
'''
This method controls the calling of other methods like checking of internet connectivity and calling notification functions.
'''
if self.val1==False and self.val2==False:
return
while(True):
time.sleep(5)
if self.flag==0:
if notify(self.val1,self.val2)==False:
self.setf()
else:
if check()==True:
self.sete()
if self.exit==1:
break
from webcam import myThread2
from screen import myThread1
import json
from check_internet import myThread5,thread1,thread2
from check_internet import stop
from PyQt5 import QtCore, QtGui, QtWidgets
import threading
import os
import sys
class Ui_CloseWindow(object):
def setupUi(self, CloseWindow,val1,val2,val3,val4,thread3,thread4,thread5):
self.val1=val1
self.val2=val2
self.val3=val3
self.val4=val4
self.CloseWindow=CloseWindow
self.CloseWindow.setObjectName("CloseWindow")
self.CloseWindow.resize(549, 382)
self.centralwidget = QtWidgets.QWidget(self.CloseWindow)
self.centralwidget.setObjectName("centralwidget")
self.stopbutton = QtWidgets.QPushButton(self.centralwidget)
self.stopbutton.setGeometry(QtCore.QRect(200, 150, 161, 71))
self.stopbutton.setObjectName("stopbutton")
self.stopbutton.clicked.connect(self.stop)
self.textBrowser_stopWindow = QtWidgets.QTextBrowser(self.centralwidget)
self.textBrowser_stopWindow.setGeometry(QtCore.QRect(140, 80, 261, 41))
self.textBrowser_stopWindow.setObjectName("textBrowser_stopWindow")
self.CloseWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(self.CloseWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 549, 22))
self.menubar.setObjectName("menubar")
self.CloseWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(self.CloseWindow)
self.statusbar.setObjectName("statusbar")
self.CloseWindow.setStatusBar(self.statusbar)
self.retranslateUi(CloseWindow)
QtCore.QMetaObject.connectSlotsByName(CloseWindow)
self.thread5=thread5
self.thread3=thread3
self.thread4=thread4
def stop(self):
stop(self.val1,self.val2)
if self.val3==True:
self.thread3.set()
if self.val4==True:
self.thread4.set()
self.thread5.sete()
self.CloseWindow.close()
def retranslateUi(self, CloseWindow):
_translate = QtCore.QCoreApplication.translate
CloseWindow.setWindowTitle(_translate("CloseWindow", "Close Window"))
self.stopbutton.setText(_translate("CloseWindow", "Stop"))
self.textBrowser_stopWindow.setHtml(_translate("CloseWindow", "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
"<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n"
"p, li { white-space: pre-wrap; }\n"
"</style></head><body style=\" font-family:\'Ubuntu\'; font-size:11pt; font-weight:400; font-style:normal;\">\n"
"<p align=\"center\" style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">Press to stop the proctoring</p></body></html>"))
{"course_name": ""}
\ No newline at end of file
import subprocess
import time
import threading
import signal
import json
from read_config import read_config
from datetime import datetime
class myThread3 (threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.flag=0
def set(self):
self.flag=1
self.p.send_signal(signal.SIGINT)
def run(self):
'''
This method runs the thread and nethogs commands for gathering data usage per app.
This parses the ouput generated by the nethogs command, parses the information,
stores the information in a json file. Through the json file, graph is generated for
better representation of data
:returns: A json file with a graph depicting the data usage
'''
t1 = time.time()
######### Running the nethogs command
self.p = subprocess.Popen(['nethogs', '-v3', '-d1',"-t"],stdout=subprocess.PIPE,stderr=subprocess.STDOUT,universal_newlines=True)
local_time1 = time.ctime(t1) ####### Storing the start time
out, err = self.p.communicate()####### storing the output of the nethogs command
lines = out.split("\n")
############################################################################################################
########################## Making dictionary data out of the stored output ################################
data = {}
count=0
for i in range(len(lines)):
entries = lines[i].split("\t")
try:
Name = entries[0]
if Name.split("/")[-1]=="0" or Name=="unknown":
continue
else:
x = Name.split("/")
if len(x[-3])<=25:
Name=x[-3]
else:
for k in range(len(x)):
if len(x[i]>25):
Name=x[i].split(" ")[0]
break
Upload = entries[1]
Download=entries[2].split("\n")[0]
if Name in data:
data[Name]["Upload"]= float(Upload)
data[Name]["Download"]= float(Download)
else:
data[Name]={}
data[Name]["Upload"]= float(Upload)
data[Name]["Download"]= float(Download)
except:
continue
for key in data.keys():
data[key]["total"]=data[key]["Upload"]+data[key]["Download"]
json_object = json.dumps(data, indent = 4)
###############################################################################################################
############################# Writing Data in a json file ####################################################
with open('../results/DataUsage/'+read_config()+'_'+datetime.now().strftime("%d-%m-%Y_%H:%M:%S")+'.json', "w") as outfile:
outfile.write(json_object)
#############################################################################################################
########################################## Plotting the graph for per app data usage#######################
import matplotlib.pyplot as plt
import operator
x= []
y= []
for key in data.keys():
y.append(data[key]["total"])
x.append(key)
zipped = zip(x, y)
zipped = list(zipped)
res = sorted(zipped, key = operator.itemgetter(1),reverse=True)
x_p = [res[i][0] for i in range(len(res))]
y_p = [res[i][1] for i in range(len(res))]
plt.bar(range(len(x_p)), y_p, align='center')
plt.xticks(range(len(x_p)), x_p)
plt.xlabel('Applications')
plt.ylabel('Data usage in Mbs')
t2 = time.time()
local_time2 = time.ctime(t2)
plt.title("From "+local_time1+" to "+local_time2)
plt.savefig("../results/DataUsage/"+read_config()+"_"+datetime.now().strftime("%d-%m-%Y_%H:%M:%S")+"plt.png")
################################## saving the graph according to the course name and start time #######################
This diff is collapsed.
import json
def read_config():
'''
This method reads the config file to get the course no.
:returns: course no stored in the config file
'''
with open("config.json") as json_data_file:
data = json.load(json_data_file)
return data['course_name']
def write_config(str):
'''
This method updates the config file with new course no.
:param str: String argument
'''
with open("config.json", "w") as outfile:
json.dump({"course_name":str}, outfile)
import pyautogui
import cv2
import numpy as np
import time
import threading
from datetime import datetime
from read_config import read_config
class myThread1 (threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.flag=0
def set(self):
self.flag=1
def run(self):
'''
This method generates the Screen Recording file.
It specifies the device screen resolution and takes screenshot using PyAutoGUI.
The screenshot is converted to a numpy array.
PyAutoGUI captures the screen in RGB(Red, Green, Blue) form and OpenCV converts it to BGR(Blue, Green, Red) and then writes that in an output file.
The file is saved in ScreenRecordings folder and named by current date and time in .avi format.
:returns: Screen Recording file
'''
resolution = pyautogui.size()
codec = cv2.VideoWriter_fourcc(*"XVID")
filename = "../results/ScreenRecordings/"+read_config()+"_"+datetime.now().strftime('%d-%m-%Y_%H:%M:%S')+".avi"
fps =5.0
out = cv2.VideoWriter(filename, codec, fps, resolution)
while True:
img = pyautogui.screenshot()
frame = np.array(img)
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
out.write(frame)
if self.flag==1:
break
cv2.destroyAllWindows()
import cv2
import threading
import time
from datetime import datetime
from read_config import read_config
class myThread2 (threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.flag=0
def set(self):
self.flag=1
def run(self):
'''
This method captures video from Webcam using OpenCV and writes each frame of the video in an output file.
The file is then saved in WebcamRecordings folder and named by current date and time in .avi format.
:returns: Webcam Recording file
'''
video = cv2.VideoCapture(0)
frame_width = int(video.get(3))
frame_height = int(video.get(4))
size = (frame_width, frame_height)
filename="../results/WebcamRecordings/"+read_config()+"_"+datetime.now().strftime('%d-%m-%Y_%H:%M:%S')+".avi"
result = cv2.VideoWriter(filename,cv2.VideoWriter_fourcc(*'XVID'), 10, size)
while(True):
ret, frame = video.read()
result.write(frame)
if self.flag==1:
break
video.release()
result.release()
import subprocess
import csv
import time
import os
from operator import itemgetter
from pathlib import Path
import threading
from read_config import read_config
import pandas as pd
from matplotlib import pyplot as plt
period = 1
order = "up"
maindir = "../results/ActivityTracker"
try:
os.mkdir(maindir)
except FileExistsError:
pass
logdir = "../results/ActivityTracker/text"
csvdir = "../results/ActivityTracker/csv"
pltdir = "../results/ActivityTracker/usage_graph"
def currtime(tformat=None):
return time.strftime("%Y_%m_%d_%H_%M_%S") if tformat == "file"\
else time.strftime("%Y-%m-%d %H:%M:%S")
try:
os.mkdir("../results/ActivityTracker/text")
except FileExistsError:
pass
try:
os.mkdir("../results/ActivityTracker/csv")
except FileExistsError:
pass
try:
os.mkdir("../results/ActivityTracker/usage_graph")
except FileExistsError:
pass
filename = currtime("file")
log1 = logdir+"/"+read_config()+currtime("file")+".txt"; startt = currtime()
log2 = csvdir+"/"+read_config()+currtime("file")+".csv"; startt = currtime()
def get(command):
try:
return subprocess.check_output(command).decode("utf-8").strip()
except subprocess.CalledProcessError:
pass
def time_format(s):
m, s = divmod(s, 60); h, m = divmod(m, 60)
return "%d:%02d:%02d" % (h, m, s)
def summarize(t,winlist,applist):
with open(log1, "wt" ) as report:
totaltime = sum([it[2] for it in winlist])
report.write("")
alldata = []
for app in applist:
appdata = []; windata = []; appinfo = []
apptime = sum([it[2] for it in winlist if it[0] == app])
appperc = round(100*apptime/totaltime)
for d in [app, apptime, appperc]:
appdata.append(d)
wins = [r for r in winlist if r[0] == app]
for w in wins:
wperc = str(round(100*w[2]/apptime))
windata.append([w[1], w[2], wperc])
windata = sorted(windata, key=itemgetter(1))
windata = windata[::-1] if order == "up" else windata
appdata.append(windata); alldata.append(appdata)
alldata = sorted(alldata, key = itemgetter(1))
alldata = alldata[::-1] if order == "up" else alldata
for item in alldata:
app = item[0]; apptime = item[1]; appperc = item[2]
report.write(("-"*60)+"\n"+app+"\n"+time_format(apptime)+" ("+str(appperc)+"%)\n"+("-"*60)+"\n")
field_names = ['Application', 'Time', 'Percentage']
dict = {'Application': app, 'Time': time_format(apptime), 'Percentage': appperc}
appinfo.append(dict)
with open(log2, "w") as csvfile:
writer = csv.DictWriter(csvfile, fieldnames = field_names)
writer.writeheader()
writer.writerows(appinfo)
for w in item[3]:
wname = w[0]; time = w[1]; perc = w[2]
report.write(" "+time_format(time)+" ("+perc+"%)"+(6-len(perc))*" "+wname+"\n")
report.write("\n"+"="*60+"\nstarted: "+startt+"\t"+"updated: "+currtime()+"\n"+"="*60)
def plot():
data = pd.read_csv(log2)
df = pd.DataFrame(data)
name = df['Application']
perc = df['Percentage']
fig, ax = plt.subplots(figsize =(13, 7))
ax.barh(name, perc)
for s in ['top', 'bottom', 'left', 'right']:
ax.spines[s].set_visible(False)
ax.xaxis.set_ticks_position('none')
ax.yaxis.set_ticks_position('none')
ax.xaxis.set_tick_params(pad = 5)
ax.yaxis.set_tick_params(pad = 10)
ax.grid(b = True, color ='grey', linestyle ='-.', linewidth = 0.5, alpha = 0.2)
ax.invert_yaxis()
for i in ax.patches:
plt.text(i.get_width()+0.2, i.get_y()+0.5, str(round((i.get_width()), 2)), fontsize = 10, fontweight ='bold', color ='grey')
ax.set_title('Activity Tracker Result', loc ='center')
plt.xlabel('Percentage of time used', fontweight ='bold')
fig.text(0.9, 0.15, 'Protracktor', fontsize = 12, color ='grey', ha ='right', va ='bottom', alpha = 0.7)
fig.savefig(pltdir+'/'+filename+'.png')
class myThread4 (threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.flag=0
self.t = 0
self.applist = []
self.winlist = []
def set(self):
self.flag=1
def run(self):
while True:
time.sleep(period)
if self.flag==1:
plot()
break
frpid = get(["xdotool", "getactivewindow", "getwindowpid"])
frname = get(["xdotool", "getactivewindow", "getwindowname"])
app = get(["ps", "-p", frpid, "-o", "comm="]) if frpid != None else "Unknown"
if "gnome-terminal" in app:
app = "gnome-terminal"
elif app == "soffice.bin":
app = "libreoffice"
if not app in self.applist:
self.applist.append(app)
checklist = [item[1] for item in self.winlist]
if not frname in checklist:
self.winlist.append([app, frname, 1*period])
else:
self.winlist[checklist.index(frname)][2] = self.winlist[checklist.index(frname)][2]+1*period
if self.t == 60/period:
summarize(self.t,self.winlist,self.applist)
self.t = 0
else:
self.t += 1
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