bsenst commited on
Commit
30e2215
·
1 Parent(s): c850600

remove bash calls

Browse files
src/notebooks/Gesetze_im_Internet_Aktualitätendienst.ipynb CHANGED
The diff for this file is too large to render. See raw diff
 
src/notebooks/quotes_scraper.ipynb CHANGED
@@ -39,7 +39,7 @@
39
  ],
40
  "source": [
41
  "# Installieren der benötigten Bibliotheken\n",
42
- "! pip install requests beautifulsoup4 pandas"
43
  ]
44
  },
45
  {
 
39
  ],
40
  "source": [
41
  "# Installieren der benötigten Bibliotheken\n",
42
+ "# ! pip install requests beautifulsoup4 pandas"
43
  ]
44
  },
45
  {
src/notebooks/youtube-transcript-extraction.ipynb CHANGED
@@ -1 +1,290 @@
1
- {"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.14","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[],"dockerImageVersionId":30804,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"! pip install pytube youtube-transcript-api reportlab -q","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-08T19:21:41.981023Z","iopub.execute_input":"2024-12-08T19:21:41.981395Z","iopub.status.idle":"2024-12-08T19:21:56.468283Z","shell.execute_reply.started":"2024-12-08T19:21:41.981364Z","shell.execute_reply":"2024-12-08T19:21:56.466400Z"}},"outputs":[],"execution_count":1},{"cell_type":"code","source":"from pytube import YouTube, Playlist\nfrom youtube_transcript_api import YouTubeTranscriptApi, TranscriptsDisabled, NoTranscriptFound\nfrom reportlab.lib.pagesizes import letter\nfrom reportlab.pdfgen import canvas\nimport os\nimport zipfile","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-08T19:21:56.470984Z","iopub.execute_input":"2024-12-08T19:21:56.471492Z","iopub.status.idle":"2024-12-08T19:21:56.658038Z","shell.execute_reply.started":"2024-12-08T19:21:56.471442Z","shell.execute_reply":"2024-12-08T19:21:56.656869Z"}},"outputs":[],"execution_count":2},{"cell_type":"code","source":"# Funktion zum Extrahieren des Transkripts\ndef get_transcript_with_language_preference(video_id, preferred_languages=(\"en\", \"de\"), prefer_manual=True):\n \"\"\"\n Extracts a transcript based on the preferred languages.\n If no transcript is found in the preferred languages, it checks available languages.\n \n Args:\n video_id (str): The ID of the YouTube video.\n preferred_languages (tuple): Preferred languages in order of priority.\n prefer_manual (bool): Whether to prefer manually created transcripts over auto-generated ones.\n \n Returns:\n str: The transcript or an error message if not found.\n \"\"\"\n try:\n # Fetch the transcript list for the video\n transcript_list = YouTubeTranscriptApi.list_transcripts(video_id)\n \n # Filter transcripts for preferred languages and filter by manual or auto-generated type\n transcript = None\n for lang in preferred_languages:\n try:\n if prefer_manual:\n transcript = transcript_list.find_transcript([lang]) # Prefer manually created transcripts\n else:\n transcript = transcript_list.find_transcript([lang], filter_types=['generated']) # Prefer auto-generated transcripts\n \n # If a valid transcript is found, break the loop\n if transcript:\n break\n except Exception as e:\n # Continue if no transcript found for this language\n continue\n\n # If no transcript was found in the preferred languages, return available transcripts\n if not transcript:\n available_transcripts = transcript_list\n available_languages = {t.language_code: t.language for t in available_transcripts}\n \n error_message = f\"Kein Transkript in den gewünschten Sprachen {preferred_languages} gefunden.\\n\"\n error_message += \"Verfügbare Sprachen:\\n\"\n error_message += \"\\n\".join([f\"- {code} ({lang})\" for code, lang in available_languages.items()])\n return error_message\n\n # Get the actual transcript text\n transcript_data = transcript.fetch()\n return \"\\n\".join([entry[\"text\"] for entry in transcript_data])\n\n except TranscriptsDisabled:\n return \"Transkripte sind für dieses Video deaktiviert.\"\n except NoTranscriptFound:\n return \"Es wurden keine Transkripte für dieses Video gefunden.\"\n except Exception as e:\n return f\"Allgemeiner Fehler beim Abrufen des Transkripts: {e}\"\n\n# Funktion zum Erstellen eines PDFs\ndef save_transcript_as_pdf_with_pagination(title, transcript):\n \"\"\"\n Speichert ein Transkript als PDF mit Seitenumbrüchen, wenn der Text zu lang ist.\n Args:\n title (str): Der Titel, der als Dateiname verwendet wird.\n transcript (str): Der Text des Transkripts.\n Returns:\n str: Der Name der gespeicherten PDF-Datei.\n \"\"\"\n # Sicheren Dateinamen erstellen\n filename = f\"{title}.pdf\".replace(\"/\", \"_\").replace(\"\\\\\", \"_\")\n\n # PDF-Einstellungen\n c = canvas.Canvas(filename, pagesize=letter)\n width, height = letter\n\n # Schrift- und Layout-Einstellungen\n margin = 50\n line_height = 14\n max_lines_per_page = int((height - 2 * margin) / line_height) - 2 # Platz für Titel und Seitenrand\n\n # Titel schreiben\n c.setFont(\"Helvetica-Bold\", 16)\n c.drawString(margin, height - margin, title)\n\n # Textblock initialisieren\n c.setFont(\"Helvetica\", 12)\n y_position = height - margin - 30 # Platz für Titel\n\n # Text Zeile für Zeile schreiben\n lines = transcript.split(\"\\n\")\n line_count = 0\n\n for line in lines:\n if line_count >= max_lines_per_page: # Neue Seite bei Bedarf\n c.showPage()\n c.setFont(\"Helvetica-Bold\", 16)\n c.drawString(margin, height - margin, title)\n c.setFont(\"Helvetica\", 12)\n y_position = height - margin - 30\n line_count = 0\n \n c.drawString(margin, y_position, line)\n y_position -= line_height\n line_count += 1\n\n # PDF speichern\n c.save()\n return filename","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-08T19:21:56.659720Z","iopub.execute_input":"2024-12-08T19:21:56.660152Z","iopub.status.idle":"2024-12-08T19:21:56.675204Z","shell.execute_reply.started":"2024-12-08T19:21:56.660092Z","shell.execute_reply":"2024-12-08T19:21:56.674023Z"}},"outputs":[],"execution_count":3},{"cell_type":"code","source":"# Eingabe der URLs\nurls = \"https://www.youtube.com/playlist?list=PLf8HAovJg47MN7bswKf73pffom98Fx8Q8\"\n\npdf_filenames = [] # Liste zur Speicherung der PDF-Dateinamen\n\n# URL-Liste verarbeiten\nurl_list = urls.splitlines()\nfor url in url_list:\n url = url.strip()\n if not url:\n continue\n\n # Playlist-URL oder Video-URL?\n if \"playlist\" in url:\n playlist = Playlist(url)\n video_urls = playlist.video_urls\n else:\n video_urls = [url]\n\n for video_url in video_urls:\n try:\n yt = YouTube(video_url)\n video_id = yt.video_id\n title = video_id\n print(f\"Verarbeite Video: {title}\")\n\n transcript = get_transcript_with_language_preference(video_id)\n\n # Speichern des Transkripts als PDF\n pdf_filename = save_transcript_as_pdf_with_pagination(title, transcript)\n pdf_filenames.append(pdf_filename)\n print(f\"Transkript für '{title}' gespeichert als {pdf_filename}.\")\n\n except Exception as e:\n print(f\"Fehler bei der Verarbeitung von {video_url}: {e}\")\n\n# Erstellen eines ZIP-Archivs\nzip_filename = \"transcripts.zip\"\nwith zipfile.ZipFile(zip_filename, 'w') as zipf:\n for pdf_filename in pdf_filenames:\n zipf.write(pdf_filename)\n os.remove(pdf_filename) # Lösche die PDF-Dateien nach dem Hinzufügen zum ZIP-Archiv\n\nprint(f\"ZIP-Archiv '{zip_filename}' wurde erstellt.\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-08T19:32:38.175463Z","iopub.execute_input":"2024-12-08T19:32:38.175946Z","iopub.status.idle":"2024-12-08T19:32:40.711451Z","shell.execute_reply.started":"2024-12-08T19:32:38.175908Z","shell.execute_reply":"2024-12-08T19:32:40.710172Z"}},"outputs":[{"name":"stdout","text":"Verarbeite Video: B7uzC96-930\nTranskript für 'B7uzC96-930' gespeichert als B7uzC96-930.pdf.\nVerarbeite Video: 4QeKMnd8B7Q\nTranskript für '4QeKMnd8B7Q' gespeichert als 4QeKMnd8B7Q.pdf.\nVerarbeite Video: 30wxAJ5uyzs\nTranskript für '30wxAJ5uyzs' gespeichert als 30wxAJ5uyzs.pdf.\nZIP-Archiv 'transcripts.zip' wurde erstellt.\n","output_type":"stream"}],"execution_count":15},{"cell_type":"code","source":"# os.remove(zip_filename)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-08T19:30:12.194141Z","iopub.execute_input":"2024-12-08T19:30:12.194584Z","iopub.status.idle":"2024-12-08T19:30:12.200364Z","shell.execute_reply.started":"2024-12-08T19:30:12.194548Z","shell.execute_reply":"2024-12-08T19:30:12.198944Z"}},"outputs":[],"execution_count":13}]}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "code",
5
+ "execution_count": 1,
6
+ "metadata": {
7
+ "execution": {
8
+ "iopub.execute_input": "2024-12-08T19:21:41.981395Z",
9
+ "iopub.status.busy": "2024-12-08T19:21:41.981023Z",
10
+ "iopub.status.idle": "2024-12-08T19:21:56.468283Z",
11
+ "shell.execute_reply": "2024-12-08T19:21:56.466400Z",
12
+ "shell.execute_reply.started": "2024-12-08T19:21:41.981364Z"
13
+ },
14
+ "trusted": true
15
+ },
16
+ "outputs": [],
17
+ "source": [
18
+ "# ! pip install pytube youtube-transcript-api reportlab -q"
19
+ ]
20
+ },
21
+ {
22
+ "cell_type": "code",
23
+ "execution_count": 2,
24
+ "metadata": {
25
+ "execution": {
26
+ "iopub.execute_input": "2024-12-08T19:21:56.471492Z",
27
+ "iopub.status.busy": "2024-12-08T19:21:56.470984Z",
28
+ "iopub.status.idle": "2024-12-08T19:21:56.658038Z",
29
+ "shell.execute_reply": "2024-12-08T19:21:56.656869Z",
30
+ "shell.execute_reply.started": "2024-12-08T19:21:56.471442Z"
31
+ },
32
+ "trusted": true
33
+ },
34
+ "outputs": [],
35
+ "source": [
36
+ "from pytube import YouTube, Playlist\n",
37
+ "from youtube_transcript_api import YouTubeTranscriptApi, TranscriptsDisabled, NoTranscriptFound\n",
38
+ "from reportlab.lib.pagesizes import letter\n",
39
+ "from reportlab.pdfgen import canvas\n",
40
+ "import os\n",
41
+ "import zipfile"
42
+ ]
43
+ },
44
+ {
45
+ "cell_type": "code",
46
+ "execution_count": 3,
47
+ "metadata": {
48
+ "execution": {
49
+ "iopub.execute_input": "2024-12-08T19:21:56.660152Z",
50
+ "iopub.status.busy": "2024-12-08T19:21:56.659720Z",
51
+ "iopub.status.idle": "2024-12-08T19:21:56.675204Z",
52
+ "shell.execute_reply": "2024-12-08T19:21:56.674023Z",
53
+ "shell.execute_reply.started": "2024-12-08T19:21:56.660092Z"
54
+ },
55
+ "trusted": true
56
+ },
57
+ "outputs": [],
58
+ "source": [
59
+ "# Funktion zum Extrahieren des Transkripts\n",
60
+ "def get_transcript_with_language_preference(video_id, preferred_languages=(\"en\", \"de\"), prefer_manual=True):\n",
61
+ " \"\"\"\n",
62
+ " Extracts a transcript based on the preferred languages.\n",
63
+ " If no transcript is found in the preferred languages, it checks available languages.\n",
64
+ " \n",
65
+ " Args:\n",
66
+ " video_id (str): The ID of the YouTube video.\n",
67
+ " preferred_languages (tuple): Preferred languages in order of priority.\n",
68
+ " prefer_manual (bool): Whether to prefer manually created transcripts over auto-generated ones.\n",
69
+ " \n",
70
+ " Returns:\n",
71
+ " str: The transcript or an error message if not found.\n",
72
+ " \"\"\"\n",
73
+ " try:\n",
74
+ " # Fetch the transcript list for the video\n",
75
+ " transcript_list = YouTubeTranscriptApi.list_transcripts(video_id)\n",
76
+ " \n",
77
+ " # Filter transcripts for preferred languages and filter by manual or auto-generated type\n",
78
+ " transcript = None\n",
79
+ " for lang in preferred_languages:\n",
80
+ " try:\n",
81
+ " if prefer_manual:\n",
82
+ " transcript = transcript_list.find_transcript([lang]) # Prefer manually created transcripts\n",
83
+ " else:\n",
84
+ " transcript = transcript_list.find_transcript([lang], filter_types=['generated']) # Prefer auto-generated transcripts\n",
85
+ " \n",
86
+ " # If a valid transcript is found, break the loop\n",
87
+ " if transcript:\n",
88
+ " break\n",
89
+ " except Exception as e:\n",
90
+ " # Continue if no transcript found for this language\n",
91
+ " continue\n",
92
+ "\n",
93
+ " # If no transcript was found in the preferred languages, return available transcripts\n",
94
+ " if not transcript:\n",
95
+ " available_transcripts = transcript_list\n",
96
+ " available_languages = {t.language_code: t.language for t in available_transcripts}\n",
97
+ " \n",
98
+ " error_message = f\"Kein Transkript in den gewünschten Sprachen {preferred_languages} gefunden.\\n\"\n",
99
+ " error_message += \"Verfügbare Sprachen:\\n\"\n",
100
+ " error_message += \"\\n\".join([f\"- {code} ({lang})\" for code, lang in available_languages.items()])\n",
101
+ " return error_message\n",
102
+ "\n",
103
+ " # Get the actual transcript text\n",
104
+ " transcript_data = transcript.fetch()\n",
105
+ " return \"\\n\".join([entry[\"text\"] for entry in transcript_data])\n",
106
+ "\n",
107
+ " except TranscriptsDisabled:\n",
108
+ " return \"Transkripte sind für dieses Video deaktiviert.\"\n",
109
+ " except NoTranscriptFound:\n",
110
+ " return \"Es wurden keine Transkripte für dieses Video gefunden.\"\n",
111
+ " except Exception as e:\n",
112
+ " return f\"Allgemeiner Fehler beim Abrufen des Transkripts: {e}\"\n",
113
+ "\n",
114
+ "# Funktion zum Erstellen eines PDFs\n",
115
+ "def save_transcript_as_pdf_with_pagination(title, transcript):\n",
116
+ " \"\"\"\n",
117
+ " Speichert ein Transkript als PDF mit Seitenumbrüchen, wenn der Text zu lang ist.\n",
118
+ " Args:\n",
119
+ " title (str): Der Titel, der als Dateiname verwendet wird.\n",
120
+ " transcript (str): Der Text des Transkripts.\n",
121
+ " Returns:\n",
122
+ " str: Der Name der gespeicherten PDF-Datei.\n",
123
+ " \"\"\"\n",
124
+ " # Sicheren Dateinamen erstellen\n",
125
+ " filename = f\"{title}.pdf\".replace(\"/\", \"_\").replace(\"\\\\\", \"_\")\n",
126
+ "\n",
127
+ " # PDF-Einstellungen\n",
128
+ " c = canvas.Canvas(filename, pagesize=letter)\n",
129
+ " width, height = letter\n",
130
+ "\n",
131
+ " # Schrift- und Layout-Einstellungen\n",
132
+ " margin = 50\n",
133
+ " line_height = 14\n",
134
+ " max_lines_per_page = int((height - 2 * margin) / line_height) - 2 # Platz für Titel und Seitenrand\n",
135
+ "\n",
136
+ " # Titel schreiben\n",
137
+ " c.setFont(\"Helvetica-Bold\", 16)\n",
138
+ " c.drawString(margin, height - margin, title)\n",
139
+ "\n",
140
+ " # Textblock initialisieren\n",
141
+ " c.setFont(\"Helvetica\", 12)\n",
142
+ " y_position = height - margin - 30 # Platz für Titel\n",
143
+ "\n",
144
+ " # Text Zeile für Zeile schreiben\n",
145
+ " lines = transcript.split(\"\\n\")\n",
146
+ " line_count = 0\n",
147
+ "\n",
148
+ " for line in lines:\n",
149
+ " if line_count >= max_lines_per_page: # Neue Seite bei Bedarf\n",
150
+ " c.showPage()\n",
151
+ " c.setFont(\"Helvetica-Bold\", 16)\n",
152
+ " c.drawString(margin, height - margin, title)\n",
153
+ " c.setFont(\"Helvetica\", 12)\n",
154
+ " y_position = height - margin - 30\n",
155
+ " line_count = 0\n",
156
+ " \n",
157
+ " c.drawString(margin, y_position, line)\n",
158
+ " y_position -= line_height\n",
159
+ " line_count += 1\n",
160
+ "\n",
161
+ " # PDF speichern\n",
162
+ " c.save()\n",
163
+ " return filename"
164
+ ]
165
+ },
166
+ {
167
+ "cell_type": "code",
168
+ "execution_count": 15,
169
+ "metadata": {
170
+ "execution": {
171
+ "iopub.execute_input": "2024-12-08T19:32:38.175946Z",
172
+ "iopub.status.busy": "2024-12-08T19:32:38.175463Z",
173
+ "iopub.status.idle": "2024-12-08T19:32:40.711451Z",
174
+ "shell.execute_reply": "2024-12-08T19:32:40.710172Z",
175
+ "shell.execute_reply.started": "2024-12-08T19:32:38.175908Z"
176
+ },
177
+ "trusted": true
178
+ },
179
+ "outputs": [
180
+ {
181
+ "name": "stdout",
182
+ "output_type": "stream",
183
+ "text": [
184
+ "Verarbeite Video: B7uzC96-930\n",
185
+ "Transkript für 'B7uzC96-930' gespeichert als B7uzC96-930.pdf.\n",
186
+ "Verarbeite Video: 4QeKMnd8B7Q\n",
187
+ "Transkript für '4QeKMnd8B7Q' gespeichert als 4QeKMnd8B7Q.pdf.\n",
188
+ "Verarbeite Video: 30wxAJ5uyzs\n",
189
+ "Transkript für '30wxAJ5uyzs' gespeichert als 30wxAJ5uyzs.pdf.\n",
190
+ "ZIP-Archiv 'transcripts.zip' wurde erstellt.\n"
191
+ ]
192
+ }
193
+ ],
194
+ "source": [
195
+ "# Eingabe der URLs\n",
196
+ "urls = \"https://www.youtube.com/playlist?list=PLf8HAovJg47MN7bswKf73pffom98Fx8Q8\"\n",
197
+ "\n",
198
+ "pdf_filenames = [] # Liste zur Speicherung der PDF-Dateinamen\n",
199
+ "\n",
200
+ "# URL-Liste verarbeiten\n",
201
+ "url_list = urls.splitlines()\n",
202
+ "for url in url_list:\n",
203
+ " url = url.strip()\n",
204
+ " if not url:\n",
205
+ " continue\n",
206
+ "\n",
207
+ " # Playlist-URL oder Video-URL?\n",
208
+ " if \"playlist\" in url:\n",
209
+ " playlist = Playlist(url)\n",
210
+ " video_urls = playlist.video_urls\n",
211
+ " else:\n",
212
+ " video_urls = [url]\n",
213
+ "\n",
214
+ " for video_url in video_urls:\n",
215
+ " try:\n",
216
+ " yt = YouTube(video_url)\n",
217
+ " video_id = yt.video_id\n",
218
+ " title = video_id\n",
219
+ " print(f\"Verarbeite Video: {title}\")\n",
220
+ "\n",
221
+ " transcript = get_transcript_with_language_preference(video_id)\n",
222
+ "\n",
223
+ " # Speichern des Transkripts als PDF\n",
224
+ " pdf_filename = save_transcript_as_pdf_with_pagination(title, transcript)\n",
225
+ " pdf_filenames.append(pdf_filename)\n",
226
+ " print(f\"Transkript für '{title}' gespeichert als {pdf_filename}.\")\n",
227
+ "\n",
228
+ " except Exception as e:\n",
229
+ " print(f\"Fehler bei der Verarbeitung von {video_url}: {e}\")\n",
230
+ "\n",
231
+ "# Erstellen eines ZIP-Archivs\n",
232
+ "zip_filename = \"transcripts.zip\"\n",
233
+ "with zipfile.ZipFile(zip_filename, 'w') as zipf:\n",
234
+ " for pdf_filename in pdf_filenames:\n",
235
+ " zipf.write(pdf_filename)\n",
236
+ " os.remove(pdf_filename) # Lösche die PDF-Dateien nach dem Hinzufügen zum ZIP-Archiv\n",
237
+ "\n",
238
+ "print(f\"ZIP-Archiv '{zip_filename}' wurde erstellt.\")"
239
+ ]
240
+ },
241
+ {
242
+ "cell_type": "code",
243
+ "execution_count": 13,
244
+ "metadata": {
245
+ "execution": {
246
+ "iopub.execute_input": "2024-12-08T19:30:12.194584Z",
247
+ "iopub.status.busy": "2024-12-08T19:30:12.194141Z",
248
+ "iopub.status.idle": "2024-12-08T19:30:12.200364Z",
249
+ "shell.execute_reply": "2024-12-08T19:30:12.198944Z",
250
+ "shell.execute_reply.started": "2024-12-08T19:30:12.194548Z"
251
+ },
252
+ "trusted": true
253
+ },
254
+ "outputs": [],
255
+ "source": [
256
+ "# os.remove(zip_filename)"
257
+ ]
258
+ }
259
+ ],
260
+ "metadata": {
261
+ "kaggle": {
262
+ "accelerator": "none",
263
+ "dataSources": [],
264
+ "dockerImageVersionId": 30804,
265
+ "isGpuEnabled": false,
266
+ "isInternetEnabled": true,
267
+ "language": "python",
268
+ "sourceType": "notebook"
269
+ },
270
+ "kernelspec": {
271
+ "display_name": "Python 3",
272
+ "language": "python",
273
+ "name": "python3"
274
+ },
275
+ "language_info": {
276
+ "codemirror_mode": {
277
+ "name": "ipython",
278
+ "version": 3
279
+ },
280
+ "file_extension": ".py",
281
+ "mimetype": "text/x-python",
282
+ "name": "python",
283
+ "nbconvert_exporter": "python",
284
+ "pygments_lexer": "ipython3",
285
+ "version": "3.10.14"
286
+ }
287
+ },
288
+ "nbformat": 4,
289
+ "nbformat_minor": 4
290
+ }