~liu-xiao-guo/debiantrial/mailcheck

« back to all changes in this revision

Viewing changes to src/scope/query.cpp

  • Committer: XiaoGuo, Liu
  • Date: 2014-12-23 10:17:40 UTC
  • Revision ID: xiaoguo.liu@canonical.com-20141223101740-j7lfgz4p32rmfr1g
Initial releae

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#include <boost/algorithm/string/trim.hpp>
 
2
 
 
3
#include <scope/localization.h>
 
4
#include <scope/query.h>
 
5
 
 
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>
 
11
 
 
12
#include <iomanip>
 
13
#include <sstream>
 
14
 
 
15
#include <QDebug>
 
16
#include <QFile>
 
17
#include <QJsonDocument>
 
18
#include <QJsonObject>
 
19
#include <QUrl>
 
20
#include <QCoreApplication>
 
21
#include <QNetworkAccessManager>
 
22
#include <QNetworkDiskCache>
 
23
#include <QNetworkReply>
 
24
 
 
25
namespace sc = unity::scopes;
 
26
namespace alg = boost::algorithm;
 
27
 
 
28
using namespace std;
 
29
using namespace api;
 
30
using namespace scope;
 
31
using namespace unity::scopes;
 
32
 
 
33
auto frontPage = "http://www.kuaidi100.com/";
 
34
 
 
35
QString g_scopePath;
 
36
QString g_rootDepartmentId;
 
37
QMap<QString, std::string> g_renders;
 
38
QString g_userAgent;
 
39
QString g_imageDefault;
 
40
QString g_imageError;
 
41
QMap<QString, QString> g_depts;
 
42
QString g_curScopeId;
 
43
static QMap<QString, QString> g_deptLayouts;
 
44
 
 
45
std::string CAR_GRID = R"(
 
46
{
 
47
    "schema-version" : 1,
 
48
        "template" : {
 
49
        "category-layout" : "grid",
 
50
        "card-size": "large",
 
51
        "overlay": true,
 
52
        "non-interactive":"true"
 
53
        },
 
54
    "components" : {
 
55
        "title" : "category",
 
56
        "art" : {
 
57
        "field": "art2",
 
58
        "aspect-ratio": 2.1
 
59
    }
 
60
  }
 
61
})";
 
62
 
 
63
#define qstr(s) QString::fromStdString(s)
 
64
 
 
65
 
 
66
#define FOREACH_JSON(it, results) QJsonArray::iterator it; \
 
67
    for (it = results.begin(); it != results.end(); ++it)
 
68
 
 
69
#define SET_RESULT(key, value) result[key] = value.toStdString()
 
70
 
 
71
#define LOAD_RENDERER(which) g_renders.insert(which, getRenderer(g_scopePath, which))
 
72
 
 
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);
 
80
 
 
81
    // Load all of the predefined rederers. You can comment out the renderers
 
82
    // you don't use.
 
83
    LOAD_RENDERER( "journal" );
 
84
    LOAD_RENDERER( "wide-art" );
 
85
    LOAD_RENDERER( "hgrid" );
 
86
    LOAD_RENDERER( "carousel" );
 
87
    LOAD_RENDERER( "large" );
 
88
}
 
89
 
 
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();
 
94
}
 
95
 
 
96
void Query::cancelled() {
 
97
    client_.cancel();
 
98
}
 
99
 
 
100
void Query::run(sc::SearchReplyProxy const& reply) {
 
101
    try {
 
102
        // Start by getting information about the query
 
103
        const CannedQuery &query(sc::SearchQueryBase::query());
 
104
 
 
105
        // Trim the query string of whitespace
 
106
        string query_string = alg::trim_copy(query.query_string());
 
107
        QString queryString = QString::fromStdString(query_string);
 
108
 
 
109
        qDebug() << "queryString: " << queryString;
 
110
 
 
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 );
 
115
        }
 
116
 
 
117
        search(reply);
 
118
 
 
119
    } catch ( domain_error &e ) {
 
120
        // Handle exceptions being thrown by the client API
 
121
        cerr << e.what() << endl;
 
122
        reply->error( current_exception() );
 
123
    }
 
124
}
 
125
 
 
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);
 
130
 
 
131
    CannedQuery cannedQuery = SearchQueryBase::query();
 
132
 
 
133
    QString deptId = QString::fromStdString(cannedQuery.department_id());
 
134
    qDebug() << "deptId: " << deptId;
 
135
 
 
136
    qDebug() << "m_rootDepartmentId: " << g_rootDepartmentId;
 
137
    QString url;
 
138
 
 
139
    qDebug() << "m_curScopeId: " << g_curScopeId;
 
140
 
 
141
    if ( !deptId.isEmpty() ) {
 
142
        g_curScopeId = deptId;
 
143
    }
 
144
 
 
145
    if ( deptId.isEmpty() && !g_rootDepartmentId.isEmpty()
 
146
         && g_curScopeId == g_rootDepartmentId ) {
 
147
 
 
148
        QMapIterator<QString, QString> i(g_depts);
 
149
        qDebug() << "m_depts count: "  << g_depts.count();
 
150
 
 
151
        qDebug() << "Going to set the surfacing content";
 
152
 
 
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);
 
157
 
 
158
        if ( queryString.isEmpty()) {
 
159
            url = QString(g_depts[g_rootDepartmentId]).arg(592833849048);
 
160
        } else {
 
161
            url = QString(g_depts[g_rootDepartmentId]).arg(queryString);
 
162
        }
 
163
    } else {
 
164
        QString queryString = QString::fromStdString(cannedQuery.query_string());
 
165
        qDebug() << "queryString: " << queryString;
 
166
 
 
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();
 
170
 
 
171
        while (i.hasNext()) {
 
172
            i.next();
 
173
            qDebug() << "scope id: " << i.key() << ": " << i.value();
 
174
        }
 
175
 
 
176
        url = g_depts[g_curScopeId].arg(queryString);
 
177
    }
 
178
 
 
179
    qDebug() << "url: "  << url;
 
180
    qDebug() << "m_curScopeId: " << g_curScopeId;
 
181
 
 
182
    try {
 
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());
 
188
    }
 
189
}
 
190
 
 
191
void Query::getMailInfo(QByteArray &data, SearchReplyProxy const& reply) {
 
192
    QJsonParseError e;
 
193
    QJsonDocument document = QJsonDocument::fromJson(data, &e);
 
194
    if (e.error != QJsonParseError::NoError) {
 
195
        throw QString("Failed to parse response: %1").arg(e.errorString());
 
196
    }
 
197
 
 
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);
 
208
 
 
209
    QJsonObject obj = document.object();
 
210
 
 
211
    qDebug() << "***********************\r\n";
 
212
 
 
213
    if ( obj.contains("data") ) {
 
214
        qDebug() << "it has data!";
 
215
 
 
216
        QJsonValue data1 = obj.value("data");
 
217
 
 
218
        QJsonArray results = data1.toArray();
 
219
 
 
220
        qDebug() << "g_curScopeId: " << g_curScopeId;
 
221
        QString layout = g_deptLayouts.value( g_curScopeId );
 
222
        std::string renderTemplate;
 
223
 
 
224
        if (g_renders.contains( layout )) {
 
225
            qDebug() << "it has layout: " << layout;
 
226
            renderTemplate = g_renders.value( layout, "" );
 
227
            // qDebug() << "renderTemplate: " << QString::fromStdString(renderTemplate);
 
228
        }
 
229
        else {
 
230
            qDebug() << "it does not have layout!";
 
231
            renderTemplate = g_renders.value( "journal" );
 
232
            // qDebug() << "renderTemplate: " << QString::fromStdString(renderTemplate);
 
233
        }
 
234
 
 
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);
 
240
 
 
241
        FOREACH_JSON(result, results) {
 
242
            QJsonObject o = (*result).toObject();
 
243
 
 
244
            QString time = o.value("time").toString();
 
245
//            qDebug() << "time: " << time;
 
246
 
 
247
            QString context = o.value("context").toString();
 
248
//            qDebug() << "context: " << context;
 
249
 
 
250
            QString link = "http://www.kuaidi100.com/";
 
251
            QString defaultImage ="file://"+ g_scopePath + "/images/" + g_curScopeId + ".jpg";
 
252
 
 
253
            CategorisedResult result(tracking);
 
254
 
 
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();
 
263
 
 
264
            if (!reply->push(result)) break;
 
265
        }
 
266
    }
 
267
}
 
268
 
 
269
QByteArray Query::get(sc::SearchReplyProxy const& reply, QUrl url) const {
 
270
    QNetworkRequest request(url);
 
271
    QByteArray data = makeRequest(reply, request);
 
272
    return data;
 
273
}
 
274
 
 
275
void Query::pushDepartments(unity::scopes::SearchReplyProxy const& reply) {
 
276
    qDebug() << "gScopePath: "  << g_scopePath;
 
277
 
 
278
    QString json = getDepartmentsData( g_scopePath );
 
279
 
 
280
    QJsonDocument doc = QJsonDocument::fromJson( json.toUtf8() );
 
281
    if (doc.isNull()) {
 
282
        qCritical() << "Failed to parse departments JSON!";
 
283
        return;
 
284
    }
 
285
 
 
286
    CannedQuery query = SearchQueryBase::query();
 
287
    QString queryString = QString::fromStdString(query.query_string());
 
288
 
 
289
    DepartmentList depts = getDepartments( doc );
 
290
    Department::SPtr root = getRootDepartment( depts );
 
291
    root->set_subdepartments(depts);
 
292
 
 
293
    reply->register_departments(root);
 
294
}
 
295
 
 
296
Department::SPtr Query::getRootDepartment(DepartmentList &depts) {
 
297
    //Department::SPtr top = depts.front();
 
298
    std::shared_ptr<const Department> top = depts.front();
 
299
    std::string label;
 
300
 
 
301
    if (BROWSE_AT_ROOT) {
 
302
        label = BROWSE.toStdString();
 
303
    } else {
 
304
        label = top->label();
 
305
        depts.pop_front();
 
306
    }
 
307
    std::string id = top->id();
 
308
 
 
309
    // Idea: We could show last browsed department instead.
 
310
    g_curScopeId =QString::fromStdString(id);
 
311
    qDebug() << "Init m_curScopeId: " << g_curScopeId;
 
312
 
 
313
    g_rootDepartmentId = QString::fromStdString(id);
 
314
    qDebug() <<  "Init m_rootDepartmentId: " << g_rootDepartmentId;
 
315
 
 
316
    CannedQuery topQuery(SCOPENAME.toStdString());
 
317
    topQuery.set_department_id(id);
 
318
    Department::SPtr root(Department::create("", topQuery, label));
 
319
    return root;
 
320
}
 
321
 
 
322
QString Query::getDepartmentsData(QString scopePath) {
 
323
    QString path = QString("%1/departments.json").arg(scopePath);
 
324
    qDebug() << "Departments file path: " << path;
 
325
 
 
326
    return readFile(path);
 
327
}
 
328
 
 
329
QString Query::readFile(QString path) {
 
330
    QFile file(path);
 
331
    file.open(QIODevice::ReadOnly | QIODevice::Text);
 
332
    QString data = file.readAll();
 
333
    // qDebug() << "JSON file: " << data;
 
334
    file.close();
 
335
    return data;
 
336
}
 
337
 
 
338
#define FOREACH_JSON(it, results) QJsonArray::iterator it; \
 
339
    for (it = results.begin(); it != results.end(); ++it)
 
340
 
 
341
DepartmentList Query::getDepartments(QJsonArray data) {
 
342
    qDebug() << "entering getDepartments";
 
343
 
 
344
    DepartmentList depts;
 
345
 
 
346
    // Clear the previous departments since the URL may change according to settings
 
347
    g_depts.clear();
 
348
    qDebug() << "m_depts is being cleared....!";
 
349
 
 
350
    int index = 0;
 
351
    FOREACH_JSON( json, data ) {
 
352
        auto feed = (*json).toObject();
 
353
        QString title = feed["title"].toString();
 
354
//        qDebug() << "title: " << title;
 
355
 
 
356
        QString url = feed["url"].toString();
 
357
//        qDebug() << "url: " << url;
 
358
 
 
359
        QString pinyin = feed["pinyin"].toString();
 
360
//        qDebug() << "pinyin: " << pinyin;
 
361
 
 
362
        // This is the default layout otherwise it is defined in the json file
 
363
        QString layout = SURFACING_LAYOUT;
 
364
 
 
365
        if ( feed.contains( "layout" ) ) {
 
366
            layout = feed[ "layout" ].toString();
 
367
        }
 
368
 
 
369
        g_depts.insert( pinyin, url );
 
370
        g_deptLayouts.insert( pinyin, layout );
 
371
 
 
372
        CannedQuery query( SCOPENAME.toStdString() );
 
373
        query.set_department_id( url.toStdString() );
 
374
        query.set_query_string( url.toStdString() );
 
375
 
 
376
        Department::SPtr dept( Department::create(
 
377
                               pinyin.toStdString(), query, title.toStdString() ) );
 
378
 
 
379
        depts.push_back(dept);
 
380
 
 
381
        index++;
 
382
    }
 
383
 
 
384
    // Dump the departments. The map has been sorted out
 
385
    QMapIterator<QString, QString> i(g_depts);
 
386
    while (i.hasNext()) {
 
387
        i.next();
 
388
        qDebug() << "scope id: " << i.key() << ": " << i.value();
 
389
    }
 
390
 
 
391
    qDebug() << "Going to dump tthe department layouts";
 
392
 
 
393
    QMapIterator<QString, QString> j( g_deptLayouts );
 
394
    while (j.hasNext()) {
 
395
        j.next();
 
396
        qDebug() << "scope id: " << j.key() << ": " << j.value();
 
397
    }
 
398
 
 
399
    return depts;
 
400
}
 
401
 
 
402
DepartmentList Query::getDepartments(QJsonDocument document) {
 
403
    g_deptLayouts.insert("", SURFACING_LAYOUT);
 
404
    QJsonArray jsonDepts = document.array();
 
405
    return getDepartments(jsonDepts);
 
406
}
 
407
 
 
408
QByteArray Query::makeRequest(SearchReplyProxy const& reply,QNetworkRequest &request) const {
 
409
    int argc = 1;
 
410
    char *argv = const_cast<char*>("rss-scope");
 
411
    QCoreApplication *app = new QCoreApplication( argc, &argv );
 
412
 
 
413
    QNetworkAccessManager manager;
 
414
    QByteArray response;
 
415
    QNetworkDiskCache *cache = new QNetworkDiskCache();
 
416
    QString cachePath = g_scopePath + "/cache";
 
417
    //qDebug() << "Cache dir: " << cachePath;
 
418
    cache->setCacheDirectory(cachePath);
 
419
 
 
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 );
 
423
 
 
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 );
 
430
        } else {
 
431
            response = msg->readAll();
 
432
        }
 
433
 
 
434
        msg->deleteLater();
 
435
    });
 
436
 
 
437
    manager.setCache( cache );
 
438
    manager.get( request );
 
439
    app->exec();
 
440
 
 
441
    delete cache;
 
442
    return response;
 
443
}
 
444
 
 
445
void Query::pushError(SearchReplyProxy const& reply, QString error) {
 
446
    CategoryRenderer renderer(g_renders.value( "hgrid", "" ) );
 
447
    auto errors = reply->register_category( "error", "", "", renderer );
 
448
 
 
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 );
 
455
}