ara-cli 0.1.9.50__py3-none-any.whl → 0.1.9.52__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.
- ara_cli/__main__.py +3 -1
- ara_cli/analyse_artefacts.py +133 -0
- ara_cli/ara_command_action.py +91 -76
- ara_cli/ara_command_parser.py +5 -0
- ara_cli/ara_config.py +65 -38
- ara_cli/artefact_creator.py +3 -4
- ara_cli/artefact_lister.py +57 -48
- ara_cli/artefact_models/artefact_model.py +28 -14
- ara_cli/artefact_models/feature_artefact_model.py +4 -1
- ara_cli/artefact_reader.py +104 -63
- ara_cli/artefact_renamer.py +8 -8
- ara_cli/artefact_scan.py +46 -0
- ara_cli/file_classifier.py +10 -32
- ara_cli/prompt_extractor.py +10 -2
- ara_cli/tag_extractor.py +6 -16
- ara_cli/templates/specification_breakdown_files/template.concept.md +12 -14
- ara_cli/tests/test_ara_command_action.py +242 -108
- ara_cli/tests/test_artefact_lister.py +598 -171
- ara_cli/tests/test_artefact_reader.py +7 -76
- ara_cli/tests/test_artefact_scan.py +126 -0
- ara_cli/tests/test_file_classifier.py +69 -30
- ara_cli/tests/test_tag_extractor.py +42 -61
- ara_cli/version.py +1 -1
- {ara_cli-0.1.9.50.dist-info → ara_cli-0.1.9.52.dist-info}/METADATA +1 -1
- {ara_cli-0.1.9.50.dist-info → ara_cli-0.1.9.52.dist-info}/RECORD +28 -27
- ara_cli/artefact.py +0 -172
- ara_cli/tests/test_artefact.py +0 -304
- {ara_cli-0.1.9.50.dist-info → ara_cli-0.1.9.52.dist-info}/WHEEL +0 -0
- {ara_cli-0.1.9.50.dist-info → ara_cli-0.1.9.52.dist-info}/entry_points.txt +0 -0
- {ara_cli-0.1.9.50.dist-info → ara_cli-0.1.9.52.dist-info}/top_level.txt +0 -0
|
@@ -1,45 +1,46 @@
|
|
|
1
1
|
ara_cli/__init__.py,sha256=0zl7IegxTid26EBGLav_fXZ4CCIV3H5TfAoFQiOHjvg,148
|
|
2
|
-
ara_cli/__main__.py,sha256=
|
|
3
|
-
ara_cli/
|
|
4
|
-
ara_cli/
|
|
5
|
-
ara_cli/
|
|
6
|
-
ara_cli/
|
|
7
|
-
ara_cli/artefact_creator.py,sha256=
|
|
2
|
+
ara_cli/__main__.py,sha256=3fYaQUcudgjqtJa2gj2LoYnhy-Wy1OfZIfXEYQf4m0U,1806
|
|
3
|
+
ara_cli/analyse_artefacts.py,sha256=JwA2zxkCy8vNOHoU9f3TICJesXRRXndHi2hT5m_uQ8Q,4965
|
|
4
|
+
ara_cli/ara_command_action.py,sha256=5V5QnOYoLgvayym6CW2e0jJFc2HhKTZihHiysw9ZM48,18459
|
|
5
|
+
ara_cli/ara_command_parser.py,sha256=wbuPElD9GSDpaxHRH3yoyKaLAYsk6n9lE78jERD2zXg,16770
|
|
6
|
+
ara_cli/ara_config.py,sha256=3sbTY-NzLAL6augf06wLTh_5-exwM0dHVxqS_7dxWcg,4571
|
|
7
|
+
ara_cli/artefact_creator.py,sha256=s0hY8-X0EAwEfZvIRK1F67bW55tB-EvgnvH2gYNTGpY,6022
|
|
8
8
|
ara_cli/artefact_deleter.py,sha256=Co4wwCH3yW8H9NrOq7_2p5571EeHr0TsfE-H8KqoOfY,1900
|
|
9
9
|
ara_cli/artefact_fuzzy_search.py,sha256=sDxHKfg6P5TIiwznFgrMuMMhDp6EKElCbZsfflmkCww,1339
|
|
10
10
|
ara_cli/artefact_link_updater.py,sha256=itMS_Z64jE8bBly9WA01z8PqkBeNW6ntTO7ryMeCTRg,3703
|
|
11
|
-
ara_cli/artefact_lister.py,sha256=
|
|
12
|
-
ara_cli/artefact_reader.py,sha256=
|
|
13
|
-
ara_cli/artefact_renamer.py,sha256=
|
|
11
|
+
ara_cli/artefact_lister.py,sha256=jhk4n4eqp7hDIq07q43QzS7-36BM3OfZ4EABxCeOGcw,4764
|
|
12
|
+
ara_cli/artefact_reader.py,sha256=qNaMPWShmWtDU5LLdh9efFB27djI4NAoq6zEFwdTd38,6983
|
|
13
|
+
ara_cli/artefact_renamer.py,sha256=U0cwkR6j1KVjz1LRqiHa11sqFCSyi_GaG5AWJHoLOx0,4009
|
|
14
|
+
ara_cli/artefact_scan.py,sha256=DgFGv4hnbCjYdIgPA2PbAuGDWg1q2fCjtIqxGs57b9w,1762
|
|
14
15
|
ara_cli/chat.py,sha256=MOJHRszQTUtBmGkOC96cIiXfkyGvrDkEirRagJEvm8U,28522
|
|
15
16
|
ara_cli/classifier.py,sha256=zWskj7rBYdqYBGjksBm46iTgVU5IIf2PZsJr4qeiwVU,1878
|
|
16
17
|
ara_cli/codefusionretriever.py,sha256=fCHgXdIBRzkVAnapX-KI2NQ44XbrrF4tEQmn5J6clUI,1980
|
|
17
18
|
ara_cli/codehierachieretriever.py,sha256=Xd3EgEWWhkSf1TmTWtf8X5_YvyE_4B66nRrqarwSiTU,1182
|
|
18
19
|
ara_cli/commandline_completer.py,sha256=b00Dqb5n7SecpxYIDLxAfYhp8X6e3c8a5qYz6ko0i3E,1192
|
|
19
20
|
ara_cli/directory_navigator.py,sha256=6QbSAjJrJ5a6Lutol9J4HFgVDMiAQ672ny9TATrh04U,3318
|
|
20
|
-
ara_cli/file_classifier.py,sha256=
|
|
21
|
+
ara_cli/file_classifier.py,sha256=rKgF2_tyxHUlpr_vclaZN1CKzidErRXzOng3SbSYR6Q,3903
|
|
21
22
|
ara_cli/file_lister.py,sha256=VFpUmHU1d6sQvJWSeuFqkZZ0Ci3ZYCUtAUfvgWypaYU,2314
|
|
22
23
|
ara_cli/filename_validator.py,sha256=Aw9PL8d5-Ymhp3EY6lDrUBk3cudaNqo1Uw5RzPpI1jA,118
|
|
23
24
|
ara_cli/list_filter.py,sha256=Not17hIngI37gZsLtIKxopB-BmyWoOGlBzSqBwh-Zpc,5273
|
|
24
25
|
ara_cli/output_suppressor.py,sha256=ZByUwLH2DxOb-eJ31KQbtIziBKdykoyxvwxZ0tSammA,371
|
|
25
26
|
ara_cli/prompt_chat.py,sha256=kd_OINDQFit6jN04bb7mzgY259JBbRaTaNp9F-webkc,1346
|
|
26
|
-
ara_cli/prompt_extractor.py,sha256=
|
|
27
|
+
ara_cli/prompt_extractor.py,sha256=gidrCI8wTLfPL0ktqiXyPeGdQEB0S0sZegSOiF1Nn0Y,7358
|
|
27
28
|
ara_cli/prompt_handler.py,sha256=PzHoIPTAWtRleOMtprhyYlFfo59S5T_kzHHkrwL-cNU,17155
|
|
28
29
|
ara_cli/prompt_rag.py,sha256=vmlt4-rSboWibwgO_KUF79TK99YXT5KXjmbD9FeWdZY,7449
|
|
29
30
|
ara_cli/run_file_lister.py,sha256=XbrrDTJXp1LFGx9Lv91SNsEHZPP-PyEMBF_P4btjbDA,2360
|
|
30
|
-
ara_cli/tag_extractor.py,sha256=
|
|
31
|
+
ara_cli/tag_extractor.py,sha256=IlFOeWRhuojXc4qVgBlfU0SXqfL_UW_q_iBrC8pdL1U,948
|
|
31
32
|
ara_cli/template_manager.py,sha256=YXPj2jGNDb-diIHFEK_vGJ-ZucodnXSGAPofKTnOofI,6633
|
|
32
33
|
ara_cli/update_config_prompt.py,sha256=PZgNIN3dTw6p80GyX8Sp5apkAhSoykwnkEbHo3IOkUo,4571
|
|
33
|
-
ara_cli/version.py,sha256=
|
|
34
|
+
ara_cli/version.py,sha256=pkOflZPbtrSLZdm4j5Ws2p77G9Jo5_lAqkuvQ6g5RGs,146
|
|
34
35
|
ara_cli/artefact_models/artefact_load.py,sha256=dNcwZDW2Dk0bts9YnPZ0ESmWD2NbsLIvl4Z-qQeGmTQ,401
|
|
35
36
|
ara_cli/artefact_models/artefact_mapping.py,sha256=8aD0spBjkJ8toMAmFawc6UTUxB6-tEEViZXv2I-r88Q,1874
|
|
36
|
-
ara_cli/artefact_models/artefact_model.py,sha256=
|
|
37
|
+
ara_cli/artefact_models/artefact_model.py,sha256=ORnG8xKtK_hXuhfUXpiRBUV0QUUZNfR3wFSQU7oJiDo,14454
|
|
37
38
|
ara_cli/artefact_models/artefact_templates.py,sha256=Vd7SwoRVKNGKZmxBKS6f9FE1ThUOCqZLScu0ClPfIu8,8321
|
|
38
39
|
ara_cli/artefact_models/businessgoal_artefact_model.py,sha256=u9-Rr7VDYoNP6Vy2iKE64yAxDPOnrJJFozO6Ji9s8pI,4570
|
|
39
40
|
ara_cli/artefact_models/capability_artefact_model.py,sha256=SZqHx4O2mj4urn77Stnj4_Jxtlq3-LgBBU9SMkByppI,3079
|
|
40
41
|
ara_cli/artefact_models/epic_artefact_model.py,sha256=MuK6n0Tl0TM3364pH_j5R8XvwJZFyNwrZGCYaaqzxvs,5490
|
|
41
42
|
ara_cli/artefact_models/example_artefact_model.py,sha256=UXrKbaPotg1jwcrVSdCeo-XH4tTD_-U1e3giaBn5_xg,1384
|
|
42
|
-
ara_cli/artefact_models/feature_artefact_model.py,sha256=
|
|
43
|
+
ara_cli/artefact_models/feature_artefact_model.py,sha256=HuHLPZW4HptHhKaDHekobB29ZDWXqU4LXMHUP7gMIS8,12979
|
|
43
44
|
ara_cli/artefact_models/issue_artefact_model.py,sha256=v6CpKnkqiUh6Wch2kkEmyyW49c8ysdy1qz8l1Ft9uJA,2552
|
|
44
45
|
ara_cli/artefact_models/keyfeature_artefact_model.py,sha256=nkRNQyoJrDqmRxmqJZBQId6fNv6gI3EoKtXsI7BPqdY,3968
|
|
45
46
|
ara_cli/artefact_models/task_artefact_model.py,sha256=kHMw_Tr-Ud3EeHWpRWy4jI0xFnPzGZ-FT52c5rSrT1k,3558
|
|
@@ -119,7 +120,7 @@ ara_cli/templates/prompt-modules/rules/product_owner.rules.md,sha256=NQYZ0JHOlss
|
|
|
119
120
|
ara_cli/templates/prompt-modules/rules/python_behave.rules.md,sha256=0CG8S6W-lkLEu0zz2dFwxWCIgZM8QERlk4PCWybTMwA,1257
|
|
120
121
|
ara_cli/templates/prompt-modules/rules/python_developer.rules.md,sha256=XkteyHS8wNdg4Kxtu45ca8q0kXUSBVEpDzqwNdis3fk,600
|
|
121
122
|
ara_cli/templates/specification_breakdown_files/template.concept.exploration.md,sha256=Snti9j5CXMQb0799CQe-4MlLWTcathUVQQJWlvwPXBA,2117
|
|
122
|
-
ara_cli/templates/specification_breakdown_files/template.concept.md,sha256=
|
|
123
|
+
ara_cli/templates/specification_breakdown_files/template.concept.md,sha256=tyAZojoQF0Xn4BEOtEsT1PZlpPsQSUGsojnvjTqbxks,587
|
|
123
124
|
ara_cli/templates/specification_breakdown_files/template.customer.exploration.md,sha256=mMd66EPkoCzpJH24CfOD5xBrovsdDX6OA_zQi0u8P_g,2273
|
|
124
125
|
ara_cli/templates/specification_breakdown_files/template.customer.md,sha256=gaIhWIfk41e8P5Gpi1PeyIw9MzhiWB7YUgrA5vq4v-w,1460
|
|
125
126
|
ara_cli/templates/specification_breakdown_files/template.persona.exploration.md,sha256=CAJ195w07xWSkNvssgAa_c3ub2f_XCBW0bi_dTns0r8,4183
|
|
@@ -129,26 +130,26 @@ ara_cli/templates/specification_breakdown_files/template.step.md,sha256=nzDRl9Xo
|
|
|
129
130
|
ara_cli/templates/specification_breakdown_files/template.technology.exploration.md,sha256=zQyiJcmbUfXdte-5uZwZUpT6ey0zwfZ00P4VwI97jQk,2274
|
|
130
131
|
ara_cli/templates/specification_breakdown_files/template.technology.md,sha256=bySiksz-8xtq0Nnj4svqe2MgUftWrVkbK9AcrDUE3KY,952
|
|
131
132
|
ara_cli/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
132
|
-
ara_cli/tests/test_ara_command_action.py,sha256=
|
|
133
|
+
ara_cli/tests/test_ara_command_action.py,sha256=N7712HbmN86ziGBKI5GoHQPp75OSyD0kHsHRxna8d9w,25446
|
|
133
134
|
ara_cli/tests/test_ara_config.py,sha256=1LWby_iSestTIIqK-1clggL8kmbGGbtlYfsxAHaMMF8,2232
|
|
134
|
-
ara_cli/tests/test_artefact.py,sha256=xMouG-yf2PQoqt_9atdRtMS1t8QsVXj549G78ZCeFu8,12718
|
|
135
135
|
ara_cli/tests/test_artefact_fuzzy_search.py,sha256=5Sh3_l9QK8-WHn6JpGPU1b6h4QEnl2JoMq1Tdp2cj1U,1261
|
|
136
136
|
ara_cli/tests/test_artefact_link_updater.py,sha256=gN5KFF1uY7OoBh8Mr5jWpqXp02YCU5OSIpSU76Rm4Gs,2137
|
|
137
|
-
ara_cli/tests/test_artefact_lister.py,sha256=
|
|
138
|
-
ara_cli/tests/test_artefact_reader.py,sha256=
|
|
137
|
+
ara_cli/tests/test_artefact_lister.py,sha256=VCEOCgDgnAOeUUgIoGAbWgz60hf9UT-tdHg18LGfB34,22656
|
|
138
|
+
ara_cli/tests/test_artefact_reader.py,sha256=660K-d8ed-j8hulsUB_7baPD2-hhbg9TffUR5yVc4Uo,927
|
|
139
139
|
ara_cli/tests/test_artefact_renamer.py,sha256=Dls9Z65h3HiG55Irki7aPOxoIxx8mfZTOqy5k2_Jbls,6155
|
|
140
|
+
ara_cli/tests/test_artefact_scan.py,sha256=m0V3HMLJtrOBXpJIRUI0D6MbHssgxDU_c935Wc3jatQ,4696
|
|
140
141
|
ara_cli/tests/test_chat.py,sha256=VLQYsniaQI663xy8XFeoqczrVAoiln7tUm0yDjR1oGY,45727
|
|
141
142
|
ara_cli/tests/test_classifier.py,sha256=grYGPksydNdPsaEBQxYHZTuTdcJWz7VQtikCKA6BNaQ,1920
|
|
142
143
|
ara_cli/tests/test_directory_navigator.py,sha256=7G0MVrBbtBvbrFUpL0zb_9EkEWi1dulWuHsrQxMJxDY,140
|
|
143
|
-
ara_cli/tests/test_file_classifier.py,sha256=
|
|
144
|
+
ara_cli/tests/test_file_classifier.py,sha256=6OYM-lYVYjxq4Qwl8U1btv_FYJhc5t3rKjYr2CXZ4uI,10069
|
|
144
145
|
ara_cli/tests/test_file_creator.py,sha256=ssrl7gfU13H4ogrrX1BXx4WO4bQ7OG1R2tXZC1MP8EY,2004
|
|
145
146
|
ara_cli/tests/test_file_lister.py,sha256=f6B_vIv-wAulKH2ZGgNg4SG79XqGGbfwoIvZlbEnYyM,4306
|
|
146
147
|
ara_cli/tests/test_list_filter.py,sha256=gSRKirTtFuhRS3QlFHqWl89WvCvAdVEnFsCWTYmgB2o,7928
|
|
147
|
-
ara_cli/tests/test_tag_extractor.py,sha256=
|
|
148
|
+
ara_cli/tests/test_tag_extractor.py,sha256=A4Usip4DrCS5iOGo8wYcTvIHJfN9V_yepYDjdCUyQNk,1729
|
|
148
149
|
ara_cli/tests/test_template_manager.py,sha256=bRxka6cxHsCAOvXjfG8MrVO8qSZXhxW01tnph80UtNk,3143
|
|
149
150
|
ara_cli/tests/test_update_config_prompt.py,sha256=vSsLvc18HZdVjVM93qXWVbJt752xTLL6VGjSVCrPufk,6729
|
|
150
|
-
ara_cli-0.1.9.
|
|
151
|
-
ara_cli-0.1.9.
|
|
152
|
-
ara_cli-0.1.9.
|
|
153
|
-
ara_cli-0.1.9.
|
|
154
|
-
ara_cli-0.1.9.
|
|
151
|
+
ara_cli-0.1.9.52.dist-info/METADATA,sha256=LPiTn-w5P0D8PN2cLtTCMzBNK16r6yTHyyUUCIn7W3E,367
|
|
152
|
+
ara_cli-0.1.9.52.dist-info/WHEEL,sha256=5sUXSg9e4bi7lTLOHcm6QEYwO5TIF1TNbTSVFVjcJcc,92
|
|
153
|
+
ara_cli-0.1.9.52.dist-info/entry_points.txt,sha256=v4h7MzysTgSIDYfEo3oj4Kz_8lzsRa3hq-KJHEcLVX8,45
|
|
154
|
+
ara_cli-0.1.9.52.dist-info/top_level.txt,sha256=zzee_PwFmKqfBi9XgIunP6xy2S4TIt593CLLxenNaAE,8
|
|
155
|
+
ara_cli-0.1.9.52.dist-info/RECORD,,
|
ara_cli/artefact.py
DELETED
|
@@ -1,172 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
from dataclasses import dataclass, field
|
|
3
|
-
from typing import Literal, Optional
|
|
4
|
-
from ara_cli.classifier import Classifier
|
|
5
|
-
import re
|
|
6
|
-
import os
|
|
7
|
-
|
|
8
|
-
@dataclass
|
|
9
|
-
class Artefact:
|
|
10
|
-
classifier: Literal(Classifier.ordered_classifiers())
|
|
11
|
-
name: str
|
|
12
|
-
_content: str | None = None
|
|
13
|
-
_parent: Artefact | None = field(init=False, default=None)
|
|
14
|
-
_file_path: Optional[str] = None
|
|
15
|
-
_tags: set[str] | None = None
|
|
16
|
-
|
|
17
|
-
def assemble_content(self):
|
|
18
|
-
def replace_parent(content):
|
|
19
|
-
parent = self.parent
|
|
20
|
-
if parent is None:
|
|
21
|
-
return content
|
|
22
|
-
parent_name = parent.name
|
|
23
|
-
parent_classifier = parent.classifier
|
|
24
|
-
parent_title = Classifier.get_artefact_title(parent_classifier)
|
|
25
|
-
parent_string = f"{parent_name} {parent_title}"
|
|
26
|
-
|
|
27
|
-
contribute_string = "Contributes to"
|
|
28
|
-
illustrate_string = "Illustrates"
|
|
29
|
-
|
|
30
|
-
regex_pattern = f'(?m)^(.*(?:{contribute_string}|{illustrate_string}))(.*)$'
|
|
31
|
-
|
|
32
|
-
def replacement_function(match):
|
|
33
|
-
return f"{match.group(1)} {parent_string}"
|
|
34
|
-
|
|
35
|
-
def create_contributes(content):
|
|
36
|
-
own_classifier_title = Classifier.get_artefact_title(self.classifier)
|
|
37
|
-
own_title_line = f"{own_classifier_title}: {self.name}"
|
|
38
|
-
contribution = contribute_string
|
|
39
|
-
if self.classifier == 'example':
|
|
40
|
-
contribution = illustrate_string
|
|
41
|
-
parent_line = f"{contribution} {parent_string}"
|
|
42
|
-
lines = content.splitlines()
|
|
43
|
-
for i, line in enumerate(lines):
|
|
44
|
-
if line.startswith(own_title_line):
|
|
45
|
-
lines.insert(i + 1, "")
|
|
46
|
-
lines.insert(i + 2, parent_line)
|
|
47
|
-
break
|
|
48
|
-
content = '\n'.join(lines)
|
|
49
|
-
return content
|
|
50
|
-
|
|
51
|
-
if not re.search(regex_pattern, content):
|
|
52
|
-
return create_contributes(content)
|
|
53
|
-
|
|
54
|
-
return re.sub(regex_pattern, replacement_function, content, count=1)
|
|
55
|
-
|
|
56
|
-
def replace_tags(content):
|
|
57
|
-
tag_set = self.tags
|
|
58
|
-
if tag_set is None or not tag_set:
|
|
59
|
-
return content
|
|
60
|
-
tags = ' '.join(sorted([f'@{tag}' for tag in tag_set]))
|
|
61
|
-
|
|
62
|
-
lines = content.splitlines()
|
|
63
|
-
if lines and lines[0].startswith('@'):
|
|
64
|
-
lines[0] = tags
|
|
65
|
-
return '\n'.join(lines)
|
|
66
|
-
lines.insert(0, tags)
|
|
67
|
-
return '\n'.join(lines)
|
|
68
|
-
|
|
69
|
-
content = self.content
|
|
70
|
-
content = replace_tags(content)
|
|
71
|
-
content = replace_parent(content)
|
|
72
|
-
self._content = content
|
|
73
|
-
|
|
74
|
-
@property
|
|
75
|
-
def file_name(self) -> str:
|
|
76
|
-
basename = os.path.basename(self.file_path)
|
|
77
|
-
return os.path.splitext(basename)[0]
|
|
78
|
-
|
|
79
|
-
@property
|
|
80
|
-
def content(self) -> str:
|
|
81
|
-
if self._content is not None:
|
|
82
|
-
return self._content
|
|
83
|
-
with open(self.file_path, 'r') as file:
|
|
84
|
-
self._content = file.read()
|
|
85
|
-
return self._content
|
|
86
|
-
|
|
87
|
-
@property
|
|
88
|
-
def parent(self) -> Artefact | None:
|
|
89
|
-
if self._parent is not None:
|
|
90
|
-
return self._parent
|
|
91
|
-
|
|
92
|
-
if self.content is None:
|
|
93
|
-
with open(self.file_path, 'r') as file:
|
|
94
|
-
self.content = file.read()
|
|
95
|
-
|
|
96
|
-
artefact_titles = Classifier.artefact_titles()
|
|
97
|
-
title_segment = '|'.join(artefact_titles)
|
|
98
|
-
|
|
99
|
-
regex_pattern = rf'(?:Contributes to|Illustrates)\s*:*\s*(.*)\s+({title_segment}).*'
|
|
100
|
-
regex = re.compile(regex_pattern)
|
|
101
|
-
match = re.search(regex, self.content)
|
|
102
|
-
|
|
103
|
-
if match:
|
|
104
|
-
parent_name = match.group(1).strip()
|
|
105
|
-
parent_name = parent_name.replace(' ', '_')
|
|
106
|
-
parent_title = match.group(2).strip()
|
|
107
|
-
parent_type = Classifier.get_artefact_classifier(parent_title)
|
|
108
|
-
self._parent = Artefact(classifier=parent_type, name=parent_name)
|
|
109
|
-
|
|
110
|
-
return self._parent
|
|
111
|
-
|
|
112
|
-
@property
|
|
113
|
-
def file_path(self) -> str:
|
|
114
|
-
if self._file_path is None:
|
|
115
|
-
sub_directory = Classifier.get_sub_directory(self.classifier)
|
|
116
|
-
underscore_name = self.name.replace(' ', '_')
|
|
117
|
-
self._file_path = f"{sub_directory}/{underscore_name}.{self.classifier}"
|
|
118
|
-
return self._file_path
|
|
119
|
-
|
|
120
|
-
@property
|
|
121
|
-
def tags(self) -> set[str]:
|
|
122
|
-
if self._tags is not None:
|
|
123
|
-
return self._tags
|
|
124
|
-
|
|
125
|
-
if self.content is None:
|
|
126
|
-
return set()
|
|
127
|
-
|
|
128
|
-
lines = self.content.splitlines()
|
|
129
|
-
first_line = lines[0].strip() if lines else ""
|
|
130
|
-
|
|
131
|
-
if not first_line.startswith('@'):
|
|
132
|
-
self._tags = set()
|
|
133
|
-
return self._tags
|
|
134
|
-
|
|
135
|
-
self._tags = {tag[1:] for tag in first_line.split() if tag.startswith('@')}
|
|
136
|
-
return self._tags
|
|
137
|
-
|
|
138
|
-
@classmethod
|
|
139
|
-
def from_content(cls, content: str, file_path: str | None = None) -> Artefact:
|
|
140
|
-
"""
|
|
141
|
-
Create an Artefact object from the given content.
|
|
142
|
-
"""
|
|
143
|
-
error_message = "Content does not contain valid artefact information"
|
|
144
|
-
|
|
145
|
-
if content is None:
|
|
146
|
-
raise ValueError(error_message)
|
|
147
|
-
|
|
148
|
-
artefact_titles = Classifier.artefact_titles()
|
|
149
|
-
title_segment = '|'.join(artefact_titles)
|
|
150
|
-
|
|
151
|
-
regex_pattern = rf'({title_segment})\s*:*\s*(.*)\s*'
|
|
152
|
-
regex = re.compile(regex_pattern)
|
|
153
|
-
match = re.search(regex, content)
|
|
154
|
-
|
|
155
|
-
if not match:
|
|
156
|
-
raise ValueError(error_message)
|
|
157
|
-
|
|
158
|
-
title = match.group(1).strip()
|
|
159
|
-
classifier = Classifier.get_artefact_classifier(title)
|
|
160
|
-
name = match.group(2).strip()
|
|
161
|
-
|
|
162
|
-
return cls(classifier=classifier, name=name, _content=content, _file_path=file_path)
|
|
163
|
-
|
|
164
|
-
def write_to_file(self):
|
|
165
|
-
if self.content is None:
|
|
166
|
-
raise ValueError("Artefact object does not contain content information")
|
|
167
|
-
self.assemble_content()
|
|
168
|
-
file_path = self.file_path
|
|
169
|
-
data_directory = f"{os.path.splitext(file_path)[0]}.data"
|
|
170
|
-
os.makedirs(data_directory, exist_ok=True)
|
|
171
|
-
with open(self.file_path, 'w') as file:
|
|
172
|
-
file.write(self.content)
|
ara_cli/tests/test_artefact.py
DELETED
|
@@ -1,304 +0,0 @@
|
|
|
1
|
-
import pytest
|
|
2
|
-
import os
|
|
3
|
-
from unittest.mock import patch, MagicMock, mock_open
|
|
4
|
-
from ara_cli.artefact import Artefact
|
|
5
|
-
|
|
6
|
-
mock_artefact_titles = ['Document', 'Report']
|
|
7
|
-
mock_ordered_classifiers = ['Document', 'Report']
|
|
8
|
-
mock_sub_directories = {
|
|
9
|
-
'Document': 'documents',
|
|
10
|
-
'Report': 'reports'
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
@pytest.fixture
|
|
15
|
-
def mock_classifier():
|
|
16
|
-
with patch('ara_cli.artefact.Classifier') as MockClassifier:
|
|
17
|
-
MockClassifier.artefact_titles.return_value = mock_artefact_titles
|
|
18
|
-
MockClassifier.ordered_classifiers.return_value = mock_ordered_classifiers
|
|
19
|
-
MockClassifier.get_artefact_classifier.side_effect = lambda x: 'Type1' if x == 'Document' else 'Type2'
|
|
20
|
-
MockClassifier.get_sub_directory.side_effect = lambda x: mock_sub_directories[x]
|
|
21
|
-
yield MockClassifier
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
@pytest.mark.parametrize(
|
|
25
|
-
"classifier, name, content, expected_content",
|
|
26
|
-
[
|
|
27
|
-
('Document', 'Test Artefact', 'Document: Test Artefact\nSome content',
|
|
28
|
-
'Document: Test Artefact\n\nContributes to Parent_Artefact Document\nSome content'),
|
|
29
|
-
|
|
30
|
-
('Document', 'Test Artefact', '@a_tag\nDocument: Test Artefact\nSome content',
|
|
31
|
-
'@a_tag\nDocument: Test Artefact\n\nContributes to Parent_Artefact Document\nSome content'),
|
|
32
|
-
|
|
33
|
-
('example', 'Test Artefact', 'Example: Test Artefact\nSome content',
|
|
34
|
-
'Example: Test Artefact\n\nIllustrates Parent_Artefact Document\nSome content'),
|
|
35
|
-
|
|
36
|
-
('Document', 'Test Artefact', 'Some content without title line',
|
|
37
|
-
'Some content without title line'),
|
|
38
|
-
]
|
|
39
|
-
)
|
|
40
|
-
def test_create_contributes(mock_classifier, classifier, name, content, expected_content):
|
|
41
|
-
artefact = Artefact(classifier=classifier, name=name, _content=content)
|
|
42
|
-
|
|
43
|
-
with patch.object(Artefact, 'parent', new_callable=MagicMock(return_value=Artefact('Document', 'Parent_Artefact'))), \
|
|
44
|
-
patch('ara_cli.artefact.Classifier.get_artefact_title', side_effect=lambda x: 'Document' if x == 'Document' else 'Example'):
|
|
45
|
-
|
|
46
|
-
artefact.assemble_content()
|
|
47
|
-
assert artefact._content == expected_content
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
@pytest.mark.parametrize(
|
|
51
|
-
"initial_content, parent, expected_content",
|
|
52
|
-
[
|
|
53
|
-
("Contributes to Child Artefact", Artefact('Type1', 'Parent_Artefact'), "Contributes to Parent_Artefact Document"),
|
|
54
|
-
("Contributes to: Child Artefact", Artefact('Type1', 'Parent_Artefact'), "Contributes to Parent_Artefact Document"),
|
|
55
|
-
("Illustrates Child Artefact", Artefact('Type1', 'Parent_Artefact'), "Illustrates Parent_Artefact Document"),
|
|
56
|
-
("Illustrates: Child Artefact", Artefact('Type1', 'Parent_Artefact'), "Illustrates Parent_Artefact Document"),
|
|
57
|
-
("Contributes to Child Artefact", None, "Contributes to Child Artefact"),
|
|
58
|
-
("Illustrates Child Artefact", None, "Illustrates Child Artefact")
|
|
59
|
-
]
|
|
60
|
-
)
|
|
61
|
-
def test_assemble_content_replace_parent(mock_classifier, initial_content, parent, expected_content):
|
|
62
|
-
artefact = Artefact(classifier='Document', name='Test Artefact', _content=initial_content)
|
|
63
|
-
|
|
64
|
-
with patch.object(Artefact, 'parent', new_callable=MagicMock(return_value=parent)), \
|
|
65
|
-
patch('ara_cli.artefact.Classifier.get_artefact_title', return_value='Document'):
|
|
66
|
-
artefact.assemble_content()
|
|
67
|
-
assert artefact._content == expected_content
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
@pytest.mark.parametrize(
|
|
71
|
-
"initial_content, initial_tags, expected_content",
|
|
72
|
-
[
|
|
73
|
-
("Some content", {'tag1', 'tag2'}, "@tag1 @tag2\nSome content"),
|
|
74
|
-
("@oldtag\nContent", {'newtag'}, "@newtag\nContent"),
|
|
75
|
-
("Content without tags", set(), "Content without tags"),
|
|
76
|
-
]
|
|
77
|
-
)
|
|
78
|
-
def test_assemble_content_replace_tags(mock_classifier, initial_content, initial_tags, expected_content):
|
|
79
|
-
artefact = Artefact(classifier='Document', name='Test Artefact', _content=initial_content)
|
|
80
|
-
artefact._tags = initial_tags
|
|
81
|
-
|
|
82
|
-
with patch.object(Artefact, 'parent', new_callable=MagicMock(return_value=None)):
|
|
83
|
-
artefact.assemble_content()
|
|
84
|
-
assert artefact._content == expected_content
|
|
85
|
-
|
|
86
|
-
@pytest.mark.parametrize(
|
|
87
|
-
"initial_content, parent, initial_tags, expected_content",
|
|
88
|
-
[
|
|
89
|
-
("Contributes to Child Artefact", Artefact('Type1', 'Parent_Artefact'), {'tag1'}, "@tag1\nContributes to Parent_Artefact Document"),
|
|
90
|
-
("@oldtag\nIllustrates Child Artefact", Artefact('Type1', 'Parent_Artefact'), {'newtag'}, "@newtag\nIllustrates Parent_Artefact Document"),
|
|
91
|
-
("Content without parent", None, {'tag1', 'tag2'}, "@tag1 @tag2\nContent without parent"),
|
|
92
|
-
]
|
|
93
|
-
)
|
|
94
|
-
def test_assemble_content_full(mock_classifier, initial_content, parent, initial_tags, expected_content):
|
|
95
|
-
artefact = Artefact(classifier='Document', name='Test Artefact', _content=initial_content)
|
|
96
|
-
artefact._tags = initial_tags
|
|
97
|
-
|
|
98
|
-
with patch.object(Artefact, 'parent', new_callable=MagicMock(return_value=parent)), \
|
|
99
|
-
patch('ara_cli.artefact.Classifier.get_artefact_title', return_value='Document'):
|
|
100
|
-
artefact.assemble_content()
|
|
101
|
-
assert artefact._content == expected_content
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
@pytest.mark.parametrize(
|
|
105
|
-
"content, expected_parent",
|
|
106
|
-
[
|
|
107
|
-
("Contributes to: Parent Artefact Document", Artefact('Type1', 'Parent_Artefact')),
|
|
108
|
-
("Contributes to Parent Report", Artefact('Type2', 'Parent')),
|
|
109
|
-
("No parent defined here", None),
|
|
110
|
-
("", None),
|
|
111
|
-
]
|
|
112
|
-
)
|
|
113
|
-
def test_parent_property(mock_classifier, content, expected_parent):
|
|
114
|
-
artefact = Artefact(classifier='Document', name='Test Artefact', _content=content)
|
|
115
|
-
|
|
116
|
-
parent = artefact.parent
|
|
117
|
-
|
|
118
|
-
if expected_parent is None:
|
|
119
|
-
assert parent is None
|
|
120
|
-
else:
|
|
121
|
-
assert parent is not None
|
|
122
|
-
assert parent.name == expected_parent.name
|
|
123
|
-
assert parent.classifier == expected_parent.classifier
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
@pytest.mark.parametrize(
|
|
127
|
-
"initial_parent, expected_parent",
|
|
128
|
-
[
|
|
129
|
-
(Artefact('Document', 'Existing Parent'), Artefact('Document', 'Existing Parent')),
|
|
130
|
-
]
|
|
131
|
-
)
|
|
132
|
-
def test_parent_property_initial_parent(mock_classifier, initial_parent, expected_parent):
|
|
133
|
-
artefact = Artefact(classifier='Document', name='Test Artefact', _content="Contributes to: New Parent Document")
|
|
134
|
-
artefact._parent = initial_parent # Directly set _parent to simulate pre-existing condition
|
|
135
|
-
|
|
136
|
-
parent = artefact.parent
|
|
137
|
-
|
|
138
|
-
assert parent is not None
|
|
139
|
-
assert parent.name == expected_parent.name
|
|
140
|
-
assert parent.classifier == expected_parent.classifier
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
@pytest.mark.parametrize(
|
|
144
|
-
"classifier, name, expected_file_path",
|
|
145
|
-
[
|
|
146
|
-
('Document', 'Test Artefact', 'documents/Test_Artefact.Document'),
|
|
147
|
-
('Report', 'Another Report', 'reports/Another_Report.Report'),
|
|
148
|
-
('Document', 'Report with spaces', 'documents/Report_with_spaces.Document'),
|
|
149
|
-
]
|
|
150
|
-
)
|
|
151
|
-
def test_file_path_property(mock_classifier, classifier, name, expected_file_path):
|
|
152
|
-
artefact = Artefact(classifier=classifier, name=name)
|
|
153
|
-
file_path = artefact.file_path
|
|
154
|
-
assert file_path == expected_file_path
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
@pytest.mark.parametrize(
|
|
158
|
-
"content, expected_tags",
|
|
159
|
-
[
|
|
160
|
-
("@tag1 @tag2 Some content here", {'tag1', 'tag2'}),
|
|
161
|
-
("@singleTag Content follows", {'singleTag'}),
|
|
162
|
-
("No tags in this content", set()),
|
|
163
|
-
("", set()),
|
|
164
|
-
("@mixedCase @AnotherTag @123numeric", {'mixedCase', 'AnotherTag', '123numeric'}),
|
|
165
|
-
(" @leadingSpaceTag @another ", {'leadingSpaceTag', 'another'}),
|
|
166
|
-
("Not a tag @notatag", set()),
|
|
167
|
-
]
|
|
168
|
-
)
|
|
169
|
-
def test_tags_property(mock_classifier, content, expected_tags):
|
|
170
|
-
artefact = Artefact(classifier='Document', name='Test Artefact', _content=content)
|
|
171
|
-
tags = artefact.tags
|
|
172
|
-
assert tags == expected_tags
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
@pytest.mark.parametrize(
|
|
176
|
-
"initial_tags, content, expected_tags",
|
|
177
|
-
[
|
|
178
|
-
({'preExistingTag'}, "@tag1 @tag2", {'preExistingTag'}),
|
|
179
|
-
(None, "@tag1 @tag2", {'tag1', 'tag2'}),
|
|
180
|
-
({'anotherTag'}, "", {'anotherTag'}),
|
|
181
|
-
(set(), "No tags in this content", set()),
|
|
182
|
-
]
|
|
183
|
-
)
|
|
184
|
-
def test_tags_property_when_tags_pre_set(mock_classifier, initial_tags, content, expected_tags):
|
|
185
|
-
artefact = Artefact(classifier='Document', name='Test Artefact', _content=content)
|
|
186
|
-
artefact._tags = initial_tags # Directly set _tags to simulate pre-existing condition
|
|
187
|
-
|
|
188
|
-
tags = artefact.tags
|
|
189
|
-
|
|
190
|
-
assert tags == expected_tags
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
def test_content_property_reads_file(mock_classifier):
|
|
194
|
-
artefact = Artefact(classifier='Document', name='Test Artefact')
|
|
195
|
-
mock_file_content = "This is the file content."
|
|
196
|
-
|
|
197
|
-
# Mock the open function within the specific context
|
|
198
|
-
with patch("builtins.open", mock_open(read_data=mock_file_content)):
|
|
199
|
-
content = artefact.content
|
|
200
|
-
|
|
201
|
-
assert content == mock_file_content
|
|
202
|
-
|
|
203
|
-
def test_content_property_caches_content(mock_classifier):
|
|
204
|
-
artefact = Artefact(classifier='Document', name='Test Artefact')
|
|
205
|
-
mock_file_content = "This is the file content."
|
|
206
|
-
|
|
207
|
-
# Mock the open function within the specific context
|
|
208
|
-
with patch("builtins.open", mock_open(read_data=mock_file_content)) as mocked_open:
|
|
209
|
-
content_first_call = artefact.content
|
|
210
|
-
content_second_call = artefact.content
|
|
211
|
-
|
|
212
|
-
# Ensure the file is only read once
|
|
213
|
-
mocked_open.assert_called_once_with(artefact.file_path, 'r')
|
|
214
|
-
assert content_first_call == mock_file_content
|
|
215
|
-
assert content_second_call == mock_file_content
|
|
216
|
-
|
|
217
|
-
def test_content_property_with_existing_content(mock_classifier):
|
|
218
|
-
existing_content = "Existing content should be returned."
|
|
219
|
-
artefact = Artefact(classifier='Document', name='Test Artefact', _content=existing_content)
|
|
220
|
-
|
|
221
|
-
with patch("builtins.open", mock_open()) as mocked_open:
|
|
222
|
-
content = artefact.content
|
|
223
|
-
|
|
224
|
-
# Ensure that the file is never opened because _content is already set
|
|
225
|
-
mocked_open.assert_not_called()
|
|
226
|
-
assert content == existing_content
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
@pytest.mark.parametrize(
|
|
230
|
-
"content, expected_classifier, expected_name",
|
|
231
|
-
[
|
|
232
|
-
("Document: Parent Artefact", 'Type1', 'Parent Artefact'),
|
|
233
|
-
("Report: Another Report", 'Type2', 'Another Report')
|
|
234
|
-
]
|
|
235
|
-
)
|
|
236
|
-
def test_from_content_valid(mock_classifier, content, expected_classifier, expected_name):
|
|
237
|
-
artefact = Artefact.from_content(content)
|
|
238
|
-
assert artefact.classifier == expected_classifier
|
|
239
|
-
assert artefact.name == expected_name
|
|
240
|
-
assert artefact.content == content
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
@pytest.mark.parametrize(
|
|
244
|
-
"content",
|
|
245
|
-
[
|
|
246
|
-
"Invalid content without proper structure",
|
|
247
|
-
"Classifier: Name: Missing title",
|
|
248
|
-
"",
|
|
249
|
-
None,
|
|
250
|
-
]
|
|
251
|
-
)
|
|
252
|
-
def test_from_content_invalid(mock_classifier, content):
|
|
253
|
-
with pytest.raises(ValueError, match="Content does not contain valid artefact information"):
|
|
254
|
-
Artefact.from_content(content)
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
@pytest.mark.parametrize(
|
|
258
|
-
"content, should_raise_error",
|
|
259
|
-
[
|
|
260
|
-
("Some valid content", False), # Content is valid, should not raise
|
|
261
|
-
]
|
|
262
|
-
)
|
|
263
|
-
def test_write_to_file_content_check(mock_classifier, content, should_raise_error):
|
|
264
|
-
artefact = Artefact(classifier='Document', name='Test Artefact', _content=content)
|
|
265
|
-
if should_raise_error:
|
|
266
|
-
with pytest.raises(ValueError, match="Artefact object does not contain content information"):
|
|
267
|
-
artefact.write_to_file()
|
|
268
|
-
else:
|
|
269
|
-
with patch('builtins.open', mock_open()) as mocked_file, \
|
|
270
|
-
patch('os.makedirs') as mocked_makedirs, \
|
|
271
|
-
patch.object(artefact, 'assemble_content') as mocked_assemble_content:
|
|
272
|
-
artefact.write_to_file()
|
|
273
|
-
mocked_assemble_content.assert_called_once()
|
|
274
|
-
mocked_makedirs.assert_called_once_with('documents/Test_Artefact.data', exist_ok=True)
|
|
275
|
-
mocked_file.assert_called_once_with('documents/Test_Artefact.Document', 'w')
|
|
276
|
-
mocked_file().write.assert_called_once_with(content)
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
@pytest.mark.parametrize(
|
|
280
|
-
"file_path",
|
|
281
|
-
[
|
|
282
|
-
('documents/Test_Artefact.Document'),
|
|
283
|
-
('reports/Another_Report.Report')
|
|
284
|
-
]
|
|
285
|
-
)
|
|
286
|
-
def test_write_to_file_directory_creation(mock_classifier, file_path):
|
|
287
|
-
artefact = Artefact(classifier='Document', name='Test Artefact', _content="Some content")
|
|
288
|
-
artefact._file_path = file_path # Directly set file path for testing
|
|
289
|
-
with patch('os.makedirs') as mocked_makedirs, \
|
|
290
|
-
patch('builtins.open', mock_open()):
|
|
291
|
-
artefact.write_to_file()
|
|
292
|
-
data_directory = f"{os.path.splitext(file_path)[0]}.data"
|
|
293
|
-
mocked_makedirs.assert_called_once_with(data_directory, exist_ok=True)
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
def test_write_to_file_writes_content(mock_classifier):
|
|
297
|
-
content = "Some valid content"
|
|
298
|
-
artefact = Artefact(classifier='Document', name='Test Artefact', _content=content)
|
|
299
|
-
with patch('builtins.open', mock_open()) as mocked_file, \
|
|
300
|
-
patch('os.makedirs'), \
|
|
301
|
-
patch.object(Artefact, 'assemble_content'):
|
|
302
|
-
artefact.write_to_file()
|
|
303
|
-
mocked_file.assert_called_once_with('documents/Test_Artefact.Document', 'w')
|
|
304
|
-
mocked_file().write.assert_called_once_with(content)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|