emvecchi commited on
Commit
c43652c
1 Parent(s): ff941c1

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +277 -0
app.py ADDED
@@ -0,0 +1,277 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import os
3
+ from dataclasses import dataclass
4
+ from typing import List, Optional
5
+ from PIL import Image
6
+
7
+ import pandas as pd
8
+ import streamlit as st
9
+ from huggingface_hub import HfFileSystem
10
+
11
+
12
+ @dataclass
13
+ class Field:
14
+ type: str
15
+ title: str
16
+ name: str = None
17
+ help: Optional[str] = None
18
+ children: Optional[List['Field']] = None
19
+
20
+
21
+ # Function to get user ID from URL
22
+ def get_user_id_from_url():
23
+ user_id = st.query_params.get("user_id", "")
24
+ return user_id
25
+
26
+
27
+ HF_TOKEN = os.environ.get("HF_TOKEN_WRITE")
28
+ print("is none?", HF_TOKEN is None)
29
+ hf_fs = HfFileSystem(token=HF_TOKEN)
30
+ input_repo_path = 'datasets/emvecchi/annotate-pilot'
31
+ output_repo_path = 'datasets/emvecchi/annotate-pilot'
32
+ to_annotate_file_name = 'to_annotate.csv' # CSV file to annotate
33
+ COLS_TO_SAVE = ['comment_id']
34
+
35
+ fields: List[Field] = [
36
+ Field(name="topic", type="input_col", title="**Topic:**"),
37
+ Field(name="parent_comment", type="input_col", title="**Preceeding Comment:**"),
38
+ Field(name="comment", type="input_col", title="**Comment:**"),
39
+ Field(name="image_name", type="input_col", title="**Visualization of high contributing properties:**"),
40
+
41
+ #Field(type="container", title="", children=[
42
+ # Field(name="reply_fitting", type="slider",
43
+ # title="Is the reply comment **fitting and does it make sense** as a reply to the preceding comment?"),
44
+ # Field(name="actions_clear", type="slider",
45
+ # title="**Actions clarity**: Does the reply make it clear what actions need to be done?"),
46
+ #]),
47
+ #Field(type="expander",
48
+ # title="Expand and fill-out this section if you see **issues in the original comment**",
49
+ # children=[
50
+ # Field(name="issues_in_comment", type="slider",
51
+ # title="Do **you see some issues** in the original comment?"),
52
+ # Field(name="moderator_spotted", type="slider",
53
+ # title="Based on the reply, has the **moderator spotted the issues** in the original comment?"),
54
+ # Field(name="reply_addresses_issues", type="slider",
55
+ # title="How well does the reply **address those issues**?")
56
+ # ]),
57
+
58
+ #Field(type="container", title="**Score the following properties of the moderator comment?**", children=[
59
+ # Field(name="neutrality", type="slider", title="Neutrality",
60
+ # help='Remain Neutral on the topic and on the Comment Substance and Commenter’s Viewpoint. The reply shouldn’t give away the opinion of the moderator on the topic or comment. '),
61
+ # # FieldDict(name="attitude", type="slider", title="Attitude", help=''),
62
+ # Field(name="clarity", type="slider", title="Clarity",
63
+ # help="Plain language, simple, clear, avoid overwhelming the user e.g. too many questions"),
64
+ # Field(name="curiosity", type="slider", title="Curiosity",
65
+ # help="Moderators should model a spirit of inquiry and a desire to learn from and understand commenter’s experience and views. Try to be interested in the bases upon which each commenter stakes his or her claims and the lines of reasoning that has led each commenter to those particular conclusions."),
66
+ # # TODO
67
+ # Field(name="bias", type="slider", title="Bias",
68
+ # help="Does the reply show some biases towards the commenter? Are there stereotypes or prejudices?"),
69
+ # Field(name="encouraging", type="slider", title="Encouraging",
70
+ # help="Welcoming, encouraging and acknowledging. Avoid Evaluative and/or Condescending Responses"),
71
+ #]),
72
+ #
73
+ Field(name="other_comments", type="text", title="Further comments: free text"),
74
+ ]
75
+ INPUT_FIELD_DEFAULT_VALUES = {'slider': 0,
76
+ 'text': None,
77
+ 'textarea': None,
78
+ 'checkbox': False}
79
+ SHOW_HELP_ICON = False
80
+
81
+ def read_data(_path):
82
+ with hf_fs.open(input_repo_path + '/' + _path) as f:
83
+ return pd.read_csv(f)
84
+
85
+
86
+ def read_saved_data():
87
+ _path = get_path()
88
+ if hf_fs.exists(output_repo_path + '/' + _path):
89
+ with hf_fs.open(output_repo_path + '/' + _path) as f:
90
+ try:
91
+ return json.load(f)
92
+ except json.JSONDecodeError as e:
93
+ print(e)
94
+ return None
95
+
96
+
97
+ # Write a remote file
98
+ def save_data(data):
99
+ hf_fs.mkdir(f"{output_repo_path}/{data['user_id']}")
100
+ with hf_fs.open(f"{output_repo_path}/{get_path()}", "w") as f:
101
+ f.write(json.dumps(data))
102
+
103
+
104
+ def get_path():
105
+ return f"{st.session_state.user_id}/{st.session_state.current_index}.json"
106
+
107
+
108
+ #def display_image(image_path):
109
+ # st.image(image_path, caption='Visualization of high contributing properties', use_column_width=True)
110
+
111
+ def display_image(image_path):
112
+ with hf_fs.open(image_path) as f:
113
+ img = Image.open(f)
114
+ st.image(img, caption='Visualization of high contributing properties', use_column_width=True)
115
+
116
+ #################################### Streamlit App ####################################
117
+
118
+ # Function to navigate rows
119
+ def navigate(index_change):
120
+ st.session_state.current_index += index_change
121
+ print(st.session_state.current_index)
122
+ # https://discuss.streamlit.io/t/click-twice-on-button-for-changing-state/45633/2
123
+ st.rerun()
124
+
125
+
126
+ def show_field(f: Field, index: int):
127
+ if f.type not in INPUT_FIELD_DEFAULT_VALUES.keys():
128
+ match f.type:
129
+ case 'input_col':
130
+ st.write(f.title)
131
+ if f.name == 'image_name':
132
+ st.write(f.title)
133
+ image_name = st.session_state.data.iloc[index][f.name]
134
+ if image_name: # Ensure the image name is not empty
135
+ image_path = os.path.join(input_repo_path, 'images', image_name)
136
+ display_image(image_path)
137
+ else:
138
+ st.write(st.session_state.data.iloc[index][f.name])
139
+ case 'markdown':
140
+ st.markdown(f.title)
141
+ case 'expander' | 'container':
142
+ with (st.expander(f.title) if f.type == 'expander' else st.container(border=True)):
143
+ if f.type == 'container':
144
+ st.markdown(f.title)
145
+ for child in f.children:
146
+ show_field(child, index)
147
+ else:
148
+ key = f.name + str(index)
149
+ value = st.session_state.default_values[f.name] = data_collected[f.name] if data_collected else \
150
+ INPUT_FIELD_DEFAULT_VALUES[f.type]
151
+ if not SHOW_HELP_ICON:
152
+ f.title = f'**{f.title}**\n\n{f.help}' if f.help else f.title
153
+ f.help = None
154
+ match f.type:
155
+ case 'checkbox':
156
+ st.session_state.data_inputs[f.name] = st.checkbox(f.title,
157
+ key=key,
158
+ value=value, help=f.help)
159
+ case 'slider':
160
+ st.session_state.data_inputs[f.name] = st.slider(f.title,
161
+ min_value=0, max_value=100, step=25,
162
+ key=key,
163
+ value=value, help=f.help)
164
+ case 'text':
165
+ st.session_state.data_inputs[f.name] = st.text_input(f.title, key=key, value=value)
166
+ case 'textarea':
167
+ st.session_state.data_inputs[f.name] = st.text_area(f.title, key=key, value=value)
168
+
169
+
170
+
171
+
172
+ # st.set_page_config(layout='wide')
173
+ # Title of the app
174
+ st.title("Moderation Prediction")
175
+
176
+ st.markdown(
177
+ """<style>
178
+ div[data-testid="stMarkdownContainer"] > p {
179
+ font-size: 1rem;
180
+ }
181
+ </style>
182
+ """, unsafe_allow_html=True)
183
+
184
+ st.markdown(
185
+ """<details open>
186
+ <summary>Annotation Guidelines</summary>
187
+
188
+ some guidelines here
189
+ </details>
190
+ """, unsafe_allow_html=True)
191
+
192
+ # Load the data to annotate
193
+ if 'data' not in st.session_state:
194
+ st.session_state.data = read_data(to_annotate_file_name)
195
+
196
+ # Initialize the current index
197
+ if 'current_index' not in st.session_state:
198
+ st.session_state.current_index = -1
199
+
200
+ if st.session_state.current_index == -1:
201
+ user_id_from_url = get_user_id_from_url()
202
+ if user_id_from_url:
203
+ st.session_state.user_id = user_id_from_url
204
+ navigate(1)
205
+ else:
206
+ st.session_state.user_id = st.text_input('Please enter your user ID to proceed', value=user_id_from_url)
207
+ if st.button("Next"):
208
+ navigate(1)
209
+
210
+ elif st.session_state.current_index < len(st.session_state.data):
211
+ st.write(f"username is {st.session_state.user_id}")
212
+
213
+ # Creating the form
214
+ with st.form("feedback_form"):
215
+ index = st.session_state.current_index
216
+ data_collected = read_saved_data()
217
+ st.session_state.default_values = {}
218
+ st.session_state.data_inputs = {}
219
+
220
+ for field in fields:
221
+ if field.name not in st.session_state.data.columns:
222
+ # Field doesn't exist in input dataframe, add it with a default value
223
+ st.session_state.data_inputs[field.name] = None
224
+ show_field(field, index)
225
+ else:
226
+ show_field(field, index)
227
+
228
+ submitted = st.form_submit_button("Submit")
229
+ if submitted:
230
+ with st.spinner(text="saving"):
231
+ save_data({
232
+ 'user_id': st.session_state.user_id,
233
+ 'index': st.session_state.current_index,
234
+ **st.session_state.data.iloc[index][COLS_TO_SAVE].to_dict(),
235
+ **st.session_state.data_inputs
236
+ })
237
+ st.success("Feedback submitted successfully!")
238
+ navigate(1)
239
+
240
+ else:
241
+ st.write("Finished all data points!")
242
+
243
+ # Navigation buttons
244
+ if st.session_state.current_index > 0:
245
+ if st.button("Previous"):
246
+ with st.spinner(text="in progress"):
247
+ navigate(-1)
248
+ if 0 <= st.session_state.current_index < len(st.session_state.data):
249
+ st.write(f"Page {st.session_state.current_index + 1} out of {len(st.session_state.data)}")
250
+
251
+ # disable text input enter to submit
252
+ # https://discuss.streamlit.io/t/text-input-how-to-disable-press-enter-to-apply/14457/6
253
+ import streamlit.components.v1 as components
254
+
255
+ components.html(
256
+ """
257
+ <script>
258
+ const inputs = window.parent.document.querySelectorAll('input');
259
+ inputs.forEach(input => {
260
+ input.addEventListener('keydown', function(event) {
261
+ if (event.key === 'Enter') {
262
+ event.preventDefault();
263
+ }
264
+ });
265
+ });
266
+ </script>
267
+ """,
268
+ height=0
269
+ )
270
+ st.markdown(
271
+ """<style>
272
+ div[data-testid="InputInstructions"] {
273
+ visibility: hidden;
274
+ }
275
+ </style>""", unsafe_allow_html=True
276
+ )
277
+