]> prime8.dev >> repos - csc.git/commitdiff
Add all source files
authorDamian Myrda <monkey.damianek@gmail.com>
Wed, 24 Apr 2024 19:44:36 +0000 (14:44 -0500)
committerDamian Myrda <monkey.damianek@gmail.com>
Wed, 24 Apr 2024 19:44:36 +0000 (14:44 -0500)
30 files changed:
.gitignore [new file with mode: 0755]
config.py [new file with mode: 0644]
database.json [new file with mode: 0644]
database.py [new file with mode: 0644]
main.py [new file with mode: 0644]
models/__init__.py [new file with mode: 0644]
models/discussion.py [new file with mode: 0644]
models/event.py [new file with mode: 0644]
models/group.py [new file with mode: 0644]
models/member.py [new file with mode: 0644]
models/project.py [new file with mode: 0644]
models/update.py [new file with mode: 0644]
projects.py [new file with mode: 0644]
schoology.py [new file with mode: 0644]
static/intro.css [new file with mode: 0644]
static/style.css [new file with mode: 0644]
templates/discussions.html [new file with mode: 0644]
templates/elements/authors.html [new file with mode: 0644]
templates/elements/datetime.html [new file with mode: 0644]
templates/elements/discussions.html [new file with mode: 0644]
templates/elements/events.html [new file with mode: 0644]
templates/elements/metadata.html [new file with mode: 0644]
templates/elements/navigation.html [new file with mode: 0644]
templates/elements/projects.html [new file with mode: 0644]
templates/elements/space.html [new file with mode: 0644]
templates/elements/updates.html [new file with mode: 0644]
templates/home.html [new file with mode: 0644]
templates/members.html [new file with mode: 0644]
templates/projects.html [new file with mode: 0644]
templates/updates.html [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100755 (executable)
index 0000000..4f43ac1
--- /dev/null
@@ -0,0 +1,5 @@
+lib/
+lib64
+bin/
+__pycache__/
+pyvenv.cfg
diff --git a/config.py b/config.py
new file mode 100644 (file)
index 0000000..0dbfb30
--- /dev/null
+++ b/config.py
@@ -0,0 +1,2 @@
+DOMAIN = "https://schoology.d214.org"
+GROUP_ID = 6454678062
diff --git a/database.json b/database.json
new file mode 100644 (file)
index 0000000..5184b30
--- /dev/null
@@ -0,0 +1 @@
+{"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
diff --git a/database.py b/database.py
new file mode 100644 (file)
index 0000000..785a90a
--- /dev/null
@@ -0,0 +1,31 @@
+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))
diff --git a/main.py b/main.py
new file mode 100644 (file)
index 0000000..cc0f10e
--- /dev/null
+++ b/main.py
@@ -0,0 +1,34 @@
+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)
diff --git a/models/__init__.py b/models/__init__.py
new file mode 100644 (file)
index 0000000..738d3f9
--- /dev/null
@@ -0,0 +1,6 @@
+from discussion import Discussion
+from event import Event
+from group import Group
+from member import Member
+from project import Project
+from update import Update
diff --git a/models/discussion.py b/models/discussion.py
new file mode 100644 (file)
index 0000000..45275f2
--- /dev/null
@@ -0,0 +1,30 @@
+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
diff --git a/models/event.py b/models/event.py
new file mode 100644 (file)
index 0000000..e317953
--- /dev/null
@@ -0,0 +1,19 @@
+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
diff --git a/models/group.py b/models/group.py
new file mode 100644 (file)
index 0000000..16e5e39
--- /dev/null
@@ -0,0 +1,24 @@
+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
diff --git a/models/member.py b/models/member.py
new file mode 100644 (file)
index 0000000..e00e709
--- /dev/null
@@ -0,0 +1,7 @@
+class Member:
+       name = ""
+       leader = False
+
+       def __init__(self, name = "", leader = False):
+               self.name = name
+               self.leader = leader
diff --git a/models/project.py b/models/project.py
new file mode 100644 (file)
index 0000000..11f0f17
--- /dev/null
@@ -0,0 +1,13 @@
+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
diff --git a/models/update.py b/models/update.py
new file mode 100644 (file)
index 0000000..5507110
--- /dev/null
@@ -0,0 +1,12 @@
+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
diff --git a/projects.py b/projects.py
new file mode 100644 (file)
index 0000000..fb558f6
--- /dev/null
@@ -0,0 +1,19 @@
+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)))
+               
diff --git a/schoology.py b/schoology.py
new file mode 100644 (file)
index 0000000..7ea51d9
--- /dev/null
@@ -0,0 +1,71 @@
+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()
diff --git a/static/intro.css b/static/intro.css
new file mode 100644 (file)
index 0000000..3597bfc
--- /dev/null
@@ -0,0 +1,58 @@
+* {
+       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;
+}
diff --git a/static/style.css b/static/style.css
new file mode 100644 (file)
index 0000000..c0f081e
--- /dev/null
@@ -0,0 +1,19 @@
+* {
+       color: white;
+       font-family: monospace;
+}
+
+body {
+       background-color: black;
+}
+
+input, textarea {
+       color: black;
+}
+
+center {
+       top: 50%;
+       left: 50%;
+       transform: translate(-50%,-50%);
+       position: absolute;
+}
diff --git a/templates/discussions.html b/templates/discussions.html
new file mode 100644 (file)
index 0000000..ebfe387
--- /dev/null
@@ -0,0 +1,21 @@
+<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>
diff --git a/templates/elements/authors.html b/templates/elements/authors.html
new file mode 100644 (file)
index 0000000..3a6f82c
--- /dev/null
@@ -0,0 +1,9 @@
+{% macro view(author) -%}
+<a>{{ author }}</a>
+{%- endmacro %}
+
+{% macro list(authors) -%}
+<div>
+       By: {{ view(authors[0]) }}{% for author in authors[1:] %}, {{ author }}{% endfor %}
+</div>
+{%- endmacro %}
diff --git a/templates/elements/datetime.html b/templates/elements/datetime.html
new file mode 100644 (file)
index 0000000..5218411
--- /dev/null
@@ -0,0 +1,7 @@
+{% macro time(time) -%}
+               {{ time.hour }}:{{ time.minute }}
+{%- endmacro %}
+
+{% macro date(date) -%}
+       {{ date.year }}.{{ date.month }}.{{ date.day }}
+{%- endmacro %}
diff --git a/templates/elements/discussions.html b/templates/elements/discussions.html
new file mode 100644 (file)
index 0000000..b1c563d
--- /dev/null
@@ -0,0 +1,14 @@
+{% 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 %}
diff --git a/templates/elements/events.html b/templates/elements/events.html
new file mode 100644 (file)
index 0000000..344cb14
--- /dev/null
@@ -0,0 +1,46 @@
+{% 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 %}
diff --git a/templates/elements/metadata.html b/templates/elements/metadata.html
new file mode 100644 (file)
index 0000000..70d9d79
--- /dev/null
@@ -0,0 +1,8 @@
+{% macro head(group, subsection) -%}
+       <title>{{ group.name }}
+       {% if subsection %}
+               - {{ subsection }}
+       {% endif %}
+       </title>
+       <link rel="icon" type="image/x-icon" href="{{ group.picture }}">
+{%- endmacro %}
diff --git a/templates/elements/navigation.html b/templates/elements/navigation.html
new file mode 100644 (file)
index 0000000..61d3e72
--- /dev/null
@@ -0,0 +1,9 @@
+{% 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 %}
diff --git a/templates/elements/projects.html b/templates/elements/projects.html
new file mode 100644 (file)
index 0000000..b9e3f4a
--- /dev/null
@@ -0,0 +1,49 @@
+{% 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 %}
diff --git a/templates/elements/space.html b/templates/elements/space.html
new file mode 100644 (file)
index 0000000..f725d8f
--- /dev/null
@@ -0,0 +1,26 @@
+{% 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 %}
diff --git a/templates/elements/updates.html b/templates/elements/updates.html
new file mode 100644 (file)
index 0000000..f9df6f9
--- /dev/null
@@ -0,0 +1,17 @@
+{% 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 %}
diff --git a/templates/home.html b/templates/home.html
new file mode 100644 (file)
index 0000000..6d478b4
--- /dev/null
@@ -0,0 +1,27 @@
+<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>
diff --git a/templates/members.html b/templates/members.html
new file mode 100644 (file)
index 0000000..5fd07b9
--- /dev/null
@@ -0,0 +1,39 @@
+<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>
diff --git a/templates/projects.html b/templates/projects.html
new file mode 100644 (file)
index 0000000..dadbca5
--- /dev/null
@@ -0,0 +1,25 @@
+<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>
diff --git a/templates/updates.html b/templates/updates.html
new file mode 100644 (file)
index 0000000..dfc3477
--- /dev/null
@@ -0,0 +1,21 @@
+<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>