py2ls 0.2.4.40__py3-none-any.whl → 0.2.5.1__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.
py2ls/ips.py
CHANGED
@@ -2411,7 +2411,25 @@ def is_df_abnormal(df: pd.DataFrame, verbose=False) -> bool:
|
|
2411
2411
|
if verbose:
|
2412
2412
|
print("\n".join(messages))
|
2413
2413
|
return is_abnormal # Data is abnormal
|
2414
|
+
def decrypt_excel(fpath, password):
|
2415
|
+
# * needs a password?
|
2416
|
+
import msoffcrypto # pip install msoffcrypto-tool
|
2417
|
+
from io import BytesIO
|
2414
2418
|
|
2419
|
+
# Open the encrypted Excel file
|
2420
|
+
with open(fpath, "rb") as f:
|
2421
|
+
try:
|
2422
|
+
office_file = msoffcrypto.OfficeFile(f)
|
2423
|
+
office_file.load_key(password=password) # Provide the password
|
2424
|
+
decrypted = BytesIO()
|
2425
|
+
office_file.decrypt(decrypted)
|
2426
|
+
except:
|
2427
|
+
office_file = msoffcrypto.OfficeFile(f)
|
2428
|
+
office_file.load_key(password=depass(password)) # Provide the password
|
2429
|
+
decrypted = BytesIO()
|
2430
|
+
office_file.decrypt(decrypted)
|
2431
|
+
decrypted.seek(0) # reset pointer to start
|
2432
|
+
return decrypted
|
2415
2433
|
|
2416
2434
|
def fload(fpath, kind=None, **kwargs):
|
2417
2435
|
"""
|
@@ -2749,48 +2767,64 @@ def fload(fpath, kind=None, **kwargs):
|
|
2749
2767
|
display(df.head(2))
|
2750
2768
|
print(f"shape: {df.shape}")
|
2751
2769
|
return df
|
2752
|
-
|
2770
|
+
|
2753
2771
|
def load_excel(fpath, **kwargs):
|
2754
2772
|
engine = kwargs.get("engine", "openpyxl")
|
2755
2773
|
verbose = kwargs.pop("verbose", False)
|
2756
2774
|
password = kwargs.pop("password", None)
|
2775
|
+
output = kwargs.pop("output", "DataFrame").lower()
|
2776
|
+
sheet_name = kwargs.pop("sheet_name", None)
|
2757
2777
|
|
2758
|
-
|
2759
|
-
if run_once_within(reverse=True):
|
2760
|
-
use_pd("read_excel", verbose=verbose)
|
2761
|
-
df = pd.read_excel(fpath, engine=engine, **kwargs)
|
2778
|
+
def print_sheet_info(fpath):
|
2762
2779
|
try:
|
2763
2780
|
meta = pd.ExcelFile(fpath)
|
2764
2781
|
print(f"n_sheet={len(meta.sheet_names)},\t'sheetname = 0 (default)':")
|
2765
|
-
[print(f"{i}:\t{
|
2766
|
-
except:
|
2767
|
-
|
2768
|
-
|
2769
|
-
|
2770
|
-
|
2771
|
-
|
2782
|
+
[print(f"{i}:\t{name}") for i, name in enumerate(meta.sheet_names)]
|
2783
|
+
except Exception as e:
|
2784
|
+
if verbose:
|
2785
|
+
print(f"Error retrieving sheet info: {e}")
|
2786
|
+
if output in ["dataframe", "df"]:
|
2787
|
+
if not password:
|
2788
|
+
if verbose:
|
2789
|
+
print("Reading Excel without password protection...")
|
2790
|
+
df = pd.read_excel(fpath, engine=engine, sheet_name=sheet_name, **kwargs)
|
2791
|
+
if verbose:
|
2792
|
+
print_sheet_info(fpath)
|
2793
|
+
return df
|
2794
|
+
# Handle password-protected DataFrame case
|
2795
|
+
else:
|
2796
|
+
if verbose:
|
2797
|
+
print("Decrypting and loading DataFrame...")
|
2798
|
+
decrypted = decrypt_excel(fpath, password=password)
|
2799
|
+
df = pd.read_excel(decrypted, engine=engine,sheet_name=sheet_name, **kwargs)
|
2800
|
+
if verbose:
|
2801
|
+
print_sheet_info(fpath)
|
2802
|
+
return df
|
2803
|
+
# Handle cases for non-dataframe output
|
2804
|
+
else:
|
2805
|
+
from openpyxl import load_workbook
|
2806
|
+
if verbose:
|
2807
|
+
print("Returning worksheet (non-DataFrame output)...")
|
2808
|
+
if password:
|
2809
|
+
decrypted = decrypt_excel(fpath, password=password)
|
2810
|
+
workbook = load_workbook(decrypted)
|
2811
|
+
else:
|
2812
|
+
workbook = load_workbook(fpath)
|
2813
|
+
|
2814
|
+
# If sheet_name is specified, keep only that sheet
|
2815
|
+
if sheet_name:
|
2816
|
+
if sheet_name in workbook.sheetnames:
|
2817
|
+
worksheet = workbook[sheet_name]
|
2818
|
+
# Remove all other sheets
|
2819
|
+
for sheet in workbook.sheetnames:
|
2820
|
+
if sheet != sheet_name:
|
2821
|
+
del workbook[sheet]
|
2822
|
+
else:
|
2823
|
+
raise ValueError(f"Sheet '{sheet_name}' not found in the workbook.")
|
2824
|
+
else:
|
2825
|
+
worksheet = workbook.active # Default to active sheet
|
2772
2826
|
|
2773
|
-
|
2774
|
-
with open(fpath, "rb") as f:
|
2775
|
-
try:
|
2776
|
-
office_file = msoffcrypto.OfficeFile(f)
|
2777
|
-
office_file.load_key(password=password) # Provide the password
|
2778
|
-
decrypted = BytesIO()
|
2779
|
-
office_file.decrypt(decrypted)
|
2780
|
-
except:
|
2781
|
-
office_file = msoffcrypto.OfficeFile(f)
|
2782
|
-
office_file.load_key(password=depass(password)) # Provide the password
|
2783
|
-
decrypted = BytesIO()
|
2784
|
-
office_file.decrypt(decrypted)
|
2785
|
-
decrypted.seek(0)
|
2786
|
-
df = pd.read_excel(decrypted, engine=engine, **kwargs)
|
2787
|
-
try:
|
2788
|
-
meta = pd.ExcelFile(fpath)
|
2789
|
-
print(f"n_sheet={len(meta.sheet_names)},\t'sheetname = 0 (default)':")
|
2790
|
-
[print(f"{i}:\t{i_}") for i, i_ in enumerate(meta.sheet_names)]
|
2791
|
-
except:
|
2792
|
-
pass
|
2793
|
-
return df
|
2827
|
+
return workbook
|
2794
2828
|
|
2795
2829
|
def load_parquet(fpath, **kwargs):
|
2796
2830
|
"""
|
@@ -3019,12 +3053,12 @@ def fload(fpath, kind=None, **kwargs):
|
|
3019
3053
|
return content
|
3020
3054
|
elif kind == "xlsx":
|
3021
3055
|
verbose = kwargs.pop("verbose", False)
|
3022
|
-
content = load_excel(fpath,
|
3023
|
-
(
|
3024
|
-
|
3025
|
-
|
3026
|
-
|
3027
|
-
)
|
3056
|
+
content = load_excel(fpath, verbose=verbose,**kwargs)
|
3057
|
+
# (
|
3058
|
+
# display(content.head(3))
|
3059
|
+
# if isinstance(content, pd.DataFrame) and verbose
|
3060
|
+
# else None
|
3061
|
+
# )
|
3028
3062
|
print(f"shape: {content.shape}") if isinstance(content, pd.DataFrame) else None
|
3029
3063
|
return content
|
3030
3064
|
elif kind == "mtx":
|
@@ -3382,25 +3416,11 @@ def fsave(
|
|
3382
3416
|
use_pd("to_excel", verbose=verbose)
|
3383
3417
|
|
3384
3418
|
if any(kwargs):
|
3385
|
-
format_excel(df=data, filename=fpath, **kwargs)
|
3419
|
+
format_excel(df=data, filename=fpath,sheet_name=sheet_name,password=password, **kwargs)
|
3386
3420
|
else:
|
3387
3421
|
# Remove non-relevant kwargs
|
3388
|
-
irrelevant_keys
|
3389
|
-
|
3390
|
-
"usage",
|
3391
|
-
"cell",
|
3392
|
-
"width",
|
3393
|
-
"height",
|
3394
|
-
"height_max",
|
3395
|
-
"merge",
|
3396
|
-
"shade",
|
3397
|
-
"comment",
|
3398
|
-
"link",
|
3399
|
-
"protect",
|
3400
|
-
"number_format",
|
3401
|
-
"conditional_format",
|
3402
|
-
"index_default",
|
3403
|
-
]
|
3422
|
+
irrelevant_keys=list(extract_kwargs(format_excel).keys())[4:]
|
3423
|
+
|
3404
3424
|
for key in irrelevant_keys:
|
3405
3425
|
kwargs.pop(key, None)
|
3406
3426
|
|
@@ -3839,7 +3859,7 @@ def get_os(full=False, verbose=False):
|
|
3839
3859
|
import sys
|
3840
3860
|
import platform
|
3841
3861
|
import psutil
|
3842
|
-
import GPUtil
|
3862
|
+
# import GPUtil
|
3843
3863
|
import socket
|
3844
3864
|
import uuid
|
3845
3865
|
import cpuinfo
|
@@ -4246,23 +4266,23 @@ def get_os(full=False, verbose=False):
|
|
4246
4266
|
}
|
4247
4267
|
)
|
4248
4268
|
|
4249
|
-
# GPU Information
|
4250
|
-
gpus = GPUtil.getGPUs()
|
4251
|
-
for gpu in gpus:
|
4252
|
-
|
4253
|
-
|
4254
|
-
|
4255
|
-
|
4256
|
-
|
4257
|
-
|
4258
|
-
|
4259
|
-
|
4260
|
-
|
4261
|
-
|
4262
|
-
|
4263
|
-
|
4264
|
-
|
4265
|
-
|
4269
|
+
# # GPU Information
|
4270
|
+
# gpus = GPUtil.getGPUs()
|
4271
|
+
# for gpu in gpus:
|
4272
|
+
# gpu_info = {
|
4273
|
+
# "name": gpu.name,
|
4274
|
+
# "load (%)": gpu.load * 100,
|
4275
|
+
# "free memory (MB)": gpu.memoryFree,
|
4276
|
+
# "used memory (MB)": gpu.memoryUsed,
|
4277
|
+
# "total memory (MB)": gpu.memoryTotal,
|
4278
|
+
# "driver version": gpu.driver,
|
4279
|
+
# "temperature (°C)": gpu.temperature,
|
4280
|
+
# }
|
4281
|
+
# if hasattr(gpu, "powerDraw"):
|
4282
|
+
# gpu_info["Power Draw (W)"] = gpu.powerDraw
|
4283
|
+
# if hasattr(gpu, "powerLimit"):
|
4284
|
+
# gpu_info["Power Limit (W)"] = gpu.powerLimit
|
4285
|
+
# system_info["gpu"].append(gpu_info)
|
4266
4286
|
|
4267
4287
|
res = system_info if full else get_os_type()
|
4268
4288
|
if verbose:
|
@@ -6313,7 +6333,19 @@ def hex2argb(hex_color):
|
|
6313
6333
|
"Invalid hex color format. Use RRGGBB, #RRGGBB, or aARRGGBB format."
|
6314
6334
|
)
|
6315
6335
|
|
6336
|
+
def extract_kwargs(func):
|
6337
|
+
import inspect
|
6316
6338
|
|
6339
|
+
# Get the signature of the function
|
6340
|
+
signature = inspect.signature(func)
|
6341
|
+
# Extract parameters that are kwargs (parameters with default values or **kwargs)
|
6342
|
+
kwargs = {
|
6343
|
+
param.name: param.default
|
6344
|
+
for param in signature.parameters.values()
|
6345
|
+
if param.default is not inspect.Parameter.empty
|
6346
|
+
}
|
6347
|
+
|
6348
|
+
return kwargs
|
6317
6349
|
def format_excel(
|
6318
6350
|
df=None,
|
6319
6351
|
filename=None,
|
@@ -6321,21 +6353,26 @@ def format_excel(
|
|
6321
6353
|
usage=False,
|
6322
6354
|
cell=None, # dict: or list for multiple locs setting:
|
6323
6355
|
width=None, # dict
|
6356
|
+
width_factor=2,# calculated with plus this factor
|
6324
6357
|
height=None, # dict e.g., {2: 50, 3: 25}, keys are columns
|
6325
6358
|
height_max=25,
|
6326
6359
|
merge=None, # tuple e.g., (slice(0, 1), slice(1, 3)),
|
6327
6360
|
shade=None, # dict
|
6328
6361
|
comment=None, # dict e.g., {(2, 4): "This is a comment"},
|
6362
|
+
comment_always_visible:bool=True,# always display comment
|
6329
6363
|
link=None, # dict e.g., {(2, 2): "https://example.com"},
|
6330
6364
|
protect=None, # dict
|
6331
6365
|
number_format=None, # dict: e.g., {1:"0.00", 2:"#,##0",3:"0%",4:"$#,##0.00"}
|
6332
6366
|
data_validation=None, # dict
|
6333
|
-
apply_filter=True, # add filter
|
6367
|
+
apply_filter:bool=True, # add filter
|
6368
|
+
freeze :str= False,#"A2",
|
6334
6369
|
conditional_format=None, # dict
|
6370
|
+
verbose=True,
|
6335
6371
|
**kwargs,
|
6336
6372
|
):
|
6337
6373
|
import pandas as pd
|
6338
6374
|
from datetime import datetime
|
6375
|
+
import openpyxl
|
6339
6376
|
from openpyxl import load_workbook
|
6340
6377
|
from openpyxl.styles import Font, PatternFill, Alignment, Border, Side
|
6341
6378
|
from openpyxl.utils import get_column_letter
|
@@ -6695,35 +6732,114 @@ def format_excel(
|
|
6695
6732
|
cell_.alignment = cell_alignment
|
6696
6733
|
if border:
|
6697
6734
|
cell_.border = border
|
6698
|
-
|
6699
|
-
|
6735
|
+
def generate_unique_sheet_name(wb, sheet_name):
|
6736
|
+
"""Generate a unique sheet name if the given name already exists in the workbook."""
|
6737
|
+
if sheet_name not in wb.sheetnames:
|
6738
|
+
return sheet_name
|
6739
|
+
counter = 1
|
6740
|
+
unique_name = f"{sheet_name}_{counter}"
|
6741
|
+
while unique_name in wb.sheetnames:
|
6742
|
+
counter += 1
|
6743
|
+
unique_name = f"{sheet_name}_{counter}"
|
6744
|
+
return unique_name
|
6745
|
+
# if it is already worksheet format
|
6746
|
+
if isinstance(df, pd.DataFrame):
|
6747
|
+
pass
|
6748
|
+
elif isinstance(df, openpyxl.worksheet.worksheet.Worksheet) or isinstance(df, openpyxl.workbook.workbook.Workbook):
|
6749
|
+
pass
|
6750
|
+
elif df is None:
|
6751
|
+
if any(filename):
|
6752
|
+
df = fload(filename, output="bit")
|
6753
|
+
else:
|
6700
6754
|
try:
|
6701
6755
|
print(f"is loading file {os.path.basename(df)}")
|
6702
6756
|
df = fload(df)
|
6703
|
-
except:
|
6757
|
+
except Exception as e:
|
6758
|
+
print(e)
|
6704
6759
|
print(f"check the fpath:{df}")
|
6705
6760
|
if filename is None:
|
6706
6761
|
filename = str(datetime.now().strftime("%y%m%d_%H.xlsx"))
|
6707
|
-
|
6708
|
-
|
6709
|
-
|
6710
|
-
|
6711
|
-
|
6712
|
-
|
6713
|
-
|
6714
|
-
|
6715
|
-
|
6716
|
-
|
6717
|
-
|
6718
|
-
|
6719
|
-
df = pd.DataFrame(data)
|
6762
|
+
|
6763
|
+
kwargs.pop("format", None) # 更好地跟fsave结合使用
|
6764
|
+
kwargs.pop("sheet_name", 0) # 更好地跟df.to_excel结合使用
|
6765
|
+
# 只有openpyxl才支持 append
|
6766
|
+
mode = strcmp(kwargs.get("mode", "auto"), ["a", "w","auto"])[0]
|
6767
|
+
kwargs.pop("mode", None)
|
6768
|
+
engine = strcmp(kwargs.get("engine", "openpyxl"), ["xlsxwriter", "openpyxl"])[0]
|
6769
|
+
# 通常是不需要保存index的
|
6770
|
+
index = kwargs.get("index", False)
|
6771
|
+
kwargs.pop("index", None)
|
6772
|
+
# header
|
6773
|
+
header=kwargs.pop("header",False)
|
6720
6774
|
|
6775
|
+
if isinstance(df, openpyxl.workbook.workbook.Workbook):
|
6776
|
+
wb=df
|
6777
|
+
try:
|
6778
|
+
ws = wb.worksheets[sheet_name]
|
6779
|
+
except Exception as e:
|
6780
|
+
print(e)
|
6781
|
+
if not os.path.exists(filename) or mode=="w":
|
6782
|
+
ws=wb.active
|
6783
|
+
ws.title = sheet_name
|
6784
|
+
else:# file exists
|
6785
|
+
wb = load_workbook(filename)
|
6786
|
+
# check the shtname and get the new sheet_name
|
6787
|
+
sheet_name_corr=generate_unique_sheet_name(wb, sheet_name)
|
6788
|
+
|
6789
|
+
# Save the workbook with the new sheet name
|
6790
|
+
with pd.ExcelWriter(filename, mode="a", engine=engine, if_sheet_exists="new") as writer:
|
6791
|
+
for ws in df.worksheets: # Iterate through worksheets in the input workbook
|
6792
|
+
ws_df = pd.DataFrame(ws.values)
|
6793
|
+
ws_df.to_excel(writer,
|
6794
|
+
sheet_name=sheet_name_corr,
|
6795
|
+
index=index,
|
6796
|
+
header=header,
|
6797
|
+
**kwargs)
|
6798
|
+
wb = load_workbook(filename)
|
6799
|
+
print(sheet_name,sheet_name_corr)
|
6800
|
+
print(wb.sheetnames)
|
6801
|
+
if sheet_name_corr in wb.sheetnames:
|
6802
|
+
ws = wb[sheet_name_corr]
|
6803
|
+
if not sheet_name==sheet_name_corr:
|
6804
|
+
wb.remove(wb[sheet_name])
|
6805
|
+
else:
|
6806
|
+
raise KeyError(f"Worksheet {sheet_name_corr} does not exist.")
|
6807
|
+
else:
|
6808
|
+
if not os.path.exists(filename) or mode=="w": # or overwrite
|
6809
|
+
# save file
|
6810
|
+
sheet_name_corr = (
|
6811
|
+
sheet_name if isinstance(sheet_name, str) else f"Sheet_{sheet_name}"
|
6812
|
+
)
|
6813
|
+
with pd.ExcelWriter(filename, mode="w", engine=engine) as writer:
|
6814
|
+
df.to_excel(writer, sheet_name=sheet_name_corr, index=index, header=header,**kwargs)
|
6815
|
+
wb = load_workbook(filename)
|
6816
|
+
if isinstance(sheet_name, str):
|
6817
|
+
ws = wb[sheet_name]
|
6818
|
+
elif isinstance(sheet_name, int):
|
6819
|
+
ws = wb.worksheets[sheet_name]
|
6820
|
+
else:
|
6821
|
+
ws = wb.worksheets[sheet_name] # the index of worksheets
|
6822
|
+
else:# file exists
|
6823
|
+
wb = load_workbook(filename)
|
6824
|
+
sheet_name_corr = generate_unique_sheet_name(wb, sheet_name)
|
6825
|
+
with pd.ExcelWriter(filename, mode="a", engine=engine, if_sheet_exists="new") as writer:
|
6826
|
+
df.to_excel(writer, sheet_name=sheet_name_corr, index=index, header=header,**kwargs)
|
6827
|
+
wb = load_workbook(filename)
|
6828
|
+
if sheet_name_corr in wb.sheetnames:
|
6829
|
+
ws = wb[sheet_name_corr]
|
6830
|
+
else:
|
6831
|
+
raise KeyError(f"Worksheet {sheet_name_corr} does not exist.")
|
6721
6832
|
|
6722
|
-
|
6723
|
-
|
6724
|
-
|
6725
|
-
|
6726
|
-
|
6833
|
+
# !Apply cell formatting
|
6834
|
+
if cell:
|
6835
|
+
if not isinstance(cell, list):
|
6836
|
+
cell = [cell]
|
6837
|
+
for cell_ in cell:
|
6838
|
+
for indices, format_options in cell_.items():
|
6839
|
+
cell_range = convert_indices_to_range(*indices)
|
6840
|
+
apply_format(ws, format_options, cell_range)
|
6841
|
+
if verbose:
|
6842
|
+
cell_tmp="""cell=[
|
6727
6843
|
{
|
6728
6844
|
(slice(0, 1), slice(0, len(df.columns))): {
|
6729
6845
|
"font": {
|
@@ -6754,180 +6870,64 @@ def format_excel(
|
|
6754
6870
|
"color": "000000", # Border color
|
6755
6871
|
},
|
6756
6872
|
}
|
6757
|
-
},
|
6758
|
-
|
6759
|
-
|
6760
|
-
|
6761
|
-
|
6762
|
-
|
6763
|
-
|
6764
|
-
|
6765
|
-
|
6766
|
-
|
6767
|
-
|
6768
|
-
|
6769
|
-
|
6770
|
-
|
6771
|
-
|
6772
|
-
|
6773
|
-
|
6774
|
-
|
6775
|
-
|
6776
|
-
|
6777
|
-
|
6778
|
-
|
6779
|
-
|
6780
|
-
},
|
6781
|
-
"border": {
|
6782
|
-
"left": "thin", # Left border style
|
6783
|
-
"right": "thin", # Right border style
|
6784
|
-
"top": "thin", # Top border style
|
6785
|
-
"bottom": "thin", # Bottom border style
|
6786
|
-
"color": "000000", # Border color
|
6787
|
-
},
|
6788
|
-
}
|
6789
|
-
# * border settings
|
6790
|
-
# "thin": Thin border line
|
6791
|
-
# "medium": Medium border line
|
6792
|
-
# "thick": Thick border line
|
6793
|
-
# "dotted": Dotted border line
|
6794
|
-
# "dashed": Dashed border line
|
6795
|
-
# "hair": Hairline border (very thin)
|
6796
|
-
# "mediumDashed": Medium dashed border line
|
6797
|
-
# "dashDot": Dash-dot border line
|
6798
|
-
# "dashDotDot": Dash-dot-dot border line
|
6799
|
-
# "slantDashDot": Slant dash-dot border line
|
6800
|
-
},
|
6801
|
-
],
|
6802
|
-
width={2: 30}, # when it is None, it will automatic adjust width
|
6803
|
-
height={2: 50, 3: 25}, # keys are columns
|
6804
|
-
merge=(slice(0, 1), slice(1, 3)),
|
6805
|
-
shade={
|
6806
|
-
(slice(1, 4), slice(1, 3)): {
|
6807
|
-
"bg_color": "FFFF00", # Background color
|
6873
|
+
},{}]"""
|
6874
|
+
print(cell_tmp)
|
6875
|
+
# !Apply cell shading
|
6876
|
+
if shade:
|
6877
|
+
if not isinstance(shade, list):
|
6878
|
+
shade = [shade]
|
6879
|
+
for shade_ in shade:
|
6880
|
+
for indices, shading in shade_.items():
|
6881
|
+
cell_range = convert_indices_to_range(*indices)
|
6882
|
+
fill = PatternFill(
|
6883
|
+
start_color=hex2argb(shading.get("bg_color", "FFFFFF")),
|
6884
|
+
end_color=hex2argb(shading.get("end_color", "FFFFFF")),
|
6885
|
+
fill_type=shading.get("fill_type", "solid"),
|
6886
|
+
patternType=shading.get("pattern_type", "solid"),
|
6887
|
+
fgColor=hex2argb(shading.get("fg_color", "0000FF")),
|
6888
|
+
)
|
6889
|
+
for row in ws[cell_range]:
|
6890
|
+
for cell in row:
|
6891
|
+
cell.fill = fill
|
6892
|
+
if verbose:
|
6893
|
+
shade_temp="""shade={
|
6894
|
+
(slice(1, 4), slice(1, 3)): {
|
6895
|
+
"bg_color": "#63C187", # Background color
|
6808
6896
|
"pattern_type": "solid", # Fill pattern (e.g., solid, darkGrid, lightGrid)
|
6809
6897
|
"fg_color": "#0000FF", # Foreground color, used in patterns
|
6810
6898
|
"end_color": "0000FF", # End color, useful for gradients
|
6811
6899
|
"fill_type": "solid", # Type of fill (solid, gradient, etc.)
|
6812
|
-
}
|
6813
|
-
|
6814
|
-
comment={(2, 4): "This is a comment"},
|
6815
|
-
link={(2, 2): "https://example.com"},
|
6816
|
-
protect={
|
6817
|
-
"password": "123", # Password for sheet protection
|
6818
|
-
"sheet": False, # True, # Protect the sheet
|
6819
|
-
"objects": False, # True, # Protect objects
|
6820
|
-
"scenarios": False, # True, # Protect scenarios
|
6821
|
-
"formatCells": False, # Disable formatting cells
|
6822
|
-
"formatColumns": False, # Disable formatting columns
|
6823
|
-
"formatRows": False, # Disable formatting rows
|
6824
|
-
"insertColumns": False, # Disable inserting columns
|
6825
|
-
"insertRows": False, # Disable inserting rows
|
6826
|
-
"deleteColumns": False, # Disable deleting columns
|
6827
|
-
"deleteRows": False, # Disable deleting rows
|
6828
|
-
},
|
6829
|
-
number_format={
|
6830
|
-
1: "0.00", # Two decimal places for column index 1
|
6831
|
-
2: "#,##0", # Thousands separator
|
6832
|
-
3: "0%", # Percentage format
|
6833
|
-
4: "$#,##0.00", # Currency format
|
6834
|
-
},
|
6835
|
-
data_validation={
|
6836
|
-
(slice(1, 2), slice(2, 10)): {
|
6837
|
-
"type": "list",
|
6838
|
-
"formula1": '"Option1,Option2,Option3"', # List of options
|
6839
|
-
"allow_blank": True,
|
6840
|
-
"showDropDown": True,
|
6841
|
-
"showErrorMessage": True,
|
6842
|
-
"errorTitle": "Invalid input",
|
6843
|
-
"error": "Please select a valid option.",
|
6844
|
-
}
|
6845
|
-
},
|
6846
|
-
conditional_format={
|
6847
|
-
(slice(1, 2), slice(2, 10)): [
|
6848
|
-
{
|
6849
|
-
"color_scale": {
|
6850
|
-
"start_type": "min", # Type of start point (min, max, percent)
|
6851
|
-
"start_color": "#FF0000", # Starting color
|
6852
|
-
"end_type": "max", # End type (min, max, percent)
|
6853
|
-
"end_color": "00FF00", # Ending color
|
6854
|
-
"mid_type": "percentile", # Midpoint type (optional)
|
6855
|
-
"mid_value": 50, # Midpoint value (optional)
|
6856
|
-
"mid_color": "FFFF00", # Midpoint color (optional)
|
6857
|
-
}
|
6858
|
-
}
|
6859
|
-
]
|
6860
|
-
},
|
6861
|
-
)
|
6862
|
-
"""
|
6863
|
-
if usage:
|
6864
|
-
print(func_xample)
|
6865
|
-
return None
|
6866
|
-
kwargs.pop("format", None) # 更好地跟fsave结合使用
|
6867
|
-
kwargs.pop("sheet_name", None) # 更好地跟df.to_excel结合使用
|
6868
|
-
sheet_name_corr = (
|
6869
|
-
sheet_name if isinstance(sheet_name, str) else f"Sheet_{sheet_name}"
|
6870
|
-
)
|
6871
|
-
|
6872
|
-
# 只有openpyxl才支持 append
|
6873
|
-
mode = strcmp(kwargs.get("mode", "w"), ["a", "w"])[0]
|
6874
|
-
kwargs.pop("mode", None)
|
6875
|
-
engine = strcmp(kwargs.get("engine", "openpyxl"), ["xlsxwriter", "openpyxl"])[0]
|
6876
|
-
# 通常是不需要保存index的
|
6877
|
-
index = kwargs.get("index", False)
|
6878
|
-
kwargs.pop("index", None)
|
6879
|
-
# save file
|
6880
|
-
with pd.ExcelWriter(filename, mode=mode, engine=engine) as writer:
|
6881
|
-
df.to_excel(writer, sheet_name=sheet_name_corr, index=index, **kwargs)
|
6882
|
-
|
6883
|
-
wb = load_workbook(filename)
|
6884
|
-
if isinstance(sheet_name, str):
|
6885
|
-
ws = wb[sheet_name]
|
6886
|
-
elif isinstance(sheet_name, int):
|
6887
|
-
ws = wb.worksheets[sheet_name]
|
6888
|
-
else:
|
6889
|
-
ws = wb.worksheets[sheet_name] # the index of worksheets
|
6890
|
-
|
6891
|
-
# !Apply cell formatting
|
6892
|
-
if cell:
|
6893
|
-
if not isinstance(cell, list):
|
6894
|
-
cell = [cell]
|
6895
|
-
for cell_ in cell:
|
6896
|
-
for indices, format_options in cell_.items():
|
6897
|
-
cell_range = convert_indices_to_range(*indices)
|
6898
|
-
apply_format(ws, format_options, cell_range)
|
6899
|
-
|
6900
|
-
# !Apply cell shading
|
6901
|
-
if shade:
|
6902
|
-
for indices, shading in shade.items():
|
6903
|
-
cell_range = convert_indices_to_range(*indices)
|
6904
|
-
fill = PatternFill(
|
6905
|
-
start_color=hex2argb(shading.get("bg_color", "FFFFFF")),
|
6906
|
-
end_color=hex2argb(shading.get("end_color", "FFFFFF")),
|
6907
|
-
fill_type=shading.get("fill_type", "solid"),
|
6908
|
-
patternType=shading.get("pattern_type", "solid"),
|
6909
|
-
fgColor=hex2argb(shading.get("fg_color", "0000FF")),
|
6910
|
-
)
|
6911
|
-
for row in ws[cell_range]:
|
6912
|
-
for cell in row:
|
6913
|
-
cell.fill = fill
|
6914
|
-
|
6900
|
+
}}"""
|
6901
|
+
print(shade_temp)
|
6915
6902
|
# !number formatting
|
6916
6903
|
if number_format:
|
6917
|
-
|
6918
|
-
|
6919
|
-
|
6920
|
-
|
6904
|
+
if not isinstance(number_format, list):
|
6905
|
+
number_format = [number_format]
|
6906
|
+
for number_format_ in number_format:
|
6907
|
+
for col_idx, fmt in number_format_.items():
|
6908
|
+
col_letter = get_column_letter(col_idx)
|
6909
|
+
for cell in ws[col_letter][1:]: # Skip the header
|
6910
|
+
cell.number_format = fmt
|
6911
|
+
if verbose:
|
6912
|
+
number_format_temp="""number_format={
|
6913
|
+
1: "0.00", # Two decimal places for column index 1
|
6914
|
+
2: "#,##0", # Thousands separator
|
6915
|
+
3: "0%", # Percentage format
|
6916
|
+
4: "$#,##0.00", # Currency format
|
6917
|
+
}"""
|
6918
|
+
print(number_format_temp)
|
6921
6919
|
|
6920
|
+
if freeze:
|
6921
|
+
ws.freeze_panes = freeze # Freeze everything above and to the left of A2
|
6922
6922
|
if apply_filter:
|
6923
6923
|
if isinstance(apply_filter, bool):
|
6924
6924
|
# Default: Apply filter to the entire first row (header)
|
6925
6925
|
filter_range = f"A1:{get_column_letter(ws.max_column)}1"
|
6926
6926
|
ws.auto_filter.ref = filter_range
|
6927
|
-
|
6927
|
+
if not freeze:
|
6928
|
+
ws.freeze_panes = "A2" # Freeze everything above and to the left of A2
|
6928
6929
|
elif isinstance(apply_filter, tuple):
|
6929
6930
|
row_slice, col_slice = apply_filter
|
6930
|
-
|
6931
6931
|
# Extract the start and end indices for rows and columns
|
6932
6932
|
start_row, end_row = row_slice.start, row_slice.stop
|
6933
6933
|
start_col_idx, end_col_idx = col_slice.start, col_slice.stop
|
@@ -6947,20 +6947,30 @@ def format_excel(
|
|
6947
6947
|
|
6948
6948
|
# Apply the filter
|
6949
6949
|
ws.auto_filter.ref = filter_range
|
6950
|
+
if freeze:
|
6951
|
+
ws.freeze_panes = freeze # Freeze everything above and to the left of A2
|
6950
6952
|
# !widths
|
6951
6953
|
if width is None: # automatic adust width
|
6952
6954
|
for col in ws.columns:
|
6953
6955
|
max_length = 0
|
6954
|
-
column = col[0].column_letter # Get the column letter
|
6956
|
+
"""column = col[0].column_letter # Get the column letter"""
|
6957
|
+
# Check the first cell in the column to get the column letter
|
6958
|
+
cell_first = col[0]
|
6959
|
+
|
6960
|
+
# Check if the cell is part of a merged range
|
6961
|
+
if not any(cell_first.coordinate in range_ for range_ in ws.merged_cells.ranges):
|
6962
|
+
column = get_column_letter(cell_first.column) # Get the column letter from the first cell
|
6963
|
+
else:
|
6964
|
+
# Skip the column if the first cell is merged
|
6965
|
+
continue
|
6955
6966
|
for cell_ in col:
|
6956
6967
|
try:
|
6957
6968
|
if cell_.value:
|
6958
6969
|
max_length = max(max_length, len(str(cell_.value)))
|
6959
6970
|
except Exception:
|
6960
6971
|
pass
|
6961
|
-
adjusted_width = max_length +
|
6972
|
+
adjusted_width = max_length + width_factor # You can adjust the padding value as needed
|
6962
6973
|
ws.column_dimensions[column].width = adjusted_width
|
6963
|
-
|
6964
6974
|
else:
|
6965
6975
|
for col_idx, width_ in width.items():
|
6966
6976
|
col_letter = get_column_letter(col_idx)
|
@@ -7010,11 +7020,28 @@ def format_excel(
|
|
7010
7020
|
if not isinstance(comment, list):
|
7011
7021
|
comment = [comment]
|
7012
7022
|
for comment_ in comment:
|
7023
|
+
if not isinstance(comment_, dict):
|
7024
|
+
raise TypeError("Each item in the `comments` list must be a dictionary.")
|
7025
|
+
|
7013
7026
|
for (row, col), comment_str in comment_.items():
|
7014
|
-
|
7015
|
-
|
7016
|
-
)
|
7017
|
-
|
7027
|
+
if not isinstance(row, int) or not isinstance(col, int):
|
7028
|
+
raise ValueError("Row and column indices must be integers.")
|
7029
|
+
if not isinstance(comment_str, str):
|
7030
|
+
raise ValueError("Comment text must be a string.")
|
7031
|
+
|
7032
|
+
comment_curr = Comment(comment_str, "Author")
|
7033
|
+
comment_curr.visible = comment_always_visible
|
7034
|
+
# if comment_always_visible:
|
7035
|
+
# comment_curr.width = 200 # Adjust width
|
7036
|
+
# comment_curr.height = 100 # Adjust height
|
7037
|
+
ws.cell(row=row + 1, column=col + 1).comment = comment_curr
|
7038
|
+
if verbose:
|
7039
|
+
comment_tmp="""comment=[
|
7040
|
+
{(0, 0): "This is a comment for A1"},
|
7041
|
+
{(1, 1): "This is a comment for B2"},
|
7042
|
+
{(2, 2): "This is a comment for C3"},
|
7043
|
+
]"""
|
7044
|
+
print(comment_tmp)
|
7018
7045
|
# !Add link
|
7019
7046
|
if link:
|
7020
7047
|
if not isinstance(link, list):
|
@@ -7022,7 +7049,8 @@ def format_excel(
|
|
7022
7049
|
for link_ in link:
|
7023
7050
|
for (row, col), link_str in link_.items():
|
7024
7051
|
ws.cell(row=row + 1, column=col + 1).hyperlink = link_str
|
7025
|
-
|
7052
|
+
if verbose:
|
7053
|
+
print('link={(2, 2): "https://example.com"}')
|
7026
7054
|
# !Apply data validation
|
7027
7055
|
if data_validation:
|
7028
7056
|
for indices, validation in data_validation.items():
|
@@ -7030,70 +7058,131 @@ def format_excel(
|
|
7030
7058
|
dv = DataValidation(**validation)
|
7031
7059
|
ws.add_data_validation(dv)
|
7032
7060
|
dv.add(cell_range)
|
7033
|
-
|
7061
|
+
if verbose:
|
7062
|
+
print("""data_validation={
|
7063
|
+
(slice(1, 2), slice(2, 10)): {
|
7064
|
+
"type": "list",
|
7065
|
+
"formula1": '"Option1,Option2,Option3"', # List of options
|
7066
|
+
"allow_blank": True,
|
7067
|
+
"showDropDown": True,
|
7068
|
+
"showErrorMessage": True,
|
7069
|
+
"errorTitle": "Invalid input",
|
7070
|
+
"error": "Please select a valid option.",
|
7071
|
+
}
|
7072
|
+
}"""
|
7073
|
+
)
|
7034
7074
|
# !Protect sheet with a password
|
7035
|
-
|
7036
|
-
|
7075
|
+
# Fetch the password
|
7076
|
+
password = kwargs.pop("password", None) # Use kwargs if provided
|
7077
|
+
print(password)
|
7078
|
+
if all([password is not None, any([protect, isinstance(password, (str, list, tuple)) and any(password)])]): # Check if protection options are provided
|
7079
|
+
if protect is None:
|
7080
|
+
protect={}
|
7081
|
+
password = password or protect.get("password") # Default to 'protect' password if not set
|
7082
|
+
if password: # Apply password protection if provided
|
7083
|
+
ws.protection.password = password
|
7084
|
+
ws.protection.sheet = protect.get("sheet", bool(password))
|
7037
7085
|
ws.protection.objects = protect.get("objects", True)
|
7038
|
-
|
7039
|
-
ws.protection.scenarios = protect.get("scenarios",
|
7086
|
+
# Formatting options
|
7087
|
+
ws.protection.scenarios = protect.get("scenarios", False)
|
7040
7088
|
ws.protection.formatCells = protect.get("formatCells", False)
|
7041
7089
|
ws.protection.formatColumns = protect.get("formatColumns", False)
|
7042
7090
|
ws.protection.formatRows = protect.get("formatRows", False)
|
7091
|
+
# Insert and delete options
|
7043
7092
|
ws.protection.insertColumns = protect.get("insertColumns", True)
|
7044
7093
|
ws.protection.insertRows = protect.get("insertRows", True)
|
7045
7094
|
ws.protection.deleteColumns = protect.get("deleteColumns", True)
|
7046
7095
|
ws.protection.deleteRows = protect.get("deleteRows", True)
|
7096
|
+
# Select locked or unlocked cells
|
7097
|
+
ws.protection.selectLockedCells = protect.get("selectLockedCells", False)
|
7098
|
+
ws.protection.selectUnlockedCells = protect.get("selectUnlockedCells", False)
|
7099
|
+
|
7047
7100
|
|
7048
7101
|
# !conditional formatting
|
7049
7102
|
if conditional_format:
|
7050
|
-
|
7051
|
-
|
7052
|
-
|
7053
|
-
|
7054
|
-
|
7055
|
-
|
7056
|
-
|
7057
|
-
|
7058
|
-
|
7059
|
-
|
7060
|
-
|
7061
|
-
|
7062
|
-
|
7063
|
-
|
7064
|
-
|
7065
|
-
|
7066
|
-
|
7067
|
-
|
7068
|
-
|
7069
|
-
|
7070
|
-
|
7071
|
-
|
7072
|
-
|
7073
|
-
|
7074
|
-
|
7075
|
-
|
7076
|
-
|
7077
|
-
|
7078
|
-
|
7079
|
-
|
7080
|
-
|
7081
|
-
|
7082
|
-
|
7083
|
-
|
7084
|
-
|
7085
|
-
|
7086
|
-
|
7087
|
-
|
7088
|
-
|
7089
|
-
|
7090
|
-
|
7091
|
-
|
7092
|
-
|
7093
|
-
|
7094
|
-
|
7095
|
-
|
7096
|
-
|
7103
|
+
if not isinstance(conditional_format, list):
|
7104
|
+
conditional_format = [conditional_format]
|
7105
|
+
print(f"conditional_format dict setting: 'color_scale', 'data_bar' and 'icon_set[not-ready]'")
|
7106
|
+
for conditional_format_ in conditional_format:
|
7107
|
+
for indices, rules in conditional_format_.items():
|
7108
|
+
cell_range = convert_indices_to_range(*indices)
|
7109
|
+
if not isinstance(rules, list):
|
7110
|
+
rules=[rules]
|
7111
|
+
for rule in rules:
|
7112
|
+
# Handle color scale
|
7113
|
+
if "color_scale" in rule:
|
7114
|
+
color_scale = rule["color_scale"]
|
7115
|
+
start_color = hex2argb(color_scale.get("start_color", "FFFFFF"))
|
7116
|
+
mid_color = hex2argb(color_scale.get("mid_color", "FFFFFF"))
|
7117
|
+
end_color = hex2argb(color_scale.get("end_color", "FFFFFF"))
|
7118
|
+
|
7119
|
+
color_scale_rule = ColorScaleRule(
|
7120
|
+
start_type=color_scale.get("start_type", "min"),
|
7121
|
+
start_value=color_scale.get("start_value"),
|
7122
|
+
start_color=start_color,
|
7123
|
+
mid_type=color_scale.get("mid_type"),
|
7124
|
+
mid_value=color_scale.get("mid_value"),
|
7125
|
+
mid_color=mid_color,
|
7126
|
+
end_type=color_scale.get("end_type", "max"),
|
7127
|
+
end_value=color_scale.get("end_value"),
|
7128
|
+
end_color=end_color,
|
7129
|
+
)
|
7130
|
+
ws.conditional_formatting.add(cell_range, color_scale_rule)
|
7131
|
+
# Handle data bar
|
7132
|
+
if "data_bar" in rule:
|
7133
|
+
data_bar = rule["data_bar"]
|
7134
|
+
bar_color = hex2argb(data_bar.get("color", "638EC6"))
|
7135
|
+
|
7136
|
+
data_bar_rule = DataBarRule(
|
7137
|
+
start_type=data_bar.get("start_type", "min"),
|
7138
|
+
start_value=data_bar.get("start_value"),
|
7139
|
+
end_type=data_bar.get("end_type", "max"),
|
7140
|
+
end_value=data_bar.get("end_value"),
|
7141
|
+
color=bar_color,
|
7142
|
+
showValue=data_bar.get("show_value", True),
|
7143
|
+
)
|
7144
|
+
ws.conditional_formatting.add(cell_range, data_bar_rule)
|
7145
|
+
|
7146
|
+
# Handle icon set
|
7147
|
+
if "icon_set" in rule:
|
7148
|
+
icon_set = rule["icon_set"]
|
7149
|
+
icon_set_rule = IconSet(
|
7150
|
+
iconSet=icon_set.get("iconSet", "3TrafficLights1"), # Corrected
|
7151
|
+
showValue=icon_set.get("show_value", True), # Corrected
|
7152
|
+
reverse=icon_set.get("reverse", False) # Corrected
|
7153
|
+
)
|
7154
|
+
ws.conditional_formatting.add(cell_range, icon_set_rule)
|
7155
|
+
if verbose:
|
7156
|
+
conditional_format_temp="""
|
7157
|
+
conditional_format={
|
7158
|
+
(slice(1, 3), slice(1, 4)): [
|
7159
|
+
{
|
7160
|
+
"data_bar": {
|
7161
|
+
"start_type": "min",
|
7162
|
+
"start_value": 100,
|
7163
|
+
"end_type": "max",
|
7164
|
+
"end_value": None,
|
7165
|
+
"color": "F6C9CE",
|
7166
|
+
"show_value": True,
|
7167
|
+
}
|
7168
|
+
},
|
7169
|
+
{
|
7170
|
+
"color_scale": {
|
7171
|
+
"start_type": "min",
|
7172
|
+
"start_value": 0,
|
7173
|
+
"start_color": "#74ADE9",
|
7174
|
+
"mid_type": "percentile",
|
7175
|
+
"mid_value": 50,
|
7176
|
+
"mid_color": "74ADE9",
|
7177
|
+
"end_type": "max",
|
7178
|
+
"end_value": 100,
|
7179
|
+
"end_color": "#B62833",
|
7180
|
+
}
|
7181
|
+
},
|
7182
|
+
]
|
7183
|
+
}
|
7184
|
+
"""
|
7185
|
+
print(conditional_format_temp)
|
7097
7186
|
|
7098
7187
|
# Save the workbook
|
7099
7188
|
wb.save(filename)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: py2ls
|
3
|
-
Version: 0.2.
|
3
|
+
Version: 0.2.5.1
|
4
4
|
Summary: py(thon)2(too)ls
|
5
5
|
Author: Jianfeng
|
6
6
|
Author-email: Jianfeng.Liu0413@gmail.com
|
@@ -18,7 +18,6 @@ Provides-Extra: full
|
|
18
18
|
Requires-Dist: CacheControl (>=0.13.1)
|
19
19
|
Requires-Dist: Cython (>=3.0.10)
|
20
20
|
Requires-Dist: Deprecated (>=1.2.14)
|
21
|
-
Requires-Dist: GPUtil (>=1.4.0)
|
22
21
|
Requires-Dist: Jinja2 (>=3.1.4)
|
23
22
|
Requires-Dist: Markdown (>=3.6)
|
24
23
|
Requires-Dist: MarkupSafe (>=2.1.5)
|
@@ -243,7 +243,7 @@ py2ls/export_requirements.py,sha256=x2WgUF0jYKz9GfA1MVKN-MdsM-oQ8yUeC6Ua8oCymio,
|
|
243
243
|
py2ls/fetch_update.py,sha256=9LXj661GpCEFII2wx_99aINYctDiHni6DOruDs_fdt8,4752
|
244
244
|
py2ls/freqanalysis.py,sha256=F4218VSPbgL5tnngh6xNCYuNnfR-F_QjECUUxrPYZss,32594
|
245
245
|
py2ls/ich2ls.py,sha256=3E9R8oVpyYZXH5PiIQgT3CN5NxLe4Dwtm2LwaeacE6I,21381
|
246
|
-
py2ls/ips.py,sha256=
|
246
|
+
py2ls/ips.py,sha256=feg70WFrLxtO-nx7Zvc62Snk4XHvCFdpdJk6J5K5f5k,432563
|
247
247
|
py2ls/ml2ls.py,sha256=I-JFPdikgEtfQjhv5gBz-QSeorpTJI_Pda_JwkTioBY,209732
|
248
248
|
py2ls/mol.py,sha256=AZnHzarIk_MjueKdChqn1V6e4tUle3X1NnHSFA6n3Nw,10645
|
249
249
|
py2ls/netfinder.py,sha256=OhqD3S9PuwweL2013D-q4GNP1WvJjuYfZzq5BZgGddE,68980
|
@@ -255,6 +255,6 @@ py2ls/sleep_events_detectors.py,sha256=bQA3HJqv5qnYKJJEIhCyhlDtkXQfIzqksnD0YRXso
|
|
255
255
|
py2ls/stats.py,sha256=qBn2rJmNa_QLLUqjwYqXUlGzqmW94sgA1bxJU2FC3r0,39175
|
256
256
|
py2ls/translator.py,sha256=77Tp_GjmiiwFbEIJD_q3VYpQ43XL9ZeJo6Mhl44mvh8,34284
|
257
257
|
py2ls/wb_detector.py,sha256=7y6TmBUj9exCZeIgBAJ_9hwuhkDh1x_-yg4dvNY1_GQ,6284
|
258
|
-
py2ls-0.2.
|
259
|
-
py2ls-0.2.
|
260
|
-
py2ls-0.2.
|
258
|
+
py2ls-0.2.5.1.dist-info/METADATA,sha256=rhiKgjEDp6x6mkBonb6xGelg00roAhr7NaeZYCfmhuM,20440
|
259
|
+
py2ls-0.2.5.1.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
|
260
|
+
py2ls-0.2.5.1.dist-info/RECORD,,
|
File without changes
|