diff --git a/README.md b/README.md index b585ff747..c2c8a6d69 100644 --- a/README.md +++ b/README.md @@ -149,7 +149,8 @@ the following libraries. - File Operation - Text Processing - Multi Modality -- Wikipedia search and retrieval +- Wikipedia Search and Retrieval +- TripAdvisor Search **Example Applications** diff --git a/README_ZH.md b/README_ZH.md index f73306edc..458b589b2 100644 --- a/README_ZH.md +++ b/README_ZH.md @@ -140,6 +140,7 @@ AgentScope支持使用以下库快速部署本地模型服务。 - 文本处理 - 多模态生成 - 维基百科搜索 +- TripAdvisor搜索 **样例应用** diff --git a/docs/sphinx_doc/en/source/tutorial/204-service.md b/docs/sphinx_doc/en/source/tutorial/204-service.md index 0cfaec6a3..572b7e5af 100644 --- a/docs/sphinx_doc/en/source/tutorial/204-service.md +++ b/docs/sphinx_doc/en/source/tutorial/204-service.md @@ -12,47 +12,48 @@ AgentScope and how to use them to enhance the capabilities of your agents. The following table outlines the various Service functions by type. These functions can be called using `agentscope.service.{function_name}`. -| Service Scene | Service Function Name | Description | -|-----------------------------|----------------------------|----------------------------------------------------------------------------------------------------------------| -| Code | `execute_python_code` | Execute a piece of Python code, optionally inside a Docker container. | -| | `NoteBookExecutor.run_code_on_notebook` | Compute Execute a segment of Python code in the IPython environment of the NoteBookExecutor, adhering to the IPython interactive computing style. | -| Retrieval | `retrieve_from_list` | Retrieve a specific item from a list based on given criteria. | -| | `cos_sim` | Compute the cosine similarity between two different embeddings. | -| SQL Query | `query_mysql` | Execute SQL queries on a MySQL database and return results. | -| | `query_sqlite` | Execute SQL queries on a SQLite database and return results. | -| | `query_mongodb` | Perform queries or operations on a MongoDB collection. | -| Text Processing | `summarization` | Summarize a piece of text using a large language model to highlight its main points. | -| Web | `bing_search` | Perform bing search | -| | `google_search` | Perform google search | -| | `arxiv_search` | Perform arXiv search | -| | `download_from_url` | Download file from given URL. | -| | `load_web` | Load and parse the web page of the specified url (currently only supports HTML). | -| | `digest_webpage` | Digest the content of a already loaded web page (currently only supports HTML). -| | `dblp_search_publications` | Search publications in the DBLP database -| | `dblp_search_authors` | Search for author information in the DBLP database | -| | `dblp_search_venues` | Search for venue information in the DBLP database | -| File | `create_file` | Create a new file at a specified path, optionally with initial content. | -| | `delete_file` | Delete a file specified by a file path. | -| | `move_file` | Move or rename a file from one path to another. | -| | `create_directory` | Create a new directory at a specified path. | -| | `delete_directory` | Delete a directory and all its contents. | -| | `move_directory` | Move or rename a directory from one path to another. | -| | `read_text_file` | Read and return the content of a text file. | -| | `write_text_file` | Write text content to a file at a specified path. | -| | `read_json_file` | Read and parse the content of a JSON file. | -| | `write_json_file` | Serialize a Python object to JSON and write to a file. | -| Multi Modality | `dashscope_text_to_image` | Convert text to image using Dashscope API. | -| | `dashscope_image_to_text` | Convert image to text using Dashscope API. | -| | `dashscope_text_to_audio` | Convert text to audio using Dashscope API. | -| | `openai_text_to_image` | Convert text to image using OpenAI API -| | `openai_edit_image` | Edit an image based on the provided mask and prompt using OpenAI API -| | `openai_create_image_variation` | Create variations of an image using OpenAI API -| | `openai_image_to_text` | Convert text to image using OpenAI API -| | `openai_text_to_audio` | Convert text to audio using OpenAI API -| | `openai_audio_to_text` | Convert audio to text using OpenAI API - - -| *More services coming soon* | | More service functions are in development and will be added to AgentScope to further enhance its capabilities. | +| Service Scene | Service Function Name | Description | +|-----------------------------|---------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------| +| Code | `execute_python_code` | Execute a piece of Python code, optionally inside a Docker container. | +| | `NoteBookExecutor` | Compute Execute a segment of Python code in the IPython environment of the NoteBookExecutor, adhering to the IPython interactive computing style. | +| Retrieval | `retrieve_from_list` | Retrieve a specific item from a list based on given criteria. | +| | `cos_sim` | Compute the cosine similarity between two different embeddings. | +| SQL Query | `query_mysql` | Execute SQL queries on a MySQL database and return results. | +| | `query_sqlite` | Execute SQL queries on a SQLite database and return results. | +| | `query_mongodb` | Perform queries or operations on a MongoDB collection. | +| Text Processing | `summarization` | Summarize a piece of text using a large language model to highlight its main points. | +| Web | `bing_search` | Perform bing search | +| | `google_search` | Perform google search | +| | `arxiv_search` | Perform arXiv search | +| | `download_from_url` | Download file from given URL. | +| | `load_web` | Load and parse the web page of the specified url (currently only supports HTML). | +| | `digest_webpage` | Digest the content of a already loaded web page (currently only supports HTML). | +| | `dblp_search_publications` | Search publications in the DBLP database | +| | `dblp_search_authors` | Search for author information in the DBLP database | +| | `dblp_search_venues` | Search for venue information in the DBLP database | +| | `tripadvisor_search` | Search for locations using the TripAdvisor API. | +| | `tripadvisor_search_location_photos` | Retrieve photos for a specific location using the TripAdvisor API. | +| | `tripadvisor_search_location_details` | Get detailed information about a specific location using the TripAdvisor API. | +| File | `create_file` | Create a new file at a specified path, optionally with initial content. | +| | `delete_file` | Delete a file specified by a file path. | +| | `move_file` | Move or rename a file from one path to another. | +| | `create_directory` | Create a new directory at a specified path. | +| | `delete_directory` | Delete a directory and all its contents. | +| | `move_directory` | Move or rename a directory from one path to another. | +| | `read_text_file` | Read and return the content of a text file. | +| | `write_text_file` | Write text content to a file at a specified path. | +| | `read_json_file` | Read and parse the content of a JSON file. | +| | `write_json_file` | Serialize a Python object to JSON and write to a file. | +| Multi Modality | `dashscope_text_to_image` | Convert text to image using Dashscope API. | +| | `dashscope_image_to_text` | Convert image to text using Dashscope API. | +| | `dashscope_text_to_audio` | Convert text to audio using Dashscope API. | +| | `openai_text_to_image` | Convert text to image using OpenAI API | +| | `openai_edit_image` | Edit an image based on the provided mask and prompt using OpenAI API | +| | `openai_create_image_variation` | Create variations of an image using OpenAI API | +| | `openai_image_to_text` | Convert text to image using OpenAI API | +| | `openai_text_to_audio` | Convert text to audio using OpenAI API | +| | `openai_audio_to_text` | Convert audio to text using OpenAI API | +| *More services coming soon* | | More service functions are in development and will be added to AgentScope to further enhance its capabilities. | About each service function, you can find detailed information in the [API document](https://modelscope.github.io/agentscope/). diff --git a/docs/sphinx_doc/zh_CN/source/tutorial/204-service.md b/docs/sphinx_doc/zh_CN/source/tutorial/204-service.md index 00de68001..88afc655b 100644 --- a/docs/sphinx_doc/zh_CN/source/tutorial/204-service.md +++ b/docs/sphinx_doc/zh_CN/source/tutorial/204-service.md @@ -9,45 +9,48 @@ 下面的表格按照类型概述了各种Service函数。以下函数可以通过`agentscope.service.{函数名}`进行调用。 -| Service场景 | Service函数名称 | 描述 | -|------------|-----------------------|-----------------------------------------| -| 代码 | `execute_python_code` | 执行一段 Python 代码,可选择在 Docker
容器内部执行。 | -| | `NoteBookExecutor.run_code_on_notebook` | 在 NoteBookExecutor 的 IPython 环境中执行一段 Python 代码,遵循 IPython 交互式计算风格。 | -| 检索 | `retrieve_from_list` | 根据给定的标准从列表中检索特定项目。 | -| | `cos_sim` | 计算2个embedding的余弦相似度。 | -| SQL查询 | `query_mysql` | 在 MySQL 数据库上执行 SQL 查询并返回结果。 | -| | `query_sqlite` | 在 SQLite 数据库上执行 SQL 查询并返回结果。 | -| | `query_mongodb` | 对 MongoDB 集合执行查询或操作。 | -| 文本处理 | `summarization` | 使用大型语言模型总结一段文字以突出其主要要点。 | -| 网络 | `bing_search` | 使用bing搜索。 | -| | `google_search` | 使用google搜索。 | -| | `arxiv_search` | 使用arxiv搜索。 | -| | `download_from_url` | 从指定的 URL 下载文件。 | -| | `load_web` | 爬取并解析指定的网页链接 (目前仅支持爬取 HTML 页面) | -| | `digest_webpage` | 对已经爬取好的网页生成摘要信息(目前仅支持 HTML 页面 -| | `dblp_search_publications` | 在dblp数据库里搜索文献。 -| | `dblp_search_authors` | 在dblp数据库里搜索作者。 | -| | `dblp_search_venues` | 在dblp数据库里搜索期刊,会议及研讨会。 | -| 文件处理 | `create_file` | 在指定路径创建一个新文件,并可选择添加初始内容。 | -| | `delete_file` | 删除由文件路径指定的文件。 | -| | `move_file` | 将文件从一个路径移动或重命名到另一个路径。 | -| | `create_directory` | 在指定路径创建一个新的目录。 | -| | `delete_directory` | 删除一个目录及其所有内容。 | -| | `move_directory` | 将目录从一个路径移动或重命名到另一个路径。 | -| | `read_text_file` | 读取并返回文本文件的内容。 | -| | `write_text_file` | 向指定路径的文件写入文本内容。 | -| | `read_json_file` | 读取并解析 JSON 文件的内容。 | -| | `write_json_file` | 将 Python 对象序列化为 JSON 并写入到文件。 | -| 多模态 | `dashscope_text_to_image` | 使用 DashScope API 将文本生成图片。 | -| | `dashscope_image_to_text` | 使用 DashScope API 根据图片生成文字。 | -| | `dashscope_text_to_audio` | 使用 DashScope API 根据文本生成音频。 | -| | `openai_text_to_image` | 使用 OpenAI API根据文本生成图片。 -| | `openai_edit_image` | 使用 OpenAI API 根据提供的遮罩和提示编辑图像。 -| | `openai_create_image_variation` | 使用 OpenAI API 创建图像的变体。 -| | `openai_image_to_text` | 使用 OpenAI API 根据图片生成文字。 -| | `openai_text_to_audio` | 使用 OpenAI API 根据文本生成音频。 -| | `openai_audio_to_text` | 使用OpenAI API将音频转换为文本。 -| *更多服务即将推出* | | 正在开发更多服务功能,并将添加到 AgentScope 以进一步增强其能力。 | +| Service场景 | Service函数名称 | 描述 | +|------------|---------------------------------------|--------------------------------------------------------------------| +| 代码 | `execute_python_code` | 执行一段 Python 代码,可选择在 Docker 容器内部执行。 | +| | `NoteBookExecutor` | 在 NoteBookExecutor 的 IPython 环境中执行一段 Python 代码,遵循 IPython 交互式计算风格。 | +| 检索 | `retrieve_from_list` | 根据给定的标准从列表中检索特定项目。 | +| | `cos_sim` | 计算2个embedding的余弦相似度。 | +| SQL查询 | `query_mysql` | 在 MySQL 数据库上执行 SQL 查询并返回结果。 | +| | `query_sqlite` | 在 SQLite 数据库上执行 SQL 查询并返回结果。 | +| | `query_mongodb` | 对 MongoDB 集合执行查询或操作。 | +| 文本处理 | `summarization` | 使用大型语言模型总结一段文字以突出其主要要点。 | +| 网络 | `bing_search` | 使用bing搜索。 | +| | `google_search` | 使用google搜索。 | +| | `arxiv_search` | 使用arxiv搜索。 | +| | `download_from_url` | 从指定的 URL 下载文件。 | +| | `load_web` | 爬取并解析指定的网页链接 (目前仅支持爬取 HTML 页面) | +| | `digest_webpage` | 对已经爬取好的网页生成摘要信息(目前仅支持 HTML 页面) | +| | `dblp_search_publications` | 在dblp数据库里搜索文献。 | +| | `dblp_search_authors` | 在dblp数据库里搜索作者。 | +| | `dblp_search_venues` | 在dblp数据库里搜索期刊,会议及研讨会。 | +| | `tripadvisor_search` | 使用 TripAdvisor API 搜索位置。 | +| | `tripadvisor_search_location_photos` | 使用 TripAdvisor API 检索特定位置的照片。 | +| | `tripadvisor_search_location_details` | 使用 TripAdvisor API 获取特定位置的详细信息。 | +| 文件处理 | `create_file` | 在指定路径创建一个新文件,并可选择添加初始内容。 | +| | `delete_file` | 删除由文件路径指定的文件。 | +| | `move_file` | 将文件从一个路径移动或重命名到另一个路径。 | +| | `create_directory` | 在指定路径创建一个新的目录。 | +| | `delete_directory` | 删除一个目录及其所有内容。 | +| | `move_directory` | 将目录从一个路径移动或重命名到另一个路径。 | +| | `read_text_file` | 读取并返回文本文件的内容。 | +| | `write_text_file` | 向指定路径的文件写入文本内容。 | +| | `read_json_file` | 读取并解析 JSON 文件的内容。 | +| | `write_json_file` | 将 Python 对象序列化为 JSON 并写入到文件。 | +| 多模态 | `dashscope_text_to_image` | 使用 DashScope API 将文本生成图片。 | +| | `dashscope_image_to_text` | 使用 DashScope API 根据图片生成文字。 | +| | `dashscope_text_to_audio` | 使用 DashScope API 根据文本生成音频。 | +| | `openai_text_to_image` | 使用 OpenAI API根据文本生成图片。 | +| | `openai_edit_image` | 使用 OpenAI API 根据提供的遮罩和提示编辑图像。 | +| | `openai_create_image_variation` | 使用 OpenAI API 创建图像的变体。 | +| | `openai_image_to_text` | 使用 OpenAI API 根据图片生成文字。 | +| | `openai_text_to_audio` | 使用 OpenAI API 根据文本生成音频。 | +| | `openai_audio_to_text` | 使用OpenAI API将音频转换为文本。 | +| *更多服务即将推出* | | 正在开发更多服务功能,并将添加到 AgentScope 以进一步增强其能力。 | 关于详细的参数、预期输入格式、返回类型,请参阅[API文档](https://modelscope.github.io/agentscope/)。 diff --git a/src/agentscope/service/__init__.py b/src/agentscope/service/__init__.py index 2e74d4dec..bce6878f1 100644 --- a/src/agentscope/service/__init__.py +++ b/src/agentscope/service/__init__.py @@ -22,6 +22,11 @@ from .sql_query.mongodb import query_mongodb from .web.search import bing_search, google_search from .web.arxiv import arxiv_search +from .web.tripadvisor import ( + tripadvisor_search_location_photos, + tripadvisor_search, + tripadvisor_search_location_details, +) from .web.dblp import ( dblp_search_publications, dblp_search_authors, @@ -110,6 +115,9 @@ def get_help() -> None: "openai_image_to_text", "openai_edit_image", "openai_create_image_variation", + "tripadvisor_search", + "tripadvisor_search_location_photos", + "tripadvisor_search_location_details", # to be deprecated "ServiceFactory", ] diff --git a/src/agentscope/service/web/tripadvisor.py b/src/agentscope/service/web/tripadvisor.py new file mode 100644 index 000000000..fa7deb0a1 --- /dev/null +++ b/src/agentscope/service/web/tripadvisor.py @@ -0,0 +1,538 @@ +# -*- coding: utf-8 -*- +"""TripAdvisor APIs for searching and retrieving location information.""" + +from loguru import logger +import requests +from ..service_response import ServiceResponse +from ..service_status import ServiceExecStatus + + +def tripadvisor_search_location_photos( + api_key: str, + location_id: str = None, + query: str = None, + language: str = "en", +) -> ServiceResponse: + """ + Retrieve photos for a specific location using the TripAdvisor API. + + Args: + api_key (`str`): + Your TripAdvisor API key. + location_id (`str`, optional): + The unique identifier for a location on Tripadvisor. The location + ID can be obtained using the tripadvisor_search function + query (`str`, optional): + The search query to find a location. Required if + location_id is not provided. + language (`str`, optional): + The language for the response. Defaults to 'en'. + + Returns: + `ServiceResponse`: A dictionary with two variables: `status` and + `content`. The `status` variable is from the ServiceExecStatus enum, + and `content` is the JSON response from TripAdvisor API or error + information, which depends on the `status` variable. + + If successful, the `content` will be a dictionary + with the following structure: + + .. code-block:: json + + { + 'photo_data': { + 'data': [ + { + 'id': int, + 'is_blessed': bool, + 'caption': str, + 'published_date': str, + 'images': { + 'thumbnail': { + 'height': int, + 'width': int, + 'url': str + }, + 'small': { + 'height': int, + 'width': int, + 'url': str + }, + 'medium': { + 'height': int, + 'width': int, + 'url': str + }, + 'large': { + 'height': int, + 'width': int, + 'url': str + }, + 'original': { + 'height': int, + 'width': int, + 'url': str + } + }, + 'album': str, + 'source': {'name': str, 'localized_name': str}, + 'user': {'username': str} + }, + ... + ] + } + } + + Each item in the 'data' list represents a photo associated with the + location. + + Note: + Either `location_id` or `query` must be provided. If both are provided, + `location_id` takes precedence. + + Example: + .. code-block:: python + + # Using location_id + result = tripadvisor_search_location_photos( + "your_api_key", location_id="123456", language="en" + ) + if result.status == ServiceExecStatus.SUCCESS: + print(result.content) + + # Or using a query + result = tripadvisor_search_location_photos( + "your_api_key", query="Eiffel Tower", language="en" + ) + if result.status == ServiceExecStatus.SUCCESS: + print(result.content) + + Example of successful `content`: + { + 'photo_data': { + 'data': [ + { + 'id': 215321638, + 'is_blessed': False, + 'caption': '', + 'published_date': '2016-09-04T20:40:14.284Z', + 'images': { + 'thumbnail': {'height': 50, 'width': 50, + 'url': 'https://media-cdn.../photo0.jpg'}, + 'small': {'height': 150, 'width': 150, + 'url': 'https://media-cdn.../photo0.jpg'}, + 'medium': {'height': 188, 'width': 250, + 'url': 'https://media-cdn.../photo0.jpg'}, + 'large': {'height': 413, 'width': 550, + 'url': 'https://media-cdn.../photo0.jpg'}, + 'original': {'height': 1920, 'width': 2560, + 'url': 'https://media-cdn.../photo0.jpg'} + }, + 'album': 'Other', + 'source': { + 'name': 'Traveler', + 'localized_name': 'Traveler' + }, + 'user': {'username': 'EvaFalleth'} + }, + # ... more photo entries ... + ] + } + } + + Raises: + ValueError: If neither location_id nor query is provided. + """ + if location_id is None and query is None: + raise ValueError("Either location_id or query must be provided.") + + if location_id is None: + # Use search_tripadvisor to get the location_id + search_result = tripadvisor_search(api_key, query, language) + if search_result.status != ServiceExecStatus.SUCCESS: + return search_result + + # Get the first location_id from the search results + locations = search_result.content.get("data", []) + if not locations: + return ServiceResponse( + status=ServiceExecStatus.ERROR, + content={"error": "No locations found for the given query."}, + ) + + location_id = locations[0]["location_id"] + logger.info(f"Using location_id {location_id} from search results.") + + # Warning message if there are multiple locations + if len(locations) > 1: + logger.warning( + f"Multiple locations found for query '{query}'. " + f"Using the first result. " + f"Other {len(locations) - 1} results are ignored.", + ) + + # Now proceed with the original function logic using the location_id + url = ( + f"https://api.content.tripadvisor.com/api/v1/location/{location_id}/" + f"photos?language={language}&key={api_key}" + ) + headers = { + "accept": "application/json", + } + + logger.info(f"Requesting photos for location ID {location_id}") + + try: + response = requests.get(url, headers=headers, timeout=20) + logger.info( + f"Received response with status code {response.status_code}", + ) + + if response.status_code == 200: + logger.info("Successfully retrieved the photo") + return ServiceResponse( + status=ServiceExecStatus.SUCCESS, + content=response.json(), + ) + error_detail = ( + response.json() + .get("error", {}) + .get("message", f"HTTP Error: {response.status_code}") + ) + logger.error(f"Error in response: {error_detail}") + return ServiceResponse( + status=ServiceExecStatus.ERROR, + content={"error": error_detail}, + ) + except Exception as e: + logger.exception("Exception occurred while requesting location photos") + return ServiceResponse( + status=ServiceExecStatus.ERROR, + content={"error": str(e)}, + ) + + +def tripadvisor_search( + api_key: str, + query: str, + language: str = "en", +) -> ServiceResponse: + """ + Search for locations using the TripAdvisor API. + + Args: + api_key (`str`): + Your TripAdvisor API key. + query (`str`): + The search query. + language (`str`, optional): + The language for the response. Defaults to 'en'. + + Returns: + `ServiceResponse`: A dictionary with two variables: `status` and + `content`. The `status` variable is from the ServiceExecStatus enum, + and `content` is the JSON response from TripAdvisor API or error + information, which depends on the `status` variable. + + If successful, the `content` will be a + dictionary with the following structure: + { + 'data': [ + { + 'location_id': str, + 'name': str, + 'address_obj': { + 'street1': str, + 'street2': str, + 'city': str, + 'state': str, + 'country': str, + 'postalcode': str, + 'address_string': str + } + }, + ... + ] + } + Each item in the 'data' list represents + a location matching the search query. + + Example: + .. code-block:: python + + result = search_tripadvisor("your_api_key", "Socotra", "en") + if result.status == ServiceExecStatus.SUCCESS: + print(result.content) + + Example of successful `content`: + { + 'data': [ + { + 'location_id': '574818', + 'name': 'Socotra Island', + 'address_obj': { + 'street2': '', + 'city': 'Aden', + 'country': 'Yemen', + 'postalcode': '', + 'address_string': 'Aden Yemen' + } + }, + { + 'location_id': '25395815', + 'name': 'Tour Socotra', + 'address_obj': { + 'street1': '20th Street', + 'city': 'Socotra Island', + 'state': 'Socotra Island', + 'country': 'Yemen', + 'postalcode': '111', + 'address_string': + '20th Street, Socotra Island 111 Yemen' + } + }, + # ... more results ... + ] + } + """ + url = ( + f"https://api.content.tripadvisor.com/api/v1/location/search?" + f"searchQuery={query}&language={language}&key={api_key}" + ) + headers = { + "accept": "application/json", + } + + logger.info(f"Searching for locations with query '{query}'") + + try: + response = requests.get(url, headers=headers, timeout=20) + logger.info( + f"Received response with status code {response.status_code}", + ) + + if response.status_code == 200: + logger.info("Successfully retrieved search results") + return ServiceResponse( + status=ServiceExecStatus.SUCCESS, + content=response.json(), + ) + error_detail = ( + response.json() + .get("error", {}) + .get("message", f"HTTP Error: {response.status_code}") + ) + logger.error(f"Error in response: {error_detail}") + return ServiceResponse( + status=ServiceExecStatus.ERROR, + content={"error": error_detail}, + ) + except Exception as e: + logger.exception("Exception occurred while searching for locations") + return ServiceResponse( + status=ServiceExecStatus.ERROR, + content={"error": str(e)}, + ) + + +def tripadvisor_search_location_details( + api_key: str, + location_id: str = None, + query: str = None, + language: str = "en", + currency: str = "USD", +) -> ServiceResponse: + """ + Get detailed information about a specific location using the TripAdvisor API. + + Args: + api_key (`str`): + Your TripAdvisor API key. + location_id (`str`, optional): + The unique identifier for the location. Required if + query is not provided. + query (`str`, optional): + The search query to find a location. Required if + location_id is not provided. + language (`str`, optional): + The language for the response. Defaults to 'en', 'zh' for Chinese. + currency (`str`, optional): + The currency code to use for request and response + (should follow ISO 4217). Defaults to 'USD'. + + Returns: + `ServiceResponse`: A dictionary with two variables: `status` and + `content`. The `status` variable is from the ServiceExecStatus enum, + and `content` is the JSON response from TripAdvisor API or error + information, which depends on the `status` variable. + + If successful, the `content` will be a dictionary with + detailed information about the location, including + name, address, ratings, reviews, and more. + + Note: + Either `location_id` or `query` must be provided. If both are provided, + `location_id` takes precedence. + + Example: + .. code-block:: python + + # Using location_id + result = get_tripadvisor_location_details( + "your_api_key", + location_id="574818", + language="en", + currency="USD" + ) + if result.status == ServiceExecStatus.SUCCESS: + print(result.content) + + # Or using a query + result = get_tripadvisor_location_details( + "your_api_key", + query="Socotra Island", + language="en", + currency="USD" + ) + if result.status == ServiceExecStatus.SUCCESS: + print(result.content) + + Example of successful `content`: + { + 'location_id': '574818', + 'name': 'Socotra Island', + 'web_url': 'https://www.tripadvisor.com/Attraction_Review...', + 'address_obj': { + 'street2': '', + 'city': 'Aden', + 'country': 'Yemen', + 'postalcode': '', + 'address_string': 'Aden Yemen' + }, + 'ancestors': [ + {'level': 'City', 'name': 'Aden', 'location_id': '298087'}, + {'level': 'Country', 'name': 'Yemen', 'location_id': '294014'} + ], + 'latitude': '12.46342', + 'longitude': '53.82374', + 'timezone': 'Asia/Aden', + 'write_review': 'https://www.tripadvisor.com/UserReview...', + 'ranking_data': { + 'geo_location_id': '298087', + 'ranking_string': '#1 of 7 things to do in Aden', + 'geo_location_name': 'Aden', + 'ranking_out_of': '7', + 'ranking': '1' + }, + 'rating': '5.0', + 'rating_image_url': 'https://www.tripadvisor.com/.../5.svg', + 'num_reviews': '62', + 'review_rating_count': { + '1': '1', + '2': '0', + '3': '1', + '4': '1', + '5': '59', + }, + 'photo_count': '342', + 'see_all_photos': 'https://www.tripadvisor.com/Attraction...', + 'category': {'name': 'attraction', 'localized_name': 'Attraction'}, + 'subcategory': [ + {'name': 'nature_parks', 'localized_name': 'Nature & Parks'}, + {'name': 'attractions', 'localized_name': 'Attractions'} + ], + 'groups': [ + { + 'name': 'Nature & Parks', + 'localized_name': 'Nature & Parks', + 'categories': [{'name': 'Islands', + 'localized_name': 'Islands'}] + } + ], + 'neighborhood_info': [], + 'trip_types': [ + {'name': 'business', 'localized_name': + 'Business', 'value': '2'}, + {'name': 'couples', 'localized_name': + 'Couples', 'value': '10'}, + {'name': 'solo', 'localized_name': + 'Solo travel', 'value': '11'}, + {'name': 'family', 'localized_name': + 'Family', 'value': '2'}, + {'name': 'friends', 'localized_name': + 'Friends getaway', 'value': '22'} + ], + 'awards': [] + } + + Raises: + ValueError: If neither location_id nor query is provided. + """ # noqa + if location_id is None and query is None: + raise ValueError("Either location_id or query must be provided.") + + if location_id is None: + # Use search_tripadvisor to get the location_id + search_result = tripadvisor_search(api_key, query, language) + if search_result.status != ServiceExecStatus.SUCCESS: + return search_result + + # Get the first location_id from the search results + locations = search_result.content.get("data", []) + if not locations: + return ServiceResponse( + status=ServiceExecStatus.ERROR, + content={"error": "No locations found for the given query."}, + ) + + location_id = locations[0]["location_id"] + logger.info(f"Using location_id {location_id} from search results.") + + # Warning message if there are multiple locations + if len(locations) > 1: + logger.warning( + f"Multiple locations found for query '{query}'. " + f"Using the first result. " + f"Other {len(locations) - 1} results are ignored.", + ) + + url = ( + f"https://api.content.tripadvisor.com/api/v1/location/{location_id}/" + f"details?language={language}¤cy={currency}&key={api_key}" + ) + headers = { + "accept": "application/json", + } + + logger.info(f"Requesting details for location ID {location_id}") + + try: + response = requests.get(url, headers=headers, timeout=20) + logger.info( + f"Received response with status code {response.status_code}", + ) + + if response.status_code == 200: + logger.info("Successfully retrieved location details") + return ServiceResponse( + status=ServiceExecStatus.SUCCESS, + content=response.json(), + ) + error_detail = ( + response.json() + .get("error", {}) + .get("message", f"HTTP Error: {response.status_code}") + ) + logger.error(f"Error in response: {error_detail}") + return ServiceResponse( + status=ServiceExecStatus.ERROR, + content={"error": error_detail}, + ) + except Exception as e: + logger.exception( + "Exception occurred while requesting location details", + ) + return ServiceResponse( + status=ServiceExecStatus.ERROR, + content={"error": str(e)}, + )