Skip to content

Commit

Permalink
update to new OpenData API
Browse files Browse the repository at this point in the history
  • Loading branch information
superfashi committed Apr 12, 2022
1 parent e763b40 commit b6aa71e
Show file tree
Hide file tree
Showing 9 changed files with 636 additions and 257 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# Penn OpenData API
[![Go Reference](https://pkg.go.dev/badge/github.com/penn-automate/penn-opendata-api.svg)](https://pkg.go.dev/github.com/penn-automate/penn-opendata-api)

This package provides an easy-to-use Go Binding for accessing University of Pennsylvania's OpenData API.

This API is implemented following the specifications in this [documentation](https://esb.isc-seo.upenn.edu/8091/documentation).
This API is implemented following the specifications in this [documentation](https://app.swaggerhub.com/apis-docs/UPennISC/open-data/prod).

This package is licensed under [Unlicense](LICENSE).
48 changes: 48 additions & 0 deletions course.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package opendata

import (
"fmt"
"regexp"
"strconv"
"strings"
)

// Course is a normalized struct for course and section ID.
type Course struct{ string }

func validCourse(course string) bool {
if len(course) <= 0 {
return false
}
switch course[len(course)-1] {
case 'A', 'B', 'a', 'b':
course = course[:len(course)-1]
}
num, err := strconv.Atoi(course)
if err != nil || num <= 0 {
return false
}
return num <= 9999
}

// NewCourse generates a new Course instance based on course subject, number, and section ID.
func NewCourse(subject, number, section string) *Course {
subject = strings.ToUpper(strings.TrimSpace(subject))
section = strings.ToUpper(strings.TrimSpace(section))
if len(subject) > 4 || len(subject) <= 1 || !validCourse(number) || len(section) != 3 {
return nil
}
return &Course{fmt.Sprintf("%s%04s%s", subject, number, section)}
}

var courseRegex = regexp.MustCompile(`^([a-zA-Z]{2,4})\s*-?([0-9]{2,4}[AB]?)-?([0-9a-zA-Z]{3})$`)

// ParseCourse generates a new Course instance based on course ID string using regex to match.
// ParseCourse("MUSC0050003")
func ParseCourse(course string) *Course {
match := courseRegex.FindStringSubmatch(course)
if len(match) != 4 {
return nil
}
return NewCourse(match[1], match[2], match[3])
}
48 changes: 10 additions & 38 deletions course_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,15 @@ package opendata
import "testing"

func TestParseCourseFullWidth(t *testing.T) {
course := ParseCourse("NETS150001")
if course == nil || course.string != "NETS150001" {
course := ParseCourse("NETS1120001")
if course == nil || course.string != "NETS1120001" {
t.Fail()
}
}

func TestParseCourseTrimWidth(t *testing.T) {
course := ParseCourse("CIS120001")
if course == nil || course.string != "CIS 120001" {
t.Fail()
}
}

func TestParseCourseFullWidthWhiteSpace(t *testing.T) {
course := ParseCourse("CIS 120001")
if course == nil || course.string != "CIS 120001" {
course := ParseCourse("CIS1200001")
if course == nil || course.string != "CIS1200001" {
t.Fail()
}
}
Expand All @@ -37,37 +30,16 @@ func TestParseCourseInvalidDepartWidth(t *testing.T) {
}
}

func TestParseCourseInvalidCourseWidth(t *testing.T) {
course := ParseCourse("CIS50001")
if course != nil {
t.Fail()
}
}

func TestParseCourseInvalidSection(t *testing.T) {
course := ParseCourse("CIS001000")
if course != nil {
func TestParseCourseNormalized(t *testing.T) {
course := ParseCourse("CIS-1200-001")
if course == nil || course.string != "CIS1200001" {
t.Fail()
}
}

func TestParseCourseReadable(t *testing.T) {
course := ParseCourseReadable("CIS-120-001")
if course == nil || course.string != "CIS 120001" {
t.Fail()
}
}

func TestParseCourseRegex(t *testing.T) {
course := ParseCourseRegex("MUSC50001")
if course == nil || course.string != "MUSC050001" {
t.Fail()
}
}

func TestParseCourseRegexInvalid(t *testing.T) {
course := ParseCourseRegex("GARBage1023023")
if course != nil {
func TestParseCourseMultiTerm(t *testing.T) {
course := ParseCourse("CRIM-6004A-301")
if course == nil || course.string != "CRIM6004A301" {
t.Fail()
}
}
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
module github.com/penn-automate/penn-opendata-api

go 1.13

require golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5
367 changes: 367 additions & 0 deletions go.sum

Large diffs are not rendered by default.

25 changes: 16 additions & 9 deletions opendata.go
Original file line number Diff line number Diff line change
@@ -1,31 +1,38 @@
package opendata

import (
"context"
"encoding/json"
"errors"
"html"
"net/http"
"strconv"

"golang.org/x/oauth2"
"golang.org/x/oauth2/clientcredentials"
)

const openDataURL = `https://esb.isc-seo.upenn.edu/8091/open_data/`
const openDataURL = `https://3scale-public-prod-open-data.apps.k8s.upenn.edu/api/v1/`

// OpenData is a struct that stores OpenData API username and password.
type OpenData struct {
user, pass string
client *http.Client
}

// NewOpenDataAPI generates an instance of OpenData
// with specific username and password.
func NewOpenDataAPI(username, password string) *OpenData {
return &OpenData{user: username, pass: password}
func NewOpenDataAPI(clientId, clientSecret string) *OpenData {
return &OpenData{client: (&clientcredentials.Config{
ClientID: clientId,
ClientSecret: clientSecret,
TokenURL: "https://sso.apps.k8s.upenn.edu/auth/realms/master/protocol/openid-connect/token",
AuthStyle: oauth2.AuthStyleInHeader,
}).Client(context.TODO())}
}

func (o *OpenData) access(req *http.Request) (*http.Response, error) {
req.Header.Set("Authorization-Bearer", o.user)
req.Header.Set("Authorization-Token", o.pass)
req.Header.Set("Content-Type", "application/json; charset=utf-8")
return http.DefaultClient.Do(req)
req.Header.Set("Accept", "application/json; charset=utf-8")
return o.client.Do(req)
}

// GetRegistrar generates a Registrar instance using the current OpenData instance.
Expand Down Expand Up @@ -83,7 +90,7 @@ func (i *PageIterator) NextPage() bool {
return true
}

if i.data.ServiceMeta.ErrorText != "" {
if i.data.ServiceMeta.Error {
i.err = errors.New(html.UnescapeString(i.data.ServiceMeta.ErrorText))
return true
}
Expand Down
Loading

0 comments on commit b6aa71e

Please sign in to comment.