ABAO77 commited on
Commit
02d6bde
·
verified ·
1 Parent(s): 8c4ead2

Upload 157 files

Browse files
src/apis/controllers/__pycache__/comment_controller.cpython-311.pyc CHANGED
Binary files a/src/apis/controllers/__pycache__/comment_controller.cpython-311.pyc and b/src/apis/controllers/__pycache__/comment_controller.cpython-311.pyc differ
 
src/apis/controllers/__pycache__/destination_controller.cpython-311.pyc CHANGED
Binary files a/src/apis/controllers/__pycache__/destination_controller.cpython-311.pyc and b/src/apis/controllers/__pycache__/destination_controller.cpython-311.pyc differ
 
src/apis/controllers/__pycache__/location_controller.cpython-311.pyc CHANGED
Binary files a/src/apis/controllers/__pycache__/location_controller.cpython-311.pyc and b/src/apis/controllers/__pycache__/location_controller.cpython-311.pyc differ
 
src/apis/controllers/__pycache__/post_controller.cpython-311.pyc CHANGED
Binary files a/src/apis/controllers/__pycache__/post_controller.cpython-311.pyc and b/src/apis/controllers/__pycache__/post_controller.cpython-311.pyc differ
 
src/apis/controllers/__pycache__/reaction_controller.cpython-311.pyc CHANGED
Binary files a/src/apis/controllers/__pycache__/reaction_controller.cpython-311.pyc and b/src/apis/controllers/__pycache__/reaction_controller.cpython-311.pyc differ
 
src/apis/controllers/location_controller.py CHANGED
@@ -1,8 +1,13 @@
1
  from src.utils.logger import logger
2
- from src.utils.helper import format_geoapify_response, format_weather_data
 
 
 
 
3
  from fastapi.responses import JSONResponse
4
  import requests
5
  import os
 
6
 
7
 
8
  def get_location_details(lat, long):
@@ -35,6 +40,7 @@ def get_nearby_places(lat, long, radius, kinds):
35
  "apikey": api_key,
36
  }
37
  response = requests.get(url, params=params, headers={"accept": "application/json"})
 
38
  if response.status_code == 200:
39
  logger.info("Places fetched successfully")
40
  return JSONResponse(
@@ -75,3 +81,43 @@ def get_weather(lat, long):
75
  return JSONResponse(
76
  content={"error": "Error fetching weather"}, status_code=500
77
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  from src.utils.logger import logger
2
+ from src.utils.helper import (
3
+ format_geoapify_response,
4
+ format_weather_data,
5
+ get_google_map_url,
6
+ )
7
  from fastapi.responses import JSONResponse
8
  import requests
9
  import os
10
+ import json
11
 
12
 
13
  def get_location_details(lat, long):
 
40
  "apikey": api_key,
41
  }
42
  response = requests.get(url, params=params, headers={"accept": "application/json"})
43
+ # print(response.json())
44
  if response.status_code == 200:
45
  logger.info("Places fetched successfully")
46
  return JSONResponse(
 
81
  return JSONResponse(
82
  content={"error": "Error fetching weather"}, status_code=500
83
  )
84
+
85
+
86
+ def get_lat_long_location(location_name):
87
+ """
88
+ Retrieve the latitude and longitude for a given location name using Geoapify's Geocoding API.
89
+
90
+ Parameters:
91
+ - location_name (str): The name of the location to geocode.
92
+ - api_key (str): Your Geoapify API key.
93
+
94
+ Returns:
95
+ - tuple: (latitude, longitude) if the location is found; otherwise, (None, None).
96
+ """
97
+ api_key = os.getenv("GEOAPIFY_API_KEY", None)
98
+ base_url = "https://api.geoapify.com/v1/geocode/search"
99
+ params = {"text": location_name, "apiKey": api_key}
100
+ response = requests.get(base_url, params=params)
101
+ data = response.json()
102
+ if response.status_code == 200 and data.get("features"):
103
+ # Extract latitude and longitude from the response
104
+ latitude = data["features"][0]["properties"]["lat"]
105
+ longitude = data["features"][0]["properties"]["lon"]
106
+ return latitude, longitude
107
+ else:
108
+ print(f"Location '{location_name}' not found.")
109
+ return None, None
110
+
111
+
112
+ def get_geometry_nearby_places(lat, lon, radius, kinds):
113
+ res = get_nearby_places(lat=lat, long=lon, radius=radius, kinds=kinds)
114
+ data = json.loads(res.body)
115
+ recommend_places = data.get("places", [])
116
+ recommend_places = [
117
+ {
118
+ "name": place["properties"]["name"],
119
+ "link_google_map": get_google_map_url(place["geometry"]["coordinates"]),
120
+ }
121
+ for place in recommend_places
122
+ ]
123
+ return recommend_places
src/apis/controllers/reaction_controller.py CHANGED
@@ -3,7 +3,6 @@ from datetime import datetime
3
  from src.utils.mongo import ReactionCRUD, PostCRUD
4
  from src.utils.logger import logger
5
  from datetime import datetime
6
- from bson import ObjectId
7
  from datetime import datetime
8
  from bson import ObjectId
9
  from src.utils.mongo import UserCRUD
 
3
  from src.utils.mongo import ReactionCRUD, PostCRUD
4
  from src.utils.logger import logger
5
  from datetime import datetime
 
6
  from datetime import datetime
7
  from bson import ObjectId
8
  from src.utils.mongo import UserCRUD
src/apis/models/__pycache__/post_models.cpython-311.pyc CHANGED
Binary files a/src/apis/models/__pycache__/post_models.cpython-311.pyc and b/src/apis/models/__pycache__/post_models.cpython-311.pyc differ
 
src/apis/routes/__pycache__/post_router.cpython-311.pyc CHANGED
Binary files a/src/apis/routes/__pycache__/post_router.cpython-311.pyc and b/src/apis/routes/__pycache__/post_router.cpython-311.pyc differ
 
src/apis/routes/__pycache__/reaction_route.cpython-311.pyc CHANGED
Binary files a/src/apis/routes/__pycache__/reaction_route.cpython-311.pyc and b/src/apis/routes/__pycache__/reaction_route.cpython-311.pyc differ
 
src/apis/routes/reaction_route.py CHANGED
@@ -1,5 +1,5 @@
1
  from fastapi import APIRouter, status, Depends
2
- from typing import Annotated
3
  from fastapi.responses import JSONResponse
4
  from pydantic import Field
5
  from src.apis.models.user_models import User
@@ -11,8 +11,10 @@ from src.apis.controllers.reaction_controller import (
11
  update_a_reaction_controller,
12
  delete_a_reaction_controller,
13
  )
 
14
  from src.utils.redis import set_key_redis, get_key_redis
15
  from src.utils.logger import logger
 
16
 
17
  router = APIRouter(prefix="/reaction", tags=["Reaction"])
18
 
@@ -21,7 +23,7 @@ user_dependency = Annotated[User, Depends(get_current_user)]
21
 
22
  class BodyLike(BaseDocument):
23
  post_id: str = Field("", description="Post's id", min_length=1)
24
- type: int = Field(0, description="Type of like", gt=0, lt=5)
25
 
26
  class Config:
27
  json_schema_extra = {
@@ -59,7 +61,7 @@ async def get_reactions_of_a_post(post_id: str):
59
 
60
  class BodyUpdateReaction(BaseDocument):
61
  reaction_id: str = Field("", description="Reaction's id")
62
- type: str = Field("", description="Type of like", gt=0, lt=3)
63
 
64
  class Config:
65
  json_schema_extra = {
@@ -70,6 +72,53 @@ class BodyUpdateReaction(BaseDocument):
70
  }
71
 
72
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73
  @router.patch("/update/", status_code=status.HTTP_200_OK)
74
  async def update_reaction(body: BodyUpdateReaction, user: user_dependency):
75
  if user is None:
 
1
  from fastapi import APIRouter, status, Depends
2
+ from typing import Annotated, Optional
3
  from fastapi.responses import JSONResponse
4
  from pydantic import Field
5
  from src.apis.models.user_models import User
 
11
  update_a_reaction_controller,
12
  delete_a_reaction_controller,
13
  )
14
+ from src.utils.mongo import ReactionCRUD, PostCRUD
15
  from src.utils.redis import set_key_redis, get_key_redis
16
  from src.utils.logger import logger
17
+ from bson import ObjectId
18
 
19
  router = APIRouter(prefix="/reaction", tags=["Reaction"])
20
 
 
23
 
24
  class BodyLike(BaseDocument):
25
  post_id: str = Field("", description="Post's id", min_length=1)
26
+ type: int = Field(0, description="Type of like", ge=0, lt=5)
27
 
28
  class Config:
29
  json_schema_extra = {
 
61
 
62
  class BodyUpdateReaction(BaseDocument):
63
  reaction_id: str = Field("", description="Reaction's id")
64
+ type: str = Field("", description="Type of like", gt=0, lt=5)
65
 
66
  class Config:
67
  json_schema_extra = {
 
72
  }
73
 
74
 
75
+ class BodyUpdateReactionInteract(BaseDocument):
76
+ reaction_id: Optional[str] = Field("", description="Reaction's id")
77
+ post_id: str = Field("", description="Post's id", min_length=1)
78
+ current_type: int = Field("", description="Current type of like", ge=0, lt=5)
79
+ type: int = Field(0, description="Type of like", ge=0, lt=5)
80
+ model_config = {
81
+ "json_schema_extra": {
82
+ "examples": [
83
+ {
84
+ "reaction_id": "1234567890",
85
+ "post_id": "1234567890",
86
+ "current_type": 1,
87
+ "type": 2,
88
+ },
89
+ ]
90
+ }
91
+ }
92
+
93
+
94
+ @router.post("/interact/", status_code=status.HTTP_200_OK)
95
+ async def interact_reaction(body: BodyUpdateReactionInteract, user: user_dependency):
96
+ if user is None:
97
+ return JSONResponse(content={"message": "User not found"}, status_code=404)
98
+ if not body.reaction_id:
99
+ response = await ReactionCRUD.create(
100
+ {"user_id": user["id"], "post_id": body.post_id, "type": body.type}
101
+ )
102
+ await PostCRUD.update(
103
+ {"_id": ObjectId(body.post_id)}, {"$inc": {"reaction_count": 1}}
104
+ )
105
+
106
+ return JSONResponse(content={"reaction_id": str(response)}, status_code=201)
107
+ elif body.current_type == body.type:
108
+ await ReactionCRUD.delete_one({"_id": ObjectId(body.reaction_id)})
109
+ await PostCRUD.update(
110
+ {"_id": ObjectId(body.post_id)}, {"$inc": {"reaction_count": -1}}
111
+ )
112
+ return JSONResponse(content={"message": "Reaction deleted"}, status_code=200)
113
+ elif body.current_type != body.type:
114
+ await ReactionCRUD.update(
115
+ {"_id": ObjectId(body.reaction_id)}, {"$set": {"type": body.type}}
116
+ )
117
+ return JSONResponse(content={"message": "Reaction updated"}, status_code=200)
118
+
119
+ return JSONResponse(content={"message": "Reaction not found"}, status_code=404)
120
+
121
+
122
  @router.patch("/update/", status_code=status.HTTP_200_OK)
123
  async def update_reaction(body: BodyUpdateReaction, user: user_dependency):
124
  if user is None:
src/langgraph/models/__pycache__/model_validator.cpython-311.pyc CHANGED
Binary files a/src/langgraph/models/__pycache__/model_validator.cpython-311.pyc and b/src/langgraph/models/__pycache__/model_validator.cpython-311.pyc differ
 
src/langgraph/models/model_validator.py CHANGED
@@ -1,4 +1,5 @@
1
- # from pydantic import BaseModel, Field, EmailStr
 
2
  # from typing import Optional
3
  # from datetime import datetime, timezone
4
 
@@ -46,3 +47,22 @@
46
  # "website": "https://www.bluelagoonresort.com",
47
  # }
48
  # }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from pydantic import BaseModel, Field, EmailStr
2
+
3
  # from typing import Optional
4
  # from datetime import datetime, timezone
5
 
 
47
  # "website": "https://www.bluelagoonresort.com",
48
  # }
49
  # }
50
+
51
+
52
+ class NearlyDestinationRecommendation(BaseModel):
53
+ query: str = Field(
54
+ ...,
55
+ title="Query related to wanting to go somewhere",
56
+ description="Auto extracted from user's message. Using Vietnamese language for better results.",
57
+ )
58
+ places: str = Field(..., title="Places", description="place mention in query")
59
+ kinds: str = Field(
60
+ ...,
61
+ title="Kinds",
62
+ description="""
63
+ Kinds of places you want to find choose one of options:
64
+ ["architecture", "historic", "cultural", "natural", "sport", "religion", "accomodations", "foods", "shops", "other"]""",
65
+ )
66
+
67
+ class Config:
68
+ schema_extra = {"example": {"query": "Địa điểm gần Hà Nội" "places" "Hà Nội"}}
src/langgraph/multi_agent/chat/__pycache__/chat_flow.cpython-311.pyc CHANGED
Binary files a/src/langgraph/multi_agent/chat/__pycache__/chat_flow.cpython-311.pyc and b/src/langgraph/multi_agent/chat/__pycache__/chat_flow.cpython-311.pyc differ
 
src/langgraph/multi_agent/chat/chat_flow.py CHANGED
@@ -16,12 +16,12 @@ from src.langgraph.utils_function.function_graph import (
16
  get_history,
17
  save_history,
18
  )
19
- from src.langgraph.tools.destination_tools import destination_recommendation
20
  from src.utils.logger import logger
21
 
22
  primary_assistant_tools = [
23
  TavilySearchResults(max_results=2),
24
- destination_recommendation,
25
  ]
26
 
27
  assistant_runnable = primary_assistant_prompt | llm.bind_tools(
@@ -58,6 +58,7 @@ class ChatBot:
58
  self.primary_assistant_tools = [
59
  TavilySearchResults(max_results=2),
60
  destination_recommendation,
 
61
  ]
62
  self.assistant_runnable = assistant_runnable
63
 
 
16
  get_history,
17
  save_history,
18
  )
19
+ from src.langgraph.tools.destination_tools import destination_recommendation, nearly_destination_recommendation
20
  from src.utils.logger import logger
21
 
22
  primary_assistant_tools = [
23
  TavilySearchResults(max_results=2),
24
+ destination_recommendation,nearly_destination_recommendation
25
  ]
26
 
27
  assistant_runnable = primary_assistant_prompt | llm.bind_tools(
 
58
  self.primary_assistant_tools = [
59
  TavilySearchResults(max_results=2),
60
  destination_recommendation,
61
+ nearly_destination_recommendation
62
  ]
63
  self.assistant_runnable = assistant_runnable
64
 
src/langgraph/tools/__pycache__/destination_tools.cpython-311.pyc CHANGED
Binary files a/src/langgraph/tools/__pycache__/destination_tools.cpython-311.pyc and b/src/langgraph/tools/__pycache__/destination_tools.cpython-311.pyc differ
 
src/langgraph/tools/destination_tools.py CHANGED
@@ -4,6 +4,15 @@ from src.apis.controllers.destination_controller import (
4
  )
5
  from langchain_core.runnables.config import RunnableConfig
6
  from src.utils.logger import logger
 
 
 
 
 
 
 
 
 
7
 
8
 
9
  @tool
@@ -16,3 +25,15 @@ async def destination_recommendation(query: str, config: RunnableConfig):
16
  logger.info(f"Destination recommendation query: {query}")
17
  output = await destination_suggestion_controller(query, 3)
18
  return output
 
 
 
 
 
 
 
 
 
 
 
 
 
4
  )
5
  from langchain_core.runnables.config import RunnableConfig
6
  from src.utils.logger import logger
7
+ from src.apis.controllers.location_controller import (
8
+ get_nearby_places,
9
+ get_lat_long_location,
10
+ get_geometry_nearby_places,
11
+ )
12
+ from src.langgraph.models.model_validator import NearlyDestinationRecommendation
13
+ from src.utils.helper import get_google_map_url
14
+
15
+ import json
16
 
17
 
18
  @tool
 
25
  logger.info(f"Destination recommendation query: {query}")
26
  output = await destination_suggestion_controller(query, 3)
27
  return output
28
+
29
+
30
+ @tool(args_schema=NearlyDestinationRecommendation)
31
+ async def nearly_destination_recommendation(
32
+ query: str, places, kinds, config: RunnableConfig
33
+ ):
34
+ """Gọi tool này khi query đề cập đến việc tìm địa điểm gần một khu vực/ địa điểm nào đó"""
35
+ radius = 1000
36
+ lat, lon = get_lat_long_location(places)
37
+ recommend_places = get_geometry_nearby_places(lat, lon, radius, kinds)
38
+ logger.info(f"Nearly Destination recommendation: {places} >> {kinds} >> {recommend_places}")
39
+ return recommend_places
src/utils/__pycache__/helper.cpython-311.pyc CHANGED
Binary files a/src/utils/__pycache__/helper.cpython-311.pyc and b/src/utils/__pycache__/helper.cpython-311.pyc differ
 
src/utils/__pycache__/mongo.cpython-311.pyc CHANGED
Binary files a/src/utils/__pycache__/mongo.cpython-311.pyc and b/src/utils/__pycache__/mongo.cpython-311.pyc differ
 
src/utils/__pycache__/redis.cpython-311.pyc CHANGED
Binary files a/src/utils/__pycache__/redis.cpython-311.pyc and b/src/utils/__pycache__/redis.cpython-311.pyc differ
 
src/utils/helper.py CHANGED
@@ -301,3 +301,6 @@ def serialize_datetime(obj):
301
  if isinstance(obj, ObjectId):
302
  return str(obj)
303
  return obj
 
 
 
 
301
  if isinstance(obj, ObjectId):
302
  return str(obj)
303
  return obj
304
+
305
+ def get_google_map_url(geometry:list):
306
+ return f"https://www.google.com/maps/search/{geometry[1]},{geometry[0]}"
src/utils/mongo.py CHANGED
@@ -93,16 +93,22 @@ class MongoCRUD:
93
  async def update(self, query: Dict, data: Dict) -> int:
94
  """Update documents in the collection based on a query asynchronously."""
95
  await self._ensure_ttl_index()
96
-
97
  # Check if update operators like $inc, $set, etc., are used
98
- if any(key.startswith('$') for key in data.keys()):
99
  update_data = data
100
  else:
101
  # If no MongoDB operators are used, treat it as a normal update
102
  data["updated_at"] = get_date_time().replace(tzinfo=None)
103
  if self.ttl_seconds is not None:
104
- data["expire_at"] = data["updated_at"] + timedelta(seconds=self.ttl_seconds)
105
- update_data = {"$set": self._order_fields(self.model(**data).model_dump(exclude_unset=True))}
 
 
 
 
 
 
106
 
107
  result = await self.collection.update_many(query, update_data)
108
  return result.modified_count
@@ -112,6 +118,11 @@ class MongoCRUD:
112
  result = await self.collection.delete_many(query)
113
  return result.deleted_count
114
 
 
 
 
 
 
115
  async def find_by_id(self, id: str) -> Optional[Dict]:
116
  """Find a document by its ID asynchronously."""
117
  return await self.read_one({"_id": ObjectId(id)})
 
93
  async def update(self, query: Dict, data: Dict) -> int:
94
  """Update documents in the collection based on a query asynchronously."""
95
  await self._ensure_ttl_index()
96
+
97
  # Check if update operators like $inc, $set, etc., are used
98
+ if any(key.startswith("$") for key in data.keys()):
99
  update_data = data
100
  else:
101
  # If no MongoDB operators are used, treat it as a normal update
102
  data["updated_at"] = get_date_time().replace(tzinfo=None)
103
  if self.ttl_seconds is not None:
104
+ data["expire_at"] = data["updated_at"] + timedelta(
105
+ seconds=self.ttl_seconds
106
+ )
107
+ update_data = {
108
+ "$set": self._order_fields(
109
+ self.model(**data).model_dump(exclude_unset=True)
110
+ )
111
+ }
112
 
113
  result = await self.collection.update_many(query, update_data)
114
  return result.modified_count
 
118
  result = await self.collection.delete_many(query)
119
  return result.deleted_count
120
 
121
+ async def delete_one(self, query: Dict) -> int:
122
+ """Delete a single document from the collection based on a query asynchronously."""
123
+ result = await self.collection.delete_one(query)
124
+ return result.deleted_count
125
+
126
  async def find_by_id(self, id: str) -> Optional[Dict]:
127
  """Find a document by its ID asynchronously."""
128
  return await self.read_one({"_id": ObjectId(id)})
src/utils/redis.py CHANGED
@@ -1,19 +1,19 @@
1
  from src.langgraph.config.constant import RedisCfg
2
  import redis.asyncio as redis
3
 
4
- redis_client = redis.from_url(
5
- RedisCfg.REDIS_URL, encoding="utf-8", decode_responses=True
6
- )
7
 
8
 
9
  async def set_key_redis(key, value, time=300):
10
- await redis_client.set(key, value, time)
11
 
12
 
13
  async def get_key_redis(key):
14
 
15
- return await redis_client.get(key)
16
 
17
 
18
  async def delete_key_redis(key):
19
- await redis_client.delete(key)
 
1
  from src.langgraph.config.constant import RedisCfg
2
  import redis.asyncio as redis
3
 
4
+ # redis_client = redis.from_url(
5
+ # RedisCfg.REDIS_URL, encoding="utf-8", decode_responses=True
6
+ # )
7
 
8
 
9
  async def set_key_redis(key, value, time=300):
10
+ return None #redis_client.set(key, value, time)
11
 
12
 
13
  async def get_key_redis(key):
14
 
15
+ return None #await redis_client.get(key)
16
 
17
 
18
  async def delete_key_redis(key):
19
+ return None#redis_client.delete(key)