package main import ( "encoding/json" "errors" "log" "net/http" "strings" "time" ) type Attendee struct { AttendeeId int64 GroupId int64 `json:"-"` Name string Date time.Time `json:"-"` } type PopularAttendee struct { Name string Popularity int64 } type AttendeeList struct { Attendees *[]*Attendee `json:"attendees"` } type PopularAttendeeList struct { PopularAttendees *[]*PopularAttendee `json:"popularattendees"` } func (a *Attendee) Write(w http.ResponseWriter) error { enc := json.NewEncoder(w) return enc.Encode(a) } func (a *Attendee) Read(json_str string) error { dec := json.NewDecoder(strings.NewReader(json_str)) return dec.Decode(a) } func (al *AttendeeList) Write(w http.ResponseWriter) error { enc := json.NewEncoder(w) return enc.Encode(al) } func (pa *PopularAttendee) Write(w http.ResponseWriter) error { enc := json.NewEncoder(w) return enc.Encode(pa) } func (pal *PopularAttendeeList) Write(w http.ResponseWriter) error { enc := json.NewEncoder(w) return enc.Encode(pal) } type AttendeeExistsError struct{} func (aeu AttendeeExistsError) Error() string { return "Attendee exists" } type AttendeeInUseError struct{} func (aeu AttendeeInUseError) Error() string { return "Attendee in use (by suggestion)" } func GetAttendees(groupid int64, date time.Time) (*[]*Attendee, error) { var attendees []*Attendee _, err := DB.Select(&attendees, "SELECT * from attendees WHERE GroupId=? AND Date=?", groupid, date) if err != nil { return nil, err } return &attendees, nil } func GetPopularAttendees() (*[]*PopularAttendee, error) { var attendees []*Attendee attendeeMap := make(map[string]int64) popularAttendees := make([]*PopularAttendee, 0) _, err := DB.Select(&attendees, "SELECT * from attendees") if err != nil { return nil, err } for i := range attendees { attendeeMap[attendees[i].Name] += 1 } for name, count := range attendeeMap { var popularAttendee PopularAttendee popularAttendee.Name = name popularAttendee.Popularity = count popularAttendees = append(popularAttendees, &popularAttendee) } return &popularAttendees, nil } func InsertAttendee(a *Attendee) error { transaction, err := DB.Begin() if err != nil { return err } existing, err := transaction.SelectInt("SELECT count(*) from attendees where GroupId=? AND Name=? AND Date=?", a.GroupId, a.Name, a.Date) if err != nil { transaction.Rollback() return err } if existing > 0 { transaction.Rollback() return AttendeeExistsError{} } err = transaction.Insert(a) if err != nil { transaction.Rollback() return err } err = transaction.Commit() if err != nil { transaction.Rollback() return err } return nil } func GetAttendee(attendeeid int64, groupid int64, date time.Time) (*Attendee, error) { var a Attendee err := DB.SelectOne(&a, "SELECT * from attendees where GroupId=? AND AttendeeId=? AND Date=?", groupid, attendeeid, date) if err != nil { return nil, err } return &a, nil } func DeleteAttendee(a *Attendee) error { transaction, err := DB.Begin() if err != nil { return err } // Ensure attendee isn't used in any suggestions suggestions, err := GetAttendeesSuggestions(transaction, a.GroupId, a.Date, a.AttendeeId) if err != nil { transaction.Rollback() return err } if len(*suggestions) > 0 { transaction.Rollback() return AttendeeInUseError{} } count, err := transaction.Delete(a) if err != nil { transaction.Rollback() return err } if count != 1 { transaction.Rollback() return errors.New("Was going to delete more than one attendee") } err = transaction.Commit() if err != nil { transaction.Rollback() return err } return nil } func AttendeeHandler(w http.ResponseWriter, r *http.Request) { user, err := GetUserFromSession(r) if err != nil { WriteError(w, 1 /*Not Signed In*/) return } today := time.Now().Truncate(time.Hour * 24) if r.Method == "POST" { attendee_json := r.PostFormValue("attendee") if attendee_json == "" { WriteError(w, 3 /*Invalid Request*/) return } var attendee Attendee err := attendee.Read(attendee_json) if err != nil { WriteError(w, 3 /*Invalid Request*/) return } attendee.AttendeeId = -1 attendee.GroupId = user.GroupId attendee.Date = today err = InsertAttendee(&attendee) if err != nil { if _, ok := err.(AttendeeExistsError); ok { WriteError(w, 5 /*Attendee Exists*/) } else { WriteError(w, 999 /*Internal Error*/) log.Print(err) } return } w.WriteHeader(201 /*Created*/) err = attendee.Write(w) if err != nil { WriteError(w, 999 /*Internal Error*/) log.Print(err) return } } else if r.Method == "GET" { var al AttendeeList attendees, err := GetAttendees(user.GroupId, today) if err != nil { WriteError(w, 999 /*Internal Error*/) log.Print(err) return } al.Attendees = attendees err = (&al).Write(w) if err != nil { WriteError(w, 999 /*Internal Error*/) log.Print(err) return } } else if r.Method == "DELETE" { attendeeid, err := GetURLID(r.URL.Path) if err != nil { WriteError(w, 3 /* Invalid Request */) return } attendee, err := GetAttendee(attendeeid, user.GroupId, today) if err != nil { WriteError(w, 3 /*Invalid Request*/) return } err = DeleteAttendee(attendee) if err != nil { if _, ok := err.(AttendeeInUseError); ok { WriteError(w, 7 /*Attendee In Use*/) } else { WriteError(w, 999 /*Internal Error*/) log.Print(err) } return } WriteSuccess(w) } else { /* No PUT */ WriteError(w, 3 /*Invalid Request*/) return } } func PopularAttendeeHandler(w http.ResponseWriter, r *http.Request) { if r.Method == "GET" { var pal PopularAttendeeList attendees, err := GetPopularAttendees() if err != nil { WriteError(w, 999 /*Internal Error*/) log.Print(err) return } pal.PopularAttendees = attendees err = (&pal).Write(w) if err != nil { WriteError(w, 999 /*Internal Error*/) log.Print(err) return } } else { /* No POST, PUT, or DELETE */ WriteError(w, 3 /*Invalid Request*/) return } }