blob: 972ffe84938993dac2544dd7ac60233e005cef74 [file] [log] [blame]
package mart
import (
"bytes"
"fmt"
"math/rand"
"net/http"
"strconv"
"strings"
"appengine"
"appengine/datastore"
"appengine/mail"
"appengine/user"
"github.com/mjibson/appstats"
)
func init() {
http.HandleFunc("/", front)
http.Handle("/checkout", appstats.NewHandler(checkout))
http.HandleFunc("/admin/populate", adminPopulate)
}
func front(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/" {
http.NotFound(w, r)
return
}
fmt.Fprintf(w, "Hello, welcome to Gopher Mart!")
}
const (
numItems = 100 // number of different items for sale
appAdmin = "noreply@google.com" // an admin of this app, for sending mail
)
// Item represents an item for sale in Gopher Mart.
type Item struct {
Name string
Price float64
}
func itemKey(c appengine.Context, i int) *datastore.Key {
return datastore.NewKey(c, "Item", fmt.Sprintf("item%04d", i), 0, nil)
}
func checkout(c appengine.Context, w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
num, err := strconv.Atoi(r.FormValue("num"))
if err == nil && (num < 1 || num > 30) {
err = fmt.Errorf("%d out of range [1,30]", num)
}
if err != nil {
http.Error(w, fmt.Sprintf("bad number of items: %v", err), http.StatusBadRequest)
return
}
// Pick random items.
keys := make([]*datastore.Key, num)
for i := range keys {
keys[i] = itemKey(c, rand.Intn(numItems))
}
// Dumb load.
var items []*Item
for _, key := range keys {
item := new(Item)
if err := datastore.Get(c, key, item); err != nil {
http.Error(w, fmt.Sprintf("datastore.Get: %v", err), http.StatusBadRequest)
return
}
items = append(items, item)
}
// Print items.
var b bytes.Buffer
fmt.Fprintf(&b, "Here's what you bought:\n")
var sum float64
for _, item := range items {
fmt.Fprintf(&b, "\t%s", item.Name)
fmt.Fprint(&b, strings.Repeat("\t", (40-len(item.Name)+7)/8))
fmt.Fprintf(&b, "$%5.2f\n", item.Price)
sum += item.Price
}
fmt.Fprintln(&b, strings.Repeat("-", 55))
fmt.Fprintf(&b, "\tTotal:\t\t\t\t\t$%.2f\n", sum)
w.Write(b.Bytes())
sendReceipt(c, user.Current(c).Email, b.String())
}
func sendReceipt(c appengine.Context, dst, body string) {
msg := &mail.Message{
Sender: appAdmin,
To: []string{dst},
Subject: "Your Gopher Mart receipt",
Body: body,
}
if err := mail.Send(c, msg); err != nil {
c.Errorf("mail.Send: %v", err)
}
}
func adminPopulate(w http.ResponseWriter, r *http.Request) {
c := appengine.NewContext(r)
for i := range [numItems]struct{}{} { // r hates this. tee hee.
key := itemKey(c, i)
good := goods[rand.Intn(len(goods))]
item := &Item{
// TODO: vary names more
Name: fmt.Sprintf("%s %dg", good.name, i+1),
Price: float64(rand.Intn(1999)+1) / 100,
}
if _, err := datastore.Put(c, key, item); err != nil {
http.Error(w, err.Error(), 500)
return
}
}
fmt.Fprintf(w, "ok. %d items populated.", numItems)
}
var goods = [...]struct {
name string
}{
{"Gopher Bran"},
{"Gopher Flakes"},
{"Gopher Grease"},
{"Gopher Litter"},
}