During my GCSEs I kept running into the same problem: plenty of great resources existed, but never in one place. Revology is my answer — a Django platform where students can build a shared library of notes and flashcards instead of starting from scratch every exam season.
One of the main problems as a student was figuring out which platforms to use, what to use them for, and how to keep all my revision together. So Revology became a library of resources, where a student could revise, track progress and access everything else from one hub — flashcards and notes, growing with every user who contributes to it.
Before writing a line of code, I surveyed 78 students from Years 7–12. 83% didn't feel completely sure how to revise. 65% made their own notes, and a further 18% relied on pre-made ones. 58% said finding pre-made notes for their subjects "requires a bit of digging." That gap — a common platform where resources can be accessed from one place — is exactly what Revology tries to close.
I was careful about the limits of that data too: the sample came from one independent, academically competitive school, so if anything the national picture is probably worse than what I found.
Before any Python was written, every table was mapped out with its primary and foreign keys — Users, Notes, Flashcard Sets, Flashcards, and an Activity table to track usage. Notes and flashcards both have a many-to-one relationship with a user (the author) and with a subject; flashcards also belong to a flashcard set. That structure translated almost directly into the Django models below.
from django.db import models
from django.contrib.auth.models import User
class Notes(models.Model):
notes_content = models.TextField()
author = models.ForeignKey(User, on_delete=models.CASCADE, related_name='authored_notes')
subject = models.ForeignKey(Subject, on_delete=models.CASCADE)
title = models.CharField(max_length=30)
date_uploaded = models.DateTimeField('date uploaded', auto_now_add=True)
image = models.ImageField(null=True, blank=True)
def __str__(self):
return f'{self.id}: {self.title} ({self.subject})'
class Flashcard(models.Model):
front_of_card = models.TextField(max_length=2000)
back_of_card = models.TextField(max_length=2000)
flashcard_set = models.ForeignKey(Flashcard_set, on_delete=models.CASCADE, related_name='flashcards')
def get_rag(self, user):
# returns red/amber/green confidence rating for this user
try:
return Flashcard_rating.objects.filter(flashcard=self, user=user)[0].status.colour
except IndexError:
return Status.red
Two of the eight models in supernova/models.py — Notes and Flashcard, with the RAG (red/amber/green) confidence rating logic that drives the flashcard filtering feature.
Two decisions shaped the whole build, and I want to be upfront about why I made them rather than just listing the stack.
| decision | option A | option B (chosen) | why |
|---|---|---|---|
| Web framework | Flask | Django | Built-in user authentication and admin interface meant more time for Revology's actual features, not boilerplate. |
| CSS framework | Bootstrap | Bulma | No JS dependencies to fight with on a Python-first project, and cleaner documentation. |
Almost everything is server-rendered Python and Django templates, but two features needed real client-side JS.
Flipping flashcards. Each card needed to flip on click, replicating a physical flashcard's front/back. The script listens for a click, toggles which side is showing, and updates the background colour as a visual cue:
function flipCard() {
if (cards.length === 0) return;
flipped = !flipped;
const card = cards[current];
document.getElementById('card-text').textContent = flipped ? card.back : card.front;
document.getElementById('flip-hint').textContent = flipped ? 'Click to flip back' : 'Click to flip';
document.getElementById('flashcard').style.background = flipped ? '#f0fdf4' : 'white';
document.getElementById('rag-section').style.display = flipped ? 'block' : 'none';
}
From supernova/templates/supernova/view_flashcards.html.
Previewing PDFs. Revology's own verified notes carry more weight than user uploads, so I wanted a way to show their first page before the click — using Mozilla's PDF.js library to render page one onto a canvas, since PDFs can't display natively inside a <div>:
pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.worker.min.js';
document.querySelectorAll('.pdf-thumb').forEach(canvas => {
const url = canvas.dataset.url;
pdfjsLib.getDocument(url).promise.then(pdf => pdf.getPage(1)).then(page => {
const viewport = page.getViewport({ scale: 1.5 });
canvas.width = viewport.width;
canvas.height = viewport.height / 2;
page.render({ canvasContext: canvas.getContext('2d'), viewport });
});
});
This exact technique now renders the live preview below — for the actual project write-up.
Planning, market research, database design, the iterative build, ethics & GDPR considerations, beta feedback and a full bibliography — exactly as submitted for my Gold CREST Award.
Open full PDF ↗Because the site collects user data, I had to take data protection seriously from the start. Sign-up only asks for a first/last name, email, username and password — all credential handling is delegated to Django's built-in authentication system, which encrypts passwords and follows established security practice. User-created resources fall under UK GDPR, which requires data to be collected lawfully, stored securely, and used only for its stated purpose.
Another suggested adding past paper questions organised by topic — a strong idea for Version 2, alongside activity streaks and leaderboards, in the spirit of how platforms like Duolingo gamify consistency.
I learned how to navigate Django properly — from authentication to database administration — across four languages, the most I'd used in a single project, deployed on PythonAnywhere via a Linux Bash console. But the real takeaway wasn't technical. It was the rhythm of the development process itself: write, debug, test, repeat, for months.