openapi: 3.0.2
info:
title: Penn Courses API Documentation [Accounts] User [Accounts] User [PCA] Registration API
version: ''
description: '
# Introduction
Penn Courses ([GitHub](https://github.com/pennlabs/penn-courses)) is the umbrella
categorization for [Penn Labs](https://pennlabs.org/)
products designed to help students navigate the course registration process. It currently
includes three products, each with their own API documented on this page:
Penn Course Alert, Penn Course Plan, and Penn Course Review.
See `Penn Labs Notion > Penn Courses` for more details on each of our (currently) three apps.
For instructions on how to maintain this documentation while writing code,
see the comments in `backend/PennCourses/docs_settings.py` (it is easy, and will be helpful
for maintaining Labs knowledge in spite of our high member turnover rate).
See our [GitHub](https://github.com/pennlabs/penn-courses) repo for instructions on
installation, running in development, and loading in course data for development. Visit
the `/admin/doc/` route ([link](/admin/doc/)) for the backend documentation generated by Django
(admin account required, which can be made by running
`python manage.py createsuperuser` in terminal/CLI).
# Unified Penn Courses
By virtue of the fact that all Penn Courses products deal with, well, courses,
it would make sense for all three products to share the same backend.
We realized the necessity of a unified backend when attempting to design a new Django backend
for Penn Course Plan. We like to live by the philosophy of keeping it
[DRY](https://en.wikipedia.org/wiki/Don''t_repeat_yourself), and
PCA and PCP''s data models both need to reference course and
section information. We could have simply copied over code (a bad idea)
or created a shared reusable Django app (a better idea) for course data,
but each app would still need to download copies of the same data.
Additionally, this will help us build integrations between our Courses products.
# Authentication
PCx user authentication is handled by platform''s Penn Labs Accounts Engine.
See [Penn Labs Notion > Platform > The Accounts Engine](https://www.notion.so/pennlabs/The-Accounts-Engine-726ccf8875e244f4b8dbf8a8f2c97a87?pvs=4)
for extensive documentation and links to repositories for this system. When tags or routes
are described as requiring user authentication, they are referring to this system.
I highly recommend the [official video course on OAuth2](https://oauth.net/2/) (by Aaron Parecki),
then the Platform Notion docs on the "Accounts Engine" for anyone who wants to understand
Labs authentication better. Platform is our OAuth2 "Authorization Server",
and Django Labs Accounts is an OAuth2 client run by our Django backends (Clubs, Penn Courses, etc),
exposing client-facing authentication routes like `penncourseplan.com/accounts/login`.
There''s also this Wikipedia page explaining [Shibboleth](https://en.wikipedia.org/wiki/Shibboleth_(software))
(which is used by Penn for authentication, and by the Platform authorization server).
See the Django docs for more on Django''s features for
[User Authentication](https://docs.djangoproject.com/en/3.0/topics/auth/),
which are used by PCX apps, as part of Platform''s accounts system.
'
x-logo:
url: https://i.imgur.com/tVsRNxJ.png
altText: Labs Logo
contact:
email: contact@pennlabs.org
servers:
- url: https://penncoursereview.com
description: Penn Course Review
- url: https://penncourseplan.com
description: Penn Course Plan
tags:
- name: '[PCA] Registration'
description: '
As the main API endpoints for PCA, these routes allow interaction with the user''s
PCA registrations. An important concept which is referenced throughout the documentation
for these routes is that of the "resubscribe chain". A resubscribe chain is a chain
of PCA registrations where the tail of the chain was an original registration created
through a POST request to `/api/alert/registrations/` specifying a new section (one that
the user wasn''t already registered to receive alerts for). Each next element in the chain
is a registration created by resubscribing to the previous registration (once that
registration had triggered an alert to be sent), either manually by the user or
automatically if auto_resubscribe was set to true. Then, it follows that the head of the
resubscribe chain is the most relevant Registration for that user/section combo; if any
of the registrations in the chain are active, it would be the head. And if the head
is active, none of the other registrations in the chain are active.
Note that a registration will send an alert when the section it is watching opens, if and
only if it hasn''t sent one before, it isn''t cancelled, and it isn''t deleted. If a
registration would send an alert when the section it is watching opens, we call it
"active". See the Create Registration docs for an explanation of how to create a new
registration, and the Update Registration docs for an explanation of how you can modify
a registration after it is created.
In addition to sending alerts for when a class opens up, we have also implemented
an optionally user-enabled feature called "close notifications".
If a registration has close_notification enabled, it will act normally when the watched
section opens up for the first time (triggering an alert to be sent). However, once the
watched section closes, it will send another alert (the email alert will be in the same
chain as the original alert) to let the user know that the section has closed. Thus,
if a user sees a PCA notification on their phone during a class for instance, they won''t
need to frantically open up their laptop and check PennInTouch to see if the class is still
open just to find that it is already closed. To avoid spam and wasted money, we DO NOT
send any close notifications over text. So the user must have an email saved or use
push notifications in order to be able to enable close notifications on a registration.
Note that the close_notification setting carries over across resubscriptions, but can be
disabled at any time using Update Registration.
After the PCA backend refactor in 2019C/2020A, all PCA Registrations have a `user` field
pointing to the user''s Penn Labs Accounts User object. In other words, we implemented a
user/accounts system for PCA which required that
people log in to use the website. Thus, the contact information used in PCA alerts
is taken from the user''s User Profile. You can edit this contact information using
Update User or Partial Update User. If push_notifications is set to True, then
a push notification will be sent when the user is alerted, but no text notifications will
be sent (as that would be a redundant alert to the user''s phone). Otherwise, an email
or a text alert is sent if and only if contact information for that medium exists in
the user''s profile.
'
paths:
/api/alert/registrations/:
get:
operationId: List Registrations
description: '(GET `/api/alert/registrations/`)
Returns all registrations which are not deleted or made obsolete by resubscription. Put
another way, this endpoint will return a superset of all active registrations: all
active registrations (meaning registrations which would trigger an alert to be sent if their
section were to open up), IN ADDITION TO all inactive registrations from the current semester
which are at the head of their resubscribe chains and not deleted. However, one extra
modification is made: if multiple registrations for the same section are included in the
above-specified set, then all but the most recent (latest `created_at` value) are removed from
the list. This ensures that at most 1 registration is returned for each section. Note that this
is still a superset of all active registrations. If a registration is active, its `created_at`
value will be greater than any other registrations for the same section (our code ensures no
registration can be created or resubscribed to when an active registration exists for the same
section). This extra modification is actually made to prevent the user from being able to
resubscribe to an older registration after creating a new one (which would cause the backend to
return a 409 error).
Each object in the returned list of registrations is of the same form as the object returned
by Retrieve Registration.
<span style="color:red;">User authentication required</span>.'
parameters: []
responses:
'200':
content:
application/json:
schema:
type: array
items:
type: object
properties:
id:
type: integer
readOnly: true
created_at:
type: string
format: date-time
readOnly: true
description: The datetime at which this registration was created.
original_created_at:
type: string
format: date-time
readOnly: true
nullable: true
description: '
The datetime at which the tail of the resubscribe chain to which this registration belongs
was created. In other words, the datetime at which the user created the original
registration for this section, before resubscribing some number of times
(0 or more) to reach this registration.
'
updated_at:
type: string
format: date-time
readOnly: true
description: The datetime at which this registration was last modified.
section:
type: string
description: The dash-separated full code of the section associated with this Registration.
user:
type: string
readOnly: true
description: The Penn Labs Accounts username of the User who owns this Registration.
cancelled:
type: boolean
readOnly: true
description: '
Defaults to False, changed to True if the registration has been cancelled. A cancelled
registration will not trigger any alerts to be sent even if the relevant section opens.
A cancelled section can be resubscribed to (unlike deleted alerts), and will show up
on the manage alerts page on the frontend (also unlike deleted alerts). Note that once
a registration is cancelled, it cannot be uncancelled (resubscribing creates a new
registration which is accessible via the resubscribed_to field, related name of
resubscribed_from).
'
cancelled_at:
type: string
format: date-time
readOnly: true
nullable: true
description: When was the registration cancelled? Null if it hasn't been cancelled.
deleted:
type: boolean
readOnly: true
description: '
Defaults to False, changed to True if the registration has been deleted. A deleted
registration will not trigger any alerts to be sent even if the relevant section opens.
A deleted section cannot be resubscribed to or undeleted, and will not show up on the
manage alerts page on the frontend. It is kept in the database for analytics purposes,
even though it serves no immediate functional purpose for its original user.
'
deleted_at:
type: string
format: date-time
readOnly: true
nullable: true
description: When was the registration deleted? Null if it hasn't been deleted.
auto_resubscribe:
type: boolean
readOnly: true
description: '
Defaults to False, in which case a registration will not be automatically resubscribed
after it triggers an alert to be sent (but the user can still resubscribe to a sent alert,
as long as it is not deleted). If set to True, the registration will be automatically
resubscribed to once it triggers an alert to be sent (this is useful in the case of
volatile sections which are opening and closing frequently, often before the user has
time to register).
'
notification_sent:
type: boolean
readOnly: true
description: True if an alert has been sent to the user, false otherwise.
notification_sent_at:
type: string
format: date-time
readOnly: true
nullable: true
description: '
When was an alert sent to the user as a result of this registration?
Null if an alert was not sent.
'
last_notification_sent_at:
type: string
readOnly: true
description: '
The last time the user was sent an opening notification for this registration''s
section. This property is None (or null in JSON) if no notification has been sent to the
user for this registration''s section.
This is used on the frontend to tell the user a last time an alert was sent for
the SECTION of a certain registration in the manage alerts page. Since the idea of
Registration objects and resubscribe chains is completely abstracted out of the User''s
understanding, they expect alerts to work by section (so the "LAST NOTIFIED"
column should tell them the last time they were alerted about that section).
'
close_notification:
type: boolean
readOnly: true
description: "Defaults to false. If set to true, the user will receive\n a close notification (an alert when the section closes after an\n alert was sent for it opening).\n"
close_notification_sent:
type: boolean
readOnly: true
description: True if a close notification has been sent to the user, false otherwise.
close_notification_sent_at:
type: string
format: date-time
readOnly: true
nullable: true
description: '
When was a close notification sent to the user as a result of this registration?
Null if a close notification was not sent.
'
is_active:
type: string
readOnly: true
description: '
True if the registration would send an alert when the watched section changes to open,
False otherwise. This is equivalent to
[not(notification_sent or deleted or cancelled) and semester is current].
'
is_waiting_for_close:
type: string
readOnly: true
description: '
True if the registration is waiting to send a close notification to the user
once the section closes. False otherwise.
'
section_status:
type: string
readOnly: true
description: 'The current status of the watched section. Options and meanings: <table width=100%><tr><td>"O"</td><td>"Open"</td></tr><tr><td>"C"</td><td>"Closed"</td></tr><tr><td>"X"</td><td>"Cancelled"</td></tr><tr><td>""</td><td>"Unlisted"</td></tr></table>'
description: Registrations successfully listed.
'403':
description: Access denied (missing or improper authentication).
tags:
- '[PCA] Registration'
post:
operationId: Create Registration
description: '(POST `/api/alert/registrations/`)
Use this route to create a PCA registration for a certain section. A PCA registration
represents a "subscription" to receive alerts for that section. The body of the request must
include a section field (with the dash-separated full code of the section) and optionally
can contain an auto_resubscribe field (defaults to false) which sets whether the registration
will automatically create a new registration once it triggers an alerts (i.e. whether it will
automatically resubscribe the user to receive alerts for that section). It can also optionally
contain a "close_notification" field, to enable close notifications on the registration.
Note that close notifications CANNOT be sent by text so you shouldn''t allow the user to
enable close notifications for any registration unless they have an email set in their
User Profile or have push notifications enabled. If you try to create a registration with
close_notification enabled and the user only has texts enabled, , a 406 will be returned
and the registration will not be created.
Note that if you include the "id" field in the body of your POST request, and that id
does not already exist, the id of the created registration will be set to the given value.
However, if the given id does exist, the request will instead be treated as a PUT request for
the registration (see the Update Registration docs for more info on how
PUT requests are handled).
This route returns a 201 if the registration is successfully created, a 400 if the input is
invalid (for instance if a null section is given), a 404 if the given section is not found in
the database, a 406 if the authenticated user does not have either a phone or an email set
in their profile (thus making it impossible for them to receive alerts), and a 409 if the
user is already currently registered to receive alerts for the given section. If the request
is redirected to update (when the passed in id is already associated with a registration),
other response codes may be returned (see the Update Registration docs for more info). If
registration is not currently open on PCA, a 503 is returned.
<span style="color:red;">User authentication required</span>.'
parameters: []
requestBody:
content:
application/json:
schema:
type: object
properties:
id:
type: integer
description: The id of the registration (can optionally be used to customize the id of a new registration, or to update an existing registration).
created_at:
type: string
format: date-time
readOnly: true
description: The datetime at which this registration was created.
original_created_at:
type: string
format: date-time
readOnly: true
nullable: true
description: '
The datetime at which the tail of the resubscribe chain to which this registration belongs
was created. In other words, the datetime at which the user created the original
registration for this section, before resubscribing some number of times
(0 or more) to reach this registration.
'
updated_at:
type: string
format: date-time
readOnly: true
description: The datetime at which this registration was last modified.
section:
type: string
description: The dash-separated full code of the section associated with this Registration.
maxLength: 16
user:
type: string
readOnly: true
nullable: true
description: '
The User that registered for this alert. This object will be none if registration occurred
before the PCA refresh of Spring 2020 (before the refresh user''s were only identified by
their email and phone numbers, which are legacy fields in this model now). This object
might also be none if registration occurred through a 3rd part API such as Penn Course
Notify (now that Notify has fallen this is an unlikely event).
'
cancelled:
type: boolean
readOnly: true
description: '
Defaults to False, changed to True if the registration has been cancelled. A cancelled
registration will not trigger any alerts to be sent even if the relevant section opens.
A cancelled section can be resubscribed to (unlike deleted alerts), and will show up
on the manage alerts page on the frontend (also unlike deleted alerts). Note that once
a registration is cancelled, it cannot be uncancelled (resubscribing creates a new
registration which is accessible via the resubscribed_to field, related name of
resubscribed_from).
'
cancelled_at:
type: string
format: date-time
readOnly: true
nullable: true
description: When was the registration cancelled? Null if it hasn't been cancelled.
deleted:
type: boolean
readOnly: true
description: '
Defaults to False, changed to True if the registration has been deleted. A deleted
registration will not trigger any alerts to be sent even if the relevant section opens.
A deleted section cannot be resubscribed to or undeleted, and will not show up on the
manage alerts page on the frontend. It is kept in the database for analytics purposes,
even though it serves no immediate functional purpose for its original user.
'
deleted_at:
type: string
format: date-time
readOnly: true
nullable: true
description: When was the registration deleted? Null if it hasn't been deleted.
auto_resubscribe:
type: boolean
description: '
Set this to true to turn on auto resubscribe (causing the registration to automatically
resubscribe once it sends out a notification). Default is false if not specified.
'
notification_sent:
type: boolean
readOnly: true
description: True if an alert has been sent to the user, false otherwise.
notification_sent_at:
type: string
format: date-time
readOnly: true
nullable: true
description: '
When was an alert sent to the user as a result of this registration?
Null if an alert was not sent.
'
last_notification_sent_at:
type: string
readOnly: true
description: '
The last time the user was sent an opening notification for this registration''s
section. This property is None (or null in JSON) if no notification has been sent to the
user for this registration''s section.
This is used on the frontend to tell the user a last time an alert was sent for
the SECTION of a certain registration in the manage alerts page. Since the idea of
Registration objects and resubscribe chains is completely abstracted out of the User''s
understanding, they expect alerts to work by section (so the "LAST NOTIFIED"
column should tell them the last time they were alerted about that section).
'
close_notification:
type: boolean
description: "Defaults to false. If set to true, the user will receive\n a close notification (an alert when the section closes after an\n alert was sent for it opening).\n"
close_notification_sent:
type: boolean
readOnly: true
description: True if a close notification has been sent to the user, false otherwise.
close_notification_sent_at:
type: string
format: date-time
readOnly: true
nullable: true
description: '
When was a close notification sent to the user as a result of this registration?
Null if a close notification was not sent.
'
is_active:
type: string
readOnly: true
description: '
True if the registration would send an alert when the watched section changes to open,
False otherwise. This is equivalent to
[not(notification_sent or deleted or cancelled) and semester is current].
'
is_waiting_for_close:
type: string
readOnly: true
description: '
True if the registration is waiting to send a close notification to the user
once the section closes. False otherwise.
'
required:
- section
responses:
'201':
content:
application/json:
schema:
properties:
message:
type: string
id:
type: integer
description: Registration successfully created.
'403':
description: Access denied (missing or improper authentication).
'400':
description: Bad request (e.g. given null section).
'404':
description: Given section not found in database.
'406':
description: No contact information (phone or email) set for user.
'409':
description: Registration for given section already exists.
'503':
description: Registration not currently open.
tags:
- '[PCA] Registration'
/api/alert/registrations/{id}/:
get:
operationId: Retrieve Registration
description: '(GET `/api/alert/registrations/{id}/`)
Get one of the logged-in user''s PCA registrations for the current semester, using
the registration''s ID. Note that if a registration with the specified ID exists, but that
registration is not at the head of its resubscribe chain (i.e. there is a more recent
registration which was created by resubscribing to the specified registration), the
HEAD of the resubscribe chain will be returned. This means the same registration could be
returned from a GET request to 2 distinct IDs (if they are in the same resubscribe chain).
If a registration with the specified ID exists, a 200 response code is returned, along
with the head registration object. If no registration with the given id exists,
a 404 is returned.
<span style="color:red;">User authentication required</span>.'
parameters:
- name: id
in: path
required: true
description: A unique integer value identifying this registration.
schema:
type: string
responses:
'200':
content:
application/json:
schema:
type: object
properties:
id:
type: integer
readOnly: true
created_at:
type: string
format: date-time
readOnly: true
description: The datetime at which this registration was created.
original_created_at:
type: string
format: date-time
readOnly: true
nullable: true
description: '
The datetime at which the tail of the resubscribe chain to which this registration belongs
was created. In other words, the datetime at which the user created the original
registration for this section, before resubscribing some number of times
(0 or more) to reach this registration.
'
updated_at:
type: string
format: date-time
readOnly: true
description: The datetime at which this registration was last modified.
section:
type: string
description: The dash-separated full code of the section associated with this Registration.
user:
type: string
readOnly: true
description: The Penn Labs Accounts username of the User who owns this Registration.
cancelled:
type: boolean
readOnly: true
description: '
Defaults to False, changed to True if the registration has been cancelled. A cancelled
registration will not trigger any alerts to be sent even if the relevant section opens.
A cancelled section can be resubscribed to (unlike deleted alerts), and will show up
on the manage alerts page on the frontend (also unlike deleted alerts). Note that once
a registration is cancelled, it cannot be uncancelled (resubscribing creates a new
registration which is accessible via the resubscribed_to field, related name of
resubscribed_from).
'
cancelled_at:
type: string
format: date-time
# --- truncated at 32 KB (49 KB total) ---
# Full source: https://raw.githubusercontent.com/api-evangelist/university-of-pennsylvania/refs/heads/main/openapi/university-of-pennsylvania-pca-registration-api-openapi.yml