finished documenting skoolos.py

This commit is contained in:
Nathaniel Kenschaft 2020-06-16 17:57:21 -04:00
parent 86746c5700
commit 38532e7bfa
3 changed files with 365 additions and 251 deletions

View File

@ -14,52 +14,89 @@ import datetime
# get teacher info from api
def getStudent(ion_user):
"""
Get's student information from the api
:param ion_user: a student
:return: student information or error
"""
URL = "http://127.0.0.1:8000/api/students/" + ion_user + "/"
r = requests.get(url=URL, auth=('raffukhondaker', 'hackgroup1'))
if (r.status_code == 200):
if r.status_code == 200:
data = r.json()
return data
elif (r.status_code == 404):
elif r.status_code == 404:
return None
print("Make new account!")
elif (r.status_code == 403):
elif r.status_code == 403:
return None
print("Invalid username/password")
else:
return None
print(r.status_code)
#makes a GET request to given url, returns dict
# makes a GET request to given url, returns dict
def getDB(url):
"""
Sends a GET request to the URL
:param url: URL for request
"""
r = requests.get(url=url, auth=('raffukhondaker', 'hackgroup1'))
print("GET:" + str(r.status_code))
return (r.json())
return r.json()
#makes a PATCH (updates instance) request to given url, returns dict
# makes a PATCH (updates instance) request to given url, returns dict
def patchDB(data, url):
r = requests.patch(url = url, data=data, auth=('raffukhondaker','hackgroup1'))
"""
Sends a PATCH request to the URL
:param data:
:param url: URL for request
"""
r = requests.patch(url=url, data=data, auth=('raffukhondaker', 'hackgroup1'))
print("PATCH:" + str(r.status_code))
return(r.json())
return r.json()
#makes a POST (makes new instance) request to given url, returns dict
# makes a POST (makes new instance) request to given url, returns dict
def postDB(data, url):
"""
Sends a POST request to the URL
:param data:
:param url: URL for request
"""
r = requests.post(url=url, data=data, auth=('raffukhondaker', 'hackgroup1'))
print("POST:" + str(r.status_code))
return (r.json())
return r.json()
#makes a PUT (overwrites instance) request to given url, returns dict
# makes a PUT (overwrites instance) request to given url, returns dict
def putDB(data, url):
"""
Sends a PUT request to the URL
:param data:
:param url: URL for request
"""
r = requests.put(url=url, data=data, auth=('raffukhondaker', 'hackgroup1'))
print("PUT:" + str(r.status_code))
return (r.json())
return r.json()
#makes a DELETE (delete instance) request to given url, returns dict
# makes a DELETE (delete instance) request to given url, returns dict
def delDB(url):
"""
Sends a DELETE request to the URL
:param url: URL for request
"""
r = requests.delete(url=url, auth=('raffukhondaker', 'hackgroup1'))
print("DELETE:" + str(r.status_code))
def command(command):
"""
Runs a shell command
:param command: shell command
"""
ar = []
command = command.split(" ")
for c in command:
@ -78,6 +115,10 @@ class Student:
def __init__(self, data):
# teacher info already stored in API
# intitialze fields after GET request
"""
Initializes a Student with data from the api
:param data: api data
"""
self.git = data['git']
self.username = data['ion_user']
self.url = "http://127.0.0.1:8000/api/students/" + self.username + "/"
@ -122,8 +163,8 @@ class Student:
self.snew = str(data['added_to'])
self.repo = data['repo']
if (os.path.isdir(self.username) == False):
if (self.repo == ""):
if os.path.isdir(self.username) == False:
if self.repo == "":
user = self.git
pwd = input("Enter Github password: ")
# curl -i -u USER:PASSWORD -d '{"name":"REPO"}' https://api.github.com/user/repos
@ -147,11 +188,18 @@ class Student:
print("Synced to " + self.username)
def getClasses(self):
"""
Gets a lists of classes the student is enrolled in
"""
classes = self.classes
for c in classes:
print(c['name'])
def getAssignments(self, course, span):
def getAssignments(self, span):
"""
Gets a list of assignments the student has
:param span: time span to check
"""
span = datetime.timedelta(span, 0)
classes = self.classes
for c in classes:
@ -166,7 +214,7 @@ class Student:
diff = now - due
zero = datetime.timedelta(0, 0)
# check due ddate is in span range is now past date (- timdelta)
if (diff < span and diff > zero):
if diff < span and diff > zero:
print(a + " due in:" + str(now - due))
except Exception as e:
@ -175,6 +223,9 @@ class Student:
# update API and Github, all assignments / classes
def update(self):
"""
Updates the api, github, and all assignments and classes with new information
"""
cdir = os.getcwd()
os.chdir(self.username)
command("git checkout master")
@ -197,7 +248,11 @@ class Student:
# updates 1 class, does not switch to master
def updateClass(self, course):
if ((course in self.sclass) == False):
"""
Updates a class with new information
:param course: class name in the format <course-name>_<ion_user>
"""
if (course in self.sclass) == False:
print("Class not found")
return
cdir = os.getcwd()
@ -212,16 +267,20 @@ class Student:
# add classes from 'new' field
def addClass(self, cid):
"""
Add student to a class
:param cid: the id number of the class
:return: data from the class, None if an error occures
"""
data = getDB('http://127.0.0.1:8000/api/classes/' + str(cid))
if ((cid in self.snew) == False or (self.username in data['confirmed'])):
if not (cid in self.snew) or (self.username in data['confirmed']):
print("Already enrolled in this class.")
return None
if ((cid in self.sclass) or (self.username in data['unconfirmed']) == False):
if (cid in self.sclass) or not (self.username in data['unconfirmed']):
print("Not added by teacher yet.")
return None
# add class teacher as cocllaborator to student repo
# add class teacher as collaborator to student repo
print(os.getcwd())
pwd = input("Enter Github password: ")
tgit = getDB("http://127.0.0.1:8000/api/teachers/" + data['teacher'] + "/")['git']
@ -241,7 +300,7 @@ class Student:
# os.chdir(self.username)
# push to git, start at master
#os.chdir(self.username)
# os.chdir(self.username)
command("git checkout master")
command("git branch " + data['name'])
command("git commit -m initial")
@ -263,7 +322,7 @@ class Student:
user = self.git
self.classes.append(data)
if (len(self.sclass) == 0):
if len(self.sclass) == 0:
self.sclass = data['name']
else:
self.sclass = self.sclass + "," + str(data['name'])
@ -272,7 +331,7 @@ class Student:
snew = ""
new = []
for i in range(len(self.new)):
if (self.new[i]['name'] == data['name']):
if self.new[i]['name'] == data['name']:
del self.new[i]
# recreate sclass field, using ids
for c in self.new:
@ -292,46 +351,12 @@ class Student:
print(patchDB(data, self.url))
return data
def submit(self, path):
# 2022rkhondak/English11_eharris1/Essay1
# check if valid assignment
parts = path.split("/")
if (len(parts) != 3):
print("Assignment path too short")
return
isclass = False
for c in self.classes:
if (c['name'] == parts[1]):
isclass == True
break
if (parts[0] != self.username and isclass and os.path.isdir(path) == False):
print("Not valid assignment")
return
if ((parts[1] + "/" + parts[2]) in self.completed):
print(parts[2] + " already submited. ")
# return
resp = input("Are you sure you want to submit? You cannot do this again.(y/N) ")
if (resp == 'y'):
os.chdir(self.username + "/" + parts[1])
command("git add .")
command("git commit -m submit")
command("git tag " + parts[1] + "-final")
command("git push -u origin " + self.username + " --tags")
self.completed = self.completed + "," + parts[1] + "/" + parts[2]
data = {
'user': self.user,
'git': self.git,
'ion_user': self.username,
'student_id': self.student_id,
'added_to': self.snew,
'url': self.url,
'classes': self.sclass,
'grade': self.grade,
'completed': self.completed
}
# print(putDB(data, "http://127.0.0.1:8000/api/students/" + self.username + "/"))
def viewClass(self, courses):
"""
Sets the current git branch to view each class in courses
:param courses: a list of classes
:return:
"""
self.update()
cdir = os.getcwd()
os.chdir(self.username)
@ -346,11 +371,20 @@ class Student:
return
def exitCLI(self):
"""
Exits the cli
"""
print(os.getcwd())
self.update()
command("git checkout master")
def submit(self, course, assignment):
"""
Submits an assignment
:param course: the class the assignment belongs to
:param assignment: the assignment
:return:
"""
cdir = os.getcwd()
os.chdir(self.username)
print(os.getcwd())
@ -364,7 +398,7 @@ class Student:
if a == assignment:
inclass = True
break
if (inclass == False):
if inclass == False:
print(assignment + " not an assignment of " + course)
command('git checkout master')
os.chdir(cdir)

View File

@ -25,7 +25,12 @@ scope = ["read"]
USER = ""
PWD = ""
def main():
"""
The Command Line Interface (CLI) for SkoolOS
Serves to allow both teachers and students to access the majority of the features of SkoolOS
"""
print("")
print("░██████╗██╗░░██╗░█████╗░░█████╗░██╗░░░░░  ░█████╗░░██████╗")
print("██╔════╝██║░██╔╝██╔══██╗██╔══██╗██║░░░░░  ██╔══██╗██╔════╝")
@ -38,13 +43,13 @@ def main():
if not (os.path.exists(".sprofile") or os.path.exists(".tprofile")):
try:
URL = "http://127.0.0.1:8000/api/"
r = requests.get(url = URL)
r = requests.get(url=URL)
except:
print("Run Django server on http://127.0.0.1:8000/ before continuing")
sys.exit(0)
input("Welcome to SkoolOS. Press any key to create an account")
#webbrowser.open("http://127.0.0.1:8000/login", new=2)
# webbrowser.open("http://127.0.0.1:8000/login", new=2)
authenticate()
else:
profiles = os.listdir()
@ -53,47 +58,59 @@ def main():
count = 1
for i in range(len(profiles)):
p = profiles[i]
if('profile' in p):
f = open(p,'r')
if 'profile' in p:
f = open(p, 'r')
d = json.loads(f.read())
f.close()
info.append(d)
users.append(str(count) + ") " + d['username'])
count = count+1
count = count + 1
user = [
{
'type': 'list',
'name': 'user',
'choices':users,
'choices': users,
'message': 'Select User: ',
},
]
u = int(prompt(user)['user'].split(")")[0]) -1
u = int(prompt(user)['user'].split(")")[0]) - 1
data = info[u]
PWD = data['password']
USER = data['username']
print(data['username'])
if(data['is_student']):
if data['is_student']:
studentCLI(USER, PWD)
else:
teacherCLI(USER, PWD)
################################################ STUDENT METHODS
def studentCLI(user, password):
"""
The CLI for students to access
:param user: student username
:param password: student password
"""
from CLI import student
data = getUser(user, password, 'student')
student = student.Student(data)
student.update()
EXIT = False
while(not EXIT):
while not EXIT:
course = chooseClassStudent(student)
EXIT = classOptionsStudent(student, course)
#return class
# return class
def chooseClassStudent(student):
"""
Chooses a class for a student to view and work on
:param student: a student
:return: a course prompt
"""
carray = student.sclass.split(",")
if(len(carray) == 1 and carray[0] == ""):
if len(carray) == 1 and carray[0] == "":
carray.remove("")
print("No classes")
@ -102,7 +119,7 @@ def chooseClassStudent(student):
{
'type': 'list',
'name': 'course',
'choices':carray,
'choices': carray,
'message': 'Select class: ',
},
]
@ -110,35 +127,48 @@ def chooseClassStudent(student):
print(course)
return course
def classOptionsStudent(student, course):
"""
Allows students to choose what they want to do related to a class
The student can save, exit, or go back
:param student: a student
:param course: a course
:return: True if exiting, False if going back
"""
student.viewClass(course)
student.getAssignments(course, 100)
choices = ["Save","Back","Exit SkoolOS"]
choices = ["Save", "Back", "Exit SkoolOS"]
options = [
{
'type': 'list',
'name': 'option',
'choices':choices,
'choices': choices,
'message': 'Select: ',
},
]
option = prompt(options)['option']
if(option == "Save"):
if option == "Save":
student.update()
print("Saved!")
classOptionsStudent(student, course)
if(option == "Back"):
if option == "Back":
student.exitCLI()
#dont exit cli
# dont exit cli
return False
if(option == "Exit SkoolOS"):
if option == "Exit SkoolOS":
student.exitCLI()
#exit cli
# exit cli
return True
################################################ TEACHER METHODS
def chooseGeneralTeacher(teacher):
"""
Presents teachers with their options
:param teacher: a teacher
:return: a course prompt
"""
carray = []
for c in teacher.classes:
carray.append(c)
@ -148,14 +178,20 @@ def chooseGeneralTeacher(teacher):
{
'type': 'list',
'name': 'course',
'choices':carray,
'choices': carray,
'message': 'Select class: ',
},
]
course = prompt(courses)['course']
return course
def teacherCLI(user, password):
"""
The teachers' view of the CLI
:param user: username
:param password: password
"""
from CLI import teacher
data = getUser(user, password, 'teacher')
print(data)
@ -174,7 +210,7 @@ def teacherCLI(user, password):
{
'type': 'list',
'name': 'course',
'choices':carray,
'choices': carray,
'message': 'Select class: ',
},
]
@ -196,20 +232,20 @@ def teacherCLI(user, password):
questions = [
{
'type': 'list',
'choices':soption,
'choices': soption,
'name': 'students',
'message': 'Add Students): ',
},
]
choice = prompt(questions)['students'].split(")")[0]
if("1" == choice):
if "1" == choice:
s = input("Student name: ")
teacher.addStudent(s, cname)
if("2" == choice):
if "2" == choice:
print("File must be .txt and have 1 student username per line")
path = input("Relative Path: ")
while(not os.path.exists(path)):
if(path == 'N'):
while not os.path.exists(path):
if path == 'N':
sys.exit(0)
print(path + " is not a valid path")
path = input("Enter file path ('N' to exit): ")
@ -227,23 +263,23 @@ def teacherCLI(user, password):
{
'type': 'list',
'name': 'course',
'choices':options,
'choices': options,
'message': 'Select option: ',
},
]
option = prompt(questions)['course'].split(")")[0]
if(option == '1'):
if option == '1':
soption = ["1) Add individual student", "2) Add list of students through path", "3) Exit"]
questions = [
{
'type': 'list',
'choices':soption,
'choices': soption,
'name': 'students',
'message': 'Add list of students (input path): ',
},
]
schoice = prompt(questions)['students'].split(")")[0]
if(schoice == '1'):
if schoice == '1':
questions = [
{
'type': 'input',
@ -253,7 +289,7 @@ def teacherCLI(user, password):
]
s = prompt(questions)['student']
teacher.reqStudent(s, course)
if(schoice == '2'):
if schoice == '2':
questions = [
{
'type': 'input',
@ -262,8 +298,8 @@ def teacherCLI(user, password):
},
]
path = prompt(questions)['path']
while(not os.path.exists(path)):
if(path == 'N'):
while not os.path.exists(path):
if path == 'N':
sys.exit(0)
print(path + " is not a valid path")
path = input("Enter file path ('N' to exit): ")
@ -272,7 +308,7 @@ def teacherCLI(user, password):
teacher.reqAddStudentList(students, course)
else:
sys.exit(0)
if(option == '2'):
if option == '2':
nlist = os.listdir(teacher.username + "/" + course)
alist = getDB("http://localhost:8000/api/classes/" + course)['assignments']
print(nlist)
@ -282,23 +318,22 @@ def teacherCLI(user, password):
b = True
print(teacher.username + "/" + course + "/" + n)
for a in alist:
if(n in a or n == a):
#print("Assignments: " + n)
if n in a or n == a:
# print("Assignments: " + n)
b = False
if(not os.path.isdir(teacher.username + "/" + course + "/" + n)):
if not os.path.isdir(teacher.username + "/" + course + "/" + n):
b = False
if(b):
if b:
tlist.append(n)
nlist = tlist
if(len(nlist) == 0):
if len(nlist) == 0:
print("No new assignments found")
sys.exit(0)
questions = [
{
'type': 'list',
'choices':nlist,
'choices': nlist,
'name': 'assignment',
'message': 'Select new assignment: ',
},
@ -309,7 +344,7 @@ def teacherCLI(user, password):
due = due + ":33.383124"
due = due.strip()
f = False
while(not f):
while not f:
try:
datetime.datetime.strptime(due, '%Y-%m-%d %H:%M:%S.%f')
f = True
@ -320,55 +355,90 @@ def teacherCLI(user, password):
due = due + ":33.383124"
teacher.addAssignment(apath, course, due)
######################################################################
def getUser(ion_user, password, utype):
if('student' in utype):
"""
Returns user information
:param ion_user: user
:param password: user's password
:param utype: type of user (student or teacher
:return: api user information
"""
if 'student' in utype:
URL = "http://127.0.0.1:8000/api/students/" + ion_user + "/"
else:
URL = "http://127.0.0.1:8000/api/teachers/" + ion_user + "/"
r = requests.get(url = URL, auth=(ion_user,password))
r = requests.get(url=URL, auth=(ion_user, password))
print(r.json())
if(r.status_code == 200):
if r.status_code == 200:
data = r.json()
print(200)
return data
elif(r.status_code == 404):
elif r.status_code == 404:
print("Make new account!")
return None
elif(r.status_code == 403):
elif r.status_code == 403:
print("Invalid username/password")
return None
else:
print(r.status_code)
return None
def patchDB(data, url):
r = requests.patch(url = url, data=data, auth=('raffukhondaker','hackgroup1'))
r = requests.patch(url=url, data=data, auth=('raffukhondaker', 'hackgroup1'))
print("PATH:" + str(r.status_code))
return(r.json())
return r.json()
def getDB(url):
r = requests.get(url = url, auth=('raffukhondaker','hackgroup1'))
"""
Sends a GET request to the URL
:param url: URL for request
"""
r = requests.get(url=url, auth=('raffukhondaker', 'hackgroup1'))
print("GET:" + str(r.status_code))
return(r.json())
return r.json()
def postDB(data, url):
r = requests.post(url = url, data=data, auth=('raffukhondaker','hackgroup1'))
"""
Sends a POST request to the URL
:param url: URL for request
"""
r = requests.post(url=url, data=data, auth=('raffukhondaker', 'hackgroup1'))
print("POST:" + str(r.status_code))
return(r.json())
return r.json()
def putDB(data, url):
r = requests.put(url = url, data=data, auth=('raffukhondaker','hackgroup1'))
"""
Sends a PUT request to the URL
:param url: URL for request
"""
r = requests.put(url=url, data=data, auth=('raffukhondaker', 'hackgroup1'))
print("PUT:" + str(r.status_code))
return(r.json())
return r.json()
def delDB(url):
r = requests.delete(url = url, auth=('raffukhondaker','hackgroup1'))
"""
Sends a DEL request to the URL
:param url: URL for request
"""
r = requests.delete(url=url, auth=('raffukhondaker', 'hackgroup1'))
print("DELETE:" + str(r.status_code))
return None
def makePass():
"""
Prompts the user to create a password
:return: the password
"""
questions = [
{
'type': 'password',
@ -377,7 +447,7 @@ def makePass():
},
]
pwd = prompt(questions)['pwd']
while(len(pwd) < 7):
while len(pwd) < 7:
print("Password too short (Must be over 6 characters)")
pwd = prompt(questions)['pwd']
conf = [
@ -388,23 +458,27 @@ def makePass():
},
]
pwd2 = prompt(conf)['pwd']
while(not pwd == pwd2):
while not pwd == pwd2:
print("Passwords do not match.")
pwd2 = prompt(conf)['pwd']
else:
print("PASSWORD SAVED")
return pwd
def authenticate():
"""
Authenticates the user via Ion OAuth
"""
oauth = OAuth2Session(client_id=client_id, redirect_uri=redirect_uri, scope=scope)
authorization_url, state = oauth.authorization_url("https://ion.tjhsst.edu/oauth/authorize/")
cdir = os.getcwd()
#Linux: chromdriver-linux
#Macos: chromdriver-mac
#Windows: chromdriver.exe
if('CLI' in os.getcwd()):
path = os.path.join(os.getcwd(), '../','chromedriver-mac')
# Linux: chromdriver-linux
# Macos: chromdriver-mac
# Windows: chromdriver.exe
if 'CLI' in os.getcwd():
path = os.path.join(os.getcwd(), '../', 'chromedriver-mac')
else:
path = os.path.join(os.getcwd(), 'chromedriver-mac')
@ -417,7 +491,8 @@ def authenticate():
url = browser.current_url
gets = url_decode(url.replace("http://localhost:8000/login/?", ""))
while "http://localhost:8000/login/?username=" not in browser.current_url and (not browser.current_url == "http://localhost:8000/"): #http://localhost:8000/
while "http://localhost:8000/login/?username=" not in browser.current_url and (
not browser.current_url == "http://localhost:8000/"): # http://localhost:8000/
time.sleep(0.25)
url = browser.current_url
@ -439,31 +514,31 @@ def authenticate():
'message': 'Enter SkoolOS Password (NOT ION PASSWORD): ',
},
]
data =prompt(questions)
data = prompt(questions)
pwd = data['pwd']
user = data['username']
r = requests.get(url = "http://localhost:8000/api/", auth=(user,pwd))
while(r.status_code != 200):
r = requests.get(url="http://localhost:8000/api/", auth=(user, pwd))
while r.status_code != 200:
print("INCORRECT LOGIN CREDENTIALS")
r = requests.get(url = "http://localhost:8000/api/", auth=(user,pwd))
data =prompt(questions)
r = requests.get(url="http://localhost:8000/api/", auth=(user, pwd))
data = prompt(questions)
pwd = data['pwd']
user = data['username']
print(r.status_code)
r = requests.get(url = "http://localhost:8000/api/students/" + user + "/", auth=(user,pwd))
r = requests.get(url="http://localhost:8000/api/students/" + user + "/", auth=(user, pwd))
is_student = False
if(r.status_code == 200):
if r.status_code == 200:
is_student = True
print("Welcome, student " + user)
r = requests.get(url = "http://localhost:8000/api/students/" + user + "/", auth=(user,pwd))
r = requests.get(url="http://localhost:8000/api/students/" + user + "/", auth=(user, pwd))
profile = r.json()
username = profile['ion_user']
grade = profile['grade']
profile = {
'username':username,
'grade':grade,
'is_student':is_student,
'password':pwd,
'username': username,
'grade': grade,
'is_student': is_student,
'password': pwd,
}
profileFile = open(".sprofile", "w")
profileFile.write(json.dumps(profile))
@ -471,26 +546,31 @@ def authenticate():
else:
print("Welcome, teacher " + user)
r = requests.get(url = "http://localhost:8000/api/teachers/" + user + "/", auth=(user,pwd))
r = requests.get(url="http://localhost:8000/api/teachers/" + user + "/", auth=(user, pwd))
profile = r.json()
username = profile['ion_user']
profile = {
'username':username,
'is_student':is_student,
'password':pwd,
'username': username,
'is_student': is_student,
'password': pwd,
}
profileFile = open(".tprofile", "w")
profileFile.write(json.dumps(profile))
profileFile.close()
sys.exit
sys.exit(0)
def create_server():
"""
Creates a simple HTTP server for creating api requests from the CLI
"""
port = 8000
handler = http.server.SimpleHTTPRequestHandler
httpd = socketserver.TCPServer(("", port), handler)
print("serving at port:" + str(port))
httpd.serve_forever()
if __name__ == "__main__":
main()