diff --git a/pyproject.toml b/pyproject.toml index 74dfe03c3..0913b477b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,3 +24,6 @@ keywords = ["steel design", "civil engineering", "engineering"] [project.gui-scripts] osdag = "osdag.osdagMainPage:do_stuff" + +[project.scripts] +osdag-cli = "osdag_cli:testing" diff --git a/src/osdag/Common.py b/src/osdag/Common.py index bd5c20630..21f4f7fdb 100644 --- a/src/osdag/Common.py +++ b/src/osdag/Common.py @@ -10,6 +10,8 @@ from importlib.resources import files PATH_TO_DATABASE = files("osdag.data.ResourceFiles.Database").joinpath("Intg_osdag.sqlite") +#PATH_TO_DATABASE = "D:/Internship/My Forked Clone for CLI/Osdag/src/osdag/data/ResourceFiles/Database/Intg_osdag.sqlite" + import sqlite3 diff --git a/src/osdag/Example_1.1.1.1.2.osi b/src/osdag/Example_1.1.1.1.2.osi new file mode 100644 index 000000000..2ac76beb6 --- /dev/null +++ b/src/osdag/Example_1.1.1.1.2.osi @@ -0,0 +1,26 @@ +Bolt.Bolt_Hole_Type: Standard +Bolt.Diameter: +- '20' +Bolt.Grade: +- '6.8' +Bolt.Slip_Factor: '0.3' +Bolt.TensionType: Non pre-tensioned +Bolt.Type: Bearing Bolt +Connectivity: Column Flange-Beam Web +Connector.Material: E 300 (Fe 440) +Connector.Plate.Thickness_List: +- '14' +Design.Design_Method: Limit State Design +Detailing.Corrosive_Influences: 'No' +Detailing.Edge_type: Sheared or hand flame cut +Detailing.Gap: '10' +Load.Axial: '35' +Load.Shear: '175' +Material: E 300 (Fe 440) +Member.Supported_Section.Designation: WB 300 +Member.Supported_Section.Material: E 300 (Fe 440) +Member.Supporting_Section.Designation: PBP 320 X 88.48 +Member.Supporting_Section.Material: E 300 (Fe 440) +Module: Fin Plate Connection +Weld.Fab: Shop Weld +Weld.Material_Grade_OverWrite: '440' \ No newline at end of file diff --git a/src/osdag/Example_1.1.2.1.1.osi b/src/osdag/Example_1.1.2.1.1.osi new file mode 100644 index 000000000..1c0380be2 --- /dev/null +++ b/src/osdag/Example_1.1.2.1.1.osi @@ -0,0 +1,38 @@ +Bolt.Bolt_Hole_Type: Standard +Bolt.Diameter: +- '16' +- '20' +- '24' +- '30' +Bolt.Grade: +- '4.8' +- '5.6' +- '6.8' +- '9.8' +Bolt.Slip_Factor: '0.3' +Bolt.TensionType: Non pre-tensioned +Bolt.Type: Bearing Bolt +Connectivity: Column Flange-Beam Web +Connector.Material: E 250 (Fe 410 W)A +Connector.Plate.Thickness_List: +- '14' +- '16' +- '18' +- '20' +- '22' +- '25' +- '28' +Design.Design_Method: Limit State Design +Detailing.Corrosive_Influences: 'No' +Detailing.Edge_type: Rolled, machine-flame cut, sawn and planed +Detailing.Gap: '10' +Load.Axial: '125' +Load.Shear: '240' +Material: E 350 (Fe 490) +Member.Supported_Section.Designation: LB 400 +Member.Supported_Section.Material: E 250 (Fe 410 W)A +Member.Supporting_Section.Designation: PBP 300 X 109.54 +Member.Supporting_Section.Material: E 350 (Fe 490) +Module: End Plate Connection +Weld.Fab: Shop Weld +Weld.Material_Grade_OverWrite: '490' diff --git a/src/osdag/New folder/Example_1.1.1.1.2.osi b/src/osdag/New folder/Example_1.1.1.1.2.osi new file mode 100644 index 000000000..e1ec44608 --- /dev/null +++ b/src/osdag/New folder/Example_1.1.1.1.2.osi @@ -0,0 +1,26 @@ +Bolt.Bolt_Hole_Type: Standard +Bolt.Diameter: +- '20' +Bolt.Grade: +- '6.8' +Bolt.Slip_Factor: '0.3' +Bolt.TensionType: Non pre-tensioned +Bolt.Type: Bearing Bolt +Connectivity: Column Flange-Beam Web +Connector.Material: E 300 (Fe 440) +Connector.Plate.Thickness_List: +- '14' +Design.Design_Method: Limit State Design +Detailing.Corrosive_Influences: 'No' +Detailing.Edge_type: Sheared or hand flame cut +Detailing.Gap: '10' +Load.Axial: '35' +Load.Shear: '175' +Material: E 300 (Fe 440) +Member.Supported_Section.Designation: WB 300 +Member.Supported_Section.Material: E 300 (Fe 440) +Member.Supporting_Section.Designation: PBP 320 X 88.48 +Member.Supporting_Section.Material: E 300 (Fe 440) +Module: Fin Plate Connection +Weld.Fab: Shop Weld +Weld.Material_Grade_OverWrite: '440' diff --git a/src/osdag/README.md b/src/osdag/README.md new file mode 100644 index 000000000..227da97f5 --- /dev/null +++ b/src/osdag/README.md @@ -0,0 +1,147 @@ +# Osdag CLI Tool + +A command-line interface for Osdag structural engineering design software. + +## Installation + +```bash +pip install -e . +``` + +## Usage + +The Osdag CLI tool provides several commands for processing design files and generating reports: + +### Process Design + +Process an OSI file, run design calculations, show output values, and generate a design report: + +```bash +osdag-cli process-design [-i input_file.osi] [-o output_directory] [-v] +``` + +Options: + +- `-i, --input-file`: Path to the OSI input file (optional, will prompt if not provided) +- `-o, --output-dir`: Directory to save the design report (optional) +- `-v, --verbose`: Enable verbose output + +### Show Design Output + +Process an OSI file, run design calculations, and show output values without generating a report: + +```bash +osdag-cli show-design-output [-i input_file.osi] +``` + +Options: + +- `-i, --input-file`: Path to the OSI file (optional, will prompt if not provided) + +### Custom Design + +Run design calculations directly using a custom implementation (currently supports only Fin Plate Connection): + +```bash +osdag-cli custom-design [-i input_file.osi] +``` + +Options: + +- `-i, --input-file`: Path to the OSI file (optional, will prompt if not provided) + +This command implements a more direct design calculation approach that may be more reliable for certain modules. + +### Debug Module + +Analyze a module, show its methods and inheritance hierarchy: + +```bash +osdag-cli debug-module [-i input_file.osi] +``` + +Options: + +- `-i, --input-file`: Path to the OSI input file (optional, will prompt if not provided) + +### Show Content + +Parse and display the content of an OSI file: + +```bash +osdag-cli show-content [-i input_file.osi] +``` + +Options: + +- `-i, --input-file`: Path to the OSI input file (optional, will prompt if not provided) + +### List Modules + +List all valid module names: + +```bash +osdag-cli list-modules +``` + +### Interactive Shell + +Start an interactive shell to run multiple commands: + +```bash +osdag-cli shell +``` + +In shell mode, you can run any of the above commands without the `osdag-cli` prefix. Use `exit` or `quit` to exit the shell. + +## Examples + +1. Process a design file and generate a report in the specified directory: + +```bash +osdag-cli process-design -i examples/fin_plate.osi -o ./reports +``` + +2. Process a design file with verbose output: + +```bash +osdag-cli process-design -i examples/fin_plate.osi -o ./reports -v +``` + +3. Show design output values without generating a report: + +```bash +osdag-cli show-design-output -i examples/fin_plate.osi +``` + +4. Debug a module to see its methods and attributes: + +```bash +osdag-cli debug-module -i examples/fin_plate.osi +``` + +5. View all supported modules: + +```bash +osdag-cli list-modules +``` + +6. Start an interactive shell: + +```bash +osdag-cli shell +``` + +Then within the shell: + +``` +> process-design +> list-modules +> exit +``` + +7. Try the custom design implementation for a Fin Plate Connection: + +```bash +osdag-cli custom-design -i examples/fin_plate.osi +``` diff --git a/src/osdag/cli_shell.py b/src/osdag/cli_shell.py new file mode 100644 index 000000000..ef9e839a3 --- /dev/null +++ b/src/osdag/cli_shell.py @@ -0,0 +1,291 @@ +import click +import yaml +import click_repl +import sys +import os +#import readline +import re +from .Common import ( + TYPE_COMBOBOX, TYPE_COMBOBOX_CUSTOMIZED, TYPE_TEXTBOX, + KEY_DISP_FINPLATE, KEY_DISP_ENDPLATE, KEY_DISP_CLEATANGLE, + KEY_DISP_SEATED_ANGLE, KEY_DISP_COLUMNCOVERPLATE, + KEY_DISP_COLUMNCOVERPLATEWELD, KEY_DISP_BEAMCOVERPLATE, + KEY_DISP_BEAMCOVERPLATEWELD, KEY_DISP_BB_EP_SPLICE, + KEY_DISP_COLUMNENDPLATE, KEY_DISP_BASE_PLATE, + KEY_DISP_TENSION_BOLTED, KEY_DISP_TENSION_WELDED +) +from .design_type.connection.fin_plate_connection import FinPlateConnection +from .design_type.connection.end_plate_connection import EndPlateConnection +from .design_type.connection.cleat_angle_connection import CleatAngleConnection +from .design_type.connection.seated_angle_connection import SeatedAngleConnection +from .design_type.connection.column_cover_plate import ColumnCoverPlate +from .design_type.connection.column_cover_plate_weld import ColumnCoverPlateWeld +from .design_type.connection.beam_cover_plate import BeamCoverPlate +from .design_type.connection.beam_cover_plate_weld import BeamCoverPlateWeld +from .design_type.connection.beam_beam_end_plate_splice import BeamBeamEndPlateSplice +from .design_type.connection.column_end_plate import ColumnEndPlate +from .design_type.connection.beam_column_end_plate import BeamColumnEndPlate +from .design_type.connection.base_plate_connection import BasePlateConnection +from .design_type.tension_member.tension_bolted import Tension_bolted +from .design_type.tension_member.tension_welded import Tension_welded + + + + +base_dir = r"D:\Internship\My Forked Clone for CLI\Osdag\src\osdag" +file_path = os.path.join(base_dir, "osdagMainPage.py") + + +############################### ALL FUNCTIONS ################################ + +############## 1 ) MODULE VALIDATION AND VERIFICATION ######################### +VALID_MODULES = ["Fin Plate Connection","End Plate Connection"] + + +def validate_module_name(module_name): + if module_name.lower() in [mod.lower() for mod in VALID_MODULES]: # Case-insensitive check + return True + else: + raise ValueError(f"Invalid module name: {module_name}.") + +############## 2 ) AUTO MODULE DETECTION ######################### + +def detect_imported_modules(): + """Auto-detect imported modules in osdagMainPage.py and filter relevant ones.""" + # file_path = r"D:\Internship\My Forked Clone for CLI\Osdag\src\osdag\osdagMainPage.py" + pattern = re.compile(r'^(?:from)\s+([\w\.]+)(?:\s+import\s+([\w\*, ]+))?') + + relevant_modules = set() + + if os.path.exists(file_path): + with open(file_path, 'r') as file: + for line in file: + match = pattern.match(line.strip()) + if match: + module_path = match.group(1) # Full module path + imports = match.group(2) # Extracts specific imports (like FinPlateConnection) + + if imports: + # If specific imports exist, extract and store them + for item in imports.split(","): + item = item.strip() + if item and item != "*": # Ignore wildcard imports + relevant_modules.add(item) + else: + # Otherwise, store the full module name if no specific imports + relevant_modules.add(module_path) + + return sorted(relevant_modules) + + + + +# VALID_MODULES = detect_imported_modules() # Auto-update detected modules + +################################# END OF ALL FUNCTIONS ################################## + + + + +################################## START ALL COMMANDS #################################### + + + + +@click.group() +def cli(): + """Osdag CLI tool with interactive shell""" + pass + +@cli.command() +def shell(): + """Start the interactive shell""" + #os.environ["CLICK_REPL_PROMPT"] = "osdag> " + click.echo("Entering Osdag shell. Type '--help' for commands.") + from click import Context + ctx = Context(cli) + click_repl.repl(ctx) + + +@cli.command() +@click.option('-i', '--input-file', required=True, type=click.Path(exists=True), help='Input YAML file path') +def find_module(input_file): + """Find and extract the Module line from a YAML file.""" + with open(input_file, 'r') as file: + for line in file: + if line.strip().startswith('Module:'): + module_name = line.split('Module:')[1].strip() + try: + validate_module_name(module_name) + click.echo(f"Found module: {module_name}") + except ValueError as e: + print(e) + + # return + # click.echo("No Module line found in the file") + +@cli.command() +@click.option('-i', '--input-file', required=True, type=click.Path(exists=True), help='Input YAML file path') +def parse_yaml(input_file): + """Parse and display the entire YAML file content.""" + with open(input_file, 'r') as file: + data = yaml.safe_load(file) + click.echo(data) + + +################################## TESTING FUNCTIONS ONLY ################################# + +@cli.command() +def list_modules(): + ##"""List all detected modules.""" + click.echo("Detected Modules:") + modules = detect_imported_modules() + # print("Filtered Modules:") + for module in modules: + print(f"- {module}") + + + +################################# END OF TESTING FUNCTIONS ########################### + + +@cli.command() +def exit(): + ##"""Exit the Osdag shell.""" + click.echo("Exiting Osdag shell.") + os._exit(0) + +@cli.command() +def quit(): + ##"""Exit the Osdag shell.""" + click.echo("Exiting Osdag shell.") + os._exit(0) + +def parse_osi_file(input_file): + """Parse an OSI file and extract values in the same format as the GUI. + + Args: + input_file (str): Path to the OSI file + + Returns: + dict: Dictionary containing the design inputs in the same format as the GUI + """ + with open(input_file, 'r') as file: + data = yaml.safe_load(file) + + # Extract module name + module = data.get('Module') + if not module: + raise ValueError("No Module specified in OSI file") + + # Get the appropriate module class + module_class = get_module_class(module) + if not module_class: + raise ValueError(f"Unknown module {module}") + + # Create module instance to get input definitions + module_instance = module_class() + + # Get input definitions from module + input_definitions = module_instance.input_values() + + # Create design inputs dictionary + design_inputs = {} + + # Process each input definition + for input_def in input_definitions: + key = input_def[0] # Input key + input_type = input_def[2] # Input type (combobox, textbox, etc.) + + if key is None: # Skip titles and other non-input elements + continue + + if key not in data: + continue + + value = data[key] + + # Handle different input types + if input_type == TYPE_COMBOBOX: + # For combobox, validate against allowed values + allowed_values = input_def[3] # List of allowed values + if value not in allowed_values: + raise ValueError(f"Invalid value '{value}' for {key}. Must be one of: {allowed_values}") + design_inputs[key] = value + + elif input_type == TYPE_COMBOBOX_CUSTOMIZED: + # For customized combobox, handle special cases + if key == 'Member.Designation': + # Handle section designation specially + design_inputs[key] = value + else: + # For other customized inputs, validate against allowed values + allowed_values = input_def[3] + if value not in allowed_values: + raise ValueError(f"Invalid value '{value}' for {key}. Must be one of: {allowed_values}") + design_inputs[key] = value + + elif input_type == TYPE_TEXTBOX: + # For textbox, just store the value + design_inputs[key] = str(value) + + else: + # For other types, store as is + design_inputs[key] = value + + return design_inputs, module + +@cli.command() +@click.option('-i', '--input-file', required=True, type=click.Path(exists=True), help='Input OSI file path') +def process_design(input_file): + """Process an OSI file and run design calculations.""" + try: + # Parse the OSI file + design_inputs, module = parse_osi_file(input_file) + + # Get the appropriate module class + module_class = get_module_class(module) + if not module_class: + click.echo(f"Error: Unknown module {module}") + return + + # Create module instance and set inputs + module_instance = module_class() + module_instance.set_input_values(design_inputs) + + # Run design calculations + module_instance.trial_design() + + # Get output values + output_values = module_instance.get_output_values() + + # Display results + click.echo("\nDesign Results:") + for key, value in output_values.items(): + click.echo(f"{key}: {value}") + + except Exception as e: + click.echo(f"Error processing design: {str(e)}") + +def get_module_class(module_name): + """Get the appropriate module class based on module name.""" + module_map = { + 'Fin Plate Connection': FinPlateConnection, + 'End Plate Connection': EndPlateConnection, + 'Cleat Angle Connection': CleatAngleConnection, + 'Seated Angle Connection': SeatedAngleConnection, + 'Column Cover Plate': ColumnCoverPlate, + 'Column Cover Plate Weld': ColumnCoverPlateWeld, + 'Beam Cover Plate': BeamCoverPlate, + 'Beam Cover Plate Weld': BeamCoverPlateWeld, + 'Beam-Beam End Plate Splice': BeamBeamEndPlateSplice, + 'Column End Plate': ColumnEndPlate, + 'Beam-Column End Plate': BeamColumnEndPlate, + 'Base Plate Connection': BasePlateConnection, + 'Tension Member Design - Bolted to End Gusset': Tension_bolted, + 'Tension Member Design - Welded to End Gusset': Tension_welded + } + return module_map.get(module_name) + +if __name__ == '__main__': + cli() \ No newline at end of file diff --git a/src/osdag/cli_tool.py b/src/osdag/cli_tool.py new file mode 100644 index 000000000..4f490b089 --- /dev/null +++ b/src/osdag/cli_tool.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python + +import click +import os +# import yaml + +# @click.command() +# @click.option('-i', help = 'Enter the input File Path for the report') +# @click.argument('file_path', type=click.Path(exists=True)) +# def determine_module(file_path): +# """ +# Reads a plain text file line by line, extracts the 'Module' field, +# and determines the module type. +# """ +# try: +# with open(file_path, 'r') as file: +# for line in file: +# # Check if the line contains the 'Module' field +# if line.startswith("Module:"): +# # Extract and print the module name +# module_name = line.split(':', 1)[1].strip() +# click.echo(f"Module: {module_name}") +# return +# click.echo("Error: 'Module' field not found in the file.") +# except Exception as e: +# click.echo(f"An error occurred: {e}") + +@click.command() +@click.option('--name','-n',default = 'OSDAG', help = 'Firstname description') +def testing(name): + print("hello world, My name is {}".format(name)) + + +if __name__ == "__main__": + testing() diff --git a/src/osdag/data/ResourceFiles/Database/osdag.db b/src/osdag/data/ResourceFiles/Database/osdag.db new file mode 100644 index 000000000..6ddc5add2 Binary files /dev/null and b/src/osdag/data/ResourceFiles/Database/osdag.db differ diff --git a/src/osdag/osdagMainPage.py b/src/osdag/osdagMainPage.py index d49647a3b..29d15d463 100644 --- a/src/osdag/osdagMainPage.py +++ b/src/osdag/osdagMainPage.py @@ -261,7 +261,7 @@ def __init__(self): self.ui.myStackedWidget.currentChanged.connect(self.current_changed) self.Under_Development='UNDER DEVELOPMENT' self.Modules={ - 'Connection' : { + 'Connections' : { 'Shear Connection' : [ ('Fin Plate',str(files("osdag.data.ResourceFiles.images").joinpath("finplate.png")),'Fin_Plate'), ('Cleat Angle',str(files("osdag.data.ResourceFiles.images").joinpath("cleatAngle.png")),'Cleat_Angle'), diff --git a/src/osdag/setup.py b/src/osdag/setup.py new file mode 100644 index 000000000..09c40c433 --- /dev/null +++ b/src/osdag/setup.py @@ -0,0 +1,20 @@ +# setup.py + +from setuptools import setup, find_packages + +setup( + name='osdag-cli', + version='0.1.0', + packages=find_packages(), + include_package_data=True, + install_requires=[ + 'Click', + 'PyYAML', + 'PyQt5', + ], + entry_points={ + 'console_scripts': [ + 'osdag-cli = osdag.cli_shell:cli', + ], + }, +)