Skip to content

Commit

Permalink
This is why you don't let friends push code for you.
Browse files Browse the repository at this point in the history
  • Loading branch information
jhleath committed May 5, 2014
1 parent 1faff41 commit 615c6c7
Show file tree
Hide file tree
Showing 14 changed files with 560 additions and 31 deletions.
13 changes: 13 additions & 0 deletions app/controllers/api.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package controllers

import (
"github.com/robfig/revel"
)

type API struct {
App
}

func (c API) Documentation() revel.Result {
return c.Render()
}
102 changes: 94 additions & 8 deletions app/controllers/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@ var lastUpdate time.Time
var classRegex, sectionRegex *regexp.Regexp

var collection *mgo.Collection
var users *mgo.Collection
var lists *mgo.Collection

func init() {
session, _ := mgo.Dial("mongodb://leath:hunter0813@oceanic.mongohq.com:10000/list")
collection = session.DB("list").C("classes")
users = session.DB("list").C("users")
lists = session.DB("list").C("lists")

f := func(now time.Time) {
fmt.Println("Updating Lou's List at", now)
Expand Down Expand Up @@ -99,20 +99,93 @@ func init() {
return "panel-default"
}

revel.TemplateFuncs["classCreator"] = func(class interface{}, loggedIn interface{}) map[string]interface{} {
return map[string]interface{}{
"class": class,
"loggedIn": loggedIn,
}
}

revel.TemplateFuncs["sectionBorder"] = func(v *schedule.Section) string {
totalEnrollment := float64(v.Enrollment)
totalCapacity := float64(v.Capacity)
if totalEnrollment/totalCapacity >= 0.75 {
if totalEnrollment/totalCapacity >= 1 {
return "border-danger"
} else if totalEnrollment/totalCapacity >= 0.5 {
return "border-warning"
}
return "border-default"
}

revel.InterceptMethod(App.Init, revel.BEFORE)
}

type App struct {
*revel.Controller
session string
}

func (c App) Init() revel.Result {
_, loggedIn := c.Session["user"]
c.RenderArgs["loggedIn"] = loggedIn
return nil
}

func (c App) ClassAdd(dept string, num int) revel.Result {
user, ok := c.Session["user"]
if !ok {
c.Response.Status = 403
return c.Render()
}

var u User
err := users.Find(map[string]string{"email": user}).One(&u)
if err != nil {
panic(err)
}

u.ClassBucket = append(u.ClassBucket, fmt.Sprintf("%v %v", dept, num))

err = users.Update(map[string]string{"email": user}, u)
if err != nil {
panic(err)
}

return c.Redirect(routes.App.Index())
}

func (c App) ClassRemove(dept string, num int) revel.Result {
user, ok := c.Session["user"]
if !ok {
c.Response.Status = 403
return c.Render()
}

var u User
err := users.Find(map[string]string{"email": user}).One(&u)
if err != nil {
panic(err)
}

newBucket := make([]string, len(u.ClassBucket)-1)
found := false
i := 0
for _, v := range u.ClassBucket {
if v == fmt.Sprintf("%v %v", dept, num) && !found {
found = true
continue
}
newBucket[i] = v
i++
}
u.ClassBucket = newBucket

err = users.Update(map[string]string{"email": user}, u)
if err != nil {
panic(err)
}

return c.Redirect(routes.App.Index())
}

func (c App) Index() revel.Result {
Expand All @@ -122,15 +195,31 @@ func (c App) Index() revel.Result {
panic(err)
}

user, ok := c.Session["user"]
var classes []*schedule.Class
if ok {
var u User
err := users.Find(map[string]string{"email": user}).One(&u)
if err != nil {
panic(err)
}
classes = make([]*schedule.Class, len(u.ClassBucket))
for i, v := range u.ClassBucket {
classes[i] = classList[v]
}
}

sort.Sort(sort.StringSlice(result))

return c.Render(result)
return c.Render(result, classes)
}

func (c App) NotFound() revel.Result {
return c.Render()
}

// "sections": {"$elemMatch": {"instructor": "Aaron Bloomfield"}}

//"sections":{"$elemMatch":{"sisnumber":10120}}

func (c App) Class(dept string, num int) revel.Result {
Expand All @@ -141,10 +230,7 @@ func (c App) Class(dept string, num int) revel.Result {
return c.Redirect(routes.App.NotFound())
}

c.RenderArgs = map[string]interface{}{
"class": class,
"lastUpdated": lastUpdate,
}
c.RenderArgs["class"] = class

return c.Render()
}
Expand Down
92 changes: 92 additions & 0 deletions app/controllers/users.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package controllers

import (
"code.google.com/p/go.crypto/bcrypt"
"encoding/hex"
"github.com/huntaub/list/app/routes"
"github.com/robfig/revel"
"labix.org/v2/mgo"
"strings"
)

type User struct {
Email string
HashedPassword string
FullName string
ClassBucket []string
}

var users *mgo.Collection

func init() {
session, _ := mgo.Dial("mongodb://leath:hunter0813@oceanic.mongohq.com:10000/list")
users = session.DB("list").C("users")
}

type Users struct {
*revel.Controller
}

func (u *Users) Login(email string, password string) revel.Result {
var user *User
err := users.Find(map[string]string{"email": email}).One(&user)
if err != nil {
u.Flash.Error("Incorrect username or password.")
return u.Redirect(routes.App.Index())
}

bytes, _ := hex.DecodeString(user.HashedPassword)
if bcrypt.CompareHashAndPassword(bytes, []byte(password)) != nil {
u.Flash.Error("Incorrect username or password.")
return u.Redirect(routes.App.Index())
}

u.Session["user"] = email

return u.Redirect(routes.App.Index())
}

func (u *Users) Register(name string, email string, password string, cpassword string) revel.Result {
if name == "" {
u.Flash.Error("Name is required.")
return u.Redirect(routes.App.Index())
}
if email == "" {
u.Flash.Error("Email is required.")
return u.Redirect(routes.App.Index())
}
if !strings.HasSuffix(email, "@virginia.edu") {
u.Flash.Error("Only @virginia.edu emails allowed.")
return u.Redirect(routes.App.Index())
}
if password == "" {
u.Flash.Error("Password is required.")
return u.Redirect(routes.App.Index())
}
if password != cpassword {
u.Flash.Error("Passwords do not match.")
return u.Redirect(routes.App.Index())
}

var existing *User
if users.Find(map[string]string{"email": email}).One(&existing) == nil {
u.Flash.Error("Someone with that email has already registered.")
return u.Redirect(routes.App.Index())
}

pass, _ := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
users.Insert(&User{
Email: email,
FullName: name,
HashedPassword: hex.EncodeToString(pass),
})

u.Session["user"] = email

return u.Redirect(routes.App.Index())
}

func (u *Users) Logout() revel.Result {
delete(u.Session, "user")
return u.Redirect(routes.App.Index())
}
73 changes: 73 additions & 0 deletions app/views/API/Documentation.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
{{set . "title" "API Documentation" }}
{{ append . "moreScripts" "js/prism.js" }}
{{ append . "moreStyles" "css/prism.css" }}
{{template "header.html" .}}

<div class="container" id="main">
<div class="row">
<div class="col-md-4" style="position: fixed; top: 70px;">
<ul class="list-unstyled nav-list">
<li><a href="#overview" class="visit">Overview</a></li>
<li><a href="#class">Class Information</a></li>
<li><a href="#">Department Information</a></li>
<li><a href="#">Schedule Building</a></li>
</ul>
</div>
<div class="col-md-8 col-md-offset-4">
<h1 id="overview">Leath's List API Documentation</h1>
<p>The Leath's List API was created in order to free class data fully from SIS. It allows students and faculty to create applications that rely on the Data scraped from Lou's List and more without having to go through that work themselves.</p>
<p>In order to use the documentation, you must be registered through Leath's List and have a working "API Key" which you can find by clicking "API" and "Account Settings" on the navbar above.</p>
<h3 id="class">Class Information</h3>
<p><code>GET /api/YOUR_API_KEY/class</code></p>
<p>The Class API will return information about a class (enrollment, name, sections) by sending information about the class.</p>
<h4>Input</h4>
<pre style="padding-top: 0; padding-bottom: 0;">
<code class="language-javascript">
{
"department": "CS",
"number": 2110
}
</code>
</pre>
<h4>Output</h4>
<pre style="padding-top: 0; padding-bottom: 0;">
<code class="language-javascript">
{
name: "Software Development Methods",
department: "CS",
number: 2110,
sections: [
{
number: 1,
type: "Lecture", // or "Discussion", "Laboratory", "Seminar", etc..
sisnumber: 17567,
meetings: [
{
location: "Rice Hall 130",
instructor: "Thomas Horton",
starttime: ISODate("0-01-01T10:00:00Z"), // EST
endtime: ISODate("0-01-01T10:50:00Z"),
days: [1, 3, 5], // Days of the Week where 0 = Sunday
tba: false
}
],
capacity: 110,
enrollment: 106,
topic: "",
credits: 3
},
]
}
</code>
</pre>
<h3 id="class">Department Information</h3>
<p><code>GET /api/YOUR_API_KEY/department</code></p>
<p>The Class API will return a list of classes by department mnemonic.</p>
<h3 id="class">Schedule Building</h3>
<p><code>GET /api/YOUR_API_KEY/schedule</code></p>
<p>The Schedule API will return a list of schedules from a list of classes.</p>
</div>
</div>
</div>

{{template "footer.html" .}}
3 changes: 1 addition & 2 deletions app/views/App/Class.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@

<div class="container" id="main">
<br/>
{{ template "class.html" .class }}
<p>Last Updated - {{ .lastUpdated.Format "January 2, 3:04PM" }}</p>
{{ template "class.html" . }}
</div>
</div>

Expand Down
4 changes: 3 additions & 1 deletion app/views/App/Department.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@

<div class="container" id="main">
<br/>
{{ $loggedIn := .loggedIn }}
{{ range .classes }}
{{ template "class.html" . }}
{{ $op := classCreator . $loggedIn }}
{{ template "class.html" $op }}
{{ else }}
<h1>Sorry, couldn't find any classes for that department.</h1>
{{ end }}
Expand Down
Loading

0 comments on commit 615c6c7

Please sign in to comment.