From 56f08652f8177befa40d7dd4f8c7d052b4e34f58 Mon Sep 17 00:00:00 2001 From: Sermet Pekin Date: Wed, 6 Nov 2024 16:41:40 +0300 Subject: [PATCH] Makefile --- .gitignore | 2 + Makefile | 47 +++++++++++ README.md | 5 ++ example.txt | 2 + include/e_utils.h | 182 +++++++++++++++++++++++++++++++++++++++++++ include/get.h | 24 +++--- include/get_series.h | 28 +++---- include/shorten.h | 21 +++-- include/types.h | 13 ++-- src/main.cpp | 165 +++++---------------------------------- 10 files changed, 295 insertions(+), 194 deletions(-) create mode 100644 Makefile create mode 100644 example.txt create mode 100644 include/e_utils.h diff --git a/.gitignore b/.gitignore index 9257837..a2cf517 100644 --- a/.gitignore +++ b/.gitignore @@ -17,4 +17,6 @@ build/ evdscpp evdscpp.dSYM +*.txt +!example.txt diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..8d61444 --- /dev/null +++ b/Makefile @@ -0,0 +1,47 @@ +# Makefile for evdscpp +BUILD_DIR := build + +# Compiler options +CXX := g++ +CXXFLAGS := -std=c++20 + +# Default target +.PHONY: all +all: configure build + +# Configure the project using CMake +.PHONY: configure +configure: + mkdir -p $(BUILD_DIR) + cd $(BUILD_DIR) && cmake .. + +# Build the project +.PHONY: build +build: configure + cd $(BUILD_DIR) && make + +# Run the executable +.PHONY: run +run: build + ./$(BUILD_DIR)/bin/evdscpp + ./$(BUILD_DIR)/bin/evdscpp ./example.txt --cache true + + +# Run tests +.PHONY: test +test: build + cd $(BUILD_DIR) && ctest --output-on-failure + +# Clean the build directory +.PHONY: clean +clean: + rm -rf $(BUILD_DIR) + +# Valgrind memory check (requires Valgrind) +.PHONY: valgrind +valgrind: build + if command -v valgrind >/dev/null 2>&1; then \ + valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes --verbose ./$(BUILD_DIR)/bin/evdscpp; \ + else \ + echo "Valgrind not found. Skipping memory check."; \ + fi diff --git a/README.md b/README.md index 2e53578..6f7c7c6 100644 --- a/README.md +++ b/README.md @@ -89,6 +89,11 @@ You can obtain a free EVDS API key by registering on the EVDS website provided b # this will create two different files. One for the first index `bie_yssk` and one for these indexes TP.DK.USD.A-TP.DK.EUR.A ./evdscpp bie_yssk,TP.DK.USD.A-TP.DK.EUR.A --cache true +# indexes can be read from a text file as well +./evdscpp example.txt --cache true + + + ``` diff --git a/example.txt b/example.txt new file mode 100644 index 0000000..79278f3 --- /dev/null +++ b/example.txt @@ -0,0 +1,2 @@ +bie_yssk +TP.DK.USD.A-TP.DK.EUR.A diff --git a/include/e_utils.h b/include/e_utils.h new file mode 100644 index 0000000..fa9fb5e --- /dev/null +++ b/include/e_utils.h @@ -0,0 +1,182 @@ +#include +#include +#include +#include +#include +#include + +#include + +struct ParseArgsOptions +{ + int argc; + char **argv; + std::vector &indexes; +}; + +std::vector indexes_from_file(const std::string &file_path); + + +template +bool looks_like_filename(const T &name, const std::string& extension = ".txt") { + static_assert(std::is_same_v || std::is_same_v, + "T must be either std::string or std::string_view"); + return name.find(extension) != std::string::npos; +} + + +std::unordered_map parseArgs(ParseArgsOptions &options) +{ + std::unordered_map args; + + bool namedArgsFound = false; + + for (int i = 1; i < options.argc; ++i) + { + std::string arg = options.argv[i]; + + if (arg.rfind("--", 0) == 0) + { + namedArgsFound = true; + std::string key = arg.substr(2); + if (i + 1 < options.argc) + { + args[key] = options.argv[++i]; + } + else + { + args[key] = "true"; + } + } + else if (!namedArgsFound) + { + + std::istringstream iss(arg); + std::string index; + while (std::getline(iss, index, ',')) + { + if (looks_like_filename(index)) + options.indexes = indexes_from_file(index); + else + options.indexes.push_back(index); + } + } + } + + return args; +} + + + + +std::vector indexes_from_file(const std::string &file_path) +{ + std::vector indexes; + std::ifstream file(file_path); + + if (!file.is_open()) + { + std::cerr << "Error: Could not open file " << file_path << std::endl; + return indexes; + } + + std::string line; + + while (std::getline(file, line)) + { + indexes.push_back(line); + } + + file.close(); + return indexes; +} + + +void setConfigOptions(const std::unordered_map &args, Config &config) +{ + std::unordered_map> configSetters = { + {"cache", [&](const std::string &val) + { config.cache = (val == "true"); }}, + {"test", [&](const std::string &val) + { config.test = (val == "true"); }}, + + // auto_confirm + {"confirm", [&](const std::string &val) + { config.auto_confirm = (val == "true"); }}, + + {"auto_confirm", [&](const std::string &val) + { config.auto_confirm = (val == "true"); }}, + + {"verbose", [&](const std::string &val) + { config.verbose = (val == "true"); }}, + + {"start_date", [&](const std::string &val) + { config.start_date = val; }}, + {"end_date", [&](const std::string &val) + { config.end_date = val; }}, + {"frequency", [&](const std::string &val) + { config.frequency = val; }}, + {"formulas", [&](const std::string &val) + { config.formulas = val; }}, + {"aggregation", [&](const std::string &val) + { config.aggregation = val; }}}; + + for (const auto &arg : args) + { + if (configSetters.count(arg.first)) + { + configSetters[arg.first](arg.second); + } + } +} + + + +void show_usage() +{ + std::cout << "Usage:\n"; + std::cout << " evdscpp [options]\n\n"; + std::cout << "Description:\n"; + std::cout << " evdscpp is a command-line tool for requesting data from the EVDS API.\n\n"; + + std::cout << "Arguments:\n"; + std::cout << " Comma-separated list of indexes to request.\n"; + std::cout << " Example: TP.DK.USD.A,TP.DK.EUR.A\n"; + std::cout << " Use hyphen (-) to group indexes in a single file.\n\n"; + + std::cout << "Options:\n"; + std::cout << " --start_date Specify the start date (format: DD-MM-YYYY).\n"; + std::cout << " Example: --start_date 01-01-2021\n"; + std::cout << " --end_date Specify the end date (format: DD-MM-YYYY).\n"; + std::cout << " Example: --end_date 31-12-2021\n"; + std::cout << " --cache Enable or disable caching.\n"; + std::cout << " Example: --cache true\n"; + std::cout << " --frequency Set the frequency (e.g., daily, monthly, annual).\n"; + std::cout << " Example: --frequency monthly\n"; + std::cout << " --formulas Set the formulas (e.g., avg, sum).\n"; + std::cout << " Example: --formulas avg\n"; + std::cout << " --aggregation Set the aggregation type.\n"; + std::cout << " Example: --aggregation avg\n\n"; + + std::cout << "Examples:\n"; + std::cout << " # 1. Each index will have its own file:\n"; + std::cout << " ./evdscpp TP.DK.USD.A,TP.DK.EUR.A\n\n"; + + std::cout << " # 2. Two indexes in the same file:\n"; + std::cout << " ./evdscpp TP.DK.USD.A-TP.DK.EUR.A\n\n"; + + std::cout << " # 3. Using indexes with named arguments:\n"; + std::cout << " ./evdscpp TP.DK.USD.A,TP.DK.EUR.A --start_date 01-01-2021 --end_date 31-12-2021 --cache true\n\n"; + + std::cout << " # Another example with a different index:\n"; + std::cout << " ./evdscpp bie_yssk --cache true\n\n"; + + std::cout << " # Another example with three different indexes:\n"; + std::cout << " # This will create two different files. One for `bie_yssk` and one for `TP.DK.USD.A-TP.DK.EUR.A`\n"; + std::cout << " ./evdscpp bie_yssk,TP.DK.USD.A-TP.DK.EUR.A --cache true\n\n"; + + std::cout << "Notes:\n"; + std::cout << " - The argument is required.\n"; + std::cout << " - Options are optional but provide more control over the query.\n"; + std::cout << " - examples and code on https://github.com/SermetPekin/evdscpp\n"; +} diff --git a/include/get.h b/include/get.h index 995f4fb..fc42abf 100644 --- a/include/get.h +++ b/include/get.h @@ -1,21 +1,21 @@ /* * evdscpp: An open-source data wrapper for accessing the EVDS API. * Author: Sermet Pekin - * + * * MIT License - * + * * Copyright (c) 2024 Sermet Pekin - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -25,7 +25,6 @@ * SOFTWARE. */ - #include "dotenv_.h" #include @@ -119,7 +118,7 @@ std::enable_if_t::value, const char *> get_proxy_for_url(T url, T return ""; } -std::string get_request_real(const GetParams ¶ms, bool test); +std::string get_request_real(const GetParams ¶ms, bool test, bool auto_confirm); std::string get_request(const GetParams ¶ms, const Config &config) { @@ -143,16 +142,19 @@ std::string get_request(const GetParams ¶ms, const Config &config) return cached_result; } - auto res = get_request_real(params, config.test); + if (!config.auto_confirm) + throw std::runtime_error("[2]Something is wrong with auto confirm"); + + auto res = get_request_real(params, config.test, config.auto_confirm); if (cache_option) cache.save_cache(fnc_name, res, params_str); return res; } -std::string get_request_real(const GetParams ¶ms, bool test) +std::string get_request_real(const GetParams ¶ms, bool test, bool auto_confirm) { - // if (config.test) + - if (!test & !evds::confirm("Request?", params.url, params.verbose)) + if (!auto_confirm && !test && !evds::confirm("Request?", params.url, params.verbose)) { std::cout << "Not requesting ..."; throw std::runtime_error("Request was cancelled "); diff --git a/include/get_series.h b/include/get_series.h index 640a562..23455ea 100644 --- a/include/get_series.h +++ b/include/get_series.h @@ -1,21 +1,21 @@ /* * evdscpp: An open-source data wrapper for accessing the EVDS API. * Author: Sermet Pekin - * + * * MIT License - * + * * Copyright (c) 2024 Sermet Pekin - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -25,7 +25,6 @@ * SOFTWARE. */ - #include // for sprintf #include "header.h" #include "json.h" @@ -35,13 +34,12 @@ #include "get.h" #include "shorten.h" - using namespace evds; static inline bool TEST = false; #include - + template struct deduce_series_type; @@ -57,17 +55,16 @@ struct deduce_series_type using type = std::optional; }; - template <> struct deduce_series_type { using type = std::optional; }; -DataFrame get_series( std::string &str, const Config &config = Config(), bool verbose = false) +DataFrame get_series(std::string &str, const Config &config = Config(), bool verbose = false) { - // str = normalizeDelimiters(str ); + // str = normalizeDelimiters(str ); evds::Index index(str); std::cout << "index1 : " << index.get() << "\n"; @@ -84,9 +81,9 @@ DataFrame get_series( std::string &str, const Config &config = Config(), bool v std::string res; + res = getEvds(url, config); - json parsed_json = json::parse(res); for (const auto &item : parsed_json["items"]) // TODO possible break for future changes @@ -98,19 +95,14 @@ DataFrame get_series( std::string &str, const Config &config = Config(), bool v return df; } - std::vector check_df(DataFrame &df, const std::string &col) { Series series = df[col]; - - - char buffer[256]; + char buffer[256]; snprintf(buffer, sizeof(buffer), "[produced series] %s", col.c_str()); std::cout << buffer << std::endl; auto vals = series.values(); return vals; } - - diff --git a/include/shorten.h b/include/shorten.h index 240adc7..6c77c7a 100644 --- a/include/shorten.h +++ b/include/shorten.h @@ -1,21 +1,21 @@ /* * evdscpp: An open-source data wrapper for accessing the EVDS API. * Author: Sermet Pekin - * + * * MIT License - * + * * Copyright (c) 2024 Sermet Pekin - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -25,7 +25,6 @@ * SOFTWARE. */ - #ifndef getShortFilenames_ #define getShortFilenames_ @@ -75,7 +74,6 @@ std::string normalizeDelimiters(const std::string &input) return normalized; } - std::string capitalizeWords(const std::string &input) { std::string result; @@ -84,7 +82,7 @@ std::string capitalizeWords(const std::string &input) for (char c : input) { if (std::isalnum(c)) - { + { if (capitalizeNext) { result += std::toupper(c); @@ -97,17 +95,16 @@ std::string capitalizeWords(const std::string &input) } else { - capitalizeNext = true; + capitalizeNext = true; } } return result; } - std::string getShortFilename(const std::string &input) { - if (input.size() < 9) + if (input.size() < 9 || input.find("bie_") != std::string::npos) { return input; @@ -123,7 +120,7 @@ std::string getShortFilename(const std::string &input) } else if (c == '-' || c == '_' || c == '.') { - cleaned += ' '; + cleaned += ' '; } } diff --git a/include/types.h b/include/types.h index 584cbbf..e9900d4 100644 --- a/include/types.h +++ b/include/types.h @@ -1,21 +1,21 @@ /* * evdscpp: An open-source data wrapper for accessing the EVDS API. * Author: Sermet Pekin - * + * * MIT License - * + * * Copyright (c) 2024 Sermet Pekin - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -25,7 +25,6 @@ * SOFTWARE. */ - #include #include #include @@ -117,6 +116,8 @@ namespace evds std::string formulas = "default"; // | level | percentage_change | difference | year_to_year_percent_change | year_to_year_differences | std::string aggregation = "default"; // | avg |min | max | first | last | sum bool cache = true; + + bool auto_confirm = true; }; } \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 2047cf3..74ddfc7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,21 +1,21 @@ /* * evdscpp: An open-source data wrapper for accessing the EVDS API. * Author: Sermet Pekin - * + * * MIT License - * + * * Copyright (c) 2024 Sermet Pekin - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -25,13 +25,16 @@ * SOFTWARE. */ - #include "get_series.h" #include "dotenv_.h" #include "shorten.h" -#include +#include "e_utils.h" + +#include +#include #include +#include #include #include #include @@ -39,141 +42,12 @@ #include #include #include - -#include "../extern/nlohmann/json.hpp" - -struct ParseArgsOptions -{ - int argc; - char **argv; - std::vector &indexes; -}; - -std::unordered_map parseArgs(ParseArgsOptions &options) -{ - std::unordered_map args; - - bool namedArgsFound = false; - - for (int i = 1; i < options.argc; ++i) - { - std::string arg = options.argv[i]; - - if (arg.rfind("--", 0) == 0) - { - namedArgsFound = true; - std::string key = arg.substr(2); - if (i + 1 < options.argc) - { - args[key] = options.argv[++i]; - } - else - { - args[key] = "true"; - } - } - else if (!namedArgsFound) - { - std::istringstream iss(arg); - std::string index; - while (std::getline(iss, index, ',')) - { - options.indexes.push_back(index); - } - } - } - - return args; -} - -#include -#include #include - -void setConfigOptions(const std::unordered_map &args, Config &config) -{ - std::unordered_map> configSetters = { - {"cache", [&](const std::string &val) - { config.cache = (val == "true"); }}, - {"test", [&](const std::string &val) - { config.test = (val == "true"); }}, - {"verbose", [&](const std::string &val) - { config.verbose = (val == "true"); }}, - - {"start_date", [&](const std::string &val) - { config.start_date = val; }}, - {"end_date", [&](const std::string &val) - { config.end_date = val; }}, - {"frequency", [&](const std::string &val) - { config.frequency = val; }}, - {"formulas", [&](const std::string &val) - { config.formulas = val; }}, - {"aggregation", [&](const std::string &val) - { config.aggregation = val; }}}; - - for (const auto &arg : args) - { - if (configSetters.count(arg.first)) - { - configSetters[arg.first](arg.second); - } - } -} - -#include - -void show_usage() -{ - std::cout << "Usage:\n"; - std::cout << " evdscpp [options]\n\n"; - std::cout << "Description:\n"; - std::cout << " evdscpp is a command-line tool for requesting data from the EVDS API.\n\n"; - - std::cout << "Arguments:\n"; - std::cout << " Comma-separated list of indexes to request.\n"; - std::cout << " Example: TP.DK.USD.A,TP.DK.EUR.A\n"; - std::cout << " Use hyphen (-) to group indexes in a single file.\n\n"; - - std::cout << "Options:\n"; - std::cout << " --start_date Specify the start date (format: DD-MM-YYYY).\n"; - std::cout << " Example: --start_date 01-01-2021\n"; - std::cout << " --end_date Specify the end date (format: DD-MM-YYYY).\n"; - std::cout << " Example: --end_date 31-12-2021\n"; - std::cout << " --cache Enable or disable caching.\n"; - std::cout << " Example: --cache true\n"; - std::cout << " --frequency Set the frequency (e.g., daily, monthly, annual).\n"; - std::cout << " Example: --frequency monthly\n"; - std::cout << " --formulas Set the formulas (e.g., avg, sum).\n"; - std::cout << " Example: --formulas avg\n"; - std::cout << " --aggregation Set the aggregation type.\n"; - std::cout << " Example: --aggregation avg\n\n"; - - std::cout << "Examples:\n"; - std::cout << " # 1. Each index will have its own file:\n"; - std::cout << " ./evdscpp TP.DK.USD.A,TP.DK.EUR.A\n\n"; - - std::cout << " # 2. Two indexes in the same file:\n"; - std::cout << " ./evdscpp TP.DK.USD.A-TP.DK.EUR.A\n\n"; - - std::cout << " # 3. Using indexes with named arguments:\n"; - std::cout << " ./evdscpp TP.DK.USD.A,TP.DK.EUR.A --start_date 01-01-2021 --end_date 31-12-2021 --cache true\n\n"; - - std::cout << " # Another example with a different index:\n"; - std::cout << " ./evdscpp bie_yssk --cache true\n\n"; - - std::cout << " # Another example with three different indexes:\n"; - std::cout << " # This will create two different files. One for `bie_yssk` and one for `TP.DK.USD.A-TP.DK.EUR.A`\n"; - std::cout << " ./evdscpp bie_yssk,TP.DK.USD.A-TP.DK.EUR.A --cache true\n\n"; - - std::cout << "Notes:\n"; - std::cout << " - The argument is required.\n"; - std::cout << " - Options are optional but provide more control over the query.\n"; - std::cout << " - examples and code on https://github.com/SermetPekin/evdscpp\n"; -} +#include "../extern/nlohmann/json.hpp" int main(int argc, char *argv[]) { - + evds::Config config; config.cache = false; config.start_date = "01-01-2020"; @@ -183,15 +57,13 @@ int main(int argc, char *argv[]) config.frequency = "default"; // | monthly | weekly | annually | semimonthly | semiannually | business config.formulas = "default"; // | level | percentage_change | difference | year_to_year_percent_change | year_to_year_differences | config.aggregation = "default"; // | avg |min | max | first | last | sum - + config.auto_confirm = true; // if true it will not ask for confirmation before requesting which is convenient when many indexes are given. + std::vector indexes; ParseArgsOptions poptions{argc, argv, indexes}; // Pass indexes by reference auto args = parseArgs(poptions); config.indexes = poptions.indexes; - - - setConfigOptions(args, config); @@ -206,21 +78,20 @@ int main(int argc, char *argv[]) DataFrame df_current; - for (auto &x : poptions.indexes) + for (auto &CurrentIndex : poptions.indexes) { try { - auto df = get_series(x, config); + auto df = get_series(CurrentIndex, config); df_current = df; - std::string f_name = getShortFilename(x); + std::string f_name = getShortFilename(CurrentIndex); df.to_csv("data_" + f_name + ".csv", ','); } catch (const std::exception &ex) { - std::cerr << "passing: " << ex.what() << std::endl; - + std::cerr << "passing: " << CurrentIndex << ex.what() << std::endl; } }