project-ryland 1.3.8__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (24) hide show
  1. project_ryland-1.3.8/LICENSE +21 -0
  2. project_ryland-1.3.8/PKG-INFO +92 -0
  3. project_ryland-1.3.8/README.md +65 -0
  4. project_ryland-1.3.8/project_ryland/__init__.py +0 -0
  5. project_ryland-1.3.8/project_ryland/data_utils/__init__.py +0 -0
  6. project_ryland-1.3.8/project_ryland/data_utils/analysis_utils.py +14 -0
  7. project_ryland-1.3.8/project_ryland/data_utils/io_utils.py +61 -0
  8. project_ryland-1.3.8/project_ryland/data_utils/keyword_mappings.py +330 -0
  9. project_ryland-1.3.8/project_ryland/data_utils/note_filter_utils.py +352 -0
  10. project_ryland-1.3.8/project_ryland/data_utils/text_extraction_utils.py +274 -0
  11. project_ryland-1.3.8/project_ryland/llm_utils/__init__.py +0 -0
  12. project_ryland-1.3.8/project_ryland/llm_utils/llm_config.py +41 -0
  13. project_ryland-1.3.8/project_ryland/llm_utils/llm_generation_utils.py +602 -0
  14. project_ryland-1.3.8/project_ryland/llm_utils/llm_prompt_gallery/config_llm_prompts.yaml +66 -0
  15. project_ryland-1.3.8/project_ryland/llm_utils/llm_prompt_gallery/gwas_symptoms_prompt_v1.txt +38 -0
  16. project_ryland-1.3.8/project_ryland/llm_utils/llm_prompt_gallery/nano_prompt_imaging_v1.txt +5 -0
  17. project_ryland-1.3.8/project_ryland/llm_utils/llm_prompt_gallery/nano_prompt_imaging_v2.txt +6 -0
  18. project_ryland-1.3.8/project_ryland/llm_utils/llm_prompt_gallery/nano_prompt_pathology_v1.txt +3 -0
  19. project_ryland-1.3.8/project_ryland/llm_utils/llm_prompt_gallery/nano_prompt_pathology_v2.txt +5 -0
  20. project_ryland-1.3.8/project_ryland/llm_utils/llm_prompt_gallery/nano_prompt_progress_notes_v1.txt +22 -0
  21. project_ryland-1.3.8/project_ryland/llm_utils/llm_prompt_gallery/test_prompt.txt +1 -0
  22. project_ryland-1.3.8/project_ryland/llm_utils/llm_prompt_structures/__init__.py +0 -0
  23. project_ryland-1.3.8/project_ryland/llm_utils/llm_prompt_structures/prompt_structs.py +129 -0
  24. project_ryland-1.3.8/pyproject.toml +64 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Justin Vinh
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,92 @@
1
+ Metadata-Version: 2.4
2
+ Name: project_ryland
3
+ Version: 1.3.8
4
+ Summary: This project develops standardized tools to use LLMs in research studies for improving patient care.
5
+ Author-email: Justin Vinh <jvinh21@gmail.com>
6
+ Requires-Python: >=3.10
7
+ Description-Content-Type: text/markdown
8
+ Classifier: Programming Language :: Python :: 3
9
+ License-File: LICENSE
10
+ Requires-Dist: pandas>=2.0
11
+ Requires-Dist: numpy>=1.26
12
+ Requires-Dist: matplotlib>=3.9
13
+ Requires-Dist: scikit-learn>=1.5
14
+ Requires-Dist: lifelines>=0.28
15
+ Requires-Dist: tqdm>=4.66
16
+ Requires-Dist: loguru>=0.7
17
+ Requires-Dist: orjson>=3.10
18
+ Requires-Dist: pyyaml>=6.0
19
+ Requires-Dist: environs>=9.5
20
+ Requires-Dist: openai>=1.43
21
+ Requires-Dist: azure-identity>=1.17
22
+ Requires-Dist: azure-core>=1.30
23
+ Requires-Dist: pydantic>=2.6
24
+ Requires-Dist: python-dateutil>=2.9
25
+ Requires-Dist: requests>=2.31
26
+
27
+ # project_ryland
28
+
29
+ <a target="_blank" href="https://cookiecutter-data-science.drivendata.org/">
30
+ <img src="https://img.shields.io/badge/CCDS-Project%20template-328F97?logo=cookiecutter" />
31
+ </a>
32
+
33
+ This project develops standardized tools to use LLMs in research studies for improving patient care.
34
+
35
+ RYLAND stands for Research sYstem for LLM-based Analytics of Novel Data. Ryland is the protagonist of Justin's favorite book (he'll leave it to you to figure out which one)
36
+
37
+ Ignore the file tree - it needs to be updated.
38
+
39
+ ## Project Organization
40
+
41
+ ```
42
+ ├── LICENSE <- Open-source license if one is chosen
43
+ ├── Makefile <- Makefile with convenience commands like `make data` or `make train`
44
+ ├── README.md <- The top-level README for developers using this project.
45
+ ├── data
46
+ │ ├── external <- Data from third party sources.
47
+ │ ├── interim <- Intermediate data that has been transformed.
48
+ │ ├── processed <- The final, canonical data sets for modeling.
49
+ │ └── raw <- The original, immutable data dump.
50
+
51
+ ├── docs <- A default mkdocs project; see www.mkdocs.org for details
52
+
53
+ ├── models <- Trained and serialized models, model predictions, or model summaries
54
+
55
+ ├── notebooks <- Jupyter notebooks. Naming convention is a number (for ordering),
56
+ │ the creator's initials, and a short `-` delimited description, e.g.
57
+ │ `1.0-jqp-initial-data-exploration`.
58
+
59
+ ├── pyproject.toml <- Project configuration file with package metadata for
60
+ │ project_ryland_code and configuration for tools like black
61
+
62
+ ├── references <- Data dictionaries, manuals, and all other explanatory materials.
63
+
64
+ ├── reports <- Generated analysis as HTML, PDF, LaTeX, etc.
65
+ │ └── figures <- Generated graphics and figures to be used in reporting
66
+
67
+ ├── requirements.txt <- The requirements file for reproducing the analysis environment, e.g.
68
+ │ generated with `pip freeze > requirements.txt`
69
+
70
+ ├── setup.cfg <- Configuration file for flake8
71
+
72
+ └── project_ryland_code <- Source code for use in this project.
73
+
74
+ ├── __init__.py <- Makes project_ryland_code a Python module
75
+
76
+ ├── config.py <- Store useful variables and configuration
77
+
78
+ ├── dataset.py <- Scripts to download or generate data
79
+
80
+ ├── features.py <- Code to create features for modeling
81
+
82
+ ├── modeling
83
+ │ ├── __init__.py
84
+ │ ├── predict.py <- Code to run model inference with trained models
85
+ │ └── train.py <- Code to train models
86
+
87
+ └── plots.py <- Code to create visualizations
88
+ ```
89
+
90
+ --------
91
+
92
+
@@ -0,0 +1,65 @@
1
+ # project_ryland
2
+
3
+ <a target="_blank" href="https://cookiecutter-data-science.drivendata.org/">
4
+ <img src="https://img.shields.io/badge/CCDS-Project%20template-328F97?logo=cookiecutter" />
5
+ </a>
6
+
7
+ This project develops standardized tools to use LLMs in research studies for improving patient care.
8
+
9
+ RYLAND stands for Research sYstem for LLM-based Analytics of Novel Data. Ryland is the protagonist of Justin's favorite book (he'll leave it to you to figure out which one)
10
+
11
+ Ignore the file tree - it needs to be updated.
12
+
13
+ ## Project Organization
14
+
15
+ ```
16
+ ├── LICENSE <- Open-source license if one is chosen
17
+ ├── Makefile <- Makefile with convenience commands like `make data` or `make train`
18
+ ├── README.md <- The top-level README for developers using this project.
19
+ ├── data
20
+ │ ├── external <- Data from third party sources.
21
+ │ ├── interim <- Intermediate data that has been transformed.
22
+ │ ├── processed <- The final, canonical data sets for modeling.
23
+ │ └── raw <- The original, immutable data dump.
24
+
25
+ ├── docs <- A default mkdocs project; see www.mkdocs.org for details
26
+
27
+ ├── models <- Trained and serialized models, model predictions, or model summaries
28
+
29
+ ├── notebooks <- Jupyter notebooks. Naming convention is a number (for ordering),
30
+ │ the creator's initials, and a short `-` delimited description, e.g.
31
+ │ `1.0-jqp-initial-data-exploration`.
32
+
33
+ ├── pyproject.toml <- Project configuration file with package metadata for
34
+ │ project_ryland_code and configuration for tools like black
35
+
36
+ ├── references <- Data dictionaries, manuals, and all other explanatory materials.
37
+
38
+ ├── reports <- Generated analysis as HTML, PDF, LaTeX, etc.
39
+ │ └── figures <- Generated graphics and figures to be used in reporting
40
+
41
+ ├── requirements.txt <- The requirements file for reproducing the analysis environment, e.g.
42
+ │ generated with `pip freeze > requirements.txt`
43
+
44
+ ├── setup.cfg <- Configuration file for flake8
45
+
46
+ └── project_ryland_code <- Source code for use in this project.
47
+
48
+ ├── __init__.py <- Makes project_ryland_code a Python module
49
+
50
+ ├── config.py <- Store useful variables and configuration
51
+
52
+ ├── dataset.py <- Scripts to download or generate data
53
+
54
+ ├── features.py <- Code to create features for modeling
55
+
56
+ ├── modeling
57
+ │ ├── __init__.py
58
+ │ ├── predict.py <- Code to run model inference with trained models
59
+ │ └── train.py <- Code to train models
60
+
61
+ └── plots.py <- Code to create visualizations
62
+ ```
63
+
64
+ --------
65
+
File without changes
@@ -0,0 +1,14 @@
1
+ """
2
+ ------------------------------------------------------------------------------
3
+ Author: Justin Vinh
4
+ Collaborators: John Rhee, MD
5
+ Parent Package: Project Ryland
6
+ Creation Date: 2025.10.16
7
+ Last Modified: 2025.10.16
8
+
9
+ Purpose:
10
+ This module contains functions designed to aid in statistical analyses of
11
+ data processed by LLMs
12
+ ------------------------------------------------------------------------------
13
+ """
14
+
@@ -0,0 +1,61 @@
1
+ """
2
+ ------------------------------------------------------------------------------
3
+ Author: Justin Vinh
4
+ Parent Package: Project Ryland
5
+ Creation Date: 2025.09.29
6
+ Last Modified: 2025.09.29
7
+
8
+ Purpose:
9
+ Contains functions to import/output data and do basic cleaning
10
+ ------------------------------------------------------------------------------
11
+ """
12
+
13
+ from pathlib import Path
14
+
15
+ import orjson
16
+ import pandas as pd
17
+
18
+
19
+ def normalize_newlines(text: str) -> str:
20
+ """
21
+ Normalize all line endings of text to use Unix-style \n
22
+ (helps in using regex downstream)
23
+ """
24
+ if not isinstance(text, str):
25
+ return text
26
+ else:
27
+ # Replace \\r\\n in the text to just \n
28
+ return text.replace('\\r\\n', '\n').replace('\r\n', '\n')
29
+
30
+
31
+ def load_oncdrs_json_to_df(path_name):
32
+ """
33
+ 1) Loads the given OncDRS-exported json file and changes it to a
34
+ dataframe.
35
+ 2) Normalize all line endings of text in the RPT_TEXT and NARRATIVE_TEXT
36
+ columns to use Unix-style \n (helps in using regex downstream)
37
+ 3) Has error handling for missing file or decoding issues
38
+ """
39
+ # Handles incorrect path names, raises an error if there is a bad path
40
+ path = Path(path_name)
41
+ if not path.exists():
42
+ raise FileNotFoundError(f"File {path_name} not found")
43
+
44
+ # Tries opening the json file, raises an error if unable to read it
45
+ # try:
46
+ # with path.open('r') as json_file:
47
+ # data = json.load(json_file)
48
+ try:
49
+ data = orjson.loads(path.read_bytes())
50
+ except orjson.JSONDecodeError as e:
51
+ raise ValueError(f'Invalid JSON format in file: {path}') from e
52
+
53
+ # Creates a dataframe using the key-value pairs under response>docs
54
+ df = pd.DataFrame(data['response']['docs'])
55
+
56
+ # Normalizes line endings with \n in RPT_TEXT and NARRATIVE_TEXT columns
57
+ for col in ['RPT_TEXT', 'NARRATIVE_TEXT']:
58
+ if col in df.columns:
59
+ df[col] = df[col].map(normalize_newlines)
60
+
61
+ return df
@@ -0,0 +1,330 @@
1
+ """
2
+ ------------------------------------------------------------------------------
3
+ Author: Justin Vinh
4
+ Collaborators: Zach Tentor
5
+ Parent Package: Project Ryland
6
+ Creation Date: 2025.09.29
7
+ Last Modified: 2025.10.07
8
+
9
+ Purpose:
10
+ Contain the keyword mappings for the Project Ryland Utils. This module
11
+ also contains keywords for several scripts that are project-specific.
12
+ ------------------------------------------------------------------------------
13
+ """
14
+
15
+
16
+ # Symptoms of Interest
17
+ # ----------------------------------------------------------------------------
18
+ gwas_prompt_variables_v1 = {
19
+ 'symptoms': [
20
+ 'headache',
21
+ 'hair loss',
22
+ 'fatigue',
23
+ 'nausea',
24
+ 'anxiety',
25
+ 'difficulty sleeping',
26
+ 'numbness and tingling',
27
+ 'joint pain',
28
+ 'rash',
29
+ 'diarrhea',
30
+ 'constipation',
31
+ 'other'
32
+ ]
33
+ }
34
+
35
+
36
+ # Input text keywords
37
+ progress_note_text_filters = ['CENTER FOR NEURO-ONCOLOGY',
38
+ 'NEURO-ONCOLOGY PROGRESS NOTE',
39
+ 'Subjective: Patient ID',
40
+ 'HISTORY OF PRESENT ILLNESS',
41
+ 'INTERVAL HISTORY'
42
+ ]
43
+
44
+ neuro_onc_tumor_keywords = ["glioblastoma",
45
+ "astrocytoma",
46
+ "oligodendroglioma",
47
+ "glioma"
48
+ ]
49
+
50
+ # Note types (process description) of interest
51
+ # (Used to pre-filter in the main script)
52
+ # ----------------------------------------------------------------------------
53
+ pathology_proc_desc_of_interest = [
54
+ 'SURGICAL PATHOLOGY',
55
+ 'ANATOMIC PATHOLOGY',
56
+ 'OTHER PATHOLOGY RESULTS',
57
+ 'OUTSIDE PATHOLOGY REVIEW',
58
+ 'FLOW CYTOMETRY']
59
+
60
+ image_proc_desc_of_interest = [
61
+ 'CT CHEST',
62
+ 'CT PET CHEST'
63
+ ]
64
+
65
+ # Mappings of keywords by process descriptions
66
+ # This dict contains rules for keywords and conditions for each process
67
+ # description in RPT_TEXT. It will be used to extract specific text segments
68
+ # to be fed to the LLM.
69
+ # ----------------------------------------------------------------------------
70
+ image_mappings_by_proc_desc = {
71
+ "*": [
72
+ {
73
+ "start": r"IMPRESSION:",
74
+ "end": ["END IMPRESSION",
75
+ "This report was electronically signed"],
76
+ "condition": None
77
+ }
78
+ ]
79
+ }
80
+
81
+ progress_note_mappings_by_proc_desc = {
82
+ "Progress Note": [
83
+ {
84
+ "start": r"EXAM:",
85
+ "end": ["DATA:",
86
+ "MRI"],
87
+ "condition": None
88
+ },
89
+ {
90
+ "start": r"EXAMINATION",
91
+ "end": ["LABORATORY",
92
+ "ASSESSMENT AND PLAN"],
93
+ "condition": None
94
+ },
95
+ {
96
+ "start": r"PHYSICAL EXAM",
97
+ "end": ["LABORATORY",
98
+ "IMAGING",
99
+ "IMPRESSION AND PLAN","LABS",
100
+ "RADIOGRAPHIC EXAMINATION",
101
+ "Radiology",
102
+ "PLAN",
103
+ "LAB RESULT",
104
+ "IMPRESSION",
105
+ "ASSESSMENT/PLAN"],
106
+ "condition": None
107
+ },
108
+ {
109
+ "start": r"Physical Exam",
110
+ "end": ["TEST",
111
+ "Results",
112
+ "Labs",
113
+ "Blood Draw",
114
+ "LABORATORY"],
115
+ "condition": None
116
+ },
117
+ {
118
+ "start": r"Physical exam",
119
+ "end": ["Lab Results"],
120
+ "condition": None
121
+ },
122
+ {
123
+ "start": r"Examination",
124
+ "end": ["Data Review",
125
+ "Prior Work up",
126
+ "Assessment and Plan"],
127
+ "condition": None
128
+ }
129
+ ]
130
+ }
131
+
132
+ pathology_mappings_by_proc_desc = {
133
+ "SURGICAL PATHOLOGY": [
134
+ {
135
+ "start": r"INTERPRETATION",
136
+ "end": ["TEST INFORMATION",
137
+ "Final Diagnosis",
138
+ "Prior Results"],
139
+ "condition": None
140
+ },
141
+ {
142
+ "start": r"FINAL PATHOLOGIC DIAGNOSIS",
143
+ "end": ["Electronically Signed Out"],
144
+ "condition": None
145
+ },
146
+ {
147
+ "start": r"PATHOLOGIC DIAGNOSIS",
148
+ "end": ["CLINICAL DATA"],
149
+ "condition": None
150
+ },
151
+ {
152
+ "start": r"FINAL DIAGNOSIS",
153
+ "end": ["GROSS DESCRIPTION",
154
+ "Final Diagnosis"],
155
+ "condition": None
156
+ },
157
+ {
158
+ "start": r"DIAGNOSIS",
159
+ "end": ["Gross Description",
160
+ "Final Diagnosis"],
161
+ "condition": None
162
+ },
163
+ {
164
+ "start": r"Diagnosis",
165
+ "end": ["Gross Description"],
166
+ "condition": "exclude",
167
+ "exclude_after": "CLINICAL DATA"
168
+ },
169
+ {
170
+ "start": r"Diagnosis",
171
+ "end": ["Electronically Signed Out"],
172
+ "condition": None
173
+ },
174
+ {
175
+ "start": r"Final Diagnosis",
176
+ "end": ["Electronically Signed Out"],
177
+ "condition": None
178
+ }
179
+ ],
180
+ "ANATOMIC PATHOLOGY": [
181
+ {
182
+ "start": r"PATHOLOGIC DIAGNOSIS",
183
+ "end": ["CLINICAL DATA"],
184
+ "condition": None
185
+ },
186
+ {
187
+ "start": r"FINAL PATHOLOGIC DIAGNOSIS",
188
+ "end": ["Electronically Signed Out"],
189
+ "condition": None
190
+ },
191
+ {
192
+ "start": r"FINAL DIAGNOSIS",
193
+ "end": ["Clinical History"],
194
+ "condition": None
195
+ },
196
+ {
197
+ "start": r"RESULTS",
198
+ "end": ["REFERENCES"],
199
+ "condition": None
200
+ },
201
+ {
202
+ "start": r"INTERPRETATION",
203
+ "end": ["CLINICAL DATA"],
204
+ "condition": None
205
+ },
206
+ {
207
+ "start": r"Diagnosis",
208
+ "end": ["Electronically Signed Out"],
209
+ "condition": None
210
+ },
211
+ {
212
+ "start": r"DIAGNOSIS",
213
+ "end": ["Gross Description"],
214
+ "condition": "exclude",
215
+ "exclude_after": "CLINICAL DATA"
216
+ },
217
+ {
218
+ "start": r"Final Diagnosis",
219
+ "end": ["Clinical History:"],
220
+ "condition": None
221
+ }
222
+ ],
223
+ "FLOW CYTOMETRY": [
224
+ {
225
+ "start": r"INTERPRETATION",
226
+ "end": ["By his/her signature",
227
+ "These tests were developed"],
228
+ "condition": None
229
+ }
230
+ # Additional mappings can be added if necessary
231
+ ],
232
+ "OUTSIDE PATHOLOGY REVIEW": [
233
+ {
234
+ "start": r"PATHOLOGIC DIAGNOSIS",
235
+ "end": ["CLINICAL DATA"],
236
+ "condition": None
237
+ },
238
+ {
239
+ "start": r"INTEGRATED DIAGNOSIS",
240
+ "end": ["Electronically Signed"],
241
+ "condition": None
242
+ }
243
+ # Clarification needed on how to handle overlapping diagnostics
244
+ ],
245
+ "OTHER PATHOLOGY RESULTS": [
246
+ {
247
+ "start": r"DIAGNOSIS",
248
+ "end": ["CLINICAL DATA"],
249
+ "condition": None
250
+ },
251
+ {
252
+ "start": r"PATHOLOGIC DIAGNOSIS",
253
+ "end": ["CLINICAL DATA"],
254
+ "condition": None
255
+ },
256
+ {
257
+ "start": r"FINAL DIAGNOSIS",
258
+ "end": ["GROSS DESCRIPTION"],
259
+ "condition": None
260
+ },
261
+ {
262
+ "start": r"CYTOLOGIC DIAGNOSIS",
263
+ "end": [], # No specific end keywords provided
264
+ "condition": None
265
+ },
266
+ {
267
+ "start": r"Final Diagnosis",
268
+ "end": ["Clinical History"],
269
+ "condition": None
270
+ },
271
+ {
272
+ "start": r"INTERPRETATION",
273
+ "end": ["Final Diagnosis"],
274
+ "condition": None
275
+ },
276
+ {
277
+ "start": r"RESULT",
278
+ "end": ["Final Diagnosis"],
279
+ "condition": None
280
+ },
281
+ {
282
+ "start": r"Result",
283
+ "end": ["Final Diagnosis"],
284
+ "condition": None
285
+ }
286
+ ],
287
+ "PROGRESS NOTES": [
288
+ {
289
+ "start": r"EXAMINATION",
290
+ "end": ["LABORATORY",
291
+ "ASSESSMENT AND PLAN"],
292
+ "condition": None
293
+ },
294
+ {
295
+ "start": r"PHYSICAL EXAM",
296
+ "end": ["LABORATORY",
297
+ "IMAGING",
298
+ "IMPRESSION AND PLAN","LABS",
299
+ "RADIOGRAPHIC EXAMINATION",
300
+ "Radiology",
301
+ "PLAN",
302
+ "LAB RESULT",
303
+ "IMPRESSION",
304
+ "ASSESSMENT/PLAN"],
305
+ "condition": None
306
+ },
307
+ {
308
+ "start": r"Physical Exam",
309
+ "end": ["TEST",
310
+ "Results",
311
+ "Labs",
312
+ "Blood Draw",
313
+ "LABORATORY"],
314
+ "condition": None
315
+ },
316
+ {
317
+ "start": r"Physical exam",
318
+ "end": ["Lab Results"],
319
+ "condition": None
320
+ },
321
+ {
322
+ "start": r"Examination",
323
+ "end": ["Data Review",
324
+ "Prior Work up",
325
+ "Assessment and Plan"],
326
+ "condition": None
327
+ }
328
+ ]
329
+ }
330
+