0% found this document useful (0 votes)
4 views16 pages

PostgreSQL API for Preference Management

The document is a Python script that defines a serverless application for managing user preferences and distributor details using AWS Lambda and PostgreSQL. It includes various functions for handling HTTP requests, interacting with a database, and executing SQL queries based on user inputs. The application supports adding, updating, and fetching preferences, as well as retrieving distributor information while managing blocked records and query conditions.

Uploaded by

evitalgpu
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
4 views16 pages

PostgreSQL API for Preference Management

The document is a Python script that defines a serverless application for managing user preferences and distributor details using AWS Lambda and PostgreSQL. It includes various functions for handling HTTP requests, interacting with a database, and executing SQL queries based on user inputs. The application supports adding, updating, and fetching preferences, as well as retrieving distributor information while managing blocked records and query conditions.

Uploaded by

evitalgpu
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd

import datetime

import json
import os
import psycopg2, [Link]
from itertools import chain
from [Link] import parse_qs
import sys

import pytz

connection = None
cursor_pgsql = None

CUSTOM_OPERATORS = {
"equal_to": "=",
"in": "IN",
"not in": "NOT IN",
"not_equal_to": "!=",
"greater_than": ">",
"less_than": "<",
"greater_than_or_equal_to": ">=",
"less_than_or_equal_to": "<=",
}

# NOTE : In `records_type` field, we can define that module accepts single_record or


multiple_records for particular user.
MODULE_DATA = {
"distributor":{
"target_table": "distributor_master_audience",
"avoid_blocked_records": True,
"blocked_query": """ left join customers on customers.client_entity = 'distributor' and
customers.owner_entity = 'chemist' and customers.owner_entity_id =
current_owner_entity_id and customers.client_entity_id = distributor_master_audience.id
where (customers."status" is null or customers."status" = 'active')"""
}
}

def lambda_handler(event, context):

http_method = event['httpMethod']
path = event['path']

# Routing logic based on the path and method


if "/add_update_preferences" in path and http_method == "POST":
return add_update_preferences_handler(event)
elif "/get_preferences" in path and http_method == "GET":
return get_preferences_handler(event)
elif "/fetch_preferences_results" in path and http_method == "POST":
return fetch_preferences_results_handler(event)
elif "/fetch_distributor_details" in path and http_method == "POST":
return fetch_distributor_details_handler(event)
elif "/save_preferences" in path and http_method == "POST":
return save_selected_preferences_handler(event)
elif "/get_saved_preferences" in path and http_method == "POST":
return get_saved_preferences_handler(event)
elif "/update_preferences" in path and http_method == "POST":
return update_preferences_handler(event)
elif "/audience_membership" in path and http_method == "POST":
return check_audience_membership_handler(event)
else:
return {
"statusCode": 404,
"body": [Link]({"message": f"API not found-{path}"})
}

def get_db_connection():
try:
global connection
global cursor_pgsql
if connection is None or [Link] != 0:
connection = [Link]([Link]['DATABASE_URL'])
cursor_pgsql = [Link](cursor_factory=[Link])
print("Connection to RDS PostgreSQL database successful.")
return cursor_pgsql, connection
except Exception as e:
return {
"statusCode": 400,
"body": [Link]({"message": f"Error occured in creating DB connection - {e}"})
}

def insert(target_table, columns, values, execute_dict=False, is_multiplicity=False,


db_type="master"):
try:
caller_function = sys._getframe().f_back.f_code.co_name
query = "INSERT INTO " + target_table

columns_template = ",".join(columns)
query += "(" + columns_template + ")"

values_template = ",".join(["%s"] * len(columns))


if is_multiplicity == True:
query += " VALUES (" + values_template + ")"
else:
query += " VALUES (" + values_template + ") RETURNING id"
data = execute_db_query(query, values, is_multiplicity)
return data
except Exception as e:
return {
"statusCode": 400,
"body": [Link]({"message": f"Error occured in insert record in Database - {e}"})
}

def update(target_table, where_clause, data,


execute_dict=False,params=(),is_multiplicity=False,ignore_str=False):
try:
update_string = ""
for key in [Link]():
if update_string != " ":
update_string += " "
if isinstance(data[key], int) or ignore_str:
data[key] = str(data[key])
update_string += key + " = " + data[key] + ","
else:
data[key] = str(data[key])
data[key] = str(data[key]).replace("%", "%%")
update_string += key + "='" + data[key] + "',"
update_string = update_string.rstrip(update_string[-1])
query = (
"UPDATE "
+ target_table
+ " SET "
+ update_string
+""
+ where_clause
+""
+ "RETURNING id"
)
print("QUERY", query)
if execute_dict:
return execute_db_query_dict(query, params, is_multiplicity)
return execute_db_query(query, params, is_multiplicity)
except Exception as e:
print(e)

def execute_db_query_dict(query, params=None, is_multiquery=False):


connection = None
try:
# Establish a new connection for each request
cursor_pgsql, connection = get_db_connection()

master_cursor = [Link](cursor_factory=[Link])

if is_multiquery:
master_cursor.executemany(query, params)
else:
master_cursor.execute(query, params)

if (
[Link](" ")[0].lower() == "select"
or [Link](" ")[0].lower() == "with"
):
rec = master_cursor.fetchall()
ans1 = []
for row in rec:
[Link](dict(row))
return ans1
else:
if [Link]().find("returning") > 1:

return master_cursor.fetchone()[0]
[Link]()
return 1

except Exception as e:
print(f"Database error: {e}")
raise

finally:
# Close the cursor and connection after each use
if connection:
cursor_pgsql.close()
[Link]()
print("Database connection closed.")

def execute_db_query(query, params=None, is_multiquery=False):


connection = None
try:
# Establish a new connection for each request
cursor_pgsql, connection = get_db_connection()

master_cursor = [Link](cursor_factory=[Link])

if [Link](" ")[0].upper() == "SELECT":


master_cursor.execute(query, params)
result = master_cursor.fetchall()
else:
if is_multiquery:
master_cursor.executemany(query, params)
else:
master_cursor.execute(query, params)
if master_cursor.[Link](" ")[0] == "UPDATE":
[Link]()
result = master_cursor.rowcount
elif master_cursor.[Link](" ")[0] == "DELETE":
[Link]()
result = master_cursor.rowcount
elif master_cursor.[Link](" ")[0] == "INSERT":
[Link]()
if [Link]().find("returning") > 1:
result = master_cursor.fetchone()[0]
else:
result = master_cursor.rowcount
elif (
[Link](" ")[0] == "REFRESH"
or [Link](" ")[0] == "TRUNCATE"
):
[Link]()
result = True
else:
result = master_cursor.fetchall()

return result

except Exception as e:
print(f"Database error: {e}")
raise

finally:
# Close the cursor and connection after each use
if connection:
cursor_pgsql.close()
[Link]()
print("Database connection closed.")

def add_update_preferences_handler(event):
try:
body = [Link]([Link]('body', '{}'))
if body["action_type"] == "add":
# Add Query
module = body["module"]
preferences = [Link](body["preferences"])
field_data = [Link](body["field_data"])
add_query = f"INSERT INTO prefrences VALUES ( DEFAULT, '{module}',
{preferences}, {field_data});"
data = execute_db_query_dict(add_query)
result = {
"message": "Preferences added successfully",
"received_data": data
}
return {
"statusCode": 200,
"body": [Link](result)
}
if body["action_type"] == "update":
# TODO : Have to implement feature to update preferences
pass
except Exception as e:
print(f"Add/Update Preferences Error: {e}")
return {
"statusCode": 400,
"body": [Link]({"message": f"Error occured in Add/Update Preferences - {e}"})
}
def get_preferences_handler(event):
try:
query_params = [Link]('queryStringParameters', {})
print("Query Params", query_params)
if not query_params:
return {
"statusCode": 400,
"body": [Link]({"message": f"Provide valid query parameters"})
}
module = query_params.get('module')
query = f"SELECT id, module, preferences, field_data FROM preferences
WHERE \"module\" = '{module}' AND status = 'active'"
data = execute_db_query_dict(query)
result = {
"message": f"Fetched Preferences for {module}",
"data": data
}

return {
"statusCode": 200,
"body": [Link](result)
}
except Exception as e:
print(f"Get Preferences Error: {e}")
return {
"statusCode": 400,
"body": [Link]({"message": f"Error occured in Get Preferences - {e}"})
}

def fetch_preferences_results_handler(event):
global MODULE_DATA
try:
body = [Link]([Link]('body', '{}'))
if not body or body == {}:
return {
"statusCode": 400,
"body": [Link]({"message": f"Provide valid body"})
}
module = [Link]('module')
target_table = MODULE_DATA[module]["target_table"]
preferences = [Link]('preferences')
users_list = [Link]('users_list')
query_builder = [Link]('query_builder')
return_query = [Link]('return_query', False)
owner_entity_id = [Link]('owner_entity_id', None)
avoid_blocked_records = MODULE_DATA[module].get("avoid_blocked_records",
False)
if avoid_blocked_records:
blocked_query = MODULE_DATA[module].get("blocked_query")
blocked_query = blocked_query.replace("current_owner_entity_id",
str(owner_entity_id))

else:
blocked_query = None
query = query_builder_lambda(payload=body, avoid_blocked_records =
avoid_blocked_records, blocked_query = blocked_query)
print("------Query---------", query)
if return_query:
return {
"statusCode": 200,
"body": [Link]({"message": f"Fetched Preferences for {target_table}", "query":
query})
}
query_response = execute_db_query_dict(query)
if not query_response:
return {
"statusCode": 400,
"body": [Link]({"message": f"No data found for the given query"})
}

data_list = [item['id'] for item in query_response]


response_data = {
"message": f"Fetched Preferences for {target_table}"
}
response_data["data"] = {
"users_list": data_list,
"total_users": len(data_list)
}
return {
"statusCode": 200,
"body": [Link](response_data)
}
except Exception as e:
print(f"Fetch Preferences Results Error: {e}")
return {
"statusCode": 400,
"body": [Link]({"message": f"Error occured in Fetch Preferences Results - {e}"})
}

def query_builder_lambda(payload, avoid_blocked_records = False, blocked_query = None):


global CUSTOM_OPERATORS
global MODULE_DATA

preferences = [Link]("preferences", [])


query_builder = [Link]("query_builder", "")
users_list = [Link]("users_list", False)
module = [Link]("module")
target_table = MODULE_DATA.get(module, {}).get("target_table", "default_table") #
Default table if not provided

conditions = {}

for preference in preferences:


ref = preference["ref"].strip()
condition_operator = CUSTOM_OPERATORS.get(preference["condition"])
if not condition_operator:
raise ValueError(f"Invalid condition: {preference['condition']}")

data = preference["data"]
if type(data) is list:
data_list = ", ".join([f"'{item}'" for item in data]) # Format data as SQL-safe strings

# Generate SQL condition


condition = f"{ref} {condition_operator} ({data_list})"
else:
condition = f"{ref} {condition_operator} '{data}'"

# Group by operator if specified


if [Link]("operator") == "OR":
condition = f"({condition})"

# Map the condition to the ID


conditions[str(preference["id"])] = condition

# Replace placeholders in query_builder


final_conditions = query_builder
for key, value in [Link]():
final_conditions = final_conditions.replace(key, value)

# Remove unused placeholders and strip extra spaces


final_conditions = final_conditions.strip()
if final_conditions.lower() in ("", "none"):
final_conditions = None

# Generate the final query


if users_list:
query = f"SELECT CAST({target_table}.id AS VARCHAR) AS id FROM {target_table}"
else:
query = f"SELECT COUNT({target_table}.id) as count FROM {target_table}"

# Avoid blocked records


if avoid_blocked_records:
query += blocked_query
if final_conditions:
query += f" AND {final_conditions}"
else:
# Add WHERE clause only if final_conditions is valid
if final_conditions:
query += f" WHERE {final_conditions}"

return query

def fetch_distributor_details_handler(event):
global MODULE_DATA
body = [Link]([Link]('body', '{}'))
if not body or body == {}:
return {
"statusCode": 400,
"body": [Link]({"message": f"Provide valid body"})
}
module = [Link]("module")
owner_entity_id = [Link]("owner_entity_id")
distributor_list = [Link]("distributor_list")
target_table = MODULE_DATA[module]['target_table']
if not distributor_list or not owner_entity_id:
return {
"status_code": 400,
"status_message": "Please provide valid required parameters"
}
try:
if not len(distributor_list) > 1:
distributor_list = f'({distributor_list[0]})'
else:
distributor_list = tuple(distributor_list)

existing_distributor_query = f"SELECT [Link], d.distributor_name, [Link], [Link],


d.gstn_number, CASE WHEN d.distributor_software = '' OR d.distributor_software IS NULL
THEN FALSE ELSE TRUE END AS csv_provided, d.distributor_rating FROM ( SELECT
DISTINCT distributor_id AS dist_id FROM chemist_purchases WHERE chemist_id = 681
AND distributor_id IN {distributor_list}) cp LEFT JOIN distributors d ON cp.dist_id = [Link];"

existing_distributor_list = execute_db_query_dict(existing_distributor_query)

if not existing_distributor_list:
existing_distributor_list = []
new_distributor_list = distributor_list
else:
data_list = [str(item['id']) for item in existing_distributor_list]
new_distributor_list = [item for item in list(distributor_list) if item not in data_list]
new_distributor_list = tuple(new_distributor_list)
if not len(new_distributor_list) > 1:
new_distributor_list = f'({new_distributor_list[0]})'
else:
new_distributor_list = tuple(new_distributor_list)
new_distributor_query = f"SELECT id, distributor_name, city, state from {target_table}
where id IN {new_distributor_list}"

new_distributor_list = execute_db_query_dict(new_distributor_query)
print("QUERY", new_distributor_query)
if not new_distributor_list:
return {
"statusCode": 400,
"body": [Link]({"message": f"No data found for the given query"})
}
result = {
"message": "Fetched Distributors list",
"existing_distributors": existing_distributor_list,
"new_distributors": new_distributor_list
}
return {"statusCode":200,
"body":[Link](result)}
except Exception as e:
print(f"Fetch Distributor List Error: {e}")
return {
"statusCode": 400,
"body": [Link]({"message": f"Error occured in Fetch Distributor List - {e}"})
}

def save_selected_preferences_handler(event):
global MODULE_DATA
body = [Link]([Link]('body', '{}'))
target_table = "preference_value"
name = [Link]("name")
owner_entity = [Link]("owner_entity")
owner_entity_id = [Link]("owner_entity_id")
module = [Link]("module")
selected_preferences = [Link]("selected_preferences")
try:
preference_value_columns = (
"name",
"owner_entity",
"owner_entity_id",
"module",
"selected_preferences"
)
if not (owner_entity or owner_entity_id or module or selected_preferences or name) or
selected_preferences == [] or selected_preferences == {}:
return {
"statusCode": 400,
"body": [Link]({"message": f"Provide all required parameters"})
}
# Perform Insert operations
insert_data = (
name,
owner_entity,
owner_entity_id,
module,
[Link](selected_preferences)
)
preference_insert_response = insert(target_table, preference_value_columns,
insert_data, is_multiplicity=False)
if not preference_insert_response:
return {
"statusCode": 400,
"body": [Link]({"message": f"Error occured in insert preference"})
}
return {
"statusCode": 200,
"body":[Link]({
"message": "Preference Saved/Updated successfully!"
})}
except Exception as e:
print(f"Fetch Distributor List Error: {e}")
return {
"statusCode": 400,
"body": [Link]({"message": f"Error occured in saving preference - {e}"})
}

def get_saved_preferences_handler(event):
global MODULE_DATA
try:
body = [Link]([Link]('body', '{}'))
target_table = "preference_value"
owner_entity = [Link]("owner_entity")
owner_entity_id = [Link]("owner_entity_id")
module = [Link]("module")
status = [Link]("status")
order_by = [Link]("order_by")
fetch_user_list = [Link]("fetch_user_list", False)

id = [Link]("id")
query = f"""SELECT id, "name", owner_entity,owner_entity_id, "module",
selected_preferences, query_builder, to_char(created_date, 'YYYY-MM-DD"T"HH24:MI:SS')
AS created_date, to_char(updated_date, 'YYYY-MM-DD"T"HH24:MI:SS') AS updated_date,
"status" FROM {target_table}"""
where_clause = f" WHERE owner_entity = '{owner_entity}' AND owner_entity_id =
{owner_entity_id}"
if order_by:
order_by = f" ORDER BY {order_by}"
else:
order_by = " ORDER BY id DESC"
if status:
where_clause += f" AND status = '{status}'"
if id:
where_clause += f" AND id = {id}"
if module:
where_clause += f" AND module = '{module}'"
query += where_clause + order_by
print("QUERY", query)
query_response = execute_db_query_dict(query)
if not query_response:
return {
"statusCode": 200,
"body": [Link]({"message": f"No data found for the given query"})
}

if fetch_user_list:
query_response = query_response[0]
api_payload = {
"module": module,
"users_list": True,
"preferences": query_response['selected_preferences'],
"query_builder": query_response['query_builder']
}
user_list_response = fetch_preferences_results_handler(event =
{"body":[Link](api_payload)})
if user_list_response['statusCode'] == 200:
body = [Link](user_list_response['body'])
result = {
"message": "Fetched user list as per preferences",
"data": body['data']['users_list']
}
return {"statusCode":200,
"body":[Link](result)}

result = {
"message": "Fetched saved preferences",
"data": query_response
}
return {"statusCode":200,
"body":[Link](result)}
except Exception as e:
return {
"statusCode": 400,
"body": [Link]({"message": f"Error occured in get saved preferences - {e}"})
}

def update_preferences_handler(event):
try:
body = [Link]([Link]('body', '{}'))
if not body or body == {}:
return {
"statusCode": 400,
"body": [Link]({"message": f"Provide valid body"})
}
id = [Link]('id')
if not id or id == {}:
return {
"statusCode": 400,
"body": [Link]({"message": f"Provide valid id"})
}
module = [Link]('module')
selected_preferences = [Link]('selected_preferences')
selected_preferences = [Link](selected_preferences)
name = [Link]('name')
query_builder = [Link]('query_builder')
current_datetime = [Link]([Link]("Asia/Kolkata"))
update_data = {
"selected_preferences": selected_preferences,
"updated_date": current_datetime
}
if name:
update_data['name'] = name
if query_builder:
update_data['query_builder'] = query_builder

where_clause = f"WHERE id = {id} AND module = '{module}'"


preferences_update_response =
update(target_table="preference_value",where_clause=where_clause,data=update_data)
if not preferences_update_response:
return {
"statusCode": 400,
"body": [Link]({"message": "Error occured in Update Preferences"})
}
return {
"statusCode": 200,
"body": [Link]({"message": "Preferences updated successfully",
"data": preferences_update_response})
}

except Exception as e:
return {
"statusCode": 400,
"body": [Link]({"message": f"Error occured in Update Preferences - {e}"})
}

def get_audience_data(audience_id):
try:
query = f"SELECT * FROM preference_value WHERE id = {audience_id}"
audience_data = execute_db_query_dict(query)
if not audience_data:
return False, "No data found for this "
return True, audience_data
except Exception as e:
return False, "Error occured in get audience data"

def check_audience_membership_handler(event):
try:
body = [Link]([Link]('body', '{}'))
if not body or body == {}:
return {
"statusCode": 400,
"body": [Link]({"message": f"Provide valid body"})
}
module = [Link]('module') # TODO : Might be used in future uses
audience_id = [Link]('audience_id')
if not audience_id:
return {
"statusCode": 400,
"body": [Link]({"message": f"Provide valid audience_id"})
}
membership_data = [Link]('membership_data')
if not membership_data or membership_data == {}:
return {
"statusCode": 400,
"body": [Link]({"message": f"Provide valid membership_data"})
}

# Fetch Audience data


audience_data_response, audience_data = get_audience_data(audience_id)
if not audience_data_response:
return {
"statusCode": 400,
"body": [Link]({"message": audience_data})
}

preferences = audience_data[0].get('selected_preferences')
query_builder = audience_data[0].get('query_builder')

if 'user' in membership_data:
# Check for Audience from Master Table
body = {
"preferences": preferences,
"module": module,
"users_list": True,
"query_builder": query_builder

}
payload = {"body":[Link](body)}
response = fetch_preferences_results_handler(event = payload)
if response['statusCode'] == 400:
return{
"statusCode": 400,
"body": f"Error occured while fetching results for {module}"
}
if response['statusCode'] == 200:
response_body = [Link](response['body'])
data = response_body['data']
audience_list = data['users_list']
if membership_data['user'] in audience_list:
return {
"statusCode": 200,
"body": [Link]({"message": "Audience Membership Verified",
"status":True, "data": f"{module} - {membership_data['user']} exists in audience"})
}
else:
return {
"statusCode": 200,
"body": [Link]({"message": "Audience Membership Verified",
"status":False, "data": f"{module} - {membership_data['user']} does not exist in audience"})
}
else:
data_found = False
for data in audience_data:
key = next(iter(membership_data))
value = membership_data[key]
preferences_data = data['selected_preferences']
for preference in preferences_data:
if preference['name'] == key and value in preference['data']:
data_found = True
return {
"statusCode": 200,
"body": [Link]({"message": "Audience Membership Verified",
"status":True, "data": f"{key} - {value} exists in audience"})
}
if not data_found:
return {
"statusCode": 200,
"body": [Link]({"message": "Audience Membership Verified",
"status":False, "data": f"{key} - {value} does not exists in audience"})
}

except Exception as e:
return {
"statusCode": 400,
"body": [Link]({"message": f"Error occured in Check Audience Membership -
{e}"})
}

You might also like