edi-core 1.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
edi_core/__init__.py ADDED
File without changes
@@ -0,0 +1 @@
1
+ https://help.aliyun.com/zh/oss/developer-reference/get-started-with-oss-sdk-for-python-v2?spm=a2c4g.11186623.help-menu-31815.d_5_2_2_0_0.31152f8dIqy5FD
File without changes
edi_core/cloud/oss.py ADDED
@@ -0,0 +1,64 @@
1
+ from abc import ABC
2
+
3
+ import alibabacloud_oss_v2 as oss
4
+ from alibabacloud_oss_v2 import GetObjectMetaRequest, GetObjectMetaResult, GetObjectRequest
5
+ from loguru import logger as log
6
+
7
+
8
+ class OssClient(ABC):
9
+ def get_object_metadata(self, key: str):
10
+ pass
11
+
12
+ def get_object(self, key: str, target_path: str):
13
+ pass
14
+
15
+ def put_object(self, source_path: str, key: str):
16
+ pass
17
+
18
+ def delete_object(self, key: str):
19
+ pass
20
+
21
+
22
+ class AliCloudOssClient(OssClient):
23
+ _config: oss.Config
24
+ _client: oss.Client
25
+ _bucket: str
26
+
27
+ def __init__(self, region: str, access_key: str, secret_key: str, endpoint: str, bucket: str):
28
+ config = oss.config.load_default()
29
+ config.region = region
30
+ config.credentials_provider = oss.credentials.StaticCredentialsProvider(access_key_id=access_key,
31
+ access_key_secret=secret_key)
32
+ config.endpoint = endpoint
33
+ self._config = config
34
+ self.print_config()
35
+ self._client = oss.Client(self._config)
36
+ self._bucket = bucket
37
+
38
+ def get_config(self) -> oss.Config:
39
+ return self._config
40
+
41
+ def get_object_metadata(self, key: str) -> GetObjectMetaResult:
42
+ request = GetObjectMetaRequest(bucket=self._bucket, key=key)
43
+ return self._client.get_object_meta(request)
44
+
45
+ def get_object(self, key: str, target_path: str):
46
+ log.debug(f"downloading {key} to {target_path}")
47
+ request = GetObjectRequest(bucket=self._bucket, key=key)
48
+ result = self._client.get_object(request)
49
+ with result.body as body_stream:
50
+ data = body_stream.read()
51
+ with open(target_path, 'wb') as f:
52
+ f.write(data)
53
+ log.debug(f"finished downloading {key} to {target_path}")
54
+
55
+ def put_object(self, source_path: str, key: str):
56
+ pass
57
+
58
+ def delete_object(self, key: str):
59
+ pass
60
+
61
+ def print_config(self):
62
+ config_attrs = vars(self._config)
63
+ config_str = "\n".join(f"{key}={value}" for key, value in config_attrs.items())
64
+ log.debug(f"OSS Config: {config_str}")
File without changes
@@ -0,0 +1,50 @@
1
+ from abc import ABC, abstractmethod
2
+ from enum import Enum
3
+
4
+ from tinydb import TinyDB, Query
5
+
6
+ from edi_core.utils.file import get_path_from_app_dir, ensure_parent_dir_exists
7
+
8
+
9
+ class CreateStrategy(Enum):
10
+ RECREATE = 1
11
+ REUSE_IF_PRESENT = 2
12
+
13
+
14
+ class SimpleKV(ABC):
15
+ @abstractmethod
16
+ def get_str(self, key: str):
17
+ raise NotImplementedError("get_str is not implemented")
18
+
19
+ @abstractmethod
20
+ def set_str(self, key: str, value: str):
21
+ raise NotImplementedError("set_str is not implemented")
22
+
23
+ @abstractmethod
24
+ def print_all(self):
25
+ raise NotImplementedError("print_all is not implemented")
26
+
27
+
28
+ class TinyDbSimpleKV(SimpleKV):
29
+ db_path: str
30
+ db: TinyDB
31
+
32
+ def __init__(self, db_name: str, create_strategy: CreateStrategy = CreateStrategy.RECREATE):
33
+ self.db_path = get_path_from_app_dir(db_name)
34
+ ensure_parent_dir_exists(self.db_path)
35
+ self.db = TinyDB(f'{self.db_path}.json')
36
+ if create_strategy == CreateStrategy.RECREATE:
37
+ self.db.truncate()
38
+
39
+ def get_str(self, key: str):
40
+ results = self.db.search(Query().key == key)
41
+ if len(results) == 0:
42
+ raise KeyError("Key not found")
43
+ return results[0]["value"]
44
+
45
+ def set_str(self, key: str, value: str):
46
+ self.db.remove(Query().key == key)
47
+ self.db.insert({"key": key, "value": value})
48
+
49
+ def print_all(self):
50
+ return self.db.all()
edi_core/db/tinydb.py ADDED
@@ -0,0 +1,23 @@
1
+ from typing import List
2
+
3
+ from tinydb import TinyDB
4
+ from tinydb.table import Document
5
+
6
+
7
+ class Tiny:
8
+ _db = None
9
+
10
+ def __init__(self, db_file):
11
+ self._db = TinyDB(db_file)
12
+
13
+ def insert(self, data: dict) -> int:
14
+ return self._db.insert(data)
15
+
16
+ def all(self) -> List[Document]:
17
+ return self._db.all()
18
+
19
+ def search(self, query) -> List[Document]:
20
+ return self._db.search(query)
21
+
22
+ def close(self):
23
+ self._db.close()
File without changes
edi_core/scm/git.py ADDED
@@ -0,0 +1,6 @@
1
+ import git
2
+
3
+
4
+ def clone_and_checkout(path, url, branch):
5
+ repo = git.Repo.clone_from(url=url, to_path=path)
6
+ repo.create_head(branch).checkout()
File without changes
@@ -0,0 +1,22 @@
1
+ import os.path
2
+ from typing import Callable
3
+
4
+
5
+ def assert_file_exists(
6
+ path: str,
7
+ exception_supplier: Callable[[], Exception] | None = None,
8
+ ):
9
+ if not os.path.isfile(path):
10
+ if exception_supplier is None:
11
+ raise ValueError(f"File does not exist: {path}")
12
+ raise exception_supplier()
13
+
14
+
15
+ def assert_dir_exists(
16
+ path: str,
17
+ exception_supplier: Callable[[], Exception] | None = None,
18
+ ):
19
+ if not os.path.isdir(path):
20
+ if exception_supplier is None:
21
+ raise ValueError(f"Directory does not exist: {path}")
22
+ raise exception_supplier()
@@ -0,0 +1,37 @@
1
+ import os
2
+ import subprocess
3
+
4
+ from edi_core.utils.assertions import assert_file_exists
5
+ from edi_core.utils.file import dir_exists, get_file_name_without_extension
6
+
7
+
8
+ def extract_audio(file_path: str, output_path: str):
9
+ assert_file_exists(file_path)
10
+ result = subprocess.run(
11
+ ['ffmpeg', '-i', file_path, '-q:a', '0', '-map', 'a', output_path],
12
+ stdout=subprocess.PIPE,
13
+ stderr=subprocess.PIPE
14
+ )
15
+ if result.returncode != 0:
16
+ raise RuntimeError(f"Failed to extract audio: {result.stderr.decode()}")
17
+
18
+
19
+ def split_audio(source_file: str, dest_dir: str, seconds: int):
20
+ assert_file_exists(source_file)
21
+ if not dir_exists(dest_dir):
22
+ os.makedirs(dest_dir, exist_ok=True)
23
+
24
+ base_name = get_file_name_without_extension(source_file)
25
+ segment_pattern = os.path.join(dest_dir, f"{base_name}_%03d.mp3")
26
+
27
+ result = subprocess.run(
28
+ ['ffmpeg', '-i', source_file, '-f', 'segment', '-segment_time', str(seconds), '-c', 'copy', segment_pattern],
29
+ stdout=subprocess.PIPE,
30
+ stderr=subprocess.PIPE
31
+ )
32
+
33
+ if result.returncode != 0:
34
+ raise RuntimeError(f"Failed to split audio: {result.stderr.decode()}")
35
+
36
+ return sorted([os.path.join(dest_dir, f) for f in os.listdir(dest_dir) if f.startswith(base_name)],
37
+ key=lambda x: x.lower())
edi_core/utils/cli.py ADDED
@@ -0,0 +1,51 @@
1
+ from typing import TypeVar, Callable
2
+
3
+ T = TypeVar('T')
4
+
5
+
6
+ def select_option(
7
+ options: list[T],
8
+ prompt: str,
9
+ display_names: list[str] | None = None,
10
+ to_display_name: Callable[[T], str] | None = None,
11
+ ) -> T:
12
+ """
13
+ Interactive CLI option selector.
14
+
15
+ Args:
16
+ options: List of options to choose from
17
+ prompt: Message to display to the user
18
+ display_names: Optional list of display names for options
19
+ to_display_name: Optional function to convert option to display name
20
+ (used if display_names is not provided)
21
+
22
+ Returns:
23
+ The selected option
24
+
25
+ Raises:
26
+ ValueError: If options is empty or display_names length doesn't match
27
+ """
28
+ if not options:
29
+ raise ValueError("Options list cannot be empty")
30
+
31
+ if display_names is not None:
32
+ if len(display_names) != len(options):
33
+ raise ValueError("display_names must have same length as options")
34
+ names = display_names
35
+ elif to_display_name is not None:
36
+ names = [to_display_name(opt) for opt in options]
37
+ else:
38
+ names = [str(opt) for opt in options]
39
+
40
+ print(prompt)
41
+ for i, name in enumerate(names, start=1):
42
+ print(f"{i}. {name}")
43
+
44
+ while True:
45
+ try:
46
+ choice = int(input())
47
+ if 1 <= choice <= len(options):
48
+ return options[choice - 1]
49
+ print(f"Please enter a number between 1 and {len(options)}")
50
+ except ValueError:
51
+ print("Please enter a valid number")
@@ -0,0 +1,6 @@
1
+ import pendulum
2
+ from pendulum import DateTime
3
+
4
+
5
+ def now(tz='Asia/Shanghai') -> DateTime:
6
+ return pendulum.now(tz)
@@ -0,0 +1,25 @@
1
+ import base64
2
+
3
+
4
+ def encode_base64_str(data: str, encoding: str = 'utf-8'):
5
+ data_bytes = data.encode(encoding)
6
+ base64_bytes = base64.b64encode(data_bytes)
7
+ return base64_bytes.decode(encoding)
8
+
9
+
10
+ def decode_base64_str(data: str, encoding: str = 'utf-8'):
11
+ data_bytes = data.encode(encoding)
12
+ base64_bytes = base64.b64decode(data_bytes)
13
+ return base64_bytes.decode(encoding)
14
+
15
+
16
+ def encode_base64_bytes(data: bytes):
17
+ return base64.b64encode(data)
18
+
19
+
20
+ def decode_base64_bytes(data: bytes):
21
+ return base64.b64decode(data)
22
+
23
+
24
+ def to_bytes(data: str, encoding: str = 'utf-8'):
25
+ return data.encode(encoding)
edi_core/utils/env.py ADDED
@@ -0,0 +1,41 @@
1
+ import os
2
+ import platform
3
+
4
+ from dotenv import load_dotenv
5
+
6
+ dotenv_loaded = False
7
+
8
+
9
+ def load_env():
10
+ global dotenv_loaded
11
+ if not dotenv_loaded:
12
+ load_dotenv()
13
+ dotenv_loaded = True
14
+
15
+
16
+ def get_env(key: str, default: str = None):
17
+ load_env()
18
+ value = os.environ.get(key)
19
+ if value:
20
+ return value
21
+
22
+ if default is None:
23
+ raise ValueError(f'Env {key} not found')
24
+ else:
25
+ return default
26
+
27
+
28
+ def get_platform():
29
+ return platform.platform()
30
+
31
+
32
+ def get_system():
33
+ return platform.system()
34
+
35
+
36
+ def is_windows():
37
+ return get_system() == 'Windows'
38
+
39
+
40
+ def get_app_name():
41
+ return get_env('APP_NAME')
edi_core/utils/file.py ADDED
@@ -0,0 +1,98 @@
1
+ import os.path
2
+ import shutil
3
+ from io import TextIOWrapper
4
+
5
+ from edi_core.utils.assertions import assert_file_exists
6
+ from edi_core.utils.env import get_env
7
+
8
+
9
+ def dir_exists(path: str) -> bool:
10
+ return os.path.isdir(path)
11
+
12
+
13
+ def file_exists(path: str) -> bool:
14
+ return os.path.isfile(path)
15
+
16
+
17
+ def path_exists(path: str) -> bool:
18
+ return os.path.exists(path)
19
+
20
+
21
+ def move(src, dest):
22
+ shutil.move(src, dest)
23
+
24
+
25
+ def delete_file(path: str):
26
+ if file_exists(path):
27
+ os.remove(path)
28
+
29
+
30
+ def get_file_name_without_extension(path: str) -> str:
31
+ base_name = os.path.basename(path)
32
+ name_without_extension, _ = os.path.splitext(base_name)
33
+ return name_without_extension
34
+
35
+
36
+ def get_file_name_with_extension(path: str) -> str:
37
+ return os.path.basename(path)
38
+
39
+
40
+ def get_output_dir():
41
+ return os.path.expanduser(get_env("OUTPUT_DIR"))
42
+
43
+
44
+ def get_input_dir():
45
+ return os.path.expanduser(get_env("INPUT_DIR"))
46
+
47
+
48
+ def to_output_path(relative_path: str):
49
+ return os.path.join(get_output_dir(), relative_path)
50
+
51
+
52
+ def to_input_path(relative_path: str):
53
+ return os.path.join(get_input_dir(), relative_path)
54
+
55
+
56
+ def ensure_parent_dir_exists(path: str):
57
+ os.makedirs(os.path.dirname(path), exist_ok=True)
58
+
59
+
60
+ def save_str_to_output_dir(relative_path: str, content: str) -> str:
61
+ output_path = to_output_path(relative_path)
62
+ ensure_parent_dir_exists(output_path)
63
+ with open(output_path, 'w', encoding='utf-8') as file:
64
+ file.write(content)
65
+ return output_path
66
+
67
+
68
+ def save_bytes_to_output_dir(relative_path: str, content: bytes) -> str:
69
+ output_path = to_output_path(relative_path)
70
+ ensure_parent_dir_exists(output_path)
71
+ with open(output_path, 'wb') as file:
72
+ file.write(content)
73
+ return output_path
74
+
75
+
76
+ def open_file_in_output_for_write(relative_path: str) -> TextIOWrapper:
77
+ output_path = to_output_path(relative_path)
78
+ ensure_parent_dir_exists(output_path)
79
+ return open(output_path, 'w', encoding='utf-8')
80
+
81
+
82
+ def open_file_in_input_for_read(relative_path: str) -> TextIOWrapper:
83
+ input_path = to_input_path(relative_path)
84
+ return open(input_path, 'r', encoding='utf-8')
85
+
86
+
87
+ def read_file(path: str) -> str:
88
+ assert_file_exists(path)
89
+ with open(path, 'r', encoding='utf-8') as file:
90
+ return file.read()
91
+
92
+
93
+ def get_app_dir():
94
+ return os.path.expanduser(get_env("APP_DIR"))
95
+
96
+
97
+ def get_path_from_app_dir(relative_path: str) -> str:
98
+ return os.path.join(get_app_dir(), relative_path)
@@ -0,0 +1,5 @@
1
+ import filetype
2
+
3
+
4
+ def guess_mime(file):
5
+ return filetype.guess_mime(file)
edi_core/utils/hash.py ADDED
@@ -0,0 +1,9 @@
1
+ import hashlib
2
+
3
+ algorithms_available = hashlib.algorithms_available
4
+
5
+
6
+ def hash_str(data: str, algo='md5') -> str:
7
+ h = hashlib.new(algo)
8
+ h.update(data.encode('utf-8'))
9
+ return h.hexdigest()
File without changes
@@ -0,0 +1,5 @@
1
+ import pyautogui
2
+
3
+
4
+ def write(text: str, interval: float = 0.1):
5
+ pyautogui.write(text, interval)
edi_core/win/reg.py ADDED
@@ -0,0 +1,24 @@
1
+ import winreg
2
+
3
+ SUB_KEY_KEYBOARD_LAYOUT = "SYSTEM\CurrentControlSet\Control\Keyboard Layout"
4
+
5
+ type_dict = {
6
+ winreg.REG_BINARY: "Binary",
7
+ winreg.REG_SZ: "String",
8
+ winreg.REG_EXPAND_SZ: "Expandable String",
9
+ winreg.REG_DWORD: "DWORD (32-bit number)",
10
+ winreg.REG_MULTI_SZ: "Multi-String",
11
+ winreg.REG_QWORD: "QWORD (64-bit number)",
12
+ }
13
+
14
+
15
+ def read_key_for_current_user(sub_key: str, name: str):
16
+ hkey = winreg.OpenKey(winreg.HKEY_CURRENT_USER, sub_key, 0, winreg.KEY_READ)
17
+ return winreg.QueryValueEx(hkey, name)
18
+
19
+
20
+ def get_keyboard_scancode_map():
21
+ hkey = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, SUB_KEY_KEYBOARD_LAYOUT, 0, winreg.KEY_READ)
22
+ value, type_id = winreg.QueryValueEx(hkey, "Scancode Map")
23
+ print(type_id)
24
+ print(value[0])
@@ -0,0 +1,28 @@
1
+ Metadata-Version: 2.4
2
+ Name: edi-core
3
+ Version: 1.1.0
4
+ Summary:
5
+ License: MIT
6
+ Author: LtttAZ
7
+ Author-email: louis_zhang_1303@126.com
8
+ Requires-Python: ==3.13.2
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Programming Language :: Python :: 3
11
+ Requires-Dist: alibabacloud-oss-v2 (>=1.2.1,<2.0.0)
12
+ Requires-Dist: filetype (>=1.2.0,<2.0.0)
13
+ Requires-Dist: gitpython (>=3.1.45,<4.0.0)
14
+ Requires-Dist: loguru (>=0.7.3,<0.8.0)
15
+ Requires-Dist: matplotlib (>=3.10.7,<4.0.0)
16
+ Requires-Dist: packaging (>=25.0,<26.0)
17
+ Requires-Dist: pendulum (>=3.1.0,<4.0.0)
18
+ Requires-Dist: poetry-core (>=2.2.1,<3.0.0)
19
+ Requires-Dist: pyautogui (>=0.9.54,<0.10.0)
20
+ Requires-Dist: pydantic (>=2.12.4,<3.0.0)
21
+ Requires-Dist: python-dotenv (>=1.2.1,<2.0.0)
22
+ Requires-Dist: requests (>=2.32.5,<3.0.0)
23
+ Requires-Dist: tinydb (>=4.8.2,<5.0.0)
24
+ Description-Content-Type: text/markdown
25
+
26
+ # EDI
27
+
28
+ [PYPI](https://pypi.org/project/edi-core/)
@@ -0,0 +1,25 @@
1
+ edi_core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ edi_core/cloud/README.md,sha256=eORYs2CT7hkm2i2A7xmREaW4I_Scl1uTbYNwu8TzhhI,151
3
+ edi_core/cloud/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ edi_core/cloud/oss.py,sha256=8w5CB1INo1e69VJSjPhbvnqKOUig3JjF3fDMcags1uE,2151
5
+ edi_core/db/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
+ edi_core/db/simple_kv.py,sha256=8PzWUzwuOOWUXhVr9CzsNf1ljeqaLJvzgw5eLlfaLcg,1420
7
+ edi_core/db/tinydb.py,sha256=DB8trZcsxsLPSwlQjJemlOs68F4AcEMJJmyK0x7BXg0,463
8
+ edi_core/scm/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
+ edi_core/scm/git.py,sha256=-TVM0_IDd34zAuJPZweksTItzqhUV75DtG4YXe9qQvM,150
10
+ edi_core/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
+ edi_core/utils/assertions.py,sha256=HFgfxOkNUp0RkI2NpAthy7wnlfBEJSYL3zps6JNrG7Y,596
12
+ edi_core/utils/audio.py,sha256=gBLIntKW7ScLiN93jl_SJtM1LHMYtTUftxc-EGxVGO8,1325
13
+ edi_core/utils/cli.py,sha256=40_YXu8NCcC4_7Z650Clb49nk-xgG9zd5suvWO459XE,1524
14
+ edi_core/utils/datetime.py,sha256=sctJzZw_Uruq-qp6IW_JuqZvcJq_fXDbTMpfEwkAPlg,117
15
+ edi_core/utils/encoding.py,sha256=6QgiPsuKPE8rhTkNqC7KqEzV_41YilykMW2LqbkDLf0,625
16
+ edi_core/utils/env.py,sha256=r4ffjexjq8tUh-8xlailykoKO7OiM23rx6DDuYc_3l0,648
17
+ edi_core/utils/file.py,sha256=ohlwL2bMTyhwA239vv4VgVH6sud-CK5rIKGJWrG4JKY,2454
18
+ edi_core/utils/filetype.py,sha256=yfGavZWzMkbmRUeYPFySZ7xVd1olbeCBc7oQVOMvtCE,77
19
+ edi_core/utils/hash.py,sha256=9YRLLDbPdTlZok6zGKfCLmDL9wXUpYVyHcTgz1Gt0Yc,200
20
+ edi_core/win/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
21
+ edi_core/win/control.py,sha256=W8JROSAAc900qUWNlCIQV9BikqeAtEycOXxA_7Eimqc,100
22
+ edi_core/win/reg.py,sha256=-l5aCGvyHtRY9pclxrov9-FtbkCroZInX9RFh6a0rpM,770
23
+ edi_core-1.1.0.dist-info/METADATA,sha256=sgZfP515CvmEJM8737bTvvdBdPPRsQnanTx7BpTeF7Q,902
24
+ edi_core-1.1.0.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
25
+ edi_core-1.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: poetry-core 2.2.1
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any