RedPanda-CPP/RedPandaIDE/widgets/ojproblemsetmodel.cpp

464 lines
14 KiB
C++
Raw Normal View History

2021-12-26 23:18:28 +08:00
/*
* Copyright (C) 2020-2022 Roy Qu (royqh1979@gmail.com)
*
* This program 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.
*
* This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
*/
2021-10-31 17:01:34 +08:00
#include "ojproblemsetmodel.h"
2021-11-01 00:40:11 +08:00
#include <QFile>
2021-11-01 20:44:08 +08:00
#include <QIcon>
2021-11-01 00:40:11 +08:00
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include "../utils.h"
2021-11-01 20:44:08 +08:00
#include "../iconsmanager.h"
#include "../systemconsts.h"
2021-11-01 00:40:11 +08:00
2021-10-31 17:01:34 +08:00
OJProblemSetModel::OJProblemSetModel(QObject *parent) : QAbstractListModel(parent)
{
}
2021-11-01 00:40:11 +08:00
void OJProblemSetModel::clear()
{
beginResetModel();
2021-11-01 00:40:11 +08:00
mProblemSet.problems.clear();
mProblemSet.exportFilename.clear();
endResetModel();
2021-11-01 00:40:11 +08:00
}
int OJProblemSetModel::count()
{
return mProblemSet.problems.count();
}
void OJProblemSetModel::create(const QString& name)
{
mProblemSet.name = name;
clear();
}
void OJProblemSetModel::rename(const QString &newName)
{
if (mProblemSet.name!=newName)
mProblemSet.name = newName;
}
QString OJProblemSetModel::name() const
2021-11-01 00:40:11 +08:00
{
return mProblemSet.name;
}
QString OJProblemSetModel::exportFilename() const
{
return mProblemSet.exportFilename;
}
2021-11-01 00:40:11 +08:00
void OJProblemSetModel::addProblem(POJProblem problem)
{
beginInsertRows(QModelIndex(), mProblemSet.problems.count(), mProblemSet.problems.count());
mProblemSet.problems.append(problem);
endInsertRows();
}
POJProblem OJProblemSetModel::problem(int index)
{
return mProblemSet.problems[index];
}
void OJProblemSetModel::removeProblem(int index)
{
Q_ASSERT(index>=0 && index < mProblemSet.problems.count());
beginRemoveRows(QModelIndex(),index,index);
2021-11-01 00:40:11 +08:00
mProblemSet.problems.removeAt(index);
endRemoveRows();
2021-11-01 00:40:11 +08:00
}
bool OJProblemSetModel::problemNameUsed(const QString &name)
{
foreach (const POJProblem& problem, mProblemSet.problems) {
if (name == problem->name)
return true;
}
return false;
}
void OJProblemSetModel::removeAllProblems()
{
clear();
}
void OJProblemSetModel::saveToFile(const QString &fileName)
{
QFile file(fileName);
if (file.open(QFile::WriteOnly | QFile::Truncate)) {
QJsonObject obj;
mProblemSet.exportFilename=fileName;
2021-11-01 00:40:11 +08:00
obj["name"]=mProblemSet.name;
QJsonArray problemsArray;
foreach (const POJProblem& problem, mProblemSet.problems) {
QJsonObject problemObj;
problemObj["name"]=problem->name;
problemObj["url"]=problem->url;
problemObj["description"]=problem->description;
if (fileExists(problem->answerProgram))
problemObj["answer_program"] = problem->answerProgram;
2021-11-01 00:40:11 +08:00
QJsonArray cases;
foreach (const POJProblemCase& problemCase, problem->cases) {
QJsonObject caseObj;
caseObj["name"]=problemCase->name;
caseObj["input"]=problemCase->input;
QString path = problemCase->inputFileName;
QString prefix = includeTrailingPathDelimiter(extractFileDir(fileName));
if (path.startsWith(prefix, PATH_SENSITIVITY)) {
path = "%ProblemSetPath%/"+ path.mid(prefix.length());
}
caseObj["input_filename"]=path;
path = problemCase->expectedOutputFileName;
if (path.startsWith(prefix, PATH_SENSITIVITY)) {
path = "%ProblemSetPath%/"+ path.mid(prefix.length());
}
caseObj["expected_output_filename"]=path;
2021-11-01 00:40:11 +08:00
caseObj["expected"]=problemCase->expected;
cases.append(caseObj);
}
problemObj["cases"]=cases;
problemsArray.append(problemObj);
}
obj["problems"]=problemsArray;
QJsonDocument doc;
doc.setObject(obj);
file.write(doc.toJson());
file.close();
} else {
throw FileError(QObject::tr("Can't open file '%1' for read.")
.arg(fileName));
}
}
void OJProblemSetModel::loadFromFile(const QString &fileName)
2021-10-31 17:01:34 +08:00
{
2021-11-01 00:40:11 +08:00
QFile file(fileName);
if (file.open(QFile::ReadOnly)) {
QByteArray content = file.readAll();
QJsonParseError error;
QJsonDocument doc(QJsonDocument::fromJson(content,&error));
if (error.error!=QJsonParseError::NoError) {
throw FileError(QObject::tr("Can't parse problem set file '%1':%2")
.arg(fileName)
.arg(error.errorString()));
}
beginResetModel();
QJsonObject obj = doc.object();
mProblemSet.name = obj["name"].toString();
mProblemSet.problems.clear();
QJsonArray problemsArray = obj["problems"].toArray();
foreach (const QJsonValue& problemVal, problemsArray) {
QJsonObject problemObj = problemVal.toObject();
POJProblem problem = std::make_shared<OJProblem>();
problem->name = problemObj["name"].toString();
problem->url = problemObj["url"].toString();
problem->description = problemObj["description"].toString();
problem->answerProgram = problemObj["answer_program"].toString();
2021-11-01 00:40:11 +08:00
QJsonArray casesArray = problemObj["cases"].toArray();
foreach (const QJsonValue& caseVal, casesArray) {
QJsonObject caseObj = caseVal.toObject();
POJProblemCase problemCase = std::make_shared<OJProblemCase>();
problemCase->name = caseObj["name"].toString();
problemCase->input = caseObj["input"].toString();
problemCase->expected = caseObj["expected"].toString();
QString path = caseObj["input_filename"].toString();
if (path.startsWith("%ProblemSetPath%/")) {
path = includeTrailingPathDelimiter(extractFileDir(fileName))+
path.mid(QLatin1String("%ProblemSetPath%/").size());
}
problemCase->inputFileName=path;
path = caseObj["expected_output_filename"].toString();
if (path.startsWith("%ProblemSetPath%/")) {
path = includeTrailingPathDelimiter(extractFileDir(fileName))+
path.mid(QLatin1String("%ProblemSetPath%/").size());
}
problemCase->expectedOutputFileName=path;
problemCase->testState = ProblemCaseTestState::NotTested;
2021-11-01 00:40:11 +08:00
problem->cases.append(problemCase);
}
mProblemSet.problems.append(problem);
}
endResetModel();
} else {
throw FileError(QObject::tr("Can't open file '%1' for read.")
.arg(fileName));
}
}
2021-10-31 17:01:34 +08:00
void OJProblemSetModel::updateProblemAnswerFilename(const QString &oldFilename, const QString &newFilename)
{
foreach (POJProblem problem, mProblemSet.problems) {
if (QString::compare(problem->answerProgram,oldFilename,PATH_SENSITIVITY)==0) {
problem->answerProgram = newFilename;
}
}
}
int OJProblemSetModel::rowCount(const QModelIndex &) const
2021-11-01 00:40:11 +08:00
{
return mProblemSet.problems.count();
2021-10-31 17:01:34 +08:00
}
2021-11-01 00:40:11 +08:00
QVariant OJProblemSetModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
2021-11-01 09:18:23 +08:00
if (role == Qt::DisplayRole || role == Qt::EditRole) {
2021-11-01 00:40:11 +08:00
return mProblemSet.problems[index.row()]->name;
}
return QVariant();
}
2021-11-01 09:18:23 +08:00
bool OJProblemSetModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (!index.isValid())
return false;
if (role == Qt::EditRole) {
QString s = value.toString();
if (!s.isEmpty()) {
mProblemSet.problems[index.row()]->name = s;
emit problemNameChanged(index.row());
2021-11-01 09:18:23 +08:00
return true;
}
}
return false;
}
Qt::ItemFlags OJProblemSetModel::flags(const QModelIndex &) const
{
return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable;
}
OJProblemModel::OJProblemModel(QObject *parent): QAbstractTableModel(parent)
2021-11-01 00:40:11 +08:00
{
}
const POJProblem &OJProblemModel::problem() const
2021-10-31 17:01:34 +08:00
{
return mProblem;
}
2021-11-01 00:40:11 +08:00
void OJProblemModel::setProblem(const POJProblem &newProblem)
{
if (newProblem!=mProblem) {
beginResetModel();
mProblem = newProblem;
endResetModel();
}
}
void OJProblemModel::addCase(POJProblemCase problemCase)
{
if (mProblem==nullptr)
return;
beginInsertRows(QModelIndex(),mProblem->cases.count(),mProblem->cases.count());
mProblem->cases.append(problemCase);
endInsertRows();
}
void OJProblemModel::removeCase(int index)
2021-10-31 17:01:34 +08:00
{
2021-11-01 00:40:11 +08:00
if (mProblem==nullptr)
return;
Q_ASSERT(index >= 0 && index < mProblem->cases.count());
beginRemoveRows(QModelIndex(),index,index);
mProblem->cases.removeAt(index);
endRemoveRows();
2021-10-31 17:01:34 +08:00
}
void OJProblemModel::removeCases()
{
beginRemoveRows(QModelIndex(),0,mProblem->cases.count());
mProblem->cases.clear();
endRemoveRows();
}
2021-11-01 00:40:11 +08:00
POJProblemCase OJProblemModel::getCase(int index)
2021-10-31 17:01:34 +08:00
{
2021-11-01 00:40:11 +08:00
if (mProblem==nullptr)
return POJProblemCase();
return mProblem->cases[index];
}
2021-11-01 20:44:08 +08:00
POJProblemCase OJProblemModel::getCaseById(const QString& id)
{
if (mProblem==nullptr)
return POJProblemCase();
foreach (const POJProblemCase& problemCase, mProblem->cases){
if (problemCase->getId() == id)
return problemCase;
}
return POJProblemCase();
}
int OJProblemModel::getCaseIndexById(const QString &id)
{
if (mProblem==nullptr)
return -1;
for (int i=0;i<mProblem->cases.size();i++) {
const POJProblemCase& problemCase = mProblem->cases[i];
if (problemCase->getId() == id)
return i;
}
return -1;
}
2021-11-01 00:40:11 +08:00
void OJProblemModel::clear()
{
if (mProblem==nullptr)
return;
beginResetModel();
2021-11-01 00:40:11 +08:00
mProblem->cases.clear();
endResetModel();
2021-11-01 00:40:11 +08:00
}
2021-10-31 17:01:34 +08:00
2021-11-01 00:40:11 +08:00
int OJProblemModel::count()
{
if (mProblem == nullptr)
return 0;
return mProblem->cases.count();
}
2021-11-01 20:44:08 +08:00
void OJProblemModel::update(int row)
{
emit dataChanged(index(row,0),index(row,0));
}
QString OJProblemModel::getTitle()
{
if (!mProblem)
return "";
int total = mProblem->cases.count();
int passed = 0;
foreach (const POJProblemCase& problemCase, mProblem->cases) {
if (problemCase->testState == ProblemCaseTestState::Passed)
passed ++ ;
}
QString title = QString("%1 (%2/%3)").arg(mProblem->name)
.arg(passed).arg(total);
if (!mProblem->url.isEmpty()) {
title = QString("<a href=\"%1\">%2</a>").arg(mProblem->url,title);
}
return title;
}
QString OJProblemModel::getTooltip()
{
if (!mProblem)
return "";
QString s;
s=QString("<h3>%1</h3>").arg(mProblem->name);
if (!mProblem->description.isEmpty())
s+=QString("<p>%1</p>")
.arg(mProblem->description);
return s;
}
2021-11-01 00:40:11 +08:00
int OJProblemModel::rowCount(const QModelIndex &) const
{
if (mProblem==nullptr)
return 0;
return mProblem->cases.count();
}
QVariant OJProblemModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
if (mProblem==nullptr)
return QVariant();
switch (index.column()) {
case 0:
if (role == Qt::DisplayRole || role == Qt::EditRole) {
POJProblemCase problemCase = mProblem->cases[index.row()];
return problemCase->name;
} else if (role == Qt::DecorationRole) {
switch (mProblem->cases[index.row()]->testState) {
case ProblemCaseTestState::Failed:
return pIconsManager->getIcon(IconsManager::ACTION_PROBLEM_FALIED);
case ProblemCaseTestState::Passed:
return pIconsManager->getIcon(IconsManager::ACTION_PROBLEM_PASSED);
case ProblemCaseTestState::Testing:
return pIconsManager->getIcon(IconsManager::ACTION_PROBLEM_TESTING);
default:
return QVariant();
}
2021-11-01 20:44:08 +08:00
}
break;
case 1:
if (role == Qt::DisplayRole) {
POJProblemCase problemCase = mProblem->cases[index.row()];
if (problemCase->testState == ProblemCaseTestState::Passed
|| problemCase->testState == ProblemCaseTestState::Failed)
return problemCase->runningTime;
else
return "";
}
break;
2021-11-01 00:40:11 +08:00
}
2021-11-01 00:40:11 +08:00
return QVariant();
2021-10-31 17:01:34 +08:00
}
2021-11-01 09:18:23 +08:00
bool OJProblemModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (!index.isValid())
return false;
if (index.column()!=0)
return false;
2021-11-01 20:44:08 +08:00
if (mProblem==nullptr)
return false;
if (role == Qt::EditRole ) {
2021-11-01 20:44:08 +08:00
QString s = value.toString();
if (!s.isEmpty()) {
mProblem->cases[index.row()]->name = s;
return true;
}
}
return false;
2021-11-01 09:18:23 +08:00
}
Qt::ItemFlags OJProblemModel::flags(const QModelIndex &idx) const
2021-11-01 09:18:23 +08:00
{
Qt::ItemFlags flags=Qt::ItemIsEnabled | Qt::ItemIsSelectable;
if (idx.column()==0)
flags |= Qt::ItemIsEditable ;
return flags;
}
2022-07-04 11:39:06 +08:00
int OJProblemModel::columnCount(const QModelIndex &/*parent*/) const
{
return 2;
}
QVariant OJProblemModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
switch (section) {
case 0:
return tr("Name");
case 1:
return tr("Time(ms)");
}
}
return QVariant();
2021-11-01 09:18:23 +08:00
}