From 0a8b4ca6c804f737ee164ad300c2fc7f016aa289 Mon Sep 17 00:00:00 2001 From: Erik Winter Date: Sat, 6 Jan 2024 14:22:22 +0100 Subject: [PATCH] review mentions and quality --- cmd/api-service/handler/moviereview.go | 50 +++++++++++++++++++++++ cmd/api-service/handler/review.go | 11 +++--- cmd/api-service/moviestore/review.go | 55 ++++++++++++++++++++------ cmd/api-service/moviestore/sqlite.go | 4 ++ cmd/api-service/service.go | 3 +- 5 files changed, 103 insertions(+), 20 deletions(-) create mode 100644 cmd/api-service/handler/moviereview.go diff --git a/cmd/api-service/handler/moviereview.go b/cmd/api-service/handler/moviereview.go new file mode 100644 index 0000000..fb56f8d --- /dev/null +++ b/cmd/api-service/handler/moviereview.go @@ -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 + } +} diff --git a/cmd/api-service/handler/review.go b/cmd/api-service/handler/review.go index f095d4c..8907203 100644 --- a/cmd/api-service/handler/review.go +++ b/cmd/api-service/handler/review.go @@ -26,18 +26,17 @@ func (reviewAPI *ReviewAPI) ServeHTTP(w http.ResponseWriter, r *http.Request) { subPath, _ := ShiftPath(r.URL.Path) switch { - case r.Method == http.MethodGet && subPath == "": - reviewAPI.List(w, r) + case r.Method == http.MethodGet && subPath == "unrated": + reviewAPI.ListUnrated(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 *ReviewAPI) List(w http.ResponseWriter, r *http.Request) { - logger := reviewAPI.logger.With("method", "list") +func (reviewAPI *ReviewAPI) ListUnrated(w http.ResponseWriter, r *http.Request) { + logger := reviewAPI.logger.With("method", "listUnrated") - movieID := r.Context().Value(MovieKey).(string) - reviews, err := reviewAPI.repo.FindByMovieID(movieID) + reviews, err := reviewAPI.repo.FindUnrated() if err != nil { Error(w, http.StatusInternalServerError, "could not get reviews", err, logger) return diff --git a/cmd/api-service/moviestore/review.go b/cmd/api-service/moviestore/review.go index 5e7ea70..d75e0c6 100644 --- a/cmd/api-service/moviestore/review.go +++ b/cmd/api-service/moviestore/review.go @@ -1,5 +1,7 @@ package moviestore +import "strings" + const ( ReviewSourceIMDB = "imdb" ) @@ -7,11 +9,13 @@ const ( type ReviewSource string type Review struct { - ID string - MovieID string - Source ReviewSource - URL string - Review string + ID string + MovieID string + Source ReviewSource + URL string + Review string + Quality int + Mentions []string } type ReviewRepository struct { @@ -25,8 +29,8 @@ func NewReviewRepository(db *SQLite) *ReviewRepository { } func (rr *ReviewRepository) Store(r Review) error { - if _, err := rr.db.Exec(`REPLACE INTO review (id, movie_id, source, url, review) VALUES (?, ?, ?, ?, ?)`, - r.ID, r.MovieID, r.Source, r.URL, r.Review); err != nil { + 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, r.Quality, strings.Join(r.Mentions, ",")); err != nil { return err } @@ -34,31 +38,56 @@ func (rr *ReviewRepository) Store(r 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 { return Review{}, row.Err() } - review := Review{} - if err := row.Scan(&review.ID, &review.MovieID, &review.Source, &review.URL, &review.Review); err != nil { + r := Review{} + 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 } + r.Mentions = strings.Split(mentions, ",") - return review, nil + return r, nil } 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 { 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); err != nil { + 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) + } + 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) } rows.Close() diff --git a/cmd/api-service/moviestore/sqlite.go b/cmd/api-service/moviestore/sqlite.go index d3df191..70e4963 100644 --- a/cmd/api-service/moviestore/sqlite.go +++ b/cmd/api-service/moviestore/sqlite.go @@ -63,6 +63,10 @@ var sqliteMigrations = []sqliteMigration{ `INSERT INTO job_queue (movie_id, action, status) SELECT id, 'fetch-imdb-reviews', 'todo' 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 ( diff --git a/cmd/api-service/service.go b/cmd/api-service/service.go index c724b86..95f3c54 100644 --- a/cmd/api-service/service.go +++ b/cmd/api-service/service.go @@ -40,8 +40,9 @@ func main() { apis := handler.APIIndex{ "admin": handler.NewAdminAPI(jobQueue, logger), "movie": handler.NewMovieAPI(handler.APIIndex{ - "review": handler.NewReviewAPI(moviestore.NewReviewRepository(db), logger), + "review": handler.NewMovieReviewAPI(moviestore.NewReviewRepository(db), 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))