articlib 0.2.9__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.
- articlib-0.2.9/LICENSE +21 -0
- articlib-0.2.9/PKG-INFO +20 -0
- articlib-0.2.9/README.md +1 -0
- articlib-0.2.9/articlib/articFileUtils.py +130 -0
- articlib-0.2.9/articlib/consoleUtils.py +45 -0
- articlib-0.2.9/articlib/dateTime.py +139 -0
- articlib-0.2.9/articlib/dice.py +61 -0
- articlib-0.2.9/articlib/jsonHandler.py +132 -0
- articlib-0.2.9/articlib/logUtils.py +47 -0
- articlib-0.2.9/articlib/py.typed +0 -0
- articlib-0.2.9/articlib/sqliteEngine.py +72 -0
- articlib-0.2.9/pyproject.toml +32 -0
articlib-0.2.9/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023 Iñaki Arrondo
|
|
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.
|
articlib-0.2.9/PKG-INFO
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: articlib
|
|
3
|
+
Version: 0.2.9
|
|
4
|
+
Summary: Small set of tools and utilities in python. Destined to be use in my personal projects.
|
|
5
|
+
License: MIT
|
|
6
|
+
Author: Artic42
|
|
7
|
+
Author-email: engineer@artic42.com
|
|
8
|
+
Requires-Python: >=3.10,<4.0
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
15
|
+
Requires-Dist: colorama (>=0.4.6,<0.5.0)
|
|
16
|
+
Requires-Dist: pytest (>=9.0.2,<10.0.0)
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
|
|
19
|
+
New documentation been written slowly
|
|
20
|
+
|
articlib-0.2.9/README.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
New documentation been written slowly
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import shutil
|
|
3
|
+
import logging
|
|
4
|
+
import logUtils as LU
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
logObj = logging.getLogger()
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def deleteFile(path: str) -> None:
|
|
11
|
+
logObj.debug(f"Delete the file on path {path}")
|
|
12
|
+
os.remove(path)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def createFile(path: str) -> None:
|
|
16
|
+
logObj.debug(f"Create file on path {path}")
|
|
17
|
+
file = open(path, "w")
|
|
18
|
+
file.close()
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def copyFile(source: str, destination: str) -> None:
|
|
22
|
+
logObj.debug(f"Copy file from {source} to {destination}")
|
|
23
|
+
shutil.copy(source, destination)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def fileExists(path: str) -> bool:
|
|
27
|
+
return os.path.isfile(path)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def getFilesInDirectory(path: str) -> list[str]:
|
|
31
|
+
return os.listdir(path)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def deleteDirectory(path: str) -> None:
|
|
35
|
+
logObj.debug(f"Delete directory on {path}")
|
|
36
|
+
os.rmdir(path)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def deleteDirectoryTree(path: str) -> None:
|
|
40
|
+
logObj.debug(f"Remove entire directory tree in {path}")
|
|
41
|
+
shutil.rmtree(path)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def createDirectory(path: str) -> None:
|
|
45
|
+
logObj.debug(f"Create directory on {path}")
|
|
46
|
+
os.makedirs(path, exist_ok=True)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def directoryExists(path: str) -> bool:
|
|
50
|
+
return os.path.isdir(path)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class FileIO:
|
|
54
|
+
def __init__(self, path: str, readIt: bool = False, log: bool = True) -> None:
|
|
55
|
+
self.log = log
|
|
56
|
+
self.path = path
|
|
57
|
+
if self.log is True:
|
|
58
|
+
logObj.debug(f"Create FileIO object with path {self.path}")
|
|
59
|
+
if readIt is True:
|
|
60
|
+
logObj.info(f"Reading content in path {self.path}")
|
|
61
|
+
self.readFile()
|
|
62
|
+
else:
|
|
63
|
+
logObj.info("Load file as empty without reading")
|
|
64
|
+
self.lines: list[str] = []
|
|
65
|
+
|
|
66
|
+
def readFile(self) -> None:
|
|
67
|
+
filePointer = open(self.path, "r")
|
|
68
|
+
if self.log is True:
|
|
69
|
+
logObj.info(f"Read file in {self.path}")
|
|
70
|
+
LU.logFile(self.path, debugFlag=True)
|
|
71
|
+
self.lines = filePointer.readlines()
|
|
72
|
+
self.lines = [line[:-1] for line in self.lines]
|
|
73
|
+
filePointer.close()
|
|
74
|
+
|
|
75
|
+
def writeFile(self) -> None:
|
|
76
|
+
filePointer = open(self.path, "w")
|
|
77
|
+
for line in self.lines:
|
|
78
|
+
filePointer.write(line + "\n")
|
|
79
|
+
if self.log is True:
|
|
80
|
+
logObj.info(f"Write file in {self.path}")
|
|
81
|
+
LU.logFile(self.path, debugFlag=True)
|
|
82
|
+
|
|
83
|
+
filePointer.close()
|
|
84
|
+
|
|
85
|
+
def appendToFile(self) -> None:
|
|
86
|
+
filePointer = open(self.path, "a")
|
|
87
|
+
for line in self.lines:
|
|
88
|
+
filePointer.write(line + "\n")
|
|
89
|
+
if self.log is True:
|
|
90
|
+
logObj.info(f"Append to file in {self.path}")
|
|
91
|
+
LU.logFile(self.path, debugFlag=True)
|
|
92
|
+
filePointer.close()
|
|
93
|
+
|
|
94
|
+
def addLine(self, line: str) -> None:
|
|
95
|
+
if self.log is True:
|
|
96
|
+
logObj.info(f"Addline: {line}")
|
|
97
|
+
self.lines.append(line)
|
|
98
|
+
|
|
99
|
+
def modifyLine(self, lineNumber: int, line: str) -> None:
|
|
100
|
+
if self.log is True:
|
|
101
|
+
logObj.info(f"Modify line {lineNumber} to {line}")
|
|
102
|
+
self.lines[lineNumber] = line
|
|
103
|
+
|
|
104
|
+
def removeLine(self, lineNumber: int) -> None:
|
|
105
|
+
if self.log is True:
|
|
106
|
+
logObj.info(f"Remove line {lineNumber}")
|
|
107
|
+
logObj.debug(f"Removed content is {self.lines[lineNumber]}")
|
|
108
|
+
del self.lines[lineNumber]
|
|
109
|
+
|
|
110
|
+
def removeLastLine(self) -> None:
|
|
111
|
+
if self.log is True:
|
|
112
|
+
logObj.info("Remove last line")
|
|
113
|
+
logObj.debug(f"Removed content is {self.lines[-1]}")
|
|
114
|
+
del self.lines[-1]
|
|
115
|
+
|
|
116
|
+
def findLine(self, text: str) -> list[int]:
|
|
117
|
+
result: list[int] = []
|
|
118
|
+
for i in range(len(self.lines)):
|
|
119
|
+
if text in self.lines[i]:
|
|
120
|
+
result.append(i)
|
|
121
|
+
return result
|
|
122
|
+
|
|
123
|
+
def findAndReplace(self, find: str, replace: str) -> None:
|
|
124
|
+
if self.log is True:
|
|
125
|
+
logObj.info(f"Find {find} and replace it with {replace}")
|
|
126
|
+
for i in range(len(self.lines)):
|
|
127
|
+
self.lines[i] = self.lines[i].replace(find, replace)
|
|
128
|
+
|
|
129
|
+
def lineCount(self) -> int:
|
|
130
|
+
return len(self.lines)
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
from colorama import Fore, Style
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def printRed(text: str) -> None:
|
|
5
|
+
print(Fore.RED + text + Style.RESET_ALL)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def printGreen(text: str) -> None:
|
|
9
|
+
print(Fore.GREEN + text + Style.RESET_ALL)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def printYellow(text: str) -> None:
|
|
13
|
+
print(Fore.YELLOW + text + Style.RESET_ALL)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def printBlue(text: str) -> None:
|
|
17
|
+
print(Fore.BLUE + text + Style.RESET_ALL)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def printMagenta(text: str) -> None:
|
|
21
|
+
print(Fore.MAGENTA + text + Style.RESET_ALL)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def printBold(text: str) -> None:
|
|
25
|
+
print(Style.BRIGHT + text + Style.RESET_ALL)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def printRedBold(text: str) -> None:
|
|
29
|
+
print(Style.BRIGHT + Fore.RED + text + Style.RESET_ALL)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def printGreenBold(text: str) -> None:
|
|
33
|
+
print(Style.BRIGHT + Fore.GREEN + text + Style.RESET_ALL)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def printYellowBold(text: str) -> None:
|
|
37
|
+
print(Style.BRIGHT + Fore.YELLOW + text + Style.RESET_ALL)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def printBlueBold(text: str) -> None:
|
|
41
|
+
print(Style.BRIGHT + Fore.BLUE + text + Style.RESET_ALL)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def printMagentaBold(text: str) -> None:
|
|
45
|
+
print(Style.BRIGHT + Fore.MAGENTA + text + Style.RESET_ALL)
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
|
|
3
|
+
YYYYMMDD = 1
|
|
4
|
+
DDMMYYYY = 2
|
|
5
|
+
MMDDYYYY = 3
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class createDate:
|
|
9
|
+
def __init__(self, mode: int) -> None:
|
|
10
|
+
self.mode = mode
|
|
11
|
+
|
|
12
|
+
def setToNow(self) -> None:
|
|
13
|
+
date = datetime.now()
|
|
14
|
+
self.year = int(date.year)
|
|
15
|
+
self.month = int(date.month)
|
|
16
|
+
self.day = int(date.day)
|
|
17
|
+
self.hour = int(date.hour)
|
|
18
|
+
self.minute = int(date.minute)
|
|
19
|
+
self.second = int(date.second)
|
|
20
|
+
self.calculateStringDate()
|
|
21
|
+
self.calculateStringTime()
|
|
22
|
+
|
|
23
|
+
def setTo(
|
|
24
|
+
self, year: int, month: int, day: int, hour: int, minute: int, second: int
|
|
25
|
+
) -> None:
|
|
26
|
+
self.year = year
|
|
27
|
+
self.month = month
|
|
28
|
+
self.day = day
|
|
29
|
+
self.hour = hour
|
|
30
|
+
self.minute = minute
|
|
31
|
+
self.second = second
|
|
32
|
+
self.calculateStringDate()
|
|
33
|
+
self.calculateStringTime()
|
|
34
|
+
|
|
35
|
+
def setToString(self, string: str) -> None:
|
|
36
|
+
stringList: list[str] = string.split(" ")
|
|
37
|
+
date: str = stringList[0]
|
|
38
|
+
time: str = stringList[1]
|
|
39
|
+
self.dateString = date
|
|
40
|
+
self.timeString = time
|
|
41
|
+
self.calculateDate()
|
|
42
|
+
self.calculateTime()
|
|
43
|
+
|
|
44
|
+
def calculateDate(self) -> None:
|
|
45
|
+
if self.mode == YYYYMMDD:
|
|
46
|
+
self.year = int(self.dateString[0:4])
|
|
47
|
+
self.month = int(self.dateString[5:7])
|
|
48
|
+
self.day = int(self.dateString[8:10])
|
|
49
|
+
elif self.mode == DDMMYYYY:
|
|
50
|
+
self.day = int(self.dateString[0:2])
|
|
51
|
+
self.month = int(self.dateString[3:5])
|
|
52
|
+
self.year = int(self.dateString[6:10])
|
|
53
|
+
elif self.mode == MMDDYYYY:
|
|
54
|
+
self.month = int(self.dateString[0:2])
|
|
55
|
+
self.day = int(self.dateString[3:5])
|
|
56
|
+
self.year = int(self.dateString[6:10])
|
|
57
|
+
else:
|
|
58
|
+
raise ValueError("Invalid mode")
|
|
59
|
+
|
|
60
|
+
def calculateTime(self) -> None:
|
|
61
|
+
self.hour = int(self.timeString[0:2])
|
|
62
|
+
self.minute = int(self.timeString[3:5])
|
|
63
|
+
self.second = int(self.timeString[6:8])
|
|
64
|
+
|
|
65
|
+
def calculateStringDate(self) -> None:
|
|
66
|
+
if self.mode == YYYYMMDD:
|
|
67
|
+
self.dateString = str(self.year) + "/"
|
|
68
|
+
self.dateString += str(self.month).zfill(2) + "/"
|
|
69
|
+
self.dateString += str(self.day).zfill(2)
|
|
70
|
+
elif self.mode == DDMMYYYY:
|
|
71
|
+
self.dateString = str(self.day).zfill(2) + "/"
|
|
72
|
+
self.dateString += str(self.month).zfill(2) + "/"
|
|
73
|
+
self.dateString += str(self.year)
|
|
74
|
+
elif self.mode == MMDDYYYY:
|
|
75
|
+
self.dateString = str(self.month).zfill(2) + "/"
|
|
76
|
+
self.dateString += str(self.day).zfill(2) + "/"
|
|
77
|
+
self.dateString += str(self.year)
|
|
78
|
+
else:
|
|
79
|
+
raise ValueError("Invalid mode")
|
|
80
|
+
|
|
81
|
+
def calculateStringTime(self) -> None:
|
|
82
|
+
self.timeString = str(self.hour).zfill(2) + ":"
|
|
83
|
+
self.timeString += str(self.minute).zfill(2) + ":"
|
|
84
|
+
self.timeString += str(self.second).zfill(2)
|
|
85
|
+
|
|
86
|
+
def setMode(self, mode: int) -> None:
|
|
87
|
+
self.mode = mode
|
|
88
|
+
self.calculateStringDate()
|
|
89
|
+
|
|
90
|
+
# Output values
|
|
91
|
+
def getDateTimePathFormat(self) -> str:
|
|
92
|
+
if self.mode == YYYYMMDD:
|
|
93
|
+
dateString = (
|
|
94
|
+
str(self.year) + str(self.month).zfill(2) + str(self.day).zfill(2)
|
|
95
|
+
)
|
|
96
|
+
elif self.mode == DDMMYYYY:
|
|
97
|
+
dateString = (
|
|
98
|
+
str(self.day).zfill(2) + str(self.month).zfill(2) + str(self.year)
|
|
99
|
+
)
|
|
100
|
+
elif self.mode == MMDDYYYY:
|
|
101
|
+
dateString = (
|
|
102
|
+
str(self.month).zfill(2) + str(self.day).zfill(2) + str(self.year)
|
|
103
|
+
)
|
|
104
|
+
else:
|
|
105
|
+
raise ValueError("Invalid mode")
|
|
106
|
+
timeString = str(self.hour).zfill(2)
|
|
107
|
+
timeString += str(self.minute).zfill(2)
|
|
108
|
+
timeString += str(self.second).zfill(2)
|
|
109
|
+
return f"{dateString}_{timeString}"
|
|
110
|
+
|
|
111
|
+
def getDate(self) -> str:
|
|
112
|
+
return self.dateString
|
|
113
|
+
|
|
114
|
+
def getTime(self) -> str:
|
|
115
|
+
return self.timeString
|
|
116
|
+
|
|
117
|
+
def getDateTime(self) -> str:
|
|
118
|
+
return self.dateString + " " + self.timeString
|
|
119
|
+
|
|
120
|
+
def getYear(self) -> int:
|
|
121
|
+
return self.year
|
|
122
|
+
|
|
123
|
+
def getMonth(self) -> int:
|
|
124
|
+
return self.month
|
|
125
|
+
|
|
126
|
+
def getDay(self) -> int:
|
|
127
|
+
return self.day
|
|
128
|
+
|
|
129
|
+
def getHour(self) -> int:
|
|
130
|
+
return self.hour
|
|
131
|
+
|
|
132
|
+
def getMinute(self) -> int:
|
|
133
|
+
return self.minute
|
|
134
|
+
|
|
135
|
+
def getSecond(self) -> int:
|
|
136
|
+
return self.second
|
|
137
|
+
|
|
138
|
+
def getMode(self) -> int:
|
|
139
|
+
return self.mode
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import random
|
|
2
|
+
import logging
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
log = logging.getLogger()
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class dice:
|
|
9
|
+
def __init__(self, size: int = 6, log: bool = True) -> None:
|
|
10
|
+
self.log: bool = log
|
|
11
|
+
self.size: int = size
|
|
12
|
+
|
|
13
|
+
def setSize(self, size: int) -> None:
|
|
14
|
+
self.size = size
|
|
15
|
+
|
|
16
|
+
def setLog(self, log: bool) -> None:
|
|
17
|
+
self.log = log
|
|
18
|
+
|
|
19
|
+
def roll(self) -> int:
|
|
20
|
+
result: int = random.randint(1, self.size)
|
|
21
|
+
if self.log is True:
|
|
22
|
+
log.info(f"Dice of size {self.size} rolled result is {result}")
|
|
23
|
+
return result
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class customDice:
|
|
27
|
+
def __init__(self, faces: list[str], name: str, log: bool = True) -> None:
|
|
28
|
+
self.faces: list[str] = faces
|
|
29
|
+
self.name: str = name
|
|
30
|
+
self.log: bool = log
|
|
31
|
+
self.size: int = len(faces)
|
|
32
|
+
|
|
33
|
+
def roll(self) -> str:
|
|
34
|
+
face: str = random.choice(self.faces)
|
|
35
|
+
if self.log is True:
|
|
36
|
+
log.info(f"{self.name} dice roll result is {face}")
|
|
37
|
+
return face
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class dicePool:
|
|
41
|
+
def __init__(self, diceList: list[dice], log: bool = True) -> None:
|
|
42
|
+
self.log: bool = log
|
|
43
|
+
self.diceList: list[dice] = diceList
|
|
44
|
+
|
|
45
|
+
def roll(self) -> list[int]:
|
|
46
|
+
result: list[int] = [d.roll() for d in self.diceList]
|
|
47
|
+
if self.log is True:
|
|
48
|
+
log.info(f"Dice pool rolled with the following result {result}")
|
|
49
|
+
return result
|
|
50
|
+
|
|
51
|
+
def rollSum(self) -> int:
|
|
52
|
+
result: int = sum(self.roll())
|
|
53
|
+
if self.log is True:
|
|
54
|
+
log.info(f"Dice pool sum is {result}")
|
|
55
|
+
return result
|
|
56
|
+
|
|
57
|
+
def rollSuccesses(self, target: int) -> int:
|
|
58
|
+
result: int = sum([1 for roll in self.roll() if roll >= target])
|
|
59
|
+
if self.log is True:
|
|
60
|
+
log.info(f"Roll dice pool looking for {target}, {result} successes")
|
|
61
|
+
return result
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import socket
|
|
3
|
+
import logging
|
|
4
|
+
import mmap
|
|
5
|
+
from typing import Any
|
|
6
|
+
from datetime import datetime
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
log = logging.getLogger()
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class JsonFile:
|
|
13
|
+
def __init__(self, path: str,
|
|
14
|
+
type: str = "",
|
|
15
|
+
size: int = 1024,
|
|
16
|
+
read: bool = False,
|
|
17
|
+
write: bool = False) -> None:
|
|
18
|
+
self.clock: datetime = datetime.now()
|
|
19
|
+
self.content: dict[str, Any] = {}
|
|
20
|
+
self._setHostname()
|
|
21
|
+
self.read: bool = read
|
|
22
|
+
self.write: bool = write
|
|
23
|
+
|
|
24
|
+
if read is False and write is False:
|
|
25
|
+
msg = "No mode selected at class creation"
|
|
26
|
+
log.error(msg)
|
|
27
|
+
raise ValueError(msg)
|
|
28
|
+
|
|
29
|
+
if read is True and write is True:
|
|
30
|
+
msg = "Both mode selected at class creation, only one allowed"
|
|
31
|
+
log.error(msg)
|
|
32
|
+
raise ValueError(msg)
|
|
33
|
+
|
|
34
|
+
if type == "":
|
|
35
|
+
self._setType(path)
|
|
36
|
+
else:
|
|
37
|
+
self._setType(type)
|
|
38
|
+
|
|
39
|
+
self.size = size
|
|
40
|
+
self.path = path
|
|
41
|
+
|
|
42
|
+
if write is True:
|
|
43
|
+
filePtr = open(path, "wb")
|
|
44
|
+
filePtr.write(b"\x00" * size)
|
|
45
|
+
filePtr.close()
|
|
46
|
+
del filePtr
|
|
47
|
+
|
|
48
|
+
if read is True:
|
|
49
|
+
filePtr2 = open(path, "r+b")
|
|
50
|
+
filePtr2.seek(size-1)
|
|
51
|
+
filePtr2.write(b"\x00")
|
|
52
|
+
filePtr2.close()
|
|
53
|
+
del filePtr2
|
|
54
|
+
|
|
55
|
+
self.filePtr = open(path, "r+b")
|
|
56
|
+
self.fmap = mmap.mmap(self.filePtr.fileno(), size)
|
|
57
|
+
|
|
58
|
+
def __del__(self) -> None:
|
|
59
|
+
if hasattr(self, "fmap"):
|
|
60
|
+
self.fmap.close()
|
|
61
|
+
if self.read is True:
|
|
62
|
+
return
|
|
63
|
+
if self.write is True:
|
|
64
|
+
self.bufferedWrite()
|
|
65
|
+
|
|
66
|
+
def _setHostname(self) -> None:
|
|
67
|
+
self.content["Hostname"] = socket.gethostname()
|
|
68
|
+
|
|
69
|
+
def _setType(self, type: str) -> None:
|
|
70
|
+
self.content["type"] = type
|
|
71
|
+
|
|
72
|
+
def _setDate(self) -> None:
|
|
73
|
+
self.content["year"] = int(self.clock.year)
|
|
74
|
+
self.content["month"] = int(self.clock.month)
|
|
75
|
+
self.content["day"] = int(self.clock.day)
|
|
76
|
+
|
|
77
|
+
def _setTime(self) -> None:
|
|
78
|
+
self.content["hour"] = int(self.clock.hour)
|
|
79
|
+
self.content["minute"] = int(self.clock.minute)
|
|
80
|
+
self.content["second"] = int(self.clock.second)
|
|
81
|
+
|
|
82
|
+
def bufferedWrite(self) -> None:
|
|
83
|
+
FilePtr = open(self.path, "w")
|
|
84
|
+
json.dump(self.content, FilePtr, indent=4)
|
|
85
|
+
FilePtr.close()
|
|
86
|
+
|
|
87
|
+
def writeData(self, data: dict[str, Any]) -> int:
|
|
88
|
+
if self.write is False:
|
|
89
|
+
log.error("File was open without write mode and can't be written")
|
|
90
|
+
return 3
|
|
91
|
+
|
|
92
|
+
self.clock = datetime.now()
|
|
93
|
+
self._setDate()
|
|
94
|
+
self._setTime()
|
|
95
|
+
self.content["data"] = data
|
|
96
|
+
|
|
97
|
+
# Convert dict into bytes
|
|
98
|
+
file_bytes = json.dumps(self.content, indent=4).encode()
|
|
99
|
+
|
|
100
|
+
if len(file_bytes) > self.size:
|
|
101
|
+
log.error("File too small, too much data to dump")
|
|
102
|
+
return 1
|
|
103
|
+
|
|
104
|
+
# Clear memory map
|
|
105
|
+
self.fmap.seek(0)
|
|
106
|
+
self.fmap.write(b"\x00" * self.size)
|
|
107
|
+
|
|
108
|
+
# Write data
|
|
109
|
+
self.fmap.seek(0)
|
|
110
|
+
self.fmap.write(file_bytes)
|
|
111
|
+
|
|
112
|
+
return 0
|
|
113
|
+
|
|
114
|
+
def readData(self) -> int:
|
|
115
|
+
if self.read is False:
|
|
116
|
+
log.error("File was open without read mode and can't be read")
|
|
117
|
+
return 2
|
|
118
|
+
|
|
119
|
+
# Read the entire memory-mapped region (this IS the file)
|
|
120
|
+
raw = self.fmap[:]
|
|
121
|
+
|
|
122
|
+
# Stop at the first null byte (padding begins here)
|
|
123
|
+
json_bytes = raw.split(b"\x00", 1)[0]
|
|
124
|
+
|
|
125
|
+
# If file is empty or only padding
|
|
126
|
+
if not json_bytes.strip():
|
|
127
|
+
log.error("File is empty and can't be read")
|
|
128
|
+
return 3
|
|
129
|
+
|
|
130
|
+
# Decode and parse JSON
|
|
131
|
+
self.content = json.loads(json_bytes.decode())
|
|
132
|
+
return 0
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import articFileUtils as FU
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
log = logging.getLogger()
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def logFile(path: str, debugFlag: bool = False) -> None:
|
|
9
|
+
if debugFlag is True:
|
|
10
|
+
logFunc = log.debug
|
|
11
|
+
else:
|
|
12
|
+
logFunc = log.info
|
|
13
|
+
|
|
14
|
+
logFunc("===================================================")
|
|
15
|
+
logFunc(" Log contents of file")
|
|
16
|
+
logFunc(f" Path: {path}")
|
|
17
|
+
logFunc("===================================================")
|
|
18
|
+
if FU.fileExists(path):
|
|
19
|
+
FP = open(path, "r")
|
|
20
|
+
lines = FP.readlines()
|
|
21
|
+
for line in lines:
|
|
22
|
+
logFunc(line[:-1])
|
|
23
|
+
else:
|
|
24
|
+
log.error("File doesn't exist")
|
|
25
|
+
logFunc("===================================================")
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def logTestStart(title: str) -> None:
|
|
29
|
+
logSepDoubleLine()
|
|
30
|
+
log.info(f" {title}")
|
|
31
|
+
logSepDoubleLine()
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def logTestEnd() -> None:
|
|
35
|
+
logSepDoubleLine()
|
|
36
|
+
log.info(" Test finished")
|
|
37
|
+
logSepDoubleLine()
|
|
38
|
+
log.info("")
|
|
39
|
+
log.info("")
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def logSepDoubleLine() -> None:
|
|
43
|
+
log.info("===================================================")
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def logSepLine() -> None:
|
|
47
|
+
log.info("---------------------------------------------------")
|
|
File without changes
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import sqlite3
|
|
2
|
+
import logging
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
log = logging.getLogger()
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class sqliteEngine:
|
|
10
|
+
def __init__(self, dbPath: str):
|
|
11
|
+
self.con = sqlite3.connect(dbPath)
|
|
12
|
+
self.cursor = self.con.cursor()
|
|
13
|
+
|
|
14
|
+
def executeCommand(self, command: str) -> None:
|
|
15
|
+
log.debug(f"Executing command: {command}")
|
|
16
|
+
self.cursor.execute(command)
|
|
17
|
+
|
|
18
|
+
def createTable(self, name: str, columns: str) -> None:
|
|
19
|
+
self.executeCommand(f"CREATE TABLE {name} ({columns});")
|
|
20
|
+
|
|
21
|
+
def addColumn(self, table: str, column: str) -> None:
|
|
22
|
+
self.executeCommand(f"ALTER TABLE {table} ADD COLUMN {column};")
|
|
23
|
+
|
|
24
|
+
def deleteColumn(self, table: str, column: str) -> None:
|
|
25
|
+
self.executeCommand(f"ALTER TABLE {table} DROP COLUMN {column}")
|
|
26
|
+
|
|
27
|
+
def addEntry(self, table: str, columns: str, values: str) -> None:
|
|
28
|
+
self.executeCommand(f"INSERT INTO {table} ({columns}) VALUES ({values});")
|
|
29
|
+
|
|
30
|
+
def updateEntry(self, table: str, columns: str, values: str, condition: str) -> None:
|
|
31
|
+
self.executeCommand(
|
|
32
|
+
f'UPDATE {table} SET {columns} = "{values}" WHERE {condition};'
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
def deleteEntry(self, table: str, condition: str) -> None:
|
|
36
|
+
self.executeCommand(f"DELETE FROM {table} WHERE {condition};")
|
|
37
|
+
|
|
38
|
+
def readEntry(self, table: str, columns: str) -> list[Any]:
|
|
39
|
+
self.executeCommand(f"SELECT {columns} FROM {table};")
|
|
40
|
+
result = self.cursor.fetchall()
|
|
41
|
+
log.debug(f"Read entry: {result}")
|
|
42
|
+
return result
|
|
43
|
+
|
|
44
|
+
def readNumberOfEntries(self, table: str) -> Any:
|
|
45
|
+
self.executeCommand(f"SELECT COUNT(*) FROM {table};")
|
|
46
|
+
result = self.cursor.fetchall()
|
|
47
|
+
log.debug(f"Read number of entries: {result}")
|
|
48
|
+
return result[0][0]
|
|
49
|
+
|
|
50
|
+
def readEntryFiltered(self, table: str, columns: str, filter: str) -> list[Any]:
|
|
51
|
+
self.executeCommand(f"SELECT {columns} FROM {table} WHERE {filter};")
|
|
52
|
+
result = self.cursor.fetchall()
|
|
53
|
+
log.debug(f"Read entry: {result}")
|
|
54
|
+
return result
|
|
55
|
+
|
|
56
|
+
def entryExistsOnTable(self, table: str, condition: str) -> bool:
|
|
57
|
+
self.executeCommand(f"SELECT * FROM {table} WHERE {condition};")
|
|
58
|
+
entry = self.cursor.fetchall()
|
|
59
|
+
if len(entry) != 0:
|
|
60
|
+
return True
|
|
61
|
+
else:
|
|
62
|
+
return False
|
|
63
|
+
|
|
64
|
+
def deleteEntryFromTable(self, table: str, condition: str) -> None:
|
|
65
|
+
self.executeCommand(f"DELETE FROM {table} WHERE {condition};")
|
|
66
|
+
|
|
67
|
+
def commit(self) -> None:
|
|
68
|
+
self.con.commit()
|
|
69
|
+
|
|
70
|
+
def commitClose(self) -> None:
|
|
71
|
+
self.commit()
|
|
72
|
+
self.con.close()
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
[tool.poetry]
|
|
2
|
+
name = "articlib"
|
|
3
|
+
version = "0.2.9"
|
|
4
|
+
description = "Small set of tools and utilities in python. Destined to be use in my personal projects."
|
|
5
|
+
authors = ["Artic42 <engineer@artic42.com>"]
|
|
6
|
+
license = "MIT"
|
|
7
|
+
readme = "README.md"
|
|
8
|
+
|
|
9
|
+
[tool.poetry.dependencies]
|
|
10
|
+
python = "^3.10"
|
|
11
|
+
colorama = "^0.4.6"
|
|
12
|
+
pytest = "^9.0.2"
|
|
13
|
+
|
|
14
|
+
[tool.poetry.group.dev.dependencies]
|
|
15
|
+
flake8-pyproject = "^1.2.3"
|
|
16
|
+
flake8 = "^7.1.1"
|
|
17
|
+
mypy = "^1.11.2"
|
|
18
|
+
black = "^24.8.0"
|
|
19
|
+
freezegun = "^1.5.5"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
[tool.poetry.group.deb.dependencies]
|
|
23
|
+
types-colorama = "^0.4.15.20250801"
|
|
24
|
+
|
|
25
|
+
[tool.mypy]
|
|
26
|
+
python_version = "3.13"
|
|
27
|
+
strict = true
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
[build-system]
|
|
31
|
+
requires = ["poetry-core"]
|
|
32
|
+
build-backend = "poetry.core.masonry.api"
|