University of Pennsylvania [PCA] Registration API

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.

OpenAPI Specification

university-of-pennsylvania-pca-registration-api-openapi.yml Raw ↑
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