1 // For license of this file, see <project-root-folder>/LICENSE.md.
2 
3 #include "core/messageobject.h"
4 
5 #include "3rd-party/boolinq/boolinq.h"
6 #include "database/databasefactory.h"
7 
8 #include <QSqlDatabase>
9 #include <QSqlError>
10 #include <QSqlQuery>
11 
MessageObject(QSqlDatabase * db,const QString & feed_custom_id,int account_id,const QList<Label * > & available_labels,bool is_new_message,QObject * parent)12 MessageObject::MessageObject(QSqlDatabase* db, const QString& feed_custom_id, int account_id,
13                              const QList<Label*>& available_labels, bool is_new_message, QObject* parent)
14   : QObject(parent), m_db(db), m_feedCustomId(feed_custom_id), m_accountId(account_id), m_message(nullptr),
15   m_availableLabels(available_labels), m_runningAfterFetching(is_new_message) {}
16 
setMessage(Message * message)17 void MessageObject::setMessage(Message* message) {
18   m_message = message;
19 }
20 
isDuplicate(DuplicationAttributeCheck attribute_check) const21 bool MessageObject::isDuplicate(DuplicationAttributeCheck attribute_check) const {
22   return isDuplicateWithAttribute(attribute_check);
23 }
24 
isDuplicateWithAttribute(MessageObject::DuplicationAttributeCheck attribute_check) const25 bool MessageObject::isDuplicateWithAttribute(MessageObject::DuplicationAttributeCheck attribute_check) const {
26   // Check database according to duplication attribute_check.
27   QSqlQuery q(*m_db);
28   QStringList where_clauses;
29   QVector<QPair<QString, QVariant>> bind_values;
30 
31   // Now we construct the query according to parameter.
32   if ((attribute_check& DuplicationAttributeCheck::SameTitle) == DuplicationAttributeCheck::SameTitle) {
33     where_clauses.append(QSL("title = :title"));
34     bind_values.append({ QSL(":title"), title() });
35   }
36 
37   if ((attribute_check& DuplicationAttributeCheck::SameUrl) == DuplicationAttributeCheck::SameUrl) {
38     where_clauses.append(QSL("url = :url"));
39     bind_values.append({ QSL(":url"), url() });
40   }
41 
42   if ((attribute_check& DuplicationAttributeCheck::SameAuthor) == DuplicationAttributeCheck::SameAuthor) {
43     where_clauses.append(QSL("author = :author"));
44     bind_values.append({ QSL(":author"), author() });
45   }
46 
47   if ((attribute_check& DuplicationAttributeCheck::SameDateCreated) == DuplicationAttributeCheck::SameDateCreated) {
48     where_clauses.append(QSL("date_created = :date_created"));
49     bind_values.append({ QSL(":date_created"), created().toMSecsSinceEpoch() });
50   }
51 
52   if ((attribute_check& DuplicationAttributeCheck::SameCustomId) == DuplicationAttributeCheck::SameCustomId) {
53     where_clauses.append(QSL("custom_id = :custom_id"));
54     bind_values.append({ QSL(":custom_id"), customId() });
55   }
56 
57   where_clauses.append(QSL("account_id = :account_id"));
58   bind_values.append({ QSL(":account_id"), accountId() });
59 
60   if ((attribute_check& DuplicationAttributeCheck::AllFeedsSameAccount) != DuplicationAttributeCheck::AllFeedsSameAccount) {
61     // Limit to current feed.
62     where_clauses.append(QSL("feed = :feed"));
63     bind_values.append({ QSL(":feed"), feedCustomId() });
64   }
65 
66   QString full_query = QSL("SELECT COUNT(*) FROM Messages WHERE ") + where_clauses.join(QSL(" AND ")) + QSL(";");
67 
68   qDebugNN << LOGSEC_MESSAGEMODEL
69            << "Prepared query for MSG duplicate identification is:"
70            << QUOTE_W_SPACE_DOT(full_query);
71 
72   q.setForwardOnly(true);
73   q.prepare(full_query);
74 
75   for (const auto& bind : bind_values) {
76     q.bindValue(bind.first, bind.second);
77   }
78 
79   if (q.exec() && q.next()) {
80     qDebugNN << LOGSEC_DB
81              << "Executed SQL for message duplicates check:"
82              << QUOTE_W_SPACE_DOT(DatabaseFactory::lastExecutedQuery(q));
83 
84     if (q.record().value(0).toInt() > 0) {
85       // Whoops, we have the "same" message in database.
86       qDebugNN << LOGSEC_CORE
87                << "Message"
88                << QUOTE_W_SPACE(title())
89                << "was identified as duplicate by filter script.";
90       return true;
91     }
92   }
93   else if (q.lastError().isValid()) {
94     qWarningNN << LOGSEC_CORE
95                << "Error when checking for duplicate messages via filtering system, error:"
96                << QUOTE_W_SPACE_DOT(q.lastError().text());
97   }
98 
99   return false;
100 }
101 
assignLabel(const QString & label_custom_id) const102 bool MessageObject::assignLabel(const QString& label_custom_id) const {
103   if (m_message->m_id <= 0 && m_message->m_customId.isEmpty()) {
104     return false;
105   }
106 
107   Label* lbl = boolinq::from(m_availableLabels).firstOrDefault([label_custom_id](Label* lbl) {
108     return lbl->customId() == label_custom_id;
109   });
110 
111   if (lbl != nullptr) {
112     if (!m_message->m_assignedLabels.contains(lbl)) {
113       m_message->m_assignedLabels.append(lbl);
114     }
115 
116     return true;
117   }
118   else {
119     return false;
120   }
121 }
122 
deassignLabel(const QString & label_custom_id) const123 bool MessageObject::deassignLabel(const QString& label_custom_id) const {
124   if (m_message->m_id <= 0 && m_message->m_customId.isEmpty()) {
125     return false;
126   }
127 
128   Label* lbl = boolinq::from(m_message->m_assignedLabels).firstOrDefault([label_custom_id](Label* lbl) {
129     return lbl->customId() == label_custom_id;
130   });
131 
132   if (lbl != nullptr) {
133     m_message->m_assignedLabels.removeAll(lbl);
134     return true;
135   }
136   else {
137     return false;
138   }
139 }
140 
title() const141 QString MessageObject::title() const {
142   return m_message->m_title;
143 }
144 
setTitle(const QString & title)145 void MessageObject::setTitle(const QString& title) {
146   m_message->m_title = title;
147 }
148 
url() const149 QString MessageObject::url() const {
150   return m_message->m_url;
151 }
152 
setUrl(const QString & url)153 void MessageObject::setUrl(const QString& url) {
154   m_message->m_url = url;
155 }
156 
author() const157 QString MessageObject::author() const {
158   return m_message->m_author;
159 }
160 
setAuthor(const QString & author)161 void MessageObject::setAuthor(const QString& author) {
162   m_message->m_author = author;
163 }
164 
contents() const165 QString MessageObject::contents() const {
166   return m_message->m_contents;
167 }
168 
setContents(const QString & contents)169 void MessageObject::setContents(const QString& contents) {
170   m_message->m_contents = contents;
171 }
172 
rawContents() const173 QString MessageObject::rawContents() const {
174   return m_message->m_rawContents;
175 }
176 
setRawContents(const QString & raw_contents)177 void MessageObject::setRawContents(const QString& raw_contents) {
178   m_message->m_rawContents = raw_contents;
179 }
180 
created() const181 QDateTime MessageObject::created() const {
182   return m_message->m_created;
183 }
184 
setCreated(const QDateTime & created)185 void MessageObject::setCreated(const QDateTime& created) {
186   m_message->m_created = created;
187 }
188 
isRead() const189 bool MessageObject::isRead() const {
190   return m_message->m_isRead;
191 }
192 
setIsRead(bool is_read)193 void MessageObject::setIsRead(bool is_read) {
194   m_message->m_isRead = is_read;
195 }
196 
isImportant() const197 bool MessageObject::isImportant() const {
198   return m_message->m_isImportant;
199 }
200 
setIsImportant(bool is_important)201 void MessageObject::setIsImportant(bool is_important) {
202   m_message->m_isImportant = is_important;
203 }
204 
isDeleted() const205 bool MessageObject::isDeleted() const {
206   return m_message->m_isDeleted;
207 }
208 
setIsDeleted(bool is_deleted)209 void MessageObject::setIsDeleted(bool is_deleted) {
210   m_message->m_isDeleted = is_deleted;
211 }
212 
score() const213 double MessageObject::score() const {
214   return m_message->m_score;
215 }
216 
setScore(double score)217 void MessageObject::setScore(double score) {
218   m_message->m_score = score;
219 }
220 
feedCustomId() const221 QString MessageObject::feedCustomId() const {
222   if (m_feedCustomId.isEmpty() || m_feedCustomId == QString::number(NO_PARENT_CATEGORY)) {
223     return m_message->m_feedId;
224   }
225   else {
226     return m_feedCustomId;
227   }
228 }
229 
accountId() const230 int MessageObject::accountId() const {
231   return m_accountId;
232 }
233 
customId() const234 QString MessageObject::customId() const {
235   return m_message->m_customId;
236 }
237 
id() const238 int MessageObject::id() const {
239   return m_message->m_id;
240 }
241 
assignedLabels() const242 QList<Label*> MessageObject::assignedLabels() const {
243   return m_message->m_assignedLabels;
244 }
245 
availableLabels() const246 QList<Label*> MessageObject::availableLabels() const {
247   return m_availableLabels;
248 }
249 
runningFilterWhenFetching() const250 bool MessageObject::runningFilterWhenFetching() const {
251   return m_runningAfterFetching;
252 }
253