py2ls 0.1.10.12__py3-none-any.whl → 0.2.7.10__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.
Potentially problematic release.
This version of py2ls might be problematic. Click here for more details.
- py2ls/.DS_Store +0 -0
- py2ls/.git/.DS_Store +0 -0
- py2ls/.git/index +0 -0
- py2ls/.git/logs/refs/remotes/origin/HEAD +1 -0
- py2ls/.git/objects/.DS_Store +0 -0
- py2ls/.git/refs/.DS_Store +0 -0
- py2ls/ImageLoader.py +621 -0
- py2ls/__init__.py +7 -5
- py2ls/apptainer2ls.py +3940 -0
- py2ls/batman.py +164 -42
- py2ls/bio.py +2595 -0
- py2ls/cell_image_clf.py +1632 -0
- py2ls/container2ls.py +4635 -0
- py2ls/corr.py +475 -0
- py2ls/data/.DS_Store +0 -0
- py2ls/data/email/email_html_template.html +88 -0
- py2ls/data/hyper_param_autogluon_zeroshot2024.json +2383 -0
- py2ls/data/hyper_param_tabrepo_2024.py +1753 -0
- py2ls/data/mygenes_fields_241022.txt +355 -0
- py2ls/data/re_common_pattern.json +173 -0
- py2ls/data/sns_info.json +74 -0
- py2ls/data/styles/.DS_Store +0 -0
- py2ls/data/styles/example/.DS_Store +0 -0
- py2ls/data/styles/stylelib/.DS_Store +0 -0
- py2ls/data/styles/stylelib/grid.mplstyle +15 -0
- py2ls/data/styles/stylelib/high-contrast.mplstyle +6 -0
- py2ls/data/styles/stylelib/high-vis.mplstyle +4 -0
- py2ls/data/styles/stylelib/ieee.mplstyle +15 -0
- py2ls/data/styles/stylelib/light.mplstyl +6 -0
- py2ls/data/styles/stylelib/muted.mplstyle +6 -0
- py2ls/data/styles/stylelib/nature-reviews-latex.mplstyle +616 -0
- py2ls/data/styles/stylelib/nature-reviews.mplstyle +616 -0
- py2ls/data/styles/stylelib/nature.mplstyle +31 -0
- py2ls/data/styles/stylelib/no-latex.mplstyle +10 -0
- py2ls/data/styles/stylelib/notebook.mplstyle +36 -0
- py2ls/data/styles/stylelib/paper.mplstyle +290 -0
- py2ls/data/styles/stylelib/paper2.mplstyle +305 -0
- py2ls/data/styles/stylelib/retro.mplstyle +4 -0
- py2ls/data/styles/stylelib/sans.mplstyle +10 -0
- py2ls/data/styles/stylelib/scatter.mplstyle +7 -0
- py2ls/data/styles/stylelib/science.mplstyle +48 -0
- py2ls/data/styles/stylelib/std-colors.mplstyle +4 -0
- py2ls/data/styles/stylelib/vibrant.mplstyle +6 -0
- py2ls/data/tiles.csv +146 -0
- py2ls/data/usages_pd.json +1417 -0
- py2ls/data/usages_sns.json +31 -0
- py2ls/docker2ls.py +5446 -0
- py2ls/ec2ls.py +61 -0
- py2ls/fetch_update.py +145 -0
- py2ls/ich2ls.py +1955 -296
- py2ls/im2.py +8242 -0
- py2ls/image_ml2ls.py +2100 -0
- py2ls/ips.py +33909 -3418
- py2ls/ml2ls.py +7700 -0
- py2ls/mol.py +289 -0
- py2ls/mount2ls.py +1307 -0
- py2ls/netfinder.py +873 -351
- py2ls/nl2ls.py +283 -0
- py2ls/ocr.py +1581 -458
- py2ls/plot.py +10394 -314
- py2ls/rna2ls.py +311 -0
- py2ls/ssh2ls.md +456 -0
- py2ls/ssh2ls.py +5933 -0
- py2ls/ssh2ls_v01.py +2204 -0
- py2ls/stats.py +66 -172
- py2ls/temp20251124.py +509 -0
- py2ls/translator.py +2 -0
- py2ls/utils/decorators.py +3564 -0
- py2ls/utils_bio.py +3453 -0
- {py2ls-0.1.10.12.dist-info → py2ls-0.2.7.10.dist-info}/METADATA +113 -224
- {py2ls-0.1.10.12.dist-info → py2ls-0.2.7.10.dist-info}/RECORD +72 -16
- {py2ls-0.1.10.12.dist-info → py2ls-0.2.7.10.dist-info}/WHEEL +0 -0
py2ls/batman.py
CHANGED
|
@@ -3,33 +3,106 @@ from email.mime.text import MIMEText
|
|
|
3
3
|
from email.mime.multipart import MIMEMultipart
|
|
4
4
|
from email.mime.base import MIMEBase
|
|
5
5
|
from email import encoders
|
|
6
|
-
import schedule
|
|
7
6
|
import time
|
|
8
7
|
from datetime import datetime
|
|
9
8
|
import os
|
|
10
9
|
from pprint import pp
|
|
11
10
|
import json
|
|
11
|
+
import mimetypes
|
|
12
|
+
import sys
|
|
12
13
|
|
|
13
14
|
|
|
14
|
-
|
|
15
|
-
def convert_to_html(text_list):
|
|
15
|
+
def convert_to_html(text_list, strict=False):
|
|
16
16
|
if not isinstance(text_list, list):
|
|
17
17
|
text_list = [text_list]
|
|
18
18
|
|
|
19
|
-
#
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
19
|
+
# Define a mapping for escape sequences to their HTML representations
|
|
20
|
+
escape_mapping = {
|
|
21
|
+
"\\": "\", # Backslash
|
|
22
|
+
"'": "'", # Single quote
|
|
23
|
+
'"': """, # Double quote
|
|
24
|
+
"\n": "<br>", # Newline
|
|
25
|
+
"\r": "", # Carriage return (not represented in HTML)
|
|
26
|
+
"\t": " ", # Tab (4 spaces)
|
|
27
|
+
"\b": "", # Backspace (not typically represented)
|
|
28
|
+
"\f": "", # Form feed (not typically represented)
|
|
29
|
+
"\v": "", # Vertical tab (not typically represented)
|
|
30
|
+
"\a": "", # Bell/alert sound (not typically represented)
|
|
31
|
+
"\0": "", # Null character (not typically represented)
|
|
32
|
+
}
|
|
33
|
+
# Convert text by replacing escape sequences with their HTML equivalents
|
|
34
|
+
html_content = ""
|
|
35
|
+
for text in text_list:
|
|
36
|
+
for escape_seq, html_rep in escape_mapping.items():
|
|
37
|
+
text = text.replace(escape_seq, html_rep)
|
|
38
|
+
html_content += text.replace("\n", "<br>") # Add line breaks for newlines
|
|
39
|
+
if strict:
|
|
40
|
+
# html_content = "<html><body>\n" + html_content + "\n</body></html>"
|
|
41
|
+
# Include mobile-friendly CSS for responsive tables
|
|
42
|
+
css_style = """
|
|
43
|
+
<style>
|
|
44
|
+
.table-container {
|
|
45
|
+
overflow-x: auto;
|
|
46
|
+
-webkit-overflow-scrolling: touch; /* Enable smooth scrolling on iOS */
|
|
47
|
+
margin-bottom: 20px;
|
|
48
|
+
}
|
|
49
|
+
table {
|
|
50
|
+
width: 100%;
|
|
51
|
+
border-collapse: collapse;
|
|
52
|
+
}
|
|
53
|
+
th, td {
|
|
54
|
+
padding: 8px;
|
|
55
|
+
border: 1px solid #ddd;
|
|
56
|
+
text-align: left;
|
|
57
|
+
}
|
|
58
|
+
</style>
|
|
59
|
+
"""
|
|
25
60
|
|
|
61
|
+
# Wrap the HTML content in the responsive container div
|
|
62
|
+
html_content = f"""
|
|
63
|
+
<html>
|
|
64
|
+
<head>{css_style}</head>
|
|
65
|
+
<body>
|
|
66
|
+
<div class="table-container">
|
|
67
|
+
{html_content}
|
|
68
|
+
</div>
|
|
69
|
+
</body>
|
|
70
|
+
</html>
|
|
71
|
+
"""
|
|
26
72
|
return html_content
|
|
27
73
|
|
|
28
74
|
|
|
75
|
+
def get_name(email_str, by="@"):
|
|
76
|
+
if "num" in by:
|
|
77
|
+
for i, char in enumerate(email_str):
|
|
78
|
+
if char.isdigit():
|
|
79
|
+
break
|
|
80
|
+
else:
|
|
81
|
+
return email_str
|
|
82
|
+
return email_str[:i]
|
|
83
|
+
|
|
84
|
+
# Handle the case where '@' is the criteria
|
|
85
|
+
elif "@" in by:
|
|
86
|
+
name = email_str.split("@")[0]
|
|
87
|
+
name = get_name(name, by="num")
|
|
88
|
+
name = " ".join([part.capitalize() for part in name.split(".")])
|
|
89
|
+
return name
|
|
90
|
+
|
|
91
|
+
|
|
29
92
|
def extract_kv(
|
|
30
|
-
dir_data=
|
|
93
|
+
dir_data=None,
|
|
31
94
|
idx=0,
|
|
32
95
|
): # select the index
|
|
96
|
+
if dir_data is None:
|
|
97
|
+
if "dar" in sys.platform:
|
|
98
|
+
dir_data = "/Users/macjianfeng/Dropbox/github/python/py2ls/confidential_data/gmail_login.json"
|
|
99
|
+
else:
|
|
100
|
+
if "win" in sys.platform:
|
|
101
|
+
dir_data = (
|
|
102
|
+
"Z:\\Jianfeng\\Apps\\settings\\confidential_data\\gmail_login.json"
|
|
103
|
+
)
|
|
104
|
+
elif "lin" in sys.platform:
|
|
105
|
+
dir_data = "/Users/macjianfeng/Dropbox/github/python/py2ls/confidential_data/gmail_login.json"
|
|
33
106
|
with open(dir_data, "r") as file:
|
|
34
107
|
email_login = json.load(file)
|
|
35
108
|
for i, (user, pd) in enumerate(email_login.items()):
|
|
@@ -38,25 +111,36 @@ def extract_kv(
|
|
|
38
111
|
|
|
39
112
|
|
|
40
113
|
def email_to(**kwargs):
|
|
114
|
+
return email(**kwargs)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def email(**kwargs):
|
|
41
118
|
"""
|
|
42
119
|
usage:
|
|
43
|
-
email_to(who="example@gmail.com",
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
120
|
+
email_to(who=["example@gmail.com"],
|
|
121
|
+
subject="test",
|
|
122
|
+
what="this is the body",
|
|
123
|
+
attachments=["/Users/test.pdf"],
|
|
124
|
+
cc=["cc1@example.com"],
|
|
125
|
+
bcc=["bcc@example.com"],
|
|
126
|
+
html_template="template.html")
|
|
47
127
|
Send an email with optional attachments.
|
|
48
128
|
|
|
49
|
-
:param who: Recipient email address
|
|
129
|
+
:param who: Recipient email address (string or list)
|
|
50
130
|
:param subject: Email subject
|
|
51
|
-
:param what: Email
|
|
131
|
+
:param what: Email body (can be HTML or plain text)
|
|
52
132
|
:param attachments: List of file paths to attach
|
|
133
|
+
:param cc: List of CC email addresses
|
|
134
|
+
:param bcc: List of BCC email addresses, "blind carbon copy"接受者之间互相看不到
|
|
135
|
+
:param html_template: Path to an HTML file for the email body
|
|
53
136
|
"""
|
|
54
137
|
who, what, subject, signature = None, None, None, None
|
|
55
138
|
attachments = False
|
|
56
139
|
pause_sec = False # default 10 seconds pause
|
|
57
|
-
|
|
140
|
+
bcc, cc = [], []
|
|
58
141
|
signature_styles = extract_kv(idx=1)[1] # signature list
|
|
59
142
|
signature = signature_styles[0] # default signature,None
|
|
143
|
+
read_receipt = False
|
|
60
144
|
verbose = True
|
|
61
145
|
if not kwargs:
|
|
62
146
|
print(
|
|
@@ -64,49 +148,73 @@ def email_to(**kwargs):
|
|
|
64
148
|
)
|
|
65
149
|
return None
|
|
66
150
|
# params config
|
|
151
|
+
for k, v in kwargs.items():
|
|
152
|
+
if any([i in k.lower() for i in ["who", "whe", "to", "@"]]): # 'who' or "where"
|
|
153
|
+
if isinstance(v, list):
|
|
154
|
+
who = v
|
|
155
|
+
else:
|
|
156
|
+
who = [v]
|
|
67
157
|
for k, v in kwargs.items():
|
|
68
158
|
if any(
|
|
69
|
-
[
|
|
70
|
-
|
|
71
|
-
"whe" in k.lower(),
|
|
72
|
-
"to" in k.lower(),
|
|
73
|
-
"@" in k.lower(),
|
|
74
|
-
all(["@" in k.lower(), "." in k.lower()]),
|
|
75
|
-
]
|
|
76
|
-
): # 'who' or "where"
|
|
77
|
-
who = v
|
|
78
|
-
if any(["sub" in k.lower(), "hea" in k.lower()]): # 'subject', 'header'
|
|
159
|
+
[i in k.lower() for i in ["sub", "hea", "top"]]
|
|
160
|
+
): # 'subject', 'header','topic'
|
|
79
161
|
subject = v
|
|
80
162
|
if any(
|
|
81
|
-
[
|
|
163
|
+
[i in k.lower() for i in ["wha", "con", "bod"]]
|
|
82
164
|
): # 'what','content','body'
|
|
83
165
|
what = v
|
|
84
|
-
if any([
|
|
166
|
+
if any([i in k.lower() for i in ["att", "fil"]]):
|
|
85
167
|
attachments = v # optional
|
|
86
|
-
if any(
|
|
87
|
-
["paus" in k.lower(), "tim" in k.lower(), "delay" in k.lower()]
|
|
88
|
-
): # 'attachments', 'file'
|
|
168
|
+
if any([i in k.lower() for i in ["paus", "tim", "delay"]]):
|
|
89
169
|
pause_sec = v # optional
|
|
90
170
|
if any(["sig" in k.lower()]): # 'attachments', 'file'
|
|
91
171
|
signature = v # optional
|
|
92
172
|
if not isinstance(v, str):
|
|
93
|
-
print(signature_styles)
|
|
94
173
|
signature = signature_styles[v]
|
|
95
|
-
if any([
|
|
174
|
+
if any([i in k.lower() for i in ["verb", "show"]]): # 'verbose', 'show'
|
|
96
175
|
verbose = v # verbose
|
|
97
|
-
|
|
176
|
+
if any(["cc" in k.lower() and "bcc" not in k.lower(), "ward" in k.lower()]):
|
|
177
|
+
if isinstance(v, list):
|
|
178
|
+
cc = v
|
|
179
|
+
else:
|
|
180
|
+
cc = [v]
|
|
181
|
+
if any([i in k.lower() for i in ["bcc", "blind", "secret"]]):
|
|
182
|
+
if isinstance(v, list):
|
|
183
|
+
bcc = v
|
|
184
|
+
else:
|
|
185
|
+
bcc = [v]
|
|
186
|
+
if any(["temp" in k.lower(), "html" in k.lower()]):
|
|
187
|
+
# Load HTML template if specified
|
|
188
|
+
if isinstance(v, str) and v.endswith("html"):
|
|
189
|
+
with open(kwargs["html_template"], "r") as f:
|
|
190
|
+
what = f.read()
|
|
98
191
|
# Create email message
|
|
99
192
|
message = MIMEMultipart()
|
|
100
193
|
message["From"] = extract_kv()[0]
|
|
101
|
-
|
|
194
|
+
print(who)
|
|
195
|
+
message["To"] = ", ".join(who)
|
|
196
|
+
|
|
197
|
+
if len(who) == 1:
|
|
198
|
+
who = ", ".join(who)
|
|
199
|
+
|
|
102
200
|
message["Subject"] = subject
|
|
201
|
+
# Add CC addresses if provided
|
|
202
|
+
if cc:
|
|
203
|
+
message["Cc"] = ", ".join(cc) # Join CC addresses as a comma-separated string
|
|
103
204
|
|
|
104
205
|
# the eamil's body
|
|
105
206
|
if what is None:
|
|
106
207
|
what = ""
|
|
107
208
|
if isinstance(what, list):
|
|
108
209
|
what = convert_to_html(what)
|
|
109
|
-
if 'class="
|
|
210
|
+
if 'class="' in str(what):
|
|
211
|
+
# Combine HTML content correctly
|
|
212
|
+
html_content = "<html><body>\n"
|
|
213
|
+
html_content += f"{what}"
|
|
214
|
+
html_content += f"<br><br>{convert_to_html([signature])}"
|
|
215
|
+
html_content += "\n</body></html>"
|
|
216
|
+
message.attach(MIMEText(html_content, "html"))
|
|
217
|
+
elif 'class="' in str(attachments):
|
|
110
218
|
# Combine HTML content correctly
|
|
111
219
|
html_content = "<html><body>\n"
|
|
112
220
|
html_content += f"{convert_to_html(what)}<br>{attachments}"
|
|
@@ -124,15 +232,24 @@ def email_to(**kwargs):
|
|
|
124
232
|
if isinstance(attachments, str):
|
|
125
233
|
attachments = [attachments]
|
|
126
234
|
for file in attachments:
|
|
127
|
-
|
|
235
|
+
# 有时候一些不常用的文件类型, 无法自动识别
|
|
236
|
+
mime_type, _ = mimetypes.guess_type(file)
|
|
237
|
+
if mime_type is None:
|
|
238
|
+
if file.endswith(".xlsx"):
|
|
239
|
+
mime_type = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
|
240
|
+
else:
|
|
241
|
+
mime_type = "application/octet-stream"
|
|
242
|
+
|
|
243
|
+
main_type, sub_type = mime_type.split("/", 1)
|
|
244
|
+
# part = MIMEBase("application", "octet-stream") # 旧的版本
|
|
245
|
+
part = MIMEBase(main_type, sub_type)
|
|
128
246
|
try:
|
|
129
247
|
with open(file, "rb") as attachment:
|
|
130
248
|
part.set_payload(attachment.read())
|
|
131
249
|
encoders.encode_base64(part)
|
|
132
|
-
|
|
250
|
+
filename = os.path.basename(file)
|
|
133
251
|
part.add_header(
|
|
134
|
-
"Content-Disposition",
|
|
135
|
-
f'attachment; filename= "{os.path.basename(file)}"',
|
|
252
|
+
"Content-Disposition", f'attachment; filename="{filename}"'
|
|
136
253
|
)
|
|
137
254
|
message.attach(part)
|
|
138
255
|
except FileNotFoundError:
|
|
@@ -147,7 +264,12 @@ def email_to(**kwargs):
|
|
|
147
264
|
with smtplib.SMTP("smtp.gmail.com", 587) as server:
|
|
148
265
|
server.starttls()
|
|
149
266
|
server.login(extract_kv()[0], extract_kv()[1])
|
|
150
|
-
|
|
267
|
+
if any(bcc):
|
|
268
|
+
recipient_list = who + cc + bcc # Include all recipients
|
|
269
|
+
server.sendmail(extract_kv()[0], recipient_list, message.as_string())
|
|
270
|
+
else:
|
|
271
|
+
server.sendmail(extract_kv()[0], who, message.as_string())
|
|
272
|
+
|
|
151
273
|
if verbose:
|
|
152
274
|
if attachments:
|
|
153
275
|
print(
|