package main import ( "encoding/json" "errors" "gopkg.in/gorp.v1" "log" "net/http" "strings" "time" ) type Suggestion struct { SuggestionId int64 VetoingId int64 // -1 for initial suggestion AttendeeId int64 UserId int64 `json:"-"` RestaurantName string Date time.Time `json:"-"` } type PopularSuggestion struct { RestaurantName string Popularity int64 } type SuggestionList struct { Suggestions *[]*Suggestion `json:"suggestions"` } type PopularSuggestionList struct { PopularSuggestions *[]*PopularSuggestion `json:"popularsuggestions"` } func (s *Suggestion) Write(w http.ResponseWriter) error { enc := json.NewEncoder(w) return enc.Encode(s) } func (s *Suggestion) Read(json_str string) error { dec := json.NewDecoder(strings.NewReader(json_str)) return dec.Decode(s) } func (sl *SuggestionList) Write(w http.ResponseWriter) error { enc := json.NewEncoder(w) return enc.Encode(sl) } type SuggestionExistsError struct{} func (aeu SuggestionExistsError) Error() string { return "Suggestion exists" } type SuggestionNotLastError struct{} func (snle SuggestionNotLastError) Error() string { return "Suggestion not last on the stack" } func (s *PopularSuggestion) Write(w http.ResponseWriter) error { enc := json.NewEncoder(w) return enc.Encode(s) } func (sl *PopularSuggestionList) Write(w http.ResponseWriter) error { enc := json.NewEncoder(w) return enc.Encode(sl) } func GetAttendeesSuggestions(transaction *gorp.Transaction, userid int64, date time.Time, attendeeid int64) (*[]*Suggestion, error) { var suggestions []*Suggestion _, err := transaction.Select(&suggestions, "SELECT * from suggestions WHERE UserId=? AND Date=? AND AttendeeID=?", userid, date, attendeeid) if err != nil { return nil, err } return &suggestions, nil } func GetSuggestions(userid int64, date time.Time) (*[]*Suggestion, error) { var suggestions []*Suggestion _, err := DB.Select(&suggestions, "SELECT * from suggestions WHERE UserId=? AND Date=?", userid, date) if err != nil { return nil, err } return &suggestions, nil } func GetSuggestion(suggestionid int64, userid int64, date time.Time) (*Suggestion, error) { var s Suggestion err := DB.SelectOne(&s, "SELECT * from suggestions where UserId=? AND SuggestionId=? AND Date=?", userid, suggestionid, date) if err != nil { return nil, err } return &s, nil } func VetoedBy(transaction *gorp.Transaction, suggestion *Suggestion) (*[]*Suggestion, error) { var suggestions []*Suggestion _, err := transaction.Select(&suggestions, "SELECT * from suggestions WHERE UserId=? AND Date=? AND VetoingId=?", suggestion.UserId, suggestion.Date, suggestion.SuggestionId) if err != nil { return nil, err } return &suggestions, nil } func GetPopularSuggestions() (*[]*PopularSuggestion, error) { var suggestions []*Suggestion suggestionMap := make(map[string]int64) popularSuggestions := make([]*PopularSuggestion, 0) _, err := DB.Select(&suggestions, "SELECT * from suggestions") if err != nil { return nil, err } for i := range suggestions { suggestionMap[suggestions[i].RestaurantName] += 1 } for name, count := range suggestionMap { var popularSuggestion PopularSuggestion popularSuggestion.RestaurantName = name popularSuggestion.Popularity = count popularSuggestions = append(popularSuggestions, &popularSuggestion) } return &popularSuggestions, nil } func InsertSuggestion(s *Suggestion) error { transaction, err := DB.Begin() if err != nil { return err } existing, err := transaction.SelectInt("SELECT count(*) from suggestions where RestaurantName=? AND UserId=? AND Date=?", s.RestaurantName, s.UserId, s.Date) if err != nil { transaction.Rollback() return err } if existing > 0 { transaction.Rollback() return SuggestionExistsError{} } err = transaction.Insert(s) if err != nil { transaction.Rollback() return err } err = transaction.Commit() if err != nil { transaction.Rollback() return err } return nil } func DeleteSuggestion(s *Suggestion) error { transaction, err := DB.Begin() if err != nil { return err } // Ensure suggestion hasn't been vetoed suggestions, err := VetoedBy(transaction, s) if err != nil { transaction.Rollback() return err } if len(*suggestions) > 0 { transaction.Rollback() return SuggestionNotLastError{} } count, err := transaction.Delete(s) if err != nil { transaction.Rollback() return err } if count != 1 { transaction.Rollback() return errors.New("Was going to delete more than one suggestion") } err = transaction.Commit() if err != nil { transaction.Rollback() return err } return nil } func SuggestionHandler(w http.ResponseWriter, r *http.Request) { user, err := GetUserFromSession(r) if err != nil { WriteError(w, 1 /*Not Signed In*/) return } today := time.Now().UTC().Truncate(time.Hour * 24) if r.Method == "POST" { suggestion_json := r.PostFormValue("suggestion") if suggestion_json == "" { WriteError(w, 3 /*Invalid Request*/) return } var suggestion Suggestion err := suggestion.Read(suggestion_json) if err != nil { WriteError(w, 3 /*Invalid Request*/) return } suggestion.SuggestionId = -1 suggestion.UserId = user.UserId suggestion.Date = today err = InsertSuggestion(&suggestion) if err != nil { if _, ok := err.(SuggestionExistsError); ok { WriteError(w, 6 /*Suggestion Exists*/) } else { WriteError(w, 999 /*Internal Error*/) log.Print(err) } return } w.WriteHeader(201 /*Created*/) err = suggestion.Write(w) if err != nil { WriteError(w, 999 /*Internal Error*/) log.Print(err) return } } else if r.Method == "GET" { var sl SuggestionList suggestions, err := GetSuggestions(user.UserId, today) if err != nil { WriteError(w, 999 /*Internal Error*/) log.Print(err) return } sl.Suggestions = suggestions err = (&sl).Write(w) if err != nil { WriteError(w, 999 /*Internal Error*/) log.Print(err) return } } else if r.Method == "DELETE" { suggestionid, err := GetURLID(r.URL.Path) if err != nil { WriteError(w, 3 /* Invalid Request */) return } suggestion, err := GetSuggestion(suggestionid, user.UserId, today) if err != nil { WriteError(w, 3 /*Invalid Request*/) return } err = DeleteSuggestion(suggestion) if err != nil { if _, ok := err.(SuggestionNotLastError); ok { WriteError(w, 8 /*Suggestion Not Last*/) } else { WriteError(w, 999 /*Internal Error*/) log.Print(err) } return } WriteSuccess(w) } else { /* No PUT */ WriteError(w, 3 /*Invalid Request*/) return } } func PopularSuggestionHandler(w http.ResponseWriter, r *http.Request) { if r.Method == "GET" { var sl PopularSuggestionList suggestions, err := GetPopularSuggestions() if err != nil { WriteError(w, 999 /*Internal Error*/) log.Print(err) return } sl.PopularSuggestions = suggestions err = (&sl).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 } }