Skip to content

Commit

Permalink
Merge pull request #23 from Ulas-Scan/development
Browse files Browse the repository at this point in the history
feat: refactoring code and make swagger docs
  • Loading branch information
iyoubee authored Jun 21, 2024
2 parents bec5eff + c804eb2 commit 9e93d91
Show file tree
Hide file tree
Showing 14 changed files with 1,412 additions and 151 deletions.
23 changes: 23 additions & 0 deletions controller/history.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,18 @@ func NewHistoryController(hs service.HistoryService) HistoryController {
}
}

// GetHistories godoc
// @Summary Retrieve the user's analysis histories.
// @Description Retrieve the user's analysis histories.
// @Tags History
// @Accept json
// @Produce json
// @Param page query int true "Page number"
// @Param limit query int true "Maximum number of results per page"
// @Success 200 {object} utils.Response
// @Failure 400 {object} utils.Response
// @Security BearerAuth
// @Router /api/history [get]
func (c *historyController) GetHistories(ctx *gin.Context) {
userId := ctx.MustGet("user_id").(string)

Expand Down Expand Up @@ -64,6 +76,17 @@ func (c *historyController) GetHistories(ctx *gin.Context) {
ctx.JSON(http.StatusOK, res)
}

// GetHistory godoc
// @Summary Retrieve the user's analysis history by id.
// @Description Retrieve the user's analysis history by id.
// @Tags History
// @Accept json
// @Produce json
// @Param id path string true "History ID"
// @Success 200 {object} utils.Response
// @Failure 400 {object} utils.Response
// @Security BearerAuth
// @Router /api/history/{id} [get]
func (c *historyController) GetHistory(ctx *gin.Context) {
userId := ctx.MustGet("user_id").(string)
id := ctx.Param("id")
Expand Down
272 changes: 241 additions & 31 deletions controller/ml.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
type (
MLController interface {
GetSentimentAnalysisAndSummarization(ctx *gin.Context)
GetSentimentAnalysisAndSummarizationAsGuest(ctx *gin.Context)
}

mlController struct {
Expand All @@ -43,7 +44,17 @@ func NewMLController(
}
}

func (c *mlController) GetSentimentAnalysisAndSummarization(ctx *gin.Context) {
// GetSentimentAnalysisAndSummarizationAsGuest godoc
// @Summary Get product analysis as guest
// @Description Get product analysis form url link as guest.
// @Tags Analysis
// @Accept json
// @Produce json
// @Param product_url query string true "Tokopedia Product Link"
// @Success 200 {object} utils.Response
// @Failure 400 {object} utils.Response
// @Router /api/ml/guest/analysis [get]
func (c *mlController) GetSentimentAnalysisAndSummarizationAsGuest(ctx *gin.Context) {
productUrl := ctx.Query("product_url")
if productUrl == "" {
res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_GET_REVIEWS, dto.ErrProductUrlMissing.Error(), nil)
Expand Down Expand Up @@ -206,46 +217,245 @@ func (c *mlController) GetSentimentAnalysisAndSummarization(ctx *gin.Context) {
return
}

userID, exists := ctx.Get("user_id")
if exists {
userIDStr, ok := userID.(string)
if !ok {
res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_CREATE_HISTORY, "Invalid user ID type", nil)
ctx.JSON(http.StatusInternalServerError, res)
return
}
res := utils.BuildResponseSuccess(dto.MESSAGE_SUCCESS_GET_REVIEWS, dto.MLResult{
ProductName: product.ProductName,
ProductDescription: product.ProductDescription,
Rating: len(reviews),
Ulasan: predictResult.CountNegative + predictResult.CountPositive,
Bintang: ratingAvg,
ImageUrls: product.ImageUrls,
ShopName: product.ShopName,
ShopAvatar: shopAvatar,
CountNegative: predictResult.CountNegative,
CountPositive: predictResult.CountPositive,
Packaging: analyzeResult.Packaging,
Delivery: analyzeResult.Delivery,
AdminResponse: analyzeResult.AdminResponse,
ProductCondition: analyzeResult.ProductCondition,
Summary: summarizeResult,
})
ctx.JSON(http.StatusOK, res)
}

userUUID, err := uuid.Parse(userIDStr)
// GetSentimentAnalysisAndSummarization godoc
// @Summary Get product analysis
// @Description Get product analysis form url link.
// @Tags Analysis
// @Accept json
// @Produce json
// @Param product_url query string true "Tokopedia Product Link"
// @Success 200 {object} utils.Response
// @Failure 400 {object} utils.Response
// @Security BearerAuth
// @Router /api/ml/analysis [get]
func (c *mlController) GetSentimentAnalysisAndSummarization(ctx *gin.Context) {
productUrl := ctx.Query("product_url")
if productUrl == "" {
res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_GET_REVIEWS, dto.ErrProductUrlMissing.Error(), nil)
ctx.AbortWithStatusJSON(http.StatusBadRequest, res)
return
}

parsedUrl, err := url.Parse(productUrl)
if err != nil {
res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_GET_REVIEWS, err.Error(), nil)
ctx.AbortWithStatusJSON(http.StatusBadRequest, res)
return
}

if parsedUrl.Host == "tokopedia.link" {
expandedUrl, err := expandUrl(productUrl)
if err != nil {
res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_CREATE_HISTORY, "Invalid user ID format", nil)
ctx.JSON(http.StatusInternalServerError, res)
res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_GET_REVIEWS, err.Error(), nil)
ctx.AbortWithStatusJSON(http.StatusBadRequest, res)
return
}
productUrl = expandedUrl

history := dto.HistoryCreateRequest{
UserID: userUUID,
ProductID: product.ProductId,
Rating: len(reviews),
Ulasan: predictResult.CountNegative + predictResult.CountPositive,
Bintang: ratingAvg,
URL: productReq.ProductUrl,
ProductName: product.ProductName,
CountPositive: predictResult.CountPositive,
CountNegative: predictResult.CountNegative,
Packaging: analyzeResult.Packaging,
Delivery: analyzeResult.Delivery,
AdminResponse: analyzeResult.AdminResponse,
ProductCondition: analyzeResult.ProductCondition,
Summary: summarizeResult,
}
_, err = c.historyService.CreateHistory(ctx, history)
parsedUrl, err = url.Parse(productUrl)
if err != nil {
res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_CREATE_HISTORY, err.Error(), nil)
ctx.JSON(http.StatusInternalServerError, res)
res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_GET_REVIEWS, err.Error(), nil)
ctx.AbortWithStatusJSON(http.StatusBadRequest, res)
return
}
}

// Validate that the URL is from tokopedia.com
if parsedUrl.Host != "www.tokopedia.com" && parsedUrl.Host != "tokopedia.com" {
res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_GET_REVIEWS, dto.ErrNotTokopediaUrls.Error(), nil)
ctx.AbortWithStatusJSON(http.StatusBadRequest, res)
return
}

pathParts := strings.Split(parsedUrl.Path, "/")
if len(pathParts) < 3 {
res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_GET_REVIEWS, dto.ErrProductUrlWrongFormat.Error(), nil)
ctx.AbortWithStatusJSON(http.StatusBadRequest, res)
return
}

productReq := dto.GetProductRequest{
ShopDomain: pathParts[1],
ProductKey: pathParts[2],
ProductUrl: "https://www.tokopedia.com/" + pathParts[1] + "/" + pathParts[2],
}

product, err := c.tokopediaService.GetProduct(ctx, productReq)
if err != nil {
res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_GET_PRODUCT_ID, err.Error(), nil)
ctx.JSON(http.StatusBadRequest, res)
return
}

// fmt.Println("=== PRODUCT ID ===")
// fmt.Println(product)

reviewsReq := dto.GetReviewsRequest{
ProductUrl: productReq.ProductUrl,
ProductId: product.ProductId,
}

reviews, err := c.tokopediaService.GetReviews(ctx, reviewsReq)
if err != nil {
res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_GET_REVIEWS, err.Error(), nil)
ctx.JSON(http.StatusBadRequest, res)
return
}

// fmt.Println("=== REVIEWS ===")
// fmt.Println(reviews)

// Assuming `reviews` is a slice of a struct with `Message` and `Rating` fields
statements := make([]string, len(reviews))
ratingSum := 0.0

for i, review := range reviews {
statements[i] = review.Message
ratingSum += float64(review.Rating)
}

var ratingAvg float64
if len(reviews) > 0 {
ratingAvg = ratingSum / float64(len(reviews))
} else {
ratingAvg = 0.0 // or handle the case where there are no reviews
}

predictReq := dto.PredictRequest{
Statements: statements,
}

// fmt.Println("=== PREDICT REQ ===")
// fmt.Println(predictReq)

var builder strings.Builder
for _, review := range reviews {
builder.WriteString(review.Message)
builder.WriteString("\n")
}
concatenatedMessage := builder.String()

var shopAvatar string
var predictResult dto.PredictResponse
var analyzeResult dto.AnalyzeResponse
var summarizeResult string

var wg sync.WaitGroup
// var shopAvatarErr, predictErr error
var shopAvatarErr, predictErr, analyzeErr, summarizeErr error

wg.Add(4)

go func() {
defer wg.Done()
shopAvatar, shopAvatarErr = c.tokopediaService.GetShopAvatar(ctx, productReq.ShopDomain)
}()

go func() {
defer wg.Done()
predictResult, predictErr = c.modelService.Predict(ctx, predictReq)
}()

go func() {
defer wg.Done()
analyzeResult, analyzeErr = c.geminiService.Analyze(ctx, concatenatedMessage)
}()

go func() {
defer wg.Done()
summarizeResult, err = c.geminiService.Summarize(ctx, concatenatedMessage)
}()

wg.Wait()

if shopAvatarErr != nil {
res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_GET_SHOP_AVATAR, shopAvatarErr.Error(), nil)
ctx.JSON(http.StatusBadRequest, res)
return
}

if predictErr != nil {
res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_PREDICT, predictErr.Error(), nil)
ctx.JSON(http.StatusBadRequest, res)
return
}

if summarizeErr != nil {
res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_ANALYZE, summarizeErr.Error(), nil)
ctx.JSON(http.StatusBadRequest, res)
return
}
if analyzeErr != nil {
res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_ANALYZE, analyzeErr.Error(), nil)
ctx.JSON(http.StatusBadRequest, res)
return
}

userID, exists := ctx.Get("user_id")

if !exists {
res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_GET_USER, analyzeErr.Error(), nil)
ctx.JSON(http.StatusBadRequest, res)
return
}

userIDStr, ok := userID.(string)
if !ok {
res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_CREATE_HISTORY, "Invalid user ID type", nil)
ctx.JSON(http.StatusInternalServerError, res)
return
}

userUUID, err := uuid.Parse(userIDStr)
if err != nil {
res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_CREATE_HISTORY, "Invalid user ID format", nil)
ctx.JSON(http.StatusInternalServerError, res)
return
}

history := dto.HistoryCreateRequest{
UserID: userUUID,
ProductID: product.ProductId,
Rating: len(reviews),
Ulasan: predictResult.CountNegative + predictResult.CountPositive,
Bintang: ratingAvg,
URL: productReq.ProductUrl,
ProductName: product.ProductName,
CountPositive: predictResult.CountPositive,
CountNegative: predictResult.CountNegative,
Packaging: analyzeResult.Packaging,
Delivery: analyzeResult.Delivery,
AdminResponse: analyzeResult.AdminResponse,
ProductCondition: analyzeResult.ProductCondition,
Summary: summarizeResult,
}
_, err = c.historyService.CreateHistory(ctx, history)
if err != nil {
res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_CREATE_HISTORY, err.Error(), nil)
ctx.JSON(http.StatusInternalServerError, res)
return
}

res := utils.BuildResponseSuccess(dto.MESSAGE_SUCCESS_GET_REVIEWS, dto.MLResult{
ProductName: product.ProductName,
ProductDescription: product.ProductDescription,
Expand Down
Loading

0 comments on commit 9e93d91

Please sign in to comment.