169 lines
8.4 KiB
Python
169 lines
8.4 KiB
Python
import pandas as pd
|
|
import re
|
|
import json
|
|
import csv
|
|
from io import StringIO
|
|
import config
|
|
import ast
|
|
|
|
import utils
|
|
import preprocess
|
|
import table_funcs
|
|
import prompts
|
|
import prompt_funcs
|
|
import claude_funcs
|
|
|
|
|
|
def BOTTOM_UP_PRIMARY(page, payer):
|
|
return f"""
|
|
### PAGE START ### {page} ### PAGE END
|
|
|
|
The preceding text is one page of a contract between Payer {payer} and a provider in their network. Your job is to extract attributes related to the reimbursement of different services and specialties.
|
|
|
|
The reimbursement values will be identified with >>> <<< indicators (e.g. >>>105%<<<). Make sure there is at least one dictionary object for EVERY reimbursement value seen (either % or $).
|
|
|
|
If any of the attributes are not found, return N/A. For all attributes, only write what is written on the page. Do not make up new phrases or words.
|
|
|
|
Return at least one json object for each combination of attributes seen. It is possible that a page will not have any attributes. When this is the case, return an empty list.
|
|
|
|
Here are the attributes to be included in each dictionary, and instructions on how to correctly answer:
|
|
'SERVICE' : What is the Service that is being reimbursed? Use your best judgement and knowledge of the healthcare industry to answer. Use only exact text from the document. Do not leave this field N/A.
|
|
'REIMBURSEMENT_FLAT_FEE' : If the listed reimbursement is dollar value, return only that dollar value. There can only be one answer for each object. If more than one rate is found, create additional objects for them.
|
|
'REIMBURSEMENT_RATE' : If the listed reimbursement is a percent of something, return only that percent. There can only be one answer for each object. If more than one rate is found, create additional objects for them. Do NOT include multiple percentages in this value.
|
|
'FULL_METHODOLOGY' : If the listed reimbursement is a percent of something, what is it the percent of? If the listed reimbursement is a direct dollar value, how is that dollar value paid (per diem, per unit, etc)? Write the full sentence in text describing the payment methology.
|
|
|
|
Note that only one of REIMBURSEMENT_FLAT_FEE or REIMBURSEMENT_RATE should be populated in each dictionary. Ensure all 4 keys are in the dictionary.
|
|
|
|
Only return the list, with no other commentary or explanation. Ensure you abide by proper JSON formatting.
|
|
"""
|
|
|
|
def BOTTOM_UP_CODES(d, page):
|
|
return f"""### PAGE START ### {page} ### PAGE END
|
|
|
|
The above text contains a number of reimbursement terms. For the rest of this request, focus only on the specific term listed below:
|
|
Service: {d['SERVICE']}
|
|
Methodology: {d['FULL_METHODOLOGY']}
|
|
|
|
Your job is to identify codes associated with this specific term.
|
|
|
|
Read and analyze the page, then populate a JSON dictionary with the following code types if they are associated with the given Service and Methodology (descriptions provided):
|
|
'REIMBURSEMENT_ADMITTYPE_CODES' : Single-digit codes that indicate the type of admission to a facility.
|
|
'REIMBURSEMENT_DIAG_CODES' : Multi-digit alphanumeric codes related to diagnosis. They may contain a '.' after the first 3 characters. These may be labelled as ICD-10 or ICD-10-CM codes.
|
|
'REIMBURSEMENT_GROUPER_CODES' : 3- or 4-digit codes that may be labelled DRG or or similar.
|
|
'REIMBURSEMENT_GROUPER' : If there are grouper codes, what type are they? They could be MSDRG, APDRG, APRDRG, APC, APG, or EAPG. Choose only from these options.
|
|
'REIMBURSEMENT_PLACEOFSERVICE_CODES' : 2-digit codes related to the place of service.
|
|
'REIMBURSEMENT_PROC_CODES' : 5-digit numeric or alphanumeric codes. They may be labelled as CPT or HCPCS codes. These are related to specific procedures.
|
|
'REIMBURSEMENT_PROC_CODE_MODIFIER' : 2-digit alphanumeric codes accompanying a PROC code.
|
|
'REIMBURSEMENT_REVENUE_CODES' : 3- or 4-digit numeric or alphanumeric codes related to revenue. They may be labelled as 'Rev' codes.
|
|
'REIMBURSEMENT_STATUS_INDICATOR_CODES' : 1- or 2-digit alphanumeric codes indicating the payment status.
|
|
|
|
Return a value for each type of code - return N/A for any values not found. Multiple codes for each type may be found. In this case, return them all. They may also present as a range. If so, return the range.
|
|
|
|
Ensure you ONLY return codes if they are associated directly with the Service listed. Codes referring to other services should not be included.
|
|
|
|
Only return the dictionary, with no other commentary or explanation. Ensure you abide by proper JSON formatting.
|
|
"""
|
|
|
|
|
|
def BOTTOM_UP_METHODOLOGY(d):
|
|
return f"""Analyze the full methodology text listed below:
|
|
{d['FULL_METHODOLOGY']}
|
|
|
|
Your job is to identify what the concise methodology is based on the full methodology.
|
|
|
|
Choose the closest option from the following:
|
|
Allowed Amount, Allowable Charges, Fee Schedule, Medicare Fee Schedule, Medicare Allowed Amount, Medicaid Fee Schedule, Medicaid Allowed Amount, Provider's Charges, Billed Charges, IME/DME Amount, MSRP, Per Unit, Per Diem, Per Visit, Per Member, Per Member Per Month, Per Hour
|
|
|
|
For 'Allowed Amount' and 'Fee Schedule', use the more specific answer (Medicare/Medicaid) if applicable.
|
|
For the 'Per ___' values, note that this list is not necessarily exhaustive. It could be anything like 'Per ___'.
|
|
|
|
Return only the answer, without using complete sentences. If there is nothing even close to the above options in the full methodology text, simply return 'N/A'
|
|
"""
|
|
|
|
|
|
|
|
input_dict = utils.read_input()
|
|
|
|
filename = list(input_dict.keys())[0]
|
|
contract_text = input_dict[filename]
|
|
print(filename)
|
|
|
|
|
|
|
|
|
|
|
|
def convert_to_dict(table_text):
|
|
table_text = table_text.strip('{}')
|
|
table_text_list = table_text.split(']')
|
|
table_text_list = [item for item in table_text_list if len(item) > 0]
|
|
|
|
final_dict = {}
|
|
num_elements = 0
|
|
for key_value_text in table_text_list:
|
|
key = key_value_text.split(':')[0].strip(', \'')
|
|
value = ':'.join(key_value_text.split(':')[1:]).strip()+']'
|
|
value_list = ast.literal_eval(value)
|
|
final_dict[key] = value_list
|
|
num_elements = len(value_list)
|
|
return final_dict, num_elements
|
|
|
|
|
|
def format_table(table_json, table_size):
|
|
table_text = ""
|
|
keys = [key for key in table_json.keys()]
|
|
table_text += ' : '.join(keys) + "\n"
|
|
for i in range(table_size):
|
|
for key in keys:
|
|
if table_json[key][i]:
|
|
#table_text += key + ': ' + table_json[key][i] + ', '
|
|
table_text += table_json[key][i] + ' : '
|
|
table_text += r'\n'
|
|
return table_text
|
|
|
|
def align_and_format_tables(text_dict):
|
|
aligned_text_dict = {}
|
|
for key, text in text_dict.items():
|
|
aligned_text = text
|
|
if 'Table Start' in text:
|
|
table_texts = re.findall(r'-------Table Start--------(.*?)-------Table End--------', text, re.DOTALL)
|
|
for table_text in table_texts:
|
|
try:
|
|
# Extract pretable text
|
|
pretable = table_text.split('{')[0].strip()
|
|
# Extract and format table text
|
|
#table_only = "{" + table_text.split('{', 1)[1].rsplit('}', 1)[0].replace("'", '"') + "}"
|
|
table_only = "{" + table_text.split('{', 1)[1].rsplit('}', 1)[0] + "}"
|
|
|
|
table, table_size = convert_to_dict(table_only)
|
|
table_formatted = format_table(table, table_size)
|
|
except:
|
|
table_formatted = table_text
|
|
|
|
# Align
|
|
if text.count(pretable) == 2: # One table
|
|
# Remove original table
|
|
aligned_text = text.replace(table_text, "")
|
|
# Align new table
|
|
aligned_text = aligned_text.replace(pretable, pretable + table_formatted)
|
|
|
|
else:
|
|
aligned_text = aligned_text.replace(table_text, pretable + table_formatted)
|
|
aligned_text_dict[key] = aligned_text.replace("-------Table Start--------", "").replace("-------Table End--------", "")
|
|
else:
|
|
aligned_text_dict[key] = text
|
|
|
|
return aligned_text_dict
|
|
|
|
contract_text = preprocess.clean_newlines(contract_text)
|
|
text_dict = preprocess.split_text(contract_text)
|
|
text_dict = align_and_format_tables({'5' : text_dict['5']})
|
|
text_dict = preprocess.highlight_rates(text_dict)
|
|
|
|
prompt = BOTTOM_UP_CODES({'SERVICE' : 'Psychiatric Diagnostic Evaluation', 'FULL_METHODOLOGY' : 'N/A' }, text_dict['5'])
|
|
|
|
answer = claude_funcs.invoke_claude_3(prompt, max_tokens=4000)
|
|
print(answer)
|
|
|
|
|
|
|