query_filter.py 4.7 KB

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