Merge remote-tracking branch 'origin/DEV' into DEV

This commit is contained in:
krivoshein
2025-04-09 14:35:10 +03:00
116 changed files with 5645 additions and 1581 deletions

11
.gitignore vendored
View File

@@ -1,5 +1,4 @@
#
/TestServerLMS/Debug64/Application/
/BUILDS/
CMakeLists.txt.user
@@ -16,10 +15,8 @@ _deps
CMakeUserPresets.json
TestServerLMS/TestServerLMS/CMakeLists.txt.user
ServerLMS/ServerLMS/CMakeLists.txt.user
DB_IaT/InstructorsAndTrainees/CMakeLists.txt.user
GUIdataBaseLMS/GUIdataBaseLMS/CMakeLists.txt.user
DB_LMS/DataBaseLMS/CMakeLists.txt.user
DB_LMS/DataBaseLMS/CMakeLists.txt.user
GUIdataBaseLMS/GUIdataBaseLMS/CMakeLists.txt.user
InstructorsAndTrainees/CMakeLists.txt.user
GUIdataBaseLMS/CMakeLists.txt.user
DataBaseLMS/CMakeLists.txt.user
DOCS/.obsidian
GUIdataBaseLMS/GUIdataBaseLMS/StaticData
GUIdataBaseLMS/StaticData

View File

@@ -6,17 +6,22 @@ kanban-plugin: board
## backLog
- [ ] окошко с выбором перенести в GUI
- [ ] Клиент НЕ СМОЖЕТ ВЫБИРАТЬ ВЕРСИИ
- [ ] увеличить размер текста в окне обновлений
## bugs
- [ ] QT сервер Найти причину двойного вызова проверки при логине инструктором
- [ ] sig fault если не выбрана версия
- [ ] после удаления версии сбрасывать текст в описании
## feature client Unity
- [ ] отправлять сигнал на пересчет хэша с саб частью
- [ ] Добавить обновление инструктора, если он перелогинился
- [ ] При нажатии на кнопку обновить, менять надпись на Загрузка
- [ ] сохранение не отправленных задач
## feature client QT
@@ -24,7 +29,6 @@ kanban-plugin: board
- [ ] Иерархия проекта - папка application, папка updater и линк на основной экзешник
- [ ] на старте все мониторы должны быть активны
- [ ] Нужен ли дополнительный выбор для загрузки с мат моделью или нет?
- [ ] при создании копии переключение сервера и переключения клиента
## feature server
@@ -32,6 +36,7 @@ kanban-plugin: board
- [ ] добавить генерацию пустых файлов, если shared не найден
- [ ] добавить подключение без DB
- [ ] ПЕРЕВЕСТИ все действия под операции и формировать процент из них
- [ ] блокировать выгрузку под инструктором, если режим версия base
## NOW
@@ -40,6 +45,16 @@ kanban-plugin: board
## Complete
- [ ] qt клиент сервера, запрет на ввод латиницей и запрещенные знаки
- [ ] после выгрузки бесконечное обновление
- [ ] Уведомление о том, что версия сервера неизменяемая и инструкция, что нужно переключить версию сервера, подтянуть изменяемую версию и перекинуть файлы + путь
- [ ] Qt ClientЖ отключать виджет версии при разьединении
- [ ] Отображать версию на сервере
- [ ] При удалении с клиента не удаляется папка на сервере
- [ ] убрать функционал смены версии с клиента
- [ ] добавить информацию Авторе и изменяемая версия или нет
- [ ] отправлять сигнал на пересчет хэша с саб частью
- [ ] GUI server: при неподключенном сервере, отключать попытку изменить версию
- [ ] добавить автоматическое выключение после создания копии
- [ ] sendSystem::sendXmlAnswer новый вариант отпарвки XML пакетов
- [ ] выписать все варианты взаимодействия между всеми клиентами и сервером
@@ -270,6 +285,8 @@ kanban-plugin: board
## BUGFIX Complete
- [ ] при создании копии переключение сервера и переключения клиента
- [ ] QT сервер Найти причину двойного вызова проверки при логине инструктором
- [ ] QT клиент: device not open после прерывания загрузки
- [ ] QT сервер При изменении версии правильный списке с файлами прилетает со второго раза
- [ ] QT клиент, если обновление в режиме инструктора доступно, кнопку запуск отключать
@@ -297,6 +314,7 @@ kanban-plugin: board
## Cancel
- [ ] При старт стопе не сканится шэред дата
- [ ] разделения на серверное и GUI приложение
- [ ] gui thread должен жить один
- [ ] подготовить фасад для ui сервера

View File

@@ -231,368 +231,372 @@ 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=
hmMFsgiQKjLhiMcAbCNeQMeXfZcZcBduhSfRMMtNzsMvsLb+UcL3ucJlHuZ1wXuUIYpMj/RThe3ofua/yZDOAAloMCg4AHAATQDYg+zNAA4oBkBZmhJgbgAwA+YJcl9JRTyRrG5E5klUSekIeATaoTKfjIrxw5U0RI5RhAg5STLmWbHKEifHL0gCVS7/qsBU5T/AVOukATQIcsc5RHL85StKJBEXL05fWDckuXKqhpHKm4QT9q5XnL9AIhAq6eMk
G5ZHLm5QCjRAm3L0gPehhMX7K0icXKo5eJkZMm9yQ8N3L9ALNgRDJbLxDPJkw5WnKa5ekAbiFOjLCCVBs5cwB5/jyB8ABEYUhErKbQuBEDGKQIjcC1AN5YaAkmklA2EJfVzpQNNakiUBHvAYA93AwACAMnBaMvGhzOnHZx5ZKxQxduRYYNnLZQCQByAX7K/5cQATQAgBZiGfALUCQAlsGwAdoLNhcADORwJBArwwkpAMIDyAgUIp1JQAAAKPUrUA
A/oCIXBU4KocQAASl1AccGUA+YC1AcwAROuACwVyE14AtCrFSDIDMgxCveI1coLlroCbh05Wwx7ajjgxYHDgd9CUgmQDgVwQBUg1uUTKhAFmI1uUJo3staxwgFU6tsD+kLCrsAE5E9Mm5MJoRTmgVsGkJo8Cq3UwKAWAhAEYAF1B5AD8vWwYQGCAeisGaYcuZABgGXl9IBdptPAwyryD0VBiu4egnkv522HAACkHLUXmzoQFiBbAQAA=
```
%%

View File

@@ -20,14 +20,16 @@ add_library(DataBaseLMS SHARED
group.h
computer.cpp
computer.h
task.cpp
task.h
classroom.cpp
classroom.h
tasksAmmFim.cpp
tasksAmmFim.h
typeQueryToDB.h
)
target_link_libraries(DataBaseLMS PRIVATE Qt5::Widgets)
target_link_libraries(DataBaseLMS PRIVATE Qt5::Sql)
target_link_libraries(DataBaseLMS PRIVATE Qt5::Xml)
target_compile_definitions(DataBaseLMS PRIVATE DATABASELMS_LIBRARY)

File diff suppressed because one or more lines are too long

View File

@@ -2,6 +2,7 @@
#include <QtSql>
#include <QSqlDatabase>
#include <QSqlDriver>
#include <QMessageBox>
DataBaseLMS::DataBaseLMS():
@@ -20,14 +21,16 @@ bool DataBaseLMS::createConnection()
{
mtxAccess.lock();
db = new QSqlDatabase(QSqlDatabase::addDatabase(dbType));
db = new QSqlDatabase(QSqlDatabase::addDatabase(dbType, connectionName));
db->setDatabaseName(dbName);
db->setUserName(dbUserName);
db->setPassword(dbPassword);
db->setPort(5432);
db->setHostName("192.168.100.87");
if(!db->open())
bool res = db->open();
if(!res)
{
mtxAccess.unlock();
deleteConnection();
@@ -35,6 +38,11 @@ bool DataBaseLMS::createConnection()
}
else
{
bool flHas = db->driver()->hasFeature(QSqlDriver::Transactions);
//bool resBool = QSqlDatabase::database(connectionName).transaction();
//resBool = QSqlDatabase::database(connectionName).commit();
mtxAccess.unlock();
return true;
}
@@ -173,8 +181,6 @@ QList<Trainee> DataBaseLMS::selectAllTrainees()
Computer computer = Computer(query.value(8).toInt(), query.value(9).toString(), query.value(10).toString(), classroom);
trainee.setComputer(computer);
trainee.setTasks(selectTasksOfTrainee(trainee.getID()));
listTrainees.append(trainee);
}
}
@@ -426,12 +432,12 @@ int DataBaseLMS::insertGroup(Group group)
return queryExecInt(queryStr);
}
int DataBaseLMS::deleteGroup(int group_id)
int DataBaseLMS::deleteGroup(int id_group)
{
QString queryStr = QString("DELETE FROM public.groups "
"WHERE group_id = %1 "
"RETURNING groups.group_id").arg(
QString::number(group_id));
QString::number(id_group));
return queryExecInt(queryStr);
}
@@ -447,6 +453,285 @@ int DataBaseLMS::updateGroup(Group group)
return queryExecInt(queryStr);
}
int DataBaseLMS::insertTaskAMM(TaskAmmFim task, int id_trainee)
{
task.ammProcedure.title = task.ammProcedure.title.replace("'", "''"); //Задваиваем одинарные кавычки
QString queryStr = QString("INSERT INTO public.tasks_amm (title, dm_code, trainee_task) "
"VALUES ('%1', '%2', %3) "
"RETURNING tasks_amm.task_id").arg(
task.ammProcedure.title,
task.ammProcedure.dmCode,
QString::number(id_trainee));
return queryExecInt(queryStr);
}
int DataBaseLMS::updateTaskAMM(TaskAmmFim task)
{
QString queryStr = QString("UPDATE public.tasks_amm SET title = '%1', dm_code = '%2' "
"WHERE task_id = %3 "
"RETURNING tasks_amm.task_id").arg(
task.ammProcedure.title,
task.ammProcedure.dmCode,
QString::number(task.getID()) );
return queryExecInt(queryStr);
}
int DataBaseLMS::deleteTaskAMM(int id_task)
{
QString queryStr;
bool resBool = false;
int id_trainee = 0;
resBool = db->transaction();
queryStr = QString("SELECT trainees.trainee_id "
"FROM public.trainees JOIN public.tasks_amm ON trainees.trainee_id = tasks_amm.trainee_task "
"WHERE tasks_amm.task_id = %1 "
"ORDER BY trainees.trainee_id ASC").arg(
QString::number(id_task));
QSqlQuery query = QSqlQuery(*db);
if(queryExec(queryStr, &query))
{
if (query.first())
{//Обучаемый
id_trainee = query.value(0).toInt();
}
}
if(!id_trainee)
{
resBool = db->rollback();
return 0;
}
queryStr = QString("DELETE FROM public.tasks_amm "
"WHERE task_id = %1 "
"RETURNING tasks_amm.task_id").arg(
QString::number(id_task));
if(!queryExecInt(queryStr))
{
resBool = db->rollback();
return 0;
}
resBool = db->commit();
return id_trainee;
}
QList<TaskAmmFim> DataBaseLMS::selectTasksAMMofTrainee(int id_trainee)
{
QList<TaskAmmFim> listTasks;
QString queryStr = QString("SELECT tasks_amm.task_id, tasks_amm.title, tasks_amm.dm_code, "
"trainees.trainee_id "
"FROM public.tasks_amm JOIN public.trainees ON trainees.trainee_id = tasks_amm.trainee_task "
"WHERE tasks_amm.trainee_task = %1 "
"ORDER BY tasks_amm.task_id ASC").arg(
id_trainee);
QSqlQuery query = QSqlQuery(*db);
if(queryExec(queryStr, &query))
{
while (query.next())
{//Задача
TaskAmmFim task;
task.setID(query.value(0).toInt());
task.ammProcedure.title = query.value(1).toString();
task.ammProcedure.dmCode = query.value(2).toString();
listTasks.append(task);
}
}
return listTasks;
}
int DataBaseLMS::insertTaskFIM(TaskAmmFim task, int id_trainee)
{
QString queryStr;
bool resBool = false;
resBool = db->transaction();
task.title = task.title.replace("'", "''"); //Задваиваем одинарные кавычки
queryStr = QString("INSERT INTO public.tasks_fim (title, trainee_task) "
"VALUES ('%1', %2) "
"RETURNING tasks_fim.task_id").arg(
task.title,
QString::number(id_trainee));
int task_id = queryExecInt(queryStr);
if(!task_id)
{
resBool = db->rollback();
return 0;
}
for(Malfunction malfanction : task.malfunctionList)
{
malfanction.description = malfanction.description.replace("'", "''"); //Задваиваем одинарные кавычки
queryStr = QString("INSERT INTO public.malfunctions (num, dm_code, description, task_fim_malf) "
"VALUES ('%1', '%2', '%3', %4) "
"RETURNING malfunctions.malfunction_id").arg(
malfanction.num,
malfanction.dmCode,
malfanction.description,
QString::number(task_id));
if(!queryExecInt(queryStr))
{
resBool = db->rollback();
return 0;
}
}
resBool = db->commit();
return task_id;
}
int DataBaseLMS::updateTaskFIM(TaskAmmFim task)
{
QString queryStr = QString("UPDATE public.tasks_fim SET title = '%1' "
"WHERE task_id = %2 "
"RETURNING tasks_fim.task_id").arg(
task.title,
QString::number(task.getID()) );
return queryExecInt(queryStr);
}
int DataBaseLMS::deleteTaskFIM(int id_task)
{
QString queryStr;
bool resBool = false;
int id_trainee = 0;
resBool = db->transaction();
queryStr = QString("SELECT trainees.trainee_id "
"FROM public.trainees JOIN public.tasks_fim ON trainees.trainee_id = tasks_fim.trainee_task "
"WHERE tasks_fim.task_id = %1 "
"ORDER BY trainees.trainee_id ASC").arg(
QString::number(id_task));
QSqlQuery query = QSqlQuery(*db);
if(queryExec(queryStr, &query))
{
if (query.first())
{//Обучаемый
id_trainee = query.value(0).toInt();
}
}
if(!id_trainee)
{
resBool = db->rollback();
return 0;
}
queryStr = QString("DELETE FROM public.malfunctions "
"WHERE task_fim_malf = %1 ").arg(
QString::number(id_task));
QSqlQuery query1 = QSqlQuery(*db);
if(!queryExec(queryStr, &query1))
{
resBool = db->rollback();
return 0;
}
queryStr = QString("DELETE FROM public.tasks_fim "
"WHERE task_id = %1 "
"RETURNING tasks_fim.task_id").arg(
QString::number(id_task));
if(!queryExecInt(queryStr))
{
resBool = db->rollback();
return 0;
}
resBool = db->commit();
return id_trainee;
}
QList<TaskAmmFim> DataBaseLMS::selectTasksFIMofTrainee(int id_trainee)
{
QList<TaskAmmFim> listTasks;
QString queryStr;
bool resBool = false;
resBool = db->transaction();
queryStr = QString("SELECT tasks_fim.task_id, tasks_fim.title, "
"trainees.trainee_id "
"FROM public.tasks_fim JOIN public.trainees ON trainees.trainee_id = tasks_fim.trainee_task "
"WHERE tasks_fim.trainee_task = %1 "
"ORDER BY tasks_fim.task_id ASC").arg(
id_trainee);
QSqlQuery query = QSqlQuery(*db);
if(queryExec(queryStr, &query))
{
while (query.next())
{//Задача
TaskAmmFim task;
task.setID(query.value(0).toInt());
task.title = query.value(1).toString();
//Выгребаем все malfunction для этой задачи
queryStr = QString("SELECT malfunctions.malfunction_id, malfunctions.num, malfunctions.dm_code, malfunctions.description, "
"tasks_fim.task_id "
"FROM public.malfunctions JOIN public.tasks_fim ON tasks_fim.task_id = malfunctions.task_fim_malf "
"WHERE malfunctions.task_fim_malf = %1 "
"ORDER BY malfunctions.num ASC").arg(
task.getID());
QSqlQuery queryMalf = QSqlQuery(*db);
if(queryExec(queryStr, &queryMalf))
{
while (queryMalf.next())
{//Неисправность
Malfunction malfanction;
malfanction.num = queryMalf.value(1).toString();
malfanction.dmCode = queryMalf.value(2).toString();
malfanction.description = queryMalf.value(3).toString();
task.addMalfunction(malfanction);
};
}
else
{
resBool = db->rollback();
return QList<TaskAmmFim>();
}
listTasks.append(task);
};
}
else
{
resBool = db->rollback();
return QList<TaskAmmFim>();
}
resBool = db->commit();
return listTasks;
}
Trainee DataBaseLMS::selectTrainee(int id_trainee)
{
Trainee trainee;
@@ -481,8 +766,6 @@ Trainee DataBaseLMS::selectTrainee(int id_trainee)
Classroom classroom = Classroom(query.value(11).toInt(), query.value(12).toString());
Computer computer = Computer(query.value(8).toInt(), query.value(9).toString(), query.value(10).toString(), classroom);
trainee.setComputer(computer);
trainee.setTasks(selectTasksOfTrainee(trainee.getID()));
}
}
@@ -526,8 +809,6 @@ QList<Trainee> DataBaseLMS::selectAllTraineesInGroup(int id_group)
Computer computer = Computer(query.value(8).toInt(), query.value(9).toString(), query.value(10).toString(), classroom);
trainee.setComputer(computer);
trainee.setTasks(selectTasksOfTrainee(trainee.getID()));
listTrainees.append(trainee);
}
}
@@ -611,8 +892,6 @@ Trainee DataBaseLMS::selectTraineeOnComputer(QString computer_name)
Classroom classroom = Classroom(query.value(11).toInt(), query.value(12).toString());
Computer computer = Computer(query.value(8).toInt(), query.value(9).toString(), query.value(10).toString(), classroom);
trainee.setComputer(computer);
trainee.setTasks(selectTasksOfTrainee(trainee.getID()));
}
}
@@ -699,14 +978,89 @@ int DataBaseLMS::insertTrainee(Trainee trainee)
return queryExecInt(queryStr);
}
int DataBaseLMS::deleteTrainee(int trainee_id)
int DataBaseLMS::deleteTrainee(int id_trainee)
{
QString queryStr = QString("DELETE FROM public.trainees "
QString queryStr;
int res = 0;
bool resBool = false;
resBool = db->transaction();
QSqlQuery query = QSqlQuery(*db);
//Удаление задач AMM
queryStr = QString("DELETE FROM public.tasks_amm "
"WHERE trainee_task = %1 ").arg(
QString::number(id_trainee));
if(!queryExec(queryStr, &query))
{
resBool = db->rollback();
return 0;
}
//Удаление задач FIM
/*Выборка задач FIM для этого обучаемого*/
queryStr = QString("SELECT tasks_fim.task_id "
"FROM public.tasks_fim "
"WHERE tasks_fim.trainee_task = %1 "
"ORDER BY tasks_fim.task_id ASC").arg(
id_trainee);
if(queryExec(queryStr, &query))
{
while (query.next())
{//Задача
/*Удаляем все malfunction для этой задачи*/
queryStr = QString("DELETE FROM public.malfunctions "
"WHERE malfunctions.task_fim_malf = %1 ").arg(
query.value(0).toInt());
QSqlQuery queryDel = QSqlQuery(*db);
if(!queryExec(queryStr, &queryDel))
{
resBool = db->rollback();
return 0;
}
};
}
else
{
resBool = db->rollback();
return 0;
}
/*Удаление непосредственно задач FIM*/
queryStr = QString("DELETE FROM public.tasks_fim "
"WHERE trainee_task = %1 ").arg(
QString::number(id_trainee));
if(!queryExec(queryStr, &query))
{
resBool = db->rollback();
return 0;
}
//Удаление обучаемого
queryStr = QString("DELETE FROM public.trainees "
"WHERE trainee_id = %1 "
"RETURNING trainees.trainee_id").arg(
QString::number(trainee_id));
QString::number(id_trainee));
return queryExecInt(queryStr);
res = queryExecInt(queryStr);
if(res)
{
resBool = db->commit();
return res;
}
else
{
resBool = db->rollback();
return 0;
}
}
int DataBaseLMS::updateTrainee(Trainee trainee)
@@ -731,34 +1085,6 @@ int DataBaseLMS::updateTrainee(Trainee trainee)
return queryExecInt(queryStr);
}
QList<Task> DataBaseLMS::selectTasksOfTrainee(int trainee_id)
{
QList<Task> tasks;
QString queryStr = QString("SELECT tasks.task_id, tasks.name "
"FROM public.trainees "
"JOIN public.trainees_tasks ON trainees_tasks.trainee_id = trainees.trainee_id "
"JOIN public.tasks ON tasks.task_id = trainees_tasks.task_id "
"WHERE trainees.trainee_id = %1 "
"ORDER BY tasks.name ASC").arg(
trainee_id);
QSqlQuery query = QSqlQuery(*db);
if(queryExec(queryStr, &query))
{
while (query.next())
{//Задача
Task task;
task.setID(query.value(0).toInt());
task.setName(query.value(1).toString());
tasks.append(task);
}
}
return tasks;
}
int DataBaseLMS::queryExecInt(QString queryStr)
{
QSqlQuery query = QSqlQuery(*db);

View File

@@ -7,6 +7,7 @@
#include "instructor.h"
#include "trainee.h"
#include "group.h"
#include "tasksAmmFim.h"
class DataBaseLMS
{
@@ -49,9 +50,20 @@ protected:
Group selectGroup(int id_group);
int insertGroup();
int insertGroup(Group group);
int deleteGroup(int group_id);
int deleteGroup(int id_group);
int updateGroup(Group group);
//Задача AMM
int insertTaskAMM(TaskAmmFim task, int id_trainee);
int updateTaskAMM(TaskAmmFim task);
int deleteTaskAMM(int id_task);
QList<TaskAmmFim> selectTasksAMMofTrainee(int id_trainee);
//Задача FIM
int insertTaskFIM(TaskAmmFim task, int id_trainee);
int updateTaskFIM(TaskAmmFim task);
int deleteTaskFIM(int id_task);
QList<TaskAmmFim> selectTasksFIMofTrainee(int id_trainee);
//Обучаемый
Trainee selectTrainee(int id_trainee);
QList<Trainee> selectAllTraineesInGroup(int id_group);
@@ -67,11 +79,9 @@ protected:
int insertTrainee(int id_group);
int insertTrainee(Trainee trainee);
int deleteTrainee(int trainee_id);
int deleteTrainee(int id_trainee);
int updateTrainee(Trainee trainee);
QList<Task> selectTasksOfTrainee(int trainee_id);
private:
int queryExecInt(QString queryStr);
QString queryExecString(QString queryStr);
@@ -82,6 +92,7 @@ private:
protected:
QSqlDatabase* db;
const QString dbName = "DataBaseLMS";
const QString connectionName = "Connection_DataBaseLMS";
private:
bool transactionBegined;
const QString dbUserName = "postgres";

View File

@@ -42,6 +42,7 @@ bool InterfaceDataBaseLMS::DBisConnected()
return isConnected();
}
//Инструкторы
bool InterfaceDataBaseLMS::AuthorizationInstructor(QString login, QString password)
@@ -51,6 +52,11 @@ bool InterfaceDataBaseLMS::AuthorizationInstructor(QString login, QString passwo
if(int id = selectInstructorID(login, password))
{
if(isArchivedInstructor(id) || isLoggedInInstructor(id))
{
transactionEnd();
return false;
}
if(updateInstructorLoggedIn(id, true))
return transactionEnd();
}
@@ -174,6 +180,11 @@ bool InterfaceDataBaseLMS::AuthorizationTrainee(QString login, QString password,
if(int id = selectTraineeID(login, password))
{
if(isArchivedTrainee(id) || isLoggedInTrainee(id))
{
transactionEnd();
return false;
}
if(updateTraineeLoggedIn(id, true))
return transactionEnd();
}
@@ -202,11 +213,6 @@ bool InterfaceDataBaseLMS::deAuthorizationAllTrainees()
return updateAllTraineesLoggedIn(false);
}
QList<Task> InterfaceDataBaseLMS::getTasksTrainee(int id)
{
return selectTasksOfTrainee(id);
}
QString InterfaceDataBaseLMS::getNameTraineeOnComputer(QString computer_name)
{
return selectTraineeNameOnComputer(computer_name);
@@ -222,6 +228,11 @@ QString InterfaceDataBaseLMS::getNameTraineeByLogin(QString login)
return selectTraineeNameByLogin(login);
}
int InterfaceDataBaseLMS::getIdTraineeByLogin(QString login)
{
return selectTraineeID(login);
}
QList<Trainee> InterfaceDataBaseLMS::getListTraineesInGroup(int id)
{
return selectAllTraineesInGroup(id);
@@ -281,6 +292,46 @@ int InterfaceDataBaseLMS::editGroup(Group group)
return updateGroup(group);
}
int InterfaceDataBaseLMS::newTaskAMM(TaskAmmFim task, int id_trainee)
{
return insertTaskAMM(task, id_trainee);
}
int InterfaceDataBaseLMS::delTaskAMM(int id)
{
return deleteTaskAMM(id);
}
int InterfaceDataBaseLMS::editTaskAMM(TaskAmmFim task)
{
return updateTaskAMM(task);
}
QList<TaskAmmFim> InterfaceDataBaseLMS::getListTasksAMMofTrainee(int id_trainee)
{
return selectTasksAMMofTrainee(id_trainee);
}
QList<TaskAmmFim> InterfaceDataBaseLMS::getListTasksFIMofTrainee(int id_trainee)
{
return selectTasksFIMofTrainee(id_trainee);
}
int InterfaceDataBaseLMS::newTaskFIM(TaskAmmFim task, int id_trainee)
{
return insertTaskFIM(task, id_trainee);
}
int InterfaceDataBaseLMS::delTaskFIM(int id)
{
return deleteTaskFIM(id);
}
int InterfaceDataBaseLMS::editTaskFIM(TaskAmmFim task)
{
return updateTaskFIM(task);
}
int InterfaceDataBaseLMS::newTrainee(int id_group)
{
return insertTrainee(id_group);

View File

@@ -51,12 +51,12 @@ public:
bool deAuthorizationAllTrainees();
//void setTasks(QString login, QStringList tasks);
QList<Task> getTasksTrainee(int id);
QString getNameTraineeOnComputer(QString computer_name);
Trainee getTraineeOnComputer(QString computer_name);
QString getNameTraineeByLogin(QString login);
int getIdTraineeByLogin(QString login);
QList<Trainee> getListTraineesInGroup(int id);
QList<Group> getListGroups();
@@ -69,6 +69,16 @@ public:
int delGroup(int id);
int editGroup(Group group);
int newTaskAMM(TaskAmmFim task, int id_trainee);
int delTaskAMM(int id);
int editTaskAMM(TaskAmmFim task);
QList<TaskAmmFim> getListTasksAMMofTrainee(int id_trainee);
QList<TaskAmmFim> getListTasksFIMofTrainee(int id_trainee);
int newTaskFIM(TaskAmmFim task, int id_trainee);
int delTaskFIM(int id);
int editTaskFIM(TaskAmmFim task);
int newTrainee(int id_group);
int delTrainee(int id);
int editTrainee(Trainee trainee);

View File

@@ -1,7 +0,0 @@
#include "task.h"
Task::Task():
BasicEntity()
{
}

View File

@@ -1,12 +0,0 @@
#ifndef TASK_H
#define TASK_H
#include "basicentity.h"
class DATABASELMS_EXPORT Task: public BasicEntity
{
public:
Task();
};
#endif // TASK_H

View File

@@ -3,6 +3,7 @@
#include <QFile>
#include <QMessageBox>
int TaskAmmFim::lastID = 1;
void TaskAmmFim::initialize(int id, QString type, QString title, QString status, QString created_date, QString changed_date)
{

View File

@@ -3,8 +3,9 @@
#include <QString>
#include <QList>
#include "DataBaseLMS_global.h"
class ProcedureID
class DATABASELMS_EXPORT ProcedureID
{
public:
ProcedureID(){};
@@ -16,7 +17,7 @@ public:
QString result; // "" - нет результата, "viewed" - процедура изучена (просмотрена при отсутствующем сценарии), "completed" - выполнена (в т.ч. режим "контроль" сценария)
};
class MalfunctionSign // признак неисправности
class DATABASELMS_EXPORT MalfunctionSign // признак неисправности
{
public:
MalfunctionSign(){};
@@ -29,7 +30,7 @@ public:
QString description; // описание (напр. "ЭРРД, 25, DOOR_FAIL_TO_CLOSE" - для БСТО)
};
class Malfunction // неисправность
class DATABASELMS_EXPORT Malfunction // неисправность
{
public:
Malfunction(){};
@@ -44,7 +45,7 @@ public:
QList<MalfunctionSign> malfunctionSigns;// список соответствующих неисправности признаков
};
class FIMReportItem
class DATABASELMS_EXPORT FIMReportItem
{
public:
FIMReportItem(){};
@@ -54,7 +55,7 @@ public:
ProcedureID procedure; // ссылка на процедуру, при необходимости
};
class FIMReport
class DATABASELMS_EXPORT FIMReport
{
public:
FIMReport(){};
@@ -63,7 +64,7 @@ public:
QList<FIMReportItem> itemList;
};
class TaskAmmFim
class DATABASELMS_EXPORT TaskAmmFim
{
public:
TaskAmmFim(){};
@@ -71,6 +72,10 @@ public:
public:
void initialize(int id, QString type, QString title, QString status, QString created_date, QString changed_date);
void addMalfunction(Malfunction malfunction);
public:
void setID(int id){this->id = id;};
int getID(){return id;};
public:
int id; // для идентификации в БД
@@ -97,6 +102,8 @@ public:
// fim:
QList<Malfunction> malfunctionList; // список неисправностей
FIMReport report; // отчет по выполнению "fim"
static int lastID;
};
#endif // TASKSAMMFIM_H

View File

@@ -3,8 +3,7 @@
Trainee::Trainee():
User(),
group(),
computer(),
tasks()
computer()
{
}

View File

@@ -6,7 +6,6 @@
#include "user.h"
#include "group.h"
#include "computer.h"
#include "task.h"
class DATABASELMS_EXPORT Trainee: public User
{
@@ -19,13 +18,9 @@ public:
void setComputer(Computer computer){this->computer = computer;}
Computer getComputer(){return computer;}
void setTasks(QList<Task> tasks){this->tasks = tasks;}
QList<Task> getTasks(){return tasks;}
private:
Group group;
Computer computer;
QList<Task> tasks;
};
#endif // TRAINEE_H

View File

@@ -0,0 +1,26 @@
#ifndef TYPEQUERYTODB_H
#define TYPEQUERYTODB_H
#include "DataBaseLMS_global.h"
enum TypeQueryToDB{
TYPE_QUERY_GET_ALL_LISTS,
TYPE_QUERY_NEW_INSTRUCTOR,
TYPE_QUERY_DEL_INSTRUCTOR,
TYPE_QUERY_EDIT_INSTRUCTOR,
TYPE_QUERY_NEW_GROUP,
TYPE_QUERY_DEL_GROUP,
TYPE_QUERY_EDIT_GROUP,
TYPE_QUERY_NEW_TRAINEE,
TYPE_QUERY_DEL_TRAINEE,
TYPE_QUERY_EDIT_TRAINEE,
TYPE_QUERY_ASSIGN_TASK_AMM_TO_TRAINEE,
TYPE_QUERY_ASSIGN_TASK_FIM_TO_TRAINEE,
TYPE_QUERY_GET_TASKS_AMM_FOR_TRAINEE,
TYPE_QUERY_GET_TASKS_FIM_FOR_TRAINEE,
TYPE_QUERY_DEL_TASK_AMM_TO_TRAINEE,
TYPE_QUERY_DEL_TASK_FIM_TO_TRAINEE
};
#endif // TYPEQUERYTODB_H

View File

@@ -12,16 +12,18 @@ MainWindow::MainWindow(QWidget *parent)
{
ui->setupUi(this);
this->setWindowTitle(tr("Learning management system (LMS)"));
//Задаём два пункта с текстом локалей в комбобоксе
ui->cmbLanguage->addItems(QStringList() << "English" << "Русский");
m_instructorsAndTraineesWidget = new InstructorsAndTraineesWidget(this);
connect(this, &MainWindow::signal_LanguageChanged, m_instructorsAndTraineesWidget, &InstructorsAndTraineesWidget::slot_LanguageChanged);
ui->horizontalLayout->addWidget(m_instructorsAndTraineesWidget);
this->move(0, 0);
connect(this, &MainWindow::signal_LanguageChanged, m_instructorsAndTraineesWidget, &InstructorsAndTraineesWidget::slot_LanguageChanged);
//this->move(0, 0);
//this->showNormal();
this->showMaximized();
}

View File

@@ -9,6 +9,7 @@ add_library(InstructorsAndTrainees SHARED
instructorsandtraineeswidget.ui
commonview.cpp
commonview.h
trainees/editortrainees.cpp
trainees/editortrainees.h
trainees/editortrainees.ui
@@ -25,6 +26,7 @@ add_library(InstructorsAndTrainees SHARED
trainees/viewertrainees.ui
trainees/traineesview.cpp
trainees/traineesview.h
instructors/viewerinstructors.cpp
instructors/viewerinstructors.h
instructors/viewerinstructors.ui
@@ -39,14 +41,7 @@ add_library(InstructorsAndTrainees SHARED
instructors/dialogauthorizationinstructor.ui
instructors/instructorsview.cpp
instructors/instructorsview.h
tasks/taskswidget.cpp
tasks/taskswidget.h
tasks/taskswidget.ui
docTasks/doctaskswidget.cpp
docTasks/doctaskswidget.h
docTasks/doctaskswidget.ui
docTasks/module.cpp
docTasks/module.h
connectorToServer/connectortoserver.cpp
connectorToServer/connectortoserver.h
connectorToServer/Core/sendsystem.cpp
@@ -60,7 +55,12 @@ add_library(InstructorsAndTrainees SHARED
connectorToServer/Core/tools.cpp
connectorToServer/Core/tools.h
connectorToServer/Core/FileData.h
connectorToServer/Core/notifycontroller.cpp
connectorToServer/Core/notifycontroller.h
connectorToServer/Core/versioncontainer.cpp
connectorToServer/Core/versioncontainer.h
connectorToServer/Datas.h
connectorToServer/streamingversiondata.h
messanger/messangerwidget.cpp
messanger/messangerwidget.h
messanger/messangerwidget.ui
@@ -69,11 +69,29 @@ add_library(InstructorsAndTrainees SHARED
messanger/msgwidget.ui
messanger/tabdialogmessenger.cpp
messanger/tabdialogmessenger.h
docTasks/fimtaskswidget.cpp
docTasks/fimtaskswidget.h
docTasks/fimtaskswidget.ui
docTasks/tasksAmmFim.cpp
docTasks/tasksAmmFim.h
tasks/ammtaskswidget.cpp
tasks/ammtaskswidget.h
tasks/ammtaskswidget.ui
tasks/module.cpp
tasks/module.h
tasks/fimtaskswidget.cpp
tasks/fimtaskswidget.h
tasks/fimtaskswidget.ui
tasks/tasktreepreparation.cpp
tasks/tasktreepreparation.h
#tasks/tasksAmmFim.cpp
#tasks/tasksAmmFim.h
widgets/newversionwidget.cpp
widgets/newversionwidget.h
widgets/newversionwidget.ui
widgets/versionselectwidget.cpp
widgets/versionselectwidget.h
widgets/versionselectwidget.ui
widgets/waitanimationwidget.cpp
widgets/waitanimationwidget.h
widgets/waitanimationwidget.ui
resources.qrc
)

View File

@@ -9,7 +9,7 @@ CommonView::CommonView(ConnectorToServer* connectorToServer, TypeView type, QWid
treeWidget(nullptr),
typeView(type),
archiveVisible(false),
notLoggedInVisible(false),
notLoggedInVisible(true),
adminMode(false),
authComplited(false),
lastCurrentID(0),

View File

@@ -34,10 +34,11 @@ public:
void setAdminMode(bool adminMode)
{
this->adminMode = adminMode;
}
void setAuthComplited(bool authComplited)
}
void deactivate()
{
this->authComplited = authComplited;
treeWidget->clear();
lastCurrentID = 0;
}
protected:

View File

@@ -6,6 +6,8 @@
#include "instructor.h"
#include "trainee.h"
#include "group.h"
#include "tasksAmmFim.h"
#include "streamingversiondata.h"
#include <QDir>
@@ -52,13 +54,12 @@ void DataParser::createFileDataList(QList<FileData> fileDataList,QString filenam
file.close();
}
void DataParser::createAuthMessage(ClientAutorization *auth)
QByteArray DataParser::createAuthMessage(ClientAutorization *auth)
{
authPassCache = auth; //кэширование даных авторизации, для сохранения при успешном заходе
QFile file(tempName);
file.open(QIODevice::WriteOnly);
QXmlStreamWriter xmlWriter(&file);
QByteArray array;
QXmlStreamWriter xmlWriter(&array);
xmlWriter.setAutoFormatting(true);
xmlWriter.writeStartDocument();
@@ -72,14 +73,13 @@ void DataParser::createAuthMessage(ClientAutorization *auth)
xmlWriter.writeEndElement();
xmlWriter.writeEndDocument();
file.close();
return array;
}
void DataParser::createToClientMessage(ToClientMessage *toClientMessage)
QByteArray DataParser::createToClientMessage(ToClientMessage *toClientMessage)
{
QFile file(tempName);
file.open(QIODevice::WriteOnly);
QXmlStreamWriter xmlWriter(&file);
QByteArray array;
QXmlStreamWriter xmlWriter(&array);
xmlWriter.setAutoFormatting(true);
xmlWriter.writeStartDocument();
@@ -93,16 +93,15 @@ void DataParser::createToClientMessage(ToClientMessage *toClientMessage)
xmlWriter.writeEndElement();
xmlWriter.writeEndDocument();
file.close();
return array;
}
void DataParser::createQueryToDBMessage(ClientQueryToDB *queryToDB, int id, void* data)
QByteArray DataParser::createQueryToDBMessage(ClientQueryToDB *queryToDB, int id, void* data)
{
QFile file(tempName);
file.open(QIODevice::WriteOnly);
QXmlStreamWriter xmlWriter(&file);
QByteArray array;
QXmlStreamWriter xmlWriter(&array);
xmlWriter.setAutoFormatting(true);
xmlWriter.writeStartDocument();
@@ -170,20 +169,68 @@ void DataParser::createQueryToDBMessage(ClientQueryToDB *queryToDB, int id, void
xmlWriter.writeAttribute("name", group->getName());
}
}
else if(queryToDB->typeQuery == TypeQueryToDB::TYPE_QUERY_ASSIGN_TASK_AMM_TO_TRAINEE)
{
TaskAmmFim* task = (TaskAmmFim*)data;
if(task)
{
xmlWriter.writeAttribute("title", task->ammProcedure.title);
xmlWriter.writeAttribute("dmCode", task->ammProcedure.dmCode);
}
}
else if(queryToDB->typeQuery == TypeQueryToDB::TYPE_QUERY_ASSIGN_TASK_FIM_TO_TRAINEE)
{
TaskAmmFim* task = (TaskAmmFim*)data;
if(task)
{
xmlWriter.writeAttribute("title", task->title);
for(Malfunction malfunction : task->malfunctionList)
{
xmlWriter.writeStartElement("malfunction");
xmlWriter.writeAttribute("dmCode", malfunction.dmCode);
xmlWriter.writeAttribute("num", malfunction.num);
xmlWriter.writeAttribute("description", malfunction.description);
xmlWriter.writeEndElement();
}
}
}
}
xmlWriter.writeEndElement();
xmlWriter.writeEndElement();
xmlWriter.writeEndDocument();
QFile file("QueryToDB.xml");
file.open(QIODevice::WriteOnly);
file.write(array);
file.close();
return array;
}
void DataParser::createDeAuthMessage(ClientDeAutorization *deAuth)
QByteArray DataParser::createQueryTasksXMLMessage(QString type)
{
QFile file(tempName);
file.open(QIODevice::WriteOnly);
QXmlStreamWriter xmlWriter(&file);
QByteArray array;
QXmlStreamWriter xmlWriter(&array);
xmlWriter.setAutoFormatting(true);
xmlWriter.writeStartDocument();
xmlWriter.writeStartElement("QueryTasksXML");
xmlWriter.writeAttribute("Type", type);
xmlWriter.writeEndElement();
xmlWriter.writeEndElement();
xmlWriter.writeEndDocument();
return array;
}
QByteArray DataParser::createDeAuthMessage(ClientDeAutorization *deAuth)
{
QByteArray array;
QXmlStreamWriter xmlWriter(&array);
xmlWriter.setAutoFormatting(true);
xmlWriter.writeStartDocument();
@@ -195,7 +242,7 @@ void DataParser::createDeAuthMessage(ClientDeAutorization *deAuth)
xmlWriter.writeEndElement();
xmlWriter.writeEndDocument();
file.close();
return array;
}
@@ -308,7 +355,8 @@ ServerSettings *DataParser::getServerSettings()
if(xmlReader.isStartElement()){
if(xmlReader.name() == "ServerSettings"){
if(xmlReader.name() == "ServerSettings")
{
foreach(const QXmlStreamAttribute &attr, xmlReader.attributes()){
QString name = attr.name().toString();

View File

@@ -21,10 +21,13 @@ public:
void createServerSettings(QString server,QString port);
void saveClientSettrings(QString language,bool isAutoStart);
void createFileDataList(QList<FileData> fileDataList,QString filename);
void createAuthMessage(ClientAutorization *auth);
void createToClientMessage(ToClientMessage *toClientMessage);
void createQueryToDBMessage(ClientQueryToDB *queryToDB, int id = 0, void* data = nullptr);
void createDeAuthMessage(ClientDeAutorization *deAuth);
QByteArray createAuthMessage(ClientAutorization *auth);
QByteArray createToClientMessage(ToClientMessage *toClientMessage);
QByteArray createQueryToDBMessage(ClientQueryToDB *queryToDB, int id = 0, void* data = nullptr);
QByteArray createQueryTasksXMLMessage(QString type);
QByteArray createDeAuthMessage(ClientDeAutorization *deAuth);
void createAuthData(ServerAuthorization *serverAuth);
void createAuthDataOffline(QString username,QString pass);
void addRunData(QList<int> displays);

View File

@@ -0,0 +1,16 @@
#include "notifycontroller.h"
NotifyController::NotifyController(QObject *parent) : QObject(parent)
{
}
void NotifyController::showWarning(QString text)
{
QMessageBox warning;
warning.setText(text);
warning.setIcon(QMessageBox::Warning);
warning.setWindowTitle(tr("Error"));
warning.exec();
}

View File

@@ -0,0 +1,18 @@
#ifndef NOTIFYCONTROLLER_H
#define NOTIFYCONTROLLER_H
#include <QObject>
#include <QMessageBox>
class NotifyController : public QObject
{
Q_OBJECT
public:
explicit NotifyController(QObject *parent = nullptr);
void showWarning(QString text);
signals:
};
#endif // NOTIFYCONTROLLER_H

View File

@@ -2,7 +2,9 @@
#include <QThread>
#include <QDir>
#include <QDomDocument>
#include <QMessageBox>
#include "instructor.h"
#include "tasksAmmFim.h"
RecognizeSystem::RecognizeSystem(QObject *parent):
@@ -252,6 +254,17 @@ void RecognizeSystem::recognize(QTcpSocket *socket)
packetType = PacketType::TYPE_NONE;
}
if(packetType == PacketType::BUSY)
{
emit sigAnimationActivated(true);
}
if(packetType == PacketType::FREE)
{
emit sigAnimationActivated(false);
}
//xml-ответы на запросы к БД
switch(packetType)
{
@@ -260,7 +273,8 @@ void RecognizeSystem::recognize(QTcpSocket *socket)
case TYPE_XMLANSWER_QUERY_DB__LIST_TRAINEES:
case TYPE_XMLANSWER_QUERY_DB__LIST_COMPUTERS:
case TYPE_XMLANSWER_QUERY_DB__LIST_CLASSROOMS:
case TYPE_XMLANSWER_QUERY_DB__LIST_TASKS:
case TYPE_XMLANSWER_QUERY_TASKS_AMM_FOR_TRAINEE:
case TYPE_XMLANSWER_QUERY_TASKS_FIM_FOR_TRAINEE:
{
QByteArray array;
stream.startTransaction();
@@ -276,6 +290,35 @@ void RecognizeSystem::recognize(QTcpSocket *socket)
break;
};
//xml-ответы на запросы AdditionalFiles
if(packetType == PacketType::TYPE_XMLANSWER_QUERY_TASKS_XML_FIM ||
packetType == PacketType::TYPE_XMLANSWER_QUERY_TASKS_XML_AMM)
{
QByteArray array;
QString xmlFileName = "";
if(packetType == PacketType::TYPE_XMLANSWER_QUERY_TASKS_XML_FIM)
xmlFileName = "./" + additionalFilesFolderName + "/tasksFIM.xml";
else
xmlFileName = "./" + additionalFilesFolderName + "/tasksAMM.xml";
QFile xmlInFile(xmlFileName);
if (!xmlInFile.open(QFile::ReadOnly | QFile::Text))
{
QMessageBox::critical(nullptr, tr("Attention!"), tr("The file could not be opened ") + xmlFileName);
return;
}
else
{
array = xmlInFile.readAll();
xmlInFile.close();
}
xmlParserQueryTasksXML(packetType, array);
packetType = PacketType::TYPE_NONE;
}
packetType = PacketType::TYPE_NONE;
}
}
@@ -319,6 +362,21 @@ void RecognizeSystem::xmlParser(QByteArray array)
{
emit sigStartCompare();
}
if (value == "BASEDELETETRY")
{
emit sigNotify(tr("You cannot delete the basic version!"));
}
if (value == "TRYACTIVEDELETE")
{
emit sigNotify(tr("You cannot delete the active version"));
}
if (value == "DUPLICATEVERNAME")
{
emit sigNotify(tr("This name already exists"));
}
}
}
}
@@ -396,6 +454,75 @@ void RecognizeSystem::xmlParser(QByteArray array)
}
emit sigDeAuth(serverDeAuth);
}
if(xmlReader.name() == "VersionList")
{
QList<StreamingVersionData*> *serverStreamingVersionDataList = new QList<StreamingVersionData*>;
xmlReader.readNext();
while (!xmlReader.atEnd())
{
if(xmlReader.isStartElement())
{
if(xmlReader.name() == "VersionData")
{
StreamingVersionData *data = new StreamingVersionData;
foreach(const QXmlStreamAttribute &attr,xmlReader.attributes())
{
QString name = attr.name().toString();
QString value = attr.value().toString();
if(name == "Version")
data->setName(value);
else if(name == "Created")
data->setCreateData(QDateTime::fromString(value));
else if(name == "isChangeable")
data->setIsChangeable(value.toInt());
else if(name == "author")
data->setAuthor(value);
}
serverStreamingVersionDataList->append(data);
}
}
xmlReader.readNext();
}
emit sigShowServerDataList(serverStreamingVersionDataList);
}
if(xmlReader.name() == "VersionData")
{
StreamingVersionData *serverVersion = new StreamingVersionData;
foreach(const QXmlStreamAttribute &attr,xmlReader.attributes())
{
QString name = attr.name().toString();
QString value = attr.value().toString();
if (name == "Version")
{
serverVersion->setName(value);
}
if (name == "Created")
{
serverVersion->setCreateData(QDateTime::fromString(value));
}
if (name == "isChangeable")
{
serverVersion->setIsChangeable(value.toInt());
}
}
emit sigSetVersion(serverVersion);
}
xmlReader.readNext();
@@ -524,29 +651,80 @@ void RecognizeSystem::xmlParserQueryToDB(PacketType packetType, QByteArray array
emit sigAnswerQueryToDB_ListClassrooms(listClassrooms);
}
break;
case TYPE_XMLANSWER_QUERY_DB__LIST_TASKS:
case TYPE_XMLANSWER_QUERY_TASKS_AMM_FOR_TRAINEE:
{
QList<Task> listTasks;
QDomNode listNode = commonDOM.namedItem("ListTasks");
QList<TaskAmmFim> listTasks;
int trainee_id = 0;
QDomNode listNode = commonDOM.namedItem("ListTasksAMM");
trainee_id = listNode.toElement().attribute("trainee_id").toInt();
for(int i = 0; i < listNode.childNodes().count(); i++)
{
QDomNode taskNode = listNode.childNodes().at(i);
if(taskNode.nodeName() == "Task")
if(taskNode.nodeName() == "taskAMM")
{//Задача
Task task;
TaskAmmFim task;
task.setID(taskNode.toElement().attribute("task_id").toInt());
task.setName(taskNode.toElement().attribute("name"));
task.ammProcedure.title = taskNode.toElement().attribute("title");
task.ammProcedure.dmCode = taskNode.toElement().attribute("dmCode");
listTasks.append(task);
}
}
emit sigAnswerQueryToDB_ListTasks(listTasks);
emit sigAnswerQueryToDB_ListTasksAMMforTrainee(listTasks, trainee_id);
}
break;
case TYPE_XMLANSWER_QUERY_TASKS_FIM_FOR_TRAINEE:
{
QList<TaskAmmFim> listTasks;
int trainee_id = 0;
QDomNode listNode = commonDOM.namedItem("ListTasksFIM");
trainee_id = listNode.toElement().attribute("trainee_id").toInt();
for(int i = 0; i < listNode.childNodes().count(); i++)
{//Задачи
QDomNode taskNode = listNode.childNodes().at(i);
if(taskNode.nodeName() == "taskFIM")
{
TaskAmmFim task;
task.setID(taskNode.toElement().attribute("task_id").toInt());
task.title = taskNode.toElement().attribute("title");
for(int j = 0; j < taskNode.childNodes().count(); j++)
{//Неисправности
QDomNode malfunctionNode = taskNode.childNodes().at(j);
if(malfunctionNode.nodeName() == "malfunction")
{
Malfunction malfunction;
malfunction.num = malfunctionNode.toElement().attribute("num");
malfunction.dmCode = malfunctionNode.toElement().attribute("dmCode");
malfunction.description = malfunctionNode.toElement().attribute("description");
task.malfunctionList.append(malfunction);
}
}
listTasks.append(task);
}
}
emit sigAnswerQueryToDB_ListTasksFIMforTrainee(listTasks, trainee_id);
}
break;
};
}
void RecognizeSystem::xmlParserQueryTasksXML(PacketType packetType, QByteArray array)
{
if(packetType == TYPE_XMLANSWER_QUERY_TASKS_XML_FIM)
{
emit sigAnswerQueryTasksXML_FIM(array);
}
else if(packetType == TYPE_XMLANSWER_QUERY_TASKS_XML_AMM)
{
emit sigAnswerQueryTasksXML_AMM(array);
}
}
void RecognizeSystem::checkAccessType(QString type)
{
if(type == "instructor")

View File

@@ -4,6 +4,7 @@
#include <QObject>
#include <QDataStream>
#include <QTcpSocket>
#include <streamingVersionData.h>
//#include <mainwindow.h>
#include <Core\tools.h>
#include "dataparser.h"
@@ -11,6 +12,7 @@
#include "trainee.h"
#include "group.h"
#include "Datas.h"
#include "tasksAmmFim.h"
class RecognizeSystem : public QObject
@@ -46,7 +48,14 @@ signals:
void sigAnswerQueryToDB_ListTrainees(QList<Trainee> listTrainees);
void sigAnswerQueryToDB_ListComputers(QList<Computer> listComputers);
void sigAnswerQueryToDB_ListClassrooms(QList<Classroom> listClassrooms);
void sigAnswerQueryToDB_ListTasks(QList<Task> listTasks);
void sigAnswerQueryToDB_ListTasksAMMforTrainee(QList<TaskAmmFim>listTasks, int trainee_id);
void sigAnswerQueryToDB_ListTasksFIMforTrainee(QList<TaskAmmFim>listTasks, int trainee_id);
void sigAnswerQueryTasksXML_FIM(QByteArray array);
void sigAnswerQueryTasksXML_AMM(QByteArray array);
void sigShowServerDataList(QList<StreamingVersionData*> *versions);
void sigSetVersion(StreamingVersionData* serverVersion);
void sigNotify(QString text);
void sigAnimationActivated(bool flag);
private:
QList<QString> *folderList;
@@ -63,6 +72,7 @@ private:
void xmlParser(QByteArray array);
void xmlParserQueryToDB(PacketType packetType, QByteArray array);
void xmlParserQueryTasksXML(PacketType packetType, QByteArray array);
void checkAccessType(QString type);
};

View File

@@ -31,21 +31,14 @@ void SendSystem::sendDisable()
socket->waitForBytesWritten();
}
void SendSystem::sendXMLmsgGUItoServer()
void SendSystem::sendXMLmsgGUItoServer(QByteArray array)
{
QDataStream stream(socket);
stream.setVersion(QDataStream::Qt_DefaultCompiledVersion);
QFile file(tempName);
file.open(QIODevice::ReadOnly);
QByteArray array = file.readAll();
stream << PacketType::TYPE_XMLANSWER;
stream << array;
socket->waitForBytesWritten();
file.close();
}
void SendSystem::sendFileBlock(QString path)
@@ -129,6 +122,43 @@ void SendSystem::sendFinish()
socket->waitForReadyRead(100);
}
void SendSystem::sendChangeVersion(StreamingVersionData *streamingVersion)
{
QDataStream stream(socket);
stream.setVersion(QDataStream::Qt_DefaultCompiledVersion);
stream << PacketType::CHANGE_DATA_VERSION;
stream << streamingVersion->getViewName();
socket->waitForReadyRead(100);
}
void SendSystem::sendDeleteVersion(StreamingVersionData *streamingVersion)
{
QDataStream stream(socket);
stream.setVersion(QDataStream::Qt_DefaultCompiledVersion);
stream << PacketType::DELETE_DATA_VERSION;
stream << streamingVersion->getViewName();
socket->waitForReadyRead(100);
}
void SendSystem::sendCopyVersion(QString versionName)
{
QDataStream stream(socket);
stream.setVersion(QDataStream::Qt_DefaultCompiledVersion);
stream << PacketType::COPY_VERSION;
stream << versionName;
}
void SendSystem::sendPacketType(PacketType packetType)
{
QDataStream stream(socket);
QByteArray data;
stream.setVersion(QDataStream::Qt_DefaultCompiledVersion);
stream << packetType;
}
SendSystem::~SendSystem()
{

View File

@@ -5,13 +5,16 @@
#include <QTcpSocket>
#include <QDataStream>
#include <streamingVersionData.h>
#include "Core/tools.h"
class SendSystem :public QObject
{
Q_OBJECT
public:
explicit SendSystem(QObject* parent = nullptr);
void setSocket(QTcpSocket *socket);
void sendXMLmsgGUItoServer();
void sendXMLmsgGUItoServer(QByteArray array);
void sendDisable();
void sendFileBlock(QString path);
void sendFolderBlock(QString path);
@@ -19,6 +22,10 @@ public:
void sendXMLAnswer(QByteArray array);
~SendSystem();
void sendFinish();
void sendChangeVersion(StreamingVersionData *streamingVersion);
void sendDeleteVersion(StreamingVersionData *streamingVersion);
void sendCopyVersion(QString versionName);
void sendPacketType(PacketType packetType);
signals:
void sigSend();

View File

@@ -48,6 +48,7 @@ void TCPClient::setConnect(ServerSettings *serverSettings)
else
{
isConnected = false;
emit signal_ConnectedToServer(false);
emit sigServerDisconnect();
}
}

View File

@@ -27,7 +27,7 @@ QString Tools::createLocalPath(QString path)
QString Tools::createFullPath(QString path)
{
QString fullPath;
qint8 pos = path.indexOf("Application");
qint8 pos = path.indexOf(additionalFilesFolderName);
QString localPath = path.remove(0,--pos);

View File

@@ -10,6 +10,7 @@
static QString applicationEXEName = "RRJ.exe";
static QString applicationFolderName = "/Application";
static QString staticDataFolderName = "StaticData";
static QString additionalFilesFolderName = "RRJ-95NEW-100";
static QString streamingAssetsPath = "/Application/RRJLoader/RRJ_Data/StreamingAssets";
static QString hashFilename = staticDataFolderName + "/clientHash.xml";
static QString settingsName = staticDataFolderName + "/settings.xml";
@@ -19,6 +20,8 @@ static QString displayTemp = staticDataFolderName + "/displayData.xml";
static QString streamingHashFilename = staticDataFolderName + "/streamingHash.xml";
static QString serverHash = staticDataFolderName + "/serverHash.xml";
static QString cmd_CheckVersionList = "CHECKVERSIONLIST";
enum PacketType{
TYPE_NONE = 0,
TYPE_UNITY = 1,
@@ -31,6 +34,7 @@ enum PacketType{
TYPE_XMLANSWER = 8,
TYPE_QT = 9,
TYPE_DISABLE = 11,
TYPE_CHECKVERSION = 13,
TYPE_XMLANSWER_MESSAGE_FOR_GUI = 90,
@@ -40,7 +44,20 @@ enum PacketType{
TYPE_XMLANSWER_QUERY_DB__LIST_TRAINEES = 102,
TYPE_XMLANSWER_QUERY_DB__LIST_COMPUTERS = 103,
TYPE_XMLANSWER_QUERY_DB__LIST_CLASSROOMS = 104,
TYPE_XMLANSWER_QUERY_DB__LIST_TASKS = 105
TYPE_XMLANSWER_QUERY_TASKS_AMM_FOR_TRAINEE = 106,
TYPE_XMLANSWER_QUERY_TASKS_FIM_FOR_TRAINEE = 107,
//xml-ответы на запросы AdditionalFiles
TYPE_XMLANSWER_QUERY_TASKS_XML_FIM = 130,
TYPE_XMLANSWER_QUERY_TASKS_XML_AMM = 131,
HASH_READY = 150,
CHANGE_DATA_VERSION = 151,
COPY_VERSION = 152,
DELETE_DATA_VERSION = 153,
BUSY = 154,
FREE = 155
};
Q_DECLARE_METATYPE(PacketType)

View File

@@ -0,0 +1,42 @@
#include "versioncontainer.h"
VersionContainer::VersionContainer(QObject *parent) :
QObject(parent)
{
}
VersionContainer::~VersionContainer()
{
}
QString VersionContainer::getServerVersion() const
{
return serverVersionData->getViewName();
}
QString VersionContainer::getLocalVersion() const
{
return localVersionData->getViewName();
}
StreamingVersionData *VersionContainer::getLocalVersionData() const
{
return localVersionData;
}
void VersionContainer::setLocalVersionData(StreamingVersionData *value)
{
localVersionData = value;
}
StreamingVersionData *VersionContainer::getServerVersionData() const
{
return serverVersionData;
}
void VersionContainer::setServerVersionData(StreamingVersionData *value)
{
serverVersionData = value;
}

View File

@@ -0,0 +1,30 @@
#ifndef VERSIONCONTAINER_H
#define VERSIONCONTAINER_H
#include <QObject>
#include "connectorToServer/streamingversiondata.h"
class VersionContainer : public QObject
{
Q_OBJECT
public:
explicit VersionContainer(QObject *parent = nullptr);
~VersionContainer();
QString getServerVersion() const;
QString getLocalVersion() const;
StreamingVersionData *getLocalVersionData() const;
void setLocalVersionData(StreamingVersionData *value);
StreamingVersionData *getServerVersionData() const;
void setServerVersionData(StreamingVersionData *value);
private:
StreamingVersionData *localVersionData;
StreamingVersionData *serverVersionData;
};
#endif // VERSIONCONTAINER_H

View File

@@ -2,6 +2,7 @@
#define DATAS_H
#include <QString>
#include "typeQueryToDB.h"
class ServerSettings{
public:
@@ -41,7 +42,7 @@ class ClientDeAutorization{
public:
QString Login;
};
/*
enum TypeQueryToDB{
TYPE_QUERY_GET_ALL_LISTS,
TYPE_QUERY_NEW_INSTRUCTOR,
@@ -52,8 +53,11 @@ enum TypeQueryToDB{
TYPE_QUERY_EDIT_GROUP,
TYPE_QUERY_NEW_TRAINEE,
TYPE_QUERY_DEL_TRAINEE,
TYPE_QUERY_EDIT_TRAINEE
TYPE_QUERY_EDIT_TRAINEE,
TYPE_QUERY_ASSIGN_TASK_AMM_TO_TRAINEE,
TYPE_QUERY_ASSIGN_TASK_FIM_TO_TRAINEE
};
*/
class ClientQueryToDB{
public:

View File

@@ -7,7 +7,11 @@ ConnectorToServer::ConnectorToServer(QObject *parent) :
client(nullptr),
dataParser(nullptr),
sendSystem(nullptr),
recognizeSystem(nullptr)
recognizeSystem(nullptr),
versionSelectWidget(nullptr),
versionContainer(nullptr),
notifyController(nullptr),
waitAnimationWidget(nullptr)
{
initialize();
}
@@ -24,8 +28,8 @@ bool ConnectorToServer::authorizationInstructorLocal(QString login, QString pass
autorization->Password = password;
autorization->TypeClient = TypeClientAutorization::TYPE_GUI;
dataParser->createAuthMessage(autorization);
emit signal_sendXMLmsgGUItoServer();
QByteArray array = dataParser->createAuthMessage(autorization);
emit signal_sendXMLmsgGUItoServer(array);
return true;
}
@@ -40,8 +44,8 @@ bool ConnectorToServer::deAuthorizationInstructorLocal(QString login)
ClientDeAutorization *deAutorization = new ClientDeAutorization;
deAutorization->Login = login;
dataParser->createDeAuthMessage(deAutorization);
emit signal_sendXMLmsgGUItoServer();
QByteArray array = dataParser->createDeAuthMessage(deAutorization);
emit signal_sendXMLmsgGUItoServer(array);
return true;
}
@@ -56,8 +60,8 @@ bool ConnectorToServer::sendQueryToDB(TypeQueryToDB typeQuery, int id, void* dat
ClientQueryToDB *queryToDB = new ClientQueryToDB;
queryToDB->typeQuery = typeQuery;
dataParser->createQueryToDBMessage(queryToDB, id, data);
emit signal_sendXMLmsgGUItoServer();
QByteArray array = dataParser->createQueryToDBMessage(queryToDB, id, data);
emit signal_sendXMLmsgGUItoServer(array);
return true;
}
@@ -74,15 +78,44 @@ bool ConnectorToServer::sendMessageForClient(int id, QString login, QString text
toClientMessage->Login = login;
toClientMessage->Text = text;
dataParser->createToClientMessage(toClientMessage);
emit signal_sendXMLmsgGUItoServer();
QByteArray array = dataParser->createToClientMessage(toClientMessage);
emit signal_sendXMLmsgGUItoServer(array);
return true;
}
bool ConnectorToServer::sendQueryTasksXML(QString type)
{
if (!client->getIsConnected())
{
return false;
}
QByteArray array = dataParser->createQueryTasksXMLMessage(type);
emit signal_sendXMLmsgGUItoServer(array);
return true;
}
void ConnectorToServer::setLoginName(QString name)
{
versionSelectWidget->setAuthor(name);
}
void ConnectorToServer::SetConnectToServer()
{
emit sigSetConnect(dataParser->getServerSettings(),connectionThread);
}
QByteArray ConnectorToServer::getListTaskFimArray()
{
return listTaskFimArray;
}
QByteArray ConnectorToServer::getListTaskAmmArray()
{
return listTaskAmmArray;
}
QList<Instructor> ConnectorToServer::getListInstructors()
@@ -110,9 +143,20 @@ QList<Classroom> ConnectorToServer::getListClassrooms()
return listClassrooms;
}
QList<Task> ConnectorToServer::getListTasks()
QList<TaskAmmFim> ConnectorToServer::getListTasksAMMforTrainee(int trainee_id)
{
return listTasks;
if(mapTasksAMM.contains(trainee_id))
return mapTasksAMM.value(trainee_id);
else
return QList<TaskAmmFim>();
}
QList<TaskAmmFim> ConnectorToServer::getListTasksFIMforTrainee(int trainee_id)
{
if(mapTasksFIM.contains(trainee_id))
return mapTasksFIM.value(trainee_id);
else
return QList<TaskAmmFim>();
}
bool ConnectorToServer::isArchivedInstructor(int id)
@@ -226,6 +270,12 @@ int ConnectorToServer::getIdTraineeByLogin(QString login)
return 0;
}
void ConnectorToServer::showVersionSelect()
{
QByteArray answer = dataParser->xmlAnswer_notify(cmd_CheckVersionList);
emit sigSendAnswerToServer(answer);
}
/*
void ConnectorToServer::slot_AnswerQueryToDB(QList<Instructor>* listInstructors,
QList<Trainee>* listTrainees,
@@ -268,10 +318,38 @@ void ConnectorToServer::slot_AnswerQueryToDB_ListClassrooms(QList<Classroom> lis
//emit signal_UpdateDB(false, true);
}
void ConnectorToServer::slot_AnswerQueryToDB_ListTasks(QList<Task> listTasks)
void ConnectorToServer::slot_AnswerQueryToDB_ListTasksAMMforTrainee(QList<TaskAmmFim> listTasks, int trainee_id)
{
this->listTasks = listTasks;
//emit signal_UpdateDB(false, true);
//Удаляем старые задачи этого обучаемого
mapTasksAMM.remove(trainee_id);
//Добавляем новые
mapTasksAMM.insert(trainee_id, listTasks);
emit signal_UpdateTasksAMMforTrainee(trainee_id);
}
void ConnectorToServer::slot_AnswerQueryToDB_ListTasksFIMforTrainee(QList<TaskAmmFim> listTasks, int trainee_id)
{
//Удаляем старые задачи этого обучаемого
mapTasksFIM.remove(trainee_id);
//Добавляем новые
mapTasksFIM.insert(trainee_id, listTasks);
emit signal_UpdateTasksFIMforTrainee(trainee_id);
}
void ConnectorToServer::slot_AnswerQueryTasksXML_FIM(QByteArray array)
{
this->listTaskFimArray = array;
emit signal_UpdateTasksFIM();
}
void ConnectorToServer::slot_AnswerQueryTasksXML_AMM(QByteArray array)
{
this->listTaskAmmArray = array;
emit signal_UpdateTasksAMM();
}
void ConnectorToServer::slot_msgToClientReady(QString login, QString text)
@@ -281,6 +359,11 @@ void ConnectorToServer::slot_msgToClientReady(QString login, QString text)
sendMessageForClient(id, login, text);
}
void ConnectorToServer::showServerList(QList<StreamingVersionData *> *serverList)
{
versionSelectWidget->fillView(serverList);
}
void ConnectorToServer::initialize()
{
createObjects();
@@ -289,7 +372,24 @@ void ConnectorToServer::initialize()
emit sigInitializeClient(recognizeSystem,sendSystem,connectionThread);
emit sigSetConnect(dataParser->getServerSettings(),connectionThread);
SetConnectToServer();
//emit sigSetConnect(dataParser->getServerSettings(),connectionThread);
// QByteArray answer = dataParser->xmlAnswer_notify()
// sendSystem->sendXMLAnswer()
}
void ConnectorToServer::activateLoadAnimation(bool flag)
{
if (flag)
{
waitAnimationWidget->showWithPlay();
}
else
{
waitAnimationWidget->hideWithStop();
}
}
void ConnectorToServer::bindConnection()
@@ -297,10 +397,13 @@ void ConnectorToServer::bindConnection()
connect(this,&ConnectorToServer::sigInitializeClient,client,&TCPClient::initialize,Qt::AutoConnection);
connect(this,&ConnectorToServer::sigSetConnect,client,&TCPClient::setConnect,Qt::AutoConnection);
connect(this,&ConnectorToServer::signal_sendXMLmsgGUItoServer,sendSystem,&SendSystem::sendXMLmsgGUItoServer);
connect(this,&ConnectorToServer::sigSendAnswerToServer,sendSystem,&SendSystem::sendXMLAnswer,Qt::AutoConnection);
connect(recognizeSystem,&RecognizeSystem::sigAuth,this,&ConnectorToServer::sigLoginResult);
connect(recognizeSystem,&RecognizeSystem::sigDeAuth,this,&ConnectorToServer::sigDeLoginResult);
connect(recognizeSystem,&RecognizeSystem::signal_MessageForGUI,this,&ConnectorToServer::signal_msgFromClientReady);
connect(recognizeSystem,&RecognizeSystem::sigShowServerDataList,this,&ConnectorToServer::showServerList);
connect (recognizeSystem,&RecognizeSystem::sigSetVersion,versionContainer,&VersionContainer::setServerVersionData);
//connect(recognizeSystem,&RecognizeSystem::sigAnswerQueryToDB,this,&ConnectorToServer::slot_AnswerQueryToDB);
connect(recognizeSystem,&RecognizeSystem::sigAnswerQueryToDB_ListInstructors,this,&ConnectorToServer::slot_AnswerQueryToDB_ListInstructors);
@@ -308,9 +411,17 @@ void ConnectorToServer::bindConnection()
connect(recognizeSystem,&RecognizeSystem::sigAnswerQueryToDB_ListTrainees,this,&ConnectorToServer::slot_AnswerQueryToDB_ListTrainees);
connect(recognizeSystem,&RecognizeSystem::sigAnswerQueryToDB_ListComputers,this,&ConnectorToServer::slot_AnswerQueryToDB_ListComputers);
connect(recognizeSystem,&RecognizeSystem::sigAnswerQueryToDB_ListClassrooms,this,&ConnectorToServer::slot_AnswerQueryToDB_ListClassrooms);
connect(recognizeSystem,&RecognizeSystem::sigAnswerQueryToDB_ListTasks,this,&ConnectorToServer::slot_AnswerQueryToDB_ListTasks);
connect(client,&TCPClient::signal_ConnectedToServer,this,&ConnectorToServer::signal_ConnectedToServer);
connect(recognizeSystem,&RecognizeSystem::sigAnswerQueryToDB_ListTasksAMMforTrainee,this,&ConnectorToServer::slot_AnswerQueryToDB_ListTasksAMMforTrainee);
connect(recognizeSystem,&RecognizeSystem::sigAnswerQueryToDB_ListTasksFIMforTrainee,this,&ConnectorToServer::slot_AnswerQueryToDB_ListTasksFIMforTrainee);
connect(recognizeSystem,&RecognizeSystem::sigAnswerQueryTasksXML_FIM,this,&ConnectorToServer::slot_AnswerQueryTasksXML_FIM);
connect(recognizeSystem,&RecognizeSystem::sigAnswerQueryTasksXML_AMM,this,&ConnectorToServer::slot_AnswerQueryTasksXML_AMM);
connect(recognizeSystem,&RecognizeSystem::sigAnimationActivated,this,&ConnectorToServer::activateLoadAnimation,Qt::AutoConnection);
connect(client,&TCPClient::signal_ConnectedToServer,this,&ConnectorToServer::signal_ConnectedToServer,Qt::AutoConnection);
connect(recognizeSystem,&RecognizeSystem::sigNotify,notifyController,&NotifyController::showWarning,Qt::AutoConnection);
}
void ConnectorToServer::createObjects()
@@ -322,12 +433,25 @@ void ConnectorToServer::createObjects()
dataParser = new DataParser;
waitAnimationWidget = new WaitAnimationWidget;
sendSystem = new SendSystem;
sendSystem->moveToThread(connectionThread);
recognizeSystem = new RecognizeSystem;
recognizeSystem->moveToThread(connectionThread);
notifyController = new NotifyController;
versionContainer = new VersionContainer;
versionSelectWidget = new VersionSelectWidget;
versionSelectWidget->initialize(sendSystem,versionContainer,notifyController);
QMovie *movie = new QMovie(":/resources/icons/762.gif");
waitAnimationWidget->setParent(versionSelectWidget);
waitAnimationWidget->initialize(movie,versionSelectWidget);
waitAnimationWidget->moveToThread(connectionThread);
connectionThread->start();
connectionThread->setPriority(QThread::HighestPriority);
}

View File

@@ -2,6 +2,9 @@
#define CONNECTORTOSERVER_H
#include <QObject>
#include <QMap>
#include <widgets/versionselectwidget.h>
#include <widgets/waitanimationwidget.h>
#include "Core\tcpclient.h"
#include "Core\dataparser.h"
#include "Core\sendsystem.h"
@@ -11,7 +14,7 @@
#include "group.h"
#include "computer.h"
#include "classroom.h"
#include "task.h"
#include "streamingversiondata.h"
class ConnectorToServer : public QObject
{
@@ -25,8 +28,14 @@ public:
bool sendQueryToDB(TypeQueryToDB typeQuery, int id = 0, void* data = nullptr);
bool sendMessageForClient(int id, QString login, QString text);
bool sendQueryTasksXML(QString type);
void SetConnectToServer();
public:
QByteArray getListTaskFimArray();
QByteArray getListTaskAmmArray();
public:
//Запросы к БД (локальной)
QList<Instructor> getListInstructors();
@@ -34,7 +43,8 @@ public:
QList<Group> getListGroups();
QList<Computer> getListComputers();
QList<Classroom> getListClassrooms();
QList<Task> getListTasks();
QList<TaskAmmFim> getListTasksAMMforTrainee(int trainee_id);
QList<TaskAmmFim> getListTasksFIMforTrainee(int trainee_id);
bool isArchivedInstructor(int id);
bool isAdminInstructor(int id);
@@ -48,7 +58,10 @@ public:
Group getGroup(int id);
int getIdTraineeByLogin(QString login);
void showVersionSelect();
void activateLoadAnimation(bool flag);
void setLoginName(QString name);
public slots:
/*void slot_AnswerQueryToDB(QList<Instructor>* listInstructors,
QList<Trainee>* listTrainees,
@@ -59,9 +72,14 @@ public slots:
void slot_AnswerQueryToDB_ListTrainees(QList<Trainee> listTrainees);
void slot_AnswerQueryToDB_ListComputers(QList<Computer> listComputers);
void slot_AnswerQueryToDB_ListClassrooms(QList<Classroom> listClassrooms);
void slot_AnswerQueryToDB_ListTasks(QList<Task> listTasks);
void slot_AnswerQueryToDB_ListTasksAMMforTrainee(QList<TaskAmmFim> listTasks, int trainee_id);
void slot_AnswerQueryToDB_ListTasksFIMforTrainee(QList<TaskAmmFim> listTasks, int trainee_id);
void slot_AnswerQueryTasksXML_FIM(QByteArray array);
void slot_AnswerQueryTasksXML_AMM(QByteArray array);
void slot_msgToClientReady(QString login, QString text);
void showServerList(QList<StreamingVersionData*> *serverList);
signals:
void sigSetConnect(ServerSettings* serverSettings,QThread *thread);
@@ -69,18 +87,25 @@ signals:
SendSystem *sendSystem,
QThread *thread);
void signal_sendXMLmsgGUItoServer();
void signal_sendXMLmsgGUItoServer(QByteArray array);
void sigLoginResult(ServerAuthorization * serverAuth);
void sigDeLoginResult(ServerDeAuthorization * serverDeAuth);
void signal_UpdateDB(bool treeInstructor, bool treeTrainee);
void signal_UpdateTasksFIM(); //Общий список
void signal_UpdateTasksAMM(); //Общий список
void signal_UpdateTasksFIMforTrainee(int trainee_id);
void signal_UpdateTasksAMMforTrainee(int trainee_id);
void signal_ConnectedToServer(bool state);
void signal_InitMessanger(QList<Trainee> listTrainees);
void signal_msgFromClientReady(QString login, QString text);
void sigSendAnswerToServer(QByteArray array);
private:
@@ -94,6 +119,10 @@ private:
DataParser *dataParser;
SendSystem *sendSystem;
RecognizeSystem *recognizeSystem;
VersionSelectWidget *versionSelectWidget;
VersionContainer *versionContainer;
NotifyController *notifyController;
WaitAnimationWidget *waitAnimationWidget;
//Списочная модель БД СУО
QList<Instructor> listInstructors;
@@ -101,7 +130,11 @@ private:
QList<Trainee> listTrainees;
QList<Computer> listComputers;
QList<Classroom> listClassrooms;
QList<Task> listTasks;
QMap<int, QList<TaskAmmFim>> mapTasksAMM;
QMap<int, QList<TaskAmmFim>> mapTasksFIM;
QByteArray listTaskFimArray;
QByteArray listTaskAmmArray;
};
#endif // CONNECTORTOSERVER_H

View File

@@ -0,0 +1,88 @@
#ifndef STREAMINGVERSIONDATA_H
#define STREAMINGVERSIONDATA_H
#include <QObject>
#include <qdatetime.h>
class StreamingVersionData
{
public:
StreamingVersionData(){}
StreamingVersionData(QString absoltePath,QString viewName,QDateTime data,qint32 size)
{
this->absolutePath = absoltePath;
this->viewName = viewName;
this->createData = data;
this->size = size;
this->author = "";
}
void setName(QString viewName)
{
this->viewName = viewName;
}
void setCreateData(QDateTime data)
{
this->createData = data;
}
~StreamingVersionData();
QString getAbsolutPath() const
{
return absolutePath;
}
QString getViewName() const
{
return viewName;
}
QDateTime getCreateData() const
{
return createData;
}
qint32 getSize() const
{
return size;
}
bool getIsChangeable() const
{
return isChangeable;
}
void setIsChangeable(bool value)
{
isChangeable = value;
}
QString getAuthor() const
{
return author;
}
void setAuthor(const QString &value)
{
author = value;
}
private:
QString absolutePath;
QString viewName;
QString author;
QDateTime createData;
bool isChangeable;
qint32 size;
};
#endif // STREAMINGVERSIONDATA_H

View File

@@ -1,48 +0,0 @@
#ifndef DOCTASKSWIDGET_H
#define DOCTASKSWIDGET_H
#include <QWidget>
#include <QTreeWidget>
#include <QDomNode>
#include "module.h"
namespace Ui {
class DocTasksWidget;
}
class DocTasksWidget : public QWidget
{
Q_OBJECT
private:
enum ColumnsTree{
clmn_PMorDM = 0,
clmn_ID
};
public:
explicit DocTasksWidget(QWidget *parent = nullptr);
~DocTasksWidget();
private Q_SLOTS:
void on_treeWidget_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous);
private:
void domElementParser(QDomElement element, Module* moduleParent);
void loadDocTasksFromXML();
void deleteAllModuls();
Module* searchModuleByID(int id);
void preparationTreeWidget();
void reSetHeadTreeWidget();
void updateTreeWidget();
void addModuleToTreeWidget(Module* module, QTreeWidgetItem* parentItem = nullptr);
private:
Ui::DocTasksWidget *ui;
QTreeWidget* treeWidget;
QList<Module*> listAllModules;
};
#endif // DOCTASKSWIDGET_H

View File

@@ -1,61 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>DocTasksWidget</class>
<widget class="QWidget" name="DocTasksWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="font">
<font>
<family>Tahoma</family>
<pointsize>10</pointsize>
</font>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>AMM</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_2"/>
</item>
<item row="1" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Code</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="editCode">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -1,174 +0,0 @@
#include <QDomDocument>
#include <QFile>
#include <QMessageBox>
#include <QTreeWidget>
#include "fimtaskswidget.h"
#include "ui_fimtaskswidget.h"
#include "tasksAmmFim.h"
FIMtasksWidget::FIMtasksWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::FIMtasksWidget)
{
ui->setupUi(this);
loadTasksAmmFimFromXML();
preparationTreeWidget();
fillTree();
}
FIMtasksWidget::~FIMtasksWidget()
{
delete ui;
}
void FIMtasksWidget::loadTasksAmmFimFromXML()
{
QDomDocument docTasksDOM;
QString xmlFileName = "./tasksFIM.xml";
QFile xmlInFile(xmlFileName);
if (!xmlInFile.open(QFile::ReadOnly | QFile::Text))
{
QMessageBox::critical(nullptr, tr("Attention!"), tr("The file could not be opened ") + xmlFileName);
return;
}
else
{
docTasksDOM.setContent(xmlInFile.readAll());
xmlInFile.close();
QDomElement RRJTasksElement = docTasksDOM.firstChildElement("RRJTasks");
if(RRJTasksElement.isNull())
return;
QDomElement taskElement = RRJTasksElement.firstChildElement();
if(taskElement.isNull())
return;
do
{/*task*/
QString name = taskElement.nodeName();
QDomNamedNodeMap nodeMap = taskElement.attributes();
if(name == "task")
{
TaskAmmFim task;
task.initialize(nodeMap.namedItem("id").nodeValue().toInt(),
nodeMap.namedItem("type").nodeValue(),
nodeMap.namedItem("title").nodeValue(),
nodeMap.namedItem("status").nodeValue(),
nodeMap.namedItem("created").nodeValue(),
nodeMap.namedItem("changed").nodeValue());
QDomElement malfunctionElement = taskElement.firstChildElement();
if(!malfunctionElement.isNull())
{
do
{/*malfunction*/
QString name = malfunctionElement.nodeName();
QDomNamedNodeMap nodeMap = malfunctionElement.attributes();
if(name == "malfunction")
{
Malfunction malfunction;
malfunction.initialize(nodeMap.namedItem("dmCode").nodeValue(),
nodeMap.namedItem("num").nodeValue(),
nodeMap.namedItem("description").nodeValue());
QDomElement signElement = malfunctionElement.firstChildElement();
if(!signElement.isNull())
{
do
{/*malfunctionSign*/
QString name = signElement.nodeName();
QDomNamedNodeMap nodeMap = signElement.attributes();
if(name == "malfunctionSign")
{
MalfunctionSign sign;
sign.initialize(nodeMap.namedItem("type").nodeValue().toInt(),
nodeMap.namedItem("description").nodeValue());
malfunction.addMalfunctionSign(sign);
}
}while(! (signElement = signElement.nextSiblingElement()).isNull());
}
task.addMalfunction(malfunction);
}
}while(! (malfunctionElement = malfunctionElement.nextSiblingElement()).isNull());
}
listTaskAmmFim.append(task);
}
}while (! (taskElement = taskElement.nextSiblingElement()).isNull());
}
}
void FIMtasksWidget::fillTree()
{
for(int i = 0; i < listTaskAmmFim.count(); i++)
{/*Задачи*/
TaskAmmFim task = listTaskAmmFim.at(i);
QTreeWidgetItem* itemTask = new QTreeWidgetItem();
itemTask->setText(0, task.title);
itemTask->setText(1, QString::number(task.id));
itemTask->setFlags(itemTask->flags() | Qt::ItemIsUserCheckable);
itemTask->setCheckState(0, Qt::Checked);
itemTask->setIcon(0, QIcon(":/resources/icons/procedure.png"));
ui->treeWidget->addTopLevelItem(itemTask);
for (int j = 0; j < task.malfunctionList.count(); j++)
{/*Неисправности*/
Malfunction malfunction = task.malfunctionList.at(j);
QTreeWidgetItem* itemMalfunction = new QTreeWidgetItem();
itemMalfunction->setText(0, malfunction.description);
itemMalfunction->setFlags(itemMalfunction->flags() | Qt::ItemIsUserCheckable);
itemMalfunction->setCheckState(0, Qt::Checked);
itemMalfunction->setIcon(0, QIcon(":/resources/icons/malfunction.png"));
itemTask->addChild(itemMalfunction);
for (int k = 0; k < malfunction.malfunctionSigns.count(); k++)
{/*Сигнализация*/
MalfunctionSign sign = malfunction.malfunctionSigns.at(k);
QTreeWidgetItem* itemSign = new QTreeWidgetItem();
itemSign->setText(0, sign.description);
//itemSign->setFlags(itemSign->flags() | Qt::ItemIsUserCheckable);
//itemSign->setCheckState(0, Qt::Checked);
itemSign->setIcon(0, QIcon(":/resources/icons/sign.png"));
itemMalfunction->addChild(itemSign);
}
}
}
}
void FIMtasksWidget::preparationTreeWidget()
{
ui->treeWidget->setColumnCount(2);
reSetHeadTreeWidget();
ui->treeWidget->setColumnWidth(ColumnsTree::clmn_ID, 20);
ui->treeWidget->setColumnWidth(ColumnsTree::clmn_Title, 500);
//ui->treeWidget->setColumnHidden(ColumnsTree::clmn_ID, true);
}
void FIMtasksWidget::reSetHeadTreeWidget()
{
QStringList listHeaders = {tr("Title"), tr("ID")};
ui->treeWidget->setHeaderLabels(listHeaders);
}

View File

@@ -1,40 +0,0 @@
#ifndef FIMTASKSWIDGET_H
#define FIMTASKSWIDGET_H
#include <QWidget>
#include "tasksAmmFim.h"
namespace Ui {
class FIMtasksWidget;
}
class FIMtasksWidget : public QWidget
{
Q_OBJECT
private:
enum ColumnsTree{
clmn_Title = 0,
clmn_ID
};
public:
explicit FIMtasksWidget(QWidget *parent = nullptr);
~FIMtasksWidget();
private:
void loadTasksAmmFimFromXML();
void fillTree();
void preparationTreeWidget();
void reSetHeadTreeWidget();
public:
QString userName;
QList<TaskAmmFim> listTaskAmmFim;
private:
Ui::FIMtasksWidget *ui;
};
#endif // FIMTASKSWIDGET_H

View File

@@ -1,50 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>FIMtasksWidget</class>
<widget class="QWidget" name="FIMtasksWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>472</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>FIM</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="4" column="0">
<widget class="QTreeWidget" name="treeWidget">
<column>
<property name="text">
<string notr="true">1</string>
</property>
</column>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>List of tasks</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -8,31 +8,38 @@ InstructorsView::InstructorsView(ConnectorToServer* connectorToServer, TypeView
typeObject = TypeObject::objInstructor;
}
void InstructorsView::resizeEvent(QResizeEvent *event)
{
int width = treeWidget->width();
treeWidget->setColumnWidth(ColumnsTreeInsructors::clmn_ID, 50);
treeWidget->setColumnWidth(ColumnsTreeInsructors::clmn_Login, 100);
treeWidget->setColumnWidth(ColumnsTreeInsructors::clmn_Password, 100);
treeWidget->setColumnWidth(ColumnsTreeInsructors::clmn_Administrator, 120);
treeWidget->setColumnWidth(ColumnsTreeInsructors::clmn_Archived, 80);
treeWidget->setColumnWidth(ColumnsTreeInsructors::clmn_Logged, 100);
int widthInstructor;
if(typeView == TypeView::onlyView)
{//onlyView
widthInstructor = width - (200 + 10);
}
else
{//control
if(adminMode)
widthInstructor = width - (550 + 10);
else
widthInstructor = width - (420 + 10);
}
if(widthInstructor < 250)
widthInstructor = 250;
treeWidget->setColumnWidth(ColumnsTreeInsructors::clmn_Instructor, widthInstructor);
}
void InstructorsView::slot_NeedUpdateUI(bool treeInstructor, bool treeTrainee)
{
if(typeView == TypeView::onlyView)
{
if(adminMode)
archiveVisible = false;
else
archiveVisible = false;
}
else
{
archiveVisible = true;
}
if(adminMode)
{
treeWidget->setColumnHidden(ColumnsTreeInsructors::clmn_ID, false);
treeWidget->setColumnHidden(ColumnsTreeInsructors::clmn_Archived, false);
}
else
{
treeWidget->setColumnHidden(ColumnsTreeInsructors::clmn_ID, true);
treeWidget->setColumnHidden(ColumnsTreeInsructors::clmn_Archived, true);
}
updateButtons();
if(treeInstructor)
@@ -43,41 +50,27 @@ void InstructorsView::preparationTreeWidget()
{
mtxTreeWidget.lock();
treeWidget->setColumnCount(7);
treeWidget->setColumnCount(clmn_count);
reSetHeadTreeWidget();
treeWidget->setColumnWidth(ColumnsTreeInsructors::clmn_ID, 50);
treeWidget->setColumnWidth(ColumnsTreeInsructors::clmn_Instructor, 250);
treeWidget->setColumnWidth(ColumnsTreeInsructors::clmn_Login, 100);
treeWidget->setColumnWidth(ColumnsTreeInsructors::clmn_Password, 100);
treeWidget->setColumnWidth(ColumnsTreeInsructors::clmn_Administrator, 100);
treeWidget->setColumnWidth(ColumnsTreeInsructors::clmn_Archived, 100);
treeWidget->setColumnWidth(ColumnsTreeInsructors::clmn_Logged, 100);
reSetHeadTreeWidget();
if(typeView == TypeView::onlyView)
{//onlyView
archiveVisible = false;
treeWidget->setColumnHidden(ColumnsTreeInsructors::clmn_ID, true);
//treeWidget->setColumnHidden(ColumnsTreeInsructors::clmn_Login, true);
treeWidget->setColumnHidden(ColumnsTreeInsructors::clmn_Password, true);
treeWidget->setColumnHidden(ColumnsTreeInsructors::clmn_Archived, true);
treeWidget->setColumnHidden(ColumnsTreeInsructors::clmn_Administrator, true);
if(adminMode)
archiveVisible = false;
else
archiveVisible = false;
notLoggedInVisible = true;
}
else
{//control
archiveVisible = true;
notLoggedInVisible = true;
if(adminMode)
{
treeWidget->setColumnHidden(ColumnsTreeInsructors::clmn_ID, false);
treeWidget->setColumnHidden(ColumnsTreeInsructors::clmn_Archived, false);
}
else
{
@@ -174,6 +167,8 @@ void InstructorsView::loadInstructorsFromDB()
setCurrentInstructor(lastCurrentID);
treeWidget->sortItems(ColumnsTreeInsructors::clmn_Instructor, Qt::SortOrder::AscendingOrder);
mtxTreeWidget.unlock();
}

View File

@@ -21,9 +21,13 @@ protected:
clmn_Administrator,
clmn_Archived,
clmn_Logged,
clmn_ID
clmn_ID,
clmn_count
};
public:
void resizeEvent(QResizeEvent *event) override;
public Q_SLOTS:
//Слот обработки сигнала необходимости обновления интерфейса
void slot_NeedUpdateUI(bool treeInstructor, bool treeTrainee);

View File

@@ -22,6 +22,18 @@ ViewerInstructors::~ViewerInstructors()
delete ui;
}
void ViewerInstructors::setAuthComplited(bool authComplited)
{
this->authComplited = authComplited;
updateButtons();
}
void ViewerInstructors::deactivate()
{
CommonView::deactivate();
updateButtons();
}
void ViewerInstructors::changeEvent(QEvent *event)
{
// В случае получения события изменения языка приложения

View File

@@ -17,6 +17,11 @@ public:
explicit ViewerInstructors(ConnectorToServer* connectorToServer, QWidget *parent = nullptr);
~ViewerInstructors();
public:
void setAuthComplited(bool authComplited);
void deactivate();
protected:
void changeEvent(QEvent * event) override;

View File

@@ -1,8 +1,8 @@
#include <QMessageBox>
#include <QThread>
#include "instructorsandtraineeswidget.h"
#include "ui_instructorsandtraineeswidget.h"
#include "dialogauthorizationinstructor.h"
#include "doctaskswidget.h"
InstructorsAndTraineesWidget::InstructorsAndTraineesWidget(QWidget *parent) :
QWidget(parent),
@@ -11,8 +11,8 @@ InstructorsAndTraineesWidget::InstructorsAndTraineesWidget(QWidget *parent) :
viewerTrainees(nullptr),
viewerInstructors(nullptr),
messangerWidget(nullptr),
docTasksWidget(nullptr),
fIMtasksWidget(nullptr),
ammTasksWidget(nullptr),
fimTasksWidget(nullptr),
adminMode(false),
loginInstructorLoggedInLocal(QStringLiteral("")),
nameInstructorLoggedInLocal(QStringLiteral(""))
@@ -20,6 +20,9 @@ InstructorsAndTraineesWidget::InstructorsAndTraineesWidget(QWidget *parent) :
ui->setupUi(this);
ui->btnUpdateStyleSheet->setObjectName("btnUpdateStyleSheet");
#ifndef PROJECT_TYPE_DEBUG
ui->btnUpdateStyleSheet->setVisible(false);
#endif
qRegisterMetaType<PacketType>("PacketType");
qRegisterMetaType<QList<Instructor>>("QList<Instructor>");
@@ -27,7 +30,11 @@ InstructorsAndTraineesWidget::InstructorsAndTraineesWidget(QWidget *parent) :
qRegisterMetaType<QList<Group>>("QList<Group>");
qRegisterMetaType<QList<Computer>>("QList<Computer>");
qRegisterMetaType<QList<Classroom>>("QList<Classroom>");
qRegisterMetaType<QList<Task>>("QList<Task>");
qRegisterMetaType<QList<Module*>>("QList<Module*>");
qRegisterMetaType<QList<QTreeWidgetItem*>>("QList<QTreeWidgetItem*>");
qRegisterMetaType<QList<TaskAmmFim>>("QList<TaskAmmFim>");
qDebug() << "InstructorsAndTraineesWidget init thread ID " << QThread::currentThreadId();
connectorToServer = new ConnectorToServer(this);
connect(connectorToServer,&ConnectorToServer::sigLoginResult,this,&InstructorsAndTraineesWidget::checkLoginResult);
@@ -40,36 +47,67 @@ InstructorsAndTraineesWidget::InstructorsAndTraineesWidget(QWidget *parent) :
connect(viewerInstructors, &ViewerInstructors::signal_BlockAutorization, this, &InstructorsAndTraineesWidget::signal_BlockAutorization);
connect(viewerTrainees, &ViewerTrainees::signal_BlockAutorization, this, &InstructorsAndTraineesWidget::signal_BlockAutorization);
connect(connectorToServer,&ConnectorToServer::signal_UpdateDB,viewerInstructors,&ViewerInstructors::slot_NeedUpdateUI);
connect(connectorToServer,&ConnectorToServer::signal_UpdateDB,viewerTrainees,&ViewerTrainees::slot_NeedUpdateUI);
//connect(connectorToServer,&ConnectorToServer::signal_UpdateDB,viewerInstructors,&ViewerInstructors::slot_NeedUpdateUI);
//connect(connectorToServer,&ConnectorToServer::signal_UpdateDB,viewerTrainees,&ViewerTrainees::slot_NeedUpdateUI);
connect(connectorToServer,&ConnectorToServer::signal_ConnectedToServer,this,&InstructorsAndTraineesWidget::slot_ConnectedToServer);
ammTasksWidget = new AMMtasksWidget(connectorToServer, AMMtasksWidget::TypeList::listCommon, this);
fimTasksWidget = new FIMtasksWidget(connectorToServer, FIMtasksWidget::TypeList::listCommon, this);
connect(this, &InstructorsAndTraineesWidget::signal_AssignTaskFIMtoTrainee, fimTasksWidget, &FIMtasksWidget::slot_AssignTaskFIMtoTrainee);
connect(this, &InstructorsAndTraineesWidget::signal_AssignTaskAMMtoTrainee, ammTasksWidget, &AMMtasksWidget::slot_AssignTaskAMMtoTrainee);
connect(fimTasksWidget, &FIMtasksWidget::signal_currentItemChanged, this, &InstructorsAndTraineesWidget::slot_currentItemChanged);
connect(ammTasksWidget, &AMMtasksWidget::signal_currentItemChanged, this, &InstructorsAndTraineesWidget::slot_currentItemChanged);
connect(connectorToServer, &ConnectorToServer::signal_UpdateTasksFIM, fimTasksWidget, &FIMtasksWidget::slot_NeedUpdateUI);
connect(connectorToServer, &ConnectorToServer::signal_UpdateTasksAMM, ammTasksWidget, &AMMtasksWidget::slot_NeedUpdateUI);
connect(viewerTrainees, &ViewerTrainees::signal_traineeSelected, fimTasksWidget, &FIMtasksWidget::slot_traineeSelected);
connect(viewerTrainees, &ViewerTrainees::signal_traineeSelected, ammTasksWidget, &AMMtasksWidget::slot_traineeSelected);
messangerWidget = new MessangerWidget(this);
connect(connectorToServer,&ConnectorToServer::signal_InitMessanger,messangerWidget,&MessangerWidget::slot_InitMessanger);
//connect(connectorToServer,&ConnectorToServer::signal_InitMessanger,messangerWidget,&MessangerWidget::slot_InitMessanger);
connect(viewerTrainees, &ViewerTrainees::signal_traineeSelected, messangerWidget, &MessangerWidget::slot_traineeSelected);
connect(messangerWidget, &MessangerWidget::signal_tabMessengerChanged, viewerTrainees, &ViewerTrainees::slot_tabMessengerChanged);
connect(messangerWidget, &MessangerWidget::signal_msgToClientReady, connectorToServer, &ConnectorToServer::slot_msgToClientReady);
connect(connectorToServer,&ConnectorToServer::signal_msgFromClientReady,messangerWidget,&MessangerWidget::slot_msgFromClientReady);
docTasksWidget = new DocTasksWidget(this);
fIMtasksWidget = new FIMtasksWidget(this);
ui->horizontalLayout_3->addWidget(viewerTrainees);
ui->horizontalLayout_3->addWidget(messangerWidget);
ui->verticalLayout_1->addWidget(viewerInstructors);
ui->verticalLayout_2->addWidget(docTasksWidget);
ui->verticalLayout_2->addWidget(fIMtasksWidget);
QWidget* wGB2 = new QWidget(this);
QHBoxLayout* lGB2 = new QHBoxLayout(this);
wGB2->setLayout(lGB2);
lGB2->addWidget(ui->groupBox_2);
ui->horizontalLayout_3->addWidget(wGB2);
viewerTrainees->setMinimumHeight(800);
viewerInstructors->setMinimumSize(1800, 300);
messangerWidget->setMinimumSize(500, 600);
messangerWidget->setMaximumWidth(500);
ui->verticalLayout_41->addWidget(ammTasksWidget);
ui->verticalLayout_42->addWidget(fimTasksWidget);
//ui->btnAuthorizationInstructor->setEnabled(false);
ui->verticalLayout_2->addWidget(messangerWidget);
ui->verticalLayout_2->addWidget(viewerInstructors);
ui->btnSetVersion->hide();
viewerTrainees->setMinimumSize(500, 500);
viewerTrainees->setMaximumWidth(1050);
wGB2->setMinimumSize(500, 500);
//wGB2->setMaximumWidth(1050);
viewerInstructors->setMinimumSize(400, 400);
viewerInstructors->setMaximumWidth(500);
viewerInstructors->setMaximumHeight(400);
messangerWidget->setMinimumSize(400, 500);
messangerWidget->setMaximumWidth(500);
ui->btnAuthorizationInstructor->setEnabled(false);
ui->btnAssignTask->setEnabled(false);
updateMyStyleSheet();
}
@@ -79,8 +117,8 @@ InstructorsAndTraineesWidget::~InstructorsAndTraineesWidget()
if(authorizationIsCompleted())
deAuthorizationInstructor(loginInstructorLoggedInLocal);
delete docTasksWidget;
delete fIMtasksWidget;
delete ammTasksWidget;
delete fimTasksWidget;
delete messangerWidget;
delete viewerInstructors;
delete viewerTrainees;
@@ -165,12 +203,16 @@ void InstructorsAndTraineesWidget::checkLoginResult(ServerAuthorization *serverA
viewerTrainees->setAuthComplited(true);
Q_EMIT signal_NeedUpdateUI(true, true);
ui->btnSetVersion->show();
ui->btnAuthorizationInstructor->setText(tr("Deauthorization Instructor"));
updateLabelLoggedInInstructor(serverAuth->Login, serverAuth->ClientName);
connectorToServer->setLoginName(nameInstructorLoggedInLocal);
QMessageBox::information(this, tr("Instructor authorization"), tr("Successfully!"));
connectorToServer->sendQueryTasksXML("fim");
connectorToServer->sendQueryTasksXML("amm");
//QMessageBox::information(this, tr("Instructor authorization"), tr("Successfully!"));
}
else
{
@@ -192,13 +234,12 @@ void InstructorsAndTraineesWidget::checkDeLoginResult(ServerDeAuthorization *ser
viewerInstructors->setAuthComplited(false);
viewerTrainees->setAuthComplited(false);
Q_EMIT signal_NeedUpdateUI(true, false);
//Q_EMIT signal_NeedUpdateUI(true, false);
ui->btnAuthorizationInstructor->setText(tr("Authorization Instructor"));
updateLabelLoggedInInstructor("","");
QMessageBox::information(this, tr("Instructor deauthorization"), tr("Successfully!"));
//QMessageBox::information(this, tr("Instructor deauthorization"), tr("Successfully!"));
}
else
{
@@ -211,8 +252,6 @@ void InstructorsAndTraineesWidget::slot_ConnectedToServer(bool state)
{
if(state)
{//Сервер подключен
//ui->btnConnectionToDB->setText(tr("Disconnection DB"));
ui->btnConnectionToServer->setEnabled(false);
ui->btnAuthorizationInstructor->setEnabled(true);
@@ -220,12 +259,46 @@ void InstructorsAndTraineesWidget::slot_ConnectedToServer(bool state)
}
else
{//Сервер отключен
//ui->btnConnectionToDB->setText(tr("Connection DB"));
ui->btnConnectionToServer->setEnabled(true);
ui->btnAuthorizationInstructor->setEnabled(false);
ui->btnAuthorizationInstructor->setText(tr("Authorization Instructor"));
ui->btnAuthorizationInstructor->setChecked(false);
ui->btnSetVersion->hide();
ui->lblDBisConnected->setPixmap(QPixmap(QStringLiteral(":/resources/icons/circleGray.png")));
viewerInstructors->setAuthComplited(false);
viewerTrainees->setAuthComplited(false);
viewerTrainees->deactivate();
viewerInstructors->deactivate();
ammTasksWidget->deactivate();
fimTasksWidget->deactivate();
ui->btnAssignTask->setEnabled(false);
messangerWidget->clear();
QMessageBox::warning(this, tr("Warning!"), tr("The server is disabled"));
}
}
void InstructorsAndTraineesWidget::slot_currentItemChanged()
{
int index = ui->tabWidget->currentIndex();
if(index == 0)
{
if(ammTasksWidget->getAccessAssignTask())
ui->btnAssignTask->setEnabled(true);
else
ui->btnAssignTask->setEnabled(false);
}
else if(index == 1)
{
if(fimTasksWidget->getAccessAssignTask())
ui->btnAssignTask->setEnabled(true);
else
ui->btnAssignTask->setEnabled(false);
}
}
@@ -275,51 +348,6 @@ bool InstructorsAndTraineesWidget::authorizationIsCompleted()
void InstructorsAndTraineesWidget::on_btnConnectionToServer_clicked()
{
connectorToServer->SetConnectToServer();
if(true)
{//Подключение к БД
/*
connectorToServer->SetConnectToServer();
if(! dbLMS->DBisConnected())
{
if(dbLMS->ConnectionToDB())
{
ui->btnConnectionToDB->setText(tr("Disconnection DB"));
ui->btnAuthorizationInstructor->setEnabled(true);
ui->lblDBisConnected->setPixmap(QPixmap(QStringLiteral(":/icons/circleGreen.png")));
Q_EMIT signal_NeedUpdateUI(true, true);
Q_EMIT signal_BlockAutorization(false);
Q_EMIT signal_InitMessanger(dbLMS->getListTrainees());
}
}*/
}
else
{//Отключение от БД
/*
bool stateIsCheckedAuthorization = ui->btnAuthorizationInstructor->isChecked();
if(stateIsCheckedAuthorization)
ui->btnAuthorizationInstructor->click();
if(dbLMS->DBisConnected())
{
Q_EMIT signal_BlockAutorization(true);
dbLMS->DisConnectionFromDB();
ui->btnConnectionToDB->setText(tr("Connection DB"));
ui->btnAuthorizationInstructor->setEnabled(false);
ui->lblDBisConnected->setPixmap(QPixmap(QStringLiteral(":/icons/circleGray.png")));
Q_EMIT signal_NeedUpdateUI(true, true);
}*/
}
}
void InstructorsAndTraineesWidget::on_btnAuthorizationInstructor_clicked()
@@ -330,6 +358,9 @@ void InstructorsAndTraineesWidget::on_btnAuthorizationInstructor_clicked()
{//Авторизация Инструктора локальная (Администратора)
if(authorizationInstructorDialog(this))
{
connect(connectorToServer,&ConnectorToServer::signal_UpdateDB,viewerInstructors,&ViewerInstructors::slot_NeedUpdateUI);
connect(connectorToServer,&ConnectorToServer::signal_UpdateDB,viewerTrainees,&ViewerTrainees::slot_NeedUpdateUI);
connect(connectorToServer,&ConnectorToServer::signal_InitMessanger,messangerWidget,&MessangerWidget::slot_InitMessanger);
}
else
ui->btnAuthorizationInstructor->setChecked(false);
@@ -340,6 +371,18 @@ void InstructorsAndTraineesWidget::on_btnAuthorizationInstructor_clicked()
{
if(deAuthorizationInstructor(loginInstructorLoggedInLocal))
{
disconnect(connectorToServer,&ConnectorToServer::signal_UpdateDB,viewerInstructors,&ViewerInstructors::slot_NeedUpdateUI);
disconnect(connectorToServer,&ConnectorToServer::signal_UpdateDB,viewerTrainees,&ViewerTrainees::slot_NeedUpdateUI);
disconnect(connectorToServer,&ConnectorToServer::signal_InitMessanger,messangerWidget,&MessangerWidget::slot_InitMessanger);
viewerTrainees->deactivate();
viewerInstructors->deactivate();
ammTasksWidget->deactivate();
fimTasksWidget->deactivate();
ui->btnAssignTask->setEnabled(false);
messangerWidget->clear();
}
else
ui->btnAuthorizationInstructor->setChecked(true);
@@ -367,11 +410,38 @@ void InstructorsAndTraineesWidget::updateLabelLoggedInInstructor(QString login,
void InstructorsAndTraineesWidget::on_btnUpdateStyleSheet_clicked()
{
/*
viewerTrainees->updateMyStyleSheet();
viewerInstructors->updateMyStyleSheet();
messangerWidget->updateMyStyleSheet();
*/
updateMyStyleSheet();
}
void InstructorsAndTraineesWidget::on_btnSetVersion_clicked()
{
connectorToServer->showVersionSelect();
}
void InstructorsAndTraineesWidget::on_btnAssignTask_clicked()
{
int index = ui->tabWidget->currentIndex();
if(index == 0)
Q_EMIT signal_AssignTaskAMMtoTrainee();
else if(index == 1)
Q_EMIT signal_AssignTaskFIMtoTrainee();
}
void InstructorsAndTraineesWidget::on_tabWidget_currentChanged(int index)
{
if(index == 0)
{
if(ammTasksWidget->getAccessAssignTask())
ui->btnAssignTask->setEnabled(true);
else
ui->btnAssignTask->setEnabled(false);
}
else if(index == 1)
{
if(fimTasksWidget->getAccessAssignTask())
ui->btnAssignTask->setEnabled(true);
else
ui->btnAssignTask->setEnabled(false);
}
}

View File

@@ -7,16 +7,20 @@
#include "viewertrainees.h"
#include "viewerinstructors.h"
#include "messangerwidget.h"
#include "doctaskswidget.h"
#include "ammtaskswidget.h"
#include "fimtaskswidget.h"
#include "connectortoserver.h"
#include "tasksAmmFim.h"
Q_DECLARE_METATYPE(QList<Instructor>)
Q_DECLARE_METATYPE(QList<Trainee>)
Q_DECLARE_METATYPE(QList<Group>)
Q_DECLARE_METATYPE(QList<Computer>)
Q_DECLARE_METATYPE(QList<Classroom>)
Q_DECLARE_METATYPE(QList<Task>)
Q_DECLARE_METATYPE(QList<Module*>)
Q_DECLARE_METATYPE(QList<QTreeWidgetItem*>)
Q_DECLARE_METATYPE(QList<TaskAmmFim>)
namespace Ui {
class InstructorsAndTraineesWidget;
@@ -47,6 +51,7 @@ public Q_SLOTS:
void checkDeLoginResult(ServerDeAuthorization * serverDeAuth);
void slot_ConnectedToServer(bool state);
void slot_currentItemChanged();
Q_SIGNALS:
//сигнал об изменении языка интерфейса
@@ -56,11 +61,20 @@ Q_SIGNALS:
//сигнал о блокировке авторизации
void signal_BlockAutorization(bool block);
void signal_AssignTaskAMMtoTrainee();
void signal_AssignTaskFIMtoTrainee();
private Q_SLOTS:
void on_btnConnectionToServer_clicked();
void on_btnAuthorizationInstructor_clicked();
void on_btnUpdateStyleSheet_clicked();
void on_btnSetVersion_clicked();
void on_btnAssignTask_clicked();
void on_tabWidget_currentChanged(int index);
private:
//Авторизация инструктора локальная
bool authorizationInstructorDialog(QWidget* parent = nullptr);
@@ -76,8 +90,8 @@ private:
ViewerTrainees* viewerTrainees;
ViewerInstructors* viewerInstructors;
MessangerWidget* messangerWidget;
DocTasksWidget* docTasksWidget;
FIMtasksWidget* fIMtasksWidget;
AMMtasksWidget* ammTasksWidget;
FIMtasksWidget* fimTasksWidget;
bool adminMode;
QString loginInstructorLoggedInLocal;

View File

@@ -20,7 +20,7 @@
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<item row="1" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_0">
<item>
<widget class="QGroupBox" name="groupBox">
@@ -203,6 +203,19 @@
</property>
</spacer>
</item>
<item>
<widget class="QToolButton" name="btnSetVersion">
<property name="minimumSize">
<size>
<width>58</width>
<height>58</height>
</size>
</property>
<property name="text">
<string>ChangeVersion</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="btnUpdateStyleSheet">
<property name="minimumSize">
@@ -223,6 +236,70 @@
</item>
</layout>
</item>
<item row="0" column="0">
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Tasks</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="2" column="0">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>1</number>
</property>
<widget class="QWidget" name="tab_1">
<attribute name="title">
<string>AMM</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_4">
<item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout_41"/>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_2">
<attribute name="title">
<string>FIM</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_5">
<item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout_42"/>
</item>
</layout>
</widget>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QToolButton" name="btnAssignTask">
<property name="text">
<string>Assign task</string>
</property>
<property name="icon">
<iconset resource="resources.qrc">
<normaloff>:/resources/icons/assignTask.png</normaloff>:/resources/icons/assignTask.png</iconset>
</property>
<property name="iconSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonTextUnderIcon</enum>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources>

View File

@@ -19,6 +19,8 @@ MessangerWidget::MessangerWidget(QWidget *parent) :
ui->tabWidget->removeTab(0);
ui->btnSend->setObjectName("btnSend");
ui->btnSend->setEnabled(false);
ui->editMsg->setEnabled(false);
}
MessangerWidget::~MessangerWidget()
@@ -55,6 +57,8 @@ void MessangerWidget::addTabDialogMessenger(Trainee trainee)
{//Самая первая вкладка, делаем ее активной
currLogin = trainee.getLogin();
emit signal_tabMessengerChanged(currLogin);
ui->btnSend->setEnabled(true);
ui->editMsg->setEnabled(true);
}
//Проверяем наличие диалога с этим клиентом
@@ -130,6 +134,14 @@ int MessangerWidget::getIndexTab(QString login)
return -1;
}
void MessangerWidget::clear()
{
ui->btnSend->setEnabled(false);
ui->editMsg->setEnabled(false);
listTrainees.clear();
actualizationTabsDialogMessenger();
}
void MessangerWidget::on_btnSend_clicked()
{
QString text = ui->editMsg->toPlainText();
@@ -176,9 +188,13 @@ void MessangerWidget::slot_traineeSelected(QString login)
{
//Активируем нужную вкладку
ui->tabWidget->setCurrentIndex(getIndexTab(login));
ui->btnSend->setEnabled(true);
ui->editMsg->setEnabled(true);
return;
}
}
ui->btnSend->setEnabled(false);
ui->editMsg->setEnabled(false);
}
void MessangerWidget::slot_LanguageChanged(QString language)

View File

@@ -37,6 +37,8 @@ public:
int getIndexTab(QString login);
void clear();
private slots:
void on_btnSend_clicked();
void on_tabWidget_currentChanged(int index);

View File

@@ -119,16 +119,6 @@
<attribute name="title">
<string>Tab 1</string>
</attribute>
<widget class="QListWidget" name="listWidget">
<property name="geometry">
<rect>
<x>110</x>
<y>30</y>
<width>256</width>
<height>192</height>
</rect>
</property>
</widget>
</widget>
<widget class="QWidget" name="tab_2">
<attribute name="title">

View File

@@ -40,5 +40,9 @@
<file>resources/icons/task.png</file>
<file>resources/icons/procedure.png</file>
<file>resources/icons/malfunction.png</file>
<file>resources/icons/762.gif</file>
<file>resources/icons/assignTask.png</file>
<file>resources/icons/delete.png</file>
<file>resources/icons/filter.png</file>
</qresource>
</RCC>

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

View File

@@ -0,0 +1,328 @@
#include <QFile>
#include <QXmlStreamReader>
#include <QDomDocument>
#include <QMessageBox>
#include <QThread>
#include <QResizeEvent>
#include "ammtaskswidget.h"
#include "ui_ammtaskswidget.h"
AMMtasksWidget::AMMtasksWidget(ConnectorToServer* connectorToServer, TypeList type, QWidget *parent) :
QWidget(parent),
ui(new Ui::AMMtasksWidget),
connectorToServer(connectorToServer),
treeWidget(nullptr),
type(type),
loginTraineeSelected(""),
idTraineeSelected(0),
threadPreparation(nullptr),
threadAnimation(nullptr),
taskTreePreparation(nullptr),
waitAnimationWidget(nullptr),
accessAssignTask(false),
flOnlyActive(false)
{
ui->setupUi(this);
qDebug() << "AMMtasksWidget init thread ID " << QThread::currentThreadId();
treeWidget = new QTreeWidget();
ui->horizontalLayout_1->addWidget(treeWidget);
connect(treeWidget, &QTreeWidget::currentItemChanged, this, &AMMtasksWidget::on_treeWidget_currentItemChanged);
preparationTreeWidget();
Q_EMIT signal_currentItemChanged();
threadPreparation = new QThread();
taskTreePreparation = new TaskTreePreparation();
taskTreePreparation->moveToThread(threadPreparation);
threadPreparation->start();
threadPreparation->setPriority(QThread::HighestPriority);
connect(this, &AMMtasksWidget::signal_prepareListItems, taskTreePreparation, &TaskTreePreparation::slot_prepareListItems);
connect(this, &AMMtasksWidget::signal_prepareListItemsForTrainee, taskTreePreparation, &TaskTreePreparation::slot_prepareListItemsForTrainee);
connect(taskTreePreparation, &TaskTreePreparation::signal_listItemsReady, this, &AMMtasksWidget::slot_listItemsReady);
threadAnimation = new QThread();
waitAnimationWidget = new WaitAnimationWidget;
QMovie *movie = new QMovie(":/resources/icons/762.gif");
waitAnimationWidget->setParent(this);
waitAnimationWidget->initialize(movie,this);
waitAnimationWidget->moveToThread(threadAnimation);
threadAnimation->start();
ui->btnDelete->setObjectName("btnDelete");
ui->btnDelete->setEnabled(false);
if(type == TypeList::listCommon)
ui->btnDelete->setVisible(false);
else
ui->btnOnlyActive->setVisible(false);
}
AMMtasksWidget::~AMMtasksWidget()
{
waitAnimationWidget->hideWithStop();
taskTreePreparation->stopParser();
threadAnimation->quit();
threadAnimation->wait();
threadPreparation->quit();
threadPreparation->wait();
delete threadAnimation;
delete threadPreparation;
delete taskTreePreparation;
delete waitAnimationWidget;
delete treeWidget;
delete ui;
}
void AMMtasksWidget::resizeEvent(QResizeEvent *event)
{
QSize size = event->size();
waitAnimationWidget->resize(size);
int width = treeWidget->width();
treeWidget->setColumnWidth(ColumnsTree::clmn_ID, 50);
treeWidget->setColumnWidth(ColumnsTree::clmn_code, 250);
int widthPMorDM;
if(type == TypeList::listCommon)
widthPMorDM = width - (250 + 10);
else
widthPMorDM = width - (300 + 10);
treeWidget->setColumnWidth(ColumnsTree::clmn_PMorDM, widthPMorDM);
}
void AMMtasksWidget::changeEvent(QEvent *event)
{
// В случае получения события изменения языка приложения
if (event->type() == QEvent::LanguageChange)
{// переведём окно заново
ui->retranslateUi(this);
reSetHeadTreeWidget();
//slot_NeedUpdateUI();
}
}
void AMMtasksWidget::on_treeWidget_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous)
{
if(current == nullptr)
{
ui->btnDelete->setEnabled(false);
return;
}
else
{
ui->btnDelete->setEnabled(true);
}
int id = current->text(ColumnsTree::clmn_ID).toInt();
Module* module = searchModuleByID(id);
if(module)
{
QString type = "Code";
QString code = "";
if(module->getType() == ModuleType::TYPE_PM)
{
PM* PMmodul = static_cast<PM*>(module);
type = "PM";
code = PMmodul->pmCode();
accessAssignTask = false;
}
else
{
DM* DMmodul = static_cast<DM*>(module);
type = "DM";
code = DMmodul->dmCode();
accessAssignTask = true;
}
}
else
{
accessAssignTask = false;
}
Q_EMIT signal_currentItemChanged();
}
void AMMtasksWidget::slot_NeedUpdateUI()
{
qDebug() << "AMMtasksWidget::slot_NeedUpdateUI thread ID " << QThread::currentThreadId();
loadTasksAMM();
}
void AMMtasksWidget::slot_traineeSelected(QString login)
{
qDebug() << "AMMtasksWidget::slot_traineeSelected thread ID " << QThread::currentThreadId();
loginTraineeSelected = login;
idTraineeSelected = connectorToServer->getIdTraineeByLogin(loginTraineeSelected);
if(type == TypeList::listForTrainee)
{
waitAnimationWidget->showWithPlay();
connectorToServer->sendQueryToDB(TypeQueryToDB::TYPE_QUERY_GET_TASKS_AMM_FOR_TRAINEE, idTraineeSelected);
}
}
void AMMtasksWidget::slot_UpdateTasksAMMforTrainee(int trainee_id)
{
if(type == TypeList::listForTrainee)
{
if(idTraineeSelected == trainee_id)
{
//waitAnimationWidget->showWithPlay();
QList<TaskAmmFim> listTask = connectorToServer->getListTasksAMMforTrainee(trainee_id);
signal_prepareListItemsForTrainee(listTask, &listAllModules);
}
}
}
void AMMtasksWidget::loadTasksAMM(bool flRequestFromDB)
{
//Обновление дерева
treeWidget->clear();
waitAnimationWidget->showWithPlay();
if(flRequestFromDB)
/*QByteArray array*/arrayAMM = connectorToServer->getListTaskAmmArray();
emit signal_prepareListItems(/*array*/arrayAMM, &listAllModules, flOnlyActive);
}
void AMMtasksWidget::slot_listItemsReady(QList<QTreeWidgetItem *> listItems)
{
//Обновление дерева
treeWidget->clear();
for(QTreeWidgetItem * item : listItems)
treeWidget->addTopLevelItem(item);
QTreeWidgetItem * item = treeWidget->topLevelItem(0);
if(item != nullptr)
treeWidget->setCurrentItem(item);
waitAnimationWidget->hideWithStop();
}
Module *AMMtasksWidget::searchModuleByID(int id)
{
Module* ptrModule = nullptr;
for(Module* module: listAllModules)
{
ptrModule = module->getModuleByID(id);
if(ptrModule)
return ptrModule;
}
return nullptr;
}
void AMMtasksWidget::preparationTreeWidget()
{
treeWidget->setColumnCount(clmn_count);
reSetHeadTreeWidget();
if(type == TypeList::listCommon)
treeWidget->setColumnHidden(ColumnsTree::clmn_ID, true);
}
void AMMtasksWidget::reSetHeadTreeWidget()
{
QStringList listHeaders;
if(type == TypeList::listForTrainee)
listHeaders = QStringList{tr("Task AMM"), tr("DM code"), tr("ID")};
else
listHeaders = QStringList{tr("PM/DM"), tr("Code"), tr("ID")};
treeWidget->setHeaderLabels(listHeaders);
}
void AMMtasksWidget::on_btnUpdateTasks_clicked()
{
connectorToServer->sendQueryTasksXML("amm");
}
void AMMtasksWidget::slot_AssignTaskAMMtoTrainee()
{
QTreeWidgetItem *current = treeWidget->currentItem();
if(current == nullptr)
return;
int id = current->text(ColumnsTree::clmn_ID).toInt();
Module* module = searchModuleByID(id);
if(module)
{
if(module->getType() == ModuleType::TYPE_DM)
{
QString dmCode = "";
QString techName = "";
DM* DMmodul = static_cast<DM*>(module);
dmCode = DMmodul->dmCode();
techName = DMmodul->getLangStructRus().techName;
int trainee_id = connectorToServer->getIdTraineeByLogin(loginTraineeSelected);
TaskAmmFim taskNew;
taskNew.ammProcedure.title = techName;
taskNew.ammProcedure.dmCode = dmCode;
connectorToServer->sendQueryToDB(TypeQueryToDB::TYPE_QUERY_ASSIGN_TASK_AMM_TO_TRAINEE, trainee_id, &taskNew);
}
}
}
void AMMtasksWidget::on_btnDelete_clicked()
{
QTreeWidgetItem *treeItemCurrent = treeWidget->currentItem();
if(treeItemCurrent != nullptr)
{
QTreeWidgetItem *treeItemParent = treeItemCurrent->parent();
if(treeItemParent == nullptr)
{//Выбрана задача
int id = treeItemCurrent->text(ColumnsTree::clmn_ID).toInt();
if(QMessageBox::warning(this, tr("Attention!"), tr("The deletion will be irrevocable.\nDelete it anyway?"), QMessageBox::Ok, QMessageBox::Cancel) == QMessageBox::Ok)
{
connectorToServer->sendQueryToDB(TypeQueryToDB::TYPE_QUERY_DEL_TASK_AMM_TO_TRAINEE, id);
}
}
}
}
void AMMtasksWidget::on_btnOnlyActive_clicked()
{
if(ui->btnOnlyActive->isChecked())
{
flOnlyActive = true;
//slot_listItemsReady(listItemsALL, listItemsACTIVE);
}
else
{
flOnlyActive = false;
//slot_listItemsReady(listItemsALL, listItemsACTIVE);
}
//connectorToServer->sendQueryTasksXML("amm");
loadTasksAMM(false);
}

View File

@@ -0,0 +1,103 @@
#ifndef AMMTASKSWIDGET_H
#define AMMTASKSWIDGET_H
#include <QWidget>
#include <QTreeWidget>
#include <QDomNode>
#include "module.h"
#include "connectortoserver.h"
#include "taskTreePreparation.h"
namespace Ui {
class AMMtasksWidget;
}
class AMMtasksWidget : public QWidget
{
Q_OBJECT
public:
enum TypeList
{
listCommon = 0,
listForTrainee
};
public:
explicit AMMtasksWidget(ConnectorToServer* connectorToServer, TypeList type, QWidget *parent = nullptr);
~AMMtasksWidget();
public:
void deactivate()
{
accessAssignTask = false;
//taskTreePreparation->stopParser();
treeWidget->clear();
loginTraineeSelected = "";
idTraineeSelected = 0;
}
bool getAccessAssignTask(){return accessAssignTask;}
public:
void resizeEvent(QResizeEvent *event) override;
protected:
void changeEvent(QEvent * event) override;
private Q_SLOTS:
void on_treeWidget_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous);
void on_btnUpdateTasks_clicked();
void on_btnDelete_clicked();
void on_btnOnlyActive_clicked();
public Q_SLOTS:
void slot_AssignTaskAMMtoTrainee();
public Q_SLOTS:
//Слот обработки сигнала необходимости обновления интерфейса
void slot_NeedUpdateUI();
//слот обработки сигнала о выборе обучаемого
void slot_traineeSelected(QString login);
void slot_UpdateTasksAMMforTrainee(int trainee_id);
Q_SIGNALS:
void signal_currentItemChanged();
private:
Module* searchModuleByID(int id);
void preparationTreeWidget();
void reSetHeadTreeWidget();
void loadTasksAMM(bool flRequestFromDB = true);
Q_SIGNALS:
void signal_prepareListItems(QByteArray array, QList<Module*>* listAllModules, bool flOnlyActive);
void signal_prepareListItemsForTrainee(QList<TaskAmmFim> listTask, QList<Module*>* listAllModules);
public Q_SLOTS:
void slot_listItemsReady(QList<QTreeWidgetItem*> listItems);
private:
Ui::AMMtasksWidget *ui;
ConnectorToServer* connectorToServer;
QTreeWidget* treeWidget;
TypeList type;
private:
QList<Module*> listAllModules;
QString loginTraineeSelected;
int idTraineeSelected;
QThread* threadPreparation;
QThread* threadAnimation;
TaskTreePreparation* taskTreePreparation;
WaitAnimationWidget *waitAnimationWidget;
bool accessAssignTask;
bool flOnlyActive;
QByteArray arrayAMM;
};
#endif // AMMTASKSWIDGET_H

View File

@@ -0,0 +1,108 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>AMMtasksWidget</class>
<widget class="QWidget" name="AMMtasksWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="font">
<font>
<family>Tahoma</family>
<pointsize>10</pointsize>
</font>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_1"/>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QToolButton" name="btnOnlyActive">
<property name="minimumSize">
<size>
<width>50</width>
<height>50</height>
</size>
</property>
<property name="text">
<string>Active</string>
</property>
<property name="icon">
<iconset resource="../resources.qrc">
<normaloff>:/resources/icons/filter.png</normaloff>:/resources/icons/filter.png</iconset>
</property>
<property name="iconSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonTextUnderIcon</enum>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="btnDelete">
<property name="minimumSize">
<size>
<width>50</width>
<height>50</height>
</size>
</property>
<property name="text">
<string>Delete</string>
</property>
<property name="icon">
<iconset resource="../resources.qrc">
<normaloff>:/resources/icons/delete.png</normaloff>:/resources/icons/delete.png</iconset>
</property>
<property name="iconSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonTextUnderIcon</enum>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
<resources>
<include location="../resources.qrc"/>
</resources>
<connections/>
</ui>

View File

@@ -0,0 +1,446 @@
#include <QDomDocument>
#include <QFile>
#include <QMessageBox>
#include <QTreeWidget>
#include <QThread>
#include <QResizeEvent>
#include "fimtaskswidget.h"
#include "ui_fimtaskswidget.h"
#include "tasksAmmFim.h"
FIMtasksWidget::FIMtasksWidget(ConnectorToServer* connectorToServer, TypeList type, QWidget *parent) :
QWidget(parent),
ui(new Ui::FIMtasksWidget),
connectorToServer(connectorToServer),
treeWidget(nullptr),
type(type),
userName(""),
loginTraineeSelected(""),
idTraineeSelected(0),
threadAnimation(nullptr),
waitAnimationWidget(nullptr),
accessAssignTask(false)
{
ui->setupUi(this);
qDebug() << "FIMtasksWidget init thread ID " << QThread::currentThreadId();
treeWidget = new QTreeWidget();
ui->horizontalLayout_1->addWidget(treeWidget);
connect(treeWidget, &QTreeWidget::currentItemChanged, this, &FIMtasksWidget::on_treeWidget_currentItemChanged);
preparationTreeWidget();
Q_EMIT signal_currentItemChanged();
threadAnimation = new QThread();
waitAnimationWidget = new WaitAnimationWidget;
QMovie *movie = new QMovie(":/resources/icons/762.gif");
waitAnimationWidget->setParent(this);
waitAnimationWidget->initialize(movie,this);
waitAnimationWidget->moveToThread(threadAnimation);
threadAnimation->start();
ui->btnDelete->setObjectName("btnDelete");
ui->btnDelete->setEnabled(false);
if(type == TypeList::listCommon)
ui->btnDelete->setVisible(false);
}
FIMtasksWidget::~FIMtasksWidget()
{
deleteAllTaskAmmFim();
waitAnimationWidget->hideWithStop();
threadAnimation->quit();
threadAnimation->wait();
delete threadAnimation;
delete waitAnimationWidget;
delete treeWidget;
delete ui;
}
void FIMtasksWidget::resizeEvent(QResizeEvent *event)
{
QSize size = event->size();
waitAnimationWidget->resize(size);
int width = treeWidget->width();
treeWidget->setColumnWidth(ColumnsTree::clmn_ID, 50);
int widthTitle;
if(type == TypeList::listCommon)
widthTitle = width - (0 + 10);
else
widthTitle = width - (50 + 10);
treeWidget->setColumnWidth(ColumnsTree::clmn_Title, widthTitle);
}
void FIMtasksWidget::changeEvent(QEvent *event)
{
// В случае получения события изменения языка приложения
if (event->type() == QEvent::LanguageChange)
{// переведём окно заново
ui->retranslateUi(this);
reSetHeadTreeWidget();
//slot_NeedUpdateUI();
}
}
void FIMtasksWidget::on_treeWidget_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous)
{
if(current == nullptr)
{
ui->btnDelete->setEnabled(false);
return;
}
QString code = "";
QTreeWidgetItem *treeItemParent = current->parent();
if(treeItemParent == nullptr)
{//Выбрана задача
int id = current->text(ColumnsTree::clmn_ID).toInt();
TaskAmmFim* task = getTaskByID(id);
code = task->title;
accessAssignTask = true;
ui->btnDelete->setEnabled(true);
}
else
{
accessAssignTask = false;
ui->btnDelete->setEnabled(false);
}
Q_EMIT signal_currentItemChanged();
}
void FIMtasksWidget::loadFIMtasksFromXML(QByteArray array)
{
deleteAllTaskAmmFim();
QDomDocument docTasksDOM;
docTasksDOM.setContent(array);
QDomElement RRJTasksElement = docTasksDOM.firstChildElement("RRJTasks");
if(RRJTasksElement.isNull())
return;
QDomElement taskElement = RRJTasksElement.firstChildElement();
if(taskElement.isNull())
return;
do
{/*task*/
QString name = taskElement.nodeName();
QDomNamedNodeMap nodeMap = taskElement.attributes();
if(name == "task")
{
TaskAmmFim* task = nullptr;
task = new TaskAmmFim();
task->initialize(/*nodeMap.namedItem("id").nodeValue().toInt()*/ TaskAmmFim::lastID++,
nodeMap.namedItem("type").nodeValue(),
nodeMap.namedItem("title").nodeValue(),
nodeMap.namedItem("status").nodeValue(),
nodeMap.namedItem("created").nodeValue(),
nodeMap.namedItem("changed").nodeValue());
QDomElement malfunctionElement = taskElement.firstChildElement();
if(!malfunctionElement.isNull())
{
do
{/*malfunction*/
QString name = malfunctionElement.nodeName();
QDomNamedNodeMap nodeMap = malfunctionElement.attributes();
if(name == "malfunction")
{
Malfunction malfunction;
malfunction.initialize(nodeMap.namedItem("dmCode").nodeValue(),
nodeMap.namedItem("num").nodeValue(),
nodeMap.namedItem("description").nodeValue());
QDomElement signElement = malfunctionElement.firstChildElement();
if(!signElement.isNull())
{
do
{/*malfunctionSign*/
QString name = signElement.nodeName();
QDomNamedNodeMap nodeMap = signElement.attributes();
if(name == "malfunctionSign")
{
MalfunctionSign sign;
sign.initialize(nodeMap.namedItem("type").nodeValue().toInt(),
nodeMap.namedItem("description").nodeValue());
malfunction.addMalfunctionSign(sign);
}
}while(! (signElement = signElement.nextSiblingElement()).isNull());
}
task->addMalfunction(malfunction);
}
}while(! (malfunctionElement = malfunctionElement.nextSiblingElement()).isNull());
}
listTaskAmmFim.append(task);
}
}while (! (taskElement = taskElement.nextSiblingElement()).isNull());
return;
}
void FIMtasksWidget::fillTree()
{
//Обновление дерева
treeWidget->clear();
for(int i = 0; i < listTaskAmmFim.count(); i++)
{/*Задачи*/
TaskAmmFim* task = listTaskAmmFim.at(i);
QTreeWidgetItem* itemTask = new QTreeWidgetItem();
itemTask->setText(0, task->title);
itemTask->setText(1, QString::number(task->id));
//itemTask->setFlags(itemTask->flags() | Qt::ItemIsUserCheckable);
//itemTask->setCheckState(0, Qt::Checked);
itemTask->setIcon(0, QIcon(":/resources/icons/procedure.png"));
itemTask->setToolTip(0, task->title);
treeWidget->addTopLevelItem(itemTask);
for (int j = 0; j < task->malfunctionList.count(); j++)
{/*Неисправности*/
Malfunction malfunction = task->malfunctionList.at(j);
QTreeWidgetItem* itemMalfunction = new QTreeWidgetItem();
itemMalfunction->setText(0, malfunction.description);
if(type == TypeList::listCommon)
{
itemMalfunction->setFlags(itemMalfunction->flags() | Qt::ItemIsUserCheckable);
itemMalfunction->setCheckState(0, Qt::Checked);
}
itemMalfunction->setIcon(0, QIcon(":/resources/icons/malfunction.png"));
itemMalfunction->setToolTip(0, malfunction.description);
itemTask->addChild(itemMalfunction);
for (int k = 0; k < malfunction.malfunctionSigns.count(); k++)
{/*Сигнализация*/
MalfunctionSign sign = malfunction.malfunctionSigns.at(k);
QTreeWidgetItem* itemSign = new QTreeWidgetItem();
itemSign->setText(0, sign.description);
//itemSign->setFlags(itemSign->flags() | Qt::ItemIsUserCheckable);
//itemSign->setCheckState(0, Qt::Checked);
itemSign->setIcon(0, QIcon(":/resources/icons/sign.png"));
itemSign->setToolTip(0, sign.description);
itemMalfunction->addChild(itemSign);
}
}
}
QTreeWidgetItem * item = treeWidget->topLevelItem(0);
if(item != nullptr)
treeWidget->setCurrentItem(item);
}
void FIMtasksWidget::prepareListTasksForTrainee(QList<TaskAmmFim> listTask)
{
deleteAllTaskAmmFim();
for(TaskAmmFim task : listTask)
{
TaskAmmFim* newTask = new TaskAmmFim();
*newTask = task;
listTaskAmmFim.append(newTask);
}
}
void FIMtasksWidget::preparationTreeWidget()
{
treeWidget->setColumnCount(clmn_count);
reSetHeadTreeWidget();
if(type == TypeList::listCommon)
treeWidget->setColumnHidden(ColumnsTree::clmn_ID, true);
}
void FIMtasksWidget::reSetHeadTreeWidget()
{
QStringList listHeaders;
if(type == TypeList::listForTrainee)
listHeaders = QStringList{tr("Task FIM"), tr("ID")};
else
listHeaders = QStringList{tr("Title"), tr("ID")};
treeWidget->setHeaderLabels(listHeaders);
}
void FIMtasksWidget::slot_NeedUpdateUI()
{
qDebug() << "FIMtasksWidget::slot_NeedUpdateUI thread ID " << QThread::currentThreadId();
loadTasksFIM();
}
void FIMtasksWidget::slot_traineeSelected(QString login)
{
qDebug() << "FIMtasksWidget::slot_traineeSelected thread ID " << QThread::currentThreadId();
loginTraineeSelected = login;
idTraineeSelected = connectorToServer->getIdTraineeByLogin(loginTraineeSelected);
if(type == TypeList::listForTrainee)
{
waitAnimationWidget->showWithPlay();
connectorToServer->sendQueryToDB(TypeQueryToDB::TYPE_QUERY_GET_TASKS_FIM_FOR_TRAINEE, idTraineeSelected);
}
}
void FIMtasksWidget::slot_UpdateTasksFIMforTrainee(int trainee_id)
{
if(type == TypeList::listForTrainee)
{
if(idTraineeSelected == trainee_id)
{
QList<TaskAmmFim> listTask = connectorToServer->getListTasksFIMforTrainee(trainee_id);
prepareListTasksForTrainee(listTask);
fillTree();
waitAnimationWidget->hideWithStop();
}
}
}
void FIMtasksWidget::loadTasksFIM()
{
//Обновление дерева
treeWidget->clear();
waitAnimationWidget->showWithPlay();
QByteArray array = connectorToServer->getListTaskFimArray();
loadFIMtasksFromXML(array);
//собственно обновление дерева
fillTree();
waitAnimationWidget->hideWithStop();
}
TaskAmmFim* FIMtasksWidget::getTaskByID(int id)
{
for(int i = 0; i < listTaskAmmFim.count(); i++)
{/*Задачи*/
TaskAmmFim* task = listTaskAmmFim.at(i);
if(task->id == id)
return task;
}
return nullptr;
}
void FIMtasksWidget::deleteAllTaskAmmFim()
{
for(TaskAmmFim* task: listTaskAmmFim)
delete task;
listTaskAmmFim.clear();
}
void FIMtasksWidget::updateTaskItem(QTreeWidgetItem *itemTask)
{
for (int i = 0; i < itemTask->childCount(); i++)
{
QTreeWidgetItem* itemMalfunction = itemTask->child(i);
itemMalfunction->setCheckState(0, Qt::Checked);
itemMalfunction->setExpanded(false);
}
itemTask->setExpanded(false);
}
void FIMtasksWidget::on_btnUpdateTasks_clicked()
{
connectorToServer->sendQueryTasksXML("fim");
}
void FIMtasksWidget::slot_AssignTaskFIMtoTrainee()
{
QTreeWidgetItem *current = treeWidget->currentItem();
if(current == nullptr)
return;
int id = current->text(ColumnsTree::clmn_ID).toInt();
TaskAmmFim* task = getTaskByID(id);
if(task)
{
int trainee_id = connectorToServer->getIdTraineeByLogin(loginTraineeSelected);
TaskAmmFim taskNew;
taskNew.title = task->title;
//Назначенные неисправности
for (int i = 0; i < current->childCount(); i++)
{
QTreeWidgetItem* itemMalfunction = current->child(i);
if(itemMalfunction->checkState(0) == Qt::Checked)
{
Malfunction malfunction = task->malfunctionList.at(i);
taskNew.malfunctionList.append(malfunction);
}
}
connectorToServer->sendQueryToDB(TypeQueryToDB::TYPE_QUERY_ASSIGN_TASK_FIM_TO_TRAINEE, trainee_id, &taskNew);
updateTaskItem(current);
}
}
void FIMtasksWidget::on_btnDelete_clicked()
{
QTreeWidgetItem *treeItemCurrent = treeWidget->currentItem();
if(treeItemCurrent != nullptr)
{
QTreeWidgetItem *treeItemParent = treeItemCurrent->parent();
if(treeItemParent == nullptr)
{//Выбрана задача
int id = treeItemCurrent->text(ColumnsTree::clmn_ID).toInt();
if(QMessageBox::warning(this, tr("Attention!"), tr("The deletion will be irrevocable.\nDelete it anyway?"), QMessageBox::Ok, QMessageBox::Cancel) == QMessageBox::Ok)
{
connectorToServer->sendQueryToDB(TypeQueryToDB::TYPE_QUERY_DEL_TASK_FIM_TO_TRAINEE, id);
}
}
}
}

View File

@@ -0,0 +1,104 @@
#ifndef FIMTASKSWIDGET_H
#define FIMTASKSWIDGET_H
#include <QWidget>
#include <QTreeWidget>
#include "tasksAmmFim.h"
#include "connectortoserver.h"
namespace Ui {
class FIMtasksWidget;
}
class FIMtasksWidget : public QWidget
{
Q_OBJECT
public:
enum TypeList
{
listCommon = 0,
listForTrainee
};
private:
enum ColumnsTree{
clmn_Title = 0,
clmn_ID,
clmn_count
};
public:
explicit FIMtasksWidget(ConnectorToServer* connectorToServer, TypeList type, QWidget *parent = nullptr);
~FIMtasksWidget();
public:
void deactivate()
{
accessAssignTask = false;
treeWidget->clear();
loginTraineeSelected = "";
idTraineeSelected = 0;
}
bool getAccessAssignTask(){return accessAssignTask;}
public:
void resizeEvent(QResizeEvent *event) override;
protected:
void changeEvent(QEvent * event) override;
private Q_SLOTS:
void on_treeWidget_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous);
void on_btnUpdateTasks_clicked();
void on_btnDelete_clicked();
public Q_SLOTS:
void slot_AssignTaskFIMtoTrainee();
public Q_SLOTS:
//Слот обработки сигнала необходимости обновления интерфейса
void slot_NeedUpdateUI();
//слот обработки сигнала о выборе обучаемого
void slot_traineeSelected(QString login);
void slot_UpdateTasksFIMforTrainee(int trainee_id);
Q_SIGNALS:
void signal_currentItemChanged();
private:
TaskAmmFim* getTaskByID(int id);
void loadFIMtasksFromXML(QByteArray array);
void fillTree();
void prepareListTasksForTrainee(QList<TaskAmmFim> listTask);
void preparationTreeWidget();
void reSetHeadTreeWidget();
void loadTasksFIM();
void deleteAllTaskAmmFim();
void updateTaskItem(QTreeWidgetItem *itemTask);
private:
Ui::FIMtasksWidget *ui;
ConnectorToServer* connectorToServer;
QTreeWidget* treeWidget;
TypeList type;
private:
QString userName;
QList<TaskAmmFim*> listTaskAmmFim;
QString loginTraineeSelected;
int idTraineeSelected;
QThread* threadAnimation;
WaitAnimationWidget *waitAnimationWidget;
bool accessAssignTask;
};
#endif // FIMTASKSWIDGET_H

View File

@@ -0,0 +1,67 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>FIMtasksWidget</class>
<widget class="QWidget" name="FIMtasksWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>472</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_1"/>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QToolButton" name="btnDelete">
<property name="text">
<string>Delete</string>
</property>
<property name="icon">
<iconset resource="../resources.qrc">
<normaloff>:/resources/icons/delete.png</normaloff>:/resources/icons/delete.png</iconset>
</property>
<property name="iconSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonTextUnderIcon</enum>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
<resources>
<include location="../resources.qrc"/>
</resources>
<connections/>
</ui>

View File

@@ -5,7 +5,8 @@ int Module::lastID = 0;
Module::Module():
type (ModuleType::TYPE_PM),
parentModule(nullptr),
ID(0)
ID(0),
isActive(false)
{
ID = ++lastID;
}
@@ -38,6 +39,19 @@ Module *Module::getModuleByID(int id)
return nullptr;
}
void Module::setIsActiveTrue()
{
this->isActive = true;
if(parentModule)
parentModule->setIsActiveTrue();
}
bool Module::getIsActive()
{
return this->isActive;
}
PM::PM():

View File

@@ -20,11 +20,15 @@ public:
void setParentModule(Module* parentModule){ this->parentModule = parentModule; };
Module* getModuleByID(int id);
void setIsActiveTrue();
bool getIsActive();
protected:
ModuleType type;
Module* parentModule;
int ID;
static int lastID;
bool isActive;
};
@@ -47,7 +51,7 @@ public:
void setLangStructEng(QString title);
void addChildModule(Module* childModule);
QList<Module*> getListChildModules();
QString pmCode();
QString pmCode();
private:
QString modelIdentCode;
@@ -106,7 +110,7 @@ private:
QString disassyCodeVariant;
QString infoCode;
QString infoCodeVariant;
QString itemLocationCode;
QString itemLocationCode;
dmLangStruct langRus;
dmLangStruct langEng;

View File

@@ -1,48 +0,0 @@
#include "taskswidget.h"
#include "ui_taskswidget.h"
TasksWidget::TasksWidget(InterfaceDataBaseLMS* dbLMS, QWidget *parent) :
QWidget(parent),
ui(new Ui::TasksWidget),
dbLMS(dbLMS)
{
ui->setupUi(this);
}
TasksWidget::~TasksWidget()
{
delete ui;
}
void TasksWidget::slot_traineeSelected(QString login)
{
viewListTasksForTrainee(login);
}
void TasksWidget::slot_LanguageChanged(QString language)
{
qtLanguageTranslator.load(QString(QStringLiteral("translations/InstructorsAndTrainees_")) + language, QStringLiteral("."));
qApp->installTranslator(&qtLanguageTranslator);
}
void TasksWidget::viewListTasksForTrainee(QString login)
{
/*
QList<QString> listTasks;
QStringList strListTasks;
listTasks = pDbTrainees->getTasks(login);
ui->listWidgetTasks->clear();
ui->listWidgetTasks->addItems(listTasks);
*/
}
void TasksWidget::changeEvent(QEvent *event)
{
// В случае получения события изменения языка приложения
if (event->type() == QEvent::LanguageChange)
{
ui->retranslateUi(this); // переведём окно заново
}
}

View File

@@ -1,41 +0,0 @@
#ifndef TASKSWIDGET_H
#define TASKSWIDGET_H
#include <QWidget>
#include <QTranslator>
#include "instructorsAndTrainees_global.h"
#include "interfacedatabaselms.h"
namespace Ui {
class TasksWidget;
}
class INSTRUCTORSANDTRAINEES_EXPORT TasksWidget : public QWidget
{
Q_OBJECT
public:
explicit TasksWidget(InterfaceDataBaseLMS* dbLMS, QWidget *parent = nullptr);
~TasksWidget();
protected:
// Метод получения событий
// В нём будет производиться проверка события смены перевода приложения
void changeEvent(QEvent * event) override;
public Q_SLOTS:
//слот обработки сигнала о выборе обучаемого
void slot_traineeSelected(QString login);
void slot_LanguageChanged(QString language);
private:
void viewListTasksForTrainee(QString login);
private:
Ui::TasksWidget *ui;
QTranslator qtLanguageTranslator;
InterfaceDataBaseLMS* dbLMS;
};
#endif // TASKSWIDGET_H

View File

@@ -1,41 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>TasksWidget</class>
<widget class="QWidget" name="TasksWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>Task manager</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Task manager</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QListWidget" name="listWidgetTasks"/>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -1,71 +1,112 @@
#include <QFile>
#include <QXmlStreamReader>
#include <QDomDocument>
#include <QMessageBox>
#include "doctaskswidget.h"
#include "ui_doctaskswidget.h"
#include <QThread>
#include <QDebug>
#include "tasktreepreparation.h"
DocTasksWidget::DocTasksWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::DocTasksWidget)
TaskTreePreparation::TaskTreePreparation(QObject *parent) :
QObject(parent),
listAllModules(nullptr),
flagStop(false)
{
ui->setupUi(this);
treeWidget = new QTreeWidget();
connect(treeWidget, &QTreeWidget::currentItemChanged, this, &DocTasksWidget::on_treeWidget_currentItemChanged);
ui->horizontalLayout_2->addWidget(treeWidget);
preparationTreeWidget();
loadDocTasksFromXML();
updateTreeWidget();
qDebug() << "TaskTreePreparation init thread ID " << QThread::currentThreadId();
}
DocTasksWidget::~DocTasksWidget()
TaskTreePreparation::~TaskTreePreparation()
{
deleteAllModuls();
delete ui;
}
void DocTasksWidget::on_treeWidget_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous)
void TaskTreePreparation::stopParser()
{
if(current == nullptr)
flagStop = true;
}
QTreeWidgetItem *TaskTreePreparation::addModuleToTreeWidget(Module *module, bool flOnlyActive, QTreeWidgetItem *parentItem)
{
QTreeWidgetItem* itemModule = nullptr;
if(flagStop)
return itemModule;
if(flOnlyActive && !module->getIsActive())
return nullptr;
QString text = "";
QString ID = QString::number(module->getID());
QString code = "";
itemModule = new QTreeWidgetItem();
if(parentItem)
parentItem->addChild(itemModule);
if(module->getType() == ModuleType::TYPE_PM)
{
PM* PMmodul = static_cast<PM*>(module);
text = PMmodul->getLangStructRus().title;
code = PMmodul->pmCode();
for(Module* module : PMmodul->getListChildModules())
{
addModuleToTreeWidget(module, flOnlyActive, itemModule);
}
}
else
{
DM* DMmodul = static_cast<DM*>(module);
text = DMmodul->getLangStructRus().techName;
code = DMmodul->dmCode();
//itemModule->setFlags(itemModule->flags() | Qt::ItemIsUserCheckable);
//itemModule->setCheckState(0, Qt::Checked);
itemModule->setIcon(0, QIcon(":/resources/icons/procedure.png"));
}
itemModule->setText(ColumnsTree::clmn_PMorDM, text);
itemModule->setText(ColumnsTree::clmn_code, code);
itemModule->setText(ColumnsTree::clmn_ID, ID);
itemModule->setToolTip(0, text);
return itemModule;
}
void TaskTreePreparation::loadAMMtasksFromXML(QByteArray array)
{
deleteAllModuls();
QDomDocument docTasksDOM;
docTasksDOM.setContent(array);
QDomElement manifestElement = docTasksDOM.firstChildElement("manifest");
if(manifestElement.isNull())
return;
int id = current->text(ColumnsTree::clmn_ID).toInt();
Module* module = searchModuleByID(id);
if(module)
{
QString type = "Code";
QString code = "";
if(module->getType() == ModuleType::TYPE_PM)
{
PM* PMmodul = static_cast<PM*>(module);
type = "PM";
code = PMmodul->pmCode();
}
else
{
DM* DMmodul = static_cast<DM*>(module);
type = "DM";
code = DMmodul->dmCode();
}
ui->label->setText(type + " Code");
ui->editCode->setText(code);
}
domElementParser(manifestElement, nullptr);
}
void DocTasksWidget::domElementParser(QDomElement element, Module* moduleParent)
void TaskTreePreparation::deleteAllModuls()
{
if(listAllModules == nullptr)
return;
for(Module* module: *listAllModules)
{
if(module->getType() == ModuleType::TYPE_PM)
delete static_cast<PM*>(module);
else
delete static_cast<DM*>(module);
}
listAllModules->clear();
}
void TaskTreePreparation::domElementParser(QDomElement element, Module* moduleParent)
{
QString name;
if(flagStop)
return;
QDomElement childElement = element.firstChildElement();
if(childElement.isNull())
return;
@@ -120,6 +161,10 @@ void DocTasksWidget::domElementParser(QDomElement element, Module* moduleParent)
PM* PMmodulParent = static_cast<PM*>(moduleParent);
PMmodulParent->addChildModule(module);
}
//Активность
if(nodeMap.namedItem("active").nodeValue() == "true")
module->setIsActiveTrue();
}
else if(name == "rus" || name == "eng")
{
@@ -158,129 +203,58 @@ void DocTasksWidget::domElementParser(QDomElement element, Module* moduleParent)
domElementParser(childElement, module);
if(moduleParent == nullptr)
listAllModules.append(module);
listAllModules->append(module);
}while (! (childElement = childElement.nextSiblingElement()).isNull());
}
void DocTasksWidget::loadDocTasksFromXML()
void TaskTreePreparation::slot_prepareListItems(QByteArray array, QList<Module*>* listAllModules, bool flOnlyActive)
{
QDomDocument docTasksDOM;
QString xmlFileName = "./docs.xml";
QFile xmlInFile(xmlFileName);
if (!xmlInFile.open(QFile::ReadOnly | QFile::Text))
qDebug() << "TaskTreePreparation::slot_prepareListItems thread ID " << QThread::currentThreadId();
this->listAllModules = listAllModules;
loadAMMtasksFromXML(array);
listItems.clear();
for(Module* module : *this->listAllModules)
{
QMessageBox::critical(nullptr, tr("Attention!"), tr("The file could not be opened ") + xmlFileName);
return;
QTreeWidgetItem* item = addModuleToTreeWidget(module, flOnlyActive);
listItems.append(item);
}
else
{
docTasksDOM.setContent(xmlInFile.readAll());
xmlInFile.close();
QDomElement manifestElement = docTasksDOM.firstChildElement("manifest");
if(manifestElement.isNull())
return;
//deleteAllModuls();
domElementParser(manifestElement, nullptr);
}
Q_EMIT signal_listItemsReady(listItems);
}
void DocTasksWidget::deleteAllModuls()
void TaskTreePreparation::slot_prepareListItemsForTrainee(QList<TaskAmmFim> listTask, QList<Module *> *listAllModules)
{
for(Module* module: listAllModules)
qDebug() << "TaskTreePreparation::slot_prepareListItemsForTrainee thread ID " << QThread::currentThreadId();
this->listAllModules = listAllModules;
//loadAMMtasksFromList(listTask);
listItems.clear();
for(TaskAmmFim task : listTask)
{
if(module->getType() == ModuleType::TYPE_PM)
delete static_cast<PM*>(module);
else
delete static_cast<DM*>(module);
QTreeWidgetItem* item = nullptr;
QString text = task.ammProcedure.title;
QString ID = QString::number(task.getID());
QString code = task.ammProcedure.dmCode;
item = new QTreeWidgetItem();
item->setIcon(0, QIcon(":/resources/icons/procedure.png"));
item->setText(ColumnsTree::clmn_PMorDM, text);
item->setText(ColumnsTree::clmn_code, code);
item->setText(ColumnsTree::clmn_ID, ID);
item->setToolTip(0, text);
listItems.append(item);
}
listAllModules.clear();
Q_EMIT signal_listItemsReady(listItems);
}
Module *DocTasksWidget::searchModuleByID(int id)
{
Module* ptrModule = nullptr;
for(Module* module: listAllModules)
{
ptrModule = module->getModuleByID(id);
if(ptrModule)
return ptrModule;
}
return nullptr;
}
void DocTasksWidget::preparationTreeWidget()
{
treeWidget->setColumnCount(2);
reSetHeadTreeWidget();
treeWidget->setColumnWidth(ColumnsTree::clmn_ID, 80);
treeWidget->setColumnWidth(ColumnsTree::clmn_PMorDM, 900);
//treeWidget->setColumnHidden(ColumnsTree::clmn_ID, true);
}
void DocTasksWidget::reSetHeadTreeWidget()
{
QStringList listHeaders = {tr("PM/DM"), tr("ID")};
treeWidget->setHeaderLabels(listHeaders);
}
void DocTasksWidget::updateTreeWidget()
{
//Обновление дерева
treeWidget->clear();
for(Module* module : listAllModules)
{
addModuleToTreeWidget(module);
}
}
void DocTasksWidget::addModuleToTreeWidget(Module *module, QTreeWidgetItem* parentItem)
{
QTreeWidgetItem* itemModule = nullptr;
QString text;
QString ID = QString::number(module->getID());
if(parentItem)
{
itemModule = new QTreeWidgetItem();
parentItem->addChild(itemModule);
}
else
{
itemModule = new QTreeWidgetItem(treeWidget);
}
if(module->getType() == ModuleType::TYPE_PM)
{
PM* PMmodul = static_cast<PM*>(module);
text = PMmodul->getLangStructRus().title;
for(Module* module : PMmodul->getListChildModules())
{
addModuleToTreeWidget(module, itemModule);
}
}
else
{
DM* DMmodul = static_cast<DM*>(module);
text = DMmodul->getLangStructRus().techName;
itemModule->setFlags(itemModule->flags() | Qt::ItemIsUserCheckable);
itemModule->setCheckState(0, Qt::Checked);
itemModule->setIcon(0, QIcon(":/resources/icons/procedure.png"));
}
itemModule->setText(ColumnsTree::clmn_PMorDM, text);
itemModule->setText(ColumnsTree::clmn_ID, ID);
}

View File

@@ -0,0 +1,46 @@
#ifndef TASKTREEPREPARATION_H
#define TASKTREEPREPARATION_H
#include <QObject>
#include <QTreeWidgetItem>
#include <QDomElement>
#include "module.h"
#include "tasksAmmFim.h"
enum ColumnsTree{
clmn_PMorDM = 0,
clmn_code,
clmn_ID,
clmn_count
};
class TaskTreePreparation : public QObject
{
Q_OBJECT
public:
explicit TaskTreePreparation(QObject *parent = nullptr);
~TaskTreePreparation();
public:
void stopParser();
private:
QTreeWidgetItem* addModuleToTreeWidget(Module* module, bool flOnlyActive = false, QTreeWidgetItem* parentItem = nullptr);
void loadAMMtasksFromXML(QByteArray array);
void domElementParser(QDomElement element, Module* moduleParent);
void deleteAllModuls();
public Q_SLOTS:
void slot_prepareListItems(QByteArray array, QList<Module*>* listAllModules, bool flOnlyActive);
void slot_prepareListItemsForTrainee(QList<TaskAmmFim> listTask, QList<Module*>* listAllModules);
Q_SIGNALS:
void signal_listItemsReady(QList<QTreeWidgetItem*> listItems);
private:
QList<QTreeWidgetItem*> listItems;
QList<Module*>* listAllModules;
bool flagStop;
};
#endif // TASKTREEPREPARATION_H

View File

@@ -7,31 +7,40 @@ TraineesView::TraineesView(ConnectorToServer* connectorToServer, TypeView type,
typeObject = TypeObject::objGroup;
}
void TraineesView::resizeEvent(QResizeEvent *event)
{
int width = treeWidget->width();
treeWidget->setColumnWidth(ColumnsTreeTrainees::clmn_ID, 50);
treeWidget->setColumnWidth(ColumnsTreeTrainees::clmn_Login, 100);
treeWidget->setColumnWidth(ColumnsTreeTrainees::clmn_Password, 100);
treeWidget->setColumnWidth(ColumnsTreeTrainees::clmn_Class, 100);
treeWidget->setColumnWidth(ColumnsTreeTrainees::clmn_Computer, 100);
treeWidget->setColumnWidth(ColumnsTreeTrainees::clmn_IP_address, 130);
treeWidget->setColumnWidth(ColumnsTreeTrainees::clmn_Archived, 80);
treeWidget->setColumnWidth(ColumnsTreeTrainees::clmn_Logged, 100);
int widthTrainee;
if(typeView == TypeView::onlyView)
{//onlyView
widthTrainee = width - (530 + 10);
}
else
{//control
if(adminMode)
widthTrainee = width - (760 + 10);
else
widthTrainee = width - (630 + 10);
}
if(widthTrainee < 250)
widthTrainee = 250;
treeWidget->setColumnWidth(ColumnsTreeTrainees::clmn_Trainee, widthTrainee);
}
void TraineesView::slot_NeedUpdateUI(bool treeInstructor, bool treeTrainee)
{
if(typeView == TypeView::onlyView)
{
if(adminMode)
archiveVisible = false;
else
archiveVisible = false;
}
else
{
archiveVisible = true;
}
if(adminMode)
{
treeWidget->setColumnHidden(ColumnsTreeTrainees::clmn_ID, false);
treeWidget->setColumnHidden(ColumnsTreeTrainees::clmn_Archived, false);
}
else
{
treeWidget->setColumnHidden(ColumnsTreeTrainees::clmn_ID, true);
treeWidget->setColumnHidden(ColumnsTreeTrainees::clmn_Archived, true);
}
updateButtons();
if(treeTrainee)
@@ -42,44 +51,26 @@ void TraineesView::preparationTreeWidget()
{
mtxTreeWidget.lock();
treeWidget->setColumnCount(10);
treeWidget->setColumnCount(clmn_count);
reSetHeadTreeWidget();
treeWidget->setColumnWidth(ColumnsTreeTrainees::clmn_ID, 80);
treeWidget->setColumnWidth(ColumnsTreeTrainees::clmn_Trainee, 250);
treeWidget->setColumnWidth(ColumnsTreeTrainees::clmn_Login, 100);
treeWidget->setColumnWidth(ColumnsTreeTrainees::clmn_Password, 100);
treeWidget->setColumnWidth(ColumnsTreeTrainees::clmn_Class, 130);
treeWidget->setColumnWidth(ColumnsTreeTrainees::clmn_Computer, 130);
treeWidget->setColumnWidth(ColumnsTreeTrainees::clmn_IP_address, 130);
treeWidget->setColumnWidth(ColumnsTreeTrainees::clmn_Archived, 100);
treeWidget->setColumnWidth(ColumnsTreeTrainees::clmn_Logged, 100);
treeWidget->setColumnWidth(ColumnsTreeTrainees::clmn_Tasks, 200);
if(typeView == TypeView::onlyView)
{//onlyView
archiveVisible = false;
treeWidget->setColumnHidden(ColumnsTreeTrainees::clmn_ID, true);
//treeWidget->setColumnHidden(ColumnsTreeTrainees::clmn_Login, true);
treeWidget->setColumnHidden(ColumnsTreeTrainees::clmn_Password, true);
treeWidget->setColumnHidden(ColumnsTreeTrainees::clmn_Archived, true);
if(adminMode)
archiveVisible = false;
else
archiveVisible = false;
notLoggedInVisible = true;
}
else
{//control
archiveVisible = true;
notLoggedInVisible = true;
if(adminMode)
{
treeWidget->setColumnHidden(ColumnsTreeTrainees::clmn_ID, false);
treeWidget->setColumnHidden(ColumnsTreeTrainees::clmn_Archived, false);
}
else
{
@@ -166,13 +157,6 @@ void TraineesView::loadTraineesFromDB()
ItemTrainee->setIcon(ColumnsTreeTrainees::clmn_Logged, QIcon(QStringLiteral(":/resources/icons/circleGray.png")));
}
QString tasksStr;
for(Task task: trainee.getTasks())
{
tasksStr += task.getName() + QStringLiteral("; ");
}
ItemTrainee->setText(ColumnsTreeTrainees::clmn_Tasks, tasksStr);
ItemGroup->addChild(ItemTrainee);
//Скрываем архивных (при необходимости)
@@ -209,12 +193,14 @@ void TraineesView::loadTraineesFromDB()
else
setCurrentTrainee(lastCurrentID);
treeWidget->sortItems(ColumnsTreeTrainees::clmn_Trainee, Qt::SortOrder::AscendingOrder);
mtxTreeWidget.unlock();
}
void TraineesView::reSetHeadTreeWidget()
{
QStringList listHeaders = {tr("Trainee"), tr("Login"), tr("Password"), tr("Class"), tr("Computer"), tr("IP address"), tr("Archived"), tr("Logged"), tr("Tasks"), tr("ID")};
QStringList listHeaders = {tr("Trainee"), tr("Login"), tr("Password"), tr("Class"), tr("Computer"), tr("IP address"), tr("Archived"), tr("Logged"), tr("ID")};
treeWidget->setHeaderLabels(listHeaders);
}

View File

@@ -23,10 +23,13 @@ protected:
clmn_IP_address,
clmn_Archived,
clmn_Logged,
clmn_Tasks,
clmn_ID
clmn_ID,
clmn_count
};
public:
void resizeEvent(QResizeEvent *event) override;
public Q_SLOTS:
//Слот обработки сигнала необходимости обновления интерфейса
void slot_NeedUpdateUI(bool treeInstructor, bool treeTrainee);

View File

@@ -4,7 +4,9 @@
ViewerTrainees::ViewerTrainees(ConnectorToServer* connectorToServer, QWidget *parent) :
TraineesView(connectorToServer, CommonView::TypeView::onlyView, parent),
ui(new Ui::ViewerTrainees)
ui(new Ui::ViewerTrainees),
ammTasksWidget(nullptr),
fimTasksWidget(nullptr)
{
ui->setupUi(this);
@@ -12,6 +14,18 @@ ViewerTrainees::ViewerTrainees(ConnectorToServer* connectorToServer, QWidget *pa
ui->horizontalLayout_1->addWidget(treeWidget);
ammTasksWidget = new AMMtasksWidget(connectorToServer, AMMtasksWidget::TypeList::listForTrainee, this);
fimTasksWidget = new FIMtasksWidget(connectorToServer, FIMtasksWidget::TypeList::listForTrainee, this);
connect(this, &ViewerTrainees::signal_traineeSelected, fimTasksWidget, &FIMtasksWidget::slot_traineeSelected);
connect(this, &ViewerTrainees::signal_traineeSelected, ammTasksWidget, &AMMtasksWidget::slot_traineeSelected);
connect(connectorToServer, &ConnectorToServer::signal_UpdateTasksAMMforTrainee, ammTasksWidget, &AMMtasksWidget::slot_UpdateTasksAMMforTrainee);
connect(connectorToServer, &ConnectorToServer::signal_UpdateTasksFIMforTrainee, fimTasksWidget, &FIMtasksWidget::slot_UpdateTasksFIMforTrainee);
ui->verticalLayout_2->addWidget(ammTasksWidget);
ui->verticalLayout_2->addWidget(fimTasksWidget);
preparationTreeWidget();
setNotLoggedInVisible(true);
}
@@ -21,6 +35,20 @@ ViewerTrainees::~ViewerTrainees()
delete ui;
}
void ViewerTrainees::setAuthComplited(bool authComplited)
{
this->authComplited = authComplited;
updateButtons();
}
void ViewerTrainees::deactivate()
{
CommonView::deactivate();
ammTasksWidget->deactivate();
fimTasksWidget->deactivate();
updateButtons();
}
/*
void ViewerTrainees::on_treeWidget_itemClicked(QTreeWidgetItem *item, int column)
{
@@ -33,6 +61,9 @@ void ViewerTrainees::on_treeWidget_itemClicked(QTreeWidgetItem *item, int column
void ViewerTrainees::slot_tabMessengerChanged(QString login)
{
if(login == "")
return;
for (int i = 0; i < treeWidget->topLevelItemCount(); i++)
{//Проход по группам
int countChild = treeWidget->topLevelItem(i)->childCount();
@@ -44,7 +75,12 @@ void ViewerTrainees::slot_tabMessengerChanged(QString login)
{
treeWidget->setCurrentItem(treeWidget->topLevelItem(i)->child(j));
typeObject = TypeObject::objTrainee;
lastCurrentID = connectorToServer->getIdTraineeByLogin(login);
int newCurrentID = connectorToServer->getIdTraineeByLogin(login);
if(newCurrentID == lastCurrentID)
return;
lastCurrentID = newCurrentID;
Q_EMIT signal_traineeSelected(login);
return;
}
@@ -89,10 +125,20 @@ void ViewerTrainees::on_treeWidget_currentItemChanged(QTreeWidgetItem *current,
if(current == nullptr)
return;
if(current->childCount() == 0)
//if(current->childCount() == 0)
{//Выбран обучаемый
QString login = current->text(ColumnsTreeTrainees::clmn_Login);
Q_EMIT signal_traineeSelected(login);
//if(login != "")
{
int newCurrentID = connectorToServer->getIdTraineeByLogin(login);
if(newCurrentID == lastCurrentID)
return;
lastCurrentID = newCurrentID;
Q_EMIT signal_traineeSelected(login);
}
}
}

View File

@@ -2,6 +2,8 @@
#define TRAINEESWIDGET_H
#include "traineesview.h"
#include "ammtaskswidget.h"
#include "fimtaskswidget.h"
namespace Ui {
class ViewerTrainees;
@@ -17,6 +19,11 @@ public:
explicit ViewerTrainees(ConnectorToServer* connectorToServer, QWidget *parent = nullptr);
~ViewerTrainees();
public:
void setAuthComplited(bool authComplited);
void deactivate();
protected:
void changeEvent(QEvent * event) override;
@@ -40,6 +47,10 @@ Q_SIGNALS:
private:
void updateButtons() override;
private:
AMMtasksWidget* ammTasksWidget;
FIMtasksWidget* fimTasksWidget;
private:
Ui::ViewerTrainees *ui;
};

View File

@@ -38,6 +38,9 @@
<item>
<layout class="QHBoxLayout" name="horizontalLayout_1"/>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_2"/>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>

View File

@@ -0,0 +1,44 @@
#include "newversionwidget.h"
#include "ui_newversionwidget.h"
NewVersionWidget::NewVersionWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::NewVersionWidget)
{
ui->setupUi(this);
setWindowFlags(Qt::SubWindow);
setAttribute(Qt::WA_ShowModal,true);
}
void NewVersionWidget::initialize(VersionSelectWidget *versionSelectWidget, QString prevName)
{
this->versionSelectWidget = versionSelectWidget;
ui->prevVerValue->setText(prevName);
validator = new QRegExpValidator(QRegExp("^[A-Za-z0-9]{20}$"));
ui->lineEdit->setValidator(validator);
}
void NewVersionWidget::on_createButton_clicked()
{
if(ui->lineEdit->text() != "")
{
versionSelectWidget->sendCopyEmit(ui->lineEdit->text());
hide();
}
}
void NewVersionWidget::on_cancelButton_clicked()
{
hide();
}
NewVersionWidget::~NewVersionWidget()
{
delete ui;
}
void NewVersionWidget::on_lineEdit_inputRejected()
{
QToolTip::showText(QCursor::pos(),tr("Only Latin letters and numbers"));
}

View File

@@ -0,0 +1,36 @@
#ifndef NEWVERSIONWIDGET_H
#define NEWVERSIONWIDGET_H
#include <Widgets/versionselectwidget.h>
#include <QWidget>
#include <QLineEdit>
#include <QToolTip>
namespace Ui {
class NewVersionWidget;
}
class VersionSelectWidget;
class NewVersionWidget : public QWidget
{
Q_OBJECT
public:
explicit NewVersionWidget(QWidget *parent = nullptr);
void initialize(VersionSelectWidget *versionSelectWidget,QString prevName);
~NewVersionWidget();
private slots:
void on_createButton_clicked();
void on_cancelButton_clicked();
void on_lineEdit_inputRejected();
private:
Ui::NewVersionWidget *ui;
VersionSelectWidget *versionSelectWidget;
QRegExpValidator *validator;
};
#endif // NEWVERSIONWIDGET_Hvoid on_lineEdit_inputRejected();

View File

@@ -0,0 +1,253 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>NewVersionWidget</class>
<widget class="QWidget" name="NewVersionWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>344</width>
<height>200</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Создать копию...</string>
</property>
<property name="autoFillBackground">
<bool>true</bool>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QFrame" name="NewVerBackground">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="baseVerLayout">
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<widget class="QLabel" name="prevVerTitle">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Basic version:</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="prevVerValue">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>150</horstretch>
<verstretch>30</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="newNameLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<widget class="QLabel" name="newNameVersionTitle">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>99</horstretch>
<verstretch>40</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>New name version:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="lineEdit">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>150</horstretch>
<verstretch>30</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>150</width>
<height>30</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>60</width>
<height>30</height>
</size>
</property>
<property name="toolTipDuration">
<number>1000</number>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="bottomMargin">
<number>6</number>
</property>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Minimum</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="createButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Create</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Minimum</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="cancelButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Cancel</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Minimum</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -0,0 +1,125 @@
#include "versionselectwidget.h"
#include "ui_versionselectwidget.h"
#include "ui_versionselectwidget.h"
#include <QMessageBox>
VersionSelectWidget::VersionSelectWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::VersionSelectWidget),
selectedVersion(nullptr)
{
ui->setupUi(this);
setWindowFlags(Qt::Dialog | Qt::MSWindowsFixedSizeDialogHint);
setAttribute(Qt::WA_ShowModal,true);
}
void VersionSelectWidget::initialize(SendSystem *sendSystem,VersionContainer *versionContainer,NotifyController *notifyController)
{
connect(this,&VersionSelectWidget::sigSendSwitchVersion,sendSystem,&SendSystem::sendChangeVersion,Qt::AutoConnection);
connect(this,&VersionSelectWidget::sigSendCopyVersion,sendSystem,&SendSystem::sendCopyVersion,Qt::AutoConnection);
connect(this,&VersionSelectWidget::sigSendDeleteVersion,sendSystem,&SendSystem::sendDeleteVersion,Qt::AutoConnection);
connect(this,&VersionSelectWidget::sigSendNotify,notifyController,&NotifyController::showWarning,Qt::AutoConnection);
this->versionContainer = versionContainer;
hide();
setWindowTitle(tr("Version control"));
}
void VersionSelectWidget::fillView(QList<StreamingVersionData *> *serverData)
{
show();
ui->verListView->clear();
serverDataList = serverData;
ui->verValue->setText(versionContainer->getServerVersionData()->getViewName());
foreach(StreamingVersionData *data,*serverData)
{
ui->verListView->addItem(data->getViewName());
}
}
void VersionSelectWidget::on_verListView_itemDoubleClicked(QListWidgetItem *item)
{
foreach(StreamingVersionData *data,*serverDataList)
{
if(data->getViewName() == item->text())
{
QString info = tr("Version name: ") + data->getViewName() + "\n";
info.append(tr("Created: ") + data->getCreateData().toString() + "\n");
info.append(tr("Changeable: ") + changableText(data->getIsChangeable()) + "\n");
info.append(tr("Author: ") + data->getAuthor());
ui->infoValue->setText(info);
selectedVersion = data;
}
}
}
QString VersionSelectWidget::changableText(bool flag)
{
if(flag) return tr("Yes");
else return tr("No");
}
void VersionSelectWidget::on_createDuplicateButton_clicked()
{
if (selectedVersion == nullptr)
{
sigSendNotify(tr("Version not selected"));
return;
}
NewVersionWidget *newVersionWidget = new NewVersionWidget;
newVersionWidget->initialize(this,selectedVersion->getViewName());
newVersionWidget->show();
}
void VersionSelectWidget::sendCopyEmit(QString newName)
{
QString result = selectedVersion->getViewName() + ";" + newName + ";" + authorName;
if (selectedVersion == nullptr)
{
sigSendNotify(tr("Version not selected"));
return;
}
//versionContainer->setLocalVersionData(selectedVersion);
emit sigSendCopyVersion(result);
}
void VersionSelectWidget::on_DeleteVersionButton_clicked()
{
if (selectedVersion == nullptr)
{
sigSendNotify(tr("Version not selected"));
return;
}
emit sigSendDeleteVersion(selectedVersion);
}
void VersionSelectWidget::on_switchServerVersionButton_clicked()
{
if (selectedVersion == nullptr)
{
sigSendNotify(tr("Version not selected"));
return;
}
versionContainer->setServerVersionData(selectedVersion);
ui->verValue->setText(selectedVersion->getViewName());
emit sigSendSwitchVersion(selectedVersion);
}
void VersionSelectWidget::setAuthor(QString name)
{
authorName = name;
}
VersionSelectWidget::~VersionSelectWidget()
{
delete ui;
}

View File

@@ -0,0 +1,54 @@
#ifndef VERSIONSELECTWIDGET_H
#define VERSIONSELECTWIDGET_H
#include <QListWidget>
#include <QWidget>
#include <Core/sendsystem.h>
#include <Core/versioncontainer.h>
#include <Core/notifycontroller.h>
#include <streamingversiondata.h>
#include <Widgets/newversionwidget.h>
namespace Ui {
class VersionSelectWidget;
}
class VersionSelectWidget : public QWidget
{
Q_OBJECT
public:
explicit VersionSelectWidget(QWidget *parent = nullptr);
void initialize(SendSystem *sendSystem,VersionContainer *versionContainer,NotifyController *notifyController);
void fillView(QList<StreamingVersionData*> *serverData);
void sendCopyEmit(QString newName);
void setAuthor(QString name);
~VersionSelectWidget();
private slots:
void on_verListView_itemDoubleClicked(QListWidgetItem *item);
void on_createDuplicateButton_clicked();
void on_DeleteVersionButton_clicked();
void on_switchServerVersionButton_clicked();
signals:
void sigSendDeleteVersion(StreamingVersionData *streaming);
void sigSendSwitchVersion(StreamingVersionData *selectVersion);
void sigSendCopyVersion(QString versionPair);
void sigSendNotify(QString message);
private:
Ui::VersionSelectWidget *ui;
SendSystem *sendSystem;
QList<StreamingVersionData*> *serverDataList;
VersionContainer *versionContainer;
NotifyController *notifyController;
StreamingVersionData *selectedVersion;
QString authorName;
QString changableText(bool flag);
};
#endif // VERSIONSELECTWIDGET_H

View File

@@ -0,0 +1,239 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>VersionSelectWidget</class>
<widget class="QWidget" name="VersionSelectWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>606</width>
<height>229</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<property name="autoFillBackground">
<bool>false</bool>
</property>
<widget class="QWidget" name="verticalLayoutWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>301</width>
<height>171</height>
</rect>
</property>
<layout class="QVBoxLayout" name="actualServerListLayout">
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<widget class="QLabel" name="verListTitle">
<property name="font">
<font>
<family>MS Shell Dlg 2</family>
<pointsize>8</pointsize>
</font>
</property>
<property name="contextMenuPolicy">
<enum>Qt::PreventContextMenu</enum>
</property>
<property name="text">
<string>Available versions on the server:</string>
</property>
</widget>
</item>
<item>
<widget class="QListWidget" name="verListView"/>
</item>
</layout>
</widget>
<widget class="QWidget" name="horizontalLayoutWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>180</y>
<width>601</width>
<height>41</height>
</rect>
</property>
<layout class="QHBoxLayout" name="ButtonLayout" stretch="0,0">
<property name="spacing">
<number>6</number>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="sizeConstraint">
<enum>QLayout::SetMinimumSize</enum>
</property>
<property name="leftMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<item>
<widget class="QPushButton" name="createDuplicateButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<pointsize>9</pointsize>
</font>
</property>
<property name="text">
<string>Create copy</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="DeleteVersionButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="sizeIncrement">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="font">
<font>
<pointsize>9</pointsize>
</font>
</property>
<property name="text">
<string>Delete</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QPushButton" name="switchServerVersionButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<pointsize>9</pointsize>
</font>
</property>
<property name="text">
<string>Change server version</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="verticalLayoutWidget_2">
<property name="geometry">
<rect>
<x>310</x>
<y>0</y>
<width>291</width>
<height>131</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="sizeConstraint">
<enum>QLayout::SetDefaultConstraint</enum>
</property>
<item>
<widget class="QLabel" name="infoViewTitle">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Info:</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="infoValue">
<property name="text">
<string>Double click on the version to see information...</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<widget class="QWidget" name="horizontalLayoutWidget_2">
<property name="geometry">
<rect>
<x>310</x>
<y>140</y>
<width>291</width>
<height>31</height>
</rect>
</property>
<layout class="QHBoxLayout" name="ServerInfoLayout">
<item>
<widget class="QLabel" name="verTitle">
<property name="text">
<string>Current server version:</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="verValue">
<property name="text">
<string>none</string>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -0,0 +1,40 @@
#include "waitanimationwidget.h"
#include "ui_waitanimationwidget.h"
WaitAnimationWidget::WaitAnimationWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::WaitAnimationWidget),
loadingMovie(nullptr)
{
ui->setupUi(this);
}
void WaitAnimationWidget::initialize(QMovie *movie,QWidget *parent)
{
ui->MovieLabel->setMovie(movie);
loadingMovie = movie;
setFixedSize(parent->width(),parent->height());
hide();
}
void WaitAnimationWidget::showWithPlay()
{
show();
loadingMovie->start();
}
void WaitAnimationWidget::hideWithStop()
{
hide();
loadingMovie->stop();
}
void WaitAnimationWidget::resize(QSize size)
{
setFixedSize(size);
}
WaitAnimationWidget::~WaitAnimationWidget()
{
delete ui;
}

View File

@@ -0,0 +1,28 @@
#ifndef WAITANIMATIONWIDGET_H
#define WAITANIMATIONWIDGET_H
#include <QMovie>
#include <QWidget>
namespace Ui {
class WaitAnimationWidget;
}
class WaitAnimationWidget : public QWidget
{
Q_OBJECT
public:
explicit WaitAnimationWidget(QWidget *parent = nullptr);
void initialize(QMovie *movie,QWidget *parent);
void showWithPlay();
void hideWithStop();
void resize(QSize size);
~WaitAnimationWidget();
private:
Ui::WaitAnimationWidget *ui;
QMovie *loadingMovie;
};
#endif // WAITANIMATIONWIDGET_H

View File

@@ -0,0 +1,73 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>WaitAnimationWidget</class>
<widget class="QWidget" name="WaitAnimationWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>635</width>
<height>293</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<property name="autoFillBackground">
<bool>false</bool>
</property>
<property name="styleSheet">
<string notr="true">background-color:rgba(0,0,0,50%);</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<layout class="QHBoxLayout" name="mainLayout">
<item>
<widget class="QLabel" name="MovieLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="styleSheet">
<string notr="true">background-color:rgba(0,0,0,50%)</string>
</property>
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap resource="../resources.qrc">:/resources/icons/762.gif</pixmap>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources>
<include location="../resources.qrc"/>
</resources>
<connections/>
</ui>

View File

@@ -14,6 +14,8 @@ enum PacketType
TYPE_XMLANSWER = 8,
TYPE_QT = 9,
TYPE_DISABLE = 11,
TYPE_UPDATE = 12,
TYPE_CHECK_VERSION = 13,
TYPE_FILESIZE = 20,
TYPE_XMLANSWER_MESSAGE_FOR_GUI = 90,
@@ -24,7 +26,13 @@ enum PacketType
TYPE_XMLANSWER_QUERY_DB__LIST_TRAINEES = 102,
TYPE_XMLANSWER_QUERY_DB__LIST_COMPUTERS = 103,
TYPE_XMLANSWER_QUERY_DB__LIST_CLASSROOMS = 104,
TYPE_XMLANSWER_QUERY_DB__LIST_TASKS = 105,
TYPE_XMLANSWER_QUERY_TASKS_AMM_OF_TRAINEE = 106,
TYPE_XMLANSWER_QUERY_TASKS_FIM_OF_TRAINEE = 107,
//xml-ответы на запросы AdditionalFiles
TYPE_XMLANSWER_QUERY_TASKS_XML_FIM = 130,
TYPE_XMLANSWER_QUERY_TASKS_XML_AMM = 131,
//ответы по обновлениям
HASH_READY = 150,

View File

@@ -13,9 +13,21 @@ public:
this->viewName = viewName;
this->createData = data;
this->size = size;
this->isChangeable = false;
}
StreamingVersionData(){};
~StreamingVersionData();
void fill(StreamingVersionData* data)
{
this->absolutePath = data->getAbsolutPath();
this->viewName = data->getViewName();
this->createData = data->getCreateData();
this->size = data->getSize();
this->isChangeable = data->getIsChangeable();
this->author = data->getAuthor();
}
QString getAbsolutPath() const
{
return absolutePath;
@@ -36,11 +48,55 @@ public:
return size;
}
bool getIsChangeable() const
{
return isChangeable;
}
void setIsChangeable(bool value)
{
isChangeable = value;
}
QString getAuthor() const
{
return author;
}
void setAuthor(const QString &value)
{
author = value;
}
void setViewName(const QString &value)
{
viewName = value;
}
void setCreateData(const QDateTime &value)
{
createData = value;
}
void setAbsolutePath(const QString &value)
{
absolutePath = value;
}
private:
QString absolutePath;
QString viewName;
QString author;
QDateTime createData;
bool isChangeable;
qint32 size;
};
#endif // STREAMINGVERSIONDATA_H

View File

@@ -3,6 +3,7 @@
#include <QList>
#include <QString>
#include "typeQueryToDB.h"
#define NOTIFY_SERVER_END "END"
#define NOTIFY_SERVER_BLOCKED "BLOCKED"
@@ -60,7 +61,7 @@ class ClientDeAutorization
public:
QString Login;
};
/*
enum TypeQueryToDB{
TYPE_QUERY_GET_ALL_LISTS,
TYPE_QUERY_NEW_INSTRUCTOR,
@@ -71,14 +72,23 @@ enum TypeQueryToDB{
TYPE_QUERY_EDIT_GROUP,
TYPE_QUERY_NEW_TRAINEE,
TYPE_QUERY_DEL_TRAINEE,
TYPE_QUERY_EDIT_TRAINEE
TYPE_QUERY_EDIT_TRAINEE,
TYPE_QUERY_ASSIGN_TASK_AMM_TO_TRAINEE,
TYPE_QUERY_ASSIGN_TASK_FIM_TO_TRAINEE
};
*/
class ClientQueryToDB{
public:
TypeQueryToDB typeQuery;
};
class ClientQueryTasksXML
{
public:
QString Type;
};
class ServerMessage
{
public:

View File

@@ -102,8 +102,58 @@ QByteArray DBAnswerParser::listClassrooms(bool result, QList<Classroom> *listCla
return QByteArray();
}
QByteArray DBAnswerParser::listTasks(bool result, QList<Task> *listTasks)
QByteArray DBAnswerParser::listTasksAMMofTrainee(bool result, QList<TaskAmmFim> *listTasks, int trainee_id)
{
//TODO
return QByteArray();
QDomDocument commonDOM;
if(! dataParser->loadBlankXML(":/resources/blankXML/ListTasksAMM.xml", &commonDOM))
return QByteArray();
QDomNode listNode = commonDOM.namedItem("ListTasksAMM");
listNode.toElement().setAttribute("trainee_id", QString::number(trainee_id));
for(TaskAmmFim task : *listTasks)
{
//Задача
QDomNode taskNode = commonDOM.createElement("taskAMM");
listNode.appendChild(taskNode);
taskNode.toElement().setAttribute("task_id", QString::number(task.getID()));
taskNode.toElement().setAttribute("title", task.ammProcedure.title);
taskNode.toElement().setAttribute("dmCode", task.ammProcedure.dmCode);
}
dataParser->saveDOMtoXML("ListTasksAMM.xml", &commonDOM);
return commonDOM.toByteArray();
}
QByteArray DBAnswerParser::listTasksFIMofTrainee(bool result, QList<TaskAmmFim> *listTasks, int trainee_id)
{
QDomDocument commonDOM;
if(! dataParser->loadBlankXML(":/resources/blankXML/ListTasksFIM.xml", &commonDOM))
return QByteArray();
QDomNode listNode = commonDOM.namedItem("ListTasksFIM");
listNode.toElement().setAttribute("trainee_id", QString::number(trainee_id));
for(TaskAmmFim task : *listTasks)
{
//Задача
QDomNode taskNode = commonDOM.createElement("taskFIM");
listNode.appendChild(taskNode);
taskNode.toElement().setAttribute("task_id", QString::number(task.getID()));
taskNode.toElement().setAttribute("title", task.title);
for(Malfunction malfunction : task.malfunctionList)
{//Неисправность
QDomNode malfunctionNode = commonDOM.createElement("malfunction");
taskNode.appendChild(malfunctionNode);
malfunctionNode.toElement().setAttribute("dmCode", malfunction.dmCode);
malfunctionNode.toElement().setAttribute("num", malfunction.num);
malfunctionNode.toElement().setAttribute("description", malfunction.description);
}
}
dataParser->saveDOMtoXML("ListTasksFIM.xml", &commonDOM);
return commonDOM.toByteArray();
}

View File

@@ -18,7 +18,9 @@ public:
QByteArray listTrainees(bool result, QList<Trainee> *listTrainees);
QByteArray listComputers(bool result, QList<Computer> *listComputers);
QByteArray listClassrooms(bool result, QList<Classroom> *listClassrooms);
QByteArray listTasks(bool result, QList<Task> *listTasks);
QByteArray listTasksAMMofTrainee(bool result, QList<TaskAmmFim> *listTasks, int trainee_id);
QByteArray listTasksFIMofTrainee(bool result, QList<TaskAmmFim> *listTasks, int trainee_id);
signals:
private:

View File

@@ -1,4 +1,5 @@
#include "processparser.h"
#include "tasksAmmFim.h"
ProcessParser::ProcessParser(QObject *parent) : QObject(parent)
{
@@ -41,7 +42,12 @@ void ProcessParser::read(ClientHandler *client, QByteArray array)
else if(xmlReader.name() == "QueryToDB")
{//Запрос к базе данных от клиента
queryToDb(xmlReader,client);
queryToDb(xmlReader,client, array);
}
else if(xmlReader.name() == "QueryTasksXML")
{//Запрос файла XML с задачами
queryTasksXML(xmlReader,client);
}
else if(xmlReader.name() == "ClientMessage")
{//Сообщение от клиента
@@ -53,6 +59,10 @@ void ProcessParser::read(ClientHandler *client, QByteArray array)
clientNotify(xmlReader,client);
}
else if(xmlReader.name() == "DataInfo")
{
clientDataInfo(xmlReader,client);
}
else
{
emit sigLogMessage("XmlParser: unrecognized tag");
@@ -63,6 +73,55 @@ void ProcessParser::read(ClientHandler *client, QByteArray array)
}//while(!xmlReader.atEnd())
}
void ProcessParser::clientDataInfo(QXmlStreamReader &xmlReader,ClientHandler *client)
{
DataInfo *dataInfo = new DataInfo;
foreach(const QXmlStreamAttribute &attr, xmlReader.attributes())
{
QString name = attr.name().toString();
QString value = attr.value().toString();
if(name == "path")
dataInfo->path= value.toUtf8();
if(name == "size")
dataInfo->size = value.toLong();
}
processingSystem->setCurrentDataInfo(dataInfo);
}
TaskAmmFim ProcessParser::xmlParserQueryToDB_ASSIGN_TASK_FIM_TO_TRAINEE(QByteArray array)
{
TaskAmmFim task;
QDomDocument commonDOM;
commonDOM.setContent(array);
QDomNode mainNode = commonDOM.namedItem("QueryToDB");
task.title = mainNode.toElement().attribute("title");
for(int i = 0; i < mainNode.childNodes().count(); i++)
{
QDomNode malfunctionNode = mainNode.childNodes().at(i);
if(malfunctionNode.nodeName() == "malfunction")
{//Неисправность
Malfunction malfunction;
malfunction.num = malfunctionNode.toElement().attribute("num");
malfunction.dmCode = malfunctionNode.toElement().attribute("dmCode");
malfunction.description = malfunctionNode.toElement().attribute("description");
task.malfunctionList.append(malfunction);
}
}
return task;
}
void ProcessParser::clientAuth(QXmlStreamReader &xmlReader,ClientHandler *client)
{
ClientAutorization clientAutorization;
@@ -127,13 +186,14 @@ void ProcessParser::toClientMessage(QXmlStreamReader &xmlReader,ClientHandler *c
processingSystem->processingToClientMessage(client, toClientMessage);
}
void ProcessParser::queryToDb(QXmlStreamReader &xmlReader,ClientHandler *client)
void ProcessParser::queryToDb(QXmlStreamReader &xmlReader,ClientHandler *client, QByteArray array)
{
ClientQueryToDB queryToDB;
int id = 0;
Instructor instructor;
Trainee trainee;
Group group;
TaskAmmFim task;
void* data = nullptr;
/*Перебираем все атрибуты тега*/
@@ -144,7 +204,13 @@ void ProcessParser::queryToDb(QXmlStreamReader &xmlReader,ClientHandler *client)
//addTextToLogger(name + ": " + value);
if(name == "TypeQuery")
{
queryToDB.typeQuery = (TypeQueryToDB)value.toInt();
if(queryToDB.typeQuery == TypeQueryToDB::TYPE_QUERY_ASSIGN_TASK_FIM_TO_TRAINEE)
{
task = xmlParserQueryToDB_ASSIGN_TASK_FIM_TO_TRAINEE(array);
}
}
else if(name == "id")
id = value.toInt();
else
@@ -200,6 +266,17 @@ void ProcessParser::queryToDb(QXmlStreamReader &xmlReader,ClientHandler *client)
else if(name == "name")
group.setName(value);
break;
case TypeQueryToDB::TYPE_QUERY_ASSIGN_TASK_AMM_TO_TRAINEE:
if(name == "title")
task.ammProcedure.title = value;
else if(name == "dmCode")
task.ammProcedure.dmCode = value;
break;
case TypeQueryToDB::TYPE_QUERY_ASSIGN_TASK_FIM_TO_TRAINEE:
//if(name == "title")
//task.title = value;
break;
};
}
}
@@ -218,11 +295,33 @@ void ProcessParser::queryToDb(QXmlStreamReader &xmlReader,ClientHandler *client)
case TypeQueryToDB::TYPE_QUERY_EDIT_GROUP:
data = &group;
break;
case TypeQueryToDB::TYPE_QUERY_ASSIGN_TASK_AMM_TO_TRAINEE:
case TypeQueryToDB::TYPE_QUERY_ASSIGN_TASK_FIM_TO_TRAINEE:
data = &task;
break;
};
processingSystem->processingClientQueryToDB(client, queryToDB, id, data);
}
void ProcessParser::queryTasksXML(QXmlStreamReader &xmlReader, ClientHandler *client)
{
ClientQueryTasksXML clientQueryTasksXML;
/*Перебираем все атрибуты тега*/
foreach(const QXmlStreamAttribute &attr, xmlReader.attributes())
{
QString name = attr.name().toString();
QString value = attr.value().toString();
//addTextToLogger(name + ": " + value);
if(name == "Type")
clientQueryTasksXML.Type = value;
}
processingSystem->processingClientQueryTasksXML(client, clientQueryTasksXML);
}
void ProcessParser::clientMessage(QXmlStreamReader &xmlReader,ClientHandler *client)
{
ClientMessage clientMessage;
@@ -239,6 +338,7 @@ void ProcessParser::clientMessage(QXmlStreamReader &xmlReader,ClientHandler *cli
}
processingSystem->processingFromClientMessage(client, clientMessage);
}
void ProcessParser::clientNotify(QXmlStreamReader &xmlReader,ClientHandler *client)
@@ -255,6 +355,8 @@ void ProcessParser::clientNotify(QXmlStreamReader &xmlReader,ClientHandler *clie
clientNotify.Code = value;
}
processingSystem->processingClientNotify(client, clientNotify);
}

View File

@@ -2,9 +2,9 @@
#define PROCESSPARSER_H
#include <QObject>
#include <Data/typesDataServerClient.h>
#include <qxmlstream.h>
#include <clienthandler.h>
#include "Data/typesDataServerClient.h"
class ProcessParser : public QObject
{
@@ -22,9 +22,13 @@ private:
void clientAuth(QXmlStreamReader &xmlReader,ClientHandler *client);
void clientDeAuth(QXmlStreamReader &xmlReader,ClientHandler *client);
void toClientMessage(QXmlStreamReader &xmlReader,ClientHandler *client);
void queryToDb(QXmlStreamReader &xmlReader,ClientHandler *client);
void queryToDb(QXmlStreamReader &xmlReader,ClientHandler *client, QByteArray array = QByteArray());
void queryTasksXML(QXmlStreamReader &xmlReader,ClientHandler *client);
void clientMessage(QXmlStreamReader &xmlReader,ClientHandler *client);
void clientNotify(QXmlStreamReader &xmlReader,ClientHandler *client);
void clientDataInfo(QXmlStreamReader &xmlReader, ClientHandler *client);
TaskAmmFim xmlParserQueryToDB_ASSIGN_TASK_FIM_TO_TRAINEE(QByteArray array);
};
#endif // PROCESSPARSER_H

View File

@@ -8,10 +8,76 @@ AssetsManager::AssetsManager(QObject *parent) : QObject(parent)
void AssetsManager::initialize(UpdateController* updateContoller,DataParser *dataParser)
{
this->updateController = updateContoller;
connect(this,&AssetsManager::sigSaveVersion,updateContoller,&UpdateController::saveVersionToFile);
//connect(this,&AssetsManager::sigSaveVersion,updateContoller,&UpdateController::saveVersionToFile);
datas = new QList<StreamingVersionData*>;
}
void AssetsManager::fillDatas()
{
QByteArray array;
QFile file(versionListFile);
if(!file.exists())
{
return;
}
datas->clear();
file.open(QIODevice::ReadOnly);
array = file.readAll();
file.close();
QXmlStreamReader xmlReader(array);
xmlReader.readNext();
QString name = xmlReader.name().toString();
while(!xmlReader.atEnd())
{
name = xmlReader.name().toString();
if(!xmlReader.isStartElement()) {
xmlReader.readNext();
continue;
}
if(xmlReader.name() == "VersionList")
{
xmlReader.readNext();
while (!xmlReader.atEnd())
{
if(xmlReader.isStartElement())
{
if(xmlReader.name() == "VersionData")
{
StreamingVersionData *data = new StreamingVersionData();
foreach(const QXmlStreamAttribute &attr,xmlReader.attributes())
{
QString name = attr.name().toString();
QString value = attr.value().toString();
if(name == "Version")
data->setViewName(value);
else if(name == "Created")
data->setCreateData(QDateTime::fromString(value));
else if(name == "isChangeable")
data->setIsChangeable(value.toInt());
else if(name == "author")
data->setAuthor(value);
}
datas->append(data);
}
}
xmlReader.readNext();
}
}
}
}
void AssetsManager::setVersionList(QList<StreamingVersionData*> *streamingVersion)
{
datas->clear();
@@ -41,7 +107,7 @@ QString AssetsManager::setVersion(QString versionName)
if (version->getViewName() == versionName)
{
currentVersionData = version;
emit sigSaveVersion(currentVersionData);
saveVersionToFile(currentVersionData);
return version->getAbsolutPath();
}
@@ -101,21 +167,17 @@ void AssetsManager::addVersion(StreamingVersionData *data)
datas->push_back(data);
}
void AssetsManager::createCopyVersion(QString versionName,QString newVersionName)
void AssetsManager::createCopyVersion(QString versionName,QString newVersionName,QString author)
{
qDebug() << "assetManager thread ID " << QThread::currentThreadId();
QListIterator<StreamingVersionData*> iterator(*datas);
StreamingVersionData* data;
StreamingVersionData* data = new StreamingVersionData;
while (iterator.hasNext())
{
StreamingVersionData *version = iterator.next();
if (version->getViewName() == versionName)
{
data = version;
}
}
data->setAbsolutePath(Tools::createSharedPath("/" + newVersionName));
data->setAuthor(author);
data->setIsChangeable(true);
data->setViewName(newVersionName);
data->setCreateData(QDateTime::currentDateTime());
datas->append(data);
qDebug() << "Version for copy " << versionName;
qDebug() << "New version name " << newVersionName;
@@ -208,6 +270,119 @@ void AssetsManager::copyAllRecurse(QString source,QString destination)
}
}
void AssetsManager::writeVersionsToFile(QList<StreamingVersionData*> version,bool isFirst)
{
QList<SXmlAnswerTag> listTag;
datas->clear();
QFile file(versionListFile);
foreach(StreamingVersionData* ver,version)
{
SAttribute attribute1 = {"Version", ver->getViewName()};
SAttribute attribute2 = {"Created", ver->getCreateData().toString()};
SAttribute attribute3;
SAttribute attribute4;
if(isFirst)
{
attribute3 = {"isChangeable",QString::number(false)};
QString author = tr("Константа-дизайн");
attribute4 = {"author",author};
ver->setAuthor(author);
}else
{
attribute3 ={"isChangeable",QString::number(ver->getIsChangeable())};
attribute4 = {"author",ver->getAuthor()};
}
QList<SAttribute> listAttr = {attribute1, attribute2,attribute3,attribute4};
SXmlAnswerTag tag = {"VersionData", listAttr};
listTag.append(tag);
datas->append(ver);
}
file.open(QIODevice::WriteOnly);
QXmlStreamWriter xmlWriter(&file);
xmlWriter.setAutoFormatting(true);
xmlWriter.writeStartDocument();
xmlWriter.writeStartElement("VersionList");
foreach(SXmlAnswerTag tag,listTag)
{
xmlWriter.writeStartElement(tag.elementName);
foreach(SAttribute attribute,tag.attr)
{
xmlWriter.writeAttribute(attribute.name,attribute.value);
}
xmlWriter.writeEndElement();
}
xmlWriter.writeEndElement();
xmlWriter.writeEndDocument();
file.close();
}
void AssetsManager::createFirstVersionListXML(QList<StreamingVersionData*> version) //TODO: переименовать и перебросить в AssetManager
{
QFile file(versionListFile);
QList<StreamingVersionData*> *temp = new QList<StreamingVersionData*>();
if(!file.exists())
{
writeVersionsToFile(version,true);
}
else
{
if(datas->count() == 0) fillDatas();
foreach(StreamingVersionData* ver,version)
{
foreach(StreamingVersionData* data,*datas)
{
if(ver->getViewName() == data->getViewName())
{
StreamingVersionData *tempData = new StreamingVersionData;
tempData->fill(data);
tempData->setAbsolutePath(ver->getAbsolutPath());
temp->append(tempData);
break;
}
}
}
writeVersionsToFile(*temp,false);
}
}
void AssetsManager::saveVersionToFile(StreamingVersionData *streamingVersion) //TODO: переименовать и перебросить в AssetManager
{
QFile file(version);
file.open(QFile::WriteOnly);
QXmlStreamWriter xmlWriter(&file);
xmlWriter.setAutoFormatting(true);
xmlWriter.writeStartDocument();
xmlWriter.writeStartElement("VersionData");
xmlWriter.writeAttribute("Version",streamingVersion->getViewName());
xmlWriter.writeAttribute("Created",streamingVersion->getCreateData().toString());
xmlWriter.writeAttribute("isChangeable",QString::number(streamingVersion->getIsChangeable()));
xmlWriter.writeAttribute("author",streamingVersion->getAuthor());
xmlWriter.writeEndElement();
xmlWriter.writeEndDocument();
file.close();
}
AssetsManager::~AssetsManager()
{

View File

@@ -13,10 +13,11 @@ public:
explicit AssetsManager(QObject *parent = nullptr);
void initialize(UpdateController* updateContoller,DataParser *dataParser);
void addVersion(StreamingVersionData *data);
void createCopyVersion(QString versionName,QString newName);
void createCopyVersion(QString versionName,QString newName,QString author);
void deleteVersion(QString version);
void setVersionList(QList<StreamingVersionData *> *streamingVersion);
bool findDuplicate(QString name);
void createFirstVersionListXML(QList<StreamingVersionData*> assets);
QString setVersion(QString versionName);
QList<FileData> *prepareLocalPathList(QList<FileData>*fileData);
@@ -27,6 +28,9 @@ public:
StreamingVersionData *getCurrentVersionData() const;
void saveVersionToFile(StreamingVersionData *streamingVersion);
void writeVersionsToFile(QList<StreamingVersionData*> version,bool isFirst);
signals:
void sigSaveVersion(StreamingVersionData *versionData);
@@ -36,6 +40,7 @@ private:
StreamingVersionData* currentVersionData;
void copyAllRecurse(QString source, QString destination);
void fillDatas();
};
#endif // ASSETSMANAGER_H

View File

@@ -39,7 +39,7 @@ void CommonClientHandler::sendCurrentVersionToAllClient()
}
}
void CommonClientHandler::slot_AuthChanged()
void CommonClientHandler::slot_ListsInstructorsTraineesChanged()
{
//Проходим все открытые сокеты
foreach(int idSocket, clientsMap->keys())

View File

@@ -20,7 +20,7 @@ public:
void sendNewVersionListToAllClient();
void sendCurrentVersionToAllClient();
void slot_AuthChanged();
void slot_ListsInstructorsTraineesChanged();
void slot_sendPacketToAllClients(PacketType packetType);
//слот обработки сигнала о готовности нового сообщения на отправку клиенту от мессенджера
void slot_msgToClientFromGUI(QString login, QString text);

View File

@@ -2,20 +2,29 @@
#include <clienthandler.h>
ProcessingSystem::ProcessingSystem(ProviderDBLMS* providerDBLMS, QObject *parent):
QObject(parent)
ProcessingSystem::ProcessingSystem(ProviderDBLMS* providerDBLMS, UpdateController* updateController, QObject *parent):
QObject(parent),
providerDBLMS(nullptr),
updateController(nullptr)
{
this->providerDBLMS = providerDBLMS;
this->updateController = updateController;
}
void ProcessingSystem::initialize(ServerLMSWidget *server, DataParser *dataParser, CommonClientHandler *commonClientHandler,Logger *logger)
void ProcessingSystem::initialize(ServerLMSWidget *server,
DataParser *dataParser,
CommonClientHandler *commonClientHandler,
Logger *logger,
UpdateController *updateController)
{
this->commonClientServer = commonClientHandler;
this->dataParser = dataParser;
this->server = server;
this->updateController = updateController;
connect(this,&ProcessingSystem::sigAuthChanged,commonClientHandler, &CommonClientHandler::slot_AuthChanged,Qt::AutoConnection);
connect(this,&ProcessingSystem::sigListsInstructorsTraineesChanged,commonClientHandler, &CommonClientHandler::slot_ListsInstructorsTraineesChanged,Qt::AutoConnection);
connect(this,&ProcessingSystem::sigUpdateListClients,server, &ServerLMSWidget::slotUpdateListClients,Qt::AutoConnection);
connect(this,&ProcessingSystem::sigSetData,updateController,&UpdateController::setDataInfo,Qt::AutoConnection);
connect(this,&ProcessingSystem::signal_msgToClientReady,commonClientHandler, &CommonClientHandler::slot_msgToClientFromGUI);
connect(this,&ProcessingSystem::signal_msgFromClientReady,commonClientHandler, &CommonClientHandler::slot_msgToGUIfromClient);
connect(this,&ProcessingSystem::sigLogMessage,logger,&Logger::addTextToLogger,Qt::QueuedConnection);
@@ -39,19 +48,8 @@ void ProcessingSystem::processingClientAutorization(ClientHandler *client, Clien
QString traineeName;
QByteArray arrayAnswer;
if(providerDBLMS->authorizationTrainee(clientAutorization.Login, clientAutorization.Password, "", ""))
{//Авторизуется обучаемый
client->getClient()->setLogin(clientAutorization.Login);
emit sigUpdateListClients();
//KAV redact
instructorName = providerDBLMS->getMainInstructorName();
traineeName = providerDBLMS->getNameTraineeByLogin(clientAutorization.Login);
arrayAnswer = dataParser->ClientAnswer()->authorization(true, instructorName, traineeName, "trainee", clientAutorization.Login);
}
else if(providerDBLMS->authorizationInstructor(clientAutorization.Login, clientAutorization.Password))
if(providerDBLMS->authorizationInstructor(clientAutorization.Login, clientAutorization.Password))
{//Авторизуется инструктор
client->getClient()->setLogin(clientAutorization.Login);
@@ -62,6 +60,25 @@ void ProcessingSystem::processingClientAutorization(ClientHandler *client, Clien
arrayAnswer = dataParser->ClientAnswer()->authorization(true, instructorName, instructorName, "instructor", clientAutorization.Login);
}
else if(clientAutorization.TypeClient != TypeClientAutorization::TYPE_GUI)
{
if(providerDBLMS->authorizationTrainee(clientAutorization.Login, clientAutorization.Password, "", ""))
{//Авторизуется обучаемый
client->getClient()->setLogin(clientAutorization.Login);
emit sigUpdateListClients();
//KAV redact
instructorName = providerDBLMS->getMainInstructorName();
traineeName = providerDBLMS->getNameTraineeByLogin(clientAutorization.Login);
arrayAnswer = dataParser->ClientAnswer()->authorization(true, instructorName, traineeName, "trainee", clientAutorization.Login);
}
else
{//Никто не авторизовался
arrayAnswer = dataParser->ClientAnswer()->authorization(false, "", "", "", "");
}
}
else
{//Никто не авторизовался
arrayAnswer = dataParser->ClientAnswer()->authorization(false, "", "", "", "");
@@ -69,11 +86,28 @@ void ProcessingSystem::processingClientAutorization(ClientHandler *client, Clien
client->sendXmlAnswer(arrayAnswer);
client->sendVersion();
//Отправка списков задач клиенту Юнити
if(client->getClient()->getIsUnity())
{
QString login = client->getClient()->getLogin();
int id_trainee = providerDBLMS->getIdTraineeByLogin(login);
//AMM
QList<TaskAmmFim> listTasksAMM = providerDBLMS->GetListTasksAMMofTrainee(id_trainee);
QByteArray arrayAnswerTasksAMM = dataParser->DbAnswer()->listTasksAMMofTrainee(true, &listTasksAMM, id_trainee);
client->sendXmlAnswer(arrayAnswerTasksAMM, PacketType::TYPE_XMLANSWER_QUERY_TASKS_AMM_OF_TRAINEE);
//FIM
QList<TaskAmmFim> listTasksFIM = providerDBLMS->GetListTasksFIMofTrainee(id_trainee);
QByteArray arrayAnswerFIM = dataParser->DbAnswer()->listTasksFIMofTrainee(true, &listTasksFIM, id_trainee);
client->sendXmlAnswer(arrayAnswerFIM, PacketType::TYPE_XMLANSWER_QUERY_TASKS_FIM_OF_TRAINEE);
}
QString str = QString(arrayAnswer);
//logger->addTextToLogger("To Client: " + str);
//Извещаем об изменениях в авторизации
emit sigAuthChanged();
emit sigListsInstructorsTraineesChanged();
}
void ProcessingSystem::processingClientDeAutorization(ClientHandler *client, ClientDeAutorization clientDeAutorization)
@@ -118,14 +152,13 @@ void ProcessingSystem::processingClientDeAutorization(ClientHandler *client, Cli
//logger->addTextToLogger("To Client: " + str);
//Извещаем об изменениях в авторизации
emit sigAuthChanged();
emit sigListsInstructorsTraineesChanged();
}
void ProcessingSystem::processingClientQueryToDB(ClientHandler *client, ClientQueryToDB clientQueryToDB, int id, void* data)
{
QByteArray arrayAnswer;
qDebug() << "ProcessingQueryThread " << QThread::currentThreadId();
switch (clientQueryToDB.typeQuery)
{
case TypeQueryToDB::TYPE_QUERY_GET_ALL_LISTS:
@@ -134,6 +167,8 @@ void ProcessingSystem::processingClientQueryToDB(ClientHandler *client, ClientQu
QList<Trainee> listTrainees = providerDBLMS->GetListAllTrainees();
QList<Group> listGroups = providerDBLMS->GetListAllGroups();
QByteArray arrayAnswer;
arrayAnswer = dataParser->DbAnswer()->listInstructors(true, &listInstructors);
client->sendXmlAnswer(arrayAnswer, PacketType::TYPE_XMLANSWER_QUERY_DB__LIST_INSTRUCTORS);
@@ -153,19 +188,19 @@ void ProcessingSystem::processingClientQueryToDB(ClientHandler *client, ClientQu
(*(Instructor*)data).setID(id_new);
providerDBLMS->editInstructor(*(Instructor*)data);
}
emit sigAuthChanged();
emit sigListsInstructorsTraineesChanged();
break;
}
case TypeQueryToDB::TYPE_QUERY_DEL_INSTRUCTOR:
{
providerDBLMS->delInstructor(id);
emit sigAuthChanged();
emit sigListsInstructorsTraineesChanged();
break;
}
case TypeQueryToDB::TYPE_QUERY_EDIT_INSTRUCTOR:
{
providerDBLMS->editInstructor(*(Instructor*)data);
emit sigAuthChanged();
emit sigListsInstructorsTraineesChanged();
break;
}
@@ -178,19 +213,19 @@ void ProcessingSystem::processingClientQueryToDB(ClientHandler *client, ClientQu
(*(Trainee*)data).setID(id_new);
providerDBLMS->editTrainee(*(Trainee*)data);
}
emit sigAuthChanged();
emit sigListsInstructorsTraineesChanged();
break;
}
case TypeQueryToDB::TYPE_QUERY_DEL_TRAINEE:
{
providerDBLMS->delTrainee(id);
emit sigAuthChanged();
emit sigListsInstructorsTraineesChanged();
break;
}
case TypeQueryToDB::TYPE_QUERY_EDIT_TRAINEE:
{
providerDBLMS->editTrainee(*(Trainee*)data);
emit sigAuthChanged();
emit sigListsInstructorsTraineesChanged();
break;
}
@@ -203,27 +238,119 @@ void ProcessingSystem::processingClientQueryToDB(ClientHandler *client, ClientQu
(*(Group*)data).setID(id_new);
providerDBLMS->editGroup(*(Group*)data);
}
emit sigAuthChanged();
emit sigListsInstructorsTraineesChanged();
break;
}
case TypeQueryToDB::TYPE_QUERY_DEL_GROUP:
{
providerDBLMS->delGroup(id);
emit sigAuthChanged();
emit sigListsInstructorsTraineesChanged();
break;
}
case TypeQueryToDB::TYPE_QUERY_EDIT_GROUP:
{
providerDBLMS->editGroup(*(Group*)data);
emit sigAuthChanged();
emit sigListsInstructorsTraineesChanged();
break;
}
case TypeQueryToDB::TYPE_QUERY_ASSIGN_TASK_AMM_TO_TRAINEE:
{
if(int id_new = providerDBLMS->newTaskAMM(*(TaskAmmFim*)data, id))
{
//Отправка списка задач AMM клиенту GUI
sendListTasksAMMofTraineetoClient(client, id);
//Отправка списка задач AMM клиенту Юнити
if(ClientHandler* clientUnity = getUnityClientById(id))
{//Есть такой
sendListTasksAMMofTraineetoClient(clientUnity, id);
}
}
break;
}
case TypeQueryToDB::TYPE_QUERY_ASSIGN_TASK_FIM_TO_TRAINEE:
{
if(int id_new = providerDBLMS->newTaskFIM(*(TaskAmmFim*)data, id))
{
//Отправка списка задач FIM клиенту GUI
sendListTasksFIMofTraineetoClient(client, id);
//Отправка списка задач FIM клиенту Юнити
if(ClientHandler* clientUnity = getUnityClientById(id))
{//Есть такой
sendListTasksFIMofTraineetoClient(clientUnity, id);
}
}
break;
}
case TypeQueryToDB::TYPE_QUERY_GET_TASKS_AMM_FOR_TRAINEE:
{
//Отправка списка задач AMM клиенту GUI
sendListTasksAMMofTraineetoClient(client, id);
break;
}
case TypeQueryToDB::TYPE_QUERY_GET_TASKS_FIM_FOR_TRAINEE:
{
//Отправка списка задач FIM клиенту GUI
sendListTasksFIMofTraineetoClient(client, id);
break;
}
case TypeQueryToDB::TYPE_QUERY_DEL_TASK_AMM_TO_TRAINEE:
{
if(int id_trainee = providerDBLMS->delTaskAMM(id))
{
//Отправка списка задач AMM клиенту GUI
sendListTasksAMMofTraineetoClient(client, id_trainee);
//Отправка списка задач AMM клиенту Юнити
if(ClientHandler* clientUnity = getUnityClientById(id_trainee))
{//Есть такой
sendListTasksAMMofTraineetoClient(clientUnity, id_trainee);
}
}
break;
}
case TypeQueryToDB::TYPE_QUERY_DEL_TASK_FIM_TO_TRAINEE:
{
if(int id_trainee = providerDBLMS->delTaskFIM(id))
{
//Отправка списка задач FIM клиенту GUI
sendListTasksFIMofTraineetoClient(client, id_trainee);
//Отправка списка задач FIM клиенту Юнити
if(ClientHandler* clientUnity = getUnityClientById(id_trainee))
{//Есть такой
sendListTasksFIMofTraineetoClient(clientUnity, id_trainee);
}
}
break;
}
}
}
//client->sendXmlAnswer(arrayAnswer, PacketType::TYPE_XMLANSWER_QUERY_DB_LIST_INSTRUCTORS);
void ProcessingSystem::processingClientQueryTasksXML(ClientHandler *client, ClientQueryTasksXML clientQueryTasksXML)
{
QByteArray arrayAnswer;
//QString str = QString(arrayAnswer);
//logger->addTextToLogger("To Client: " + str);
QString nameFile = "";
QString pathFile = "";
if(clientQueryTasksXML.Type == "fim")
{
nameFile = tasksFIMfileName;
pathFile = updateController->getPathAdditionalFile(nameFile);
client->sendFileBlock(pathFile);
client->sendPacketType(PacketType::TYPE_XMLANSWER_QUERY_TASKS_XML_FIM);
}
else if(clientQueryTasksXML.Type == "amm")
{
nameFile = tasksAMMfileName;
pathFile = updateController->getPathAdditionalFile(nameFile);
client->sendFileBlock(pathFile);
client->sendPacketType(PacketType::TYPE_XMLANSWER_QUERY_TASKS_XML_AMM);
}
}
void ProcessingSystem::processingToClientMessage(ClientHandler *client, ToClientMessage toClientMessage)
@@ -255,15 +382,11 @@ void ProcessingSystem::processingClientNotify(ClientHandler *client, ClientNotif
client->getSocket()->flush();
QStringList listTasks;
//TODO KAV redact
//listTasks = pInstructorsAndTrainees->getDbLMS()->getWhatItDoes(client->getClient()->getLogin());
QByteArray arrayAnswer = dataParser->ClientAnswer()->tasks(listTasks);
client->sendXmlAnswer(arrayAnswer);
QString str = QString(arrayAnswer);
emit sigLogMessage("To Client: " + str);
//QStringList listTasks;
//QByteArray arrayAnswer = dataParser->ClientAnswer()->tasks(listTasks);
//client->sendXmlAnswer(arrayAnswer);
//QString str = QString(arrayAnswer);
//emit sigLogMessage("To Client: " + str);
}
else if(clientNotify.Code == commandDisableClient)
{
@@ -278,6 +401,56 @@ void ProcessingSystem::processingClientNotify(ClientHandler *client, ClientNotif
{
client->sendVersionList();
}
else if(clientNotify.Code == commandCanChangeVersion)
{
if (updateController->getCurrentVersion()->getIsChangeable())
{
client->sigSendNotify(commandChangable);
}
else
{
client->sigSendNotify(commandUnchangable);
}
}
}
void ProcessingSystem::setCurrentDataInfo(DataInfo *dataInfo)
{
emit sigSetData(dataInfo);
}
void ProcessingSystem::sendListTasksAMMofTraineetoClient(ClientHandler *client, int id_trainee)
{
QList<TaskAmmFim> listTasks = providerDBLMS->GetListTasksAMMofTrainee(id_trainee);
QByteArray arrayAnswer = dataParser->DbAnswer()->listTasksAMMofTrainee(true, &listTasks, id_trainee);
client->sendXmlAnswer(arrayAnswer, PacketType::TYPE_XMLANSWER_QUERY_TASKS_AMM_OF_TRAINEE);
}
void ProcessingSystem::sendListTasksFIMofTraineetoClient(ClientHandler *client, int id_trainee)
{
QList<TaskAmmFim> listTasks = providerDBLMS->GetListTasksFIMofTrainee(id_trainee);
QByteArray arrayAnswer = dataParser->DbAnswer()->listTasksFIMofTrainee(true, &listTasks, id_trainee);
client->sendXmlAnswer(arrayAnswer, PacketType::TYPE_XMLANSWER_QUERY_TASKS_FIM_OF_TRAINEE);
}
ClientHandler *ProcessingSystem::getUnityClientById(int id)
{
QString login = providerDBLMS->getLoginTraineeById(id);
//Проходим все открытые сокеты, ищем нужный
foreach(int idSocket, server->getClientsMap().keys())
{
ClientHandler *handler = server->getClientsMap().value(idSocket);
if(handler->getClient()->getLogin() == login)
{
if(handler->getClient()->getIsUnity())
{
return handler;
}
}
}
return nullptr;
}

View File

@@ -21,30 +21,42 @@ class ProcessingSystem : public QObject
{
Q_OBJECT
public:
explicit ProcessingSystem(ProviderDBLMS* providerDBLMS, QObject *parent = nullptr);
explicit ProcessingSystem(ProviderDBLMS* providerDBLMS, UpdateController* updateController, QObject *parent = nullptr);
void initialize(ServerLMSWidget *server,
DataParser* dataParser,
CommonClientHandler *commonClientServer,
Logger *logger,
UpdateController *updateComtroller);
void initialize(ServerLMSWidget *server,DataParser* dataParser,CommonClientHandler *commonClientServer,Logger *logger);
void processingClientAutorization(ClientHandler *client, ClientAutorization clientAutorization);
void processingClientDeAutorization(ClientHandler *client, ClientDeAutorization clientDeAutorization);
void processingClientQueryToDB(ClientHandler *client, ClientQueryToDB clientQueryToDB, int id = 0, void* data = nullptr);
void processingClientQueryTasksXML(ClientHandler *client, ClientQueryTasksXML clientQueryTasksXML);
void processingToClientMessage(ClientHandler *client, ToClientMessage toClientMessage);
void processingFromClientMessage(ClientHandler *client, ClientMessage clientMessage);
void processingClientNotify(ClientHandler *client, ClientNotify clientNotify);
void setCurrentDataInfo(DataInfo *dataInfo);
void sendListTasksAMMofTraineetoClient(ClientHandler* client, int id_trainee);
void sendListTasksFIMofTraineetoClient(ClientHandler* client, int id_trainee);
ClientHandler* getUnityClientById(int id);
signals:
void sigUpdateListClients();
void sigAuthChanged();
void sigListsInstructorsTraineesChanged();
void sigLogMessage(QString log);
void sigAddToMessanger(QString login,QString text);
void signal_msgToClientReady(QString login, QString text);
void signal_msgFromClientReady(QString login, QString text);
void sigSetData(DataInfo *dataInfo);
private:
CommonClientHandler *commonClientServer;
ServerLMSWidget *server;
DataParser *dataParser;
//InstructorsAndTraineesWidget *pInstructorsAndTrainees;
UpdateController *updateController;
ProviderDBLMS* providerDBLMS;
};

View File

@@ -87,24 +87,22 @@ void RecognizeSystem::recognize()
if (!stream.commitTransaction()) continue;
}
if (command == commandUpdateFilesClient) //запускает процесс оновления
if (packetType == PacketType::TYPE_UPDATE)
{
sendSystem->updateFiles(updateController->getFileSendList(),
updateController->getFileDeleteList());
qDebug()<< "Call update";
packetType = PacketType::TYPE_NONE;
command = "";
}
if(command == "check")
if(packetType == PacketType::TYPE_CHECK_VERSION)
{
command = "";
QFile checkFile(clientHash);
checkFile.open(QIODevice::ReadOnly);
updateController->compareFiles(clientHandler,checkFile.readAll());
checkFile.close();
}
if (packetType == PacketType::TYPE_XMLANSWER)
@@ -170,6 +168,16 @@ void RecognizeSystem::recognize()
QFile file(filePath);
// //ПРОВЕРКА НА ИЗМЕНЕНИЕ БАЗОВОЙ ВЕРСИИ
// bool check = checkIsChangeable();
// bool check2 = checkNonStaticData(filePath);
// if(!check && check2)
// {
// packetType = PacketType::TYPE_NONE;
// sendSystem->sendNotify(commandTryBaseChange);
// mutex->unlock();
// return;
// }
if (file.exists())
{
@@ -241,6 +249,17 @@ void RecognizeSystem::recognize()
break;
}
// //ПРОВЕРКА НА ИЗМЕНЕНИЕ БАЗОВОЙ ВЕРСИИ
// bool check = checkIsChangeable();
// bool check2 = checkNonStaticData(filePath);
// if(!check && check2)
// {
// sendSystem->sendNotify(commandTryBaseChange);
// packetType = PacketType::TYPE_NONE;
// mutex->unlock();
// return;
// }
QFile file(filePath);
QFileInfo fileInfo(file);
@@ -264,7 +283,7 @@ void RecognizeSystem::recognize()
file.close();
return;
}
if(socket->waitForReadyRead(TCP_READ_TIMEOUT)){
if(socket->waitForReadyRead(100)){
continue;
}
@@ -295,7 +314,7 @@ void RecognizeSystem::recognize()
tmpBlock.clear();
sizeReceiveData = 0;
countSend = 0;
packetType = PacketType::TYPE_NONE;
}
}
@@ -347,7 +366,7 @@ void RecognizeSystem::recognize()
break;
}
emit sigCopyVersion(result[0],result[1]);
emit sigCopyVersion(result[0],result[1],result[2]);
sendSystem->sendPacketType(PacketType::BUSY);
}
@@ -414,6 +433,18 @@ QString RecognizeSystem::createFullPath(QString path)
return fullPath;
}
bool RecognizeSystem::checkIsChangeable()
{
return updateController->getCurrentVersion()->getIsChangeable();
}
bool RecognizeSystem::checkNonStaticData(QString path)
{
if(path.contains(sharedDataFolderName)) return true;
return false;
}
RecognizeSystem::~RecognizeSystem()
{

Some files were not shown because too many files have changed in this diff Show More