This repo contains the source code for the website https://isitweeka.com/, a centre of information for students of King Edward VI Camp Hill School for Boys and King Edward VI Camp Hill School for Girls.
It is primarily concerned with telling students what timetable week it is (A or B), but also provides information on school events (including house event results) and an alert banner for important news (ranging from green and blue for low level alerts, to red for breaking news).
It also has a widely followed social media account covering school news, events and house events. See @isitweeka on Instagram for this.
Our frontend in frontend/
is based on Create React App. A CI script builds this into a bundle every push and deploys it to GitHub Pages to host our website.
Here's how everything in this repo works:
At a higher level (for a deeper level, and the modules involved, read the code) we find the week like this:
- Every other week we download a copy of the school's calendar of events as a
.ical
file - This file is stored on our website as a static file
- When the site loads, the ical file is fetched
- We find the current date in the calendar, and rewind back to the nearest
weekMarkerDate
(a number, where 0 is Sunday, where an event saying what week it is should be found).- On Saturdays and Sundays however, we go to the next monday as students will be interested in what the next week is
- If there is an event "Week A" or "Week B", display the week detected. Else, say it is neither Week A or B
KECHB (but not KECHG) changed how they define the week this year (moving from a separate event each week defining the week to a recurring event), and so an algorithm change was needed:
- Every other week we download a copy of the school's calendar of events as a
.ical
file - This file is stored on our website as a static file
- When the site loads, the ical file is fetched
- If a recurring rule is used, loop through recurrences until one matching this (or next on weekends) week is found. If no recurrences, the same steps as 4. apply
- Use the current date (weekday or weekend) to determine the message shown ("It is" or "Next week is")
- If no recurrence or IIWA marker event is found, say it is neither Week A or B
Code also exists to do this detection on a server, with the site then just requesting an API that says what week it is. However this requires running a server which costws money, so instead this client side detection is used instead.
IsItWeekA makes use of a monorepo structure to hold the various modules that make it up:
@isitweeka/core
- Held in
packages/@isitweeka/core
- Contains code shared between all modules of IIWA, namely:
- Common constants for:
- Calendar URLs
WEEK_MARKER_DATE_
: the day (where 0 is Sunday) of the event (or recurrence of the root event), each week, in the school calendar that marks if the Week is A or B.- Redis keys for our sever side APIs (see explanation of API below)
- Common logger shared between modules (specfically a
LogFactory
, where you provide a location for log file and awinston
Logger is returned)
- Common constants for:
- Held in
libisitweeka
- Held in
packages/libisitweeka
- Contains a class,
IsItWeekA
, that has the actual logic to find what week it is (see doc comments for it's specific usage) - It would be nice to one day replace this with a platform neutral implementation - e.g. a Rust/WASM implementation of the algorithms
- Held in
frontend
- Held in
frontend/
- A Create React App project with all the frontend code of the site, includes SCSS (Sass) for styling and static assets
- Held in
isitweeka-server
- Held in
server/
- Contains code for a server to serve the IsItWeekA API (generally unused)
- See below for why this exists
- Held in
@isitweeka/service-___
- Services that provide information for
isitweeka-server
- See below for why this exists
- Services that provide information for
Below, find how things are structured in this repo:
.github/
- CI config files, such as the CI that buids and deploys the website, and the CI that regularly updates our copy of the school calendars stored infrontend/public/cal
- NOTE: When this runs and pushes an update to the repo, it does not trigger a site deployment!
assets/
- Core assets of IsItWeekA, namely copies of our Logo in various sizesconfig/
- Contains our shared TypeScript config file used by all modules
- The file
tsconfig.json
in the root of the repo references all the modules, allowing TS Incemental Builds and TS Projects to be used - one command builds everything
scripts/
- misc. scripts
Whilst this is currently not deployed, sometimes a server-side API is required. This is normally for when the site needs to fetch data from external APIs such as Eventbrite - this requires an API key, which we can't put in public client-side code.
Therefore, an API was created. I wanted something modular, so this is what we did:
- There are a series of services that run as their __own programs__P:
packages/@isitweeka/service-kech/
- usinglibisitweeka
figure out the current week for KECHB and KECHG once every 30 secondspackages/@isitweeka/service-eventbrite
- This is used to fetched donation data from Eventbrite
- It is configurable by environment variables for the API Key, Organisation ID and Event ID
- Runs every 30 seconds
- These services fetch data and place it into a Redis database
- This means services can be written in any platform we want so long as it can reach the Redis DB
- For simplicity, TypeScript (node.js) was used, but Python, Rust, Java, anything could be used
- Redis keys to use for each service are stored in
packages/@isitweea/core/src/constants.ts
- The server
server/
receives requests from clients at specific routes, fetches the appropriate key from redis and sends it back to the client
The API runs using docker containers:
- Each service has a docker file
- These docker files are build (manually!) and uploaded to a GitHub container registry for my username Gum-Joe
- A server then uses docker compose to pull and run these containers
Since the API is setup lik this, to add a new sevice:
- Write the service, set up to store the result of each fetch in a specfic redis key
- Build it as a docker image and add it to the
docker-compose.yml
files - Add a route in
server/
that returns the data from Redis - Rebuild server image
- Push newly built images to a container registry
- Use the docker compose config files (the
.prod.yml
one) to deploy the updated images to production so they can be used as the API
- Node.js LTS
- The
yarn
package manager
yarn
yarn run build
yarn
yarn run build
cd frontend
yarn start
yarn
yarn run build
cd frontend
yarn build
The contents of frontend/build
may then be served as a static site.
- Make a copy of
.env.template
as.env
. Here, fill in the environment variables as apprioraite:- For Eventbrite service (
@isitweeka/service-eventbrite
):IIWA_EVENTBRITE_ORG_ID
: the ID of the organisation holding the event you want to track total net revenue (i.e. amount raised) ofIIWA_EVENTBRITE_EVENT_ID
: the ID of the event you want to track total net revenue (i.e. amount raised) ofIIWA_EVENTBRITE_ACCESS_TOKEN
: Access Token of an account with appropriate permissions to access said event in the Eventbrite Organiser (get one at https://www.eventbrite.com/platform/)
- For Eventbrite service (
- Build docker containers with
docker compose build
- Push built images to GH container registry
- Deploy in production with
docker compose -f docker-compose.prod.yml up
(make sure secrets file (.env
) has been copied to prod)