Skip to content

Commit

Permalink
Merge pull request #20 from Hiseen/shengquan-smarter-recommand
Browse files Browse the repository at this point in the history
[Issue #16] Better Suggestion
  • Loading branch information
henrychen0220 authored Mar 21, 2019
2 parents 50d2879 + 798ac1b commit 97f50a4
Show file tree
Hide file tree
Showing 6 changed files with 168 additions and 47 deletions.
7 changes: 4 additions & 3 deletions new-gui/.expo/packager-info.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
{
"expoServerPort": 19000,
"expoServerNgrokUrl": "https://as-km5.anonymous.new-gui.exp.direct",
"packagerNgrokUrl": "https://packager.as-km5.anonymous.new-gui.exp.direct",
"ngrokPid": 16808,
"ngrokPid": 15688,
"devToolsPort": 19002,
"expoServerPort": 19000,
"packagerPort": 19001,
"packagerPid": 4172
"packagerPid": 13356,
"webpackServerPort": null
}
4 changes: 2 additions & 2 deletions new-gui/src/commons/serverRequest/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
// const HOST = "52.39.77.219";
const HOST = "192.168.1.8";
const HOST = "52.39.77.219";
// const HOST = "192.168.1.8";
export const SERVER_URL = "http://" + HOST + ":8080";
35 changes: 22 additions & 13 deletions new-gui/src/components/ClickSuggestionPanel/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import React, { Component } from "react";
import { View, StyleSheet, AsyncStorage, Platform,
StatusBar, Dimensions, Animated, Image, PanResponder, Alert } from "react-native";
import { SERVER_URL } from "../../commons/serverRequest";
import { Button, Text } from "react-native-elements";
import { Button, Text, AirbnbRating } from "react-native-elements";

import { connect } from "react-redux";
import { SafeAreaView, withNavigation } from "react-navigation";
Expand Down Expand Up @@ -57,13 +57,13 @@ class ClickSuggestionComponent extends Component {

this.likeOpacity = this.position.x.interpolate({
inputRange: [-SCREEN_WIDTH/2, 0, SCREEN_WIDTH/2],
outputRange: [0, 0, 1],
outputRange: [0, 0, 0.9],
extrapolate: 'clamp'
});

this.dislikeOpacity = this.position.x.interpolate({
inputRange: [-SCREEN_WIDTH/2, 0, SCREEN_WIDTH/2],
outputRange: [1, 0, 0],
outputRange: [0.9, 0, 0],
extrapolate: 'clamp'
});

Expand Down Expand Up @@ -114,6 +114,7 @@ class ClickSuggestionComponent extends Component {
.then(res => res.json())
.then(res => {
if (res.code == 1) {
// console.warn(res);
this.setState({currentIndex: 0, checkedToken: true,
recommendations: res.recommendations, recommendationsCount: res.recommendationsCount});
} else {
Expand Down Expand Up @@ -268,6 +269,7 @@ class ClickSuggestionComponent extends Component {
<Text style={styles.imageLabel}>
{item.title}
</Text>
{item.avg_ratings===0?null:<AirbnbRating defaultRating={item.avg_ratings} size={15} showRating={false} isDisabled={true}/>}
</View>

<Animated.View style={{ opacity: this.likeOpacity, transform: [{rotate: '-30deg'}],
Expand Down Expand Up @@ -302,6 +304,7 @@ class ClickSuggestionComponent extends Component {
<Text style={styles.imageLabel}>
{item.title}
</Text>
{item.avg_ratings===0?null:<AirbnbRating defaultRating={item.avg_ratings} size={15} showRating={false} isDisabled={true}/>}
</View>

<Image
Expand Down Expand Up @@ -452,32 +455,38 @@ const styles = StyleSheet.create({
resizeMode:"cover"
},
likeAnimatedText: {
borderWidth: 1,
borderColor: 'green',
color:'green',
borderWidth: 2,
borderColor: 'white',
backgroundColor: "lightgreen",
color:'white',
fontSize: 32,
fontWeight:'800',
padding: 10
},
disLikeAnimatedText: {
borderWidth: 1,
borderColor: 'red',
color:'red',
borderWidth: 2,
borderColor: 'white',
backgroundColor: "crimson",
color:'white',
fontSize: 32,
fontWeight:'800',
padding: 10
},
imageLabelView: {
padding: 2,
flex:1,
backgroundColor: 'rgba(164, 166, 164, 0.6)',
alignItems:"center",
justifyContent:'center',
position: 'absolute',
top: 40,
left: 40,
zIndex: 1000,
width: 300,
backgroundColor: 'rgba(164, 166, 164, 0.6)'
width: 300
},
imageLabel: {
fontWeight: 'bold',
fontSize: 28
fontWeight: 'bold',
fontSize: 28,
textAlign:"center"
}
})
60 changes: 46 additions & 14 deletions new-gui/src/components/UserInformationPanel/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import { mapDispatchToProps, mapStateToProps } from "../../commons/redux";
import React, { Component } from "react";
import { KeyboardAvoidingView,Platform,StatusBar,View, Text,Alert,
SectionList, StyleSheet, TouchableOpacity,Image,TextInput } from "react-native";
SectionList, StyleSheet, TouchableOpacity,Image,TextInput,Picker } from "react-native";
import { connect } from "react-redux";
import { withNavigation } from "react-navigation";
import { Ionicons } from '@expo/vector-icons';
Expand All @@ -19,7 +19,7 @@ const scaleAvatar=0.2
class EditableLabel extends Component{

componentWillMount(){
this.setState({editable:false,myText:this.props.value});
this.setState({editable:false,myText:this.props.value,oldValue:this.props.value});
}

renderInput()
Expand All @@ -37,8 +37,11 @@ class EditableLabel extends Component{
onEndEditing={()=>{
this.setState({editable:false});
if(this.props.checker==null || this.props.checker(this.state.myText)){
if(this.props.value!=this.state.myText)
if(this.state.oldValue!=this.state.myText)
{
this.setState({oldValue:this.state.myText});
this.props.receiver(this.props.fieldName,this.props.type==="number"?parseInt(this.state.myText):this.state.myText);
}
}else{
this.state.myText=this.props.value;
Alert.alert("Error", this.props.errorMessage, [{
Expand Down Expand Up @@ -85,8 +88,10 @@ class EditableLabel extends Component{
onEndEditing={()=>{
this.setState({editable:false});
if(this.props.checker==null || this.props.checker(this.state.myText)){
if(this.props.value!=this.state.myText)
if(this.state.oldValue!=this.state.myText){
this.setState({oldValue:this.state.myText});
this.props.receiver(this.props.fieldName,this.state.myText.split(",").filter(element => element.trim().length > 0));
}
}else{
this.state.myText=this.props.value;
Alert.alert("Error", this.props.errorMessage, [{
Expand All @@ -97,6 +102,27 @@ class EditableLabel extends Component{
value={this.state.myText}
/>);
}
else if(this.props.type === "picker")
{
return(<Picker
ref={component => this._input = component}
selectedValue={this.state.myText}
enabled = {this.state.editable}
onPress ={()=>this.setState({editable:false})}
onValueChange={(itemValue, itemIndex) => {
this.setState({editable:false, myText:itemValue},()=>{
if(this.state.oldValue!=this.state.myText){
this.setState({oldValue:this.state.myText});
this.props.receiver(this.props.fieldName,this.state.myText);
}
});
}}
style = {styles.infoPicker}
>
{this.props.pickerlist.map((x,i)=>{return <Picker.Item key={i} label={x} value={x}></Picker.Item>})}
</Picker>
);
}
return null;
}

Expand Down Expand Up @@ -160,7 +186,7 @@ class UserInformationComponent extends Component {
console.warn("Error in getting user info");
} else {
// console.warn(res);
this.setState({userInfo:res});
this.setState({userInfo:res},()=>this.forceUpdate);
}
})
.catch(
Expand Down Expand Up @@ -269,6 +295,7 @@ class UserInformationComponent extends Component {
checker={item[4]}
errorMessage={item[5]}
type={item[6]}
pickerlist={item[7]}
receiver={(k,v)=>this.updateInformation(k,v)}>
</EditableLabel>
<View style={styles.sectionPadding}></View>
Expand All @@ -284,15 +311,16 @@ class UserInformationComponent extends Component {
["Foods Eaten",this.state.userInfo.foodsEatenCurrently,""],
["Step Count",this.state.pastStepCount,""],
["Gender",this.state.userInfo.sexes,""]]},
{title: 'User Infomation', data: [['Age',this.state.userInfo.age.toString(),'','age',(x)=>this.isNormalInteger(x) && parseInt(x)<150,"Age is not valid(1-150)!","number"],
['Weight',this.state.userInfo.weight.toString(),'kg','weight',(x)=>this.isNormalInteger(x) && parseInt(x)<1000,"Weight is not valid(1-1000)!","number"],
['Height',this.state.userInfo.height.toString(),'cm','height',(x)=>this.isNormalInteger(x) && parseInt(x)<1000,"Height is not valid(1-1000)!","number"],
['HealthProblems',this.state.userInfo.healthProblems.join(),"","healthProblems",null,null,"list"],
['DislikeFoods',this.state.userInfo.dislikeFoods.join(),"","dislikeFoods",null,null,"list"],
['Allergies',this.state.userInfo.allergies.join(),"","alleriges",null,null,"list"],
['Workout',this.state.userInfo.workoutBoolean,"","workoutBoolean",null,null,"boolean"],
['WorkoutFrequency',this.state.userInfo.workoutFrequency.toString(),"","workoutFrequency",(x)=>this.isNormalInteger(x) && parseInt(x)<=7,"Workout Frequency should be an integer between 0 and 7!","number"],
['WorkoutType',this.state.userInfo.workoutType,"","workoutType",null,null,"string"]]},
{title: 'User Infomation', data: [['Age',this.state.userInfo.age.toString(),'','age',(x)=>this.isNormalInteger(x) && parseInt(x)<150,"Age is not valid(1-150)!","number",null],
['Weight',this.state.userInfo.weight.toString(),'kg','weight',(x)=>this.isNormalInteger(x) && parseInt(x)<1000,"Weight is not valid(1-1000)!","number",null],
['Height',this.state.userInfo.height.toString(),'cm','height',(x)=>this.isNormalInteger(x) && parseInt(x)<1000,"Height is not valid(1-1000)!","number",null],
['HealthProblems',this.state.userInfo.healthProblems.join(),"","healthProblems",null,null,"list",null],
['DislikeFoods',this.state.userInfo.dislikeFoods.join(),"","dislikeFoods",null,null,"list",null],
['Allergies',this.state.userInfo.allergies.join(),"","alleriges",null,null,"list",null],
['Food Restriction',this.state.userInfo.foodRestriction,"","foodRestriction",null,null,"picker",["None","Vegan","Vegetarian"]],
['Workout',this.state.userInfo.workoutBoolean,"","workoutBoolean",null,null,"boolean",null],
['WorkoutFrequency',this.state.userInfo.workoutFrequency.toString(),"","workoutFrequency",(x)=>this.isNormalInteger(x) && parseInt(x)<=7,"Workout Frequency should be an integer between 0 and 7!","number",null],
['WorkoutType',this.state.userInfo.workoutType,"","workoutType",null,null,"string",null]]},
]}
keyExtractor={(item, index) => item + index}
/>
Expand Down Expand Up @@ -339,6 +367,10 @@ const styles = StyleSheet.create({
fontSize: 15,
width:"40%"
},
infoPicker:{
alignSelf: 'center',
width:"40%"
},
avatarImage:{
width:300*scaleAvatar,
height:486*scaleAvatar
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@
import java.io.IOException;
import java.util.Calendar;
import java.util.Map;
import java.util.List;
import java.util.Iterator;
import java.util.Arrays;
import java.util.stream.Collectors;
import java.util.regex.Pattern;
import java.util.ArrayList;

import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
Expand All @@ -18,20 +24,26 @@
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.mongodb.client.FindIterable;
import com.mongodb.client.AggregateIterable;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.model.Filters;
import com.mongodb.client.model.Updates;

import org.bson.conversions.Bson;
import org.bson.BsonDocument;
import com.mongodb.util.JSON;

import edu.uci.ics.balancedbite.web.api.FoodInfo;
import edu.uci.ics.balancedbite.web.api.TimeManager;
import edu.uci.ics.balancedbite.web.api.UserInfo;
import edu.uci.ics.balancedbite.web.api.UserToken;
import edu.uci.ics.balancedbite.web.db.MongoDBRequest;

import static com.mongodb.client.model.Filters.*;
import static com.mongodb.client.model.Updates.*;
import static com.mongodb.client.model.Projections.*;
import static com.mongodb.client.model.Aggregates.*;

@Path("/recommendation")
@Consumes(MediaType.APPLICATION_JSON)
Expand All @@ -44,6 +56,10 @@ public RecommendationResource(String host, int port ) {
this.host = host;
this.port = port;
}





@POST
public JsonNode getRecommendations(String request) throws JsonParseException, JsonMappingException, IOException {
Expand All @@ -60,7 +76,7 @@ public JsonNode getRecommendations(String request) throws JsonParseException, Js
MongoDatabase database = MongoDBRequest.getInstance().getMongoDatabase(client);
MongoCollection<FoodInfo> foodCollection = MongoDBRequest.getInstance().getFoodInfoCollection(database);
MongoCollection<UserToken> tokenCollection = MongoDBRequest.getInstance().getUserTokenCollection(database);

MongoCollection<UserInfo> userCollection = MongoDBRequest.getInstance().getUserInfoCollection(database);
ObjectNode response = new ObjectMapper().createObjectNode();

// check token
Expand All @@ -75,8 +91,59 @@ public JsonNode getRecommendations(String request) throws JsonParseException, Js
tokenCollection.updateOne(Filters.eq("token", token), Updates.set("time",
TimeManager.getInstance().getDateFormat().format(Calendar.getInstance().getTime())));

UserInfo currentUserInfo = userCollection.find(eq("username", userToken.getUsername())).first();
if (currentUserInfo == null) {
response.put("code", 0);
client.close();
return response;
}
// fetch food info
FindIterable<FoodInfo> foundFoods = foodCollection.find(eq("meal_type", mealType)).skip(offset).limit(10);
double cal_need=currentUserInfo.getCaloriesNeeded(); // 2000
double cal_target=cal_need-currentUserInfo.getCaloriesTakenCurrently(); // 1500
if(mealType=="lunch"){
if(cal_target>cal_need*0.4)
cal_target=cal_need*0.4;
}
else if(cal_target>cal_need*0.3)
cal_target=cal_need*0.3;

List<String> disLikeFoods=currentUserInfo.getDislikeFoods();
List<String> allergies = currentUserInfo.getAllergies();
List<String> foodEatenToday = currentUserInfo.getFoodsEatenCurrently();
List<Bson> filterlist=new ArrayList<Bson>();


filterlist.add(lte("cals",cal_target));
filterlist.add(eq("meal_type", mealType));
if(!currentUserInfo.getFoodRestriction().equals("None"))
filterlist.add(eq("tags",currentUserInfo.getFoodRestriction()));

// filter out all foods that is disliked by user
for (String food: disLikeFoods) {
filterlist.add(not(regex("ingredients",Pattern.compile("^.*"+ food +".*$", Pattern.CASE_INSENSITIVE))));
}

// filter out all foods that is allergic to user

for (String allergy: allergies) {
filterlist.add(not(regex("ingredients",Pattern.compile("^.*"+ allergy +".*$", Pattern.CASE_INSENSITIVE))));
}

// filter out all foods that is eaten by the user today
for (String food: foodEatenToday) {
filterlist.add(not(eq("title", food)));
}

//System.out.println(and(filterlist).toBsonDocument(BsonDocument.class, com.mongodb.MongoClient.getDefaultCodecRegistry()));
AggregateIterable<FoodInfo> foundFoods = foodCollection.aggregate(
Arrays.asList(
match(
and(
filterlist
)
),
sample(10)
));
if (foundFoods == null) {
response.put("code", 0);
client.close();
Expand Down
Loading

0 comments on commit 97f50a4

Please sign in to comment.