Skip to content

Commit

Permalink
Cafeteria Rating (#70)
Browse files Browse the repository at this point in the history
* added new grpc call for getting mensa ratings

* added new grpc call for managing cafeteria ratings

* added tag rating to dishreply

* implemented feedback, added stubs to get meal/cafeteria Tags

* added tags to individual ratings.

* removed unnecessary part in get request

* implemented endpoints for available tags

* added inserting new ratings, added cronjob to update mensa db, prepared getmensen endpoint

* added dish rating + dish downloader

* renmaing to meals/cafeteria, added test client, error detection in mealNameDownload.go

* added cronjob to compute average ratings

* added structure to query an arbitrary amount of meal ratings

* store tag options in db

* verify tags in new ratings and parse to ids

* insert each cafeteria/meal tag only once for a rating

* stored name tags in database

* added meal name tags including mapping, Fixed type error in database

* update tag tables without deleting old values

* initialising tag tables extracted to reduce code duplicates

* added updating functionality for meal name tags

* extended structure for automatically adding mealnameratings

* split new ratings in multiple smaller methods

* extracted storeRating tags to one method, renamed models to a uniform scheme

* replaced cafeterianames, tagnames and mealnames by ids in database

* replaced cafeterianames, tagnames implemented get cafeteria(meal) name/id

* fixed input sanitization

* fixed update mechanism for downloading cafeterias

* fixed update fixed quries for mealnames

* joining tables for tag average intermediate progress

* change table names dish-> meal; mensa->cafeteria

* successfully read foreign keys

* compute average for cafeteria Rating tags

* added structure to store cafeteria tag rating results

* adapted remianing average computation to foreign keys

* added computeAverageForMealsInCafeteriasTags

* added improved input sanitization for new ratings

* fixed updating excluded/included name tags

* added mealname tag average computation

* changed to meal updating & meal querying

* fixed name tags table, fixed querying meal ids

* extracted cafeteria RPC logic into an individual file

* querying for tagratings limited to meal/cafeteria

* updated to the new .proto file version

* added getCafeterias Endpoint

* fixed bugs from new proto file conversion

* first iteration to query cafeteria Ratings

* debugged cafeteria rating query, only tagnames missing

* imporved error messages for invalid tags, fixed cafeteria rating join query

* debugged basic structure for meal ratings

* added precomputation for nametag mapping

* pulled latest version from the main branch

* added tag ratings to each rating in a query

* added name tag query, simplified tag queries in general

* fixed creating name tags

* added timestamps and correct sorting for queries

* added querying in time intervals

* move the tag initializer logic to an individual file

* correct intervals for cronjobs

* computed standardeviation for result values, fixed crontab initializer query

* stored std in db, reduced number of queries for cafeteriaID, added comments

* store images as paths in db

* query and store images as jpeg

* fixed problem with file name collisions

* split client in local and global version

* change proto file, added limit input sanitation

* adapted cafeteria Service to the new proto file version

* added first version of migration, improved db naming, rating -> points

* adapted files to new table names

* fixed further migration/renaming errors

* used actual foreign keys in model, fixed naming conflicts

* meal -> dish for consistency with eat-api

* changed use of limit parameter

* added feedback for db initializer, moved models in combined package

* added missing error checks for all queries

* Apply suggestions from code review

Co-authored-by: Joscha Henningsen <[email protected]>

* removed log-fatals, improved warnings

* changed visibility of types to unexported

* added gorm annotations

* batch create & decoder

* fixed resizing and naming of images

* pulled main branch

* adapted to new rpc messages

* fixed local client & migration issue

* debugged/ simplified get Tags/mensen request

* simplified average rating computation, simplified store tags

* debugged inserting/averageComputation

* debugged querying ratings

* fixed tag query, added logrus to client

* linted new files

* added missing error "handling" in client

* added get dishes of the day rpc stub

* added endpoitn getDishTagNames

* updated dependencies, fixed get dishes

* fixed bug in image naming, changed image storage to same path as news

* annotated local client

* improved quality converter to match 0.5mb as max size

* fixed linting errors (switch and comment name)

Co-authored-by: Joscha Henningsen <[email protected]>
  • Loading branch information
tobiasjungmann and joschahenningsen authored Sep 21, 2022
1 parent 60a6d3a commit c0ee084
Show file tree
Hide file tree
Showing 43 changed files with 2,991 additions and 96 deletions.
22 changes: 14 additions & 8 deletions client/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,21 @@ module github.com/TUM-Dev/Campus-Backend/client
go 1.18

require (
github.com/TUM-Dev/Campus-Backend v0.0.0-20210122194144-94617f6a7793
google.golang.org/grpc v1.35.0
github.com/TUM-Dev/Campus-Backend/api v0.0.0-20220804180900-56d6fe781cc2
github.com/sirupsen/logrus v1.9.0
google.golang.org/grpc v1.48.0
google.golang.org/protobuf v1.28.1
)

require (
github.com/golang/protobuf v1.4.3 // indirect
golang.org/x/net v0.0.0-20210119194325-5f4716e94777 // indirect
golang.org/x/sys v0.0.0-20210122093101-04d7465088b8 // indirect
golang.org/x/text v0.3.5 // indirect
google.golang.org/genproto v0.0.0-20210122163508-8081c04a3579 // indirect
google.golang.org/protobuf v1.25.0 // indirect
github.com/golang/glog v1.0.0 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.1 // indirect
github.com/kr/text v0.2.0 // indirect
golang.org/x/net v0.0.0-20220802222814-0bcc04d9c69b // indirect
golang.org/x/sys v0.0.0-20220803195053-6e608f9ce704 // indirect
golang.org/x/text v0.3.7 // indirect
google.golang.org/genproto v0.0.0-20220804142021-4e6b2dfa6612 // indirect
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.2.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
95 changes: 76 additions & 19 deletions client/go.sum

Large diffs are not rendered by default.

Binary file added client/localServer/images/sampleimage.jpeg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
270 changes: 270 additions & 0 deletions client/localServer/localTestClient.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,270 @@
package main

import (
"bufio"
"bytes"
"context"
"errors"
"fmt"
pb "github.com/TUM-Dev/Campus-Backend/api"
log "github.com/sirupsen/logrus"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"image"
"image/jpeg"
"os"
"time"
)

const (
localAddress = "127.0.0.1:50051"
testImage = "./localServer/images/sampleimage.jpeg"
)

// main connects to a seperatly started local server and creates ratings for both, cafeterias and dishes.
// Afterwards, they are queried and displayed on the console
func main() {
// Set up a connection to the local server.
log.Info("Connecting...")

conn, err := grpc.Dial(localAddress, grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
log.Info(err)
}
c := pb.NewCampusClient(conn)
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
defer cancel()

cafeteriaRatingTools(c, ctx)

}

func cafeteriaRatingTools(c pb.CampusClient, ctx context.Context) {

currentCafeteria := "MENSA_GARCHING"
currentDish := "Vegane rote Grütze mit Soja-Vanillesauce" //must be in the dish table
generateDishRating(c, ctx, currentCafeteria, currentDish, 3)
generateCafeteriaRating(c, ctx, currentCafeteria, 2)
queryCafeteria(currentCafeteria, c, ctx, true)
queryDish(currentCafeteria, currentDish, c, ctx, false)
generateCafeteriaRating(c, ctx, currentCafeteria, 2)
generateCafeteriaRating(c, ctx, currentCafeteria, 2)
generateDishRating(c, ctx, currentCafeteria, currentDish, 1)

queryCafeteria(currentCafeteria, c, ctx, false)
queryDish(currentCafeteria, currentDish, c, ctx, false)

}

func queryDish(cafeteria string, dish string, c pb.CampusClient, ctx context.Context, imageShouldBeStored bool) {
res, err := c.GetDishRatings(ctx, &pb.DishRatingRequest{
Dish: dish,
CafeteriaId: cafeteria,
Limit: 3,
})

if err != nil {
log.Info(err)
} else {
log.Info("Result: ")
log.Info("\tavg : ", res.Avg)
log.Info("\tmin ", res.Min)
log.Info("\tmax ", res.Max)
log.Info("\tstd ", res.Std)
log.Info("Number of individual Ratings", len(res.Rating))
path := fmt.Sprintf("%s%d%s", "./testImages/dishes/", time.Now().Unix(), "/")
for _, v := range res.Rating {
log.Info("\nRating: ", v.Points)
log.Info("Comment ", v.Comment)
log.Info("Number of Tag Ratings : ", len(v.RatingTags))
log.Info("Timestamp: ", v.Visited)
log.Info("ImageLength:", len(v.Image))
if imageShouldBeStored {
_, err := storeImage(path, v.Image)
if err != nil {
log.Info("image was not saved successfully")
}
}
}
log.Info("Rating Tags: ")
for _, v := range res.RatingTags {
log.Info("TagId: ", v.TagId)
log.Info("\tavg: ", v.Avg)
log.Info("\tmin: ", v.Min)
log.Info("\tmax: ", v.Max)
log.Info("\tstd: ", v.Std)
}
log.Info("nameTags: ")
for _, v := range res.NameTags {
log.Info("TagId: ", v.TagId)
log.Info("\tavg: ", v.Avg)
log.Info("\tmin: ", v.Min)
log.Info("\tmax: ", v.Max)
log.Info("\tstd: ", v.Std)
}
}
}

func queryCafeteria(s string, c pb.CampusClient, ctx context.Context, imageShouldBeStored bool) {
res, err := c.GetCafeteriaRatings(ctx, &pb.CafeteriaRatingRequest{
CafeteriaId: s,
Limit: 3,
// From: timestamppb.New(time.Date(2022, 7, 8, 16, 0, 0, 0, time.Local)),
// To: timestamppb.New(time.Date(2022, 7, 8, 17, 0, 0, 0, time.Local)),
})

if err != nil {
log.Info(err)
} else {
log.Info("Result: ")
log.Info("avg: ", res.Avg)
log.Info("min", res.Min)
log.Info("max", res.Max)
log.Info("Number of individual Ratings", len(res.Rating))
path := fmt.Sprintf("%s%d%s", "./testImages/cafeteria/", time.Now().Unix(), "/")
for _, v := range res.Rating {
log.Info("\nRating: ", v.Points)
log.Info("Comment ", v.Comment)
log.Info("Number of Tag Ratings: ", len(v.RatingTags))
log.Info("Timestamp: ", v.Visited)
log.Info("ImageLength:", len(v.Image))
if imageShouldBeStored {
_, err := storeImage(path, v.Image)
if err != nil {
log.Info("image was not saved successfully")
}
}
}

for _, v := range res.RatingTags {
log.Info("\nTagId: ", v.TagId)
log.Info("avg: ", v.Avg)
log.Info("min", v.Min)
log.Info("max", v.Max)
log.Info("std", v.Std)
}
}
}

func generateCafeteriaRating(c pb.CampusClient, ctx context.Context, cafeteria string, rating int32) {
y := make([]*pb.RatingTag, 2)
y[0] = &pb.RatingTag{
Points: float64(1 + rating),
TagId: 1,
}
y[1] = &pb.RatingTag{
Points: float64(2 + rating),
TagId: 2,
}

_, err := c.NewCafeteriaRating(ctx, &pb.NewCafeteriaRatingRequest{
Points: rating,
CafeteriaId: cafeteria,
Comment: "Alles super, 2 Sterne",
RatingTags: y,
Image: getImageToBytes(testImage),
})

if err != nil {
log.Info(err)
} else {
log.Info("Request successfully: Cafeteria Rating should be stored")
}
}

func generateDishRating(c pb.CampusClient, ctx context.Context, cafeteria string, dish string, rating int32) {
y := make([]*pb.RatingTag, 3)
y[0] = &pb.RatingTag{
Points: float64(1 + rating),
TagId: 1,
}
y[1] = &pb.RatingTag{
Points: float64(2 + rating),
TagId: 2,
}
y[2] = &pb.RatingTag{
Points: float64(3 + rating),
TagId: 3,
}

_, err := c.NewDishRating(ctx, &pb.NewDishRatingRequest{
Points: rating,
CafeteriaId: cafeteria,
Dish: dish,
Comment: "Alles Hähnchen",
RatingTags: y,
Image: getImageToBytes(testImage),
})

if err != nil {
log.Info(err)
} else {
log.Info("Request successfully: Dish Rating should be stored")
}
}

func getImageToBytes(path string) []byte {

file, err := os.Open(path)

if err != nil {
fmt.Println(err)
return make([]byte, 0)
}

defer func(file *os.File) {
err := file.Close()
if err != nil {
log.Info("Request successfully: Dish Rating should be stored")
}
}(file)

fileInfo, _ := file.Stat()
var size = fileInfo.Size()
byteArray := make([]byte, size)

buffer := bufio.NewReader(file)
_, err = buffer.Read(byteArray)
if err != nil {
log.Info("Unable to read the byteArray")
}
log.Info("Length of the image as bytes: ", len(byteArray))
return byteArray
}

func storeImage(path string, i []byte) (string, error) {
if _, err := os.Stat(path); errors.Is(err, os.ErrNotExist) {
err := os.MkdirAll(path, os.ModePerm)
if err != nil {
log.Info(err)
}
}
img, _, _ := image.Decode(bytes.NewReader(i))
var imgPath = fmt.Sprintf("%s%d%s", path, 3, ".jpeg") //time.Now().Unix() //use three to force file name collisions
var f, _ = os.Stat(imgPath)
var counter = 1
for {
if f == nil {
break
} else {
imgPath = fmt.Sprintf("%s%d%s%d%s", path, 3, "v", counter, ".jpeg") //time.Now().Unix()
counter++
f, _ = os.Stat(imgPath)
}
}

out, errFile := os.Create(imgPath)
if errFile != nil {
log.Info("Unable to create the new testfile")
}
defer func(out *os.File) {
err := out.Close()
if err != nil {
log.Info("File was not closed successfully")
}
}(out)
var opts jpeg.Options
opts.Quality = 100
errFile = jpeg.Encode(out, img, &opts)
return imgPath, errFile
}
3 changes: 2 additions & 1 deletion client/client.go → client/publicServer/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/metadata"
"google.golang.org/protobuf/types/known/emptypb"
"log"
"time"
)
Expand Down Expand Up @@ -37,7 +38,7 @@ func main() {
ctx = metadata.NewOutgoingContext(ctx, md)

log.Println("Trying to fetch top news")
r, err := c.GetTopNews(ctx, &pb.GetTopNewsRequest{})
r, err := c.GetTopNews(ctx, &emptypb.Empty{})
if err != nil {
log.Fatalf("could not greet: %v", err)
}
Expand Down
Loading

0 comments on commit c0ee084

Please sign in to comment.