argus-alm 0.14.2__py3-none-any.whl → 0.15.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. argus/_version.py +21 -0
  2. argus/backend/.gitkeep +0 -0
  3. argus/backend/__init__.py +0 -0
  4. argus/backend/cli.py +57 -0
  5. argus/backend/controller/__init__.py +0 -0
  6. argus/backend/controller/admin.py +20 -0
  7. argus/backend/controller/admin_api.py +355 -0
  8. argus/backend/controller/api.py +589 -0
  9. argus/backend/controller/auth.py +67 -0
  10. argus/backend/controller/client_api.py +109 -0
  11. argus/backend/controller/main.py +316 -0
  12. argus/backend/controller/notification_api.py +72 -0
  13. argus/backend/controller/notifications.py +13 -0
  14. argus/backend/controller/planner_api.py +194 -0
  15. argus/backend/controller/team.py +129 -0
  16. argus/backend/controller/team_ui.py +19 -0
  17. argus/backend/controller/testrun_api.py +513 -0
  18. argus/backend/controller/view_api.py +188 -0
  19. argus/backend/controller/views_widgets/__init__.py +0 -0
  20. argus/backend/controller/views_widgets/graphed_stats.py +54 -0
  21. argus/backend/controller/views_widgets/graphs.py +68 -0
  22. argus/backend/controller/views_widgets/highlights.py +135 -0
  23. argus/backend/controller/views_widgets/nemesis_stats.py +26 -0
  24. argus/backend/controller/views_widgets/summary.py +43 -0
  25. argus/backend/db.py +98 -0
  26. argus/backend/error_handlers.py +41 -0
  27. argus/backend/events/event_processors.py +34 -0
  28. argus/backend/models/__init__.py +0 -0
  29. argus/backend/models/argus_ai.py +24 -0
  30. argus/backend/models/github_issue.py +60 -0
  31. argus/backend/models/plan.py +24 -0
  32. argus/backend/models/result.py +187 -0
  33. argus/backend/models/runtime_store.py +58 -0
  34. argus/backend/models/view_widgets.py +25 -0
  35. argus/backend/models/web.py +403 -0
  36. argus/backend/plugins/__init__.py +0 -0
  37. argus/backend/plugins/core.py +248 -0
  38. argus/backend/plugins/driver_matrix_tests/controller.py +66 -0
  39. argus/backend/plugins/driver_matrix_tests/model.py +429 -0
  40. argus/backend/plugins/driver_matrix_tests/plugin.py +21 -0
  41. argus/backend/plugins/driver_matrix_tests/raw_types.py +62 -0
  42. argus/backend/plugins/driver_matrix_tests/service.py +61 -0
  43. argus/backend/plugins/driver_matrix_tests/udt.py +42 -0
  44. argus/backend/plugins/generic/model.py +86 -0
  45. argus/backend/plugins/generic/plugin.py +15 -0
  46. argus/backend/plugins/generic/types.py +14 -0
  47. argus/backend/plugins/loader.py +39 -0
  48. argus/backend/plugins/sct/controller.py +224 -0
  49. argus/backend/plugins/sct/plugin.py +37 -0
  50. argus/backend/plugins/sct/resource_setup.py +177 -0
  51. argus/backend/plugins/sct/service.py +682 -0
  52. argus/backend/plugins/sct/testrun.py +288 -0
  53. argus/backend/plugins/sct/udt.py +100 -0
  54. argus/backend/plugins/sirenada/model.py +118 -0
  55. argus/backend/plugins/sirenada/plugin.py +16 -0
  56. argus/backend/service/admin.py +26 -0
  57. argus/backend/service/argus_service.py +696 -0
  58. argus/backend/service/build_system_monitor.py +185 -0
  59. argus/backend/service/client_service.py +127 -0
  60. argus/backend/service/event_service.py +18 -0
  61. argus/backend/service/github_service.py +233 -0
  62. argus/backend/service/jenkins_service.py +269 -0
  63. argus/backend/service/notification_manager.py +159 -0
  64. argus/backend/service/planner_service.py +608 -0
  65. argus/backend/service/release_manager.py +229 -0
  66. argus/backend/service/results_service.py +690 -0
  67. argus/backend/service/stats.py +610 -0
  68. argus/backend/service/team_manager_service.py +82 -0
  69. argus/backend/service/test_lookup.py +172 -0
  70. argus/backend/service/testrun.py +489 -0
  71. argus/backend/service/user.py +308 -0
  72. argus/backend/service/views.py +219 -0
  73. argus/backend/service/views_widgets/__init__.py +0 -0
  74. argus/backend/service/views_widgets/graphed_stats.py +180 -0
  75. argus/backend/service/views_widgets/highlights.py +374 -0
  76. argus/backend/service/views_widgets/nemesis_stats.py +34 -0
  77. argus/backend/template_filters.py +27 -0
  78. argus/backend/tests/__init__.py +0 -0
  79. argus/backend/tests/client_service/__init__.py +0 -0
  80. argus/backend/tests/client_service/test_submit_results.py +79 -0
  81. argus/backend/tests/conftest.py +180 -0
  82. argus/backend/tests/results_service/__init__.py +0 -0
  83. argus/backend/tests/results_service/test_best_results.py +178 -0
  84. argus/backend/tests/results_service/test_cell.py +65 -0
  85. argus/backend/tests/results_service/test_chartjs_additional_functions.py +259 -0
  86. argus/backend/tests/results_service/test_create_chartjs.py +220 -0
  87. argus/backend/tests/results_service/test_result_metadata.py +100 -0
  88. argus/backend/tests/results_service/test_results_service.py +203 -0
  89. argus/backend/tests/results_service/test_validation_rules.py +213 -0
  90. argus/backend/tests/view_widgets/__init__.py +0 -0
  91. argus/backend/tests/view_widgets/test_highlights_api.py +532 -0
  92. argus/backend/util/common.py +65 -0
  93. argus/backend/util/config.py +38 -0
  94. argus/backend/util/encoders.py +56 -0
  95. argus/backend/util/logsetup.py +80 -0
  96. argus/backend/util/module_loaders.py +30 -0
  97. argus/backend/util/send_email.py +91 -0
  98. argus/client/base.py +1 -3
  99. argus/client/driver_matrix_tests/cli.py +17 -8
  100. argus/client/generic/cli.py +4 -2
  101. argus/client/generic/client.py +1 -0
  102. argus/client/generic_result.py +48 -9
  103. argus/client/sct/client.py +1 -3
  104. argus/client/sirenada/client.py +4 -1
  105. argus/client/tests/__init__.py +0 -0
  106. argus/client/tests/conftest.py +19 -0
  107. argus/client/tests/test_package.py +45 -0
  108. argus/client/tests/test_results.py +224 -0
  109. argus/common/sct_types.py +3 -0
  110. argus/common/sirenada_types.py +1 -1
  111. {argus_alm-0.14.2.dist-info → argus_alm-0.15.1.dist-info}/METADATA +43 -19
  112. argus_alm-0.15.1.dist-info/RECORD +122 -0
  113. {argus_alm-0.14.2.dist-info → argus_alm-0.15.1.dist-info}/WHEEL +2 -1
  114. argus_alm-0.15.1.dist-info/entry_points.txt +3 -0
  115. argus_alm-0.15.1.dist-info/top_level.txt +1 -0
  116. argus_alm-0.14.2.dist-info/RECORD +0 -20
  117. argus_alm-0.14.2.dist-info/entry_points.txt +0 -4
  118. {argus_alm-0.14.2.dist-info → argus_alm-0.15.1.dist-info/licenses}/LICENSE +0 -0
File without changes
@@ -0,0 +1,24 @@
1
+ from cassandra.cqlengine import columns
2
+ from cassandra.cqlengine.models import Model
3
+
4
+
5
+ class ErrorEventEmbeddings(Model):
6
+ __table_name__ = "error_event_embeddings"
7
+ run_id = columns.UUID(partition_key=True)
8
+ event_index = columns.Integer(primary_key=True)
9
+ start_time = columns.DateTime(static=True)
10
+ embedding = columns.List(value_type=columns.Float())
11
+ columns.BaseCollectionColumn._freeze_db_type(embedding)
12
+ similars_map = columns.Map(key_type=columns.UUID(), value_type=columns.Integer())
13
+ columns.BaseCollectionColumn._freeze_db_type(similars_map)
14
+
15
+
16
+ class CriticalEventEmbeddings(Model):
17
+ __table_name__ = "critical_event_embeddings"
18
+ run_id = columns.UUID(partition_key=True)
19
+ event_index = columns.Integer(primary_key=True)
20
+ start_time = columns.DateTime(static=True)
21
+ embedding = columns.List(value_type=columns.Float())
22
+ columns.BaseCollectionColumn._freeze_db_type(embedding)
23
+ similars_map = columns.Map(key_type=columns.UUID(), value_type=columns.Integer())
24
+ columns.BaseCollectionColumn._freeze_db_type(similars_map)
@@ -0,0 +1,60 @@
1
+ from enum import unique
2
+ from uuid import uuid4
3
+ from datetime import datetime
4
+ from cassandra.cqlengine.models import Model
5
+ from cassandra.cqlengine.usertype import UserType
6
+ from cassandra.cqlengine import columns
7
+
8
+
9
+ class IssueLabel(UserType):
10
+ id = columns.BigInt()
11
+ name = columns.Text()
12
+ color = columns.Text()
13
+ description = columns.Text()
14
+
15
+ def __hash__(self) -> int:
16
+ return hash((self.name, self.color, self.description))
17
+
18
+ def __eq__(self, other):
19
+ if isinstance(other, IssueLabel):
20
+ return self.name == other.name and self.color == other.color and self.description == other.description
21
+ return super().__eq__(other)
22
+
23
+
24
+ class IssueAssignee(UserType):
25
+ login = columns.Text()
26
+ html_url = columns.Text()
27
+
28
+
29
+ class GithubIssue(Model):
30
+ id = columns.UUID(primary_key=True, default=uuid4, partition_key=True)
31
+ user_id = columns.UUID(index=True) # Internal Argus UserId
32
+ type = columns.Text() # Can be: issues, pulls
33
+ owner = columns.Text() # Org or the user to which the repo belongs to
34
+ repo = columns.Text()
35
+ number = columns.Integer()
36
+ state = columns.Text() # Possible states: open, closed
37
+ title = columns.Text()
38
+ labels = columns.List(value_type=columns.UserDefinedType(user_type=IssueLabel))
39
+ assignees = columns.List(value_type=columns.UserDefinedType(user_type=IssueAssignee))
40
+ url = columns.Text(index=True)
41
+ added_on = columns.DateTime(default=datetime.utcnow)
42
+
43
+ def __hash__(self) -> int:
44
+ return hash((self.owner, self.repo, self.number))
45
+
46
+ def __eq__(self, other):
47
+ if isinstance(other, GithubIssue):
48
+ return self.owner == other.owner and self.repo == other.repo and self.number == other.number
49
+ return super().__eq__(other)
50
+
51
+ def __ne__(self, other):
52
+ return not self == other
53
+
54
+
55
+ class IssueLink(Model):
56
+ run_id = columns.UUID(primary_key=True, required=True, partition_key=True)
57
+ issue_id = columns.UUID(primary_key=True, required=True)
58
+ release_id = columns.UUID(index=True)
59
+ group_id = columns.UUID(index=True)
60
+ test_id = columns.UUID(index=True)
@@ -0,0 +1,24 @@
1
+ import datetime
2
+ from cassandra.cqlengine import columns
3
+ from cassandra.cqlengine.models import Model
4
+ from cassandra.cqlengine.usertype import UserType
5
+ from cassandra.util import uuid_from_time
6
+
7
+
8
+ class ArgusReleasePlan(Model):
9
+ id = columns.TimeUUID(partition_key=True, default=lambda: uuid_from_time(datetime.datetime.now(tz=datetime.UTC)))
10
+ name = columns.Text(required=True)
11
+ completed = columns.Boolean(default=lambda: False)
12
+ description = columns.Text()
13
+ owner = columns.UUID(required=True)
14
+ participants = columns.List(value_type=columns.UUID)
15
+ target_version = columns.Ascii(index=True)
16
+ assignee_mapping = columns.Map(key_type=columns.UUID, value_type=columns.UUID)
17
+ release_id = columns.UUID(index=True)
18
+ tests = columns.List(value_type=columns.UUID)
19
+ groups = columns.List(value_type=columns.UUID)
20
+ view_id = columns.UUID(index=True)
21
+ created_from = columns.UUID(index=True)
22
+ creation_time = columns.DateTime(default=lambda: datetime.datetime.now(tz=datetime.UTC))
23
+ last_updated = columns.DateTime(default=lambda: datetime.datetime.now(tz=datetime.UTC))
24
+ ends_at = columns.DateTime()
@@ -0,0 +1,187 @@
1
+ from datetime import datetime, timezone
2
+
3
+ from cassandra.cqlengine import columns
4
+ from cassandra.cqlengine.models import Model
5
+ from cassandra.cqlengine.usertype import UserType
6
+
7
+
8
+ class ValidationRules(UserType):
9
+ valid_from = columns.DateTime()
10
+ best_pct = columns.Double() # max value limit relative to best result in percent unit
11
+ best_abs = columns.Double() # max value limit relative to best result in absolute unit
12
+ fixed_limit = columns.Double() # fixed limit
13
+
14
+
15
+ class ColumnMetadata(UserType):
16
+ name = columns.Ascii()
17
+ unit = columns.Text()
18
+ type = columns.Ascii()
19
+ higher_is_better = columns.Boolean() # used for tracking best results, if None - no tracking
20
+ visible = columns.Boolean(default=True) # controls visibility in UI, True by default
21
+
22
+
23
+ class ArgusGenericResultMetadata(Model):
24
+ __table_name__ = "generic_result_metadata_v1"
25
+ test_id = columns.UUID(partition_key=True)
26
+ name = columns.Text(required=True, primary_key=True)
27
+ description = columns.Text()
28
+ columns_meta = columns.List(value_type=columns.UserDefinedType(ColumnMetadata))
29
+ validation_rules = columns.Map(key_type=columns.Ascii(
30
+ ), value_type=columns.List(columns.UserDefinedType(ValidationRules)))
31
+ rows_meta = columns.List(value_type=columns.Ascii())
32
+ sut_package_name = columns.Ascii()
33
+
34
+ def __init__(self, **kwargs):
35
+ kwargs["columns_meta"] = [ColumnMetadata(**col) for col in kwargs.pop('columns_meta', [])]
36
+ validation_rules = kwargs.pop('validation_rules', {})
37
+
38
+ if validation_rules:
39
+ for column, rule in validation_rules.items():
40
+ if not isinstance(rule, list):
41
+ rule['valid_from'] = datetime.now(timezone.utc)
42
+ validation_rules[column] = [rule]
43
+ kwargs["validation_rules"] = {k: [ValidationRules(**rules)
44
+ for rules in v] for k, v in validation_rules.items()}
45
+ super().__init__(**kwargs)
46
+
47
+ def update_validation_rules(self, new_rules: dict) -> "ArgusGenericResultMetadata":
48
+ """
49
+ Updates the validation rules based on the new input data.
50
+
51
+ For each key in new_rules:
52
+ - If the key exists in self.validation_rules, compare the new rule with the most recent one.
53
+ - If they differ, append the new rule.
54
+ - If the key does not exist in self.validation_rules, add the key with the new rule.
55
+
56
+ For keys in self.validation_rules but not in new_rules:
57
+ - If the most recent rule does not have all fields set to None, append a new rule with fields set to None.
58
+
59
+ :param new_rules: A dictionary where each key maps to a new rule dict.
60
+ :return: True if any rules were updated, False otherwise.
61
+ """
62
+ updated = False
63
+ input_data_keys = set(new_rules.keys())
64
+ existing_keys = set(self.validation_rules.keys())
65
+
66
+ # Handle existing keys in new input data
67
+ for key, new_rule_dict in new_rules.items():
68
+ rules_list = self.validation_rules.get(key, [])
69
+ most_recent_rule = rules_list[-1] if rules_list else None
70
+
71
+ fields_to_compare = [field for field in ValidationRules._fields if field != 'valid_from']
72
+ rules_match = True
73
+
74
+ if most_recent_rule:
75
+ for field in fields_to_compare:
76
+ db_value = getattr(most_recent_rule, field)
77
+ new_value = new_rule_dict.get(field)
78
+ if db_value != new_value:
79
+ rules_match = False
80
+ break
81
+ else:
82
+ rules_match = False # No existing rule, need to add one
83
+
84
+ if not rules_match:
85
+ new_rule = ValidationRules(
86
+ valid_from=datetime.now(timezone.utc),
87
+ best_pct=new_rule_dict.get('best_pct'),
88
+ best_abs=new_rule_dict.get('best_abs'),
89
+ fixed_limit=new_rule_dict.get('fixed_limit')
90
+ )
91
+ rules_list.append(new_rule)
92
+ self.validation_rules[key] = rules_list
93
+ updated = True
94
+
95
+ # Handle keys missing in new input data
96
+ missing_keys = existing_keys - input_data_keys
97
+ for key in missing_keys:
98
+ rules_list = self.validation_rules.get(key, [])
99
+ most_recent_rule = rules_list[-1] if rules_list else None
100
+
101
+ fields_to_compare = [field for field in ValidationRules._fields if field != 'valid_from']
102
+ all_fields_none = True
103
+
104
+ if most_recent_rule:
105
+ for field in fields_to_compare:
106
+ if getattr(most_recent_rule, field) is not None:
107
+ all_fields_none = False
108
+ break
109
+ else:
110
+ all_fields_none = False
111
+
112
+ if not all_fields_none:
113
+ new_rule = ValidationRules(
114
+ valid_from=datetime.now(timezone.utc),
115
+ best_pct=None,
116
+ best_abs=None,
117
+ fixed_limit=None
118
+ )
119
+ rules_list.append(new_rule)
120
+ self.validation_rules[key] = rules_list
121
+ updated = True
122
+
123
+ return updated
124
+
125
+ def update_if_changed(self, new_data: dict) -> "ArgusGenericResultMetadata":
126
+ """
127
+ Updates table metadata if changed column/description or new rows were added.
128
+ See that rows can only be added, not removed once was sent.
129
+ Columns may be removed, but data in results table persists.
130
+ """
131
+ updated = False
132
+ for field, value in new_data.items():
133
+ if field == "columns_meta":
134
+ value = [ColumnMetadata(**col) for col in value]
135
+ if self.columns_meta != value:
136
+ self.columns_meta = value
137
+ updated = True
138
+ elif field == "rows_meta":
139
+ added_rows = []
140
+ for row in value:
141
+ if row not in self.rows_meta:
142
+ added_rows.append(row)
143
+ updated = True
144
+ self.rows_meta += added_rows
145
+ elif field == "validation_rules":
146
+ if self.update_validation_rules(value):
147
+ updated = True
148
+ elif getattr(self, field) != value:
149
+ setattr(self, field, value)
150
+ updated = True
151
+
152
+ if updated:
153
+ self.save()
154
+ return self
155
+
156
+
157
+ class ArgusGenericResultData(Model):
158
+ __table_name__ = "generic_result_data_v1"
159
+ test_id = columns.UUID(partition_key=True)
160
+ name = columns.Text(partition_key=True)
161
+ run_id = columns.UUID(primary_key=True)
162
+ column = columns.Ascii(primary_key=True, index=True)
163
+ row = columns.Ascii(primary_key=True, index=True)
164
+ sut_timestamp = columns.DateTime() # for sorting
165
+ value = columns.Double()
166
+ value_text = columns.Text()
167
+ status = columns.Ascii()
168
+
169
+
170
+ class ArgusBestResultData(Model):
171
+ __table_name__ = "generic_result_best_v2"
172
+ test_id = columns.UUID(partition_key=True)
173
+ name = columns.Text(partition_key=True)
174
+ result_date = columns.DateTime(primary_key=True, clustering_order="DESC")
175
+ key = columns.Ascii(primary_key=True) # represents pair column:row
176
+ value = columns.Double()
177
+ run_id = columns.UUID()
178
+
179
+
180
+ class ArgusGraphView(Model):
181
+ __table_name__ = "graph_view_v1"
182
+ test_id = columns.UUID(partition_key=True)
183
+ id = columns.UUID(primary_key=True)
184
+ name = columns.Text()
185
+ description = columns.Text()
186
+ # key: graph name, value: graph properties (e.g. size)
187
+ graphs = columns.Map(key_type=columns.Text(), value_type=columns.Ascii())
@@ -0,0 +1,58 @@
1
+ from datetime import datetime, time
2
+ from cassandra.cqlengine.models import Model
3
+ from cassandra.cqlengine import columns
4
+
5
+
6
+ class RuntimeStore(Model):
7
+ """
8
+ This model provides a way for the application to store configuration
9
+ data inside its database. Supports time, datetime, int, float, str, boolean values
10
+ and will automatically manage which type is being used.
11
+
12
+ Example:
13
+
14
+ prop = RuntimeStore()
15
+ prop.key = "my_property_value"
16
+ prop.value = 0
17
+ prop.save()
18
+
19
+ """
20
+ _type_map = {
21
+ float: "float",
22
+ bool: "boolean",
23
+ time: "time",
24
+ int: "int",
25
+ str: "text",
26
+ datetime: "datetime",
27
+ }
28
+ key = columns.Text(primary_key=True, partition_key=True, required=True)
29
+ value_type = columns.Ascii(required=True)
30
+ value_int = columns.Integer()
31
+ value_text = columns.Text()
32
+ value_ts = columns.Time()
33
+ value_date = columns.DateTime()
34
+ value_float = columns.Double()
35
+ value_boolean = columns.Boolean()
36
+
37
+ @property
38
+ def value(self) -> int | str | float | datetime:
39
+ match self.value_type:
40
+ case "float": return self.value_float
41
+ case "time": return self.value_ts
42
+ case "int": return self.value_int
43
+ case "text": return self.value_text
44
+ case "boolean": return self.value_boolean
45
+ case "datetime": return self.value_date
46
+
47
+ @value.setter
48
+ def value(self, v):
49
+ if not (type_name := self._type_map.get(type(v))):
50
+ raise ValueError(f"Unsupported type: {type(v)}")
51
+ match type_name:
52
+ case "float": self.value_float = v
53
+ case "time": self.value_ts = v
54
+ case "int": self.value_int = v
55
+ case "text": self.value_text = v
56
+ case "boolean": self.value_boolean = v
57
+ case "datetime": self.value_date = v
58
+ self.value_type = type_name
@@ -0,0 +1,25 @@
1
+ from datetime import datetime, UTC
2
+
3
+ from cassandra.cqlengine import columns
4
+ from cassandra.cqlengine.models import Model
5
+
6
+
7
+ class WidgetHighlights(Model):
8
+ view_id = columns.UUID(partition_key=True, required=True)
9
+ index = columns.Integer(partition_key=True, required=True)
10
+ created_at = columns.DateTime(primary_key=True, clustering_order="DESC")
11
+ archived_at = columns.DateTime(default=datetime.fromtimestamp(0, tz=UTC))
12
+ creator_id = columns.UUID()
13
+ assignee_id = columns.UUID()
14
+ content = columns.Text()
15
+ completed = columns.Boolean(default=lambda: None) # None means it's highlight, not an action item
16
+ comments_count = columns.TinyInt()
17
+
18
+
19
+ class WidgetComment(Model):
20
+ view_id = columns.UUID(partition_key=True, required=True)
21
+ index = columns.Integer(partition_key=True, required=True)
22
+ highlight_at = columns.DateTime(partition_key=True, required=True) # reference to WidgetHighlights.created_at
23
+ created_at = columns.DateTime(primary_key=True)
24
+ creator_id = columns.UUID()
25
+ content = columns.Text()