twd-m4sc0 2.0.3__py3-none-any.whl → 3.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
twd/crud.py DELETED
@@ -1,104 +0,0 @@
1
- import os
2
- import json
3
- import hashlib
4
- import time
5
- import logging
6
- from collections import OrderedDict
7
-
8
- log = logging.getLogger("log")
9
- error_log = logging.getLogger("error")
10
-
11
-
12
- def create_alias_id():
13
- data = str(time.time()) + str(os.urandom(16))
14
- return hashlib.sha256(data.encode()).hexdigest()[:12]
15
-
16
-
17
- def get_data_file(config):
18
- return os.path.expanduser(config.get("data_file", "~/.twd/data"))
19
-
20
-
21
- def ensure_data_file_exists(config):
22
- data_file = get_data_file(config)
23
- if not os.path.exists(data_file):
24
- try:
25
- with open(data_file, "w") as f:
26
- json.dump({}, f)
27
- log.info(f"Created data file at {data_file}")
28
- except OSError as e:
29
- error_log.error(f"Error creating data file: {e}")
30
-
31
-
32
- def load_data(config):
33
- data_file = get_data_file(config)
34
- if not os.path.exists(data_file):
35
- ensure_data_file_exists(config)
36
- try:
37
- with open(data_file, "r") as f:
38
- data = json.load(f)
39
- log.info(f"Loaded data from {data_file}")
40
- return data
41
- except json.JSONDecodeError as e:
42
- error_log.error(f"Error reading data file: {e}")
43
- return {}
44
- except OSError as e:
45
- error_log.error(f"Error reading data file: {e}")
46
- return {}
47
-
48
-
49
- def save_data(config, data):
50
- data_file = get_data_file(config)
51
- try:
52
- sorted_data = OrderedDict(
53
- sorted(data.items(), key=lambda item: item[1]["alias"])
54
- )
55
- with open(data_file, "w") as f:
56
- json.dump(sorted_data, f, indent=4)
57
- log.info(f"Saved data to {data_file}")
58
- except OSError as e:
59
- error_log.error(f"Error writing to data file: {e}")
60
-
61
-
62
- def create_entry(config, data, path, alias=None):
63
- alias_id = create_alias_id()
64
- data[alias_id] = {
65
- "path": path,
66
- "alias": alias if alias else "no_alias",
67
- "created_at": time.time(),
68
- }
69
- save_data(config, data)
70
- log.info(f"Created new entry with alias_id '{alias_id}' and path '{path}'")
71
- return alias_id
72
-
73
-
74
- def delete_entry(config, data, entry_id):
75
- if entry_id in data:
76
- del data[entry_id]
77
- save_data(config, data)
78
- log.info(f"Deleted entry with alias_id '{entry_id}'")
79
- else:
80
- error_log.error(f"Entry ID '{entry_id}' not found")
81
- raise KeyError(f"Entry ID {entry_id} not found")
82
-
83
-
84
- def update_entry(config, data, entry_id, entry):
85
- if entry_id in data:
86
- data[entry_id] = entry
87
- save_data(config, data)
88
- log.info(f"Updated entry with alias_id '{entry_id}'")
89
- else:
90
- error_log.error(f"Entry ID '{entry_id}' not found")
91
- raise KeyError(f"Entry ID {entry_id} not found")
92
-
93
-
94
- def delete_data_file(config):
95
- data_file = get_data_file(config)
96
- if os.path.exists(data_file):
97
- try:
98
- os.remove(data_file)
99
- log.info(f"Deleted data file at {data_file}")
100
- except OSError as e:
101
- error_log.error(f"Error deleting data file: {e}")
102
- raise
103
- else:
104
- error_log.error("No data file found to delete")
twd/logger.py DELETED
@@ -1,47 +0,0 @@
1
- import logging
2
- import os
3
- from logging.handlers import RotatingFileHandler
4
-
5
-
6
- def setup_logger(config):
7
- """Set up loggers based on the configuration provided."""
8
- log_file = os.path.expanduser(config.get("log_file"))
9
- error_file = os.path.expanduser(config.get("error_file"))
10
- log_level = config.get("log_level", "INFO").upper()
11
- max_bytes = config.get("log_max_bytes", 5 * 1024 * 1024) # Default 5MB
12
- backup_count = config.get("log_backup_count", 3) # Default 3 backup files
13
-
14
- os.makedirs(os.path.dirname(log_file), exist_ok=True)
15
- os.makedirs(os.path.dirname(error_file), exist_ok=True)
16
-
17
- # General log configuration
18
- log_formatter = logging.Formatter(
19
- config.get("log_format", "%(asctime)s - %(levelname)s - %(message)s")
20
- )
21
-
22
- # Avoid duplicate handlers
23
- logger = logging.getLogger("log")
24
- if not logger.hasHandlers():
25
- logger.setLevel(log_level)
26
- log_handler = RotatingFileHandler(
27
- log_file, maxBytes=max_bytes, backupCount=backup_count
28
- )
29
- log_handler.setFormatter(log_formatter)
30
- logger.addHandler(log_handler)
31
-
32
- # Error log configuration
33
- error_logger = logging.getLogger("error")
34
- if not error_logger.hasHandlers():
35
- error_logger.setLevel(logging.ERROR)
36
- error_handler = RotatingFileHandler(
37
- error_file, maxBytes=max_bytes, backupCount=backup_count
38
- )
39
- error_handler.setFormatter(log_formatter)
40
- error_logger.addHandler(error_handler)
41
-
42
- return logger, error_logger
43
-
44
-
45
- def initialize_logging(config):
46
- setup_logger(config)
47
-
twd/screen.py DELETED
@@ -1,201 +0,0 @@
1
- import curses
2
- import time
3
- import os
4
- from . import crud
5
- import logging
6
-
7
- log = logging.getLogger("log")
8
- error_log = logging.getLogger("error")
9
-
10
- CONFIG = None
11
- DIRS = None
12
- filtered_DIRS = None
13
- search_query = ""
14
- original_DIRS = None
15
-
16
-
17
- def draw_hr(stdscr, y, mode=None):
18
- _, max_cols = stdscr.getmaxyx()
19
- mode = mode if mode is not None else curses.A_NORMAL
20
- stdscr.addstr(y, 1, "─" * (max_cols - 2), mode)
21
-
22
-
23
- def filter_dirs_by_search(query):
24
- global filtered_DIRS
25
- filtered_DIRS = (
26
- {k: v for k, v in DIRS.items() if query.lower() in v["alias"].lower()}
27
- if query
28
- else DIRS
29
- )
30
-
31
-
32
- def display_select_screen(stdscr):
33
- global search_query, filtered_DIRS, original_DIRS
34
- selected_entry = 0
35
- pre_selected_path = None
36
- confirm_mode = False
37
- action = None
38
- search_mode = False
39
- post_search_mode = False
40
-
41
- running = True
42
-
43
- while running:
44
- max_items = len(filtered_DIRS)
45
- stdscr.clear()
46
-
47
- # Border setup
48
- height, width = stdscr.getmaxyx()
49
- stdscr.addstr(0, 0, "╭")
50
- stdscr.addstr(0, 1, "─" * (width - 2))
51
- stdscr.addstr(0, width - 1, "╮")
52
- stdscr.addstr(height - 1, 0, "╰")
53
- stdscr.addstr(height - 1, 1, "─" * (width - 2))
54
- stdscr.addstr(height - 2, width - 1, "╯")
55
- for i in range(1, height - 1):
56
- stdscr.addstr(i, 0, "│")
57
- stdscr.addstr(i, width - 1, "│")
58
-
59
- inner_height = height - 2
60
- inner_width = width - 2
61
- stdscr.addstr(1, 1, f"Current directory: {os.getcwd()}")
62
-
63
- draw_hr(stdscr, 2)
64
-
65
- # Header
66
- max_alias_len = max(
67
- max(len(entry["alias"]) for entry in filtered_DIRS.values()), 5
68
- )
69
- max_path_len = max(
70
- max(len(entry["path"]) for entry in filtered_DIRS.values()), 4
71
- )
72
- max_id_len = max(max(len(alias_id) for alias_id in filtered_DIRS.keys()), 2)
73
-
74
- alias_col = max_alias_len + 2
75
- id_col = max_id_len + 2
76
- path_col = max_path_len
77
-
78
- header_text = f"{'ALIAS'.ljust(alias_col)}{'ID'.ljust(id_col)}{'PATH'.ljust(path_col)} CREATED AT"
79
- stdscr.addstr(3, 1, header_text[:inner_width])
80
-
81
- draw_hr(stdscr, 4)
82
-
83
- # List entries
84
- line_start = 5
85
- for entry_id, entry in enumerate(filtered_DIRS.values()):
86
- if line_start >= inner_height - 5:
87
- break
88
- alias = entry["alias"].ljust(max_alias_len)
89
- path = entry["path"].ljust(max_path_len)
90
- alias_id = list(filtered_DIRS.keys())[entry_id].ljust(max_id_len)
91
- created_at = time.strftime(
92
- "%Y-%m-%d %H:%M:%S", time.localtime(entry["created_at"])
93
- )
94
-
95
- line_text = f"{alias} {alias_id} {path} {created_at}"
96
- if entry_id == selected_entry:
97
- stdscr.addstr(line_start, 1, line_text[:inner_width], curses.A_REVERSE)
98
- pre_selected_path = entry["path"]
99
- else:
100
- stdscr.addstr(line_start, 1, line_text[:inner_width])
101
-
102
- line_start += 1
103
-
104
- # Controls
105
- controls_y = height - 5
106
- draw_hr(stdscr, controls_y, curses.A_DIM)
107
- controls_text = (
108
- "ctrls: enter=select"
109
- if search_mode
110
- else "ctrls: ↑/k=up ↓/j=down enter=select d/backspace=delete q=exit search s=search"
111
- if post_search_mode
112
- else "ctrls: ↑/k=up ↓/j=down enter=select d/backspace=delete q=quit s=search"
113
- )
114
- stdscr.addstr(controls_y + 1, 1, controls_text, curses.A_DIM)
115
-
116
- # Action area
117
- action_area_y = height - 3
118
- draw_hr(stdscr, action_area_y)
119
-
120
- if search_mode:
121
- stdscr.addstr(action_area_y + 1, 1, f"Search: {search_query}")
122
- elif confirm_mode and action == "delete":
123
- entry = filtered_DIRS[list(filtered_DIRS.keys())[selected_entry]]
124
- stdscr.addstr(
125
- action_area_y + 1,
126
- 1,
127
- f"Delete entry '{entry['alias']}' ({entry['path']})? [enter/q]",
128
- )
129
- elif pre_selected_path:
130
- stdscr.addstr(
131
- action_area_y + 1,
132
- 1,
133
- f"Command: cd {os.path.abspath(os.path.expanduser(pre_selected_path))}",
134
- )
135
-
136
- stdscr.refresh()
137
-
138
- # Handle key events
139
- key = stdscr.getch()
140
-
141
- if search_mode:
142
- if key == ord("\n"):
143
- search_mode = False
144
- post_search_mode = True
145
- elif key == curses.KEY_BACKSPACE or key == 127:
146
- search_query = search_query[:-1]
147
- filter_dirs_by_search(search_query)
148
- else:
149
- search_query += chr(key)
150
- filter_dirs_by_search(search_query)
151
- elif post_search_mode:
152
- if key == ord("q") or key == 27: # 'q' or 'esc'
153
- filtered_DIRS = original_DIRS
154
- post_search_mode = False
155
- elif key == curses.KEY_UP or key == ord("k"):
156
- selected_entry = max(0, selected_entry - 1)
157
- elif key == curses.KEY_DOWN or key == ord("j"):
158
- selected_entry = min(max_items - 1, selected_entry + 1)
159
- elif key == ord("\n"):
160
- selected_entry_id = list(filtered_DIRS.keys())[selected_entry]
161
- return filtered_DIRS[selected_entry_id]
162
- elif confirm_mode:
163
- if key == ord("\n") and action == "delete":
164
- selected_entry_id = list(filtered_DIRS.keys())[selected_entry]
165
- data = crud.load_data(CONFIG)
166
- try:
167
- crud.delete_entry(CONFIG, data, selected_entry_id)
168
- except KeyError:
169
- error_log.error(f"Entry ID {selected_entry_id} not found")
170
- del filtered_DIRS[selected_entry_id]
171
- if selected_entry >= len(filtered_DIRS):
172
- selected_entry = max(len(filtered_DIRS) - 1, 0)
173
- confirm_mode = False
174
- else:
175
- confirm_mode = False
176
- else:
177
- if key == curses.KEY_UP or key == ord("k"):
178
- selected_entry = (selected_entry - 1) % max_items
179
- elif key == curses.KEY_DOWN or key == ord("j"):
180
- selected_entry = (selected_entry + 1) % max_items
181
- elif key == ord("\n"):
182
- selected_entry_id = list(filtered_DIRS.keys())[selected_entry]
183
- return filtered_DIRS[selected_entry_id]
184
- elif key == ord("q"):
185
- return None
186
- elif key == ord("d") or key == curses.KEY_BACKSPACE:
187
- confirm_mode = True
188
- action = "delete"
189
- elif key == ord("s"):
190
- search_mode = True
191
- selected_entry = 0
192
-
193
-
194
- def display_select(config, dirs):
195
- global CONFIG, DIRS, filtered_DIRS, search_query, original_DIRS
196
- CONFIG = config
197
- DIRS = dirs
198
- filtered_DIRS = DIRS
199
- original_DIRS = DIRS
200
- search_query = ""
201
- return curses.wrapper(display_select_screen)