package grils import ( "fmt" "log" "net/http" "regexp" "strconv" "time" "strings" "fagott.pw/charakterin" "fagott.pw/grilist/frontend" "fagott.pw/grilist/grilist" "fagott.pw/grilist/cache" "github.com/julienschmidt/httprouter" ) type CachedGril struct { Created time.Time Gril *Gril } var ( pgArrayReg = regexp.MustCompile(`(((?P(([^",\\{}\s(NULL)])+|"([^"\\]|\\"|\\\\)*")))(,)?)`) pgValueIdx int ) func findIdx() { for i, subexp := range pgArrayReg.SubexpNames() { if subexp == "value" { pgValueIdx = i break } } } type GrilsModule struct { g *grilist.Grilist c *cache.Cache } func (m *GrilsModule) Name() string { return "Grils" } func (m *GrilsModule) Init(g *grilist.Grilist) { findIdx() m.g = g m.g.Router.GET("/gril/:id", m.viewGril) m.g.Router.GET("/gril/:id/*rest", m.viewGril) m.c = cache.New() } func (m *GrilsModule) getGrils(whereClause string, params ...interface{}) ([]*Gril, error) { var grils []*Gril rows, err := m.g.DB.Query(fmt.Sprintf(`SELECT id, kanji_name, romaji_name, other_names, updated_at, age, birthday, tags FROM grilist.grils_flattened WHERE %s`, whereClause), params...) if err != nil { return nil, err } defer rows.Close() for rows.Next() { gril := &Gril{} var tags []byte var otherNames []byte err = rows.Scan(&gril.ID, &gril.KanjiName, &gril.RomajiName, &otherNames, &gril.UpdatedAt, &gril.Age, &gril.Birthday, &tags) if err != nil { log.Println(gril.ID) log.Println("error scanning in getGrils:", err) continue } gril.Tags = pgArray(tags) gril.OtherNames = pgArray(otherNames) grils = append(grils, gril) } return grils, nil } func (m *GrilsModule) GetListsOfGril(gril *Gril) error { rows, err := m.g.DB.Query(`SELECT list_id FROM grilist.lists_grils WHERE gril_id = $1`, gril.ID) if err != nil { return err } defer rows.Close() for rows.Next() { var listID int if err := rows.Scan(&listID); err != nil { log.Println(err) continue } gril.Lists = append(gril.Lists, listID) } return nil } func (m *GrilsModule) ProvideDashboardData(user *charakterin.User) []grilist.DashboardCategory { var categories []grilist.DashboardCategory t1 := time.Now() rows, err := m.g.DB.Query(`SELECT id FROM grilist.grils ORDER BY updated_at DESC LIMIT 5`) if err != nil { log.Println(err) return categories } defer rows.Close() log.Printf("get_newest_grils: %dms", time.Since(t1).Nanoseconds()/1000000) cat := grilist.DashboardCategory{ Title: "Neue Grils", } for rows.Next() { var id int if err := rows.Scan(&id); err != nil { log.Println(err) continue } t2 := time.Now() gril, err := m.FromID(id) if err != nil { log.Println(err) continue } log.Printf("get_gril_from_id_in_newest_grils: %dms", time.Since(t2).Nanoseconds()/1000000) cat.Cards = append(cat.Cards, frontend.Card{ Title: gril.RomajiName, Description: gril.KanjiName, Size: "medium", Actions: []frontend.Action{ frontend.Action{ Name: "anguckieren", Link: "/gril/" + gril.Slug(), }, }, }) } categories = append(categories, cat) log.Printf("get_newest_grils_overall: %dms", time.Since(t1).Nanoseconds()/1000000) return categories } func (m *GrilsModule) FromID(id int) (*Gril, error) { if g, ok := m.c.Get(id); ok { return g.(*Gril), nil } gril := &Gril{ ID: id, } var tags []byte var otherNames []byte err := m.g.DB.QueryRow(`SELECT kanji_name, romaji_name, other_names, updated_at, age, birthday, tags FROM grilist.grils_flattened WHERE id = $1`, id).Scan(&gril.KanjiName, &gril.RomajiName, &otherNames, &gril.UpdatedAt, &gril.Age, &gril.Birthday, &tags) gril.Tags = pgArray(tags) gril.OtherNames = pgArray(otherNames) if err != nil { return nil, err } m.c.Insert(id, gril) return gril, nil } func (m *GrilsModule) FromIDs(ids []int) ([]*Gril, error) { if len(ids) == 0 { return make([]*Gril, 0), nil } idList := "(" first := true for _, v := range ids { if first { first = false } else { idList += "," } idList += strconv.Itoa(v) } idList += ")" grils, err := m.getGrils("id IN " + idList) return grils, err } func (m *GrilsModule) viewGril(w http.ResponseWriter, r *http.Request, p httprouter.Params) { user, _ := m.g.Charakterin.GetUserFromRequest(r) sid := p.ByName("id") id, err := strconv.Atoi(sid) if err != nil { http.Redirect(w, r, "/", 302) return } gril, err := m.FromID(id) if err != nil { http.Redirect(w, r, "/", 302) return } if err := m.GetListsOfGril(gril); err != nil { log.Println(err) } data := m.g.Renderer.DefaultData() data["user"] = user data["gril"] = gril // ähnliche grils holen rows, err := m.g.DB.Query(`SELECT gril_id FROM ( SELECT DISTINCT gril_id FROM gril_appearance WHERE appearance_id IN (SELECT appearance_id FROM gril_appearance WHERE gril_id = $1) ) as t ORDER BY RANDOM() LIMIT 4;`, id) if err != nil { log.Println("could not get similar grils:", err) http.Error(w, "500", http.StatusInternalServerError) return } defer rows.Close() var similar []*Gril for rows.Next() { var id int if err := rows.Scan(&id); err != nil { log.Println("error scanning for similar gril:", err) continue } g, err := m.FromID(id) if err != nil { log.Println("invalid similar gril:", err) continue } similar = append(similar, g) } data["SimilarGrils"] = similar m.g.Renderer.RenderPage("gril", w, data) } func pgArray(array []byte) []string { var results []string matches := pgArrayReg.FindAllStringSubmatch(string(array), -1) for _, match := range matches { s := match[pgValueIdx] s = strings.Trim(s, "\"") results = append(results, s) } return results } func New() *GrilsModule { return &GrilsModule{} }