Skip to content

Commit

Permalink
Implement the v2/summary endpoint
Browse files Browse the repository at this point in the history
Fixes #177

This endpoint does exactly the same calculation as the `v2/route`
endpoint. Only the result converter is changed.

The ResultToV2SummaryResponse converter takes the routing result and
creates the json response with the details for each line used. It also
calculates the number of times a line is encountered in an alternative.
  • Loading branch information
tahini authored and greenscientist committed Sep 28, 2022
1 parent ee50e74 commit 50fa6d3
Show file tree
Hide file tree
Showing 6 changed files with 384 additions and 0 deletions.
24 changes: 24 additions & 0 deletions connection_scan_algorithm/include/result_to_v2_summary.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#ifndef TR_RESULT_TO_V2_SUMMARY_RESPONSE
#define TR_RESULT_TO_V2_SUMMARY_RESPONSE

#include "json.hpp"
#include "routing_result.hpp"

namespace TrRouting
{

class RouteParameters;

/**
* @brief Convert a result object to a json object for the version 2 trRouting summary API
*/
class ResultToV2SummaryResponse {
public:
static nlohmann::json resultToJsonString(RoutingResult& result, RouteParameters& params);
static nlohmann::json noRoutingFoundResponse(RouteParameters& params, NoRoutingReason noRoutingReason);
};

}


#endif // TR_RESULT_TO_V2_SUMMARY_RESPONSE
1 change: 1 addition & 0 deletions connection_scan_algorithm/src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@ libcsa_la_SOURCES = alternatives_routing.cpp \
route_parameters.cpp \
result_to_v1.cpp \
result_to_v2.cpp \
result_to_v2_summary.cpp \
transit_routing_http_server.cpp
198 changes: 198 additions & 0 deletions connection_scan_algorithm/src/result_to_v2_summary.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
#include "json.hpp"
#include "constants.hpp"
#include "result_to_v2_summary.hpp"
#include "toolbox.hpp"
#include "parameters.hpp"
#include "routing_result.hpp"


namespace TrRouting
{
// FIXME See issue #180, this class when it's fixed, we can use the line objects directly
class LineSummary {
public:
std::string agencyUuid;
std::string agencyAcronym;
std::string agencyName;
std::string lineUuid;
std::string lineShortname;
std::string lineLongname;
int count;
LineSummary(
std::string _agencyUuid,
std::string _agencyAcronym,
std::string _agencyName,
std::string _lineUuid,
std::string _lineShortname,
std::string _lineLongname
): agencyUuid(_agencyUuid),
agencyAcronym(_agencyAcronym),
agencyName(_agencyName),
lineUuid(_lineUuid),
lineShortname(_lineShortname),
lineLongname(_lineLongname)
{
count = 1;
}

LineSummary(const LineSummary& obj)
{
agencyUuid = obj.agencyUuid;
agencyAcronym = obj.agencyAcronym;
agencyName = obj.agencyName;
lineUuid = obj.lineUuid;
lineShortname = obj.lineShortname;
lineLongname = obj.lineLongname;
count = obj.count;
}
};

/**
* @brief Visitor for the result's steps
*/
class StepToV2SummaryVisitor: public StepVisitor<std::optional<LineSummary>> {
private:
std::optional<LineSummary> response;
public:
std::optional<LineSummary> getResult() { return response; }
void visitBoardingStep(const BoardingStep& step) override;
void visitUnboardingStep(const UnboardingStep& step) override;
void visitWalkingStep(const WalkingStep& step) override;
};

/**
* @brief Visitor for the result object, returns a map of line uuid to LineSummary
*/
class ResultToV2SummaryVisitor: public ResultVisitor<std::map<std::string, LineSummary>> {
private:
std::map<std::string, LineSummary> response;
RouteParameters& params;
public:
ResultToV2SummaryVisitor(RouteParameters& _params): params(_params) {
// Nothing to initialize
}
std::map<std::string, LineSummary> getResult() { return response; }
void visitSingleCalculationResult(const SingleCalculationResult& result) override;
void visitAlternativesResult(const AlternativesResult& result) override;
void visitAllNodesResult(const AllNodesResult& result) override;
};

/**
* @brief Visitor for the result object, returns a map of line uuid to LineSummary
*/
class CountAlternativesResultVisitor: public ResultVisitor<int> {
private:
int response;
public:
CountAlternativesResultVisitor(): response(0) {
// Nothing to initialize
}
int getResult() { return response; }
void visitSingleCalculationResult(const SingleCalculationResult& result) {
response = 1;
}
void visitAlternativesResult(const AlternativesResult& result) {
response = result.alternatives.size();
}
void visitAllNodesResult(const AllNodesResult& result) {
// Nothing to do
}
};

void StepToV2SummaryVisitor::visitBoardingStep(const BoardingStep& step)
{
response.emplace(LineSummary(
boost::uuids::to_string(step.agencyUuid),
step.agencyAcronym,
step.agencyName,
boost::uuids::to_string(step.lineUuid),
step.lineShortname,
step.lineLongname
));
}

void StepToV2SummaryVisitor::visitUnboardingStep(const UnboardingStep& step)
{
response.reset();
}

void StepToV2SummaryVisitor::visitWalkingStep(const WalkingStep& step)
{
response.reset();
}

void ResultToV2SummaryVisitor::visitSingleCalculationResult(const SingleCalculationResult& result)
{
// convert the steps
StepToV2SummaryVisitor stepVisitor = StepToV2SummaryVisitor();
for (auto &step : result.steps) {
std::optional<LineSummary> optSummary = step.get()->accept(stepVisitor);
if (optSummary.has_value()) {
LineSummary summary = optSummary.value();
if (response.find(summary.lineUuid) == response.end()) {
response.emplace(summary.lineUuid, LineSummary(summary));
} else {
response.at(summary.lineUuid).count++;
}
}
}
}

void ResultToV2SummaryVisitor::visitAlternativesResult(const AlternativesResult& result)
{
for (auto &alternative : result.alternatives) {
alternative.get()->accept(*this);
}
}

void ResultToV2SummaryVisitor::visitAllNodesResult(const AllNodesResult& result)
{
// TODO This type of result is not defined in v2 yet, we should not be here
}

nlohmann::json ResultToV2SummaryResponse::noRoutingFoundResponse(RouteParameters& params, NoRoutingReason noRoutingReason)
{
nlohmann::json json;
json["status"] = STATUS_SUCCESS;
json["origin"] = { params.getOrigin()->longitude, params.getOrigin()->latitude };
json["destination"] = { params.getDestination()->longitude, params.getDestination()->latitude };
json["timeOfTrip"] = params.getTimeOfTrip();
json["timeType"] = params.isForwardCalculation() ? 0 : 1;
json["nbAlternativesCalculated"] = 0;
json["lines"] = nlohmann::json::array();

return json;
}

nlohmann::json ResultToV2SummaryResponse::resultToJsonString(RoutingResult& result, RouteParameters& params)
{
// Initialize response
nlohmann::json json;
json["status"] = STATUS_SUCCESS;
json["origin"] = { params.getOrigin()->longitude, params.getOrigin()->latitude };
json["destination"] = { params.getDestination()->longitude, params.getDestination()->latitude };
json["timeOfTrip"] = params.getTimeOfTrip();
json["timeType"] = params.isForwardCalculation() ? 0 : 1;
CountAlternativesResultVisitor countVisitor = CountAlternativesResultVisitor();
json["nbAlternativesCalculated"] = result.accept(countVisitor);
json["lines"] = nlohmann::json::array();

ResultToV2SummaryVisitor visitor = ResultToV2SummaryVisitor(params);
std::map<std::string, LineSummary> summaries = result.accept(visitor);
for (auto it = summaries.begin(); it != summaries.end(); ++it) {
nlohmann::json lineJson;
LineSummary summary = it->second;
lineJson["lineUuid"] = summary.lineUuid;
lineJson["lineShortname"] = summary.lineShortname;
lineJson["lineLongname"] = summary.lineLongname;
lineJson["agencyUuid"] = summary.agencyUuid;
lineJson["agencyAcronym"] = summary.agencyAcronym;
lineJson["agencyName"] = summary.agencyName;
lineJson["alternativeCount"] = summary.count;
json["lines"].push_back(lineJson);
}

return json;

}
}
66 changes: 66 additions & 0 deletions connection_scan_algorithm/src/transit_routing_http_server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "program_options.hpp"
#include "result_to_v1.hpp"
#include "result_to_v2.hpp"
#include "result_to_v2_summary.hpp"
#include "routing_result.hpp"
#include "osrm_fetcher.hpp"

Expand Down Expand Up @@ -509,6 +510,71 @@ int main(int argc, char** argv) {

};

// Request a summary of lines data for a route
// TODO Copy pasted from v2/route. There's a lot in common, it should be extracted to common class, just the response parser is different
server.resource["^/v2/summary[/]?$"]["GET"]=[&server, &calculator, &dataStatus](std::shared_ptr<HttpServer::Response> serverResponse, std::shared_ptr<HttpServer::Request> request) {

std::string response = getFastErrorResponse(dataStatus);

if (!response.empty()) {
*serverResponse << "HTTP/1.1 200 OK\r\nAccess-Control-Allow-Origin: *\r\nContent-Type: application/json; charset=utf-8\r\nContent-Length: " << response.length() << "\r\n\r\n" << response;
return;
}

// prepare benchmarking and timer:
// TODO Shouldn't have to do this, a query is not a benchmark
calculator.algorithmCalculationTime.start();
calculator.benchmarking.clear();

// prepare parameters:
std::vector<std::pair<std::string, std::string>> parametersWithValues;
auto queryFields = request->parse_query_string();
for(auto &field : queryFields)
{
parametersWithValues.push_back(std::make_pair(field.first, field.second));
}

spdlog::debug("-- calculating request -- {}", request->path);

try
{
std::unique_ptr<TrRouting::RoutingResult> routingResult;
TrRouting::AlternativesResult alternativeResult;

RouteParameters queryParams = RouteParameters::createRouteODParameter(parametersWithValues, calculator.scenarioIndexesByUuid, calculator.scenarios);

try {
if (queryParams.isWithAlternatives())
{
alternativeResult = calculator.alternativesRouting(queryParams);
response = ResultToV2SummaryResponse::resultToJsonString(alternativeResult, queryParams).dump(2);
}
else
{
routingResult = calculator.calculate(queryParams);
if (routingResult.get() != nullptr) {
response = ResultToV2SummaryResponse::resultToJsonString(*routingResult.get(), queryParams).dump(2);
}
}

spdlog::debug("-- total -- {} microseconds", calculator.algorithmCalculationTime.getDurationMicrosecondsNoStop());

} catch (NoRoutingFoundException e) {
response = ResultToV2SummaryResponse::noRoutingFoundResponse(queryParams, e.getReason()).dump(2);
}

*serverResponse << "HTTP/1.1 200 OK\r\nAccess-Control-Allow-Origin: *\r\nContent-Type: application/json; charset=utf-8\r\nContent-Length: " << response.length() << "\r\n\r\n" << response;

} catch (ParameterException exp) {
response = "{\"status\": \"query_error\", \"errorCode\": \"" + getResponseCode(exp.getType()) + "\"}";
*serverResponse << "HTTP/1.1 400 OK\r\nAccess-Control-Allow-Origin: *\r\nContent-Type: application/json; charset=utf-8\r\nContent-Length: " << response.length() << "\r\n\r\n" << response;
} catch (...) {
response = "{\"status\": \"query_error\", \"errorCode\": \"PARAM_ERROR_UNKNOWN\"}";
*serverResponse << "HTTP/1.1 400 OK\r\nAccess-Control-Allow-Origin: *\r\nContent-Type: application/json; charset=utf-8\r\nContent-Length: " << response.length() << "\r\n\r\n" << response;
}

};

server.default_resource["GET"] = [](std::shared_ptr<HttpServer::Response> serverResponse, std::shared_ptr<HttpServer::Request> request) {
spdlog::info("calculating request: {}", request->content.string());

Expand Down
1 change: 1 addition & 0 deletions tests/connection_scan_algorithm/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ csa_test_SOURCES = gtest.cpp \
csa_result_to_response_test.cpp \
csa_result_to_v1_test.cpp \
csa_result_to_v2_test.cpp \
csa_result_to_v2_summary_test.cpp \
route_parameters_test.cpp \
combinations_test.cpp

Expand Down
Loading

0 comments on commit 50fa6d3

Please sign in to comment.