Files
lms/src/lms/ui/admin/DatabaseSettingsView.cpp
T
2021-10-20 20:00:14 +02:00

269 lines
10 KiB
C++

/*
* Copyright (C) 2018 Emeric Poupon
*
* This file is part of LMS.
*
* LMS is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* LMS is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
*/
#include "DatabaseSettingsView.hpp"
#include <Wt/WComboBox.h>
#include <Wt/WFormModel.h>
#include <Wt/WLineEdit.h>
#include <Wt/WPushButton.h>
#include <Wt/WString.h>
#include <Wt/WTemplateFormView.h>
#include "database/Cluster.hpp"
#include "database/ScanSettings.hpp"
#include "database/Session.hpp"
#include "services/scanner/IScannerService.hpp"
#include "utils/Logger.hpp"
#include "utils/Service.hpp"
#include "utils/String.hpp"
#include "common/DirectoryValidator.hpp"
#include "common/MandatoryValidator.hpp"
#include "common/ValueStringModel.hpp"
#include "ScannerController.hpp"
#include "LmsApplication.hpp"
namespace UserInterface {
using namespace Database;
class DatabaseSettingsModel : public Wt::WFormModel
{
public:
// Associate each field with a unique string literal.
static inline constexpr Field MediaDirectoryField {"media-directory"};
static inline constexpr Field UpdatePeriodField {"update-period"};
static inline constexpr Field UpdateStartTimeField {"update-start-time"};
static inline constexpr Field RecommendationEngineTypeField {"recommendation-engine-type"};
static inline constexpr Field TagsField {"tags"};
using UpdatePeriodModel = ValueStringModel<ScanSettings::UpdatePeriod>;
DatabaseSettingsModel()
{
initializeModels();
addField(MediaDirectoryField);
addField(UpdatePeriodField);
addField(UpdateStartTimeField);
addField(RecommendationEngineTypeField);
addField(TagsField);
auto dirValidator {createDirectoryValidator()};
dirValidator->setMandatory(true);
setValidator(MediaDirectoryField, dirValidator);
setValidator(UpdatePeriodField, createMandatoryValidator());
setValidator(UpdateStartTimeField, createMandatoryValidator());
setValidator(RecommendationEngineTypeField, createMandatoryValidator());
setValidator(TagsField, createTagsValidator());
// populate the model with initial data
loadData();
}
std::shared_ptr<UpdatePeriodModel> updatePeriodModel() { return _updatePeriodModel; }
std::shared_ptr<Wt::WAbstractItemModel> updateStartTimeModel() { return _updateStartTimeModel; }
std::shared_ptr<Wt::WAbstractItemModel> recommendationEngineTypeModel() { return _recommendationEngineTypeModel; }
void loadData()
{
auto transaction {LmsApp->getDbSession().createSharedTransaction()};
const ScanSettings::pointer scanSettings {ScanSettings::get(LmsApp->getDbSession())};
setValue(MediaDirectoryField, scanSettings->getMediaDirectory().string());
auto periodRow {_updatePeriodModel->getRowFromValue(scanSettings->getUpdatePeriod())};
if (periodRow)
setValue(UpdatePeriodField, _updatePeriodModel->getString(*periodRow));
auto startTimeRow {_updateStartTimeModel->getRowFromValue(scanSettings->getUpdateStartTime())};
if (startTimeRow)
setValue(UpdateStartTimeField, _updateStartTimeModel->getString(*startTimeRow));
if (scanSettings->getUpdatePeriod() == ScanSettings::UpdatePeriod::Hourly
|| scanSettings->getUpdatePeriod() == ScanSettings::UpdatePeriod::Never)
{
setReadOnly(DatabaseSettingsModel::UpdateStartTimeField, true);
}
auto recommendationEngineTypeRow {_recommendationEngineTypeModel->getRowFromValue(scanSettings->getRecommendationEngineType())};
if (recommendationEngineTypeRow)
setValue(RecommendationEngineTypeField, _recommendationEngineTypeModel->getString(*recommendationEngineTypeRow));
auto clusterTypes {scanSettings->getClusterTypes()};
if (!clusterTypes.empty())
{
std::vector<std::string> names;
std::transform(clusterTypes.begin(), clusterTypes.end(), std::back_inserter(names), [](auto clusterType) { return clusterType->getName(); });
setValue(TagsField, StringUtils::joinStrings(names, " "));
}
}
void saveData()
{
auto transaction {LmsApp->getDbSession().createUniqueTransaction()};
ScanSettings::pointer scanSettings {ScanSettings::get(LmsApp->getDbSession())};
scanSettings.modify()->setMediaDirectory(valueText(MediaDirectoryField).toUTF8());
auto updatePeriodRow {_updatePeriodModel->getRowFromString(valueText(UpdatePeriodField))};
if (updatePeriodRow)
scanSettings.modify()->setUpdatePeriod(_updatePeriodModel->getValue(*updatePeriodRow));
auto startTimeRow {_updateStartTimeModel->getRowFromString(valueText(UpdateStartTimeField))};
if (startTimeRow)
scanSettings.modify()->setUpdateStartTime(_updateStartTimeModel->getValue(*startTimeRow));
auto recommendationEngineTypeRow {_recommendationEngineTypeModel->getRowFromString(valueText(RecommendationEngineTypeField))};
if (recommendationEngineTypeRow)
scanSettings.modify()->setRecommendationEngineType(_recommendationEngineTypeModel->getValue(*recommendationEngineTypeRow));
auto clusterTypes {StringUtils::splitStringCopy(valueText(TagsField).toUTF8(), " ")};
scanSettings.modify()->setClusterTypes(LmsApp->getDbSession(), std::set<std::string>(clusterTypes.begin(), clusterTypes.end()));
}
private:
static std::shared_ptr<Wt::WValidator> createTagsValidator()
{
auto v = std::make_shared<Wt::WValidator>();
return v;
}
void initializeModels()
{
_updatePeriodModel = std::make_shared<ValueStringModel<ScanSettings::UpdatePeriod>>();
_updatePeriodModel->add(Wt::WString::tr("Lms.Admin.Database.never"), ScanSettings::UpdatePeriod::Never);
_updatePeriodModel->add(Wt::WString::tr("Lms.Admin.Database.hourly"), ScanSettings::UpdatePeriod::Hourly);
_updatePeriodModel->add(Wt::WString::tr("Lms.Admin.Database.daily"), ScanSettings::UpdatePeriod::Daily);
_updatePeriodModel->add(Wt::WString::tr("Lms.Admin.Database.weekly"), ScanSettings::UpdatePeriod::Weekly);
_updatePeriodModel->add(Wt::WString::tr("Lms.Admin.Database.monthly"), ScanSettings::UpdatePeriod::Monthly);
_updateStartTimeModel = std::make_shared<ValueStringModel<Wt::WTime>>();
for (std::size_t i = 0; i < 24; ++i)
{
Wt::WTime time {static_cast<int>(i), 0};
_updateStartTimeModel->add(time.toString(), time);
}
_recommendationEngineTypeModel = std::make_shared<ValueStringModel<ScanSettings::RecommendationEngineType>>();
_recommendationEngineTypeModel->add(Wt::WString::tr("Lms.Admin.Database.recommendation-engine-type.clusters"), ScanSettings::RecommendationEngineType::Clusters);
_recommendationEngineTypeModel->add(Wt::WString::tr("Lms.Admin.Database.recommendation-engine-type.features"), ScanSettings::RecommendationEngineType::Features);
}
std::shared_ptr<UpdatePeriodModel> _updatePeriodModel;
std::shared_ptr<ValueStringModel<Wt::WTime>> _updateStartTimeModel;
std::shared_ptr<ValueStringModel<ScanSettings::RecommendationEngineType>> _recommendationEngineTypeModel;
};
DatabaseSettingsView::DatabaseSettingsView()
{
wApp->internalPathChanged().connect(this, [this]
{
refreshView();
});
refreshView();
}
void
DatabaseSettingsView::refreshView()
{
if (!wApp->internalPathMatches("/admin/database"))
return;
clear();
auto t {addNew<Wt::WTemplateFormView>(Wt::WString::tr("Lms.Admin.Database.template"))};
auto model {std::make_shared<DatabaseSettingsModel>()};
// Media Directory
t->setFormWidget(DatabaseSettingsModel::MediaDirectoryField, std::make_unique<Wt::WLineEdit>());
// Update Period
auto updatePeriod {std::make_unique<Wt::WComboBox>()};
updatePeriod->setModel(model->updatePeriodModel());
updatePeriod->activated().connect([=](int row)
{
const ScanSettings::UpdatePeriod period {model->updatePeriodModel()->getValue(row)};
model->setReadOnly(DatabaseSettingsModel::UpdateStartTimeField, period == ScanSettings::UpdatePeriod::Hourly || period == ScanSettings::UpdatePeriod::Never);
t->updateModel(model.get());
t->updateView(model.get());
});
t->setFormWidget(DatabaseSettingsModel::UpdatePeriodField, std::move(updatePeriod));
// Update Start Time
auto updateStartTime {std::make_unique<Wt::WComboBox>()};
updateStartTime->setModel(model->updateStartTimeModel());
t->setFormWidget(DatabaseSettingsModel::UpdateStartTimeField, std::move(updateStartTime));
// recommendation engine type
auto recommendationEngineType {std::make_unique<Wt::WComboBox>()};
recommendationEngineType->setModel(model->recommendationEngineTypeModel());
t->setFormWidget(DatabaseSettingsModel::RecommendationEngineTypeField, std::move(recommendationEngineType));
// Tags
t->setFormWidget(DatabaseSettingsModel::TagsField, std::make_unique<Wt::WLineEdit>());
// Buttons
Wt::WPushButton *saveBtn = t->bindWidget("apply-btn", std::make_unique<Wt::WPushButton>(Wt::WString::tr("Lms.apply")));
Wt::WPushButton *discardBtn = t->bindWidget("discard-btn", std::make_unique<Wt::WPushButton>(Wt::WString::tr("Lms.discard")));
Wt::WPushButton *immScanBtn = t->bindWidget("immediate-scan-btn", std::make_unique<Wt::WPushButton>(Wt::WString::tr("Lms.Admin.Database.immediate-scan")));
t->bindNew<ScannerController>("scanner-controller");
saveBtn->clicked().connect([=]
{
t->updateModel(model.get());
if (model->validate())
{
model->saveData();
Service<Scanner::IScannerService>::get()->requestImmediateScan(false);
LmsApp->notifyMsg(LmsApplication::MsgType::Success, Wt::WString::tr("Lms.Admin.Database.settings-saved"));
}
// Udate the view: Delete any validation message in the view, etc.
t->updateView(model.get());
});
discardBtn->clicked().connect([=]
{
model->loadData();
model->validate();
t->updateView(model.get());
});
immScanBtn->clicked().connect([=]
{
Service<Scanner::IScannerService>::get()->requestImmediateScan(false);
});
t->updateView(model.get());
}
} // namespace UserInterface