Note: Extract code for serializing and deserializing timestamps into a separate method
TapeWidget: Extract code for replacing the model into set_model()
note_model_helpers: Implement item_to_note() and item_to_id()
Implement note_model_helpers.all_notes() and use it to reimplement TapeWidget.notes()
ModelHelpersTest.setUp(): Make less repetitive by extracting parts into a method
NoteTest.test_from_dict_should_detect_missing_properties(): Make the test less repetitive

- Iterate over missing properties in a loop
Note: Add 'id' attribute

- This breaks file format compatibility because the ID is mandatory in
from_dict()
Unit test for Note.to_dict()
Note.from_dict(): Include the names of missing properties in the exception message
NoteEdit: Preserve Note.id
note_model_helpers: Implement dump_notes(), load_notes() and assing_note_ids()
Align exception class definitions for readability
Remove TapeWidget.load_notes() and pass it notes inside a constructed model
Make MainWindow save the whole note tree, not only the top level
Fix one of the note titles in NOTE_FILE_FIXTURES[]
import_opera_notes(): Extract tree validation and trash detection into separate iterators
Fix the name of test_import_opera_notes_should_not_detect_invalid_trash_folder_attribute_values()

It should be
test_import_opera_notes_should_not_detect_invalid_trash_folder_attribute_values()
(i.e. without 'not')
import_opera_notes(): Assume that element_to_note() always returns non-null note

- It could return None in earlier versions but it's no longer the case
import_opera_notes(): Return notes as a QStandardItemModel rather than a list
opera.hotlist.importer: Import notes as a tree rather than as a list and stop putting folder path in tags