# -*- coding: utf-8 -*- import os import sys import pandas as pd project_root_path = os.path.dirname( os.path.dirname(os.path.dirname(os.path.abspath(__file__))) ) if project_root_path not in sys.path: sys.path.append(project_root_path) import json from chinatravel.environment.world_env import WorldEnv from chinatravel.evaluation.utils import Attractions from chinatravel.symbol_verification.preference import evaluate_preference_py env = WorldEnv() attractions = Attractions() goto = env.transportation.goto city_dict = { "北京": "beijing", "上海": "shanghai", "南京": "nanjing", "苏州": "suzhou", "杭州": "hangzhou", "深圳": "shenzhen", "成都": "chengdu", "武汉": "wuhan", "广州": "guangzhou", "重庆": "chongqing", } def calc_time_delta(st_time, ed_time): st_h, st_m = int(st_time.split(":")[0]), int(st_time.split(":")[1]) ed_h, ed_m = int(ed_time.split(":")[0]), int(ed_time.split(":")[1]) return (ed_m - st_m) + (ed_h - st_h) * 60 def convenient_transport(plan_json): plan = plan_json["itinerary"] time_cost = 0 transport_count = 0 for plan_of_day in plan: for activity in plan_of_day["activities"]: if "transports" in activity: transport_count += 1 for transport in activity["transports"]: time_cost += calc_time_delta( transport["start_time"], transport["end_time"] ) average_time_cost = time_cost / transport_count return average_time_cost def convenient_restaurant(plan_json): plan = plan_json["itinerary"] restaurant_count = 0 time_cost = 0 for plan_of_day in plan: for activity in plan_of_day["activities"]: if activity["type"] in ["breakfast", "lunch", "dinner"]: restaurant_count += 1 for transport in activity["transports"]: time_cost += calc_time_delta( transport["start_time"], transport["end_time"] ) if restaurant_count == 0: return -1 average_time_cost = time_cost / restaurant_count return average_time_cost def near_poi(plan_json, poi_list): poi_count = len(poi_list) if poi_count == 0: return -1 plan = plan_json["itinerary"] if len(plan) == 1: return -1 city = plan_json["target_city"] accommodation_name = "" for plan_of_day in plan: for activity in plan_of_day["activities"]: if activity["type"] == "accommodation": accommodation_name = activity["position"] break dist_cost = 0 for poi in poi_list: # print("city", city, "accommodation_name", accommodation_name, "poi", poi) dist_cost += goto( city, accommodation_name, poi, start_time="00:00", transport_type="walk" )[0]["distance"] average_dist_cost = dist_cost / poi_count return average_dist_cost def less_walk(plan_json): plan = plan_json["itinerary"] walk_distance = 0 activity_count = 0 for plan_of_day in plan: for activity in plan_of_day["activities"]: if "transports" in activity: for transport in activity["transports"]: if transport["mode"] == "walk": walk_distance += transport["distance"] activity_count += 1 # average_walk_distance = walk_distance / activity_count return walk_distance def meal_cost_ratio(plan_json): plan = plan_json["itinerary"] meal_cost = 0 for plan_of_day in plan: for activity in plan_of_day["activities"]: if activity["type"] in ["breakfast", "lunch", "dinner"]: meal_cost += activity["cost"] return meal_cost / total_cost(plan_json) def accommodation_cost_ratio(plan_json): plan = plan_json["itinerary"] accommodation_cost = 0 for plan_of_day in plan: for activity in plan_of_day["activities"]: if activity["type"] == "accommodation": accommodation_cost += activity["cost"] return accommodation_cost / total_cost(plan_json) def attraction_cost_ratio(plan_json): plan = plan_json["itinerary"] attraction_cost = 0 for plan_of_day in plan: for activity in plan_of_day["activities"]: if activity["type"] == "attraction": attraction_cost += activity["cost"] return attraction_cost / total_cost(plan_json) def total_cost(plan_json): plan = plan_json["itinerary"] _total_cost = 0 for plan_of_day in plan: for activity in plan_of_day["activities"]: _total_cost += activity["cost"] for transport in activity["transports"]: _total_cost += transport["cost"] return _total_cost def attraction_satisfaction(plan_json): plan = plan_json["itinerary"] city = plan_json["target_city"] recommend_time_list = [] actual_time_list = [] # datapath=os.path.dirname(__file__) + "/eval_annotation/attractions/{}/attractions_tag.csv".format(city_dict[city]) # ood_attractions_dataframe = pd.read_csv(datapath) # datapath=os.path.dirname(__file__) + "/eval_annotation/attractions/{}/attractions_tag.csv".format(city_dict[city]) # ood_attractions_dataframe = pd.read_csv(datapath) for plan_of_day in plan: for activity in plan_of_day["activities"]: if activity["type"] == "attraction": attraction_name = activity["position"] attrction_info = attractions.select( city, key="name", func=lambda x: x == attraction_name ).iloc[0] # attrction_info = ood_attractions_dataframe[ood_attractions_dataframe["name"] == attraction_name].iloc[0] recommend_time = (attrction_info["recommendmintime"]) * 60 actual_time = calc_time_delta( activity["start_time"], activity["end_time"] ) recommend_time_list.append(recommend_time) actual_time_list.append(actual_time) if len(recommend_time_list) == 0: return -1 # marco = sum(recommend_time_list) / sum(actual_time_list) micro = sum( [ recommend_time_list[i] / actual_time_list[i] for i in range(len(recommend_time_list)) ] ) / len(recommend_time_list) return micro def attraction_count(plan_json): plan = plan_json["itinerary"] day_num = len(plan) attraction_count = 0 for plan_of_day in plan: for activity in plan_of_day["activities"]: if activity["type"] == "attraction": attraction_count += 1 average_attraction_count = attraction_count / day_num return average_attraction_count def indoor_attraction_ratio(plan_json): plan = plan_json["itinerary"] attraction_count = 0 indoor_attraction_count = 0 for plan_of_day in plan: for activity in plan_of_day["activities"]: if activity["type"] == "attraction": attraction_count += 1 attraction_name = activity["position"] city = plan_json["target_city"] attraction_info = attractions.select( city, key="name", func=lambda x: x == attraction_name ).iloc[0] if attraction_info["indoor"] == 1: indoor_attraction_count += 1 if attraction_count == 0: return -1 return indoor_attraction_count / attraction_count def popular_attraction_ratio(plan_json): plan = plan_json["itinerary"] attraction_count = 0 popular_score_sum = 0 for plan_of_day in plan: for activity in plan_of_day["activities"]: if activity["type"] == "attraction": attraction_count += 1 attraction_name = activity["position"] city = plan_json["target_city"] attraction_info = attractions.select( city, key="name", func=lambda x: x == attraction_name ).iloc[0] popular_score_sum += attraction_info["popularity"] if attraction_count == 0: return -1 return popular_score_sum / attraction_count func_list = [ convenient_transport, convenient_restaurant, near_poi, less_walk, meal_cost_ratio, accommodation_cost_ratio, attraction_cost_ratio, total_cost, attraction_satisfaction, attraction_count, indoor_attraction_ratio, popular_attraction_ratio, ] def _evaluate_preference(symbolic_input, plan_json): result = {} poi_list_str = "" preference_list = symbolic_input["preference_en"] for preference in preference_list: if "close to" in preference: poi_list_str = preference.split("{")[1].split("}")[0] break poi_list = poi_list_str.replace(",", ",").split(",") poi_list = ( [poi.strip().strip("'").strip('"') for poi in poi_list] if poi_list_str != "" else [] ) for func in func_list: if func == near_poi: result[func.__name__] = func(plan_json, poi_list) else: result[func.__name__] = func(plan_json) return result def evaluate_preference(query_index, query_data, result_data, commonsense_pass): result = [] for i in range(len(query_index)): if query_index[i] not in commonsense_pass: result.append( {"data_id": query_index[i]} | {func.__name__: -1 for func in func_list} ) continue symbolic_input = query_data[query_index[i]] plan_json = result_data[query_index[i]] # print("symbolic_input", symbolic_input, "plan_json", plan_json) result.append( {"data_id": query_index[i]} | _evaluate_preference(symbolic_input, plan_json) ) result_df = pd.DataFrame(result) return result_df def evaluate_preference_v2(query_index, query_data, result_data, pass_id): result = [] for i in range(len(query_index)): if query_index[i] not in pass_id: result.append( {"data_id": query_index[i], "concept": -1} ) continue evaluate_preference_py symbolic_input = query_data[query_index[i]] plan_json = result_data[query_index[i]] # print("symbolic_input", symbolic_input, "plan_json", plan_json) if isinstance(symbolic_input["preference_py"], list): pre_py = symbolic_input["preference_py"][0] else: pre_py = symbolic_input["preference_py"] index = pre_py.find("\n") concept = pre_py[:index] op = concept.split(" ")[0] op_concept = concept.split(" ")[1] code = pre_py[index + 1 :] res = evaluate_preference_py([(op, op_concept, code)], plan_json)[0] result.append( {"data_id": query_index[i], "concept": res} ) result_df = pd.DataFrame(result) return result_df def test(): test_json_txt = """ {"people_number":1,"start_city":"深圳","target_city":"南京","itinerary":[{"day":1,"activities":[{"start_time":"06:27","end_time":"15:15","start":"深圳北站","end":"南京南站","TrainID":"D376","type":"train","transports":[],"cost":694.1,"tickets":1},{"position":"中山陵景区","type":"attraction","transports":[{"start":"南京南站","end":"南京南站-地铁站","mode":"walk","start_time":"15:15","end_time":"15:18","cost":0,"distance":0.29},{"start":"南京南站-地铁站","end":"钟灵街-地铁站","mode":"metro","start_time":"15:18","end_time":"15:38","cost":4,"distance":10.21,"tickets":1},{"start":"钟灵街-地铁站","end":"中山陵景区","mode":"walk","start_time":"15:38","end_time":"16:07","cost":0,"distance":2.47}],"cost":0,"start_time":"16:07","end_time":"17:00"},{"position":"朱氏梅花糕","type":"dinner","transports":[{"start":"中山陵景区","end":"钟灵街-地铁站","mode":"walk","start_time":"17:00","end_time":"17:29","cost":0,"distance":2.47},{"start":"钟灵街-地铁站","end":"云南路-地铁站","mode":"metro","start_time":"17:29","end_time":"17:47","cost":4,"distance":9.22,"tickets":1},{"start":"云南路-地铁站","end":"朱氏梅花糕","mode":"walk","start_time":"17:47","end_time":"17:52","cost":0,"distance":0.5}],"cost":6,"start_time":"17:52","end_time":"19:22"},{"position":"行政院","type":"attraction","transports":[{"start":"朱氏梅花糕","end":"云南路-地铁站","mode":"walk","start_time":"19:22","end_time":"19:27","cost":0,"distance":0.5},{"start":"云南路-地铁站","end":"九华山-地铁站","mode":"metro","start_time":"19:27","end_time":"19:32","cost":2,"distance":2.96,"tickets":1},{"start":"九华山-地铁站","end":"行政院","mode":"walk","start_time":"19:32","end_time":"19:39","cost":0,"distance":0.62}],"cost":0,"start_time":"19:39","end_time":"21:09"},{"position":"南京玄武饭店","type":"accommodation","room_type":1,"transports":[{"start":"行政院","end":"九华山-地铁站","mode":"walk","start_time":"21:09","end_time":"21:16","cost":0,"distance":0.62},{"start":"九华山-地铁站","end":"玄武门-地铁站","mode":"metro","start_time":"21:16","end_time":"21:20","cost":2,"distance":2.5,"tickets":1},{"start":"玄武门-地铁站","end":"南京玄武饭店","mode":"walk","start_time":"21:20","end_time":"21:21","cost":0,"distance":0.1}],"cost":556.0,"start_time":"21:21","end_time":"24:00","rooms":1}]},{"day":2,"activities":[{"position":"南京玄武饭店","type":"breakfast","transports":[],"cost":0,"start_time":"08:00","end_time":"08:30"},{"position":"雨花门","type":"attraction","transports":[{"start":"南京玄武饭店","end":"玄武门-地铁站","mode":"walk","start_time":"08:30","end_time":"08:31","cost":0,"distance":0.1},{"start":"玄武门-地铁站","end":"武定门-地铁站","mode":"metro","start_time":"08:31","end_time":"08:43","cost":3,"distance":6.32,"tickets":1},{"start":"武定门-地铁站","end":"雨花门","mode":"walk","start_time":"08:43","end_time":"08:48","cost":0,"distance":0.42}],"cost":0,"start_time":"08:48","end_time":"10:18"},{"position":"江心洲大桥","type":"attraction","transports":[{"start":"雨花门","end":"武定门-地铁站","mode":"walk","start_time":"10:18","end_time":"10:23","cost":0,"distance":0.42},{"start":"武定门-地铁站","end":"雨山路-地铁 站","mode":"metro","start_time":"10:23","end_time":"10:57","cost":5,"distance":17.3,"tickets":1},{"start":"雨山路-地铁站","end":"江心洲大桥","mode":"walk","start_time":"10:57","end_time":"11:31","cost":0,"distance":2.86}],"cost":0,"start_time":"11:31","end_time":"13:01"},{"position":"胡小石纪念馆","type":"attraction","transports":[{"start":"江心洲大桥","end":"雨山路-地铁站","mode":"walk","start_time":"13:01","end_time":"13:35","cost":0,"distance":2.86},{"start":"雨山路-地铁站","end":"文德路-地铁站","mode":"metro","start_time":"13:35","end_time":"13:38","cost":2,"distance":1.8,"tickets":1},{"start":"文德路-地铁站","end":"胡小石纪念馆","mode":"walk","start_time":"13:38","end_time":"13:45","cost":0,"distance":0.61}],"cost":0,"start_time":"13:45","end_time":"15:15"},{"start_time":"17:00","end_time":"14:52","start":"南京站","end":"深圳站","TrainID":"K36","type":"train","transports":[{"start":"胡小石纪念馆","end":"文德路-地铁站","mode":"walk","start_time":"15:15","end_time":"15:22","cost":0,"distance":0.61},{"start":"文德路-地铁站","end":"南京站-地铁站","mode":"metro","start_time":"15:22","end_time":"15:54","cost":5,"distance":16.42,"tickets":1},{"start":"南京站-地铁站","end":"南京站","mode":"walk","start_time":"15:54","end_time":"15:56","cost":0,"distance":0.25}],"cost":462.73,"tickets":1}]}]} """ test_symbolic_input = {"preference_en": ["close to {中山陵景区,行政院}"]} plan_json = json.loads(test_json_txt) result = _evaluate_preference(test_symbolic_input, plan_json) print(result) if __name__ == "__main__": test()