argus-alm 0.14.2__py3-none-any.whl → 0.15.2__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.
- argus/_version.py +21 -0
- argus/backend/.gitkeep +0 -0
- argus/backend/__init__.py +0 -0
- argus/backend/cli.py +57 -0
- argus/backend/controller/__init__.py +0 -0
- argus/backend/controller/admin.py +20 -0
- argus/backend/controller/admin_api.py +355 -0
- argus/backend/controller/api.py +589 -0
- argus/backend/controller/auth.py +67 -0
- argus/backend/controller/client_api.py +109 -0
- argus/backend/controller/main.py +316 -0
- argus/backend/controller/notification_api.py +72 -0
- argus/backend/controller/notifications.py +13 -0
- argus/backend/controller/planner_api.py +194 -0
- argus/backend/controller/team.py +129 -0
- argus/backend/controller/team_ui.py +19 -0
- argus/backend/controller/testrun_api.py +513 -0
- argus/backend/controller/view_api.py +188 -0
- argus/backend/controller/views_widgets/__init__.py +0 -0
- argus/backend/controller/views_widgets/graphed_stats.py +54 -0
- argus/backend/controller/views_widgets/graphs.py +68 -0
- argus/backend/controller/views_widgets/highlights.py +135 -0
- argus/backend/controller/views_widgets/nemesis_stats.py +26 -0
- argus/backend/controller/views_widgets/summary.py +43 -0
- argus/backend/db.py +98 -0
- argus/backend/error_handlers.py +41 -0
- argus/backend/events/event_processors.py +34 -0
- argus/backend/models/__init__.py +0 -0
- argus/backend/models/argus_ai.py +24 -0
- argus/backend/models/github_issue.py +60 -0
- argus/backend/models/plan.py +24 -0
- argus/backend/models/result.py +187 -0
- argus/backend/models/runtime_store.py +58 -0
- argus/backend/models/view_widgets.py +25 -0
- argus/backend/models/web.py +403 -0
- argus/backend/plugins/__init__.py +0 -0
- argus/backend/plugins/core.py +248 -0
- argus/backend/plugins/driver_matrix_tests/controller.py +66 -0
- argus/backend/plugins/driver_matrix_tests/model.py +429 -0
- argus/backend/plugins/driver_matrix_tests/plugin.py +21 -0
- argus/backend/plugins/driver_matrix_tests/raw_types.py +62 -0
- argus/backend/plugins/driver_matrix_tests/service.py +61 -0
- argus/backend/plugins/driver_matrix_tests/udt.py +42 -0
- argus/backend/plugins/generic/model.py +86 -0
- argus/backend/plugins/generic/plugin.py +15 -0
- argus/backend/plugins/generic/types.py +14 -0
- argus/backend/plugins/loader.py +39 -0
- argus/backend/plugins/sct/controller.py +224 -0
- argus/backend/plugins/sct/plugin.py +37 -0
- argus/backend/plugins/sct/resource_setup.py +177 -0
- argus/backend/plugins/sct/service.py +682 -0
- argus/backend/plugins/sct/testrun.py +288 -0
- argus/backend/plugins/sct/udt.py +100 -0
- argus/backend/plugins/sirenada/model.py +118 -0
- argus/backend/plugins/sirenada/plugin.py +16 -0
- argus/backend/service/admin.py +26 -0
- argus/backend/service/argus_service.py +696 -0
- argus/backend/service/build_system_monitor.py +185 -0
- argus/backend/service/client_service.py +127 -0
- argus/backend/service/event_service.py +18 -0
- argus/backend/service/github_service.py +233 -0
- argus/backend/service/jenkins_service.py +269 -0
- argus/backend/service/notification_manager.py +159 -0
- argus/backend/service/planner_service.py +608 -0
- argus/backend/service/release_manager.py +229 -0
- argus/backend/service/results_service.py +690 -0
- argus/backend/service/stats.py +610 -0
- argus/backend/service/team_manager_service.py +82 -0
- argus/backend/service/test_lookup.py +172 -0
- argus/backend/service/testrun.py +489 -0
- argus/backend/service/user.py +308 -0
- argus/backend/service/views.py +219 -0
- argus/backend/service/views_widgets/__init__.py +0 -0
- argus/backend/service/views_widgets/graphed_stats.py +180 -0
- argus/backend/service/views_widgets/highlights.py +374 -0
- argus/backend/service/views_widgets/nemesis_stats.py +34 -0
- argus/backend/template_filters.py +27 -0
- argus/backend/tests/__init__.py +0 -0
- argus/backend/tests/client_service/__init__.py +0 -0
- argus/backend/tests/client_service/test_submit_results.py +79 -0
- argus/backend/tests/conftest.py +180 -0
- argus/backend/tests/results_service/__init__.py +0 -0
- argus/backend/tests/results_service/test_best_results.py +178 -0
- argus/backend/tests/results_service/test_cell.py +65 -0
- argus/backend/tests/results_service/test_chartjs_additional_functions.py +259 -0
- argus/backend/tests/results_service/test_create_chartjs.py +220 -0
- argus/backend/tests/results_service/test_result_metadata.py +100 -0
- argus/backend/tests/results_service/test_results_service.py +203 -0
- argus/backend/tests/results_service/test_validation_rules.py +213 -0
- argus/backend/tests/view_widgets/__init__.py +0 -0
- argus/backend/tests/view_widgets/test_highlights_api.py +532 -0
- argus/backend/util/common.py +65 -0
- argus/backend/util/config.py +38 -0
- argus/backend/util/encoders.py +56 -0
- argus/backend/util/logsetup.py +80 -0
- argus/backend/util/module_loaders.py +30 -0
- argus/backend/util/send_email.py +91 -0
- argus/client/base.py +1 -3
- argus/client/driver_matrix_tests/cli.py +17 -8
- argus/client/generic/cli.py +4 -2
- argus/client/generic/client.py +1 -0
- argus/client/generic_result.py +48 -9
- argus/client/sct/client.py +1 -3
- argus/client/sirenada/client.py +4 -1
- argus/client/tests/__init__.py +0 -0
- argus/client/tests/conftest.py +19 -0
- argus/client/tests/test_package.py +45 -0
- argus/client/tests/test_results.py +224 -0
- argus/common/sct_types.py +3 -0
- argus/common/sirenada_types.py +1 -1
- {argus_alm-0.14.2.dist-info → argus_alm-0.15.2.dist-info}/METADATA +43 -19
- argus_alm-0.15.2.dist-info/RECORD +122 -0
- {argus_alm-0.14.2.dist-info → argus_alm-0.15.2.dist-info}/WHEEL +2 -1
- argus_alm-0.15.2.dist-info/entry_points.txt +3 -0
- argus_alm-0.15.2.dist-info/top_level.txt +1 -0
- argus_alm-0.14.2.dist-info/RECORD +0 -20
- argus_alm-0.14.2.dist-info/entry_points.txt +0 -4
- {argus_alm-0.14.2.dist-info → argus_alm-0.15.2.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()
|