Compare commits

...

8 Commits

14 changed files with 256 additions and 24 deletions

View File

@ -139,7 +139,7 @@ void NewItemDialog::resetContent() {
m_firstNameEdit->setText("");
m_shareTypeBox->setCurrentIndex(0);
m_amountSpinBox->setValue(1);
m_biddingTypeBox->setCurrentIndex(0);
m_biddingTypeBox->setCurrentIndex(4);
m_bidding1SpinBox->setValue(0);
m_bidding2SpinBox->setValue(0);
m_bidding3SpinBox->setValue(0);

View File

@ -1,5 +1,6 @@
#include "itemdetailmapper.h"
#include <data/settingshandler.h>
#include <QAbstractItemModel>
#include <QComboBox>
#include <QDataWidgetMapper>
@ -157,6 +158,10 @@ ItemDetailMapper::ItemDetailMapper(QWidget* parent)
outerLayout->addLayout(rightLayout);
setLayout(outerLayout);
const QVariantMap serverSettings = SettingsHandler::getSettings("Server");
const QString urlValue = serverSettings.value("url").toString();
m_serverUrl = urlValue;
/// online user account
connect(m_mailEdit, &QLineEdit::textChanged, this, &ItemDetailMapper::onMailEditChanged);
connect(m_onlineIdDisplay, &QLineEdit::textChanged, this, &ItemDetailMapper::onOnlineIDChanged);
@ -287,14 +292,15 @@ void ItemDetailMapper::onOnlineIDChanged(const QString& text) {
}
}
void ItemDetailMapper::onAccessCodeChanged(const QString& text) {
if (text.isEmpty()) {
void ItemDetailMapper::onAccessCodeChanged(const QString& accessCode) {
if (accessCode.isEmpty()) {
m_sendInviteMailButton->setEnabled(false);
m_accessUrlDisplay->setText("");
updateQRCode("");
} else {
m_sendInviteMailButton->setEnabled(true);
const QString accessUrl = "http://127.0.0.1:4000/log_in/" + text;
const QString accessUrl = m_serverUrl + "/log_in/" + accessCode;
// const QString accessUrl = "https://bietrunde.das-gruene-zebra.de/log_in/" + text;
m_accessUrlDisplay->setText(accessUrl);
updateQRCode(accessUrl);
}

View File

@ -44,7 +44,7 @@ class ItemDetailMapper : public QWidget {
void updateQRCode(const QString text = "");
void onMailEditChanged(const QString& text);
void onOnlineIDChanged(const QString& text);
void onAccessCodeChanged(const QString& text);
void onAccessCodeChanged(const QString& accessCode);
void onCreateOnlineAccountTriggered();
void onSendInviteMailTriggered();
@ -57,6 +57,7 @@ class ItemDetailMapper : public QWidget {
std::unique_ptr<QDataWidgetMapper> m_mapper;
QString m_serverUrl;
/// *** GUI elements ***
/// left layout
QLabel* m_numberLabel;

View File

@ -17,13 +17,22 @@ SummaryWidget::SummaryWidget(std::shared_ptr<ModelSummary> modelSummary, QWidget
/// monthly need
QHBoxLayout* footerLayout = new QHBoxLayout();
QLabel* nTotalSharesLabel = new QLabel("Erwartete Ernteanteile:");
const qreal expectedShares = m_modelSummary->nTotalExpectedShares();
m_nTotalExpectedShares = new QLabel(QString::number(expectedShares));
QLabel* financialNeedLabel = new QLabel("monatlicher Finanzbedarf:");
m_financialNeedBox = new QSpinBox();
m_financialNeedBox->setMaximum(50000);
m_financialNeedBox->setValue(m_financialNeed);
connect(m_financialNeedBox, &QSpinBox::valueChanged, this,
&SummaryWidget::onFinancialNeedChanged);
footerLayout->addWidget(nTotalSharesLabel);
footerLayout->addWidget(m_nTotalExpectedShares);
footerLayout->addStretch(1);
footerLayout->addWidget(financialNeedLabel);
footerLayout->addWidget(m_financialNeedBox);
footerLayout->addStretch(1);
@ -50,6 +59,11 @@ void SummaryWidget::onNExpectedBiddingChanged(int newNExpected) {
m_biddingStatus3->onExpectedBiddingsChanged(newNExpected);
}
void SummaryWidget::onNTotalExpectedSharesChanged(qreal newNExpected) {
const QString expectedSharesString = QString::number(newNExpected);
m_nTotalExpectedShares->setText(expectedSharesString);
}
void SummaryWidget::onNPlacedBiddingsChanged(const int roundNumber) {
switch (roundNumber) {
case 1:
@ -140,6 +154,9 @@ void SummaryWidget::setupConnections() {
// "bindProperty(&bindable, &signal, &getter, widget)"
QObject::connect(m_modelSummary.get(), &ModelSummary::nExpectedBiddingsChanged,
[&]() { onNExpectedBiddingChanged(m_modelSummary->nExpectedBiddings()); });
QObject::connect(m_modelSummary.get(), &ModelSummary::nTotalExpectedSharesChanged, this, [&]() {
onNTotalExpectedSharesChanged(m_modelSummary->nTotalExpectedShares());
});
QObject::connect(m_modelSummary.get(), &ModelSummary::nPlacedBiddingsChanged, this,
&SummaryWidget::onNPlacedBiddingsChanged);
QObject::connect(m_modelSummary.get(), &ModelSummary::biddingSumChanged, this,

View File

@ -18,6 +18,7 @@ class SummaryWidget : public QWidget {
private slots:
void onFinancialNeedChanged(int newFinancialNeed);
void onNExpectedBiddingChanged(int newNExpected);
void onNTotalExpectedSharesChanged(qreal newNExpected);
void onNPlacedBiddingsChanged(const int roundNumber);
void onBiddingSumChanged(const int roundNumber);
@ -33,6 +34,7 @@ class SummaryWidget : public QWidget {
// TODO read from settings (maybe via model/core; maybe set in constructor)
const int m_financialNeed = 13942;
QSpinBox* m_financialNeedBox = nullptr;
QLabel* m_nTotalExpectedShares = nullptr;
QLabel* m_rowCountValueLabel;
QLabel* m_nExpectedBiddingsValueLabel;

View File

@ -23,9 +23,12 @@ QList<ModelItemValues> CsvParser::getItemsFromCSVFile(const QString& fileName) {
bool CsvParser::exportToCSVFile(const QList<QStringList>& rows, const QString& filePath) {
Document doc(std::string(), LabelParams(0, -1));
const QList<QString> headerNames = GET_HEADER_NAMES();
for (int column = 0; column < headerNames.size(); ++column) {
doc.SetColumnName(column, headerNames.at(column).toStdString());
// const QList<QString> headerNames = GET_HEADER_NAMES();
const int columnCount = USER_FACING_ROLES.size();
for (int column = 0; column < columnCount; ++column) {
const UserRoles role = GET_ROLE_FOR_COLUMN(column);
std::string columnName = ROLE_NAMES.value(role).toStdString();
doc.SetColumnName(column, columnName);
}
for (int row = 0; row < rows.size(); ++row) {
QStringList rowValues = rows.at(row);
@ -129,8 +132,22 @@ ModelItemValues CsvParser::getItemValuesForRow(
QVariant CsvParser::parseItemValue(const int role, const std::string& valueString) {
QVariant result;
if (STRING_ROLES.contains(role)) {
if (role == ShareTypeRole) {
if (valueString == "AGA") {
result = "erarbeitet";
} else if (valueString == "ja") {
result = "bezahlt";
} else if (valueString == "teils") {
result = "teils/teils";
} else {
result = "";
}
} else {
/// string values
result = QString::fromStdString(valueString);
}
} else if (INT_ROLES.contains(role)) {
/// int values

View File

@ -10,6 +10,8 @@ ModelSummary::ModelSummary(std::shared_ptr<TableModel> model, QObject* parent)
// TODO ? use existing model signals (dataChanged(role),...) instead of special signals
connect(m_model.get(), &TableModel::nExpectedBiddingsChanged, this,
&ModelSummary::nExpectedBiddingsChanged);
connect(m_model.get(), &TableModel::nTotalExpectedSharesChanged, this,
&ModelSummary::nTotalExpectedSharesChanged);
connect(m_model.get(), &TableModel::nPlacedBiddingsChanged, this,
&ModelSummary::nPlacedBiddingsChanged);
connect(m_model.get(), &TableModel::biddingSumChanged, this, &ModelSummary::biddingSumChanged);
@ -29,6 +31,8 @@ QBindable<int> ModelSummary::bindableRowCount() {
return &m_rowCount;
}
qreal ModelSummary::nTotalExpectedShares() const { return m_model->nTotalExpectedShares(); }
int ModelSummary::nExpectedBiddings() const { return m_model->nExpectedBiddings(); }
int ModelSummary::nPlacedBiddings1() const { return m_model->nPlacedBiddings1(); }

View File

@ -19,6 +19,8 @@ class ModelSummary : public QObject {
int rowCount() const;
QBindable<int> bindableRowCount();
qreal nTotalExpectedShares() const;
int nExpectedBiddings() const;
int nPlacedBiddings1() const;
int nPlacedBiddings2() const;
@ -36,6 +38,7 @@ class ModelSummary : public QObject {
void rowCountChanged();
void nExpectedBiddingsChanged();
void nTotalExpectedSharesChanged();
void nPlacedBiddingsChanged(const int roundNumber);
void biddingSumChanged(const int roundNumber);
void biddingAverageChanged(const int roundNumber);

View File

@ -340,6 +340,30 @@ void TableModel::insertItems(int startPosition,
m_undoStack->push(insertCommand);
}
qreal TableModel::nTotalExpectedShares() const {
qreal result = 0;
for (auto i = m_items.begin(), end = m_items.end(); i != end; ++i) {
/// if BiddingTypeRole is not empty
const QString biddingType = (*i)->data(BiddingTypeRole).toString();
const qreal shareAmount = (*i)->data(ShareAmountRole).toReal();
if (biddingType.isEmpty() || shareAmount == 0.0) {
continue;
}
/// add amount
const QString shareType = (*i)->data(ShareTypeRole).toString();
if (shareType == "bezahlt") {
result += shareAmount;
} else if (shareType == "teils/teils") {
result += shareAmount / 2;
} else {
qInfo() << "Share type not 'bezahlt' nor 'teils/teils'. Will be ignored..";
qDebug() << "Share type:" << shareType;
}
}
qInfo() << "Biddings expected for " << result << "shares!";
return result;
}
int TableModel::nExpectedBiddings() const {
int result = 0;
for (auto i = m_items.begin(), end = m_items.end(); i != end; ++i) {
@ -397,6 +421,7 @@ void TableModel::onRowCountChanged(const QModelIndex& parent, int first, int las
}
emit rowCountChanged();
emit nExpectedBiddingsChanged();
emit nTotalExpectedSharesChanged();
emit nPlacedBiddingsChanged(1);
emit nPlacedBiddingsChanged(2);
@ -437,9 +462,9 @@ void TableModel::execEditItemData(const int row, const QMap<int, QVariant>& chan
bool isDataChanged = item->setItemData(changedValues);
if (isDataChanged) {
/// FIXME due to the mapping from roles to the DisplayRole of different columns the complete row
/// is getting notified about (potential) data changes; dataChanged should be called only for
/// the affected columns
/// FIXME due to the mapping from roles to the DisplayRole of different columns the complete
/// row is getting notified about (potential) data changes; dataChanged should be called only
/// for the affected columns
const QModelIndex firstIndex = this->index(row, 0);
const QModelIndex lastIndex = this->index(row, USER_FACING_ROLES.size() - 1);
QList<int> roles = changedValues.keys();
@ -449,6 +474,7 @@ void TableModel::execEditItemData(const int row, const QMap<int, QVariant>& chan
if (roles.contains(BiddingTypeRole) || roles.contains(ShareAmountRole) ||
roles.contains(ShareTypeRole)) {
emit nExpectedBiddingsChanged();
emit nTotalExpectedSharesChanged();
}
if (roles.contains(ShareAmountRole) || roles.contains(ShareTypeRole)) {
emit nPlacedBiddingsChanged(1);
@ -577,6 +603,15 @@ bool TableModel::isItemEqualToItemValues(const QModelIndex& itemIndex,
int TableModel::nPlacedBiddings(const UserRoles biddingRole) const {
int result = 0;
for (auto i = m_items.begin(), end = m_items.end(); i != end; ++i) {
const QString biddingType = (*i)->data(BiddingTypeRole).toString();
const qreal shareAmount = (*i)->data(ShareAmountRole).toReal();
const QString shareType = (*i)->data(ShareTypeRole).toString();
if (biddingType.isEmpty() || shareAmount == 0.0) {
continue;
}
if (shareType == "erarbeitet" || shareType == "") {
continue;
}
int localBidding = (*i)->data(biddingRole).toInt();
if (localBidding > 0) {
result++;
@ -588,6 +623,15 @@ int TableModel::nPlacedBiddings(const UserRoles biddingRole) const {
int TableModel::biddingSum(const UserRoles biddingRole) const {
int result = 0;
for (auto i = m_items.begin(), end = m_items.end(); i != end; ++i) {
const QString biddingType = (*i)->data(BiddingTypeRole).toString();
const qreal shareAmount = (*i)->data(ShareAmountRole).toReal();
const QString shareType = (*i)->data(ShareTypeRole).toString();
if (biddingType.isEmpty() || shareAmount == 0.0) {
continue;
}
if (shareType == "erarbeitet" || shareType == "") {
continue;
}
result += (*i)->data(biddingRole).toInt();
}
return result;
@ -612,10 +656,14 @@ qreal TableModel::averageBiddingAmount(const UserRoles biddingRole) const {
qreal TableModel::totalSharesWithBiddings(const UserRoles biddingRole) const {
qreal result = 0;
for (auto i = m_items.begin(), end = m_items.end(); i != end; ++i) {
const qreal bidding = (*i)->data(biddingRole).toReal();
const QString biddingType = (*i)->data(BiddingTypeRole).toString();
const qreal shareAmount = (*i)->data(ShareAmountRole).toReal();
if (biddingType.isEmpty() || shareAmount == 0.0) {
continue;
}
const int bidding = (*i)->data(biddingRole).toInt();
const QString shareType = (*i)->data(ShareTypeRole).toString();
const bool isValid = bidding != 0 && shareAmount != 0;
const bool isValid = bidding != 0 && shareAmount != 0.0;
if (isValid) {
// qInfo() << "Including entry in bidding average calculation. MailRole:"
// << (*i)->data(MailRole);

View File

@ -63,6 +63,7 @@ class TableModel : public QAbstractTableModel {
const QModelIndex& parentIndex = QModelIndex());
/// property functions
qreal nTotalExpectedShares() const;
int nExpectedBiddings() const;
int nPlacedBiddings1() const;
int nPlacedBiddings2() const;
@ -80,6 +81,7 @@ class TableModel : public QAbstractTableModel {
void rowCountChanged();
void nExpectedBiddingsChanged();
void nTotalExpectedSharesChanged();
void nPlacedBiddingsChanged(const int roundNumber);
void biddingSumChanged(const int roundNumber);
void biddingAverageChanged(const int roundNumber);

View File

@ -194,7 +194,7 @@ void ServerCommunicator::onSendPostRequestTriggered(const PostRequestTypes type,
} else {
int statusCode = reply.httpStatus();
qWarning() << "Request not successful:" << statusCode;
qCritical() << "Content:" << reply.readJson();
qInfo() << "Content:" << reply.readJson();
onPostReplyFailure(type, QString("HTTP status code: %1").arg(statusCode));
}
}
@ -273,7 +273,7 @@ void ServerCommunicator::currentBiddingRoundChangedReply(const QJsonDocument jso
}
void ServerCommunicator::currentBiddingsReply(const QJsonDocument jsonDoc) {
qCritical() << "currentBiddingsReply:" << jsonDoc;
qInfo() << "currentBiddingsReply:" << jsonDoc;
const QList<bidding> biddings = JsonParser::extractBiddings(jsonDoc);
@ -302,7 +302,7 @@ void ServerCommunicator::onLoginTriggered() {
}
if (m_authToken.isEmpty()) {
/// get new authToken
qWarning() << "Creating a new authToken!";
qInfo() << "Creating a new authToken!";
const QByteArray loginBody = createLoginBody();
onSendPostRequestTriggered(LogInAdmin, loginBody);

View File

@ -17,7 +17,8 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(QT NAMES Qt6 REQUIRED COMPONENTS Core LinguistTools)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core LinguistTools)
add_executable(${TARGET_APP} core_test.cpp)
add_executable(${TARGET_APP} core_test.cpp
model_test.cpp)
target_include_directories(${TARGET_APP} PRIVATE ${CORE_LIB_DIR}/include)

View File

@ -9,7 +9,7 @@ inline void PrintTo(const QString& qString, ::std::ostream* os) { *os << qUtf8Pr
QT_END_NAMESPACE
TEST(CoreTests, TestEqualString) {
const QString coreName("GenericCore");
const QString coreName("BeetRoundCore");
const QString coreVersion("0.1.0");
const auto expected = QString("%1 (Version %2)").arg(coreName).arg(coreVersion);
auto core = std::make_unique<GenericCore>();

View File

@ -0,0 +1,131 @@
#include <gtest/gtest.h>
#include <QString>
#include <QtGui/QUndoCommand>
#include "../../libs/BeetRoundCore/model/tablemodel.h"
QT_BEGIN_NAMESPACE
inline void PrintTo(const QString& qString, ::std::ostream* os) { *os << qUtf8Printable(qString); }
QT_END_NAMESPACE
struct ModelTest : testing::Test {
std::shared_ptr<TableModel> model;
ModelTest() { model = make_shared<TableModel>(new QUndoStack()); }
virtual ~ModelTest() {}
};
struct ModelTestWithData : ModelTest {
ModelTestWithData() {
QList<ModelItemValues> itemList;
ModelItemValues itemValues1 = {{Bidding1Role, 100},
{ShareTypeRole, "bezahlt"},
{ShareAmountRole, 1},
{BiddingTypeRole, "digital"}};
itemList.append(itemValues1);
ModelItemValues itemValues2 = {{Bidding1Role, 100},
{ShareTypeRole, "bezahlt"},
{ShareAmountRole, 1},
{BiddingTypeRole, ""}};
itemList.append(itemValues2);
ModelItemValues itemValues3 = {{Bidding1Role, 50},
{ShareTypeRole, "teils/teils"},
{ShareAmountRole, 1},
{BiddingTypeRole, "paper"}};
itemList.append(itemValues3);
ModelItemValues itemValues4 = {{Bidding1Role, 50},
{ShareTypeRole, "bezahlt"},
{ShareAmountRole, 0.5},
{BiddingTypeRole, "paper"}};
itemList.append(itemValues4);
ModelItemValues itemValues5 = {{Bidding1Role, 100},
{ShareTypeRole, "erarbeitet"},
{ShareAmountRole, 1},
{BiddingTypeRole, "paper"}};
itemList.append(itemValues5);
ModelItemValues itemValues6 = {{Bidding1Role, 0},
{ShareTypeRole, "bezahlt"},
{ShareAmountRole, 1},
{BiddingTypeRole, "online"}};
itemList.append(itemValues6);
model->insertItems(0, itemList);
}
};
/// empty model
TEST_F(ModelTest, TestRowCountEmpty) {
const int expectedRowCount = 0;
const auto actualRowCount = model->rowCount();
ASSERT_EQ(expectedRowCount, actualRowCount);
}
TEST_F(ModelTest, TestBidding1Average) {
const int expected = 0;
const auto actualBidding1Average = model->biddingAverage1();
ASSERT_EQ(expected, actualBidding1Average);
}
TEST_F(ModelTest, ExpectedPlacedBiddings) {
const int expected = 0;
const auto expectedPlacedBiddings = model->nExpectedBiddings();
ASSERT_EQ(expected, expectedPlacedBiddings);
}
TEST_F(ModelTest, ExpectedTotalShareAmount) {
const int expected = 0;
const auto nTotalExpectedShares = model->nTotalExpectedShares();
ASSERT_EQ(expected, nTotalExpectedShares);
}
/// model with data
TEST_F(ModelTestWithData, TestRowCount) {
const int expected = 6;
const auto actual = model->rowCount();
ASSERT_EQ(expected, actual);
}
TEST_F(ModelTestWithData, TestBidding1Average) {
const qreal expected = 100;
const auto actual = model->biddingAverage1();
ASSERT_EQ(expected, actual);
}
TEST_F(ModelTestWithData, ExpectedPlacedBiddings) {
const int expected = 4;
const auto actual = model->nExpectedBiddings();
ASSERT_EQ(expected, actual);
}
TEST_F(ModelTestWithData, PlacedBiddings1) {
const int expected = 3;
const auto actual = model->nPlacedBiddings1();
ASSERT_EQ(expected, actual);
}
TEST_F(ModelTestWithData, NTotalExpectedShares) {
const qreal expected = 3;
const auto actual = model->nTotalExpectedShares();
ASSERT_EQ(expected, actual);
}