File indexing completed on 2024-05-19 09:21:26
0001 /* 0002 * SPDX-FileCopyrightText: 2012 Aleix Pol Gonzalez <aleixpol@blue-systems.com> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0005 */ 0006 0007 #include "ReviewsBackend/ReviewsModel.h" 0008 #include "libdiscover_debug.h" 0009 #include <KConfigGroup> 0010 #include <KSharedConfig> 0011 #include <QtQml/qqmllist.h> 0012 #include <ReviewsBackend/AbstractReviewsBackend.h> 0013 #include <ReviewsBackend/Review.h> 0014 #include <ksharedconfig.h> 0015 #include <resources/AbstractResource.h> 0016 #include <resources/AbstractResourcesBackend.h> 0017 #include <resources/ResourcesModel.h> 0018 0019 using namespace Qt::StringLiterals; 0020 0021 int StarsCount::one() const 0022 { 0023 return m_one; 0024 } 0025 0026 int StarsCount::two() const 0027 { 0028 return m_two; 0029 } 0030 0031 int StarsCount::three() const 0032 { 0033 return m_three; 0034 } 0035 0036 int StarsCount::four() const 0037 { 0038 return m_four; 0039 } 0040 0041 int StarsCount::five() const 0042 { 0043 return m_five; 0044 } 0045 0046 void StarsCount::addRating(int rating) 0047 { 0048 // Ratings are 1-10, but we show only 5 stars 0049 switch (int(std::ceil(qreal(rating) / 2.0))) { 0050 case 1: 0051 ++m_one; 0052 break; 0053 case 2: 0054 ++m_two; 0055 break; 0056 case 3: 0057 ++m_three; 0058 break; 0059 case 4: 0060 ++m_four; 0061 break; 0062 case 5: 0063 ++m_five; 0064 break; 0065 default: 0066 break; 0067 } 0068 } 0069 0070 void StarsCount::clear() 0071 { 0072 m_one = 0; 0073 m_two = 0; 0074 m_three = 0; 0075 m_four = 0; 0076 m_five = 0; 0077 } 0078 0079 ReviewsModel::ReviewsModel(QObject *parent) 0080 : QAbstractListModel(parent) 0081 , m_lastPage(0) 0082 { 0083 KSharedConfigPtr config = KSharedConfig::openConfig(); 0084 KConfigGroup configGroup(config, u"Reviews"_s); 0085 const QString role = configGroup.readEntry("PreferredSortRole"); 0086 if (QStringList({QStringLiteral("wilsonScore"), QStringLiteral("date"), QStringLiteral("rating")}).contains(role)) { 0087 m_preferredSortRole = role; 0088 } else { 0089 m_preferredSortRole = QStringLiteral("wilsonScore"); 0090 } 0091 } 0092 0093 ReviewsModel::~ReviewsModel() = default; 0094 0095 QHash<int, QByteArray> ReviewsModel::roleNames() const 0096 { 0097 QHash<int, QByteArray> roles = QAbstractItemModel::roleNames(); 0098 roles.insert(ShouldShow, "shouldShow"); 0099 roles.insert(Reviewer, "reviewer"); 0100 roles.insert(CreationDate, "date"); 0101 roles.insert(UsefulnessTotal, "usefulnessTotal"); 0102 roles.insert(UsefulnessFavorable, "usefulnessFavorable"); 0103 roles.insert(WilsonScore, "wilsonScore"); 0104 roles.insert(UsefulChoice, "usefulChoice"); 0105 roles.insert(Rating, "rating"); 0106 roles.insert(Summary, "summary"); 0107 roles.insert(Depth, "depth"); 0108 roles.insert(PackageVersion, "packageVersion"); 0109 return roles; 0110 } 0111 0112 QVariant ReviewsModel::data(const QModelIndex &index, int role) const 0113 { 0114 if (!index.isValid()) 0115 return QVariant(); 0116 switch (role) { 0117 case Qt::DisplayRole: 0118 return m_reviews.at(index.row())->reviewText(); 0119 case ShouldShow: 0120 return m_reviews.at(index.row())->shouldShow(); 0121 case Reviewer: 0122 return m_reviews.at(index.row())->reviewer(); 0123 case CreationDate: 0124 return m_reviews.at(index.row())->creationDate(); 0125 case UsefulnessTotal: 0126 return m_reviews.at(index.row())->usefulnessTotal(); 0127 case UsefulnessFavorable: 0128 return m_reviews.at(index.row())->usefulnessFavorable(); 0129 case WilsonScore: 0130 return m_reviews.at(index.row())->wilsonScore(); 0131 case UsefulChoice: 0132 return m_reviews.at(index.row())->usefulChoice(); 0133 case Rating: 0134 return m_reviews.at(index.row())->rating(); 0135 case Summary: 0136 return m_reviews.at(index.row())->summary(); 0137 case PackageVersion: 0138 return m_reviews.at(index.row())->packageVersion(); 0139 case Depth: 0140 return m_reviews.at(index.row())->getMetadata(QStringLiteral("NumberOfParents")).toInt(); 0141 } 0142 return QVariant(); 0143 } 0144 0145 int ReviewsModel::rowCount(const QModelIndex &parent) const 0146 { 0147 if (parent.isValid()) 0148 return 0; 0149 return m_reviews.count(); 0150 } 0151 0152 QString ReviewsModel::preferredSortRole() const 0153 { 0154 return m_preferredSortRole; 0155 } 0156 0157 void ReviewsModel::setPreferredSortRole(const QString &sorting) 0158 { 0159 if (m_preferredSortRole == sorting || !QStringList({QStringLiteral("wilsonScore"), QStringLiteral("date"), QStringLiteral("rating")}).contains(sorting)) { 0160 return; 0161 } 0162 0163 m_preferredSortRole = sorting; 0164 KSharedConfigPtr config = KSharedConfig::openConfig(); 0165 KConfigGroup configGroup(config, u"Reviews"_s); 0166 configGroup.writeEntry("PreferredSortRole", sorting); 0167 0168 Q_EMIT preferredSortRoleChanged(); 0169 } 0170 0171 AbstractResource *ReviewsModel::resource() const 0172 { 0173 return m_app; 0174 } 0175 0176 AbstractReviewsBackend *ReviewsModel::backend() const 0177 { 0178 return m_backend; 0179 } 0180 0181 void ReviewsModel::setResource(AbstractResource *app) 0182 { 0183 if (m_app != app) { 0184 beginResetModel(); 0185 m_starsCount.clear(); 0186 m_reviews.clear(); 0187 m_lastPage = 0; 0188 0189 if (m_backend) { 0190 disconnect(m_backend, &AbstractReviewsBackend::errorMessageChanged, this, &ReviewsModel::restartFetching); 0191 disconnect(m_backend, &AbstractReviewsBackend::reviewsReady, this, &ReviewsModel::addReviews); 0192 disconnect(m_backend, &AbstractReviewsBackend::fetchingChanged, this, &ReviewsModel::fetchingChanged); 0193 disconnect(m_app, &AbstractResource::versionsChanged, this, &ReviewsModel::restartFetching); 0194 } 0195 m_app = app; 0196 m_backend = app ? app->backend()->reviewsBackend() : nullptr; 0197 if (m_backend) { 0198 connect(m_backend, &AbstractReviewsBackend::errorMessageChanged, this, &ReviewsModel::restartFetching); 0199 connect(m_backend, &AbstractReviewsBackend::reviewsReady, this, &ReviewsModel::addReviews); 0200 connect(m_backend, &AbstractReviewsBackend::fetchingChanged, this, &ReviewsModel::fetchingChanged); 0201 connect(m_app, &AbstractResource::versionsChanged, this, &ReviewsModel::restartFetching); 0202 0203 QMetaObject::invokeMethod(this, &ReviewsModel::restartFetching, Qt::QueuedConnection); 0204 } 0205 endResetModel(); 0206 Q_EMIT rowsChanged(); 0207 Q_EMIT resourceChanged(); 0208 } 0209 } 0210 0211 void ReviewsModel::restartFetching() 0212 { 0213 if (!m_app || !m_backend) 0214 return; 0215 0216 m_canFetchMore = true; 0217 m_lastPage = 0; 0218 fetchMore(); 0219 Q_EMIT rowsChanged(); 0220 } 0221 0222 void ReviewsModel::fetchMore(const QModelIndex &parent) 0223 { 0224 if (!m_backend || !m_app || parent.isValid() || m_backend->isFetching() || !m_canFetchMore) 0225 return; 0226 0227 m_lastPage++; 0228 m_backend->fetchReviews(m_app, m_lastPage); 0229 // qCDebug(LIBDISCOVER_LOG) << "fetching reviews... " << m_lastPage; 0230 } 0231 0232 void ReviewsModel::addReviews(AbstractResource *app, const QVector<ReviewPtr> &reviews, bool canFetchMore) 0233 { 0234 if (app != m_app) 0235 return; 0236 0237 m_canFetchMore = canFetchMore; 0238 qCDebug(LIBDISCOVER_LOG) << "reviews arrived..." << m_lastPage << reviews.size(); 0239 0240 if (!reviews.isEmpty()) { 0241 for (ReviewPtr r : reviews) { 0242 m_starsCount.addRating(r->rating()); 0243 } 0244 beginInsertRows(QModelIndex(), rowCount(), rowCount() + reviews.size() - 1); 0245 m_reviews += reviews; 0246 endInsertRows(); 0247 Q_EMIT rowsChanged(); 0248 } 0249 } 0250 0251 bool ReviewsModel::canFetchMore(const QModelIndex & /*parent*/) const 0252 { 0253 return m_canFetchMore; 0254 } 0255 0256 void ReviewsModel::markUseful(int row, bool useful) 0257 { 0258 Review *r = m_reviews[row].data(); 0259 r->setUsefulChoice(useful ? Yes : No); 0260 // qCDebug(LIBDISCOVER_LOG) << "submitting usefulness" << r->applicationName() << r->id() << useful; 0261 m_backend->submitUsefulness(r, useful); 0262 const QModelIndex ind = index(row, 0, QModelIndex()); 0263 Q_EMIT dataChanged(ind, ind, {UsefulnessTotal, UsefulnessFavorable, UsefulChoice}); 0264 } 0265 0266 void ReviewsModel::deleteReview(int row) 0267 { 0268 Review *r = m_reviews[row].data(); 0269 m_backend->deleteReview(r); 0270 } 0271 0272 void ReviewsModel::flagReview(int row, const QString &reason, const QString &text) 0273 { 0274 Review *r = m_reviews[row].data(); 0275 m_backend->flagReview(r, reason, text); 0276 } 0277 0278 StarsCount ReviewsModel::starsCount() const 0279 { 0280 return m_starsCount; 0281 } 0282 0283 bool ReviewsModel::isFetching() const 0284 { 0285 return m_backend && m_backend->isFetching(); 0286 } 0287 0288 #include "moc_ReviewsModel.cpp"