nt25 0.1.2__py3-none-any.whl → 0.1.4__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.
nt25/__init__.py CHANGED
@@ -4,6 +4,6 @@ from .lib import fio, calc, draw
4
4
  from .lib.draw import DType
5
5
 
6
6
  __version__ = meta.version(str(__package__))
7
- __samples_path__ = __file__.replace('__init__.py', 'samples')
7
+ __data_path__ = __file__.replace('__init__.py', 'data')
8
8
 
9
- __all__ = ('__version__', '__samples_path__', 'fio', 'calc', 'draw', 'DType')
9
+ __all__ = ('__version__', '__data_path__', 'fio', 'calc', 'draw', 'DType')
nt25/data/test.xlsx ADDED
Binary file
nt25/lib/calc.py CHANGED
@@ -92,7 +92,7 @@ def solveEq(eq, output=False, foo='solve'):
92
92
  s4e = re.sub(r'([-]?\d*\.\d+)', lambda m: str2e(m.group(0)), sol)
93
93
 
94
94
  if output:
95
- print(f'{foo}: {eq}\n x0 = {s4e}\n')
95
+ print(f'{foo}: {eq}\n\n x0 = {s4e}\n')
96
96
 
97
97
  real.append({
98
98
  'org': eq,
nt25/lib/et.py ADDED
@@ -0,0 +1,142 @@
1
+ import time
2
+ import json
3
+ import argparse
4
+
5
+ from datetime import UTC, datetime, timedelta, timezone
6
+ from exif import Image
7
+
8
+ VERSION = "0.1.1"
9
+
10
+ EPOCH = datetime.fromtimestamp(0, UTC)
11
+ IGNORE = ['orientation', 'maker_note', '_interoperability_ifd_Pointer',
12
+ 'components_configuration', 'scene_type', 'flashpix_version',
13
+ 'gps_processing_method',]
14
+
15
+
16
+ def dms2dec(dms: tuple):
17
+ d, m, s = dms
18
+ return d + m/60 + s/3600
19
+
20
+
21
+ def dtFormatter(str):
22
+ return datetime.strptime(str, '%Y:%m:%d %H:%M:%S')
23
+
24
+
25
+ def dt2str(dt):
26
+ return None if dt is None else dt.strftime('%Y-%m-%d %H:%M:%S')
27
+
28
+
29
+ def gpsDt2Dt(date, time, offset=8):
30
+ d = dtFormatter(f"{date} {int(time[0])}:{int(time[1])}:{int(time[2])}")
31
+ utc = d.replace(tzinfo=timezone.utc)
32
+ return utc.astimezone(timezone(timedelta(hours=offset)))
33
+
34
+
35
+ def tryGet(img, key, default):
36
+ value = default
37
+
38
+ try:
39
+ value = img[key]
40
+ except Exception:
41
+ pass
42
+
43
+ return value
44
+
45
+
46
+ def dumpExif(file):
47
+ result = {}
48
+ with open(file, 'rb') as f:
49
+ img = Image(f)
50
+ for key in img.get_all():
51
+ try:
52
+ result[key] = str(img[key])
53
+ except Exception:
54
+ pass
55
+
56
+ return result
57
+
58
+
59
+ def parseExif(file):
60
+ with open(file, 'rb') as f:
61
+ try:
62
+ img = Image(f)
63
+ except Exception:
64
+ return {}
65
+
66
+ width = tryGet(img, 'pixel_x_dimension', -1)
67
+ height = tryGet(img, 'pixel_y_dimension', -1)
68
+
69
+ if width < 0:
70
+ width = tryGet(img, 'image_width', -1)
71
+ height = tryGet(img, 'image_height', -1)
72
+
73
+ create = tryGet(img, 'datetime_original', None)
74
+ modify = tryGet(img, 'datetime', None)
75
+
76
+ # da = []
77
+ # for d in (d1, d2, d3):
78
+ # if d is not None:
79
+ # print(d)
80
+ # da.append(dtFormatter(d))
81
+ # dt = None if len(da) == 0 else max(da)
82
+
83
+ createDt = None if create is None else dtFormatter(create)
84
+ modifyDt = None if modify is None else dtFormatter(modify)
85
+
86
+ latitude = tryGet(img, 'gps_latitude', None)
87
+ latitude = None if latitude is None else dms2dec(latitude)
88
+
89
+ longitude = tryGet(img, 'gps_longitude', None)
90
+ longitude = None if longitude is None else dms2dec(longitude)
91
+
92
+ gpsDatetime = None
93
+ gd = tryGet(img, 'gps_datestamp', None)
94
+ gt = tryGet(img, 'gps_timestamp', None)
95
+
96
+ if gd and gt:
97
+ offset = int(time.localtime().tm_gmtoff / 3600)
98
+ gpsDatetime = gpsDt2Dt(gd, gt, offset=offset)
99
+
100
+ ts = -1 if createDt is None else int(createDt.timestamp())
101
+ mTs = -1 if modifyDt is None else int(modifyDt.timestamp())
102
+ gpsTs = -1 if gpsDatetime is None else int(gpsDatetime.timestamp())
103
+ offset = max(mTs, gpsTs) - ts
104
+ offsetDelta = datetime.fromtimestamp(offset, UTC) - EPOCH
105
+
106
+ return {
107
+ "width": width,
108
+ "height": height,
109
+ "latitude": latitude,
110
+ "longitude": longitude,
111
+ "datetime.create": dt2str(createDt),
112
+ "datetime.modify": dt2str(modifyDt),
113
+ "datetime.gps": dt2str(gpsDatetime),
114
+ "ts": ts,
115
+ "offset": offset,
116
+ "offset.delta": str(offsetDelta),
117
+ }
118
+
119
+
120
+ def main():
121
+ parser = argparse.ArgumentParser(description="EXIF tool")
122
+ parser.add_argument('-v', '--version',
123
+ help='echo version', action='store_true')
124
+ parser.add_argument('-d', '--dump', help='dump meta', action='store_true')
125
+ parser.add_argument('-f', '--file', type=str, help='image file')
126
+
127
+ args = parser.parse_args()
128
+
129
+ if args.version:
130
+ print(f"et: {VERSION}")
131
+ return
132
+
133
+ if args.file is None:
134
+ print("usage: et [-h] [-v] [-d] [-f FILE]")
135
+ return
136
+
137
+ r = dumpExif(args.file) if args.dump else parseExif(args.file)
138
+ print(json.dumps(r, indent=2, sort_keys=False))
139
+
140
+
141
+ if __name__ == "__main__":
142
+ main()
nt25/main.py CHANGED
@@ -1,4 +1,5 @@
1
- from nt25 import fio, calc, draw, DType, __version__, __samples_path__
1
+ import os
2
+ from nt25 import fio, calc, draw, DType, __version__, __data_path__
2
3
 
3
4
  # import timeit
4
5
  # timeit.timeit('', number=100, globals=globals())
@@ -8,14 +9,16 @@ def main():
8
9
  print(f"Hello from {__package__}({__version__})! ")
9
10
 
10
11
  # [Neo] some file I/O: csv, xlsx
12
+ os.makedirs('output', exist_ok=True)
13
+
11
14
  fio.saveCSV({
12
15
  'Name': ['Alice', 'Bob', 'Charlie'],
13
16
  'Age': [25.2, 30, 35],
14
17
  'City': ['New York', 'Los Angeles', 'Chicago']
15
- }, "out.csv", colsInline=False)
18
+ }, "output/out.csv")
16
19
 
17
- data = fio.getXlsx(__samples_path__ + '/test.xlsx')
18
- fio.saveCSV(data, "out2.csv")
20
+ data = fio.getXlsx(__data_path__ + '/test.xlsx')
21
+ fio.saveCSV(data, "output/out2.csv")
19
22
 
20
23
  h = data[0]
21
24
  s = data[1]
@@ -67,7 +70,7 @@ def main():
67
70
  bar = calc.solveEq(foo['eq'], output=True)
68
71
 
69
72
  if len(bar) > 0:
70
- print('s> 750, 1.5 ~', bar[0]['func'](y=[750], x1=1.5))
73
+ print(f'solveEq(750, 1.5) ~ {bar[0]['func'](y=[750], x1=1.5):.4f}')
71
74
 
72
75
 
73
76
  if __name__ == "__main__":
@@ -1,11 +1,13 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nt25
3
- Version: 0.1.2
3
+ Version: 0.1.4
4
4
  Summary: Neo's Tools of Python
5
5
  Requires-Python: >=3.10
6
+ Requires-Dist: exif>=1.6.1
6
7
  Requires-Dist: matplotlib>=3.10.6
7
8
  Requires-Dist: openpyxl>=3.1.5
8
9
  Requires-Dist: pandas>=2.3.2
10
+ Requires-Dist: pyinstaller>=6.15.0
9
11
  Requires-Dist: scikit-learn>=1.7.1
10
12
  Requires-Dist: sympy>=1.14.0
11
13
  Description-Content-Type: text/markdown
@@ -14,11 +16,12 @@ Description-Content-Type: text/markdown
14
16
 
15
17
  Neo's Tools of Python in 2025
16
18
 
17
- ## plans
19
+ ## TODO
18
20
 
19
21
  - [x] fio
20
22
  - [x] calc
21
23
  - [x] draw
24
+ - [x] et: EXIF tools
22
25
  - [ ] mysql
23
26
  - [ ] redis
24
27
  - [ ] maptrans
@@ -28,7 +31,10 @@ Neo's Tools of Python in 2025
28
31
 
29
32
  ```sh
30
33
  uv init
31
- uv run ntpy
34
+ # fio, calc, draw basic demo
35
+ uv run nt25
36
+ # et
37
+ uv run et
32
38
  ```
33
39
 
34
40
  ## fun
@@ -0,0 +1,11 @@
1
+ nt25/__init__.py,sha256=oIVhU2wegSHArUmypHr3nXQa8RRtH5TBZgRdRNN-OEw,274
2
+ nt25/main.py,sha256=nVzuGx8PaxDU-QbLmVtAvDkfEFX5Z5g0xE0CWf9EwBA,2098
3
+ nt25/data/test.xlsx,sha256=7C0JDS-TLm_KmjnKtfeajkpwGKSUhcLdr2W2UFUxAgM,10542
4
+ nt25/lib/calc.py,sha256=3X3k9jisSjRP7OokSdKvoVo4IIOzk2efexW8z1gMo-w,2265
5
+ nt25/lib/draw.py,sha256=OKTlkkNVUz_LGBA9Gk7fjcnbbbl7e_hT8nWKkcfeg2k,5642
6
+ nt25/lib/et.py,sha256=X_w-f2yb2jPtauPZKfbonDH4FhNFVOiFwZBp9yga8HE,3643
7
+ nt25/lib/fio.py,sha256=WvHpG6QYR1NE19Ss3Sy2FdajTxibX5SVW3PyC5Y5Krk,2525
8
+ nt25-0.1.4.dist-info/METADATA,sha256=DbMNv3C3BKdJ0yD2iQg7ii3-pQw3qEDFbjUZqJE8MLo,654
9
+ nt25-0.1.4.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
10
+ nt25-0.1.4.dist-info/entry_points.txt,sha256=6O4CnhT__4-ORTKEY9vV-MjSzJwDM4FFbVODQsN4Gr8,62
11
+ nt25-0.1.4.dist-info/RECORD,,
@@ -1,2 +1,3 @@
1
1
  [console_scripts]
2
+ et = nt25.lib.et:main
2
3
  nt25 = nt25.main:main
nt25/scripts/test3d.py DELETED
@@ -1,11 +0,0 @@
1
- from nt25 import fio, draw, DType
2
-
3
- csv = fio.getCSV('ds/92.csv', width=11, startLine=1)
4
- if isinstance(csv, list):
5
- P = csv[0]
6
- Pa = csv[1]
7
- R = csv[9]
8
-
9
- ref = draw.draw(DType.dot3d, P, Pa, R)
10
- draw.draw(DType.wireframe, P, Pa, R, ref=ref, color='orange')
11
- draw.show()
@@ -1,10 +0,0 @@
1
- nt25/__init__.py,sha256=1KKnRfR3-cwA0HPzYYIr4Y3MfHGIcV33gHGlK5j1qOo,283
2
- nt25/main.py,sha256=H7rpN_DHcd6vbKWjO0mxxETMC7t_u_4Qp-XBP_hSH7A,2046
3
- nt25/lib/calc.py,sha256=GKDuC8k1v4lwxFThyZx7CGNjPHeyVH3FTgNU2zCWJ44,2263
4
- nt25/lib/draw.py,sha256=OKTlkkNVUz_LGBA9Gk7fjcnbbbl7e_hT8nWKkcfeg2k,5642
5
- nt25/lib/fio.py,sha256=WvHpG6QYR1NE19Ss3Sy2FdajTxibX5SVW3PyC5Y5Krk,2525
6
- nt25/scripts/test3d.py,sha256=DIvCaUc2NoALTxu4E9h4oO9dvb_C2XI0R3QzR_PXR3c,274
7
- nt25-0.1.2.dist-info/METADATA,sha256=m2_4PF9bcl7BkQz6ICj8B1RjpGc6uHG3KxGwbzuQPDI,528
8
- nt25-0.1.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
9
- nt25-0.1.2.dist-info/entry_points.txt,sha256=mApCFA2DAwrQrcWa5JqcKq7lPzogm_Yi4EejBUo-Of8,40
10
- nt25-0.1.2.dist-info/RECORD,,
File without changes