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