minio save/get recipe; parser fixings

This commit is contained in:
yash 2024-01-21 13:10:06 +03:00
parent 67e913ff3a
commit d7c777a2ca
3 changed files with 91 additions and 64 deletions

View File

@ -0,0 +1,18 @@
package stringcv
import (
"fmt"
"strings"
)
// GetFilenameFromUrl convert url to filename.
func GetFilenameFromUrl(url string) string {
url_els := strings.Split(url, "/")
return url_els[len(url_els)-1]
}
// RenameFile changes filename.
func RenameFile(old_filename, new_name string) string {
old_file_els := strings.Split(old_filename, ".")
return fmt.Sprintf("%s.%s", new_name, old_file_els[len(old_file_els)-1])
}

View File

@ -22,7 +22,7 @@ type ObjStorage struct {
} }
func New(ctx context.Context, addr, user, password string) (*ObjStorage, error) { func New(ctx context.Context, addr, user, password string) (*ObjStorage, error) {
const op = "minio.New" const op = "media_storage.minio.New"
minioClient, err := minio.New(addr, &minio.Options{ minioClient, err := minio.New(addr, &minio.Options{
Creds: credentials.NewStaticV4(user, password, ""), Creds: credentials.NewStaticV4(user, password, ""),
@ -66,7 +66,7 @@ func New(ctx context.Context, addr, user, password string) (*ObjStorage, error)
// Upload file to bucket // Upload file to bucket
func (o *ObjStorage) uploadFile(ctx context.Context, bucketName string, objectName string, fileBuffer io.Reader, contentType string, fileSize int64) error { func (o *ObjStorage) uploadFile(ctx context.Context, bucketName string, objectName string, fileBuffer io.Reader, contentType string, fileSize int64) error {
const op = "minio.UploadFile" const op = "media_storage.minio.UploadFile"
// Upload the zip file with PutObject // Upload the zip file with PutObject
info, err := o.minio.PutObject(ctx, bucketName, objectName, fileBuffer, fileSize, minio.PutObjectOptions{ContentType: contentType}) info, err := o.minio.PutObject(ctx, bucketName, objectName, fileBuffer, fileSize, minio.PutObjectOptions{ContentType: contentType})
if err != nil { if err != nil {
@ -78,7 +78,7 @@ func (o *ObjStorage) uploadFile(ctx context.Context, bucketName string, objectNa
// Get file from bucket // Get file from bucket
func (o *ObjStorage) getFile(ctx context.Context, bucketName string, objectName string) (*minio.Object, error) { func (o *ObjStorage) getFile(ctx context.Context, bucketName string, objectName string) (*minio.Object, error) {
const op = "minio.GetFile" const op = "media_storage.minio.GetFile"
// Get object from minio // Get object from minio
minio_obj, err := o.minio.GetObject(ctx, bucketName, objectName, minio.GetObjectOptions{Checksum: true}) minio_obj, err := o.minio.GetObject(ctx, bucketName, objectName, minio.GetObjectOptions{Checksum: true})
if err != nil { if err != nil {
@ -89,7 +89,7 @@ func (o *ObjStorage) getFile(ctx context.Context, bucketName string, objectName
// Delete file from bucket // Delete file from bucket
func (o *ObjStorage) delFile(ctx context.Context, bucketName string, objectName string) error { func (o *ObjStorage) delFile(ctx context.Context, bucketName string, objectName string) error {
const op = "minio.DelFile" const op = "media_storage.minio.DelFile"
err := o.minio.RemoveObject(ctx, bucketName, objectName, minio.RemoveObjectOptions{ForceDelete: true}) err := o.minio.RemoveObject(ctx, bucketName, objectName, minio.RemoveObjectOptions{ForceDelete: true})
if err != nil { if err != nil {
return fmt.Errorf("%s: %w", op, err) return fmt.Errorf("%s: %w", op, err)
@ -97,6 +97,22 @@ func (o *ObjStorage) delFile(ctx context.Context, bucketName string, objectName
return nil return nil
} }
func (o *ObjStorage) SaveRecipeImage(ctx context.Context) error { // SaveRecipeImage saves image to bucket for recipes photos.
o.uploadFile(ctx, recipeImgBucket) func (o *ObjStorage) SaveRecipeImage(ctx context.Context, imageFile io.Reader, filename string, contentType string, fileSize int64) error {
const op = "media_storage.minio.SaveRecipeImage"
err := o.uploadFile(ctx, recipeImgBucket, filename, imageFile, contentType, fileSize)
if err != nil {
return fmt.Errorf("%s: %w", op, err)
}
return err
}
// RecipeImage gets image from recipe's images bucket by filename.
func (o *ObjStorage) RecipeImage(ctx context.Context, filename string) (*minio.Object, error) {
const op = "media_storage.minio.RecipeImage"
obj, err := o.getFile(ctx, recipeImgBucket, filename)
if err != nil {
return nil, fmt.Errorf("%s: %w", op, err)
}
return obj, err
} }

View File

@ -1,17 +1,21 @@
package parser package parser
import ( import (
"context"
"errors" "errors"
"fmt" "fmt"
"io" "io"
"log/slog" "log/slog"
"net/http"
"net/url" "net/url"
"recipes/internal/domain/models" "recipes/internal/domain/models"
"recipes/internal/lib/stringcv"
"strconv" "strconv"
"strings" "strings"
"sync" "sync"
"github.com/PuerkitoBio/goquery" "github.com/PuerkitoBio/goquery"
"github.com/google/uuid"
"github.com/s32x/httpclient" "github.com/s32x/httpclient"
) )
@ -27,21 +31,34 @@ var (
CookieNotFoundErr = errors.New("cookie not found") CookieNotFoundErr = errors.New("cookie not found")
NotSuccessReqErr = errors.New("not success request") NotSuccessReqErr = errors.New("not success request")
EmptyLinkErr = errors.New("empty link") EmptyLinkErr = errors.New("empty link")
RecipeExistsErr = errors.New("recipe already exists")
) )
type pictureSaver interface {
SaveRecipeImage(ctx context.Context, imageFile io.Reader, filename string, contentType string, fileSize int64) error
}
type recipeSaver interface {
AddRecipe(ctx context.Context, recipe models.Recipe) error
}
type recipeProvider interface {
RecipeExists(ctx context.Context, title string) (bool, error)
}
// SaveAllPages saves all pages to storage. // SaveAllPages saves all pages to storage.
func SaveAllPages(log slog.Logger) error { func SaveAllPages(log slog.Logger, ps pictureSaver, rs recipeSaver, rp recipeProvider) error {
const op = "parser.SaveAllPages" const op = "parser.SaveAllPages"
// get total // get total
log.Debug("Сохраняю страницу 1...") log.Debug("Сохраняю страницу 1...")
total, err := SavePage(log, 1) total, err := SavePage(log, 1, ps, rs, rp)
if err != nil { if err != nil {
return fmt.Errorf("%s: %w", op, err) return fmt.Errorf("%s: %w", op, err)
} }
fmt.Println("Total =", total) fmt.Println("Total =", total)
for i := 2; i <= total; i++ { for i := 2; i <= total; i++ {
log.Debug(fmt.Sprintf("Сохраняю страницу %d...\n", i)) log.Debug(fmt.Sprintf("Сохраняю страницу %d...\n", i))
_, err = SavePage(log, i) _, err = SavePage(log, i, ps, rs, rp)
log.Debug(fmt.Sprintf("Страница %d сохранена\n", i)) log.Debug(fmt.Sprintf("Страница %d сохранена\n", i))
} }
@ -49,7 +66,7 @@ func SaveAllPages(log slog.Logger) error {
} }
// SavePage saves page to storage. // SavePage saves page to storage.
func SavePage(log slog.Logger, page int) (int, error) { func SavePage(log slog.Logger, page int, ps pictureSaver, rs recipeSaver, rp recipeProvider) (int, error) {
const op = "parser.SavePage" const op = "parser.SavePage"
var resp GetPageResp var resp GetPageResp
@ -102,20 +119,20 @@ func SavePage(log slog.Logger, page int) (int, error) {
var wg sync.WaitGroup var wg sync.WaitGroup
wg.Add(len(recipes)) wg.Add(len(recipes))
for i := 0; i < len(recipes); i++ { for i := 0; i < len(recipes); i++ {
go func(i int) { go func(i int, log slog.Logger) {
defer wg.Done() defer wg.Done()
err = recipes[i].GetRecipe() err = GetRecipe(&recipes[i], ps, rs, rp)
if err != nil { if err != nil {
return fmt.Errorf("%s: %w", op, err) log.Error("Failed to get recipe", "err", fmt.Errorf("%s: %w", op, err))
} }
}(i) }(i, log)
} }
wg.Wait() wg.Wait()
return resp.Data.Pages, nil return resp.Data.Pages, nil
} }
// GetRecipe gets recipe info and saves recipe to storage. // GetRecipe gets recipe info and saves recipe to storage.
func GetRecipe(r *models.Recipe) error { func GetRecipe(r *models.Recipe, ps pictureSaver, rs recipeSaver, rp recipeProvider) error {
const op = "parser.GetRecipe" const op = "parser.GetRecipe"
if r.Link == "" { if r.Link == "" {
@ -200,65 +217,41 @@ func GetRecipe(r *models.Recipe) error {
doc.Find("div.similar-items>a.similar-items__link").Each(func(i int, s *goquery.Selection) { doc.Find("div.similar-items>a.similar-items__link").Each(func(i int, s *goquery.Selection) {
r.Categories = append(r.Categories, s.Text()) r.Categories = append(r.Categories, s.Text())
}) })
// // вывод результатов
// fmt.Println("-------------------")
// fmt.Printf("%+v\n", r)
// fmt.Println("-------------------")
// check recipe exists // check recipe exists
ex, err := postgres.DB.RecipeExists(r.Title) // interface! ex, err := rp.RecipeExists(context.Background(), r.Title) // interface!
if err != nil || ex { if err != nil || ex {
return fmt.Errorf("%s: %w", op, fmt.Errorf("recipe already exists")) return fmt.Errorf("%s: %w", op, RecipeExistsErr)
} }
// save picture // save picture
err = r.SaveRecipePicture() err = SaveRecipePicture(r, ps)
// add to database if err != nil {
var final_recipe models.Recipe = models.Recipe{ return err
Title: r.Title,
Description: r.Description,
Image: r.Image,
CookingTime: r.CookingTime,
Link: r.Link,
ServingsNum: r.ServingsNum,
Calories: r.Calories,
Ingredients: r.Ingredients,
Recipe_steps: r.Recipe_steps,
Advices: r.Advices,
Categories: r.Categories,
} }
// insert recipe // insert recipe
err = postgres.DB.AddRecipe(final_recipe) err = rs.AddRecipe(context.Background(), *r)
return fmt.Errorf("%s: %w", op, err) return fmt.Errorf("%s: %w", op, err)
} }
// func (r *Recipe) SaveRecipePicture() error { func SaveRecipePicture(r *models.Recipe, ps pictureSaver) error {
// resp, err := http.Get(r.Image) const op = "parser.SaveRecipePicture"
// if err != nil {
// return err resp, err := http.Get(r.Image)
// } if err != nil {
// defer resp.Body.Close() return fmt.Errorf("%s: %w", op, err)
// content_len, _ := strconv.ParseInt(resp.Header["Content-Length"][0], 10, 64) }
// // change name to generated uuid defer resp.Body.Close()
// filename := renamefile(getFilenameFromUrl(r.Image), uuid.NewString()) content_len, _ := strconv.ParseInt(resp.Header["Content-Length"][0], 10, 64)
// // upload to minio // change name to generated uuid
// err = cminio.UploadFile(cminio.RecipeImg, filename, resp.Body, resp.Header["Content-Type"][0], content_len) filename := stringcv.RenameFile(stringcv.GetFilenameFromUrl(r.Image), uuid.NewString())
// upload to storage
// // change to filename err = ps.SaveRecipeImage(context.Background(), resp.Body, filename, resp.Header["Content-Type"][0], content_len)
// r.Image = filename if err != nil {
// return err return err
// } }
// change to filename
// // url to filename r.Image = filename
// func getFilenameFromUrl(url string) string { return err
// url_els := strings.Split(url, "/") }
// return url_els[len(url_els)-1]
// }
// // change file name
// func renamefile(old_filename, new_name string) string {
// old_file_els := strings.Split(old_filename, ".")
// return fmt.Sprintf("%s.%s", new_name, old_file_els[len(old_file_els)-1])
// }
// GetKey gets // GetKey gets
func GetKey(log slog.Logger) error { func GetKey(log slog.Logger) error {