query_filter.py 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. import csv
  2. from dataclasses import dataclass, field
  3. from urllib.parse import quote, unquote
  4. EMPTY_FILTER_VALUE = "__empty__"
  5. EMPTY_FILTER_LABEL = "Ohne Wert"
  6. def decode_filter_values(value: str | None) -> list[str]:
  7. """Split the GET-decoded parameter before decoding individual values."""
  8. return list(dict.fromkeys(decoded for part in (value or "").split(",") if (decoded := unquote(part).strip())))
  9. def encode_filter_values(values) -> str:
  10. return ",".join(quote(value, safe="") for value in dict.fromkeys(str(value).strip() for value in values) if value)
  11. def filter_clauses(config, params, exclude=None):
  12. clauses, values = [], {}
  13. for f in config:
  14. current = params.get(f.name, f.default_value)
  15. if f.name == exclude or f.filter_type == "hidden" or not current:
  16. continue
  17. parameter = f"filter_{f.name.lower()}"
  18. if f.filter_type == "date":
  19. operator = "<=" if "bis" in f.name.lower() else ">="
  20. clauses.append(f"F.[{f.key_column}] {operator} :{parameter}")
  21. values[parameter] = current
  22. continue
  23. parts = decode_filter_values(current)
  24. terms = []
  25. for index, part in enumerate(parts):
  26. key = parameter if len(parts) == 1 else f"{parameter}_{index}"
  27. if part == EMPTY_FILTER_VALUE:
  28. terms.append(f"NULLIF(F.[{f.key_column}], '') IS NULL")
  29. continue
  30. operator = "=" if f.filter_type == "select" else "LIKE"
  31. terms.append(f"F.[{f.key_column}] {operator} :{key}")
  32. values[key] = part if f.filter_type == "select" else f"%{part}%"
  33. if terms:
  34. clauses.append("(" + " OR ".join(terms) + ")")
  35. return clauses, values
  36. @dataclass
  37. class FilterConfig:
  38. name: str
  39. text: str
  40. filter_type: str
  41. width: str
  42. key_column: str
  43. value_column: str
  44. sort_column: str
  45. visible: bool = True
  46. default_value: str = ""
  47. options: dict[str, str] | None = field(default_factory=dict)
  48. current_value: str | None = None
  49. @property
  50. def selected_values(self) -> list[str]:
  51. return decode_filter_values(self.current_value)
  52. @property
  53. def selected_items(self) -> list[dict[str, str]]:
  54. options = {str(key): str(value) for key, value in (self.options or {}).items()}
  55. options[EMPTY_FILTER_VALUE] = EMPTY_FILTER_LABEL
  56. return [{"value": value, "label": options.get(value, value)} for value in self.selected_values]
  57. @property
  58. def query_param(self) -> str:
  59. if self.current_value is None or self.current_value == "":
  60. if self.default_value is None or self.default_value == "":
  61. return None
  62. self.current_value = self.default_value
  63. if self.filter_type == "date":
  64. if "bis" in self.name.lower():
  65. return f" F.[{self.key_column}] <= '{self.current_value}' "
  66. return f" F.[{self.key_column}] >= '{self.current_value}' "
  67. # if self.filter_type == "select":
  68. # return f"{self.key_column} IN ({self.current_value})"
  69. return f" F.[{self.key_column}] LIKE '%{self.current_value}%' "
  70. @staticmethod
  71. def from_dict(
  72. name: str,
  73. filter_type: str,
  74. width: str,
  75. text: str,
  76. key_column: str,
  77. value_column: str,
  78. sort_column: str,
  79. default_value: str,
  80. ) -> "FilterConfig":
  81. if filter_type not in ["text", "select", "date", "hidden"]:
  82. filter_type = "text"
  83. if width not in ["1", "2", "3", "4"]:
  84. width = "2"
  85. if not text:
  86. text = name
  87. if not key_column:
  88. key_column = name
  89. if not value_column:
  90. value_column = key_column
  91. if not sort_column:
  92. sort_column = key_column
  93. if not default_value:
  94. default_value = ""
  95. return FilterConfig(
  96. name=name,
  97. text=text,
  98. filter_type=filter_type,
  99. width=width,
  100. key_column=key_column,
  101. value_column=value_column,
  102. sort_column=sort_column,
  103. default_value=default_value,
  104. )
  105. filter_type_option = {
  106. "text": "text",
  107. "select": "selectedOptionValue",
  108. "date": "selectedDate",
  109. "hidden": "selectedOptionValue",
  110. }
  111. def import_filter_config(area: str) -> list[FilterConfig]:
  112. res = []
  113. with open(f"templates\\{area}\\liste\\config\\liste_filter.csv", "r", newline="", encoding="latin-1") as csvfile:
  114. reader = csv.DictReader(csvfile, delimiter=";")
  115. for row in reader:
  116. config = FilterConfig.from_dict(*row.values())
  117. res.append(config)
  118. return res