Skip to content
This repository was archived by the owner on Apr 4, 2025. It is now read-only.

Commit f2c9736

Browse files
committed
Initial commit of minimum viable product (can generate a run and pull output tables as CSV/Excel format) for Python implementation.
1 parent fe2c0b7 commit f2c9736

11 files changed

+917
-0
lines changed

.gitignore

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
build/
2+
dist/
3+
.vs/
4+
/PohemX-python.spec
5+
Model_Outputs/
6+
Functions/__pycache__

Functions/Create.py

+86
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
#To-do: Finished logic but adjust code to work as a module
2+
"""
3+
Author: Scott Yun Ho
4+
Date: Oct. 29, 2024
5+
Description: Functions that create something new on the server-side.
6+
"""
7+
8+
import requests
9+
import pandas as pd
10+
import time
11+
12+
13+
"""
14+
Description: Create and run a given model on OpenM++ engine. Send a request to run model, then once finished, return model_run_status.
15+
Parameters:
16+
Return:
17+
"""
18+
19+
def create_model_run(model_digest, oms_url, simulation_cases=1e5, tables=[], threads=1, sub_samples=1) :
20+
21+
# Coerce numeric parameters to strings
22+
simulation_cases = str(int(simulation_cases))
23+
threads = str(int(threads))
24+
sub_samples = str(int(sub_samples))
25+
26+
# Set JSON request
27+
json_request = {
28+
'ModelName': model_digest,
29+
'Opts': {
30+
'Parameter.SimulationCases': simulation_cases,
31+
'OpenM.Threads': threads,
32+
'OpenM.SubValues': sub_samples,
33+
'OpenM.LogToConsole': 'true',
34+
'OpenM.ProgressPercent': '100'
35+
},
36+
'Tables': tables
37+
}
38+
39+
# Send request to oms
40+
print("Sending API request to web service...")
41+
x = requests.post(oms_url + '/api/run', json = json_request)
42+
43+
# Set model run status
44+
model_run_status = ''
45+
46+
startTime = time.time() #have start time, then have currentTime in model progress to repeatedly check how much time has elapsed.
47+
# Monitor model run status
48+
while model_run_status in '' 'i' 'p' 'w' :
49+
50+
# Pause for one second to give time for information to be delivered.
51+
time.sleep(1)
52+
53+
# Get current model run status
54+
model_run_status = requests.get(oms_url + '/api/model/' + model_digest + '/run/status/last').json()['Status']
55+
currentTime = time.time()
56+
57+
# Render conditional message to console
58+
if model_run_status in '' 'i' :
59+
print("Waiting for model run to start. ", end="")
60+
print( "Current time elapsed:", round((currentTime - startTime), 2), "seconds so far... ")
61+
continue
62+
63+
elif model_run_status == 's' :
64+
print("Model run completed successfully! ", end="")
65+
print("Final time elapsed:", round((currentTime - startTime), 2), "seconds. \n")
66+
break
67+
68+
elif model_run_status in 'i' 'p' 'w' :
69+
print("Model run in progress. ", end="")
70+
print("Current time elapsed:", round((currentTime - startTime), 2), "seconds so far... ")
71+
continue
72+
73+
elif model_run_status == 'e' :
74+
print("Model run completed, but with errors. :(")
75+
print("Final time elapsed:", round((currentTime - startTime), 2), "seconds. \n")
76+
break
77+
78+
return model_run_status
79+
80+
"""
81+
Description:
82+
Parameters:
83+
Return:
84+
"""
85+
def create_scenario():
86+
return 1

Functions/Delete.py

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
"""
2+
Author: Scott Yun Ho
3+
Date: Oct. 29, 2024
4+
Description: Functions that delete something existing on the server-side.
5+
"""
6+
7+
8+
"""
9+
Description:
10+
Parameters:
11+
Return:
12+
"""
13+
def delete_model_run():
14+
return 1
15+
16+
"""
17+
Description:
18+
Parameters:
19+
Return:
20+
"""
21+
def delete_scenario():
22+
return 1

Functions/Get.py

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
"""
2+
Author: Scott Yun Ho
3+
Date: Oct. 29, 2024
4+
Description: Functions that get unspecific information from OpenM++.
5+
"""
6+
7+
import requests
8+
import pandas as pd
9+
10+
11+
"""
12+
Description: Provided that the OpenM++ web service (the "oms") is running at oms_url, get list of models from the API route oms_url/api/model-list.
13+
Parameters:
14+
Return:
15+
"""
16+
def get_models(oms_url) :
17+
x = requests.get(oms_url + '/api/model-list').json()
18+
return pd.DataFrame([item['Model'] for item in x])
19+
20+
21+
"""
22+
Description: Provided that the OpenM++ web service (the "oms") is running at oms_url, get full list of model runs from API route at oms_url/api/model/model_digest/run-list.
23+
Parameters:
24+
Return:
25+
"""
26+
def get_model_runs(oms_url, model_digest):
27+
x = requests.get(oms_url + '/api/model/' + model_digest + '/run-list').json()
28+
return pd.DataFrame([item for item in x])
29+
30+
"""
31+
Description:
32+
Parameters:
33+
Return:
34+
"""
35+
def get_scenarios():
36+
return 1

Functions/Load.py

+119
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
"""
2+
Author: Scott Yun Ho
3+
Date: Oct. 29, 2024
4+
Description: Functions that get specific information from OpenM++.
5+
"""
6+
import Functions.Get as Get
7+
8+
import requests
9+
import pandas as pd
10+
import csv
11+
12+
"""
13+
Description: Retrieve specific model run based on model_name and model_digest. Requires model_run_status from Create_model_run.py.
14+
Parameters:
15+
Return:
16+
"""
17+
def load_model_run(model_name, model_digest, model_run_status, oms_url, tables):
18+
19+
# Create model run object. Run has now been completed, so create an object that gathers the finished information in one place.
20+
model_run = {
21+
'model': model_name,
22+
'digest': Get.get_model_runs(oms_url, model_digest).iloc[-1]['RunDigest'],
23+
'status': model_run_status
24+
}
25+
26+
# Retrieve specified output tables after model run.
27+
print(tables)
28+
for table in tables:
29+
print('Retrieving ' + table + ' table...')
30+
model_run[table] = load_output_table(model_digest=model_digest, model_run_digest=model_run['digest'], table=table, oms_url=oms_url)
31+
32+
return model_run
33+
34+
#To-do: finished logic here but test
35+
"""
36+
Description:
37+
Parameters:
38+
Return:
39+
"""
40+
def load_model(model_name):
41+
x = requests.get(oms_url + '/api/model-list').json()
42+
model_list = pd.DataFrame([item['Model'] for item in x])
43+
return model_list.loc[model_list['ModelName'] == model_name]
44+
45+
#To-do: get multiple specific runs. Take array as input and output info for multiple model runs.
46+
"""
47+
Description:
48+
Parameters:
49+
Return:
50+
"""
51+
def load_model_runs():
52+
return 1
53+
54+
"""
55+
Description:
56+
Parameters:
57+
Return:
58+
"""
59+
def load_output_table(model_digest, model_run_digest, table, oms_url):
60+
# Get list from CSV format (data is neater). URL returns CSV file attachment in a response stream, which is why stream is necessary below. Informed by: https://stackoverflow.com/questions/35371043/use-python-requests-to-download-csv#:~:text=CSV_URL%20%3D%20%27http%3A//samplecsvs.s3.amazonaws.com/Sacramentorealestatetransactions.csv%27-,with%20requests,-.Session()%20as%20s%3A%0A%20%20%20%20download%20%3D%20s.get(CSV_URL)%0A%0A%20%20%20%20decoded_content
61+
print("Getting response stream output and processing into a dataframe...")
62+
raw_csv_data = []
63+
with requests.Session() as stream:
64+
download = stream.get(oms_url + '/api/model/' + model_digest + '/run/' + model_run_digest + '/table/' + table + '/expr/csv')
65+
decoded_content = download.content.decode('utf-8')
66+
cr = csv.reader(decoded_content.splitlines(), delimiter=',')
67+
raw_csv_data = list(cr)
68+
69+
# Convert list of lists table output (raw_csv_data) to pandas dataframe.
70+
headers = raw_csv_data.pop(0)
71+
long_format_df = pd.DataFrame(raw_csv_data, columns=headers)
72+
73+
# Clean data to remove negative and positive infinity. regex=True needed to recognize the unicode for infinity: \u221e
74+
#print("Cleaning infinity symbols from data...")
75+
#long_format_df = long_format_df.replace("-"+"\u221e", "MIN", regex=True)
76+
#long_format_df = long_format_df.replace("\u221e", "MAX", regex=True)
77+
78+
# Previous format that returned everything in dims col.
79+
"""
80+
#response = pd.DataFrame(requests.get(oms_url + '/api/model/' + model_digest + '/run/' + model_run_digest + '/table/' + table + '/expr').json())
81+
#print(pd.DataFrame(requests.get(oms_url + '/api/model/' + model_digest + '/run/' + model_run_digest + '/table/' + table + '/expr/csv')))
82+
#response = requests.get(oms_url + '/api/model/' + model_digest + '/run/' + model_run_digest + '/table/' + table + '/expr/csv')
83+
#print(response.headers)
84+
#output_df = pd.DataFrame(response.content)
85+
#print(response)
86+
#output_df = response
87+
88+
#output_df["Dims"] = output_df["Dims"].astype(str) # Program cannot pivot using a list as index, so Dims col must be converted to string first.
89+
#output_df = output_df.pivot(index="Dims", columns="ExprId", values="Value")
90+
#print(output_df)
91+
"""
92+
93+
# Copy headers to use as columns during pivot, but not including expr_name and expr_value since we're pivoting on these values.
94+
wide_format_indices = headers
95+
wide_format_indices.remove("expr_name")
96+
wide_format_indices.remove("expr_value")
97+
98+
# Pivot dataframe to be wide format.
99+
print("Converting data to wide format...\n")
100+
wide_format_df = long_format_df
101+
wide_format_df = pd.pivot(wide_format_df,
102+
index=wide_format_indices,
103+
columns="expr_name",
104+
values="expr_value"
105+
)
106+
107+
return wide_format_df
108+
109+
"""
110+
Description:
111+
Parameters:
112+
Return:
113+
"""
114+
def load_scenario():
115+
return 1
116+
117+
118+
def load_model_digest(df, model_name) :
119+
return df['Digest'][df.loc[df['Name'] == model_name].index.values[0]]

Functions/Utilities.py

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
"""
2+
Author: Scott Yun Ho
3+
Date: Oct. 29, 2024
4+
Description: Helper functions that don't directly perform an API call or don't interact with OpenM++ in general.
5+
"""
6+
7+
import pandas as pd
8+
import os
9+
10+
11+
12+
"""
13+
Description:
14+
Parameters:
15+
Return:
16+
"""
17+
def download_csv(file_name, df_table):
18+
return df_table.to_csv(file_name, index=True)
19+
20+
"""
21+
Description:
22+
Parameters:
23+
Return:
24+
"""
25+
def download_excel(file_name, df_table):
26+
return df_table.to_excel(file_name)
27+
28+
# Create folder within current directory and switch to directory for output download.
29+
"""
30+
Description:
31+
Parameters:
32+
Return:
33+
"""
34+
def create_output_folder(output_folder_name):
35+
current_dir = os.getcwd()
36+
folder_destination = os.path.join(current_dir, output_folder_name)
37+
if not os.path.exists(folder_destination):
38+
os.makedirs(folder_destination)
39+
os.chdir(output_folder_name)
40+
41+
"""
42+
Description:
43+
Parameters:
44+
Return:
45+
"""
46+
def generate_output_files(model_run, tables, output_folder_name):
47+
# Create output folder.
48+
create_output_folder(output_folder_name)
49+
50+
# Get and download output tables specified in tables list.
51+
for table in tables:
52+
output_table = model_run[table]
53+
#print(table)
54+
print("Downloading " + table + " in CSV format.")
55+
download_csv(table + ".csv", output_table)
56+
print("Downloading " + table + " in Excel format.")
57+
download_excel(table + ".xlsx", output_table)
58+
print("\n")

0 commit comments

Comments
 (0)