MythTV  master
sourceManager.cpp
Go to the documentation of this file.
1 
2 // QT headers
3 #include <QApplication>
4 #include <QDir>
5 #include <QFileInfo>
6 #include <QString>
7 #include <QStringList>
8 
9 // MythTV headers
11 #include <libmythbase/mythdb.h>
12 #include <libmythbase/mythdbcon.h>
13 #include <libmythbase/mythdirs.h>
16 
17 // MythWeather headers
18 #include "sourceManager.h"
19 #include "weatherScreen.h"
20 #include "weatherSource.h"
21 
22 #define LOC QString("SourceManager: ")
23 #define LOC_ERR QString("SourceManager Error: ")
24 
26 {
27  findScriptsDB();
28  setupSources();
29 }
30 
32 {
33  clearSources();
34 }
35 
37 {
39  QString query =
40  "SELECT DISTINCT wss.sourceid, source_name, update_timeout, "
41  "retrieve_timeout, path, author, version, email, types "
42  "FROM weathersourcesettings wss "
43  "LEFT JOIN weatherdatalayout wdl "
44  "ON wss.sourceid = wdl.weathersourcesettings_sourceid "
45  "WHERE hostname = :HOST;";
46 
47  db.prepare(query);
48  db.bindValue(":HOST", gCoreContext->GetHostName());
49  if (!db.exec())
50  {
51  MythDB::DBError("Finding weather source scripts for host", db);
52  return false;
53  }
54 
55  while (db.next())
56  {
57  QFileInfo fi(db.value(4).toString());
58 
59  if (!fi.isExecutable())
60  {
61  // scripts will be deleted from db in the more robust (i.e. slower)
62  // findScripts() -- run when entering setup
63  continue;
64  }
65  auto *si = new ScriptInfo;
66  si->id = db.value(0).toInt();
67  si->name = db.value(1).toString();
68  si->updateTimeout = std::chrono::seconds(db.value(2).toUInt());
69  si->scriptTimeout = std::chrono::seconds(db.value(3).toUInt());
70  si->path = fi.absolutePath();
71  si->program = fi.absoluteFilePath();
72  si->author = db.value(5).toString();
73  si->version = db.value(6).toString();
74  si->email = db.value(7).toString();
75  si->types = db.value(8).toString().split(",");
76  m_scripts.append(si);
77  }
78 
79  return true;
80 }
81 
83 {
84  QString path = GetShareDir() + "mythweather/scripts/";
85  QDir dir(path);
86  dir.setFilter(QDir::Executable | QDir::Files | QDir::Dirs);
87 
88  if (!dir.exists())
89  {
90  LOG(VB_GENERAL, LOG_ERR, "MythWeather: Scripts directory not found");
91  return false;
92  }
93  QString busymessage = tr("Searching for scripts");
94 
95  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("weather stack");
96  if (popupStack == nullptr)
97  popupStack = GetMythMainWindow()->GetStack("popup stack");
98 
99  auto *busyPopup = new MythUIBusyDialog(busymessage, popupStack,
100  "mythweatherbusydialog");
101 
102  if (busyPopup->Create())
103  {
104  popupStack->AddScreen(busyPopup, false);
105  }
106  else
107  {
108  delete busyPopup;
109  busyPopup = nullptr;
110  }
111 
112  QCoreApplication::processEvents();
113 
114  recurseDirs(dir);
115 
116  // run through and see if any scripts have been deleted
118 
119  db.prepare("SELECT sourceid, path FROM weathersourcesettings "
120  "WHERE hostname = :HOST;");
121  db.bindValue(":HOST", gCoreContext->GetHostName());
122  if (!db.exec())
123  MythDB::DBError("SourceManager::findScripts - select", db);
124  QStringList toRemove;
125  while (db.next())
126  {
127  QFileInfo fi(db.value(1).toString());
128  if (!fi.isExecutable())
129  {
130  toRemove << db.value(0).toString();
131  LOG(VB_GENERAL, LOG_ERR, QString("'%1' no longer exists")
132  .arg(fi.absoluteFilePath()));
133  }
134  }
135 
136  db.prepare("DELETE FROM weathersourcesettings WHERE sourceid = :ID;");
137  for (int i = 0; i < toRemove.count(); ++i)
138  {
139  db.bindValue(":ID", toRemove[i]);
140  if (!db.exec())
141  {
142  // MythDB::DBError("Deleting weather source settings", db);
143  }
144  }
145 
146  if (busyPopup)
147  {
148  busyPopup->Close();
149  busyPopup = nullptr;
150  }
151 
152  return m_scripts.count() > 0;
153 }
154 
156 {
157  while (!m_scripts.isEmpty())
158  delete m_scripts.takeFirst();
159  m_scripts.clear();
160 
161  while (!m_sources.isEmpty())
162  delete m_sources.takeFirst();
163  m_sources.clear();
164 }
165 
167 {
169 
170  db.prepare(
171  "SELECT DISTINCT location, weathersourcesettings_sourceid, "
172  " weatherscreens.units, weatherscreens.screen_id "
173  "FROM weatherdatalayout,weatherscreens "
174  "WHERE weatherscreens.screen_id = weatherscreens_screen_id AND "
175  " weatherscreens.hostname = :HOST");
176  db.bindValue(":HOST", gCoreContext->GetHostName());
177  if (!db.exec())
178  {
179  MythDB::DBError("Finding weather sources for this host", db);
180  return;
181  }
182 
183  m_sourcemap.clear();
184 
185  while (db.next())
186  {
187  QString loc = db.value(0).toString();
188  uint sourceid = db.value(1).toUInt();
189  units_t units = db.value(2).toUInt();
190  uint screen = db.value(3).toUInt();
191  const WeatherSource *src = needSourceFor(sourceid, loc, units);
192  if (src)
193  m_sourcemap.insert((long)screen, src);
194  }
195 }
196 
198 {
199  ScriptInfo *src = nullptr;
200  for (auto *script : std::as_const(m_scripts))
201  {
202  src = script;
203  if (src->name == name)
204  {
205  return src;
206  }
207  }
208 
209  if (!src)
210  {
211  LOG(VB_GENERAL, LOG_ERR, "No Source found for " + name);
212  }
213 
214  return nullptr;
215 }
216 
217 QStringList SourceManager::getLocationList(ScriptInfo *si, const QString &str)
218 {
219  if (!m_scripts.contains(si))
220  return {};
221  auto *ws = new WeatherSource(si);
222 
223  QStringList locationList(ws->getLocationList(str));
224 
225  delete ws;
226 
227  return locationList;
228 }
229 
230 WeatherSource *SourceManager::needSourceFor(int id, const QString &loc,
231  units_t units)
232 {
233  // matching source exists?
234  for (auto *src : std::as_const(m_sources))
235  {
236  if (src->getId() == id && src->getLocale() == loc &&
237  src->getUnits() == units)
238  {
239  return src;
240  }
241  }
242 
243  // no matching source, make one
244  auto idmatch = [id](auto *si){ return si->id == id; };
245  auto it = std::find_if(m_scripts.cbegin(), m_scripts.cend(), idmatch);
246  if (it != m_scripts.cend())
247  {
248  auto *ws = new WeatherSource(*it);
249  ws->setLocale(loc);
250  ws->setUnits(units);
251  m_sources.append(ws);
252  return ws;
253  }
254 
255  LOG(VB_GENERAL, LOG_ERR, LOC +
256  QString("NeedSourceFor: Unable to find source for %1, %2, %3")
257  .arg(id).arg(loc).arg(units));
258  return nullptr;
259 }
260 
262 {
263  for (auto *src : std::as_const(m_sources))
264  src->startUpdateTimer();
265 }
266 
268 {
269  for (auto *src : std::as_const(m_sources))
270  src->stopUpdateTimer();
271 }
272 
273 void SourceManager::doUpdate(bool forceUpdate)
274 {
275  for (auto *src : std::as_const(m_sources))
276  {
277  if (src->inUse())
278  src->startUpdate(forceUpdate);
279  }
280 }
281 
283  QList<ScriptInfo *> &sources)
284 {
285  for (auto *si : std::as_const(m_scripts))
286  {
287  QStringList stypes = si->types;
288  bool handled = true;
289  for (int i = 0; i < types.count() && handled; ++i)
290  {
291  handled = stypes.contains(types[i]);
292  }
293  if (handled)
294  sources.append(si);
295  }
296 
297  return sources.count() != 0;
298 }
299 
301 {
302  if (!screen)
303  {
304  LOG(VB_GENERAL, LOG_ERR, LOC +
305  QString("Cannot connect nonexistent screen 0x%1")
306  .arg((uint64_t)screen,0,16));
307 
308  return false;
309  }
310 
311  SourceMap::iterator it = m_sourcemap.find(id);
312  if (it == m_sourcemap.end())
313  {
314  LOG(VB_GENERAL, LOG_ERR, LOC +
315  QString("Cannot connect nonexistent source '%1'").arg(id));
316 
317  return false;
318  }
319 
320  (const_cast<WeatherSource*>(*it))->connectScreen(screen);
321 
322  return true;
323 }
324 
326 {
327  if (!screen)
328  {
329  LOG(VB_GENERAL, LOG_ERR, LOC +
330  QString("Cannot disconnect nonexistent screen 0x%1")
331  .arg((uint64_t)screen,0,16));
332 
333  return false;
334  }
335 
336  SourceMap::iterator it = m_sourcemap.find(screen->getId());
337  if (it == m_sourcemap.end())
338  {
339  LOG(VB_GENERAL, LOG_ERR, LOC +
340  QString("Cannot disconnect nonexistent source %1")
341  .arg(screen->getId()));
342 
343  return false;
344  }
345 
346  (const_cast<WeatherSource*>(*it))->disconnectScreen(screen);
347 
348  return true;
349 }
350 
351 // Recurses dir for script files
353 {
354  if (!dir.exists())
355  return;
356 
357  dir.setFilter(QDir::Executable | QDir::Files | QDir::Dirs |
358  QDir::NoDotAndDotDot);
359  QFileInfoList files = dir.entryInfoList();
360 
361  for (const auto & file : std::as_const(files))
362  {
363  QCoreApplication::processEvents();
364  if (file.isDir())
365  {
366  QDir recurseTo(file.filePath());
367  recurseDirs(recurseTo);
368  }
369 
370  if (file.isExecutable() && !(file.isDir()))
371  {
373  if (info)
374  {
375  m_scripts.append(info);
376  LOG(VB_FILE, LOG_INFO, QString("Found Script '%1'")
377  .arg(file.absoluteFilePath()));
378  }
379  }
380  }
381 }
MSqlQuery::next
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:812
MSqlQuery
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:127
sourceManager.h
mythdb.h
MSqlQuery::value
QVariant value(int i) const
Definition: mythdbcon.h:204
MythScreenStack
Definition: mythscreenstack.h:16
mythdbcon.h
MSqlQuery::exec
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:618
types
static const struct wl_interface * types[]
Definition: idle_inhibit_unstable_v1.c:39
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
build_compdb.file
file
Definition: build_compdb.py:55
mythdirs.h
WeatherSource
Definition: weatherSource.h:36
SourceManager::m_sources
QList< WeatherSource * > m_sources
Definition: sourceManager.h:44
weatherSource.h
SourceManager::startTimers
void startTimers()
Definition: sourceManager.cpp:261
mythprogressdialog.h
SourceManager::recurseDirs
void recurseDirs(QDir dir)
Definition: sourceManager.cpp:352
SourceManager::stopTimers
void stopTimers()
Definition: sourceManager.cpp:267
SourceManager::findScripts
bool findScripts()
Definition: sourceManager.cpp:82
SourceManager::m_scripts
QList< ScriptInfo * > m_scripts
Definition: sourceManager.h:43
mythlogging.h
MSqlQuery::InitCon
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:550
SourceManager::~SourceManager
~SourceManager() override
Definition: sourceManager.cpp:31
MythDB::DBError
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:226
GetShareDir
QString GetShareDir(void)
Definition: mythdirs.cpp:261
LOC
#define LOC
Definition: sourceManager.cpp:22
SourceManager::disconnectScreen
bool disconnectScreen(WeatherScreen *screen)
Definition: sourceManager.cpp:325
SourceManager::doUpdate
void doUpdate(bool forceUpdate=false)
Definition: sourceManager.cpp:273
SourceManager::connectScreen
bool connectScreen(uint id, WeatherScreen *screen)
Definition: sourceManager.cpp:300
MythUIBusyDialog
Definition: mythprogressdialog.h:36
SourceManager::findScriptsDB
bool findScriptsDB()
Definition: sourceManager.cpp:36
SourceManager::clearSources
void clearSources()
Definition: sourceManager.cpp:155
WeatherScreen
Weather screen.
Definition: weatherScreen.h:26
gCoreContext
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
Definition: mythcorecontext.cpp:57
SourceManager::setupSources
void setupSources()
Definition: sourceManager.cpp:166
units_t
unsigned char units_t
Definition: weatherUtils.h:25
SourceManager::getSourceByName
ScriptInfo * getSourceByName(const QString &name)
Definition: sourceManager.cpp:197
SourceManager::getLocationList
QStringList getLocationList(ScriptInfo *si, const QString &str)
Definition: sourceManager.cpp:217
mythcorecontext.h
MSqlQuery::bindValue
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:888
GetMythMainWindow
MythMainWindow * GetMythMainWindow(void)
Definition: mythmainwindow.cpp:104
SourceManager::findPossibleSources
bool findPossibleSources(QStringList types, QList< ScriptInfo * > &sources)
Definition: sourceManager.cpp:282
MythMainWindow::GetStack
MythScreenStack * GetStack(const QString &Stackname)
Definition: mythmainwindow.cpp:322
WeatherScreen::getId
int getId() const
Definition: weatherScreen.h:49
ScriptInfo::name
QString name
Definition: weatherSource.h:24
MythCoreContext::GetHostName
QString GetHostName(void)
Definition: mythcorecontext.cpp:844
weatherScreen.h
WeatherSource::ProbeScript
static ScriptInfo * ProbeScript(const QFileInfo &fi)
Definition: weatherSource.cpp:200
azlyrics.info
dictionary info
Definition: azlyrics.py:7
SourceManager::needSourceFor
WeatherSource * needSourceFor(int id, const QString &loc, units_t units)
Definition: sourceManager.cpp:230
ScriptInfo
Definition: serverSideScripting.h:36
SourceManager::SourceManager
SourceManager()
Definition: sourceManager.cpp:25
MythScreenStack::AddScreen
virtual void AddScreen(MythScreenType *screen, bool allowFade=true)
Definition: mythscreenstack.cpp:52
uint
unsigned int uint
Definition: freesurround.h:24
MSqlQuery::prepare
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:837
SourceManager::m_sourcemap
SourceMap m_sourcemap
Definition: sourceManager.h:45
ScriptInfo::id
int id
Definition: weatherSource.h:33