Initial commit
This commit is contained in:
@@ -0,0 +1,219 @@
|
||||
#include "ticket_generator.h"
|
||||
|
||||
#include "create_ticket_request.h"
|
||||
#include "hall_dto.h"
|
||||
#include "halls_repository.h"
|
||||
#include "repository_manager.h"
|
||||
#include "repository_result.h"
|
||||
#include "schema_metadata.h"
|
||||
#include "session_dto.h"
|
||||
#include "sessions_repository.h"
|
||||
#include "tickets_repository.h"
|
||||
#include <QDebug>
|
||||
#include <QStack>
|
||||
#include <numeric>
|
||||
|
||||
#include "repository_result.h"
|
||||
|
||||
std::random_device TicketGenerator::rd_;
|
||||
std::mt19937 TicketGenerator::generator_(rd_());
|
||||
|
||||
TicketGenerator::TicketGenerator(
|
||||
const QPair<qint32, qint32> &dayRange, const QPair<QTime, QTime> &timeRange,
|
||||
const QPair<double, double> &fillRatioPerSessionRange)
|
||||
: BaseGenerator("Ticket"), DateTimeGenerator(dayRange, timeRange) {
|
||||
if (!setFillRatioPerSessionRange(fillRatioPerSessionRange)) {
|
||||
qFatal() << "Failed to set fill ratio per session range";
|
||||
}
|
||||
|
||||
initializeRepositories();
|
||||
initializeSessionIdToAvailableSeatMap(); // After initializing repositories
|
||||
initializeSessionIdToBeginAtMap(); // After initializing repositories
|
||||
initializeRemainingCapacity(); // After initializing sessionToAvailableSeats
|
||||
|
||||
qInfo() << "Number of sessions:" << sessionIdToAvailableSeatNumbers_.size();
|
||||
}
|
||||
|
||||
void TicketGenerator::initializeRepositories() {
|
||||
RepositoryManager &repositoryManager = RepositoryManager::instance();
|
||||
SchemaMetadata metadata = SchemaMetadata::defaultSchema();
|
||||
|
||||
hallsRepository_ = repositoryManager.getRepositoryAs<HallsRepository>(
|
||||
metadata.repositoryTableName("halls"));
|
||||
sessionsRepository_ = repositoryManager.getRepositoryAs<SessionsRepository>(
|
||||
metadata.repositoryTableName("sessions"));
|
||||
ticketsRepository_ = repositoryManager.getRepositoryAs<TicketsRepository>(
|
||||
metadata.repositoryTableName("tickets"));
|
||||
|
||||
if (!bool(ticketsRepository_ && sessionsRepository_)) {
|
||||
qFatal() << "Failed to initialize repositories";
|
||||
}
|
||||
}
|
||||
|
||||
void TicketGenerator::initializeSessionIdToAvailableSeatMap() {
|
||||
QVector<HallDTO> halls;
|
||||
RepositoryResult resultGetAllHalls = hallsRepository_->getAllHalls(halls);
|
||||
if (!resultGetAllHalls.success) {
|
||||
qFatal() << "Failed to retrieve halls";
|
||||
}
|
||||
|
||||
QVector<SessionDTO> sessions;
|
||||
RepositoryResult resultGetAllSessions =
|
||||
sessionsRepository_->getAllSessions(sessions);
|
||||
if (!resultGetAllSessions.success) {
|
||||
qFatal() << "Failed to retrieve sessions";
|
||||
}
|
||||
|
||||
QMap<qint32 /* sessionId */, QSet<qint32> /* availableSeatNumbers */>
|
||||
hallIdToAvailableSeatNumbers;
|
||||
|
||||
for (const HallDTO &hall : halls) {
|
||||
qint32 targetCapacity =
|
||||
std::ceil(hall.capacity() * generateFillRatioPerSession());
|
||||
|
||||
QVector<qint32> availableSeats(targetCapacity);
|
||||
std::iota(availableSeats.begin(), availableSeats.end(), 1);
|
||||
std::shuffle(availableSeats.begin(), availableSeats.end(), generator_);
|
||||
|
||||
QSet<qint32> buffer;
|
||||
for (qint32 i = 0; i < targetCapacity; ++i) {
|
||||
buffer.insert(availableSeats[i]);
|
||||
}
|
||||
|
||||
hallIdToAvailableSeatNumbers[hall.hallId()] = std::move(buffer);
|
||||
}
|
||||
|
||||
for (const SessionDTO &session : sessions) {
|
||||
sessionIdToAvailableSeatNumbers_[session.sessionId()] =
|
||||
hallIdToAvailableSeatNumbers[session.hallId()];
|
||||
}
|
||||
}
|
||||
|
||||
void TicketGenerator::initializeSessionIdToBeginAtMap() {
|
||||
QVector<SessionDTO> sessions;
|
||||
RepositoryResult resultGetAllSessions =
|
||||
sessionsRepository_->getAllSessions(sessions);
|
||||
if (!resultGetAllSessions.success) {
|
||||
qFatal() << "Failed to retrieve sessions";
|
||||
}
|
||||
|
||||
for (const SessionDTO &session : sessions) {
|
||||
sessionIdToBeginAt_[session.sessionId()] = session.beginAt();
|
||||
}
|
||||
}
|
||||
|
||||
bool TicketGenerator::setFillRatioPerSessionRange(
|
||||
const QPair<double, double> &range) {
|
||||
const auto &[minValue, maxValue] = range;
|
||||
if (minValue > maxValue) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto &[minFillRatioPerSession, maxFillRatioPerSession] =
|
||||
fillRatioPerSessionRange_;
|
||||
minFillRatioPerSession = std::max(minValue, 0.0);
|
||||
maxFillRatioPerSession = std::min(maxValue, 1.0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
double TicketGenerator::generateFillRatioPerSession() const {
|
||||
const auto &[min, max] = fillRatioPerSessionRange_;
|
||||
std::uniform_real_distribution<double> distribution(min, max);
|
||||
return distribution(generator_);
|
||||
}
|
||||
|
||||
void TicketGenerator::initializeRemainingCapacity() {
|
||||
remainingCapacity_ =
|
||||
std::accumulate(sessionIdToAvailableSeatNumbers_.cbegin(),
|
||||
sessionIdToAvailableSeatNumbers_.cend(), 0,
|
||||
[&](qsizetype sum, const QSet<qint32> &availableSeats) {
|
||||
return sum + availableSeats.size();
|
||||
});
|
||||
}
|
||||
|
||||
QString
|
||||
TicketGenerator::createFailureMessage(const QString &errorMessage) const {
|
||||
QString additionalInfo =
|
||||
QString("Additional info: Session id: %1 Seat number: %2 Sold at: %3 "
|
||||
"Begin at: %4")
|
||||
.arg(reservationContext_.sessionId)
|
||||
.arg(reservationContext_.seatNumber)
|
||||
.arg(reservationContext_.soldAt.toString("yyyy-MM-dd hh:mm:ss"))
|
||||
.arg(reservationContext_.beginAt.toString("yyyy-MM-dd hh:mm:ss"));
|
||||
QString failureMessage = "Failure:\n\t%1\n\tError message: %2";
|
||||
return failureMessage.arg(additionalInfo).arg(errorMessage);
|
||||
}
|
||||
|
||||
QString TicketGenerator::createSuccessMessage() const {
|
||||
return QString("Success: EId: %1 SN: %2")
|
||||
.arg(reservationContext_.sessionId)
|
||||
.arg(reservationContext_.seatNumber);
|
||||
}
|
||||
|
||||
qsizetype TicketGenerator::calculateRemainingCapacity() const {
|
||||
return remainingCapacity_;
|
||||
}
|
||||
|
||||
RepositoryResult TicketGenerator::createRecord() {
|
||||
CreateTicketRequest request;
|
||||
request.setSessionId(reservationContext_.sessionId);
|
||||
request.setSeatNumber(reservationContext_.seatNumber);
|
||||
request.setSoldAt(reservationContext_.soldAt);
|
||||
|
||||
RepositoryResult result = ticketsRepository_->createTicket(request);
|
||||
|
||||
if (result.success) {
|
||||
--remainingCapacity_;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool TicketGenerator::prepareData() {
|
||||
if (remainingCapacity_ <= 0) {
|
||||
qInfo() << "All tickets have been sold";
|
||||
return false;
|
||||
}
|
||||
|
||||
qsizetype deltaIndex =
|
||||
bounded<qsizetype>(0, sessionIdToAvailableSeatNumbers_.size());
|
||||
auto it = sessionIdToAvailableSeatNumbers_.begin();
|
||||
std::advance(it, deltaIndex);
|
||||
|
||||
const qint32 sessionId = it.key();
|
||||
QSet<qint32> &availableSeatNumbers = it.value(); // Not empty
|
||||
|
||||
const QDateTime &beginAt = sessionIdToBeginAt_[sessionId];
|
||||
constexpr qint32 minus20MinutesMs = -20 * 1000 * 60;
|
||||
// soldAt < beginAt || soldAt + 20 minute <= beginAt
|
||||
QDateTime soldAt = generate(beginAt.addMSecs(minus20MinutesMs));
|
||||
|
||||
qsizetype seatNumberIndex =
|
||||
bounded<qsizetype>(0, availableSeatNumbers.size());
|
||||
auto seatNumberIt = availableSeatNumbers.cbegin();
|
||||
std::advance(seatNumberIt, seatNumberIndex);
|
||||
|
||||
qint32 seatNumber = *seatNumberIt;
|
||||
|
||||
ReservationContext context{beginAt, soldAt, seatNumber, sessionId};
|
||||
reservationContext_ = std::move(context);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void TicketGenerator::clearStaleData() {
|
||||
|
||||
qint32 sessionId = reservationContext_.sessionId;
|
||||
qint32 seatNumber = reservationContext_.seatNumber;
|
||||
QSet<qint32> &availableSeatNumbers =
|
||||
sessionIdToAvailableSeatNumbers_[sessionId];
|
||||
|
||||
availableSeatNumbers.remove(seatNumber);
|
||||
if (availableSeatNumbers.empty()) {
|
||||
qWarning() << "All tickets for session with id" << sessionId
|
||||
<< "have been sold";
|
||||
sessionIdToAvailableSeatNumbers_.remove(sessionId);
|
||||
sessionIdToBeginAt_.remove(sessionId);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user