Database updater now fully operational

This commit is contained in:
emeric
2014-08-19 23:44:20 +02:00
parent 5242be2014
commit 89147fd955
7 changed files with 180 additions and 80 deletions
+75 -4
View File
@@ -10,6 +10,45 @@
#include "Checksum.hpp" #include "Checksum.hpp"
#include "DatabaseUpdater.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 { namespace DatabaseUpdater {
using namespace Database; using namespace Database;
@@ -69,13 +108,44 @@ Updater::processNextJob(void)
MediaDirectorySettings::pointer settings = MediaDirectorySettings::get(_db.getSession()); 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) ); scheduleScan( boost::posix_time::seconds(0) );
}
else 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 void
Updater::scheduleScan( boost::posix_time::ptime time) Updater::scheduleScan( boost::posix_time::ptime time)
{ {
std::cout << "Scheduling next scan at " << time << std::endl;
_scheduleTimer.expires_at(time); _scheduleTimer.expires_at(time);
_scheduleTimer.async_wait( boost::bind( &Updater::process, this, boost::asio::placeholders::error) ); _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 // If the manual scan was required we can now set it to done
// Update only if the scan is complete! // Update only if the scan is complete!
if (settings->getManualScanRequested() & _running) if (settings->getManualScanRequested() && _running)
settings.modify()->setManualScanRequested(false); settings.modify()->setManualScanRequested(false);
} }
+11 -4
View File
@@ -16,6 +16,13 @@ class MediaDirectorySettings
{ {
public: public:
enum UpdatePeriod {
Never,
Daily,
Weekly,
Monthly
};
typedef Wt::Dbo::ptr<MediaDirectorySettings> pointer; typedef Wt::Dbo::ptr<MediaDirectorySettings> pointer;
MediaDirectorySettings() {} MediaDirectorySettings() {}
@@ -25,17 +32,17 @@ class MediaDirectorySettings
// write accessors // write accessors
void setManualScanRequested(bool value) { _manualScanRequested = value;} 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 setUpdateStartTime(boost::posix_time::time_duration dur) { _updateStartTime = dur;}
void setLastUpdate(boost::posix_time::ptime time) { _lastUpdate = time; } void setLastUpdate(boost::posix_time::ptime time) { _lastUpdate = time; }
void setLastScan(boost::posix_time::ptime time) { _lastScan = time; } void setLastScan(boost::posix_time::ptime time) { _lastScan = time; }
// Read accessors // Read accessors
bool getManualScanRequested(void) const { return _manualScanRequested; } 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::time_duration getUpdateStartTime(void) const { return _updateStartTime; }
boost::posix_time::ptime getLastUpdated(void) const { return _lastUpdate; } 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<class Action> template<class Action>
void persist(Action& a) void persist(Action& a)
@@ -51,7 +58,7 @@ class MediaDirectorySettings
private: private:
bool _manualScanRequested; // Immadiate scan has been requested by user 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::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 _lastUpdate; // last time the database has changed
boost::posix_time::ptime _lastScan; // last time the database has been scanned boost::posix_time::ptime _lastScan; // last time the database has been scanned
+1 -12
View File
@@ -245,20 +245,9 @@
</div> </div>
</div> </div>
<div class="form-group">
<label class="control-label col-sm-2" for="${id:update-request-immediate-scan}">
Request immediate scan
</label>
<div class="col-sm-5">
${update-request-immediate-scan}
</div>
<div class="help-block col-sm-5">
${update-request-immediate-scan-info}
</div>
</div>
<div class="form-group"> <div class="form-group">
<div class="col-sm-offset-2 col-sm-10"> <div class="col-sm-offset-2 col-sm-10">
${apply-button} ${discard-button} ${apply-button} ${discard-button} ${immediate-scan-button}
</div> </div>
</div> </div>
${apply-info} ${apply-info}
+11 -4
View File
@@ -48,11 +48,11 @@ _sessionData(sessionData)
if (user->isAdmin()) if (user->isAdmin())
{ {
MediaDirectories* mediaDirectory = new MediaDirectories(sessionData); MediaDirectories* mediaDirectory = new MediaDirectories(sessionData);
mediaDirectory->changed().connect(this, &Settings::handleDatabaseSettingsChanged); mediaDirectory->changed().connect(this, &Settings::handleDatabaseDirectoriesChanged);
menu->addItem("Media Folders", mediaDirectory); menu->addItem("Media Folders", mediaDirectory);
DatabaseFormView* databaseFormView = new DatabaseFormView(sessionData); DatabaseFormView* databaseFormView = new DatabaseFormView(sessionData);
databaseFormView->changed().connect(this, &Settings::handleDatabaseSettingsChanged); databaseFormView->changed().connect(this, &Settings::restartDatabaseUpdateService);
menu->addItem("Database Update", databaseFormView); menu->addItem("Database Update", databaseFormView);
menu->addItem("Users", new Users(sessionData)); menu->addItem("Users", new Users(sessionData));
@@ -65,14 +65,21 @@ _sessionData(sessionData)
} }
void 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()); Wt::Dbo::Transaction transaction(_sessionData.getDatabaseHandler().getSession());
Database::MediaDirectorySettings::get(_sessionData.getDatabaseHandler().getSession()).modify()->setManualScanRequested(true); Database::MediaDirectorySettings::get(_sessionData.getDatabaseHandler().getSession()).modify()->setManualScanRequested(true);
} }
restartDatabaseUpdateService();
}
void
Settings::restartDatabaseUpdateService()
{
// Restarting the update service // Restarting the update service
boost::lock_guard<boost::mutex> serviceLock (ServiceManager::instance().mutex()); boost::lock_guard<boost::mutex> serviceLock (ServiceManager::instance().mutex());
+3 -1
View File
@@ -12,7 +12,9 @@ class Settings : public Wt::WContainerWidget
private: private:
void handleDatabaseSettingsChanged(); void handleDatabaseDirectoriesChanged();
void restartDatabaseUpdateService(void);
SessionData& _sessionData; SessionData& _sessionData;
+76 -53
View File
@@ -1,9 +1,8 @@
#include <Wt/WLineEdit>
#include <Wt/WString> #include <Wt/WString>
#include <Wt/WPushButton> #include <Wt/WPushButton>
#include <Wt/WCheckBox> #include <Wt/WCheckBox>
#include <Wt/WComboBox> #include <Wt/WComboBox>
#include <Wt/WBreak> #include <Wt/WMessageBox>
#include <Wt/WFormModel> #include <Wt/WFormModel>
#include <Wt/WStringListModel> #include <Wt/WStringListModel>
@@ -23,7 +22,6 @@ class DatabaseFormModel : public Wt::WFormModel
// Associate each field with a unique string literal. // Associate each field with a unique string literal.
static const Field UpdatePeriodField; static const Field UpdatePeriodField;
static const Field UpdateStartTimeField; static const Field UpdateStartTimeField;
static const Field UpdateRequestImmediateField;
DatabaseFormModel(SessionData& sessionData, Wt::WObject *parent = 0) DatabaseFormModel(SessionData& sessionData, Wt::WObject *parent = 0)
: Wt::WFormModel(parent), : Wt::WFormModel(parent),
@@ -33,11 +31,9 @@ class DatabaseFormModel : public Wt::WFormModel
addField(UpdatePeriodField); addField(UpdatePeriodField);
addField(UpdateStartTimeField); addField(UpdateStartTimeField);
addField(UpdateRequestImmediateField);
setValidator(UpdatePeriodField, createUpdatePeriodValidator()); setValidator(UpdatePeriodField, createUpdatePeriodValidator());
setValidator(UpdateStartTimeField, createStartTimeValidator()); setValidator(UpdateStartTimeField, createStartTimeValidator());
setValidator(UpdateRequestImmediateField, createRequestImmediateFieldValidator());
// populate the model with initial data // populate the model with initial data
loadData(); loadData();
@@ -61,7 +57,6 @@ class DatabaseFormModel : public Wt::WFormModel
if (startTimeRow != -1) if (startTimeRow != -1)
setValue(UpdateStartTimeField, updateStartTime( startTimeRow ) ); setValue(UpdateStartTimeField, updateStartTime( startTimeRow ) );
setValue(UpdateRequestImmediateField, false);
} }
void saveData() void saveData()
@@ -79,9 +74,26 @@ class DatabaseFormModel : public Wt::WFormModel
assert(startTimeRow != -1); assert(startTimeRow != -1);
settings.modify()->setUpdateStartTime( updateStartTimeDuration( startTimeRow ) ); settings.modify()->setUpdateStartTime( updateStartTimeDuration( startTimeRow ) );
settings.modify()->setManualScanRequested( boost::any_cast<bool>(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) int getUpdatePeriodModelRow(Wt::WString value)
{ {
@@ -93,7 +105,7 @@ class DatabaseFormModel : public Wt::WFormModel
return -1; return -1;
} }
int getUpdatePeriodModelRow(boost::posix_time::time_duration duration) int getUpdatePeriodModelRow(Database::MediaDirectorySettings::UpdatePeriod duration)
{ {
for (int i = 0; i < _updatePeriodModel->rowCount(); ++i) for (int i = 0; i < _updatePeriodModel->rowCount(); ++i)
{ {
@@ -104,8 +116,8 @@ class DatabaseFormModel : public Wt::WFormModel
return -1; return -1;
} }
boost::posix_time::time_duration updatePeriodDuration(int row) { Database::MediaDirectorySettings::UpdatePeriod updatePeriodDuration(int row) {
return boost::any_cast<boost::posix_time::time_duration> return boost::any_cast<Database::MediaDirectorySettings::UpdatePeriod>
(_updatePeriodModel->data(_updatePeriodModel->index(row, 0), Wt::UserRole)); (_updatePeriodModel->data(_updatePeriodModel->index(row, 0), Wt::UserRole));
} }
@@ -155,17 +167,16 @@ class DatabaseFormModel : public Wt::WFormModel
_updatePeriodModel = new Wt::WStringListModel(this); _updatePeriodModel = new Wt::WStringListModel(this);
_updatePeriodModel->addString("Never"); _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->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->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->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); _updateStartTimeModel = new Wt::WStringListModel(this);
@@ -199,10 +210,6 @@ class DatabaseFormModel : public Wt::WFormModel
return v; return v;
} }
Wt::WValidator *createRequestImmediateFieldValidator() {
Wt::WValidator* v = new Wt::WValidator();
return v;
}
SessionData& _sessionData; SessionData& _sessionData;
Wt::WStringListModel* _updatePeriodModel; 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::UpdatePeriodField = "update-period";
const Wt::WFormModel::Field DatabaseFormModel::UpdateStartTimeField = "update-start-time"; 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) DatabaseFormView::DatabaseFormView(SessionData& sessionData, Wt::WContainerWidget *parent)
: Wt::WTemplateFormView(parent) : Wt::WTemplateFormView(parent)
{ {
model = new DatabaseFormModel(sessionData, this); _model = new DatabaseFormModel(sessionData, this);
setTemplateText(tr("databaseForm-template")); setTemplateText(tr("databaseForm-template"));
addFunction("id", &WTemplate::Functions::id); addFunction("id", &WTemplate::Functions::id);
addFunction("block", &WTemplate::Functions::id); addFunction("block", &WTemplate::Functions::id);
applyInfo = new Wt::WText(); _applyInfo = new Wt::WText();
applyInfo->setInline(false); _applyInfo->setInline(false);
applyInfo->hide(); _applyInfo->hide();
bindWidget("apply-info", applyInfo); bindWidget("apply-info", _applyInfo);
// Update Period // Update Period
Wt::WComboBox *updatePeriodCB = new Wt::WComboBox(); Wt::WComboBox *updatePeriodCB = new Wt::WComboBox();
setFormWidget(DatabaseFormModel::UpdatePeriodField, updatePeriodCB); setFormWidget(DatabaseFormModel::UpdatePeriodField, updatePeriodCB);
updatePeriodCB->setModel(model->updatePeriodModel()); updatePeriodCB->setModel(_model->updatePeriodModel());
updatePeriodCB->changed().connect(applyInfo, &Wt::WWidget::hide); updatePeriodCB->changed().connect(_applyInfo, &Wt::WWidget::hide);
// Update Start Time // Update Start Time
Wt::WComboBox *updateStartTimeCB = new Wt::WComboBox(); Wt::WComboBox *updateStartTimeCB = new Wt::WComboBox();
setFormWidget(DatabaseFormModel::UpdateStartTimeField, updateStartTimeCB); setFormWidget(DatabaseFormModel::UpdateStartTimeField, updateStartTimeCB);
updateStartTimeCB->setModel(model->updateStartTimeModel()); updateStartTimeCB->setModel(_model->updateStartTimeModel());
updateStartTimeCB->changed().connect(applyInfo, &Wt::WWidget::hide); 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);
// Title & Buttons // Title & Buttons
bindString("title", "Media folder settings"); bindString("title", "Media folder settings");
@@ -259,49 +260,71 @@ DatabaseFormView::DatabaseFormView(SessionData& sessionData, Wt::WContainerWidge
saveButton->clicked().connect(this, &DatabaseFormView::processSave); saveButton->clicked().connect(this, &DatabaseFormView::processSave);
discardButton->clicked().connect(this, &DatabaseFormView::processDiscard); 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 void
DatabaseFormView::processDiscard() DatabaseFormView::processDiscard()
{ {
applyInfo->show(); _applyInfo->show();
applyInfo->setText( Wt::WString::fromUTF8("Parameters reverted!")); _applyInfo->setText( Wt::WString::fromUTF8("Parameters reverted!"));
applyInfo->setStyleClass("alert alert-info"); _applyInfo->setStyleClass("alert alert-info");
model->loadData(); _model->loadData();
model->validate(); _model->validate();
updateView(model); updateView(_model);
} }
void void
DatabaseFormView::processSave() DatabaseFormView::processSave()
{ {
updateModel(model); updateModel(_model);
applyInfo->show(); _applyInfo->show();
if (model->validate()) { if (_model->validate()) {
// Make the model to commit data into DB // Make the model to commit data into DB
model->saveData(); _model->saveData();
_sigChanged.emit(); _sigChanged.emit();
// uncheck the special button _applyInfo->setText( Wt::WString::fromUTF8("New parameters successfully applied!"));
model->setValue(DatabaseFormModel::UpdateRequestImmediateField, false); _applyInfo->setStyleClass("alert alert-success");
applyInfo->setText( Wt::WString::fromUTF8("New parameters successfully applied!"));
applyInfo->setStyleClass("alert alert-success");
} }
else { else {
applyInfo->setText( Wt::WString::fromUTF8("Cannot apply new parameters!")); _applyInfo->setText( Wt::WString::fromUTF8("Cannot apply new parameters!"));
applyInfo->setStyleClass("alert alert-danger"); _applyInfo->setStyleClass("alert alert-danger");
} }
// Udate the view: Delete any validation message in the view, etc. // Udate the view: Delete any validation message in the view, etc.
updateView(model); updateView(_model);
} }
+3 -2
View File
@@ -26,9 +26,10 @@ class DatabaseFormView : public Wt::WTemplateFormView
void processSave(); void processSave();
void processDiscard(); void processDiscard();
void processImmediateScan();
Wt::WText *applyInfo; Wt::WText *_applyInfo;
DatabaseFormModel *model; DatabaseFormModel *_model;
}; };