File indexing completed on 2024-06-16 04:54:23
0001 /* 0002 SPDX-FileCopyrightText: 2019 Volker Krause <vkrause@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include <config-kitinerary.h> 0008 #include <kitinerary_version.h> 0009 0010 #include <KItinerary/CalendarHandler> 0011 #include <KItinerary/ExtractorCapabilities> 0012 #include <KItinerary/ExtractorEngine> 0013 #include <KItinerary/ExtractorPostprocessor> 0014 #include <KItinerary/ExtractorRepository> 0015 #include <KItinerary/ExtractorValidator> 0016 #include <KItinerary/JsonLdDocument> 0017 #include <KItinerary/MergeUtil> 0018 #include <KItinerary/Reservation> 0019 #include <KItinerary/ScriptExtractor> 0020 0021 #include <KCalendarCore/Event> 0022 #include <KCalendarCore/ICalFormat> 0023 #include <KCalendarCore/MemoryCalendar> 0024 0025 #include <QCommandLineParser> 0026 #include <QCoreApplication> 0027 #include <QDateTime> 0028 #include <QDebug> 0029 #include <QDir> 0030 #include <QFile> 0031 #include <QJsonArray> 0032 #include <QJsonDocument> 0033 #include <QJsonObject> 0034 #include <QObject> 0035 0036 #include <iostream> 0037 0038 using namespace KItinerary; 0039 0040 static QList<QList<QVariant>> 0041 batchReservations(const QList<QVariant> &reservations) { 0042 using namespace KItinerary; 0043 0044 QList<QList<QVariant>> batches; 0045 QList<QVariant> batch; 0046 0047 for (const auto &res : reservations) { 0048 if (batch.isEmpty()) { 0049 batch.push_back(res); 0050 continue; 0051 } 0052 0053 if (JsonLd::canConvert<Reservation>(res) && 0054 JsonLd::canConvert<Reservation>(batch.at(0))) { 0055 const auto trip1 = JsonLd::convert<Reservation>(res).reservationFor(); 0056 const auto trip2 = 0057 JsonLd::convert<Reservation>(batch.at(0)).reservationFor(); 0058 if (KItinerary::MergeUtil::isSame(trip1, trip2)) { 0059 batch.push_back(res); 0060 continue; 0061 } 0062 } 0063 0064 batches.push_back(batch); 0065 batch.clear(); 0066 batch.push_back(res); 0067 } 0068 0069 if (!batch.isEmpty()) { 0070 batches.push_back(batch); 0071 } 0072 return batches; 0073 } 0074 0075 static void printCapabilities() 0076 { 0077 std::cout << qPrintable(ExtractorCapabilities::capabilitiesString()); 0078 } 0079 0080 static void printExtractors() 0081 { 0082 ExtractorRepository repo; 0083 for (const auto &ext : repo.extractors()) { 0084 std::cout << qPrintable(ext->name()); 0085 if (auto scriptExt = dynamic_cast<const ScriptExtractor*>(ext.get())) { 0086 std::cout << " (" << qPrintable(scriptExt->mimeType()) << ", " 0087 << qPrintable(scriptExt->scriptFileName()) << ":" 0088 << qPrintable(scriptExt->scriptFunction()) << ")"; 0089 } 0090 std::cout << std::endl; 0091 } 0092 } 0093 0094 int main(int argc, char** argv) 0095 { 0096 QCoreApplication::setApplicationName(QStringLiteral("kitinerary-extractor")); 0097 QCoreApplication::setApplicationVersion(QStringLiteral(KITINERARY_VERSION_STRING)); 0098 QCoreApplication::setOrganizationDomain(QStringLiteral("kde.org")); 0099 QCoreApplication::setOrganizationName(QStringLiteral("KDE")); 0100 QCoreApplication app(argc, argv); 0101 0102 #ifdef KITINERARY_STANDALONE_CLI_EXTRACTOR 0103 // set additional data file search path relative to the current binary location 0104 // NOTE: QCoreApplication::applicationDirPath is only valid once QCoreApplication has been created 0105 auto xdgDataDirs = qgetenv("XDG_DATA_DIRS"); 0106 if (!xdgDataDirs.isEmpty()) { 0107 xdgDataDirs += QDir::listSeparator().toLatin1(); 0108 } 0109 xdgDataDirs += QString(QCoreApplication::applicationDirPath() + 0110 QDir::separator() + QLatin1StringView("..") + 0111 QDir::separator() + QLatin1String("share")) 0112 .toUtf8(); 0113 qputenv("XDG_DATA_DIRS", xdgDataDirs); 0114 #endif 0115 0116 QCommandLineParser parser; 0117 parser.setApplicationDescription(QStringLiteral("Command line itinerary extractor.")); 0118 parser.addHelpOption(); 0119 parser.addVersionOption(); 0120 QCommandLineOption capOpt({QStringLiteral("capabilities")}, QStringLiteral("Show available extraction capabilities.")); 0121 parser.addOption(capOpt); 0122 QCommandLineOption listExtOpt({QStringLiteral("list-extractors")}, QStringLiteral("List all available extractors.")); 0123 parser.addOption(listExtOpt); 0124 0125 QCommandLineOption ctxOpt({QStringLiteral("c"), QStringLiteral("context-date")}, QStringLiteral("ISO date/time for when this data has been received."), QStringLiteral("date")); 0126 parser.addOption(ctxOpt); 0127 QCommandLineOption typeOpt({QStringLiteral("t"), QStringLiteral("type")}, QStringLiteral("Deprecated, no longer needed and ignored."), QStringLiteral("type")); 0128 parser.addOption(typeOpt); 0129 QCommandLineOption extOpt({QStringLiteral("e"), QStringLiteral("extractors")}, QStringLiteral("Additional extractors to apply."), QStringLiteral("extractors")); 0130 parser.addOption(extOpt); 0131 QCommandLineOption pathsOpt({QStringLiteral("additional-search-path")}, QStringLiteral("Additional search path for extractors."), QStringLiteral("search-path")); 0132 parser.addOption(pathsOpt); 0133 QCommandLineOption formatOpt({QStringLiteral("o"), QStringLiteral("output")}, QStringLiteral("Output format [JsonLd, iCal]. Default: JsonLd"), QStringLiteral("format")); 0134 parser.addOption(formatOpt); 0135 QCommandLineOption noValidationOpt({QStringLiteral("no-validation")}, QStringLiteral("Disable result validation.")); 0136 parser.addOption(noValidationOpt); 0137 0138 parser.addPositionalArgument(QStringLiteral("input"), QStringLiteral("File to extract data from, omit for using stdin.")); 0139 parser.process(app); 0140 0141 ExtractorRepository repo; 0142 if (parser.isSet(pathsOpt)) { 0143 repo.setAdditionalSearchPaths(parser.values(pathsOpt)); 0144 repo.reload(); 0145 } 0146 0147 if (parser.isSet(capOpt)) { 0148 printCapabilities(); 0149 return 0; 0150 } 0151 if (parser.isSet(listExtOpt)) { 0152 printExtractors(); 0153 return 0; 0154 } 0155 0156 ExtractorEngine engine; 0157 engine.setUseSeparateProcess(false); // we are the external extractor 0158 ExtractorPostprocessor postproc; 0159 0160 auto contextDt = QDateTime::fromString(parser.value(ctxOpt), Qt::ISODate); 0161 if (!contextDt.isValid()) { 0162 contextDt = QDateTime::currentDateTime(); 0163 } 0164 postproc.setContextDate(contextDt); 0165 0166 const auto files = parser.positionalArguments().isEmpty() ? QStringList(QString()) : parser.positionalArguments(); 0167 for (const auto &arg : files) { 0168 QFile f; 0169 if (!arg.isEmpty()) { 0170 f.setFileName(arg); 0171 if (!f.open(QFile::ReadOnly)) { 0172 std::cerr << qPrintable(f.errorString()) << std::endl; 0173 return 1; 0174 } 0175 } else { 0176 f.open(stdin, QFile::ReadOnly); 0177 } 0178 0179 auto fileName = f.fileName(); 0180 0181 engine.clear(); 0182 engine.setContextDate(contextDt); 0183 0184 if (!parser.value(extOpt).isEmpty()) { 0185 const auto extNames = parser.value(extOpt).split(QLatin1Char(';'), 0186 Qt::SkipEmptyParts); 0187 std::vector<const AbstractExtractor*> exts; 0188 exts.reserve(extNames.size()); 0189 for (const auto &name : extNames) { 0190 const auto ext = repo.extractorByName(name); 0191 exts.push_back(ext); 0192 } 0193 engine.setAdditionalExtractors(std::move(exts)); 0194 } 0195 0196 engine.setData(f.readAll(), fileName); 0197 const auto result = JsonLdDocument::fromJson(engine.extract()); 0198 postproc.process(result); 0199 } 0200 0201 auto result = postproc.result(); 0202 if (!parser.isSet(noValidationOpt)) { 0203 ExtractorValidator validator; 0204 result.erase(std::remove_if(result.begin(), result.end(), [&validator](const auto &elem) { 0205 return !validator.isValidElement(elem); 0206 }), result.end()); 0207 } 0208 0209 if (parser.value(formatOpt).compare(QLatin1StringView("ical"), 0210 Qt::CaseInsensitive) == 0) { 0211 const auto batches = batchReservations(result); 0212 KCalendarCore::Calendar::Ptr cal( 0213 new KCalendarCore::MemoryCalendar(QTimeZone::systemTimeZone())); 0214 for (const auto &batch : batches) { 0215 KCalendarCore::Event::Ptr event(new KCalendarCore::Event); 0216 CalendarHandler::fillEvent(batch, event); 0217 cal->addEvent(event); 0218 } 0219 KCalendarCore::ICalFormat format; 0220 std::cout << qPrintable(format.toString(cal)); 0221 } else { 0222 const auto postProcResult = JsonLdDocument::toJson(result); 0223 std::cout << QJsonDocument(postProcResult).toJson().constData() 0224 << std::endl; 0225 } 0226 }