test_analytics_search_logic.py 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. #!/usr/bin/env python3
  2. # Copyright Materialize, Inc. and contributors. All rights reserved.
  3. #
  4. # Use of this software is governed by the Business Source License
  5. # included in the LICENSE file at the root of this repository.
  6. #
  7. # As of the Change Date specified in that file, in accordance with
  8. # the Business Source License, use of this software will be governed
  9. # by the Apache License, Version 2.0.
  10. import re
  11. import time
  12. from materialize.buildkite_insights.annotation_search.annotation_search_presentation import (
  13. print_annotation_match,
  14. print_before_search_results,
  15. )
  16. from materialize.buildkite_insights.data.build_annotation import BuildAnnotation
  17. from materialize.buildkite_insights.data.build_info import Build
  18. from materialize.test_analytics.search.test_analytics_search_source import (
  19. ANY_BRANCH_VALUE,
  20. ANY_PIPELINE_VALUE,
  21. TestAnalyticsDataSource,
  22. )
  23. def start_search(
  24. search_source: TestAnalyticsDataSource,
  25. pipeline_slug: str,
  26. branch: str | None,
  27. build_step_keys: list[str],
  28. only_failed_builds: bool,
  29. not_newer_than_build_number: int | None,
  30. like_pattern: str,
  31. max_results: int,
  32. short_result_presentation: bool,
  33. one_line_match_presentation: bool,
  34. ) -> None:
  35. assert len(like_pattern) > 0, "pattern must not be empty"
  36. if branch == ANY_BRANCH_VALUE:
  37. branch = None
  38. if not like_pattern.startswith("%"):
  39. like_pattern = f"%{like_pattern}"
  40. if not like_pattern.endswith("%"):
  41. like_pattern = f"{like_pattern}%"
  42. start_time = time.time()
  43. matches: list[tuple[Build, BuildAnnotation]] = search_source.search_annotations(
  44. pipeline=pipeline_slug,
  45. branch=branch,
  46. build_step_keys=build_step_keys,
  47. not_newer_than_build_number=not_newer_than_build_number,
  48. like_pattern=like_pattern,
  49. max_entries=max_results + 1,
  50. only_failed_builds=only_failed_builds,
  51. )
  52. end_time = time.time()
  53. duration_in_sec = round(end_time - start_time, 2)
  54. more_results_exist = len(matches) > max_results
  55. if more_results_exist:
  56. matches = matches[:max_results]
  57. search_value = _like_pattern_to_regex(like_pattern)
  58. print("Searching test-analytics database...")
  59. print_before_search_results()
  60. if len(matches) == 0:
  61. print("No matches found.")
  62. return
  63. for build, build_annotation in matches:
  64. print_annotation_match(
  65. build=build,
  66. annotation=build_annotation,
  67. search_value=search_value,
  68. use_regex=True,
  69. short_result_presentation=short_result_presentation,
  70. one_line_match_presentation=one_line_match_presentation,
  71. )
  72. _print_summary(pipeline_slug, matches, duration_in_sec, more_results_exist)
  73. def _print_summary(
  74. pipeline: str,
  75. matches: list[tuple[Build, BuildAnnotation]],
  76. duration_in_sec: float,
  77. more_results_exist: bool,
  78. ) -> None:
  79. newest_build_number_with_match = matches[0][0].number
  80. oldest_build_number_with_match = matches[-1][0].number
  81. search_scope = (
  82. f"builds #{oldest_build_number_with_match} to #{newest_build_number_with_match} of pipeline {pipeline }"
  83. if pipeline != ANY_PIPELINE_VALUE
  84. else "all pipelines"
  85. )
  86. print(
  87. f"Found {len(matches)} matches in {search_scope}. Search took {duration_in_sec}s.\n"
  88. "More matches exist in earlier builds."
  89. if more_results_exist
  90. else ""
  91. )
  92. def _like_pattern_to_regex(like_pattern: str) -> str:
  93. like_pattern = like_pattern.strip("%")
  94. like_pattern = re.escape(like_pattern)
  95. return like_pattern.replace("%", ".*")