Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ADD: export options for the stat module #19

Merged
merged 1 commit into from
Sep 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
- Adding the type of component into the display of the stat module
- Adding a flag to filter the type of components
- Adding the number of drawings using the parts
- Adding an option to export stats module to CSV or to clipboard
### Changed
### Deprecated
### Removed
Expand Down
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,11 @@ You can select the type of compoments to include with `--filter`:
- `part`: get only parts. Works only for list display
- `assembly`: get only assemblies

You can select the export that you want with `--export`:
- `none`: (default) no export, only the display
- `csv`: generate a `bom.csv` in the same directory as the solidworks project
- `clipboard`: put the bom in the clipboard. You can copy paste it directly in excel

You can get only the elements with a default density (1000 Kg/m³) with the option `--only-default-density`. This option only works with a `type `list`

### Clean
Expand Down
22 changes: 22 additions & 0 deletions pyswtools/stat/definitions.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,22 +25,44 @@ class TypeSort(str, Enum):
NAME = "name"


class TypeExport(str, Enum):
"""Class represeting an export type"""

NONE = "none"
CSV = "csv"
CLIPBOARD = "clipboard"


@dataclass
class StatComponentTree:
"""
Class represting a component for a tree structure
"""

number: int
children: list

def dict(self):
"""
Convert the class to a dict structure
"""
return {k: v for k, v in asdict(self).items()}


@dataclass
class StatComponent:
"""
Class representing a component for the stat module
"""

mass: float
density: float
number: int
typeComponent: TypeComponent
numberDrawing: int

def dict(self):
"""
Convert the class to a dict structure
"""
return {k: v for k, v in asdict(self).items()}
97 changes: 90 additions & 7 deletions pyswtools/stat/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@
import click

# pylint: disable=relative-beyond-top-level
from ..utils import check_system, check_system_verbose
from ..helper_sw import open_app_and_file, is_temp, is_assembly, open_drawing
from ..utils import check_system, check_system_verbose, do_windows_clipboard
from ..helper_sw import open_app_and_file, is_temp, is_assembly

from .definitions import (
TypeComponent,
TypeExport,
TypeOutput,
TypeSort,
StatComponent,
Expand Down Expand Up @@ -192,6 +193,36 @@ def remove_duplicate_conf_list(struct: dict, mass_struct: dict) -> None:
del struct[name_test]


def sort_tulpe(
struct: list, mass_struct: dict, type_sort: TypeSort = TypeSort.MASS
) -> list:
"""
Sort a list of tuple struct following a given sort
The first element of a tuple is the name and the second is the structure
"""

if type_sort is TypeSort.NAME:
return sorted(
struct,
key=lambda i: i[0],
reverse=True,
)
if type_sort is TypeSort.MASS:
return sorted(
struct,
key=lambda i: mass_struct[i[0]].mass * i[1].number,
reverse=True,
)
if type_sort is TypeSort.MASS_PART:
return sorted(
struct,
key=lambda i: mass_struct[i[0]].mass,
reverse=True,
)

return struct


def sort_key_struct(struct: dict, type_sort: TypeSort = TypeSort.MASS) -> list:
"""
Sort a dict struct following a given sort
Expand Down Expand Up @@ -238,6 +269,47 @@ def sort_key_struct_tree(
return tree_struct.keys()


def export_struct(
struct, mass_struct, type_export: TypeExport, type_sort: TypeSort, root_dir: str
):
"""
export struct
"""
delim = "\t"
if type_export is TypeExport.CSV:
delim = ";"
cols = ["Name", "Mtot", "n", "Mpart", "Density", "Comp", "Drw"]
txt = delim.join(cols) + "\n"
stacks = sort_tulpe(list(struct.items()), mass_struct, type_sort)

while len(stacks) > 0:
k, v = stacks.pop(0)

values = [
k,
f"{mass_struct[k].mass * v.number:.3f}",
str(v.number),
f"{mass_struct[k].mass:.3f}",
f"{mass_struct[k].density:.3f}",
str(
"Part" if mass_struct[k].typeComponent == TypeComponent.PART else "Ass."
),
str(mass_struct[k].numberDrawing),
]
txt += delim.join(values) + "\n"

if hasattr(v, "children"):
stacks = (
sort_tulpe(list(v.children.items()), mass_struct, type_sort) + stacks
)

if type_export is TypeExport.CLIPBOARD:
do_windows_clipboard(txt)
elif type_export is TypeExport.CSV:
with open(os.path.join(root_dir, "bom.csv"), "w+", encoding="utf8") as f:
f.write(txt)


def print_header():
"""
Display the header of the output
Expand Down Expand Up @@ -433,6 +505,9 @@ def complete_info_assembly(sw_comp, dict_of_comp: dict) -> dict:
@click.option(
"--type-sort", "type_sort", type=click.Choice(TypeSort), default=TypeSort.MASS
)
@click.option(
"--export", "export", type=click.Choice(TypeExport), default=TypeExport.NONE
)
@click.option(
"--filter",
"type_component",
Expand All @@ -442,6 +517,7 @@ def complete_info_assembly(sw_comp, dict_of_comp: dict) -> dict:
@click.option("--only-default-density", "only_default_density", is_flag=True)
def stat(
input_path: str,
export: TypeExport,
type_output: TypeOutput,
type_sort: TypeSort,
type_component: TypeComponent,
Expand Down Expand Up @@ -486,18 +562,25 @@ def stat(

clean_confs(tree_of_comp, dict_of_comp)

print(tree_of_comp)

filter_component_type(tree_of_comp, dict_of_comp, type_component)

if only_default_density:
dict_of_comp = filter_density_list(dict_of_comp)

if type_output is TypeOutput.TREE:
display_tree(tree_of_comp, dict_of_comp, type_sort)
elif type_output is TypeOutput.LIST:

if only_default_density:
dict_of_comp = filter_density_list(dict_of_comp)
display_list(dict_of_comp, type_sort)
else:
click.echo(
f"Type output {type_output} is unknown {type_output is TypeOutput.TREE} {type(type_output)}"
)

if export is not TypeExport.NONE:
export_struct(
tree_of_comp if type_output is TypeOutput.TREE else dict_of_comp,
dict_of_comp,
export,
type_sort,
os.path.dirname(input_path),
)
34 changes: 34 additions & 0 deletions pyswtools/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import platform
import click
import ctypes


def check_system_verbose() -> bool:
Expand All @@ -21,3 +22,36 @@ def check_system() -> bool:
Check if the system is windows
"""
return platform.system() == "Windows"


def do_windows_clipboard(text):
"""
Put text into the clipboard
"""
# from http://pylabeditor.svn.sourceforge.net/viewvc/pylabeditor/trunk/src/shells.py?revision=82&view=markup

cf_unicode_text = 13
ghnd = 66

ctypes.windll.kernel32.GlobalAlloc.restype = ctypes.c_void_p
ctypes.windll.kernel32.GlobalLock.restype = ctypes.c_void_p

buffer_size = (len(text) + 1) * 2
h_global_mem = ctypes.windll.kernel32.GlobalAlloc(
ctypes.c_uint(ghnd), ctypes.c_size_t(buffer_size)
)
lp_global_mem = ctypes.windll.kernel32.GlobalLock(ctypes.c_void_p(h_global_mem))
ctypes.cdll.msvcrt.memcpy(
ctypes.c_void_p(lp_global_mem),
ctypes.c_wchar_p(text),
ctypes.c_int(buffer_size),
)
ctypes.windll.kernel32.GlobalUnlock(ctypes.c_void_p(h_global_mem))
if ctypes.windll.user32.OpenClipboard(0):
ctypes.windll.user32.EmptyClipboard()
ctypes.windll.user32.SetClipboardData(
ctypes.c_int(cf_unicode_text), ctypes.c_void_p(h_global_mem)
)
ctypes.windll.user32.CloseClipboard()
return True
return False
Loading