oafuncs 0.0.98.44__py3-none-any.whl → 0.0.98.45__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.
- oafuncs/__init__.py +3 -1
- oafuncs/_script/email.py +5 -6
- oafuncs/oa_file.py +8 -4
- oafuncs/oa_geo.py +58 -8
- oafuncs/oa_linux.py +53 -0
- {oafuncs-0.0.98.44.dist-info → oafuncs-0.0.98.45.dist-info}/METADATA +2 -2
- {oafuncs-0.0.98.44.dist-info → oafuncs-0.0.98.45.dist-info}/RECORD +10 -9
- {oafuncs-0.0.98.44.dist-info → oafuncs-0.0.98.45.dist-info}/WHEEL +0 -0
- {oafuncs-0.0.98.44.dist-info → oafuncs-0.0.98.45.dist-info}/licenses/LICENSE.txt +0 -0
- {oafuncs-0.0.98.44.dist-info → oafuncs-0.0.98.45.dist-info}/top_level.txt +0 -0
oafuncs/__init__.py
CHANGED
@@ -42,4 +42,6 @@ from .oa_tool import *
|
|
42
42
|
from .oa_date import *
|
43
43
|
# ------------------- 2025-03-27 16:56:57 -------------------
|
44
44
|
from .oa_geo import *
|
45
|
-
# ------------------- 2025-09-04 14:08:26 -------------------
|
45
|
+
# ------------------- 2025-09-04 14:08:26 -------------------
|
46
|
+
from .oa_linux import *
|
47
|
+
# ------------------- 2025-09-14 12:30:00 -------------------
|
oafuncs/_script/email.py
CHANGED
@@ -1,14 +1,9 @@
|
|
1
|
-
import random
|
2
|
-
import smtplib
|
3
|
-
from email.header import Header
|
4
|
-
from email.mime.multipart import MIMEMultipart
|
5
|
-
from email.mime.text import MIMEText
|
6
|
-
|
7
1
|
from rich import print
|
8
2
|
|
9
3
|
__all__ = ["send"]
|
10
4
|
|
11
5
|
def _email_info():
|
6
|
+
import random
|
12
7
|
email_dict = {
|
13
8
|
"liukun0312@vip.qq.com": [4, 13, -10, 2, -10, 4, -7, -8, 8, -1, 3, -2, -11, -6, -9, -7],
|
14
9
|
"756866877@qq.com": [4, -2, -3, 13, 12, 8, -6, 9, -12, 13, -10, -12, -11, -12, -4, -11],
|
@@ -26,6 +21,10 @@ def _decode_password(password):
|
|
26
21
|
|
27
22
|
|
28
23
|
def _send_message(title, content, msg_to):
|
24
|
+
from email.header import Header
|
25
|
+
from email.mime.multipart import MIMEMultipart
|
26
|
+
from email.mime.text import MIMEText
|
27
|
+
import smtplib
|
29
28
|
# 1. 连接邮箱服务器
|
30
29
|
con = smtplib.SMTP_SSL("smtp.qq.com", 465)
|
31
30
|
|
oafuncs/oa_file.py
CHANGED
@@ -9,13 +9,14 @@ from rich import print
|
|
9
9
|
__all__ = ["find_file", "link_file", "copy_file", "rename_file", "move_file", "clear_folder", "remove_empty_folder", "remove", "file_size", "mean_size", "make_dir", "replace_content"]
|
10
10
|
|
11
11
|
|
12
|
-
def find_file(parent_dir: Union[str, os.PathLike], file_pattern: str, return_mode: str = "path") -> List[str]:
|
12
|
+
def find_file(parent_dir: Union[str, os.PathLike], file_pattern: str, return_mode: str = "path", deep_find: bool = False) -> List[str]:
|
13
13
|
"""Finds files matching a specified pattern.
|
14
14
|
|
15
15
|
Args:
|
16
16
|
parent_dir: The parent directory where to search for files
|
17
17
|
file_pattern: The file name pattern to search for
|
18
18
|
return_mode: Return mode, 'path' to return full file paths, 'file' to return only file names. Defaults to 'path'
|
19
|
+
deep_find: Whether to search recursively in subdirectories. Defaults to False
|
19
20
|
|
20
21
|
Returns:
|
21
22
|
A list of file paths or file names if files are found, otherwise an empty list
|
@@ -24,9 +25,12 @@ def find_file(parent_dir: Union[str, os.PathLike], file_pattern: str, return_mod
|
|
24
25
|
def natural_sort_key(s: str) -> List[Union[int, str]]:
|
25
26
|
"""Generate a key for natural sorting."""
|
26
27
|
return [int(text) if text.isdigit() else text.lower() for text in re.split("([0-9]+)", s)]
|
27
|
-
|
28
|
-
|
29
|
-
|
28
|
+
|
29
|
+
if deep_find:
|
30
|
+
search_pattern = os.path.join(str(parent_dir), "**", file_pattern)
|
31
|
+
else:
|
32
|
+
search_pattern = os.path.join(str(parent_dir), file_pattern)
|
33
|
+
matched_files = glob.glob(search_pattern, recursive=deep_find)
|
30
34
|
|
31
35
|
if not matched_files:
|
32
36
|
return []
|
oafuncs/oa_geo.py
CHANGED
@@ -135,14 +135,64 @@ def mask_land_ocean(
|
|
135
135
|
"""
|
136
136
|
mask = _land_sea_mask(lon, lat, keep)
|
137
137
|
|
138
|
-
#
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
)
|
138
|
+
# 将布尔掩膜转换为 xarray.DataArray
|
139
|
+
mask_da = xr.DataArray(mask, dims=("lat", "lon"))
|
140
|
+
|
141
|
+
# 如果输入已经是 xarray 对象,直接使用 where
|
142
|
+
if isinstance(data, (xr.DataArray, xr.Dataset)):
|
143
|
+
return data.where(mask_da)
|
144
|
+
|
145
|
+
# 如果输入是 numpy 数组,则假定最后两个维度是 (lat, lon)
|
146
|
+
if isinstance(data, np.ndarray):
|
147
|
+
arr = data
|
148
|
+
if arr.ndim < 2:
|
149
|
+
raise ValueError("numpy array 数据至少应包含 2 个维度 (lat, lon)")
|
150
|
+
|
151
|
+
if arr.ndim == 2:
|
152
|
+
lat_arr = np.asarray(lat)
|
153
|
+
lon_arr = np.asarray(lon)
|
154
|
+
# 支持 lat/lon 为 1D 或 2D
|
155
|
+
if lat_arr.ndim == 1 and lon_arr.ndim == 1:
|
156
|
+
da = xr.DataArray(arr, dims=("lat", "lon"), coords={"lat": lat_arr, "lon": lon_arr})
|
157
|
+
elif lat_arr.ndim == 2 and lon_arr.ndim == 2:
|
158
|
+
if lat_arr.shape != arr.shape or lon_arr.shape != arr.shape:
|
159
|
+
raise ValueError("提供的二维经纬度数组形状必须匹配数据的 (lat, lon) 维度")
|
160
|
+
da = xr.DataArray(arr, dims=("lat", "lon"), coords={"lat": (("lat", "lon"), lat_arr), "lon": (("lat", "lon"), lon_arr)})
|
161
|
+
else:
|
162
|
+
raise ValueError("lat/lon 必须同时为 1D 或同时为 2D")
|
163
|
+
else:
|
164
|
+
# 为前面的维度生成占位名称,例如 dim_0, dim_1, ...
|
165
|
+
leading_dims = [f"dim_{i}" for i in range(arr.ndim - 2)]
|
166
|
+
dims = leading_dims + ["lat", "lon"]
|
167
|
+
coords = {f"dim_{i}": np.arange(arr.shape[i]) for i in range(arr.ndim - 2)}
|
168
|
+
|
169
|
+
lat_arr = np.asarray(lat)
|
170
|
+
lon_arr = np.asarray(lon)
|
171
|
+
# 如果 lat/lon 为 1D
|
172
|
+
if lat_arr.ndim == 1 and lon_arr.ndim == 1:
|
173
|
+
if lat_arr.shape[0] != arr.shape[-2] or lon_arr.shape[0] != arr.shape[-1]:
|
174
|
+
raise ValueError("一维 lat/lon 长度必须匹配数据的最后两个维度")
|
175
|
+
coords.update({"lat": lat_arr, "lon": lon_arr})
|
176
|
+
# 如果 lat/lon 为 2D,要求其形状与数据最后两个维度一致
|
177
|
+
elif lat_arr.ndim == 2 and lon_arr.ndim == 2:
|
178
|
+
if lat_arr.shape != (arr.shape[-2], arr.shape[-1]) or lon_arr.shape != (arr.shape[-2], arr.shape[-1]):
|
179
|
+
raise ValueError("二维 lat/lon 的形状必须匹配数据的最后两个维度")
|
180
|
+
coords.update({"lat": (("lat", "lon"), lat_arr), "lon": (("lat", "lon"), lon_arr)})
|
181
|
+
else:
|
182
|
+
raise ValueError("lat/lon 必须同时为 1D 或同时为 2D")
|
183
|
+
|
184
|
+
da = xr.DataArray(arr, dims=dims, coords=coords)
|
185
|
+
|
186
|
+
masked = da.where(mask_da)
|
187
|
+
# 返回与输入相同的类型:numpy -> numpy
|
188
|
+
return masked.values
|
189
|
+
|
190
|
+
# 其他类型尝试转换为 DataArray
|
191
|
+
try:
|
192
|
+
da = xr.DataArray(data)
|
193
|
+
return da.where(mask_da)
|
194
|
+
except Exception:
|
195
|
+
raise TypeError("data must be xr.DataArray, xr.Dataset, or numpy.ndarray")
|
146
196
|
|
147
197
|
if __name__ == "__main__":
|
148
198
|
pass
|
oafuncs/oa_linux.py
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
from rich import print
|
2
|
+
|
3
|
+
|
4
|
+
__all__ = ["os_command", "get_queue_node"]
|
5
|
+
|
6
|
+
|
7
|
+
# 负责执行命令并返回输出
|
8
|
+
def os_command(cmd):
|
9
|
+
import subprocess
|
10
|
+
print(f'🔍 执行命令: {cmd}')
|
11
|
+
result = subprocess.run(
|
12
|
+
cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True
|
13
|
+
)
|
14
|
+
# 打印错误信息(若有,方便排查问题)
|
15
|
+
if result.stderr:
|
16
|
+
print(f'❌ 错误输出: {result.stderr.strip()}')
|
17
|
+
# 检查命令是否执行成功(非0为失败)
|
18
|
+
if result.returncode != 0:
|
19
|
+
print(f'❌ 命令执行失败,退出码: {result.returncode}')
|
20
|
+
return None
|
21
|
+
return result.stdout
|
22
|
+
|
23
|
+
# 返回“队列名:节点数”的字典
|
24
|
+
def get_queue_node():
|
25
|
+
import re
|
26
|
+
# 执行 sinfo | grep "idle" 获取空闲队列数据
|
27
|
+
cmd = 'sinfo | grep "idle"'
|
28
|
+
output = os_command(cmd)
|
29
|
+
if not output: # 命令执行失败或无输出,返回空字典
|
30
|
+
return {}
|
31
|
+
|
32
|
+
# 初始化结果字典:键=队列名,值=节点数
|
33
|
+
queue_node_dict = {}
|
34
|
+
# 按行解析命令输出
|
35
|
+
for line in output.strip().split('\n'):
|
36
|
+
line = line.strip()
|
37
|
+
if not line: # 跳过空行
|
38
|
+
continue
|
39
|
+
|
40
|
+
# 正则匹配:仅捕获“队列名”(第1组)和“节点数”(第2组)
|
41
|
+
# 末尾用 .* 忽略节点列表,不影响匹配
|
42
|
+
pattern = r"^(\S+)\s+\S+\s+\S+\s+(\d+)\s+idle\s+.*$"
|
43
|
+
match = re.match(pattern, line)
|
44
|
+
|
45
|
+
if match:
|
46
|
+
queue_name = match.group(1) # 提取队列名作为字典的键
|
47
|
+
node_count = int(match.group(2))# 提取节点数作为字典的值(转为整数)
|
48
|
+
queue_node_dict[queue_name] = node_count # 存入字典
|
49
|
+
|
50
|
+
return queue_node_dict
|
51
|
+
|
52
|
+
if __name__ == "__main__":
|
53
|
+
pass
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: oafuncs
|
3
|
-
Version: 0.0.98.
|
3
|
+
Version: 0.0.98.45
|
4
4
|
Summary: Oceanic and Atmospheric Functions
|
5
5
|
Home-page: https://github.com/Industry-Pays/OAFuncs
|
6
6
|
Author: Kun Liu
|
@@ -187,4 +187,4 @@ query()
|
|
187
187
|
<img title="OAFuncs" src="https://raw.githubusercontent.com/Industry-Pays/OAFuncs/main/oafuncs/_data/oafuncs.png" alt="OAFuncs">
|
188
188
|
|
189
189
|
## Wiki
|
190
|
-
更多内容,查看[
|
190
|
+
更多内容,查看[wiki_old](https://opendeep.wiki/Industry-Pays/OAFuncs/introduction) or [wiki_new](https://deepwiki.com/Industry-Pays/OAFuncs)
|
@@ -1,11 +1,12 @@
|
|
1
|
-
oafuncs/__init__.py,sha256=
|
1
|
+
oafuncs/__init__.py,sha256=G523BFVPxmODwq8j_88NYEiKbCzdQ3jfy51cmLeh7kM,1630
|
2
2
|
oafuncs/oa_cmap.py,sha256=JwZMJ36uNwiCnzXqEtH2_PpeLtEaRaXP9YeGSl0PJSU,13886
|
3
3
|
oafuncs/oa_data.py,sha256=CG2YHY_R6MFrPw3UznT4T8BE8yXdgBMnmdUAEdh9GAo,6506
|
4
4
|
oafuncs/oa_date.py,sha256=aU2wVIWXyWoRiSQ9dg8sHvShFTxw86RrgbV3Q6tDjD4,6841
|
5
5
|
oafuncs/oa_draw.py,sha256=zal0Y3RPpN0TCGN4Gw9qLtjQdT6V0ZqpSUBFVOPL0x4,13952
|
6
|
-
oafuncs/oa_file.py,sha256=
|
7
|
-
oafuncs/oa_geo.py,sha256=
|
6
|
+
oafuncs/oa_file.py,sha256=j9NOjxPOeAJsD5Zk4ODmFdVSSgr1CHVPvM1IHXy9RQA,17546
|
7
|
+
oafuncs/oa_geo.py,sha256=UbzvUqgT2QP_9B7XSJRL1HDmGu0HnLC5nSP6ZrA5WH4,7177
|
8
8
|
oafuncs/oa_help.py,sha256=0J5VaZX-cB0c090KxgmktQJBc0o00FsY-4wB8l5y00k,4178
|
9
|
+
oafuncs/oa_linux.py,sha256=1fCmpgM33x3fLdQm8DOmaE4Pz4okXVNO0e-3svYnLgo,1850
|
9
10
|
oafuncs/oa_nc.py,sha256=j501NlTuvrDIwNLXbMfE7nPPXdbbL7u9PGDj2l5AtnI,16277
|
10
11
|
oafuncs/oa_python.py,sha256=xYMQnM0cGq9xUCtcoMpnN0LG5Rc_s94tai5nC6CNJ3E,4831
|
11
12
|
oafuncs/oa_tool.py,sha256=VHx15VqpbzNlVXh0-3nJqcDgLVaECMD1FvxJ_CrV39E,8046
|
@@ -13,7 +14,7 @@ oafuncs/_data/hycom.png,sha256=MadKs6Gyj5n9-TOu7L4atQfTXtF9dvN9w-tdU9IfygI,10945
|
|
13
14
|
oafuncs/_data/oafuncs.png,sha256=o3VD7wm-kwDea5E98JqxXl04_78cBX7VcdUt7uQXGiU,3679898
|
14
15
|
oafuncs/_script/cprogressbar.py,sha256=BZi3MzF4q2Yl6fdZcLnW8MdpgpLeldI5NvnWMr-ZS94,16023
|
15
16
|
oafuncs/_script/data_interp.py,sha256=gr1coA2N1mxzS4iv6S0C4lZpEQbuuHHNW-08RrhgPAA,4774
|
16
|
-
oafuncs/_script/email.py,sha256=
|
17
|
+
oafuncs/_script/email.py,sha256=57jhRflm5QsyIshGMqtlfC6qn3b86GyiL4RQxdCOgxU,2702
|
17
18
|
oafuncs/_script/netcdf_merge.py,sha256=tM9ePqLiEsE7eIsNM5XjEYeXwxjYOdNz5ejnEuI7xKw,6066
|
18
19
|
oafuncs/_script/netcdf_modify.py,sha256=XDlAEToe_lwfAetkBSENqU5df-wnH7MGuxNTjG1gwHY,4178
|
19
20
|
oafuncs/_script/netcdf_write.py,sha256=EDNycnhlrW1c6zcpmpObQeszDRX_lRxjTL-j0G4HqjI,17420
|
@@ -38,8 +39,8 @@ oafuncs/oa_sign/__init__.py,sha256=JSx1fcWpmNhQBvX_Bmq3xysfSkkFMrjbJASxV_V6aqE,1
|
|
38
39
|
oafuncs/oa_sign/meteorological.py,sha256=3MSjy7HTcvz2zsITkjUMr_0Y027Gas1LFE9pk99990k,6110
|
39
40
|
oafuncs/oa_sign/ocean.py,sha256=3uYEzaq-27yVy23IQoqy-clhWu1I_fhPFBAQyT-OF4M,5562
|
40
41
|
oafuncs/oa_sign/scientific.py,sha256=moIl2MEY4uitbXoD596JmXookXGQtQsS-8_1NBBTx84,4689
|
41
|
-
oafuncs-0.0.98.
|
42
|
-
oafuncs-0.0.98.
|
43
|
-
oafuncs-0.0.98.
|
44
|
-
oafuncs-0.0.98.
|
45
|
-
oafuncs-0.0.98.
|
42
|
+
oafuncs-0.0.98.45.dist-info/licenses/LICENSE.txt,sha256=rMtLpVg8sKiSlwClfR9w_Dd_5WubTQgoOzE2PDFxzs4,1074
|
43
|
+
oafuncs-0.0.98.45.dist-info/METADATA,sha256=JgC1BWV311BvpDhuyBam21v_fnaplfmdxKJkmakfM4s,4446
|
44
|
+
oafuncs-0.0.98.45.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
45
|
+
oafuncs-0.0.98.45.dist-info/top_level.txt,sha256=bgC35QkXbN4EmPHEveg_xGIZ5i9NNPYWqtJqaKqTPsQ,8
|
46
|
+
oafuncs-0.0.98.45.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|