Exemplo n.º 1
0
void ItemsManagerWorker::Update() {
    if (updating_) {
        QLOG_WARN() << "ItemsManagerWorker::Update called while updating";
        return;
    }

    QLOG_INFO() << "Updating stash tabs";
    updating_ = true;
    // remove all mappings (from previous requests)
    if (signal_mapper_)
        delete signal_mapper_;
    signal_mapper_ = new QSignalMapper;
    // remove all pending requests
    queue_ = std::queue<ItemsRequest>();
    queue_id_ = 0;
    replies_.clear();
    items_.clear();
    tabs_as_string_ = "";
    items_as_string_ = "[ "; // space here is important, see ParseItems and OnTabReceived when all requests are completed
    selected_character_ = "";

    CurrentStatusUpdate status = CurrentStatusUpdate();
    status.state = ProgramState::ItemsUpdating;
    status.progress = 0;
    status.total = 100;
    emit StatusUpdate(status);

    // first, download the main page because it's the only way to know which character is selected
    QNetworkReply *main_page = network_manager_.get(QNetworkRequest(QUrl(kMainPage)));
    connect(main_page, &QNetworkReply::finished, this, &ItemsManagerWorker::OnMainPageReceived);
}
Exemplo n.º 2
0
void ItemsManagerWorker::StatusFinished() {
    CurrentStatusUpdate status = CurrentStatusUpdate();
    status.state = ProgramState::ItemsCompleted;
    status.progress = 100;
    status.total = 100;
    emit StatusUpdate(status);
}
Exemplo n.º 3
0
void Shop::SubmitSingleShop() {
    CurrentStatusUpdate status = CurrentStatusUpdate();
    status.state = ProgramState::ShopSubmitting;
    status.progress = requests_completed_;
    status.total = threads_.size();
    if (requests_completed_ == threads_.size()) {
        status.state = ProgramState::ShopCompleted;
        submitting_ = false;
        app_.data().Set("shop_hash", shop_hash_);
    } else {
        // first, get to the edit-thread page to grab CSRF token
        QNetworkReply *fetched = app_.logged_in_nm().get(QNetworkRequest(QUrl(ShopEditUrl(requests_completed_).c_str())));
        new QReplyTimeout(fetched, kEditThreadTimeout);
        connect(fetched, SIGNAL(finished()), this, SLOT(OnEditPageFinished()));
    }
    emit StatusUpdate(status);
}
Exemplo n.º 4
0
void ItemsManagerWorker::OnTabReceived(int request_id) {
    if (!replies_.count(request_id)) {
        QLOG_WARN() << "Received a reply for request" << request_id << "that was not requested.";
        return;
    }

    ItemsReply reply = replies_[request_id];
    QLOG_INFO() << "Received a reply for" << reply.request.location.GetHeader().c_str();
    QByteArray bytes = reply.network_reply->readAll();
    rapidjson::Document doc;
    doc.Parse(bytes.constData());

    bool error = false;
    if (!doc.IsObject()) {
        QLOG_WARN() << request_id << "got a non-object response";
        error = true;
    } else if (doc.HasMember("error")) {
        // this can happen if user is browsing stash in background and we can't know about it
        QLOG_WARN() << request_id << "got 'error' instead of stash tab contents";
        error = true;
    }

    // re-queue a failed request
    if (error)
        QueueRequest(reply.request.network_request, reply.request.location);

    ++requests_completed_;

    if (!error)
        ++total_completed_;

    bool throttled = false;
    if (requests_completed_ == requests_needed_ && queue_.size() > 0) {
        throttled = true;
        QLOG_INFO() << "Sleeping one minute to prevent throttling.";
        QTimer::singleShot(kThrottleSleep * 1000, this, SLOT(FetchItems()));
    }
    CurrentStatusUpdate status = CurrentStatusUpdate();
    status.state = throttled ? ProgramState::ItemsPaused : ProgramState::ItemsReceive;
    status.progress = total_completed_;
    status.total = total_needed_;
    if (total_completed_ == total_needed_)
        status.state = ProgramState::ItemsCompleted;
    emit StatusUpdate(status);

    if (error)
        return;

    ParseItems(&doc["items"], reply.request.location, doc.GetAllocator());

    if (total_completed_ == total_needed_) {
        // all requests completed
        emit ItemsRefreshed(items_, tabs_, false);

        // since we build items_as_string_ in a hackish way inside ParseItems last character will either be
        // ' ' when no items were parsed or ',' when at least one item is parsed, and the first character is '['
        items_as_string_[items_as_string_.size() - 1] = ']';

        // DataManager is thread safe so it's ok to call it here
        data_manager_.Set("items", items_as_string_);
        data_manager_.Set("tabs", tabs_as_string_);

        updating_ = false;
        QLOG_INFO() << "Finished updating stash.";

        // if we're at the verge of getting throttled, sleep so we don't
        if (requests_completed_ == kThrottleRequests)
            QTimer::singleShot(kThrottleSleep, this, SLOT(PreserveSelectedCharacter()));
        else
            PreserveSelectedCharacter();
    }

    reply.network_reply->deleteLater();
}
Exemplo n.º 5
0
void ItemsManagerWorker::OnTabReceived(int request_id) {
    if (!replies_.count(request_id)) {
        QLOG_WARN() << "Received a reply for request" << request_id << "that was not requested.";
        return;
    }

    ItemsReply reply = replies_[request_id];

    bool cache_status = reply.network_reply->attribute(QNetworkRequest::SourceIsFromCacheAttribute).toBool();

    if (cache_status) {
        QLOG_DEBUG() << "Received a cached reply for" << reply.request.location.GetHeader().c_str();
        ++cached_requests_completed_;
        ++total_cached_;
    } else {
        QLOG_DEBUG() << "Received a reply for" << reply.request.location.GetHeader().c_str();
    }

    QByteArray bytes = reply.network_reply->readAll();
    rapidjson::Document doc;
    doc.Parse(bytes.constData());

    bool error = false;
    if (!doc.IsObject()) {
        QLOG_WARN() << request_id << "got a non-object response";
        error = true;
    } else if (doc.HasMember("error")) {
        // this can happen if user is browsing stash in background and we can't know about it
        QLOG_WARN() << request_id << "got 'error' instead of stash tab contents";
        error = true;
    }

    // re-queue a failed request
    if (error) {
        // We can 'cache' error response document so make sure we remove it
        // before reque
        tab_cache_->remove(reply.request.network_request.url());
        QueueRequest(reply.request.network_request, reply.request.location);
    }

    ++requests_completed_;

    if (!error)
        ++total_completed_;

    bool throttled = false;
    if (requests_completed_ == requests_needed_ && queue_.size() > 0) {
        if (cached_requests_completed_ > 0) {
            // We basically don't want cached requests to count against throttle limit
            // so if we did get any cached requests fetch up to that number without a
            // large delay
            QTimer::singleShot(1, [&]() {
                FetchItems(cached_requests_completed_);
            });
        } else {
            throttled = true;
            QLOG_DEBUG() << "Sleeping one minute to prevent throttling.";
            QTimer::singleShot(kThrottleSleep * 1000, this, SLOT(FetchItems()));
        }
    }
    CurrentStatusUpdate status = CurrentStatusUpdate();
    status.state = throttled ? ProgramState::ItemsPaused : ProgramState::ItemsReceive;
    status.progress = total_completed_;
    status.total = total_needed_;
    status.cached = total_cached_;
    if (total_completed_ == total_needed_)
        status.state = ProgramState::ItemsCompleted;
    emit StatusUpdate(status);

    if (error)
        return;

    ParseItems(&doc["items"], reply.request.location, doc.GetAllocator());

    if (total_completed_ == total_needed_) {
        // It's possible that we receive character vs stash tabs out of order, or users
        // move items around in a tab and we get them in a different order. For
        // consistency we want to present the tab data in a deterministic way to the rest
        // of the application.  Especially so we don't try to update shop when nothing actually
        // changed.  So sort items_ here before emitting and then generate
        // item list as strings.

        std::sort(begin(items_), end(items_), [](const std::shared_ptr<Item> &a, const std::shared_ptr<Item> &b) {
            return b->location() < a->location();
        });

        QStringList tmp;
        for (auto const &item: items_) {
            tmp.push_back(item->json().c_str());
        }
        auto items_as_string = std::string("[") + tmp.join(",").toStdString() + "]";

        // all requests completed
        emit ItemsRefreshed(items_, tabs_, false);

        // DataStore is thread safe so it's ok to call it here
        data_.Set("items", items_as_string);
        data_.Set("tabs", tabs_as_string_);

        updating_ = false;
        QLOG_DEBUG() << "Finished updating stash.";

        // if we're at the verge of getting throttled, sleep so we don't
        if (requests_completed_ == kThrottleRequests)
            QTimer::singleShot(kThrottleSleep, this, SLOT(PreserveSelectedCharacter()));
        else
            PreserveSelectedCharacter();
    }

    reply.network_reply->deleteLater();
}