shreyasiv commited on
Commit
cb41426
·
verified ·
1 Parent(s): e72cf90

Upload 3 files

Browse files
Files changed (3) hide show
  1. app.py +201 -0
  2. dependencies.py +128 -0
  3. requirements.txt +285 -0
app.py ADDED
@@ -0,0 +1,201 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import cv2
2
+ import streamlit as st
3
+ import tempfile
4
+ import base64
5
+ import os
6
+ from dotenv import load_dotenv
7
+ from openai import OpenAI
8
+ import assemblyai as aai
9
+ from moviepy.editor import *
10
+ from dependencies import sign_up, fetch_users
11
+
12
+ try:
13
+ users = fetch_users()
14
+ emails = []
15
+ usernames = []
16
+ passwords = []
17
+
18
+ for user in users:
19
+ emails.append(user['key'])
20
+ usernames.append(user['username'])
21
+ passwords.append(user['password'])
22
+
23
+ credentials = {'usernames': {}}
24
+ for index in range(len(emails)):
25
+ credentials['usernames'][usernames[index]] = {'name': emails[index], 'password': passwords[index]}
26
+
27
+
28
+
29
+
30
+
31
+ info, info1 = st.columns(2)
32
+
33
+
34
+
35
+
36
+ except:
37
+ st.success('Refresh Page')
38
+
39
+
40
+ # Load environment variables
41
+ load_dotenv()
42
+ aai.settings.api_key = os.getenv("ASSEMBLYAI_API_KEY")
43
+ OpenAI.api_key = os.getenv("OPENAI_API_KEY")
44
+ client = OpenAI()
45
+
46
+ def main():
47
+ st.title('Insightly Video Content Moderation')
48
+
49
+ # Video upload section
50
+ uploaded_video = st.file_uploader('Upload a video', type=["mp4", "avi", "mov"])
51
+
52
+ if uploaded_video is not None:
53
+ # Save the video to a temp file
54
+ tfile = tempfile.NamedTemporaryFile(delete=False)
55
+ tfile.write(uploaded_video.read())
56
+ video_file_path = tfile.name
57
+ tfile.close()
58
+
59
+ transcriber = aai.Transcriber()
60
+ transcript = transcriber.transcribe(tfile.name)
61
+
62
+ # Process the video and display frames in a grid layout
63
+ base64_frames = video_to_base64_frames(video_file_path)
64
+ display_frame_grid(base64_frames[::30]) # Display every 30th frame in a 3-column grid
65
+
66
+ st.write("Actions:") # Header for the actions/buttons section
67
+
68
+ # Creating four columns to align the buttons
69
+ col1, col2, col3, col4 = st.columns(4)
70
+
71
+ with col1:
72
+ if st.button("Description"):
73
+ st.session_state['description'] = generate_description(base64_frames) if 'description' not in st.session_state else st.session_state['description']
74
+
75
+ with col2:
76
+ if st.button("Frame Description"):
77
+ st.session_state['frame_description'] = generate_frame_description(base64_frames) if 'frame_description' not in st.session_state else st.session_state['frame_description']
78
+
79
+ with col3:
80
+ if st.button("Generate Transcript"):
81
+ st.session_state['transcript'] = transcript.text if 'transcript' not in st.session_state else st.session_state['transcript']
82
+
83
+ with col4:
84
+ if st.button("Category of Video"):
85
+ st.session_state['category'] = generate_category(base64_frames) if 'category' not in st.session_state else st.session_state['category']
86
+
87
+ # If any value exists in session state then display it
88
+ if 'description' in st.session_state and st.session_state['description']:
89
+ st.subheader("Video Description")
90
+ st.write(st.session_state['description'])
91
+
92
+ if 'frame_description' in st.session_state and st.session_state['frame_description']:
93
+ st.subheader("Frame Description")
94
+ st.write(st.session_state['frame_description'])
95
+
96
+ if 'transcript' in st.session_state and st.session_state['transcript']:
97
+ st.subheader("Video Transcript")
98
+ st.write(st.session_state['transcript'])
99
+
100
+ if 'category' in st.session_state and st.session_state['category']:
101
+ st.subheader("Video Category")
102
+ st.write(st.session_state['category'])
103
+
104
+
105
+
106
+
107
+
108
+
109
+ def video_to_base64_frames(video_file_path):
110
+ # Logic to extract all frames from the video and convert them to base64
111
+ video = cv2.VideoCapture(video_file_path)
112
+ base64_frames = []
113
+
114
+ while video.isOpened():
115
+ success, frame = video.read()
116
+ if not success:
117
+ break
118
+
119
+ _, buffer = cv2.imencode('.jpg', frame)
120
+ base64_frame = base64.b64encode(buffer).decode('utf-8')
121
+ base64_frames.append(base64_frame)
122
+
123
+ video.release()
124
+ return base64_frames
125
+
126
+ #########################################
127
+ #Generate Video description
128
+ def generate_description(base64_frames):
129
+ prompt_messages = [
130
+ {
131
+ "role": "user",
132
+ "content": [
133
+ "1. Generate a description for this sequence of video frames in about 90 words.\
134
+ Return the following : 1. List of objects in the video 2. Any restrictive content or sensitive content and if so which frame ",
135
+ *map(lambda x: {"image": x, "resize": 428}, base64_frames[0::30]),
136
+ ],
137
+ },
138
+ ]
139
+ response = client.chat.completions.create(
140
+ model="gpt-4-vision-preview",
141
+ messages=prompt_messages,
142
+ max_tokens=3000,
143
+ )
144
+ return response.choices[0].message.content
145
+
146
+ #Generate frame description
147
+ def generate_frame_description(base64_frames):
148
+ prompt_messages = [
149
+ {
150
+ "role": "user",
151
+ "content": [
152
+ "Describe what is happening in each frame.",
153
+ *map(lambda x: {"image": x, "resize": 428}, base64_frames[0::30]),
154
+ ],
155
+ },
156
+ ]
157
+ response = client.chat.completions.create(
158
+ model="gpt-4-vision-preview",
159
+ messages=prompt_messages,
160
+ max_tokens=3000,
161
+ )
162
+ return response.choices[0].message.content
163
+
164
+
165
+
166
+ #Generate Category of Video
167
+ def generate_category(base64_frames):
168
+ prompt_messages = [
169
+ {
170
+ "role": "user",
171
+ "content": [
172
+ "What category can this video be tagged to?",
173
+ *map(lambda x: {"image": x, "resize": 428}, base64_frames[0::30]),
174
+ ],
175
+ },
176
+ ]
177
+ response = client.chat.completions.create(
178
+ model="gpt-4-vision-preview",
179
+ messages=prompt_messages,
180
+ max_tokens=3000,
181
+ )
182
+ return response.choices[0].message.content
183
+
184
+
185
+
186
+
187
+ ########################
188
+ def display_frame_grid(base64_frames):
189
+ cols_per_row = 3
190
+ n_frames = len(base64_frames)
191
+ for idx in range(0, n_frames, cols_per_row):
192
+ cols = st.columns(cols_per_row)
193
+ for col_index in range(cols_per_row):
194
+ frame_idx = idx + col_index
195
+ if frame_idx < n_frames:
196
+ with cols[col_index]:
197
+ frame = base64_frames[frame_idx]
198
+ st.image(base64.b64decode(frame), caption=f'Frame {frame_idx * 30 + 1}', width=200)
199
+
200
+ if __name__ == '__main__':
201
+ main()
dependencies.py ADDED
@@ -0,0 +1,128 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import streamlit_authenticator as stauth
3
+ import datetime
4
+ import re
5
+ from deta import Deta
6
+
7
+ DETA_KEY = 'YOUR_DETA_KEY'
8
+
9
+ deta = Deta(DETA_KEY)
10
+
11
+ db = deta.Base('YOUR_DETA_BASE')
12
+
13
+
14
+ def insert_user(email, username, password):
15
+ """
16
+ Inserts Users into the DB
17
+ :param email:
18
+ :param username:
19
+ :param password:
20
+ :return User Upon successful Creation:
21
+ """
22
+ date_joined = str(datetime.datetime.now())
23
+
24
+ return db.put({'key': email, 'username': username, 'password': password, 'date_joined': date_joined})
25
+
26
+
27
+ def fetch_users():
28
+ """
29
+ Fetch Users
30
+ :return Dictionary of Users:
31
+ """
32
+ users = db.fetch()
33
+ return users.items
34
+
35
+
36
+ def get_user_emails():
37
+ """
38
+ Fetch User Emails
39
+ :return List of user emails:
40
+ """
41
+ users = db.fetch()
42
+ emails = []
43
+ for user in users.items:
44
+ emails.append(user['key'])
45
+ return emails
46
+
47
+
48
+ def get_usernames():
49
+ """
50
+ Fetch Usernames
51
+ :return List of user usernames:
52
+ """
53
+ users = db.fetch()
54
+ usernames = []
55
+ for user in users.items:
56
+ usernames.append(user['key'])
57
+ return usernames
58
+
59
+
60
+ def validate_email(email):
61
+ """
62
+ Check Email Validity
63
+ :param email:
64
+ :return True if email is valid else False:
65
+ """
66
+ pattern = "^[a-zA-Z0-9-_]+@[a-zA-Z0-9]+\.[a-z]{1,3}$" #tesQQ12@gmail.com
67
+
68
+ if re.match(pattern, email):
69
+ return True
70
+ return False
71
+
72
+
73
+ def validate_username(username):
74
+ """
75
+ Checks Validity of userName
76
+ :param username:
77
+ :return True if username is valid else False:
78
+ """
79
+
80
+ pattern = "^[a-zA-Z0-9]*$"
81
+ if re.match(pattern, username):
82
+ return True
83
+ return False
84
+
85
+
86
+ def sign_up():
87
+ with st.form(key='signup', clear_on_submit=True):
88
+ st.subheader(':green[Sign Up]')
89
+ email = st.text_input(':blue[Email]', placeholder='Enter Your Email')
90
+ username = st.text_input(':blue[Username]', placeholder='Enter Your Username')
91
+ password1 = st.text_input(':blue[Password]', placeholder='Enter Your Password', type='password')
92
+ password2 = st.text_input(':blue[Confirm Password]', placeholder='Confirm Your Password', type='password')
93
+
94
+ if email:
95
+ if validate_email(email):
96
+ if email not in get_user_emails():
97
+ if validate_username(username):
98
+ if username not in get_usernames():
99
+ if len(username) >= 2:
100
+ if len(password1) >= 6:
101
+ if password1 == password2:
102
+ # Add User to DB
103
+ hashed_password = stauth.Hasher([password2]).generate()
104
+ insert_user(email, username, hashed_password[0])
105
+ st.success('Account created successfully!!')
106
+ st.balloons()
107
+ else:
108
+ st.warning('Passwords Do Not Match')
109
+ else:
110
+ st.warning('Password is too Short')
111
+ else:
112
+ st.warning('Username Too short')
113
+ else:
114
+ st.warning('Username Already Exists')
115
+
116
+ else:
117
+ st.warning('Invalid Username')
118
+ else:
119
+ st.warning('Email Already exists!!')
120
+ else:
121
+ st.warning('Invalid Email')
122
+
123
+ btn1, bt2, btn3, btn4, btn5 = st.columns(5)
124
+
125
+ with btn3:
126
+ st.form_submit_button('Sign Up')
127
+
128
+ sign_up()
requirements.txt ADDED
@@ -0,0 +1,285 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ absl-py==1.4.0
2
+ aiofiles==23.1.0
3
+ aiohttp==3.8.4
4
+ aiosignal==1.3.1
5
+ altair==4.2.2
6
+ anyio==3.6.2
7
+ appnope==0.1.3
8
+ argon2-cffi==21.3.0
9
+ argon2-cffi-bindings==21.2.0
10
+ arrow==1.2.3
11
+ asttokens==2.2.1
12
+ astunparse==1.6.3
13
+ async-lru==2.0.2
14
+ async-timeout==4.0.2
15
+ attrs==23.1.0
16
+ awscli==1.27.139
17
+ Babel==2.12.1
18
+ backcall==0.2.0
19
+ bcrypt==4.1.2
20
+ beautifulsoup4==4.12.2
21
+ bibtexparser==1.4.0
22
+ bleach==6.0.0
23
+ blinker==1.6.2
24
+ botocore==1.29.139
25
+ cachetools==5.3.0
26
+ certifi==2023.5.7
27
+ cffi==1.15.1
28
+ charset-normalizer==3.1.0
29
+ click==8.1.3
30
+ colorama==0.4.4
31
+ comm==0.1.3
32
+ contourpy==1.0.7
33
+ cycler==0.11.0
34
+ dash==2.10.2
35
+ dash-core-components==2.0.0
36
+ dash-html-components==2.0.0
37
+ dash-table==5.0.0
38
+ dataclasses-json==0.5.8
39
+ datasets==2.12.0
40
+ debugpy==1.6.7
41
+ decorator==5.1.1
42
+ defusedxml==0.7.1
43
+ deta==1.2.0
44
+ dill==0.3.6
45
+ distlib==0.3.6
46
+ docutils==0.16
47
+ entrypoints==0.4
48
+ environs==9.5.0
49
+ exceptiongroup==1.1.2
50
+ executing==1.2.0
51
+ extra-streamlit-components==0.1.60
52
+ faiss-cpu==1.7.4
53
+ fastapi==0.96.1
54
+ fastjsonschema==2.16.3
55
+ ffmpy==0.3.0
56
+ filelock==3.12.0
57
+ Flask==2.2.5
58
+ flatbuffers==23.5.9
59
+ fonttools==4.39.4
60
+ fqdn==1.5.1
61
+ frozenlist==1.3.3
62
+ fsspec==2023.6.0
63
+ gast==0.4.0
64
+ gitdb==4.0.10
65
+ GitPython==3.1.31
66
+ google-auth==2.18.1
67
+ google-auth-oauthlib==1.0.0
68
+ google-images-download @ git+https://github.com/Joeclinton1/google-images-download.git@e91e6a38ad654877f59edc7894822f5319d75df1
69
+ google-pasta==0.2.0
70
+ gradio==3.34.0
71
+ gradio_client==0.2.6
72
+ greenlet==2.0.2
73
+ grpcio==1.53.0
74
+ grpcio-tools==1.53.0
75
+ h11==0.14.0
76
+ h5py==3.8.0
77
+ httpcore==0.17.2
78
+ httpx==0.24.1
79
+ huggingface-hub==0.15.1
80
+ idna==3.4
81
+ importlib-metadata==6.6.0
82
+ install==1.3.5
83
+ ipykernel==6.23.1
84
+ ipypublish==0.10.12
85
+ ipython==8.13.2
86
+ ipython-genutils==0.2.0
87
+ ipywidgets==8.0.6
88
+ isoduration==20.11.0
89
+ itsdangerous==2.1.2
90
+ jaraco.classes==3.2.3
91
+ jax==0.4.10
92
+ jedi==0.18.2
93
+ Jinja2==3.1.2
94
+ jmespath==1.0.1
95
+ json5==0.9.14
96
+ jsonextended==0.7.11
97
+ jsonpointer==2.3
98
+ jsonschema==4.17.3
99
+ jupyter==1.0.0
100
+ jupyter-console==6.6.3
101
+ jupyter-events==0.6.3
102
+ jupyter-lsp==2.2.0
103
+ jupyter_client==8.2.0
104
+ jupyter_core==5.3.0
105
+ jupyter_server==2.5.0
106
+ jupyter_server_terminals==0.4.4
107
+ jupyterlab==4.0.2
108
+ jupyterlab-pygments==0.2.2
109
+ jupyterlab-widgets==3.0.7
110
+ jupyterlab_server==2.23.0
111
+ jupytext==1.15.0
112
+ keras==2.12.0
113
+ keyring==23.13.1
114
+ kiwisolver==1.4.4
115
+ lab==7.3
116
+ langchain==0.0.184
117
+ libclang==16.0.0
118
+ linkify-it-py==2.0.2
119
+ Markdown==3.4.3
120
+ markdown-it-py==2.2.0
121
+ MarkupSafe==2.1.2
122
+ marshmallow==3.19.0
123
+ marshmallow-enum==1.5.1
124
+ matplotlib==3.7.1
125
+ matplotlib-inline==0.1.6
126
+ mdit-py-plugins==0.3.3
127
+ mdurl==0.1.2
128
+ mistune==2.0.5
129
+ ml-dtypes==0.1.0
130
+ mmh3==4.0.0
131
+ more-itertools==9.1.0
132
+ mpmath==1.3.0
133
+ multidict==6.0.4
134
+ multiprocess==0.70.14
135
+ mypy-extensions==1.0.0
136
+ nbclassic==1.0.0
137
+ nbclient==0.7.4
138
+ nbconvert==7.4.0
139
+ nbformat==5.8.0
140
+ nest-asyncio==1.5.6
141
+ networkx==3.1
142
+ notebook==6.5.4
143
+ notebook_shim==0.2.3
144
+ numexpr==2.8.4
145
+ numpy==1.23.5
146
+ oauthlib==3.2.2
147
+ openai==0.27.6
148
+ openapi-schema-pydantic==1.2.4
149
+ opencv-python==4.7.0.72
150
+ opt-einsum==3.3.0
151
+ ordered-set==4.1.0
152
+ orjson==3.9.1
153
+ outcome==1.2.0
154
+ packaging==23.1
155
+ pandas==2.0.2
156
+ pandocfilters==1.5.0
157
+ panflute==2.3.0
158
+ parso==0.8.3
159
+ pathlib2==2.3.7.post1
160
+ pexpect==4.8.0
161
+ pickleshare==0.7.5
162
+ Pillow==9.5.0
163
+ pkginfo==1.9.6
164
+ platformdirs==3.5.1
165
+ plotly==5.15.0
166
+ progressbar==2.5
167
+ prometheus-client==0.16.0
168
+ prompt-toolkit==3.0.38
169
+ protobuf==3.20.3
170
+ psutil==5.9.5
171
+ ptyprocess==0.7.0
172
+ pure-eval==0.2.2
173
+ pyarrow==12.0.0
174
+ pyasn1==0.5.0
175
+ pyasn1-modules==0.3.0
176
+ pycairo @ file:///private/tmp/py3cairo-20231022-6916-1lfc5vx/pycairo-1.25.1
177
+ pycparser==2.21
178
+ pydantic==1.10.9
179
+ pydeck==0.8.1b0
180
+ pydub==0.25.1
181
+ Pygments==2.15.1
182
+ PyGObject==3.46.0
183
+ pygoogle-image==1.0.0
184
+ PyJWT==2.8.0
185
+ pymilvus==2.2.5
186
+ Pympler==1.0.1
187
+ pyparsing==3.0.9
188
+ PyPDF2==3.0.1
189
+ pyrsistent==0.19.3
190
+ PySocks==1.7.1
191
+ python-dateutil==2.8.2
192
+ python-dotenv==1.0.0
193
+ python-json-logger==2.0.7
194
+ python-magic-bin==0.4.14
195
+ python-multipart==0.0.6
196
+ pytz==2023.3
197
+ pytz-deprecation-shim==0.1.0.post0
198
+ PyYAML==5.4.1
199
+ pyzmq==25.0.2
200
+ qtconsole==5.4.3
201
+ QtPy==2.3.1
202
+ readme-renderer==37.3
203
+ regex==2023.6.3
204
+ reportlab==4.0.4
205
+ requests==2.30.0
206
+ requests-oauthlib==1.3.1
207
+ requests-toolbelt==1.0.0
208
+ responses==0.18.0
209
+ rfc3339-validator==0.1.4
210
+ rfc3986==2.0.0
211
+ rfc3986-validator==0.1.1
212
+ rich==13.4.1
213
+ rsa==4.7.2
214
+ ruamel.yaml==0.17.32
215
+ ruamel.yaml.clib==0.2.7
216
+ s3transfer==0.6.1
217
+ safetensors==0.3.1
218
+ scipy==1.10.1
219
+ selenium==4.10.0
220
+ semantic-version==2.10.0
221
+ Send2Trash==1.8.2
222
+ sentencepiece==0.1.99
223
+ simple-image-download==0.5
224
+ simplejson==3.19.1
225
+ six==1.16.0
226
+ smmap==5.0.0
227
+ sniffio==1.3.0
228
+ sortedcontainers==2.4.0
229
+ soupsieve==2.4.1
230
+ SQLAlchemy==2.0.16
231
+ stack-data==0.6.2
232
+ starlette==0.27.0
233
+ streamlit==1.22.0
234
+ streamlit-authenticator==0.2.3
235
+ sympy==1.12
236
+ tabulate==0.9.0
237
+ tenacity==8.2.2
238
+ tensorboard==2.12.3
239
+ tensorboard-data-server==0.7.0
240
+ tensorflow==2.12.0
241
+ tensorflow-estimator==2.12.0
242
+ tensorflow-io-gcs-filesystem==0.32.0
243
+ termcolor==2.3.0
244
+ terminado==0.17.1
245
+ tiktoken==0.4.0
246
+ tinycss2==1.2.1
247
+ tokenizers==0.13.3
248
+ toml==0.10.2
249
+ toolz==0.12.0
250
+ torch==2.0.1
251
+ torchvision==0.15.2
252
+ tornado==6.3.2
253
+ towhee==1.1.0
254
+ towhee.models==1.1.0
255
+ tqdm==4.65.0
256
+ traitlets==5.9.0
257
+ transformers==4.30.1
258
+ trio==0.22.2
259
+ trio-websocket==0.10.3
260
+ tweepy==4.14.0
261
+ twine==4.0.2
262
+ txt2tags==3.8
263
+ typing-inspect==0.9.0
264
+ typing_extensions==4.5.0
265
+ tzdata==2023.3
266
+ tzlocal==4.3
267
+ uc-micro-py==1.0.2
268
+ ujson==5.7.0
269
+ uri-template==1.2.0
270
+ urllib3==1.26.15
271
+ uvicorn==0.22.0
272
+ validators==0.20.0
273
+ virtualenv==20.23.0
274
+ wcwidth==0.2.6
275
+ webcolors==1.13
276
+ webencodings==0.5.1
277
+ websocket-client==1.5.1
278
+ websockets==11.0.3
279
+ Werkzeug==2.2.3
280
+ widgetsnbextension==4.0.7
281
+ wrapt==1.14.1
282
+ wsproto==1.2.0
283
+ xxhash==3.2.0
284
+ yarl==1.9.2
285
+ zipp==3.15.0