Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feedback #1

Open
wants to merge 22 commits into
base: feedback
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
28 changes: 28 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
**/.DS_Store
**/*.cache
api/yourscope-api/*.suo
api/yourscope-api/*.user
api/yourscope-api/_ReSharper.*/
api/yourscope-api/*.sdf
**/bin/
**/obj/
api/yourscope-api/Debug/
api/yourscope-api/Release/
api/yourscope-api/*.opensdf
api/yourscope-api/*.tlog
api/yourscope-api/*.log
api/yourscope-api/TestResult.xml
api/yourscope-api/*.VisualState.xml
api/yourscope-api/Version.cs
api/yourscope-api/Version.h
api/yourscope-api/Version.cpp
api/yourscope-api/.vs/
yourscope/.angular/cache/
yourscope/.firebase/*.cache/
yourscope/node_modules/
yourscope/public/
api/yourscope-api/firebase-config.json
/api/yourscope-api/appsettings.json
api/yourscope-api/appsettings.json
.vs
.vscode
425 changes: 425 additions & 0 deletions CreateYourScopeDB.sql

Large diffs are not rendered by default.

95 changes: 95 additions & 0 deletions api/python-scripts/getUni.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import requests
from bs4 import BeautifulSoup
import mysql.connector
from unidecode import unidecode

USERNAME = "wouldn't you like to know"
PASSWORD = "weather boy"

uniIds = {}
allPrograms = set()

groups = ["a", "b", "c", "d-e", "f-g", "h", "i", "j-l", "m", "n-p", "q-s", "t-z"]

def getProgram(id):
r = requests.get(f"https://www.ontariouniversitiesinfo.ca/programs/{id}")

soup = BeautifulSoup(r.text, 'html.parser')

website = soup.find_all("a", {"title": "Opens a new website in a new window"})[1]["href"].replace("'", "")
name = soup.find_all("h1", {"class": "template-heading"})[0].contents[0].replace("'", "")

prereqsList = soup.find("div", {"id": "requirements"}).div
if prereqsList.ul:
prereqsList = prereqsList.ul.children
else:
prereqsList = prereqsList.p

prereqs = []
for i in prereqsList:
if i != "\n":
prereqs.append(i.text)

prereqs = ",".join(prereqs).replace("'", "")

table = soup.find_all("dd")
tableContents = list(i.contents[0] for i in table)
uniName = tableContents[0][:15]
uni = uniIds[uniName]
gradeRange = tableContents[3].replace("'", "")

if len(tableContents) >= 7 and type(tableContents[6]) == str:
language = tableContents[6].replace("'", "")
else:
language = "English"

return f"INSERT INTO `yourscope`.`UniPrograms` (`Name`, `Language`, `GradeRange`, `Prerequisites`, `Website`, `UniversityId`) VALUES ('{name}', '{language}', '{gradeRange}', '{prereqs}', '{website}', '{uni}')"

def getPrograms():
for group in groups:
r2 = requests.get(f"https://www.ontariouniversitiesinfo.ca/programs/search/?search=&group={group}")
soup = BeautifulSoup(r2.text, 'html.parser')
programs = soup.find_all("article", {"class": "result result-program"})
for i in programs:
allPrograms.add(i["data-program"])

print(str(len(allPrograms)) + " programs loaded")


def main():

#Get university info
r = requests.get("https://www.ontariouniversitiesinfo.ca/universities")
soup = BeautifulSoup(r.text, 'html.parser')
unis = soup.find_all("article", {"class": "university"})
for ind, i in enumerate(unis):
uniIds[i.h2.a.contents[0][:15]] = ind+74

#Get all program ids
getPrograms()

cnx = mysql.connector.connect(user=USERNAME, password=PASSWORD,
host='yourscope.c0a92ja1esk5.us-east-1.rds.amazonaws.com',
database='yourscope')
cursor = cnx.cursor()

#Loop through all program ids and insert into db
try:
for ind, i in enumerate(allPrograms):
program = getProgram(i)
cursor.execute(program)
if ind % 50 == 0:
percent = ((ind+1)/1410) * 100
print(f"{percent:.2f}% of programs inserted")
except Exception as e:
print(i)
raise e

cnx.commit()
cursor.close()
cnx.close()


if __name__ == "__main__":
main()
print("Done!")
57 changes: 57 additions & 0 deletions api/python-scripts/populateSchools.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import requests
import sys


'''
populateSchools.py [schoolId] [schoolName]

params:
schoolId - Id of school in database
schoolName - Name of school in myblueprint
'''

MYBLUEPRINT_URL = "https://api.myblueprint.ca/v1.0/Public/Courses/"
YOURSCOPE_API_URL = "http://localhost:5212/api/schools/v1/add-courses/"

CREDITS = {
0.0: 0,
0.5: 1,
1.0: 2,
2.0: 3,
4.0: 3
}

def main(args):
schoolId = args[1]
schoolName = args[2]

r = requests.get(MYBLUEPRINT_URL + schoolName)
r.raise_for_status()

data = r.json()

formattedData = []

for course in data:

formattedData.append({
"courseCode": course["courseCode"],
"name": course["name"],
"description": course["description"],
"discipline": course["discipline"]["name"],
"type": course["type"] if course["type"] else "",
"grade": course["grade"],
"credits": CREDITS[course["credits"]],
"prerequisites": ",".join(course["prerequisites"])
})

r = requests.post(YOURSCOPE_API_URL + schoolId, json=formattedData)
if r.status_code != 201:
print(r.text)

print("Done!")



if __name__ == "__main__":
main(sys.argv)
65 changes: 65 additions & 0 deletions api/yourscope-api/Authentication/YourScopeAuthHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
using FirebaseAdmin;
using FirebaseAdmin.Auth;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Extensions.Options;
using System.Security.Claims;
using System.Text.Encodings.Web;

namespace yourscope_api.authentication
{
public class YourScopeAuthHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
private readonly FirebaseApp firebaseApp;
public YourScopeAuthHandler(IOptionsMonitor<AuthenticationSchemeOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock, FirebaseApp firebaseApp) : base(options, logger, encoder, clock)
{
this.firebaseApp = firebaseApp;
}

protected async override Task<AuthenticateResult> HandleAuthenticateAsync()
{
if (!Context.Request.Headers.ContainsKey("Authorization"))
return AuthenticateResult.NoResult();

string? token = Context.Request.Headers["Authorization"];

if (string.IsNullOrEmpty(token) || !token.StartsWith("Bearer "))
return AuthenticateResult.Fail("Empty token or invalid format.");

token = token["Bearer ".Length..];

FirebaseToken fbToken;
try
{
fbToken = await FirebaseAuth.GetAuth(firebaseApp).VerifyIdTokenAsync(token);
}
catch(FirebaseAuthException ex)
{
return AuthenticateResult.Fail(ex);
}

return AuthenticateResult.Success(GenerateAuthTicket(fbToken.Claims));
}
private static AuthenticationTicket GenerateAuthTicket(IReadOnlyDictionary<string, object> claims)
{
List<Claim> claimsList = new()
{
new Claim("uid", claims["user_id"].ToString() ?? throw new ArgumentNullException("user_id", "Missing user_id in auth token.")),
new Claim("userID", claims["userID"].ToString() ?? throw new ArgumentNullException("userID", "Missing userID in auth token.")),
new Claim("email", claims["email"].ToString() ?? throw new ArgumentNullException("email", "Missing email in auth token."))
};
// Custom claims.
if (claims.ContainsKey("name"))
claimsList.Add(new Claim("name", claims["name"].ToString()!));
if (claims.ContainsKey("role"))
claimsList.Add(new Claim("role", claims["role"].ToString()!));
if (claims.ContainsKey("affiliationID"))
claimsList.Add(new Claim("affiliationID", claims["affiliationID"].ToString()!));

List<ClaimsIdentity> identities = new() {new ClaimsIdentity(claimsList, "YourScopeType") };
ClaimsPrincipal claimsPrincipal = new(identities);
return new AuthenticationTicket(claimsPrincipal, JwtBearerDefaults.AuthenticationScheme);

}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authorization.Policy;
using Microsoft.AspNetCore.Mvc;
using System.Net.Mime;
using yourscope_api.entities;

namespace yourscope_api.Authentication
{
public class YourScopeAuthorizationMiddleware : IAuthorizationMiddlewareResultHandler
{
public async Task HandleAsync(RequestDelegate next, HttpContext context, AuthorizationPolicy policy, PolicyAuthorizationResult authorizeResult)
{
var result = authorizeResult.Challenged ? HandleChallengedResult() : authorizeResult.Forbidden ? HandleForbiddenResult() : null;

if (result is not null)
{
context.Response.StatusCode = result.StatusCode;
context.Response.ContentType = "application/json";

await context.Response.WriteAsJsonAsync(result);
return;
}

await next(context);
}

#region methods to handle different authorization results
private static ApiResponse HandleChallengedResult()
{
ApiResponse response = new(StatusCodes.Status401Unauthorized, "Authentication failure.", success: false);

return response;
}

private static ApiResponse HandleForbiddenResult()
{
ApiResponse response = new(StatusCodes.Status403Forbidden, "User is not authorized to access this resource.", success: false);

return response;
}
#endregion
}
}
Loading