Commit d0b98cd0 authored by Volker Krause's avatar Volker Krause
Browse files

Filter out combined train reservation elements if we have individual legs

This happens if we have sources with different levels of detail for the
same trip. Examples for this are RCT2 tickets or many vendor-provided
ical attachments.
parent 01fdb499
[
{
"@context": "http://schema.org",
"@type": "TrainReservation",
"reservationFor": {
"@type": "TrainTrip",
"arrivalStation": {
"@type": "TrainStation",
"name": "Köln Hbf"
},
"arrivalTime": "2014-01-24T12:09:00",
"departureDay": "2014-01-24",
"departureStation": {
"@type": "TrainStation",
"name": "Berlin Hbf"
},
"departureTime": {
"@type": "QDateTime",
"@value": "2014-01-24T07:46:00+01:00",
"timezone": "Europe/Berlin"
},
"trainNumber": "ICE 123"
},
"reservationNumber": "XXX007",
"reservationStatus": "http://schema.org/ReservationConfirmed"
},
{
"@context": "http://schema.org",
"@type": "TrainReservation",
"reservationFor": {
"@type": "TrainTrip",
"arrivalStation": {
"@type": "TrainStation",
"name": "Bonn Hbf"
},
"arrivalTime": {
"@type": "QDateTime",
"@value": "2014-01-24T13:45:00+01:00",
"timezone": "Europe/Berlin"
},
"departureDay": "2014-01-24",
"departureStation": {
"@type": "TrainStation",
"name": "Köln Hbf"
},
"departureTime": "2014-01-24T12:17:00",
"trainNumber": "RB42"
},
"reservationNumber": "XXX007",
"reservationStatus": "http://schema.org/ReservationConfirmed"
}
]
[
{
"@context": "http://schema.org",
"@type": "TrainReservation",
"reservationFor": {
"@type": "TrainTrip",
"arrivalStation": {
"@type": "TrainStation",
"name": "Bonn Hbf"
},
"arrivalTime": {
"@type": "QDateTime",
"@value": "2014-01-24T13:45:00+01:00",
"timezone": "Europe/Berlin"
},
"departureDay": "2014-01-24",
"departureStation": {
"@type": "TrainStation",
"name": "Berlin Hbf"
},
"departureTime": {
"@type": "QDateTime",
"@value": "2014-01-24T07:46:00+01:00",
"timezone": "Europe/Berlin"
}
},
"reservationStatus": "http://schema.org/ReservationConfirmed"
},
{
"@context": "http://schema.org",
"@type": "TrainReservation",
"reservationFor": {
"@type": "TrainTrip",
"arrivalStation": {
"@type": "TrainStation",
"name": "Köln Hbf"
},
"arrivalTime": "2014-01-24T12:09:00",
"departureDay": "2014-01-24",
"departureStation": {
"@type": "TrainStation",
"name": "Berlin Hbf"
},
"departureTime": {
"@type": "QDateTime",
"@value": "2014-01-24T07:46:00+01:00",
"timezone": "Europe/Berlin"
},
"trainNumber": "ICE 123"
},
"reservationNumber": "XXX007",
"reservationStatus": "http://schema.org/ReservationConfirmed"
},
{
"@context": "http://schema.org",
"@type": "TrainReservation",
"reservationFor": {
"@type": "TrainTrip",
"arrivalStation": {
"@type": "TrainStation",
"name": "Bonn Hbf"
},
"arrivalTime": {
"@type": "QDateTime",
"@value": "2014-01-24T13:45:00+01:00",
"timezone": "Europe/Berlin"
},
"departureDay": "2014-01-24",
"departureStation": {
"@type": "TrainStation",
"name": "Köln Hbf"
},
"departureTime": "2014-01-24T12:17:00",
"trainNumber": "RB42"
},
"reservationNumber": "XXX007",
"reservationStatus": "http://schema.org/ReservationConfirmed"
}
]
......@@ -131,10 +131,40 @@ void ExtractorPostprocessor::process(const QVector<QVariant> &data)
QVector<QVariant> ExtractorPostprocessor::result() const
{
if (!d->m_resultFinalized && d->m_validationEnabled) {
d->m_data.erase(std::remove_if(d->m_data.begin(), d->m_data.end(), [this](const auto &elem) {
return !d->m_validator.isValidElement(elem);
}), d->m_data.end());
if (!d->m_resultFinalized) {
if (d->m_validationEnabled) {
d->m_data.erase(std::remove_if(d->m_data.begin(), d->m_data.end(), [this](const auto &elem) {
return !d->m_validator.isValidElement(elem);
}), d->m_data.end());
}
// search for "triangular" patterns, ie. a location change element that has a matching departure
// and matching arrival to two different other location change elements (A->C vs A->B + B->C).
// we remove those, as the fine-granular results are better
if (d->m_data.size() >= 3) {
for (auto it = d->m_data.begin(); it != d->m_data.end();) {
auto depIt = it;
auto arrIt = it;
for (auto it2 = d->m_data.begin(); it2 != d->m_data.end(); ++it2) {
if (it == it2) {
continue;
}
if (MergeUtil::hasSameDeparture(*it, *it2)) {
depIt = it2;
}
if (MergeUtil::hasSameArrival(*it, *it2)) {
arrIt = it2;
}
}
if (depIt != it && arrIt != it && depIt != arrIt) {
it = d->m_data.erase(it);
} else {
++it;
}
}
}
d->m_resultFinalized = true;
}
......
......@@ -74,9 +74,28 @@ static bool isSameTouristAttraction(const TouristAttraction &lhs, const TouristA
static bool isSameEvent(const Event &lhs, const Event &rhs);
static bool isSameRentalCar(const RentalCar &lhs, const RentalCar &rhs);
static bool isSameTaxiTrip(const Taxi &lhs, const Taxi &rhs);
static bool isSameReservation(const Reservation &lhsRes, const Reservation &rhsRes);
static bool isMinimalCancelationFor(const QVariant &r, const Reservation &cancel);
static bool isSameTicketToken(const QVariant &lhs, const QVariant &rhs);
bool isSameReservation(const Reservation &lhsRes, const Reservation &rhsRes)
{
// underName either matches or is not set
const auto lhsUN = lhsRes.underName().value<Person>();
const auto rhsUN = rhsRes.underName().value<Person>();
if (!lhsUN.name().isEmpty() && !rhsUN.name().isEmpty() && !MergeUtil::isSamePerson(lhsUN, rhsUN)) {
return false;
}
const auto lhsTicket = lhsRes.reservedTicket().value<Ticket>();
const auto rhsTicket = rhsRes.reservedTicket().value<Ticket>();
if (conflictIfPresent(lhsTicket.ticketedSeat().seatNumber(), rhsTicket.ticketedSeat().seatNumber(), Qt::CaseInsensitive)) {
return false;
}
return true;
}
bool MergeUtil::isSame(const QVariant& lhs, const QVariant& rhs)
{
if (lhs.isNull() || rhs.isNull()) {
......@@ -90,19 +109,12 @@ bool MergeUtil::isSame(const QVariant& lhs, const QVariant& rhs)
if (JsonLd::canConvert<Reservation>(lhs)) {
const auto lhsRes = JsonLd::convert<Reservation>(lhs);
const auto rhsRes = JsonLd::convert<Reservation>(rhs);
// for all: underName either matches or is not set
const auto lhsUN = lhsRes.underName().value<Person>();
const auto rhsUN = rhsRes.underName().value<Person>();
if (!lhsUN.name().isEmpty() && !rhsUN.name().isEmpty() && !isSamePerson(lhsUN, rhsUN)) {
if (!isSameReservation(lhsRes, rhsRes)) {
return false;
}
const auto lhsTicket = lhsRes.reservedTicket().value<Ticket>();
const auto rhsTicket = rhsRes.reservedTicket().value<Ticket>();
if (conflictIfPresent(lhsTicket.ticketedSeat().seatNumber(), rhsTicket.ticketedSeat().seatNumber(), Qt::CaseInsensitive)) {
return false;
}
// flight ticket tokens (IATA BCBP) can differ, so we need to compare the relevant bits in them manually
// this however happens automatically as they are unpacked to other fields by post-processing
// so we can simply skip this here for flights
......@@ -602,3 +614,31 @@ bool isSameTicketToken(const QVariant &lhs, const QVariant &rhs)
qCWarning(CompareLog) << "unhandled ticket token type" << lhs << rhs;
return false;
}
bool MergeUtil::hasSameDeparture(const QVariant &lhs, const QVariant &rhs)
{
if (lhs.userType() != rhs.userType() || !JsonLd::isA<TrainReservation>(lhs)) {
return false;
}
const auto lhsRes = JsonLd::convert<Reservation>(lhs);
const auto rhsRes = JsonLd::convert<Reservation>(rhs);
if (!isSameReservation(lhsRes, rhsRes) || SortUtil::startDateTime(lhs) != SortUtil::startDateTime(rhs)) {
return false;
}
return LocationUtil::isSameLocation(LocationUtil::departureLocation(lhs), LocationUtil::departureLocation(rhs), LocationUtil::Exact);
}
bool MergeUtil::hasSameArrival(const QVariant &lhs, const QVariant &rhs)
{
if (lhs.userType() != rhs.userType() || !JsonLd::isA<TrainReservation>(lhs)) {
return false;
}
const auto lhsRes = JsonLd::convert<Reservation>(lhs);
const auto rhsRes = JsonLd::convert<Reservation>(rhs);
if (!isSameReservation(lhsRes, rhsRes) || SortUtil::endDateTime(lhs) != SortUtil::endDateTime(rhs)) {
return false;
}
return LocationUtil::isSameLocation(LocationUtil::arrivalLocation(lhs), LocationUtil::arrivalLocation(rhs), LocationUtil::Exact);
}
......@@ -39,12 +39,25 @@ KITINERARY_EXPORT bool isSame(const QVariant &lhs, const QVariant &rhs);
*/
KITINERARY_EXPORT bool isSamePerson(const Person &lhs, const Person &rhs);
/**
* Checks whether two transport reservation elements refer to the same departure.
* This considers time, location and mode of transport.
*/
bool hasSameDeparture(const QVariant &lhs, const QVariant &rhs);
/**
* Checks whether two transport reservation elements refer to the same arrival.
* This considers time, location and mode of transport.
*/
bool hasSameArrival(const QVariant &lhs, const QVariant &rhs);
/**
* Merge the two given objects.
* This is the same as JsonLdDocument::apply in most cases, but if one side
* can be determined to be "better", that one is preferred.
*/
KITINERARY_EXPORT QVariant merge(const QVariant &lhs, const QVariant &rhs);
}
}
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment