Repository: marcosschroh/dataclasses-avroschema

Issue:
Issue with case sensitivity in enums
**Describe the bug**
python Fields that represent enums values are translated directly to UPPERCASE regardless of their original casing.
This results in duplicate fields if original values from the schema only differ in casing.

I encounted this is issue in our from standards derived avro models that use SI units that have quite some cases where this is an issue like M(ega) vs m(illi), M(onth) vs m(inute) etc.

```
{
  "name": "UnitMultiplier",
  "type": "enum",
  "symbols": [
    "p",
    "P",
     ....
  ]
}
```
results in
```
class UnitMultiplier(enum.Enum):
    P = "p"
    P = "P"
    ... 
```

Perhaps rename the non-original uppercase P field to 'P_' or something when duplicates are found.

**To Reproduce**
See above

**Expected behavior**
Support for case-sensitive uniqueness such that any valid avro schema results in a valid generated python model.


Comments:

Patch:
diff --git a/dataclasses_avroschema/model_generator/generator.py b/dataclasses_avroschema/model_generator/generator.py
index 57290e3..acce0e1 100644
--- a/dataclasses_avroschema/model_generator/generator.py
+++ b/dataclasses_avroschema/model_generator/generator.py
@@ -424,10 +424,18 @@ class ModelGenerator:
 
         field_name: str = field["name"]
         enum_name = casefy.pascalcase(field_name)
+
+        symbols_map = {}
+        for symbol in field["symbols"]:
+            key = casefy.uppercase(symbol)
+            if key in symbols_map:
+                key = symbol
+            symbols_map[key] = symbol
+
         symbols = self.field_identation.join(
             [
-                templates.enum_symbol_template.safe_substitute(key=casefy.uppercase(symbol), value=f'"{symbol}"')
-                for symbol in field["symbols"]
+                templates.enum_symbol_template.safe_substitute(key=key, value=f'"{value}"')
+                for key, value in symbols_map.items()
             ]
         )
         docstring = self.render_docstring(docstring=field.get("doc"))
diff --git a/docs/model_generator.md b/docs/model_generator.md
index 5b2975c..351e554 100644
--- a/docs/model_generator.md
+++ b/docs/model_generator.md
@@ -411,3 +411,58 @@ class User(AvroModel):
     class Meta:
         field_order = ['has_pets', 'name', 'age', 'money']
 ```
+
+## Enums and case sensitivity
+
+Sometimes there are schemas that contains the `symbols` which are case sensivity, for example `"symbols": ["P", "p"]`.
+Having something like that is NOT reccomended at all because it is meaninless, really hard to undestand the intention of it. Avoid it!!!
+
+When the schema generator encounter this situation it can not generated the proper `enum` with `uppercases` key so it will use the `symbol` without any transformation
+
+```python
+from dataclasses_avroschema import ModelGenerator
+
+schema = {
+    "type": "record",
+    "name": "User",
+    "fields": [
+        {
+            "name": "unit_multi_player",
+            "type": {
+                "type": "enum",
+                "name": "unit_multi_player",
+                "symbols": ["Q", "q"],
+            },
+        }
+    ],
+}
+
+model_generator = ModelGenerator()
+result = model_generator.render(schema=schema)
+
+# save the result in a file
+with open("models.py", mode="+w") as f:
+    f.write(result)
+```
+
+Then the result will be:
+
+```python
+# models.py
+from dataclasses_avroschema import AvroModel
+import dataclasses
+import enum
+
+
+class UnitMultiPlayer(enum.Enum):
+    Q = "Q"
+    q = "q"
+
+
+@dataclasses.dataclass
+class User(AvroModel):
+    unit_multi_player: UnitMultiPlayer
+
+```
+
+As the example shows the second enum member `UnitMultiPlayer.p` is not in uppercase otherwise will collide with the first member `UnitMultiPlayer.P`


Test patch:
diff --git a/tests/model_generator/conftest.py b/tests/model_generator/conftest.py
index bec01d9..58873a6 100644
--- a/tests/model_generator/conftest.py
+++ b/tests/model_generator/conftest.py
@@ -212,6 +212,24 @@ def schema_with_enum_types() -> Dict:
     }
 
 
+@pytest.fixture
+def schema_with_enum_types_case_sensitivity() -> Dict:
+    return {
+        "type": "record",
+        "name": "User",
+        "fields": [
+            {
+                "name": "unit_multi_player",
+                "type": {
+                    "type": "enum",
+                    "name": "unit_multi_player",
+                    "symbols": ["Q", "q"],
+                },
+            }
+        ],
+    }
+
+
 @pytest.fixture
 def schema_one_to_one_relationship() -> JsonDict:
     return {
diff --git a/tests/model_generator/test_model_generator.py b/tests/model_generator/test_model_generator.py
index a3d3e6d..a0a9279 100644
--- a/tests/model_generator/test_model_generator.py
+++ b/tests/model_generator/test_model_generator.py
@@ -204,6 +204,28 @@ class User(AvroModel):
     assert result.strip() == expected_result.strip()
 
 
+def test_schema_with_enum_types_case_sensitivity(schema_with_enum_types_case_sensitivity: types.JsonDict) -> None:
+    expected_result = """
+from dataclasses_avroschema import AvroModel
+import dataclasses
+import enum
+
+
+class UnitMultiPlayer(enum.Enum):
+    Q = "Q"
+    q = "q"
+
+
+@dataclasses.dataclass
+class User(AvroModel):
+    unit_multi_player: UnitMultiPlayer
+
+"""
+    model_generator = ModelGenerator()
+    result = model_generator.render(schema=schema_with_enum_types_case_sensitivity)
+    assert result.strip() == expected_result.strip()
+
+
 def test_schema_one_to_one_relationship(schema_one_to_one_relationship: types.JsonDict) -> None:
     expected_result = """
 from dataclasses_avroschema import AvroModel