func ValidateToken(s gorp.SqlExecutor, memberID int64, token string) error {
	query := squirrel.Select("*").From(TableNameToken).
		Where(squirrel.Eq{"MemberID": memberID, "Value": token})
	tokens := []*Token{}
	sqlutil.Select(s, query, &tokens)
	if len(tokens) == 0 {
		return fmt.Errorf("token not found")
	}
	return nil
}
func ListenToFeeds(s gorp.SqlExecutor) error {
	feeds := []*Feed{}
	query := squirrel.Select("*").From(TableNameFeed).
		Where(squirrel.Eq{"Deleted": false})
	if err := sqlutil.Select(s, query, &feeds); err != nil {
		return err
	}
	for _, feed := range feeds {
		if err := feed.UpdateStories(s); err != nil {
			return err
		}
	}
	return nil
}
func FindMember(s gorp.SqlExecutor, email string) (*Member, error) {
	query := squirrel.Select("*").From(TableNameMember).
		Where(squirrel.Eq{"email": email})
	members := []*Member{}
	if err := sqlutil.Select(s, query, &members); err != nil {
		return nil, err
	}

	err := fmt.Errorf("no user for email")
	if len(members) == 0 {
		return nil, err
	}

	return members[0], nil
}
func (m *Member) PostGet(s gorp.SqlExecutor) error {
	m.Object = ObjectNameMember
	m.Images = m.ImagesSlice()
	m.Hashtags = m.HashtagsSlice()
	m.Location = m.LocationCoords()

	catIds := []int64{}
	catMems := []*CategoryMember{}
	query := squirrel.Select("*").From(TableNameCategoryMember).
		Where(squirrel.Eq{"memberID": m.ID})
	sqlutil.Select(s, query, &catMems)
	for _, catMem := range catMems {
		catIds = append(catIds, catMem.CategoryID)
	}
	m.CategoryIds = catIds

	return nil
}
func DecayScores(s gorp.SqlExecutor) error {
	current := milli.Timestamp(time.Now())
	yesterday := milli.Timestamp(time.Now().Add(time.Hour * -24))
	tenDaysAgo := milli.Timestamp(time.Now().Add((time.Hour * 24) * 10))
	query := squirrel.Select("*").From(TableNameStory).
		Where("!(LastDecayTimestamp between ? and ?)", yesterday, current).
		Where("Timestamp > ?", tenDaysAgo)

	stories := []*Story{}
	sqlutil.Select(s, query, &stories)
	for _, story := range stories {
		story.Score /= 2.0
		story.LastDecayTimestamp = milli.Timestamp(time.Now())
		if _, err := s.Update(story); err != nil {
			return err
		}
	}
	return nil
}
func TopStoriesHandler() httperr.Handler {
	return func(w http.ResponseWriter, r *http.Request) error {
		dbmap, err := getDB()
		defer dbmap.Db.Close()
		if err != nil {
			return err
		}

		v := r.URL.Query()

		// get members for category if specified
		memIDs := []string{}
		categoryID := v.Get("categoryId")
		if categoryID != "" {
			catMems := []*model.CategoryMember{}
			query := "select * from category_members where categoryID = ?"
			dbmap.Select(&catMems, query, categoryID)

			for _, catMem := range catMems {
				memIDs = append(memIDs, fmt.Sprint(catMem.MemberID))
			}
		}

		limit, err := strconv.ParseUint(v.Get("q-limit"), 10, 64)
		if err != nil {
			limit = 20
		}

		offset, _ := strconv.ParseUint(v.Get("q-offset"), 10, 64)

		query := squirrel.Select("*").From(model.TableNameStory).OrderBy("Score desc").Limit(limit).Offset(offset)
		if len(memIDs) > 0 {
			query = query.Where(squirrel.Eq{"memberId": memIDs})
		}

		stories := []*model.Story{}
		if err := sqlutil.Select(dbmap, query, &stories); err != nil {
			return err
		}

		return json.NewEncoder(w).Encode(stories)
	}
}
func LogoutHandler() httperr.Handler {
	return func(w http.ResponseWriter, r *http.Request) error {
		dbmap, err := getDB()
		defer dbmap.Db.Close()
		if err != nil {
			return err
		}

		tokenValue := r.URL.Query().Get(authTokenKey)
		query := squirrel.Select("*").
			From(model.TableNameToken).
			Where(squirrel.Eq{"Value": tokenValue})

		tokens := []*model.Token{}
		if err := sqlutil.Select(dbmap, query, &tokens); err != nil {
			return err
		}
		for _, token := range tokens {
			dbmap.Delete(token)
		}
		return nil
	}
}