Skip to content

Commit

Permalink
refactor: update to run ProMCDA from cli
Browse files Browse the repository at this point in the history
kapil-agnihotri committed Sep 24, 2024
1 parent 02a26a5 commit a5f92f9
Showing 6 changed files with 50 additions and 17 deletions.
25 changes: 21 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -194,6 +194,10 @@ effect.


### Requirements

As this project is now a submodule in ranking-service, it must be run from the root directory i.e. outside of `ProMCDA` folder.

This comment has been minimized.

Copy link
@Flaminietta

Flaminietta Oct 1, 2024

Collaborator

This is a submodel of the ranking-service but also still a stand-alone package. Isn't this confusing?

Do we explain somewhere that ProMCDA can be run from CLI as a stand-alone package and also called via an API as a service? And how?

You would need to create a virtual environment outside ProMCDA.

On Windows:
```bash
conda create --name <choose-a-name-like-Promcda> python=3.9
@@ -213,14 +217,27 @@ From the root dir, via command line
- on Windows:
```bash
activate.bat <your-env>
python3 -m mcda.mcda_run -c configuration.json
python3 -m ProMCDA.mcda.mcda_run -c ProMCDA/configuration.json
```
- on Mac and Linux:
```bash
source activate <your-env>
python3 -m mcda.mcda_run -c configuration.json
python3 -m ProMCDA.mcda.mcda_run -c ProMCDA/configuration.json
```
where an example of configuration file can be found in `./configuration.json`.
where an example of configuration file can be found in `ProMCDA/configuration.json`.


### Run ProMCDA for ranking service

Once you have set up virtual environment and installed all the requirements from requirements.txt, you can run the following command to run ProMCDA with sample data

```bash
python3 -m ProMCDA.mcda.mcda_ranking_run -c ProMCDA/input_files/toy_example/car-data-input-sample-from-ranking-service.json
```
where
- ProMCDA/input_files/toy_example/car-data-input-sample-from-ranking-service.json is the config file required to run mcda for ranking service where input matrix is part of the configuration
- ProMCDA.mcda.mcda_ranking_run is the main program that is run


We tested ProMCDA on Windows, Linux and Mac. We identified a possible issue with some Windows machines caused by the
library ```kaleido``` (used to generate static images) and reported [here](https://github.com/plotly/Kaleido/issues/126).
@@ -240,7 +257,7 @@ Change the configuration file for your needs.

### Running the tests
```bash
python3 -m pytest -s tests/unit_tests -vv
python3 -m pytest -s ProMCDA/tests/unit_tests -vv
```
### Toy example
```ProMCDA``` contains a toy example, a simple case to test run the package.
4 changes: 2 additions & 2 deletions configuration.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"input_matrix_path": "toy_example/car_data.csv",
"input_matrix_path": "ProMCDA/input_files/toy_example/car_data.csv",

This comment has been minimized.

Copy link
@Flaminietta

Flaminietta Oct 1, 2024

Collaborator

Please refer to the section Input and output file paths in the README:

All input and output files are saved by default in the input_files and output_files folders located in the project root directory.

For this reason, you don't need ProMCDA/input_files/.

"polarity_for_each_indicator": ["+","+","-","+","+","+"],

"sensitivity": {
@@ -21,5 +21,5 @@
"marginal_distribution_for_each_indicator": ["normal", "normal", "normal", "normal", "normal", "normal"]},


"output_directory_path": "toy_example"
"output_directory_path": "ProMCDA/input_files/toy_example"
}
5 changes: 5 additions & 0 deletions mcda/mcda_ranking_run.py
Original file line number Diff line number Diff line change
@@ -161,6 +161,11 @@ def read_matrix_from_file(column_names_list: list[str], file_from_stream) -> {}:


if __name__ == '__main__':
'''
Entry point to run code with ranking-service input configuration
command to run
> python3 -m ProMCDA.mcda.mcda_ranking_run -c ProMCDA/input_files/toy_example/car-data-input-sample-from-ranking-service.json
'''
t = time.time()
config_path = parse_args()
input_config = get_config(config_path)
8 changes: 5 additions & 3 deletions mcda/mcda_run.py
Original file line number Diff line number Diff line change
@@ -7,7 +7,6 @@
Usage (from root directory):
$ python3 -m mcda.mcda_run -c configuration.json
"""
import json
import time

from ProMCDA.mcda import mcda_ranking_run
@@ -51,7 +50,6 @@ def config_dict_to_configuration_model(input_config):
# Extracting relevant configuration values
config = Config(input_config)
input_matrix = read_matrix(config.input_matrix_path)
# input_matrix = input_matrix.dropna()
robustness = "none"
on_weights_level = "none"
if config.robustness["robustness_on"] == "yes" and config.robustness["on_single_weights"] == "yes":
@@ -62,7 +60,6 @@ def config_dict_to_configuration_model(input_config):
on_weights_level = "all"
elif config.robustness["robustness_on"] == "yes" and config.robustness["on_indicators"] == "yes":
robustness = "indicators"
input_json = input_matrix.to_json()
os.environ['NUM_CORES'] = str(input_config["monte_carlo_sampling"]["num_cores"])
os.environ['RANDOM_SEED'] = str(input_config["monte_carlo_sampling"]["random_seed"])
ranking_input_config = {
@@ -88,6 +85,11 @@ def config_dict_to_configuration_model(input_config):


if __name__ == '__main__':
'''
Entry class if you want to run ProMCDA from cli.
The command to run the code must be run from outside of root directory.
> python3 -m ProMCDA.mcda.mcda_run -c ProMCDA/configuration.json
'''
t = time.time()
config_path = parse_args()
input_config = get_config(config_path)
20 changes: 12 additions & 8 deletions mcda/utils/utils_for_main.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import copy
import io
import os
import argparse
@@ -7,7 +6,6 @@
import random
import logging
import sys
from pprint import pprint
from typing import Union, Any, List, Tuple
from typing import Optional

@@ -24,8 +22,8 @@
from ProMCDA.mcda.models.configuration import Configuration
from ProMCDA.mcda.utils.application_enums import RobustnessAnalysis, RobustnessWightLevels, SensitivityAnalysis

DEFAULT_INPUT_DIRECTORY_PATH = './input_files' # present in the root directory of ProMCDA
DEFAULT_OUTPUT_DIRECTORY_PATH = './output_files' # present in the root directory of ProMCDA
DEFAULT_INPUT_DIRECTORY_PATH = 'ProMCDA/input_files' # present in the root directory of ProMCDA
DEFAULT_OUTPUT_DIRECTORY_PATH = 'ProMCDA/output_files' # present in the root directory of ProMCDA

input_directory_path = os.environ.get('PROMCDA_INPUT_DIRECTORY_PATH') if os.environ.get(
'PROMCDA_INPUT_DIRECTORY_PATH') else DEFAULT_INPUT_DIRECTORY_PATH
@@ -360,7 +358,11 @@ def read_matrix(input_matrix_path: str) -> pd.DataFrame:
:rtype: pd.DataFrame
"""
try:
full_file_path = os.path.join(input_directory_path, input_matrix_path)
full_file_path = None
if os.path.exists(input_matrix_path):
full_file_path = input_matrix_path
else:
full_file_path = os.path.join(input_directory_path, input_matrix_path)
with open(full_file_path, 'r') as fp:
logger.info("Reading the input matrix in {}".format(full_file_path))
matrix = pd.read_csv(fp, sep="[,;:]", decimal='.', engine='python')
@@ -418,7 +420,8 @@ def parse_args():
parser = argparse.ArgumentParser()
parser.add_argument('-c', '--config', help='config path', required=True)
args = parser.parse_args()

print("args")
print(args)
return args.config


@@ -471,7 +474,7 @@ def save_df(df: pd.DataFrame, folder_path: str, filename: str) -> {}:
return

full_output_path = os.path.join(output_directory_path, folder_path, new_filename)

logger.info("Saving results in {}".format(full_output_path))
try:
ensure_directory_exists(os.path.dirname(full_output_path))
except Exception as e:
@@ -516,6 +519,7 @@ def save_dict(dictionary: dict, folder_path: str, filename: str) -> {}:
return

full_output_path = os.path.join(output_directory_path, folder_path, new_filename)
logger.info("Saving results in {}".format(full_output_path))
try:
ensure_directory_exists(os.path.dirname(full_output_path))
except Exception as e:
@@ -558,6 +562,7 @@ def save_config(config: Configuration, folder_path: str, filename: str):
return

full_output_path = os.path.join(output_directory_path, folder_path, new_filename)
logger.info("Saving results in {}".format(full_output_path))
try:
ensure_directory_exists(os.path.dirname(full_output_path))
except Exception as e:
@@ -1194,7 +1199,6 @@ def _save_output_files(scores: Optional[pd.DataFrame],
"""
output_filepath = "toy_example"
# full_output_path = os.path.join(output_directory_path, output_filepath )
logger.info("Saving results in {}".format(output_filepath))
# check_path_exists(output_filepath)
response = {}
if scores is not None and not scores.empty:
5 changes: 5 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -9,5 +9,10 @@ scikit-learn
scipy
Pillow
kaleido
connexion >= 2.6.0
connexion[swagger-ui] >= 2.6.0
python_dateutil == 2.6.0
setuptools >= 21.0.0
swagger-ui-bundle >= 0.0.2


0 comments on commit a5f92f9

Please sign in to comment.