Skip to content

Commit

Permalink
ADD: export options for the stat module
Browse files Browse the repository at this point in the history
  • Loading branch information
ldevillez committed Sep 18, 2024
1 parent 986bcb8 commit b5c0419
Show file tree
Hide file tree
Showing 5 changed files with 152 additions and 7 deletions.
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

0 comments on commit b5c0419

Please sign in to comment.