import tempfile
from unittest import mock
import shutil
from fastcore.test import *
from pathvalidate import validate_filename
from trouver.helper.files_and_folders import path_name_no_ext
from trouver.helper.tests import _test_directorymarkdown.obisidian.personal.index_notes
In a Obsidian math vault, it is convenient to keep index notes, which list links to other index notes or standard information notes.
The methods in this module - create (standard information) notes in appropriate folders, - set up the notes, - add links of the notes to appropriate index notes - indicate in the index note and the standard information note where the content of the information note originates from in the original text.
Identifying subsections listed in index notes and subsection folders
subsections_listed_in_index_note
subsections_listed_in_index_note (index_note:Union[trouver.markdown.obsi dian.vault.VaultNote,str], vault:os.PathLike)
*Return subsections/subchapters as listed in the index note
See Also
- The get_headings_treefunction of theMarkdownFileclass.*
| Type | Details | |
|---|---|---|
| index_note | Union | The index note | 
| vault | PathLike | |
| Returns | dict | The keys are 1. line numbers and 2. 'title'. The values are dict and str (the blank str if root node), respectively. | 
text = r"""# 1. Some section title
- [[some_note]], Page 1
- [[some_note_2]], Page 2
# 2. Some other section title
- [[some_note_3]], Page 2
- [[some_note_4]], Page 3
# 3. Section 3
- [[some_note_5|an alias]], Page 3
# 4. Section 4
# 5. Section 5
"""
with mock.patch("trouver.markdown.markdown.file.open", mock.mock_open(read_data=text)):
    fake_vn = VaultNote(rel_path='fake_note.md', vault='')  # Think of this as a VaultNote object whose underlying file has `text` as its content.
    subsections_in_text = subsections_listed_in_index_note(fake_vn, vault='')
    expected_output = {
        'title': '',
        0: {'title': '# 1. Some section title'},
        4: {'title': '# 2. Some other section title'},
        8: {'title': '# 3. Section 3'},
        11: {'title': '# 4. Section 4'},
        12: {'title': '# 5. Section 5'}}
    test_eq(subsections_in_text, expected_output)subsection_folders
subsection_folders (index_note:Union[trouver.markdown.obsidian.vault.Vau ltNote,str], vault:os.PathLike, output_type:str)
*Return subdirectories corresponding to subsections/subchapters, i.e. the folders in the same directory as the index note.
The folders are arranged in the order specified by natsorted.*
| Type | Details | |
|---|---|---|
| index_note | Union | The index note | 
| vault | PathLike | |
| output_type | str | 'absolute_path','relative_path', or'name' | 
| Returns | list | List of immediate subdirectories in the directory containing the index note. | 
with (tempfile.TemporaryDirectory(prefix='temp_dir', dir=os.getcwd()) as temp_dir):
    temp_vault = Path(temp_dir) / 'test_vault_2'
    shutil.copytree(_test_directory() / 'test_vault_2', temp_vault)
    two_chapter_reference_1_index_note = VaultNote(temp_vault, name='_index_2_chapter_reference_1')
    ss_folders = subsection_folders(two_chapter_reference_1_index_note, temp_vault, output_type='name')
    assert len(ss_folders) > 0mock_vault = Path('mock_absolute_path')
mock_path =  mock_vault / Path('mock_reference_folder') / Path('mock_chapter')
folders = [  #glob.glob would return the folders in this order, at least on Windows:
    '1 section',
    '10 section',
    '11 section',
    '2 section',
    '3 section',
    '4 section',
    '5 section',
    '6 section',
    '7 section',
    '8 section',
    '9 section']
mock_glob_return_value = [str(mock_path / folder) for folder in folders]
with mock.patch("__main__.glob.glob", return_value=mock_glob_return_value):
    mock_index_note = VaultNote(rel_path='_index_mock_chapter.md', vault= mock_vault)
    
    sample_output_absolute_path = subsection_folders(mock_index_note, mock_vault, output_type='absolute_path')
    test_shuffled(sample_output_absolute_path, mock_glob_return_value)
    test_eq(sample_output_absolute_path, natsorted(mock_glob_return_value))
    sample_output_relative_path = subsection_folders(mock_index_note, mock_vault, output_type='relative_path')
    expected_output_for_relative_paths = [os.path.relpath(folder, mock_vault) for folder in mock_glob_return_value]
    test_shuffled(sample_output_relative_path, expected_output_for_relative_paths)
    test_eq(sample_output_relative_path, natsorted(expected_output_for_relative_paths))
    # test_eq(sample_output_absolute_path, )
    sample_output_name = subsection_folders(mock_index_note, mock_vault, output_type='name')
    test_shuffled(sample_output_name, folders)
    test_eq(sample_output_name, natsorted(folders))Corresponding headings in index notes and subfolders
get_alphanumeric
get_alphanumeric (title:str, title_type:str)
*Get the alphanumeric of a title of either a folder or a heading in an index noteh.
Assumes that each folder is titled '{alphanumeric}_{folder_title}' and each heading is titled '{alphanumeric}. {heading_title}'*
| Type | Details | |
|---|---|---|
| title | str | The title of either a folder or a heading. Must start with an alphanumeric. | 
| title_type | str | Either folderorheading. | 
| Returns | str | An alphabet or a numeric (arabic or roman) | 
test_eq(get_alphanumeric('1. Higher direct images', 'heading'), '1')
test_eq(get_alphanumeric('1_higher_direct_images', 'folder'), '1')
test_eq(get_alphanumeric('12_higher_direct_images_the_leray_spectral_sequence', 'folder'), '12')
test_eq(get_alphanumeric('VII_elliptic_curves_over_local_fields', 'folder'), 'VII')
test_eq(get_alphanumeric('A_properties_of_morphisms', 'folder'), 'A')correspond_headings_with_folder
correspond_headings_with_folder (index_note:trouver.markdown.obsidian.va ult.VaultNote, vault:os.PathLike, include_non_heading:bool=True)
*Return tuples of corresponding headings in an index note with folder names.
Assumes that each folder is titled '{alphanumeric}_{folder_title}' and each heading is titled '{alphanumeric}. {heading_title}'
Returns - dict[str, str] - Each key is a str indexing the headings and folders. The keys are usually alphanumerics (arabic or roman), depending on the numbering system of chapters/sections of the reference/text. The values are tuples (folder_title, heading_title) without the alphanumeric. For the blank heading, the key/index, the folder title, and the heading title are all the empty str.*
| Type | Default | Details | |
|---|---|---|---|
| index_note | VaultNote | ||
| vault | PathLike | ||
| include_non_heading | bool | True | If True, and if there is text before any heading, then treat such text as being under a “blank” heading. | 
| Returns | dict | 
with (tempfile.TemporaryDirectory(prefix='temp_dir', dir=os.getcwd()) as temp_dir):
    temp_vault = Path(temp_dir) / 'test_vault_2'
    shutil.copytree(_test_directory() / 'test_vault_2', temp_vault)
    two_chapter_reference_1_index_note = VaultNote(temp_vault, name='_index_2_chapter_reference_1')
    test_eq(len(correspond_headings_with_folder(two_chapter_reference_1_index_note, temp_vault)), 1)mock_vault = Path('mock_absolute_path')
mock_path =  mock_vault / Path('algebraic_geometry') / Path('some_reference') / Path('chapter_18_some_chapter')
folders = ['181_some_title',
    '182_some_other_title',
    '183_yet_another_title']
mock_glob_return_value = [str(mock_path / folder) for folder in folders]
text = r"""# 18.1. Some title 
- [ ] [[some_reference 18.1|some_reference_some_alias]], 18.1, Page 300
# 18.2. Some other title 
- [ ] [[some_reference 18.2]], 18.2, Page 305
# 18.3. Yet another title 
- [ ] [[some_reference 18.3|]], 18.3, Page 308
"""
mock_index_file = MarkdownFile.from_string(text)
with (mock.patch("__main__.glob.glob", return_value=mock_glob_return_value),
      mock.patch("trouver.markdown.markdown.file.MarkdownFile.from_vault_note", return_value=mock_index_file)):
    mock_index_note = VaultNote(rel_path = '_index_mock.md', vault=mock_vault)
    # subsections_listed_in_index_note(mock_index_note, vault=mock_vault)
    sample_output = correspond_headings_with_folder(mock_index_note, mock_vault)
    print(sample_output)
    test_eq(len(sample_output), 3)
    for key, value in sample_output.items():
        assert value[0].startswith(key)
        assert value[1].startswith(key.replace('.', '')){'18.1': ('18.1. Some title', '181_some_title'), '18.2': ('18.2. Some other title', '182_some_other_title'), '18.3': ('18.3. Yet another title', '183_yet_another_title')}Move information notes to their appropriate folders.
Sometimes, I end up creating information notes in the wrong folders. It would be nice to detect which ones are in the wrong folders and to move them appropriately.
information_notes_linked_in_index_note
information_notes_linked_in_index_note (index_note:trouver.markdown.obsi dian.vault.VaultNote, vault:os.PathLike, hints:list[os.PathLike]=None)
*Find information notes to be moved to the correct folder.
Current implementation just looks at level 1 headings. This function is used in move_information_notes_to_correct_folder. Assumes that all notes in the vault have unique names.*
| Type | Default | Details | |
|---|---|---|---|
| index_note | VaultNote | The note indexing the information notes. | |
| vault | PathLike | ||
| hints | list | None | Hints on where the information notes are likely to be found at. Each path is relative to vaultand points to a directory. Defaults toNone. | 
| Returns | dict | Each key is the index for the heading (usually either an alphanumerical or a roman numerical). Each value is a list of the information notes linked in the index note. | 
VaultNote.clear_cache()
mock_vault = Path('mock_absolute_path')
mock_path =  mock_vault / Path('algebraic_geometry') / Path('some_reference') / Path('chapter_18_some_chapter')
folders = ['181_some_title',
    '182_some_other_title',
    '183_yet_another_title']
mock_glob_return_value = [str(mock_path / folder) for folder in folders]
mock_correspond_headings_with_folder_return_value = {
  '18.1': ('18.1. Some title', '181_some_title'),
  '18.2': ('18.2. Some other title', '182_some_other_title'),
  '18.3': ('18.3. Yet another title', '183_yet_another_title')}
text = r"""# 18.1. Some title 
- [ ] [[some_reference 18.1|some_reference_some_alias]], 18.1, Page 300
- [ ] [[some_reference 18.1.1|another_alias]], 18.1.1, Page 300
- [ ] [[some_reference 18.1.2]], 18.1.2, Page 301
# 18.2. Some other title 
- [ ] [[some_reference 18.2]], 18.2, Page 305
- [ ] [[some_reference 18.2.1]], 18.2.1, Page 306
# 18.3. Yet another title 
- [ ] [[some_reference 18.3]], 18.3, Page 308
- [ ] [[some_reference 18.3.1]], 18.3.1, Page 308
"""
mock_index_file = MarkdownFile.from_string(text)
mock_index_note = VaultNote(rel_path = mock_path / '_index_18_some_index_note.md', vault=mock_vault)
with (mock.patch("trouver.markdown.markdown.file.MarkdownFile.from_vault_note", return_value=mock_index_file),
      mock.patch("__main__.correspond_headings_with_folder", return_value=mock_correspond_headings_with_folder_return_value),
      # mock.patch("__main__.VaultNote", side_effect=[None, None, None, None, None, None, None])
      ):
    sample_output = information_notes_linked_in_index_note(mock_index_note, mock_vault)
    print(sample_output)
    
    test_eq(len(sample_output), 3)
    test_eq(len(sample_output['18.1']), 3)
    test_eq(len(sample_output['18.2']), 2)
    test_eq(sample_output['18.1'][0].name, 'some_reference 18.1')
    test_eq(sample_output['18.2'][1].name, 'some_reference 18.2.1'){'18.1': [<trouver.markdown.obsidian.vault.VaultNote object>, <trouver.markdown.obsidian.vault.VaultNote object>, <trouver.markdown.obsidian.vault.VaultNote object>], '18.2': [<trouver.markdown.obsidian.vault.VaultNote object>, <trouver.markdown.obsidian.vault.VaultNote object>], '18.3': [<trouver.markdown.obsidian.vault.VaultNote object>, <trouver.markdown.obsidian.vault.VaultNote object>]}move_information_notes_to_correct_folder
move_information_notes_to_correct_folder (index_note:trouver.markdown.ob sidian.vault.VaultNote, vault:os.PathLike, hints:list[os.PathLike]=None)
*Moves the information notes indexed by index_note to the correct folder.
The “correct folder” is a folder in the same directory as index_note corresponding to the heading under which the information note is indexed. The current implementation just looks at level 1 headings.*
| Type | Default | Details | |
|---|---|---|---|
| index_note | VaultNote | ||
| vault | PathLike | ||
| hints | list | None | Hints on where the information notes are likely to be found at. Each path is relative to vaultand points to a directory. Defaults toNone. | 
| Returns | None | 
In the following example, there is an index note listing links to three notes. One of the notes does not belong beneath a heading and the other two notes belong beneath a heading. Correspondingly, the move_information_notes_to_correct_folder function moves the file for the first note to the same directory that the index note itself is in and the two other notes end up in the section folder corresponding to the heading
with (tempfile.TemporaryDirectory(prefix='temp_dir', dir=os.getcwd()) as temp_dir,
      ):
    temp_vault = Path(temp_dir) / 'test_vault_2'
    shutil.copytree(_test_directory() / 'test_vault_2', temp_vault)
    index_note = VaultNote(temp_vault, name='_index_1_chapter_reference_4')
    print(f'The following is the text in the index note:\n\n{index_note.text()}')
    move_information_notes_to_correct_folder(index_note, temp_vault)
    assert str(VaultNote(temp_vault, name='this_note_stay_in_the_section_folder').path().parent).endswith('1_section')
    assert str(VaultNote(temp_vault, name='this_note_should_be_moved_to_the_chapter_folder_from_the_section_folder').path().parent).endswith('1_chapter_reference_4')
    assert str(VaultNote(temp_vault, name='this_note_should_be_moved_to_the_section_folder_from_the_chapter_folder').path().parent).endswith('1_section')The following is the text in the index note:
- [[this_note_should_be_moved_to_the_chapter_folder_from_the_section_folder]]
# 1. First section in 1_chapter_reference_3
- [[this_note_should_be_moved_to_the_section_folder_from_the_chapter_folder]]
- [[this_note_stay_in_the_section_folder]]# TODO: see if I can implement the functionalities specified by the following example
# In the following example, there is an unnamed heading not corresponding to any section and not corresponding to any folder within the directory that the index note belongs to. The notes linked beneath the unnamed heading should nevertheless be moved into the directory that the index note is in.
# with (tempfile.TemporaryDirectory(prefix='temp_dir', dir=os.getcwd()) as temp_dir,
#       ):
#     temp_vault = Path(temp_dir) / 'test_vault_2'
#     shutil.copytree(_test_directory() / 'test_vault_2', temp_vault)
#     index_note = VaultNote(temp_vault, name='_index_3_chapter_reference_4')
#     print(f'The following is the text in the index note:\n\n{index_note.text()}')
#     move_information_notes_to_correct_folder(index_note, temp_vault)
#     note_that_should_have_moved = VaultNote(temp_vault, name='this_note_is_in_the_temp_folder_and_should_be_moved_3_chapter_reference_4_where_it_is_linked_by_the_index_note')
#     assert str(note_that_should_have_moved.path().parent).endswith('3_chapter_reference_4')
#     # assert str(VaultNote(temp_vault, name='this_note_should_be_moved_to_the_chapter_folder_from_the_section_folder').path().parent).endswith('1_chapter_reference_4')
#     # assert str(VaultNote(temp_vault, name='this_note_should_be_moved_to_the_section_folder_from_the_chapter_folder').path().parent).endswith('1_section')move_information_notes_to_correct_folder_for_all_indices
move_information_notes_to_correct_folder_for_all_indices (index_of_index _notes:trouver. markdown.obsidi an.vault.VaultN ote, vault:os.P athLike, hints: list[os.PathLik e]=[])
Moves the information notes for all index notes belonging to the reference as specified by index_of_index_notes.
| Type | Default | Details | |
|---|---|---|---|
| index_of_index_notes | VaultNote | The index note indexing other index notes; index_of_index_notesis intended to be an index note for an entire reference whereas the index notes are intended to correspond to chapters/sections in the reference. | |
| vault | PathLike | ||
| hints | list | [] | Hints on where the information notes are likely to be found at. Each path is relative to vaultand points to a directory. | 
| Returns | None | 
The following example demonstrating move_information_notes_to_correct_folder and move_information_notes_to_correct_folder_for_all_indices concerns test_vault_2 in nbs/_tests.
Note that it contains the note _index_1_chapter_reference_1, which has the following content:
vn = VaultNote(_test_directory(), name='_index_1_chapter_reference_1')
print(vn.text())# 1. Section
- [[note_11]]
- [[note_12]]
- [[note_13]]
# 2. Section
- [[note_21]]
- [[note_22]]
# 3. Section
- [[note_31]]
- [[note_32]]
- [[a_note_belonging_in_3_section_1_chapter_reference_1]]
# 4. Section
- [[note_41]]
- [[note_42]]However, the following notes are in the “wrong” folders according to these index notes:
- note_21.mdis in the folder- 1_section_1_chapter_reference_1, but it should be in the folder- 2_section_1_chapter_reference_1.
- note_41.mdis in the folder- 3_section_1_chapter_reference_1, but it should be in the folder- 4_section_1_chapter_reference_1.
- note_42.mdis in the folder- 3_section_1_chapter_reference_1, but it should be in the folder- 4_section_1_chapter_reference_1.
- a_note_belonging_in_1_section_1_chapter_reference_2.mdis in the folder- 4_section_1_chapter_reference_1, but it should be in the folder- 1_section_1_chapter_reference_2.
- a_note_belonging_in_1_section_2_chapter_reference_1.mdis in the folder- 4_section_1_chapter_reference_1, but it should be in the folder- 1_section_2_chapter_reference_1.
- a_note_belonging_in_3_section_1_chapter_reference_1.mdis in the folder- 1_section_2_chapter_reference_2, but it should be in the folder- 3_section_1_chapter_reference_1.
The move_information_notes_to_correct_folder method first applied to the index note _index_1_chapter_reference_1.md moves the notes indexed in the index note to their correct locations. In particular, the following notes are moved to their correct locations:
- note_21.md,
- note_41.md,
- note_42.md, and
- a_note_belonging_in_3_section_1_chapter_reference_1.md
The move_information_notes_to_correct_folder method applied to _index_2_chapter_reference_1.md then moves a_note_belonging_in_1_section_2_chapter_reference_1.md to its correct location.
Lastly, the move_information_notes_to_correct_folder method applied to _index_1_chapter_reference_2.md then moves a_note_belonging_in_1_section_1_chapter_reference_2.md to its correct location.
with tempfile.TemporaryDirectory(prefix='temp_dir', dir=os.getcwd()) as temp_dir:
    temp_vault = Path(temp_dir) / 'test_vault_2'
    shutil.copytree(_test_directory() / 'test_vault_2', temp_vault)
    # os.startfile(temp_dir)
    # input()
    one_chapter_reference_1_index_note = VaultNote(temp_vault, name='_index_1_chapter_reference_1')
    two_chapter_reference_1_index_note = VaultNote(temp_vault, name='_index_2_chapter_reference_1')
    one_chapter_reference_2_index_note = VaultNote(temp_vault, name='_index_1_chapter_reference_2')
    move_information_notes_to_correct_folder(one_chapter_reference_1_index_note, temp_vault)
    note_21 = VaultNote(temp_vault, name='note_21')
    test_eq(path_name_no_ext(note_21.path().parent), '2_section_1_chapter_reference_1')
    note_41 = VaultNote(temp_vault, name='note_41')
    test_eq(path_name_no_ext(note_41.path().parent), '4_section_1_chapter_reference_1')
    note_42 = VaultNote(temp_vault, name='note_42')
    test_eq(path_name_no_ext(note_42.path().parent), '4_section_1_chapter_reference_1')
    note_alpha = VaultNote(temp_vault, name='a_note_belonging_in_3_section_1_chapter_reference_1')
    test_eq(path_name_no_ext(note_alpha.path().parent), '3_section_1_chapter_reference_1')
    # os.startfile(temp_vault)
    move_information_notes_to_correct_folder(two_chapter_reference_1_index_note, temp_vault)
    note_beta = VaultNote(temp_vault, name='a_note_belonging_in_1_section_2_chapter_reference_1')
    test_eq(path_name_no_ext(note_beta.path().parent), '1_section_2_chapter_reference_1')
    move_information_notes_to_correct_folder(one_chapter_reference_2_index_note, temp_vault)
    note_gamma = VaultNote(temp_vault, name='a_note_belonging_in_1_section_1_chapter_reference_2')
    test_eq(path_name_no_ext(note_gamma.path().parent), '1_section_1_chapter_reference_2')The move_information_notes_to_correct_folder_for_all_indices applied to _index_reference_1.md effectively invokes move_information_notes_to_correct_folder to the index notes which are indexed in _index_reference_1.md, i.e. to _index_1_chapter_reference_1, _index_2_chapter_reference_1, _index_3_chapter_reference_1. Note that this invocation of the method does not invoke move_information_notes_to_correct_folder to _index_1_chapter_reference_2 and hence a_note_belonging_in_1_section_1_chapter_reference_2 is not moved to its correct location.
with tempfile.TemporaryDirectory(prefix='temp_dir', dir=os.getcwd()) as temp_dir:
    temp_vault = Path(temp_dir) / 'test_vault_2'
    shutil.copytree(_test_directory() / 'test_vault_2', temp_vault)
    reference_1_index_note = VaultNote(temp_vault, name='_index_reference_1')
    move_information_notes_to_correct_folder_for_all_indices(reference_1_index_note, temp_vault)
    # These notes are moved to their correct locations because they belong to reference_1, i.e. 
    # are indexed in indexed notes which are indexed in `_index_reference_1`.
    note_21 = VaultNote(temp_vault, name='note_21')
    test_eq(path_name_no_ext(note_21.path().parent), '2_section_1_chapter_reference_1')
    note_41 = VaultNote(temp_vault, name='note_41')
    test_eq(path_name_no_ext(note_41.path().parent), '4_section_1_chapter_reference_1')
    note_42 = VaultNote(temp_vault, name='note_42')
    test_eq(path_name_no_ext(note_42.path().parent), '4_section_1_chapter_reference_1')
    note_alpha = VaultNote(temp_vault, name='a_note_belonging_in_3_section_1_chapter_reference_1')
    test_eq(path_name_no_ext(note_alpha.path().parent), '3_section_1_chapter_reference_1')
    note_beta = VaultNote(temp_vault, name='a_note_belonging_in_1_section_2_chapter_reference_1')
    test_eq(path_name_no_ext(note_beta.path().parent), '1_section_2_chapter_reference_1')
    # This note is not moved to its correct location because it does not belong to reference_2.
    note_gamma = VaultNote(temp_vault, name='a_note_belonging_in_1_section_1_chapter_reference_2')
    test_ne(path_name_no_ext(note_gamma.path().parent), '1_section_1_chapter_reference_2')
    test_eq(path_name_no_ext(note_gamma.path().parent), '4_section_1_chapter_reference_1')Automatically make subfolders based on index note headings
convert_title_to_folder_name
convert_title_to_folder_name (title:str)
*Returns a folder name for the given string, e.g. replaces spaces with underscore.
Parameters - title - str
Returns - str*
# TODO: add examples
sample_title = convert_title_to_folder_name(r'1. $\mathscr{M}_g$ and its boundary')
print(sample_title)
sample_title = convert_title_to_folder_name(r'''7. Exceptional maximal subgroups of 
\texorpdfstring{\(\GSp_4(\ff_\ell)\)}{GSp4Fell}''')
print(sample_title)
validate_filename(sample_title)1_m_g_and_its_boundary
7_exceptional_maximal_subgroups_of_texorpdfstringgsp_4ff_ellgsp4fellconvert_heading_to_folder_name
convert_heading_to_folder_name (heading:str)
*Converts a heading to a valid name for a folder.
TODO Might not work correctly.
Parameters - heading: str*
| Type | Details | |
|---|---|---|
| heading | str | Matches regex \# (\w+?)\. (.*?) | 
| Returns | str | 
print(convert_heading_to_folder_name(r'# 1. First title'))
print(convert_heading_to_folder_name(r'# A. appendix title'))
# print(convert_heading_to_folder_name(r'# A.1. appendix title')) # Works incorrectly TODO fix
print(convert_heading_to_folder_name(r"# 1. Hi I'm Bob"))1_first_title
A_appendix_title
1_hi_im_bobmake_folders_from_index_note_headers
make_folders_from_index_note_headers (index_note:trouver.markdown.obsidi an.vault.VaultNote)
*Make folders in the same directory as index note whose names are the titles of the headers of the index note.
The headers of the index note must match the regex pattern \# (\w+?)\. (.*?).*
with (tempfile.TemporaryDirectory(prefix='temp_dir', dir=os.getcwd()) as temp_dir,
      mock.patch('__main__.convert_heading_to_folder_name') as mock_convert_heading_to_folder_name):
    temp_vault = Path(temp_dir) / 'test_vault_2'
    shutil.copytree(_test_directory() / 'test_vault_2', temp_vault)
    # This mocks return values of `convert_heading_to_folder_name` which is used to
    # determine the names of the folders to be made; I am using this because 1. I 
    # do not really care what the folders are named for the purposes of this example/test
    # and 2. I anticipate that I will make several modifications to the method before
    # I am satisfied with it.
    mock_convert_heading_to_folder_name.side_effect = [
        "1_first_section_in_1_chapter_reference_3",
        "2_second_section_in_1_chapter_reference_3",
        "3_hi_im_bob"]
    
    index_note = VaultNote(temp_vault, name='_index_1_chapter_reference_3')
    make_folders_from_index_note_headers(index_note)
    absolute_path = index_note.path(relative=False)
    index_note_directory = absolute_path.parent
    list_of_folders_in_the_same_directory_as_index_note = os.listdir(
        index_note_directory)
    # 3 folders plus the index note itself will be made in the 
    test_eq(len(list_of_folders_in_the_same_directory_as_index_note), 4)
    mock_convert_heading_to_folder_name.assert_has_calls(
        [mock.call('# 1. First section in 1_chapter_reference_3'),
         mock.call('# 2. Second section in 1_chapter_reference_3'),
         mock.call("# 3. Hi I'm Bob")])Identify order of notes in index notes
get_notes_from_index_note
get_notes_from_index_note (vault:os.PathLike, index_note:trouver.markdown.obsidian.vault.Vau ltNote, as_vault_notes:bool=True, include_embedded_notes:bool=False)
*Returns the list of notes listed in the index note in the order that they are listed in.
Asssumes that the index note is “formatted correctly”.
See Also - [get_index_notes_from_index_note](https://hyunjongkimmath.github.io/trouver/markdown.obsidian.personal.reference.html#get_index_notes_from_index_note) in markdown.obsidian.personal.reference.*
| Type | Default | Details | |
|---|---|---|---|
| vault | PathLike | The path to the Obsidian vault directory | |
| index_note | VaultNote | The VaultNote object for the index note. | |
| as_vault_notes | bool | True | If True, returns the[VaultNote](https://hyunjongkimmath.github.io/trouver/markdown.obsidian.vault.html#vaultnote)objects for the index notes. Otherwise, returns the names of these notes | 
| include_embedded_notes | bool | False | If True, include in the list the embedded notes. Defaults toFalse. | 
| Returns | list | Either of the names of the index notes in the vault or of the index notes as VaultNote objects, depending on as_vault_notes. | 
We can get the notes indexed in an index note in the order that they are indexed:
vault = _test_directory() / 'test_vault_2'
index_note = VaultNote(vault, name='_index_1_chapter_reference_1')
list_of_notes_in_index_note = get_notes_from_index_note(vault, index_note)
test_eq(len(list_of_notes_in_index_note), 10)
names_of_notes_in_index_note = [vn.name for vn in list_of_notes_in_index_note]
print(names_of_notes_in_index_note)
test_eq(names_of_notes_in_index_note[0], "note_11")
test_eq(names_of_notes_in_index_note[7], "a_note_belonging_in_3_section_1_chapter_reference_1")['note_11', 'note_12', 'note_13', 'note_21', 'note_22', 'note_31', 'note_32', 'a_note_belonging_in_3_section_1_chapter_reference_1', 'note_41', 'note_42']We can just get the names of these notes instead of VaultNote objects representing these notes:
list_of_notes_in_index_note = get_notes_from_index_note(vault, index_note, as_vault_notes=False)
test_eq(len(list_of_notes_in_index_note), 10)
print(list_of_notes_in_index_note)['note_11', 'note_12', 'note_13', 'note_21', 'note_22', 'note_31', 'note_32', 'a_note_belonging_in_3_section_1_chapter_reference_1', 'note_41', 'note_42']Add line in index note
add_link_in_index_note_after_note_link
add_link_in_index_note_after_note_link (index_note:trouver.markdown.obsi dian.vault.VaultNote, note_to_add _link_after:trouver.markdown.obsi dian.vault.VaultNote, link_to_add :trouver.markdown.obsidian.links. ObsidianLink)
*Adds a link in the index note.
The link is added after the first link to note_to_add_link_after. If no link to note_to_add_link_after is found, then a link is added at the end.*
with tempfile.TemporaryDirectory(prefix='temp_dir', dir=os.getcwd()) as temp_dir:
    temp_vault = Path(temp_dir) / 'test_vault_2'
    shutil.copytree(_test_directory() / 'test_vault_2', temp_vault)
    index_note = VaultNote(temp_vault, name='_index_1_chapter_reference_1')
    note_to_add_link_after = VaultNote(temp_vault, name='note_21')
    link_to_add = ObsidianLink.from_text('[[another_note]]')
    add_link_in_index_note_after_note_link(index_note, note_to_add_link_after, link_to_add)
    new_text_of_the_index_note = index_note.text()
    assert str(link_to_add) in new_text_of_the_index_note
    print(new_text_of_the_index_note)# 1. Section
- [[note_11]]
- [[note_12]]
- [[note_13]]
# 2. Section
- [[note_21]]
- [[another_note]]
- [[note_22]]
# 3. Section
- [[note_31]]
- [[note_32]]
- [[a_note_belonging_in_3_section_1_chapter_reference_1]]
# 4. Section
- [[note_41]]
- [[note_42]]If there are multiple instances of note_to_add_link_after, then the link is added after the first link to note_to_add_link_after.
with tempfile.TemporaryDirectory(prefix='temp_dir', dir=os.getcwd()) as temp_dir:
    temp_vault = Path(temp_dir) / 'test_vault_2'
    shutil.copytree(_test_directory() / 'test_vault_2', temp_vault)
    index_note = VaultNote(temp_vault, name='_index_2_chapter_reference_3')
    note_1 = VaultNote(temp_vault, name='link_1')
    link_to_add = ObsidianLink(False, 'new_link', 0, 0)
    add_link_in_index_note_after_note_link(index_note, note_1, link_to_add)
    new_text_of_the_index_note = index_note.text()
    assert new_text_of_the_index_note.startswith('- [[link_1]]\n- [[new_link]]')
    assert str(link_to_add) in new_text_of_the_index_note
    print(new_text_of_the_index_note)- [[link_1]]
- [[new_link]]
- [[link_2]]
- [[link_3]]
- [[link_1]]If there are no links to note_to_add_link_after, then the link is added at the end.
# TODO: do example.
with tempfile.TemporaryDirectory(prefix='temp_dir', dir=os.getcwd()) as temp_dir:
    temp_vault = Path(temp_dir) / 'test_vault_2'
    shutil.copytree(_test_directory() / 'test_vault_2', temp_vault)
    index_note = VaultNote(temp_vault, name='_index_2_chapter_reference_3')
    note_1 = VaultNote(temp_vault, name='link_that_does_not_exist')
    link_to_add = ObsidianLink(False, 'new_link', 0, 0)
    add_link_in_index_note_after_note_link(index_note, note_1, link_to_add)
    new_text_of_the_index_note = index_note.text()
    print(new_text_of_the_index_note)
    assert new_text_of_the_index_note.endswith('- [[link_1]]\n- [[new_link]]')
    assert str(link_to_add) in new_text_of_the_index_note- [[link_1]]
- [[link_2]]
- [[link_3]]
- [[link_1]]
- [[new_link]]