developer-assistant 0.2.0__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.
- DA/Interface.py +241 -0
- DA/Modules/__init__.py +0 -0
- DA/Modules/config_manager.py +126 -0
- DA/Modules/opener.py +31 -0
- DA/Modules/projects_manager.py +120 -0
- DA/Modules/version_logic.py +315 -0
- DA/Templates/changelog_template.txt +6 -0
- DA/Templates/entry_template.txt +3 -0
- DA/Templates/header_template.txt +1 -0
- DA/__init__.py +0 -0
- DA/default/Test-Project.ini +5 -0
- DA/default/default-changelog.md +6 -0
- DA/default/default-memory.ini +9 -0
- DA/intro.md +62 -0
- developer_assistant-0.2.0.dist-info/METADATA +134 -0
- developer_assistant-0.2.0.dist-info/RECORD +20 -0
- developer_assistant-0.2.0.dist-info/WHEEL +5 -0
- developer_assistant-0.2.0.dist-info/entry_points.txt +2 -0
- developer_assistant-0.2.0.dist-info/licenses/LICENSE +9 -0
- developer_assistant-0.2.0.dist-info/top_level.txt +1 -0
DA/Interface.py
ADDED
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import os, sys
|
|
2
|
+
import time
|
|
3
|
+
import platform
|
|
4
|
+
import subprocess
|
|
5
|
+
import shutil
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from termcolor import colored
|
|
8
|
+
from rich.progress import track
|
|
9
|
+
from rich.console import Console
|
|
10
|
+
from rich.markdown import Markdown
|
|
11
|
+
|
|
12
|
+
from DA.Modules.projects_manager import ProjectsManager
|
|
13
|
+
from DA.Modules.config_manager import ConfigManager
|
|
14
|
+
from DA.Modules.opener import Opener
|
|
15
|
+
from importlib import resources
|
|
16
|
+
|
|
17
|
+
class Interface:
|
|
18
|
+
def __init__(self, color="light_blue"):
|
|
19
|
+
self.version = "0.2.0"
|
|
20
|
+
title = f"DA - {self.version}"
|
|
21
|
+
|
|
22
|
+
if platform.system() == "Windows":
|
|
23
|
+
os.system(f'title {title}')
|
|
24
|
+
else:
|
|
25
|
+
print(f'\33]0;{title}\a', end='', flush=True)
|
|
26
|
+
|
|
27
|
+
self.config = ConfigManager('memory.ini')
|
|
28
|
+
|
|
29
|
+
first_run = False
|
|
30
|
+
if not self.config.memory_ini.exists():
|
|
31
|
+
self.local_init()
|
|
32
|
+
self.config = ConfigManager('memory.ini')
|
|
33
|
+
first_run = True
|
|
34
|
+
|
|
35
|
+
self.projects_manager = ProjectsManager()
|
|
36
|
+
|
|
37
|
+
self.memory = self.config.load_memory()
|
|
38
|
+
self.color = self.memory.get('color') or color
|
|
39
|
+
|
|
40
|
+
#==Reusables==
|
|
41
|
+
self.header = (colored(" Developer Assistant ", f"{self.color}"))
|
|
42
|
+
self.clear_screen = 'cls' if platform.system() == 'Windows' else 'clear'
|
|
43
|
+
self.user_path = os.environ.get('USERPROFILE') or os.environ.get('HOME', 'User')
|
|
44
|
+
|
|
45
|
+
if first_run:
|
|
46
|
+
self.intro()
|
|
47
|
+
|
|
48
|
+
self.load()
|
|
49
|
+
|
|
50
|
+
def load(self):
|
|
51
|
+
temp_log = Path(__file__).parent / "CHANGELOG.tmp"
|
|
52
|
+
|
|
53
|
+
if os.path.exists(temp_log):
|
|
54
|
+
while True:
|
|
55
|
+
os.system(self.clear_screen)
|
|
56
|
+
print(colored(f"Temporary changelog detected in:\n{temp_log}\n", "yellow"))
|
|
57
|
+
print(colored("D", "light_red") + "elete or " + colored("K", "light_red") + "eep?\n")
|
|
58
|
+
choice = input(f"{self.user_path}> ").lower()
|
|
59
|
+
if choice == "d":
|
|
60
|
+
os.remove(temp_log)
|
|
61
|
+
break
|
|
62
|
+
elif choice == "k":
|
|
63
|
+
break
|
|
64
|
+
else:
|
|
65
|
+
print(colored("\nPlease make a valid choice.", "light_red"))
|
|
66
|
+
time.sleep(1.5)
|
|
67
|
+
|
|
68
|
+
os.system(self.clear_screen)
|
|
69
|
+
message = (colored("Developer Assistant Ver. ", attrs=["bold"]) + (colored(self.version, f"{self.color}", attrs=["bold"])))
|
|
70
|
+
status = (colored("Loading, please wait... ", attrs=["blink"]))
|
|
71
|
+
|
|
72
|
+
print(message.center(85))
|
|
73
|
+
print("")
|
|
74
|
+
print(status.center(73))
|
|
75
|
+
|
|
76
|
+
for i in track(range(20)):
|
|
77
|
+
time.sleep(0.10)
|
|
78
|
+
|
|
79
|
+
self.menu()
|
|
80
|
+
|
|
81
|
+
def menu(self):
|
|
82
|
+
while True:
|
|
83
|
+
os.system(self.clear_screen)
|
|
84
|
+
print("Main menu")
|
|
85
|
+
print(self.header.center(127, "="))
|
|
86
|
+
print("\nE. Exit\n")
|
|
87
|
+
print("1. Projects")
|
|
88
|
+
print("2. Settings\n")
|
|
89
|
+
|
|
90
|
+
choice = input(f"{self.user_path}> ").strip()
|
|
91
|
+
|
|
92
|
+
if choice.lower() == "e":
|
|
93
|
+
os.system(self.clear_screen)
|
|
94
|
+
print(self.header.center(127, "="))
|
|
95
|
+
print("Bye!")
|
|
96
|
+
time.sleep(2)
|
|
97
|
+
raise SystemExit
|
|
98
|
+
|
|
99
|
+
elif choice == "1":
|
|
100
|
+
self.projects()
|
|
101
|
+
|
|
102
|
+
elif choice == "2":
|
|
103
|
+
self.settings()
|
|
104
|
+
else:
|
|
105
|
+
print("")
|
|
106
|
+
print(colored("Unknown option...", "light_red", attrs=["blink"]))
|
|
107
|
+
time.sleep(2)
|
|
108
|
+
|
|
109
|
+
def projects(self):
|
|
110
|
+
config = ConfigManager('memory.ini')
|
|
111
|
+
self.memory = config.load_memory()
|
|
112
|
+
while True:
|
|
113
|
+
os.system(self.clear_screen)
|
|
114
|
+
print("Main menu / Projects")
|
|
115
|
+
print(self.header.center(127, "="))
|
|
116
|
+
print("E. Back\n")
|
|
117
|
+
print(colored("1. The last project:", attrs=["underline"]))
|
|
118
|
+
print(colored(self.memory.get('last_project'), f"{self.color}"))
|
|
119
|
+
print("\n2. Start a new project.\n")
|
|
120
|
+
print(colored("Continue a project.", attrs=["underline"]))
|
|
121
|
+
print("Pinned:")
|
|
122
|
+
print("a. " + colored(self.memory.get('pinned_project'), f"{self.color}"))
|
|
123
|
+
print("b. " + colored(self.memory.get('pinned_project1'), f"{self.color}"))
|
|
124
|
+
print("c. " + colored(self.memory.get('pinned_project2'), f"{self.color}"))
|
|
125
|
+
|
|
126
|
+
print("")
|
|
127
|
+
choice = input(f"{self.user_path}> ").strip()
|
|
128
|
+
|
|
129
|
+
if choice.lower() == "e":
|
|
130
|
+
return
|
|
131
|
+
|
|
132
|
+
elif choice == "1":
|
|
133
|
+
project = self.memory.get('last_project')
|
|
134
|
+
if not project:
|
|
135
|
+
print(colored("\nLast project has not been defined...", "light_red", attrs=["blink"]))
|
|
136
|
+
time.sleep(2)
|
|
137
|
+
else:
|
|
138
|
+
self.projects_manager.load_project(f"{project}")
|
|
139
|
+
|
|
140
|
+
elif choice == "2":
|
|
141
|
+
self.projects_manager.new_project()
|
|
142
|
+
|
|
143
|
+
elif choice.lower() in ("a", "b", "c"):
|
|
144
|
+
#==If the pinned project is empty, return an error message==
|
|
145
|
+
options_map = {
|
|
146
|
+
'a': 'pinned_project',
|
|
147
|
+
'b': 'pinned_project1',
|
|
148
|
+
'c': 'pinned_project2'
|
|
149
|
+
}
|
|
150
|
+
key = options_map.get(choice.lower())
|
|
151
|
+
project_name = self.memory.get(key)
|
|
152
|
+
if project_name == "":
|
|
153
|
+
print("")
|
|
154
|
+
print(colored(f"Project '{choice}' has not been defined...", "light_red", attrs=["blink"]))
|
|
155
|
+
time.sleep(2)
|
|
156
|
+
else:
|
|
157
|
+
project = self.memory.get(key)
|
|
158
|
+
self.projects_manager.load_project(f"{project}")
|
|
159
|
+
|
|
160
|
+
else:
|
|
161
|
+
print("")
|
|
162
|
+
print(colored("Unknown option...", "light_red", attrs=["blink"]))
|
|
163
|
+
time.sleep(2)
|
|
164
|
+
|
|
165
|
+
def settings(self):
|
|
166
|
+
while True:
|
|
167
|
+
os.system(self.clear_screen)
|
|
168
|
+
print("Main menu / Settings")
|
|
169
|
+
print(self.header.center(127, "="))
|
|
170
|
+
print("E. Back\n")
|
|
171
|
+
print(colored("Configuration options", attrs=["underline"]))
|
|
172
|
+
print("1. Edit pinned projects [WIP]")
|
|
173
|
+
print("2. Change program's color [WIP]\n")
|
|
174
|
+
print(colored("Other options", attrs=["underline"]))
|
|
175
|
+
print("3. Open memory.ini manually")
|
|
176
|
+
print("4. Open the Projects folder")
|
|
177
|
+
print("5. Open the Templates folder\n")
|
|
178
|
+
|
|
179
|
+
choice = input(f"{self.user_path}> ").strip()
|
|
180
|
+
|
|
181
|
+
if choice.lower() == "e":
|
|
182
|
+
return
|
|
183
|
+
elif choice == "1":
|
|
184
|
+
pass
|
|
185
|
+
elif choice == "2":
|
|
186
|
+
pass
|
|
187
|
+
elif choice == "3":
|
|
188
|
+
Opener.open(self.config.memory_ini)
|
|
189
|
+
elif choice == "4":
|
|
190
|
+
Opener.open(self.config.projects_folder)
|
|
191
|
+
elif choice == "5":
|
|
192
|
+
Opener.open(self.config.templates_folder)
|
|
193
|
+
else:
|
|
194
|
+
print("")
|
|
195
|
+
print(colored("Unknown option...", "light_red", attrs=["blink"]))
|
|
196
|
+
time.sleep(2)
|
|
197
|
+
|
|
198
|
+
def local_init(self):
|
|
199
|
+
default_files = resources.files("DA.default")
|
|
200
|
+
dest = self.config.memory_ini
|
|
201
|
+
|
|
202
|
+
for item in default_files.iterdir():
|
|
203
|
+
if item.name == "default-memory.ini":
|
|
204
|
+
shutil.copy(item, dest)
|
|
205
|
+
|
|
206
|
+
self.config.projects_folder.mkdir(parents=True, exist_ok=True)
|
|
207
|
+
# Add Test-Project
|
|
208
|
+
dest = self.config.projects_folder
|
|
209
|
+
for item in default_files.iterdir():
|
|
210
|
+
if item.name == "Test-Project.ini":
|
|
211
|
+
shutil.copy(item, dest)
|
|
212
|
+
|
|
213
|
+
def intro(self):
|
|
214
|
+
os.system(self.clear_screen)
|
|
215
|
+
print(colored("Welcome to the Developer Assistant\n", f"{self.color}", attrs=["bold"]))
|
|
216
|
+
print("Here's everything you need to get started:\n")
|
|
217
|
+
|
|
218
|
+
time.sleep(2)
|
|
219
|
+
|
|
220
|
+
readme_content = resources.files("DA").joinpath("intro.md").read_text()
|
|
221
|
+
|
|
222
|
+
MARKDOWN = readme_content
|
|
223
|
+
console = Console()
|
|
224
|
+
md = Markdown(MARKDOWN)
|
|
225
|
+
console.print(md)
|
|
226
|
+
|
|
227
|
+
input("\nContinue..." + colored("[Enter]", f"{self.color}"))
|
|
228
|
+
|
|
229
|
+
self.menu()
|
|
230
|
+
|
|
231
|
+
def main():
|
|
232
|
+
try:
|
|
233
|
+
app = Interface()
|
|
234
|
+
app.run()
|
|
235
|
+
except KeyboardInterrupt:
|
|
236
|
+
print("\n\n" + colored("Execution interrupted by the user. Exiting...", "magenta", attrs=["bold"]))
|
|
237
|
+
time.sleep(2)
|
|
238
|
+
sys.exit(0)
|
|
239
|
+
|
|
240
|
+
if __name__ == "__main__":
|
|
241
|
+
main()
|
DA/Modules/__init__.py
ADDED
|
File without changes
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import configparser
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
import shutil
|
|
4
|
+
import time
|
|
5
|
+
|
|
6
|
+
from platformdirs import user_config_path
|
|
7
|
+
|
|
8
|
+
from importlib import resources
|
|
9
|
+
|
|
10
|
+
class ConfigManager:
|
|
11
|
+
def __init__(self, config_file):
|
|
12
|
+
self.config = configparser.ConfigParser()
|
|
13
|
+
|
|
14
|
+
#==Path setup==
|
|
15
|
+
self.internal_dir = Path(__file__).resolve().parents[1]
|
|
16
|
+
self.global_dir = user_config_path("da-ui")
|
|
17
|
+
self.global_dir.mkdir(parents=True, exist_ok=True)
|
|
18
|
+
|
|
19
|
+
#==Specific targets==
|
|
20
|
+
self.memory_ini = self.global_dir / 'memory.ini'
|
|
21
|
+
self.projects_folder = self.global_dir / "Projects"
|
|
22
|
+
self.templates_folder = self.global_dir / "Templates"
|
|
23
|
+
self.new_project_ini = self.projects_folder / config_file
|
|
24
|
+
|
|
25
|
+
self.migrate_old_data()
|
|
26
|
+
|
|
27
|
+
#==Update last_project in memory.ini==
|
|
28
|
+
self.update_last_project = Path(config_file).stem
|
|
29
|
+
|
|
30
|
+
self.config.read(self.memory_ini)
|
|
31
|
+
|
|
32
|
+
def migrate_old_data(self):
|
|
33
|
+
old_memory = self.internal_dir / "memory.ini"
|
|
34
|
+
old_projects = self.internal_dir / "Projects"
|
|
35
|
+
|
|
36
|
+
if old_memory.exists() and not self.memory_ini.exists():
|
|
37
|
+
shutil.move(str(old_memory), str(self.memory_ini))
|
|
38
|
+
print(f"Migrated memory.ini to {self.global_dir}")
|
|
39
|
+
time.sleep(2)
|
|
40
|
+
|
|
41
|
+
if old_projects.exists() and not self.projects_folder.exists():
|
|
42
|
+
shutil.move(str(old_projects), str(self.projects_folder))
|
|
43
|
+
print(f"Migrated Projects folder to {self.global_dir}")
|
|
44
|
+
time.sleep(2)
|
|
45
|
+
|
|
46
|
+
if not self.templates_folder.exists():
|
|
47
|
+
default_templates = resources.files("DA.Templates")
|
|
48
|
+
user_templates = self.templates_folder
|
|
49
|
+
|
|
50
|
+
user_templates.mkdir(parents=True, exist_ok=True)
|
|
51
|
+
|
|
52
|
+
for item in default_templates.iterdir():
|
|
53
|
+
dest = user_templates / item.name
|
|
54
|
+
if not dest.exists():
|
|
55
|
+
shutil.copy(item, dest)
|
|
56
|
+
|
|
57
|
+
print(f"Copied default Templates to {self.global_dir}")
|
|
58
|
+
time.sleep(2)
|
|
59
|
+
|
|
60
|
+
def load_memory(self):
|
|
61
|
+
prj = self.config['ITEMS']
|
|
62
|
+
return {
|
|
63
|
+
'last_project': prj.get('last_project'),
|
|
64
|
+
'pinned_project': prj.get('pinned_project'),
|
|
65
|
+
'pinned_project1': prj.get('pinned_project1'),
|
|
66
|
+
'pinned_project2': prj.get('pinned_project2'),
|
|
67
|
+
'color': self.config.get("CONFIG", "color")
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
def update_memory(self, category: str, variable: str, value: str):
|
|
71
|
+
memory_parser = configparser.ConfigParser()
|
|
72
|
+
memory_parser.read(self.memory_ini)
|
|
73
|
+
|
|
74
|
+
memory_parser.set(category, variable, value)
|
|
75
|
+
with open(self.memory_ini, "w", encoding="utf-8") as f:
|
|
76
|
+
memory_parser.write(f)
|
|
77
|
+
return
|
|
78
|
+
|
|
79
|
+
def project_ini(self):
|
|
80
|
+
#==Create new project ini==
|
|
81
|
+
project_parser = configparser.ConfigParser()
|
|
82
|
+
project_parser["SETTINGS"] = self.data
|
|
83
|
+
|
|
84
|
+
with open(self.new_project_ini, "w", encoding="utf-8") as f:
|
|
85
|
+
project_parser.write(f)
|
|
86
|
+
|
|
87
|
+
#==Update last_project in memory.ini==
|
|
88
|
+
memory_parser = configparser.ConfigParser()
|
|
89
|
+
memory_parser.read(self.memory_ini)
|
|
90
|
+
|
|
91
|
+
memory_parser.set("ITEMS", "last_project", self.update_last_project)
|
|
92
|
+
with open(self.memory_ini, "w", encoding="utf-8") as f:
|
|
93
|
+
memory_parser.write(f)
|
|
94
|
+
return
|
|
95
|
+
|
|
96
|
+
def load_project(self):
|
|
97
|
+
#==Return project ini variables==
|
|
98
|
+
project_parser = configparser.ConfigParser()
|
|
99
|
+
project_parser.read(self.new_project_ini)
|
|
100
|
+
'''
|
|
101
|
+
try:
|
|
102
|
+
prj = project_parser['SETTINGS']
|
|
103
|
+
except KeyError:
|
|
104
|
+
print(f"\nCan't find {self.new_project_ini}")
|
|
105
|
+
time.sleep(2)
|
|
106
|
+
return
|
|
107
|
+
'''
|
|
108
|
+
prj = project_parser['SETTINGS']
|
|
109
|
+
return {
|
|
110
|
+
'path': prj.get('path'),
|
|
111
|
+
'changelog': prj.get('changelog'),
|
|
112
|
+
'version': prj.get('version'),
|
|
113
|
+
'cloud': prj.get('cloud')
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
def update_project(self, variable: str, value: str):
|
|
117
|
+
project_parser = configparser.ConfigParser()
|
|
118
|
+
project_parser.read(self.new_project_ini)
|
|
119
|
+
|
|
120
|
+
project_parser.set("SETTINGS", variable, value)
|
|
121
|
+
with open(self.new_project_ini, "w", encoding="utf-8") as f:
|
|
122
|
+
project_parser.write(f)
|
|
123
|
+
return
|
|
124
|
+
|
|
125
|
+
if __name__ == "__main__":
|
|
126
|
+
ConfigManager()
|
DA/Modules/opener.py
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from termcolor import colored
|
|
2
|
+
import subprocess
|
|
3
|
+
import platform
|
|
4
|
+
import os
|
|
5
|
+
import time
|
|
6
|
+
|
|
7
|
+
class Opener:
|
|
8
|
+
@staticmethod
|
|
9
|
+
def open(path):
|
|
10
|
+
#==Check if path exists==
|
|
11
|
+
if not os.path.exists(path):
|
|
12
|
+
print(colored("\nThis path does not exist.", "light_red"))
|
|
13
|
+
time.sleep(1.5)
|
|
14
|
+
return
|
|
15
|
+
#==Open if exists==
|
|
16
|
+
else:
|
|
17
|
+
system = platform.system()
|
|
18
|
+
if system == "Windows":
|
|
19
|
+
subprocess.Popen(['explorer', path])
|
|
20
|
+
elif system == "Darwin":
|
|
21
|
+
subprocess.Popen(['open', path])
|
|
22
|
+
else:
|
|
23
|
+
subprocess.Popen(
|
|
24
|
+
['xdg-open', path],
|
|
25
|
+
stdout=subprocess.DEVNULL,
|
|
26
|
+
stderr=subprocess.DEVNULL
|
|
27
|
+
)
|
|
28
|
+
return
|
|
29
|
+
|
|
30
|
+
if __name__ == "__main__":
|
|
31
|
+
Opener()
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import os, sys
|
|
2
|
+
import time
|
|
3
|
+
from termcolor import colored
|
|
4
|
+
import subprocess
|
|
5
|
+
import platform
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
from DA.Modules.config_manager import ConfigManager
|
|
9
|
+
from DA.Modules.version_logic import VersionLogic
|
|
10
|
+
from DA.Modules.opener import Opener
|
|
11
|
+
|
|
12
|
+
class ProjectsManager:
|
|
13
|
+
def __init__(self, color="light_blue"):
|
|
14
|
+
os.system('title Developer Assistant')
|
|
15
|
+
self.version = "1.0.0-alpha"
|
|
16
|
+
|
|
17
|
+
self.config = ConfigManager('memory.ini')
|
|
18
|
+
self.memory = self.config.load_memory()
|
|
19
|
+
self.color = self.memory.get('color') or color
|
|
20
|
+
|
|
21
|
+
#==Reusables==
|
|
22
|
+
self.header = (colored(" Developer Assistant ", f"{self.color}"))
|
|
23
|
+
self.clear_screen = 'cls' if platform.system() == 'Windows' else 'clear'
|
|
24
|
+
self.user_path = os.environ.get('USERPROFILE') or os.environ.get('HOME', 'User')
|
|
25
|
+
|
|
26
|
+
def new_project(self):
|
|
27
|
+
os.system(self.clear_screen)
|
|
28
|
+
print(self.header.center(127, "="))
|
|
29
|
+
print("E. Abort/back\n")
|
|
30
|
+
|
|
31
|
+
name = input("Enter new project name: ")
|
|
32
|
+
if name.lower() == "e":
|
|
33
|
+
return
|
|
34
|
+
|
|
35
|
+
print("")
|
|
36
|
+
path = input("Enter project path: ")
|
|
37
|
+
|
|
38
|
+
print("")
|
|
39
|
+
changelog = input("Project changelog path: ")
|
|
40
|
+
|
|
41
|
+
print("")
|
|
42
|
+
version = input("Current project version: ")
|
|
43
|
+
|
|
44
|
+
print("")
|
|
45
|
+
cloud = input("Cloud service (OneDrive/Azure/Dropbox/Google Drive): ")
|
|
46
|
+
|
|
47
|
+
print("")
|
|
48
|
+
confirm = input("Confirm(Y) or abort(E): ")
|
|
49
|
+
if confirm.lower() == "e":
|
|
50
|
+
return
|
|
51
|
+
|
|
52
|
+
elif confirm.lower() == "y":
|
|
53
|
+
self.new_manager = ConfigManager(f"{name}.ini")
|
|
54
|
+
self.new_manager.data = {
|
|
55
|
+
"path": path,
|
|
56
|
+
"changelog": changelog,
|
|
57
|
+
"version": version,
|
|
58
|
+
"cloud": cloud
|
|
59
|
+
}
|
|
60
|
+
self.new_manager.project_ini()
|
|
61
|
+
|
|
62
|
+
return
|
|
63
|
+
|
|
64
|
+
else:
|
|
65
|
+
print("")
|
|
66
|
+
print(colored("Unknown option, try again...", "light_red", attrs=["blink"]))
|
|
67
|
+
time.sleep(2)
|
|
68
|
+
return
|
|
69
|
+
|
|
70
|
+
def load_project(self, project):
|
|
71
|
+
while True:
|
|
72
|
+
chosen_project = f"{project}"
|
|
73
|
+
self.load_manager = ConfigManager(f"{project}.ini")
|
|
74
|
+
try:
|
|
75
|
+
self.setting = self.load_manager.load_project()
|
|
76
|
+
except KeyError:
|
|
77
|
+
print(colored(f"\nCan't find {chosen_project}.ini", "light_red"))
|
|
78
|
+
time.sleep(2)
|
|
79
|
+
return
|
|
80
|
+
|
|
81
|
+
self.version_logic = VersionLogic()
|
|
82
|
+
|
|
83
|
+
project_ini_path = self.load_manager.projects_folder / f"{chosen_project}.ini"
|
|
84
|
+
|
|
85
|
+
os.system(self.clear_screen)
|
|
86
|
+
print("Main menu / Projects / Project menu")
|
|
87
|
+
print(self.header.center(127, "="))
|
|
88
|
+
print("E. Back\n")
|
|
89
|
+
print(colored("Chosen project:", attrs=["underline"]))
|
|
90
|
+
print(colored(chosen_project, f"{self.color}"))
|
|
91
|
+
print("\n1. Open project folder.")
|
|
92
|
+
print("2. Update the changelog.")
|
|
93
|
+
print("3. Backup to the cloud. [WIP]")
|
|
94
|
+
print("4. Open project configurations.\n")
|
|
95
|
+
|
|
96
|
+
choice = input(f"{self.user_path}> ").strip()
|
|
97
|
+
|
|
98
|
+
if choice.lower() == "e":
|
|
99
|
+
return
|
|
100
|
+
|
|
101
|
+
elif choice == "1":
|
|
102
|
+
path = self.setting.get('path')
|
|
103
|
+
folder = Path(path)
|
|
104
|
+
Opener.open(folder)
|
|
105
|
+
|
|
106
|
+
elif choice == "2":
|
|
107
|
+
self.version_logic.project_menu(chosen_project)
|
|
108
|
+
|
|
109
|
+
# elif choice == "3":
|
|
110
|
+
|
|
111
|
+
elif choice == "4":
|
|
112
|
+
Opener.open(project_ini_path)
|
|
113
|
+
|
|
114
|
+
else:
|
|
115
|
+
print("")
|
|
116
|
+
print(colored("Unknown option...", "light_red", attrs=["blink"]))
|
|
117
|
+
time.sleep(2)
|
|
118
|
+
|
|
119
|
+
if __name__ == "__main__":
|
|
120
|
+
ProjectsManager()
|
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
import os, sys
|
|
2
|
+
import time
|
|
3
|
+
import subprocess
|
|
4
|
+
import platform
|
|
5
|
+
import shutil
|
|
6
|
+
from datetime import datetime
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from termcolor import colored
|
|
9
|
+
from prompt_toolkit import prompt
|
|
10
|
+
from rich.console import Console
|
|
11
|
+
from rich.markdown import Markdown
|
|
12
|
+
|
|
13
|
+
from DA.Modules.config_manager import ConfigManager
|
|
14
|
+
from DA.Modules.opener import Opener
|
|
15
|
+
|
|
16
|
+
class VersionLogic:
|
|
17
|
+
def __init__(self, color="light_blue"):
|
|
18
|
+
self.config = ConfigManager('memory.ini')
|
|
19
|
+
self.memory = self.config.load_memory()
|
|
20
|
+
self.color = self.memory.get('color') or color
|
|
21
|
+
|
|
22
|
+
#==Reusables==
|
|
23
|
+
self.header = (colored(" Developer Assistant ", f"{self.color}"))
|
|
24
|
+
self.clear_screen = 'cls' if platform.system() == 'Windows' else 'clear'
|
|
25
|
+
self.user_path = os.environ.get('USERPROFILE') or os.environ.get('HOME', 'User')
|
|
26
|
+
|
|
27
|
+
def project_menu(self, project):
|
|
28
|
+
self.active_project = project
|
|
29
|
+
|
|
30
|
+
self.load_manager = ConfigManager(f"{project}.ini")
|
|
31
|
+
self.setting = self.load_manager.load_project()
|
|
32
|
+
self.changelog_path = Path(self.setting.get('changelog'))
|
|
33
|
+
|
|
34
|
+
now = datetime.now()
|
|
35
|
+
self.today = now.strftime("%Y-%m-%d")
|
|
36
|
+
|
|
37
|
+
ROOT = Path(__file__).resolve().parents[1]
|
|
38
|
+
self.templog_path = ROOT / "CHANGELOG.tmp"
|
|
39
|
+
|
|
40
|
+
while True:
|
|
41
|
+
os.system(self.clear_screen)
|
|
42
|
+
print("Main menu / Projects / Project menu / Changelog")
|
|
43
|
+
print(self.header.center(127, "="))
|
|
44
|
+
print("E. Back\n")
|
|
45
|
+
print(colored("Chosen project:", attrs=["underline"]))
|
|
46
|
+
print(colored(self.active_project, f"{self.color}"))
|
|
47
|
+
print("Version: " + colored(self.setting.get('version'), f"{self.color}"))
|
|
48
|
+
print("\n1. Create a new changelog")
|
|
49
|
+
print("2. Add new changes")
|
|
50
|
+
print("3. Open the changelog")
|
|
51
|
+
print("4. Preview .md changelog\n")
|
|
52
|
+
|
|
53
|
+
choice = input(f"{self.user_path}> ").strip()
|
|
54
|
+
|
|
55
|
+
if choice.lower() == "e":
|
|
56
|
+
return
|
|
57
|
+
|
|
58
|
+
elif choice == "1":
|
|
59
|
+
self.create_changelog()
|
|
60
|
+
|
|
61
|
+
elif choice == "2":
|
|
62
|
+
#==Check if the file path exists==
|
|
63
|
+
if not os.path.exists(self.changelog_path):
|
|
64
|
+
print(colored("\nSystem cannot find the changelog path.", "light_red"))
|
|
65
|
+
time.sleep(1.5)
|
|
66
|
+
else:
|
|
67
|
+
self.update_changelog()
|
|
68
|
+
|
|
69
|
+
elif choice == "3":
|
|
70
|
+
Opener.open(self.changelog_path)
|
|
71
|
+
|
|
72
|
+
elif choice == "4":
|
|
73
|
+
#==Check if the file path exists==
|
|
74
|
+
if not os.path.exists(self.changelog_path):
|
|
75
|
+
print(colored("\nSystem cannot find the changelog path.", "light_red"))
|
|
76
|
+
time.sleep(1.5)
|
|
77
|
+
else:
|
|
78
|
+
self.view_md(self.changelog_path)
|
|
79
|
+
|
|
80
|
+
else:
|
|
81
|
+
print("")
|
|
82
|
+
print(colored("Unknown option...", "light_red", attrs=["blink"]))
|
|
83
|
+
time.sleep(2)
|
|
84
|
+
|
|
85
|
+
def create_changelog(self):
|
|
86
|
+
os.system(self.clear_screen)
|
|
87
|
+
print(self.header.center(127, "="))
|
|
88
|
+
print("E. Back/abort\n")
|
|
89
|
+
|
|
90
|
+
version = input("Version: ")
|
|
91
|
+
if version.lower() == "e":
|
|
92
|
+
return
|
|
93
|
+
change_type = input("\nChange type: ")
|
|
94
|
+
|
|
95
|
+
entry = prompt("\nFirst entry: ")
|
|
96
|
+
|
|
97
|
+
comment = prompt("\nOptional comment: ")
|
|
98
|
+
|
|
99
|
+
choice = input("\nSave (Enter) Cancel (E).\nAdd further entries with 'Add new changes' in the menu.")
|
|
100
|
+
if choice.lower() == "e":
|
|
101
|
+
return
|
|
102
|
+
|
|
103
|
+
data = {
|
|
104
|
+
"date": self.today,
|
|
105
|
+
"version": version,
|
|
106
|
+
"type": change_type,
|
|
107
|
+
"changes": entry,
|
|
108
|
+
"comments": comment
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
template = self.template_loader("changelog_template.txt")
|
|
112
|
+
|
|
113
|
+
rendered = self.template_renderer(template, data)
|
|
114
|
+
|
|
115
|
+
mode = "a" if os.path.exists(self.changelog_path) else "w"
|
|
116
|
+
with open(self.changelog_path, mode, encoding="utf-8") as f:
|
|
117
|
+
f.write(rendered + "\n")
|
|
118
|
+
return
|
|
119
|
+
|
|
120
|
+
def update_changelog(self):
|
|
121
|
+
self.change_type = None
|
|
122
|
+
self.change = None
|
|
123
|
+
self.comment = None
|
|
124
|
+
while True:
|
|
125
|
+
os.system(self.clear_screen)
|
|
126
|
+
print("Main menu / Projects / Project menu / Changelogs / Add changes")
|
|
127
|
+
print(self.header.center(127, "="))
|
|
128
|
+
print("E. Back/abort")
|
|
129
|
+
print("O. Open templog for fixes.")
|
|
130
|
+
print("S. Review changes & save\n")
|
|
131
|
+
print("Version: " + colored(self.setting.get('version'), f"{self.color}"))
|
|
132
|
+
print(colored("\nChoose the change type:", f"{self.color}"))
|
|
133
|
+
print("1. Added")
|
|
134
|
+
print("2. Removed")
|
|
135
|
+
print("3. Fixed")
|
|
136
|
+
print("4. Changed")
|
|
137
|
+
print("5. Deprecated")
|
|
138
|
+
print("6. Security\n")
|
|
139
|
+
|
|
140
|
+
print(colored("Last changelog entry:", f"{self.color}"))
|
|
141
|
+
if not self.change_type:
|
|
142
|
+
print("No changes yet.\n")
|
|
143
|
+
else:
|
|
144
|
+
print("Type: " + self.change_type)
|
|
145
|
+
print("Change: " + self.change)
|
|
146
|
+
print("Comment: " + self.comment + "\n")
|
|
147
|
+
|
|
148
|
+
type_choice = input(f"{self.user_path}> ").strip()
|
|
149
|
+
|
|
150
|
+
#==Assign change types to keys==
|
|
151
|
+
type_map = {
|
|
152
|
+
"1": "Added",
|
|
153
|
+
"2": "Removed",
|
|
154
|
+
"3": "Fixed",
|
|
155
|
+
"4": "Changed",
|
|
156
|
+
"5": "Deprecated",
|
|
157
|
+
"6": "Security"
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
self.change_type = type_map.get(type_choice.lower())
|
|
161
|
+
|
|
162
|
+
if type_choice.lower() == "e":
|
|
163
|
+
if os.path.exists(self.templog_path):
|
|
164
|
+
os.remove(self.templog_path)
|
|
165
|
+
return
|
|
166
|
+
elif type_choice in ("1", "2", "3", "4", "5", "6"):
|
|
167
|
+
self.prepend_changes()
|
|
168
|
+
elif type_choice.lower() == "o":
|
|
169
|
+
Opener.open(self.templog_path)
|
|
170
|
+
elif type_choice.lower() == "s":
|
|
171
|
+
self.save_changes()
|
|
172
|
+
else:
|
|
173
|
+
print("")
|
|
174
|
+
print(colored("Unknown option...", "light_red", attrs=["blink"]))
|
|
175
|
+
time.sleep(2)
|
|
176
|
+
|
|
177
|
+
def prepend_changes(self):
|
|
178
|
+
header = f"### {self.change_type}"
|
|
179
|
+
|
|
180
|
+
existing = ""
|
|
181
|
+
if os.path.exists(self.templog_path):
|
|
182
|
+
with open(self.templog_path, "r", encoding="utf-8") as f:
|
|
183
|
+
existing = f.read()
|
|
184
|
+
|
|
185
|
+
#==Add the change type header if needed==
|
|
186
|
+
if header not in existing:
|
|
187
|
+
with open(self.templog_path, "a", encoding="utf-8") as f:
|
|
188
|
+
f.write(header + "\n")
|
|
189
|
+
|
|
190
|
+
while True:
|
|
191
|
+
os.system(self.clear_screen)
|
|
192
|
+
print(self.header.center(127, "="))
|
|
193
|
+
|
|
194
|
+
print("Chosen change type:")
|
|
195
|
+
print(colored(self.change_type, f"{self.color}"))
|
|
196
|
+
|
|
197
|
+
self.change = prompt("\nChange entry: ")
|
|
198
|
+
|
|
199
|
+
self.comment = prompt("\nOptional comment: ")
|
|
200
|
+
|
|
201
|
+
data = {
|
|
202
|
+
"changes": self.change,
|
|
203
|
+
"comments": self.comment
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
template = self.template_loader("entry_template.txt")
|
|
207
|
+
|
|
208
|
+
rendered = self.template_renderer(template, data)
|
|
209
|
+
|
|
210
|
+
with open(self.templog_path, "a", encoding="utf-8") as f:
|
|
211
|
+
f.write(rendered + "\n")
|
|
212
|
+
|
|
213
|
+
choice = input("\nSave & exit [E] or add more [Enter]? ")
|
|
214
|
+
if choice.lower() == "e":
|
|
215
|
+
return
|
|
216
|
+
|
|
217
|
+
def save_changes(self):
|
|
218
|
+
os.system(self.clear_screen)
|
|
219
|
+
print(self.header.center(127, "="))
|
|
220
|
+
print(colored("Please check the output file:", f"{self.color}", attrs=["underline"]))
|
|
221
|
+
templog_content = ""
|
|
222
|
+
if os.path.exists(self.templog_path):
|
|
223
|
+
with open(self.templog_path, "r", encoding="utf-8") as f:
|
|
224
|
+
templog_content = f.read()
|
|
225
|
+
print(colored(f"[version] - {self.today}\n", "magenta", attrs=["underline"]))
|
|
226
|
+
MARKDOWN = templog_content
|
|
227
|
+
console = Console()
|
|
228
|
+
md = Markdown(MARKDOWN)
|
|
229
|
+
console.print(md)
|
|
230
|
+
else:
|
|
231
|
+
print(colored("No changes added.", "light_red"))
|
|
232
|
+
time.sleep(2)
|
|
233
|
+
return
|
|
234
|
+
choice = input("\nContinue" + colored("[Enter]", f"{self.color}") + " or go back" + colored("[E] ", f"{self.color}"))
|
|
235
|
+
if choice.lower() == "e":
|
|
236
|
+
return
|
|
237
|
+
|
|
238
|
+
version = input("\nNew version number: ").strip()
|
|
239
|
+
|
|
240
|
+
print("\nWorking...")
|
|
241
|
+
|
|
242
|
+
header_data = {
|
|
243
|
+
"version": version,
|
|
244
|
+
"date": self.today
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
header_template = self.template_loader("header_template.txt")
|
|
248
|
+
header_rendered = self.template_renderer(header_template, header_data)
|
|
249
|
+
|
|
250
|
+
old_content = ""
|
|
251
|
+
if os.path.exists(self.changelog_path):
|
|
252
|
+
#==Make a duplicate of the old changelog==
|
|
253
|
+
log_name = f"Changelog-{self.today}.md"
|
|
254
|
+
project_folder = Path(self.setting.get('path'))
|
|
255
|
+
duplicate_path = project_folder / log_name
|
|
256
|
+
|
|
257
|
+
src = self.changelog_path
|
|
258
|
+
dest = duplicate_path
|
|
259
|
+
shutil.copy2(src,dest)
|
|
260
|
+
|
|
261
|
+
print("\nOld changelog backed up as: " + colored(f"{log_name}", f"{self.color}"))
|
|
262
|
+
|
|
263
|
+
with open(self.changelog_path, "r", encoding="utf-8") as f:
|
|
264
|
+
old_content = f.read()
|
|
265
|
+
|
|
266
|
+
combined = header_rendered + "\n" + templog_content + "\n" + old_content
|
|
267
|
+
|
|
268
|
+
with open(self.changelog_path, "w", encoding="utf-8") as f:
|
|
269
|
+
f.write(combined)
|
|
270
|
+
|
|
271
|
+
os.remove(self.templog_path)
|
|
272
|
+
|
|
273
|
+
#==Update ini files==
|
|
274
|
+
config = ConfigManager(f"{self.active_project}.ini")
|
|
275
|
+
config.update_project("version", version)
|
|
276
|
+
|
|
277
|
+
last_project = self.memory.get('last_project')
|
|
278
|
+
if last_project != self.active_project:
|
|
279
|
+
self.config.update_memory("ITEMS", "last_project", self.active_project)
|
|
280
|
+
|
|
281
|
+
print(colored("\nChangelog updated, returning...", f"{self.color}"))
|
|
282
|
+
time.sleep(2)
|
|
283
|
+
return
|
|
284
|
+
|
|
285
|
+
def view_md(self, log_path):
|
|
286
|
+
os.system(self.clear_screen)
|
|
287
|
+
print(self.header.center(127, "="))
|
|
288
|
+
print("")
|
|
289
|
+
|
|
290
|
+
log_content = ""
|
|
291
|
+
with open(log_path, "r", encoding="utf-8") as f:
|
|
292
|
+
log_content = f.read()
|
|
293
|
+
|
|
294
|
+
MARKDOWN = log_content
|
|
295
|
+
console = Console()
|
|
296
|
+
md = Markdown(MARKDOWN)
|
|
297
|
+
console.print(md)
|
|
298
|
+
|
|
299
|
+
input("\nReturn..." + colored("[Enter]", f"{self.color}"))
|
|
300
|
+
return
|
|
301
|
+
|
|
302
|
+
def template_loader(self, template_file):
|
|
303
|
+
template_path = self.config.templates_folder / template_file
|
|
304
|
+
|
|
305
|
+
with open(template_path, "r", encoding="utf-8") as f:
|
|
306
|
+
return f.read()
|
|
307
|
+
|
|
308
|
+
def template_renderer(self, template: str, data: dict):
|
|
309
|
+
for key, value in data.items():
|
|
310
|
+
template = template.replace(f"{{{{{key}}}}}", value)
|
|
311
|
+
|
|
312
|
+
return template
|
|
313
|
+
|
|
314
|
+
if __name__ == "__main__":
|
|
315
|
+
VersionLogic()
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
## [{{version}}] - {{date}}
|
DA/__init__.py
ADDED
|
File without changes
|
DA/intro.md
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Developer-Assistant
|
|
2
|
+
|
|
3
|
+
## Introduction
|
|
4
|
+
|
|
5
|
+
### Setting up your first project
|
|
6
|
+
For a dummy changelog to experiment with, navigate to `Main menu / Projects`, choose `Test-Project`, then choose option `4.` to start adjusting this projects paths.
|
|
7
|
+
|
|
8
|
+
The `Test-Project/` folder is included in the programs root folder for repo clones and is safe to experiment with. If you installed from an URL just make a `CHANGELOG.md` anywhere and point the `.ini` file to it.
|
|
9
|
+
|
|
10
|
+
Once configured, you can create as many changelog entries as you want by picking that project in the menu.
|
|
11
|
+
|
|
12
|
+
> *This quick guide is also at the end of the README for future reference*
|
|
13
|
+
|
|
14
|
+
### Using the program.
|
|
15
|
+
1. **What *not* to do**
|
|
16
|
+
|
|
17
|
+
Don't change the folder structure or modify variable names inside `.ini` files.
|
|
18
|
+
|
|
19
|
+
2. **Features and information**
|
|
20
|
+
|
|
21
|
+
**Your user data (`Templates/`, `Projects/`, `memory.ini`) is stored in standard locations:**
|
|
22
|
+
|
|
23
|
+
Windows: `C:\Users\...\AppData\Roaming\da-ui\`
|
|
24
|
+
|
|
25
|
+
Linux: `~/.config/da-ui/`
|
|
26
|
+
|
|
27
|
+
macOS: `~/Library/Application Support/da-ui/`
|
|
28
|
+
|
|
29
|
+
**The `da-ui/` folder has been created automatically. Updates won't overwrite it, only you can default it.
|
|
30
|
+
You can access its content quickly when going to: `Main menu / Settings`**
|
|
31
|
+
|
|
32
|
+
- *Customizable templates*
|
|
33
|
+
|
|
34
|
+
Explore the `Templates/` folder and modify the template contents to your liking - **just avoid changing the `{{placeholder}}` names**.
|
|
35
|
+
|
|
36
|
+
- *Linked projects all in one place*
|
|
37
|
+
|
|
38
|
+
The `Projects/` folder holds the `.ini` files you create when starting a new project with the program.
|
|
39
|
+
|
|
40
|
+
- *Safe changelog updates*
|
|
41
|
+
|
|
42
|
+
Before applying any changes, your previous `CHANGELOG.md` is automatically backed up into your project folder.
|
|
43
|
+
New changes are first written to a temporary file and only applied to the real changelog after you confirm them.
|
|
44
|
+
This ensures your existing changelog is never overwritten or corrupted, and you always have a fallback copy.
|
|
45
|
+
|
|
46
|
+
- *Ease of navigation*
|
|
47
|
+
|
|
48
|
+
You can access files/folders and configuration straight from the menus, so you shouldn't find yourself searching through the program's directory or even your local user data very often.
|
|
49
|
+
|
|
50
|
+
- *Configuration*
|
|
51
|
+
|
|
52
|
+
The `memory.ini` file does exactly what you'd expect, it features:
|
|
53
|
+
|
|
54
|
+
> Last project
|
|
55
|
+
|
|
56
|
+
> Pinned projects
|
|
57
|
+
|
|
58
|
+
> Custom colour
|
|
59
|
+
|
|
60
|
+
Last project gets updated automatically, the rest are up to you.
|
|
61
|
+
|
|
62
|
+
- *`Ctrl+C` works everywhere to quickly exit DA.*
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: developer-assistant
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: A lightweight TUI designed to simplify formatting of Markdown changelogs.
|
|
5
|
+
Requires-Python: >=3.14
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Requires-Dist: platformdirs>=4.9.4
|
|
9
|
+
Requires-Dist: prompt-toolkit>=3.0.52
|
|
10
|
+
Requires-Dist: rich>=14.3.2
|
|
11
|
+
Requires-Dist: termcolor>=3.3.0
|
|
12
|
+
Dynamic: license-file
|
|
13
|
+
|
|
14
|
+
# Developer Assistant
|
|
15
|
+
|
|
16
|
+
> **Setup info:** Start with [SETUP](./SETUP.md) to get started.
|
|
17
|
+
|
|
18
|
+
> **Latest changes:** Yes, I keep a [CHANGELOG](./CHANGELOG.md)
|
|
19
|
+
|
|
20
|
+
**Requirements:** Python 3.13 or later.
|
|
21
|
+
|
|
22
|
+
**Cross-platform:** Windows, Linux, macOS(unverified)
|
|
23
|
+
|
|
24
|
+
## Appearance
|
|
25
|
+
|
|
26
|
+
### Coloured, easy-to-use menus
|
|
27
|
+

|
|
28
|
+
|
|
29
|
+
### Beautiful changelog previews
|
|
30
|
+
Preview your Markdown changelogs directly in the terminal with Rich rendering:
|
|
31
|
+
|
|
32
|
+

|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
## Introduction
|
|
36
|
+
|
|
37
|
+
### What does this tool do?
|
|
38
|
+
Developer Assistant is a lightweight TUI tool for automating and managing your changelogs. You can customize the templates to match your existing format, and use DA as a central hub to access every changelog and project folder you maintain.
|
|
39
|
+
|
|
40
|
+
You can manage as many projects as you like. Each project gets its own `.ini` file, created automatically through the menu based on the information you provide. These act as links that tell DA where your changelogs are and what's the last version number.
|
|
41
|
+
|
|
42
|
+
Your files are kept safe at all times. Before adding new changes, your existing `CHANGELOG.md` is automatically backed up. While editing, all changes are written to a temporary file first and only applied to the real changelog once you confirm them.
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
### Using the program.
|
|
46
|
+
1. **What *not* to do**
|
|
47
|
+
|
|
48
|
+
Don't change the folder structure or modify variable names inside `.ini` files.
|
|
49
|
+
|
|
50
|
+
2. **Features and information**
|
|
51
|
+
|
|
52
|
+
**The user's data (`Templates/`, `Projects/`, `memory.ini`) is stored in standard locations:**
|
|
53
|
+
|
|
54
|
+
Windows: `C:\Users\...\AppData\Roaming\da-ui\`
|
|
55
|
+
|
|
56
|
+
Linux: `~/.config/da-ui/`
|
|
57
|
+
|
|
58
|
+
macOS: `~/Library/Application Support/da-ui/`
|
|
59
|
+
|
|
60
|
+
The `da-ui/` folder will be created automatically.
|
|
61
|
+
|
|
62
|
+
You can access its content quickly when going to: `Main menu / Settings`
|
|
63
|
+
|
|
64
|
+
- *Customizable templates*
|
|
65
|
+
|
|
66
|
+
In the **local** `Templates/` folder you can modify the template contents to your liking - **just avoid changing the `{{placeholder}}` names**.
|
|
67
|
+
|
|
68
|
+
- *Linked projects all in one place*
|
|
69
|
+
|
|
70
|
+
The `Projects/` folder holds the `.ini` files you create when starting a new project with DA.
|
|
71
|
+
|
|
72
|
+
- *Safe changelog updates*
|
|
73
|
+
|
|
74
|
+
Before applying any changes, your previous `CHANGELOG.md` is automatically backed up into your project folder.
|
|
75
|
+
New changes are first written to a temporary file and only applied to the real changelog after you confirm them.
|
|
76
|
+
This ensures your existing changelog is never overwritten or corrupted, and you always have a fallback copy.
|
|
77
|
+
If the temporary changelog is present on startup you are prompted to remove or keep it.
|
|
78
|
+
|
|
79
|
+
- *Ease of navigation*
|
|
80
|
+
|
|
81
|
+
You can access files/folders and configuration straight from the menus, so you shouldn't find yourself searching through the program's directory or even your local user data very often.
|
|
82
|
+
|
|
83
|
+
- *Configuration*
|
|
84
|
+
|
|
85
|
+
The `memory.ini` file does exactly what you'd expect, it features:
|
|
86
|
+
|
|
87
|
+
> Last project
|
|
88
|
+
|
|
89
|
+
> Pinned projects
|
|
90
|
+
|
|
91
|
+
> Custom colour
|
|
92
|
+
|
|
93
|
+
Last project gets updated automatically, the rest are up to you.
|
|
94
|
+
|
|
95
|
+
- *`Ctrl+C` works everywhere to quickly exit DA.*
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
### Documentation.
|
|
99
|
+
Documentation includes SYSTEM STRUCTURE.txt, example -and default files. If you ever need to replace a file, the example/default files can be used for that.
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
### Setting up your first project
|
|
103
|
+
For a dummy changelog to experiment with, navigate to `Main menu / Projects`, choose `Test-Project`, then choose option `4.` to start adjusting this projects paths.
|
|
104
|
+
|
|
105
|
+
The `Test-Project/` folder is included in the programs root folder for repo clones and is safe to experiment with. If you installed from an URL just make a `CHANGELOG.md` anywhere and point the `.ini` file to it.
|
|
106
|
+
|
|
107
|
+
Once configured, you can create as many changelog entries as you want by picking that project in the menu.
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
## Updating DA
|
|
111
|
+
Two possibilities, depending on how you installed.
|
|
112
|
+
|
|
113
|
+
### 1. Installed directly from GitHub URL
|
|
114
|
+
A. **Using uv:**
|
|
115
|
+
1. `uv tool install git+https://github.com/Ivory-Hubert/Developer-Assistant`
|
|
116
|
+
|
|
117
|
+
B. **Using pip:**
|
|
118
|
+
1. `pip install --upgrade git+https://github.com/Ivory-Hubert/Developer-Assistant`
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
### 2. Installed from a local clone
|
|
122
|
+
*Run all terminal commands in the repo folder*
|
|
123
|
+
|
|
124
|
+
A. **Using uv:**
|
|
125
|
+
1. `git pull`
|
|
126
|
+
2. `uv tool install .`
|
|
127
|
+
|
|
128
|
+
B. **Using pip:**
|
|
129
|
+
1. `git pull`
|
|
130
|
+
2. `pip install .`
|
|
131
|
+
|
|
132
|
+
C. **No install, running from repo root:**
|
|
133
|
+
|
|
134
|
+
Just `git pull`
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
DA/Interface.py,sha256=z0P7l0FvcvuVOIw5_rYYyAYLKYyfk5BiXsg4Em7kDFs,8693
|
|
2
|
+
DA/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
|
+
DA/intro.md,sha256=e8rnWZF6USGyHC-tOEbnd2cTFUPU9g-kEs7LK7EokYo,2337
|
|
4
|
+
DA/Modules/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
|
+
DA/Modules/config_manager.py,sha256=_fR_D24uDKaHd2k1k5ehzxOjyYlUzgxh-xJe6nmWYUA,4518
|
|
6
|
+
DA/Modules/opener.py,sha256=Ym8BRZQyVjAvBuMBPXhAomfvzdTfFcfqo_3Rbq0Aewo,847
|
|
7
|
+
DA/Modules/projects_manager.py,sha256=RtJOdA2u5WxgHraJJRM_0Iwncab0Vw6Xo2Q5UrHkuMg,3964
|
|
8
|
+
DA/Modules/version_logic.py,sha256=0et-0DuNtjXsdU0OKp7qh_VRBLAlPYG2hfFRz7ky7Ac,11177
|
|
9
|
+
DA/Templates/changelog_template.txt,sha256=6Gpm7KdEpD-gWz7_SehQ7CjryAg218c-uAROIzRnw0g,74
|
|
10
|
+
DA/Templates/entry_template.txt,sha256=o8KvPZPn0AKKks-DcPdMdX4xhR3bvZsXImJfGE7B14c,32
|
|
11
|
+
DA/Templates/header_template.txt,sha256=KUZHDXu4NOIee-aOwkquN74vy1T6Z64a398z_B3JCdE,28
|
|
12
|
+
DA/default/Test-Project.ini,sha256=UhDpxDpHQ4ojbxwFsbhSFfj_XGUuuRvRObIPpe0OS-U,163
|
|
13
|
+
DA/default/default-changelog.md,sha256=PJKhTupBCte6ZSnGroVnDm9qmz68XYvkgmwILoZ4pBY,73
|
|
14
|
+
DA/default/default-memory.ini,sha256=yuFalOgOplzodZffCIJQWyxvbd-NoftCjRfpfiEVGtw,112
|
|
15
|
+
developer_assistant-0.2.0.dist-info/licenses/LICENSE,sha256=g9ya0zNLIfzVTAr5hNvTsy1qE9Toxwnhcw0j7FHPpFg,1083
|
|
16
|
+
developer_assistant-0.2.0.dist-info/METADATA,sha256=yV6eU-bKGDRKLx3n-DFmqNmcEfqErysAc3re-1GhLB8,4697
|
|
17
|
+
developer_assistant-0.2.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
18
|
+
developer_assistant-0.2.0.dist-info/entry_points.txt,sha256=-yfEhFvSV4Ms9VmDB8Tw_7Qj1846_cIUjqD9qZEXeAM,44
|
|
19
|
+
developer_assistant-0.2.0.dist-info/top_level.txt,sha256=iFBaOpbsgyNLDxxwfykK-xBJlzOUhDimcmpF1FUUZ6o,3
|
|
20
|
+
developer_assistant-0.2.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ivori Huobolainen
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
6
|
+
|
|
7
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
DA
|