diff --git a/.gitignore b/.gitignore
index 9ac83bb12b0684eb095915e78e31739918ed7a0d..431c3b84c5fc7845f5d93429ce56b2fc2998ad0f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -34,4 +34,7 @@ node_modules
 /docs/build/
 /config.json
 
-.env
\ No newline at end of file
+.env
+
+# script output
+survey_text.*
\ No newline at end of file
diff --git a/compendium_v2/scripts/get_survey_text_as_excel.py b/compendium_v2/scripts/get_survey_text_as_excel.py
new file mode 100644
index 0000000000000000000000000000000000000000..9a3062de308cc233ca20cf83c4ab567f1d695fd4
--- /dev/null
+++ b/compendium_v2/scripts/get_survey_text_as_excel.py
@@ -0,0 +1,252 @@
+import os
+import json
+import openpyxl
+import csv
+
+SURVEY_MODEL = 'survey_model_2022.json'
+MODEL_FILE = os.path.join(os.path.dirname(__file__), f'../migrations/surveymodels/{SURVEY_MODEL}')
+
+contents = json.load(open(MODEL_FILE))
+
+
+def _parse_validators(name, title, validators):
+    for idx, validator in enumerate(validators):
+        if 'text' not in validator:
+            continue
+        yield f'{name}>>validator({idx+1})', title, validator['text']
+
+
+def _parse_choices(name, title, choices):
+    for idx, choice in enumerate(choices):
+        if isinstance(choice, str):
+            yield f'{name}>>choice({idx+1})', title, choice
+        else:
+            yield f'{name}>>choice({idx+1})', title, choice['text']
+
+
+def _parse_items(name, items):
+    for item in items:
+        item_name = f'{name}>>items>>{item["name"]}'
+        item_title = item['title']
+        yield item_name, item_title, ''
+
+        validators = item.get('validators')
+        if validators:
+            yield from _parse_validators(item_name, item_title, validators)
+
+
+def _parse_dropdown(name, dropdown):
+    _name = f'{name}>>{dropdown["name"]}'
+    title = dropdown['title']
+    yield _name, title, ''
+
+    choices = dropdown.get('choices')
+    if choices:
+        yield from _parse_choices(_name, title, choices)
+
+
+def _parse_html(name, html):
+    _name = f'{name}>>{html["name"]}'
+    return _name, html['html'], ''
+
+
+def _parse_text(name, text):
+    _name = f'{name}>>{text["name"]}'
+    title = text['title']
+    description = text.get('description')
+
+    yield _name, title, ''
+
+    if description:
+        yield f'{_name}>>description', title, description
+
+    validators = text.get('validators')
+    if validators:
+        yield from _parse_validators(_name, title, validators)
+
+    items = text.get('items')
+    if items:
+        yield from _parse_items(_name, items)
+
+
+def _parse_matrixdropdown(name, dropdown):
+    _name = f'{name}>>{dropdown["name"]}'
+    title = dropdown['title']
+    columns = dropdown['columns']
+    rows = dropdown['rows']
+
+    yield _name, title, ''
+
+    for column in columns:
+        col_name = f'{_name}>>columns>>{column["name"]}'
+        col_title = column['title']
+        yield col_name, col_title, ''
+
+        placeholder = column.get('placeholder')
+        if placeholder:
+            yield f'{col_name}>>placeholder', col_title, placeholder
+
+        has_choices = 'choices' in column
+        if has_choices:
+            _parse_choices(col_name, col_title, column['choices'])
+
+    for row in rows:
+        row_name = f'{_name}>>rows>>{row["value"]}'
+        row_title = row['text']
+        yield row_name, row_title, ''
+
+        description = row.get('customDescription')
+        placeholder = row.get('placeholder')
+        if description:
+            yield f'{row_name}>>description', row_title, description
+        if placeholder:
+            yield f'{row_name}>>placeholder', row_title, placeholder
+
+        has_choices = 'choices' in row
+        if has_choices:
+            _parse_choices(row_name, row_title, row['choices'])
+
+
+def parse_multipletext(name, multipletext):
+    _name = f'{name}>>{multipletext["name"]}'
+    _title = multipletext['title']
+    yield _name, _title, ''
+
+    description = multipletext.get('description')
+    if description:
+        yield f'{_name}>>description', _title, description
+
+    validators = multipletext.get('validators')
+    if validators:
+        yield from _parse_validators(_name, _title, validators)
+
+    items = multipletext.get('items')
+    if items:
+        yield from _parse_items(_name, items)
+
+
+def parse_radiogroup(name, radiogroup):
+    _name = f'{name}>>{radiogroup["name"]}'
+    title = radiogroup['title']
+    yield _name, title, ''
+
+    choices = radiogroup.get('choices')
+    if choices:
+        yield from _parse_choices(_name, title, choices)
+
+
+def _parse_checkbox(name, checkbox):
+    _name = f'{name}>>{checkbox["name"]}'
+    title = checkbox['title']
+    yield _name, title, ''
+
+    choices = checkbox.get('choices')
+    if choices:
+        yield from _parse_choices(_name, title, choices)
+
+
+def _parse_matrixdynamic(name, matrixdynamic):
+    _name = f'{name}>>{matrixdynamic["name"]}'
+    title = matrixdynamic['title']
+    yield _name, title, ''
+
+    columns = matrixdynamic['columns']
+    for column in columns:
+        col_name = f'{_name}>>columns>>{column["name"]}'
+        col_title = column['title']
+        yield col_name, col_title, ''
+
+        placeholder = column.get('placeholder')
+        if placeholder:
+            yield f'{col_name}>>placeholder', col_title, placeholder
+
+        has_choices = 'choices' in column
+        if has_choices:
+            _parse_choices(col_name, col_title, column['choices'])
+
+    rows = matrixdynamic.get('rows')
+    if rows:
+        for row in rows:
+            row_name = f'{_name}>>rows>>{row["value"]}'
+            row_title = row['text']
+            yield row_name, row_title, ''
+
+            description = row.get('customDescription')
+            placeholder = row.get('placeholder')
+            if description:
+                yield f'{row_name}>>description', row_title, description
+            if placeholder:
+                yield f'{row_name}>>placeholder', row_title, placeholder
+
+            has_choices = 'choices' in row
+            if has_choices:
+                _parse_choices(row_name, row_title, row['choices'])
+
+    validators = matrixdynamic.get('validators')
+    if validators:
+        yield from _parse_validators(_name, title, validators)
+
+    items = matrixdynamic.get('items')
+    if items:
+        yield from _parse_items(_name, items)
+
+
+def parse_element(page_name, element):
+    _type = element['type']
+
+    if _type == 'html':
+        yield _parse_html(page_name, element)
+    elif _type == 'comment':
+        yield '', '', ''
+    elif _type == 'text':
+        yield from _parse_text(page_name, element)
+    elif _type == 'matrixdropdown':
+        yield from _parse_matrixdropdown(page_name, element)
+    elif _type == 'panel':
+        name = f'{page_name}>>{element["name"]}'
+        title = element['title']
+        yield name, title, ''
+        for sub_element in element['elements']:
+            yield from parse_element(page_name, sub_element)
+    elif _type == 'multipletext':
+        yield from parse_multipletext(page_name, element)
+    elif _type == 'radiogroup':
+        yield from parse_radiogroup(page_name, element)
+    elif _type == 'dropdown':
+        yield from _parse_dropdown(page_name, element)
+    elif _type == 'checkbox':
+        yield from _parse_checkbox(page_name, element)
+    elif _type == 'matrixdynamic':
+        yield from _parse_matrixdynamic(page_name, element)
+    else:
+        raise ValueError(f'Unknown element type: {_type}')
+
+
+def _as_csv(pages):
+    with open('survey_text.csv', 'w', newline='') as csvfile:
+        writer = csv.writer(csvfile)
+        writer.writerow(['Survey Path', 'Text1', 'Text2', 'Text1 Replacement', 'Text2 Replacement'])
+        for page in pages:
+            for element in page['elements']:
+                for name, text1, text2 in parse_element(page['name'], element):
+                    writer.writerow(name, text1, text2, '', '')
+            writer.writerow('', '', '', '', '')
+            writer.writerow('', '', '', '', '')
+
+
+def _as_excel(pages):
+    wb = openpyxl.Workbook()
+    ws = wb.active
+    ws.append(['Survey Path', 'Text1', 'Text2', 'Text1 Replacement', 'Text2 Replacement'])
+    for page in pages:
+        for element in page['elements']:
+            for name, text1, text2 in parse_element(page['name'], element):
+                ws.append([name, text1, text2, '', ''])
+        ws.append(['', '', '', '', ''])
+        ws.append(['', '', '', '', ''])
+    wb.save('survey_text.xlsx')
+
+
+if __name__ == '__main__':
+    _as_excel(contents['pages'])
+    # _as_csv(contents['pages'])