diff --git a/.gitattributes b/.gitattributes index 2da9daf..677981e 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,3 @@ research/** linguist-vendored -src/am4utils/stubs/** linguist-vendored \ No newline at end of file +src/am4utils/stubs/** linguist-vendored +src-v2-v3/** linguist-vendored \ No newline at end of file diff --git a/debug.py b/debug.py index a613dda..1792a05 100644 --- a/debug.py +++ b/debug.py @@ -7,16 +7,19 @@ print(f'py: am4utils ({am4utils._core.__version__}), executable_path={am4utils.__path__[0]}') am4utils.db.init(am4utils.__path__[0]) - print(dir(am4utils._core)) + # print(dir(am4utils._core)) - ap0 = Airport.from_auto('HKG') - ap1 = Airport.from_auto('MTR') - ac = Aircraft.from_auto('b744') - r = Route.from_airports_with_aircraft(ap0, ap1, ac) - cfg = r.aircraft.config.pax_config - print(cfg.y, cfg.j, cfg.f, cfg.algorithm) - print(r.direct_distance) - print(r.ticket.pax_ticket.y) - print(r.ticket.pax_ticket.j) - print(r.ticket.pax_ticket.f) - print(r.income) \ No newline at end of file + ac = Aircraft.search('b744').ac + print(ac) + + # ap0 = Airport.from_auto('HKG') + # ap1 = Airport.from_auto('MTR') + # ac = Aircraft.from_auto('b744') + # r = Route.from_airports_with_aircraft(ap0, ap1, ac) + # cfg = r.aircraft.config.pax_config + # print(cfg.y, cfg.j, cfg.f, cfg.algorithm) + # print(r.direct_distance) + # print(r.ticket.pax_ticket.y) + # print(r.ticket.pax_ticket.j) + # print(r.ticket.pax_ticket.f) + # print(r.income) \ No newline at end of file diff --git a/src/am4utils/CMakeLists.txt b/src/am4utils/CMakeLists.txt index b485632..d7af2de 100644 --- a/src/am4utils/CMakeLists.txt +++ b/src/am4utils/CMakeLists.txt @@ -42,7 +42,7 @@ target_link_libraries(_core PRIVATE duckdb) install(TARGETS _core DESTINATION .) install(FILES $ DESTINATION .) install(DIRECTORY ${CMAKE_SOURCE_DIR}/data DESTINATION .) -install(DIRECTORY ${CMAKE_SOURCE_DIR}/stubs/ DESTINATION .) # / is important! +install(DIRECTORY ${CMAKE_SOURCE_DIR}/stubs/ DESTINATION . OPTIONAL) # / is important! install(DIRECTORY ${CMAKE_SOURCE_DIR}/py/ DESTINATION .) # debug executable diff --git a/src/am4utils/cpp/aircraft.cpp b/src/am4utils/cpp/aircraft.cpp index d5704a1..3663a7b 100644 --- a/src/am4utils/cpp/aircraft.cpp +++ b/src/am4utils/cpp/aircraft.cpp @@ -4,185 +4,130 @@ #include "include/db.hpp" #include "include/aircraft.hpp" -using std::string; - Aircraft::Aircraft() : valid(false) {} -Aircraft Aircraft::from_str(string s) { - Aircraft ac; - Aircraft::SearchType search_type = Aircraft::SearchType::ALL; - +Aircraft::ParseResult Aircraft::parse(const string& s) { string s_lower = s; std::transform(s_lower.begin(), s_lower.end(), s_lower.begin(), ::tolower); - // search airports if (s_lower.substr(0, 5) == "name:") { - search_type = Aircraft::SearchType::NAME; - s = s_lower.substr(5); - ac = Aircraft::from_name(s); + return Aircraft::ParseResult(Aircraft::SearchType::NAME, s_lower.substr(5)); } else if (s_lower.substr(0, 10) == "shortname:") { - search_type = Aircraft::SearchType::SHORTNAME; - s = s_lower.substr(10); - ac = Aircraft::from_shortname(s); - } else if (s_lower.substr(0, 4) == "all:") { - s = s_lower.substr(4); - ac = Aircraft::from_all(s); + return Aircraft::ParseResult(Aircraft::SearchType::SHORTNAME, s_lower.substr(10)); } else if (s_lower.substr(0, 3) == "id:") { - search_type = Aircraft::SearchType::ID; - s = s.substr(3); try { - ac = Aircraft::from_id(std::stoi(s)); + uint16_t id = std::stoi(s.substr(3)); + return Aircraft::ParseResult(Aircraft::SearchType::ID, s.substr(3)); } catch (std::invalid_argument& e) { - } catch (std::out_of_range& e) { // silently skipping, empty suggestions will be thrown later on + } catch (std::out_of_range& e) { } - } else { - s = s_lower; - ac = Aircraft::from_all(s); + } else if (s_lower.substr(0, 4) == "all:") { + return Aircraft::ParseResult(Aircraft::SearchType::ALL, s_lower.substr(4)); } + return Aircraft::ParseResult(Aircraft::SearchType::ALL, s_lower); +} - if (ac.valid) return ac; - - // empty airports, suggest and throw error - std::vector aircrafts; - switch (search_type) { +Aircraft::SearchResult Aircraft::search(const string& s) { + auto parse_result = Aircraft::ParseResult(Aircraft::parse(s)); + int8_t priority = 0; // TODO: attempt to parse engine type! + duckdb::unique_ptr result; + switch (parse_result.search_type) { case Aircraft::SearchType::ALL: - aircrafts = Aircraft::suggest_all(s); + result = Database::Client()->get_aircraft_by_all->Execute(parse_result.search_str.c_str(), priority); break; case Aircraft::SearchType::NAME: - aircrafts = Aircraft::suggest_name(s); + result = Database::Client()->get_aircraft_by_name->Execute(parse_result.search_str.c_str(), priority); break; case Aircraft::SearchType::SHORTNAME: - aircrafts = Aircraft::suggest_shortname(s); + result = Database::Client()->get_aircraft_by_shortname->Execute(parse_result.search_str.c_str(), priority); + break; + case Aircraft::SearchType::ID: + result = Database::Client()->get_aircraft_by_id->Execute(std::stoi(parse_result.search_str), priority); break; } - - throw AircraftNotFoundException(search_type, s, aircrafts); -} - -Aircraft::Aircraft(const duckdb::DataChunk& chunk, idx_t row) : - id(chunk.GetValue(0, row).GetValue()), - shortname(chunk.GetValue(1, row).GetValue()), - manufacturer(chunk.GetValue(2, row).GetValue()), - name(chunk.GetValue(3, row).GetValue()), - type(static_cast(chunk.GetValue(4, row).GetValue())), - priority(chunk.GetValue(5, row).GetValue()), - eid(chunk.GetValue(6, row).GetValue()), - ename(chunk.GetValue(7, row).GetValue()), - speed(chunk.GetValue(8, row).GetValue()), - fuel(chunk.GetValue(9, row).GetValue()), - co2(chunk.GetValue(10, row).GetValue()), - cost(chunk.GetValue(11, row).GetValue()), - capacity(chunk.GetValue(12, row).GetValue()), - rwy(chunk.GetValue(13, row).GetValue()), - check_cost(chunk.GetValue(14, row).GetValue()), - range(chunk.GetValue(15, row).GetValue()), - ceil(chunk.GetValue(16, row).GetValue()), - maint(chunk.GetValue(17, row).GetValue()), - pilots(chunk.GetValue(18, row).GetValue()), - crew(chunk.GetValue(19, row).GetValue()), - engineers(chunk.GetValue(20, row).GetValue()), - technicians(chunk.GetValue(21, row).GetValue()), - img(chunk.GetValue(22, row).GetValue()), - wingspan(chunk.GetValue(23, row).GetValue()), - length(chunk.GetValue(24, row).GetValue()), - valid(true) -{}; - -Aircraft Aircraft::from_id(uint16_t id, uint8_t priority) { - auto result = Database::Client()->get_aircraft_by_id->Execute(id, priority); - CHECK_SUCCESS(result); - auto chunk = result->Fetch(); - if (!chunk || chunk->size() == 0) return Aircraft(); - - return Aircraft(*chunk, 0); -} - -Aircraft Aircraft::from_shortname(const string& shortname, uint8_t priority) { - auto result = Database::Client()->get_aircraft_by_shortname->Execute(shortname.c_str(), priority); - CHECK_SUCCESS(result); - auto chunk = result->Fetch(); - if (!chunk || chunk->size() == 0) return Aircraft(); - - return Aircraft(*chunk, 0); -} - -// TODO: also search for concat(manufacturer, ' ', name)? -Aircraft Aircraft::from_name(const string& s, uint8_t priority) { - auto result = Database::Client()->get_aircraft_by_name->Execute(s.c_str(), priority); CHECK_SUCCESS(result); - auto chunk = result->Fetch(); - if (!chunk || chunk->size() == 0) return Aircraft(); + duckdb::unique_ptr chunk = result->Fetch(); + if (!chunk || chunk->size() == 0) return Aircraft::SearchResult(make_shared(), parse_result); - return Aircraft(*chunk, 0); + return Aircraft::SearchResult(make_shared(chunk, 0), parse_result); } -Aircraft Aircraft::from_all(const string& s, uint8_t priority) { - auto result = Database::Client()->get_aircraft_by_all->Execute(s.c_str(), priority); - CHECK_SUCCESS(result); - auto chunk = result->Fetch(); - if (!chunk || chunk->size() == 0) return Aircraft(); - - return Aircraft(*chunk, 0); -} - - - -std::vector Aircraft::suggest_shortname(const string& s, uint8_t priority) { - std::vector aircrafts; - auto result = Database::Client()->suggest_aircraft_by_shortname->Execute(s.c_str(), priority); - CHECK_SUCCESS(result); - while (auto chunk = result->Fetch()) { - for (idx_t i = 0; i < chunk->size(); i++) { - aircrafts.emplace_back(*chunk, i); +std::vector Aircraft::suggest(const ParseResult& parse_result) { + std::vector suggestions; + int8_t priority = 0; // TODO: attempt to parse engine type! + if (parse_result.search_type == Aircraft::SearchType::ALL) { + for (auto& stmt : { + Database::Client()->suggest_aircraft_by_shortname.get(), + Database::Client()->suggest_aircraft_by_name.get(), + }) { + auto result = stmt->Execute(parse_result.search_str.c_str(), priority); + CHECK_SUCCESS(result); + auto chunk = result->Fetch(); + if (!chunk || chunk->size() == 0) continue; + + for (idx_t i = 0; i < chunk->size(); i++) { + suggestions.emplace_back( + make_shared(chunk, i), + chunk->GetValue(25, i).GetValue() + ); + } } - } - return aircrafts; -} - -std::vector Aircraft::suggest_name(const string& s, uint8_t priority) { - std::vector aircrafts; - auto result = Database::Client()->suggest_aircraft_by_name->Execute(s.c_str(), priority); - CHECK_SUCCESS(result); - while (auto chunk = result->Fetch()) { - for (idx_t i = 0; i < chunk->size(); i++) { - aircrafts.emplace_back(*chunk, i); + std::partial_sort(suggestions.begin(), suggestions.begin() + 5, suggestions.end(), [](const Aircraft::Suggestion& a, const Aircraft::Suggestion& b) { + return a.score > b.score; + }); + suggestions.resize(5); + } else { + duckdb::unique_ptr result; + switch (parse_result.search_type) { + case Aircraft::SearchType::NAME: + result = Database::Client()->suggest_aircraft_by_name->Execute(parse_result.search_str.c_str(), priority); + break; + case Aircraft::SearchType::SHORTNAME: + result = Database::Client()->suggest_aircraft_by_shortname->Execute(parse_result.search_str.c_str(), priority); + break; } - } - return aircrafts; -} - -// TODO: remove duplicates -std::vector Aircraft::suggest_all(const string& s, uint8_t priority) { - std::vector aircrafts; - std::vector suggestions; - for (auto& stmt : { - Database::Client()->suggest_aircraft_by_shortname.get(), - Database::Client()->suggest_aircraft_by_name.get(), - }) { - auto result = stmt->Execute(s.c_str(), priority); CHECK_SUCCESS(result); - auto chunk = result->Fetch(); - if (!chunk || chunk->size() == 0) continue; - - for (idx_t i = 0; i < chunk->size(); i++) { - suggestions.emplace_back( - Aircraft(*chunk, i), - chunk->GetValue(5, i).GetValue() - ); + while (auto chunk = result->Fetch()) { + for (idx_t i = 0; i < chunk->size(); i++) { + suggestions.emplace_back( + make_shared(chunk, i), + chunk->GetValue(25, i).GetValue() + ); + } } } - - std::partial_sort(suggestions.begin(), suggestions.begin() + 5, suggestions.end(), [](const AircraftSuggestion& a, const AircraftSuggestion& b) { - return a.score > b.score; - }); - - for (size_t i = 0; i < std::min(5, suggestions.size()); i++) { - aircrafts.push_back(std::move(suggestions[i].ac)); - } - - return aircrafts; + return suggestions; } +Aircraft::Aircraft(const duckdb::unique_ptr& chunk, idx_t row) : + id(chunk->GetValue(0, row).GetValue()), + shortname(chunk->GetValue(1, row).GetValue()), + manufacturer(chunk->GetValue(2, row).GetValue()), + name(chunk->GetValue(3, row).GetValue()), + type(static_cast(chunk->GetValue(4, row).GetValue())), + priority(chunk->GetValue(5, row).GetValue()), + eid(chunk->GetValue(6, row).GetValue()), + ename(chunk->GetValue(7, row).GetValue()), + speed(chunk->GetValue(8, row).GetValue()), + fuel(chunk->GetValue(9, row).GetValue()), + co2(chunk->GetValue(10, row).GetValue()), + cost(chunk->GetValue(11, row).GetValue()), + capacity(chunk->GetValue(12, row).GetValue()), + rwy(chunk->GetValue(13, row).GetValue()), + check_cost(chunk->GetValue(14, row).GetValue()), + range(chunk->GetValue(15, row).GetValue()), + ceil(chunk->GetValue(16, row).GetValue()), + maint(chunk->GetValue(17, row).GetValue()), + pilots(chunk->GetValue(18, row).GetValue()), + crew(chunk->GetValue(19, row).GetValue()), + engineers(chunk->GetValue(20, row).GetValue()), + technicians(chunk->GetValue(21, row).GetValue()), + img(chunk->GetValue(22, row).GetValue()), + wingspan(chunk->GetValue(23, row).GetValue()), + length(chunk->GetValue(24, row).GetValue()), + valid(true) +{}; + const string to_string(Aircraft::Type type) { switch(type) { case Aircraft::Type::PAX: @@ -191,8 +136,9 @@ const string to_string(Aircraft::Type type) { return "CARGO"; case Aircraft::Type::VIP: return "VIP"; + default: + return "[UNKNOWN]"; } - return "UNKNOWN"; } const string to_string(Aircraft::SearchType searchtype) { @@ -212,10 +158,8 @@ const string to_string(Aircraft::SearchType searchtype) { const string Aircraft::repr(const Aircraft& ac) { std::stringstream ss; - std::string actype = to_string(ac.type); - ss << ""; - return ss.str(); } @@ -327,6 +271,7 @@ CargoConfig CargoConfig::calc_l_conf(const CargoDemand& d_pf, uint32_t capacity) config.h = 100 - config.l; config.valid = d_pf.h >= (l_cap - d_pf.l) / 0.7; } + config.algorithm = CargoConfig::Algorithm::L; return config; } @@ -342,6 +287,7 @@ CargoConfig CargoConfig::calc_h_conf(const CargoDemand& d_pf, uint32_t capacity) config.l = 100 - config.h; config.valid = d_pf.l >= capacity - d_pf.h; } + config.algorithm = CargoConfig::Algorithm::H; return config; } diff --git a/src/am4utils/cpp/airport.cpp b/src/am4utils/cpp/airport.cpp index 9081be7..9616b3a 100644 --- a/src/am4utils/cpp/airport.cpp +++ b/src/am4utils/cpp/airport.cpp @@ -2,204 +2,130 @@ #include #include +#include "include/db.hpp" #include "include/airport.hpp" #include "include/route.hpp" -#include "include/db.hpp" - -using std::string; -using namespace duckdb; Airport::Airport() : valid(false) {} -Airport Airport::from_str(string s) { - Airport ap; - Airport::SearchType search_type = Airport::SearchType::ALL; - +Airport::ParseResult Airport::parse(const string& s) { string s_upper = s; std::transform(s_upper.begin(), s_upper.end(), s_upper.begin(), ::toupper); - // search airports if (s_upper.substr(0, 5) == "IATA:") { - search_type = Airport::SearchType::IATA; - s = s_upper.substr(5); - ap = Airport::from_iata(s); + return Airport::ParseResult(Airport::SearchType::IATA, s_upper.substr(5)); } else if (s_upper.substr(0, 5) == "ICAO:") { - search_type = Airport::SearchType::ICAO; - s = s_upper.substr(5); - ap = Airport::from_icao(s); + return Airport::ParseResult(Airport::SearchType::ICAO, s_upper.substr(5)); } else if (s_upper.substr(0, 5) == "NAME:") { - search_type = Airport::SearchType::NAME; - s = s_upper.substr(5); - ap = Airport::from_name(s); + return Airport::ParseResult(Airport::SearchType::NAME, s_upper.substr(5)); } else if (s_upper.substr(0, 3) == "ID:") { - search_type = Airport::SearchType::ID; - s = s.substr(3); try { - ap = Airport::from_id(std::stoi(s)); + uint16_t _ = std::stoi(s.substr(3)); + return Airport::ParseResult(Airport::SearchType::ID, s.substr(3)); } catch (std::invalid_argument& e) { - } catch (std::out_of_range& e) { // silently skipping, empty suggestions will be thrown later on + } catch (std::out_of_range& e) { } } else if (s_upper.substr(0, 4) == "ALL:") { - s = s_upper.substr(4); - ap = Airport::from_all(s); - } else { - s = s_upper; - ap = Airport::from_all(s); + return Airport::ParseResult(Airport::SearchType::ALL, s_upper.substr(4)); } + return Airport::ParseResult(Airport::SearchType::ALL, s_upper); +} - if (ap.valid) return ap; - - // empty airports, suggest and throw error - std::vector airports; - switch (search_type) { +Airport::SearchResult Airport::search(const string& s) { + auto parse_result = Airport::ParseResult(Airport::parse(s)); + duckdb::unique_ptr result; + switch (parse_result.search_type) { case Airport::SearchType::ALL: - airports = Airport::suggest_all(s); + result = Database::Client()->get_airport_by_all->Execute(parse_result.search_str.c_str()); break; case Airport::SearchType::IATA: - airports = Airport::suggest_iata(s); + result = Database::Client()->get_airport_by_iata->Execute(parse_result.search_str.c_str()); break; case Airport::SearchType::ICAO: - airports = Airport::suggest_icao(s); + result = Database::Client()->get_airport_by_icao->Execute(parse_result.search_str.c_str()); break; case Airport::SearchType::NAME: - airports = Airport::suggest_name(s); + result = Database::Client()->get_airport_by_name->Execute(parse_result.search_str.c_str()); + break; + case Airport::SearchType::ID: + result = Database::Client()->get_airport_by_id->Execute(std::stoi(parse_result.search_str)); break; } - - throw AirportNotFoundException(search_type, s, airports); -} - - -Airport::Airport(const duckdb::DataChunk& chunk, idx_t row) : - id(chunk.GetValue(0, row).GetValue()), - name(chunk.GetValue(1, row).GetValue()), - fullname(chunk.GetValue(2, row).GetValue()), - country(chunk.GetValue(3, row).GetValue()), - continent(chunk.GetValue(4, row).GetValue()), - iata(chunk.GetValue(5, row).GetValue()), - icao(chunk.GetValue(6, row).GetValue()), - lat(chunk.GetValue(7, row).GetValue()), - lng(chunk.GetValue(8, row).GetValue()), - rwy(chunk.GetValue(9, row).GetValue()), - market(chunk.GetValue(10, row).GetValue()), - hub_cost(chunk.GetValue(11, row).GetValue()), - rwy_codes(chunk.GetValue(12, row).GetValue()), - valid(true) {} - -Airport Airport::from_id(uint16_t id) { - auto result = Database::Client()->get_airport_by_id->Execute(id); - CHECK_SUCCESS(result); - auto chunk = result->Fetch(); - if (!chunk || chunk->size() == 0) return Airport(); - - return Airport(*chunk, 0); -} - -Airport Airport::from_iata(const string& s) { - auto result = Database::Client()->get_airport_by_iata->Execute(s.c_str()); // note: std::string somehow converts to BLOB - CHECK_SUCCESS(result); - auto chunk = result->Fetch(); - if (!chunk || chunk->size() == 0) return Airport(); - - return Airport(*chunk, 0); -} - -Airport Airport::from_icao(const string& s) { - auto result = Database::Client()->get_airport_by_icao->Execute(s.c_str()); - CHECK_SUCCESS(result); - auto chunk = result->Fetch(); - if (!chunk || chunk->size() == 0) return Airport(); - - return Airport(*chunk, 0); -} - -Airport Airport::from_name(const string& s) { - auto result = Database::Client()->get_airport_by_name->Execute(s.c_str()); CHECK_SUCCESS(result); - auto chunk = result->Fetch(); - if (!chunk || chunk->size() == 0) return Airport(); + duckdb::unique_ptr chunk = result->Fetch(); + if (!chunk || chunk->size() == 0) return Airport::SearchResult(make_shared(), parse_result); - return Airport(*chunk, 0); + return Airport::SearchResult(make_shared(chunk, 0), parse_result); } -Airport Airport::from_all(const string& s) { - auto result = Database::Client()->get_airport_by_all->Execute(s.c_str()); - CHECK_SUCCESS(result); - auto chunk = result->Fetch(); - if (!chunk || chunk->size() == 0) return Airport(); - - return Airport(*chunk, 0); -} - - -std::vector Airport::suggest_iata(const string& s) { - std::vector airports; - auto result = Database::Client()->suggest_airport_by_iata->Execute(s.c_str()); - CHECK_SUCCESS(result); - while (auto chunk = result->Fetch()) { - for (idx_t i = 0; i < chunk->size(); i++) { - airports.emplace_back(*chunk, i); +// note: searchtype id will return no suggestions. +std::vector Airport::suggest(const Airport::ParseResult& parse_result) { + std::vector suggestions; + if (parse_result.search_type == Airport::SearchType::ALL) { + for (auto& stmt : { + Database::Client()->suggest_airport_by_iata.get(), + Database::Client()->suggest_airport_by_icao.get(), + Database::Client()->suggest_airport_by_name.get(), + }) { + auto result = stmt->Execute(parse_result.search_str.c_str()); + CHECK_SUCCESS(result); + auto chunk = result->Fetch(); + if (!chunk || chunk->size() == 0) continue; + + for (idx_t i = 0; i < chunk->size(); i++) { + // TODO: ensure no duplicates + suggestions.emplace_back( + make_shared(chunk, i), + chunk->GetValue(13, i).GetValue() + ); + } } - } - return airports; -} - -std::vector Airport::suggest_icao(const string& s) { - std::vector airports; - auto result = Database::Client()->suggest_airport_by_icao->Execute(s.c_str()); - CHECK_SUCCESS(result); - while (auto chunk = result->Fetch()) { - for (idx_t i = 0; i < chunk->size(); i++) { - airports.emplace_back(*chunk, i); - } - } - return airports; -} - -std::vector Airport::suggest_name(const string& s) { - std::vector airports; - auto result = Database::Client()->suggest_airport_by_name->Execute(s.c_str()); - CHECK_SUCCESS(result); - while (auto chunk = result->Fetch()) { - for (idx_t i = 0; i < chunk->size(); i++) { - airports.emplace_back(*chunk, i); + std::partial_sort(suggestions.begin(), suggestions.begin() + 5, suggestions.end(), [](const Airport::Suggestion& a, const Airport::Suggestion& b) { + return a.score > b.score; + }); + suggestions.resize(5); + } else { + duckdb::unique_ptr result; + switch (parse_result.search_type) { + case Airport::SearchType::IATA: + result = Database::Client()->suggest_airport_by_iata->Execute(parse_result.search_str.c_str()); + break; + case Airport::SearchType::ICAO: + result = Database::Client()->suggest_airport_by_icao->Execute(parse_result.search_str.c_str()); + break; + case Airport::SearchType::NAME: + result = Database::Client()->suggest_airport_by_name->Execute(parse_result.search_str.c_str()); + break; } - } - return airports; -} - -// TODO: remove duplicates -std::vector Airport::suggest_all(const string& s) { - std::vector airports; - std::vector suggestions; - for (auto& stmt : { - Database::Client()->suggest_airport_by_iata.get(), - Database::Client()->suggest_airport_by_icao.get(), - Database::Client()->suggest_airport_by_name.get(), - }) { - auto result = stmt->Execute(s.c_str()); CHECK_SUCCESS(result); - auto chunk = result->Fetch(); - if (!chunk || chunk->size() == 0) continue; - - for (idx_t i = 0; i < chunk->size(); i++) { - suggestions.emplace_back( - Airport(*chunk, i), - chunk->GetValue(13, i).GetValue() - ); + while (auto chunk = result->Fetch()) { + for (idx_t i = 0; i < chunk->size(); i++) { + suggestions.emplace_back( + make_shared(chunk, i), + chunk->GetValue(13, i).GetValue() + ); + } } } + return suggestions; +} - std::partial_sort(suggestions.begin(), suggestions.begin() + 5, suggestions.end(), [](const AirportSuggestion& a, const AirportSuggestion& b) { - return a.score > b.score; - }); - - for (size_t i = 0; i < std::min(5, suggestions.size()); i++) { - airports.push_back(std::move(suggestions[i].ap)); - } - return airports; -} +Airport::Airport(const duckdb::unique_ptr& chunk, idx_t row) : + id(chunk->GetValue(0, row).GetValue()), + name(chunk->GetValue(1, row).GetValue()), + fullname(chunk->GetValue(2, row).GetValue()), + country(chunk->GetValue(3, row).GetValue()), + continent(chunk->GetValue(4, row).GetValue()), + iata(chunk->GetValue(5, row).GetValue()), + icao(chunk->GetValue(6, row).GetValue()), + lat(chunk->GetValue(7, row).GetValue()), + lng(chunk->GetValue(8, row).GetValue()), + rwy(chunk->GetValue(9, row).GetValue()), + market(chunk->GetValue(10, row).GetValue()), + hub_cost(chunk->GetValue(11, row).GetValue()), + rwy_codes(chunk->GetValue(12, row).GetValue()), + valid(true) {} const string to_string(Airport::SearchType st) { switch (st) { diff --git a/src/am4utils/cpp/binder.cpp b/src/am4utils/cpp/binder.cpp index 195cef1..fa17770 100644 --- a/src/am4utils/cpp/binder.cpp +++ b/src/am4utils/cpp/binder.cpp @@ -1,4 +1,5 @@ #include +#include #include "include/db.hpp" #include "include/user.hpp" @@ -35,15 +36,59 @@ PYBIND11_MODULE(_core, m) { py::register_exception(m_db, "DatabaseException"); - // needs to be defined before classes for default arguments to work + /*** USER ***/ py::enum_(m_user, "GameMode") .value("EASY", User::GameMode::EASY) .value("REALISM", User::GameMode::REALISM); + /*** TICKET ***/ + py::class_(m_ticket, "PaxTicket") + .def_readonly("y", &PaxTicket::y) + .def_readonly("j", &PaxTicket::j) + .def_readonly("f", &PaxTicket::f) + .def_static("from_optimal", &PaxTicket::from_optimal, "distance"_a, "game_mode"_a = User::GameMode::EASY) + .def("__repr__", &PaxTicket::repr); + + py::class_(m_ticket, "CargoTicket") + .def_readonly("l", &CargoTicket::l) + .def_readonly("h", &CargoTicket::h) + .def_static("from_optimal", &CargoTicket::from_optimal, "distance"_a, "game_mode"_a = User::GameMode::EASY) + .def("__repr__", &CargoTicket::repr); + + py::class_(m_ticket, "VIPTicket") + .def_readonly("y", &VIPTicket::y) + .def_readonly("j", &VIPTicket::j) + .def_readonly("f", &VIPTicket::f) + .def_static("from_optimal", &VIPTicket::from_optimal, "distance"_a) + .def("__repr__", &VIPTicket::repr); + + py::class_(m_ticket, "Ticket") + .def_readonly("pax_ticket", &Ticket::pax_ticket) + .def_readonly("cargo_ticket", &Ticket::cargo_ticket) + .def_readonly("vip_ticket", &Ticket::vip_ticket); + + + /*** DEMAND ***/ + py::class_(m_demand, "PaxDemand") + .def_readonly("y", &PaxDemand::y) + .def_readonly("j", &PaxDemand::j) + .def_readonly("f", &PaxDemand::f) + .def("__repr__", &PaxDemand::repr); + + py::class_(m_demand, "CargoDemand") + .def_readonly("l", &CargoDemand::l) + .def_readonly("h", &CargoDemand::h) + .def("__repr__", &CargoDemand::repr); + + /*** AIRCRAFT ***/ - py::class_ ac_class(m_ac, "Aircraft"); + py::class_> ac_class(m_ac, "Aircraft"); + py::enum_(ac_class, "Type") + .value("PAX", Aircraft::Type::PAX) + .value("CARGO", Aircraft::Type::CARGO) + .value("VIP", Aircraft::Type::VIP); + ac_class - .def(py::init()) .def_readonly("id", &Aircraft::id) .def_readonly("shortname", &Aircraft::shortname) .def_readonly("manufacturer", &Aircraft::manufacturer) @@ -70,62 +115,69 @@ PYBIND11_MODULE(_core, m) { .def_readonly("wingspan", &Aircraft::wingspan) .def_readonly("length", &Aircraft::length) .def_readonly("valid", &Aircraft::valid) - .def_static("from_str", &Aircraft::from_str, "s"_a) - .def("__repr__", [](const Aircraft &ac) { - return Aircraft::repr(ac); - }); + .def("__repr__", &Aircraft::repr); - py::enum_(ac_class, "Type") - .value("PAX", Aircraft::Type::PAX) - .value("CARGO", Aircraft::Type::CARGO) - .value("VIP", Aircraft::Type::VIP); - - py::class_ p_ac_class(m_ac, "PurchasedAircraft"); - p_ac_class - .def(py::init<>()) - .def_readonly("config", &PurchasedAircraft::config) - .def("__repr__", [](const PurchasedAircraft &ac) { - return PurchasedAircraft::repr(ac); - }) - - py::class_(p_ac_class, "Config") - .def(py::init<>()) - .def_readonly("pax_config", &PurchasedAircraft::Config::pax_config) - .def_readonly("cargo_config", &PurchasedAircraft::Config::cargo_config); + py::enum_(ac_class, "SearchType") + .value("ALL", Aircraft::SearchType::ALL) + .value("ID", Aircraft::SearchType::ID) + .value("SHORTNAME", Aircraft::SearchType::SHORTNAME) + .value("NAME", Aircraft::SearchType::NAME); + + py::class_(ac_class, "ParseResult") + .def(py::init()) + .def_readonly("search_type", &Aircraft::ParseResult::search_type) + .def_readonly("search_str", &Aircraft::ParseResult::search_str); + + py::class_(ac_class, "SearchResult") + .def(py::init, Aircraft::ParseResult>()) + .def_readonly("ac", &Aircraft::SearchResult::ac) + .def_readonly("parse_result", &Aircraft::SearchResult::parse_result); + + py::class_(ac_class, "Suggestion") + .def(py::init, double>()) + .def_readonly("ac", &Aircraft::Suggestion::ac) + .def_readonly("score", &Aircraft::Suggestion::score); + + ac_class + .def_static("search", &Aircraft::search, "s"_a) + .def_static("suggest", &Aircraft::suggest, "s"_a); + /*** PURCHASED AIRCRAFT ***/ py::class_ pc_class(m_ac, "PaxConfig"); + py::enum_(pc_class, "Algorithm") + .value("FJY", PaxConfig::Algorithm::FJY).value("FYJ", PaxConfig::Algorithm::FYJ) + .value("JFY", PaxConfig::Algorithm::JFY).value("JYF", PaxConfig::Algorithm::JYF) + .value("YJF", PaxConfig::Algorithm::YJF).value("YFJ", PaxConfig::Algorithm::YFJ) + .value("NONE", PaxConfig::Algorithm::NONE); pc_class - .def(py::init<>()) .def_readonly("y", &PaxConfig::y) .def_readonly("j", &PaxConfig::j) .def_readonly("f", &PaxConfig::f) .def_readonly("valid", &PaxConfig::valid) .def_readonly("algorithm", &PaxConfig::algorithm); - py::enum_(pc_class, "Algorithm") - .value("FJY", PaxConfig::Algorithm::FJY).value("FYJ", PaxConfig::Algorithm::FYJ) - .value("JFY", PaxConfig::Algorithm::JFY).value("JYF", PaxConfig::Algorithm::JYF) - .value("YJF", PaxConfig::Algorithm::YJF).value("YFJ", PaxConfig::Algorithm::YFJ) - .value("NONE", PaxConfig::Algorithm::NONE); - py::class_ cc_class(m_ac, "CargoConfig"); + py::enum_(cc_class, "Algorithm") + .value("L", CargoConfig::Algorithm::L).value("H", CargoConfig::Algorithm::H) + .value("NONE", CargoConfig::Algorithm::NONE); cc_class - .def(py::init<>()) .def_readonly("l", &CargoConfig::l) .def_readonly("h", &CargoConfig::h) .def_readonly("valid", &CargoConfig::valid) .def_readonly("algorithm", &CargoConfig::algorithm); - py::enum_(cc_class, "Algorithm") - .value("L", CargoConfig::Algorithm::L).value("H", CargoConfig::Algorithm::H) - .value("NONE", CargoConfig::Algorithm::NONE); - - py::register_exception(m_ac, "AircraftNotFoundException"); + py::class_, Aircraft> p_ac_class(m_ac, "PurchasedAircraft"); + py::class_(p_ac_class, "Config") + .def_readonly("pax_config", &PurchasedAircraft::Config::pax_config) + .def_readonly("cargo_config", &PurchasedAircraft::Config::cargo_config); + p_ac_class + .def_readonly("config", &PurchasedAircraft::config) + .def("__repr__", &PurchasedAircraft::repr); /*** AIRPORT ***/ - py::class_(m_ap, "Airport") - .def(py::init<>()) + py::class_> ap_class(m_ap, "Airport"); + ap_class .def_readonly("id", &Airport::id) .def_readonly("name", &Airport::name) .def_readonly("fullname", &Airport::fullname) @@ -140,17 +192,37 @@ PYBIND11_MODULE(_core, m) { .def_readonly("hub_cost", &Airport::hub_cost) .def_readonly("rwy_codes", &Airport::rwy_codes) .def_readonly("valid", &Airport::valid) - .def_static("from_str", &Airport::from_str, "s"_a) - .def("__repr__", [](const Airport &ac) { - return Airport::repr(ac); - }); - - py::register_exception(m_ap, "AirportNotFoundException"); - + .def("__repr__", &Airport::repr); + + py::enum_(ap_class, "SearchType") + .value("ALL", Airport::SearchType::ALL) + .value("IATA", Airport::SearchType::IATA) + .value("ICAO", Airport::SearchType::ICAO) + .value("NAME", Airport::SearchType::NAME) + .value("ID", Airport::SearchType::ID); + + py::class_(ap_class, "ParseResult") + .def(py::init()) + .def_readonly("search_type", &Airport::ParseResult::search_type) + .def_readonly("search_str", &Airport::ParseResult::search_str); + + py::class_(ap_class, "SearchResult") + .def(py::init, Airport::ParseResult>()) + .def_readonly("ap", &Airport::SearchResult::ap) + .def_readonly("parse_result", &Airport::SearchResult::parse_result); + + py::class_(ap_class, "Suggestion") + .def(py::init, double>()) + .def_readonly("ap", &Airport::Suggestion::ap) + .def_readonly("score", &Airport::Suggestion::score); + + // defined after nested classes + ap_class + .def_static("search", &Airport::search, "s"_a) + .def_static("suggest", &Airport::suggest, "s"_a); /*** ROUTE ***/ py::class_(m_route, "Route") - .def(py::init<>()) .def_readonly("origin", &Route::origin) .def_readonly("destination", &Route::destination) .def_readonly("pax_demand", &Route::pax_demand) @@ -160,63 +232,9 @@ PYBIND11_MODULE(_core, m) { .def_readonly("income", &Route::income) .def_readonly("direct_distance", &Route::direct_distance) .def_readonly("valid", &Route::valid) - .def_static("create_optimal_pax_ticket", &PaxTicket::from_optimal, "distance"_a, "game_mode"_a) - .def_static("create_optimal_cargo_ticket", &CargoTicket::from_optimal, "distance"_a, "game_mode"_a) .def_static("from_airports", &Route::from_airports, "ap1"_a, "ap2"_a) .def_static("from_airports_with_aircraft", &Route::from_airports_with_aircraft, "ap1"_a, "ap2"_a, "ac"_a, "trips_per_day"_a = 1, "game_mode"_a = User::GameMode::EASY) - .def("__repr__", [](const Route& r) { - return Route::repr(r); - }); - + .def("__repr__", &Route::repr); - py::class_(m_ticket, "PaxTicket") - .def(py::init<>()) - .def_readonly("y", &PaxTicket::y) - .def_readonly("j", &PaxTicket::j) - .def_readonly("f", &PaxTicket::f) - .def("__repr__", [](const PaxTicket &a) { - return PaxTicket::repr(a); - }); - - py::class_(m_ticket, "CargoTicket") - .def(py::init<>()) - .def_readonly("l", &CargoTicket::l) - .def_readonly("h", &CargoTicket::h) - .def("__repr__", [](const CargoTicket &a) { - return CargoTicket::repr(a); - }); - - py::class_(m_ticket, "VIPTicket") - .def(py::init<>()) - .def_readonly("y", &VIPTicket::y) - .def_readonly("j", &VIPTicket::j) - .def_readonly("f", &VIPTicket::f) - .def("__repr__", [](const VIPTicket &a) { - return VIPTicket::repr(a); - }); - - py::class_(m_ticket, "Ticket") - .def(py::init<>()) - .def_readonly("pax_ticket", &Ticket::pax_ticket) - .def_readonly("cargo_ticket", &Ticket::cargo_ticket) - .def_readonly("vip_ticket", &Ticket::vip_ticket); - - py::class_(m_demand, "PaxDemand") - .def(py::init<>()) - .def_readonly("y", &PaxDemand::y) - .def_readonly("j", &PaxDemand::j) - .def_readonly("f", &PaxDemand::f) - .def("__repr__", [](const PaxDemand &a) { - return PaxDemand::repr(a); - }); - - py::class_(m_demand, "CargoDemand") - .def(py::init<>()) - .def_readonly("l", &CargoDemand::l) - .def_readonly("h", &CargoDemand::h) - .def("__repr__", [](const CargoDemand &a) { - return CargoDemand::repr(a); - }); - m.attr("__version__") = version; } \ No newline at end of file diff --git a/src/am4utils/cpp/demand.cpp b/src/am4utils/cpp/demand.cpp index db80f95..7edc5ba 100644 --- a/src/am4utils/cpp/demand.cpp +++ b/src/am4utils/cpp/demand.cpp @@ -1,5 +1,5 @@ #include "include/demand.hpp" - +#include PaxDemand::PaxDemand() : y(0), j(0), f(0) {}; PaxDemand::PaxDemand(uint16_t y, uint16_t j, uint16_t f) : y(y), j(j), f(f) {}; @@ -10,11 +10,11 @@ PaxDemand::PaxDemand(const duckdb::DataChunk& chunk, idx_t row) : CargoDemand::CargoDemand() : l(0), h(0) {}; CargoDemand::CargoDemand(uint32_t l, uint32_t h) : l(l), h(h) {}; -CargoDemand::CargoDemand(uint16_t y, uint16_t j) : l(y * 1000), h(round((j / 2.0F) * 1000)) {}; +CargoDemand::CargoDemand(uint16_t y, uint16_t j) : l(round((y / 2.0F) * 1000)), h(j * 1000) {}; CargoDemand::CargoDemand(const duckdb::DataChunk& chunk, idx_t row) : - l(chunk.GetValue(0, row).GetValue() * 1000), - h(round(chunk.GetValue(1, row).GetValue() / 2) * 1000) {}; -CargoDemand::CargoDemand(const PaxDemand& pax_demand) : l(pax_demand.y * 1000), h(round(pax_demand.j / 2.0F) * 500) {}; + l(round(chunk.GetValue(0, row).GetValue() / 2) * 1000), + h(chunk.GetValue(1, row).GetValue() * 1000) {}; +CargoDemand::CargoDemand(const PaxDemand& pax_demand) : l(round(pax_demand.y / 2.0F) * 1000), h(pax_demand.j * 1000) {}; const string PaxDemand::repr(const PaxDemand& demand) { diff --git a/src/am4utils/cpp/include/aircraft.hpp b/src/am4utils/cpp/include/aircraft.hpp index 95f653c..a023555 100644 --- a/src/am4utils/cpp/include/aircraft.hpp +++ b/src/am4utils/cpp/include/aircraft.hpp @@ -10,6 +10,8 @@ using std::string; using std::to_string; +using std::shared_ptr; +using std::make_shared; struct Aircraft { enum class Type { @@ -52,17 +54,34 @@ struct Aircraft { uint8_t length; bool valid = false; + struct ParseResult { + Aircraft::SearchType search_type; + string search_str; + + ParseResult(Aircraft::SearchType search_type, const string& search_str) : search_type(search_type), search_str(search_str) {} + }; + + struct SearchResult { + shared_ptr ac; + Aircraft::ParseResult parse_result; + + SearchResult(shared_ptr ac, Aircraft::ParseResult parse_result) : ac(ac), parse_result(parse_result) {} + }; + + struct Suggestion { + shared_ptr ac; + double score; + + Suggestion() : ac(make_shared()), score(0) {} + Suggestion(shared_ptr ac, double score) : ac(ac), score(score) {} + }; + Aircraft(); - static Aircraft from_str(string s); - - Aircraft(const duckdb::DataChunk& chunk, idx_t row); - static Aircraft from_id(uint16_t id, uint8_t priority = 0); - static Aircraft from_shortname(const string& s, uint8_t priority = 0); - static Aircraft from_name(const string& s, uint8_t priority = 0); - static Aircraft from_all(const string& s, uint8_t priority = 0); - static std::vector suggest_shortname(const string& s, uint8_t priority = 0); - static std::vector suggest_name(const string& s, uint8_t priority = 0); - static std::vector suggest_all(const string& s, uint8_t priority = 0); + static ParseResult parse(const string& s); + static SearchResult search(const string& s); + static std::vector suggest(const ParseResult& parse_result); + + Aircraft(const duckdb::unique_ptr& chunk, idx_t row); static const string repr(const Aircraft& ac); }; @@ -76,30 +95,6 @@ struct AircraftSuggestion { AircraftSuggestion(const Aircraft& ac, double score) : ac(ac), score(score) {} }; -class AircraftNotFoundException : public std::exception { -private: - Aircraft::SearchType searchtype; - string searchstr; - std::vector suggestions; -public: - AircraftNotFoundException(Aircraft::SearchType searchtype, string searchstr, std::vector suggestions) : searchtype(searchtype), searchstr(searchstr), suggestions(suggestions) {} - const char* what() const throw() { - std::stringstream ss; - string searchtype_str = to_string(searchtype); - ss << "Aircraft not found - " << searchtype_str << ":" << searchstr; - if (suggestions.size() > 0) { - ss << ". Did you mean: "; - for (auto ac : suggestions) { - ss << "\n " << std::setw(3) << ac.id << ": " << ac.shortname << "/" << ac.name; - } - } - return ss.str().c_str(); - } - Aircraft::SearchType get_searchtype() { return searchtype; } - string get_searchstr() { return searchstr; } - std::vector get_suggestions() { return suggestions; } -}; - struct PaxConfig { enum class Algorithm { FJY, FYJ, diff --git a/src/am4utils/cpp/include/airport.hpp b/src/am4utils/cpp/include/airport.hpp index 6c3c491..40b8825 100644 --- a/src/am4utils/cpp/include/airport.hpp +++ b/src/am4utils/cpp/include/airport.hpp @@ -1,11 +1,13 @@ #pragma once #include #include +#include #include using std::string; using std::to_string; -using std::vector; +using std::shared_ptr; +using std::make_shared; struct Airport { enum class SearchType { @@ -29,53 +31,37 @@ struct Airport { uint8_t market; uint32_t hub_cost; string rwy_codes; - bool valid = false; + bool valid; - Airport(); - static Airport from_str(string s); + struct ParseResult { + Airport::SearchType search_type; + string search_str; - Airport(const duckdb::DataChunk& chunk, idx_t row); - static Airport from_id(uint16_t id); - static Airport from_iata(const string& s); - static Airport from_icao(const string& s); - static Airport from_name(const string& s); - static Airport from_all(const string& s); - static std::vector suggest_iata(const string& s); - static std::vector suggest_icao(const string& s); - static std::vector suggest_name(const string& s); - static std::vector suggest_all(const string& s); - static const string repr(const Airport& ap); -}; + ParseResult(Airport::SearchType search_type, const string& search_str) : search_type(search_type), search_str(search_str) {} + }; + + struct SearchResult { + shared_ptr ap; + Airport::ParseResult parse_result; + + SearchResult(shared_ptr ap, Airport::ParseResult parse_result) : ap(ap), parse_result(parse_result) {} + }; -inline const string to_string(Airport::SearchType st); + struct Suggestion { + shared_ptr ap; + double score; -struct AirportSuggestion { - Airport ap; - double score; + Suggestion() : ap(make_shared()), score(0) {} + Suggestion(shared_ptr ap, double score) : ap(ap), score(score) {} + }; - AirportSuggestion(const Airport& ap, double score) : ap(ap), score(score) {} + Airport(); + static ParseResult parse(const string& s); + static SearchResult search(const string& s); + static std::vector suggest(const ParseResult& parse_result); + + Airport(const duckdb::unique_ptr& chunk, idx_t row); + static const string repr(const Airport& ap); }; -class AirportNotFoundException : public std::exception { -private: - Airport::SearchType searchtype; - string searchstr; - std::vector suggestions; -public: - AirportNotFoundException(Airport::SearchType searchtype, string searchstr, std::vector suggestions) : searchtype(searchtype), searchstr(searchstr), suggestions(suggestions) {} - const char* what() const throw() { - std::stringstream ss; - string searchtype_str = to_string(searchtype); - ss << "Airport not found - " << searchtype_str << ":" << searchstr; - if (suggestions.size() > 0) { - ss << ". Did you mean: "; - for (auto ap : suggestions) { - ss << "\n " << ap.id << ": " << ap.iata << "/" << ap.icao << "/" << ap.name; - } - } - return ss.str().c_str(); - } - Airport::SearchType get_searchtype() { return searchtype; } - string get_searchstr() { return searchstr; } - std::vector get_suggestions() { return suggestions; } -}; \ No newline at end of file +inline const string to_string(Airport::SearchType st); \ No newline at end of file diff --git a/src/am4utils/cpp/include/db.hpp b/src/am4utils/cpp/include/db.hpp index c34d228..464dca7 100644 --- a/src/am4utils/cpp/include/db.hpp +++ b/src/am4utils/cpp/include/db.hpp @@ -9,6 +9,7 @@ using duckdb::PreparedStatement; #define STRINGIFY(x) #x #define MACRO_STRINGIFY(x) STRINGIFY(x) #define CHECK_SUCCESS(q) if (q->HasError()) throw DatabaseException(q->GetError()); +#define CHECK_SUCCESS_REF(q) if (q.HasError()) throw DatabaseException(q.GetError()); // multiple threads can use the same connection? // https://github.com/duckdb/duckdb/blob/8c32403411d628a400cc32e5fe73df87eb5aad7d/test/api/test_api.cpp#L142 diff --git a/src/am4utils/cpp/main.cpp b/src/am4utils/cpp/main.cpp index 7eda393..f4a57d6 100644 --- a/src/am4utils/cpp/main.cpp +++ b/src/am4utils/cpp/main.cpp @@ -55,36 +55,34 @@ int main(int argc, char **argv) { try { init(executable_path); // 1.3s - } catch (DatabaseException &e) { - cerr << "DatabaseException: " << e.what() << endl; - return 1; - } - // cout << "initialised database" << endl; + // cout << "initialised database" << endl; - // PaxTicket pt = PaxTicket::from_optimal(10000, User::GameMode::EASY); - // cout << pt.y << " | " << pt.j << " | " << pt.f << endl; - - // Aircraft ac; - // try { - // ac = Aircraft::from_str("name:B747-400"); - // cout << ac.repr() << endl; - // } catch (DatabaseException &e) { - // cerr << "DatabaseException: " << e.what() << endl; - // return 1; - // } catch (AircraftNotFoundException &e) { - // cerr << "AircraftNotFoundException: " << e.what() << endl; - // return 1; + // auto sr = Airport::search("hong kong"); + // cout << sr.ap->name << endl; + // auto suggestions = Airport::suggest(sr.parse_result); + // for (auto s : suggestions) { + // cout << s.ap->name << " (" << s.score << ")" << endl; // } + auto sr = Aircraft::search("b744"); + cout << sr.ac->name << endl; + auto suggestions = Aircraft::suggest(sr.parse_result); + for (auto s : suggestions) { + cout << s.ac->name << " (" << s.score << ")" << endl; + } - Aircraft ac = Aircraft::from_str("name:B747-400"); - Airport ap0 = Airport::from_str("icao:VhHH"); - Airport ap1 = Airport::from_str("iata:LhR"); - Route r = Route::from_airports_with_aircraft(ap0, ap1, ac); - cout << Route::repr(r) << endl; + // Aircraft ac = Aircraft::search("name:B747-400").ac; + // Airport ap0 = Airport::search("icao:VhHH").ap; + // Airport ap1 = Airport::search("iata:LhR").ap; + // Route r = Route::from_airports_with_aircraft(ap0, ap1, ac); + // cout << Route::repr(r) << endl; // Route r = Route::from_airports(ap0, ap1); // _debug_query("SELECT current_setting('home_directory')"); + } catch (DatabaseException &e) { + cerr << "DatabaseException: " << e.what() << endl; + return 1; + } return 0; } \ No newline at end of file diff --git a/src/am4utils/cpp/ticket.cpp b/src/am4utils/cpp/ticket.cpp index dddca85..fa55a6c 100644 --- a/src/am4utils/cpp/ticket.cpp +++ b/src/am4utils/cpp/ticket.cpp @@ -1,4 +1,5 @@ #include "include/ticket.hpp" +#include PaxTicket PaxTicket::from_optimal(float distance, User::GameMode game_mode) { PaxTicket ticket; diff --git a/src/am4utils/generate-stubs.py b/src/am4utils/generate-stubs.py index 02ee2c2..3ba9449 100644 --- a/src/am4utils/generate-stubs.py +++ b/src/am4utils/generate-stubs.py @@ -1,14 +1,15 @@ import re import shutil import pybind11_stubgen +import logging if __name__ == '__main__': shutil.rmtree('stubs', ignore_errors=True) pybind11_stubgen.StubsGenerator.GLOBAL_CLASSNAME_REPLACEMENTS.update({ - re.compile(r'union (\w+)'): lambda m: m.group(1), - re.compile(r'PurchasedAircaftConfig'): lambda m: 'PurchasedAircraftConfig', - re.compile(r'::'): lambda m: '.', # scoped enum in class + # re.compile(r'union (\w+)'): lambda m: m.group(1), + # re.compile(r'PurchasedAircaftConfig'): lambda m: 'PurchasedAircraftConfig', + # re.compile(r'::'): lambda m: '.', # scoped enum in class }) pybind11_stubgen.main( @@ -18,7 +19,25 @@ "am4utils", ], ) + shutil.move('am4utils-stubs', 'stubs') + + def replace(fn, map): + with open(fn, 'r+') as f: + contents = f.read() + for k, v in map.items(): + contents = contents.replace(k, v) + f.seek(0) + f.write(contents) + f.truncate() + + replace('stubs/_core/route/__init__.pyi', { + 'import am4utils._core.user': 'import am4utils._core.user\nimport am4utils._core.ticket\nimport am4utils._core.demand', + 'GameMode = GameMode.EASY': 'GameMode = am4utils._core.user.GameMode.EASY', + }) + replace('stubs/_core/ticket/__init__.pyi', { + 'GameMode = GameMode.EASY': 'GameMode = am4utils._core.user.GameMode.EASY', + }) try: import am4utils diff --git a/src/am4utils/py/py.typed b/src/am4utils/py/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/src/am4utils/pyproject.toml b/src/am4utils/pyproject.toml index 2a9b313..ef4cc57 100644 --- a/src/am4utils/pyproject.toml +++ b/src/am4utils/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "am4utils" -version = "0.1.2" +version = "0.1.3" description = "Tools and utilities for the game Airline Manager 4." authors = [{ name = "cathaypacific8747", email = "58929011+cathaypacific8747@users.noreply.github.com" }] license = { text = "MIT License" } diff --git a/src/am4utils/stubs/_core/__init__.pyi b/src/am4utils/stubs/_core/__init__.pyi index 4c2b485..f7cd06b 100644 --- a/src/am4utils/stubs/_core/__init__.pyi +++ b/src/am4utils/stubs/_core/__init__.pyi @@ -13,4 +13,4 @@ __all__ = [ ] -__version__ = '0.1.2' +__version__ = '0.1.3' diff --git a/src/am4utils/stubs/_core/aircraft/__init__.pyi b/src/am4utils/stubs/_core/aircraft/__init__.pyi index 14a9d9d..7d95d1b 100644 --- a/src/am4utils/stubs/_core/aircraft/__init__.pyi +++ b/src/am4utils/stubs/_core/aircraft/__init__.pyi @@ -4,7 +4,6 @@ import typing __all__ = [ "Aircraft", - "AircraftNotFoundException", "CargoConfig", "PaxConfig", "PurchasedAircraft" @@ -12,6 +11,82 @@ __all__ = [ class Aircraft(): + class ParseResult(): + def __init__(self, arg0: Aircraft.SearchType, arg1: str) -> None: ... + @property + def search_str(self) -> str: + """ + :type: str + """ + @property + def search_type(self) -> Aircraft.SearchType: + """ + :type: Aircraft.SearchType + """ + pass + class SearchResult(): + def __init__(self, arg0: Aircraft, arg1: Aircraft.ParseResult) -> None: ... + @property + def ac(self) -> Aircraft: + """ + :type: Aircraft + """ + @property + def parse_result(self) -> Aircraft.ParseResult: + """ + :type: Aircraft.ParseResult + """ + pass + class SearchType(): + """ + Members: + + ALL + + ID + + SHORTNAME + + NAME + """ + def __eq__(self, other: object) -> bool: ... + def __getstate__(self) -> int: ... + def __hash__(self) -> int: ... + def __index__(self) -> int: ... + def __init__(self, value: int) -> None: ... + def __int__(self) -> int: ... + def __ne__(self, other: object) -> bool: ... + def __repr__(self) -> str: ... + def __setstate__(self, state: int) -> None: ... + @property + def name(self) -> str: + """ + :type: str + """ + @property + def value(self) -> int: + """ + :type: int + """ + ALL: am4utils._core.aircraft.Aircraft.SearchType # value = + ID: am4utils._core.aircraft.Aircraft.SearchType # value = + NAME: am4utils._core.aircraft.Aircraft.SearchType # value = + SHORTNAME: am4utils._core.aircraft.Aircraft.SearchType # value = + __members__: dict # value = {'ALL': , 'ID': , 'SHORTNAME': , 'NAME': } + pass + class Suggestion(): + def __init__(self, arg0: Aircraft, arg1: float) -> None: ... + @property + def ac(self) -> Aircraft: + """ + :type: Aircraft + """ + @property + def score(self) -> float: + """ + :type: float + """ + pass class Type(): """ Members: @@ -46,18 +121,11 @@ class Aircraft(): VIP: am4utils._core.aircraft.Aircraft.Type # value = __members__: dict # value = {'PAX': , 'CARGO': , 'VIP': } pass - def __init__(self) -> None: ... def __repr__(self) -> str: ... @staticmethod - def _from_all(s: str, priority: int = 0) -> Aircraft: ... - @staticmethod - def _from_id(id: int, priority: int = 0) -> Aircraft: ... - @staticmethod - def _from_name(s: str, priority: int = 0) -> Aircraft: ... + def search(s: str) -> Aircraft.SearchResult: ... @staticmethod - def _from_shortname(s: str, priority: int = 0) -> Aircraft: ... - @staticmethod - def from_auto(s: str) -> Aircraft: ... + def suggest(s: Aircraft.ParseResult) -> typing.List[Aircraft.Suggestion]: ... @property def capacity(self) -> int: """ @@ -189,8 +257,6 @@ class Aircraft(): :type: int """ pass -class AircraftNotFoundException(Exception, BaseException): - pass class CargoConfig(): class Algorithm(): """ @@ -198,7 +264,7 @@ class CargoConfig(): L - HL + H NONE """ @@ -221,12 +287,11 @@ class CargoConfig(): """ :type: int """ - HL: am4utils._core.aircraft.CargoConfig.Algorithm # value = + H: am4utils._core.aircraft.CargoConfig.Algorithm # value = L: am4utils._core.aircraft.CargoConfig.Algorithm # value = NONE: am4utils._core.aircraft.CargoConfig.Algorithm # value = - __members__: dict # value = {'L': , 'HL': , 'NONE': } + __members__: dict # value = {'L': , 'H': , 'NONE': } pass - def __init__(self) -> None: ... @property def algorithm(self) -> CargoConfig.Algorithm: """ @@ -295,7 +360,6 @@ class PaxConfig(): YJF: am4utils._core.aircraft.PaxConfig.Algorithm # value = __members__: dict # value = {'FJY': , 'FYJ': , 'JFY': , 'JYF': , 'YJF': , 'YFJ': , 'NONE': } pass - def __init__(self) -> None: ... @property def algorithm(self) -> PaxConfig.Algorithm: """ @@ -324,7 +388,6 @@ class PaxConfig(): pass class PurchasedAircraft(Aircraft): class Config(): - def __init__(self) -> None: ... @property def cargo_config(self) -> CargoConfig: """ @@ -336,7 +399,7 @@ class PurchasedAircraft(Aircraft): :type: PaxConfig """ pass - def __init__(self) -> None: ... + def __repr__(self) -> str: ... @property def config(self) -> PurchasedAircraft.Config: """ diff --git a/src/am4utils/stubs/_core/airport/__init__.pyi b/src/am4utils/stubs/_core/airport/__init__.pyi index 295643b..ecd6fff 100644 --- a/src/am4utils/stubs/_core/airport/__init__.pyi +++ b/src/am4utils/stubs/_core/airport/__init__.pyi @@ -3,26 +3,95 @@ import am4utils._core.airport import typing __all__ = [ - "Airport", - "AirportNotFoundException" + "Airport" ] class Airport(): - def __init__(self) -> None: ... + class ParseResult(): + def __init__(self, arg0: Airport.SearchType, arg1: str) -> None: ... + @property + def search_str(self) -> str: + """ + :type: str + """ + @property + def search_type(self) -> Airport.SearchType: + """ + :type: Airport.SearchType + """ + pass + class SearchResult(): + def __init__(self, arg0: Airport, arg1: Airport.ParseResult) -> None: ... + @property + def ap(self) -> Airport: + """ + :type: Airport + """ + @property + def parse_result(self) -> Airport.ParseResult: + """ + :type: Airport.ParseResult + """ + pass + class SearchType(): + """ + Members: + + ALL + + IATA + + ICAO + + NAME + + ID + """ + def __eq__(self, other: object) -> bool: ... + def __getstate__(self) -> int: ... + def __hash__(self) -> int: ... + def __index__(self) -> int: ... + def __init__(self, value: int) -> None: ... + def __int__(self) -> int: ... + def __ne__(self, other: object) -> bool: ... + def __repr__(self) -> str: ... + def __setstate__(self, state: int) -> None: ... + @property + def name(self) -> str: + """ + :type: str + """ + @property + def value(self) -> int: + """ + :type: int + """ + ALL: am4utils._core.airport.Airport.SearchType # value = + IATA: am4utils._core.airport.Airport.SearchType # value = + ICAO: am4utils._core.airport.Airport.SearchType # value = + ID: am4utils._core.airport.Airport.SearchType # value = + NAME: am4utils._core.airport.Airport.SearchType # value = + __members__: dict # value = {'ALL': , 'IATA': , 'ICAO': , 'NAME': , 'ID': } + pass + class Suggestion(): + def __init__(self, arg0: Airport, arg1: float) -> None: ... + @property + def ap(self) -> Airport: + """ + :type: Airport + """ + @property + def score(self) -> float: + """ + :type: float + """ + pass def __repr__(self) -> str: ... @staticmethod - def _from_all(s: str) -> Airport: ... + def search(s: str) -> Airport.SearchResult: ... @staticmethod - def _from_iata(s: str) -> Airport: ... - @staticmethod - def _from_icao(s: str) -> Airport: ... - @staticmethod - def _from_id(id: int) -> Airport: ... - @staticmethod - def _from_name(s: str) -> Airport: ... - @staticmethod - def from_auto(s: str) -> Airport: ... + def suggest(s: Airport.ParseResult) -> typing.List[Airport.Suggestion]: ... @property def continent(self) -> str: """ @@ -94,5 +163,3 @@ class Airport(): :type: bool """ pass -class AirportNotFoundException(Exception, BaseException): - pass diff --git a/src/am4utils/stubs/_core/demand/__init__.pyi b/src/am4utils/stubs/_core/demand/__init__.pyi index b47457d..6e6d601 100644 --- a/src/am4utils/stubs/_core/demand/__init__.pyi +++ b/src/am4utils/stubs/_core/demand/__init__.pyi @@ -9,7 +9,6 @@ __all__ = [ class CargoDemand(): - def __init__(self) -> None: ... def __repr__(self) -> str: ... @property def h(self) -> int: @@ -23,7 +22,6 @@ class CargoDemand(): """ pass class PaxDemand(): - def __init__(self) -> None: ... def __repr__(self) -> str: ... @property def f(self) -> int: diff --git a/src/am4utils/stubs/_core/route/__init__.pyi b/src/am4utils/stubs/_core/route/__init__.pyi index c1ed814..a32f0c9 100644 --- a/src/am4utils/stubs/_core/route/__init__.pyi +++ b/src/am4utils/stubs/_core/route/__init__.pyi @@ -4,6 +4,8 @@ import typing import am4utils._core.aircraft import am4utils._core.airport import am4utils._core.user +import am4utils._core.ticket +import am4utils._core.demand __all__ = [ "Route" @@ -11,25 +13,20 @@ __all__ = [ class Route(): - def __init__(self) -> None: ... def __repr__(self) -> str: ... @staticmethod - def create_optimal_cargo_ticket(distance: float, game_mode: am4utils._core.user.GameMode) -> CargoTicket: ... - @staticmethod - def create_optimal_pax_ticket(distance: float, game_mode: am4utils._core.user.GameMode) -> PaxTicket: ... - @staticmethod def from_airports(ap1: am4utils._core.airport.Airport, ap2: am4utils._core.airport.Airport) -> Route: ... @staticmethod - def from_airports_with_aircraft(ap1: am4utils._core.airport.Airport, ap2: am4utils._core.airport.Airport, ac: am4utils._core.aircraft.Aircraft, trips_per_day: int = 1, game_mode: am4utils._core.user.GameMode = GameMode.EASY) -> Route: ... + def from_airports_with_aircraft(ap1: am4utils._core.airport.Airport, ap2: am4utils._core.airport.Airport, ac: am4utils._core.aircraft.Aircraft, trips_per_day: int = 1, game_mode: am4utils._core.user.GameMode = am4utils._core.user.GameMode.EASY) -> Route: ... @property def aircraft(self) -> am4utils._core.aircraft.PurchasedAircraft: """ :type: am4utils._core.aircraft.PurchasedAircraft """ @property - def cargo_demand(self) -> CargoDemand: + def cargo_demand(self) -> am4utils._core.demand.CargoDemand: """ - :type: CargoDemand + :type: am4utils._core.demand.CargoDemand """ @property def destination(self) -> am4utils._core.airport.Airport: @@ -52,14 +49,14 @@ class Route(): :type: am4utils._core.airport.Airport """ @property - def pax_demand(self) -> PaxDemand: + def pax_demand(self) -> am4utils._core.demand.PaxDemand: """ - :type: PaxDemand + :type: am4utils._core.demand.PaxDemand """ @property - def ticket(self) -> Ticket: + def ticket(self) -> am4utils._core.ticket.Ticket: """ - :type: Ticket + :type: am4utils._core.ticket.Ticket """ @property def valid(self) -> bool: diff --git a/src/am4utils/stubs/_core/ticket/__init__.pyi b/src/am4utils/stubs/_core/ticket/__init__.pyi index 0ea2b69..f996218 100644 --- a/src/am4utils/stubs/_core/ticket/__init__.pyi +++ b/src/am4utils/stubs/_core/ticket/__init__.pyi @@ -1,6 +1,7 @@ from __future__ import annotations import am4utils._core.ticket import typing +import am4utils._core.user __all__ = [ "CargoTicket", @@ -11,8 +12,9 @@ __all__ = [ class CargoTicket(): - def __init__(self) -> None: ... def __repr__(self) -> str: ... + @staticmethod + def from_optimal(distance: float, game_mode: am4utils._core.user.GameMode = am4utils._core.user.GameMode.EASY) -> CargoTicket: ... @property def h(self) -> float: """ @@ -25,8 +27,9 @@ class CargoTicket(): """ pass class PaxTicket(): - def __init__(self) -> None: ... def __repr__(self) -> str: ... + @staticmethod + def from_optimal(distance: float, game_mode: am4utils._core.user.GameMode = am4utils._core.user.GameMode.EASY) -> PaxTicket: ... @property def f(self) -> int: """ @@ -44,7 +47,6 @@ class PaxTicket(): """ pass class Ticket(): - def __init__(self) -> None: ... @property def cargo_ticket(self) -> CargoTicket: """ @@ -62,8 +64,9 @@ class Ticket(): """ pass class VIPTicket(): - def __init__(self) -> None: ... def __repr__(self) -> str: ... + @staticmethod + def from_optimal(distance: float) -> VIPTicket: ... @property def f(self) -> int: """ diff --git a/src/am4utils/tests/test_all.py b/src/am4utils/tests/test_all.py index 4c62e85..8b33004 100644 --- a/src/am4utils/tests/test_all.py +++ b/src/am4utils/tests/test_all.py @@ -2,10 +2,10 @@ import am4utils from am4utils.aircraft import ( - Aircraft, AircraftNotFoundException, + Aircraft, PaxConfig, CargoConfig ) -from am4utils.airport import Airport, AirportNotFoundException +from am4utils.airport import Airport from am4utils.route import Route am4utils.db.init(am4utils.__path__[0]) @@ -16,19 +16,21 @@ 'shortname:b744', 'name:B747-400' ]) -def test_aircraft_auto(inp): - a0 = Aircraft.from_auto(inp) - assert a0.shortname == "b744" +def test_aircraft_search(inp): + a0 = Aircraft.search(inp) + assert a0.ac.valid + assert a0.ac.shortname == "b744" @pytest.mark.parametrize("inp", [ - 'b745', - 'shortname:b745' - 'name:B747-500' + 'b7440', + 'shortname:b7440' + 'name:B747-4000' ]) -def test_aircraft_fail(inp): - with pytest.raises(AircraftNotFoundException): - _a = Aircraft.from_auto(inp) - +def test_aircraft_fail_and_suggest(inp): + a0 = Aircraft.search(inp) + assert not a0.ac.valid + suggs = Aircraft.suggest(a0.parse_result) + assert suggs[0].ac.shortname == "b744" ## airport tests @pytest.mark.parametrize("inp", [ @@ -38,37 +40,41 @@ def test_aircraft_fail(inp): 'name:hong kong', 'hong kong' ]) -def test_airport_auto(inp): - a0 = Airport.from_auto(inp) - assert a0.iata == "HKG" +def test_airport_search(inp): + a0 = Airport.search(inp) + assert a0.ap.valid + assert a0.ap.iata == "HKG" @pytest.mark.parametrize("inp", [ - "VHHX", - "iata:VHHX", - "icao:VHHX", + "VHHX ", + "iata:hkgA", + "icao:VHHx", + "name:hng kong", ]) -def test_airport_fail(inp): - with pytest.raises(AirportNotFoundException): - _a = Airport.from_auto(inp) +def test_airport_fail_and_suggest(inp): + a0 = Airport.search(inp) + assert not a0.ap.valid + suggs = Airport.suggest(a0.parse_result) + assert suggs[0].ap.iata == "HKG" ## route tests def test_route(): - a0 = Airport.from_auto('VHHH') - a1 = Airport.from_auto('LHR') + a0 = Airport.search('VHHH').ap + a1 = Airport.search('LHR').ap r = Route.from_airports(a0, a1) assert int(r.direct_distance) == 9630 assert r.pax_demand.y == 1093 def test_invalid_route_to_self(): - a0 = Airport.from_auto('VHHH') + a0 = Airport.search('VHHH').ap with pytest.raises(ValueError): _r = Route.from_airports(a0, a0) def test_route_with_aircraft(): - ap0 = Airport.from_auto('VHHH') - ap1 = Airport.from_auto('LHR') - ac = Aircraft.from_auto('b744') + ap0 = Airport.search('VHHH').ap + ap1 = Airport.search('LHR').ap + ac = Aircraft.search('b744').ac r = Route.from_airports_with_aircraft(ap0, ap1, ac) assert int(r.direct_distance) == 9630 assert r.pax_demand.y == 1093 @@ -78,7 +84,7 @@ def test_route_with_aircraft(): assert cfg.f == 128 assert cfg.algorithm == PaxConfig.Algorithm.FJY - ap2 = Airport.from_auto('MTR') + ap2 = Airport.search('MTR').ap r = Route.from_airports_with_aircraft(ap0, ap2, ac) assert int(r.direct_distance) == 16394 assert r.pax_demand.y == 303 @@ -86,4 +92,25 @@ def test_route_with_aircraft(): assert cfg.y == 303 assert cfg.j == 56 assert cfg.f == 1 - assert cfg.algorithm == PaxConfig.Algorithm.YJF \ No newline at end of file + assert cfg.algorithm == PaxConfig.Algorithm.YJF + +def test_cargo_route_with_aircraft(): + ap0 = Airport.search('VHHH').ap + ap1 = Airport.search('LHR').ap + ac = Aircraft.search('b744f').ac + r = Route.from_airports_with_aircraft(ap0, ap1, ac) + assert r.cargo_demand.l == 547000 + assert r.cargo_demand.h == 681000 + cfg = r.aircraft.config.cargo_config + assert cfg.l == 100 + assert cfg.h == 0 + assert cfg.algorithm == CargoConfig.Algorithm.L + + ap1 = Airport.search('BPC').ap + r = Route.from_airports_with_aircraft(ap0, ap1, ac) + assert r.cargo_demand.l == 148000 + assert r.cargo_demand.h == 220000 + cfg = r.aircraft.config.cargo_config + assert cfg.l == 70 + assert cfg.h == 30 + assert cfg.algorithm == CargoConfig.Algorithm.L \ No newline at end of file