diff --git a/.gitignore b/.gitignore index 8df82ed..e67073c 100644 --- a/.gitignore +++ b/.gitignore @@ -128,11 +128,129 @@ dmypy.json # Pyre type checker .pyre/ -# PyCharm Files -.idea/ - # VSCode .vscode/ -#lmao mac +# PyCharm Files +.idea/ + +# Mac lmao .DS_Store + +# Django # +*.log +*.pot +*.pyc +__pycache__ +db.sqlite3 +media + +# Backup files # +*.bak + +# If you are using PyCharm # +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/dictionaries +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.xml +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/gradle.xml +.idea/**/libraries +*.iws /out/ + +# Python # +*.py[cod] +*$py.class + +# Distribution / packaging +.Python build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +.pytest_cache/ +nosetests.xml +coverage.xml +*.cover +.hypothesis/ + +# Jupyter Notebook +.ipynb_checkpoints + +# pyenv +.python-version + +# celery +celerybeat-schedule.* + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ + +# Sublime Text # +*.tmlanguage.cache +*.tmPreferences.cache +*.stTheme.cache +*.sublime-workspace +*.sublime-project + +# sftp configuration file +sftp-config.json + +# Package control specific files Package +Control.last-run +Control.ca-list +Control.ca-bundle +Control.system-ca-bundle +GitHub.sublime-settings + +# Visual Studio Code # +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +.history + diff --git a/2022rkhondak b/2022rkhondak new file mode 160000 index 0000000..8dca8b7 --- /dev/null +++ b/2022rkhondak @@ -0,0 +1 @@ +Subproject commit 8dca8b78c03fab721e9976a4675e2996802328a7 diff --git a/CLI/commands.py b/CLI/commands.py index 1780d32..ed1c1f3 100644 --- a/CLI/commands.py +++ b/CLI/commands.py @@ -5,134 +5,4 @@ import os import argparse -#already ccrerrated account through website, has to login -def update(): - #get data from database - return - -def yesorno(question): - questions = [ - { - 'type': 'input', - 'name': 'response', - 'message': question, - }, - ] - answers = prompt(questions) - if(answers["response"] == "y"): - return True - return False - -def login(): - #enter username - #enter password - questions = [ - { - 'type': 'input', - 'name': 'webmail', - 'message': 'What\'s TJ Webmail', - }, - { - 'type': 'password', - 'name': 'password', - 'message': 'Password?', - }, - ] - user = prompt(questions) - #reading from json of users (replace w GET to database) to check if user is registered - with open('users.json', 'r') as json_file: - data = json.load(json_file) - for i in range(len(data)): - if user["webmail"] == data[i]["webmail"]: - if(user["password"] == data[i]["password"]): - print("Logged in!") - return data[i] - else: - print("Password incorrect. Try again.") - return None - print("User not found. Please Try again") - return None - -#did not create account through website, has to signup/login -def signup(): - questions = [ - { - 'type': 'input', - 'name': 'first-name', - 'message': 'What\'s your first name', - }, - { - 'type': 'input', - 'name': 'last-name', - 'message': 'What\'s your last name?', - }, - { - 'type': 'list', - 'name': 'grade', - 'message': 'Grade?', - 'choices':["9","10","11","12"] - }, - { - 'type': 'input', - 'name': 'webmail', - 'message': 'What\'s your TJ Webmail?', - }, - { - 'type': 'password', - 'name': 'password', - 'message': 'Password?', - }, - ] - user = prompt(questions) - for i in user: - if user[i] == "": - print("Some forms were left blank. Try again.\n") - return None - if len(user["password"]) < 6: - print("Password is too short. Try again.") - return None - if (("@tjhsst.edu" in user['webmail']) == False): - print("Webmail entered was not a @tjhhsst.edu. Try again.") - return None - - user["classes"] = [] - with open('users.json', 'r') as json_file: - data = json.load(json_file) - data.append(user) - open("users.json", "w").write(str(json.dumps(data))) - return user - -def relogin(): - questions = [ - { - 'type': 'list', - 'name': 'choice', - 'message': '', - 'choices':["Continue as current user","Login into new user","Sign up into new account"] - }, - ] - answer = prompt(questions) - - -def setup(user): - #Read classes/assignenments and setup directory: - #SkoolOS/Math/Week1 - for c in user["classes"]: - os.makedirs("../" + c) - for a in user["classes"][c]: - os.makedirs("../" + c + "/" + a) - -def start(): - if(os.path.exists(".login.txt") == False): - b = yesorno("Do you have a SkoolOS account?(y/N)") - if(b): - user = login() - if(user != None): - setup(user) - open(".login.txt", "w").write(str(user)) - else: - user = signup() - if(user != None): - open(".login.txt").write(str(user)) - \ No newline at end of file diff --git a/CLI/index.html b/CLI/index.html new file mode 100644 index 0000000..a21b847 --- /dev/null +++ b/CLI/index.html @@ -0,0 +1,9 @@ + + + + Sign in with Ion + + + Sign in with + + \ No newline at end of file diff --git a/CLI/oauth/index.html b/CLI/oauth/index.html new file mode 100644 index 0000000..31fa9e2 --- /dev/null +++ b/CLI/oauth/index.html @@ -0,0 +1,23 @@ + + + Sign into Ion + + + + + + +
+ + + Sign in with Ion + +
+ + \ No newline at end of file diff --git a/CLI/oauth/template.html b/CLI/oauth/template.html new file mode 100644 index 0000000..6452ea8 --- /dev/null +++ b/CLI/oauth/template.html @@ -0,0 +1,23 @@ + + + Sign into Ion + + + + + + +
+ + + Sign in with Ion + +
+ + \ No newline at end of file diff --git a/CLI/requirements.txt b/CLI/requirements.txt new file mode 100644 index 0000000..fbc7e54 --- /dev/null +++ b/CLI/requirements.txt @@ -0,0 +1,21 @@ +asgiref==3.2.7 +certifi==2020.4.5.1 +chardet==3.0.4 +click==7.1.2 +Django==3.0.7 +django-cors-middleware==1.5.0 +django-oauth-toolkit==1.3.2 +djangorestframework==3.11.0 +idna==2.9 +oauthlib==3.1.0 +prompt-toolkit==1.0.14 +Pygments==2.6.1 +PyInquirer==1.0.3 +pytz==2020.1 +regex==2020.5.14 +requests==2.23.0 +selenium==3.141.0 +six==1.15.0 +sqlparse==0.3.1 +urllib3==1.25.9 +wcwidth==0.2.3 diff --git a/CLI/run.py b/CLI/run.py deleted file mode 100644 index aca8859..0000000 --- a/CLI/run.py +++ /dev/null @@ -1,22 +0,0 @@ -from __future__ import print_function, unicode_literals -from PyInquirer import prompt, print_json -from commands import start, update -import argparse -import json -import os -import argparse - -my_parser = argparse.ArgumentParser(prog='skool', description='Let SkoolOS control your system', epilog="Try again") -my_parser.add_argument('--init', action="store_true") #returns true if run argument -args = my_parser.parse_args() - -update() -outputs = vars(args) -if(outputs['init']): - start() - - - - - - diff --git a/CLI/s-git.py b/CLI/s-git.py new file mode 100644 index 0000000..1203091 --- /dev/null +++ b/CLI/s-git.py @@ -0,0 +1,101 @@ +import subprocess +import os +import requests +import pprint +import json + +#git clone student directory ==> /classes/assignments +def initStudent(ion_user): + #check if git has already been initialized + if(os.path.exists(str(ion_user) +"/" + ".git")): + print("Already synced to: " + str(ion_user)) + return + + #get student repo from API + URL = "http://127.0.0.1:8000/students/" + ion_user + "/" + r = requests.get(url = URL, auth=('student','_$YFE#34.9_37jr')) + if(r.status_code == 200): + data = r.json() + repo = data['repo'] + classes = data['classes'] + print(data) + #git clone repo + process = subprocess.Popen(['git', 'clone', repo], stdout=subprocess.PIPE,stderr=subprocess.PIPE) + process.wait() + + # make classes directory + for c in classes: + cpath = str(ion_user) + "/" + c['name'] + if(os.path.exists(cpath)): + print(cpath + " already exists...") + else: + os.mkdir(str(ion_user) + "/" + c['name']) + + #make assignments directory + for a in c['assignments']: + path = str(ion_user) + "/" + c['name'] + "/" + a['name'] + print(path) + if(os.path.exists("/" +path)): + print(path + " already exists...") + else: + os.mkdir(str(ion_user) + "/" + c['name'] + "/" + a['name']) + + #push to remote repo + os.chdir(ion_user) + process = subprocess.Popen(['git', 'init'], stdout=subprocess.PIPE,stderr=subprocess.PIPE) + process.wait() + process = subprocess.Popen(['git', 'add', '.'], stdout=subprocess.PIPE,stderr=subprocess.PIPE) + process.wait() + process = subprocess.Popen(['git', 'commit', '-m', "First Commit"], stdout=subprocess.PIPE,stderr=subprocess.PIPE) + process.wait() + process = subprocess.Popen(['git', 'push', '-u', 'origin','master'], stdout=subprocess.PIPE,stderr=subprocess.PIPE) + process.wait() + + elif(r.status_code == 404): + print("Make new account!") + elif(r.status_code == 403): + print("Invalid username/password") + else: + print(r.status_code) + + + +#Teachers + +#make student repo by student id +def addStudent(stid, teacher): + os.mkdir(stid) + os.chdir(os.getcwd() + "/" + stid) + process = subprocess.Popen(['git', 'init'], stdout=subprocess.PIPE,stderr=subprocess.PIPE) + process.communicate() + process = subprocess.Popen(['git', 'add', '.'], stdout=subprocess.PIPE,stderr=subprocess.PIPE) + process.communicate() + process = subprocess.Popen(['git', 'commit', '-m', "First Commit"], stdout=subprocess.PIPE,stderr=subprocess.PIPE) + process.communicate() + +def addStudents(filename): + print(filename) + +def addAsignment(name): + print(name) + +def updateAssignment(name): + print(name) + +def comment(filename, text): + print(text) + +#initStudent("2022rkhondak") +ion_user = "2022rkhondak" +headers = {'Content-type': 'application/json'} +data = {'first_name': 'Jeff', + 'git': 'https://github.com/', + 'grade': 10, + 'ion_user': '2022jlol1', + 'last_name': 'lol.', + 'student_id': 11111 + } +data = json.dumps(data) +URL = "http://127.0.0.1:8000/students/" + ion_user + "/" +r = requests.put(url = URL, data= data, headers=headers ,auth=('raffukhondaker','hackgroup1')) +pprint.pprint(r.json()) \ No newline at end of file diff --git a/CLI/server.py b/CLI/server.py new file mode 100644 index 0000000..0aef4c5 --- /dev/null +++ b/CLI/server.py @@ -0,0 +1,23 @@ +from socket import * +from selenium import webdriver +import http.server +import socketserver +import threading + + +def create_server(): + port = 8000 + handler = http.server.SimpleHTTPRequestHandler + httpd = socketserver.TCPServer(("", port), handler) + print("serving at port:" + str(port)) + httpd.serve_forever() + + +threading.Thread(target=create_server).start() + +print("Server has started. Continuing..") + +browser = webdriver.Chrome() +browser.get("http://localhost:8000") + +assert "" in browser.page_source diff --git a/CLI/skoolos.py b/CLI/skoolos.py index bf46ef4..428415a 100644 --- a/CLI/skoolos.py +++ b/CLI/skoolos.py @@ -3,15 +3,19 @@ from urllib.parse import urlparse import requests from requests_oauthlib import OAuth2Session -from selenium import webdriver; +from selenium import webdriver import os.path import time +import http.server +import socketserver +from threading import Thread +from werkzeug.urls import url_decode client_id = r'QeZPBSKqdvWFfBv1VYTSv9iFGz5T9pVJtNUjbEr6' client_secret = r'0Wl3hAIGY9SvYOqTOLUiLNYa4OlCgZYdno9ZbcgCT7RGQ8x2f1l2HzZHsQ7ijC74A0mrOhhCVeZugqAmOADHIv5fHxaa7GqFNtQr11HX9ySTw3DscKsphCVi5P71mlGY' redirect_uri = 'http://localhost:8000/' token_url = 'https://ion.tjhsst.edu/oauth/token/' -scope=["read"] +scope = ["read"] def main(): @@ -25,38 +29,79 @@ def main(): if not os.path.exists(".profile"): authenticate() + print(open(".profile", "r").read()) else: print(open(".profile", "r").read()) + while True: + pass def authenticate(): oauth = OAuth2Session(client_id=client_id, redirect_uri=redirect_uri, scope=scope) authorization_url, state = oauth.authorization_url("https://ion.tjhsst.edu/oauth/authorize/") + + web_dir = os.path.join(os.path.dirname(__file__), 'oauth') + os.chdir(web_dir) + if os.path.exists("index.html"): + os.remove("index.html") + + template = open("template.html", "r") + index = open("index.html", "w") + for line in template: + index.write(line.replace('AUTH_URL', authorization_url)) + template.close() + index.close() + + # server = Thread(target=create_server) + # server.daemon = True + # server.start() + browser = webdriver.Chrome() - browser.get(authorization_url) + browser.get("localhost:8000/") while "http://localhost:8000/?code" not in browser.current_url: + http.server. time.sleep(0.25) - code = urlparse(browser.current_url).query[5:] + url = browser.current_url + gets = url_decode(url.replace("http://localhost:8000/?", "")) + code = gets.get("code") + if state == gets.get("state"): + state = gets.get("state") + print("states good") browser.quit() + print(code) + print(state) + payload = {'grant_type': 'authorization_code', 'code': code, 'redirect_uri': redirect_uri, 'client_id': client_id, 'client_secret': client_secret, 'csrfmiddlewaretoken': state} token = requests.post("https://ion.tjhsst.edu/oauth/token/", data=payload).json() print(token) - # headers = {'Authorization': f"Bearer {token['access_token']}"} - # - # # And finally get the user's profile! - # profile = requests.get("https://ion.tjhsst.edu/api/profile", headers=headers).json() - # username = profile['ion_username'] - # email = profile['tj_email'] - # first_name = profile['first_name'] - # last_name = profile['last_name'] + headers = {'Authorization': f"Bearer {token['access_token']}"} - #print(profile) + # And finally get the user's profile! + profile = requests.get("https://ion.tjhsst.edu/api/profile", headers=headers).json() + print(profile) + username = profile['ion_username'] + email = profile['tj_email'] + first_name = profile['first_name'] + last_name = profile['last_name'] + profileFile = open(".profile", "w") + profileFile.write(profile) + profileFile.close() + + # server.stop + + +def create_server(): + port = 8000 + handler = http.server.SimpleHTTPRequestHandler + httpd = socketserver.TCPServer(("", port), handler) + print("serving at port:" + str(port)) + httpd.serve_forever() if __name__ == "__main__": main() diff --git a/CLI/t-git-old.py b/CLI/t-git-old.py new file mode 100644 index 0000000..d2a3316 --- /dev/null +++ b/CLI/t-git-old.py @@ -0,0 +1,457 @@ +import subprocess +import os +import requests +import webbrowser +import pprint +import json + +#git clone student directory ==> <student-id>/classes/assignments +''' +{ + "url": "http://127.0.0.1:8000/teachers/eharris1/", + "first_name": "Errin", + "last_name": "Harris", + "classes": [ + { + "url": "http://127.0.0.1:8000/classes/1/", + "name": "Math5", + "assignments": [ + { + "name": "Week1_HW", + "due_date": "2020-06-07T07:46:30.537197Z", + "url": "http://127.0.0.1:8000/assignments/1/", + "files": [ + { + "name": "instructions.txt" + } + ] + }, + { + "name": "Week2_HW", + "due_date": "2020-06-07T07:46:30.548596Z", + "url": "http://127.0.0.1:8000/assignments/2/", + "files": [ + { + "name": "instructions.txt" + } + ] + } + ], + "repo": "" + } + ], + "git": "therealraffi", + "ion_user": "eharris1" + }, +''' +#get teacher info from api +def getTeacher(ion_user): + URL = "http://127.0.0.1:8000/teachers/" + ion_user + "/" + r = requests.get(url = URL, auth=('raffukhondaker','hackgroup1')) + if(r.status_code == 200): + data = r.json() + return data + elif(r.status_code == 404): + return None + print("Make new account!") + elif(r.status_code == 403): + return None + print("Invalid username/password") + else: + return None + print(r.status_code) + +def getDB(url): + r = requests.get(url = url, auth=('raffukhondaker','hackgroup1')) + return r.json() + +class Teacher: + def __init__(self, data): + # teacher info already stored in API + # intitialze fields after GET request + self.first_name=data['first_name'] + self.last_name=data['last_name'] + self.git=data['git'] + self.username=data['ion_user'] + self.url= "http://127.0.0.1:8000/teachers/" + self.username + "/" + + classes=data['classes'].split(",") + cdict = [] + for cid in classes: + adict=[] + c = getDB("http://127.0.0.1:8000/classes/" + cid + "/") + name=c['name'] + repo=c['repo'] + path=c['path'] + teacher=c['teacher'] + id=c['id'] + assignments=c['assignments'].split(",") + dfs = c['default_file'].split(' ') + dfdict=[] + for fid in dfs: + f = getDB("http://127.0.0.1:8000/files/" + fid + "/") + fname=f['name'] + fpath=f['path'] + dfdict.append({ + 'name':fname, + 'path':fpath, + 'classes':classes, + 'assignment':"none", + 'id':fid, + }) + for aid in assignments: + fdict=[] + a = getDB("http://127.0.0.1:8000/assignments/" + aid + "/") + aname= a['name'] + due_date = a['due_date'] + apath=a['path'] + classes=a['classes'] + files = a['files'].split(' ') + for fid in files: + f = getDB("http://127.0.0.1:8000/files/" + fid + "/") + fname=f['name'] + fpath=f['path'] + fdict.append({ + 'name':fname, + 'path':fpath, + 'classes':classes, + 'assignment':aname, + 'id':fid, + }) + adict.append({ + 'name':aname, + 'due_date':due_date, + 'path':apath, + 'classes':classes, + 'teacher':teacher, + 'files':fdict, + }) + cdict.append({ + 'name':name, + 'path':path, + 'teacher':teacher, + 'assignments':adict, + 'default_file':adict, + }) + + self.classes = cdict + + def command(self,command): + ar = [] + command = command.split(" ") + for c in command: + ar.append(c) + process = subprocess.Popen(ar, stdout=subprocess.PIPE,stderr=subprocess.PIPE) + p=process.poll() + output = process.communicate()[0] + #print(output.decode('utf-8')) + + def initTeacher(self): + if(os.path.exists(self.username )): + print("Already synced to: " + str(self.username)) + return + os.mkdir(self.username) + classes = self.classes + # make classes directory + for c in classes: + cname= c['name'] + cpath = self.username + "/" + cname + + input("Make new Git Repo with name: " + cname + " (Press any key to continue)\n") + webbrowser.open('https://github.com/new') + input("Repo created? (Press any key to continue)\n") + + url='https://github.com/' + self.git + "/" + cname + print(url) + while(requests.get(url).status_code != 200): + print(requests.get(url)) + r = input("Repo not created yet. (Press any key to continue after repo created, or 'N' to exit)\n") + if(r=="N" or r=="No"): + return + cdir = os.getcwd() + os.chdir(self.username) + self.command('git clone ' + url) + os.chdir(cdir) + + #make class directory + #make default files for each class + for filename in c['default_file']: + f=open(cpath+"/"+filename['name'], "w") + f.close() + + #make assignments directory + for a in c['assignments']: + path = cpath + "/" + a['name'] + if(os.path.exists(path)): + print(path + " already exists...") + else: + os.mkdir(path) + f=open(path + "/instructions.txt", "w") + f.close() + + #push to remote repo + os.chdir(cpath) + print(cpath) + self.command('git add .') + self.command('git commit -m Hello_Class') + self.command('git push -u origin master') + + def checkGit(self, ass): + for a in ass: + if a =='.git': + return True + return False + + def compareDB(self, fdict, url): + URL = url + r = requests.get(url = URL, auth=('raffukhondaker','hackgroup1')) + data = r.json()['results'] + allfiles = data + # far = [] + # for f in allfiles: + # far.append(f['path']) + + for f in fdict: + URL = url + in_db = False + pid=None + for dbf in allfiles: + if(dbf['path'] == f['path']): + in_db=True + break + if(in_db==False): + r = requests.post(url = URL, data=f , auth=('raffukhondaker','hackgroup1')) + print("POST: (" + URL + "): " + f['path']) + f['url']=r.json()['url'] + print(r.status_code) + else: + URL = URL + str(dbf['id']) + "/" + r = requests.put(url = URL, data=f , auth=('raffukhondaker','hackgroup1')) + print(r.status_code) + print("UPDATED: (" + URL + "): " + f['path']) + f['url']=r.json()['url'] + f['id']=dbf['id'] + print(f) + + return fdict + + #update API and Github, all assignments / classes + def update(self): + #lists all classes + classes = os.listdir(self.username) + + #checks all class directories first + for c in classes: + path = self.username + "/" + c + if(self.checkClass(path) == False): + return + cdict = [] + for c in classes: + path = self.username + "/" + c + #lists all assignments and default files + ass = os.listdir(path) + #if no .git, directory not synced to git or API + if (self.checkGit(ass)==False): + self.addClass(path) + else: + #push to git + loc = os.getcwd() + os.chdir(path) + self.command('git add .') + self.command('git commit -m "Update"') + self.command('git push -u origin master') + os.chdir(loc) + ass.remove('.git') + + loc = os.getcwd() + os.chdir(path) + repo = self.command('git config --get remote.origin.url') + os.chdir(loc) + + #assignments + adict = [] + #default files for classes + fdict=[] + #default files for assignments + afdict=[] + for a in ass: + aname=a + path = self.username + "/" + c + "/" + a + #need to add option + due_date= '2020-06-07T07:46:30.537197Z', + #check for default file + if(os.path.isfile(path)): + fdict.append({ + 'name':a, + 'path':path + }) + elif(os.path.isdir(path)): + for af in os.listdir(path): + path = path+ "/" + af + if(os.path.isfile(path)): + afdict.append({ + 'name':af, + 'path':path + }) + path = self.username + "/" + c + "/" + a + adict.append({ + 'name':aname, + 'due_date': due_date[0], + 'path':path, + 'files':afdict + }) + + fdict=self.compareDB(fdict, "http://127.0.0.1:8000/files/") + afdict=self.compareDB(afdict,"http://127.0.0.1:8000/files/") + + adict=self.compareDB(adict, 'http://127.0.0.1:8000/assignments/') + + path = self.username + "/" + c + cdict.append({ + 'name':c, + 'repo': repo, + 'path':path, + 'assignments':adict, + 'default_file':fdict, + }) + cdict=self.compareDB(cdict,'http://127.0.0.1:8000/classes/') + + mdict= { + 'first_name':self.first_name, + 'last_name':self.last_name, + 'git':self.git, + 'ion_user':self.username, + 'classes':cdict, + } + + data = json.dumps(mdict) + # r = requests.put(url = 'http://127.0.0.1:8000/teachers/eharris1/', data=data, headers={'Content-type': 'application/json'} , auth=('raffukhondaker','hackgroup1')) + # print(print(r.json())) + + + #class name format: <course-name>_<ion_user> + + #turn existing directory into class, Pre-condition: directory exists + #relative path to class: 2022rkhondak/Math4 + def checkClass(self,path): + cname = path.split("/") + cname = cname[len(cname)-1] + if(("_" + self.username) in cname) == False: + print("Incorrect class name: Must be in the format: <course-name>_<ion_user>") + return False + dirs = os.listdir(path) + #checks if there is a file (not within Assignments) in class, need at least 1 + deffile = False + #checks if there is a file in an Assignment, need at least 1 (default True in case no assignments) + as_file = True + as_bad = "" + + for d in dirs: + if(os.path.isfile(d)): + deffile=True + if(os.path.isdir(d)) and d != '.git': + #checks if there is a file in an Assignment, need at least 1 + as_file = False + asdir = os.listdir(d) + for a in asdir: + if(os.path.isfile(a)): + as_file=True + if(as_file==False): + as_bad = a + break + if(as_file==False): + print("Assignment '" + as_bad + "' does not have a default file!") + return False + + if(deffile): + print("Need a default file in the " + path + " Directory!") + return False + return True + + #adds class to git, not API + def addClasstoGit(self, path): + cname = path.split("/") + cname = cname[len(cname)-1] + #push to remote repo + if(self.checkClass(path)): + input("Make new Git Repo with name: " + cname + " (Press any key to continue)\n") + webbrowser.open('https://github.com/new') + input("Repo created? (Press any key to continue)\n") + + url='https://github.com/' + self.git + "/" + cname + print(url) + while(requests.get(url).status_code != 200): + print(requests.get(url)) + r = input("Repo not created yet. (Press any key to continue after repo created, or 'N' to exit)\n") + if(r=="N" or r=="No"): + return None + cdir = os.getcwd() + os.chdir(path) + self.command('git init') + self.command('git add .') + self.command('git commit -m Hello_Class') + self.command('git remote add origin ' + url + '.git') + self.command('git push -u origin master') + os.chdir(cdir) + + + #make a new class from scratch + def makeClass(self, subject): + cname = subject + "_" + self.username + os.chdir(self.username) + #check if class exists + if(os.path.exists(cname)): + print("Already synced to: " + str(self.username)) + return + else: + os.mkdir(cname) + f=open(cname + "/README.md", "w") + f.close() + #push to remote repo + os.chdir(cname) + command(self, 'git init') + command(self, 'git add .') + command(self, 'git commit -m "Hello Class!"') + #git remote add origin git@github.com:alexpchin/<reponame>.git + command(self, 'git remote add origin git@github.com:'+ self.git + "/" + cname + ".git") + command(self, 'git push -u origin master') + + cinfo=[ + { + "name":cname, + "assignments":[], + "repo": "https://github.com:" + self.git + "/" + cname + ".git", + "default_file": [ + { + "name":"README.md" + } + ] + } + ] + + #update rest API + self.classes.append(cinfo) + update(self) + data = { + "url": self.url, + "first_name": self.first_name, + "last_name": self.first_name, + "classes": self.classes, + "git": self.git, + "ion_user": self.username + }, + r = requests.put(url = self.url, data= data, headers={'Content-type': 'application/json'} ,auth=('raffukhondaker','hackgroup1')) + +#make student repo by student id + def addStudent(self,stid): + print(stid) + + def comment(self): + print("heheheh") + +# data = getData("eharris1") +data={'url': 'http://127.0.0.1:8000/teachers/eharris1/', 'first_name': 'Errin', 'last_name': 'Harris', 'git': 'therealraffi', 'ion_user': 'eharris1', 'classes': [{'url': 'http://127.0.0.1:8000/classes/1/', 'name': 'Math5_eharris1', 'repo': 'http://127.0.0.1:8000/assignments/3/', 'assignments': [{'name': 'Week1_HW', 'due_date': '2020-06-07T07:46:30.537197Z', 'url': 'http://127.0.0.1:8000/assignments/1/', 'files': [{'name': 'instructions.txt'}]}, {'name': 'Week2_HW', 'due_date': '2020-06-07T07:46:30.548596Z', 'url': 'http://127.0.0.1:8000/assignments/2/', 'files': [{'name': 'instructions.txt'}]}], 'default_file': [{'name': 'instructions.txt'}]}]} +t = Teacher(data) +t.update() + + diff --git a/CLI/t-git.py b/CLI/t-git.py new file mode 100644 index 0000000..9c2f4e9 --- /dev/null +++ b/CLI/t-git.py @@ -0,0 +1,366 @@ +import subprocess +import os +import requests +import webbrowser +import pprint +import json +import shutil +import time +import pyperclip + +#git clone student directory ==> <student-id>/classes/assignments + +#get teacher info from api +def getTeacher(ion_user): + URL = "http://127.0.0.1:8000/teachers/" + ion_user + "/" + r = requests.get(url = URL, auth=('raffukhondaker','hackgroup1')) + if(r.status_code == 200): + data = r.json() + return data + elif(r.status_code == 404): + return None + print("Make new account!") + elif(r.status_code == 403): + return None + print("Invalid username/password") + else: + return None + print(r.status_code) + +def getDB(url): + r = requests.get(url = url, auth=('raffukhondaker','hackgroup1')) + print("GET:" + str(r.status_code)) + return(r.json()) + +def postDB(data, url): + r = requests.post(url = url, data=data, auth=('raffukhondaker','hackgroup1')) + print("POST:" + str(r.status_code)) + return(r.json()) + +def putDB(data, url): + r = requests.put(url = url, data=data, auth=('raffukhondaker','hackgroup1')) + print("PUT:" + str(r.status_code)) + return(r.json()) + +def delDB(url): + r = requests.delete(url = url, auth=('raffukhondaker','hackgroup1')) + print("DELETE:" + str(r.status_code)) + return None + +def command(command): + ar = [] + command = command.split(" ") + for c in command: + ar.append(c) + process = subprocess.Popen(ar, stdout=subprocess.PIPE,stderr=subprocess.PIPE) + p=process.poll() + output = process.communicate()[1] + #print(output.decode('utf-8')) + +#public methods: deleteClass, makeClass, update +class Teacher: + def __init__(self, data): + # teacher info already stored in API + # intitialze fields after GET request + self.first_name=data['first_name'] + self.last_name=data['last_name'] + self.git=data['git'] + self.username=data['ion_user'] + self.url= "http://127.0.0.1:8000/teachers/" + self.username + "/" + #classes in id form (Example: 4,5) + + cid=data['classes'].split(",") + try: + cid.remove('') + except: + pass + try: + cid.remove("") + except: + pass + classes=[] + for c in cid: + url = "http://127.0.0.1:8000/classes/" + str(c) + "/" + classes.append(getDB(url)) + + self.classes = classes + self.sclass=str(data['classes']) + if(os.path.isdir(self.username)): + print("Synced to " + self.username) + else: + os.mkdir(self.username) + + #update API and Github, all assignments / classes + def update(self): + #lists all classes + ignore=['.git','.DS_Store'] + classes = os.listdir(self.username) + for i in ignore: + try: + classes.remove(i) + except: + pass + #list of classes that have been deleted (not with deleteClass) + extra = [] + for c in self.classes: + extra.append(c) + for i in range(len(extra)): + e = extra[i]['path'] + extra[i] = e + print("Extra: "+str(extra)) + print("Local:" + str(classes)) + #checks all class directories first + for c in classes: + path = self.username + "/" + c + if(self.checkClass(path) == False): + return + extra.remove(path) + print("Current classes: " + path) + + for e in extra: + self.deleteClass(e) + + for i in range(len(classes)): + c = classes[i] + path = self.username + "/" + c + #lists all assignments and default files + #if no .git, directory not synced to git or API + if (self.checkInDB(path)==False): + self.addClass(path) + else: + #push to git + loc = os.getcwd() + os.chdir(path) + command('git fetch origin') + command('git pull origin master') + command('git add .') + command('git commit -m "Update"') + command('git push -u origin master') + os.chdir(loc) + + #class name format: <course-name>_<ion_user> + + #turn existing directory into class, Pre-condition: directory exists + #relative path to class: 2022rkhondak/Math4 + def checkClass(self,path): + cname = path.split("/") + cname = cname[len(cname)-1] + if(os.path.isfile(path)): + print(path + " must be in a Class directory.") + return False + if(("_" + self.username) in cname) == False: + print("Incorrect class name: Must be in the format: " + self.username+ "/<course-name>_<ion_user>, not " + path) + return False + dirs = os.listdir(path) + #checks if there is a file (not within Assignments) in class, need at least 1 + deffile = False + #checks if there is a file in an Assignment, need at least 1 (default True in case no assignments) + as_file = True + as_bad = "" + + for d in dirs: + if(os.path.isfile(d)): + deffile=True + else: + #checks if there is a file in an Assignment, need at least 1 + as_file = False + asdir = os.listdir(path + "/" + d) + for a in asdir: + if(os.path.isfile(path + "/" + d + "/" +a)): + as_file=True + if(as_file==False): + as_bad = d + break + if(as_file==False): + print("Assignment '" + as_bad + "' does not have a default file!") + return False + + if(deffile==False): + print("Need a default file in the " + path + " Directory!") + return False + return True + + def checkInDB(self, path): + n = path.split("/") + n = n[len(n)-1] + for c in self.classes: + if(n == c['name']): + return True + return False + + #adds class to git, not API + #Assuming valid class name + def addClasstoGit(self, path): + cname = path.split("/") + cname = cname[len(cname)-1] + #push to remote repo + url='https://github.com/' + self.git + "/" + cname + if(requests.get(url).status_code != 200): + input("Make new Git Repo with name: " + cname + " (Press any key to continue)\n") + try: + pyperclip.copy(cname) + print(cname + " copied to clipboard.") + except: + pass + time.sleep(2) + webbrowser.open('https://github.com/new') + input("Repo created? (Press any key to continue)\n") + + print(url) + while(requests.get(url).status_code != 200): + r = input("Repo not created yet. (Press any key to continue after repo created, or 'N' to exit)\n") + if(r=="N" or r=="No"): + return None + cdir = os.getcwd() + os.chdir(path) + command('git init') + command('git add .') + command('git commit -m Hello_Class') + command('git remote add origin ' + url + '.git') + command('git push -u origin master') + else: + cdir = os.getcwd() + os.chdir(path) + print("Repo already exists. Cloning instead.") + command('git clone') + command('git fetch origin') + command('git pull') + command('git add .') + command('git commit -m Hello_Class') + command('git push -u origin master') + os.chdir(cdir) + print(cdir) + data={ + 'name':cname, + 'repo':url, + 'path':path, + 'teacher':self.username + } + return data + + #make class from existing directory, add to git and api + def addClass(self, path): + if (self.checkClass(path)): + data = self.addClasstoGit(path) + #make class instance in db + data = postDB(data, 'http://127.0.0.1:8000/classes/') + #add to instance + #upate self.classes + self.classes.append(data) + if(len(self.sclass)==0): + self.sclass = data['id'] + else: + self.sclass = self.sclass + "," + str(data['id']) + + #update teacher instance in db, classes field + data={ + 'first_name':self.first_name, + 'last_name':self.last_name, + 'git':self.git, + 'ion_user':self.username, + 'url':self.url, + 'classes':self.sclass + } + putDB(data, self.url) + + return data + + + #make a new class from scratch + #subject: string, assignments: list + #class name must be: <subject>_<ion_user> + def makeClass(self, cname, assignments): + #check if class exists + path = self.username + "/" + cname + if(os.path.exists(path)): + print("Class already exists: " + cname) + return + else: + if((("_" + self.username) in cname) == False): + print("class name must be: "+ cname + "_" + self.username) + return + cdir = os.getcwd() + os.mkdir(path) + f=open(path + "/README.md", "w") + f.close() + #push to remote repo + os.chdir(path) + for a in assignments: + os.mkdir(a) + f=open(a + "/instructions.txt", "w") + f.close() + os.chdir(cdir) + + data = self.addClass(path) + return data + + def deleteClass(self, path): + if(os.path.exists(path) == False): + print(path + " does not exist locally.") + resp = input("Do you want to delete " + path + " from the SkoolOS system? (y/N) ") + if(resp != 'y'): + return + + cname = path.split("/") + cname = cname[len(cname)-1] + cid = None + repo = '' + for c in self.classes: + if cname == c['name']: + cid = str(c['id']) + repo = c['repo'] + + #remove from api + for i in range(len(self.classes)): + if(self.classes[i]['id'] == int(cid)): + print("DELETE: " + self.classes[i]['name']) + del self.classes[i] + s="" + #recreate sclass field, using ids + for c in self.classes: + s = s + str(c['id']) + "," + print(s) + s = s[:-1] + print(s) + data={ + 'first_name':self.first_name, + 'last_name':self.last_name, + 'git':self.git, + 'ion_user':self.username, + 'url':self.url, + 'classes':s + } + print(putDB(data, self.url)) + delDB("http://127.0.0.1:8000/classes/" + cid + "/") + break + + #remove locally + try: + shutil.rmtree(path) + except: + pass + + #remove from git + input("Delete repository: " + cname + ". Scroll to the bottom of the page and press 'Delete this repository' (Press any key to continue) ") + print(repo) + time.sleep(2) + webbrowser.open(repo + "/settings") + input("Repo deleted? (Press any key to continue) ") + + print(repo) + while(requests.get(repo).status_code == 200): + r = input("Repo still no deleted yet. (Press any key to continue after repo deleted, or 'N' to exit)\n") + if(r=="N" or r=="No" or r=='n'): + return None + +#make student repo by student id + def addStudent(self,stid): + print(stid) + + def comment(self): + print("heheheh") + +data = getTeacher("eharris1") +t = Teacher(data) +t.makeClass('English11_eharris1', ["Essay1"]) +t.update() diff --git a/CLI/test.py b/CLI/test.py new file mode 100644 index 0000000..11389ac --- /dev/null +++ b/CLI/test.py @@ -0,0 +1,16 @@ +import subprocess +import os +import time + +def command(command): + ar = [] + command = command.split(" ") + for c in command: + ar.append(c) + process = subprocess.Popen(ar, stdout=subprocess.PIPE,stderr=subprocess.PIPE) + p=process.poll() + output = process.communicate()[0] + print(output) + +command('echo hello') +command('python runtest.py') diff --git a/CLI/text.py b/CLI/text.py new file mode 100644 index 0000000..60b8cca --- /dev/null +++ b/CLI/text.py @@ -0,0 +1,2 @@ +import textract +text = textract.process('test.py') \ No newline at end of file diff --git a/CLI/users.json b/CLI/users.json deleted file mode 100644 index bf1713e..0000000 --- a/CLI/users.json +++ /dev/null @@ -1 +0,0 @@ -[{"first_name": "Raffu", "last_name": "Khondaker", "password": "password", "webmail": "2022rkhondak@tjhsst.edu", "classes": {"Math": ["week1_hw", "week2_hw", "week3_hw", "unit3_quiz"], "English": ["journal1", "journal2", "journal3"]}}] \ No newline at end of file diff --git a/Website/api/__init__.py b/Website/api/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Website/api/admin.py b/Website/api/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/Website/api/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/Website/api/apps.py b/Website/api/apps.py new file mode 100644 index 0000000..d87006d --- /dev/null +++ b/Website/api/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class ApiConfig(AppConfig): + name = 'api' diff --git a/Website/api/auth.py b/Website/api/auth.py new file mode 100644 index 0000000..e69de29 diff --git a/Website/api/maker.py b/Website/api/maker.py new file mode 100644 index 0000000..dd31bb6 --- /dev/null +++ b/Website/api/maker.py @@ -0,0 +1,128 @@ +from datetime import datetime + +f1 = DefFiles( + name="instructions.txt" +) +f1.save() +f2 = DefFiles( + name="instructions.txt" +) +f2.save() +f3 = DefFiles( + name="sample.txt" +) +f3.save() +f4 = DefFiles( + name="rubric.txt" +) +f4.save() + +a1 = Assignment.objects.get(pk=1) +a1.files.add(f1) +a1.save() +a2 = Assignment.objects.get(pk=2) +a2.files.add(f2) +a2.save() +a3 = Assignment.objects.get(pk=3) +a3.files.add(f3) +a3.files.add(f4) +a3.save() + +#################################### + +from api.models import Assignment, Student, Classes, Teacher, DefFiles +from datetime import datetime + +f1 = DefFiles( + name="instructions.txt" +) +f1.save() +f2 = DefFiles( + name="instructions.txt" +) +f2.save() +f3 = DefFiles( + name="sample.txt" +) +f3.save() +f4 = DefFiles( + name="rubric.txt" +) +f4.save() + +A1 = Assignment( + name='Week1_HW', + due_date=datetime.now(), +) +A1.save() +A1.files.add(f1) +A1.save() + +A2 = Assignment( + name='Week2_HW', + due_date=datetime.now(), + +) +A2.save() +A2.files.add(f2) +A2.save() + +A3 = Assignment( + name='Journal1', + due_date=datetime.now(), +) +A3.save() +A3.files.add(f3) +A3.files.add(f4) +A3.save() + +#classes +math = Classes( + name='Math5', + +) +math.save() +math.assignments.add(A1) +math.assignments.add(A2) +math.save() + +english = Classes( + name='English', +) +english.save() +english.assignments.add(A3) +english.save() + +#students +raffu = Student( + first_name = "Raffu", + last_name = "Khondaker", + student_id = 1579460, + ion_user="2022rkhondak", + webmail = "2022rkhondak@tjhsst.edu", + grade = 10, + repo="https://github.com/therealraffi/2022rkhondak.git", +) +raffu.save() +raffu.classes.add(math) +raffu.classes.add(english) +raffu.save() + +#teachers +ng = Teacher( + first_name = "Errin", + last_name = "Harris", + ion_user="eharris1" +) +ng.save() +ng.classes.add(math) +ng.save() + +chao = Teacher( + first_name = "Abagail", + last_name = "Bailey", + ion_user="AKBailey" +) +chao.save() +chao.classes.add(english) +chao.save() diff --git a/Website/api/migrations/0001_initial.py b/Website/api/migrations/0001_initial.py new file mode 100644 index 0000000..11b47e4 --- /dev/null +++ b/Website/api/migrations/0001_initial.py @@ -0,0 +1,75 @@ +# Generated by Django 3.0.7 on 2020-06-09 02:03 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Assignment', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=100)), + ('due_date', models.DateTimeField()), + ('files', models.CharField(max_length=100)), + ('path', models.CharField(max_length=100)), + ('classes', models.CharField(max_length=100)), + ('teacher', models.CharField(max_length=100)), + ], + ), + migrations.CreateModel( + name='Classes', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=100)), + ('repo', models.URLField(default='')), + ('path', models.CharField(default='', max_length=100)), + ('teacher', models.CharField(default='', max_length=100)), + ('assignments', models.CharField(default='', max_length=100)), + ('default_file', models.CharField(default='', max_length=100)), + ], + ), + migrations.CreateModel( + name='DefFiles', + 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)), + ('assignment', models.CharField(default='', max_length=100)), + ('classes', models.CharField(max_length=100)), + ('teacher', models.CharField(max_length=100)), + ], + ), + migrations.CreateModel( + name='Teacher', + fields=[ + ('created', models.DateTimeField(auto_now_add=True)), + ('first_name', models.CharField(max_length=100)), + ('last_name', models.CharField(max_length=100)), + ('classes', models.CharField(default='', max_length=100)), + ('ion_user', models.CharField(max_length=100, primary_key=True, serialize=False)), + ('git', models.CharField(max_length=100)), + ], + ), + migrations.CreateModel( + name='Student', + fields=[ + ('created', models.DateTimeField(auto_now_add=True)), + ('first_name', models.CharField(max_length=100)), + ('last_name', models.CharField(max_length=100)), + ('student_id', models.IntegerField()), + ('ion_user', models.CharField(max_length=100, primary_key=True, serialize=False)), + ('webmail', models.EmailField(blank=True, max_length=254)), + ('grade', models.IntegerField()), + ('git', models.CharField(max_length=100)), + ('repo', models.URLField(default='')), + ('classes', models.ManyToManyField(default='', to='api.Classes')), + ], + ), + ] diff --git a/Website/api/migrations/0002_auto_20200609_0358.py b/Website/api/migrations/0002_auto_20200609_0358.py new file mode 100644 index 0000000..bb2af02 --- /dev/null +++ b/Website/api/migrations/0002_auto_20200609_0358.py @@ -0,0 +1,18 @@ +# Generated by Django 3.0.7 on 2020-06-09 03:58 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='teacher', + name='classes', + field=models.CharField(blank=True, default='', max_length=100), + ), + ] diff --git a/Website/api/migrations/__init__.py b/Website/api/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Website/api/models.py b/Website/api/models.py new file mode 100644 index 0000000..a8770e4 --- /dev/null +++ b/Website/api/models.py @@ -0,0 +1,59 @@ +from django.db import models + +class DefFiles(models.Model): + name=models.CharField(max_length=100) + path=models.CharField(max_length=100) + assignment=models.CharField(max_length=100, default="") + classes=models.CharField(max_length=100) + teacher=models.CharField(max_length=100) + +class Assignment(models.Model): + name=models.CharField(max_length=100) + due_date=models.DateTimeField() + # files = models.ManyToManyField(DefFiles) + files=models.CharField(max_length=100) + path=models.CharField(max_length=100) + classes=models.CharField(max_length=100) + teacher=models.CharField(max_length=100) + def __str__(self): + return '%s' % (self.name) + +class Classes(models.Model): + name = models.CharField(max_length=100) + repo=models.URLField(default="") + path=models.CharField(max_length=100, default="") + teacher=models.CharField(max_length=100, default="") + assignments=models.CharField(max_length=100, default="") + default_file=models.CharField(max_length=100, default="") + + # assignments = models.ManyToManyField(Assignment, default="") + # default_file = models.ManyToManyField(DefFiles) + def save(self, *args, **kwargs): + return super(Classes, self).save(*args, **kwargs) + +class Teacher(models.Model): + created = models.DateTimeField(auto_now_add=True) + first_name = models.CharField(max_length=100) + last_name = models.CharField(max_length=100) + # classes = models.ManyToManyField(Classes, default="") + classes=models.CharField(max_length=100, default="", blank=True) + ion_user=models.CharField(primary_key=True, max_length=100) + git=models.CharField(max_length=100) + +class Student(models.Model): + created = models.DateTimeField(auto_now_add=True) + first_name = models.CharField(max_length=100) + last_name = models.CharField(max_length=100) + student_id = models.IntegerField() + ion_user=models.CharField(primary_key=True, max_length=100) + webmail = models.EmailField(blank=True) + grade = models.IntegerField() + git=models.CharField(max_length=100) + classes = models.ManyToManyField(Classes, default="") + repo = models.URLField(default="") + + def save(self, *args, **kwargs): + return super(Student, self).save(*args, **kwargs) + + + diff --git a/Website/api/serializers.py b/Website/api/serializers.py new file mode 100644 index 0000000..ca13238 --- /dev/null +++ b/Website/api/serializers.py @@ -0,0 +1,37 @@ +from django.contrib.auth.models import User, Group +from .models import Student, Teacher, Classes, Assignment, DefFiles +from rest_framework import serializers, permissions + +class DefFilesSerializer(serializers.HyperlinkedModelSerializer): + permissions_classes = [permissions.IsAuthenticatedOrReadOnly] + class Meta: + model = DefFiles + fields = ['name', 'path','assignment','classes', "teacher",'url', 'id'] + +class AssignmentSerializer(serializers.HyperlinkedModelSerializer): + permissions_classes = [permissions.IsAuthenticatedOrReadOnly] + # files = DefFilesSerializer(many=True, read_only=True,allow_null=True) + class Meta: + model = Assignment + fields = ['name', 'due_date', 'url', 'path' , "classes","teacher",'files', 'id'] + +class ClassesSerializer(serializers.HyperlinkedModelSerializer): + # assignments = AssignmentSerializer(many=True, read_only=True,allow_null=True) + # default_file=DefFilesSerializer(many=True, read_only=True,allow_null=True) + class Meta: + model = Classes + fields = ['url', 'name', 'repo','path', "teacher",'assignments',"default_file",'id'] + +class StudentSerializer(serializers.HyperlinkedModelSerializer): + classes = ClassesSerializer(many=True, read_only=True,allow_null=True) + class Meta: + model = Student + fields = ['url', 'first_name', 'last_name', 'grade','webmail','student_id', 'git','repo','ion_user','classes'] + +class TeacherSerializer(serializers.ModelSerializer): + # classes = ClassesSerializer(many=True, read_only=True,allow_null=True) + class Meta: + model = Teacher + fields = ['url', 'first_name', 'last_name','git','ion_user','classes'] + + diff --git a/Website/api/tests.py b/Website/api/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/Website/api/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/Website/api/urls.py b/Website/api/urls.py new file mode 100644 index 0000000..e69de29 diff --git a/Website/api/views-back.py b/Website/api/views-back.py new file mode 100644 index 0000000..5c936f9 --- /dev/null +++ b/Website/api/views-back.py @@ -0,0 +1,131 @@ +# class StudentList(APIView): +# """ +# List all snippets, or create a new snippet. +# """ +# def get(self, request, format=None): +# snippets = Student.objects.all() +# serializer = StudentSerializer(snippets, many=True) +# return response.Response(serializer.data) + +# def post(self, request, format=None): +# serializer = StudentSerializer(data=request.data) +# if serializer.is_valid(): +# serializer.save() +# return response.Response(serializer.data, status=status.HTTP_201_CREATED) +# return response.Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + +# class StudentDetail(APIView): +# """ +# Retrieve, update or delete a snippet instance. +# """ +# def get_object(self, pk): +# try: +# return Student.objects.get(pk=pk) +# except Student.DoesNotExist: +# raise Http404 + +# def get(self, request, pk, format=None): +# snippet = self.get_object(pk) +# serializer = StudentSerializer(snippet) +# return response.Response(serializer.data) + +# def put(self, request, pk, format=None): +# snippet = self.get_object(pk) +# serializer = StudentSerializer(snippet, data=request.data) +# if serializer.is_valid(): +# serializer.save() +# return response.Response(serializer.data) +# return response.Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + +# def delete(self, request, pk, format=None): +# snippet = self.get_object(pk) +# snippet.delete() +# return response.Response(status=status.HTTP_204_NO_CONTENT) + +# class TeacherList(APIView): +# """ +# List all snippets, or create a new snippet. +# """ +# def get(self, request, format=None): +# snippets = Teacher.objects.all() +# serializer = TeacherSerializer(snippets, many=True) +# return response.Response(serializer.data) + +# def post(self, request, format=None): +# serializer = TeacherSerializer(data=request.data) +# if serializer.is_valid(): +# serializer.save() +# return response.Response(serializer.data, status=status.HTTP_201_CREATED) +# return response.Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + +# class TeacherDetail(APIView): +# """ +# Retrieve, update or delete a snippet instance. +# """ +# def get_object(self, pk): +# try: +# return Teacher.objects.get(pk=pk) +# except Teacher.DoesNotExist: +# raise Http404 + +# def get(self, request, pk, format=None): +# snippet = self.get_object(pk) +# serializer = TeacherSerializer(snippet) +# return response.Response(serializer.data) + +# def put(self, request, pk, format=None): +# snippet = self.get_object(pk) +# serializer = TeacherSerializer(snippet, data=request.data) +# if serializer.is_valid(): +# serializer.save() +# return response.Response(serializer.data) +# return response.Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + +# def delete(self, request, pk, format=None): +# snippet = self.get_object(pk) +# snippet.delete() +# return response.Response(status=status.HTTP_204_NO_CONTENT) + +# class ClassesList(APIView): +# """ +# List all snippets, or create a new snippet. +# """ +# def get(self, request, format=None): +# snippets = Classes.objects.all() +# serializer = ClassesSerializer(snippets, many=True) +# return response.Response(serializer.data) + +# def post(self, request, format=None): +# serializer = ClassesSerializer(data=request.data) +# if serializer.is_valid(): +# serializer.save() +# return response.Response(serializer.data, status=status.HTTP_201_CREATED) +# return response.Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + +# class ClassesDetail(APIView): +# """ +# Retrieve, update or delete a snippet instance. +# """ +# def get_object(self, pk): +# try: +# return Classes.objects.get(pk=pk) +# except Classes.DoesNotExist: +# raise Http404 + +# def get(self, request, pk, format=None): +# snippet = self.get_object(pk) +# serializer = ClassesSerializer(snippet) +# return response.Response(serializer.data) + +# def put(self, request, pk, format=None): +# snippet = self.get_object(pk) +# serializer = ClassesSerializer(snippet, data=request.data) +# if serializer.is_valid(): +# serializer.save() +# return response.Response(serializer.data) +# return response.Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + +# def delete(self, request, pk, format=None): +# snippet = self.get_object(pk) +# snippet.delete() +# return response.Response(status=status.HTTP_204_NO_CONTENT) \ No newline at end of file diff --git a/Website/api/views.py b/Website/api/views.py new file mode 100644 index 0000000..439c30b --- /dev/null +++ b/Website/api/views.py @@ -0,0 +1,48 @@ +from .models import Student, Teacher, Classes, Assignment, DefFiles +from .serializers import StudentSerializer, TeacherSerializer, ClassesSerializer, AssignmentSerializer, DefFilesSerializer +from rest_framework import generics, viewsets, permissions, response, status +from django.http import Http404 +from rest_framework.views import APIView + +class StudentViewSet(viewsets.ModelViewSet): + """ + API endpoint that allows users to be viewed or edited. + """ + queryset = Student.objects.all() + serializer_class = StudentSerializer + permissions_classes = [permissions.IsAuthenticatedOrReadOnly] + + +class TeacherViewSet(viewsets.ModelViewSet): + """ + API endpoint that allows users to be viewed or edited. + """ + queryset = Teacher.objects.all() + serializer_class = TeacherSerializer + permissions_classes = [permissions.IsAuthenticatedOrReadOnly] + + +class ClassesViewSet(viewsets.ModelViewSet): + """ + API endpoint that allows users to be viewed or edited. + """ + queryset = Classes.objects.all() + serializer_class = ClassesSerializer + permissions_classes = [permissions.IsAuthenticatedOrReadOnly] + + +class AssignmentViewSet(viewsets.ModelViewSet): + """ + API endpoint that allows users to be viewed or edited. + """ + queryset = Assignment.objects.all() + serializer_class = AssignmentSerializer + permissions_classes = [permissions.IsAuthenticatedOrReadOnly] + +class DefFilesViewSet(viewsets.ModelViewSet): + """ + API endpoint that allows users to be viewed or edited. + """ + queryset = DefFiles.objects.all() + serializer_class = DefFilesSerializer + permissions_classes = [permissions.IsAuthenticatedOrReadOnly] diff --git a/Website/manage.py b/Website/manage.py new file mode 100755 index 0000000..063e014 --- /dev/null +++ b/Website/manage.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python +"""Django's command-line utility for administrative tasks.""" +import os +import sys + + +def main(): + os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'skoolsite.settings') + try: + from django.core.management import execute_from_command_line + except ImportError as exc: + raise ImportError( + "Couldn't import Django. Are you sure it's installed and " + "available on your PYTHONPATH environment variable? Did you " + "forget to activate a virtual environment?" + ) from exc + execute_from_command_line(sys.argv) + + +if __name__ == '__main__': + main() diff --git a/Website/skoolsite/__init__.py b/Website/skoolsite/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Website/skoolsite/asgi.py b/Website/skoolsite/asgi.py new file mode 100644 index 0000000..805af95 --- /dev/null +++ b/Website/skoolsite/asgi.py @@ -0,0 +1,16 @@ +""" +ASGI config for skoolsite project. + +It exposes the ASGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/3.0/howto/deployment/asgi/ +""" + +import os + +from django.core.asgi import get_asgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'skoolsite.settings') + +application = get_asgi_application() diff --git a/Website/skoolsite/settings.py b/Website/skoolsite/settings.py new file mode 100644 index 0000000..b5bda98 --- /dev/null +++ b/Website/skoolsite/settings.py @@ -0,0 +1,131 @@ +""" +Django settings for skoolsite project. + +Generated by 'django-admin startproject' using Django 3.0.7. + +For more information on this file, see +https://docs.djangoproject.com/en/3.0/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/3.0/ref/settings/ +""" + +import os + +# Build paths inside the project like this: os.path.join(BASE_DIR, ...) +BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/3.0/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = '$v+-0wm%yys7r3&e0s&*tyh-vyc9v&twb_8yk6==290io9yq3(' + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +ALLOWED_HOSTS = [] + + +# Application definition + +INSTALLED_APPS = [ + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + 'rest_framework', + 'api', + +] + +REST_FRAMEWORK = { + 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', + 'PAGE_SIZE': 10, + 'DEFAULT_PERMISSION_CLASSES': [ + 'rest_framework.permissions.IsAuthenticated', + ] +} + +MIDDLEWARE = [ + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', +] + +ROOT_URLCONF = 'skoolsite.urls' + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] + +WSGI_APPLICATION = 'skoolsite.wsgi.application' + + +# Database +# https://docs.djangoproject.com/en/3.0/ref/settings/#databases + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), + } +} + + +# Password validation +# https://docs.djangoproject.com/en/3.0/ref/settings/#auth-password-validators + +AUTH_PASSWORD_VALIDATORS = [ + { + 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + }, +] + + +# Internationalization +# https://docs.djangoproject.com/en/3.0/topics/i18n/ + +LANGUAGE_CODE = 'en-us' + +TIME_ZONE = 'UTC' + +USE_I18N = True + +USE_L10N = True + +USE_TZ = True + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/3.0/howto/static-files/ + +STATIC_URL = '/static/' diff --git a/Website/skoolsite/urls.py b/Website/skoolsite/urls.py new file mode 100644 index 0000000..1c8f9f2 --- /dev/null +++ b/Website/skoolsite/urls.py @@ -0,0 +1,22 @@ +from django.urls import path +from rest_framework import routers +from api import views +from django.contrib import admin +from django.conf.urls import include + +router = routers.DefaultRouter() +router.register(r'students', views.StudentViewSet) +router.register(r'teachers', views.TeacherViewSet) +router.register(r'assignments', views.AssignmentViewSet) +router.register(r'classes', views.ClassesViewSet) +router.register(r'files', views.DefFilesViewSet) + + +# Wire up our API using automatic URL routing. +# Additionally, we include login URLs for the browsable API. +urlpatterns = [ + path('', include(router.urls)), + path('api-auth/', include('rest_framework.urls')), + path('admin/', admin.site.urls), + +] \ No newline at end of file diff --git a/Website/skoolsite/wsgi.py b/Website/skoolsite/wsgi.py new file mode 100644 index 0000000..c3b05f8 --- /dev/null +++ b/Website/skoolsite/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config for skoolsite project. + +It exposes the WSGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/3.0/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'skoolsite.settings') + +application = get_wsgi_application() diff --git a/Website/templates/base.html b/Website/templates/base.html new file mode 100644 index 0000000..664dcf4 --- /dev/null +++ b/Website/templates/base.html @@ -0,0 +1,3 @@ + +{% block page_content %} +{% endblock %} diff --git a/Website/templates/oauth2_provider/authorize.html b/Website/templates/oauth2_provider/authorize.html new file mode 100644 index 0000000..e69de29 diff --git a/Website/templates/oauth2_provider/logged_out.html b/Website/templates/oauth2_provider/logged_out.html new file mode 100644 index 0000000..52cdfa1 --- /dev/null +++ b/Website/templates/oauth2_provider/logged_out.html @@ -0,0 +1,6 @@ +{% extends "base.html" %} + +{% block content %} + <p>Logged out!</p> + <a href="{% url 'login'%}">Click here to login again.</a> +{% endblock %} \ No newline at end of file diff --git a/Website/templates/oauth2_provider/login.html b/Website/templates/oauth2_provider/login.html new file mode 100644 index 0000000..5018145 --- /dev/null +++ b/Website/templates/oauth2_provider/login.html @@ -0,0 +1,37 @@ +{% extends "base.html" %} + +{% block page_content %} + + {% if form.errors %} + <p>Your username and password didn't match. Please try again.</p> + {% endif %} + + {% if next %} + {% if user.is_authenticated %} + <p>Your account doesn't have access to this page. To proceed, + please login with an account that has access.</p> + {% else %} + <p>Please login to see this page.</p> + {% endif %} + {% endif %} + + <form method="post" action="{% url 'login' %}"> + {% csrf_token %} + <table> + <tr> + <td>{{ form.username.label_tag }}</td> + <td>{{ form.username }}</td> + </tr> + <tr> + <td>{{ form.password.label_tag }}</td> + <td>{{ form.password }}</td> + </tr> + </table> + <input type="submit" value="login" /> + <input type="hidden" name="next" value="{{ next }}" /> + </form> + + {# Assumes you setup the password_reset view in your URLconf #} + <p><a href="{% url 'password_reset' %}">Lost password?</a></p> + +{% endblock %} \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index a73f1cc..fbc7e54 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,21 @@ +asgiref==3.2.7 +certifi==2020.4.5.1 +chardet==3.0.4 +click==7.1.2 +Django==3.0.7 +django-cors-middleware==1.5.0 +django-oauth-toolkit==1.3.2 +djangorestframework==3.11.0 +idna==2.9 +oauthlib==3.1.0 prompt-toolkit==1.0.14 Pygments==2.6.1 PyInquirer==1.0.3 +pytz==2020.1 regex==2020.5.14 +requests==2.23.0 +selenium==3.141.0 six==1.15.0 +sqlparse==0.3.1 +urllib3==1.25.9 wcwidth==0.2.3 diff --git a/runtest.py b/runtest.py new file mode 100644 index 0000000..837cd82 --- /dev/null +++ b/runtest.py @@ -0,0 +1,2 @@ +for i in range(0, 100000): + print(i) \ No newline at end of file