review mentions and quality

This commit is contained in:
Erik Winter 2024-01-06 14:22:22 +01:00
parent 8c1dcfa396
commit 0a8b4ca6c8
5 changed files with 103 additions and 20 deletions

View File

@ -0,0 +1,50 @@
package handler
import (
"encoding/json"
"fmt"
"log/slog"
"net/http"
"ewintr.nl/emdb/cmd/api-service/moviestore"
)
type MovieReviewAPI struct {
repo *moviestore.ReviewRepository
logger *slog.Logger
}
func NewMovieReviewAPI(repo *moviestore.ReviewRepository, logger *slog.Logger) *MovieReviewAPI {
return &MovieReviewAPI{
repo: repo,
logger: logger.With("api", "moviereview"),
}
}
func (reviewAPI *MovieReviewAPI) ServeHTTP(w http.ResponseWriter, r *http.Request) {
logger := reviewAPI.logger.With("method", "serveHTTP")
subPath, _ := ShiftPath(r.URL.Path)
switch {
case r.Method == http.MethodGet && subPath == "":
reviewAPI.List(w, r)
default:
Error(w, http.StatusNotFound, "unregistered path", fmt.Errorf("method %q with subpath %q was not registered in /review", r.Method, subPath), logger)
}
}
func (reviewAPI *MovieReviewAPI) List(w http.ResponseWriter, r *http.Request) {
logger := reviewAPI.logger.With("method", "list")
movieID := r.Context().Value(MovieKey).(string)
reviews, err := reviewAPI.repo.FindByMovieID(movieID)
if err != nil {
Error(w, http.StatusInternalServerError, "could not get reviews", err, logger)
return
}
if err := json.NewEncoder(w).Encode(reviews); err != nil {
Error(w, http.StatusInternalServerError, "could not encode reviews", err, logger)
return
}
}

View File

@ -26,18 +26,17 @@ func (reviewAPI *ReviewAPI) ServeHTTP(w http.ResponseWriter, r *http.Request) {
subPath, _ := ShiftPath(r.URL.Path) subPath, _ := ShiftPath(r.URL.Path)
switch { switch {
case r.Method == http.MethodGet && subPath == "": case r.Method == http.MethodGet && subPath == "unrated":
reviewAPI.List(w, r) reviewAPI.ListUnrated(w, r)
default: default:
Error(w, http.StatusNotFound, "unregistered path", fmt.Errorf("method %q with subpath %q was not registered in /review", r.Method, subPath), logger) Error(w, http.StatusNotFound, "unregistered path", fmt.Errorf("method %q with subpath %q was not registered in /review", r.Method, subPath), logger)
} }
} }
func (reviewAPI *ReviewAPI) List(w http.ResponseWriter, r *http.Request) { func (reviewAPI *ReviewAPI) ListUnrated(w http.ResponseWriter, r *http.Request) {
logger := reviewAPI.logger.With("method", "list") logger := reviewAPI.logger.With("method", "listUnrated")
movieID := r.Context().Value(MovieKey).(string) reviews, err := reviewAPI.repo.FindUnrated()
reviews, err := reviewAPI.repo.FindByMovieID(movieID)
if err != nil { if err != nil {
Error(w, http.StatusInternalServerError, "could not get reviews", err, logger) Error(w, http.StatusInternalServerError, "could not get reviews", err, logger)
return return

View File

@ -1,5 +1,7 @@
package moviestore package moviestore
import "strings"
const ( const (
ReviewSourceIMDB = "imdb" ReviewSourceIMDB = "imdb"
) )
@ -7,11 +9,13 @@ const (
type ReviewSource string type ReviewSource string
type Review struct { type Review struct {
ID string ID string
MovieID string MovieID string
Source ReviewSource Source ReviewSource
URL string URL string
Review string Review string
Quality int
Mentions []string
} }
type ReviewRepository struct { type ReviewRepository struct {
@ -25,8 +29,8 @@ func NewReviewRepository(db *SQLite) *ReviewRepository {
} }
func (rr *ReviewRepository) Store(r Review) error { func (rr *ReviewRepository) Store(r Review) error {
if _, err := rr.db.Exec(`REPLACE INTO review (id, movie_id, source, url, review) VALUES (?, ?, ?, ?, ?)`, if _, err := rr.db.Exec(`REPLACE INTO review (id, movie_id, source, url, review, quality, mentions) VALUES (?, ?, ?, ?, ?, ?, ?)`,
r.ID, r.MovieID, r.Source, r.URL, r.Review); err != nil { r.ID, r.MovieID, r.Source, r.URL, r.Review, r.Quality, strings.Join(r.Mentions, ",")); err != nil {
return err return err
} }
@ -34,31 +38,56 @@ func (rr *ReviewRepository) Store(r Review) error {
} }
func (rr *ReviewRepository) FindOne(id string) (Review, error) { func (rr *ReviewRepository) FindOne(id string) (Review, error) {
row := rr.db.QueryRow(`SELECT id, movie_id, source, url, review FROM review WHERE id=?`, id) row := rr.db.QueryRow(`SELECT id, movie_id, source, url, review, quality, mentions FROM review WHERE id=?`, id)
if row.Err() != nil { if row.Err() != nil {
return Review{}, row.Err() return Review{}, row.Err()
} }
review := Review{} r := Review{}
if err := row.Scan(&review.ID, &review.MovieID, &review.Source, &review.URL, &review.Review); err != nil { var mentions string
if err := row.Scan(&r.ID, &r.MovieID, &r.Source, &r.URL, &r.Review, &r.Quality, &mentions); err != nil {
return Review{}, err return Review{}, err
} }
r.Mentions = strings.Split(mentions, ",")
return review, nil return r, nil
} }
func (rr *ReviewRepository) FindByMovieID(movieID string) ([]Review, error) { func (rr *ReviewRepository) FindByMovieID(movieID string) ([]Review, error) {
rows, err := rr.db.Query(`SELECT id, movie_id, source, url, review FROM review WHERE movie_id=?`, movieID) rows, err := rr.db.Query(`SELECT id, movie_id, source, url, review, quality, mentions FROM review WHERE movie_id=?`, movieID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
reviews := make([]Review, 0) reviews := make([]Review, 0)
var mentions string
for rows.Next() { for rows.Next() {
r := Review{} r := Review{}
if err := rows.Scan(&r.ID, &r.MovieID, &r.Source, &r.URL, &r.Review); err != nil { if err := rows.Scan(&r.ID, &r.MovieID, &r.Source, &r.URL, &r.Review, &r.Quality, &mentions); err != nil {
return nil, err return nil, err
} }
r.Mentions = strings.Split(mentions, ",")
reviews = append(reviews, r)
}
rows.Close()
return reviews, nil
}
func (rr *ReviewRepository) FindUnrated() ([]Review, error) {
rows, err := rr.db.Query(`SELECT id, movie_id, source, url, review, quality, mentions FROM review WHERE quality=0`)
if err != nil {
return nil, err
}
reviews := make([]Review, 0)
var mentions string
for rows.Next() {
r := Review{}
if err := rows.Scan(&r.ID, &r.MovieID, &r.Source, &r.URL, &r.Review, &r.Quality, &mentions); err != nil {
return nil, err
}
r.Mentions = strings.Split(mentions, ",")
reviews = append(reviews, r) reviews = append(reviews, r)
} }
rows.Close() rows.Close()

View File

@ -63,6 +63,10 @@ var sqliteMigrations = []sqliteMigration{
`INSERT INTO job_queue (movie_id, action, status) `INSERT INTO job_queue (movie_id, action, status)
SELECT id, 'fetch-imdb-reviews', 'todo' SELECT id, 'fetch-imdb-reviews', 'todo'
FROM movie`, FROM movie`,
`AlTER TABLE review ADD COLUMN "references" TEXT NOT NULL DEFAULT ""`,
`ALTER TABLE review ADD COLUMN "quality" INTEGER NOT NULL DEFAULT 0`,
`ALTER TABLE review DROP COLUMN "references"`,
`ALTER TABLE review ADD COLUMN "mentions" TEXT NOT NULL DEFAULT ""`,
} }
var ( var (

View File

@ -40,8 +40,9 @@ func main() {
apis := handler.APIIndex{ apis := handler.APIIndex{
"admin": handler.NewAdminAPI(jobQueue, logger), "admin": handler.NewAdminAPI(jobQueue, logger),
"movie": handler.NewMovieAPI(handler.APIIndex{ "movie": handler.NewMovieAPI(handler.APIIndex{
"review": handler.NewReviewAPI(moviestore.NewReviewRepository(db), logger), "review": handler.NewMovieReviewAPI(moviestore.NewReviewRepository(db), logger),
}, moviestore.NewMovieRepository(db), jobQueue, logger), }, moviestore.NewMovieRepository(db), jobQueue, logger),
"review": handler.NewReviewAPI(moviestore.NewReviewRepository(db), logger),
} }
go http.ListenAndServe(fmt.Sprintf(":%d", *port), handler.NewServer(*apiKey, apis, logger)) go http.ListenAndServe(fmt.Sprintf(":%d", *port), handler.NewServer(*apiKey, apis, logger))