We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Simple condition (without and/or) does not work in event rule
### Deployment Type

Self-hosted

### NetBox Version

v3.7.0

### Python Version

3.11

### Steps to Reproduce

1. Create webhook: Name = Test, URL = http://127.0.0.1:9000 (doesn't matter in this case, it won't be triggered but is required to configure event rule)
2. Go to **Event rules - Add**:
- Name = Test
- Content types = Circuit
- select Updates
- set Conditions:
```
{
    "attr": "status.value",
    "value": "active"
}
```

- Action type = Webhook
- Webhook = Test
- **Create**


### Expected Behavior

Event rule is created

### Observed Behavior

Error is shown about the condition:

**Ruleset must have exactly one logical operator (found 2)** 

The examples in https://docs.netbox.dev/en/stable/reference/conditions/ look the same: simple JSON object with attributes `attr` and `value`.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `netbox/extras/conditions.py`
Content:
```
1 import functools
2 import re
3 from django.utils.translation import gettext as _
4 
5 __all__ = (
6     'Condition',
7     'ConditionSet',
8 )
9 
10 
11 AND = 'and'
12 OR = 'or'
13 
14 
15 def is_ruleset(data):
16     """
17     Determine whether the given dictionary looks like a rule set.
18     """
19     return type(data) is dict and len(data) == 1 and list(data.keys())[0] in (AND, OR)
20 
21 
22 class Condition:
23     """
24     An individual conditional rule that evaluates a single attribute and its value.
25 
26     :param attr: The name of the attribute being evaluated
27     :param value: The value being compared
28     :param op: The logical operation to use when evaluating the value (default: 'eq')
29     """
30     EQ = 'eq'
31     GT = 'gt'
32     GTE = 'gte'
33     LT = 'lt'
34     LTE = 'lte'
35     IN = 'in'
36     CONTAINS = 'contains'
37     REGEX = 'regex'
38 
39     OPERATORS = (
40         EQ, GT, GTE, LT, LTE, IN, CONTAINS, REGEX
41     )
42 
43     TYPES = {
44         str: (EQ, CONTAINS, REGEX),
45         bool: (EQ, CONTAINS),
46         int: (EQ, GT, GTE, LT, LTE, CONTAINS),
47         float: (EQ, GT, GTE, LT, LTE, CONTAINS),
48         list: (EQ, IN, CONTAINS),
49         type(None): (EQ,)
50     }
51 
52     def __init__(self, attr, value, op=EQ, negate=False):
53         if op not in self.OPERATORS:
54             raise ValueError(_("Unknown operator: {op}. Must be one of: {operators}").format(
55                 op=op, operators=', '.join(self.OPERATORS)
56             ))
57         if type(value) not in self.TYPES:
58             raise ValueError(_("Unsupported value type: {value}").format(value=type(value)))
59         if op not in self.TYPES[type(value)]:
60             raise ValueError(_("Invalid type for {op} operation: {value}").format(op=op, value=type(value)))
61 
62         self.attr = attr
63         self.value = value
64         self.eval_func = getattr(self, f'eval_{op}')
65         self.negate = negate
66 
67     def eval(self, data):
68         """
69         Evaluate the provided data to determine whether it matches the condition.
70         """
71         def _get(obj, key):
72             if isinstance(obj, list):
73                 return [dict.get(i, key) for i in obj]
74 
75             return dict.get(obj, key)
76 
77         try:
78             value = functools.reduce(_get, self.attr.split('.'), data)
79         except TypeError:
80             # Invalid key path
81             value = None
82         result = self.eval_func(value)
83 
84         if self.negate:
85             return not result
86         return result
87 
88     # Equivalency
89 
90     def eval_eq(self, value):
91         return value == self.value
92 
93     def eval_neq(self, value):
94         return value != self.value
95 
96     # Numeric comparisons
97 
98     def eval_gt(self, value):
99         return value > self.value
100 
101     def eval_gte(self, value):
102         return value >= self.value
103 
104     def eval_lt(self, value):
105         return value < self.value
106 
107     def eval_lte(self, value):
108         return value <= self.value
109 
110     # Membership
111 
112     def eval_in(self, value):
113         return value in self.value
114 
115     def eval_contains(self, value):
116         return self.value in value
117 
118     # Regular expressions
119 
120     def eval_regex(self, value):
121         return re.match(self.value, value) is not None
122 
123 
124 class ConditionSet:
125     """
126     A set of one or more Condition to be evaluated per the prescribed logic (AND or OR). Example:
127 
128     {"and": [
129         {"attr": "foo", "op": "eq", "value": 1},
130         {"attr": "bar", "op": "eq", "value": 2, "negate": true}
131     ]}
132 
133     :param ruleset: A dictionary mapping a logical operator to a list of conditional rules
134     """
135     def __init__(self, ruleset):
136         if type(ruleset) is not dict:
137             raise ValueError(_("Ruleset must be a dictionary, not {ruleset}.").format(ruleset=type(ruleset)))
138         if len(ruleset) != 1:
139             raise ValueError(_("Ruleset must have exactly one logical operator (found {ruleset})").format(
140                 ruleset=len(ruleset)))
141 
142         # Determine the logic type
143         logic = list(ruleset.keys())[0]
144         if type(logic) is not str or logic.lower() not in (AND, OR):
145             raise ValueError(_("Invalid logic type: {logic} (must be '{op_and}' or '{op_or}')").format(
146                 logic=logic, op_and=AND, op_or=OR
147             ))
148         self.logic = logic.lower()
149 
150         # Compile the set of Conditions
151         self.conditions = [
152             ConditionSet(rule) if is_ruleset(rule) else Condition(**rule)
153             for rule in ruleset[self.logic]
154         ]
155 
156     def eval(self, data):
157         """
158         Evaluate the provided data to determine whether it matches this set of conditions.
159         """
160         func = any if self.logic == 'or' else all
161         return func(d.eval(data) for d in self.conditions)
162
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/netbox/extras/conditions.py b/netbox/extras/conditions.py
--- a/netbox/extras/conditions.py
+++ b/netbox/extras/conditions.py
@@ -135,23 +135,23 @@
     def __init__(self, ruleset):
         if type(ruleset) is not dict:
             raise ValueError(_("Ruleset must be a dictionary, not {ruleset}.").format(ruleset=type(ruleset)))
-        if len(ruleset) != 1:
-            raise ValueError(_("Ruleset must have exactly one logical operator (found {ruleset})").format(
-                ruleset=len(ruleset)))
-
-        # Determine the logic type
-        logic = list(ruleset.keys())[0]
-        if type(logic) is not str or logic.lower() not in (AND, OR):
-            raise ValueError(_("Invalid logic type: {logic} (must be '{op_and}' or '{op_or}')").format(
-                logic=logic, op_and=AND, op_or=OR
-            ))
-        self.logic = logic.lower()
 
-        # Compile the set of Conditions
-        self.conditions = [
-            ConditionSet(rule) if is_ruleset(rule) else Condition(**rule)
-            for rule in ruleset[self.logic]
-        ]
+        if len(ruleset) == 1:
+            self.logic = (list(ruleset.keys())[0]).lower()
+            if self.logic not in (AND, OR):
+                raise ValueError(_("Invalid logic type: must be 'AND' or 'OR'. Please check documentation."))
+
+            # Compile the set of Conditions
+            self.conditions = [
+                ConditionSet(rule) if is_ruleset(rule) else Condition(**rule)
+                for rule in ruleset[self.logic]
+            ]
+        else:
+            try:
+                self.logic = None
+                self.conditions = [Condition(**ruleset)]
+            except TypeError:
+                raise ValueError(_("Incorrect key(s) informed. Please check documentation."))
 
     def eval(self, data):
         """

{"golden_diff": "diff --git a/netbox/extras/conditions.py b/netbox/extras/conditions.py\n--- a/netbox/extras/conditions.py\n+++ b/netbox/extras/conditions.py\n@@ -135,23 +135,23 @@\n     def __init__(self, ruleset):\n         if type(ruleset) is not dict:\n             raise ValueError(_(\"Ruleset must be a dictionary, not {ruleset}.\").format(ruleset=type(ruleset)))\n-        if len(ruleset) != 1:\n-            raise ValueError(_(\"Ruleset must have exactly one logical operator (found {ruleset})\").format(\n-                ruleset=len(ruleset)))\n-\n-        # Determine the logic type\n-        logic = list(ruleset.keys())[0]\n-        if type(logic) is not str or logic.lower() not in (AND, OR):\n-            raise ValueError(_(\"Invalid logic type: {logic} (must be '{op_and}' or '{op_or}')\").format(\n-                logic=logic, op_and=AND, op_or=OR\n-            ))\n-        self.logic = logic.lower()\n \n-        # Compile the set of Conditions\n-        self.conditions = [\n-            ConditionSet(rule) if is_ruleset(rule) else Condition(**rule)\n-            for rule in ruleset[self.logic]\n-        ]\n+        if len(ruleset) == 1:\n+            self.logic = (list(ruleset.keys())[0]).lower()\n+            if self.logic not in (AND, OR):\n+                raise ValueError(_(\"Invalid logic type: must be 'AND' or 'OR'. Please check documentation.\"))\n+\n+            # Compile the set of Conditions\n+            self.conditions = [\n+                ConditionSet(rule) if is_ruleset(rule) else Condition(**rule)\n+                for rule in ruleset[self.logic]\n+            ]\n+        else:\n+            try:\n+                self.logic = None\n+                self.conditions = [Condition(**ruleset)]\n+            except TypeError:\n+                raise ValueError(_(\"Incorrect key(s) informed. Please check documentation.\"))\n \n     def eval(self, data):\n         \"\"\"\n", "issue": "Simple condition (without and/or) does not work in event rule\n### Deployment Type\n\nSelf-hosted\n\n### NetBox Version\n\nv3.7.0\n\n### Python Version\n\n3.11\n\n### Steps to Reproduce\n\n1. Create webhook: Name = Test, URL = http://127.0.0.1:9000 (doesn't matter in this case, it won't be triggered but is required to configure event rule)\r\n2. Go to **Event rules - Add**:\r\n- Name = Test\r\n- Content types = Circuit\r\n- select Updates\r\n- set Conditions:\r\n```\r\n{\r\n    \"attr\": \"status.value\",\r\n    \"value\": \"active\"\r\n}\r\n```\r\n\r\n- Action type = Webhook\r\n- Webhook = Test\r\n- **Create**\r\n\n\n### Expected Behavior\n\nEvent rule is created\n\n### Observed Behavior\n\nError is shown about the condition:\r\n\r\n**Ruleset must have exactly one logical operator (found 2)** \r\n\r\nThe examples in https://docs.netbox.dev/en/stable/reference/conditions/ look the same: simple JSON object with attributes `attr` and `value`.\n", "before_files": [{"content": "import functools\nimport re\nfrom django.utils.translation import gettext as _\n\n__all__ = (\n    'Condition',\n    'ConditionSet',\n)\n\n\nAND = 'and'\nOR = 'or'\n\n\ndef is_ruleset(data):\n    \"\"\"\n    Determine whether the given dictionary looks like a rule set.\n    \"\"\"\n    return type(data) is dict and len(data) == 1 and list(data.keys())[0] in (AND, OR)\n\n\nclass Condition:\n    \"\"\"\n    An individual conditional rule that evaluates a single attribute and its value.\n\n    :param attr: The name of the attribute being evaluated\n    :param value: The value being compared\n    :param op: The logical operation to use when evaluating the value (default: 'eq')\n    \"\"\"\n    EQ = 'eq'\n    GT = 'gt'\n    GTE = 'gte'\n    LT = 'lt'\n    LTE = 'lte'\n    IN = 'in'\n    CONTAINS = 'contains'\n    REGEX = 'regex'\n\n    OPERATORS = (\n        EQ, GT, GTE, LT, LTE, IN, CONTAINS, REGEX\n    )\n\n    TYPES = {\n        str: (EQ, CONTAINS, REGEX),\n        bool: (EQ, CONTAINS),\n        int: (EQ, GT, GTE, LT, LTE, CONTAINS),\n        float: (EQ, GT, GTE, LT, LTE, CONTAINS),\n        list: (EQ, IN, CONTAINS),\n        type(None): (EQ,)\n    }\n\n    def __init__(self, attr, value, op=EQ, negate=False):\n        if op not in self.OPERATORS:\n            raise ValueError(_(\"Unknown operator: {op}. Must be one of: {operators}\").format(\n                op=op, operators=', '.join(self.OPERATORS)\n            ))\n        if type(value) not in self.TYPES:\n            raise ValueError(_(\"Unsupported value type: {value}\").format(value=type(value)))\n        if op not in self.TYPES[type(value)]:\n            raise ValueError(_(\"Invalid type for {op} operation: {value}\").format(op=op, value=type(value)))\n\n        self.attr = attr\n        self.value = value\n        self.eval_func = getattr(self, f'eval_{op}')\n        self.negate = negate\n\n    def eval(self, data):\n        \"\"\"\n        Evaluate the provided data to determine whether it matches the condition.\n        \"\"\"\n        def _get(obj, key):\n            if isinstance(obj, list):\n                return [dict.get(i, key) for i in obj]\n\n            return dict.get(obj, key)\n\n        try:\n            value = functools.reduce(_get, self.attr.split('.'), data)\n        except TypeError:\n            # Invalid key path\n            value = None\n        result = self.eval_func(value)\n\n        if self.negate:\n            return not result\n        return result\n\n    # Equivalency\n\n    def eval_eq(self, value):\n        return value == self.value\n\n    def eval_neq(self, value):\n        return value != self.value\n\n    # Numeric comparisons\n\n    def eval_gt(self, value):\n        return value > self.value\n\n    def eval_gte(self, value):\n        return value >= self.value\n\n    def eval_lt(self, value):\n        return value < self.value\n\n    def eval_lte(self, value):\n        return value <= self.value\n\n    # Membership\n\n    def eval_in(self, value):\n        return value in self.value\n\n    def eval_contains(self, value):\n        return self.value in value\n\n    # Regular expressions\n\n    def eval_regex(self, value):\n        return re.match(self.value, value) is not None\n\n\nclass ConditionSet:\n    \"\"\"\n    A set of one or more Condition to be evaluated per the prescribed logic (AND or OR). Example:\n\n    {\"and\": [\n        {\"attr\": \"foo\", \"op\": \"eq\", \"value\": 1},\n        {\"attr\": \"bar\", \"op\": \"eq\", \"value\": 2, \"negate\": true}\n    ]}\n\n    :param ruleset: A dictionary mapping a logical operator to a list of conditional rules\n    \"\"\"\n    def __init__(self, ruleset):\n        if type(ruleset) is not dict:\n            raise ValueError(_(\"Ruleset must be a dictionary, not {ruleset}.\").format(ruleset=type(ruleset)))\n        if len(ruleset) != 1:\n            raise ValueError(_(\"Ruleset must have exactly one logical operator (found {ruleset})\").format(\n                ruleset=len(ruleset)))\n\n        # Determine the logic type\n        logic = list(ruleset.keys())[0]\n        if type(logic) is not str or logic.lower() not in (AND, OR):\n            raise ValueError(_(\"Invalid logic type: {logic} (must be '{op_and}' or '{op_or}')\").format(\n                logic=logic, op_and=AND, op_or=OR\n            ))\n        self.logic = logic.lower()\n\n        # Compile the set of Conditions\n        self.conditions = [\n            ConditionSet(rule) if is_ruleset(rule) else Condition(**rule)\n            for rule in ruleset[self.logic]\n        ]\n\n    def eval(self, data):\n        \"\"\"\n        Evaluate the provided data to determine whether it matches this set of conditions.\n        \"\"\"\n        func = any if self.logic == 'or' else all\n        return func(d.eval(data) for d in self.conditions)\n", "path": "netbox/extras/conditions.py"}], "after_files": [{"content": "import functools\nimport re\nfrom django.utils.translation import gettext as _\n\n__all__ = (\n    'Condition',\n    'ConditionSet',\n)\n\n\nAND = 'and'\nOR = 'or'\n\n\ndef is_ruleset(data):\n    \"\"\"\n    Determine whether the given dictionary looks like a rule set.\n    \"\"\"\n    return type(data) is dict and len(data) == 1 and list(data.keys())[0] in (AND, OR)\n\n\nclass Condition:\n    \"\"\"\n    An individual conditional rule that evaluates a single attribute and its value.\n\n    :param attr: The name of the attribute being evaluated\n    :param value: The value being compared\n    :param op: The logical operation to use when evaluating the value (default: 'eq')\n    \"\"\"\n    EQ = 'eq'\n    GT = 'gt'\n    GTE = 'gte'\n    LT = 'lt'\n    LTE = 'lte'\n    IN = 'in'\n    CONTAINS = 'contains'\n    REGEX = 'regex'\n\n    OPERATORS = (\n        EQ, GT, GTE, LT, LTE, IN, CONTAINS, REGEX\n    )\n\n    TYPES = {\n        str: (EQ, CONTAINS, REGEX),\n        bool: (EQ, CONTAINS),\n        int: (EQ, GT, GTE, LT, LTE, CONTAINS),\n        float: (EQ, GT, GTE, LT, LTE, CONTAINS),\n        list: (EQ, IN, CONTAINS),\n        type(None): (EQ,)\n    }\n\n    def __init__(self, attr, value, op=EQ, negate=False):\n        if op not in self.OPERATORS:\n            raise ValueError(_(\"Unknown operator: {op}. Must be one of: {operators}\").format(\n                op=op, operators=', '.join(self.OPERATORS)\n            ))\n        if type(value) not in self.TYPES:\n            raise ValueError(_(\"Unsupported value type: {value}\").format(value=type(value)))\n        if op not in self.TYPES[type(value)]:\n            raise ValueError(_(\"Invalid type for {op} operation: {value}\").format(op=op, value=type(value)))\n\n        self.attr = attr\n        self.value = value\n        self.eval_func = getattr(self, f'eval_{op}')\n        self.negate = negate\n\n    def eval(self, data):\n        \"\"\"\n        Evaluate the provided data to determine whether it matches the condition.\n        \"\"\"\n        def _get(obj, key):\n            if isinstance(obj, list):\n                return [dict.get(i, key) for i in obj]\n\n            return dict.get(obj, key)\n\n        try:\n            value = functools.reduce(_get, self.attr.split('.'), data)\n        except TypeError:\n            # Invalid key path\n            value = None\n        result = self.eval_func(value)\n\n        if self.negate:\n            return not result\n        return result\n\n    # Equivalency\n\n    def eval_eq(self, value):\n        return value == self.value\n\n    def eval_neq(self, value):\n        return value != self.value\n\n    # Numeric comparisons\n\n    def eval_gt(self, value):\n        return value > self.value\n\n    def eval_gte(self, value):\n        return value >= self.value\n\n    def eval_lt(self, value):\n        return value < self.value\n\n    def eval_lte(self, value):\n        return value <= self.value\n\n    # Membership\n\n    def eval_in(self, value):\n        return value in self.value\n\n    def eval_contains(self, value):\n        return self.value in value\n\n    # Regular expressions\n\n    def eval_regex(self, value):\n        return re.match(self.value, value) is not None\n\n\nclass ConditionSet:\n    \"\"\"\n    A set of one or more Condition to be evaluated per the prescribed logic (AND or OR). Example:\n\n    {\"and\": [\n        {\"attr\": \"foo\", \"op\": \"eq\", \"value\": 1},\n        {\"attr\": \"bar\", \"op\": \"eq\", \"value\": 2, \"negate\": true}\n    ]}\n\n    :param ruleset: A dictionary mapping a logical operator to a list of conditional rules\n    \"\"\"\n    def __init__(self, ruleset):\n        if type(ruleset) is not dict:\n            raise ValueError(_(\"Ruleset must be a dictionary, not {ruleset}.\").format(ruleset=type(ruleset)))\n\n        if len(ruleset) == 1:\n            self.logic = (list(ruleset.keys())[0]).lower()\n            if self.logic not in (AND, OR):\n                raise ValueError(_(\"Invalid logic type: must be 'AND' or 'OR'. Please check documentation.\"))\n\n            # Compile the set of Conditions\n            self.conditions = [\n                ConditionSet(rule) if is_ruleset(rule) else Condition(**rule)\n                for rule in ruleset[self.logic]\n            ]\n        else:\n            try:\n                self.logic = None\n                self.conditions = [Condition(**ruleset)]\n            except TypeError:\n                raise ValueError(_(\"Incorrect key(s) informed. Please check documentation.\"))\n\n    def eval(self, data):\n        \"\"\"\n        Evaluate the provided data to determine whether it matches this set of conditions.\n        \"\"\"\n        func = any if self.logic == 'or' else all\n        return func(d.eval(data) for d in self.conditions)\n", "path": "netbox/extras/conditions.py"}]}