--- /dev/null
+lib/
+lib64
+bin/
+__pycache__/
+pyvenv.cfg
--- /dev/null
+DOMAIN = "https://schoology.d214.org"
+GROUP_ID = 6454678062
--- /dev/null
+{"py/object": "database.Data", "projects": [{"py/object": "project.Project", "name": "Computer Science Club Website", "description": "Made for Computer Science Club's Mini-Hackathon.", "authors": ["Damian Myrda"], "source": "https://replit.com/@Moncheeto/Computer-Science-Club?v=1", "images": null}]}
\ No newline at end of file
--- /dev/null
+import os
+from jsonpickle import decode, encode
+
+class Data:
+ projects = []
+
+ def __init__(self, projects):
+ self.projects = projects
+
+class Database:
+ def read(self):
+ if not os.path.isfile("database.json"):
+ data = Data([])
+ self.write(data)
+ return data
+ with open("database.json", "r") as file:
+ return decode(file.read())
+
+ def write(self, data):
+ with open("database.json", "w") as file:
+ file.write(encode(data))
+
+class ProjectDatabase:
+ def read(self):
+ projects = []
+ for project in Database().read().projects:
+ projects.append(project)
+ return projects
+
+ def write(self, projects):
+ Database().write(Data(projects))
--- /dev/null
+import os, sys, pathlib
+PROJECT_DIR = os.path.join(pathlib.Path(__file__).parent)
+sys.path.append(os.path.join(PROJECT_DIR, "config.py"))
+sys.path.append(os.path.join(PROJECT_DIR, "models"))
+sys.path.append(os.path.join(PROJECT_DIR, "projects.py"))
+sys.path.append(os.path.join(PROJECT_DIR, "schoology.py"))
+from schoology import group
+from flask import Flask, render_template
+
+app = Flask(__name__)
+app.secret_key = "https://computer-science-club.moncheeto.repl.co"
+
+@app.route("/")
+def index():
+ return render_template("home.html", group=group)
+
+@app.route("/updates")
+def updates():
+ return render_template("updates.html", group=group)
+
+@app.route("/discussions")
+def discussions():
+ return render_template("discussions.html", group=group)
+
+@app.route("/projects", methods = ["GET", "POST"])
+def projects():
+ return render_template("projects.html", group=group)
+
+@app.route("/members")
+def members():
+ return render_template("members.html", group=group)
+
+if __name__ == "__main__":
+ app.run(host='0.0.0.0', port=8000)
--- /dev/null
+from discussion import Discussion
+from event import Event
+from group import Group
+from member import Member
+from project import Project
+from update import Update
--- /dev/null
+from config import DOMAIN
+
+class Discussion:
+ name = ""
+ description = ""
+ link = f"{DOMAIN}/discussion/"
+
+ def __init__(self, id, name, description):
+ self.link += str(id)
+ self.name = name
+ self.description = description
+from datetime import datetime
+from config import DOMAIN
+
+class Event:
+ name = ""
+ description = ""
+ start = datetime.now()
+ end = None
+ differentDay = False
+ link = f"{DOMAIN}/event/"
+
+ def __init__(self, id, name, description, start, end):
+ self.link += str(id)
+ self.name = name
+ self.description = description
+ self.start = start
+ self.end = end
+ if end and self.end.year >= self.start.year and self.end.month >= self.start.month and self.end.day > start.day:
+ differentDay = True
--- /dev/null
+from datetime import datetime
+from config import DOMAIN
+
+class Event:
+ name = ""
+ description = ""
+ start = datetime.now()
+ end = None
+ differentDay = False
+ link = f"{DOMAIN}/event/"
+
+ def __init__(self, id, name, description, start, end):
+ self.link += str(id)
+ self.name = name
+ self.description = description
+ self.start = start
+ self.end = end
+ if end and self.end.year >= self.start.year and self.end.month >= self.start.month and self.end.day > start.day:
+ differentDay = True
--- /dev/null
+class Group:
+ name = ""
+ description = ""
+ picture = "https://www.shutterstock.com/image-vector/computer-science-icon-outline-thin-600nw-1613513884.jpg"
+ leaders = []
+ members = []
+ events = []
+ updates = []
+ discussions = []
+ projects = []
+
+ def __init__(self, name, description, events, updates, discussions, projects, members):
+ self.name = name
+ self.description = description
+ self.events = events
+ self.updates = updates
+ self.discussions = discussions
+ self.projects = projects
+ for member in members:
+ if member.leader:
+ self.leaders.append(member)
+ continue
+ self.members.append(member)
+
\ No newline at end of file
--- /dev/null
+class Member:
+ name = ""
+ leader = False
+
+ def __init__(self, name = "", leader = False):
+ self.name = name
+ self.leader = leader
--- /dev/null
+class Project:
+ name = ""
+ description = ""
+ authors = []
+ source = None
+ images = None
+
+ def __init__(self, name, description, authors, source = None, images = None):
+ self.name = name
+ self.description = description
+ self.authors = authors
+ self.source = source
+ self.images = images
--- /dev/null
+from datetime import datetime
+from member import Member
+
+class Update:
+ member = Member()
+ text = ""
+ time = datetime.now()
+
+ def __init__(self, member, text, time):
+ self.member = member
+ self.text = text
+ self.time = time
--- /dev/null
+from models import Project
+from database import Database, Data
+
+class ProjectDatabase:
+ def read(self):
+ projects = []
+ for project in Database().read()["projects"]:
+ name = project.get("name")
+ description = project.get("description")
+ authors = project.get("authors")
+ source = project.get("source")
+ images = project.get("images")
+ projects.append(Project(name, description, authors, source, images))
+ return projects
+
+ def write(self, projects):
+ with open("database.json", "w") as file:
+ file.write(encode(Data(projects)))
+
--- /dev/null
+import os
+from datetime import datetime
+from config import DOMAIN, GROUP_ID
+from models import Group, Event, Update, Discussion, Project, Member
+from schoolopy import Schoology, Auth
+
+class SchoologyAPI:
+ api = None
+ auth = Auth(os.environ["SCHOOLOGY_API_KEY"], os.environ["SCHOOLOGY_API_SECRET"], domain = DOMAIN)
+
+ def __init__(self):
+ self.api = Schoology(self.auth)
+ self.api.limit = 64
+
+ def name(self):
+ return self.api.get_group(GROUP_ID).title
+
+ def description(self):
+ return self.api.get_group(GROUP_ID).description
+
+ def events(self):
+ events = []
+ for event in self.api.get_group_events(GROUP_ID):
+ start = datetime.strptime(event.start, "%Y-%m-%d %H:%M:%S")
+ end = None
+ differentDay = False
+ if event.has_end:
+ end = datetime.strptime(event.end, "%Y-%m-%d %H:%M:%S")
+ event = Event(event.id, event.title, event.description, start, end)
+ events.append(event)
+ return events
+
+ def updates(self):
+ updates = []
+ for update in self.api.get_group_updates(GROUP_ID):
+ user = self.api.get_user(update.uid)
+ member = Member(user.name_display)
+ #time = datetime.utcfromtimestamp(int(update.created))
+ time = datetime.utcfromtimestamp(int(update.last_updated))
+ updates.append(Update(member, update.body, time))
+ return updates
+
+ def discussions(self):
+ discussions = []
+ for discussion in self.api.get_group_discussions(GROUP_ID):
+ discussions.append(Discussion(discussion.id, discussion.title, discussion.body))
+ return discussions
+
+ def members(self):
+ members = []
+ for enrolled in self.api.get_group_enrollments(GROUP_ID):
+ member = Member(enrolled.name_display)
+ if enrolled.admin == 1:
+ member.leader = True
+ members.append(member)
+ return members
+
+ def group(self):
+ name = self.name()
+ description = self.description()
+ events = self.events()
+ updates = self.updates()
+ discussions = self.discussions()
+ projects = [
+ Project("Computer Science Club Website", "(this website)", ["Damian Myrda"], "https://replit.com/@Moncheeto/Computer-Science-Club?v=1")
+ ]
+ members = self.members()
+ return Group(name, description, events, updates, discussions, projects, members)
+
+api = SchoologyAPI()
+group = api.group()
--- /dev/null
+* {
+ color: #FFFF82;
+ text-align: center;
+ background-color: black;
+}
+
+#board {
+ font-family: sans-serif;
+ transform: perspective(300px) rotateX(25deg);
+ transform-origin: 50% 100%;
+ text-align: justify;
+ position: absolute;
+ margin-left: -9em;
+ font-weight: bold;
+ overflow: hidden;
+ font-size: 350%;
+ height: 50em;
+ width: 18em;
+ bottom: 0;
+ left: 50%;
+}
+
+#board:after {
+ position: absolute;
+ content: ' ';
+ bottom: 60%;
+ left: 0;
+ right: 0;
+ top: 0;
+}
+
+#content {
+ animation: scroll 128s;
+ position: absolute;
+ top: 100%;
+}
+
+@keyframes scroll {
+ 0% {
+ top: 100%;
+ }
+ 100% {
+ top: -350%;
+ }
+}
+
+#back {
+ color: white;
+}
+
+#back.hidden {
+ opacity: 0;
+}
+
+#back.shown {
+ opacity: 1;
+ transition: opacity 2000ms;
+}
--- /dev/null
+* {
+ color: white;
+ font-family: monospace;
+}
+
+body {
+ background-color: black;
+}
+
+input, textarea {
+ color: black;
+}
+
+center {
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%,-50%);
+ position: absolute;
+}
--- /dev/null
+<html>
+<head>
+ {% import "elements/metadata.html" as metadata %}
+ {{ metadata.head(group, "Discussions") }}
+ <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
+</head>
+
+<header>
+ {% import "elements/navigation.html" as navigation %}
+ {{ navigation.bar() }}
+</header>
+
+<body>
+ {% import "elements/space.html" as space %}
+ {{ space.stars(500) }}
+
+ <h1>Discussions</h1>
+ {% import "elements/discussions.html" as discussions %}
+ {{ discussions.list(group.discussions) }}
+</body>
+</html>
--- /dev/null
+{% macro view(author) -%}
+<a>{{ author }}</a>
+{%- endmacro %}
+
+{% macro list(authors) -%}
+<div>
+ By: {{ view(authors[0]) }}{% for author in authors[1:] %}, {{ author }}{% endfor %}
+</div>
+{%- endmacro %}
--- /dev/null
+{% macro time(time) -%}
+ {{ time.hour }}:{{ time.minute }}
+{%- endmacro %}
+
+{% macro date(date) -%}
+ {{ date.year }}.{{ date.month }}.{{ date.day }}
+{%- endmacro %}
--- /dev/null
+{% macro view(discussion) -%}
+ <a target="_blank" href="{{ discussion.link }}">
+ {{ discussion.name }}
+ {{ discussion.description }}
+ </a>
+{%- endmacro %}
+
+{% macro list(discussions) -%}
+{% for discussion in discussions %}
+<div>
+ {{ view(discussion) }}
+</div>
+{% endfor %}
+{%- endmacro %}
--- /dev/null
+{% import "elements/datetime.html" as datetime %}
+
+{% macro view(event) -%}
+<a target="_blank" href="{{ event.link }}">
+ {{ event.name }}
+
+ at {{ datetime.time(event.start) }}
+ {% if event.end %}
+ to {{ datetime.time(event.end) }}
+ {% endif %}
+
+ on {{ datetime.date(event.start) }}
+ {% if event.end %}
+ {% if event.differentDay %}
+ to {{ datetime.date(event.end) }}
+ {% endif %}
+ {% endif %}
+</a>
+{%- endmacro %}
+
+{% macro next(event) -%}
+<a target="_blank" href="{{ event.link }}">
+ Upcoming:
+ {{ event.name }}
+
+ at {{ datetime.time(event.start) }}
+ {% if event.end %}
+ to {{ datetime.time(event.end) }}
+ {% endif %}
+
+ on {{ datetime.date(event.start) }}
+ {% if event.end %}
+ {% if event.differentDay %}
+ to {{ datetime.date(event.end) }}
+ {% endif %}
+ {% endif %}
+</a>
+{%- endmacro %}
+
+{% macro upcoming(events) -%}
+{% for event in events %}
+<div>
+ view(event)
+</div>
+{% endfor %}
+{%- endmacro %}
--- /dev/null
+{% macro head(group, subsection) -%}
+ <title>{{ group.name }}
+ {% if subsection %}
+ - {{ subsection }}
+ {% endif %}
+ </title>
+ <link rel="icon" type="image/x-icon" href="{{ group.picture }}">
+{%- endmacro %}
--- /dev/null
+{% macro bar() -%}
+<nav>
+ <a href="/">Home</a>
+ <a href="/updates">Updates</a>
+ <a href="/discussions">Discussions</a>
+ <a href="/projects">Projects</a>
+ <a href="/members">Members</a>
+</nav>
+{%- endmacro %}
--- /dev/null
+{% import "elements/authors.html" as authors %}
+
+{% macro view(project) -%}
+ <h2>{{ project.name }}</h2>
+ <p>{{ project.description }}</p>
+ {% if project.source %}
+ <a target="_blank" href="{{ project.source }}">Source Code</a>
+ <br>
+ {% endif %}
+ {{ authors.list(project.authors) }}
+ {% if project.images %}
+ {% for image in project.images %}
+ <img src="{{ image }}">
+ {% endfor %}
+ {% endif %}
+{%- endmacro %}
+
+{% macro list(projects) -%}
+{% for project in projects %}
+<div>
+ {{ view(project) }}
+ <br>
+</div>
+{% endfor %}
+{%- endmacro %}
+
+{% macro new() -%}
+<center>
+ <h1>Add New Project</h1>
+ <form method="post" enctype="multipart/form-data">
+ <label for="name">Name:</label>
+ <input name="name" id="name" required>
+ <br>
+ <label for="description">Description:</label>
+ <textarea name="description" id="description" required></textarea>
+ <br>
+ <label for="authors">Authors:</label>
+ <input name="authors" id="authors" required>
+ <br>
+ <label for="source">Source:</label>
+ <input name="source" id="source">
+ <br>
+ <label for="images">Images:</label>
+ <input name="images" id="images" type="file" accept="image/*" multiple>
+ <br>
+ <input type="submit" value="Add">
+ </form>
+</center>
+{%- endmacro %}
--- /dev/null
+{% macro stars(num) -%}
+<style>
+ #star {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ background-color: white;
+ }
+</style>
+<script>
+ for (let i = 0; i < {{ num }}; i++) {
+ let star = document.createElement("div");
+ star.id = "star";
+ var [w, h] = randomPosition();
+ star.style.top = h + "px";
+ star.style.left = w + "px";
+ document.body.append(star);
+ }
+
+ function randomPosition() {
+ var rw = Math.floor(Math.random() * window.innerWidth);
+ var rh = Math.floor(Math.random() * window.innerHeight);
+ return [rw, rh];
+ }
+</script>
+{%- endmacro %}
--- /dev/null
+{% import "elements/datetime.html" as datetime %}
+
+{% macro view(update) -%}
+ <p>{{ update.member.name }} on {{ datetime.date(update.time) }} at {{ datetime.time(update.time) }}: {{ update.text }}</p>
+{%- endmacro %}
+
+{% macro recent(update) -%}
+ <p>Annocement from {{ update.member.name }} on {{ datetime.date(update.time) }} at {{ datetime.time(update.time) }}: {{ update.text }}</p>
+{%- endmacro %}
+
+{% macro list(updates) -%}
+{% for update in updates %}
+<div>
+ {{ view(update) }}
+</div>
+{% endfor %}
+{%- endmacro %}
--- /dev/null
+<html>
+<head>
+ {% import "elements/metadata.html" as metadata %}
+ {{ metadata.head(group)}}
+ <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
+</head>
+
+<header>
+ {% import "elements/navigation.html" as navigation %}
+ {{ navigation.bar() }}
+</header>
+
+<body>
+ {% import "elements/space.html" as space %}
+ {{ space.stars(500) }}
+ <center>
+ <h1>{{ group.name }}</h1>
+ <p>{{ group.description }}</p>
+
+ {% import "elements/events.html" as events %}
+ {{ events.next(group.events[0]) }}
+
+ {% import "elements/updates.html" as updates %}
+ {{ updates.recent(group.updates[0]) }}
+ </center>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+ {% import "elements/metadata.html" as metadata %}
+ {{ metadata.head(group, "Members") }}
+ <link rel="stylesheet" href="{{ url_for('static', filename='intro.css') }}">
+</head>
+
+<body>
+ {% import "elements/space.html" as space %}
+ {{ space.stars(200) }}
+ <div id="board">
+ <div id="content">
+ <center>
+ <h1>COMPUTER</h1>
+ <h1>SCIENCE</h1>
+ <h1>CLUB</h1>
+ <h2>LEADERS</h2>
+ {% for leader in group.leaders %}
+ <p>{{ leader.name }}</p>
+ {% endfor %}
+ <br>
+ <h2>MEMBERS</h2>
+ {% for member in group.members %}
+ <p>{{ member.name }}</p>
+ {% endfor %}
+ <br>
+ <h2>CREATED BY</h2>
+ <p>Damian Myrda</p>
+ </center>
+ </div>
+ </div>
+ <a id="back" class="hidden" href="/">Back</a>
+ <script>
+ setTimeout(function() {
+ document.getElementById("back").className = "shown";
+ }, 8000);
+ </script>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+ {% import "elements/metadata.html" as metadata %}
+ {% if create %}
+ {{ metadata.head(group, "New Project") }}
+ {% else %}
+ {{ metadata.head(group, "Projects") }}
+ {% endif %}
+ <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
+</head>
+
+<header>
+ {% import "elements/navigation.html" as navigation %}
+ {{ navigation.bar() }}
+</header>
+
+<body>
+ {% import "elements/space.html" as space %}
+ {{ space.stars(500) }}
+
+ {% import "elements/projects.html" as projects %}
+ <h1>Projects</h1>
+ {{ projects.list(group.projects) }}
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+ {% import "elements/metadata.html" as metadata %}
+ {{ metadata.head(group, "Updates") }}
+ <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
+</head>
+
+<header>
+ {% import "elements/navigation.html" as navigation %}
+ {{ navigation.bar() }}
+</header>
+
+<body>
+ {% import "elements/space.html" as space %}
+ {{ space.stars(500) }}
+
+ <h1>Updates</h1>
+ {% import "elements/updates.html" as updates %}
+ {{ updates.list(group.updates) }}
+</body>
+</html>