diff --git a/.env.sample b/.env.sample new file mode 100644 index 0000000..ee0d23b --- /dev/null +++ b/.env.sample @@ -0,0 +1 @@ +OPENAI_API_KEYS = ['sk-xxx',] diff --git a/.gitignore b/.gitignore index 68bc17f..8396695 100644 --- a/.gitignore +++ b/.gitignore @@ -26,6 +26,11 @@ share/python-wheels/ *.egg MANIFEST +data/ + + +.env + # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. diff --git a/code/.DS_Store b/code/.DS_Store new file mode 100644 index 0000000..aff4fa0 Binary files /dev/null and b/code/.DS_Store differ diff --git a/code/app.py b/code/app.py index 89fec4e..90be621 100644 --- a/code/app.py +++ b/code/app.py @@ -3,31 +3,43 @@ from document import Report from reader import Reader from user_qa import UserQA -#import cfg import webbrowser import asyncio from langchain.callbacks import get_openai_callback +from utils import load_config, structure_data, generate_csv, load_all_data, combine_data +import yaml + +from dotenv import load_dotenv + +# Load environment variables from a .env file +load_dotenv() + -#TOP_K = cfg.retriever_top_k TOP_K = 20 def main(): parser = argparse.ArgumentParser() parser.add_argument("--pdf_path", type=str, default=None) parser.add_argument("--pdf_url", type=str, default=None) - parser.add_argument("--basic_info_dir", type=str, default='data/basic_info') parser.add_argument("--llm_name", type=str, default='gpt-3.5-turbo') - parser.add_argument("--answers_dir", type=str, default='data/answers') - parser.add_argument("--assessment_dir", type=str, default='data/assessment') - parser.add_argument("--vector_db_dir", type=str, default='data/vector_db') - parser.add_argument("--retrieved_chunks_dir", type=str, default='data/retrieved_chunks') - parser.add_argument("--user_qa_dir", type=str, default='data/user_qa') parser.add_argument("--user_question", type=str, default='') parser.add_argument("--answer_length", type=int, default=50) parser.add_argument("--detail", action='store_true', default=False) parser.add_argument("--top_k", type=int, default=20) + parser.add_argument("--question_set", type=str, default='default', help="Specify the question set to use") args = parser.parse_args() + with open(f'question_sets/{args.question_set}.yaml', 'r') as file: + question_set = yaml.safe_load(file) + + base_dir = f"data/{args.question_set}" + basic_info_dir = os.path.join(base_dir, 'basic_info') + answers_dir = os.path.join(base_dir, 'answers') + assessment_dir = os.path.join(base_dir, 'assessment') + vector_db_dir = os.path.join(base_dir, 'vector_db') + retrieved_chunks_dir = os.path.join(base_dir, 'retrieved_chunks') + user_qa_dir = os.path.join(base_dir, 'user_qa') + if args.pdf_path: report_name = os.path.basename(args.pdf_path) else: @@ -36,18 +48,18 @@ def main(): assert report_name.endswith('.pdf') report_name = report_name.replace('.pdf', '') - if not os.path.exists(args.basic_info_dir): - os.makedirs(args.basic_info_dir) - if not os.path.exists(args.answers_dir): - os.makedirs(args.answers_dir) - if not os.path.exists(args.assessment_dir): - os.makedirs(args.assessment_dir) - if not os.path.exists(args.vector_db_dir): - os.makedirs(args.vector_db_dir) - if not os.path.exists(args.retrieved_chunks_dir): - os.makedirs(args.retrieved_chunks_dir) - if not os.path.exists(args.user_qa_dir): - os.makedirs(args.user_qa_dir) + if not os.path.exists(basic_info_dir): + os.makedirs(basic_info_dir) + if not os.path.exists(answers_dir): + os.makedirs(answers_dir) + if not os.path.exists(assessment_dir): + os.makedirs(assessment_dir) + if not os.path.exists(vector_db_dir): + os.makedirs(vector_db_dir) + if not os.path.exists(retrieved_chunks_dir): + os.makedirs(retrieved_chunks_dir) + if not os.path.exists(user_qa_dir): + os.makedirs(user_qa_dir) destination_folder = "data/pdf/" if not os.path.exists(destination_folder): os.makedirs(destination_folder) @@ -56,14 +68,14 @@ def main(): path=args.pdf_path, url=args.pdf_url, store_path=os.path.join(destination_folder, report_name + '.pdf'), - db_path=os.path.join(args.vector_db_dir, report_name), - retrieved_chunks_path=os.path.join(args.retrieved_chunks_dir, report_name) + db_path=os.path.join(vector_db_dir, report_name), + retrieved_chunks_path=os.path.join(retrieved_chunks_dir, report_name), + question_set= question_set ) - if args.user_question == '': + if True: try: - reader = Reader(llm_name=args.llm_name, answer_length=str(args.answer_length),) - # qa_prompt="tcfd_summary_source", answer_key_name='SUMMARY', q_name='Q', a_name='Summary') + reader = Reader(llm_name=args.llm_name, answer_length=str(args.answer_length), question_set=question_set) result_qa = asyncio.run(reader.qa_with_chat(report_list=[report])) result_analysis = asyncio.run(reader.analyze_with_chat(report_list=[report])) except Exception as e: @@ -76,11 +88,10 @@ def main(): path=os.path.join(destination_folder, args.pdf_path.split('/')[-1]), store_path=None, top_k=TOP_K - 5, - db_path=os.path.join(args.vector_db_dir, report_name), - retrieved_chunks_path=os.path.join(args.retrieved_chunks_dir, report_name), + db_path=os.path.join(vector_db_dir, report_name), + retrieved_chunks_path=os.path.join(retrieved_chunks_dir, report_name) ) - reader = Reader(llm_name=args.llm_name, answer_length=str(args.answer_length),) - #qa_prompt="tcfd_summary_source", answer_key_name='SUMMARY', q_name='Q', a_name='Summary') + reader = Reader(llm_name=args.llm_name, answer_length=str(args.answer_length)) result_qa = asyncio.run(reader.qa_with_chat(report_list=[report])) result_analysis = asyncio.run(reader.analyze_with_chat(report_list=[report])) @@ -90,23 +101,34 @@ def main(): f.write(result_qa[0]) with open(html_path_analysis, 'w') as f: f.write(result_analysis[0]) - # webbrowser.open(html_path) - with open(os.path.join(args.basic_info_dir, report_name + '_' + args.llm_name + '.json'), 'w') as f: + with open(os.path.join(basic_info_dir, report_name + '_' + args.llm_name + '.json'), 'w') as f: json.dump(reader.basic_info_answers[0], f) - with open(os.path.join(args.answers_dir, report_name + '_' + args.llm_name + '.json'), 'w') as f: + with open(os.path.join(answers_dir, report_name + '_' + args.llm_name + '.json'), 'w') as f: json.dump(reader.answers[0], f) - with open(os.path.join(args.assessment_dir, report_name + '_' + args.llm_name + '.json'), 'w') as f: + with open(os.path.join(assessment_dir, report_name + '_' + args.llm_name + '.json'), 'w') as f: json.dump(reader.assessment_results[0], f) + # Load all data + all_answers, all_assessments, retrieved_chunks = load_all_data(answers_dir, assessment_dir, retrieved_chunks_dir) + + # Combine all data + combined_answers, combined_assessments = combine_data(all_answers, all_assessments) + + # Structure data for CSV + structured_data = structure_data(report_name, combined_answers, combined_assessments, retrieved_chunks) + + # Generate or update CSV file + csv_output_path = os.path.join(base_dir, f"{args.question_set}_answers_assessments.csv") + generate_csv(structured_data, csv_output_path) else: qa = UserQA(llm_name=args.llm_name) answer, _ = qa.user_qa( args.user_question, report, - basic_info_path=os.path.join(args.basic_info_dir, report_name + '_' + args.llm_name + '.json'), + basic_info_path=os.path.join(basic_info_dir, report_name + '_' + args.llm_name + '.json'), answer_length=args.answer_length, ) print(answer) - with open(os.path.join(args.user_qa_dir, report_name + '_' + args.llm_name + '.jsonl'), 'a') as f: + with open(os.path.join(user_qa_dir, report_name + '_' + args.llm_name + '.jsonl'), 'a') as f: qa_json = json.dumps(answer) f.write(qa_json + '\n') @@ -114,4 +136,4 @@ def main(): if __name__ == '__main__': with get_openai_callback() as cb: main() - print(cb) + print(cb) \ No newline at end of file diff --git a/code/cfg.py b/code/cfg.py index 62b40b2..d5e38ed 100644 --- a/code/cfg.py +++ b/code/cfg.py @@ -1,3 +1,10 @@ +from dotenv import load_dotenv +import os + +load_dotenv() +api_key = os.getenv('API_KEY') + + # key: topic, value: list of search key # temperature for generation temperature = 0. diff --git a/code/config.py b/code/config.py new file mode 100644 index 0000000..9a73c1b --- /dev/null +++ b/code/config.py @@ -0,0 +1,21 @@ +import yaml + +class Config: + _instance = None + + def __new__(cls, file_path=None): + if cls._instance is None: + cls._instance = super(Config, cls).__new__(cls) + cls._instance.config = {} # Initialize config attribute + if file_path: + cls._instance.load_config(file_path) + return cls._instance + + def load_config(self, file_path): + import pdb; pdb.set_trace() + + with open(file_path, 'r') as file: + self.config = yaml.safe_load(file) + + def get_config(self): + return self.config \ No newline at end of file diff --git a/code/document.py b/code/document.py index 5d80b65..000fbec 100644 --- a/code/document.py +++ b/code/document.py @@ -14,46 +14,34 @@ from langchain.embeddings.openai import OpenAIEmbeddings import time import requests -import configparser import os -config = configparser.ConfigParser() -config.read('apikey.ini') -chat_api_list = config.get('OpenAI', 'OPENAI_API_KEYS')[1:-1].replace('\'', '').split(',') -os.environ["OPENAI_API_KEY"] = chat_api_list[0] +from dotenv import load_dotenv +from config import Config + +# Load environment variables from a .env file +load_dotenv() + + TOP_K = 20 CHUNK_SIZE = 500 CHUNK_OVERLAP = 20 COMPRESSION = False -QUERIES = { - 'general': ["What is the company of the report?", "What sector does the company belong to?", "Where is the company located?", - #"What climate-related issues are discussed in this report?" - ], - 'tcfd_1': "How does the company's board oversee climate-related risks and opportunities?", - 'tcfd_2': "What is the role of management in assessing and managing climate-related risks and opportunities?", - 'tcfd_3': "What are the most relevant climate-related risks and opportunities that the organisation has identified over the short, medium, and long term? Are risks clearly associated with a horizon?", - 'tcfd_4': "How do climate-related risks and opportunities impact the organisation's businesses strategy, economic and financial performance, and financial planning?", - 'tcfd_5': "How resilient is the organisation's strategy when considering different climate-related scenarios, including a 2°C target or lower scenario? How resilient is the organisation's strategy when considering climate physical risks?", - 'tcfd_6': "What processes does the organisation use to identify and assess climate-related risks?", - 'tcfd_7': "How does the organisation manage climate-related risks?", - 'tcfd_8': "How are the processes for identifying, assessing, and managing climate-related risks integrated into the organisation's overall risk management?", - 'tcfd_9': "What metrics does the organisation use to assess climate-related risks and opportunities? How do the metrics help ensure that the performance is in line with its strategy and risk management process?", - 'tcfd_10': "Does the organisation disclose its Scope 1, Scope 2, and, if appropriate, Scope 3 greenhouse gas (GHG) emissions? What are the related risks and do they differ depending on the scope?", - 'tcfd_11': "What targets does the organisation use to understand/quantify/benchmark climate-related risks and opportunities? How is the organization performing against these targets?", -} class Report: - def __init__(self, path=None, url=None, title='', abs='', authers=[], store_path=None, top_k=TOP_K, db_path=None, retrieved_chunks_path=None): + def __init__(self, path=None, url=None, title='', abs='', authers=[], store_path=None, top_k=TOP_K, db_path=None, retrieved_chunks_path=None, question_set={}): # Init the class on pdf with given path + config = Config().get_config() + self.chunks = [] self.page_idx = [] self.path = path # pdf path self.url = url # pdf url assert ((path is None and url is not None) or (path is not None and url is None)) # only need to pass in an url or a path self.store_path = store_path - self.queries = QUERIES + self.queries = question_set['queries'] self.top_k = top_k # retriever top-k self.compression = COMPRESSION self.section_names = [] # title @@ -176,8 +164,8 @@ def get_image_path(self, image_path=''): # recongnize the title by fontsize def get_chapter_names(self, ): # # open pdf - doc = fitz.open(self.path) - text_list = [page.get_text() for page in doc] + with fitz.open(self.path) as doc: + text_list = [page.get_text() for page in doc] all_text = '' for text in text_list: all_text += text @@ -197,44 +185,44 @@ def get_chapter_names(self, ): return chapter_names def get_title(self): - doc = self.pdf - max_font_size = 0 # init fontsize 0 - max_string = "" # init max string - max_font_sizes = [0] - for page in doc: # go through all pages - text = page.get_text("dict") # acquire the info in the page - blocks = text["blocks"] # acquire text block - for block in blocks: # go through all blocks - if block["type"] == 0 and len(block['lines']): # if str - if len(block["lines"][0]["spans"]): - font_size = block["lines"][0]["spans"][0]["size"] # acquire fontsize - max_font_sizes.append(font_size) - if font_size > max_font_size: - max_font_size = font_size # update max fontsize - max_string = block["lines"][0]["spans"][0]["text"] # update the title str - max_font_sizes.sort() - print("max_font_sizes", max_font_sizes[-10:]) - cur_title = '' - for page in doc: # go through all pages - text = page.get_text("dict") - blocks = text["blocks"] - for block in blocks: - if block["type"] == 0 and len(block['lines']): - if len(block["lines"][0]["spans"]): - cur_string = block["lines"][0]["spans"][0]["text"] - font_flags = block["lines"][0]["spans"][0]["flags"] - font_size = block["lines"][0]["spans"][0]["size"] - # print(font_size) - if abs(font_size - max_font_sizes[-1]) < 0.3 or abs(font_size - max_font_sizes[-2]) < 0.3: - # print("The string is bold.", max_string, "font_size:", font_size, "font_flags:", font_flags) - if len(cur_string) > 4: + with fitz.open(self.path) as doc: + max_font_size = 0 # init fontsize 0 + max_string = "" # init max string + max_font_sizes = [0] + for page in doc: # go through all pages + text = page.get_text("dict") # acquire the info in the page + blocks = text["blocks"] # acquire text block + for block in blocks: # go through all blocks + if block["type"] == 0 and len(block['lines']): # if str + if len(block["lines"][0]["spans"]): + font_size = block["lines"][0]["spans"][0]["size"] # acquire fontsize + max_font_sizes.append(font_size) + if font_size > max_font_size: + max_font_size = font_size # update max fontsize + max_string = block["lines"][0]["spans"][0]["text"] # update the title str + max_font_sizes.sort() + print("max_font_sizes", max_font_sizes[-10:]) + cur_title = '' + for page in doc: # go through all pages + text = page.get_text("dict") + blocks = text["blocks"] + for block in blocks: + if block["type"] == 0 and len(block['lines']): + if len(block["lines"][0]["spans"]): + cur_string = block["lines"][0]["spans"][0]["text"] + font_flags = block["lines"][0]["spans"][0]["flags"] + font_size = block["lines"][0]["spans"][0]["size"] + # print(font_size) + if abs(font_size - max_font_sizes[-1]) < 0.3 or abs(font_size - max_font_sizes[-2]) < 0.3: # print("The string is bold.", max_string, "font_size:", font_size, "font_flags:", font_flags) - if cur_title == '': - cur_title += cur_string - else: - cur_title += ' ' + cur_string - # break - title = cur_title.replace('\n', ' ') + if len(cur_string) > 4: + # print("The string is bold.", max_string, "font_size:", font_size, "font_flags:", font_flags) + if cur_title == '': + cur_title += cur_string + else: + cur_title += ' ' + cur_string + # break + title = cur_title.replace('\n', ' ') return title # def _get_all_page_index(self): @@ -286,7 +274,7 @@ def _get_retriever(self, db_path): self.page_idx.extend([i + 1] * len(page_chunks)) self.chunks.extend(page_chunks) if os.path.exists(db_path): - doc_search = FAISS.load_local(db_path, embeddings=embeddings) + doc_search = FAISS.load_local(db_path, embeddings=embeddings, allow_dangerous_deserialization=True) else: doc_search = FAISS.from_texts(self.chunks, embeddings, metadatas=[{"source": str(i), "page": str(page_idx)} for i, page_idx in @@ -315,4 +303,3 @@ def search_page(content, search_list): return True else: return False - diff --git a/code/reader.py b/code/reader.py index e673835..6744509 100644 --- a/code/reader.py +++ b/code/reader.py @@ -1,9 +1,7 @@ import os import re import tenacity -import configparser import markdown - from langchain.llms import OpenAI from langchain.chat_models import ChatOpenAI from langchain.schema import ( @@ -15,286 +13,26 @@ import cfg import json import tiktoken -# main class for reading the pdf and communicate with openai - - -config = configparser.ConfigParser() -config.read('apikey.ini') -chat_api_list = config.get('OpenAI', 'OPENAI_API_KEYS')[1:-1].replace('\'', '').split(',') -os.environ["OPENAI_API_KEY"] = chat_api_list[0] - -TOP_K = 20 -PROMPTS = { - 'general': - """You are tasked with the role of a climate scientist, assigned to analyze a company's sustainability report. Based on the following extracted parts from the sustainability report, answer the given QUESTIONS. -If you don't know the answer, just say that you don't know. Don't try to make up an answer. -Format your answers in JSON format with the following keys: COMPANY_NAME, COMPANY_SECTOR, and COMPANY_LOCATION. - -QUESTIONS: -1. What is the company of the report? -2. What sector does the company belong to? -3. Where is the company located? - -========= -{context} -========= -Your FINAL_ANSWER in JSON (ensure there's no format error): -""", - 'tcfd_qa_source': """As a senior equity analyst with expertise in climate science evaluating a company's sustainability report, you are presented with the following background information: - -{basic_info} - -With the above information and the following extracted components (which may have incomplete sentences at the beginnings and the ends) of the sustainability report at hand, please respond to the posed question, ensuring to reference the relevant parts ("SOURCES"). -Format your answer in JSON format with the two keys: ANSWER (this should contain your answer string without sources), and SOURCES (this should be a list of the source numbers that were referenced in your answer). - -QUESTION: {question} -========= -{summaries} -========= - -Please adhere to the following guidelines in your answer: -1. Your response must be precise, thorough, and grounded on specific extracts from the report to verify its authenticity. -2. If you are unsure, simply acknowledge the lack of knowledge, rather than fabricating an answer. -3. Keep your ANSWER within {answer_length} words. -4. Be skeptical to the information disclosed in the report as there might be greenwashing (exaggerating the firm's environmental responsibility). Always answer in a critical tone. -5. cheap talks are statements that are costless to make and may not necessarily reflect the true intentions or future actions of the company. Be critical for all cheap talks you discovered in the report. -6. Always acknowledge that the information provided is representing the company's view based on its report. -7. Scrutinize whether the report is grounded in quantifiable, concrete data or vague, unverifiable statements, and communicate your findings. -{guidelines} - -Your FINAL_ANSWER in JSON (ensure there's no format error): -""", - 'user_qa_source': """As a senior equity analyst with expertise in climate science evaluating a company's sustainability report, you are presented with the following background information: - -{basic_info} - -With the above information and the following extracted components (which may have incomplete sentences at the beginnings and the ends) of the sustainability report at hand, please respond to the posed question, ensuring to reference the relevant parts ("SOURCES"). -Format your answer in JSON format with the two keys: ANSWER (this should contain your answer string without sources), and SOURCES (this should be a list of the source numbers that were referenced in your answer). - -QUESTION: {question} -========= -{summaries} -========= - -Please adhere to the following guidelines in your answer: -1. Your response must be precise, thorough, and grounded on specific extracts from the report to verify its authenticity. -2. If certain information is unclear or unavailable, admit the lack of knowledge rather than devising an answer. -3. Answer the question strictly based on the provided extracts. If the information available is insufficient, clearly state that the question cannot be answered based on the given report. -4. Keep your ANSWER within {answer_length} words. -5. Be skeptical to the information disclosed in the report as there might be greenwashing (exaggerating the firm's environmental responsibility). Always answer in a critical tone. -6. cheap talks are statements that are costless to make and may not necessarily reflect the true intentions or future actions of the company. Be critical for all cheap talks you discovered in the report. -7. Always acknowledge that the information provided is representing the company's view based on its report. -8. Scrutinize whether the report is grounded in quantifiable, concrete data or vague, unverifiable statements, and communicate your findings. - -Your FINAL_ANSWER in JSON (ensure there's no format error): -""", - 'tcfd_summary_source': """Your task is to analyze and summarize any disclosures related to the following in a company's sustainability report: - -: {question} - -Provided below is some basic information about the company under evaluation: - -{basic_info} - -In addition to the above, the following extracted sections of the sustainability report have been made available to you for review: -{summaries} +from config import Config +from dotenv import load_dotenv -Your task is to summarize the company's disclosure of the aforementioned , based on the information presented in these extracts. Please adhere to the following guidelines in your summary: -1. If the is disclosed in the report, try to summarize by direct extractions from the report. Reference the source of this information from the provided extracts to confirm its credibility. -2. If the is not addressed in the report, state this clearly without attempting to extrapolate or manufacture information. -3. Keep your SUMMARY within {answer_length} words. -4. Be skeptical to the information disclosed in the report as there might be greenwashing (exaggerating the firm's environmental responsibility). Always answer in a critical tone. -5. cheap talks are statements that are costless to make and may not necessarily reflect the true intentions or future actions of the company. Be critical for all cheap talks you discovered in the report. -6. Always acknowledge that the information provided is representing the company's view based on its report. -7. Scrutinize whether the report is grounded in quantifiable, concrete data or vague, unverifiable statements, and communicate your findings. -{guidelines} +# Load environment variables from a .env file +load_dotenv() -Your summarization should be formatted in JSON with two keys: -1. SUMMARY: This should contain your summary without source references. -2. SOURCES: This should be a list of the source numbers that were referenced in your summary. - -Your FINAL_ANSWER in JSON (ensure there's no format error): -""", - 'tcfd_qa': """As a senior equity analyst with expertise in climate science evaluating a company's sustainability report, you are presented with the following essential information about the report: - -{basic_info} - -With the above information and the following extracted components (which may have incomplete sentences at the beginnings and the ends) of the sustainability report at hand, please respond to the posed question. -Your answer should be precise, comprehensive, and substantiated by direct extractions from the report to establish its credibility. -If you don't know the answer, just say that you don't know. Don't try to make up an answer. - -QUESTION: {question} -========= -{summaries} -========= -""", - 'tcfd_assessment': """Your task is to rate a sustainability report's disclosure quality on the following : - -: {question} - -These are the that outline the necessary components for high-quality disclosure pertaining to the : - -: -==== -{requirements} -==== - -Presented below are select excerpts from the sustainability report, which pertain to the : - -: -==== -{disclosure} -==== - -Please analyze the extent to which the given satisfies the aforementioned . Your ANALYSIS should specify which have been met and which ones have not been satisfied. -Your response should be formatted in JSON with two keys: -1. ANALYSIS: A paragraph of analysis (be in a string format). No longer than 150 words. -2. SCORE: An integer score from 0 to 100. A score of 0 indicates that most of the have not been met or are insufficiently detailed. In contrast, a score of 100 suggests that the majority of the have been met and are accompanied by specific details. +# main class for reading the pdf and communicate with openai -Your FINAL_ANSWER in JSON (ensure there's no format error): -""", - 'scoring': """Your task is to rate the disclosure quality of a sustainability report. You'll be provided with a that contains {question_number} (DISCLOSURE_REQUIREMENT, DISCLOSURE_CONTENT) pairs. DICLOSURE_REQUIREMENT corresponds to a key piece of information that the report should disclose. DISCLOSURE_CONTENT summarizes the report's disclosed information on that topic. -For each pair, you should assign a score reflecting the depth and comprehensiveness of the disclosed information. A score of 1 denotes a detailed and comprehensive disclosure. A score of 0.5 suggests that the disclosed information is lacking in detail. A score of 0 indicates that the requested information is either not disclosed or is disclosed without any detail. -Please format your response in a JSON structure, with the keys 'COMMENT' (providing your overall assessment of the report's quality) and 'SCORES' (a list containing the {question_number} scores corresponding to each question-and-answer pair). +TOP_K = 20 -: -==== -{summaries} -==== -Your FINAL_ANSWER in JSON (ensure there's no format error): -""", - 'to_question': """Examine the following statement and transform it into a question, suitable for a ChatGPT prompt, if it is not already phrased as one. If the statement is already a question, return it as it is. -Statement: {statement}""" -# 'scoring': """Your role is that of a climate scientist rating the disclosure quality of a sustainability report. You'll be provided with a that contains {question_number} question-and-answer pairs. Each pair corresponds to a key piece of information that the report should disclose, with the answer summarizing the report's disclosed information on that topic. Your responsibility is to assess the quality of these disclosures. -# For each question-and-answer pair, assign a score based on the question-anwering quality and the disclosure detailedness and comprehensiveness. If the question is thoroughly answered and the disclosed information is thoroughly detailed, assign a score of 1. If the question is only partially answered or the dsclosed information lacks substantial detail, assign a score of 0.5. If the information asked by the question is either not disclosed or disclosed without any detail, assign a score of 0. -# Please format your response in a JSON structure, with the keys 'COMMENT' (providing your overall assessment of the report's quality) and 'SCORES' (a list containing the {question_number} scores corresponding to each question-and-answer pair). -# : -# --- -# {summaries} -# --- -# FINAL_ANSWER in JSON (ensure there's no format error): -# """, -} +import os +import csv -QUERIES = { - 'general': ["What is the company of the report?", "What sector does the company belong to?", "Where is the company located?", - #"What climate-related issues are discussed in this report?" - ], - 'tcfd_1': "How does the company's board oversee climate-related risks and opportunities?", - 'tcfd_2': "What is the role of management in assessing and managing climate-related risks and opportunities?", - 'tcfd_3': "What are the most relevant climate-related risks and opportunities that the organisation has identified over the short, medium, and long term? Are risks clearly associated with a horizon?", - 'tcfd_4': "How do climate-related risks and opportunities impact the organisation's businesses strategy, economic and financial performance, and financial planning?", - 'tcfd_5': "How resilient is the organisation's strategy when considering different climate-related scenarios, including a 2°C target or lower scenario? How resilient is the organisation's strategy when considering climate physical risks?", - 'tcfd_6': "What processes does the organisation use to identify and assess climate-related risks?", - 'tcfd_7': "How does the organisation manage climate-related risks?", - 'tcfd_8': "How are the processes for identifying, assessing, and managing climate-related risks integrated into the organisation's overall risk management?", - 'tcfd_9': "What metrics does the organisation use to assess climate-related risks and opportunities? How do the metrics help ensure that the performance is in line with its strategy and risk management process?", - 'tcfd_10': "Does the organisation disclose its Scope 1, Scope 2, and, if appropriate, Scope 3 greenhouse gas (GHG) emissions? What are the related risks and do they differ depending on the scope?", - 'tcfd_11': "What targets does the organisation use to understand/quantify/benchmark climate-related risks and opportunities? How is the organization performing against these targets?", -} -TCFD_ASSESSMENT = { - 'tcfd_1': """In describing the board's oversight of climate-related issues, organizations should consider including a discussion of the following: -1. processes and frequency by which the board and/or board committees (e.g., audit, risk, or other committees) are informed about climate-related issues; -2. whether the board and/or board committees consider climate-related issues when reviewing and guiding strategy, major plans of action, risk management policies, annual budgets, and business plans as well as setting the organization’s performance objectives, monitoring implementation and performance, and overseeing major capital expenditures, acquisitions, and divestitures; and -3. how the board monitors and oversees progress against goals and targets for addressing climate-related issues. -""", - 'tcfd_2': """In describing management's role related to the assessment and management of climate-related issues, organizations should consider including the following information: -1. whether the organization has assigned climate-related responsibilities to management-level positions or committees; and, if so, whether such management positions or committees report to the board or a committee of the board and whether those responsibilities include assessing and/or managing climate-related issues; -2. a description of the associated organizational structure(s); -3. processes by which management is informed about climate-related issues; and -4. how management (through specific positions and/or management committees) monitors climate-related issues. -""", - 'tcfd_3': """In describing the climate-related risks and opportunities the organization has identified over the short, medium, and long term, organizations should provide the following information: -1. a description of what they consider to be the relevant short-, medium-, and long-term time horizons, taking into consideration the useful life of the organization's assets or infrastructure and the fact that climate-related issues often manifest themselves over the medium and longer terms; -2. a description of the specific climate-related issues potentially arising in each time horizon (short, medium, and long term) that could have a material financial impact on the organization; and -3. a description of the process(es) used to determine which risks and opportunities could have a material financial impact on the organization. -Organizations should consider providing a description of their risks and opportunities by sector and/or geography, as appropriate. -""", - 'tcfd_4': """In describing impact of climate-related risks and opportunities on the organization's businesses, strategy, and financial planning, organizations should discuss how identified climate-related issues have affected their businesses, strategy, and financial planning. -Organizations should consider including the impact on their businesses, strategy, and financial planning in the following areas: -1. Products and services -2. Supply chain and/or value chain -3. Adaptation and mitigation activities -4. Investment in research and development -5. Operations (including types of operations and location of facilities) -6. Acquisitions or divestments -7. Access to capital -Organizations should describe how climate-related issues serve as an input to their financial planning process, the time period(s) used, and how these risks and opportunities are prioritized. Organizations' disclosures should reflect a holistic picture of the interdependencies among the factors that affect their ability to create value over time. -Organizations should describe the impact of climate-related issues on their financial performance (e.g., revenues, costs) and financial position (e.g., assets, liabilities). If climate-related scenarios were used to inform the organization's strategy and financial planning, such scenarios should be described. -Organizations that have made GHG emissions reduction commitments, operate in jurisdictions that have made such commitments, or have agreed to meet investor expectations regarding GHG emissions reductions should describe their plans for transitioning to a low-carbon economy, which could include GHG emissions targets and specific activities intended to reduce GHG emissions in their operations and value chain or to otherwise support the transition. -""", - 'tcfd_5': """In describing the resilience of the organization's strategy, organizations should describe how resilient their strategies are to climate-related risks and opportunities, taking into consideration a transition to a low-carbon economy consistent with a 2°C or lower scenario and, where relevant to the organization, scenarios consistent with increased physical climate-related risks. -Organizations should consider discussing: -1. where they believe their strategies may be affected by climate-related risks and opportunities; -2. how their strategies might change to address such potential risks and opportunities; -3. the potential impact of climate-related issues on financial performance (e.g., revenues, costs) and financial position (e.g., assets, liabilities); and -4. the climate-related scenarios and associated time horizon(s) considered. -""", - 'tcfd_6': """In describing the organization's processes for identifying and assessing climate-related risks, organizations should describe their risk management processes for identifying and assessing climate-related risks. An important aspect of this description is how organizations determine the relative significance of climate-related risks in relation to other risks. -Organizations should describe whether they consider existing and emerging regulatory requirements related to climate change (e.g., limits on emissions) as well as other relevant factors considered. -Organizations should also consider disclosing the following: -1. processes for assessing the potential size and scope of identified climate-related -risks and -2. definitions of risk terminology used or references to existing risk classification -frameworks used. -""", - 'tcfd_7': """In describing the organization's processes for managing climate-related risks, organizations should describe their processes for managing climate-related risks, including how they make decisions to mitigate, transfer, accept, or control those risks. In addition, organizations should describe their processes for prioritizing climate-related risks, including how materiality determinations are made within their organizations. -""", - 'tcfd_8': """In describing how processes for identifying, assessing, and managing climate-related risks are integrated into the organization's overall risk management, organizations should describe how their processes for identifying, assessing, and managing climate-related risks are integrated into their overall risk management. -""", - 'tcfd_9': """In describing the metrics used by the organization to assess climate-related risks and opportunities in line with its strategy and risk management process, organizations should provide the key metrics used to measure and manage climate-related risks and opportunities, as well as metrics consistent with the cross-industry. -Organizations should consider including metrics on climate-related risks associated with water, energy, land use, and waste management where relevant and applicable. -Where climate-related issues are material, organizations should consider describing whether and how related performance metrics are incorporated into remuneration policies. -Where relevant, organizations should provide their internal carbon prices as well as climate-related opportunity metrics such as revenue from products and services designed for a low-carbon economy. -Metrics should be provided for historical periods to allow for trend analysis. Where appropriate, organizations should consider providing forward-looking metrics for the cross-industry, consistent with their business or strategic planning time horizons. In addition, where not apparent, organizations should provide a description of the methodologies used to calculate or estimate climate-related metrics. -""", - 'tcfd_10': """In disclosing Scope 1, Scope 2, and, if appropriate, Scope 3 greenhouse gas (GHG) emissions, and the related risks, organizations should provide their Scope 1 and Scope 2 GHG emissions independent of a materiality assessment, and, if appropriate, Scope 3 GHG emissions and the related risks. All organizations should consider disclosing Scope 3 GHG emissions. -GHG emissions should be calculated in line with the GHG Protocol methodology to allow for aggregation and comparability across organizations and jurisdictions. As appropriate, organizations should consider providing related, generally accepted industry-specific GHG efficiency ratios. -GHG emissions and associated metrics should be provided for historical periods to allow for trend analysis. In addition, where not apparent, organizations should provide a description of the methodologies used to calculate or estimate the metrics. -""", - 'tcfd_11': """In describing the targets used by the organization to manage climate-related risks and opportunities and performance against targets, organizations should describe their key climate-related targets such as those related to GHG emissions, water usage, energy usage, etc., in line with the cross-industry, where relevant, and in line with anticipated regulatory requirements or market constraints or other goals. Other goals may include efficiency or financial goals, financial loss tolerances, avoided GHG emissions through the entire product life cycle, or net revenue goals for products and services designed for a low-carbon economy. -In describing their targets, organizations should consider including the following: -1. whether the target is absolute or intensity based; -2. time frames over which the target applies; -3. base year from which progress is measured; and -4. key performance indicators used to assess progress against targets. -Organizations disclosing medium-term or long-term targets should also disclose associated interim targets in aggregate or by business line, where available. -Where not apparent, organizations should provide a description of the methodologies used to calculate targets and measures. -""", -} -TCFD_GUIDELINES = { - 'tcfd_1': """8. Please concentrate on the board's direct responsibilities and actions pertaining to climate issues, without discussing the company-wide risk management system or other topics. -""", - 'tcfd_2': """8. Please focus on their direct duties related to climate issues, without introducing other topics such as the broader corporate risk management system. -""", - 'tcfd_3': """8. Avoid discussing the company-wide risk management system or how these risks and opportunities are identified and managed. -""", - 'tcfd_4': """8. Please do not include the process of risk identification, assessment or management in your answer. -""", - 'tcfd_5': """8. In your response, focus solely on the resilience of strategy in these scenarios, and refrain from discussing processes of risk identification, assessment, or management strategies. -""", - 'tcfd_6': """8. Restrict your answer to the identification and assessment processes, without discussing the management or integration of these risks. -""", - 'tcfd_7': """8. Please focus on the concrete actions and strategies implemented to manage these risks, excluding the process of risk identification or assessment. -""", - 'tcfd_8': """8. Please focus on the integration aspect and avoid discussing the process of risk identification, assessment, or the specific management actions taken. -""", - 'tcfd_9': """8. Do not include information regarding the organization's general risk identification and assessment methods or their broader corporate strategy and initiatives. -""", - 'tcfd_10': """8. Confirm whether the organisation discloses its Scope 1, Scope 2, and, if appropriate, Scope 3 greenhouse gas (GHG) emissions. If so, provide any available data or specific figures on these emissions. Additionally, identify the related risks. The risks should be specific to the GHG emissions rather than general climate-related risks. -""", - 'tcfd_11': """8. Please detail the precise targets and avoid discussing the company's general risk identification and assessment methods or their commitment to disclosure through the TCFD. -""", -} -SYSTEM_PROMPT = "You are an AI assistant in the role of a Senior Equity Analyst with expertise in climate science that analyzes companys' sustainability reports." def remove_brackets(string): return re.sub(r'\([^)]*\)', '', string).strip() - def _docs_to_string(docs, num_docs=TOP_K, with_source=True): output = "" docs = docs[:num_docs] @@ -305,7 +43,6 @@ def _docs_to_string(docs, num_docs=TOP_K, with_source=True): output += "\n---\n" return output - def _find_answer(string, name="ANSWER"): for l in string.split('\n'): if name in l: @@ -314,19 +51,16 @@ def _find_answer(string, name="ANSWER"): return l[start:end] return string - def _find_sources(string): pattern = r'\d+' numbers = [int(n) for n in re.findall(pattern, string)] return numbers - def _find_float_numbers(string): pattern = r"[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?" float_numbers = [float(n) for n in re.findall(pattern, string)] return float_numbers - def _find_score(string): for l in string.split('\n'): if "SCORE" in l: @@ -334,29 +68,17 @@ def _find_score(string): break return d[0] - class Reader: def __init__(self, llm_name='gpt-3.5-turbo', answer_key_name='ANSWER', max_token=512, q_name='Q', a_name='A', - queries=QUERIES, qa_prompt='tcfd_qa_source', guidelines=TCFD_GUIDELINES, - assessments=TCFD_ASSESSMENT, - answer_length='60', - root_path='./', - gitee_key='', - user_name='defualt', language='en'): + answer_length='60', root_path='./', gitee_key='', user_name='default', language='en', question_set=None): self.user_name = user_name # user name self.language = language self.root_path = root_path self.max_token = max_token self.llm_name = llm_name - # self.tiktoken_encoder = tiktoken.encoding_for_model(self.llm_name) self.cur_api = 0 self.file_format = 'md' # or 'txt' - self.prompts = PROMPTS - self.assessments = assessments - self.queries = queries - self.guidelines = guidelines - self.qa_prompt = qa_prompt self.answer_key_name = answer_key_name self.q_name = q_name self.a_name = a_name @@ -366,21 +88,21 @@ def __init__(self, llm_name='gpt-3.5-turbo', answer_key_name='ANSWER', max_token self.assessment_results = [] self.user_questions = [] self.user_answers = [] - # self.save_image = False - # if self.save_image: - # self.gitee_key = self.config.get('Gitee', 'api') - # else: - # self.gitee_key = '' + self.question_set = question_set + self.qa_prompt = 'tcfd_qa_source' + self.prompts = cfg.prompts + self.system_prompt = cfg.system_prompt + self.assessments = question_set['assessments'] + self.queries = question_set['questions'] + self.guidelines = question_set['guidelines'] async def qa_with_chat(self, report_list): htmls = [] for report_index, report in enumerate(report_list): basic_info_prompt = PromptTemplate(template=self.prompts['general'], input_variables=["context"]) if "turbo" in self.llm_name: - # title = "Title: " + report.title + '\n' - # first_page = "First Page: " + report.pdf[0].get_text() + '\n' message = [ - SystemMessage(content=SYSTEM_PROMPT), + SystemMessage(content=self.system_prompt), HumanMessage(content=basic_info_prompt.format( context=_docs_to_string(report.section_text_dict['general'], with_source=False))) ] @@ -422,7 +144,7 @@ async def qa_with_chat(self, report_list): current_prompt = tcfd_prompt.format(basic_info=basic_info_string, summaries=_docs_to_string(report.section_text_dict[k], num_docs=num_docs), question=q, guidelines=self.guidelines[k], answer_length=self.answer_length) if "turbo" in self.llm_name: message = [ - SystemMessage(content=SYSTEM_PROMPT), + SystemMessage(content=self.system_prompt), HumanMessage(content=current_prompt) ] else: @@ -519,7 +241,7 @@ async def analyze_with_chat(self, report_list): with_source=False)) if "turbo" in self.llm_name: message = [ - SystemMessage(content=SYSTEM_PROMPT), + SystemMessage(content=self.system_prompt), HumanMessage(content=current_prompt) ] else: @@ -589,7 +311,4 @@ async def analyze_with_chat(self, report_list): all_scores = [float(s['SCORE']) for s in assessments.values()] htmls.append(markdown.markdown(questionnaire + '\n\n' + "Average score: {}".format(sum(all_scores) / 11))) - return htmls - - - + return htmls \ No newline at end of file diff --git a/code/user_qa.py b/code/user_qa.py index e7ba7ff..3c9fe8f 100644 --- a/code/user_qa.py +++ b/code/user_qa.py @@ -13,11 +13,13 @@ import cfg import json import tiktoken +from dotenv import load_dotenv + +# Load environment variables from a .env file +load_dotenv() config = configparser.ConfigParser() config.read('apikey.ini') -chat_api_list = config.get('OpenAI', 'OPENAI_API_KEYS')[1:-1].replace('\'', '').split(',') -os.environ["OPENAI_API_KEY"] = chat_api_list[0] TOP_K = cfg.retriever_top_k PROMPTS = cfg.prompts diff --git a/code/utils.py b/code/utils.py new file mode 100644 index 0000000..4c0d04e --- /dev/null +++ b/code/utils.py @@ -0,0 +1,101 @@ +import os +import json +import csv +import yaml + +def load_config(file_path): + with open(file_path, 'r') as file: + config = yaml.safe_load(file) + return config + +def structure_data(report_name, answers, assessments, retrieved_chunks): + data = [] + for question_id, answer in answers.items(): + assessment = assessments.get(question_id, {}) + relevant_chunks = retrieved_chunks.get(question_id, {}).values() + + data.append({ + "Report Name": report_name, + "Question ID": question_id, + "Question": answer.get("question", ""), + "Answer": answer.get("answer", ""), + "Sources": ", ".join(map(str, answer.get("sources", []))), + "Pages": answer.get("pages", ""), # Ensure this key exists in your JSON + "Assessment": assessment.get("analysis", ""), + "Score": assessment.get("score", ""), + "Retrieved Chunks": " | ".join(relevant_chunks) + }) + return data + +def load_json_data(directory): + data = {} + for filename in os.listdir(directory): + if filename.endswith(".json"): + with open(os.path.join(directory, filename), 'r') as file: + data[filename] = json.load(file) + return data + +def load_all_json_files(directory): + data = [] + for filename in os.listdir(directory): + if filename.endswith('.json'): + with open(os.path.join(directory, filename), 'r') as f: + data.append(json.load(f)) + return data + +def load_retrieved_chunks(directory): + retrieved_chunks = {} + for filename in os.listdir(directory): + if filename.endswith('.json'): + with open(os.path.join(directory, filename), 'r') as f: + retrieved_chunks.update(json.load(f)) + return retrieved_chunks + +def load_all_data(answers_dir, assessment_dir, retrieved_chunks_dir): + all_answers = load_all_json_files(answers_dir) + all_assessments = load_all_json_files(assessment_dir) + retrieved_chunks = load_retrieved_chunks(retrieved_chunks_dir) + return all_answers, all_assessments, retrieved_chunks + +def combine_data(all_answers, all_assessments): + combined_answers = {k: v for d in all_answers for k, v in d.items()} + combined_assessments = {k: v for d in all_assessments for k, v in d.items()} + return combined_answers, combined_assessments + +def generate_csv(data, output_path): + keys = data[0].keys() if data else [] + with open(output_path, 'w', newline='') as output_file: + dict_writer = csv.DictWriter(output_file, fieldnames=keys) + dict_writer.writeheader() + dict_writer.writerows(data) + +def process_and_generate_csv(report_name, combined_answers, combined_assessments, retrieved_chunks, base_dir): + structured_data = structure_data(report_name, combined_answers, combined_assessments, retrieved_chunks) + question_set = os.path.basename(base_dir) + csv_output_path = os.path.join(base_dir, f"{question_set}_answers_assessments.csv") + generate_csv(structured_data, csv_output_path) + +def create_csv_from_json(data_dir, question_set, output_dir): + assessments_dir = os.path.join(data_dir, question_set, "assessment") + + # Load assessment data + assessments = load_json_data(assessments_dir) + + all_data = [["Report Name", "TCFD Key", "Question", "Analysis", "Score"]] # Initialize with header + for report_filename, assessment_data in assessments.items(): + report_name = os.path.splitext(report_filename)[0] # Extract report name without extension + for tcfd_key, tcfd_data in assessment_data.items(): + row = [ + report_name, + tcfd_key, + tcfd_key, # Assuming the TCFD key is the question + tcfd_data.get("ANALYSIS", ""), + tcfd_data.get("SCORE", "") + ] + all_data.append(row) + + # Write to CSV + output_csv_path = os.path.join(data_dir, question_set, output_dir, f"{question_set}_answers_assessments.csv") + with open(output_csv_path, 'w', newline='') as output_file: + writer = csv.writer(output_file) + writer.writerows(all_data) \ No newline at end of file diff --git a/code/web.py b/code/web.py new file mode 100644 index 0000000..7616b59 --- /dev/null +++ b/code/web.py @@ -0,0 +1,268 @@ +import streamlit as st +import os +import subprocess +import pandas as pd +import matplotlib.pyplot as plt +import numpy as np +from dotenv import load_dotenv +from utils import create_csv_from_json # Import the function + +# Load environment variables from a .env file +load_dotenv() + +# This code was authored by Christian Woerle from Climate+Tech. +# For more information, visit our website at https://www.climateandtech.com. +# You can also check out Christian's GitHub at https://github.com/suung. + +# Password protection +def check_password(): + def password_entered(): + if st.session_state["password"] == os.getenv("PASSWORD"): # Get password from environment variable + st.session_state["password_correct"] = True + else: + st.session_state["password_correct"] = False + + if "password_correct" not in st.session_state: + # First run, show input for password. + st.text_input("Password", type="password", on_change=password_entered, key="password") + st.stop() + elif not st.session_state["password_correct"]: + # Password not correct, show input + error. + st.text_input("Password", type="password", on_change=password_entered, key="password") + st.error("😕 Password incorrect") + st.stop() + else: + return True + +if check_password(): + # Define directories + input_dir = "input" + output_dir = "output" + data_dir = "data" + question_sets_dir = "question_sets" + + if not os.path.exists(input_dir): + os.makedirs(input_dir) + if not os.path.exists(output_dir): + os.makedirs(output_dir) + + # Load question sets + question_sets = [f.replace('.yaml', '') for f in os.listdir(question_sets_dir) if f.endswith('.yaml')] + # Streamlit app + st.title("Climate+Tech Sustainability Report Analytics App") + st.markdown("For more information, visit [Climate+Tech](https://www.climateandtech.com). For custom tool development, get in touch.") + st.markdown(""" + This tool is based on the scientific research by Jingwei Ni, Julia Bingler, Chiara Colesanti-Senni, and others. For more details, refer to their paper [here](https://papers.ssrn.com/sol3/papers.cfm?abstract_id=4476733). + """) + + # Dropdown for selecting question set + question_set = st.selectbox("Select a Question Set", question_sets) + + # Download button for the selected question set + if question_set: + question_set_path = os.path.join(question_sets_dir, f"{question_set}.yaml") + with open(question_set_path, "r") as file: + question_set_content = file.read() + st.download_button( + label="Download Question Set", + data=question_set_content, + file_name=f"{question_set}.yaml", + mime="text/yaml" + ) + with st.expander("Customize questionset"): + # File uploader for uploading a new question set + uploaded_question_set = st.file_uploader("Upload a Question Set", type=["yaml"]) + if uploaded_question_set is not None: + uploaded_question_set_path = os.path.join(question_sets_dir, uploaded_question_set.name) + with open(uploaded_question_set_path, "wb") as file: + file.write(uploaded_question_set.getbuffer()) + st.success(f"Uploaded {uploaded_question_set.name}") + st.experimental_rerun() + + # File uploader for report + uploaded_file = st.file_uploader("Upload a Report", type=["pdf"]) + + # Flag to track if a valid report is uploaded + valid_report_uploaded = False + + if uploaded_file is not None: + # Sanitize file name + sanitized_filename = os.path.basename(uploaded_file.name) + report_path = os.path.join(input_dir, sanitized_filename) + + # Check if the report already exists + if os.path.exists(report_path): + st.warning(f"We already have this report: {sanitized_filename}") + else: + # Save uploaded file to input directory + with open(report_path, "wb") as f: + f.write(uploaded_file.getbuffer()) + st.success(f"Uploaded {sanitized_filename}") + valid_report_uploaded = True + + # Run the app script with the selected input and question set + if st.button("Run Analysis", disabled=not valid_report_uploaded): + with st.spinner("Running analysis..."): + # Define the command to run the app script + command = [ + "python", "code/app.py", # Adjusted to include the relative path to app.py + "--pdf_path", report_path, + "--question_set", question_set + ] + # Run the command and capture output + result = subprocess.run(command, capture_output=True, text=True) + if result.returncode == 0: + st.success("Analysis completed successfully!") + st.text(result.stdout) # Display standard output + + output_csv_path = os.path.join(output_dir, f"{question_set}_answers_assessments.csv") + create_csv_from_json(data_dir, question_set, output_dir) + + # Copy the generated CSV to the output directory + if os.path.exists(output_csv_path): + st.success(f"CSV file generated: {output_csv_path}") + # Offer the user to download the CSV + with open(output_csv_path, "rb") as f: + st.download_button( + label="Download CSV", + data=f, + file_name=os.path.basename(output_csv_path), + mime="text/csv" + ) + else: + st.error("CSV file not found.") + else: + st.error("Error running analysis.") + st.text(result.stderr) + + # Button to retrigger CSV generation + if st.button("Regenerate CSV"): + with st.spinner("Regenerating CSV..."): + output_csv_path = os.path.join(output_dir, f"{question_set}_answers_assessments.csv") + create_csv_from_json(data_dir, question_set, output_dir) + + if os.path.exists(output_csv_path): + st.success(f"CSV file regenerated: {output_csv_path}") + with open(output_csv_path, "rb") as f: + st.download_button( + label="Download CSV", + data=f, + file_name=os.path.basename(output_csv_path), + mime="text/csv" + ) + else: + st.error("CSV file not found.") + + selected_reports = [] + # List report names in the data directory + if question_set: + question_set_dir = os.path.join(data_dir, question_set, 'output') + if os.path.exists(question_set_dir): + report_names = set() + for file in os.listdir(question_set_dir): + if file.endswith(f"{question_set}_answers_assessments.csv"): + csv_path = os.path.join(question_set_dir, file) + df = pd.read_csv(csv_path) + report_names.update(df['Report Name'].unique()) + report_names = list(report_names) + selected_reports = st.multiselect("Select Reports to Compare", report_names, default=report_names) + else: + st.warning("Please select a question set.") + + if selected_reports: + data_frames = [] + for report in selected_reports: + csv_path = os.path.join(data_dir, question_set, output_dir, f"{question_set}_answers_assessments.csv") + if os.path.exists(csv_path): + df = pd.read_csv(csv_path) + if report in df['Report Name'].unique(): + df = df[df['Report Name'] == report] + df['Report'] = report + data_frames.append(df) + + if data_frames: + combined_df = pd.concat(data_frames) + st.dataframe(combined_df) + + # Plot the scores per TCFD and report using a radar chart + if "Score" in combined_df.columns: + st.subheader("Scores per TCFD and Report") + radar_data = combined_df.pivot(index='TCFD Key', columns='Report', values='Score').fillna(0) + st.write(radar_data) # Debugging line to check the data + + # Create radar chart using Matplotlib + labels = radar_data.index + num_vars = len(labels) + + # Compute angle for each axis + angles = np.linspace(0, 2 * np.pi, num_vars, endpoint=False).tolist() + angles += angles[:1] + + fig, ax = plt.subplots(figsize=(6, 6), subplot_kw=dict(polar=True)) + + # Define different line styles and colors + line_styles = ['-', '--', '-.', ':'] + colors = plt.cm.viridis(np.linspace(0, 1, len(radar_data.columns))) + + for i, report in enumerate(radar_data.columns): + values = radar_data[report].tolist() + values += values[:1] + + # Add a small offset to each report's data + offset = np.random.normal(0, 0.01, len(values)) + values = [v + o for v, o in zip(values, offset)] + + ax.plot(angles, values, label=report, linestyle=line_styles[i % len(line_styles)], color=colors[i]) + + ax.set_yticklabels([]) + ax.set_xticks(angles[:-1]) + ax.set_xticklabels(labels) + ax.legend(loc='upper right', bbox_to_anchor=(1.1, 1.1)) + + st.pyplot(fig) + else: + st.warning("No reports analyzed yet in this question set.") + else: + st.warning("No reports selected for comparison.") + + # Always display the download button if the CSV file exists + csv_filename = f"{question_set}_answers_assessments.csv" + csv_path = os.path.join(output_dir, csv_filename) + if os.path.exists(csv_path): + with open(csv_path, "rb") as f: + st.download_button( + label="Download CSV", + key="download_csv", + data=f, + file_name=csv_filename, + mime="text/csv" + ) + +# Create a Streamlit footer +footer = """ + + +""" +st.markdown(footer, unsafe_allow_html=True) \ No newline at end of file diff --git a/question_sets/default.yaml b/question_sets/default.yaml new file mode 100644 index 0000000..1dee9c7 --- /dev/null +++ b/question_sets/default.yaml @@ -0,0 +1,114 @@ +questions: + general: + - "What is the company of the report?" + - "What sector does the company belong to?" + - "Where is the company located?" + tcfd_1: "How does the company's board oversee climate-related risks and opportunities?" + tcfd_2: "What is the role of management in assessing and managing climate-related risks and opportunities?" + tcfd_3: "What are the most relevant climate-related risks and opportunities that the organisation has identified over the short, medium, and long term? Are risks clearly associated with a horizon?" + tcfd_4: "How do climate-related risks and opportunities impact the organisation's businesses strategy, economic and financial performance, and financial planning?" + tcfd_5: "How resilient is the organisation's strategy when considering different climate-related scenarios, including a 2°C target or lower scenario? How resilient is the organisation's strategy when considering climate physical risks?" + tcfd_6: "What processes does the organisation use to identify and assess climate-related risks?" + tcfd_7: "How does the organisation manage climate-related risks?" + tcfd_8: "How are the processes for identifying, assessing, and managing climate-related risks integrated into the organisation's overall risk management?" + tcfd_9: "What metrics does the organisation use to assess climate-related risks and opportunities? How do the metrics help ensure that the performance is in line with its strategy and risk management process?" + tcfd_10: "Does the organisation disclose its Scope 1, Scope 2, and, if appropriate, Scope 3 greenhouse gas (GHG) emissions? What are the related risks and do they differ depending on the scope?" + tcfd_11: "What targets does the organisation use to understand/quantify/benchmark climate-related risks and opportunities? How is the organization performing against these targets?" + +queries: + general: + - "What is the company of the report?" + - "What sector does the company belong to?" + - "Where is the company located?" + tcfd_1: "How does the company's board oversee climate-related risks and opportunities?" + tcfd_2: "What is the role of management in assessing and managing climate-related risks and opportunities?" + tcfd_3: "What are the most relevant climate-related risks and opportunities that the organisation has identified over the short, medium, and long term? Are risks clearly associated with a horizon?" + tcfd_4: "How do climate-related risks and opportunities impact the organisation's businesses strategy, economic and financial performance, and financial planning?" + tcfd_5: "How resilient is the organisation's strategy when considering different climate-related scenarios, including a 2°C target or lower scenario? How resilient is the organisation's strategy when considering climate physical risks?" + tcfd_6: "What processes does the organisation use to identify and assess climate-related risks?" + tcfd_7: "How does the organisation manage climate-related risks?" + tcfd_8: "How are the processes for identifying, assessing, and managing climate-related risks integrated into the organisation's overall risk management?" + tcfd_9: "What metrics does the organisation use to assess climate-related risks and opportunities? How do the metrics help ensure that the performance is in line with its strategy and risk management process?" + tcfd_10: "Does the organisation disclose its Scope 1, Scope 2, and, if appropriate, Scope 3 greenhouse gas (GHG) emissions? What are the related risks and do they differ depending on the scope?" + tcfd_11: "What targets does the organisation use to understand/quantify/benchmark climate-related risks and opportunities? How is the organization performing against these targets?" + +assessments: + tcfd_1: | + In describing the board's oversight of climate-related issues, organizations should consider including a discussion of the following: + 1. processes and frequency by which the board and/or board committees (e.g., audit, risk, or other committees) are informed about climate-related issues; + 2. whether the board and/or board committees consider climate-related issues when reviewing and guiding strategy, major plans of action, risk management policies, annual budgets, and business plans as well as setting the organization’s performance objectives, monitoring implementation and performance, and overseeing major capital expenditures, acquisitions, and divestitures; and + 3. how the board monitors and oversees progress against goals and targets for addressing climate-related issues. + tcfd_2: | + In describing management's role related to the assessment and management of climate-related issues, organizations should consider including the following information: + 1. whether the organization has assigned climate-related responsibilities to management-level positions or committees; and, if so, whether such management positions or committees report to the board or a committee of the board and whether those responsibilities include assessing and/or managing climate-related issues; + 2. a description of the associated organizational structure(s); + 3. processes by which management is informed about climate-related issues; and + 4. how management (through specific positions and/or management committees) monitors climate-related issues. + tcfd_3: | + In describing the climate-related risks and opportunities the organization has identified over the short, medium, and long term, organizations should provide the following information: + 1. a description of what they consider to be the relevant short-, medium-, and long-term time horizons, taking into consideration the useful life of the organization's assets or infrastructure and the fact that climate-related issues often manifest themselves over the medium and longer terms; + 2. a description of the specific climate-related issues potentially arising in each time horizon (short, medium, and long term) that could have a material financial impact on the organization; and + 3. a description of the process(es) used to determine which risks and opportunities could have a material financial impact on the organization. + Organizations should consider providing a description of their risks and opportunities by sector and/or geography, as appropriate. + tcfd_4: | + In describing impact of climate-related risks and opportunities on the organization's businesses, strategy, and financial planning, organizations should discuss how identified climate-related issues have affected their businesses, strategy, and financial planning. + Organizations should consider including the impact on their businesses, strategy, and financial planning in the following areas: + 1. Products and services + 2. Supply chain and/or value chain + 3. Adaptation and mitigation activities + 4. Investment in research and development + 5. Operations (including types of operations and location of facilities) + 6. Acquisitions or divestments + 7. Access to capital + Organizations should describe how climate-related issues serve as an input to their financial planning process, the time period(s) used, and how these risks and opportunities are prioritized. Organizations' disclosures should reflect a holistic picture of the interdependencies among the factors that affect their ability to create value over time. + Organizations should describe the impact of climate-related issues on their financial performance (e.g., revenues, costs) and financial position (e.g., assets, liabilities). If climate-related scenarios were used to inform the organization's strategy and financial planning, such scenarios should be described. + Organizations that have made GHG emissions reduction commitments, operate in jurisdictions that have made such commitments, or have agreed to meet investor expectations regarding GHG emissions reductions should describe their plans for transitioning to a low-carbon economy, which could include GHG emissions targets and specific activities intended to reduce GHG emissions in their operations and value chain or to otherwise support the transition. + tcfd_5: | + In describing the resilience of the organization's strategy, organizations should describe how resilient their strategies are to climate-related risks and opportunities, taking into consideration a transition to a low-carbon economy consistent with a 2°C or lower scenario and, where relevant to the organization, scenarios consistent with increased physical climate-related risks. + Organizations should consider discussing: + 1. where they believe their strategies may be affected by climate-related risks and opportunities; + 2. how their strategies might change to address such potential risks and opportunities; + 3. the potential impact of climate-related issues on financial performance (e.g., revenues, costs) and financial position (e.g., assets, liabilities); and + 4. the climate-related scenarios and associated time horizon(s) considered. + tcfd_6: | + In describing the organization's processes for identifying and assessing climate-related risks, organizations should describe their risk management processes for identifying and assessing climate-related risks. An important aspect of this description is how organizations determine the relative significance of climate-related risks in relation to other risks. + Organizations should describe whether they consider existing and emerging regulatory requirements related to climate change (e.g., limits on emissions) as well as other relevant factors considered. + Organizations should also consider disclosing the following: + 1. processes for assessing the potential size and scope of identified climate-related risks and + 2. definitions of risk terminology used or references to existing risk classification frameworks used. + tcfd_7: | + In describing the organization's processes for managing climate-related risks, organizations should describe their processes for managing climate-related risks, including how they make decisions to mitigate, transfer, accept, or control those risks. In addition, organizations should describe their processes for prioritizing climate-related risks, including how materiality determinations are made within their organizations. + tcfd_8: | + In describing how processes for identifying, assessing, and managing climate-related risks are integrated into the organization's overall risk management, organizations should describe how their processes for identifying, assessing, and managing climate-related risks are integrated into their overall risk management. + tcfd_9: | + In describing the metrics used by the organization to assess climate-related risks and opportunities in line with its strategy and risk management process, organizations should provide the key metrics used to measure and manage climate-related risks and opportunities, as well as metrics consistent with the cross-industry. + Organizations should consider including metrics on climate-related risks associated with water, energy, land use, and waste management where relevant and applicable. + Where climate-related issues are material, organizations should consider describing whether and how related performance metrics are incorporated into remuneration policies. + Where relevant, organizations should provide their internal carbon prices as well as climate-related opportunity metrics such as revenue from products and services designed for a low-carbon economy. + Metrics should be provided for historical periods to allow for trend analysis. Where appropriate, organizations should consider providing forward-looking metrics for the cross-industry, consistent with their business or strategic planning time horizons. In addition, where not apparent, organizations should provide a description of the methodologies used to calculate or estimate climate-related metrics. + tcfd_10: | + In disclosing Scope 1, Scope 2, and, if appropriate, Scope 3 greenhouse gas (GHG) emissions, and the related risks, organizations should provide their Scope 1 and Scope 2 GHG emissions independent of a materiality assessment, and, if appropriate, Scope 3 GHG emissions and the related risks. All organizations should consider disclosing Scope 3 GHG emissions. + GHG emissions should be calculated in line with the GHG Protocol methodology to allow for aggregation and comparability across organizations and jurisdictions. As appropriate, organizations should consider providing related, generally accepted industry-specific GHG efficiency ratios. + GHG emissions and associated metrics should be provided for historical periods to allow for trend analysis. In addition, where not apparent, organizations should provide a description of the methodologies used to calculate or estimate the metrics. + tcfd_11: | + In describing the targets used by the organization to manage climate-related risks and opportunities and performance against targets, organizations should describe their key climate-related targets such as those related to GHG emissions, water usage, energy usage, etc., in line with the cross-industry, where relevant, and in line with anticipated regulatory requirements or market constraints or other goals. Other goals may include efficiency or financial goals, financial loss tolerances, avoided GHG emissions through the entire product life cycle, or net revenue goals for products and services designed for a low-carbon economy. + In describing their targets, organizations should consider including the following: + 1. whether the target is absolute or intensity based; + 2. time frames over which the target applies; + 3. base year from which progress is measured; and + 4. key performance indicators used to assess progress against targets. + Organizations disclosing medium-term or long-term targets should also disclose associated interim targets in aggregate or by business line, where available. + Where not apparent, organizations should provide a description of the methodologies used to calculate targets and measures. + +guidelines: + tcfd_1: "Please concentrate on the board's direct responsibilities and actions pertaining to climate issues, without discussing the company-wide risk management system or other topics." + tcfd_2: "Please focus on their direct duties related to climate issues, without introducing other topics such as the broader corporate risk management system." + tcfd_3: "Avoid discussing the company-wide risk management system or how these risks and opportunities are identified and managed." + tcfd_4: "Please do not include the process of risk identification, assessment or management in your answer." + tcfd_5: "In your response, focus solely on the resilience of strategy in these scenarios, and refrain from discussing processes of risk identification, assessment, or management strategies." + tcfd_6: "Restrict your answer to the identification and assessment processes, without discussing the management or integration of these risks." + tcfd_7: "Please focus on the concrete actions and strategies implemented to manage these risks, excluding the process of risk identification or assessment." + tcfd_8: "Please focus on the integration aspect and avoid discussing the process of risk identification, assessment, or the specific management actions taken." + tcfd_9: "Do not include information regarding the organization's general risk identification and assessment methods or their broader corporate strategy and initiatives." + tcfd_10: "Confirm whether the organisation discloses its Scope 1, Scope 2, and, if appropriate, Scope 3 greenhouse gas (GHG) emissions. If so, provide any available data or specific figures on these emissions. Additionally, identify the related risks. The risks should be specific to the GHG emissions rather than general climate-related risks." + tcfd_11: "Please detail the precise targets and avoid discussing the company's general risk identification and assessment methods or their commitment to disclosure through the TCFD." \ No newline at end of file diff --git a/question_sets/evasion.yaml b/question_sets/evasion.yaml new file mode 100644 index 0000000..a270445 --- /dev/null +++ b/question_sets/evasion.yaml @@ -0,0 +1,87 @@ +questions: + general: + - "What is the company of the report?" + - "What sector does the company belong to?" + - "Where is the company located?" + tcfd_1: "How does the company’s board oversee carbon emissions-related risks and opportunities in its sustainability strategy?" + tcfd_2: "How detailed and complete are the metrics disclosed in the environment section concerning carbon emissions?" + tcfd_3: "What are the most relevant carbon emissions-related risks and opportunities that the organization has identified over the short, medium, and long term? Are risks clearly associated with a specific timeframe?" + tcfd_4: "Does the organization disclose its Scope 1, Scope 2, and, if appropriate, Scope 3 greenhouse gas (GHG) emissions? What are the related risks, and do they differ depending on the scope?" + tcfd_5: "How are the processes for identifying, assessing, and managing carbon emissions-related risks integrated into the organization’s overall risk management?" + tcfd_6: "How well is the carbon emissions data presented? Does it include visual aids such as infographics, and are key points clearly highlighted?" + tcfd_7: "How clearly does the report define the perimeter of activities and locations covered by the carbon emissions data?" + tcfd_8: "To what extent does the carbon emissions reporting include all of the company’s operations, including international activities?" + tcfd_9: "How thoroughly has the information in the report related to carbon emissions been audited by an external party?" + +queries: + general: + - "What is the company of the report?" + - "What sector does the company belong to?" + - "Where is the company located?" + tcfd_1: "How does the company’s board oversee carbon emissions-related risks and opportunities in its sustainability strategy?" + tcfd_2: "How detailed and complete are the metrics disclosed in the environment section concerning carbon emissions?" + tcfd_3: "What are the most relevant carbon emissions-related risks and opportunities that the organization has identified over the short, medium, and long term? Are risks clearly associated with a specific timeframe?" + tcfd_4: "Does the organization disclose its Scope 1, Scope 2, and, if appropriate, Scope 3 greenhouse gas (GHG) emissions? What are the related risks, and do they differ depending on the scope?" + tcfd_5: "How are the processes for identifying, assessing, and managing carbon emissions-related risks integrated into the organization’s overall risk management?" + tcfd_6: "How well is the carbon emissions data presented? Does it include visual aids such as infographics, and are key points clearly highlighted?" + tcfd_7: "How clearly does the report define the perimeter of activities and locations covered by the carbon emissions data?" + tcfd_8: "To what extent does the carbon emissions reporting include all of the company’s operations, including international activities?" + tcfd_9: "How thoroughly has the information in the report related to carbon emissions been audited by an external party?" + +assessments: + tcfd_1: | + In describing the board's oversight of carbon emissions-related risks and opportunities, organizations should consider including a discussion of the following: + 1. Processes and frequency by which the board and/or board committees (e.g., audit, risk, or other committees) are informed about carbon emissions-related issues; + 2. Whether the board and/or board committees consider carbon emissions-related issues when reviewing and guiding strategy, major plans of action, risk management policies, annual budgets, and business plans, as well as setting the organization’s performance objectives, monitoring implementation and performance, and overseeing major capital expenditures, acquisitions, and divestitures; + 3. How the board monitors and oversees progress against goals and targets for addressing carbon emissions-related issues. + tcfd_2: | + In describing the metrics disclosed in the environment section concerning carbon emissions, organizations should consider providing the following information: + 1. A comprehensive list of the specific metrics used to measure and report carbon emissions, covering all scopes (Scope 1, Scope 2, and, if appropriate, Scope 3); + 2. The degree of detail and granularity in these metrics, including any benchmarks or targets set by the organization; + 3. How these metrics align with industry standards or regulations, and how they contribute to the organization’s overall carbon management strategy. + tcfd_3: | + In describing the carbon emissions-related risks and opportunities the organization has identified over the short, medium, and long term, organizations should consider providing the following information: + 1. A clear description of what the organization considers to be the relevant short-, medium-, and long-term time horizons, taking into consideration the useful life of the organization's assets or infrastructure; + 2. A detailed description of the specific carbon emissions-related issues potentially arising in each time horizon (short, medium, and long term) that could have a material financial impact on the organization; + 3. An explanation of the process(es) used to determine which risks and opportunities could have a material financial impact on the organization. Organizations should consider providing a description of their risks and opportunities by sector and/or geography, as appropriate. + tcfd_4: | + In disclosing Scope 1, Scope 2, and, if appropriate, Scope 3 greenhouse gas (GHG) emissions, and the related risks, organizations should consider providing the following: + 1. A comprehensive disclosure of Scope 1, Scope 2, and, where relevant, Scope 3 GHG emissions, in line with recognized methodologies such as the GHG Protocol; + 2. An assessment of the risks associated with each scope, and how these risks differ across scopes; + 3. A description of the methodologies used to calculate or estimate these emissions, ensuring transparency and comparability. + tcfd_5: | + In describing the integration of processes for identifying, assessing, and managing carbon emissions-related risks into the organization’s overall risk management, organizations should consider including the following: + 1. A description of how carbon emissions-related risks are incorporated into the broader enterprise risk management framework; + 2. An explanation of how these risks are prioritized relative to other risks, including the criteria used for determining their significance; + 3. The specific processes used for monitoring and mitigating these risks over time, including any feedback mechanisms or continuous improvement processes. + tcfd_6: | + In describing the clarity and effectiveness of the presentation of carbon emissions data, organizations should consider discussing: + 1. The use of visual aids such as infographics, charts, and tables to enhance the clarity of carbon emissions data; + 2. The highlighting of key points and metrics to ensure they are easily identifiable and understandable; + 3. The overall structure and readability of the report, including the avoidance of dense text blocks that could hinder comprehension. + tcfd_7: | + In describing the perimeter of activities and locations covered by the carbon emissions data, organizations should consider including: + 1. A clear definition of the geographic and operational boundaries covered by the carbon emissions data; + 2. An explanation of any exclusions or limitations in the data coverage, including the rationale for these exclusions; + 3. A description of how the defined perimeter aligns with the organization’s overall business operations and carbon management strategy. + tcfd_8: | + In describing the comprehensiveness of the carbon emissions reporting in covering all of the company’s operations, including international activities, organizations should consider providing: + 1. A detailed account of how the carbon emissions reporting covers all relevant business units, subsidiaries, and international operations; + 2. An assessment of any areas where data might be incomplete or less accurate, along with steps taken to address these gaps; + 3. A discussion on how the organization ensures that the reporting is consistent across all operations, including those in different jurisdictions or regulatory environments. + tcfd_9: | + In describing the extent to which the carbon emissions data in the report has been audited by an external party, organizations should consider including: + 1. A clear statement on which sections of the carbon emissions data have been independently audited, and by whom; + 2. An explanation of the auditing process, including the standards or guidelines followed; + 3. A discussion of any areas where data was not audited, including the reasons for this and the potential impact on the overall accuracy and reliability of the report. + +guidelines: + tcfd_1: "Please focus specifically on the board's direct oversight of carbon emissions-related risks and opportunities within the sustainability strategy. Avoid discussing broader corporate risk management systems or unrelated topics." + tcfd_2: "Concentrate on the detail and completeness of the metrics disclosed in the environment section concerning carbon emissions. Do not include broader environmental metrics that do not directly pertain to carbon emissions." + tcfd_3: "Please focus solely on the identification of carbon emissions-related risks and opportunities over the specified timeframes (short, medium, and long term). Avoid discussing the company-wide risk management system or unrelated risks." + tcfd_4: "Confirm the disclosure of Scope 1, Scope 2, and, if appropriate, Scope 3 greenhouse gas (GHG) emissions. Provide any available data or specific figures on these emissions. Focus on the risks directly related to these emissions rather than general climate-related risks." + tcfd_5: "Concentrate on how the processes for identifying, assessing, and managing carbon emissions-related risks are integrated into the organization’s overall risk management. Exclude discussions on broader risk identification or unrelated management strategies." + tcfd_6: "Focus on the clarity and effectiveness of the presentation of carbon emissions data, particularly the use of visual aids such as infographics and the clear highlighting of key points. Avoid discussing the content of the data itself." + tcfd_7: "Please provide a clear analysis of how well the report defines the perimeter of activities and locations covered by the carbon emissions data. Avoid discussing any aspects outside the defined perimeter." + tcfd_8: "Concentrate on the comprehensiveness of the carbon emissions reporting in covering all of the company’s operations, including international activities. Avoid discussing operations outside the scope of the carbon emissions reporting." + tcfd_9: "Focus on the extent to which the carbon emissions data in the report has been audited by an external party. Provide details on which sections have been audited and by whom. Avoid discussing non-carbon-related data or general audit practices."