slacker 2.0
This commit is contained in:
13 files changed
+712
-538
No files matched your search
Regular → Executable
+3
-2
@@ -1,5 +1,6 @@
|
||||
__pycache__/*
|
||||
teams.json
|
||||
key.py
|
||||
boards/*
|
||||
venv/*
|
||||
repos/*
|
||||
urls.json
|
||||
teams.json
|
||||
Regular → Executable
+141
-53
@@ -1,100 +1,188 @@
|
||||
#!/usr/bin/env python3
|
||||
from links import urls
|
||||
from typing import List, Dict
|
||||
from json import JSONDecodeError
|
||||
import json
|
||||
import asyncio
|
||||
import aiohttp
|
||||
import time
|
||||
from tqdm import tqdm
|
||||
import os
|
||||
import subprocess
|
||||
import shutil
|
||||
|
||||
from parse import parse
|
||||
from key import token
|
||||
IGNORED: List[str] = [
|
||||
"node_modules/",
|
||||
".min.",
|
||||
"bootstrap/",
|
||||
".history/",
|
||||
]
|
||||
|
||||
MAX_RETRY: int = 5
|
||||
urls: Dict[str, str] = {}
|
||||
with open("urls.json", "r") as file:
|
||||
urls = json.load(file)
|
||||
|
||||
async def getInsights(repoURL: str, attempt: int = 0) -> Dict[str, str|None] | None:
|
||||
owner: str = repoURL.split("/")[3]
|
||||
repo: str = repoURL.split("/")[4]
|
||||
def setupDir(directory: str) -> None:
|
||||
folder = f'./{directory}'
|
||||
if not os.path.exists(folder):
|
||||
os.makedirs(folder)
|
||||
|
||||
query: str = f"https://api.github.com/repos/{owner}/{repo}/stats/contributors"
|
||||
headers: Dict[str, str] = {
|
||||
"Accept": "application/vnd.github+json",
|
||||
"Authorization": f"Bearer {token}",
|
||||
"X-GitHub-Api-Version": "2022-11-28"
|
||||
}
|
||||
for filename in os.listdir(folder):
|
||||
file_path = os.path.join(folder, filename)
|
||||
try:
|
||||
if os.path.isfile(file_path) or os.path.islink(file_path):
|
||||
os.unlink(file_path)
|
||||
elif os.path.isdir(file_path):
|
||||
shutil.rmtree(file_path)
|
||||
except Exception as e:
|
||||
print('Failed to delete %s. Reason: %s' % (file_path, e))
|
||||
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(query, headers=headers, timeout=5) as resp:
|
||||
if resp.status > 400:
|
||||
return None
|
||||
if resp.status == 202 and attempt <= MAX_RETRY:
|
||||
time.sleep(15)
|
||||
attempt += 1
|
||||
team = await getInsights(repoURL, attempt)
|
||||
return team
|
||||
elif attempt > MAX_RETRY: return None
|
||||
|
||||
async def getInsights(repoURL: str, teamID: str) -> Dict[str, str|List]:
|
||||
repo: str = repoURL.split("/")[4].split(".git")[0]
|
||||
|
||||
execDir: str = f"{os.getcwd()}/repos"
|
||||
repoDir: str = f"{execDir}/{repo}"
|
||||
|
||||
clear: List[str] = ["rm", "-rf", repo]
|
||||
clone: List[str] = ["git", "clone", repoURL]
|
||||
audit: List[str] = ["git", "log", "--stat"]
|
||||
|
||||
subprocess.run(clear, cwd=execDir)
|
||||
subprocess.run(clone, cwd=execDir)
|
||||
|
||||
with open(f"{repoDir}/audit.txt", "w") as auditFile:
|
||||
subprocess.run(audit, cwd=repoDir, stdout=auditFile, text=True)
|
||||
|
||||
team: Dict[str, str|List] = {
|
||||
"repo": repoURL,
|
||||
"team": repo,
|
||||
"team": teamID,
|
||||
"contributors": []
|
||||
}
|
||||
|
||||
data: List[Dict[str, any]] = await resp.json()
|
||||
with open(f"{repoDir}/audit.txt", "r") as auditFile:
|
||||
lines = auditFile.readlines()
|
||||
|
||||
for contributor in data:
|
||||
newContributor: Dict[str, str|int] = {
|
||||
"author": contributor["author"]["login"],
|
||||
"commits": contributor["total"],
|
||||
authorStats: Dict[str, str|int] | None = None
|
||||
|
||||
for i in range(len(lines)):
|
||||
line: str = lines[i].replace("\n", "")
|
||||
if "Author: " in line:
|
||||
author = line.split("Author: ")[1].split(" <")[0]
|
||||
email = line.split("<")[1].split(">")[0].lower()
|
||||
|
||||
found: bool = False
|
||||
for authorStat in team["contributors"]:
|
||||
if found: break
|
||||
if authorStat["author"] == author or authorStat["email"] == email:
|
||||
authorStats = authorStat
|
||||
team["contributors"].remove(authorStat)
|
||||
found = True
|
||||
|
||||
if not found and "@users.noreply.github.com" in email: continue
|
||||
|
||||
if not found:
|
||||
authorStats = {
|
||||
"email": email,
|
||||
"author": author,
|
||||
"commits": 0,
|
||||
"added": 0,
|
||||
"deleted": 0
|
||||
}
|
||||
|
||||
for week in contributor["weeks"]:
|
||||
newContributor["added"] += week["a"]
|
||||
newContributor["deleted"] += week["d"]
|
||||
if "Merge: " in lines[i - 1]:
|
||||
authorStats["commits"] += 1
|
||||
team["contributors"].append(authorStats)
|
||||
continue
|
||||
|
||||
team["contributors"].append(newContributor)
|
||||
offset: int = i + 5
|
||||
done: bool = False
|
||||
|
||||
while not done:
|
||||
if "files changed," in lines[offset] or "file changed," in lines[offset]:
|
||||
done = True
|
||||
break
|
||||
|
||||
ignore: bool = False
|
||||
for IGNORE in IGNORED:
|
||||
if IGNORE in lines[offset]:
|
||||
ignore = True
|
||||
break
|
||||
|
||||
if ignore:
|
||||
offset += 1
|
||||
continue
|
||||
|
||||
if " | " not in lines[offset]:
|
||||
offset += 1
|
||||
continue
|
||||
|
||||
if "Bin 0 -> " in lines[offset]:
|
||||
offset += 1
|
||||
continue
|
||||
|
||||
fileDiffNum: int = 0
|
||||
try:
|
||||
fileDiffNum = int(list(filter(None, lines[offset].split("|")[1].split(" ")))[0])
|
||||
except ValueError:
|
||||
offset += 1
|
||||
continue
|
||||
|
||||
if fileDiffNum == 0:
|
||||
offset += 1
|
||||
continue
|
||||
|
||||
ratioString: str = list(filter(None, lines[offset].split("|")[1].split(" ")))[1].replace("\n", "")
|
||||
|
||||
numAdd: int = ratioString.count("+")
|
||||
numDel: int = ratioString.count("-")
|
||||
|
||||
added: int = round((numAdd / len(ratioString)) * fileDiffNum)
|
||||
deleted: int = round((numDel / len(ratioString)) * fileDiffNum)
|
||||
|
||||
authorStats["added"] += added
|
||||
authorStats["deleted"] += deleted
|
||||
|
||||
offset += 1
|
||||
|
||||
authorStats["commits"] += 1
|
||||
team["contributors"].append(authorStats)
|
||||
|
||||
return team
|
||||
|
||||
async def main() -> None:
|
||||
teamsOriginal: List[Dict] = []
|
||||
teamsOrigin: List[Dict] = []
|
||||
try:
|
||||
with open("teams.json", "r") as file:
|
||||
teamsOriginal = json.load(file)
|
||||
except FileNotFoundError:
|
||||
teamsOrigin = json.load(file)
|
||||
except (FileNotFoundError, JSONDecodeError):
|
||||
pass
|
||||
|
||||
teams: List[Dict] = []
|
||||
|
||||
for i in tqdm(range(len(urls)),
|
||||
desc="Reading Insights",
|
||||
ascii=False, ncols=75):
|
||||
url = urls[i]
|
||||
team: Dict[str, str|List] = await getInsights(url)
|
||||
for teamID in urls:
|
||||
if urls[teamID] == "": continue
|
||||
team: Dict[str, str|List] | None = None
|
||||
try:
|
||||
team = await getInsights(urls[teamID], teamID)
|
||||
except Exception:
|
||||
pass
|
||||
if team is not None:
|
||||
teams.append(team)
|
||||
time.sleep(5)
|
||||
|
||||
print(f"Completed {len(teams)}/{len(urls)}")
|
||||
|
||||
# Parse gets Campus, Set, and Team ID
|
||||
teams = parse(teams)
|
||||
|
||||
for originalTeam in teamsOriginal:
|
||||
originalTeamID = originalTeam["id"]
|
||||
# If failed to get new team data, populate with old team data
|
||||
for original in teamsOrigin:
|
||||
originalID = original["team"]
|
||||
exists: bool = False
|
||||
for team in teams:
|
||||
if team["id"] == originalTeamID:
|
||||
if team["team"] == originalID:
|
||||
exists = True
|
||||
break
|
||||
|
||||
if not exists:
|
||||
teams.append(originalTeam)
|
||||
teams.append(original)
|
||||
|
||||
with open("teams.json", "w") as file:
|
||||
json.dump(teams, file, indent=2)
|
||||
|
||||
if __name__ == "__main__":
|
||||
setupDir("repos")
|
||||
setupDir("boards")
|
||||
asyncio.run(main())
|
||||
Regular → Executable
+178
-223
@@ -1,75 +1,112 @@
|
||||
#!/usr/bin/env python3
|
||||
from typing import List, Dict
|
||||
import json
|
||||
from prettytable import PrettyTable
|
||||
import os, shutil
|
||||
from datetime import datetime
|
||||
|
||||
def pretty_table(dct: Dict, title: str, add: str="") -> None:
|
||||
table = PrettyTable()
|
||||
|
||||
for c in dct.keys():
|
||||
table.add_column(c, [])
|
||||
|
||||
table.add_row(['\n'.join(dct[c]) for c in dct.keys()])
|
||||
table.align = "l"
|
||||
|
||||
with open(f"./boards/{title}.txt", "a") as file:
|
||||
if add != "":
|
||||
file.write(f"{add}\n")
|
||||
|
||||
file.write(table.__str__())
|
||||
file.write("\n\n")
|
||||
|
||||
def setupDir() -> None:
|
||||
if not os.path.exists("./boards"):
|
||||
os.makedirs("./boards")
|
||||
|
||||
folder = './boards'
|
||||
for filename in os.listdir(folder):
|
||||
file_path = os.path.join(folder, filename)
|
||||
try:
|
||||
if os.path.isfile(file_path) or os.path.islink(file_path):
|
||||
os.unlink(file_path)
|
||||
elif os.path.isdir(file_path):
|
||||
shutil.rmtree(file_path)
|
||||
except Exception as e:
|
||||
print('Failed to delete %s. Reason: %s' % (file_path, e))
|
||||
|
||||
def main() -> None:
|
||||
# This is sooooo poorly written with this dumb structure but oh well
|
||||
def generateLeaderboards(teams: List[Dict]) -> None:
|
||||
date = datetime.today().strftime('%Y-%m-%d %H:%M:%S')
|
||||
|
||||
teams: List[Dict[str, str|List]] = {}
|
||||
with open("teams.json", "r") as file:
|
||||
teams = json.load(file)
|
||||
# Empty leaderboard structures
|
||||
projects_per_campus: Dict[str, List] = {
|
||||
"title": "Largest Project per Campus",
|
||||
"boards": [
|
||||
{
|
||||
"title": "Burnaby Campus",
|
||||
"headers": [
|
||||
"Rank",
|
||||
"Team",
|
||||
"Size",
|
||||
"Commits"
|
||||
],
|
||||
"entries": []
|
||||
},
|
||||
{
|
||||
"title": "Downtown Campus",
|
||||
"headers": [
|
||||
"Rank",
|
||||
"Team",
|
||||
"Size",
|
||||
"Commits"
|
||||
],
|
||||
"entries": []
|
||||
}
|
||||
],
|
||||
"updatedAt": date
|
||||
}
|
||||
projects_all_time: Dict[str, List] = {
|
||||
"title": "Largets Project of All",
|
||||
"boards": [
|
||||
{
|
||||
"title": "",
|
||||
"headers": [
|
||||
"Rank",
|
||||
"Team",
|
||||
"Size",
|
||||
"Commits"
|
||||
],
|
||||
"entries": []
|
||||
}
|
||||
],
|
||||
"updatedAt": date
|
||||
}
|
||||
contributors_per_campus: Dict[str, List] = {
|
||||
"title": "Top Contributor per Campus",
|
||||
"boards": [
|
||||
{
|
||||
"title": "Burnaby Campus",
|
||||
"headers": [
|
||||
"Rank",
|
||||
"Author",
|
||||
"Added",
|
||||
"Deleted",
|
||||
"Actual",
|
||||
"Commits"
|
||||
],
|
||||
"entries": []
|
||||
},
|
||||
{
|
||||
"title": "Downtown Campus",
|
||||
"headers": [
|
||||
"Rank",
|
||||
"Author",
|
||||
"Added",
|
||||
"Deleted",
|
||||
"Actual",
|
||||
"Commits"
|
||||
],
|
||||
"entries": []
|
||||
}
|
||||
],
|
||||
"updatedAt": date
|
||||
}
|
||||
contributors_per_project: Dict[str, List] = {
|
||||
"title": "Top Contributor per Project",
|
||||
"boards": [], # Generated later
|
||||
"updatedAt": date
|
||||
}
|
||||
tracked_teams: int = 0
|
||||
teams_in_project: Dict[str, int] = {} # keep track of teams added to contributors_per_project
|
||||
contributors_all_time: Dict[str, List] = {
|
||||
"title": "Top Contributor of All",
|
||||
"boards": [
|
||||
{
|
||||
"title": "",
|
||||
"headers": [
|
||||
"Rank",
|
||||
"Author",
|
||||
"Added",
|
||||
"Deleted",
|
||||
"Actual",
|
||||
"Commits"
|
||||
],
|
||||
"entries": []
|
||||
}
|
||||
],
|
||||
"updatedAt": date
|
||||
}
|
||||
|
||||
projects_per_campus = {
|
||||
"Burnaby": [],
|
||||
"Downtown": []
|
||||
}
|
||||
projects_per_set = {
|
||||
"A": [],
|
||||
"B": [],
|
||||
"C": [],
|
||||
"D": [],
|
||||
"E": [],
|
||||
"F": []
|
||||
}
|
||||
projects_all_time = []
|
||||
contributors_per_campus = {
|
||||
"Burnaby": [],
|
||||
"Downtown": []
|
||||
}
|
||||
contributors_per_set = {
|
||||
"A": [],
|
||||
"B": [],
|
||||
"C": [],
|
||||
"D": [],
|
||||
"E": [],
|
||||
"F": []
|
||||
}
|
||||
contributors_per_project = {}
|
||||
contributors_all_time = []
|
||||
# === Populate leaderboards === #
|
||||
for team in teams:
|
||||
added, deleted, commits = 0, 0, 0
|
||||
for author in team["contributors"]:
|
||||
@@ -77,191 +114,109 @@ def main() -> None:
|
||||
deleted += author["deleted"]
|
||||
commits += author["commits"]
|
||||
|
||||
author["contributed"] = author["added"] - author["deleted"]
|
||||
author["actual"] = author["added"] - author["deleted"]
|
||||
|
||||
contributors_per_campus[team["campus"]].append(author)
|
||||
contributors_per_set[team["set"]].append(author)
|
||||
authorEntry: List[any] = [
|
||||
None, author["author"], author["added"], author["deleted"], author["actual"], author["commits"]
|
||||
]
|
||||
|
||||
if team["id"] not in contributors_per_project:
|
||||
contributors_per_project[team["id"]] = []
|
||||
contributors_per_project[team["id"]].append(author)
|
||||
campus: int = 0 if "BBY" in team["team"] else 1
|
||||
contributors_per_campus["boards"][campus]["entries"].append(authorEntry)
|
||||
|
||||
contributors_all_time.append(author)
|
||||
if team["team"] not in teams_in_project:
|
||||
teams_in_project[team["team"]] = tracked_teams
|
||||
tracked_teams += 1
|
||||
contributors_per_project["boards"].append({
|
||||
"title": team["team"],
|
||||
"headers": [
|
||||
"Rank",
|
||||
"Author",
|
||||
"Added",
|
||||
"Deleted",
|
||||
"Actual",
|
||||
"Commits"
|
||||
],
|
||||
"entries": []
|
||||
})
|
||||
|
||||
contributors_per_project["boards"][teams_in_project[team["team"]]]["entries"].append(authorEntry)
|
||||
|
||||
contributors_all_time["boards"][0]["entries"].append(authorEntry)
|
||||
|
||||
size = added - deleted
|
||||
|
||||
project = {
|
||||
"team": team["id"],
|
||||
"size": size,
|
||||
"added": added,
|
||||
"deleted": deleted,
|
||||
"commits": commits
|
||||
}
|
||||
projectEntry: List[str|int] = [
|
||||
None,
|
||||
team["team"],
|
||||
size,
|
||||
commits
|
||||
]
|
||||
|
||||
projects_per_campus[team["campus"]].append(project)
|
||||
projects_per_set[team["set"]].append(project)
|
||||
projects_all_time.append(project)
|
||||
|
||||
#######################################################################
|
||||
|
||||
# Largest project per campus
|
||||
for campus in projects_per_campus:
|
||||
filtered = sorted(projects_per_campus[campus], key=lambda d: d['size'], reverse=True)
|
||||
|
||||
data = {
|
||||
"Rank": [],
|
||||
"Team": [],
|
||||
"Size": []
|
||||
}
|
||||
|
||||
rank = 0
|
||||
for project in filtered:
|
||||
rank += 1
|
||||
data["Rank"].append(str(rank))
|
||||
data["Team"].append(project["team"])
|
||||
data["Size"].append(str(project["size"]))
|
||||
|
||||
pretty_table(data, "LargestProjectsPerCampus", f"{campus} Campus - Updated at {date}")
|
||||
|
||||
# Largest project per set
|
||||
for set in projects_per_set:
|
||||
filtered = sorted(projects_per_set[set], key=lambda d: d['size'], reverse=True)
|
||||
|
||||
data = {
|
||||
"Rank": [],
|
||||
"Team": [],
|
||||
"Size": []
|
||||
}
|
||||
|
||||
rank = 0
|
||||
for project in filtered:
|
||||
rank += 1
|
||||
data["Rank"].append(str(rank))
|
||||
data["Team"].append(project["team"])
|
||||
data["Size"].append(str(project["size"]))
|
||||
|
||||
pretty_table(data, "LargestProjectPerSet", f"Set {set} - Updated at {date}")
|
||||
|
||||
# Largest project of all
|
||||
filtered = sorted(projects_all_time, key=lambda d: d['size'], reverse=True)
|
||||
|
||||
data = {
|
||||
"Rank": [],
|
||||
"Team": [],
|
||||
"Size": []
|
||||
}
|
||||
|
||||
rank = 0
|
||||
for project in filtered:
|
||||
rank += 1
|
||||
data["Rank"].append(str(rank))
|
||||
data["Team"].append(project["team"])
|
||||
data["Size"].append(str(project["size"]))
|
||||
|
||||
pretty_table(data, "LargestProjectAllTeams", f"Updated at {date}")
|
||||
campus: int = 0 if "BBY" in team["team"] else 1
|
||||
projects_per_campus["boards"][campus]["entries"].append(projectEntry)
|
||||
projects_all_time["boards"][0]["entries"].append(projectEntry)
|
||||
|
||||
|
||||
# Contributors ranker per campus
|
||||
for campus in contributors_per_campus:
|
||||
filtered = sorted(contributors_per_campus[campus], key=lambda d: d['added'], reverse=True)
|
||||
# === Sort leaderboards and write to JSON === #
|
||||
|
||||
data = {
|
||||
"Rank": [],
|
||||
"Author": [],
|
||||
"Added": [],
|
||||
"Deleted": [],
|
||||
"Actual": [],
|
||||
"Commits": [],
|
||||
}
|
||||
# Project per Campus
|
||||
for campus in range(len(projects_per_campus["boards"])):
|
||||
filtered = sorted(projects_per_campus["boards"][campus]["entries"], key=lambda e: e[2], reverse=True)
|
||||
for i in range(len(filtered)):
|
||||
filtered[i][0] = i + 1
|
||||
|
||||
rank = 0
|
||||
for author in filtered:
|
||||
rank += 1
|
||||
data["Rank"].append(str(rank))
|
||||
data["Author"].append(author['author'])
|
||||
data["Commits"].append(str(author['commits']))
|
||||
data["Added"].append(f"+{author['added']}")
|
||||
data["Deleted"].append(f"-{author['deleted']}")
|
||||
data["Actual"].append(str(author['contributed']))
|
||||
projects_per_campus["boards"][campus]["entries"] = filtered
|
||||
|
||||
pretty_table(data, "TopContributorPerCampus", f"{campus} Campus - Updated at {date}")
|
||||
with open("boards/LargestProjectsPerCampus.json", "w") as file:
|
||||
json.dump(projects_per_campus, file, indent=2)
|
||||
|
||||
# Projects all time
|
||||
filtered = sorted(projects_all_time["boards"][0]["entries"], key=lambda e: e[2], reverse=True)
|
||||
for i in range(len(filtered)):
|
||||
filtered[i][0] = i + 1
|
||||
|
||||
# Contributors ranked per set
|
||||
for set in contributors_per_set:
|
||||
filtered = sorted(contributors_per_set[set], key=lambda d: d['added'], reverse=True)
|
||||
projects_all_time["boards"][0]["entries"] = filtered
|
||||
|
||||
data = {
|
||||
"Rank": [],
|
||||
"Author": [],
|
||||
"Added": [],
|
||||
"Deleted": [],
|
||||
"Actual": [],
|
||||
"Commits": [],
|
||||
}
|
||||
with open("boards/LargestProjectAllTeams.json", "w") as file:
|
||||
json.dump(projects_all_time, file, indent=2)
|
||||
|
||||
rank = 0
|
||||
for author in filtered:
|
||||
rank += 1
|
||||
data["Rank"].append(str(rank))
|
||||
data["Author"].append(author['author'])
|
||||
data["Commits"].append(str(author['commits']))
|
||||
data["Added"].append(f"+{author['added']}")
|
||||
data["Deleted"].append(f"-{author['deleted']}")
|
||||
data["Actual"].append(str(author['contributed']))
|
||||
# Contributor per Campus
|
||||
for campus in range(len(contributors_per_campus["boards"])):
|
||||
filtered = sorted(contributors_per_campus["boards"][campus]["entries"], key=lambda e: e[2], reverse=True)
|
||||
for i in range(len(filtered)):
|
||||
filtered[i][0] = i + 1
|
||||
|
||||
pretty_table(data, "TopContributorPerSet", f"Set {set} - Updated at {date}")
|
||||
contributors_per_campus["boards"][campus]["entries"] = filtered
|
||||
|
||||
with open("boards/TopContributorPerCampus.json", "w") as file:
|
||||
json.dump(contributors_per_campus, file, indent=2)
|
||||
|
||||
# Contributors ranked per Team
|
||||
for project in contributors_per_project:
|
||||
filtered = sorted(contributors_per_project[project], key=lambda d: d['added'], reverse=True)
|
||||
# Contributor per Project
|
||||
for project in range(len(contributors_per_project["boards"])):
|
||||
filtered = sorted(contributors_per_project["boards"][project]["entries"], key=lambda e: e[2], reverse=True)
|
||||
for i in range(len(filtered)):
|
||||
filtered[i][0] = i + 1
|
||||
|
||||
data = {
|
||||
"Rank": [],
|
||||
"Author": [],
|
||||
"Added": [],
|
||||
"Deleted": [],
|
||||
"Actual": [],
|
||||
"Commits": [],
|
||||
}
|
||||
contributors_per_project["boards"][project]["entries"] = filtered
|
||||
|
||||
rank = 0
|
||||
for author in filtered:
|
||||
rank += 1
|
||||
data["Rank"].append(str(rank))
|
||||
data["Author"].append(author['author'])
|
||||
data["Commits"].append(str(author['commits']))
|
||||
data["Added"].append(f"+{author['added']}")
|
||||
data["Deleted"].append(f"-{author['deleted']}")
|
||||
data["Actual"].append(str(author['contributed']))
|
||||
with open("boards/TopContributorsPerTeam.json", "w") as file:
|
||||
json.dump(contributors_per_project, file, indent=2)
|
||||
|
||||
pretty_table(data, "TopContributorsPerTeam", f"{project} - Updated at {date}");
|
||||
# Contributor all time
|
||||
filtered = sorted(contributors_all_time["boards"][0]["entries"], key=lambda e: e[2], reverse=True)
|
||||
for i in range(len(filtered)):
|
||||
filtered[i][0] = i + 1
|
||||
|
||||
# Contributors ranked all time
|
||||
contributors_all_time = sorted(contributors_all_time, key=lambda d: d['added'], reverse=True)
|
||||
contributors_all_time["boards"][0]["entries"] = filtered
|
||||
|
||||
data = {
|
||||
"Rank": [],
|
||||
"Author": [],
|
||||
"Added": [],
|
||||
"Deleted": [],
|
||||
"Actual": [],
|
||||
"Commits": [],
|
||||
}
|
||||
with open("boards/TopContributorsAllTime.json", "w") as file:
|
||||
json.dump(contributors_all_time, file, indent=2)
|
||||
|
||||
rank = 0
|
||||
for author in contributors_all_time:
|
||||
rank += 1
|
||||
data["Rank"].append(str(rank))
|
||||
data["Author"].append(author['author'])
|
||||
data["Actual"].append(str(author["contributed"]))
|
||||
data["Added"].append(f"+{author['added']}")
|
||||
data["Deleted"].append(f"-{author['deleted']}")
|
||||
data["Commits"].append(str(author['commits']))
|
||||
def main() -> None:
|
||||
with open("teams.json", "r") as file:
|
||||
teams: List[Dict] = json.load(file)
|
||||
|
||||
pretty_table(data, "TopContributorsAllTime", f"Updated at {date}")
|
||||
generateLeaderboards(teams)
|
||||
|
||||
if __name__ == "__main__":
|
||||
setupDir()
|
||||
main()
|
||||
@@ -1,47 +0,0 @@
|
||||
from typing import List
|
||||
|
||||
urls: List[str] = [
|
||||
"https://github.com/ZihengZhaoJerry/1800_202510_BBY17",
|
||||
"https://github.com/mskim9097/1800_202510_BBY18",
|
||||
"https://github.com/dodaniel006/1800_202510_BBY19",
|
||||
"https://github.com/NikR-Os/1800_202501_BBY20",
|
||||
"https://github.com/cubeo77/1800_202510_BBY21",
|
||||
"https://github.com/RayenBMoussa/1800_202510_BBY22",
|
||||
"https://github.com/Likeemapples/1800_2025_BBY23",
|
||||
"https://github.com/westudioss/1800_202510_BBY01",
|
||||
"https://github.com/RodrickVy/1800_202510_BBY02",
|
||||
"https://github.com/kevarome/1800_202510_BBY03",
|
||||
"https://github.com/arshiaadamian/1800_202510_Team4",
|
||||
"https://github.com/adilascio/1800_202510_bby5",
|
||||
"https://github.com/dnlsvdr/1800_202430_BBY06",
|
||||
"https://github.com/Dartleader1/1800_202510_BBY07",
|
||||
"https://github.com/sukhrajsandhar/1800_202510_BBY8",
|
||||
"https://github.com/Glonkz/CleanPath_BBY09",
|
||||
"https://github.com/TaylorHillier/1800_202510_BBY11",
|
||||
"https://github.com/cnguyen50/1800_202510_BBY12",
|
||||
"https://github.com/emphs/1800_202510_BBY13",
|
||||
"https://github.com/useluss-dev/1800_202510_BBY14",
|
||||
"https://github.com/Jacob-Lebl-BCIT/1800_202510_BBY15",
|
||||
"https://github.com/annoyingcoder174/2025_01_Group16",
|
||||
"https://github.com/ShivaunBartoo/1800_202510_BBY25",
|
||||
"https://github.com/ffloras/1800_202510_BBY26",
|
||||
"https://github.com/Tlpadilla18/1800_202510_BBY27",
|
||||
"https://github.com/anajnsilva/1800_202510_BBY28",
|
||||
"https://github.com/amangrewal10/1800_202510_BBY_29",
|
||||
"https://github.com/TirathJaggee/1800_202510_30",
|
||||
"https://github.com/SowinskiBraeden/1800_202510_BBY32",
|
||||
"https://github.com/klockwork3/1800_202430_DTC01",
|
||||
"https://github.com/lawr-lau16/1800_202510_DTC02",
|
||||
"https://github.com/tushitgrg/1800_202510_DTC03",
|
||||
"https://github.com/ttyw24/1800_202510_DTC04",
|
||||
"https://github.com/achepakovich/1800_202510_DTC05",
|
||||
"https://github.com/ttrinh0/1800_202510_DTC06",
|
||||
"https://github.com/dcao1205/1800_202510_DTC07",
|
||||
"https://github.com/ivanekavalashvili/1800_202510_DTC09",
|
||||
"https://github.com/wandereryiu/1800_202510_DTC10",
|
||||
"https://github.com/Choudoufuhezi/1800_202510_DTC11",
|
||||
"https://github.com/anthonyherradura/1800_202510_DTC12",
|
||||
"https://github.com/andacg1/1800_202510_DTC13",
|
||||
"https://github.com/senukzzzzz/1800_202510_DTC15",
|
||||
"https://github.com/knighthawk4227/1800_202510_31"
|
||||
]
|
||||
@@ -1,76 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
from typing import List, Dict
|
||||
|
||||
# Only ever really needs to be run one time
|
||||
|
||||
def getSet(team: str) -> str:
|
||||
team = team.upper()
|
||||
campus = "DTC" if "DTC" in team else "BBY"
|
||||
num = ""
|
||||
if "BBY" in team:
|
||||
num = team.split("BBY")[1]
|
||||
elif "DTC" in team:
|
||||
num = team.split("DTC")[1]
|
||||
elif "TEAM" in team:
|
||||
num = team.split("TEAM")[1]
|
||||
elif "GROUP" in team:
|
||||
num = team.split("GROUP")[1]
|
||||
else:
|
||||
num = team
|
||||
|
||||
num = int(num)
|
||||
if campus == "BBY":
|
||||
if 17 <= num <= 24: return "A"
|
||||
if 1 <= num <= 8: return "B"
|
||||
if 9 <= num <= 16: return "C"
|
||||
if 25 <= num <= 32: return "D"
|
||||
else:
|
||||
print(team, num)
|
||||
return "X"
|
||||
elif campus == "DTC":
|
||||
if 1 <= num <= 8: return "E"
|
||||
if 9 <= num <= 15: return "F"
|
||||
else:
|
||||
print(team)
|
||||
return "X"
|
||||
else:
|
||||
print(team)
|
||||
return "Z"
|
||||
|
||||
def getCampus(team: str) -> str:
|
||||
team = team.upper()
|
||||
campus = "Downtown" if "DTC" in team else "Burnaby"
|
||||
return campus
|
||||
|
||||
def createTeamID(team: str) -> str:
|
||||
team = team.upper()
|
||||
campus = "DTC" if "DTC" in team else "BBY"
|
||||
num = ""
|
||||
if "BBY" in team:
|
||||
num = team.split("BBY")[1]
|
||||
elif "DTC" in team:
|
||||
num = team.split("DTC")[1]
|
||||
elif "TEAM" in team:
|
||||
num = team.split("TEAM")[1]
|
||||
elif "GROUP" in team:
|
||||
num = team.split("GROUP")[1]
|
||||
else:
|
||||
num = team
|
||||
|
||||
num = int(num)
|
||||
return f"{campus}-{'0' if num < 10 else ''}{num}"
|
||||
|
||||
def parse(teams: List[Dict[str, str|List]]) -> List[Dict[str, str|List]]:
|
||||
|
||||
for team in teams:
|
||||
teamID = team["repo"].split("_")[len(team["repo"].split("_")) - 1]
|
||||
team["set"] = getSet(teamID)
|
||||
team["campus"] = getCampus(teamID)
|
||||
team["id"] = createTeamID(teamID)
|
||||
|
||||
return teams
|
||||
# with open("teams.json", "w") as file:
|
||||
# json.dump(teams, file, indent=2)
|
||||
|
||||
if __name__ == "__main__":
|
||||
parse()
|
||||
@@ -1,21 +0,0 @@
|
||||
aiohappyeyeballs==2.6.1
|
||||
aiohttp==3.11.13
|
||||
aiosignal==1.3.2
|
||||
attrs==25.3.0
|
||||
blinker==1.9.0
|
||||
click==8.1.8
|
||||
Flask==3.1.0
|
||||
frozenlist==1.5.0
|
||||
gunicorn==23.0.0
|
||||
idna==3.10
|
||||
itsdangerous==2.2.0
|
||||
Jinja2==3.1.6
|
||||
MarkupSafe==3.0.2
|
||||
multidict==6.1.0
|
||||
packaging==24.2
|
||||
prettytable==3.15.1
|
||||
propcache==0.3.0
|
||||
tqdm==4.67.1
|
||||
wcwidth==0.2.13
|
||||
Werkzeug==3.1.3
|
||||
yarl==1.18.3
|
||||
Regular → Executable
+21
-15
@@ -1,28 +1,34 @@
|
||||
#!/usr/bin/env python3
|
||||
from flask import Flask, Response
|
||||
from flask import render_template
|
||||
import os
|
||||
from flask import Flask
|
||||
from flask import render_template, redirect, url_for
|
||||
from typing import Dict, List
|
||||
import json
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
def root_dir():
|
||||
return os.path.abspath(os.path.dirname(__file__))
|
||||
|
||||
def get_file(filename):
|
||||
try:
|
||||
src = os.path.join(root_dir(), filename)
|
||||
return open(src).read()
|
||||
except IOError as exc:
|
||||
return str(exc)
|
||||
|
||||
@app.route("/", methods=["GET"])
|
||||
def index() -> str:
|
||||
return render_template("index.html")
|
||||
|
||||
@app.route("/boards/<boardID>", methods=["GET"])
|
||||
def loadBoard(boardID: str) -> str:
|
||||
path = os.path.join(root_dir(), f"boards/{boardID}.txt")
|
||||
return Response(get_file(path), mimetype="text/plain")
|
||||
leaderboard: Dict[str, List] = {}
|
||||
try:
|
||||
with open(f"boards/{boardID}.json", "r") as file:
|
||||
leaderboard = json.load(file)
|
||||
except FileNotFoundError:
|
||||
return redirect(url_for('404'))
|
||||
|
||||
return render_template(
|
||||
"board.html",
|
||||
leaderboards=leaderboard["boards"],
|
||||
title=leaderboard["title"],
|
||||
updatedAt=leaderboard["updatedAt"]
|
||||
)
|
||||
|
||||
@app.route("/404", methods=["GET"])
|
||||
def notFound() -> str:
|
||||
return render_template("not_found.html")
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run(host="0.0.0.0")
|
||||
Executable
+91
@@ -0,0 +1,91 @@
|
||||
@import url("https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;700;800&display=swap");
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, segoe ui, Roboto, Oxygen, Ubuntu, Cantarell, open sans, helvetica neue, sans-serif;
|
||||
padding: 0pt;
|
||||
margin: 0pt;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 40px;
|
||||
}
|
||||
|
||||
.main {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 25%;
|
||||
height: auto;
|
||||
padding: 24pt;
|
||||
border-radius: 24px;
|
||||
justify-content: center;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
ul li {
|
||||
margin-bottom: 12pt;
|
||||
}
|
||||
|
||||
.link {
|
||||
font-size: 30px;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.flex-container-c {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.flex-container-r {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-evenly;
|
||||
}
|
||||
|
||||
footer {
|
||||
width: 100vw;
|
||||
position: fixed;
|
||||
bottom: 0%;
|
||||
left: 0%;
|
||||
background-color: #aaaaaa;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
transition: .8s;
|
||||
padding: 8pt;
|
||||
}
|
||||
|
||||
footer a {
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
font-style: normal;
|
||||
text-decoration: underline;
|
||||
color: black
|
||||
}
|
||||
|
||||
footer p {
|
||||
margin-top: 16pt;
|
||||
}
|
||||
|
||||
.social-icons {
|
||||
padding: 12pt 0;
|
||||
}
|
||||
|
||||
.social-icons a:not(:last-of-type) {
|
||||
margin-inline-end: 12pt;
|
||||
}
|
||||
|
||||
.social-icons a svg {
|
||||
height: 26pt;
|
||||
width: 26pt;
|
||||
}
|
||||
|
||||
.social-icons a {
|
||||
color: var(--primary);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.icon {
|
||||
transition: 0.8s;
|
||||
}
|
||||
Executable
+168
@@ -0,0 +1,168 @@
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
background-color: #f8f9fa;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.grid-container {
|
||||
display: grid;
|
||||
grid-template-areas:
|
||||
"header header"
|
||||
"content content"
|
||||
"footer footer";
|
||||
}
|
||||
/* header {
|
||||
grid-area: header;
|
||||
} */
|
||||
.back-btn {
|
||||
background-color: #007BFF;
|
||||
border: none;
|
||||
color: white;
|
||||
padding: 15px 32px;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
font-size: 16px;
|
||||
border-radius: 4px;
|
||||
margin: 16pt;
|
||||
}
|
||||
.footer {
|
||||
grid-area: footer;
|
||||
}
|
||||
.container {
|
||||
grid-area: content;
|
||||
width: 90%;
|
||||
max-width: 1200px;
|
||||
margin: 40px auto;
|
||||
background: #fff;
|
||||
padding: 30px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
h1 {
|
||||
text-align: center;
|
||||
color: #333;
|
||||
}
|
||||
.leaderboard, .score-table {
|
||||
margin-top: 30px;
|
||||
}
|
||||
.leaderboard {
|
||||
text-align: center;
|
||||
background-color: #f1f1f1;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
.leaderboard h2 {
|
||||
color: #007BFF;
|
||||
}
|
||||
.leaderboard ul {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
}
|
||||
.leaderboard li {
|
||||
padding: 8px;
|
||||
background: #fff;
|
||||
margin: 10px auto;
|
||||
width: 80%;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
.leaderboard li span {
|
||||
font-weight: bold;
|
||||
color: #007BFF;
|
||||
}
|
||||
.filters {
|
||||
margin-top: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
.filters select, .filters input {
|
||||
padding: 10px;
|
||||
margin: 5px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 5px;
|
||||
}
|
||||
.score-table {
|
||||
margin-top: 20px;
|
||||
}
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin-top: 20px;
|
||||
}
|
||||
table th, table td {
|
||||
padding: 12px;
|
||||
text-align: left;
|
||||
border: 1px solid #ddd;
|
||||
}
|
||||
table th {
|
||||
background-color: #007BFF;
|
||||
color: white;
|
||||
}
|
||||
.btn {
|
||||
padding: 8px 12px;
|
||||
background-color: #007BFF;
|
||||
color: #fff;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
font-size: 14px;
|
||||
}
|
||||
.btn:hover {
|
||||
background-color: #0056b3;
|
||||
}
|
||||
|
||||
.flex-container-c {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.flex-container-r {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-evenly;
|
||||
}
|
||||
|
||||
footer {
|
||||
width: 100vw;
|
||||
background-color: #aaaaaa;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
transition: .8s;
|
||||
padding: 8pt;
|
||||
}
|
||||
|
||||
footer a {
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
font-style: normal;
|
||||
text-decoration: underline;
|
||||
color: black
|
||||
}
|
||||
|
||||
footer p {
|
||||
margin-top: 16pt;
|
||||
}
|
||||
|
||||
.social-icons {
|
||||
padding: 12pt 0;
|
||||
}
|
||||
|
||||
.social-icons a:not(:last-of-type) {
|
||||
margin-inline-end: 12pt;
|
||||
}
|
||||
|
||||
.social-icons a svg {
|
||||
height: 26pt;
|
||||
width: 26pt;
|
||||
}
|
||||
|
||||
.social-icons a {
|
||||
color: var(--primary);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.icon {
|
||||
transition: 0.8s;
|
||||
}
|
||||
Executable
+81
@@ -0,0 +1,81 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>COMP 2800 - Slacker</title>
|
||||
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='styles/leaderboard.css') }}">
|
||||
</head>
|
||||
<body>
|
||||
<div class="grid-container">
|
||||
|
||||
<header>
|
||||
<a class="back-btn" href="/">Back</a>
|
||||
</header>
|
||||
|
||||
<div class="container">
|
||||
<h1>{{ title }}</h1>
|
||||
<p><small>Last Updated: {{ updatedAt }}</small></p>
|
||||
|
||||
<!-- Filters Section -->
|
||||
<!-- <div class="filters">
|
||||
<select>
|
||||
<option value="all">All Categories</option>
|
||||
<option value="quizzes">Quizzes</option>
|
||||
<option value="projects">Projects</option>
|
||||
<option value="exams">Exams</option>
|
||||
</select>
|
||||
<input type="text" placeholder="Search by name or category">
|
||||
<button class="btn">Apply</button>
|
||||
</div> -->
|
||||
|
||||
<!-- Leaderboard Section -->
|
||||
{% for leaderboard in leaderboards %}
|
||||
<div class="score-table">
|
||||
<h2>{{ leaderboard.title }}</h2>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
{% for header in leaderboard.headers %}
|
||||
<th>{{ header }}</th>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for entry in leaderboard.entries %}
|
||||
<tr>
|
||||
{% for attr in entry %}
|
||||
<td>{{ attr }}</td>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<footer class="footer flex-container-c">
|
||||
<div class="flex-container-r">
|
||||
<div>
|
||||
<p><a href="https://sowinski.dev">sowinski.dev</a></p>
|
||||
</div>
|
||||
<div class="social-icons">
|
||||
<a class="icon" href="https://www.linkedin.com/in/SowinskiBraeden" target=_blank rel="noopener noreferrer me"
|
||||
title=LinkedIn>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-linkedin"
|
||||
viewBox="0 0 16 16">
|
||||
<path
|
||||
d="M0 1.146C0 .513.526 0 1.175 0h13.65C15.474 0 16 .513 16 1.146v13.708c0 .633-.526 1.146-1.175 1.146H1.175C.526 16 0 15.487 0 14.854zm4.943 12.248V6.169H2.542v7.225zm-1.2-8.212c.837 0 1.358-.554 1.358-1.248-.015-.709-.52-1.248-1.342-1.248S2.4 3.226 2.4 3.934c0 .694.521 1.248 1.327 1.248zm4.908 8.212V9.359c0-.216.016-.432.08-.586.173-.431.568-.878 1.232-.878.869 0 1.216.662 1.216 1.634v3.865h2.401V9.25c0-2.22-1.184-3.252-2.764-3.252-1.274 0-1.845.7-2.165 1.193v.025h-.016l.016-.025V6.169h-2.4c.03.678 0 7.225 0 7.225z" />
|
||||
</svg>
|
||||
</a>
|
||||
<a class="icon" href=https://github.com/SowinskiBraeden/slacker target=_blank rel="noopener noreferrer me" title=Github>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2"
|
||||
stroke-linecap="round" stroke-linejoin="round">
|
||||
<path
|
||||
d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37.0 00-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44.0 0020 4.77 5.07 5.07.0 0019.91 1S18.73.65 16 2.48a13.38 13.38.0 00-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07.0 005 4.77 5.44 5.44.0 003.5 8.55c0 5.42 3.3 6.61 6.44 7A3.37 3.37.0 009 18.13V22" />
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Regular → Executable
+13
-101
@@ -1,118 +1,30 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>COMP 1800 - Slacker</title>
|
||||
|
||||
<style>
|
||||
@import url("https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;700;800&display=swap");
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, segoe ui, Roboto, Oxygen, Ubuntu, Cantarell, open sans, helvetica neue, sans-serif;
|
||||
padding: 0pt;
|
||||
margin: 0pt;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 40px;
|
||||
}
|
||||
|
||||
.main {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 25%;
|
||||
height: auto;
|
||||
padding: 24pt;
|
||||
border-radius: 24px;
|
||||
justify-content: center;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
ul li {
|
||||
margin-bottom: 12pt;
|
||||
}
|
||||
|
||||
.link {
|
||||
font-size: 30px;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.flex-container-c {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.flex-container-r {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-evenly;
|
||||
}
|
||||
|
||||
footer {
|
||||
width: 100vw;
|
||||
position: fixed;
|
||||
bottom: 0%;
|
||||
left: 0%;
|
||||
background-color: #aaaaaa;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
transition: .8s;
|
||||
padding: 8pt;
|
||||
}
|
||||
|
||||
footer a {
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
font-style: normal;
|
||||
text-decoration: underline;
|
||||
color: black
|
||||
}
|
||||
|
||||
footer p {
|
||||
margin-top: 16pt;
|
||||
}
|
||||
|
||||
.social-icons {
|
||||
padding: 12pt 0;
|
||||
}
|
||||
|
||||
.social-icons a:not(:last-of-type) {
|
||||
margin-inline-end: 12pt;
|
||||
}
|
||||
|
||||
.social-icons a svg {
|
||||
height: 26pt;
|
||||
width: 26pt;
|
||||
}
|
||||
|
||||
.social-icons a {
|
||||
color: var(--primary);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.icon {
|
||||
transition: 0.8s;
|
||||
}
|
||||
</style>
|
||||
<title>COMP 2800 - Slacker</title>
|
||||
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='styles/index.css') }}">
|
||||
</head>
|
||||
<body>
|
||||
<section class="main">
|
||||
<div>
|
||||
<h1 class="title">COMP 1800 Slacker</h1>
|
||||
<h1 class="title">COMP 2800 Slacker</h1>
|
||||
<h3>See Leaderboards</h3>
|
||||
<p>PS: Some projects & users may not be up to date with others.</p>
|
||||
<p>Also, I am lazy and don't want to make a pretty table so you just get .txt files</p>
|
||||
<p>Check back for daily updates.</p>
|
||||
<p>
|
||||
Disclaimer: This reads commits from git log and may not match
|
||||
commits from Github insights.
|
||||
</p>
|
||||
<p>
|
||||
Additionally, any commits made through the Github web interface are ignored.
|
||||
</p>
|
||||
<p>
|
||||
Files with matching patterns may be ignored in your commits. E.g. node_module files.
|
||||
</p>
|
||||
<ul>
|
||||
<li><a class="link" href="/boards/TopContributorsAllTime">Top Contributor of All</a></li>
|
||||
<li><a class="link" href="/boards/TopContributorPerCampus">Top Contributor per Campus</a></li>
|
||||
<li><a class="link" href="/boards/TopContributorPerSet">Top Contributor per Set</a></li>
|
||||
<li><a class="link" href="/boards/TopContributorsPerTeam">Top Contributor per Team</a></li>
|
||||
<li><a class="link" href="/boards/LargestProjectAllTeams">Largest Project of All</a></li>
|
||||
<li><a class="link" href="/boards/LargestProjectsPerCampus">Largest Project per Campus</a></li>
|
||||
<li><a class="link" href="/boards/LargestProjectPerSet">Largest Project per Set</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
#!/usr/bin/env python3
|
||||
from insights import setupDir, main
|
||||
from leaderboard import generateLeaderboards
|
||||
import asyncio
|
||||
import json
|
||||
from typing import List, Dict
|
||||
|
||||
if __name__ == "__main__":
|
||||
setupDir("repos")
|
||||
setupDir("boards")
|
||||
asyncio.run(main())
|
||||
|
||||
with open("teams.json", "r") as file:
|
||||
teams: List[Dict] = json.load(file)
|
||||
|
||||
generateLeaderboards(teams)
|
||||
Reference in new issue
Block a user