Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update output file location #37

Merged
merged 3 commits into from
Nov 27, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ classifiers = [
"Topic :: Scientific/Engineering",
]
keywords = ["eco routing"]
dependencies = []
dependencies = ["toml"]
[project.optional-dependencies]
dev = [
"black",
Expand Down
39 changes: 37 additions & 2 deletions python/nrel/routee/compass/compass_app.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,24 @@
from __future__ import annotations

import json
import logging

from pathlib import Path
from typing import Any, Dict, List, Optional, Union
from nrel.routee.compass.routee_compass_py import (
CompassAppWrapper,
)

import toml


Query = Dict[str, Any]
Result = List[Dict[str, Any]]


log = logging.getLogger(__name__)


class CompassApp:
"""
The CompassApp holds everything needed to run a route query.
Expand All @@ -23,12 +30,15 @@ def __init__(self, app: CompassAppWrapper):
self._app = app

@classmethod
def from_config_file(cls, config_file: Union[str, Path]) -> CompassApp:
def from_config_file(
cls, config_file: Union[str, Path], output_file: Optional[str] = None
) -> CompassApp:
"""
Build a CompassApp from a config file

Args:
config_file (Union[str, Path]): Path to the config file
output_file (Optional[str]): Path to the output file. This overrides whatever is in the config file

Returns:
CompassApp: A CompassApp object
Expand All @@ -40,7 +50,32 @@ def from_config_file(cls, config_file: Union[str, Path]) -> CompassApp:
config_path = Path(config_file)
if not config_path.is_file():
raise ValueError(f"Config file {str(config_path)} does not exist")
app = CompassAppWrapper._from_config_file(str(config_path.absolute()))
with open(config_path) as f:
toml_config = toml.load(f)

# inject the output file override into the to_disk plugin config
if output_file is not None:
override = False
plugins = toml_config.get("plugin")
if plugins is not None:
output_plugins = plugins.get("output_plugins")
if output_plugins is not None:
for plugin in output_plugins:
if plugin.get("type") == "to_disk":
override = True
plugin["output_file"] = output_file
if not override:
log.warning(
f"Output file override {output_file} was provided but "
"to_disk plugin was not found in config file"
)

toml_string = toml.dumps(toml_config)
config_path_string = str(config_path.absolute())

app = CompassAppWrapper._from_config_toml_string(
toml_string, config_path_string
)
return cls(app)

def run(self, query: Union[Query, List[Query]]) -> Result:
Expand Down
1 change: 1 addition & 0 deletions rust/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,4 @@ csv = { version = "1.2.2" }
itertools = { version = "0.11.0" }
chrono = "0.4.26"
regex = "1.9.5"
config = "0.13.3"
1 change: 1 addition & 0 deletions rust/routee-compass-py/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ documentation = "https://docs.rs/routee-compass"
routee-compass = { path = "../routee-compass", version = "0.3.0" }
pyo3 = { version = "0.19", features = ["extension-module"] }
serde_json = { workspace = true }
config = {workspace = true }

[lib]
name = "routee_compass_py"
Expand Down
32 changes: 31 additions & 1 deletion rust/routee-compass-py/src/app_wrapper.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
use std::path::Path;

use pyo3::{exceptions::PyException, prelude::*, types::PyType};
use routee_compass::app::compass::compass_app::CompassApp;
use routee_compass::app::compass::{
compass_app::CompassApp, compass_app_ops::read_config_from_string,
config::compass_app_builder::CompassAppBuilder,
};

#[pyclass]
pub struct CompassAppWrapper {
Expand All @@ -10,6 +13,33 @@ pub struct CompassAppWrapper {

#[pymethods]
impl CompassAppWrapper {
#[classmethod]
pub fn _from_config_toml_string(
_cls: &PyType,
config_string: String,
original_file_path: String,
) -> PyResult<Self> {
let config = read_config_from_string(
config_string.clone(),
config::FileFormat::Toml,
original_file_path,
)
.map_err(|e| {
PyException::new_err(format!(
"Could not create CompassApp from config string: {}",
e
))
})?;
let builder = CompassAppBuilder::default();
let routee_compass = CompassApp::try_from((&config, &builder)).map_err(|e| {
PyException::new_err(format!(
"Could not create CompassApp from config string {}: {}",
config_string.clone(),
e
))
})?;
Ok(CompassAppWrapper { routee_compass })
}
#[classmethod]
pub fn _from_config_file(_cls: &PyType, config_file: String) -> PyResult<Self> {
let config_path = Path::new(&config_file);
Expand Down
2 changes: 1 addition & 1 deletion rust/routee-compass/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,6 @@ rayon = { workspace = true }
serde_repr = "0.1"
rand = "0.8.5"
chrono = { workspace = true }
config = "0.13.3"
config = { workspace = true }
clap = { version = "4.3.19", features = ["derive"] }
itertools = { workspace = true }
2 changes: 1 addition & 1 deletion rust/routee-compass/src/app/compass/compass_app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ impl TryFrom<&Path> for CompassApp {
///
/// * an instance of [`CompassApp`], or an error if load failed.
fn try_from(conf_file: &Path) -> Result<Self, Self::Error> {
let config = ops::read_config(conf_file)?;
let config = ops::read_config_from_file(conf_file)?;
let builder = CompassAppBuilder::default();
let compass_app = CompassApp::try_from((&config, &builder))?;
Ok(compass_app)
Expand Down
44 changes: 41 additions & 3 deletions rust/routee-compass/src/app/compass/compass_app_ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@ use std::path::Path;
/// # Returns
///
/// A config object read from file, or an error
pub fn read_config(config: &Path) -> Result<Config, CompassAppError> {
pub fn read_config_from_file(config_path: &Path) -> Result<Config, CompassAppError> {
let default_config = config::File::from_str(
include_str!("config.default.toml"),
config::FileFormat::Toml,
);

// We want to store the location of where the config file
// was found so we can use it later to resolve relative paths
let conf_file_string = config
let conf_file_string = config_path
.to_str()
.ok_or(CompassAppError::InternalError(
"Could not parse incoming config file path".to_string(),
Expand All @@ -29,7 +29,7 @@ pub fn read_config(config: &Path) -> Result<Config, CompassAppError> {

let config = Config::builder()
.add_source(default_config)
.add_source(config::File::from(config))
.add_source(config::File::from(config_path))
.set_override(
CompassInputField::ConfigInputFile.to_string(),
conf_file_string,
Expand All @@ -39,3 +39,41 @@ pub fn read_config(config: &Path) -> Result<Config, CompassAppError> {

Ok(config)
}

/// Reads a configuration file from a deserializable string in the specified format.
/// This also requires the file path of where the string was loaded from since we use that
/// to normalize paths later.
///
/// # Arguments
///
/// * `config_as_string` - the configuration file as a string
/// * `format` - the format of the string
/// * `original_file_path` - the path to the file that was loaded
///
/// # Returns
///
/// A config object read from file, or an error
pub fn read_config_from_string(
config_as_string: String,
format: config::FileFormat,
original_file_path: String,
) -> Result<Config, CompassAppError> {
let default_config = config::File::from_str(
include_str!("config.default.toml"),
config::FileFormat::Toml,
);

let user_config = config::File::from_str(&config_as_string, format);

let config = Config::builder()
.add_source(default_config)
.add_source(user_config)
.set_override(
CompassInputField::ConfigInputFile.to_string(),
original_file_path,
)?
.build()
.map_err(CompassAppError::ConfigError)?;

Ok(config)
}