filertk 1.0.0__tar.gz
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.
- filertk-1.0.0/LICENSE +21 -0
- filertk-1.0.0/PKG-INFO +74 -0
- filertk-1.0.0/README.md +59 -0
- filertk-1.0.0/pyproject.toml +24 -0
- filertk-1.0.0/setup.cfg +4 -0
- filertk-1.0.0/src/filertk/__init__.py +0 -0
- filertk-1.0.0/src/filertk/command_reader.py +187 -0
- filertk-1.0.0/src/filertk/file_manager.py +335 -0
- filertk-1.0.0/src/filertk/main.py +143 -0
- filertk-1.0.0/src/filertk.egg-info/PKG-INFO +74 -0
- filertk-1.0.0/src/filertk.egg-info/SOURCES.txt +13 -0
- filertk-1.0.0/src/filertk.egg-info/dependency_links.txt +1 -0
- filertk-1.0.0/src/filertk.egg-info/entry_points.txt +2 -0
- filertk-1.0.0/src/filertk.egg-info/requires.txt +1 -0
- filertk-1.0.0/src/filertk.egg-info/top_level.txt +1 -0
filertk-1.0.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Dylan Alves Santos
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
filertk-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: filertk
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: package used for rewriting certain aspects of text in files
|
|
5
|
+
Author-email: Dylan Santos <dasantos726@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/dylanasantos/filertk
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Operating System :: OS Independent
|
|
10
|
+
Requires-Python: >=3.14
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Requires-Dist: colorama>=0.4.6
|
|
14
|
+
Dynamic: license-file
|
|
15
|
+
|
|
16
|
+
# filertk 1.0.0
|
|
17
|
+
Filertk is a simple terminal-based system that allows for quick basic file rewrite instructions that would take excessive work by hand, such as renaming variables.\
|
|
18
|
+
\
|
|
19
|
+
**To activate:** simply write "filertk" within your terminal and paste in the file directory you want to modify. Afterwards input your commands when requested.\
|
|
20
|
+
- All file modifications are located in a directory inside of <ins>filertk\results</ins>
|
|
21
|
+
|
|
22
|
+
## Commands
|
|
23
|
+
filertk has 17 different commands displayed at each launch of the program. These commands are used to specify the desired modifications to the file or to the results directory.
|
|
24
|
+
|
|
25
|
+
> [!NOTE]
|
|
26
|
+
> The general order of commands used does not matter. However, the identifiers you provide are a first-come-first-serve basis.
|
|
27
|
+
|
|
28
|
+
### Identifiers
|
|
29
|
+
Identifiers are used to inform filertk what exactly you are referencing in your commands, such as the text you want to find or replace.
|
|
30
|
+
|
|
31
|
+
- **Name Identifiers "":** Used for specifying the text to match, rewrite, or exclude
|
|
32
|
+
- **Range Identifiers (x-y):** Used for specifying the lines from start-end you want to either exclude or include
|
|
33
|
+
<br><br>
|
|
34
|
+
|
|
35
|
+
### Special Commands
|
|
36
|
+
- **exit:** closes filertk program
|
|
37
|
+
- **clear_res:** clears the entire file results directory, first displays all files being deleted and requests for permission
|
|
38
|
+
- **clear "filename.ext":** clears the specified file from results directory, first displays file direcotry and requests for permission
|
|
39
|
+
<br><br>
|
|
40
|
+
|
|
41
|
+
### Reader Commands
|
|
42
|
+
Commands used to inform filertk how the text to match should be interpereted.
|
|
43
|
+
|
|
44
|
+
> [!NOTE]
|
|
45
|
+
> Reader Commands do not handle casing sensitivity, only character matching, refer to Casing Commands for more info.
|
|
46
|
+
|
|
47
|
+
- **match "text":** finds any words that contain the text
|
|
48
|
+
- **exact "text":** only finds words that contain the exact text
|
|
49
|
+
- **excluding "text" or (start-end):** excludes any matches of the word found (only used in match type) or ignores lines specified
|
|
50
|
+
- **from (start-end):** will only read/modify the lines specified (start and end are inclusive)
|
|
51
|
+
<br><br>
|
|
52
|
+
|
|
53
|
+
### Writer Commands
|
|
54
|
+
Commands used to inform filertk how the matches found should be rewritten.
|
|
55
|
+
|
|
56
|
+
- **with "text":** removes and replaces the new text only with what matched (ex: demo_1 -> new_1)
|
|
57
|
+
- **replace "text":** removes the entire text with the match found and inserts the new text (ex: demo_1 -> new)
|
|
58
|
+
- **r_append "text":** appends the new text to the end of the match (ex: demo_1 -> demo_1new)
|
|
59
|
+
- **f_append "text":** appends the new text to the front of the match (ex: demo_1 -> newdemo_1)
|
|
60
|
+
<br><br>
|
|
61
|
+
|
|
62
|
+
### Casing Commands
|
|
63
|
+
Commands used to inform filertk how the text casing should be handled.
|
|
64
|
+
|
|
65
|
+
- **!ignore_case:** ignores the casing of matches, any and all capitalizations are treated the same via the reader but are replaced by the exact casing of the writer identifier input.
|
|
66
|
+
- **!keep_case:** will attempt to copy the exact casing via index-by-index (this is only recommended for matches and writes of the same size), will automatically lowercase and uppercase all chars regardless if reader and writer identifier sizes match.<br>
|
|
67
|
+
- **!camel_case:** given the exact casing of the user's writer identifier, will replace all words with said input UNLESS the match if full capitalized or lowercased. If so, it will match the casing.
|
|
68
|
+
|
|
69
|
+
### File Copy Command
|
|
70
|
+
Commands used on how the file modifications should be handled.
|
|
71
|
+
|
|
72
|
+
- **~h_copy:** creates a copy of the exact file name inside of the results directory. (filertk defaults to this, but will display a warning if defaulted)
|
|
73
|
+
- **~modify:** modifies the original file specified, creates an emergency backup inside of the results directory if the output is unwanted.
|
|
74
|
+
- **~s_copy "filename.ext":** creates a copy of the file with a custom name provided inside of the results directory.
|
filertk-1.0.0/README.md
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# filertk 1.0.0
|
|
2
|
+
Filertk is a simple terminal-based system that allows for quick basic file rewrite instructions that would take excessive work by hand, such as renaming variables.\
|
|
3
|
+
\
|
|
4
|
+
**To activate:** simply write "filertk" within your terminal and paste in the file directory you want to modify. Afterwards input your commands when requested.\
|
|
5
|
+
- All file modifications are located in a directory inside of <ins>filertk\results</ins>
|
|
6
|
+
|
|
7
|
+
## Commands
|
|
8
|
+
filertk has 17 different commands displayed at each launch of the program. These commands are used to specify the desired modifications to the file or to the results directory.
|
|
9
|
+
|
|
10
|
+
> [!NOTE]
|
|
11
|
+
> The general order of commands used does not matter. However, the identifiers you provide are a first-come-first-serve basis.
|
|
12
|
+
|
|
13
|
+
### Identifiers
|
|
14
|
+
Identifiers are used to inform filertk what exactly you are referencing in your commands, such as the text you want to find or replace.
|
|
15
|
+
|
|
16
|
+
- **Name Identifiers "":** Used for specifying the text to match, rewrite, or exclude
|
|
17
|
+
- **Range Identifiers (x-y):** Used for specifying the lines from start-end you want to either exclude or include
|
|
18
|
+
<br><br>
|
|
19
|
+
|
|
20
|
+
### Special Commands
|
|
21
|
+
- **exit:** closes filertk program
|
|
22
|
+
- **clear_res:** clears the entire file results directory, first displays all files being deleted and requests for permission
|
|
23
|
+
- **clear "filename.ext":** clears the specified file from results directory, first displays file direcotry and requests for permission
|
|
24
|
+
<br><br>
|
|
25
|
+
|
|
26
|
+
### Reader Commands
|
|
27
|
+
Commands used to inform filertk how the text to match should be interpereted.
|
|
28
|
+
|
|
29
|
+
> [!NOTE]
|
|
30
|
+
> Reader Commands do not handle casing sensitivity, only character matching, refer to Casing Commands for more info.
|
|
31
|
+
|
|
32
|
+
- **match "text":** finds any words that contain the text
|
|
33
|
+
- **exact "text":** only finds words that contain the exact text
|
|
34
|
+
- **excluding "text" or (start-end):** excludes any matches of the word found (only used in match type) or ignores lines specified
|
|
35
|
+
- **from (start-end):** will only read/modify the lines specified (start and end are inclusive)
|
|
36
|
+
<br><br>
|
|
37
|
+
|
|
38
|
+
### Writer Commands
|
|
39
|
+
Commands used to inform filertk how the matches found should be rewritten.
|
|
40
|
+
|
|
41
|
+
- **with "text":** removes and replaces the new text only with what matched (ex: demo_1 -> new_1)
|
|
42
|
+
- **replace "text":** removes the entire text with the match found and inserts the new text (ex: demo_1 -> new)
|
|
43
|
+
- **r_append "text":** appends the new text to the end of the match (ex: demo_1 -> demo_1new)
|
|
44
|
+
- **f_append "text":** appends the new text to the front of the match (ex: demo_1 -> newdemo_1)
|
|
45
|
+
<br><br>
|
|
46
|
+
|
|
47
|
+
### Casing Commands
|
|
48
|
+
Commands used to inform filertk how the text casing should be handled.
|
|
49
|
+
|
|
50
|
+
- **!ignore_case:** ignores the casing of matches, any and all capitalizations are treated the same via the reader but are replaced by the exact casing of the writer identifier input.
|
|
51
|
+
- **!keep_case:** will attempt to copy the exact casing via index-by-index (this is only recommended for matches and writes of the same size), will automatically lowercase and uppercase all chars regardless if reader and writer identifier sizes match.<br>
|
|
52
|
+
- **!camel_case:** given the exact casing of the user's writer identifier, will replace all words with said input UNLESS the match if full capitalized or lowercased. If so, it will match the casing.
|
|
53
|
+
|
|
54
|
+
### File Copy Command
|
|
55
|
+
Commands used on how the file modifications should be handled.
|
|
56
|
+
|
|
57
|
+
- **~h_copy:** creates a copy of the exact file name inside of the results directory. (filertk defaults to this, but will display a warning if defaulted)
|
|
58
|
+
- **~modify:** modifies the original file specified, creates an emergency backup inside of the results directory if the output is unwanted.
|
|
59
|
+
- **~s_copy "filename.ext":** creates a copy of the file with a custom name provided inside of the results directory.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "filertk"
|
|
3
|
+
version = "1.0.0"
|
|
4
|
+
dependencies = [
|
|
5
|
+
"colorama>=0.4.6"
|
|
6
|
+
]
|
|
7
|
+
authors = [
|
|
8
|
+
{ name="Dylan Santos", email="dasantos726@gmail.com" },
|
|
9
|
+
]
|
|
10
|
+
description = "package used for rewriting certain aspects of text in files"
|
|
11
|
+
readme = "README.md"
|
|
12
|
+
requires-python = ">=3.14"
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Programming Language :: Python :: 3",
|
|
15
|
+
"Operating System :: OS Independent",
|
|
16
|
+
]
|
|
17
|
+
license = "MIT"
|
|
18
|
+
license-files = ["LICEN[CS]E*"]
|
|
19
|
+
|
|
20
|
+
[project.scripts]
|
|
21
|
+
filertk = "filertk.main:main"
|
|
22
|
+
|
|
23
|
+
[project.urls]
|
|
24
|
+
Homepage = "https://github.com/dylanasantos/filertk"
|
filertk-1.0.0/setup.cfg
ADDED
|
File without changes
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import re
|
|
2
|
+
from enum import Enum
|
|
3
|
+
|
|
4
|
+
class ReadType(Enum):
|
|
5
|
+
"""Enum of all reader OrderTypes"""
|
|
6
|
+
MATCH = 1
|
|
7
|
+
EXACT = 2
|
|
8
|
+
|
|
9
|
+
class WriteType(Enum):
|
|
10
|
+
"""Enum of all Writer OrderTypes"""
|
|
11
|
+
WITH = 1
|
|
12
|
+
REPLACE = 2
|
|
13
|
+
R_APPEND = 3
|
|
14
|
+
F_APPEND = 4
|
|
15
|
+
|
|
16
|
+
class CopyType(Enum):
|
|
17
|
+
"""Enum of all Copy OrderTypes"""
|
|
18
|
+
HARD = 1
|
|
19
|
+
SOFT = 2
|
|
20
|
+
MODIFY = 3
|
|
21
|
+
|
|
22
|
+
class IdentifierType(Enum):
|
|
23
|
+
"""Enum of all identifier OrderTypes"""
|
|
24
|
+
READER = 1
|
|
25
|
+
WRITER = 2
|
|
26
|
+
FROM = 3
|
|
27
|
+
EXCLUDING = 4
|
|
28
|
+
|
|
29
|
+
class CaseType(Enum):
|
|
30
|
+
"""Enum of all Casing OrderTypes"""
|
|
31
|
+
KEEP = 1
|
|
32
|
+
IGNORE = 2
|
|
33
|
+
CAMEL = 3
|
|
34
|
+
|
|
35
|
+
class OrderType(Enum):
|
|
36
|
+
READER = 1
|
|
37
|
+
WRITER = 2
|
|
38
|
+
COPY = 3
|
|
39
|
+
IDN = 4
|
|
40
|
+
CASE = 5
|
|
41
|
+
|
|
42
|
+
OrderType.Read = ReadType
|
|
43
|
+
OrderType.Write = WriteType
|
|
44
|
+
OrderType.Copy = CopyType
|
|
45
|
+
OrderType.Idn = IdentifierType
|
|
46
|
+
OrderType.Case = CaseType
|
|
47
|
+
|
|
48
|
+
class CommandReader:
|
|
49
|
+
TOKEN_PATTERN = r'"[^"]*"|\S+'
|
|
50
|
+
|
|
51
|
+
EXIT_CMD = "exit"
|
|
52
|
+
FULL_CLEAR_CMD = "clear_res"
|
|
53
|
+
CLEAR_CMD = "clear"
|
|
54
|
+
|
|
55
|
+
ARG_TYPES = {
|
|
56
|
+
"match": ReadType.MATCH,
|
|
57
|
+
"exact": ReadType.EXACT,
|
|
58
|
+
|
|
59
|
+
"excluding": IdentifierType.EXCLUDING,
|
|
60
|
+
"from": IdentifierType.FROM,
|
|
61
|
+
|
|
62
|
+
"with": WriteType.WITH,
|
|
63
|
+
"replace": WriteType.REPLACE,
|
|
64
|
+
"r_append": WriteType.R_APPEND,
|
|
65
|
+
"f_append": WriteType.F_APPEND,
|
|
66
|
+
|
|
67
|
+
"!keep_case": CaseType.KEEP,
|
|
68
|
+
"!ignore_case": CaseType.IGNORE,
|
|
69
|
+
"!camel_case": CaseType.CAMEL,
|
|
70
|
+
|
|
71
|
+
"~h_copy": CopyType.HARD,
|
|
72
|
+
"~modify": CopyType.MODIFY,
|
|
73
|
+
"~s_copy": CopyType.SOFT
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
def __init__(self, commands: list[str]):
|
|
77
|
+
"""Tokenizes & verifies commands given to terminal"""
|
|
78
|
+
self.__args = commands
|
|
79
|
+
self.__instructions = { OrderType.IDN: {}}
|
|
80
|
+
self.__identifier_queue: list[IdentifierType] = []
|
|
81
|
+
|
|
82
|
+
self.set_instructions()
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
@staticmethod
|
|
86
|
+
def is_name(potential_name: str) -> bool:
|
|
87
|
+
"""Used for checking if a name identifier token is provided"""
|
|
88
|
+
if(len(potential_name) <= 2): return False
|
|
89
|
+
|
|
90
|
+
if(potential_name[0] == '"' and potential_name[-1] == '"'): return True
|
|
91
|
+
|
|
92
|
+
return False
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
@staticmethod
|
|
96
|
+
def is_range(potential_range: str) -> bool:
|
|
97
|
+
"""Used for checking if a range identifier token is provided"""
|
|
98
|
+
|
|
99
|
+
if(potential_range[0] == "(" and potential_range[-1] == ")"): return True
|
|
100
|
+
|
|
101
|
+
return False
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
@staticmethod
|
|
105
|
+
def get_range_nums(range_token: str) -> list[int, int]:
|
|
106
|
+
"""grabs ranges for FROM identifier, returns [start, end]"""
|
|
107
|
+
|
|
108
|
+
numbers = re.findall(r'\d+', range_token)
|
|
109
|
+
numbers = [int(num) for num in numbers]
|
|
110
|
+
|
|
111
|
+
if(len(numbers) != 2):
|
|
112
|
+
raise ValueError(
|
|
113
|
+
"CommandReader get_rane_nums() was given an invalid amount of range values provided for FROM, received: "
|
|
114
|
+
+ len(numbers))
|
|
115
|
+
|
|
116
|
+
return numbers
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def handle_identifier(self, idn: str) -> None:
|
|
120
|
+
"""handles identifier queue status & allocation of identifiers with corresponding type"""
|
|
121
|
+
|
|
122
|
+
if(len(idn) < 3):
|
|
123
|
+
raise ValueError("CommandReader handle_identifier() found an identifier with not enough characters.\n"
|
|
124
|
+
+ "[identifier: " + idn + "]\n"
|
|
125
|
+
+ "make sure identifiers have a starting and ending quotation, with all characters inbetween")
|
|
126
|
+
|
|
127
|
+
if(self.__identifier_queue == 0):
|
|
128
|
+
raise ValueError("CommandReader handle_identifier() found an identifier but did not know where to place it\n"
|
|
129
|
+
+ "\t[identifier: " + idn + "]\n"
|
|
130
|
+
+ "make sure your identifiers have an identifier type (readers, writers, from, excluding) before writing them")
|
|
131
|
+
|
|
132
|
+
if(idn[0] != '"' or idn[-1] != '"'):
|
|
133
|
+
raise ValueError("CommandReader handle_identifier found an identifier lacking proper quotations.\n"
|
|
134
|
+
+ "\t[identifier: " + idn + "]\n"
|
|
135
|
+
+ "make sure all identifiers have leading and trailing quotations, with all characters inbetween")
|
|
136
|
+
|
|
137
|
+
self.__instructions[OrderType.IDN][self.__identifier_queue[0]] = idn[1 : -1] #to remove quotations from checks
|
|
138
|
+
self.__identifier_queue.pop(0)
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def set_instructions(self):
|
|
142
|
+
"""Reads through the args list and updates the instructions dictionary"""
|
|
143
|
+
|
|
144
|
+
for arg in self.__args:
|
|
145
|
+
arg_type = CommandReader.ARG_TYPES.get(arg)
|
|
146
|
+
|
|
147
|
+
if(arg_type):
|
|
148
|
+
match arg_type:
|
|
149
|
+
case ReadType():
|
|
150
|
+
self.__instructions[OrderType.READER] = arg_type
|
|
151
|
+
self.__identifier_queue.append(OrderType.READER)
|
|
152
|
+
|
|
153
|
+
case WriteType():
|
|
154
|
+
self.__instructions[OrderType.WRITER] = arg_type
|
|
155
|
+
self.__identifier_queue.append(OrderType.WRITER)
|
|
156
|
+
|
|
157
|
+
case CopyType():
|
|
158
|
+
self.__instructions[OrderType.COPY] = arg_type
|
|
159
|
+
|
|
160
|
+
if(arg_type == CopyType.SOFT):
|
|
161
|
+
self.__identifier_queue.append(OrderType.COPY)
|
|
162
|
+
|
|
163
|
+
case IdentifierType():
|
|
164
|
+
self.__identifier_queue.append(arg_type)
|
|
165
|
+
|
|
166
|
+
case CaseType():
|
|
167
|
+
self.__instructions[OrderType.CASE] = arg_type
|
|
168
|
+
|
|
169
|
+
elif(CommandReader.is_name(arg)):
|
|
170
|
+
self.handle_identifier(arg)
|
|
171
|
+
|
|
172
|
+
elif(CommandReader.is_range(arg)):
|
|
173
|
+
if(self.__identifier_queue[0] == IdentifierType.EXCLUDING):
|
|
174
|
+
self.__instructions[OrderType.IDN][IdentifierType.EXCLUDING] = CommandReader.get_range_nums(arg)
|
|
175
|
+
self.__identifier_queue.pop(0)
|
|
176
|
+
else:
|
|
177
|
+
self.__instructions[OrderType.IDN][IdentifierType.FROM] = CommandReader.get_range_nums(arg)
|
|
178
|
+
self.__identifier_queue.pop(0)
|
|
179
|
+
|
|
180
|
+
else:
|
|
181
|
+
raise ValueError("CommandReader set_instructions() detected an unexpected token, only use the token types specified"
|
|
182
|
+
+ " or ensure text specifications use \" before and after (ex: \"text\")\n"
|
|
183
|
+
+ "\t[token interpereted: " + arg + "]")
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def get_instructions(self):
|
|
187
|
+
return self.__instructions
|
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
import shutil
|
|
2
|
+
import re
|
|
3
|
+
|
|
4
|
+
from colorama import Fore, Style
|
|
5
|
+
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
from .command_reader import CommandReader, OrderType
|
|
9
|
+
|
|
10
|
+
class DataFormatter:
|
|
11
|
+
"""Used for cleaner and easier access of individual lines and words inside said lines"""
|
|
12
|
+
|
|
13
|
+
def __init__(self, line_number=0, words=""):
|
|
14
|
+
self.line_number = line_number
|
|
15
|
+
self.words: list[str] = [text for text in re.split(r'(\s+)', words) if text]
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class WordMatches:
|
|
20
|
+
"""Used for cleaner and easier access of individual word matches"""
|
|
21
|
+
|
|
22
|
+
def __init__(self, word: str, line_number=0, cpos=0):
|
|
23
|
+
self.word = word
|
|
24
|
+
self.line_number = line_number
|
|
25
|
+
self.cpos = cpos
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class FileManager:
|
|
30
|
+
"""Reviews tokenized instructions and documents changes to file"""
|
|
31
|
+
|
|
32
|
+
RESULTS_DIR = Path(__file__).resolve().parent / "results"
|
|
33
|
+
ACCEPT_ANY_CASE = (OrderType.Case.KEEP, OrderType.Case.IGNORE, OrderType.Case.CAMEL)
|
|
34
|
+
|
|
35
|
+
def __init__(self, original: Path, commands: CommandReader):
|
|
36
|
+
self.__path = original
|
|
37
|
+
self.__commands = commands.get_instructions()
|
|
38
|
+
|
|
39
|
+
self.__original_data: list[DataFormatter] = []
|
|
40
|
+
self.read_original()
|
|
41
|
+
|
|
42
|
+
self.__matches: list[WordMatches] = []
|
|
43
|
+
self.__instance_changes_ctr = 0
|
|
44
|
+
self.__camel_ref = self.__commands[OrderType.IDN][OrderType.WRITER] if self.__commands.get(OrderType.CASE) == OrderType.Case.CAMEL else None
|
|
45
|
+
|
|
46
|
+
self.interperit_matcher(self.__commands.get(OrderType.READER))
|
|
47
|
+
self.interperit_writer(self.__commands.get(OrderType.WRITER))
|
|
48
|
+
self.write_to_file()
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def __search_match(self, word: str) -> bool:
|
|
53
|
+
"""searches for a match based off of casing condition issued and whether it matches the text"""
|
|
54
|
+
|
|
55
|
+
case_rule = self.__commands.get(OrderType.CASE)
|
|
56
|
+
|
|
57
|
+
match(self.__commands[OrderType.READER]):
|
|
58
|
+
case(OrderType.Read.MATCH):
|
|
59
|
+
if(case_rule in FileManager.ACCEPT_ANY_CASE):
|
|
60
|
+
return self.__commands[OrderType.IDN][OrderType.READER].lower() in word.lower()
|
|
61
|
+
|
|
62
|
+
else:
|
|
63
|
+
return self.__commands[OrderType.IDN][OrderType.READER] in word
|
|
64
|
+
|
|
65
|
+
case(OrderType.Read.EXACT):
|
|
66
|
+
return self.__commands[OrderType.IDN][OrderType.READER] == word
|
|
67
|
+
|
|
68
|
+
raise ValueError("file_manager __search_match() was called with an invalid READER type")
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def __get_word_index(self, word: str) -> int:
|
|
73
|
+
"""returns index of match if found inside of word"""
|
|
74
|
+
|
|
75
|
+
search: str = self.__commands[OrderType.IDN][OrderType.READER]
|
|
76
|
+
|
|
77
|
+
case_rule = self.__commands.get(OrderType.CASE)
|
|
78
|
+
if(case_rule in FileManager.ACCEPT_ANY_CASE):
|
|
79
|
+
return word.lower().find(search.lower())
|
|
80
|
+
|
|
81
|
+
return word.find(search)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def __set_match_casing(self, word: str) -> str:
|
|
86
|
+
"""checks if the match inside a word provided is either fully capitalized or lowercased, and returns a string according to said capitalization"""
|
|
87
|
+
|
|
88
|
+
match_index = self.__get_word_index(word)
|
|
89
|
+
word_match = word[match_index : match_index + len(self.__commands[OrderType.IDN][OrderType.READER])]
|
|
90
|
+
|
|
91
|
+
if(word_match.isupper()):
|
|
92
|
+
return self.__commands[OrderType.IDN][OrderType.WRITER].upper()
|
|
93
|
+
|
|
94
|
+
if(word_match.islower()):
|
|
95
|
+
return self.__commands[OrderType.IDN][OrderType.WRITER].lower()
|
|
96
|
+
|
|
97
|
+
return self.__commands[OrderType.IDN][OrderType.WRITER]
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def __with_formatter(self, word: str) -> str:
|
|
102
|
+
"""returns the modified word under the with formatting context"""
|
|
103
|
+
|
|
104
|
+
replacement = ""
|
|
105
|
+
index = self.__get_word_index(word)
|
|
106
|
+
to_insert = self.__commands[OrderType.IDN][OrderType.WRITER]
|
|
107
|
+
|
|
108
|
+
if(index > -1):
|
|
109
|
+
replacement = word[0 : index] + to_insert + word[index + len(self.__commands[OrderType.IDN][OrderType.READER]) : ]
|
|
110
|
+
self.__instance_changes_ctr += 1
|
|
111
|
+
|
|
112
|
+
return replacement
|
|
113
|
+
|
|
114
|
+
raise ValueError("file_manager __with_formatter() was ordered to modify a word that does not match reader [unexpected behavior]"
|
|
115
|
+
+ "\tpossibly an error with match detection?\n" +
|
|
116
|
+
"[reader idn: " + self.__commands[OrderType.IDN][OrderType.READER] + " word: " + word)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def __replace_formatter(self, word: str) -> str:
|
|
121
|
+
"""returns the modified word under the replace formatting context"""
|
|
122
|
+
|
|
123
|
+
index = self.__get_word_index(word)
|
|
124
|
+
to_insert = self.__commands[OrderType.IDN][OrderType.WRITER]
|
|
125
|
+
|
|
126
|
+
if(index > -1):
|
|
127
|
+
self.__instance_changes_ctr += 1
|
|
128
|
+
return to_insert
|
|
129
|
+
|
|
130
|
+
raise ValueError("file_manager __replace_formatter() was ordered to modify a word that does not match reader [unexpected behavior]"
|
|
131
|
+
+ "\tpossibly an error with match detection?\n" +
|
|
132
|
+
"[reader idn: " + self.__commands[OrderType.IDN][OrderType.READER] + " word: " + word)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def __append_formatter(self, word: str) -> str:
|
|
137
|
+
"""returns the modified word under the append formatting context"""
|
|
138
|
+
|
|
139
|
+
replacement = ""
|
|
140
|
+
index = self.__get_word_index(word)
|
|
141
|
+
to_insert = self.__commands[OrderType.IDN][OrderType.WRITER]
|
|
142
|
+
|
|
143
|
+
if(index > -1):
|
|
144
|
+
word_end = index + len(word)
|
|
145
|
+
|
|
146
|
+
if(self.__commands.get(OrderType.Write.R_APPEND)):
|
|
147
|
+
replacement = word[0 : word_end] + to_insert + word[(word_end + len(to_insert)) : ]
|
|
148
|
+
else:
|
|
149
|
+
replacement = word[0 : index] + to_insert + word[index + len(to_insert) : ]
|
|
150
|
+
|
|
151
|
+
self.__instance_changes_ctr += 1
|
|
152
|
+
return replacement
|
|
153
|
+
|
|
154
|
+
raise ValueError("file_manager __append_formatter() was ordered to modify a word that does not match reader [unexpected behavior]"
|
|
155
|
+
+ "\tpossibly an error with match detection?\n" +
|
|
156
|
+
"[reader idn: " + self.__commands[OrderType.IDN][OrderType.READER] + " word: " + word)
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def __update_keep_casing(self, match: WordMatches) -> WordMatches:
|
|
161
|
+
"""Updates the casing of writer to properly replace text off of KEEP request, returns match inserted (no changes)"""
|
|
162
|
+
|
|
163
|
+
idn: dict[OrderType] = self.__commands[OrderType.IDN]
|
|
164
|
+
original_word: str = self.__original_data[match.line_number - 1].words[match.cpos]
|
|
165
|
+
|
|
166
|
+
new_writer_word = idn[OrderType.WRITER]
|
|
167
|
+
|
|
168
|
+
if(original_word.isupper()): #picks up on capitalizations automatically
|
|
169
|
+
idn[OrderType.WRITER] = new_writer_word.upper()
|
|
170
|
+
return match
|
|
171
|
+
|
|
172
|
+
for i in range(min(len(original_word), len(idn[OrderType.WRITER]))):
|
|
173
|
+
writer_char = new_writer_word[i]
|
|
174
|
+
original_char = original_word[i]
|
|
175
|
+
|
|
176
|
+
if(original_char.isalpha() and writer_char.isalpha()):
|
|
177
|
+
if(original_char.isupper()):
|
|
178
|
+
new_writer_word = new_writer_word[ : i] + writer_char.upper() + new_writer_word[i + 1: ]
|
|
179
|
+
else:
|
|
180
|
+
new_writer_word = new_writer_word[ : i] + writer_char.lower() + new_writer_word[i + 1: ]
|
|
181
|
+
|
|
182
|
+
idn[OrderType.WRITER] = new_writer_word
|
|
183
|
+
return match
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def __update_camel_casing(self, match: WordMatches) -> WordMatches:
|
|
188
|
+
"""Updates the casing of the new writing text to match if it is any sort of camel type, including Titles, StanDard, lowercase, and FULL_CAPS"""
|
|
189
|
+
|
|
190
|
+
capital_ctr = 0
|
|
191
|
+
idn: str = self.__commands[OrderType.IDN]
|
|
192
|
+
|
|
193
|
+
idn[OrderType.WRITER] = self.__camel_ref
|
|
194
|
+
original_word: str = self.__original_data[match.line_number - 1].words[match.cpos]
|
|
195
|
+
|
|
196
|
+
casing_change = self.__set_match_casing(match.word)
|
|
197
|
+
|
|
198
|
+
if(casing_change != idn[OrderType.WRITER]):
|
|
199
|
+
idn[OrderType.WRITER] = casing_change
|
|
200
|
+
return match
|
|
201
|
+
|
|
202
|
+
for i in range(len(original_word)):
|
|
203
|
+
original_char = original_word[i]
|
|
204
|
+
|
|
205
|
+
if(original_char.isupper()):
|
|
206
|
+
capital_ctr += 1
|
|
207
|
+
|
|
208
|
+
if(capital_ctr > 1): #any text with > 1 capitalized chars is presumed to be a CamelCase of some sort
|
|
209
|
+
break
|
|
210
|
+
return match
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def __create_backup(self) -> Path:
|
|
215
|
+
"""used for creating backup files of original file"""
|
|
216
|
+
|
|
217
|
+
backup_path = self.RESULTS_DIR / (self.__path.stem + "_backup" + self.__path.suffix)
|
|
218
|
+
shutil.copy2(self.__path, backup_path)
|
|
219
|
+
|
|
220
|
+
return backup_path
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
def read_original(self):
|
|
225
|
+
"""reads thorugh the original file provided"""
|
|
226
|
+
line_num = 0
|
|
227
|
+
|
|
228
|
+
with open(self.__path, 'r') as file:
|
|
229
|
+
for line in file:
|
|
230
|
+
line_num += 1
|
|
231
|
+
self.__original_data.append(DataFormatter(line_num, line))
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
def interperit_matcher(self, match_type: OrderType.Read):
|
|
236
|
+
"""scans file for matches based off of match type specifier to be modified later"""
|
|
237
|
+
|
|
238
|
+
validator = lambda arg: False
|
|
239
|
+
|
|
240
|
+
range_based = self.__commands[OrderType.IDN].get(OrderType.Idn.FROM)
|
|
241
|
+
excluding = self.__commands[OrderType.IDN].get(OrderType.Idn.EXCLUDING)
|
|
242
|
+
|
|
243
|
+
excluding_handler = lambda _, __: False
|
|
244
|
+
if(isinstance(excluding, str)):
|
|
245
|
+
excluding_handler = lambda arg, _: excluding == arg #range arg in unwated in word exclude type
|
|
246
|
+
|
|
247
|
+
elif(isinstance(excluding, list)):
|
|
248
|
+
excluding_handler = lambda _, arg: arg < excluding[0] or arg > excluding[1] #word arg is unwanted in range exclude type
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
for data in self.__original_data:
|
|
252
|
+
for word_pos in range(len(data.words)):
|
|
253
|
+
word = data.words[word_pos]
|
|
254
|
+
|
|
255
|
+
if(range_based and (data.line_number < range_based[0] or data.line_number > range_based[1])):
|
|
256
|
+
break
|
|
257
|
+
|
|
258
|
+
if(excluding_handler(word, data.line_number)):
|
|
259
|
+
continue
|
|
260
|
+
|
|
261
|
+
if(self.__search_match(word)):
|
|
262
|
+
self.__matches.append(WordMatches(word, data.line_number, word_pos))
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
def interperit_writer(self, write_type: OrderType.WRITER):
|
|
267
|
+
"""modifies matches found in file to specified write type"""
|
|
268
|
+
|
|
269
|
+
formatter: str = None
|
|
270
|
+
match(write_type):
|
|
271
|
+
case OrderType.Write.WITH:
|
|
272
|
+
formatter = lambda match: self.__with_formatter(match.word)
|
|
273
|
+
|
|
274
|
+
case OrderType.Write.REPLACE:
|
|
275
|
+
formatter = lambda match: self.__replace_formatter(match.word)
|
|
276
|
+
|
|
277
|
+
case OrderType.Write.APPEND:
|
|
278
|
+
formatter = lambda match: self.__append_formatter(match.word)
|
|
279
|
+
|
|
280
|
+
handler: str = formatter
|
|
281
|
+
if(self.__commands.get(OrderType.CASE) == OrderType.Case.KEEP):
|
|
282
|
+
if(len(self.__commands[OrderType.IDN][OrderType.WRITER]) != len(self.__commands[OrderType.IDN][OrderType.READER])):
|
|
283
|
+
print(Fore.YELLOW + "WARNING: !casing_kp [keep casing to match] request was made, but reader and writer identifiers differ in size.\n" \
|
|
284
|
+
+ "\tResults may not be as expected due to size mismatch." + Style.RESET_ALL)
|
|
285
|
+
|
|
286
|
+
handler = lambda match: formatter(self.__update_keep_casing(match))
|
|
287
|
+
|
|
288
|
+
if(self.__commands.get(OrderType.CASE) == OrderType.Case.CAMEL):
|
|
289
|
+
handler = lambda match: formatter(self.__update_camel_casing(match))
|
|
290
|
+
|
|
291
|
+
for match in self.__matches:
|
|
292
|
+
match_result: WordMatches = WordMatches(word=handler(match))
|
|
293
|
+
|
|
294
|
+
while(self.__search_match(match_result.word)):
|
|
295
|
+
match_result.word = handler(match_result)
|
|
296
|
+
|
|
297
|
+
self.__original_data[match.line_number - 1].words[match.cpos] = match_result.word
|
|
298
|
+
|
|
299
|
+
print(f"{Fore.GREEN}{self.__instance_changes_ctr} instance changes made in \033]8;;{self.__path}\033\\'{self.__path}\033]8;;\033\\'")
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
def write_to_file(self):
|
|
303
|
+
"""takes all data collected & modified from original file & writes it into instructed file"""
|
|
304
|
+
|
|
305
|
+
file_mode = self.__commands.get(OrderType.COPY)
|
|
306
|
+
file_path: Path
|
|
307
|
+
|
|
308
|
+
if(not file_mode):
|
|
309
|
+
print(Fore.YELLOW + "WARNING: FileManager write_to_file() detected no copy command from command list.\n"
|
|
310
|
+
+ "\tFileManager write_to_file() is defaulting to hard copy [~h_copy]" + Style.RESET_ALL)
|
|
311
|
+
|
|
312
|
+
file_mode = OrderType.Copy.HARD
|
|
313
|
+
|
|
314
|
+
match(file_mode):
|
|
315
|
+
case OrderType.Copy.HARD:
|
|
316
|
+
file_path = FileManager.RESULTS_DIR / self.__path.name
|
|
317
|
+
|
|
318
|
+
case OrderType.Copy.SOFT:
|
|
319
|
+
requested_name = self.__commands[OrderType.IDN].get(OrderType.COPY)
|
|
320
|
+
|
|
321
|
+
if(not requested_name):
|
|
322
|
+
raise ValueError("FileManager write_to_file() was provided with ~s_copy() but recieved no identifier for file name; did you specify one?\n"
|
|
323
|
+
+ "\t~s_copy requires a name for the file, make sure you wrote one!")
|
|
324
|
+
file_path = FileManager.RESULTS_DIR / requested_name
|
|
325
|
+
|
|
326
|
+
case OrderType.Copy.MODIFY:
|
|
327
|
+
file_path = self.__path
|
|
328
|
+
backup = self.__create_backup()
|
|
329
|
+
|
|
330
|
+
print(Fore.GREEN + "~modify copy type created a backup file at:", backup)
|
|
331
|
+
|
|
332
|
+
with open(file_path, 'w') as file:
|
|
333
|
+
for data in self.__original_data:
|
|
334
|
+
for word in data.words:
|
|
335
|
+
file.write(word)
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import re
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
from colorama import Fore, Style
|
|
5
|
+
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
from .file_manager import FileManager
|
|
9
|
+
from .command_reader import CommandReader
|
|
10
|
+
|
|
11
|
+
INSTRUCTIONS = """fileformatter cmdlet:
|
|
12
|
+
SPECIAL:
|
|
13
|
+
exit: exit program early
|
|
14
|
+
clear_res: clears results file
|
|
15
|
+
clear "file.ext": deletes file from results/
|
|
16
|
+
|
|
17
|
+
READ TYPES:
|
|
18
|
+
match "text": find words that contain the text
|
|
19
|
+
exact "text": words that only meet the exact input
|
|
20
|
+
excluding "text" or (num): do not include items of the exact syntax or line number (use for match or from)
|
|
21
|
+
from (start-end): only modify lines specified
|
|
22
|
+
|
|
23
|
+
WRITE TYPES:
|
|
24
|
+
with "text": removes only the text that matches with new text
|
|
25
|
+
replace "text": replaces matches with new text
|
|
26
|
+
r_append "text": appends text to the end of the matches
|
|
27
|
+
f_append "text": appends text to the front of the matches
|
|
28
|
+
|
|
29
|
+
CASING TYPES:
|
|
30
|
+
!keep_case: finds read matches ignoring casing and writes to the same casing style as the replacing text
|
|
31
|
+
!ignore_case: finds read matches ignoring casing, writes each match the same as write casing specified
|
|
32
|
+
!camel_case: with the exact casing of the write provided, it is utilized on all camel cased matches, otherwise fully lowercased/uppercased
|
|
33
|
+
|
|
34
|
+
FILE COPY TYPES:
|
|
35
|
+
~h_copy: creates a copy of the same file name in results folder [defaulted to this]
|
|
36
|
+
~modify: modified the original file (a backup is creates in results/)
|
|
37
|
+
~s_copy "name": creates a copy of the same file with a custom name
|
|
38
|
+
|
|
39
|
+
Writing Commands:
|
|
40
|
+
simply combine the commands you want in no necessary order, for example:
|
|
41
|
+
from (1-10) excluding "var_name2" match "var_name" with "new_name" ~h_copy
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
def full_res_clear() -> None:
|
|
45
|
+
"""Clears every file inside of the results folder"""
|
|
46
|
+
|
|
47
|
+
to_delete = []
|
|
48
|
+
|
|
49
|
+
for file_path in FileManager.RESULTS_DIR.iterdir():
|
|
50
|
+
print("\trequested delete: ", file_path)
|
|
51
|
+
to_delete.append(file_path)
|
|
52
|
+
|
|
53
|
+
validate = input("Are you sure you want to delete all these files? [Y/N]: ").strip()
|
|
54
|
+
|
|
55
|
+
if(validate.lower() == "y"):
|
|
56
|
+
for file in to_delete:
|
|
57
|
+
file.unlink(missing_ok=True)
|
|
58
|
+
print('\t' + str(file) + " deleted successfuly")
|
|
59
|
+
print(f"{Fore.GREEN}All files in results deleted!{Style.RESET_ALL}")
|
|
60
|
+
else:
|
|
61
|
+
print(f"{Fore.GREEN}clear_res was canceled{Style.RESET_ALL}")
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def specify_res_clear(args: list[str]) -> None:
|
|
65
|
+
"""Analyzes the tokenized input provided to allocate the file name specified"""
|
|
66
|
+
|
|
67
|
+
if(len(args) < 2):
|
|
68
|
+
print(f"{Fore.YELLOW}filertk clear command was used, but could not find a file identifier{Style.RESET_ALL}")
|
|
69
|
+
return
|
|
70
|
+
|
|
71
|
+
if(len(args) > 2):
|
|
72
|
+
print(f"{Fore.YELLOW}filertk clear command was used with too many identifiers; only provide the clear command, and the file name inside the reuslts folder{Style.RESET_ALL}")
|
|
73
|
+
return
|
|
74
|
+
|
|
75
|
+
file_idn = args[0] if args[0] != CommandReader.CLEAR_CMD else args[1]
|
|
76
|
+
if(not CommandReader.is_name(file_idn)):
|
|
77
|
+
print(f"{Fore.YELLOW}file identifier requires quotations around to ensure it's properly retrieved, make sure there is double quotes both at the front and end of your file's name{Style.RESET_ALL}")
|
|
78
|
+
|
|
79
|
+
else:
|
|
80
|
+
to_clear: Path = FileManager.RESULTS_DIR / file_idn[1 : -1]
|
|
81
|
+
|
|
82
|
+
if(to_clear.exists):
|
|
83
|
+
to_clear.unlink(missing_ok=True)
|
|
84
|
+
print(f"{Fore.GREEN}{to_clear} successfully deleted{Style.RESET_ALL}")
|
|
85
|
+
|
|
86
|
+
else:
|
|
87
|
+
print(f"{Fore.YELLOW}{to_clear} file was not found; are you sure you typed it correctly?{Style.RESET_ALL}")
|
|
88
|
+
return
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def read_input(instructions: str, expect_file_dir=False) -> str:
|
|
92
|
+
user_input = input(instructions)
|
|
93
|
+
result: list[str] = re.findall(CommandReader.TOKEN_PATTERN, user_input)
|
|
94
|
+
|
|
95
|
+
if(CommandReader.EXIT_CMD in result):
|
|
96
|
+
print("exit called, closing filertk...")
|
|
97
|
+
sys.exit()
|
|
98
|
+
|
|
99
|
+
if(CommandReader.FULL_CLEAR_CMD in result):
|
|
100
|
+
full_res_clear()
|
|
101
|
+
return
|
|
102
|
+
|
|
103
|
+
if(CommandReader.CLEAR_CMD in result):
|
|
104
|
+
specify_res_clear(result)
|
|
105
|
+
return
|
|
106
|
+
|
|
107
|
+
if(expect_file_dir):
|
|
108
|
+
if(CommandReader.is_name(user_input)):
|
|
109
|
+
return user_input[1 : -1]
|
|
110
|
+
return user_input
|
|
111
|
+
|
|
112
|
+
return result
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def main():
|
|
116
|
+
print(INSTRUCTIONS)
|
|
117
|
+
|
|
118
|
+
user_input: str
|
|
119
|
+
file_path: Path
|
|
120
|
+
|
|
121
|
+
while(True):
|
|
122
|
+
user_input = read_input("Enter a file directory: ", expect_file_dir=True)
|
|
123
|
+
|
|
124
|
+
if(user_input):
|
|
125
|
+
file_path = Path(user_input)
|
|
126
|
+
if(file_path.exists()): break
|
|
127
|
+
else:
|
|
128
|
+
continue
|
|
129
|
+
|
|
130
|
+
print(f"{Fore.YELLOW}\nInvalid file path provided! Make sure your file path is correct\n"
|
|
131
|
+
+ f"\t[provided path: {file_path}{Style.RESET_ALL}]")
|
|
132
|
+
|
|
133
|
+
print("path", file_path, "accepted")
|
|
134
|
+
|
|
135
|
+
args: list[str] | bool = None
|
|
136
|
+
while(not args):
|
|
137
|
+
args = read_input("Enter command instructions:$// ")
|
|
138
|
+
|
|
139
|
+
reader = CommandReader(args)
|
|
140
|
+
manager = FileManager(file_path, reader)
|
|
141
|
+
|
|
142
|
+
if __name__ == "__main__":
|
|
143
|
+
main()
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: filertk
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: package used for rewriting certain aspects of text in files
|
|
5
|
+
Author-email: Dylan Santos <dasantos726@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/dylanasantos/filertk
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Operating System :: OS Independent
|
|
10
|
+
Requires-Python: >=3.14
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Requires-Dist: colorama>=0.4.6
|
|
14
|
+
Dynamic: license-file
|
|
15
|
+
|
|
16
|
+
# filertk 1.0.0
|
|
17
|
+
Filertk is a simple terminal-based system that allows for quick basic file rewrite instructions that would take excessive work by hand, such as renaming variables.\
|
|
18
|
+
\
|
|
19
|
+
**To activate:** simply write "filertk" within your terminal and paste in the file directory you want to modify. Afterwards input your commands when requested.\
|
|
20
|
+
- All file modifications are located in a directory inside of <ins>filertk\results</ins>
|
|
21
|
+
|
|
22
|
+
## Commands
|
|
23
|
+
filertk has 17 different commands displayed at each launch of the program. These commands are used to specify the desired modifications to the file or to the results directory.
|
|
24
|
+
|
|
25
|
+
> [!NOTE]
|
|
26
|
+
> The general order of commands used does not matter. However, the identifiers you provide are a first-come-first-serve basis.
|
|
27
|
+
|
|
28
|
+
### Identifiers
|
|
29
|
+
Identifiers are used to inform filertk what exactly you are referencing in your commands, such as the text you want to find or replace.
|
|
30
|
+
|
|
31
|
+
- **Name Identifiers "":** Used for specifying the text to match, rewrite, or exclude
|
|
32
|
+
- **Range Identifiers (x-y):** Used for specifying the lines from start-end you want to either exclude or include
|
|
33
|
+
<br><br>
|
|
34
|
+
|
|
35
|
+
### Special Commands
|
|
36
|
+
- **exit:** closes filertk program
|
|
37
|
+
- **clear_res:** clears the entire file results directory, first displays all files being deleted and requests for permission
|
|
38
|
+
- **clear "filename.ext":** clears the specified file from results directory, first displays file direcotry and requests for permission
|
|
39
|
+
<br><br>
|
|
40
|
+
|
|
41
|
+
### Reader Commands
|
|
42
|
+
Commands used to inform filertk how the text to match should be interpereted.
|
|
43
|
+
|
|
44
|
+
> [!NOTE]
|
|
45
|
+
> Reader Commands do not handle casing sensitivity, only character matching, refer to Casing Commands for more info.
|
|
46
|
+
|
|
47
|
+
- **match "text":** finds any words that contain the text
|
|
48
|
+
- **exact "text":** only finds words that contain the exact text
|
|
49
|
+
- **excluding "text" or (start-end):** excludes any matches of the word found (only used in match type) or ignores lines specified
|
|
50
|
+
- **from (start-end):** will only read/modify the lines specified (start and end are inclusive)
|
|
51
|
+
<br><br>
|
|
52
|
+
|
|
53
|
+
### Writer Commands
|
|
54
|
+
Commands used to inform filertk how the matches found should be rewritten.
|
|
55
|
+
|
|
56
|
+
- **with "text":** removes and replaces the new text only with what matched (ex: demo_1 -> new_1)
|
|
57
|
+
- **replace "text":** removes the entire text with the match found and inserts the new text (ex: demo_1 -> new)
|
|
58
|
+
- **r_append "text":** appends the new text to the end of the match (ex: demo_1 -> demo_1new)
|
|
59
|
+
- **f_append "text":** appends the new text to the front of the match (ex: demo_1 -> newdemo_1)
|
|
60
|
+
<br><br>
|
|
61
|
+
|
|
62
|
+
### Casing Commands
|
|
63
|
+
Commands used to inform filertk how the text casing should be handled.
|
|
64
|
+
|
|
65
|
+
- **!ignore_case:** ignores the casing of matches, any and all capitalizations are treated the same via the reader but are replaced by the exact casing of the writer identifier input.
|
|
66
|
+
- **!keep_case:** will attempt to copy the exact casing via index-by-index (this is only recommended for matches and writes of the same size), will automatically lowercase and uppercase all chars regardless if reader and writer identifier sizes match.<br>
|
|
67
|
+
- **!camel_case:** given the exact casing of the user's writer identifier, will replace all words with said input UNLESS the match if full capitalized or lowercased. If so, it will match the casing.
|
|
68
|
+
|
|
69
|
+
### File Copy Command
|
|
70
|
+
Commands used on how the file modifications should be handled.
|
|
71
|
+
|
|
72
|
+
- **~h_copy:** creates a copy of the exact file name inside of the results directory. (filertk defaults to this, but will display a warning if defaulted)
|
|
73
|
+
- **~modify:** modifies the original file specified, creates an emergency backup inside of the results directory if the output is unwanted.
|
|
74
|
+
- **~s_copy "filename.ext":** creates a copy of the file with a custom name provided inside of the results directory.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
src/filertk/__init__.py
|
|
5
|
+
src/filertk/command_reader.py
|
|
6
|
+
src/filertk/file_manager.py
|
|
7
|
+
src/filertk/main.py
|
|
8
|
+
src/filertk.egg-info/PKG-INFO
|
|
9
|
+
src/filertk.egg-info/SOURCES.txt
|
|
10
|
+
src/filertk.egg-info/dependency_links.txt
|
|
11
|
+
src/filertk.egg-info/entry_points.txt
|
|
12
|
+
src/filertk.egg-info/requires.txt
|
|
13
|
+
src/filertk.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
colorama>=0.4.6
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
filertk
|