leavoigt commited on
Commit
5cfddb1
1 Parent(s): 0487009

Upload 4 files

Browse files
appStore/info.py ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+
3
+ def app():
4
+
5
+
6
+ with open('style.css') as f:
7
+ st.markdown(f"<style>{f.read()}</style>", unsafe_allow_html=True)
8
+
9
+ st.markdown("<h2 style='text-align: center; \
10
+ color: black;'> Policy Action Tracker</h2>",
11
+ unsafe_allow_html=True)
12
+
13
+
14
+ st.markdown("<div style='text-align: center; \
15
+ color: grey;'>The Policy Action Tracker is an open-source\
16
+ digital tool which aims to assist policy analysts and \
17
+ other users in extracting and filtering relevant \
18
+ information from policy documents.</div>",
19
+ unsafe_allow_html=True)
20
+ footer = """
21
+ <div class="footer-custom">
22
+ Guidance & Feedback - <a href="https://www.linkedin.com/in/maren-bernlöhr-149891222" target="_blank">Maren Bernlöhr</a> |
23
+ <a href="https://www.linkedin.com/in/manuelkuhm" target="_blank">Manuel Kuhm</a> |
24
+ Developer - <a href="https://www.linkedin.com/in/erik-lehmann-giz/" target="_blank">Erik Lehmann</a> |
25
+ <a href="https://www.linkedin.com/in/jonas-nothnagel-bb42b114b/" target="_blank">Jonas Nothnagel</a> |
26
+ <a href="https://www.linkedin.com/in/prashantpsingh/" target="_blank">Prashant Singh</a> |
27
+
28
+ </div>
29
+ """
30
+ st.markdown(footer, unsafe_allow_html=True)
31
+
32
+ c1, c2, c3 = st.columns([8,1,12])
33
+ with c1:
34
+ st.image("docStore/img/ndc.png")
35
+ with c3:
36
+ st.markdown('<div style="text-align: justify;">The manual extraction \
37
+ of relevant information from text documents is a \
38
+ time-consuming task for any policy analyst. As the amount and length of \
39
+ public policy documents in relation to sustainable development (such as \
40
+ National Development Plans and Nationally Determined Contributions) \
41
+ continuously increases, a major challenge for policy action tracking – the \
42
+ evaluation of stated goals and targets and their actual implementation on \
43
+ the ground – arises. Luckily, Artificial Intelligence (AI) and Natural \
44
+ Language Processing (NLP) methods can help in shortening and easing this \
45
+ task for policy analysts.</div><br>',
46
+ unsafe_allow_html=True)
47
+
48
+ intro = """
49
+ <div style="text-align: justify;">
50
+
51
+ For this purpose, the United Nations Sustainable Development Solutions \
52
+ Network (SDSN) and the Deutsche Gesellschaft für Internationale \
53
+ Zusammenarbeit (GIZ) GmbH are collaborated in the development \
54
+ of this AI-powered open-source web application that helps find and extract \
55
+ relevant information from public policy documents faster to facilitate \
56
+ evidence-based decision-making processes in sustainable development and beyond.
57
+
58
+ This tool allows policy analysts and other users the possibility to rapidly \
59
+ search for relevant information/paragraphs in the document according to the \
60
+ user’s interest, classify the document’s content according to the Sustainable \
61
+ Development Goals (SDGs), and compare climate-related policy documents and NDCs \
62
+ across countries using open data from the German Institute of Development and \
63
+ Sustainability’s (IDOS) NDC Explorer.
64
+ To understand the application's functionalities and learn more about ß
65
+ the project, see the attached concept note. We hope you like our application 😊
66
+
67
+
68
+ </div>
69
+ <br>
70
+ """
71
+ st.markdown(intro, unsafe_allow_html=True)
72
+ # st.image("docStore/img/paris.png")
appStore/keyword_search.py ADDED
@@ -0,0 +1,176 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # set path
2
+ import glob, os, sys;
3
+ sys.path.append('../utils')
4
+
5
+ import streamlit as st
6
+ import json
7
+ import logging
8
+ from utils.lexical_search import runLexicalPreprocessingPipeline, lexical_search
9
+ from utils.semantic_search import runSemanticPreprocessingPipeline, semantic_keywordsearch
10
+ from utils.checkconfig import getconfig
11
+ from utils.streamlitcheck import checkbox_without_preselect
12
+
13
+ # Declare all the necessary variables
14
+ config = getconfig('paramconfig.cfg')
15
+ split_by = config.get('semantic_search','SPLIT_BY')
16
+ split_length = int(config.get('semantic_search','SPLIT_LENGTH'))
17
+ split_overlap = int(config.get('semantic_search','SPLIT_OVERLAP'))
18
+ split_respect_sentence_boundary = bool(int(config.get('semantic_search',
19
+ 'RESPECT_SENTENCE_BOUNDARY')))
20
+ remove_punc = bool(int(config.get('semantic_search','REMOVE_PUNC')))
21
+ embedding_model = config.get('semantic_search','RETRIEVER')
22
+ embedding_model_format = config.get('semantic_search','RETRIEVER_FORMAT')
23
+ embedding_layer = int(config.get('semantic_search','RETRIEVER_EMB_LAYER'))
24
+ embedding_dim = int(config.get('semantic_search','EMBEDDING_DIM'))
25
+ max_seq_len = int(config.get('semantic_search','MAX_SEQ_LENGTH'))
26
+ retriever_top_k = int(config.get('semantic_search','RETRIEVER_TOP_K'))
27
+ reader_model = config.get('semantic_search','READER')
28
+ reader_top_k = int(config.get('semantic_search','RETRIEVER_TOP_K'))
29
+ top_k_per_candidate = int(config.get('semantic_search','READER_TOP_K_PER_CANDIDATE'))
30
+ lexical_split_by= config.get('lexical_search','SPLIT_BY')
31
+ lexical_split_length=int(config.get('lexical_search','SPLIT_LENGTH'))
32
+ lexical_split_overlap = int(config.get('lexical_search','SPLIT_OVERLAP'))
33
+ lexical_remove_punc = bool(int(config.get('lexical_search','REMOVE_PUNC')))
34
+ lexical_top_k=int(config.get('lexical_search','TOP_K'))
35
+
36
+ def app():
37
+
38
+ with st.container():
39
+ st.markdown("<h1 style='text-align: center; \
40
+ color: black;'> Search</h1>",
41
+ unsafe_allow_html=True)
42
+ st.write(' ')
43
+ st.write(' ')
44
+
45
+ with st.expander("ℹ️ - About this app", expanded=False):
46
+
47
+ st.write(
48
+ """
49
+ The *Search* app is an interface \
50
+ for doing contextual and keyword searches in \
51
+ policy documents. \
52
+ """)
53
+ st.write("")
54
+ st.write(""" The application allows its user to perform a search\
55
+ based on two options: a lexical search([TFIDF](https://en.wikipedia.org/wiki/Tf%E2%80%93idf))\
56
+ and semantic search. [bi-encoder](https://www.sbert.net/examples/applications/retrieve_rerank/README.html)\
57
+ The lexical search only \
58
+ displays paragraphs in the document with exact matching results, \
59
+ the semantic search shows paragraphs with meaningful connections \
60
+ (e.g., synonyms) based on the search context. Both \
61
+ methods employ a probabilistic retrieval framework in its identification\
62
+ of relevant paragraphs. By defualt the search is performed using \
63
+ 'Semantic Search', and to find 'Exact/Lexical Matches' please tick the \
64
+ checkbox provided which will by-pass semantic search. Furthermore,\
65
+ the application allows the user to search for pre-defined keywords \
66
+ from different thematic buckets present in sidebar.""")
67
+ st.write("")
68
+ st.write(""" The Exact Matches gives back top {} findings, and Semantic
69
+ search provides with top {} answers.""".format(lexical_top_k, retriever_top_k))
70
+ st.write("")
71
+ st.write("")
72
+ st.markdown("Some runtime metrics tested with cpu: Intel(R) Xeon(R) CPU @ 2.20GHz, memory: 13GB")
73
+ col1,col2,col3= st.columns([2,4,4])
74
+ with col1:
75
+ st.caption("OCR File processing")
76
+ # st.markdown('<div style="text-align: center;">50 sec</div>', unsafe_allow_html=True)
77
+ st.write("50 sec")
78
+
79
+ with col2:
80
+ st.caption("Lexical Search on 200 paragraphs(~ 35 pages)")
81
+ # st.markdown('<div style="text-align: center;">12 sec</div>', unsafe_allow_html=True)
82
+ st.write("15 sec")
83
+
84
+ with col3:
85
+ st.caption("Semantic search on 200 paragraphs(~ 35 pages)")
86
+ # st.markdown('<div style="text-align: center;">120 sec</div>', unsafe_allow_html=True)
87
+ st.write("120 sec(including emebedding creation)")
88
+
89
+ with st.sidebar:
90
+ with open('docStore/sample/keywordexample.json','r') as json_file:
91
+ keywordexample = json.load(json_file)
92
+
93
+ # genre = st.radio("Select Keyword Category", list(keywordexample.keys()))
94
+ st.caption("Select Keyword Category")
95
+ genre = checkbox_without_preselect(list(keywordexample.keys()))
96
+ if genre:
97
+ keywordList = keywordexample[genre]
98
+ else:
99
+ keywordList = None
100
+
101
+ st.markdown("---")
102
+
103
+ with st.container():
104
+ type_hinting = "Please enter here your question and we \
105
+ will look for an answer in the document\
106
+ OR enter the keyword you are looking \
107
+ for and we will look for similar\
108
+ context in the document.\
109
+ You can also explore predefined sets of keywords from sidebar. "
110
+ if keywordList is not None:
111
+ # queryList = st.text_input("You selected the {} category we \
112
+ # will look for these keywords in document".format(genre)
113
+ # value="{}".format(keywordList))
114
+ queryList = st.text_input(type_hinting,
115
+ value = "{}".format(keywordList))
116
+ else:
117
+ queryList = st.text_input(type_hinting,
118
+ placeholder="Enter keyword/query here")
119
+
120
+ searchtype = st.checkbox("Show only Exact Matches")
121
+ if st.button("Find them"):
122
+
123
+ if queryList == "":
124
+ st.info("🤔 No keyword provided, if you dont have any, \
125
+ please try example sets from sidebar!")
126
+ logging.warning("Terminated as no keyword provided")
127
+ else:
128
+ if 'filepath' in st.session_state:
129
+
130
+ if searchtype:
131
+ all_documents = runLexicalPreprocessingPipeline(
132
+ file_name=st.session_state['filename'],
133
+ file_path=st.session_state['filepath'],
134
+ split_by=lexical_split_by,
135
+ split_length=lexical_split_length,
136
+ split_overlap=lexical_split_overlap,
137
+ remove_punc=lexical_remove_punc)
138
+ logging.info("performing lexical search")
139
+ with st.spinner("Performing Exact matching search \
140
+ (Lexical search) for you"):
141
+ lexical_search(query=queryList,
142
+ documents = all_documents['documents'],
143
+ top_k = lexical_top_k )
144
+ else:
145
+ all_documents = runSemanticPreprocessingPipeline(
146
+ file_path= st.session_state['filepath'],
147
+ file_name = st.session_state['filename'],
148
+ split_by=split_by,
149
+ split_length= split_length,
150
+ split_overlap=split_overlap,
151
+ remove_punc= remove_punc,
152
+ split_respect_sentence_boundary=split_respect_sentence_boundary)
153
+ if len(all_documents['documents']) > 100:
154
+ warning_msg = ": This might take sometime, please sit back and relax."
155
+ else:
156
+ warning_msg = ""
157
+
158
+ logging.info("starting semantic search")
159
+ with st.spinner("Performing Similar/Contextual search{}".format(warning_msg)):
160
+ semantic_keywordsearch(query = queryList,
161
+ documents = all_documents['documents'],
162
+ embedding_model=embedding_model,
163
+ embedding_layer=embedding_layer,
164
+ embedding_model_format=embedding_model_format,
165
+ reader_model=reader_model,reader_top_k=reader_top_k,
166
+ retriever_top_k=retriever_top_k, embedding_dim=embedding_dim,
167
+ max_seq_len=max_seq_len,
168
+ top_k_per_candidate = top_k_per_candidate)
169
+
170
+ else:
171
+ st.info("🤔 No document found, please try to upload it at the sidebar!")
172
+ logging.warning("Terminated as no document provided")
173
+
174
+
175
+
176
+
appStore/multiapp.py ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Frameworks for running multiple Streamlit applications as a single app.
2
+ """
3
+ import streamlit as st
4
+ from PIL import Image
5
+ from streamlit_option_menu import option_menu
6
+ from utils.uploadAndExample import add_upload
7
+
8
+ class MultiApp:
9
+ """Framework for combining multiple streamlit applications.
10
+ Usage:
11
+ def foo():
12
+ st.title("Hello Foo")
13
+ def bar():
14
+ st.title("Hello Bar")
15
+ app = MultiApp()
16
+ app.add_app("Foo", foo)
17
+ app.add_app("Bar", bar)
18
+ app.run()
19
+ It is also possible keep each application in a separate file.
20
+ import foo
21
+ import bar
22
+ app = MultiApp()
23
+ app.add_app("Foo", foo.app)
24
+ app.add_app("Bar", bar.app)
25
+ app.run()
26
+ """
27
+ def __init__(self):
28
+ self.apps = []
29
+
30
+ def add_app(self,title,icon, func):
31
+ """Adds a new application.
32
+ Parameters
33
+ ----------
34
+ func:
35
+ the python function to render this app.
36
+ title:
37
+ title of the app. Appears in the dropdown in the sidebar.
38
+ """
39
+ self.apps.append({
40
+ "title": title,
41
+ "icon": icon,
42
+ "function": func
43
+ })
44
+
45
+ def run(self):
46
+
47
+ st.sidebar.write(format_func=lambda app: app['title'])
48
+ image = Image.open('docStore/img/sdsn.png')
49
+ st.sidebar.image(image, width =200)
50
+
51
+ with st.sidebar:
52
+ selected = option_menu(None, [page["title"] for page in self.apps],
53
+ icons=[page["icon"] for page in self.apps],
54
+ menu_icon="cast", default_index=0)
55
+ st.markdown("---")
56
+
57
+
58
+ for index, item in enumerate(self.apps):
59
+ if item["title"] == selected:
60
+ self.apps[index]["function"]()
61
+ break
62
+
63
+
64
+ choice = st.sidebar.radio(label = 'Select the Document',
65
+ help = 'You can upload the document \
66
+ or else you can try a example document',
67
+ options = ('Upload Document', 'Try Example'),
68
+ horizontal = True)
69
+ add_upload(choice)
70
+
appStore/sdg_analysis.py ADDED
@@ -0,0 +1,179 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # set path
2
+ import glob, os, sys;
3
+ sys.path.append('../utils')
4
+
5
+ #import needed libraries
6
+ import seaborn as sns
7
+ import matplotlib.pyplot as plt
8
+ import numpy as np
9
+ import pandas as pd
10
+ import streamlit as st
11
+ from st_aggrid import AgGrid
12
+ from st_aggrid.shared import ColumnsAutoSizeMode
13
+ from utils.sdg_classifier import sdg_classification
14
+ from utils.sdg_classifier import runSDGPreprocessingPipeline, load_sdgClassifier
15
+ from utils.keyword_extraction import textrank
16
+ import logging
17
+ logger = logging.getLogger(__name__)
18
+ from utils.checkconfig import getconfig
19
+
20
+
21
+ # Declare all the necessary variables
22
+ config = getconfig('paramconfig.cfg')
23
+ model_name = config.get('sdg','MODEL')
24
+ split_by = config.get('sdg','SPLIT_BY')
25
+ split_length = int(config.get('sdg','SPLIT_LENGTH'))
26
+ split_overlap = int(config.get('sdg','SPLIT_OVERLAP'))
27
+ remove_punc = bool(int(config.get('sdg','REMOVE_PUNC')))
28
+ split_respect_sentence_boundary = bool(int(config.get('sdg','RESPECT_SENTENCE_BOUNDARY')))
29
+ threshold = float(config.get('sdg','THRESHOLD'))
30
+ top_n = int(config.get('sdg','TOP_KEY'))
31
+
32
+
33
+ def app():
34
+
35
+ #### APP INFO #####
36
+ with st.container():
37
+ st.markdown("<h1 style='text-align: center; color: black;'> SDG Classification and Keyphrase Extraction </h1>", unsafe_allow_html=True)
38
+ st.write(' ')
39
+ st.write(' ')
40
+
41
+ with st.expander("ℹ️ - About this app", expanded=False):
42
+
43
+ st.write(
44
+ """
45
+ The *SDG Analysis* app is an easy-to-use interface built \
46
+ in Streamlit for analyzing policy documents with respect to SDG \
47
+ Classification for the paragraphs/texts in the document and \
48
+ extracting the keyphrase per SDG label - developed by GIZ Data \
49
+ and the Sustainable Development Solution Network. \n
50
+ """)
51
+ st.write("""**Document Processing:** The Uploaded/Selected document is \
52
+ automatically cleaned and split into paragraphs with a maximum \
53
+ length of 120 words using a Haystack preprocessing pipeline. The \
54
+ length of 120 is an empirical value which should reflect the length \
55
+ of a “context” and should limit the paragraph length deviation. \
56
+ However, since we want to respect the sentence boundary the limit \
57
+ can breach and hence this limit of 120 is tentative. \n
58
+ """)
59
+ st.write("""**SDG cLassification:** The application assigns paragraphs \
60
+ to 16 of the 17 United Nations Sustainable Development Goals (SDGs).\
61
+ SDG 17 “Partnerships for the Goals” is excluded from the analysis due \
62
+ to its broad nature which could potentially inflate the results. \
63
+ Each paragraph is assigned to one SDG only. Again, the results are \
64
+ displayed in a summary table including the number of the SDG, a \
65
+ relevancy score highlighted through a green color shading, and the \
66
+ respective text of the analyzed paragraph. Additionally, a pie \
67
+ chart with a blue color shading is displayed which illustrates the \
68
+ three most prominent SDGs in the document. The SDG classification \
69
+ uses open-source training [data](https://zenodo.org/record/5550238#.Y25ICHbMJPY) \
70
+ from [OSDG.ai](https://osdg.ai/) which is a global \
71
+ partnerships and growing community of researchers and institutions \
72
+ interested in the classification of research according to the \
73
+ Sustainable Development Goals. The summary table only displays \
74
+ paragraphs with a calculated relevancy score above 85%. \n""")
75
+
76
+ st.write("""**Keyphrase Extraction:** The application extracts 15 \
77
+ keyphrases from the document, for each SDG label and displays the \
78
+ results in a summary table. The keyphrases are extracted using \
79
+ using [Textrank](https://github.com/summanlp/textrank)\
80
+ which is an easy-to-use computational less expensive \
81
+ model leveraging combination of TFIDF and Graph networks.
82
+ """)
83
+ st.write("")
84
+ st.write("")
85
+ st.markdown("Some runtime metrics tested with cpu: Intel(R) Xeon(R) CPU @ 2.20GHz, memory: 13GB")
86
+ col1,col2,col3,col4 = st.columns([2,2,4,4])
87
+ with col1:
88
+ st.caption("Loading Time Classifier")
89
+ # st.markdown('<div style="text-align: center;">12 sec</div>', unsafe_allow_html=True)
90
+ st.write("12 sec")
91
+ with col2:
92
+ st.caption("OCR File processing")
93
+ # st.markdown('<div style="text-align: center;">50 sec</div>', unsafe_allow_html=True)
94
+ st.write("50 sec")
95
+ with col3:
96
+ st.caption("SDG Classification of 200 paragraphs(~ 35 pages)")
97
+ # st.markdown('<div style="text-align: center;">120 sec</div>', unsafe_allow_html=True)
98
+ st.write("120 sec")
99
+ with col4:
100
+ st.caption("Keyword extraction for 200 paragraphs(~ 35 pages)")
101
+ # st.markdown('<div style="text-align: center;">3 sec</div>', unsafe_allow_html=True)
102
+ st.write("3 sec")
103
+
104
+
105
+
106
+
107
+ ### Main app code ###
108
+ with st.container():
109
+ if st.button("RUN SDG Analysis"):
110
+
111
+ if 'filepath' in st.session_state:
112
+ file_name = st.session_state['filename']
113
+ file_path = st.session_state['filepath']
114
+ classifier = load_sdgClassifier(classifier_name=model_name)
115
+ st.session_state['sdg_classifier'] = classifier
116
+ all_documents = runSDGPreprocessingPipeline(file_name= file_name,
117
+ file_path= file_path, split_by= split_by,
118
+ split_length= split_length,
119
+ split_respect_sentence_boundary= split_respect_sentence_boundary,
120
+ split_overlap= split_overlap, remove_punc= remove_punc)
121
+
122
+ if len(all_documents['documents']) > 100:
123
+ warning_msg = ": This might take sometime, please sit back and relax."
124
+ else:
125
+ warning_msg = ""
126
+
127
+ with st.spinner("Running SDG Classification{}".format(warning_msg)):
128
+
129
+ df, x = sdg_classification(haystack_doc=all_documents['documents'],
130
+ threshold= threshold)
131
+ df = df.drop(['Relevancy'], axis = 1)
132
+ sdg_labels = x.SDG.unique()
133
+ textrank_keyword_list = []
134
+ for label in sdg_labels:
135
+ sdgdata = " ".join(df[df.SDG == label].text.to_list())
136
+ textranklist_ = textrank(textdata=sdgdata, words= top_n)
137
+ if len(textranklist_) > 0:
138
+ textrank_keyword_list.append({'SDG':label, 'TextRank Keywords':",".join(textranklist_)})
139
+ textrank_keywords_df = pd.DataFrame(textrank_keyword_list)
140
+
141
+
142
+ plt.rcParams['font.size'] = 25
143
+ colors = plt.get_cmap('Blues')(np.linspace(0.2, 0.7, len(x)))
144
+ # plot
145
+ fig, ax = plt.subplots()
146
+ ax.pie(x['count'], colors=colors, radius=2, center=(4, 4),
147
+ wedgeprops={"linewidth": 1, "edgecolor": "white"},
148
+ textprops={'fontsize': 14},
149
+ frame=False,labels =list(x.SDG_Num),
150
+ labeldistance=1.2)
151
+ # fig.savefig('temp.png', bbox_inches='tight',dpi= 100)
152
+
153
+
154
+ st.markdown("#### Anything related to SDGs? ####")
155
+
156
+ c4, c5, c6 = st.columns([1,2,2])
157
+
158
+ with c5:
159
+ st.pyplot(fig)
160
+ with c6:
161
+ labeldf = x['SDG_name'].values.tolist()
162
+ labeldf = "<br>".join(labeldf)
163
+ st.markdown(labeldf, unsafe_allow_html=True)
164
+ st.write("")
165
+ st.markdown("###### What keywords are present under SDG classified text? ######")
166
+
167
+ AgGrid(textrank_keywords_df, reload_data = False,
168
+ update_mode="value_changed",
169
+ columns_auto_size_mode = ColumnsAutoSizeMode.FIT_CONTENTS)
170
+ st.write("")
171
+ st.markdown("###### Top few SDG Classified paragraph/text results ######")
172
+
173
+ AgGrid(df, reload_data = False, update_mode="value_changed",
174
+ columns_auto_size_mode = ColumnsAutoSizeMode.FIT_CONTENTS)
175
+ else:
176
+ st.info("🤔 No document found, please try to upload it at the sidebar!")
177
+ logging.warning("Terminated as no document provided")
178
+
179
+