Repository: AllenNeuralDynamics/aind-data-schema

Issue:
Typo in AuditoryStimulation class property
**Describe the bug**
The following property has a typo:
https://github.com/AllenNeuralDynamics/aind-data-schema/blob/07cb5b2c5933699b7598af3128fa8d997e016c9a/src/aind_data_schema/components/stimulus.py#L127

`sitmulus_name` should read `stimulus_name`

**Additional context**
A potential solution to get around these problems in the future is to run linters that also run a quick spell check, for instance look into [codespell](https://github.com/codespell-project/codespell) 
Edit: it appears that codespell is already part of the dependencies but perhaps not run during actions? 

I would recommend using a strategy similar to the one described in https://github.com/AllenNeuralDynamics/aind-data-schema-models/pull/61 to allow for some degree of backward compatibility. In other words, have a `BeforeValidator` that uses the current field name into the corrected one.

Discussion:

Solution patch:
diff --git a/docs/source/quality_control.md b/docs/source/quality_control.md
index 6fbf43af2..a561ecb0f 100644
--- a/docs/source/quality_control.md
+++ b/docs/source/quality_control.md
@@ -72,7 +72,7 @@ We recommend that you write PNG files for images and static multi-panel figures,
 
 **Metadata**
 
-We'll post documentation on how to append `QCEvaluations` to pre-existing quality_control.json files, via DocDB using the `aind-data-access-api`, in the future.
+We'll post documentation on how to append `QCEvaluations` to pre-existing quality_control.json files, via DocDB using the `aind-data-access-api`, in the future. For now, you can refer to the code snippet in the [`aind-qc-capsule-example`](https://github.com/AllenNeuralDynamics/aind-qc-capsule-example/).
 
 **References**
 
@@ -90,17 +90,15 @@ The QC portal automatically calls ``QualityControl.evaluate_status()`` whenever
 
 **Q: How do reference URLs get pulled into the QC Portal?**
 
-Each metric is associated with a reference figure, image, or video. The QC portal can interpret four ways of setting the reference field:
+Each metric is associated with a reference figure (PDF preferred), image (png preferred), or video (mp4 preferred). The QC portal can interpret four ways of setting the reference field:
 
-- Provide a relative path to a file in this data asset's S3 bucket
+- Provide a relative path to a file in this data asset's S3 bucket, i.e. "figures/my_figure.png". The mount/asset name should not be included.
 - Provide a url to a FigURL figure
 - Provide the name of one of the interactive plots, e.g. "ecephys-drift-map"
 
-<!-- There are many situations where it's helpful to be able to "swipe" between two images. If you have two identical images separated by a ';' the portal will allow users to swipe between them. For example, you might show snippets of the raw electrophysiology raster with detected spikes overlaid on the swipe. -->
-
 **Q: I saw fancy things like dropdowns in the QC Portal, how do I do that?**
 
-By default the QC portal displays dictionaries as tables where the values can be edited. We also support a few special cases to allow a bit more flexibility or to constrain the actions that manual annotators can take. Install the `aind-qcportal-schema` package and set the `value` field to the corresponding pydantic object to use these. 
+By default the QC portal displays dictionaries as tables where the values can be edited. We also support a few special cases to allow a bit more flexibility or to constrain the actions that manual annotators can take. Install the [`aind-qcportal-schema`](https://github.com/AllenNeuralDynamics/aind-qcportal-schema/blob/dev/src/aind_qcportal_schema/metric_value.py) package and set the `value` field to the corresponding pydantic object to use these. 
 
 ### Multi-asset QC
 
diff --git a/src/aind_data_schema/components/stimulus.py b/src/aind_data_schema/components/stimulus.py
index c7dcedd42..81e4454eb 100644
--- a/src/aind_data_schema/components/stimulus.py
+++ b/src/aind_data_schema/components/stimulus.py
@@ -5,7 +5,7 @@
 from typing import List, Literal, Optional
 
 from aind_data_schema_models.units import ConcentrationUnit, FrequencyUnit, PowerUnit, TimeUnit
-from pydantic import Field
+from pydantic import Field, model_validator
 
 from aind_data_schema.base import AindGeneric, AindGenericType, AindModel
 
@@ -124,7 +124,7 @@ class AuditoryStimulation(AindModel):
     """Description of an auditory stimulus"""
 
     stimulus_type: Literal["Auditory Stimulation"] = "Auditory Stimulation"
-    sitmulus_name: str = Field(..., title="Stimulus name")
+    stimulus_name: str = Field(..., title="Stimulus name")
     sample_frequency: Decimal = Field(..., title="Sample frequency")
     amplitude_modulation_frequency: Optional[int] = Field(default=None, title="Amplitude modulation frequency")
     frequency_unit: FrequencyUnit = Field(default=FrequencyUnit.HZ, title="Tone frequency unit")
@@ -133,3 +133,10 @@ class AuditoryStimulation(AindModel):
     bandpass_filter_type: Optional[FilterType] = Field(default=None, title="Bandpass filter type")
     bandpass_order: Optional[int] = Field(default=None, title="Bandpass order")
     notes: Optional[str] = Field(default=None, title="Notes")
+
+    @model_validator(mode="before")
+    def correct_typo(cls, values):
+        """Correct 'sitmulus_name' typo."""
+        if "sitmulus_name" in values:
+            values["stimulus_name"] = values.pop("sitmulus_name")
+        return values


Test patch:
diff --git a/tests/test_components_stimulus.py b/tests/test_components_stimulus.py
new file mode 100644
index 000000000..853005065
--- /dev/null
+++ b/tests/test_components_stimulus.py
@@ -0,0 +1,21 @@
+"""Test components.stimulus"""
+
+import unittest
+from aind_data_schema.components.stimulus import AuditoryStimulation
+
+
+class StimulusTests(unittest.TestCase):
+    """tests components.stimulus"""
+
+    def test_typo(self):
+        """tests that the sitmulus typo is corrected"""
+        a = AuditoryStimulation(
+            stimulus_type="Auditory Stimulation",
+            stimulus_name="test",
+            sample_frequency=0.5,
+        )
+
+        a_dict = a.model_dump()
+        a_dict["sitmulus_name"] = a_dict.pop("stimulus_name")
+
+        self.assertEqual(a.model_dump(), AuditoryStimulation(**a_dict).model_dump())