diff --git a/database-updater/DatabaseUpdater.cpp b/database-updater/DatabaseUpdater.cpp index 756bb7c5..c7f06545 100644 --- a/database-updater/DatabaseUpdater.cpp +++ b/database-updater/DatabaseUpdater.cpp @@ -10,6 +10,45 @@ #include "Checksum.hpp" #include "DatabaseUpdater.hpp" +namespace { + +boost::gregorian::date +getNextDay(const boost::gregorian::date& current) +{ + boost::gregorian::day_iterator it(current); + return *(++it); +} + +boost::gregorian::date +getNextMonday(const boost::gregorian::date& current) +{ + boost::gregorian::day_iterator it(current); + + ++it; + // While it's not monday + while( it->day_of_week() != 1 ) + ++it; + + return *(it); +} + +boost::gregorian::date +getNextFirstOfMonth(const boost::gregorian::date& current) +{ + boost::gregorian::day_iterator it(current); + + ++it; + // While it's not the 1st of the month + while( it->day() != 1 ) + ++it; + + return (*it); +} + + +} + + namespace DatabaseUpdater { using namespace Database; @@ -69,13 +108,44 @@ Updater::processNextJob(void) MediaDirectorySettings::pointer settings = MediaDirectorySettings::get(_db.getSession()); - if (settings->getManualScanRequested()) + if (settings->getManualScanRequested()) { + std::cout << "Manual scan requested!" << std::endl; scheduleScan( boost::posix_time::seconds(0) ); + } else { -// boost::posix_time::ptime now = boost::posix_time::second_clock::local_time(); + boost::posix_time::ptime now = boost::posix_time::second_clock::local_time(); + boost::posix_time::time_duration startTime = settings->getUpdateStartTime(); - // TODO + boost::gregorian::date nextScanDate; + + switch( settings->getUpdatePeriod() ) + { + case Database::MediaDirectorySettings::Never: + // Nothing to do + break; + case Database::MediaDirectorySettings::Daily: + if (now.time_of_day() < startTime) + nextScanDate = now.date(); + else + nextScanDate = getNextDay(now.date()); + break; + case Database::MediaDirectorySettings::Weekly: + if (now.time_of_day() < startTime && now.date().day_of_week() == 1) + nextScanDate = now.date(); + else + nextScanDate = getNextMonday(now.date()); + break; + case Database::MediaDirectorySettings::Monthly: + if (now.time_of_day() < startTime && now.date().day() == 1) + nextScanDate = now.date(); + else + nextScanDate = getNextFirstOfMonth(now.date()); + break; + } + + if (!nextScanDate.is_special()) + scheduleScan( boost::posix_time::ptime (nextScanDate, settings->getUpdateStartTime() ) ); } } @@ -90,6 +160,7 @@ Updater::scheduleScan( boost::posix_time::time_duration duration) void Updater::scheduleScan( boost::posix_time::ptime time) { + std::cout << "Scheduling next scan at " << time << std::endl; _scheduleTimer.expires_at(time); _scheduleTimer.async_wait( boost::bind( &Updater::process, this, boost::asio::placeholders::error) ); } @@ -135,7 +206,7 @@ Updater::process(boost::system::error_code err) // If the manual scan was required we can now set it to done // Update only if the scan is complete! - if (settings->getManualScanRequested() & _running) + if (settings->getManualScanRequested() && _running) settings.modify()->setManualScanRequested(false); } diff --git a/database/MediaDirectory.hpp b/database/MediaDirectory.hpp index d466f704..ae0e2912 100644 --- a/database/MediaDirectory.hpp +++ b/database/MediaDirectory.hpp @@ -16,6 +16,13 @@ class MediaDirectorySettings { public: + enum UpdatePeriod { + Never, + Daily, + Weekly, + Monthly + }; + typedef Wt::Dbo::ptr pointer; MediaDirectorySettings() {} @@ -25,17 +32,17 @@ class MediaDirectorySettings // write accessors void setManualScanRequested(bool value) { _manualScanRequested = value;} - void setUpdatePeriod(boost::posix_time::time_duration dur) { _updatePeriod = dur;} + void setUpdatePeriod(UpdatePeriod period) { _updatePeriod = period;} void setUpdateStartTime(boost::posix_time::time_duration dur) { _updateStartTime = dur;} void setLastUpdate(boost::posix_time::ptime time) { _lastUpdate = time; } void setLastScan(boost::posix_time::ptime time) { _lastScan = time; } // Read accessors bool getManualScanRequested(void) const { return _manualScanRequested; } - boost::posix_time::time_duration getUpdatePeriod(void) const { return _updatePeriod; } + UpdatePeriod getUpdatePeriod(void) const { return _updatePeriod; } boost::posix_time::time_duration getUpdateStartTime(void) const { return _updateStartTime; } boost::posix_time::ptime getLastUpdated(void) const { return _lastUpdate; } - boost::posix_time::ptime getLastScan(void) const { return _lastScan; } + boost::posix_time::ptime getLastScan(void) const { return _lastScan; } template void persist(Action& a) @@ -51,7 +58,7 @@ class MediaDirectorySettings private: bool _manualScanRequested; // Immadiate scan has been requested by user - boost::posix_time::time_duration _updatePeriod; // How long between updates + UpdatePeriod _updatePeriod; // How long between updates boost::posix_time::time_duration _updateStartTime; // Time of day to begin the update boost::posix_time::ptime _lastUpdate; // last time the database has changed boost::posix_time::ptime _lastScan; // last time the database has been scanned diff --git a/ui/approot/templates.xml b/ui/approot/templates.xml index e5300643..b0ad9eec 100644 --- a/ui/approot/templates.xml +++ b/ui/approot/templates.xml @@ -245,20 +245,9 @@ -
- -
- ${update-request-immediate-scan} -
-
- ${update-request-immediate-scan-info} -
-
- ${apply-button} ${discard-button} + ${apply-button} ${discard-button} ${immediate-scan-button}
${apply-info} diff --git a/ui/settings/Settings.cpp b/ui/settings/Settings.cpp index 2434643b..a01c9da4 100644 --- a/ui/settings/Settings.cpp +++ b/ui/settings/Settings.cpp @@ -48,11 +48,11 @@ _sessionData(sessionData) if (user->isAdmin()) { MediaDirectories* mediaDirectory = new MediaDirectories(sessionData); - mediaDirectory->changed().connect(this, &Settings::handleDatabaseSettingsChanged); + mediaDirectory->changed().connect(this, &Settings::handleDatabaseDirectoriesChanged); menu->addItem("Media Folders", mediaDirectory); DatabaseFormView* databaseFormView = new DatabaseFormView(sessionData); - databaseFormView->changed().connect(this, &Settings::handleDatabaseSettingsChanged); + databaseFormView->changed().connect(this, &Settings::restartDatabaseUpdateService); menu->addItem("Database Update", databaseFormView); menu->addItem("Users", new Users(sessionData)); @@ -65,14 +65,21 @@ _sessionData(sessionData) } void -Settings::handleDatabaseSettingsChanged() +Settings::handleDatabaseDirectoriesChanged() { - // On settings change, request an immediate scan + std::cout << "Media directories have changed: requesting imediate scan" << std::endl; + // On directory add or delete, request an immediate scan { Wt::Dbo::Transaction transaction(_sessionData.getDatabaseHandler().getSession()); Database::MediaDirectorySettings::get(_sessionData.getDatabaseHandler().getSession()).modify()->setManualScanRequested(true); } + restartDatabaseUpdateService(); +} + +void +Settings::restartDatabaseUpdateService() +{ // Restarting the update service boost::lock_guard serviceLock (ServiceManager::instance().mutex()); diff --git a/ui/settings/Settings.hpp b/ui/settings/Settings.hpp index eb9c53ca..8ef1e4ce 100644 --- a/ui/settings/Settings.hpp +++ b/ui/settings/Settings.hpp @@ -12,7 +12,9 @@ class Settings : public Wt::WContainerWidget private: - void handleDatabaseSettingsChanged(); + void handleDatabaseDirectoriesChanged(); + + void restartDatabaseUpdateService(void); SessionData& _sessionData; diff --git a/ui/settings/SettingsDatabaseFormView.cpp b/ui/settings/SettingsDatabaseFormView.cpp index 59a37029..e26a5fe0 100644 --- a/ui/settings/SettingsDatabaseFormView.cpp +++ b/ui/settings/SettingsDatabaseFormView.cpp @@ -1,9 +1,8 @@ -#include #include #include #include #include -#include +#include #include #include @@ -23,7 +22,6 @@ class DatabaseFormModel : public Wt::WFormModel // Associate each field with a unique string literal. static const Field UpdatePeriodField; static const Field UpdateStartTimeField; - static const Field UpdateRequestImmediateField; DatabaseFormModel(SessionData& sessionData, Wt::WObject *parent = 0) : Wt::WFormModel(parent), @@ -33,11 +31,9 @@ class DatabaseFormModel : public Wt::WFormModel addField(UpdatePeriodField); addField(UpdateStartTimeField); - addField(UpdateRequestImmediateField); setValidator(UpdatePeriodField, createUpdatePeriodValidator()); setValidator(UpdateStartTimeField, createStartTimeValidator()); - setValidator(UpdateRequestImmediateField, createRequestImmediateFieldValidator()); // populate the model with initial data loadData(); @@ -61,7 +57,6 @@ class DatabaseFormModel : public Wt::WFormModel if (startTimeRow != -1) setValue(UpdateStartTimeField, updateStartTime( startTimeRow ) ); - setValue(UpdateRequestImmediateField, false); } void saveData() @@ -79,9 +74,26 @@ class DatabaseFormModel : public Wt::WFormModel assert(startTimeRow != -1); settings.modify()->setUpdateStartTime( updateStartTimeDuration( startTimeRow ) ); - settings.modify()->setManualScanRequested( boost::any_cast(value(UpdateRequestImmediateField )) ); } + bool setImmediateScan(Wt::WString& error) + { + try { + Wt::Dbo::Session& session( _sessionData.getDatabaseHandler().getSession()); + Wt::Dbo::Transaction transaction(session); + + ::Database::MediaDirectorySettings::pointer settings = ::Database::MediaDirectorySettings::get(_sessionData.getDatabaseHandler().getSession() ); + + settings.modify()->setManualScanRequested( true ); + } + catch(Wt::Dbo::Exception& exception) + { + std::cerr << "Dbo exception: " << exception.what() << std::endl; + return false; + } + + return true; + } int getUpdatePeriodModelRow(Wt::WString value) { @@ -93,7 +105,7 @@ class DatabaseFormModel : public Wt::WFormModel return -1; } - int getUpdatePeriodModelRow(boost::posix_time::time_duration duration) + int getUpdatePeriodModelRow(Database::MediaDirectorySettings::UpdatePeriod duration) { for (int i = 0; i < _updatePeriodModel->rowCount(); ++i) { @@ -104,8 +116,8 @@ class DatabaseFormModel : public Wt::WFormModel return -1; } - boost::posix_time::time_duration updatePeriodDuration(int row) { - return boost::any_cast + Database::MediaDirectorySettings::UpdatePeriod updatePeriodDuration(int row) { + return boost::any_cast (_updatePeriodModel->data(_updatePeriodModel->index(row, 0), Wt::UserRole)); } @@ -155,17 +167,16 @@ class DatabaseFormModel : public Wt::WFormModel _updatePeriodModel = new Wt::WStringListModel(this); _updatePeriodModel->addString("Never"); - _updatePeriodModel->setData(0, 0, boost::posix_time::time_duration( boost::posix_time::hours(0) ), Wt::UserRole); + _updatePeriodModel->setData(0, 0, Database::MediaDirectorySettings::Never, Wt::UserRole); _updatePeriodModel->addString("Daily"); - _updatePeriodModel->setData(1, 0, boost::posix_time::time_duration( boost::posix_time::hours(24) ), Wt::UserRole); + _updatePeriodModel->setData(1, 0, Database::MediaDirectorySettings::Daily, Wt::UserRole); _updatePeriodModel->addString("Weekly"); - _updatePeriodModel->setData(2, 0, boost::posix_time::time_duration( boost::posix_time::hours(24*7) ), Wt::UserRole); + _updatePeriodModel->setData(2, 0, Database::MediaDirectorySettings::Weekly, Wt::UserRole); _updatePeriodModel->addString("Monthly"); - _updatePeriodModel->setData(3, 0, boost::posix_time::time_duration(boost::posix_time::hours(24*30) ), Wt::UserRole); - + _updatePeriodModel->setData(3, 0, Database::MediaDirectorySettings::Monthly, Wt::UserRole); _updateStartTimeModel = new Wt::WStringListModel(this); @@ -199,10 +210,6 @@ class DatabaseFormModel : public Wt::WFormModel return v; } - Wt::WValidator *createRequestImmediateFieldValidator() { - Wt::WValidator* v = new Wt::WValidator(); - return v; - } SessionData& _sessionData; Wt::WStringListModel* _updatePeriodModel; @@ -212,39 +219,33 @@ class DatabaseFormModel : public Wt::WFormModel const Wt::WFormModel::Field DatabaseFormModel::UpdatePeriodField = "update-period"; const Wt::WFormModel::Field DatabaseFormModel::UpdateStartTimeField = "update-start-time"; -const Wt::WFormModel::Field DatabaseFormModel::UpdateRequestImmediateField = "update-request-immediate-scan"; DatabaseFormView::DatabaseFormView(SessionData& sessionData, Wt::WContainerWidget *parent) : Wt::WTemplateFormView(parent) { - model = new DatabaseFormModel(sessionData, this); + _model = new DatabaseFormModel(sessionData, this); setTemplateText(tr("databaseForm-template")); addFunction("id", &WTemplate::Functions::id); addFunction("block", &WTemplate::Functions::id); - applyInfo = new Wt::WText(); - applyInfo->setInline(false); - applyInfo->hide(); - bindWidget("apply-info", applyInfo); + _applyInfo = new Wt::WText(); + _applyInfo->setInline(false); + _applyInfo->hide(); + bindWidget("apply-info", _applyInfo); // Update Period Wt::WComboBox *updatePeriodCB = new Wt::WComboBox(); setFormWidget(DatabaseFormModel::UpdatePeriodField, updatePeriodCB); - updatePeriodCB->setModel(model->updatePeriodModel()); - updatePeriodCB->changed().connect(applyInfo, &Wt::WWidget::hide); + updatePeriodCB->setModel(_model->updatePeriodModel()); + updatePeriodCB->changed().connect(_applyInfo, &Wt::WWidget::hide); // Update Start Time Wt::WComboBox *updateStartTimeCB = new Wt::WComboBox(); setFormWidget(DatabaseFormModel::UpdateStartTimeField, updateStartTimeCB); - updateStartTimeCB->setModel(model->updateStartTimeModel()); - updateStartTimeCB->changed().connect(applyInfo, &Wt::WWidget::hide); - - // Request Immediate scan - Wt::WCheckBox *immScan = new Wt::WCheckBox(); - setFormWidget(DatabaseFormModel::UpdateRequestImmediateField, immScan); - immScan->changed().connect(applyInfo, &Wt::WWidget::hide); + updateStartTimeCB->setModel(_model->updateStartTimeModel()); + updateStartTimeCB->changed().connect(_applyInfo, &Wt::WWidget::hide); // Title & Buttons bindString("title", "Media folder settings"); @@ -259,49 +260,71 @@ DatabaseFormView::DatabaseFormView(SessionData& sessionData, Wt::WContainerWidge saveButton->clicked().connect(this, &DatabaseFormView::processSave); discardButton->clicked().connect(this, &DatabaseFormView::processDiscard); - updateView(model); + Wt::WPushButton *immediateScanButton = new Wt::WPushButton("Immediate scan"); + immediateScanButton->setStyleClass("btn-warning"); + bindWidget("immediate-scan-button", immediateScanButton); + immediateScanButton->clicked().connect(this, &DatabaseFormView::processImmediateScan); + updateView(_model); + +} + +void +DatabaseFormView::processImmediateScan() +{ + Wt::WString error; + _applyInfo->show(); + + if (_model->setImmediateScan(error)) + { + _applyInfo->setText( Wt::WString::fromUTF8("Media folder scan has been started!" ) ); + _applyInfo->setStyleClass("alert alert-warning"); + + _sigChanged.emit(); + } + else + { + _applyInfo->setText( error ); + _applyInfo->setStyleClass("alert alert-danger"); + } } void DatabaseFormView::processDiscard() { - applyInfo->show(); + _applyInfo->show(); - applyInfo->setText( Wt::WString::fromUTF8("Parameters reverted!")); - applyInfo->setStyleClass("alert alert-info"); + _applyInfo->setText( Wt::WString::fromUTF8("Parameters reverted!")); + _applyInfo->setStyleClass("alert alert-info"); - model->loadData(); + _model->loadData(); - model->validate(); - updateView(model); + _model->validate(); + updateView(_model); } void DatabaseFormView::processSave() { - updateModel(model); + updateModel(_model); - applyInfo->show(); + _applyInfo->show(); - if (model->validate()) { + if (_model->validate()) { // Make the model to commit data into DB - model->saveData(); + _model->saveData(); _sigChanged.emit(); - // uncheck the special button - model->setValue(DatabaseFormModel::UpdateRequestImmediateField, false); - - applyInfo->setText( Wt::WString::fromUTF8("New parameters successfully applied!")); - applyInfo->setStyleClass("alert alert-success"); + _applyInfo->setText( Wt::WString::fromUTF8("New parameters successfully applied!")); + _applyInfo->setStyleClass("alert alert-success"); } else { - applyInfo->setText( Wt::WString::fromUTF8("Cannot apply new parameters!")); - applyInfo->setStyleClass("alert alert-danger"); + _applyInfo->setText( Wt::WString::fromUTF8("Cannot apply new parameters!")); + _applyInfo->setStyleClass("alert alert-danger"); } // Udate the view: Delete any validation message in the view, etc. - updateView(model); + updateView(_model); } diff --git a/ui/settings/SettingsDatabaseFormView.hpp b/ui/settings/SettingsDatabaseFormView.hpp index 3681ca91..b0e8fd6f 100644 --- a/ui/settings/SettingsDatabaseFormView.hpp +++ b/ui/settings/SettingsDatabaseFormView.hpp @@ -26,9 +26,10 @@ class DatabaseFormView : public Wt::WTemplateFormView void processSave(); void processDiscard(); + void processImmediateScan(); - Wt::WText *applyInfo; - DatabaseFormModel *model; + Wt::WText *_applyInfo; + DatabaseFormModel *_model; };