diff --git a/src/vars_gridview/lib/m3/query.py b/src/vars_gridview/lib/m3/query.py index c811250..d843793 100644 --- a/src/vars_gridview/lib/m3/query.py +++ b/src/vars_gridview/lib/m3/query.py @@ -16,6 +16,7 @@ class QueryConstraint: in_: list[str] | None = None isnull: bool | None = None like: str | None = None + notlike: str | None = None max: float | None = None min: float | None = None minmax: list[float] | None = None @@ -29,6 +30,7 @@ def to_dict(self, skip_null: bool = True) -> dict: "in": self.in_, "isnull": self.isnull, "like": self.like, + "notlike": self.notlike, "max": self.max, "min": self.min, "minmax": self.minmax, diff --git a/src/vars_gridview/ui/QueryDialog.py b/src/vars_gridview/ui/QueryDialog.py index 30755cd..0ad6ad2 100644 --- a/src/vars_gridview/ui/QueryDialog.py +++ b/src/vars_gridview/ui/QueryDialog.py @@ -199,6 +199,19 @@ def __str__(self) -> str: return "Verified" +class NotVerifiedConstraintResult(BaseResult): + """ + A constraint result that creates a constraint to check if the verifier field is NOT present (i.e., not verified). + """ + + @property + def constraints(self) -> Iterable[QueryConstraint]: + yield QueryConstraint(column="link_value", notlike='%"verifier":%') + + def __str__(self) -> str: + return "Not Verified" + + class ConceptInConstraintResult(InConstraintResult): """ A constraint result that creates an IN constraint for a list of concepts. @@ -880,6 +893,7 @@ def _create_default_filters(self) -> List[BaseFilter]: GeneratorFilter(self, "Generator", "generator", prompt="Generator"), VerifierFilter(self, "Verifier", "verifier", prompt="Verifier"), FunctionalFilter(self, "Verified", lambda: VerifiedConstraintResult()), + FunctionalFilter(self, "Not Verified", lambda: NotVerifiedConstraintResult()), ] @pyqtSlot() diff --git a/tests/test_query_constraints.py b/tests/test_query_constraints.py index a8cf84e..b2565da 100644 --- a/tests/test_query_constraints.py +++ b/tests/test_query_constraints.py @@ -51,5 +51,38 @@ def test_constraint_to_dict_skip_null(self): self.assertNotIn("equals", result_dict) +class TestLikeConstraints(unittest.TestCase): + """Test the like and notlike constraints functionality.""" + + def test_like_constraint(self): + """Test that a like constraint is created correctly.""" + constraint = QueryConstraint(column="link_value", like='%"verifier":%') + result_dict = constraint.to_dict() + + self.assertEqual(result_dict["column"], "link_value") + self.assertEqual(result_dict["like"], '%"verifier":%') + self.assertNotIn("notlike", result_dict) + + def test_notlike_constraint(self): + """Test that a notlike constraint is created correctly.""" + constraint = QueryConstraint(column="link_value", notlike='%"verifier":%') + result_dict = constraint.to_dict() + + self.assertEqual(result_dict["column"], "link_value") + self.assertEqual(result_dict["notlike"], '%"verifier":%') + self.assertNotIn("like", result_dict) + + def test_notlike_skip_null(self): + """Test that null notlike value is excluded when skip_null=True.""" + constraint = QueryConstraint( + column="link_value", + notlike=None + ) + result_dict = constraint.to_dict(skip_null=True) + + self.assertIn("column", result_dict) + self.assertNotIn("notlike", result_dict) + + if __name__ == "__main__": unittest.main()