Introduction
This website is so so so cursed. The website has been primarly maintained by three different people over the course of a year. Thus it has been a complete mess. It is now being revamped to be consistant, well documented, and easily maintained by future generations. If you are intrested in helping please join our discord.
We also have a repo for our layout stuff yipee!
Setup
This page covers setting our developmental Docker image on your local machine and getting started.
Docker
An important note is that we have stopped supporting Windows development. If you are on windows you need to have WSL installed. Here is the offical Microsoft page on how to install WSL.
Docker desktop is really easy and intuative to use. If you don't already have Docker experance I would recommend installing Docker desktop. Instructions can be found here.
If you are on Windows with WSL this page contains the information needed to make sure that Docker is running using WSL.
Now that you have Docker installed we want to make a clone of our developmental Docker repo. If you are on Windows with WSL open a new terminal with WSL. If you are on Mac or Linux just open your normal terminal.
Then run git clone https://github.com/cmureadme/website-docker-local.git
From there follow the instructions on the repo's readme
Note that when you deploy the website locally you will run with Debug=True.
Our actual production websites run with Debug=False.
Contributing
Write all of your code on the dev branch.
If you are doing a much larger feature then fork off of dev make your feature and then do a pull request back into dev.
When you are done implementing your code on dev open a pull request to main.
If you are wondering what to work on asking in the discord is great or you can choose of the the issues to work on.
If you commit any of these to the repo we will be sad and you will be banned from contributing for a period of time:
- secret keys
- media (outside of the few images in the static folder)
- databases
Production
KGB has a server hosted by computer club. We use this to run all of our websites. We run two readme websites on this server cmureadme.com and dev.cmureadme.com. Each website runs in it's own Docker container, as it's own Docker image.
Actual Production Website
Our actual production website is cmureadme.com.
We use this repo for our Docker image, and scripts to build and redeploy the website.
Notably our database + media is backed up on every redeploy.
This Docker image runs of of the main branch of the readme-website git repo.
All of the media is hosted on the nginx server. The django code itself is run using gunicorn.
We also have our sample development databases hosted at https://cmureadme.com/sample_dbs/.
Everytime we push to main we run .\redeploy.sh.
Development Website
Our development website is dev.cmureadme.com. We use this repo for our Docker image, and scripts to build and redeploy the website. Notably we make a copy of the production database and media folder on every redeploy. This ensures that any changes we make on the development website don't affect anything on the production database. It also ensures that any changes that we are about to implement work with our actual dataset.
Note that if you don't have a superuser/user account on our actual production website you will not have a superuser/user account on the development website. This means you need to test any admin changes you make on your local deployment extensivly.
All of the media is hosted on the nginx server. The django code itself is run using gunicorn. Everything about the deployment is the same as the production deployment except for what git branch it pulls from.
Uploading
Uploading to the website uses Markdown. This does not require any programming knowledge or much technical knowledge.
As an uploader you are responsible for creating new author profiles and digitizing our print content.
Getting started
Ask in the discord to have one of the current tech people make you an account. Then, go to cmureadme.com/admin to log into our admin page.
Creating New Author Profiles
Every single author gets uploaded to the website. This includes both recurring authors and one-off joke characters.
The article's author profile must exist before the article is uploaded. Create a mostly blank profile before uploading articles, then message the author for the rest of the information (which can be filled in afterwards).
To add a new author, click on the "authors" button on the left hand side. Then, click the "add author" button.
Author data fields
| Field | Description |
|---|---|
| Name* | The author's name |
| Slug* | The part of the url that takes you to an authors page. For example, firstname-lastname in cmureadme.com/staff/firstname-lastname/ |
| Img | The author's profile picture. Defaults to anon.png |
| Bio | The author's bio |
| Roles | Any roles the author has. Defaults to Staffwriter |
| Pronouns | The author's pronouns |
| Major | The author's major |
| Year | The author's graduation year |
| Fact | A fun fact about the author |
| The author's email | |
| Author status* | The author's status in the organization. Defaults to Usual Suspect |
*required
For Author status, if the author is a real person or frequently reoccurring character they are a Usual Suspect.
If they are a one-off bit, they are an Independent Contractor.
Finally, if this is a real person who is no longer part of Readme they are an Escapee.
When you are done entering information, hit the "save" button on the bottom of the page. If you are uploading multiple authors sequentially, you can hit the "save and add another" button.
Uploading Issues of Readme
First, go to the layout repo and download the most recent issue's full pdf.
It will be named VOLUME#ISSUE#FULL.pdf.
If it is not in the Github repo, you can run the combineme.py script.
If you need help, ask someone in the Discord to run it.
I recommend having the pdf open in Adobe Acrobat as it makes it very easy to copy and paste text from the pdf.
Click on the "Issues" button on the left side of the admin page, then click on the "add issue" button on the right side.
Issue data fields
| Field | Description |
|---|---|
| Name* | The title of the issue, starting with the issue in which. Ex. the issue in which we promise you that our alibi holds up under scrutiny |
| Vol* | The volume number |
| Num* | The issue number |
| Archive* | The complete pdf of the issue |
| Release Date* | The publication date of the issue. Defaults to the current day, so make sure to double-check |
Make sure to hit the save button.
"Paid for" gag:
The "paid for" gag is a gag on the first centerfold of the issue. Ex. Paid for by: Extensive lawyer fees and two bungled investigations
Click on the Paid Fors button on the left side of the screen.
Then, click on the add Paid For button on the right side and enter the gag. Don't include the words Paid for by:.
Make sure to hit save.
Rejected Headlines
Click on the rejected headlines button on the left side of the screen. Then, click on the add rejected headline button on the right side of the screen. Copy and paste the rejected headlines into the title field. Select the issue that the rejected headline is associated with. If you think the rejected headline is particularly funny, check the featured box, so it has a high chance to be featured on the homepage. Make sure to save.
Uploading Articles and Images:
This part is the most time intensive, taking 1-2 hours depending on the length of the issue. Going through the PDF in order page by page ensures that you don't forget something.
Uploading Articles:
When you upload articles you want to start on the main page.
Because copy editing happens on the actual pdf itself, the articles as they appear in the Google Drive are not the final versions, so copy text from the pdf.
The hyphen character (-) is often not copied properly, so you have to manually add that in.
Line breaks will also be messed up, so you have to go through and add in the paragraph breaks.
Article data fields
| Field | Description |
|---|---|
| Title* | The title of the article |
| Authors | The article's authors. Leave empty if the author is anonymous. |
| Body* | The article text in markdown format. |
| Slug* | The part of the url that takes you to an article's page. Ex. example-article in cmureadme.com/article/example-article/ |
| Issue* | The issue the article was published in |
| Published | Whether the article is published. True by default. Uncheck only if the article is uploaded prior to publication. |
| Front page | Whether the article was on the front page. For "magazine" layouts, only the cover image is on the front page. |
| Featured | Whether the article is featured. This puts it at the top of its issue's page and increases the chances of being featured on the homepage. |
| Created on | Unused. The date the article was published, if different from the issue. |
| Article images | Any images associated with the article |
*required
Any images associated with the article should be embedded with . For example,  embeds anon.png. The files can be found in the layout repo in the issue folder in a folder called images.
Uploading Images:
This is for images that stand on their own and have no article attached to them. They also may have a short caption.
Front cover art should be uploaded without any of the added text. The version without text will most likely be in the Google Drive. If it's not, contact the artist to upload it to the Drive.
The files can be found in the layout repo in the issue folder in a folder called images.
However, for the front cover,
Sometimes, we run advertisements for upcoming KGB events or for other clubs. These don't get uploaded to the website.
Image data fields
| Field | Description |
|---|---|
| Title* | The title of the image |
| Artists | The image's artists. Leave empty if the author is anonymous. |
| Anonymous artists* | The number of anonymous artists. Defaults to 0 |
| Image* | The image file |
| Alt text | Accessible text describing the image for screenreaders. |
| Caption | The image caption in markdown format |
| Slug | The part of the url that takes you to an image's page. Ex. example-image in `cmureadme.com/image/example-image/ |
| Issue | The issue the image was published in |
| Published | Whether the image is published. True by default. Uncheck only if the image is uploaded prior to publication. |
| Front page | Whether the image was on the front page. For "magazine" layouts, only the cover image is on the front page. |
| Featured | Whether the image is featured. This puts it at the top of its issue's page and increases the chances of being featured on the homepage. |
| Created on | Unused. The date the image was published, if different from the issue. |
*required
If you have any questions, ask them in the Discord.
Graveyard
This is a graveyard of many of our past mistakes. Read this file and DONT repeat these mistakes please. Please it has taken years off of my life to fix some of these.
This website is so cursed.
The old repo for our website has been archived, but you can witness the horrors here
Hosting our production server inside of a student dorm
Before we moved the Readme website to be hosted on the KGB cclub server the website was hosted off of a student owned server running in a dorm.
This server also hosted other personal projects for students. Many of our tech people also did not have ssh permissions into this server and pushing any updates was extreamly inconsistant.
Since this was hosted in a dorm and not a dedicated server room the server would also sometimes randomly go down if the dorm lost power.
It was also in the living room of this dorm where at least 8 people had physical access to it.
Production server was run with Debug=True
I should not have to explain why this is an issue. Don't do this lol.
Primary keys for core models in our database where set to be strings
This means that every time you want to update certain fields it creates a whole new object. This is so so so so awful. Django by default has increminting numbers as primary keys. This is how it is suppost to be done. DO NOT manually set primary keys ever.
This is also basicly impossible to undo when it is done, so we have to create a new repo and remake our database schema.
API key stored in plain text on the git repo
I should not have to explain why this is an issue. Don't do this lol.
Another reason why we have to make a new repo.
Large media files hosted on the git repo
I should not have to explain why this is an issue. Don't do this lol.
This made the .git file massive.
It was larger than the entire Linux repo.
Another reason why we have to make a new repo.
requirements.txt
This entire file was cursed.
There were multiple libraries in there that where just completely unused.
Also requirments where set with == not ~= so we never got security updates leading to 14 different security vulnrabilities.
Also psycopg2-binary which is a library for PostgreSQL when we use sqlite3.
This library gave so many dependancy issues and hours of debugging when it was completely unused and beyond useless for our uses.
Also the precompiled binary should not be used in production mode as there are security issues.
User accounts
There were many shared user accounts on the Django website that an unknown amount of people had the login information to. There should be one account per user and one user per account for security purposes, and loging purposes.
Wiki
This wiki chapter should contain enough information to familiarize you with the readme-website codebase somewhat.
Requirements
In the readme-website directory, there are two requirements files:
requirements-local.txt: This requirements file specifies the bare minimum requirements and uses~=to match the latest patch of the library.requirements-host.txt: This requirements file is what's used in the production Docker build, and should contain a fullpip freezeof a working environment.
Currently the following dependencies are in requirements-local.txt:
Django: self-explanatorygunicorn: used in production deploymentMarkdown: used for uploading articlespillow: needed for Django
When in your virtual environment for local development, run pip freeze | diff requirements-host.txt - from time to time. If any differences are printed, this indicates staging and prod are now using an out-of-date version of some library. Confirm that your local copy is working fine with the latest version, and if so, update requirements-host.txt to match your pip freeze.
Settings
Django supports having multiple different settings files for different contexts.
All of the settings files are python files placed in settings/.
In general you should not need to edit any of the settings files.
NOTE
Django expects you to have a variable in settings named SECRET_KEY.
This is used for things like hashing passwords.
It is bad to have a secret key tracked in settings.
We have seprate secret keys that we use for the production and staging servers.
When you are running the website locally you need to make a secret key for yourself.
Since the passwords and other sensative data you are storing locally dosen't matter you can make your secret key anything.
Create a file called .env and put in it SECRET_KEY = "whateveryouwant".
Base
base.py contains the base settings that apply to every instance of the website.
No matter if you are running the website locally, or in a production context these settings apply.
The bulk of our settings live in this file, because there are not many settings that are dependent on the hosting context.
Host
host.py contains the settings that are needed when running on a server, and not your local machine.
It extends base.py meaning it has all of the settings defined in base.py and all of the settings defined in host.py.
Production
prod.py contains the settings that are needed for running on our main production website.
It builds off of host.py.
Staging
staging.py contains the settings that are needed for running our staging website.
It builds off of host.py.
Local
local.py contains the settings that are needed for running the website on your local machine.
It builds off of base.py.
Linting
We use ruff for linting and formatting of our Python code. It should install along with the rest of the dependencies if you install requirements-local.txt.
To use the linter, run ./lint.sh from the project root directory. This is currently equivalent to ruff check && ruff format. To automate this, you can run ./install-lint-hook.sh from the project root directory. This will symlink .git/hooks/pre-commit to lint.sh, causing it to run before every commit. Note that you can only have one pre-commit hook per local repository, so if you're using another piece of software like pre-commit, you'll need to make them play nice.
Media
Media for the readme website is stored in one of two places: static/ or media/.
static/
This contains only a few images. These are not user uploaded content but rather images the website needs to display. This includes images for the favicon, and some logo images for a few of the pages.
media/
This is where the vast majority of all of the media we have is stored. All of the media in here is user uploaded through the Django Admin interface.
author_images
These are the profile pictures for all of the authors.
When you upload an author profile picture foo.png it is stored in media/author_images/foo.png.
Magazine PDFs
The pdfs of the print version of the magazine are renamed on upload, so it does not matter what you call them.
For vol X issue Y the pdf is named CMURREADME_VOLX_ISSUEY.pdf.
It is stored in media/volX/issueY/CMURREADME_VOLX_ISSUEY.pdf.
Article Images
Article images are not renamed on upload it does matter what you call them, don't name them something stupid.
When you refrance an image bar.png in an article you do by writting {{bar.png}} in the article body where you want the image to appear.
For the image bar.png that was published in vol X issue Y it is stored in media/volX/issueY/images/bar.png.
Style Guide
- Use 4-space indents everywhere.
- Please comment things that need extra explanation.
- Write Python functions and variables in
snake_case, notcamelCase. Classes are still inPascalCase. - Anything in HTML/CSS (including django variables e.g.
{% block head-content %}) should be inkebab-case - HTML styling for should be in separate files from the HTML.
- A file named
.../foo.htmlshould should place its correspondidng styling instatic/styles/foo.css
- A file named
- MAKE AT LEAST SOME effort to write efficient/clean code. People will work with it later. These people might be you.
Layout
Eshaan add stuff documenting this pls <3
combineme
combineme is a very simple python script to properly combine each of the individual pdfs meant for printing into one pdf to put on the website.
Getting combineme to work
This section assumes you have never used python or a command line before (but that's ok).
combineme is a python script which means you need to have python installed on your computer. Go to the offical page to download the newest version of python.
Now that you have python installed we need to install 2 packages.
If you are on Windows open the terminal and run:
pip install argparse
pip install pypdf
If you are on Linux or Mac open the terminal and run:
pip3 install argparse
pip3 install pypdf
Using combineme
combineme expects pdf files to be formated in a certain way.
The tabloid file should be 4 pages.
It is passed in with the -t flag.
The first two pages will be the first two pages of the outputed pdf file.
The last two pages will be the last two pages of the outputed pdf file.
combineme takes two optional pdf arguments.
These are for the centerfolds.
It expects the centerfolds to each be two pages.
Centerfold one will be placed before centerfold 2 in the outputed pdf file.
Centerfold one is passed in with the -c1 flag and centerfold two is passed in with the c2 flag.
There are 3 other required arguments.
You need to pass in the volume number with the -v flag.
You need to pass in the issue number with the -i flag.
You also need to pass in the destination path of where the outputed pdf file will go in the -d flag.
The outputed pdf will be named VOLUMEvISSUEiFULL.pdf where v and i are the numbers passed into the -v and -i flags.
If you ever forget any of this information you can run the script with the -h flag.
On Windows this looks like:
python combineme.py -h
On Linux or Mac this looks like:
python3 combineme.py -h
Here is a sample running of combineme on Windows:
python3 combineme.py -v 1 -i 1 -t path\to\tabloid.pdf -c1 path\to\centerfold1.pdf -c2 path\to\centerfold2.pdf -d path\to\destination\folder\
Here is a sample running of combineme on Linux or Mac:
python3 combineme.py -v 1 -i 1 -t path/to/tabloid.pdf -c1 path/to/centerfold1.pdf -c2 path/to/centerfold2.pdf -d path/to/destination/folder/
NOTE: If you have never used the command line before it might seem annoying to type out long paths by hand. Once you have enough of the path typed out you can use the tab key to autocomplete the rest of the path.
Quick Start
You can click on the edit buttons in the top right corner.
If you want to build the mdBook locally, install mdBook and mdbook-admonish and follow mdBook's documentation.
requirements
- readme-website/README SHALL explain everything a dev needs to know to be a productive contributor.