Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
adf3f13a0d |
No files matched your search
BIN
Binary file not shown.
Whitespace-only changes.
+2
-107
@@ -1,5 +1,5 @@
|
||||
from database.db_connection import get_db
|
||||
from database.db_commands import register_user, create_program, get_all_programs
|
||||
from database.db_commands import register_user
|
||||
from datetime import datetime
|
||||
|
||||
SEED_SPECIES = [
|
||||
@@ -223,23 +223,6 @@ def init_accounts_database():
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
)
|
||||
""")
|
||||
conn.execute("""
|
||||
CREATE TABLE IF NOT EXISTS messages (
|
||||
message_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
sender_id INTEGER NOT NULL,
|
||||
receiver_id INTEGER NOT NULL,
|
||||
subject TEXT,
|
||||
body TEXT NOT NULL,
|
||||
is_read BOOLEAN DEFAULT 0,
|
||||
sender_deleted BOOLEAN DEFAULT 0,
|
||||
receiver_deleted BOOLEAN DEFAULT 0,
|
||||
parent_message_id INTEGER,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (sender_id) REFERENCES users(user_id),
|
||||
FOREIGN KEY (receiver_id) REFERENCES users(user_id),
|
||||
FOREIGN KEY (parent_message_id) REFERENCES messages(message_id)
|
||||
)
|
||||
""")
|
||||
print("Accounts database initialised.")
|
||||
|
||||
|
||||
@@ -310,97 +293,9 @@ def init_programs_database():
|
||||
""")
|
||||
print("Programs database tables initialised.")
|
||||
|
||||
def init_activities_database():
|
||||
activities_list = [
|
||||
{
|
||||
"title": "Build a Bird Feeder",
|
||||
"description": "Create a bird feeder using recycled materials.",
|
||||
"species": "Bird Conservation",
|
||||
"due_date": "12/04/2026",
|
||||
"difficulty": "Easy"
|
||||
},
|
||||
{
|
||||
"title": "Plant a Tree",
|
||||
"description": "Plant a tree in your garden or local area to help restore natural habitats and support wildlife.",
|
||||
"species": "Habitat Restoration",
|
||||
"due_date": "13/04/2026",
|
||||
"difficulty": "Medium"
|
||||
},
|
||||
{
|
||||
"title": "Organise a Conservation Awareness Campaign",
|
||||
"description": "Plan and deliver a campaign to raise awareness about endangered species.",
|
||||
"species": "Education & Conservation",
|
||||
"due_date": "3/05/2026",
|
||||
"difficulty": "Hard"
|
||||
}
|
||||
]
|
||||
|
||||
with get_db() as conn:
|
||||
|
||||
conn.execute("""
|
||||
CREATE TABLE IF NOT EXISTS activities (
|
||||
activity_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
title TEXT NOT NULL,
|
||||
description TEXT,
|
||||
species TEXT,
|
||||
due_date TEXT,
|
||||
difficulty TEXT,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
)
|
||||
""")
|
||||
print("Activities table created/found.")
|
||||
|
||||
for activity in activities_list:
|
||||
exists = conn.execute(
|
||||
"SELECT activity_id FROM activities WHERE title = ?",
|
||||
(activity["title"],)
|
||||
).fetchone()
|
||||
if exists:
|
||||
print(f" SKIP {activity['title']} (already exists)")
|
||||
continue
|
||||
|
||||
conn.execute("""
|
||||
INSERT INTO activities
|
||||
(title, description, species, due_date, difficulty, created_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?)
|
||||
""", (
|
||||
activity["title"],
|
||||
activity["description"],
|
||||
activity["species"],
|
||||
activity["due_date"],
|
||||
activity["difficulty"],
|
||||
datetime.now(),
|
||||
))
|
||||
|
||||
with get_db() as conn:
|
||||
conn.execute("""
|
||||
CREATE TABLE IF NOT EXISTS user_activities (
|
||||
user_id INTEGER NOT NULL,
|
||||
activity_id INTEGER NOT NULL,
|
||||
status TEXT DEFAULT 'in_progress',
|
||||
started_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (user_id, activity_id),
|
||||
FOREIGN KEY (user_id) REFERENCES users(user_id),
|
||||
FOREIGN KEY (activity_id) REFERENCES activities(activity_id)
|
||||
)
|
||||
""")
|
||||
print(f" ADD {activity['title']}")
|
||||
print("Activities seeding complete.")
|
||||
|
||||
|
||||
init_database()
|
||||
init_accounts_database()
|
||||
init_classes_database()
|
||||
init_programs_database()
|
||||
init_articles()
|
||||
init_activities_database()
|
||||
register_user("admin", "admin@komodohub.org", "admin", "admin")
|
||||
register_user("private", "private@komodohub.org", "private_user", "private")
|
||||
register_user("teacher", "teacher@komodohub.org", "teacher", "teacher")
|
||||
register_user("student", "student@komodohub.org", "student", "student")
|
||||
register_user("communityleader", "communityleader@komodohub.org", "community_leader", "community_leader")
|
||||
if len(get_all_programs()) < 1:
|
||||
create_program(leader_id=1, title="Sumatran Tiger Watch", description="Report sightings of Sumatran Tigers and help track population movement across Sumatra.")
|
||||
create_program(leader_id=1, title="Javan Rhino Guardians", description="Coordinate community efforts to report rhino activity and protect their remaining habitat in Ujung Kulon.")
|
||||
create_program(leader_id=1, title="Bali Myna Recovery Project", description="Monitor and protect the critically endangered Bali Myna by documenting nesting sites and sightings across Bali.")
|
||||
create_program(leader_id=1, title="Indonesia's Endemic Species Library", description="Contribute articles, essays, and columns about Indonesian endemic species to grow the public knowledge base.")
|
||||
register_user("admin", "admin@komodohub.org", "admin", "admin")
|
||||
@@ -1,8 +0,0 @@
|
||||
services:
|
||||
app:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
ports:
|
||||
- "7087:8000"
|
||||
restart: unless-stopped
|
||||
-26
@@ -1,26 +0,0 @@
|
||||
FROM --platform=$BUILDPLATFORM python:alpine AS builder
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY requirements.txt /app
|
||||
RUN --mount=type=cache,target=/root/.cache/pip \
|
||||
pip3 install -r requirements.txt
|
||||
|
||||
COPY . /app
|
||||
|
||||
ENTRYPOINT ["gunicorn"]
|
||||
CMD ["--bind","0.0.0.0:8000","main:app", "--timeout","120", "--access-logfile", "-", "--error-logfile", "-","--log-level","info"]
|
||||
|
||||
FROM builder as dev-envs
|
||||
|
||||
RUN <<EOF
|
||||
apk update
|
||||
apk add git
|
||||
EOF
|
||||
|
||||
RUN <<EOF
|
||||
addgroup -S docker
|
||||
adduser -S --shell /bin/bash --ingroup docker vscode
|
||||
EOF
|
||||
|
||||
COPY --from=gloursdocker/docker / /
|
||||
@@ -1,63 +0,0 @@
|
||||
<mxfile host="Electron" agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/29.6.6 Chrome/144.0.7559.236 Electron/40.8.4 Safari/537.36" version="29.6.6">
|
||||
<diagram name="Page-1" id="2wT2WU9NkvpTAcdHzYou">
|
||||
<mxGraphModel dx="1152" dy="834" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1600" pageHeight="1200" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0" />
|
||||
<mxCell id="1" parent="0" />
|
||||
<mxCell id="HZnixJc9v59I_crZTgQJ-1" parent="1" style="rounded=1;whiteSpace=wrap;html=1;" value="" vertex="1">
|
||||
<mxGeometry height="310" width="380" x="370" y="210" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="HZnixJc9v59I_crZTgQJ-2" parent="1" style="rounded=1;whiteSpace=wrap;html=1;" value="Let's Encrypt<div>(TLS / SSL certs)</div>" vertex="1">
|
||||
<mxGeometry height="40" width="100" x="400" y="230" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="HZnixJc9v59I_crZTgQJ-19" edge="1" parent="1" source="HZnixJc9v59I_crZTgQJ-3" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.25;exitY=1;exitDx=0;exitDy=0;entryX=0.75;entryY=1;entryDx=0;entryDy=0;dashed=1;" target="HZnixJc9v59I_crZTgQJ-4">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="HZnixJc9v59I_crZTgQJ-3" parent="1" style="rounded=1;whiteSpace=wrap;html=1;" value="nginx<div>(Reverse proxy)<br>(TLS termination)</div>" vertex="1">
|
||||
<mxGeometry height="60" width="120" x="390" y="330" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="HZnixJc9v59I_crZTgQJ-4" parent="1" style="rounded=1;whiteSpace=wrap;html=1;" value="Internet<div>(HTTPS request)</div>" vertex="1">
|
||||
<mxGeometry height="70" width="120" x="170" y="325" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="HZnixJc9v59I_crZTgQJ-5" edge="1" parent="1" source="HZnixJc9v59I_crZTgQJ-4" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" target="HZnixJc9v59I_crZTgQJ-3">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="HZnixJc9v59I_crZTgQJ-6" parent="1" style="text;html=1;whiteSpace=wrap;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;" value="Private Server" vertex="1">
|
||||
<mxGeometry height="30" width="100" x="510" y="180" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="HZnixJc9v59I_crZTgQJ-7" parent="1" style="rounded=1;whiteSpace=wrap;html=1;" value="" vertex="1">
|
||||
<mxGeometry height="195" width="180" x="550" y="262.5" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="HZnixJc9v59I_crZTgQJ-9" edge="1" parent="1" source="HZnixJc9v59I_crZTgQJ-3" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" target="HZnixJc9v59I_crZTgQJ-7">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="HZnixJc9v59I_crZTgQJ-10" parent="1" style="text;html=1;whiteSpace=wrap;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;" value="Docker Compose" vertex="1">
|
||||
<mxGeometry height="30" width="100" x="590" y="235" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="HZnixJc9v59I_crZTgQJ-15" edge="1" parent="1" source="HZnixJc9v59I_crZTgQJ-11" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" target="HZnixJc9v59I_crZTgQJ-12">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="HZnixJc9v59I_crZTgQJ-11" parent="1" style="rounded=1;whiteSpace=wrap;html=1;" value="Dockerfile<div>(Defines container image)</div>" vertex="1">
|
||||
<mxGeometry height="40" width="140" x="570" y="280" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="HZnixJc9v59I_crZTgQJ-17" edge="1" parent="1" source="HZnixJc9v59I_crZTgQJ-12" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" target="HZnixJc9v59I_crZTgQJ-16">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="HZnixJc9v59I_crZTgQJ-12" parent="1" style="rounded=1;whiteSpace=wrap;html=1;" value="Gunicorn<div>(WSGI server)</div>" vertex="1">
|
||||
<mxGeometry height="40" width="120" x="580" y="340" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="HZnixJc9v59I_crZTgQJ-18" edge="1" parent="1" source="HZnixJc9v59I_crZTgQJ-16" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;dashed=1;entryX=0.5;entryY=1;entryDx=0;entryDy=0;" target="HZnixJc9v59I_crZTgQJ-3">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="640" y="480" />
|
||||
<mxPoint x="450" y="480" />
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="HZnixJc9v59I_crZTgQJ-16" parent="1" style="rounded=1;whiteSpace=wrap;html=1;" value="Komodo Hub<div>(Application)</div>" vertex="1">
|
||||
<mxGeometry height="40" width="120" x="580" y="400" as="geometry" />
|
||||
</mxCell>
|
||||
</root>
|
||||
</mxGraphModel>
|
||||
</diagram>
|
||||
</mxfile>
|
||||
@@ -1,43 +0,0 @@
|
||||
<mxfile host="Electron" agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/29.6.6 Chrome/144.0.7559.236 Electron/40.8.4 Safari/537.36" version="29.6.6">
|
||||
<diagram name="Page-1" id="Y7i12buB9koV2zs5vw_M">
|
||||
<mxGraphModel dx="2066" dy="1248" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1600" pageHeight="1200" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0" />
|
||||
<mxCell id="1" parent="0" />
|
||||
<mxCell id="Y1GkeIaNaN_39Ro0tFyQ-1" parent="1" style="rounded=0;whiteSpace=wrap;html=1;" value="User Browser" vertex="1">
|
||||
<mxGeometry height="60" width="120" x="90" y="50" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Y1GkeIaNaN_39Ro0tFyQ-6" edge="1" parent="1" source="Y1GkeIaNaN_39Ro0tFyQ-2" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" target="Y1GkeIaNaN_39Ro0tFyQ-3">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Y1GkeIaNaN_39Ro0tFyQ-2" parent="1" style="rounded=0;whiteSpace=wrap;html=1;" value="Flask Web App" vertex="1">
|
||||
<mxGeometry height="60" width="120" x="90" y="170" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Y1GkeIaNaN_39Ro0tFyQ-7" edge="1" parent="1" source="Y1GkeIaNaN_39Ro0tFyQ-3" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" target="Y1GkeIaNaN_39Ro0tFyQ-4">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Y1GkeIaNaN_39Ro0tFyQ-3" parent="1" style="rounded=0;whiteSpace=wrap;html=1;" value="Webpage Logic<br>(inside the routes directory)" vertex="1">
|
||||
<mxGeometry height="60" width="120" x="90" y="290" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Y1GkeIaNaN_39Ro0tFyQ-8" edge="1" parent="1" source="Y1GkeIaNaN_39Ro0tFyQ-4" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;" target="Y1GkeIaNaN_39Ro0tFyQ-2">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="240" y="440" />
|
||||
<mxPoint x="240" y="200" />
|
||||
</Array>
|
||||
<mxPoint x="230" y="200" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="Y1GkeIaNaN_39Ro0tFyQ-4" parent="1" style="rounded=0;whiteSpace=wrap;html=1;" value="Jinja2 Templates" vertex="1">
|
||||
<mxGeometry height="60" width="120" x="90" y="410" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Y1GkeIaNaN_39Ro0tFyQ-11" edge="1" parent="1" source="Y1GkeIaNaN_39Ro0tFyQ-2" style="endArrow=classic;startArrow=classic;html=1;rounded=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;exitX=0.5;exitY=0;exitDx=0;exitDy=0;" target="Y1GkeIaNaN_39Ro0tFyQ-1" value="">
|
||||
<mxGeometry height="50" relative="1" width="50" as="geometry">
|
||||
<mxPoint x="10" y="180" as="sourcePoint" />
|
||||
<mxPoint x="60" y="130" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
</root>
|
||||
</mxGraphModel>
|
||||
</diagram>
|
||||
</mxfile>
|
||||
@@ -1,87 +0,0 @@
|
||||
<mxfile host="Electron" agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/29.6.6 Chrome/144.0.7559.236 Electron/40.8.4 Safari/537.36" version="29.6.6">
|
||||
<diagram name="Page-1" id="2wT2WU9NkvpTAcdHzYou">
|
||||
<mxGraphModel dx="794" dy="575" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1600" pageHeight="1200" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0" />
|
||||
<mxCell id="1" parent="0" />
|
||||
<mxCell id="HZnixJc9v59I_crZTgQJ-1" parent="1" style="rounded=1;whiteSpace=wrap;html=1;" value="" vertex="1">
|
||||
<mxGeometry height="310" width="380" x="370" y="210" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="HZnixJc9v59I_crZTgQJ-30" edge="1" parent="1" source="HZnixJc9v59I_crZTgQJ-2" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;dashed=1;dashPattern=8 4 1 4;" target="HZnixJc9v59I_crZTgQJ-3">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="HZnixJc9v59I_crZTgQJ-2" parent="1" style="rounded=1;whiteSpace=wrap;html=1;" value="Let's Encrypt<div>(TLS / SSL certs)</div>" vertex="1">
|
||||
<mxGeometry height="40" width="100" x="400" y="230" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="HZnixJc9v59I_crZTgQJ-19" edge="1" parent="1" source="HZnixJc9v59I_crZTgQJ-3" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.25;exitY=1;exitDx=0;exitDy=0;entryX=0.75;entryY=1;entryDx=0;entryDy=0;dashed=1;" target="HZnixJc9v59I_crZTgQJ-4">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="HZnixJc9v59I_crZTgQJ-3" parent="1" style="rounded=1;whiteSpace=wrap;html=1;" value="nginx<div>(Reverse proxy)<br>(TLS termination)</div>" vertex="1">
|
||||
<mxGeometry height="60" width="120" x="390" y="330" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="HZnixJc9v59I_crZTgQJ-4" parent="1" style="rounded=1;whiteSpace=wrap;html=1;" value="Internet<div>(HTTPS request)</div>" vertex="1">
|
||||
<mxGeometry height="70" width="120" x="170" y="325" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="HZnixJc9v59I_crZTgQJ-5" edge="1" parent="1" source="HZnixJc9v59I_crZTgQJ-4" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" target="HZnixJc9v59I_crZTgQJ-3">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="HZnixJc9v59I_crZTgQJ-6" parent="1" style="text;html=1;whiteSpace=wrap;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;" value="Private Server" vertex="1">
|
||||
<mxGeometry height="30" width="100" x="510" y="180" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="HZnixJc9v59I_crZTgQJ-7" parent="1" style="rounded=1;whiteSpace=wrap;html=1;" value="" vertex="1">
|
||||
<mxGeometry height="195" width="180" x="550" y="262.5" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="HZnixJc9v59I_crZTgQJ-9" edge="1" parent="1" source="HZnixJc9v59I_crZTgQJ-3" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" target="HZnixJc9v59I_crZTgQJ-7">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="HZnixJc9v59I_crZTgQJ-10" parent="1" style="text;html=1;whiteSpace=wrap;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;" value="Docker Compose" vertex="1">
|
||||
<mxGeometry height="30" width="100" x="590" y="235" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="HZnixJc9v59I_crZTgQJ-15" edge="1" parent="1" source="HZnixJc9v59I_crZTgQJ-11" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" target="HZnixJc9v59I_crZTgQJ-12">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="HZnixJc9v59I_crZTgQJ-11" parent="1" style="rounded=1;whiteSpace=wrap;html=1;" value="Dockerfile<div>(Defines container image)</div>" vertex="1">
|
||||
<mxGeometry height="40" width="140" x="570" y="280" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="HZnixJc9v59I_crZTgQJ-17" edge="1" parent="1" source="HZnixJc9v59I_crZTgQJ-12" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" target="HZnixJc9v59I_crZTgQJ-16">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="HZnixJc9v59I_crZTgQJ-12" parent="1" style="rounded=1;whiteSpace=wrap;html=1;" value="Gunicorn<div>(WSGI server)</div>" vertex="1">
|
||||
<mxGeometry height="40" width="120" x="580" y="340" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="HZnixJc9v59I_crZTgQJ-18" edge="1" parent="1" source="HZnixJc9v59I_crZTgQJ-16" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;dashed=1;entryX=0.5;entryY=1;entryDx=0;entryDy=0;" target="HZnixJc9v59I_crZTgQJ-3">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="640" y="480" />
|
||||
<mxPoint x="450" y="480" />
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="HZnixJc9v59I_crZTgQJ-16" parent="1" style="rounded=1;whiteSpace=wrap;html=1;" value="Komodo Hub<div>(Application)</div>" vertex="1">
|
||||
<mxGeometry height="40" width="120" x="580" y="400" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="HZnixJc9v59I_crZTgQJ-22" parent="1" style="rounded=0;whiteSpace=wrap;html=1;" value="" vertex="1">
|
||||
<mxGeometry height="70" width="120" x="630" y="570" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="HZnixJc9v59I_crZTgQJ-24" edge="1" parent="1" style="endArrow=classic;html=1;rounded=0;" value="">
|
||||
<mxGeometry height="50" relative="1" width="50" as="geometry">
|
||||
<mxPoint x="700" y="582.36" as="sourcePoint" />
|
||||
<mxPoint x="730" y="582.36" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="HZnixJc9v59I_crZTgQJ-25" parent="1" style="text;html=1;whiteSpace=wrap;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;fontSize=8;" value="Request" vertex="1">
|
||||
<mxGeometry height="25" width="50" x="650" y="570" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="HZnixJc9v59I_crZTgQJ-26" edge="1" parent="1" style="endArrow=classic;html=1;rounded=0;dashed=1;" value="">
|
||||
<mxGeometry height="50" relative="1" width="50" as="geometry">
|
||||
<mxPoint x="700" y="620" as="sourcePoint" />
|
||||
<mxPoint x="730" y="620" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="HZnixJc9v59I_crZTgQJ-27" parent="1" style="text;html=1;whiteSpace=wrap;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;fontSize=8;" value="Response" vertex="1">
|
||||
<mxGeometry height="25" width="50" x="650" y="610" as="geometry" />
|
||||
</mxCell>
|
||||
</root>
|
||||
</mxGraphModel>
|
||||
</diagram>
|
||||
</mxfile>
|
||||
+2
-3
@@ -2,9 +2,8 @@ bcrypt==5.0.0
|
||||
blinker==1.9.0
|
||||
click==8.3.1
|
||||
Flask==3.1.2
|
||||
gunicorn==25.3.0
|
||||
itsdangerous==2.2.0
|
||||
Jinja2==3.1.6
|
||||
MarkupSafe==3.0.3
|
||||
packaging==26.0
|
||||
Werkzeug==3.1.5
|
||||
Werkzeug==3.1.5
|
||||
psycopg2-binary==2.9.10
|
||||
@@ -1,79 +0,0 @@
|
||||
from flask import render_template, Blueprint, g, request, redirect, url_for
|
||||
from database.db_connection import get_db
|
||||
|
||||
page = Blueprint('activities', __name__, url_prefix='/activities')
|
||||
|
||||
@page.route('/', methods=['GET', 'POST'])
|
||||
def home():
|
||||
if request.method == 'POST':
|
||||
if g.user and g.user["account_type"] == "admin":
|
||||
with get_db() as conn:
|
||||
conn.execute("""
|
||||
INSERT INTO activities
|
||||
(title, description, species, due_date, difficulty)
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
""", (
|
||||
request.form['title'],
|
||||
request.form['description'],
|
||||
request.form['species'],
|
||||
request.form['due_date'],
|
||||
request.form['difficulty']
|
||||
))
|
||||
return redirect(url_for('activities.home'))
|
||||
|
||||
with get_db() as conn:
|
||||
activities = conn.execute("SELECT * FROM activities").fetchall()
|
||||
|
||||
started_ids = []
|
||||
if g.user:
|
||||
started = conn.execute("""
|
||||
SELECT activity_id FROM user_activities
|
||||
WHERE user_id = ?
|
||||
""", (g.user["user_id"],)).fetchall()
|
||||
|
||||
started_ids = [row["activity_id"] for row in started]
|
||||
|
||||
return render_template(
|
||||
'activities/activities.jinja',
|
||||
activities=activities,
|
||||
started_ids=started_ids
|
||||
)
|
||||
|
||||
|
||||
@page.route('/start/<int:activity_id>')
|
||||
def start_activity(activity_id):
|
||||
if not g.user:
|
||||
return redirect(url_for('login.login'))
|
||||
|
||||
with get_db() as conn:
|
||||
exists = conn.execute("""
|
||||
SELECT * FROM user_activities
|
||||
WHERE user_id = ? AND activity_id = ?
|
||||
""", (g.user["user_id"], activity_id)).fetchone()
|
||||
|
||||
if not exists:
|
||||
conn.execute("""
|
||||
INSERT INTO user_activities (user_id, activity_id)
|
||||
VALUES (?, ?)
|
||||
""", (g.user["user_id"], activity_id))
|
||||
|
||||
return redirect(url_for('activities.home'))
|
||||
|
||||
|
||||
@page.route('/my-activities')
|
||||
def my_activities():
|
||||
if not g.user:
|
||||
return redirect(url_for('login.login'))
|
||||
|
||||
with get_db() as conn:
|
||||
activities = conn.execute("""
|
||||
SELECT a.*, ua.started_at, ua.completed
|
||||
FROM user_activities ua
|
||||
JOIN activities a ON ua.activity_id = a.activity_id
|
||||
WHERE ua.user_id = ?
|
||||
""", (g.user["user_id"],)).fetchall()
|
||||
|
||||
return render_template(
|
||||
'activities/my_activities.jinja',
|
||||
activities=activities
|
||||
)
|
||||
@@ -0,0 +1,15 @@
|
||||
CREATE TABLE IF NOT EXISTS messages (
|
||||
message_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
sender_id INTEGER NOT NULL,
|
||||
receiver_id INTEGER NOT NULL,
|
||||
subject TEXT,
|
||||
body TEXT NOT NULL,
|
||||
is_read BOOLEAN DEFAULT 0,
|
||||
sender_deleted BOOLEAN DEFAULT 0,
|
||||
receiver_deleted BOOLEAN DEFAULT 0,
|
||||
parent_message_id INTEGER,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (sender_id) REFERENCES users(user_id),
|
||||
FOREIGN KEY (receiver_id) REFERENCES users(user_id),
|
||||
FOREIGN KEY (parent_message_id) REFERENCES messages(message_id)
|
||||
);
|
||||
@@ -1,71 +0,0 @@
|
||||
{% extends "base.jinja" %}
|
||||
{% block title %}Activities{% endblock %}
|
||||
{% block content %}
|
||||
{% from 'button.jinja' import button %}
|
||||
|
||||
{% block head %}
|
||||
<style>
|
||||
.activity-container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
gap: 30px;
|
||||
margin-top: 40px;
|
||||
}
|
||||
|
||||
.activity-card {
|
||||
background: #0b5d2a;
|
||||
padding: 25px;
|
||||
border-radius: 12px;
|
||||
width: 300px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
<section style="padding: 50px 20px; text-align: center;">
|
||||
<h1>Activities</h1>
|
||||
|
||||
<!-- Activities List -->
|
||||
<div class="activity-container">
|
||||
{% for activity in activities %}
|
||||
<div class="activity-card">
|
||||
<h3>{{ activity.title }}</h3>
|
||||
<p>{{ activity.description }}</p>
|
||||
<p><strong>Category:</strong> {{ activity.species }}</p>
|
||||
<p><strong>Due date:</strong> {{ activity.due_date }}</p>
|
||||
<p><strong>Difficulty:</strong> {{ activity.difficulty }}</p>
|
||||
|
||||
{% if activity.activity_id in started_ids %}
|
||||
<p><strong>In Progress</strong></p>
|
||||
{% else %}
|
||||
{{ button(url=url_for('activities.start_activity', activity_id=activity.activity_id), button_text='Start Activity', extra_classes='homepage-btn') }}
|
||||
</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ADMIN ONLY SECTION -->
|
||||
{% if g.user and g.user["account_type"] == "admin" %}
|
||||
<section style="padding: 50px 20px; text-align: center; background-color: #eee;">
|
||||
<h2>Create New Activity (Admin)</h2>
|
||||
|
||||
<form method="POST" class="activity-form">
|
||||
<input type="text" name="title" placeholder="Activity Title" required><br><br>
|
||||
|
||||
<textarea name="description" placeholder="Description" required></textarea><br><br>
|
||||
|
||||
<input type="text" name="species" placeholder="Species / Program" required><br><br>
|
||||
|
||||
<input type="number" name="due_date" placeholder="due_date"><br><br>
|
||||
|
||||
<input type="text" name="difficulty" placeholder="difficulty"><br><br>
|
||||
|
||||
<button type="submit" class="homepage-btn">Publish Activity</button>
|
||||
</form>
|
||||
</section>
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
||||
@@ -16,7 +16,6 @@
|
||||
{{ button(url='/donation', button_text='Donate Now', extra_classes='homepage-btn') }}
|
||||
{{ button(url='/species', button_text='Explore Species', extra_classes='homepage-btn') }}
|
||||
{{ button(url='/register', button_text='Join the Movement', extra_classes='homepage-btn') }}
|
||||
{{ button(url='/activities', button_text ='Activities', extra_classes='homepage-btn')}}
|
||||
</div>
|
||||
</section>
|
||||
<!-- About section -->
|
||||
|
||||
Reference in new issue
Block a user