1
#include <boost/algorithm/string/trim.hpp>
3
#include <scope/localization.h>
4
#include <scope/query.h>
6
#include <unity/scopes/Annotation.h>
7
#include <unity/scopes/CategorisedResult.h>
8
#include <unity/scopes/CategoryRenderer.h>
9
#include <unity/scopes/QueryBase.h>
10
#include <unity/scopes/SearchReply.h>
17
#include <QJsonDocument>
18
#include <QJsonObject>
20
#include <QCoreApplication>
21
#include <QNetworkAccessManager>
22
#include <QNetworkDiskCache>
23
#include <QNetworkReply>
25
namespace sc = unity::scopes;
26
namespace alg = boost::algorithm;
30
using namespace scope;
31
using namespace unity::scopes;
33
auto frontPage = "http://www.kuaidi100.com/";
36
QString g_rootDepartmentId;
37
QMap<QString, std::string> g_renders;
39
QString g_imageDefault;
41
QMap<QString, QString> g_depts;
43
static QMap<QString, QString> g_deptLayouts;
45
std::string CAR_GRID = R"(
49
"category-layout" : "grid",
52
"non-interactive":"true"
63
#define qstr(s) QString::fromStdString(s)
66
#define FOREACH_JSON(it, results) QJsonArray::iterator it; \
67
for (it = results.begin(); it != results.end(); ++it)
69
#define SET_RESULT(key, value) result[key] = value.toStdString()
71
#define LOAD_RENDERER(which) g_renders.insert(which, getRenderer(g_scopePath, which))
73
Query::Query( const sc::CannedQuery &query, const sc::SearchMetadata &metadata,
74
QString scopePath, Config::Ptr config ) :
75
sc::SearchQueryBase( query, metadata ), client_( config ) {
76
g_scopePath = scopePath;
77
g_userAgent = QString("%1 (Ubuntu)").arg(SCOPE_PACKAGE);
78
g_imageDefault = QString("file://%1/images/%2").arg(scopePath).arg(IMG_DEFAULT);
79
g_imageError = QString("file://%1/images/%2").arg(scopePath).arg(IMG_ERROR);
81
// Load all of the predefined rederers. You can comment out the renderers
83
LOAD_RENDERER( "journal" );
84
LOAD_RENDERER( "wide-art" );
85
LOAD_RENDERER( "hgrid" );
86
LOAD_RENDERER( "carousel" );
87
LOAD_RENDERER( "large" );
90
std::string Query::getRenderer( QString scopePath, QString name ) {
91
QString renderer = readFile( QString("%1/renderer/%2.json" )
92
.arg(scopePath).arg(name));
93
return renderer.toStdString();
96
void Query::cancelled() {
100
void Query::run(sc::SearchReplyProxy const& reply) {
102
// Start by getting information about the query
103
const CannedQuery &query(sc::SearchQueryBase::query());
105
// Trim the query string of whitespace
106
string query_string = alg::trim_copy(query.query_string());
107
QString queryString = QString::fromStdString(query_string);
109
qDebug() << "queryString: " << queryString;
111
// Only push the departments when the query string is null
112
if ( queryString.length() == 0 ) {
113
qDebug() << "it is going to push the departments...!";
114
pushDepartments( reply );
119
} catch ( domain_error &e ) {
120
// Handle exceptions being thrown by the client API
121
cerr << e.what() << endl;
122
reply->error( current_exception() );
126
void Query::search(sc::SearchReplyProxy const& reply) {
127
CategoryRenderer renderer(g_renders.value("journal", ""));
128
auto search = reply->register_category(
129
"search", RESULTS.toStdString(), "", renderer);
131
CannedQuery cannedQuery = SearchQueryBase::query();
133
QString deptId = QString::fromStdString(cannedQuery.department_id());
134
qDebug() << "deptId: " << deptId;
136
qDebug() << "m_rootDepartmentId: " << g_rootDepartmentId;
139
qDebug() << "m_curScopeId: " << g_curScopeId;
141
if ( !deptId.isEmpty() ) {
142
g_curScopeId = deptId;
145
if ( deptId.isEmpty() && !g_rootDepartmentId.isEmpty()
146
&& g_curScopeId == g_rootDepartmentId ) {
148
QMapIterator<QString, QString> i(g_depts);
149
qDebug() << "m_depts count: " << g_depts.count();
151
qDebug() << "Going to set the surfacing content";
153
const CannedQuery &query(sc::SearchQueryBase::query());
154
// Trim the query string of whitespace
155
string query_string = alg::trim_copy(query.query_string());
156
QString queryString = QString::fromStdString(query_string);
158
if ( queryString.isEmpty()) {
159
url = QString(g_depts[g_rootDepartmentId]).arg(592833849048);
161
url = QString(g_depts[g_rootDepartmentId]).arg(queryString);
164
QString queryString = QString::fromStdString(cannedQuery.query_string());
165
qDebug() << "queryString: " << queryString;
167
// Dump the departments. The map has been sorted out
168
QMapIterator<QString, QString> i(g_depts);
169
qDebug() << "m_depts count: " << g_depts.count();
171
while (i.hasNext()) {
173
qDebug() << "scope id: " << i.key() << ": " << i.value();
176
url = g_depts[g_curScopeId].arg(queryString);
179
qDebug() << "url: " << url;
180
qDebug() << "m_curScopeId: " << g_curScopeId;
183
QByteArray data = get(reply, QUrl(url));
184
getMailInfo(data, reply);
185
} catch (domain_error &e ) {
186
cerr << e.what() << endl;
187
reply->error(current_exception());
191
void Query::getMailInfo(QByteArray &data, SearchReplyProxy const& reply) {
193
QJsonDocument document = QJsonDocument::fromJson(data, &e);
194
if (e.error != QJsonParseError::NoError) {
195
throw QString("Failed to parse response: %1").arg(e.errorString());
198
// This creates a big picture on the top
199
CategoryRenderer rssCAR(CAR_GRID);
200
auto catCARR = reply->register_category("A", "", "", rssCAR);
201
CategorisedResult res_car(catCARR);
202
res_car.set_uri("frontPage");
203
QString defaultImage1 ="file://"+ g_scopePath + "/images/" + g_curScopeId + ".jpg";
204
qDebug() << "defaultImage1: " << defaultImage1;
205
res_car["largepic"] = defaultImage1.toStdString();
206
res_car["art2"] = res_car["largepic"];
207
reply->push(res_car);
209
QJsonObject obj = document.object();
211
qDebug() << "***********************\r\n";
213
if ( obj.contains("data") ) {
214
qDebug() << "it has data!";
216
QJsonValue data1 = obj.value("data");
218
QJsonArray results = data1.toArray();
220
qDebug() << "g_curScopeId: " << g_curScopeId;
221
QString layout = g_deptLayouts.value( g_curScopeId );
222
std::string renderTemplate;
224
if (g_renders.contains( layout )) {
225
qDebug() << "it has layout: " << layout;
226
renderTemplate = g_renders.value( layout, "" );
227
// qDebug() << "renderTemplate: " << QString::fromStdString(renderTemplate);
230
qDebug() << "it does not have layout!";
231
renderTemplate = g_renders.value( "journal" );
232
// qDebug() << "renderTemplate: " << QString::fromStdString(renderTemplate);
235
CategoryRenderer grid(renderTemplate);
236
std::string categoryId = "root";
237
std::string categoryTitle = " "; // #1330899 workaround
238
std::string icon = "";
239
auto tracking = reply->register_category(categoryId, categoryTitle, icon, grid);
241
FOREACH_JSON(result, results) {
242
QJsonObject o = (*result).toObject();
244
QString time = o.value("time").toString();
245
// qDebug() << "time: " << time;
247
QString context = o.value("context").toString();
248
// qDebug() << "context: " << context;
250
QString link = "http://www.kuaidi100.com/";
251
QString defaultImage ="file://"+ g_scopePath + "/images/" + g_curScopeId + ".jpg";
253
CategorisedResult result(tracking);
255
SET_RESULT("uri", link);
256
SET_RESULT("image", defaultImage);
257
// SET_RESULT("video", video);
258
SET_RESULT("title", time);
259
// SET_RESULT("subtitle", context);
260
SET_RESULT("summary", context);
261
// SET_RESULT("full_summary", fullSummary);
262
// result["actions"] = actions.end();
264
if (!reply->push(result)) break;
269
QByteArray Query::get(sc::SearchReplyProxy const& reply, QUrl url) const {
270
QNetworkRequest request(url);
271
QByteArray data = makeRequest(reply, request);
275
void Query::pushDepartments(unity::scopes::SearchReplyProxy const& reply) {
276
qDebug() << "gScopePath: " << g_scopePath;
278
QString json = getDepartmentsData( g_scopePath );
280
QJsonDocument doc = QJsonDocument::fromJson( json.toUtf8() );
282
qCritical() << "Failed to parse departments JSON!";
286
CannedQuery query = SearchQueryBase::query();
287
QString queryString = QString::fromStdString(query.query_string());
289
DepartmentList depts = getDepartments( doc );
290
Department::SPtr root = getRootDepartment( depts );
291
root->set_subdepartments(depts);
293
reply->register_departments(root);
296
Department::SPtr Query::getRootDepartment(DepartmentList &depts) {
297
//Department::SPtr top = depts.front();
298
std::shared_ptr<const Department> top = depts.front();
301
if (BROWSE_AT_ROOT) {
302
label = BROWSE.toStdString();
304
label = top->label();
307
std::string id = top->id();
309
// Idea: We could show last browsed department instead.
310
g_curScopeId =QString::fromStdString(id);
311
qDebug() << "Init m_curScopeId: " << g_curScopeId;
313
g_rootDepartmentId = QString::fromStdString(id);
314
qDebug() << "Init m_rootDepartmentId: " << g_rootDepartmentId;
316
CannedQuery topQuery(SCOPENAME.toStdString());
317
topQuery.set_department_id(id);
318
Department::SPtr root(Department::create("", topQuery, label));
322
QString Query::getDepartmentsData(QString scopePath) {
323
QString path = QString("%1/departments.json").arg(scopePath);
324
qDebug() << "Departments file path: " << path;
326
return readFile(path);
329
QString Query::readFile(QString path) {
331
file.open(QIODevice::ReadOnly | QIODevice::Text);
332
QString data = file.readAll();
333
// qDebug() << "JSON file: " << data;
338
#define FOREACH_JSON(it, results) QJsonArray::iterator it; \
339
for (it = results.begin(); it != results.end(); ++it)
341
DepartmentList Query::getDepartments(QJsonArray data) {
342
qDebug() << "entering getDepartments";
344
DepartmentList depts;
346
// Clear the previous departments since the URL may change according to settings
348
qDebug() << "m_depts is being cleared....!";
351
FOREACH_JSON( json, data ) {
352
auto feed = (*json).toObject();
353
QString title = feed["title"].toString();
354
// qDebug() << "title: " << title;
356
QString url = feed["url"].toString();
357
// qDebug() << "url: " << url;
359
QString pinyin = feed["pinyin"].toString();
360
// qDebug() << "pinyin: " << pinyin;
362
// This is the default layout otherwise it is defined in the json file
363
QString layout = SURFACING_LAYOUT;
365
if ( feed.contains( "layout" ) ) {
366
layout = feed[ "layout" ].toString();
369
g_depts.insert( pinyin, url );
370
g_deptLayouts.insert( pinyin, layout );
372
CannedQuery query( SCOPENAME.toStdString() );
373
query.set_department_id( url.toStdString() );
374
query.set_query_string( url.toStdString() );
376
Department::SPtr dept( Department::create(
377
pinyin.toStdString(), query, title.toStdString() ) );
379
depts.push_back(dept);
384
// Dump the departments. The map has been sorted out
385
QMapIterator<QString, QString> i(g_depts);
386
while (i.hasNext()) {
388
qDebug() << "scope id: " << i.key() << ": " << i.value();
391
qDebug() << "Going to dump tthe department layouts";
393
QMapIterator<QString, QString> j( g_deptLayouts );
394
while (j.hasNext()) {
396
qDebug() << "scope id: " << j.key() << ": " << j.value();
402
DepartmentList Query::getDepartments(QJsonDocument document) {
403
g_deptLayouts.insert("", SURFACING_LAYOUT);
404
QJsonArray jsonDepts = document.array();
405
return getDepartments(jsonDepts);
408
QByteArray Query::makeRequest(SearchReplyProxy const& reply,QNetworkRequest &request) const {
410
char *argv = const_cast<char*>("rss-scope");
411
QCoreApplication *app = new QCoreApplication( argc, &argv );
413
QNetworkAccessManager manager;
415
QNetworkDiskCache *cache = new QNetworkDiskCache();
416
QString cachePath = g_scopePath + "/cache";
417
//qDebug() << "Cache dir: " << cachePath;
418
cache->setCacheDirectory(cachePath);
420
request.setRawHeader( "User-Agent", g_userAgent.toStdString().c_str() );
421
request.setRawHeader( "Content-Type", "application/rss+xml, text/xml" );
422
request.setAttribute( QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache );
424
QObject::connect(&manager, SIGNAL(finished(QNetworkReply*)), app, SLOT(quit()));
425
QObject::connect(&manager, &QNetworkAccessManager::finished,
426
[this, &reply, &response](QNetworkReply *msg) {
427
if (msg->error() != QNetworkReply::NoError) {
428
qCritical() << "Failed to get data: " << msg->error();
429
pushError( reply, NO_CONNECTION );
431
response = msg->readAll();
437
manager.setCache( cache );
438
manager.get( request );
445
void Query::pushError(SearchReplyProxy const& reply, QString error) {
446
CategoryRenderer renderer(g_renders.value( "hgrid", "" ) );
447
auto errors = reply->register_category( "error", "", "", renderer );
449
CategorisedResult result( errors );
450
result[ "uri" ] = HOME_URL.toStdString();
451
result[ "title" ] = ERROR.toStdString();
452
result[ "subtitle" ] = error.toStdString();
453
result[ "image" ] = g_imageError.toStdString();
454
reply->push( result );