[generator] add FMD_DURATION and tests

This commit is contained in:
Mikhail Gorbushin 2019-08-22 13:59:40 +03:00 committed by Vladimir Byko-Ianko
parent 3824e3ca42
commit 96d3a20182
6 changed files with 174 additions and 1 deletions

View file

@ -201,3 +201,59 @@ UNIT_TEST(Metadata_ValidateAndFormat_wikipedia)
#undef WIKIHOST
}
UNIT_TEST(Metadata_ValidateAndFormat_duration)
{
FeatureParams params;
MetadataTagProcessor p(params);
Metadata & md = params.GetMetadata();
auto const test = [&](std::string const & osm, std::string const & expected) {
p("duration", osm);
if (expected.empty())
{
TEST(md.Empty(), ());
}
else
{
TEST_EQUAL(md.Get(Metadata::FMD_DURATION), expected, ());
md.Drop(Metadata::FMD_DURATION);
}
};
test("10", "0.16667");
test("10:00", "10");
test("QWE", "");
test("1:1:1", "1.0169");
test("10:30", "10.5");
test("30", "0.5");
test("60", "1");
test("120", "2");
test("35:10", "35.167");
test("35::10", "");
test("", "");
test("0", "");
test("asd", "");
test("10 minutes", "");
test("01:15 h", "");
test("08:00;07:00;06:30", "");
test("3-4 minutes", "");
test("5:00 hours", "");
test("12 min", "");
test("PT20S", "0.0055556");
test("PT7M", "0.11667");
test("PT10M40S", "0.17778");
test("PT50M", "0.83333");
test("PT2H", "2");
test("PT7H50M", "7.8333");
test("PT60M", "1");
test("PT15M", "0.25");
test("PT1000Y", "");
test("PTPT", "");
test("P4D", "");
test("PT50:20", "");
}

View file

@ -7,17 +7,20 @@
#include "coding/url_encode.hpp"
#include "base/logging.hpp"
#include "base/math.hpp"
#include "base/string_utils.hpp"
#include <algorithm>
#include <array>
#include <cctype>
#include <cmath>
#include <cstdlib>
#include <iostream>
#include <sstream>
#include <unordered_map>
#include <unordered_set>
#include "boost/optional.hpp"
using namespace std;
namespace
@ -284,3 +287,110 @@ string MetadataTagProcessorImpl::ValidateAndFormat_airport_iata(string const & v
}
return str;
}
string MetadataTagProcessorImpl::ValidateAndFormat_duration(string const & v) const
{
auto const format = [](double hours) -> std::string {
if (base::AlmostEqualAbs(hours, 0.0, 1e-5))
return "";
std::stringstream ss;
ss << std::setprecision(5);
ss << hours;
return ss.str();
};
auto const readNumber = [&v](size_t startPos, size_t & endPos) -> boost::optional<uint32_t> {
uint32_t number = 0;
while (startPos < v.size() && isdigit(v[startPos]))
{
number *= 10;
number += v[startPos] - '0';
++startPos;
}
if (startPos == endPos)
return {};
endPos = startPos;
return {number};
};
auto const convert = [](char type, uint32_t number) -> boost::optional<double> {
switch (type)
{
case 'H': return number;
case 'M': return number / 60.0;
case 'S': return number / 3600.0;
}
return {};
};
if (v.empty())
return {};
double hours = 0;
size_t pos = 0;
boost::optional<uint32_t> op;
if (v[0] == 'P')
{
if (v.size() < 4)
return {};
if (v.substr(0, 2) != "PT")
return {};
pos = 2;
while (pos < v.size() && (op = readNumber(pos, pos)))
{
if (pos >= v.size())
return {};
char const type = v[pos];
auto const addHours = convert(type, *op);
if (addHours)
hours += *addHours;
else
return {};
++pos;
}
if (!op)
return {};
return format(hours);
}
// "hh:mm:ss" or just "mm"
vector<uint32_t> numbers;
while (pos < v.size() && (op = readNumber(pos, pos)))
{
numbers.emplace_back(*op);
if (pos >= v.size())
break;
if (v[pos] != ':')
return {};
++pos;
}
if (numbers.size() > 3 || !op)
return {};
if (numbers.size() == 1)
return format(numbers.back() / 60.0);
double pow = 1;
for (auto number : numbers)
{
hours += number / pow;
pow *= 60;
}
return format(hours);
}

View file

@ -34,6 +34,7 @@ struct MetadataTagProcessorImpl
std::string ValidateAndFormat_denomination(std::string const & v) const;
std::string ValidateAndFormat_wikipedia(std::string v) const;
std::string ValidateAndFormat_airport_iata(std::string const & v) const;
std::string ValidateAndFormat_duration(std::string const & v) const;
protected:
FeatureParams & m_params;
@ -96,6 +97,7 @@ public:
case Metadata::FMD_BANNER_URL: valid = ValidateAndFormat_url(v); break;
case Metadata::FMD_LEVEL: valid = ValidateAndFormat_level(v); break;
case Metadata::FMD_AIRPORT_IATA: valid = ValidateAndFormat_airport_iata(v); break;
case Metadata::FMD_DURATION: valid = ValidateAndFormat_duration(v); break;
// Metadata types we do not get from OSM.
case Metadata::FMD_SPONSORED_ID:
case Metadata::FMD_PRICE_RATE:

View file

@ -94,6 +94,8 @@ bool Metadata::TypeFromString(string const & k, Metadata::EType & outType)
outType = Metadata::FMD_LEVEL;
else if (k == "iata")
outType = Metadata::FMD_AIRPORT_IATA;
else if (k == "duration")
outType = Metadata::FMD_DURATION;
else
return false;
@ -193,6 +195,7 @@ string ToString(Metadata::EType type)
case Metadata::FMD_LEVEL: return "level";
case Metadata::FMD_AIRPORT_IATA: return "iata";
case Metadata::FMD_BRAND: return "brand";
case Metadata::FMD_DURATION: return "duration";
case Metadata::FMD_COUNT: CHECK(false, ("FMD_COUNT can not be used as a type."));
};

View file

@ -133,6 +133,7 @@ public:
FMD_LEVEL = 28,
FMD_AIRPORT_IATA = 29,
FMD_BRAND = 30,
FMD_DURATION = 31,
FMD_COUNT
};

View file

@ -167,6 +167,7 @@ std::vector<Props> MetadataToProps(std::vector<T> const & metadata)
case Metadata::FMD_BANNER_URL:
case Metadata::FMD_AIRPORT_IATA:
case Metadata::FMD_BRAND:
case Metadata::FMD_DURATION:
case Metadata::FMD_COUNT:
break;
// Please add new cases when compiler issues an "unhandled switch case" warning here.