Skip to content

Commit

Permalink
Self tests for get memory function
Browse files Browse the repository at this point in the history
  • Loading branch information
JanPeterDatakind committed Jun 30, 2024
1 parent 358a316 commit 0fd1204
Show file tree
Hide file tree
Showing 7 changed files with 1,114 additions and 0 deletions.
62 changes: 62 additions & 0 deletions tests/test_cases.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
{
"tests": [
{
"test_case": "Test Case 1",
"user_input": "Get all recipes",
"chat_history": "[{'role': 'user', 'content': 'Get all recipes'}]",
"generate_intent": "true",
"expected_output": {
"result": {
"type": "text",
"file": "",
"value": "| | type | intent |\n|---:|:-------|:-----------------------------------------------------------------------------------------------------------------------------------------------------|\n| 0 | recipe | plot a line chart of conflict events by month for a country using HDX data as an image |\n| 1 | recipe | plot a scatterplot of food price movements and number of fatalities by country using HDXData data, including regression line as an image |\n| 2 | recipe | plot a line chart of fatalities by month for a country using HDX data as an image |\n| 3 | recipe | plot a line chart of commodity prices monthly relative change for a country from 2008-01-01 using HDX data as an image |\n| 4 | recipe | plot a bar chart of humanitarian organizations by sector for a given region using Humanitarian Data Exchange data as an image |\n| 5 | recipe | provide a list of organizations providing food security for a region in a country |\n| 6 | recipe | provide the total population of a provided country using HDX data as text |\n| 7 | recipe | plot population pyramids by age for a country using HDX data as an image |\n| 8 | recipe | plot a scatterplot of food price movements and number of fatalities by country using HDXData data, including regression line as an image |\n| 9 | recipe | plot a map of population by admin1 or admin2 for a country using HAPI data as an image |\n| 10 | recipe | plot a line chart of commodity prices monthly relative change for a country from 2008-01-01 using HDX data as an image |\n| 11 | recipe | List organizations in top 3 states by population in provided IPC Phase in a country, using HAPI data |\n| 12 | recipe | plot a bar chart of humanitarian organizations by sector for a given region using Humanitarian Data Exchange data as an image |\n| 13 | recipe | provide a text summary of metadata by subnational region using HAPI data as text |\n| 14 | recipe | get all recipes |\n| 15 | recipe | plot a map of IPC phase data by admin_1 using HDX data as an image |\n| 0 | memory | plot a line chart of conflict events by month for Chad using HDX data as an image |\n| 1 | memory | plot a scatterplot of food price movements and number of fatalities in TCD from 2008-01-01 using HDXData data, including regression line as an image |\n| 2 | memory | plot a line chart of fatalities by month for Chad using HDX data as an image |\n| 3 | memory | plot a line chart of commodity prices monthly relative change for Chad from 2008-01-01 using HDX data as an image |\n| 4 | memory | plot a bar chart of humanitarian organizations in Wadi Fira by sector using Humanitarian Data Exchange data as an image |\n| 5 | memory | provide a list of organizations providing food security in Wadi Fira, Chad |\n| 6 | memory | provide the total population of Mali using HDX data as text |\n| 7 | memory | plot population pyramids by age for Chad using HDX data as an image |\n| 8 | memory | plot a scatterplot of food price movements and number of fatalities in TCD from 2008-01-01 using HDXData data, including regression line as an image |\n| 9 | memory | plot a map of population by admin1 for Haiti using HAPI data as an image |\n| 10 | memory | plot a line chart of commodity prices monthly relative change for Chad from 2008-01-01 using HDX data as an image |\n| 11 | memory | List organizations in the top 3 states by population in IPC Phase 3+ in Chad, using HAPI data |\n| 12 | memory | plot a bar chart of humanitarian organizations in Wadi Fira by sector using Humanitarian Data Exchange data as an image |\n| 13 | memory | provide a text summary of metadata for Wadi Fira using HAPI data as text |\n| 14 | memory | retrieve all recipes |\n| 15 | memory | plot a map of IPC phase 3 data by admin_1 in Chad using HDX data as an image |"
},
"metadata": "{\"params\": {}, \"attribution\": \"\", \"data_url\": \"\", \"time_period\": {\"start\": \"\", \"end\": \"\"}}",
"memory_type": "memory",
"memory": "retrieve all recipes",
"memory_found": "true"
}
},
{
"test_case": "Test Case 2",
"user_input": "provide the total population of Mali using HDX data as text",
"chat_history": "[{'role': 'user', 'content': 'Get all recipes'}, {'role': 'user', 'content': 'provide the total population of Mali using HDX data as text'}]",
"generate_intent": "true",
"expected_output": {
"result": {
"type": "number",
"file": "",
"value": "17907114"
},
"metadata": "{\"params\": {\"country_code\": \"MLI\"}, \"attribution\": \"https://data.humdata.org/dataset/ce21c7db-d8f0-40f8-adc2-452d2d2d105c\", \"data_url\": \"https://data.humdata.org/dataset/ce21c7db-d8f0-40f8-adc2-452d2d2d105c/resource/6f243ba2-4d4a-4663-a7c4-e917dbbde73a/download/mli_pop_adm0_v2.csv\", \"time_period\": {\"start\": \"2018-01-01\", \"end\": \"2018-12-31T23:59:59\"}}",
"memory_type": "memory",
"memory": "provide the total population of Mali using HDX data as text",
"memory_found": "true"
}
},
{
"test_case": "Test Case 3",
"user_input": "plot a bar chart of humanitarian organizations in Wadi Fira by sector using Humanitarian Data Exchange data as an image",
"chat_history": "[{'role': 'user', 'content': 'Get all recipes'}, {'role': 'user', 'content': 'provide the total population of Mali using HDX data as text'}, {'role': 'user', 'content': 'plot a bar chart of humanitarian organizations in Wadi Fira by sector using Humanitarian Data Exchange data as an image'}]",
"generate_intent": "true",
"expected_output": {
"result": {
"type": "image",
"file": "http://localhost:8000/public/images/memory_image_e9d3e3ee-977f-4291-a51a-7e4e3e4cd5f3.png",
"value": ""
},
"metadata": "{\"params\": {\"region\": \"Wadi Fira\"}, \"attribution\": \"https://data.humdata.org/dataset/682c3db6-e253-430f-a3f3-305ef079e2de\", \"data_url\": \"https://data.humdata.org/dataset/682c3db6-e253-430f-a3f3-305ef079e2de/resource/1e3ba1f4-2dbc-4f46-9ce7-2274e157188e/download/3w-tcd-20240508.xlsx\", \"time_period\": {\"start\": \"2023-12-01\", \"end\": \"2024-05-31T23:59:59.999999\"}}",
"memory_type": "memory",
"memory": "plot a bar chart of humanitarian organizations in Wadi Fira by sector using Humanitarian Data Exchange data as an image",
"memory_found": "true"
}
},
{
"test_case": "Test Case 4",
"user_input": "Plot the distribution of internet access in Chad",
"chat_history": "[{'role': 'user', 'content': 'Get all recipes'}, {'role': 'user', 'content': 'provide the total population of Mali using HDX data as text'}, {'role': 'user', 'content': 'plot a bar chart of humanitarian organizations in Wadi Fira by sector using Humanitarian Data Exchange data as an image'}, {'role': 'user', 'content': 'Plot the distribution of internet access in Chad'}]",
"generate_intent": "true",
"expected_output": {"result":"Sorry, no recipe or memory found","memory_found":"false"}
}
]
}
24 changes: 24 additions & 0 deletions tests/test_get_memory.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from utils.general import call_execute_query_api, call_get_memory_recipe_api
import json
import pytest

#load json file into variable and print it
@pytest.fixture
def get_test_cases():
with open('test_cases.json') as f:
test_data = json.load(f)
return test_data

def test_get_memory_recipe(get_test_cases):
"""
Tests the get memory recipe API endpoint.
"""

for test in get_test_cases.get('tests', []):
user_input = test["user_input"]
chat_history = test["chat_history"]
generate_intent = test["generate_intent"]
expected_output = test["expected_output"]
response = call_get_memory_recipe_api(user_input, chat_history, generate_intent)
assert response == expected_output

Empty file added tests/utils/__init__.py
Empty file.
131 changes: 131 additions & 0 deletions tests/utils/db.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import os

import pandas as pd
import psycopg2
from dotenv import load_dotenv
from sqlalchemy import create_engine

from utils.general import call_execute_query_api, is_running_in_docker

load_dotenv()


def get_connection(instance="data"):
"""
This function gets a connection to the database
Args:
instance (str): The instance of the database to connect to, "recipe" or "data". Default is "data"
"""
instance = instance.upper()

host = os.getenv(f"POSTGRES_{instance}_HOST")
port = os.getenv(f"POSTGRES_{instance}_PORT")
database = os.getenv(f"POSTGRES_{instance}_DB")
user = os.getenv(f"POSTGRES_{instance}_USER")
password = os.getenv(f"POSTGRES_{instance}_PASSWORD")

conn = psycopg2.connect(
dbname=database, user=user, password=password, host=host, port=port
)
return conn


def execute_query(query, instance="data"):
"""
Executes a SQL query and returns the result as a DataFrame.
Parameters:
query (str): The SQL query to execute.
instance (str): The database instance to connect to. Default is "data".
Returns:
pandas.DataFrame: The result of the query as a DataFrame
"""

conn = get_connection(instance)
cur = conn.cursor()

# Set to read-only mode
cur.execute("SET TRANSACTION READ ONLY;")

print(f"Executing query: {query}")

# Execute the query
cur.execute(query)

# Fetch all the returned rows
rows = cur.fetchall()

print(f"Query returned {len(rows)} rows")

# Get column names
column_names = [desc[0] for desc in cur.description]

# Close the cursor and connection
cur.close()
conn.close()

# Convert rows to DataFrame
df = pd.DataFrame(rows, columns=column_names)

return df


def connect_to_db(instance="recipe"):
"""
Connects to the specified database instance (RECIPE or DATA) DB and returns a connection object.
Args:
instance (str): The name of the database instance to connect to. Defaults to "RECIPE".
Returns:
sqlalchemy.engine.base.Engine: The connection object for the specified database instance.
"""

instance = instance.upper()

# Fallback for CLI running outside of docker
if not is_running_in_docker():
os.environ[f"POSTGRES_{instance}_HOST"] = "localhost"
os.environ[f"POSTGRES_{instance}_PORT"] = "5433"

host = os.getenv(f"POSTGRES_{instance}_HOST")
port = os.getenv(f"POSTGRES_{instance}_PORT")
database = os.getenv(f"POSTGRES_{instance}_DB")
user = os.getenv(f"POSTGRES_{instance}_USER")
password = os.getenv(f"POSTGRES_{instance}_PASSWORD")
conn_str = f"postgresql://{user}:{password}@{host}:{port}/{database}"

# add an echo=True to see the SQL queries
conn = create_engine(conn_str)
return conn


async def get_data_info():
"""
Get data info from the database.
Returns:
str: The data info.
"""

global data_info

# run this query: select table_name, summary, columns from table_metadata

query = """
SELECT
table_name,
summary,
columns
FROM
table_metadata
--WHERE
-- countries is not null
"""

data_info = await call_execute_query_api(query)

return data_info
153 changes: 153 additions & 0 deletions tests/utils/general.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
import base64
import json
import os
import re
import sys
import warnings

import pandas as pd
import requests
from dotenv import load_dotenv

load_dotenv()

# Suppress all warnings
warnings.filterwarnings("ignore")

execute_query_url = f"{os.getenv('RECIPE_SERVER_API')}execute_query"
get_memory_recipe_url = f"{os.getenv('RECIPE_SERVER_API')}get_memory_recipe"

data_info = None


def replace_env_variables(value):
"""
Recursively replaces environment variable placeholders in a given value.
Args:
value (dict, list, str): The value to process
Returns:
The processed value with environment variable placeholders replaced.
"""
if isinstance(value, dict):
return {k: replace_env_variables(v) for k, v in value.items()}
elif isinstance(value, list):
return [replace_env_variables(v) for v in value]
elif isinstance(value, str):
matches = re.findall(r"\{\{ (.+?) \}\}", value)
for match in matches:
if os.getenv(match) is None:
print(f"Environment variable {match} is not set.")
sys.exit(1)

value = value.replace("{{ " + match + " }}", os.getenv(match))
return value
else:
return value


def read_integration_config(integration_config_file):
"""
Read the APIs configuration from the integration config file.
Args:
integration_config_file (str): The path to the integration config file.
Returns:
apis (dict): A dictionary containing the API configurations.
field_map (dict): A dictionary containing the field mappings.
standard_names (dict): A dictionary containing the standard names.
data_node (str): The data node to use for the integration.
"""
with open(integration_config_file) as f:
print(f"Reading {integration_config_file}")
config = json.load(f)
config = replace_env_variables(config)
apis = config["openapi_interfaces"]
field_map = config["field_map"]
standard_names = config["standard_names"]

return apis, field_map, standard_names


def is_running_in_docker():
"""
Check if the code is running inside a Docker container.
Returns:
bool: True if running inside a Docker container, False otherwise.
"""
return os.path.exists("/.dockerenv")


def make_api_request(url, payload):
"""
Makes an API request to the specified URL with the given payload.
Args:
url (str): The URL to make the API request to.
payload (dict): The payload to send with the API request.
Returns:
dict: The response from the API as a dictionary.
Raises:
requests.exceptions.RequestException: If an error occurs while making the API request.
"""
headers = {"Content-Type": "application/json"}
print(f"API URL: {url}")
print(f"API Payload: {payload}")
response = requests.post(url, headers=headers, json=payload)
print(f"API Response Status Code: {response.status_code}")
response = response.content
print(f"API Response {response}")
return response


def call_execute_query_api(sql):
"""
Calls the execute query action API endpoint with the given SQL query.
Args:
sql (str): The SQL query to execute.
Returns:
dict: The response from the API.
"""
data = {"query": f"{sql}"}
print(f"Calling execute query API {execute_query_url} with {sql} ...")
return make_api_request(execute_query_url, data)


def call_get_memory_recipe_api(user_input, history, generate_intent="true"):
"""
Calls the API to get a memory recipe action.
Args:
user_input (str): The user input.
history (str): The chat history.
generate_intent (str): Whether to generate the intent.
Returns:
The API response from the make_api_request function.
"""

data = {
"user_input": f"{user_input}",
"chat_history": history,
"generate_intent": "true",
}
print(f"Calling execute query API {get_memory_recipe_url} with {data} ...")
result = make_api_request(get_memory_recipe_url, data)

if isinstance(result, bytes):
result = result.decode("utf-8")

print("IN API CALL", result)
result = json.loads(result)

return result
Loading

0 comments on commit 0fd1204

Please sign in to comment.