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.

Files changed (72) hide show
  1. py2ls/.DS_Store +0 -0
  2. py2ls/.git/.DS_Store +0 -0
  3. py2ls/.git/index +0 -0
  4. py2ls/.git/logs/refs/remotes/origin/HEAD +1 -0
  5. py2ls/.git/objects/.DS_Store +0 -0
  6. py2ls/.git/refs/.DS_Store +0 -0
  7. py2ls/ImageLoader.py +621 -0
  8. py2ls/__init__.py +7 -5
  9. py2ls/apptainer2ls.py +3940 -0
  10. py2ls/batman.py +164 -42
  11. py2ls/bio.py +2595 -0
  12. py2ls/cell_image_clf.py +1632 -0
  13. py2ls/container2ls.py +4635 -0
  14. py2ls/corr.py +475 -0
  15. py2ls/data/.DS_Store +0 -0
  16. py2ls/data/email/email_html_template.html +88 -0
  17. py2ls/data/hyper_param_autogluon_zeroshot2024.json +2383 -0
  18. py2ls/data/hyper_param_tabrepo_2024.py +1753 -0
  19. py2ls/data/mygenes_fields_241022.txt +355 -0
  20. py2ls/data/re_common_pattern.json +173 -0
  21. py2ls/data/sns_info.json +74 -0
  22. py2ls/data/styles/.DS_Store +0 -0
  23. py2ls/data/styles/example/.DS_Store +0 -0
  24. py2ls/data/styles/stylelib/.DS_Store +0 -0
  25. py2ls/data/styles/stylelib/grid.mplstyle +15 -0
  26. py2ls/data/styles/stylelib/high-contrast.mplstyle +6 -0
  27. py2ls/data/styles/stylelib/high-vis.mplstyle +4 -0
  28. py2ls/data/styles/stylelib/ieee.mplstyle +15 -0
  29. py2ls/data/styles/stylelib/light.mplstyl +6 -0
  30. py2ls/data/styles/stylelib/muted.mplstyle +6 -0
  31. py2ls/data/styles/stylelib/nature-reviews-latex.mplstyle +616 -0
  32. py2ls/data/styles/stylelib/nature-reviews.mplstyle +616 -0
  33. py2ls/data/styles/stylelib/nature.mplstyle +31 -0
  34. py2ls/data/styles/stylelib/no-latex.mplstyle +10 -0
  35. py2ls/data/styles/stylelib/notebook.mplstyle +36 -0
  36. py2ls/data/styles/stylelib/paper.mplstyle +290 -0
  37. py2ls/data/styles/stylelib/paper2.mplstyle +305 -0
  38. py2ls/data/styles/stylelib/retro.mplstyle +4 -0
  39. py2ls/data/styles/stylelib/sans.mplstyle +10 -0
  40. py2ls/data/styles/stylelib/scatter.mplstyle +7 -0
  41. py2ls/data/styles/stylelib/science.mplstyle +48 -0
  42. py2ls/data/styles/stylelib/std-colors.mplstyle +4 -0
  43. py2ls/data/styles/stylelib/vibrant.mplstyle +6 -0
  44. py2ls/data/tiles.csv +146 -0
  45. py2ls/data/usages_pd.json +1417 -0
  46. py2ls/data/usages_sns.json +31 -0
  47. py2ls/docker2ls.py +5446 -0
  48. py2ls/ec2ls.py +61 -0
  49. py2ls/fetch_update.py +145 -0
  50. py2ls/ich2ls.py +1955 -296
  51. py2ls/im2.py +8242 -0
  52. py2ls/image_ml2ls.py +2100 -0
  53. py2ls/ips.py +33909 -3418
  54. py2ls/ml2ls.py +7700 -0
  55. py2ls/mol.py +289 -0
  56. py2ls/mount2ls.py +1307 -0
  57. py2ls/netfinder.py +873 -351
  58. py2ls/nl2ls.py +283 -0
  59. py2ls/ocr.py +1581 -458
  60. py2ls/plot.py +10394 -314
  61. py2ls/rna2ls.py +311 -0
  62. py2ls/ssh2ls.md +456 -0
  63. py2ls/ssh2ls.py +5933 -0
  64. py2ls/ssh2ls_v01.py +2204 -0
  65. py2ls/stats.py +66 -172
  66. py2ls/temp20251124.py +509 -0
  67. py2ls/translator.py +2 -0
  68. py2ls/utils/decorators.py +3564 -0
  69. py2ls/utils_bio.py +3453 -0
  70. {py2ls-0.1.10.12.dist-info → py2ls-0.2.7.10.dist-info}/METADATA +113 -224
  71. {py2ls-0.1.10.12.dist-info → py2ls-0.2.7.10.dist-info}/RECORD +72 -16
  72. {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
- # Helper to convert text or list of text to HTML
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
- # Convert each element into HTML and add line breaks
20
- html_content = "" # "<html><body>\n"
21
- html_content += "".join(
22
- text.replace("\n", "<br>") for text in text_list if text.strip()
23
- )
24
- # html_content += "\n</body></html>"
19
+ # Define a mapping for escape sequences to their HTML representations
20
+ escape_mapping = {
21
+ "\\": "&bsol;", # Backslash
22
+ "'": "&apos;", # Single quote
23
+ '"': "&quot;", # Double quote
24
+ "\n": "<br>", # Newline
25
+ "\r": "", # Carriage return (not represented in HTML)
26
+ "\t": "&nbsp;&nbsp;&nbsp;&nbsp;", # 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="/Users/macjianfeng/Dropbox/github/python/py2ls/confidential_data/gmail_login.json",
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
- subject="test",
45
- what="this is the body",
46
- attachments="/Users/test.pdf")
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 what
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
- "who" in k.lower(),
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
- ["wha" in k.lower(), "con" in k.lower(), "bod" in k.lower()]
163
+ [i in k.lower() for i in ["wha", "con", "bod"]]
82
164
  ): # 'what','content','body'
83
165
  what = v
84
- if any(["att" in k.lower(), "fil" in k.lower()]): # 'attachments', 'file'
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(["verb" in k.lower(), "show" in k.lower()]): # 'verbose', 'show'
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
- message["To"] = who
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="dataframe"' in str(attachments):
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
- part = MIMEBase("application", "octet-stream")
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
- server.sendmail(extract_kv()[0], who, message.as_string())
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(