Initial commit

This commit is contained in:
user
2026-07-12 14:34:52 +04:00
commit 3abf6bd9c2
557 changed files with 68706 additions and 0 deletions
+294
View File
@@ -0,0 +1,294 @@
#include "application_connector.h"
#include "model_manager.h"
#include "repository_manager.h"
#include "schema_metadata.h"
#include <QObject>
ApplicationConnector::ApplicationConnector(QObject *rootObject)
: modelManager_(ModelManager::instance()),
repositoryManager_(RepositoryManager::instance()),
rootObject_(rootObject) {}
bool ApplicationConnector::connect() {
if (!connectSelfReferenceTableLinks()) {
return false;
}
if (!connectTablePairLinks()) {
return false;
}
if (!connectUpdateDeleteRFLinks()) {
return false;
}
if (!connectDeleteRFLinks()) {
return false;
}
return true;
}
ApplicationConnector::RMLinkList
ApplicationConnector::initializeSelfReferenceTableLinks() {
return {linkRepositoryToModel("genres", "genres"),
linkRepositoryToModel("halls", "halls"),
linkRepositoryToModel("movies", "movies"),
linkRepositoryToModel("sessions", "sessions"),
linkRepositoryToModel("refunds", "refunds"),
linkRepositoryToModel("tickets", "tickets")};
}
ApplicationConnector::RMLinkList
ApplicationConnector::initializeTablePairLinks() {
return {linkRepositoryToModel("genres", "movies"),
linkRepositoryToModel("genres", "sessions"),
linkRepositoryToModel("genres", "tickets"),
linkRepositoryToModel("genres", "refunds"),
linkRepositoryToModel("halls", "sessions"),
linkRepositoryToModel("halls", "tickets"),
linkRepositoryToModel("halls", "refunds"),
linkRepositoryToModel("movies", "sessions"),
linkRepositoryToModel("movies", "tickets"),
linkRepositoryToModel("movies", "refunds"),
linkRepositoryToModel("sessions", "refunds"),
linkRepositoryToModel("tickets", "refunds")};
}
ApplicationConnector::RFLinkList
ApplicationConnector::flattenLinkList(const QVector<RFLinkList> &linkLists) {
RFLinkList result;
for (const RFLinkList &currentList : linkLists) {
std::move(currentList.cbegin(), currentList.cend(),
std::back_inserter(result));
}
return result;
}
ApplicationConnector::RFLinkList
ApplicationConnector::initializeUpdateDeleteCandidates() {
const QVector<RFLinkList> updateDeleteCandidates = {
linkRepositoryToFinder("genres", "genreFinder"),
linkRepositoryToFinder("genres", "movieFinder"),
linkRepositoryToFinder("genres", "sessionFinder"),
linkRepositoryToFinder("genres", "ticketFinder"),
linkRepositoryToFinder("genres", "entityFinder"),
linkRepositoryToFinder("halls", "hallFinder"),
linkRepositoryToFinder("halls", "movieFinder"),
linkRepositoryToFinder("halls", "sessionFinder"),
linkRepositoryToFinder("halls", "ticketFinder"),
linkRepositoryToFinder("halls", "entityFinder"),
linkRepositoryToFinder("movies", "movieFinder"),
linkRepositoryToFinder("movies", "sessionFinder"),
linkRepositoryToFinder("movies", "ticketFinder"),
linkRepositoryToFinder("movies", "entityFinder"),
linkRepositoryToFinder("sessions", "sessionFinder"),
linkRepositoryToFinder("sessions", "ticketFinder"),
linkRepositoryToFinder("sessions", "entityFinder"),
linkRepositoryToFinder("tickets", "ticketFinder"),
linkRepositoryToFinder("tickets", "entityFinder"),
linkRepositoryToFinder("refunds", "entityFinder")};
return flattenLinkList(updateDeleteCandidates);
}
ApplicationConnector::RFLinkList
ApplicationConnector::initializeDeleteCandidates() {
// foreignKeyFinder will reset to default on signal primaryKeyFinder
const QVector<RFLinkList> deleteCandidates = {
linkRepositoryToFinder("genres", "primaryKeyFinder"),
linkRepositoryToFinder("halls", "primaryKeyFinder"),
linkRepositoryToFinder("movies", "primaryKeyFinder"),
linkRepositoryToFinder("sessions", "primaryKeyFinder"),
linkRepositoryToFinder("tickets", "primaryKeyFinder"),
linkRepositoryToFinder("refunds", "primaryKeyFinder")};
return flattenLinkList(deleteCandidates);
}
bool ApplicationConnector::connectSelfReferenceTableLinks() {
const QList<RMLink> selfReferenceTableLinks =
initializeSelfReferenceTableLinks();
qInfo() << "Connecting self-reference tables...";
if (!connect(selfReferenceTableLinks, singleTableConnections())) {
qWarning() << "Failed to connect self-reference tables";
return false;
}
return true;
}
bool ApplicationConnector::connectTablePairLinks() {
const RMLinkList tablePairLinks = initializeTablePairLinks();
qInfo() << "Connecting pair tables...";
if (!connect(tablePairLinks, pairTableConnections())) {
qWarning() << "Failed to connect pair tables";
return false;
}
return true;
}
bool ApplicationConnector::connectUpdateDeleteRFLinks() {
const RFLinkList repositoryToFinderLinks = initializeUpdateDeleteCandidates();
qInfo() << "Connecting repository to finder...";
if (!connect(repositoryToFinderLinks, repositoryToFinderConnections())) {
qWarning() << "Failed to connect repository to finder";
return false;
}
return true;
}
bool ApplicationConnector::connectDeleteRFLinks() {
const RFLinkList repositoryToFinderLinks = initializeDeleteCandidates();
qInfo() << "Connecting repository to finder...";
using Signal = void (Repository::*)();
using Slot = void (Finder::*)();
RFConnectionList connections;
connections.append(Connection<Signal, Slot>(&Repository::dataDeleted,
&Finder::resetToDefault));
if (!connect(repositoryToFinderLinks, connections)) {
qWarning() << "Failed to connect repository to finder";
return false;
}
return true;
}
ApplicationConnector::RMConnectionList
ApplicationConnector::pairTableConnections() {
using Signal = void (Repository::*)();
using Slot = bool (Model::*)();
RMConnectionList result;
std::initializer_list<Connection<Signal, Slot>> initList = {
{&Repository::dataUpdated, &Model::refreshAll},
{&Repository::dataDeleted, &Model::refreshAll}};
for (const auto &connection : initList) {
result.append(connection);
}
return result;
}
ApplicationConnector::RMConnectionList
ApplicationConnector::singleTableConnections() {
using Signal = void (Repository::*)();
using Slot = bool (Model::*)();
RMConnectionList result;
std::initializer_list<Connection<Signal, Slot>> initList = {
{&Repository::dataCreated, &Model::refreshAll},
{&Repository::dataUpdated, &Model::refreshAll},
{&Repository::dataDeleted, &Model::refreshAll}};
for (const auto &connection : initList) {
result.append(connection);
}
return result;
}
ApplicationConnector::RMLink ApplicationConnector::linkRepositoryToModel(
const QString &senderDatabaseTableName,
const QString &receiverDatabaseTableName) {
SchemaMetadata metadata = SchemaMetadata::defaultSchema();
Repository *sender = repositoryManager_.getRepositoryAs<Repository>(
metadata.repositoryTableName(senderDatabaseTableName));
if (!sender) {
qWarning() << "Sender with type" << typeid(sender).name() << "is null";
}
Model *receiver = modelManager_.getModelAs<Model>(
metadata.modelTableName(receiverDatabaseTableName));
if (!receiver) {
qWarning() << "Receiver with type" << typeid(receiver).name() << "is null";
}
if (!sender || !receiver) {
return qMakePair(nullptr, nullptr);
}
return qMakePair(sender, receiver);
}
ApplicationConnector::RFLinkList ApplicationConnector::linkRepositoryToFinder(
const QString &senderDatabaseTableName,
const QString &receiverEntityFinderName) {
SchemaMetadata metadata = SchemaMetadata::defaultSchema();
Repository *sender = repositoryManager_.getRepositoryAs<Repository>(
metadata.repositoryTableName(senderDatabaseTableName));
if (!sender) {
qWarning() << "Sender with type" << typeid(sender).name() << "is null";
}
QList<Finder *> receiver =
rootObject_->findChildren<Finder *>(receiverEntityFinderName);
if (receiver.isEmpty()) {
qWarning() << "Receivers with type" << typeid(receiver).name()
<< "there is not";
}
if (!sender || receiver.isEmpty()) {
return QList<RFLink>();
}
RFLinkList result;
for (Finder *currentReceiver : receiver) {
result.append(qMakePair(sender, currentReceiver));
}
return result;
}
ApplicationConnector::RFConnectionList
ApplicationConnector::repositoryToFinderConnections() {
using Signal = void (Repository::*)();
using Slot = void (Finder::*)();
RFConnectionList result;
std::initializer_list<Connection<Signal, Slot>> initList = {
{&Repository::dataDeleted, &Finder::resetToDefault},
{&Repository::dataUpdated, &Finder::resetToDefault}};
for (const auto &connection : initList) {
result.append(connection);
}
return result;
}
QString ApplicationConnector::getAddress(const QObject *object) {
quintptr address = reinterpret_cast<quintptr>(object);
return object ? QString::number(address, 16) : "null";
};
QString ApplicationConnector::getMetaClassName(const QMetaObject *meta) {
return meta ? meta->className() : "Unknown";
};
QString ApplicationConnector::getMethodSignature(const QMetaObject *meta,
const char *methodType,
bool isSignal) {
if (!meta) {
return "Unknown";
}
QByteArray normalizedSignature = QMetaObject::normalizedSignature(methodType);
int methodIndex = isSignal ? meta->indexOfSignal(normalizedSignature)
: meta->indexOfSlot(normalizedSignature);
if (methodIndex < 0) {
return normalizedSignature;
}
QString result = meta->method(methodIndex).methodSignature();
return result;
};
+238
View File
@@ -0,0 +1,238 @@
#ifndef APPLICATION_CONNECTOR_H
#define APPLICATION_CONNECTOR_H
#include <QList>
#include <QPair>
#include <QVariant>
// WARNING: Not used directly in cpp file
// You can transfer it to the cpp file, it will not be an error, but a
// warning will remain
#include "base_sql_table_model.h"
#include "data_repository_signals.h"
#include "entity_finder_widget.h"
// Forward declarations
class ModelManager;
class RepositoryManager;
class ApplicationConnector {
private:
// WARNING: Not use directly in cpp file
template <typename SenderType, typename ReceiverType>
using Link = QPair<SenderType *, ReceiverType *>;
template <typename SignalType, typename SlotType>
using Connection = QPair<SignalType, SlotType>;
template <typename SenderType, typename ReceiverType>
using ConnectionVariant = std::variant<
Connection<void (SenderType::*)(), bool (ReceiverType::*)()>,
Connection<void (SenderType::*)() const, bool (ReceiverType::*)()>,
Connection<void (SenderType::*)(), void (ReceiverType::*)()>,
Connection<void (SenderType::*)() const, void (ReceiverType::*)()>>;
template <typename SenderType, typename ReceiverType>
using ConnectionList = QList<ConnectionVariant<SenderType, ReceiverType>>;
template <typename Sender, typename Receiver>
using LinkList = QList<Link<Sender, Receiver>>;
// Derivatives
using Repository = DataRepositorySignals;
using Model = BaseSqlTableModel;
using Finder = EntityFinderWidget;
// WARNING: Use only producing types in cpp file
// Producing types of Repository and Model
using RMLink = QPair<Repository *, Model *>;
using RMConnection = ConnectionVariant<Repository, Model>;
using RMConnectionList = QList<RMConnection>;
using RMLinkList = QList<RMLink>;
// Producing types of Repository and Finder
using RFLink = QPair<Repository *, Finder *>;
using RFConnection = ConnectionVariant<Repository, Finder>;
using RFConnectionList = QList<RFConnection>;
using RFLinkList = QList<RFLink>;
private:
ModelManager &modelManager_;
QObject *rootObject_;
RepositoryManager &repositoryManager_;
public:
ApplicationConnector(QObject *rootObject);
bool connect();
private:
template <typename SenderType, typename ReceiverType>
bool accessible(const Link<SenderType, ReceiverType> &link);
template <typename SignalType, typename SlotType, typename SenderType,
typename ReceiverType>
bool connect(SenderType *sender, ReceiverType *receiver,
const ConnectionList<SignalType, SlotType> &connections);
template <typename SignalType, typename SlotType, typename SenderType,
typename ReceiverType>
bool connect(const LinkList<SenderType, ReceiverType> &links,
const ConnectionList<SignalType, SlotType> &connections);
template <typename SignalType, typename SlotType>
QString templateString(SignalType signal, SlotType slot, QObject *sender,
QObject *receiver);
private:
RFConnectionList repositoryToFinderConnections();
RMConnectionList pairTableConnections();
RMConnectionList singleTableConnections();
RFLinkList flattenLinkList(const QVector<RFLinkList> &linkLists);
RFLinkList linkRepositoryToFinder(const QString &sender,
const QString &receiver);
RMLink linkRepositoryToModel(const QString &sender, const QString &receiver);
RFLinkList initializeDeleteCandidates();
RFLinkList initializeUpdateDeleteCandidates();
RMLinkList initializeSelfReferenceTableLinks();
RMLinkList initializeTablePairLinks();
bool connectDeleteRFLinks();
bool connectSelfReferenceTableLinks();
bool connectTablePairLinks();
bool connectUpdateDeleteRFLinks();
private:
QString getAddress(const QObject *object);
QString getMetaClassName(const QMetaObject *meta);
QString getMethodSignature(const QMetaObject *meta, const char *methodType,
bool isSignal);
};
template <typename SenderType, typename ReceiverType>
bool ApplicationConnector::accessible(
const Link<SenderType, ReceiverType> &link) {
return bool(link.first && link.second);
}
template <typename SignalType, typename SlotType, typename SenderType,
typename ReceiverType>
bool ApplicationConnector::connect(
const LinkList<SenderType, ReceiverType> &links,
const ConnectionList<SignalType, SlotType> &connections) {
for (const Link<SenderType, ReceiverType> &currentLink : links) {
qInfo() << "Connecting link: Index of ConnectionList:"
<< links.indexOf(currentLink);
if (!accessible(currentLink)) {
qWarning() << "Link is not accessible: Index of ConnectionList:"
<< links.indexOf(currentLink);
continue;
}
SenderType *sender = currentLink.first;
ReceiverType *receiver = currentLink.second;
if (!connect(sender, receiver, connections)) {
return false;
}
}
return true;
}
template <typename SignalType, typename SlotType, typename SenderType,
typename ReceiverType>
bool ApplicationConnector::connect(
SenderType *sender, ReceiverType *receiver,
const ConnectionList<SignalType, SlotType> &connections) {
// WARNING: "auto" keyword is necessary
auto handler = [&](const auto &currentLink) -> bool {
auto &[signal, slot] = currentLink;
bool connected = QObject::connect(sender, signal, receiver, slot);
if (!connected) {
qWarning().noquote() << "Failed to connect signal to slot:"
<< templateString(signal, slot, sender, receiver);
} else {
qInfo().noquote() << "Connected signal to slot:"
<< templateString(signal, slot, sender, receiver);
}
return connected;
};
for (const ConnectionVariant<SenderType, ReceiverType> &currentConnection :
connections) {
bool connected = std::visit(handler, currentConnection);
if (!connected) {
return false;
}
}
return true;
}
template <typename SignalType, typename SlotType>
QString ApplicationConnector::templateString(SignalType signal, SlotType slot,
QObject *sender,
QObject *receiver) {
const QMetaObject *senderMeta = sender ? sender->metaObject() : nullptr;
const QMetaObject *receiverMeta = receiver ? receiver->metaObject() : nullptr;
auto width = [](int index) {
switch (index) {
case 0:
return 10;
case 1:
return -30;
default:
return 0;
}
};
QString billetTA = QString("\n\t|%0: Type: %1 Address: 0x%2");
QString billetTAN = billetTA + QString(" Name: \"%3\"");
QString result;
QString signalT = getMethodSignature(senderMeta, typeid(signal).name(), true);
QString signalA = senderMeta ? getAddress(sender) : "null";
result +=
billetTA.arg("Signal", width(0)).arg(signalT, width(1)).arg(signalA);
QString slotT = getMethodSignature(receiverMeta, typeid(slot).name(), false);
QString slotA = receiverMeta ? getAddress(receiver) : "null";
result += billetTA.arg("Slot", width(0)).arg(slotT, width(1)).arg(slotA);
QString senderT = getMetaClassName(senderMeta);
QString senderA = getAddress(sender);
QString senderN = sender->objectName();
result += billetTAN.arg("Sender", width(0))
.arg(senderT, width(1))
.arg(senderA)
.arg(senderN);
QString receiverT = getMetaClassName(receiverMeta);
QString receiverA = getAddress(receiver);
QString receiverN = receiver->objectName();
result += billetTAN.arg("Receiver", width(0))
.arg(receiverT, width(1))
.arg(receiverA)
.arg(receiverN);
return result;
}
#endif // APPLICATION_CONNECTOR_H
+150
View File
@@ -0,0 +1,150 @@
#include "application_controller.h"
#include "config.h"
#include "database_manager.h"
// Translations
#include <QCoreApplication>
#include <QLocale>
#include <QTranslator>
// Generation of records
#include "refund_generator.h"
#include "session_generator.h"
#include "ticket_generator.h"
// Auntification
#include "auth_dialog.h"
qint32 ApplicationController::userId_ = -1;
qint32 ApplicationController::accessLevel_ = 0;
bool ApplicationController::setupApplication() {
if (!ensureDatabaseExists(DATABASE_NAME)) {
return false;
}
if (!connectToDatabase(DATABASE_NAME)) {
return false;
}
if (!loadTranslations("ru", "app", "_", ":/translations")) {
return false;
}
AuthDialog authDialog;
QObject::connect(&authDialog, &AuthDialog::loginSuccessful,
&ApplicationController::onLoginSuccessful);
QObject::connect(&authDialog, &AuthDialog::loginFailed,
&ApplicationController::onLoginFailed);
if (authDialog.exec() != QDialog::Accepted) {
return false; // User has not logged in
}
return true;
}
bool ApplicationController::ensureDatabaseExists(
const QString &databaseFileName) {
DatabaseManager::CreateDatabaseResult result =
DatabaseManager::createDefaultDatabaseIfMissing(databaseFileName);
switch (result) {
case DatabaseManager::CreateDatabaseResult::Success: {
#ifndef NDEBUG
qInfo() << "Generating test data...";
DayManager dayManager(QTime(8, 0), QTime(17, 0));
dayManager.setMode(DayManager::SlotSelectionMode::Quick);
dayManager.setMode(DayManager::StartTimeMode::Random);
const QDate begin = QDate(2025, 1, 1); // Since 01-01-2025
const QDate end = begin.addDays(5);
ReservationManager reservationManager(14, begin.daysTo(end),
std::move(dayManager));
SessionGenerator sessionGenerator(reservationManager);
sessionGenerator.generateAll(
sessionGenerator.defaultShouldLog(1 /* percent */, 60 /* seconds */));
QPair<QTime, QTime> timeRange(QTime(9, 30), QTime(15, 30));
TicketGenerator ticketsGenerator(
{0, 30} /* day range */, timeRange,
{0.0, 1.0} /* fill ratio per session range*/);
GeneratorInterface::ShouldLogSuccess performOtherwise =
ticketsGenerator.shouldLogFirstAndLastPercent();
ticketsGenerator.generateByFillRatio(
0.35,
ticketsGenerator.defaultShouldLog(1 /* percent */, 600 /* seconds */));
RefundGenerator refundGenerator({0.1, 0.5} /* refund ratio range */,
{0, 7} /* day range */, timeRange);
refundGenerator.generateByFillRatio(
0.1,
refundGenerator.defaultShouldLog(1 /* percent */, 600 /* seconds
*/));
qInfo() << "Test data generated successfully";
#endif
qInfo() << "Database created and initialized successfully";
} break;
case DatabaseManager::CreateDatabaseResult::AlreadyExists:
break;
case DatabaseManager::CreateDatabaseResult::Error:
qCritical() << "Failed to create and connect to the database";
return false;
}
return true;
}
void ApplicationController::onLoginFailed() {
userId_ = -1;
accessLevel_ = 0;
}
void ApplicationController::onLoginSuccessful(qint32 userId,
qint32 accessLevel) {
userId_ = userId;
accessLevel_ = accessLevel;
}
bool ApplicationController::connectToDatabase(const QString &databaseName) {
if (!DatabaseManager::connectToDefaultDatabase(databaseName)) {
qCritical() << "Failed to connect to the database";
return false;
}
qInfo() << "Connected to database" << databaseName << "successfully";
return true;
}
bool ApplicationController::loadTranslations(const QString &locale,
const QString &filename,
const QString &prefix,
const QString &directory) {
QCoreApplication *app = QCoreApplication::instance();
QTranslator *translator = new QTranslator(app);
bool loaded = translator->load(QLocale(locale), filename, prefix, directory);
if (!loaded) {
qWarning() << "Failed to load translations";
return false;
}
bool installed = app->installTranslator(translator);
if (!installed) {
qWarning() << "Failed to install translations";
return false;
}
qInfo() << "Translations are loaded successfully";
return true;
}
+37
View File
@@ -0,0 +1,37 @@
#ifndef APPLICATION_CONTROLLER_H
#define APPLICATION_CONTROLLER_H
#include <QString>
class ApplicationController {
private:
static qint32 userId_;
static qint32 accessLevel_;
public:
explicit ApplicationController() = default;
static bool setupApplication();
static inline qint32 accessLevel();
static inline qint32 userId();
private slots:
static void onLoginFailed();
static void onLoginSuccessful(qint32 userId, qint32 accessLevel);
private:
// Database
static bool connectToDatabase(const QString &databaseFileName);
static bool ensureDatabaseExists(const QString &databaseFileName);
// Translations
static bool loadTranslations(const QString &locale, const QString &filename,
const QString &prefix, const QString &directory);
};
inline qint32 ApplicationController::accessLevel() { return accessLevel_; }
inline qint32 ApplicationController::userId() { return userId_; }
#endif // APPLICATION_CONTROLLER_H