Skip to content

Commit

Permalink
Added Feature to DatabaseTable reads
Browse files Browse the repository at this point in the history
- Can export as pandas dataframe (default) or `list[dict[str, Any]]`
- Always returns in default SI units (N, m, C)
  • Loading branch information
rpakishore committed Feb 27, 2024
1 parent 1d6730b commit daf211d
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 27 deletions.
15 changes: 11 additions & 4 deletions documentation/Layout.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,16 @@ Control the database values
Usage Examples

```python
#Database
sap.Table.list_available() #Lists available database tables
sap.Table.list_all() #Lists all database tables
tables = sap.Table
tables.list_available() #Lists available database tables
tables.list_all() #Lists all database tables
tables.get_table_fields('Analysis Options') #Get table Field Info
tables.get(TableKey='Load Case Definitions', dataframe=False) #Get Table data in `list[dict]` format
df = tables.get('Material Properties 01 - General') #Get Table data in pandas dataframe

# Update Table
df.iloc[0,0] = 'New Value'
tables.update(TableKey='Material Properties 01 - General', data=df, apply=True)
```

## 2.4. Loads
Expand Down Expand Up @@ -247,4 +254,4 @@ rebar.delete(name='R1') #Delete existing rebar property
rebar.list_all() #List all defined rebar Properties
rebar.set_prop(name='MyRebar2', area=1.05, dia=1.0) #Define a rebar property
rebar.get_prop(name='MyRebar2') #Get rebar property
```
```
12 changes: 7 additions & 5 deletions documentation/Usage.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -158,14 +158,16 @@
"metadata": {},
"outputs": [],
"source": [
"sap.Table.list_available() #Lists available database tables\n",
"sap.Table.list_all() #Lists all database tables\n",
"sap.Table.get_table_fields('Analysis Options') #Get table Field Info\n",
"df = sap.Table.data('Material Properties 01 - General') #Get Table data\n",
"tables = sap.Table\n",
"tables.list_available() #Lists available database tables\n",
"tables.list_all() #Lists all database tables\n",
"tables.get_table_fields('Analysis Options') #Get table Field Info\n",
"tables.get(TableKey='Load Case Definitions', dataframe=False) #Get Table data in `list[dict]` format\n",
"df = tables.get('Material Properties 01 - General') #Get Table data in pandas dataframe\n",
"\n",
"# Update Table\n",
"df.iloc[0,0] = 'New Value'\n",
"sap.Table.update(TableKey='Material Properties 01 - General', data=df, apply=True)"
"tables.update(TableKey='Material Properties 01 - General', data=df, apply=True)"
]
},
{
Expand Down
62 changes: 45 additions & 17 deletions src/ak_sap/Database/tables.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
import pandas as pd

import typing
from typing import Any

from ak_sap.utils import log
from .table_structured_data import DatabaseTable, FieldData
from .table_constants import ImportType_Literals

class Table:
def __init__(self, mySapObject) -> None:
def __init__(self, mySapObject, Model) -> None:
self.mySapObject = mySapObject
self.SapModel = self.mySapObject.SapModel
self.Model = Model
self.DatabaseTables = self.SapModel.DatabaseTables
log.debug('Instance of `Table` module initialized.')

Expand Down Expand Up @@ -73,21 +75,33 @@ def get_table_fields(self, TableKey: str) -> list[FieldData]:
log.critical(str(e) + f'Return data: {_table_data}')
return tables

def data(self, TableKey: str) -> pd.DataFrame:
def get(self, TableKey: str, dataframe: bool=True) -> pd.DataFrame|list[dict[str, Any]]:
"""Extract Table Data.
See `.list_available()` for `TableKey`s.
See `.get_table_fields()` for info on fields."""
log.debug(f'Extracting data for TableKey: {TableKey}')
_data: list = []
_current_units = self.Model.units
self.Model.set_units(value="N_m_C")
try:
_data = self.DatabaseTables.GetTableForDisplayArray(TableKey,'','')
assert _data[-1] == 0
df = _array_to_pandas(headers=_data[2], array=_data[4])
log.debug(f'Info of retrieved dataframe: \n{df.info(verbose=True)}')
return df
if dataframe:
df = self.__array_to_pandas(headers=_data[2], array=_data[4])
log.debug(f'Info of retrieved dataframe: \n{df.info(verbose=True)}')
self.Model.set_units(value=_current_units)
return df
else:
data = self.__array_to_list_of_dicts(headers=_data[2], array=_data[4])
self.Model.set_units(value=_current_units)
return data
except Exception as e:
self.Model.set_units(value=_current_units)
log.critical(str(e) + f'\ndata: {_data}')
return pd.DataFrame()
if dataframe:
return pd.DataFrame()
else:
return []

def update(self, TableKey: str, data: pd.DataFrame, apply: bool=True):
"""Update the database table value"""
Expand Down Expand Up @@ -131,17 +145,31 @@ def discard(self):
except Exception as e:
log.critical(str(e))

def _array_to_pandas(headers: tuple, array: tuple) -> pd.DataFrame:
"""Given the table headers as tuple and table data as a single tuple;
Returns table as a dataframe."""
num_fields = len(headers)
assert len(array) % num_fields == 0, f'Array length ({len(array)}) is not divisible by header length ({num_fields})'

df_data:dict[str,list] = {header: [] for header in headers}
for array_idx, value in enumerate(array):
_header: str = headers[array_idx % num_fields]
df_data[_header].append(value)
return pd.DataFrame(df_data)
def __array_to_pandas(self, headers: tuple, array: tuple) -> pd.DataFrame:
"""Given the table headers as tuple and table data as a single tuple;
Returns table as a dataframe."""
num_fields = len(headers)
assert len(array) % num_fields == 0, f'Array length ({len(array)}) is not divisible by header length ({num_fields})'

df_data:dict[str,list] = {header: [] for header in headers}
for array_idx, value in enumerate(array):
_header: str = headers[array_idx % num_fields]
df_data[_header].append(value)
return pd.DataFrame(df_data)

def __array_to_list_of_dicts(self, headers: tuple, array: tuple) -> list[dict[str, Any]]:
"""Given the table headers as tuple and table data as a single tuple;
Returns table as a list of dictionaries."""
num_fields = len(headers)
assert len(array) % num_fields == 0, f'Array length ({len(array)}) is not divisible by header length ({num_fields})'

list_of_dicts = []
for i in range(len(array) // num_fields):
row_dict = {headers[j]: array[i * num_fields + j] for j in range(num_fields)}
list_of_dicts.append(row_dict)

return list_of_dicts


def flatten_dataframe(df: pd.DataFrame) -> tuple:
"""Convert values of a dataframe to single-dimension array for SapOAPI"""
Expand Down
2 changes: 1 addition & 1 deletion src/ak_sap/wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def __init__(self, attach_to_exist: bool = True, program_path: str|Path|None = N
#Attach submodules and functions
self.Model = Model(mySapObject=self.mySapObject)
self.Element = Element(mySapObject=self.mySapObject)
self.Table = Table(mySapObject=self.mySapObject)
self.Table = Table(mySapObject=self.mySapObject, Model=self.Model)
self.Load = Load(mySapObject=self.mySapObject)
self.Results = Results(mySapObject=self.mySapObject)
self.Material = Material(mySapObject=self.mySapObject)
Expand Down

0 comments on commit daf211d

Please sign in to comment.