diff --git a/.gitignore b/.gitignore index e5d8405..2c69128 100644 --- a/.gitignore +++ b/.gitignore @@ -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 diff --git a/DOCS/Алексей/Board.md b/DOCS/Алексей/Board.md index b0760b5..b3097fb 100644 --- a/DOCS/Алексей/Board.md +++ b/DOCS/Алексей/Board.md @@ -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 сервера diff --git a/DOCS/Алексей/DataParser scheme.md b/DOCS/Алексей/DataParser scheme.md index 515fe1a..28d350b 100644 --- a/DOCS/Алексей/DataParser scheme.md +++ b/DOCS/Алексей/DataParser scheme.md @@ -231,368 +231,372 @@ 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= +hmMFsgiQKjLhiMcAbCNeQMeXfZcZcBduhSfRMMtNzsMvsLb+UcL3ucJlHuZ1wXuUIYpMj/RThe3ofua/yZDOAAloMCg4AHAATQDYg+zNAA4oBkBZmhJgbgAwA+YJcl9JRTyRrG5E5klUSekIeATaoTKfjIrxw5U0RI5RhAg5STLmWbHKEifHL0gCVS7/qsBU5T/AVOukATQIcsc5RHL85StKJBEXL05fWDckuXKqhpHKm4QT9q5XnL9AIhAq6eMk + +G5ZHLm5QCjRAm3L0gPehhMX7K0icXKo5eJkZMm9yQ8N3L9ALNgRDJbLxDPJkw5WnKa5ekAbiFOjLCCVBs5cwB5/jyB8ABEYUhErKbQuBEDGKQIjcC1AN5YaAkmklA2EJfVzpQNNakiUBHvAYA93AwACAMnBaMvGhzOnHZx5ZKxQxduRYYNnLZQCQByAX7K/5cQATQAgBZiGfALUCQAlsGwAdoLNhcADORwJBArwwkpAMIDyAgUIp1JQAAAKPUrUA + +A/oCIXBU4KocQAASl1AccGUA+YC1AcwAROuACwVyE14AtCrFSDIDMgxCveI1coLlroCbh05Wwx7ajjgxYHDgd9CUgmQDgVwQBUg1uUTKhAFmI1uUJo3staxwgFU6tsD+kLCrsAE5E9Mm5MJoRTmgVsGkJo8Cq3UwKAWAhAEYAF1B5AD8vWwYQGCAeisGaYcuZABgGXl9IBdptPAwyryD0VBiu4egnkv522HAACkHLUXmzoQFiBbAQAA= ``` %% \ No newline at end of file diff --git a/DataBaseLMS/CMakeLists.txt b/DataBaseLMS/CMakeLists.txt index e2eced4..a74f588 100644 --- a/DataBaseLMS/CMakeLists.txt +++ b/DataBaseLMS/CMakeLists.txt @@ -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) diff --git a/DataBaseLMS/DataBaseLMS.pgerd b/DataBaseLMS/DataBaseLMS.pgerd new file mode 100644 index 0000000..efc4590 --- /dev/null +++ b/DataBaseLMS/DataBaseLMS.pgerd @@ -0,0 +1 @@ +{"version":"80700","data":{"id":"1833ae95-5ccc-4798-aa33-bb759b37c92c","offsetX":0,"offsetY":0,"zoom":100,"gridSize":15,"layers":[{"id":"b496c6d6-bd77-4288-8ff9-733c3c7dd4d8","type":"diagram-links","isSvg":true,"transformed":true,"models":{"f9bb0269-3ae7-4326-9fce-56cd5119e8c6":{"id":"f9bb0269-3ae7-4326-9fce-56cd5119e8c6","locked":true,"type":"onetomany","source":"53909bdf-4aa2-481a-8c53-9ee6ca5dafa0","sourcePort":"80ff8eae-75d8-4a4f-9226-a7e28088fc84","target":"34b9cf90-0590-4349-9b0d-3313faf3d15b","targetPort":"fd73da6a-b8eb-4d74-ae17-5e2eb7fc6c49","points":[{"id":"e15aa1ca-4bc2-4b00-9f42-9b55939830b3","type":"point","x":209,"y":183.71875},{"id":"f0e35797-d7bb-4e14-8f1c-bfbf05c2ef1e","type":"point","x":209,"y":333.390625},{"id":"c7fda61c-b3fb-43b6-abf0-e3b2cb5d633b","type":"point","x":209,"y":533.578125}],"labels":[],"width":1,"color":"gray","curvyness":50,"selectedColor":"rgb(0,192,255)","data":{"local_table_uid":"34b9cf90-0590-4349-9b0d-3313faf3d15b","local_column_attnum":3,"referenced_table_uid":"53909bdf-4aa2-481a-8c53-9ee6ca5dafa0","referenced_column_attnum":1}},"940f0aaa-b74b-4c49-bde3-6dbefac0155a":{"id":"940f0aaa-b74b-4c49-bde3-6dbefac0155a","locked":true,"type":"onetomany","selected":false,"source":"71db2a40-71cf-45bf-980c-7678e3a5bc95","sourcePort":"93c89b03-b14a-43fa-a170-94353adcbdb0","target":"a0df494c-7a50-4e1c-a56a-78fa8e8b23bc","targetPort":"de1d7319-481f-42e7-b3fb-65da382dcbbf","points":[{"id":"8cf0cb28-b7a1-4972-9be5-ad1d73c6a183","type":"point","x":609,"y":764.109375},{"id":"44d8f146-eaf6-4e66-80ba-3f0829362754","type":"point","x":851,"y":764.109375},{"id":"bedde3da-6cd6-4180-97b3-31aa97d42b62","type":"point","x":851,"y":596.703125},{"id":"05c6a1a6-998b-43c3-9205-e40b88bf7f76","type":"point","x":1006,"y":596.703125}],"labels":[],"width":1,"color":"gray","curvyness":50,"selectedColor":"rgb(0,192,255)","data":{"local_table_uid":"a0df494c-7a50-4e1c-a56a-78fa8e8b23bc","local_column_attnum":3,"referenced_table_uid":"71db2a40-71cf-45bf-980c-7678e3a5bc95","referenced_column_attnum":1}},"da9cd2a3-a482-4c7d-b56f-e03a77fa1cbf":{"id":"da9cd2a3-a482-4c7d-b56f-e03a77fa1cbf","locked":true,"type":"onetomany","selected":false,"source":"71db2a40-71cf-45bf-980c-7678e3a5bc95","sourcePort":"93c89b03-b14a-43fa-a170-94353adcbdb0","target":"c9c11923-8020-4a9d-b8e4-3ced8a388796","targetPort":"1cd53830-5e8b-446b-9329-e87e9e738e0c","points":[{"id":"7852a048-8d12-433b-b614-4ad99f0e1e05","type":"point","x":609,"y":764.109375},{"id":"2fd1ac6c-0857-4665-b7fd-b390c95b0ca6","type":"point","x":745,"y":764.109375},{"id":"f52aa648-0d25-4929-96b8-0f4a1aaaf493","type":"point","x":745,"y":956.703125},{"id":"43e5b5ae-9971-4263-9377-f3b5fe06c0c9","type":"point","x":1006,"y":956.703125}],"labels":[],"width":1,"color":"gray","curvyness":50,"selectedColor":"rgb(0,192,255)","data":{"local_table_uid":"c9c11923-8020-4a9d-b8e4-3ced8a388796","local_column_attnum":3,"referenced_table_uid":"71db2a40-71cf-45bf-980c-7678e3a5bc95","referenced_column_attnum":1}},"b4c9f742-0892-4e73-98fe-a2e594f7ceab":{"id":"b4c9f742-0892-4e73-98fe-a2e594f7ceab","locked":true,"type":"onetomany","source":"34b9cf90-0590-4349-9b0d-3313faf3d15b","sourcePort":"839b371f-6343-48ec-bdd4-a31d3fddc38c","target":"71db2a40-71cf-45bf-980c-7678e3a5bc95","targetPort":"5fdbde0a-fe69-4ee8-af93-bb05340acc1a","points":[{"id":"f0aaa09f-d58d-439c-99d9-9a52adecd293","type":"point","x":209,"y":462.5},{"id":"a3ac0df5-da69-4356-b33b-3231000b2126","type":"point","x":376,"y":627.625},{"id":"596f7023-eccb-4524-8eba-e62f0fee380a","type":"point","x":376,"y":974.90625}],"labels":[],"width":1,"color":"gray","curvyness":50,"selectedColor":"rgb(0,192,255)","data":{"local_table_uid":"71db2a40-71cf-45bf-980c-7678e3a5bc95","local_column_attnum":11,"referenced_table_uid":"34b9cf90-0590-4349-9b0d-3313faf3d15b","referenced_column_attnum":1}},"66d26303-38db-407f-a2e9-301c3da0c8a5":{"id":"66d26303-38db-407f-a2e9-301c3da0c8a5","locked":true,"type":"onetomany","selected":false,"source":"5757cd17-d261-4285-afaa-9c87eb259e1a","sourcePort":"ba2d98b3-f801-4cdf-a3e0-237d39956fa9","target":"71db2a40-71cf-45bf-980c-7678e3a5bc95","targetPort":"dfe822b2-d69c-4a88-85f5-d1dc32ce85b3","points":[{"id":"b2c9828e-dfbd-4a69-bc9c-8c1e1b8921bd","type":"point","x":249,"y":1109.109375},{"id":"0ba0b97b-47c8-4c84-bc8f-1d8995dfeeda","type":"point","x":299,"y":1109.109375},{"id":"fd388e09-fbde-42c0-bfd2-ca4d6fb9d428","type":"point","x":299,"y":922.8125},{"id":"366dee2c-18da-4a4a-8fe2-0589b94fc8ff","type":"point","x":376,"y":922.8125}],"labels":[],"width":1,"color":"gray","curvyness":50,"selectedColor":"rgb(0,192,255)","data":{"local_table_uid":"71db2a40-71cf-45bf-980c-7678e3a5bc95","local_column_attnum":6,"referenced_table_uid":"5757cd17-d261-4285-afaa-9c87eb259e1a","referenced_column_attnum":1}}}},{"id":"8e1a2013-5ca1-4a86-99b6-e84c5a16e420","type":"diagram-nodes","isSvg":false,"transformed":true,"models":{"53909bdf-4aa2-481a-8c53-9ee6ca5dafa0":{"id":"53909bdf-4aa2-481a-8c53-9ee6ca5dafa0","type":"table","x":5,"y":79.609375,"ports":[{"id":"d708d39f-467e-414f-b621-c8076e877eb7","type":"onetomany","x":6,"y":183.71875,"name":"coll-port-1-left","alignment":"left","parentNode":"53909bdf-4aa2-481a-8c53-9ee6ca5dafa0","links":[]},{"id":"80ff8eae-75d8-4a4f-9226-a7e28088fc84","type":"onetomany","x":179,"y":183.71875,"name":"coll-port-1-right","alignment":"right","parentNode":"53909bdf-4aa2-481a-8c53-9ee6ca5dafa0","links":["f9bb0269-3ae7-4326-9fce-56cd5119e8c6"]}],"name":"Untitled","color":"rgb(0,192,255)","portsInOrder":[],"portsOutOrder":["d708d39f-467e-414f-b621-c8076e877eb7","80ff8eae-75d8-4a4f-9226-a7e28088fc84"],"otherInfo":{"data":{"columns":[{"name":"classroom_id","atttypid":23,"attlen":null,"attnum":1,"attndims":0,"atttypmod":-1,"attacl":[],"attnotnull":true,"attoptions":null,"attfdwoptions":null,"attstattarget":-1,"attstorage":"p","attidentity":"a","defval":null,"typname":"integer","displaytypname":"integer","cltype":"integer","inheritedfrom":null,"inheritedid":null,"elemoid":23,"typnspname":"pg_catalog","defaultstorage":"p","description":null,"indkey":"1","isdup":false,"collspcname":"","is_fk":false,"seclabels":null,"is_sys_column":false,"colconstype":"i","genexpr":null,"relname":"classrooms","is_view_only":false,"attcompression":null,"seqrelid":16478,"seqtypid":23,"seqstart":"1","seqincrement":"1","seqmax":"2147483647","seqmin":"1","seqcache":"1","seqcycle":false,"is_pk":true,"is_primary_key":true,"attprecision":null,"coloptions":[],"edit_types":["bigint","double precision","information_schema.cardinal_number","integer","money","numeric","oid","real","regclass","regcollation","regconfig","regdictionary","regnamespace","regoper","regoperator","regproc","regprocedure","regrole","regtype","smallint"]},{"name":"name","atttypid":1043,"attlen":"30","attnum":2,"attndims":0,"atttypmod":34,"attacl":[],"attnotnull":true,"attoptions":null,"attfdwoptions":null,"attstattarget":-1,"attstorage":"x","attidentity":"","defval":"''::character varying","typname":"character varying","displaytypname":"character varying(30)","cltype":"character varying","inheritedfrom":null,"inheritedid":null,"elemoid":1043,"typnspname":"pg_catalog","defaultstorage":"x","description":null,"indkey":"1","isdup":false,"collspcname":"pg_catalog.\"default\"","is_fk":false,"seclabels":null,"is_sys_column":false,"colconstype":"n","genexpr":null,"relname":"classrooms","is_view_only":false,"attcompression":null,"seqrelid":null,"seqtypid":null,"seqstart":null,"seqincrement":null,"seqmax":null,"seqmin":null,"seqcache":null,"seqcycle":null,"is_pk":false,"is_primary_key":false,"attprecision":null,"coloptions":[],"edit_types":["\"char\"","character","character varying","character varying","information_schema.character_data","information_schema.yes_or_no","name","regclass","text"]}],"name":"classrooms","schema":"public","description":null,"rlspolicy":false,"forcerlspolicy":false,"fillfactor":null,"toast_tuple_target":null,"parallel_workers":null,"relpersistence":false,"primary_key":[{"oid":16465,"name":"educational_classes_pkey","col_count":1,"indnullsnotdistinct":false,"spcname":"pg_default","comment":null,"condeferrable":false,"condeferred":false,"conislocal":true,"fillfactor":null,"columns":[{"column":"classroom_id"}],"include":[]}],"unique_constraint":[],"foreign_key":[]},"note":"","metadata":{"data_failed":false,"is_promise":false}}},"34b9cf90-0590-4349-9b0d-3313faf3d15b":{"id":"34b9cf90-0590-4349-9b0d-3313faf3d15b","type":"table","x":5,"y":358.390625,"ports":[{"id":"7c5957b6-bdf8-40cc-b588-75d9c44beaf2","type":"onetomany","x":6,"y":533.578125,"name":"coll-port-3-left","alignment":"left","parentNode":"34b9cf90-0590-4349-9b0d-3313faf3d15b","links":[]},{"id":"fd73da6a-b8eb-4d74-ae17-5e2eb7fc6c49","type":"onetomany","x":179,"y":533.578125,"name":"coll-port-3-right","alignment":"right","parentNode":"34b9cf90-0590-4349-9b0d-3313faf3d15b","links":["f9bb0269-3ae7-4326-9fce-56cd5119e8c6"]},{"id":"223ec961-e683-4199-914d-2b3bdf1acc3b","type":"onetomany","x":6,"y":462.5,"name":"coll-port-1-left","alignment":"left","parentNode":"34b9cf90-0590-4349-9b0d-3313faf3d15b","links":[]},{"id":"839b371f-6343-48ec-bdd4-a31d3fddc38c","type":"onetomany","x":179,"y":462.5,"name":"coll-port-1-right","alignment":"right","parentNode":"34b9cf90-0590-4349-9b0d-3313faf3d15b","links":["b4c9f742-0892-4e73-98fe-a2e594f7ceab"]}],"name":"Untitled","color":"rgb(0,192,255)","portsInOrder":[],"portsOutOrder":["7c5957b6-bdf8-40cc-b588-75d9c44beaf2","fd73da6a-b8eb-4d74-ae17-5e2eb7fc6c49","223ec961-e683-4199-914d-2b3bdf1acc3b","839b371f-6343-48ec-bdd4-a31d3fddc38c"],"otherInfo":{"data":{"columns":[{"name":"computer_id","atttypid":23,"attlen":null,"attnum":1,"attndims":0,"atttypmod":-1,"attacl":[],"attnotnull":true,"attoptions":null,"attfdwoptions":null,"attstattarget":-1,"attstorage":"p","attidentity":"a","defval":null,"typname":"integer","displaytypname":"integer","cltype":"integer","inheritedfrom":null,"inheritedid":null,"elemoid":23,"typnspname":"pg_catalog","defaultstorage":"p","description":null,"indkey":"1","isdup":false,"collspcname":"","is_fk":false,"seclabels":null,"is_sys_column":false,"colconstype":"i","genexpr":null,"relname":"computers","is_view_only":false,"attcompression":null,"seqrelid":16477,"seqtypid":23,"seqstart":"1","seqincrement":"1","seqmax":"2147483647","seqmin":"1","seqcache":"1","seqcycle":false,"is_pk":true,"is_primary_key":true,"attprecision":null,"coloptions":[],"edit_types":["bigint","double precision","information_schema.cardinal_number","integer","money","numeric","oid","real","regclass","regcollation","regconfig","regdictionary","regnamespace","regoper","regoperator","regproc","regprocedure","regrole","regtype","smallint"]},{"name":"name","atttypid":1043,"attlen":"30","attnum":2,"attndims":0,"atttypmod":34,"attacl":[],"attnotnull":true,"attoptions":null,"attfdwoptions":null,"attstattarget":-1,"attstorage":"x","attidentity":"","defval":"''::character varying","typname":"character varying","displaytypname":"character varying(30)","cltype":"character varying","inheritedfrom":null,"inheritedid":null,"elemoid":1043,"typnspname":"pg_catalog","defaultstorage":"x","description":null,"indkey":"1","isdup":false,"collspcname":"pg_catalog.\"default\"","is_fk":false,"seclabels":null,"is_sys_column":false,"colconstype":"n","genexpr":null,"relname":"computers","is_view_only":false,"attcompression":null,"seqrelid":null,"seqtypid":null,"seqstart":null,"seqincrement":null,"seqmax":null,"seqmin":null,"seqcache":null,"seqcycle":null,"is_pk":false,"is_primary_key":false,"attprecision":null,"coloptions":[],"edit_types":["\"char\"","character","character varying","character varying","information_schema.character_data","information_schema.yes_or_no","name","regclass","text"]},{"name":"classroom_computer","atttypid":23,"attlen":null,"attnum":3,"attndims":0,"atttypmod":-1,"attacl":[],"attnotnull":true,"attoptions":null,"attfdwoptions":null,"attstattarget":-1,"attstorage":"p","attidentity":"","defval":null,"typname":"integer","displaytypname":"integer","cltype":"integer","inheritedfrom":null,"inheritedid":null,"elemoid":23,"typnspname":"pg_catalog","defaultstorage":"p","description":null,"indkey":"1","isdup":false,"collspcname":"","is_fk":true,"seclabels":null,"is_sys_column":false,"colconstype":"n","genexpr":null,"relname":"computers","is_view_only":false,"attcompression":null,"seqrelid":null,"seqtypid":null,"seqstart":null,"seqincrement":null,"seqmax":null,"seqmin":null,"seqcache":null,"seqcycle":null,"is_pk":false,"is_primary_key":false,"attprecision":null,"coloptions":[],"edit_types":["bigint","double precision","information_schema.cardinal_number","integer","integer","money","numeric","oid","real","regclass","regcollation","regconfig","regdictionary","regnamespace","regoper","regoperator","regproc","regprocedure","regrole","regtype","smallint"]},{"name":"ip_address","atttypid":1043,"attlen":"30","attnum":4,"attndims":0,"atttypmod":34,"attacl":[],"attnotnull":true,"attoptions":null,"attfdwoptions":null,"attstattarget":-1,"attstorage":"x","attidentity":"","defval":"''::character varying","typname":"character varying","displaytypname":"character varying(30)","cltype":"character varying","inheritedfrom":null,"inheritedid":null,"elemoid":1043,"typnspname":"pg_catalog","defaultstorage":"x","description":null,"indkey":"1","isdup":false,"collspcname":"pg_catalog.\"default\"","is_fk":false,"seclabels":null,"is_sys_column":false,"colconstype":"n","genexpr":null,"relname":"computers","is_view_only":false,"attcompression":null,"seqrelid":null,"seqtypid":null,"seqstart":null,"seqincrement":null,"seqmax":null,"seqmin":null,"seqcache":null,"seqcycle":null,"is_pk":false,"is_primary_key":false,"attprecision":null,"coloptions":[],"edit_types":["\"char\"","character","character varying","character varying","character varying","information_schema.character_data","information_schema.yes_or_no","name","regclass","text"]}],"name":"computers","schema":"public","description":null,"rlspolicy":false,"forcerlspolicy":false,"fillfactor":null,"toast_tuple_target":null,"parallel_workers":null,"relpersistence":false,"primary_key":[{"oid":16470,"name":"computers_pkey","col_count":1,"indnullsnotdistinct":false,"spcname":"pg_default","comment":null,"condeferrable":false,"condeferred":false,"conislocal":true,"fillfactor":null,"columns":[{"column":"computer_id"}],"include":[]}],"foreign_key":[{"name":"computers_class_fkey","condeferrable":false,"condeferred":false,"confupdtype":"a","confdeltype":"a","confmatchtype":false,"conkey":[3],"confkey":[1],"confrelid":16462,"fknsp":"public","fktab":"computers","refnspoid":2200,"refnsp":"public","reftab":"classrooms","comment":null,"convalidated":true,"conislocal":true,"columns":[{"local_column":"classroom_computer","references":"53909bdf-4aa2-481a-8c53-9ee6ca5dafa0","referenced":"classroom_id","references_table_name":"public.classrooms"}],"remote_schema":"public","remote_table":"classrooms","coveringindex":null,"autoindex":false,"hasindex":false}],"unique_constraint":[]},"note":"","metadata":{"data_failed":false,"is_promise":false}}},"5757cd17-d261-4285-afaa-9c87eb259e1a":{"id":"5757cd17-d261-4285-afaa-9c87eb259e1a","type":"table","selected":false,"x":45,"y":1005,"ports":[{"id":"115fc3a6-8c48-48d9-9660-4454f550fd67","type":"onetomany","x":46,"y":1109.109375,"name":"coll-port-1-left","alignment":"left","parentNode":"5757cd17-d261-4285-afaa-9c87eb259e1a","links":[]},{"id":"ba2d98b3-f801-4cdf-a3e0-237d39956fa9","type":"onetomany","x":219,"y":1109.109375,"name":"coll-port-1-right","alignment":"right","parentNode":"5757cd17-d261-4285-afaa-9c87eb259e1a","links":["66d26303-38db-407f-a2e9-301c3da0c8a5"]}],"name":"Untitled","color":"rgb(0,192,255)","portsInOrder":[],"portsOutOrder":["115fc3a6-8c48-48d9-9660-4454f550fd67","ba2d98b3-f801-4cdf-a3e0-237d39956fa9"],"otherInfo":{"data":{"columns":[{"name":"group_id","atttypid":23,"attlen":null,"attnum":1,"attndims":0,"atttypmod":-1,"attacl":[],"attnotnull":true,"attoptions":null,"attfdwoptions":null,"attstattarget":-1,"attstorage":"p","attidentity":"a","defval":null,"typname":"integer","displaytypname":"integer","cltype":"integer","inheritedfrom":null,"inheritedid":null,"elemoid":23,"typnspname":"pg_catalog","defaultstorage":"p","description":null,"indkey":"1","isdup":false,"collspcname":"","is_fk":false,"seclabels":null,"is_sys_column":false,"colconstype":"i","genexpr":null,"relname":"groups","is_view_only":false,"attcompression":null,"seqrelid":16447,"seqtypid":23,"seqstart":"1","seqincrement":"1","seqmax":"2147483647","seqmin":"1","seqcache":"1","seqcycle":false,"is_pk":true,"is_primary_key":true,"attprecision":null,"coloptions":[],"edit_types":["bigint","double precision","information_schema.cardinal_number","integer","money","numeric","oid","real","regclass","regcollation","regconfig","regdictionary","regnamespace","regoper","regoperator","regproc","regprocedure","regrole","regtype","smallint"]},{"name":"name","atttypid":1043,"attlen":"30","attnum":2,"attndims":0,"atttypmod":34,"attacl":[],"attnotnull":true,"attoptions":null,"attfdwoptions":null,"attstattarget":-1,"attstorage":"x","attidentity":"","defval":"''::character varying","typname":"character varying","displaytypname":"character varying(30)","cltype":"character varying","inheritedfrom":null,"inheritedid":null,"elemoid":1043,"typnspname":"pg_catalog","defaultstorage":"x","description":null,"indkey":"1","isdup":false,"collspcname":"pg_catalog.\"default\"","is_fk":false,"seclabels":null,"is_sys_column":false,"colconstype":"n","genexpr":null,"relname":"groups","is_view_only":false,"attcompression":null,"seqrelid":null,"seqtypid":null,"seqstart":null,"seqincrement":null,"seqmax":null,"seqmin":null,"seqcache":null,"seqcycle":null,"is_pk":false,"is_primary_key":false,"attprecision":null,"coloptions":[],"edit_types":["\"char\"","character","character varying","character varying","information_schema.character_data","information_schema.yes_or_no","name","regclass","text"]}],"name":"groups","schema":"public","description":null,"rlspolicy":false,"forcerlspolicy":false,"fillfactor":null,"toast_tuple_target":null,"parallel_workers":null,"relpersistence":false,"primary_key":[{"oid":16451,"name":"groups_pkey","col_count":1,"indnullsnotdistinct":false,"spcname":"pg_default","comment":null,"condeferrable":false,"condeferred":false,"conislocal":true,"fillfactor":null,"columns":[{"column":"group_id"}],"include":[]}],"unique_constraint":[],"foreign_key":[]},"note":"","metadata":{"data_failed":false,"is_promise":false}}},"7cc17f62-3301-4b64-b41e-4b991a256c6a":{"id":"7cc17f62-3301-4b64-b41e-4b991a256c6a","type":"table","selected":false,"x":375,"y":45,"ports":[],"name":"Untitled","color":"rgb(0,192,255)","portsInOrder":[],"portsOutOrder":[],"otherInfo":{"data":{"columns":[{"name":"instructor_id","atttypid":23,"attlen":null,"attnum":1,"attndims":0,"atttypmod":-1,"attacl":[],"attnotnull":true,"attoptions":null,"attfdwoptions":null,"attstattarget":-1,"attstorage":"p","attidentity":"a","defval":null,"typname":"integer","displaytypname":"integer","cltype":"integer","inheritedfrom":null,"inheritedid":null,"elemoid":23,"typnspname":"pg_catalog","defaultstorage":"p","description":null,"indkey":"1","isdup":false,"collspcname":"","is_fk":false,"seclabels":null,"is_sys_column":false,"colconstype":"i","genexpr":null,"relname":"instructors","is_view_only":false,"attcompression":null,"seqrelid":16435,"seqtypid":23,"seqstart":"1","seqincrement":"1","seqmax":"2147483647","seqmin":"1","seqcache":"1","seqcycle":false,"is_pk":true,"is_primary_key":true,"attprecision":null,"coloptions":[],"edit_types":["bigint","double precision","information_schema.cardinal_number","integer","money","numeric","oid","real","regclass","regcollation","regconfig","regdictionary","regnamespace","regoper","regoperator","regproc","regprocedure","regrole","regtype","smallint"]},{"name":"name","atttypid":1043,"attlen":"30","attnum":2,"attndims":0,"atttypmod":34,"attacl":[],"attnotnull":true,"attoptions":null,"attfdwoptions":null,"attstattarget":-1,"attstorage":"x","attidentity":"","defval":"''::character varying","typname":"character varying","displaytypname":"character varying(30)","cltype":"character varying","inheritedfrom":null,"inheritedid":null,"elemoid":1043,"typnspname":"pg_catalog","defaultstorage":"x","description":null,"indkey":"1","isdup":false,"collspcname":"pg_catalog.\"default\"","is_fk":false,"seclabels":null,"is_sys_column":false,"colconstype":"n","genexpr":null,"relname":"instructors","is_view_only":false,"attcompression":null,"seqrelid":null,"seqtypid":null,"seqstart":null,"seqincrement":null,"seqmax":null,"seqmin":null,"seqcache":null,"seqcycle":null,"is_pk":false,"is_primary_key":false,"attprecision":null,"coloptions":[],"edit_types":["\"char\"","character","character varying","character varying","information_schema.character_data","information_schema.yes_or_no","name","regclass","text"]},{"name":"login","atttypid":1043,"attlen":"10","attnum":3,"attndims":0,"atttypmod":14,"attacl":[],"attnotnull":true,"attoptions":null,"attfdwoptions":null,"attstattarget":-1,"attstorage":"x","attidentity":"","defval":"''::character varying","typname":"character varying","displaytypname":"character varying(10)","cltype":"character varying","inheritedfrom":null,"inheritedid":null,"elemoid":1043,"typnspname":"pg_catalog","defaultstorage":"x","description":null,"indkey":"1","isdup":false,"collspcname":"pg_catalog.\"default\"","is_fk":false,"seclabels":null,"is_sys_column":false,"colconstype":"n","genexpr":null,"relname":"instructors","is_view_only":false,"attcompression":null,"seqrelid":null,"seqtypid":null,"seqstart":null,"seqincrement":null,"seqmax":null,"seqmin":null,"seqcache":null,"seqcycle":null,"is_pk":false,"is_primary_key":false,"attprecision":null,"coloptions":[],"edit_types":["\"char\"","character","character varying","character varying","character varying","information_schema.character_data","information_schema.yes_or_no","name","regclass","text"]},{"name":"password","atttypid":1043,"attlen":"10","attnum":4,"attndims":0,"atttypmod":14,"attacl":[],"attnotnull":true,"attoptions":null,"attfdwoptions":null,"attstattarget":-1,"attstorage":"x","attidentity":"","defval":"''::character varying","typname":"character varying","displaytypname":"character varying(10)","cltype":"character varying","inheritedfrom":null,"inheritedid":null,"elemoid":1043,"typnspname":"pg_catalog","defaultstorage":"x","description":null,"indkey":"1","isdup":false,"collspcname":"pg_catalog.\"default\"","is_fk":false,"seclabels":null,"is_sys_column":false,"colconstype":"n","genexpr":null,"relname":"instructors","is_view_only":false,"attcompression":null,"seqrelid":null,"seqtypid":null,"seqstart":null,"seqincrement":null,"seqmax":null,"seqmin":null,"seqcache":null,"seqcycle":null,"is_pk":false,"is_primary_key":false,"attprecision":null,"coloptions":[],"edit_types":["\"char\"","character","character varying","character varying","character varying","character varying","information_schema.character_data","information_schema.yes_or_no","name","regclass","text"]},{"name":"is_admin","atttypid":16,"attlen":null,"attnum":5,"attndims":0,"atttypmod":-1,"attacl":[],"attnotnull":true,"attoptions":null,"attfdwoptions":null,"attstattarget":-1,"attstorage":"p","attidentity":"","defval":"false","typname":"boolean","displaytypname":"boolean","cltype":"boolean","inheritedfrom":null,"inheritedid":null,"elemoid":16,"typnspname":"pg_catalog","defaultstorage":"p","description":null,"indkey":"1","isdup":false,"collspcname":"","is_fk":false,"seclabels":null,"is_sys_column":false,"colconstype":"n","genexpr":null,"relname":"instructors","is_view_only":false,"attcompression":null,"seqrelid":null,"seqtypid":null,"seqstart":null,"seqincrement":null,"seqmax":null,"seqmin":null,"seqcache":null,"seqcycle":null,"is_pk":false,"is_primary_key":false,"attprecision":null,"coloptions":[],"edit_types":["boolean","character","character varying","text"]},{"name":"archived","atttypid":16,"attlen":null,"attnum":6,"attndims":0,"atttypmod":-1,"attacl":[],"attnotnull":true,"attoptions":null,"attfdwoptions":null,"attstattarget":-1,"attstorage":"p","attidentity":"","defval":"false","typname":"boolean","displaytypname":"boolean","cltype":"boolean","inheritedfrom":null,"inheritedid":null,"elemoid":16,"typnspname":"pg_catalog","defaultstorage":"p","description":null,"indkey":"1","isdup":false,"collspcname":"","is_fk":false,"seclabels":null,"is_sys_column":false,"colconstype":"n","genexpr":null,"relname":"instructors","is_view_only":false,"attcompression":null,"seqrelid":null,"seqtypid":null,"seqstart":null,"seqincrement":null,"seqmax":null,"seqmin":null,"seqcache":null,"seqcycle":null,"is_pk":false,"is_primary_key":false,"attprecision":null,"coloptions":[],"edit_types":["boolean","boolean","character","character varying","text"]},{"name":"logged_in","atttypid":16,"attlen":null,"attnum":7,"attndims":0,"atttypmod":-1,"attacl":[],"attnotnull":true,"attoptions":null,"attfdwoptions":null,"attstattarget":-1,"attstorage":"p","attidentity":"","defval":"false","typname":"boolean","displaytypname":"boolean","cltype":"boolean","inheritedfrom":null,"inheritedid":null,"elemoid":16,"typnspname":"pg_catalog","defaultstorage":"p","description":null,"indkey":"1","isdup":false,"collspcname":"","is_fk":false,"seclabels":null,"is_sys_column":false,"colconstype":"n","genexpr":null,"relname":"instructors","is_view_only":false,"attcompression":null,"seqrelid":null,"seqtypid":null,"seqstart":null,"seqincrement":null,"seqmax":null,"seqmin":null,"seqcache":null,"seqcycle":null,"is_pk":false,"is_primary_key":false,"attprecision":null,"coloptions":[],"edit_types":["boolean","boolean","boolean","character","character varying","text"]}],"name":"instructors","schema":"public","description":null,"rlspolicy":false,"forcerlspolicy":false,"fillfactor":null,"toast_tuple_target":null,"parallel_workers":null,"relpersistence":false,"primary_key":[{"oid":16439,"name":"instructors_pkey","col_count":1,"indnullsnotdistinct":false,"spcname":"pg_default","comment":null,"condeferrable":false,"condeferred":false,"conislocal":true,"fillfactor":null,"columns":[{"column":"instructor_id"}],"include":[]}],"unique_constraint":[],"foreign_key":[]},"note":"","metadata":{"data_failed":false,"is_promise":false}}},"22eb3d01-2146-4012-a7ce-5cfda8f9145a":{"id":"22eb3d01-2146-4012-a7ce-5cfda8f9145a","type":"table","selected":false,"x":660,"y":45,"ports":[],"name":"Untitled","color":"rgb(0,192,255)","portsInOrder":[],"portsOutOrder":[],"otherInfo":{"data":{"columns":[{"name":"malfunction_id","atttypid":23,"attlen":null,"attnum":1,"attndims":0,"atttypmod":-1,"attacl":[],"attnotnull":true,"attoptions":null,"attfdwoptions":null,"attstattarget":-1,"attstorage":"p","attidentity":"a","defval":null,"typname":"integer","displaytypname":"integer","cltype":"integer","inheritedfrom":null,"inheritedid":null,"elemoid":23,"typnspname":"pg_catalog","defaultstorage":"p","description":null,"indkey":"1","isdup":false,"collspcname":"","is_fk":false,"seclabels":null,"is_sys_column":false,"colconstype":"i","genexpr":null,"relname":"malfunctions","is_view_only":false,"attcompression":null,"seqrelid":49315,"seqtypid":23,"seqstart":"1","seqincrement":"1","seqmax":"2147483647","seqmin":"1","seqcache":"1","seqcycle":false,"is_pk":true,"is_primary_key":true,"attprecision":null,"coloptions":[],"edit_types":["bigint","double precision","information_schema.cardinal_number","integer","money","numeric","oid","real","regclass","regcollation","regconfig","regdictionary","regnamespace","regoper","regoperator","regproc","regprocedure","regrole","regtype","smallint"]},{"name":"num","atttypid":23,"attlen":null,"attnum":2,"attndims":0,"atttypmod":-1,"attacl":[],"attnotnull":true,"attoptions":null,"attfdwoptions":null,"attstattarget":-1,"attstorage":"p","attidentity":"","defval":"0","typname":"integer","displaytypname":"integer","cltype":"integer","inheritedfrom":null,"inheritedid":null,"elemoid":23,"typnspname":"pg_catalog","defaultstorage":"p","description":null,"indkey":"1","isdup":false,"collspcname":"","is_fk":false,"seclabels":null,"is_sys_column":false,"colconstype":"n","genexpr":null,"relname":"malfunctions","is_view_only":false,"attcompression":null,"seqrelid":null,"seqtypid":null,"seqstart":null,"seqincrement":null,"seqmax":null,"seqmin":null,"seqcache":null,"seqcycle":null,"is_pk":false,"is_primary_key":false,"attprecision":null,"coloptions":[],"edit_types":["bigint","double precision","information_schema.cardinal_number","integer","integer","money","numeric","oid","real","regclass","regcollation","regconfig","regdictionary","regnamespace","regoper","regoperator","regproc","regprocedure","regrole","regtype","smallint"]},{"name":"dm_code","atttypid":1043,"attlen":null,"attnum":3,"attndims":0,"atttypmod":-1,"attacl":[],"attnotnull":true,"attoptions":null,"attfdwoptions":null,"attstattarget":-1,"attstorage":"x","attidentity":"","defval":"''::character varying","typname":"character varying","displaytypname":"character varying","cltype":"character varying","inheritedfrom":null,"inheritedid":null,"elemoid":1043,"typnspname":"pg_catalog","defaultstorage":"x","description":null,"indkey":"1","isdup":false,"collspcname":"pg_catalog.\"default\"","is_fk":false,"seclabels":null,"is_sys_column":false,"colconstype":"n","genexpr":null,"relname":"malfunctions","is_view_only":false,"attcompression":null,"seqrelid":null,"seqtypid":null,"seqstart":null,"seqincrement":null,"seqmax":null,"seqmin":null,"seqcache":null,"seqcycle":null,"is_pk":false,"is_primary_key":false,"attprecision":null,"coloptions":[],"edit_types":["\"char\"","character","character varying","character varying","information_schema.character_data","information_schema.yes_or_no","name","regclass","text"]},{"name":"description","atttypid":1043,"attlen":null,"attnum":4,"attndims":0,"atttypmod":-1,"attacl":[],"attnotnull":true,"attoptions":null,"attfdwoptions":null,"attstattarget":-1,"attstorage":"x","attidentity":"","defval":"''::character varying","typname":"character varying","displaytypname":"character varying","cltype":"character varying","inheritedfrom":null,"inheritedid":null,"elemoid":1043,"typnspname":"pg_catalog","defaultstorage":"x","description":null,"indkey":"1","isdup":false,"collspcname":"pg_catalog.\"default\"","is_fk":false,"seclabels":null,"is_sys_column":false,"colconstype":"n","genexpr":null,"relname":"malfunctions","is_view_only":false,"attcompression":null,"seqrelid":null,"seqtypid":null,"seqstart":null,"seqincrement":null,"seqmax":null,"seqmin":null,"seqcache":null,"seqcycle":null,"is_pk":false,"is_primary_key":false,"attprecision":null,"coloptions":[],"edit_types":["\"char\"","character","character varying","character varying","character varying","information_schema.character_data","information_schema.yes_or_no","name","regclass","text"]},{"name":"task_fim_malf","atttypid":23,"attlen":null,"attnum":5,"attndims":0,"atttypmod":-1,"attacl":[],"attnotnull":true,"attoptions":null,"attfdwoptions":null,"attstattarget":-1,"attstorage":"p","attidentity":"","defval":null,"typname":"integer","displaytypname":"integer","cltype":"integer","inheritedfrom":null,"inheritedid":null,"elemoid":23,"typnspname":"pg_catalog","defaultstorage":"p","description":null,"indkey":"1","isdup":false,"collspcname":"","is_fk":false,"seclabels":null,"is_sys_column":false,"colconstype":"n","genexpr":null,"relname":"malfunctions","is_view_only":false,"attcompression":null,"seqrelid":null,"seqtypid":null,"seqstart":null,"seqincrement":null,"seqmax":null,"seqmin":null,"seqcache":null,"seqcycle":null,"is_pk":false,"is_primary_key":false,"attprecision":null,"coloptions":[],"edit_types":["bigint","double precision","information_schema.cardinal_number","integer","integer","integer","money","numeric","oid","real","regclass","regcollation","regconfig","regdictionary","regnamespace","regoper","regoperator","regproc","regprocedure","regrole","regtype","smallint"]}],"name":"malfunctions","schema":"public","description":null,"rlspolicy":false,"forcerlspolicy":false,"fillfactor":null,"toast_tuple_target":null,"parallel_workers":null,"relpersistence":false,"primary_key":[{"oid":49298,"name":"malfunctions_pkey","col_count":1,"indnullsnotdistinct":false,"spcname":"pg_default","comment":null,"condeferrable":false,"condeferred":false,"conislocal":true,"fillfactor":null,"columns":[{"column":"malfunction_id"}],"include":[]}],"unique_constraint":[],"foreign_key":[]},"note":"","metadata":{"data_failed":false,"is_promise":false}}},"a0df494c-7a50-4e1c-a56a-78fa8e8b23bc":{"id":"a0df494c-7a50-4e1c-a56a-78fa8e8b23bc","type":"table","selected":true,"x":1035,"y":450,"ports":[{"id":"de1d7319-481f-42e7-b3fb-65da382dcbbf","type":"onetomany","x":1036,"y":596.703125,"name":"coll-port-3-left","alignment":"left","parentNode":"a0df494c-7a50-4e1c-a56a-78fa8e8b23bc","links":["940f0aaa-b74b-4c49-bde3-6dbefac0155a"]},{"id":"dc95baf0-b380-4ae2-8761-dd7650c52af4","type":"onetomany","x":1209,"y":596.703125,"name":"coll-port-3-right","alignment":"right","parentNode":"a0df494c-7a50-4e1c-a56a-78fa8e8b23bc","links":[]}],"name":"Untitled","color":"rgb(0,192,255)","portsInOrder":[],"portsOutOrder":["de1d7319-481f-42e7-b3fb-65da382dcbbf","dc95baf0-b380-4ae2-8761-dd7650c52af4"],"otherInfo":{"data":{"columns":[{"name":"task_id","atttypid":23,"attlen":null,"attnum":1,"attndims":0,"atttypmod":-1,"attacl":[],"attnotnull":true,"attoptions":null,"attfdwoptions":null,"attstattarget":-1,"attstorage":"p","attidentity":"a","defval":null,"typname":"integer","displaytypname":"integer","cltype":"integer","inheritedfrom":null,"inheritedid":null,"elemoid":23,"typnspname":"pg_catalog","defaultstorage":"p","description":null,"indkey":"1","isdup":false,"collspcname":"","is_fk":false,"seclabels":null,"is_sys_column":false,"colconstype":"i","genexpr":null,"relname":"tasks_amm","is_view_only":false,"attcompression":null,"seqrelid":16506,"seqtypid":23,"seqstart":"1","seqincrement":"1","seqmax":"2147483647","seqmin":"1","seqcache":"1","seqcycle":false,"is_pk":true,"is_primary_key":true,"attprecision":null,"coloptions":[],"edit_types":["bigint","double precision","information_schema.cardinal_number","integer","money","numeric","oid","real","regclass","regcollation","regconfig","regdictionary","regnamespace","regoper","regoperator","regproc","regprocedure","regrole","regtype","smallint"]},{"name":"dm_code","atttypid":1043,"attlen":null,"attnum":2,"attndims":0,"atttypmod":-1,"attacl":[],"attnotnull":true,"attoptions":null,"attfdwoptions":null,"attstattarget":-1,"attstorage":"x","attidentity":"","defval":"''::character varying","typname":"character varying","displaytypname":"character varying","cltype":"character varying","inheritedfrom":null,"inheritedid":null,"elemoid":1043,"typnspname":"pg_catalog","defaultstorage":"x","description":null,"indkey":"1","isdup":false,"collspcname":"pg_catalog.\"default\"","is_fk":false,"seclabels":null,"is_sys_column":false,"colconstype":"n","genexpr":null,"relname":"tasks_amm","is_view_only":false,"attcompression":null,"seqrelid":null,"seqtypid":null,"seqstart":null,"seqincrement":null,"seqmax":null,"seqmin":null,"seqcache":null,"seqcycle":null,"is_pk":false,"is_primary_key":false,"attprecision":null,"coloptions":[],"edit_types":["\"char\"","character","character varying","character varying","information_schema.character_data","information_schema.yes_or_no","name","regclass","text"]},{"name":"trainee_task","atttypid":23,"attlen":null,"attnum":3,"attndims":0,"atttypmod":-1,"attacl":[],"attnotnull":true,"attoptions":null,"attfdwoptions":null,"attstattarget":-1,"attstorage":"p","attidentity":"","defval":null,"typname":"integer","displaytypname":"integer","cltype":"integer","inheritedfrom":null,"inheritedid":null,"elemoid":23,"typnspname":"pg_catalog","defaultstorage":"p","description":null,"indkey":"1","isdup":false,"collspcname":"","is_fk":true,"seclabels":null,"is_sys_column":false,"colconstype":"n","genexpr":null,"relname":"tasks_amm","is_view_only":false,"attcompression":null,"seqrelid":null,"seqtypid":null,"seqstart":null,"seqincrement":null,"seqmax":null,"seqmin":null,"seqcache":null,"seqcycle":null,"is_pk":false,"is_primary_key":false,"attprecision":null,"coloptions":[],"edit_types":["bigint","double precision","information_schema.cardinal_number","integer","integer","money","numeric","oid","real","regclass","regcollation","regconfig","regdictionary","regnamespace","regoper","regoperator","regproc","regprocedure","regrole","regtype","smallint"]},{"name":"title","atttypid":1043,"attlen":null,"attnum":4,"attndims":0,"atttypmod":-1,"attacl":[],"attnotnull":true,"attoptions":null,"attfdwoptions":null,"attstattarget":-1,"attstorage":"x","attidentity":"","defval":"''::character varying","typname":"character varying","displaytypname":"character varying","cltype":"character varying","inheritedfrom":null,"inheritedid":null,"elemoid":1043,"typnspname":"pg_catalog","defaultstorage":"x","description":null,"indkey":"1","isdup":false,"collspcname":"pg_catalog.\"default\"","is_fk":false,"seclabels":null,"is_sys_column":false,"colconstype":"n","genexpr":null,"relname":"tasks_amm","is_view_only":false,"attcompression":null,"seqrelid":null,"seqtypid":null,"seqstart":null,"seqincrement":null,"seqmax":null,"seqmin":null,"seqcache":null,"seqcycle":null,"is_pk":false,"is_primary_key":false,"attprecision":null,"coloptions":[],"edit_types":["\"char\"","character","character varying","character varying","character varying","information_schema.character_data","information_schema.yes_or_no","name","regclass","text"]}],"name":"tasks_amm","schema":"public","description":null,"rlspolicy":false,"forcerlspolicy":false,"fillfactor":null,"toast_tuple_target":null,"parallel_workers":null,"relpersistence":false,"primary_key":[{"oid":16489,"name":"tasks_pkey","col_count":1,"indnullsnotdistinct":false,"spcname":"pg_default","comment":null,"condeferrable":false,"condeferred":false,"conislocal":true,"fillfactor":null,"columns":[{"column":"task_id"}],"include":[]}],"foreign_key":[{"name":"trainees_tasks","condeferrable":false,"condeferred":false,"confupdtype":"a","confdeltype":"a","confmatchtype":false,"conkey":[3],"confkey":[1],"confrelid":16442,"fknsp":"public","fktab":"tasks_amm","refnspoid":2200,"refnsp":"public","reftab":"trainees","comment":null,"convalidated":false,"conislocal":true,"columns":[{"local_column":"trainee_task","references":"71db2a40-71cf-45bf-980c-7678e3a5bc95","referenced":"trainee_id","references_table_name":"public.trainees"}],"remote_schema":"public","remote_table":"trainees","coveringindex":null,"autoindex":false,"hasindex":false}],"unique_constraint":[]},"note":"","metadata":{"data_failed":false,"is_promise":false}}},"c9c11923-8020-4a9d-b8e4-3ced8a388796":{"id":"c9c11923-8020-4a9d-b8e4-3ced8a388796","type":"table","selected":false,"x":1035,"y":810,"ports":[{"id":"1cd53830-5e8b-446b-9329-e87e9e738e0c","type":"onetomany","x":1036,"y":956.703125,"name":"coll-port-3-left","alignment":"left","parentNode":"c9c11923-8020-4a9d-b8e4-3ced8a388796","links":["da9cd2a3-a482-4c7d-b56f-e03a77fa1cbf"]},{"id":"e565bb9a-04c7-4d10-8366-baada0244430","type":"onetomany","x":1209,"y":956.703125,"name":"coll-port-3-right","alignment":"right","parentNode":"c9c11923-8020-4a9d-b8e4-3ced8a388796","links":[]}],"name":"Untitled","color":"rgb(0,192,255)","portsInOrder":[],"portsOutOrder":["1cd53830-5e8b-446b-9329-e87e9e738e0c","e565bb9a-04c7-4d10-8366-baada0244430"],"otherInfo":{"data":{"columns":[{"name":"task_id","atttypid":23,"attlen":null,"attnum":1,"attndims":0,"atttypmod":-1,"attacl":[],"attnotnull":true,"attoptions":null,"attfdwoptions":null,"attstattarget":-1,"attstorage":"p","attidentity":"a","defval":null,"typname":"integer","displaytypname":"integer","cltype":"integer","inheritedfrom":null,"inheritedid":null,"elemoid":23,"typnspname":"pg_catalog","defaultstorage":"p","description":null,"indkey":"1","isdup":false,"collspcname":"","is_fk":false,"seclabels":null,"is_sys_column":false,"colconstype":"i","genexpr":null,"relname":"tasks_fim","is_view_only":false,"attcompression":null,"seqrelid":41108,"seqtypid":23,"seqstart":"1","seqincrement":"1","seqmax":"2147483647","seqmin":"1","seqcache":"1","seqcycle":false,"is_pk":true,"is_primary_key":true,"attprecision":null,"coloptions":[],"edit_types":["bigint","double precision","information_schema.cardinal_number","integer","money","numeric","oid","real","regclass","regcollation","regconfig","regdictionary","regnamespace","regoper","regoperator","regproc","regprocedure","regrole","regtype","smallint"]},{"name":"title","atttypid":1043,"attlen":null,"attnum":2,"attndims":0,"atttypmod":-1,"attacl":[],"attnotnull":true,"attoptions":null,"attfdwoptions":null,"attstattarget":-1,"attstorage":"x","attidentity":"","defval":"'<title>'::character varying","typname":"character varying","displaytypname":"character varying","cltype":"character varying","inheritedfrom":null,"inheritedid":null,"elemoid":1043,"typnspname":"pg_catalog","defaultstorage":"x","description":null,"indkey":"1","isdup":false,"collspcname":"pg_catalog.\"default\"","is_fk":false,"seclabels":null,"is_sys_column":false,"colconstype":"n","genexpr":null,"relname":"tasks_fim","is_view_only":false,"attcompression":null,"seqrelid":null,"seqtypid":null,"seqstart":null,"seqincrement":null,"seqmax":null,"seqmin":null,"seqcache":null,"seqcycle":null,"is_pk":false,"is_primary_key":false,"attprecision":null,"coloptions":[],"edit_types":["\"char\"","character","character varying","character varying","information_schema.character_data","information_schema.yes_or_no","name","regclass","text"]},{"name":"trainee_task","atttypid":23,"attlen":null,"attnum":3,"attndims":0,"atttypmod":-1,"attacl":[],"attnotnull":true,"attoptions":null,"attfdwoptions":null,"attstattarget":-1,"attstorage":"p","attidentity":"","defval":null,"typname":"integer","displaytypname":"integer","cltype":"integer","inheritedfrom":null,"inheritedid":null,"elemoid":23,"typnspname":"pg_catalog","defaultstorage":"p","description":null,"indkey":"1","isdup":false,"collspcname":"","is_fk":true,"seclabels":null,"is_sys_column":false,"colconstype":"n","genexpr":null,"relname":"tasks_fim","is_view_only":false,"attcompression":null,"seqrelid":null,"seqtypid":null,"seqstart":null,"seqincrement":null,"seqmax":null,"seqmin":null,"seqcache":null,"seqcycle":null,"is_pk":false,"is_primary_key":false,"attprecision":null,"coloptions":[],"edit_types":["bigint","double precision","information_schema.cardinal_number","integer","integer","money","numeric","oid","real","regclass","regcollation","regconfig","regdictionary","regnamespace","regoper","regoperator","regproc","regprocedure","regrole","regtype","smallint"]}],"name":"tasks_fim","schema":"public","description":null,"rlspolicy":false,"forcerlspolicy":false,"fillfactor":null,"toast_tuple_target":null,"parallel_workers":null,"relpersistence":false,"primary_key":[{"oid":41106,"name":"task_fim_pkey","col_count":1,"indnullsnotdistinct":false,"spcname":"pg_default","comment":null,"condeferrable":false,"condeferred":false,"conislocal":true,"fillfactor":null,"columns":[{"column":"task_id"}],"include":[]}],"foreign_key":[{"name":"trainees_tasks","condeferrable":false,"condeferred":false,"confupdtype":"a","confdeltype":"a","confmatchtype":false,"conkey":[3],"confkey":[1],"confrelid":16442,"fknsp":"public","fktab":"tasks_fim","refnspoid":2200,"refnsp":"public","reftab":"trainees","comment":null,"convalidated":false,"conislocal":true,"columns":[{"local_column":"trainee_task","references":"71db2a40-71cf-45bf-980c-7678e3a5bc95","referenced":"trainee_id","references_table_name":"public.trainees"}],"remote_schema":"public","remote_table":"trainees","coveringindex":null,"autoindex":false,"hasindex":false}],"unique_constraint":[]},"note":"","metadata":{"data_failed":false,"is_promise":false}}},"71db2a40-71cf-45bf-980c-7678e3a5bc95":{"id":"71db2a40-71cf-45bf-980c-7678e3a5bc95","type":"table","selected":false,"x":405,"y":660,"ports":[{"id":"c4597e0d-4d1c-41ae-83bb-ebeb67da1ce3","type":"onetomany","x":406,"y":764.109375,"name":"coll-port-1-left","alignment":"left","parentNode":"71db2a40-71cf-45bf-980c-7678e3a5bc95","links":[]},{"id":"93c89b03-b14a-43fa-a170-94353adcbdb0","type":"onetomany","x":579,"y":764.109375,"name":"coll-port-1-right","alignment":"right","parentNode":"71db2a40-71cf-45bf-980c-7678e3a5bc95","links":["940f0aaa-b74b-4c49-bde3-6dbefac0155a","da9cd2a3-a482-4c7d-b56f-e03a77fa1cbf"]},{"id":"5fdbde0a-fe69-4ee8-af93-bb05340acc1a","type":"onetomany","x":406,"y":974.90625,"name":"coll-port-11-left","alignment":"left","parentNode":"71db2a40-71cf-45bf-980c-7678e3a5bc95","links":["b4c9f742-0892-4e73-98fe-a2e594f7ceab"]},{"id":"8c6462ca-10f1-4ff5-a5a2-6403b1ffcd3c","type":"onetomany","x":579,"y":974.90625,"name":"coll-port-11-right","alignment":"right","parentNode":"71db2a40-71cf-45bf-980c-7678e3a5bc95","links":[]},{"id":"dfe822b2-d69c-4a88-85f5-d1dc32ce85b3","type":"onetomany","x":406,"y":922.8125,"name":"coll-port-6-left","alignment":"left","parentNode":"71db2a40-71cf-45bf-980c-7678e3a5bc95","links":["66d26303-38db-407f-a2e9-301c3da0c8a5"]},{"id":"bd348bff-aef5-45a4-b2d6-6119709d7c48","type":"onetomany","x":579,"y":922.8125,"name":"coll-port-6-right","alignment":"right","parentNode":"71db2a40-71cf-45bf-980c-7678e3a5bc95","links":[]}],"name":"Untitled","color":"rgb(0,192,255)","portsInOrder":[],"portsOutOrder":["c4597e0d-4d1c-41ae-83bb-ebeb67da1ce3","93c89b03-b14a-43fa-a170-94353adcbdb0","5fdbde0a-fe69-4ee8-af93-bb05340acc1a","8c6462ca-10f1-4ff5-a5a2-6403b1ffcd3c","dfe822b2-d69c-4a88-85f5-d1dc32ce85b3","bd348bff-aef5-45a4-b2d6-6119709d7c48"],"otherInfo":{"data":{"columns":[{"name":"trainee_id","atttypid":23,"attlen":null,"attnum":1,"attndims":0,"atttypmod":-1,"attacl":[],"attnotnull":true,"attoptions":null,"attfdwoptions":null,"attstattarget":-1,"attstorage":"p","attidentity":"a","defval":null,"typname":"integer","displaytypname":"integer","cltype":"integer","inheritedfrom":null,"inheritedid":null,"elemoid":23,"typnspname":"pg_catalog","defaultstorage":"p","description":null,"indkey":"1","isdup":false,"collspcname":"","is_fk":false,"seclabels":null,"is_sys_column":false,"colconstype":"i","genexpr":null,"relname":"trainees","is_view_only":false,"attcompression":null,"seqrelid":16441,"seqtypid":23,"seqstart":"1","seqincrement":"1","seqmax":"2147483647","seqmin":"1","seqcache":"1","seqcycle":false,"is_pk":true,"is_primary_key":true,"attprecision":null,"coloptions":[],"edit_types":["bigint","double precision","information_schema.cardinal_number","integer","money","numeric","oid","real","regclass","regcollation","regconfig","regdictionary","regnamespace","regoper","regoperator","regproc","regprocedure","regrole","regtype","smallint"]},{"name":"name","atttypid":1043,"attlen":"30","attnum":2,"attndims":0,"atttypmod":34,"attacl":[],"attnotnull":true,"attoptions":null,"attfdwoptions":null,"attstattarget":-1,"attstorage":"x","attidentity":"","defval":"'<trainee>'::character varying","typname":"character varying","displaytypname":"character varying(30)","cltype":"character varying","inheritedfrom":null,"inheritedid":null,"elemoid":1043,"typnspname":"pg_catalog","defaultstorage":"x","description":null,"indkey":"1","isdup":false,"collspcname":"pg_catalog.\"default\"","is_fk":false,"seclabels":null,"is_sys_column":false,"colconstype":"n","genexpr":null,"relname":"trainees","is_view_only":false,"attcompression":null,"seqrelid":null,"seqtypid":null,"seqstart":null,"seqincrement":null,"seqmax":null,"seqmin":null,"seqcache":null,"seqcycle":null,"is_pk":false,"is_primary_key":false,"attprecision":null,"coloptions":[],"edit_types":["\"char\"","character","character varying","character varying","information_schema.character_data","information_schema.yes_or_no","name","regclass","text"]},{"name":"login","atttypid":1043,"attlen":"10","attnum":3,"attndims":0,"atttypmod":14,"attacl":[],"attnotnull":true,"attoptions":null,"attfdwoptions":null,"attstattarget":-1,"attstorage":"x","attidentity":"","defval":"'<login>'::character varying","typname":"character varying","displaytypname":"character varying(10)","cltype":"character varying","inheritedfrom":null,"inheritedid":null,"elemoid":1043,"typnspname":"pg_catalog","defaultstorage":"x","description":null,"indkey":"1","isdup":false,"collspcname":"pg_catalog.\"default\"","is_fk":false,"seclabels":null,"is_sys_column":false,"colconstype":"n","genexpr":null,"relname":"trainees","is_view_only":false,"attcompression":null,"seqrelid":null,"seqtypid":null,"seqstart":null,"seqincrement":null,"seqmax":null,"seqmin":null,"seqcache":null,"seqcycle":null,"is_pk":false,"is_primary_key":false,"attprecision":null,"coloptions":[],"edit_types":["\"char\"","character","character varying","character varying","character varying","information_schema.character_data","information_schema.yes_or_no","name","regclass","text"]},{"name":"password","atttypid":1043,"attlen":"10","attnum":4,"attndims":0,"atttypmod":14,"attacl":[],"attnotnull":true,"attoptions":null,"attfdwoptions":null,"attstattarget":-1,"attstorage":"x","attidentity":"","defval":"'<password>'::character varying","typname":"character varying","displaytypname":"character varying(10)","cltype":"character varying","inheritedfrom":null,"inheritedid":null,"elemoid":1043,"typnspname":"pg_catalog","defaultstorage":"x","description":null,"indkey":"1","isdup":false,"collspcname":"pg_catalog.\"default\"","is_fk":false,"seclabels":null,"is_sys_column":false,"colconstype":"n","genexpr":null,"relname":"trainees","is_view_only":false,"attcompression":null,"seqrelid":null,"seqtypid":null,"seqstart":null,"seqincrement":null,"seqmax":null,"seqmin":null,"seqcache":null,"seqcycle":null,"is_pk":false,"is_primary_key":false,"attprecision":null,"coloptions":[],"edit_types":["\"char\"","character","character varying","character varying","character varying","character varying","information_schema.character_data","information_schema.yes_or_no","name","regclass","text"]},{"name":"archived","atttypid":16,"attlen":null,"attnum":5,"attndims":0,"atttypmod":-1,"attacl":[],"attnotnull":true,"attoptions":null,"attfdwoptions":null,"attstattarget":-1,"attstorage":"p","attidentity":"","defval":"false","typname":"boolean","displaytypname":"boolean","cltype":"boolean","inheritedfrom":null,"inheritedid":null,"elemoid":16,"typnspname":"pg_catalog","defaultstorage":"p","description":null,"indkey":"1","isdup":false,"collspcname":"","is_fk":false,"seclabels":null,"is_sys_column":false,"colconstype":"n","genexpr":null,"relname":"trainees","is_view_only":false,"attcompression":null,"seqrelid":null,"seqtypid":null,"seqstart":null,"seqincrement":null,"seqmax":null,"seqmin":null,"seqcache":null,"seqcycle":null,"is_pk":false,"is_primary_key":false,"attprecision":null,"coloptions":[],"edit_types":["boolean","character","character varying","text"]},{"name":"group_trainee","atttypid":23,"attlen":null,"attnum":6,"attndims":0,"atttypmod":-1,"attacl":[],"attnotnull":true,"attoptions":null,"attfdwoptions":null,"attstattarget":-1,"attstorage":"p","attidentity":"","defval":null,"typname":"integer","displaytypname":"integer","cltype":"integer","inheritedfrom":null,"inheritedid":null,"elemoid":23,"typnspname":"pg_catalog","defaultstorage":"p","description":null,"indkey":"1","isdup":false,"collspcname":"","is_fk":true,"seclabels":null,"is_sys_column":false,"colconstype":"n","genexpr":null,"relname":"trainees","is_view_only":false,"attcompression":null,"seqrelid":null,"seqtypid":null,"seqstart":null,"seqincrement":null,"seqmax":null,"seqmin":null,"seqcache":null,"seqcycle":null,"is_pk":false,"is_primary_key":false,"attprecision":null,"coloptions":[],"edit_types":["bigint","double precision","information_schema.cardinal_number","integer","integer","money","numeric","oid","real","regclass","regcollation","regconfig","regdictionary","regnamespace","regoper","regoperator","regproc","regprocedure","regrole","regtype","smallint"]},{"name":"logged_in","atttypid":16,"attlen":null,"attnum":7,"attndims":0,"atttypmod":-1,"attacl":[],"attnotnull":true,"attoptions":null,"attfdwoptions":null,"attstattarget":-1,"attstorage":"p","attidentity":"","defval":"false","typname":"boolean","displaytypname":"boolean","cltype":"boolean","inheritedfrom":null,"inheritedid":null,"elemoid":16,"typnspname":"pg_catalog","defaultstorage":"p","description":null,"indkey":"1","isdup":false,"collspcname":"","is_fk":false,"seclabels":null,"is_sys_column":false,"colconstype":"n","genexpr":null,"relname":"trainees","is_view_only":false,"attcompression":null,"seqrelid":null,"seqtypid":null,"seqstart":null,"seqincrement":null,"seqmax":null,"seqmin":null,"seqcache":null,"seqcycle":null,"is_pk":false,"is_primary_key":false,"attprecision":null,"coloptions":[],"edit_types":["boolean","boolean","character","character varying","text"]},{"name":"computer_trainee","atttypid":23,"attlen":null,"attnum":11,"attndims":0,"atttypmod":-1,"attacl":[],"attnotnull":false,"attoptions":null,"attfdwoptions":null,"attstattarget":-1,"attstorage":"p","attidentity":"","defval":null,"typname":"integer","displaytypname":"integer","cltype":"integer","inheritedfrom":null,"inheritedid":null,"elemoid":23,"typnspname":"pg_catalog","defaultstorage":"p","description":null,"indkey":"1","isdup":false,"collspcname":"","is_fk":true,"seclabels":null,"is_sys_column":false,"colconstype":"n","genexpr":null,"relname":"trainees","is_view_only":false,"attcompression":null,"seqrelid":null,"seqtypid":null,"seqstart":null,"seqincrement":null,"seqmax":null,"seqmin":null,"seqcache":null,"seqcycle":null,"is_pk":false,"is_primary_key":false,"attprecision":null,"coloptions":[],"edit_types":["bigint","double precision","information_schema.cardinal_number","integer","integer","integer","money","numeric","oid","real","regclass","regcollation","regconfig","regdictionary","regnamespace","regoper","regoperator","regproc","regprocedure","regrole","regtype","smallint"]}],"name":"trainees","schema":"public","description":null,"rlspolicy":false,"forcerlspolicy":false,"fillfactor":null,"toast_tuple_target":null,"parallel_workers":null,"relpersistence":false,"primary_key":[{"oid":16445,"name":"trainees_pkey","col_count":1,"indnullsnotdistinct":false,"spcname":"pg_default","comment":null,"condeferrable":false,"condeferred":false,"conislocal":true,"fillfactor":null,"columns":[{"column":"trainee_id"}],"include":[]}],"foreign_key":[{"name":"computer","condeferrable":false,"condeferred":false,"confupdtype":"a","confdeltype":"a","confmatchtype":false,"conkey":[11],"confkey":[1],"confrelid":16467,"fknsp":"public","fktab":"trainees","refnspoid":2200,"refnsp":"public","reftab":"computers","comment":null,"convalidated":false,"conislocal":true,"columns":[{"local_column":"computer_trainee","references":"34b9cf90-0590-4349-9b0d-3313faf3d15b","referenced":"computer_id","references_table_name":"public.computers"}],"remote_schema":"public","remote_table":"computers","coveringindex":null,"autoindex":false,"hasindex":false},{"name":"trainees_groups","condeferrable":false,"condeferred":false,"confupdtype":"a","confdeltype":"a","confmatchtype":false,"conkey":[6],"confkey":[1],"confrelid":16448,"fknsp":"public","fktab":"trainees","refnspoid":2200,"refnsp":"public","reftab":"groups","comment":null,"convalidated":false,"conislocal":true,"columns":[{"local_column":"group_trainee","references":"5757cd17-d261-4285-afaa-9c87eb259e1a","referenced":"group_id","references_table_name":"public.groups"}],"remote_schema":"public","remote_table":"groups","coveringindex":null,"autoindex":false,"hasindex":false}],"unique_constraint":[]},"note":"","metadata":{"data_failed":false,"is_promise":false}}}}}]}} \ No newline at end of file diff --git a/DataBaseLMS/databaselms.cpp b/DataBaseLMS/databaselms.cpp index 668b07e..719c1f3 100644 --- a/DataBaseLMS/databaselms.cpp +++ b/DataBaseLMS/databaselms.cpp @@ -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); diff --git a/DataBaseLMS/databaselms.h b/DataBaseLMS/databaselms.h index 98240df..29a2371 100644 --- a/DataBaseLMS/databaselms.h +++ b/DataBaseLMS/databaselms.h @@ -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"; diff --git a/DataBaseLMS/interfacedatabaselms.cpp b/DataBaseLMS/interfacedatabaselms.cpp index 0767ce2..0ed724b 100644 --- a/DataBaseLMS/interfacedatabaselms.cpp +++ b/DataBaseLMS/interfacedatabaselms.cpp @@ -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); diff --git a/DataBaseLMS/interfacedatabaselms.h b/DataBaseLMS/interfacedatabaselms.h index dd08015..6fce95c 100644 --- a/DataBaseLMS/interfacedatabaselms.h +++ b/DataBaseLMS/interfacedatabaselms.h @@ -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); diff --git a/DataBaseLMS/task.cpp b/DataBaseLMS/task.cpp deleted file mode 100644 index d99ae8e..0000000 --- a/DataBaseLMS/task.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "task.h" - -Task::Task(): - BasicEntity() -{ - -} diff --git a/DataBaseLMS/task.h b/DataBaseLMS/task.h deleted file mode 100644 index bc5889d..0000000 --- a/DataBaseLMS/task.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef TASK_H -#define TASK_H - -#include "basicentity.h" - -class DATABASELMS_EXPORT Task: public BasicEntity -{ -public: - Task(); -}; - -#endif // TASK_H diff --git a/InstructorsAndTrainees/docTasks/tasksAmmFim.cpp b/DataBaseLMS/tasksAmmFim.cpp similarity index 96% rename from InstructorsAndTrainees/docTasks/tasksAmmFim.cpp rename to DataBaseLMS/tasksAmmFim.cpp index 2ebb064..05d42c3 100644 --- a/InstructorsAndTrainees/docTasks/tasksAmmFim.cpp +++ b/DataBaseLMS/tasksAmmFim.cpp @@ -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) { diff --git a/InstructorsAndTrainees/docTasks/tasksAmmFim.h b/DataBaseLMS/tasksAmmFim.h similarity index 89% rename from InstructorsAndTrainees/docTasks/tasksAmmFim.h rename to DataBaseLMS/tasksAmmFim.h index 01ee7de..aadeeb8 100644 --- a/InstructorsAndTrainees/docTasks/tasksAmmFim.h +++ b/DataBaseLMS/tasksAmmFim.h @@ -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 diff --git a/DataBaseLMS/trainee.cpp b/DataBaseLMS/trainee.cpp index 32ecb02..0182f4d 100644 --- a/DataBaseLMS/trainee.cpp +++ b/DataBaseLMS/trainee.cpp @@ -3,8 +3,7 @@ Trainee::Trainee(): User(), group(), - computer(), - tasks() + computer() { } diff --git a/DataBaseLMS/trainee.h b/DataBaseLMS/trainee.h index 15d42de..7dad41a 100644 --- a/DataBaseLMS/trainee.h +++ b/DataBaseLMS/trainee.h @@ -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 diff --git a/DataBaseLMS/typeQueryToDB.h b/DataBaseLMS/typeQueryToDB.h new file mode 100644 index 0000000..7bc4df0 --- /dev/null +++ b/DataBaseLMS/typeQueryToDB.h @@ -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 diff --git a/GUIdataBaseLMS/mainwindow.cpp b/GUIdataBaseLMS/mainwindow.cpp index 4413928..a71b858 100644 --- a/GUIdataBaseLMS/mainwindow.cpp +++ b/GUIdataBaseLMS/mainwindow.cpp @@ -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(); } diff --git a/InstructorsAndTrainees/CMakeLists.txt b/InstructorsAndTrainees/CMakeLists.txt index 8e13872..ad4daa0 100644 --- a/InstructorsAndTrainees/CMakeLists.txt +++ b/InstructorsAndTrainees/CMakeLists.txt @@ -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 ) diff --git a/InstructorsAndTrainees/commonview.cpp b/InstructorsAndTrainees/commonview.cpp index aa7ab43..8e6bb54 100644 --- a/InstructorsAndTrainees/commonview.cpp +++ b/InstructorsAndTrainees/commonview.cpp @@ -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), diff --git a/InstructorsAndTrainees/commonview.h b/InstructorsAndTrainees/commonview.h index ea9e755..dd1305d 100644 --- a/InstructorsAndTrainees/commonview.h +++ b/InstructorsAndTrainees/commonview.h @@ -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: diff --git a/InstructorsAndTrainees/connectorToServer/Core/dataparser.cpp b/InstructorsAndTrainees/connectorToServer/Core/dataparser.cpp index 70cbbdb..b0163d6 100644 --- a/InstructorsAndTrainees/connectorToServer/Core/dataparser.cpp +++ b/InstructorsAndTrainees/connectorToServer/Core/dataparser.cpp @@ -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(); diff --git a/InstructorsAndTrainees/connectorToServer/Core/dataparser.h b/InstructorsAndTrainees/connectorToServer/Core/dataparser.h index d041e85..8e2e85c 100644 --- a/InstructorsAndTrainees/connectorToServer/Core/dataparser.h +++ b/InstructorsAndTrainees/connectorToServer/Core/dataparser.h @@ -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); diff --git a/InstructorsAndTrainees/connectorToServer/Core/notifycontroller.cpp b/InstructorsAndTrainees/connectorToServer/Core/notifycontroller.cpp new file mode 100644 index 0000000..86d4ca6 --- /dev/null +++ b/InstructorsAndTrainees/connectorToServer/Core/notifycontroller.cpp @@ -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(); +} diff --git a/InstructorsAndTrainees/connectorToServer/Core/notifycontroller.h b/InstructorsAndTrainees/connectorToServer/Core/notifycontroller.h new file mode 100644 index 0000000..1b08ef9 --- /dev/null +++ b/InstructorsAndTrainees/connectorToServer/Core/notifycontroller.h @@ -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 diff --git a/InstructorsAndTrainees/connectorToServer/Core/recognizesystem.cpp b/InstructorsAndTrainees/connectorToServer/Core/recognizesystem.cpp index d06662a..be3d0c3 100644 --- a/InstructorsAndTrainees/connectorToServer/Core/recognizesystem.cpp +++ b/InstructorsAndTrainees/connectorToServer/Core/recognizesystem.cpp @@ -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") diff --git a/InstructorsAndTrainees/connectorToServer/Core/recognizesystem.h b/InstructorsAndTrainees/connectorToServer/Core/recognizesystem.h index b2ece21..55d910a 100644 --- a/InstructorsAndTrainees/connectorToServer/Core/recognizesystem.h +++ b/InstructorsAndTrainees/connectorToServer/Core/recognizesystem.h @@ -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); }; diff --git a/InstructorsAndTrainees/connectorToServer/Core/sendsystem.cpp b/InstructorsAndTrainees/connectorToServer/Core/sendsystem.cpp index 051dbf2..3c34e5b 100644 --- a/InstructorsAndTrainees/connectorToServer/Core/sendsystem.cpp +++ b/InstructorsAndTrainees/connectorToServer/Core/sendsystem.cpp @@ -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() { diff --git a/InstructorsAndTrainees/connectorToServer/Core/sendsystem.h b/InstructorsAndTrainees/connectorToServer/Core/sendsystem.h index b260776..924b411 100644 --- a/InstructorsAndTrainees/connectorToServer/Core/sendsystem.h +++ b/InstructorsAndTrainees/connectorToServer/Core/sendsystem.h @@ -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(); diff --git a/InstructorsAndTrainees/connectorToServer/Core/tcpclient.cpp b/InstructorsAndTrainees/connectorToServer/Core/tcpclient.cpp index c6fcde2..ebbcb7b 100644 --- a/InstructorsAndTrainees/connectorToServer/Core/tcpclient.cpp +++ b/InstructorsAndTrainees/connectorToServer/Core/tcpclient.cpp @@ -48,6 +48,7 @@ void TCPClient::setConnect(ServerSettings *serverSettings) else { isConnected = false; + emit signal_ConnectedToServer(false); emit sigServerDisconnect(); } } diff --git a/InstructorsAndTrainees/connectorToServer/Core/tools.cpp b/InstructorsAndTrainees/connectorToServer/Core/tools.cpp index 1461a9d..d719b39 100644 --- a/InstructorsAndTrainees/connectorToServer/Core/tools.cpp +++ b/InstructorsAndTrainees/connectorToServer/Core/tools.cpp @@ -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); diff --git a/InstructorsAndTrainees/connectorToServer/Core/tools.h b/InstructorsAndTrainees/connectorToServer/Core/tools.h index 187a466..30ef3b3 100644 --- a/InstructorsAndTrainees/connectorToServer/Core/tools.h +++ b/InstructorsAndTrainees/connectorToServer/Core/tools.h @@ -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) diff --git a/InstructorsAndTrainees/connectorToServer/Core/versioncontainer.cpp b/InstructorsAndTrainees/connectorToServer/Core/versioncontainer.cpp new file mode 100644 index 0000000..1c8bd1c --- /dev/null +++ b/InstructorsAndTrainees/connectorToServer/Core/versioncontainer.cpp @@ -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; +} diff --git a/InstructorsAndTrainees/connectorToServer/Core/versioncontainer.h b/InstructorsAndTrainees/connectorToServer/Core/versioncontainer.h new file mode 100644 index 0000000..8b12c77 --- /dev/null +++ b/InstructorsAndTrainees/connectorToServer/Core/versioncontainer.h @@ -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 diff --git a/InstructorsAndTrainees/connectorToServer/Datas.h b/InstructorsAndTrainees/connectorToServer/Datas.h index 0d2573f..5f03268 100644 --- a/InstructorsAndTrainees/connectorToServer/Datas.h +++ b/InstructorsAndTrainees/connectorToServer/Datas.h @@ -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: diff --git a/InstructorsAndTrainees/connectorToServer/connectortoserver.cpp b/InstructorsAndTrainees/connectorToServer/connectortoserver.cpp index 4421bc0..301e84b 100644 --- a/InstructorsAndTrainees/connectorToServer/connectortoserver.cpp +++ b/InstructorsAndTrainees/connectorToServer/connectortoserver.cpp @@ -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); } diff --git a/InstructorsAndTrainees/connectorToServer/connectortoserver.h b/InstructorsAndTrainees/connectorToServer/connectortoserver.h index 77eb228..c235ddf 100644 --- a/InstructorsAndTrainees/connectorToServer/connectortoserver.h +++ b/InstructorsAndTrainees/connectorToServer/connectortoserver.h @@ -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 diff --git a/InstructorsAndTrainees/connectorToServer/streamingversiondata.h b/InstructorsAndTrainees/connectorToServer/streamingversiondata.h new file mode 100644 index 0000000..45c8f4d --- /dev/null +++ b/InstructorsAndTrainees/connectorToServer/streamingversiondata.h @@ -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 + + + + + diff --git a/InstructorsAndTrainees/docTasks/doctaskswidget.h b/InstructorsAndTrainees/docTasks/doctaskswidget.h deleted file mode 100644 index fc08f04..0000000 --- a/InstructorsAndTrainees/docTasks/doctaskswidget.h +++ /dev/null @@ -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 diff --git a/InstructorsAndTrainees/docTasks/doctaskswidget.ui b/InstructorsAndTrainees/docTasks/doctaskswidget.ui deleted file mode 100644 index cefa158..0000000 --- a/InstructorsAndTrainees/docTasks/doctaskswidget.ui +++ /dev/null @@ -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> diff --git a/InstructorsAndTrainees/docTasks/fimtaskswidget.cpp b/InstructorsAndTrainees/docTasks/fimtaskswidget.cpp deleted file mode 100644 index 2c3ff25..0000000 --- a/InstructorsAndTrainees/docTasks/fimtaskswidget.cpp +++ /dev/null @@ -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); -} diff --git a/InstructorsAndTrainees/docTasks/fimtaskswidget.h b/InstructorsAndTrainees/docTasks/fimtaskswidget.h deleted file mode 100644 index 981bc00..0000000 --- a/InstructorsAndTrainees/docTasks/fimtaskswidget.h +++ /dev/null @@ -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 diff --git a/InstructorsAndTrainees/docTasks/fimtaskswidget.ui b/InstructorsAndTrainees/docTasks/fimtaskswidget.ui deleted file mode 100644 index 70106c9..0000000 --- a/InstructorsAndTrainees/docTasks/fimtaskswidget.ui +++ /dev/null @@ -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> diff --git a/InstructorsAndTrainees/instructors/instructorsview.cpp b/InstructorsAndTrainees/instructors/instructorsview.cpp index 1ab1788..0df79e3 100644 --- a/InstructorsAndTrainees/instructors/instructorsview.cpp +++ b/InstructorsAndTrainees/instructors/instructorsview.cpp @@ -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(); } diff --git a/InstructorsAndTrainees/instructors/instructorsview.h b/InstructorsAndTrainees/instructors/instructorsview.h index 0411ea6..71de18a 100644 --- a/InstructorsAndTrainees/instructors/instructorsview.h +++ b/InstructorsAndTrainees/instructors/instructorsview.h @@ -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); diff --git a/InstructorsAndTrainees/instructors/viewerinstructors.cpp b/InstructorsAndTrainees/instructors/viewerinstructors.cpp index c72d8e9..2ba92ec 100644 --- a/InstructorsAndTrainees/instructors/viewerinstructors.cpp +++ b/InstructorsAndTrainees/instructors/viewerinstructors.cpp @@ -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) { // В случае получения события изменения языка приложения diff --git a/InstructorsAndTrainees/instructors/viewerinstructors.h b/InstructorsAndTrainees/instructors/viewerinstructors.h index 3febad1..8d551b5 100644 --- a/InstructorsAndTrainees/instructors/viewerinstructors.h +++ b/InstructorsAndTrainees/instructors/viewerinstructors.h @@ -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; diff --git a/InstructorsAndTrainees/instructorsandtraineeswidget.cpp b/InstructorsAndTrainees/instructorsandtraineeswidget.cpp index dc84728..de9ce81 100644 --- a/InstructorsAndTrainees/instructorsandtraineeswidget.cpp +++ b/InstructorsAndTrainees/instructorsandtraineeswidget.cpp @@ -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); + } +} diff --git a/InstructorsAndTrainees/instructorsandtraineeswidget.h b/InstructorsAndTrainees/instructorsandtraineeswidget.h index 4f822da..0849baa 100644 --- a/InstructorsAndTrainees/instructorsandtraineeswidget.h +++ b/InstructorsAndTrainees/instructorsandtraineeswidget.h @@ -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; diff --git a/InstructorsAndTrainees/instructorsandtraineeswidget.ui b/InstructorsAndTrainees/instructorsandtraineeswidget.ui index 164f714..a47deef 100644 --- a/InstructorsAndTrainees/instructorsandtraineeswidget.ui +++ b/InstructorsAndTrainees/instructorsandtraineeswidget.ui @@ -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> diff --git a/InstructorsAndTrainees/messanger/messangerwidget.cpp b/InstructorsAndTrainees/messanger/messangerwidget.cpp index da36b37..30c2bfe 100644 --- a/InstructorsAndTrainees/messanger/messangerwidget.cpp +++ b/InstructorsAndTrainees/messanger/messangerwidget.cpp @@ -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) diff --git a/InstructorsAndTrainees/messanger/messangerwidget.h b/InstructorsAndTrainees/messanger/messangerwidget.h index 48b4c66..b4438e7 100644 --- a/InstructorsAndTrainees/messanger/messangerwidget.h +++ b/InstructorsAndTrainees/messanger/messangerwidget.h @@ -37,6 +37,8 @@ public: int getIndexTab(QString login); + void clear(); + private slots: void on_btnSend_clicked(); void on_tabWidget_currentChanged(int index); diff --git a/InstructorsAndTrainees/messanger/messangerwidget.ui b/InstructorsAndTrainees/messanger/messangerwidget.ui index 6b4da74..ff637d9 100644 --- a/InstructorsAndTrainees/messanger/messangerwidget.ui +++ b/InstructorsAndTrainees/messanger/messangerwidget.ui @@ -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"> diff --git a/InstructorsAndTrainees/resources.qrc b/InstructorsAndTrainees/resources.qrc index 56c11bf..d2a1133 100644 --- a/InstructorsAndTrainees/resources.qrc +++ b/InstructorsAndTrainees/resources.qrc @@ -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> diff --git a/InstructorsAndTrainees/resources/icons/762.gif b/InstructorsAndTrainees/resources/icons/762.gif new file mode 100644 index 0000000..d1a1bca Binary files /dev/null and b/InstructorsAndTrainees/resources/icons/762.gif differ diff --git a/InstructorsAndTrainees/resources/icons/assignTask.png b/InstructorsAndTrainees/resources/icons/assignTask.png new file mode 100644 index 0000000..cd87789 Binary files /dev/null and b/InstructorsAndTrainees/resources/icons/assignTask.png differ diff --git a/InstructorsAndTrainees/resources/icons/delete.png b/InstructorsAndTrainees/resources/icons/delete.png new file mode 100644 index 0000000..1abb5de Binary files /dev/null and b/InstructorsAndTrainees/resources/icons/delete.png differ diff --git a/InstructorsAndTrainees/resources/icons/filter.png b/InstructorsAndTrainees/resources/icons/filter.png new file mode 100644 index 0000000..7f4e679 Binary files /dev/null and b/InstructorsAndTrainees/resources/icons/filter.png differ diff --git a/InstructorsAndTrainees/tasks/ammtaskswidget.cpp b/InstructorsAndTrainees/tasks/ammtaskswidget.cpp new file mode 100644 index 0000000..0c93034 --- /dev/null +++ b/InstructorsAndTrainees/tasks/ammtaskswidget.cpp @@ -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); +} diff --git a/InstructorsAndTrainees/tasks/ammtaskswidget.h b/InstructorsAndTrainees/tasks/ammtaskswidget.h new file mode 100644 index 0000000..10f3232 --- /dev/null +++ b/InstructorsAndTrainees/tasks/ammtaskswidget.h @@ -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 diff --git a/InstructorsAndTrainees/tasks/ammtaskswidget.ui b/InstructorsAndTrainees/tasks/ammtaskswidget.ui new file mode 100644 index 0000000..9d5f36d --- /dev/null +++ b/InstructorsAndTrainees/tasks/ammtaskswidget.ui @@ -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> diff --git a/InstructorsAndTrainees/tasks/fimtaskswidget.cpp b/InstructorsAndTrainees/tasks/fimtaskswidget.cpp new file mode 100644 index 0000000..f832f3c --- /dev/null +++ b/InstructorsAndTrainees/tasks/fimtaskswidget.cpp @@ -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); + } + } + } +} diff --git a/InstructorsAndTrainees/tasks/fimtaskswidget.h b/InstructorsAndTrainees/tasks/fimtaskswidget.h new file mode 100644 index 0000000..1c36d65 --- /dev/null +++ b/InstructorsAndTrainees/tasks/fimtaskswidget.h @@ -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 diff --git a/InstructorsAndTrainees/tasks/fimtaskswidget.ui b/InstructorsAndTrainees/tasks/fimtaskswidget.ui new file mode 100644 index 0000000..03e934d --- /dev/null +++ b/InstructorsAndTrainees/tasks/fimtaskswidget.ui @@ -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> diff --git a/InstructorsAndTrainees/docTasks/module.cpp b/InstructorsAndTrainees/tasks/module.cpp similarity index 94% rename from InstructorsAndTrainees/docTasks/module.cpp rename to InstructorsAndTrainees/tasks/module.cpp index c7c5c32..6a7e716 100644 --- a/InstructorsAndTrainees/docTasks/module.cpp +++ b/InstructorsAndTrainees/tasks/module.cpp @@ -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(): diff --git a/InstructorsAndTrainees/docTasks/module.h b/InstructorsAndTrainees/tasks/module.h similarity index 94% rename from InstructorsAndTrainees/docTasks/module.h rename to InstructorsAndTrainees/tasks/module.h index 3c5ca8e..009a328 100644 --- a/InstructorsAndTrainees/docTasks/module.h +++ b/InstructorsAndTrainees/tasks/module.h @@ -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; diff --git a/InstructorsAndTrainees/tasks/taskswidget.cpp b/InstructorsAndTrainees/tasks/taskswidget.cpp deleted file mode 100644 index 1a81740..0000000 --- a/InstructorsAndTrainees/tasks/taskswidget.cpp +++ /dev/null @@ -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); // переведём окно заново - } -} diff --git a/InstructorsAndTrainees/tasks/taskswidget.h b/InstructorsAndTrainees/tasks/taskswidget.h deleted file mode 100644 index c663b36..0000000 --- a/InstructorsAndTrainees/tasks/taskswidget.h +++ /dev/null @@ -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 diff --git a/InstructorsAndTrainees/tasks/taskswidget.ui b/InstructorsAndTrainees/tasks/taskswidget.ui deleted file mode 100644 index 526413b..0000000 --- a/InstructorsAndTrainees/tasks/taskswidget.ui +++ /dev/null @@ -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> diff --git a/InstructorsAndTrainees/docTasks/doctaskswidget.cpp b/InstructorsAndTrainees/tasks/tasktreepreparation.cpp similarity index 59% rename from InstructorsAndTrainees/docTasks/doctaskswidget.cpp rename to InstructorsAndTrainees/tasks/tasktreepreparation.cpp index 23a7f65..c7980c9 100644 --- a/InstructorsAndTrainees/docTasks/doctaskswidget.cpp +++ b/InstructorsAndTrainees/tasks/tasktreepreparation.cpp @@ -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); -} - diff --git a/InstructorsAndTrainees/tasks/tasktreepreparation.h b/InstructorsAndTrainees/tasks/tasktreepreparation.h new file mode 100644 index 0000000..8cb0847 --- /dev/null +++ b/InstructorsAndTrainees/tasks/tasktreepreparation.h @@ -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 diff --git a/InstructorsAndTrainees/trainees/traineesview.cpp b/InstructorsAndTrainees/trainees/traineesview.cpp index da57579..263ab02 100644 --- a/InstructorsAndTrainees/trainees/traineesview.cpp +++ b/InstructorsAndTrainees/trainees/traineesview.cpp @@ -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); } diff --git a/InstructorsAndTrainees/trainees/traineesview.h b/InstructorsAndTrainees/trainees/traineesview.h index f37f7cd..dab045a 100644 --- a/InstructorsAndTrainees/trainees/traineesview.h +++ b/InstructorsAndTrainees/trainees/traineesview.h @@ -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); diff --git a/InstructorsAndTrainees/trainees/viewertrainees.cpp b/InstructorsAndTrainees/trainees/viewertrainees.cpp index 487fc24..52f7f1a 100644 --- a/InstructorsAndTrainees/trainees/viewertrainees.cpp +++ b/InstructorsAndTrainees/trainees/viewertrainees.cpp @@ -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); + } } } diff --git a/InstructorsAndTrainees/trainees/viewertrainees.h b/InstructorsAndTrainees/trainees/viewertrainees.h index 6613484..40cd3e7 100644 --- a/InstructorsAndTrainees/trainees/viewertrainees.h +++ b/InstructorsAndTrainees/trainees/viewertrainees.h @@ -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; }; diff --git a/InstructorsAndTrainees/trainees/viewertrainees.ui b/InstructorsAndTrainees/trainees/viewertrainees.ui index aee5a98..122e49d 100644 --- a/InstructorsAndTrainees/trainees/viewertrainees.ui +++ b/InstructorsAndTrainees/trainees/viewertrainees.ui @@ -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> diff --git a/InstructorsAndTrainees/translations/InstructorsAndTraineesWidget_ru_RU.qm b/InstructorsAndTrainees/translations/InstructorsAndTraineesWidget_ru_RU.qm index 7b82e24..43be936 100644 Binary files a/InstructorsAndTrainees/translations/InstructorsAndTraineesWidget_ru_RU.qm and b/InstructorsAndTrainees/translations/InstructorsAndTraineesWidget_ru_RU.qm differ diff --git a/InstructorsAndTrainees/translations/InstructorsAndTraineesWidget_ru_RU.ts b/InstructorsAndTrainees/translations/InstructorsAndTraineesWidget_ru_RU.ts index 73d7bb9..31107af 100644 --- a/InstructorsAndTrainees/translations/InstructorsAndTraineesWidget_ru_RU.ts +++ b/InstructorsAndTrainees/translations/InstructorsAndTraineesWidget_ru_RU.ts @@ -1,26 +1,60 @@ <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE TS> <TS version="2.1" language="ru_RU"> +<context> + <name>AMMtasksWidget</name> + <message> + <location filename="../tasks/ammtaskswidget.ui" line="20"/> + <source>Form</source> + <translation type="unfinished">Форма</translation> + </message> + <message> + <location filename="../tasks/ammtaskswidget.cpp" line="216"/> + <source>Task AMM</source> + <translation>Задача AMM</translation> + </message> + <message> + <location filename="../tasks/ammtaskswidget.cpp" line="216"/> + <source>DM code</source> + <translation>DM код</translation> + </message> + <message> + <location filename="../tasks/ammtaskswidget.cpp" line="216"/> + <location filename="../tasks/ammtaskswidget.cpp" line="218"/> + <source>ID</source> + <translation type="unfinished">ID</translation> + </message> + <message> + <location filename="../tasks/ammtaskswidget.cpp" line="218"/> + <source>PM/DM</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../tasks/ammtaskswidget.cpp" line="218"/> + <source>Code</source> + <translation>Код</translation> + </message> +</context> <context> <name>DialogAuthorizationInstructor</name> <message> - <location filename="../instructors/dialogauthorizationinstructor.ui" line="19"/> + <location filename="../instructors/dialogauthorizationinstructor.ui" line="20"/> <source>Instructor authorization</source> <translation>Авторизация инструктора</translation> </message> <message> - <location filename="../instructors/dialogauthorizationinstructor.ui" line="29"/> + <location filename="../instructors/dialogauthorizationinstructor.ui" line="41"/> <source>Login</source> <translation>Логин</translation> </message> <message> - <location filename="../instructors/dialogauthorizationinstructor.ui" line="36"/> - <location filename="../instructors/dialogauthorizationinstructor.ui" line="54"/> + <location filename="../instructors/dialogauthorizationinstructor.ui" line="69"/> + <location filename="../instructors/dialogauthorizationinstructor.ui" line="81"/> <source>admin</source> <translation></translation> </message> <message> - <location filename="../instructors/dialogauthorizationinstructor.ui" line="47"/> + <location filename="../instructors/dialogauthorizationinstructor.ui" line="53"/> <source>Password</source> <translation>Пароль</translation> </message> @@ -28,12 +62,12 @@ <context> <name>DialogEditGroup</name> <message> - <location filename="../trainees/dialogeditgroup.ui" line="19"/> + <location filename="../trainees/dialogeditgroup.ui" line="20"/> <source>Group</source> <translation>Группа</translation> </message> <message> - <location filename="../trainees/dialogeditgroup.ui" line="33"/> + <location filename="../trainees/dialogeditgroup.ui" line="39"/> <source>Name</source> <translation>Имя</translation> </message> @@ -41,17 +75,17 @@ <context> <name>DialogEditInstructor</name> <message> - <location filename="../instructors/dialogeditinstructor.ui" line="19"/> + <location filename="../instructors/dialogeditinstructor.ui" line="20"/> <source>Instructor</source> <translation>Инструктор</translation> </message> <message> - <location filename="../instructors/dialogeditinstructor.ui" line="29"/> + <location filename="../instructors/dialogeditinstructor.ui" line="37"/> <source>Name</source> <translation>Имя</translation> </message> <message> - <location filename="../instructors/dialogeditinstructor.ui" line="47"/> + <location filename="../instructors/dialogeditinstructor.ui" line="49"/> <source>Login</source> <translation>Логин</translation> </message> @@ -61,17 +95,17 @@ <translation>Пароль</translation> </message> <message> - <location filename="../instructors/dialogeditinstructor.ui" line="78"/> + <location filename="../instructors/dialogeditinstructor.ui" line="99"/> <source>Administrator</source> <translation>Администратор</translation> </message> <message> - <location filename="../instructors/dialogeditinstructor.ui" line="100"/> + <location filename="../instructors/dialogeditinstructor.ui" line="126"/> <source>Archived</source> <translation>Архивный</translation> </message> <message> - <location filename="../instructors/dialogeditinstructor.ui" line="122"/> + <location filename="../instructors/dialogeditinstructor.ui" line="153"/> <source>Logged</source> <translation>Залогирован</translation> </message> @@ -79,32 +113,32 @@ <context> <name>DialogEditTrainee</name> <message> - <location filename="../trainees/dialogedittrainee.ui" line="19"/> + <location filename="../trainees/dialogedittrainee.ui" line="20"/> <source>Trainee</source> <translation>Обучаемый</translation> </message> <message> - <location filename="../trainees/dialogedittrainee.ui" line="46"/> + <location filename="../trainees/dialogedittrainee.ui" line="59"/> <source>Name</source> <translation>Имя</translation> </message> <message> - <location filename="../trainees/dialogedittrainee.ui" line="60"/> + <location filename="../trainees/dialogedittrainee.ui" line="71"/> <source>Login</source> <translation>Логин</translation> </message> <message> - <location filename="../trainees/dialogedittrainee.ui" line="74"/> + <location filename="../trainees/dialogedittrainee.ui" line="83"/> <source>Password</source> <translation>Пароль</translation> </message> <message> - <location filename="../trainees/dialogedittrainee.ui" line="91"/> + <location filename="../trainees/dialogedittrainee.ui" line="135"/> <source>Archived</source> <translation>Архивный</translation> </message> <message> - <location filename="../trainees/dialogedittrainee.ui" line="113"/> + <location filename="../trainees/dialogedittrainee.ui" line="162"/> <source>Logged</source> <translation>Залогирован</translation> </message> @@ -112,63 +146,123 @@ <context> <name>EditorInstructors</name> <message> - <location filename="../instructors/editorinstructors.ui" line="14"/> + <location filename="../instructors/editorinstructors.ui" line="20"/> <source>List instructors</source> <translation>Список инструкторов</translation> </message> <message> - <location filename="../instructors/editorinstructors.ui" line="104"/> + <location filename="../instructors/editorinstructors.ui" line="51"/> <source>New instructor</source> <translation>Новый инструктор</translation> </message> <message> - <location filename="../instructors/editorinstructors.ui" line="142"/> + <location filename="../instructors/editorinstructors.ui" line="89"/> <source>Delete instructor</source> <translation>Удалить инструктора</translation> </message> <message> - <location filename="../instructors/editorinstructors.ui" line="174"/> - <location filename="../instructors/editorinstructors.cpp" line="224"/> + <location filename="../instructors/editorinstructors.ui" line="121"/> + <location filename="../instructors/editorinstructors.cpp" line="229"/> <source>To archive</source> <translation>Архивировать</translation> </message> <message> - <location filename="../instructors/editorinstructors.ui" line="212"/> + <location filename="../instructors/editorinstructors.ui" line="159"/> <source>Edit</source> <translation>Редактировать</translation> </message> <message> - <location filename="../instructors/editorinstructors.ui" line="263"/> + <location filename="../instructors/editorinstructors.ui" line="210"/> <source>Show archive</source> <translation>Показать архив</translation> </message> <message> - <location filename="../instructors/editorinstructors.cpp" line="89"/> + <location filename="../instructors/editorinstructors.cpp" line="102"/> + <location filename="../instructors/editorinstructors.cpp" line="108"/> + <location filename="../instructors/editorinstructors.cpp" line="145"/> + <location filename="../instructors/editorinstructors.cpp" line="181"/> <source>Error!</source> <translation>Ошибка!</translation> </message> <message> - <location filename="../instructors/editorinstructors.cpp" line="89"/> + <location filename="../instructors/editorinstructors.cpp" line="102"/> <source>You cannot delete the Administrator.</source> <translation>Нельзя удалить администратора.</translation> </message> <message> - <location filename="../instructors/editorinstructors.cpp" line="93"/> + <location filename="../instructors/editorinstructors.cpp" line="108"/> + <source>You cannot delete a logged-in instructor.</source> + <translation>Вы не можете удалить инструктора, вошедшего в систему.</translation> + </message> + <message> + <location filename="../instructors/editorinstructors.cpp" line="112"/> <source>Attention!</source> <translation>Внимание!</translation> </message> <message> - <location filename="../instructors/editorinstructors.cpp" line="93"/> + <location filename="../instructors/editorinstructors.cpp" line="112"/> <source>The deletion will be irrevocable. Delete it anyway?</source> <translation>Удаление будет безвозвратным. Всё равно удалить?</translation> </message> <message> - <location filename="../instructors/editorinstructors.cpp" line="219"/> + <location filename="../instructors/editorinstructors.cpp" line="145"/> + <source>You cannot archive a logged-in instructor.</source> + <translation>Вы не можете заархивировать инструктора, вошедшего в систему.</translation> + </message> + <message> + <location filename="../instructors/editorinstructors.cpp" line="181"/> + <source>You cannot edit a logged-in instructor.</source> + <translation>Вы не можете редактировать инструктора, вошедшего в систему.</translation> + </message> + <message> + <location filename="../instructors/editorinstructors.cpp" line="224"/> <source>From archive</source> <translation>Разархивировать</translation> </message> + <message> + <location filename="../instructors/editorinstructors.cpp" line="264"/> + <location filename="../instructors/editorinstructors.cpp" line="271"/> + <location filename="../instructors/editorinstructors.cpp" line="278"/> + <location filename="../instructors/editorinstructors.cpp" line="290"/> + <location filename="../instructors/editorinstructors.cpp" line="297"/> + <source>Editing error!</source> + <translation>Ошибка редактирования!</translation> + </message> + <message> + <location filename="../instructors/editorinstructors.cpp" line="265"/> + <source>Unacceptable instructor name has been entered. +The changes will not be accepted.</source> + <translation>Введено неприемлемое имя инструктора. +Изменения приняты не будут.</translation> + </message> + <message> + <location filename="../instructors/editorinstructors.cpp" line="272"/> + <source>Unacceptable instructor login has been entered. +The changes will not be accepted.</source> + <translation>Введен неприемлемый логин инструктора. +Изменения приняты не будут.</translation> + </message> + <message> + <location filename="../instructors/editorinstructors.cpp" line="279"/> + <source>Unacceptable instructor password has been entered. +The changes will not be accepted.</source> + <translation>Введен неприемлемый пароль инструктора. +Изменения приняты не будут.</translation> + </message> + <message> + <location filename="../instructors/editorinstructors.cpp" line="291"/> + <source>An existing instructor name has been entered.</source> + <translation>Введено имя существующего инструктора.</translation> + </message> + <message> + <location filename="../instructors/editorinstructors.cpp" line="298"/> + <source>An existing instructor login has been entered. +The changes will not be accepted.</source> + <translation>Был введен существующий логин инструктора. +Изменения приняты не будут.</translation> + </message> </context> <context> <name>EditorTrainees</name> @@ -178,139 +272,272 @@ Delete it anyway?</source> <translation>Список обучаемых</translation> </message> <message> - <location filename="../trainees/editortrainees.ui" line="113"/> + <location filename="../trainees/editortrainees.ui" line="54"/> <source>New group</source> <translation>Новая группа</translation> </message> <message> - <location filename="../trainees/editortrainees.ui" line="151"/> + <location filename="../trainees/editortrainees.ui" line="92"/> <source>Delete group</source> <translation>Удалить группу</translation> </message> <message> - <location filename="../trainees/editortrainees.ui" line="189"/> + <location filename="../trainees/editortrainees.ui" line="130"/> <source>New trainee</source> <translation>Новый обучаемый</translation> </message> <message> - <location filename="../trainees/editortrainees.ui" line="227"/> + <location filename="../trainees/editortrainees.ui" line="168"/> <source>Delete trainee</source> <translation>Удалить обучаемого</translation> </message> <message> - <location filename="../trainees/editortrainees.ui" line="265"/> - <location filename="../trainees/editortrainees.cpp" line="394"/> - <location filename="../trainees/editortrainees.cpp" line="416"/> + <location filename="../trainees/editortrainees.ui" line="206"/> + <location filename="../trainees/editortrainees.cpp" line="350"/> + <location filename="../trainees/editortrainees.cpp" line="375"/> <source>To archive</source> <translation>Архивировать</translation> </message> <message> - <location filename="../trainees/editortrainees.ui" line="303"/> + <location filename="../trainees/editortrainees.ui" line="244"/> <source>Edit</source> <translation>Редактировать</translation> </message> <message> - <location filename="../trainees/editortrainees.ui" line="354"/> + <location filename="../trainees/editortrainees.ui" line="295"/> <source>Show archive</source> <translation>Показать архив</translation> </message> <message> - <location filename="../trainees/editortrainees.cpp" line="90"/> + <location filename="../trainees/editortrainees.cpp" line="102"/> + <location filename="../trainees/editortrainees.cpp" line="410"/> + <location filename="../trainees/editortrainees.cpp" line="421"/> + <location filename="../trainees/editortrainees.cpp" line="436"/> + <location filename="../trainees/editortrainees.cpp" line="443"/> + <location filename="../trainees/editortrainees.cpp" line="450"/> + <location filename="../trainees/editortrainees.cpp" line="461"/> + <location filename="../trainees/editortrainees.cpp" line="468"/> <source>Editing error!</source> <translation>Ошибка редактирования!</translation> </message> <message> - <location filename="../trainees/editortrainees.cpp" line="90"/> + <location filename="../trainees/editortrainees.cpp" line="102"/> <source>The group is not empty. It is not possible to delete a non-empty group.</source> <translation>Группа не пуста. Невозможно удалить непустую группу.</translation> </message> <message> - <location filename="../trainees/editortrainees.cpp" line="95"/> - <location filename="../trainees/editortrainees.cpp" line="187"/> + <location filename="../trainees/editortrainees.cpp" line="107"/> + <location filename="../trainees/editortrainees.cpp" line="208"/> <source>Attention!</source> <translation>Внимание!</translation> </message> <message> - <location filename="../trainees/editortrainees.cpp" line="95"/> - <location filename="../trainees/editortrainees.cpp" line="187"/> + <location filename="../trainees/editortrainees.cpp" line="107"/> + <location filename="../trainees/editortrainees.cpp" line="208"/> <source>The deletion will be irrevocable. Delete anyway?</source> <translation>Удаление будет безвозвратным. Всё равно удалить?</translation> </message> <message> - <location filename="../trainees/editortrainees.cpp" line="409"/> + <location filename="../trainees/editortrainees.cpp" line="204"/> + <location filename="../trainees/editortrainees.cpp" line="241"/> + <location filename="../trainees/editortrainees.cpp" line="283"/> + <source>Error!</source> + <translation>Ошибка!</translation> + </message> + <message> + <location filename="../trainees/editortrainees.cpp" line="204"/> + <source>You cannot delete a logged-in trainee.</source> + <translation>Вы не можете удалить обучаемого, вошедшего в систему.</translation> + </message> + <message> + <location filename="../trainees/editortrainees.cpp" line="241"/> + <source>You cannot archive a logged-in trainee.</source> + <translation>Вы не можете заархивировать обучаемого, вошедшего в систему.</translation> + </message> + <message> + <location filename="../trainees/editortrainees.cpp" line="283"/> + <source>You cannot edit a logged-in trainee.</source> + <translation>Вы не можете редактировать обучаемого, вошедшего в систему.</translation> + </message> + <message> + <location filename="../trainees/editortrainees.cpp" line="368"/> <source>From archive</source> <translation>Разархивировать</translation> </message> + <message> + <location filename="../trainees/editortrainees.cpp" line="411"/> + <source>Unacceptable group name has been entered. +The changes will not be accepted.</source> + <translation>Введено неприемлемое название группы. +Изменения приняты не будут.</translation> + </message> + <message> + <location filename="../trainees/editortrainees.cpp" line="422"/> + <source>An existing group name has been entered. +The changes will not be accepted.</source> + <translation>Введено существующее название группы. +Изменения приняты не будут.</translation> + </message> + <message> + <location filename="../trainees/editortrainees.cpp" line="437"/> + <source>Unacceptable trainee name has been entered. +The changes will not be accepted.</source> + <translation>Введено неприемлемое имя обучаемого. +Изменения приняты не будут.</translation> + </message> + <message> + <location filename="../trainees/editortrainees.cpp" line="444"/> + <source>Unacceptable trainee login has been entered. +The changes will not be accepted.</source> + <translation>Введен неприемлемый логин обучаемого. +Изменения приняты не будут.</translation> + </message> + <message> + <location filename="../trainees/editortrainees.cpp" line="451"/> + <source>Unacceptable trainee password has been entered. +The changes will not be accepted.</source> + <translation>Был введен неприемлемый пароль обучаемого. +Изменения приняты не будут.</translation> + </message> + <message> + <location filename="../trainees/editortrainees.cpp" line="462"/> + <source>An existing trainee name has been entered.</source> + <translation>Введено имя существующего обучаемого.</translation> + </message> + <message> + <location filename="../trainees/editortrainees.cpp" line="469"/> + <source>An existing trainee login has been entered. +The changes will not be accepted.</source> + <translation>Был введен существующий логин обучаемого. +Изменения приняты не будут.</translation> + </message> +</context> +<context> + <name>FIMtasksWidget</name> + <message> + <location filename="../tasks/fimtaskswidget.ui" line="14"/> + <source>Form</source> + <translation type="unfinished">Форма</translation> + </message> + <message> + <location filename="../tasks/fimtaskswidget.cpp" line="267"/> + <source>Task FIM</source> + <translation>Задача FIM</translation> + </message> + <message> + <location filename="../tasks/fimtaskswidget.cpp" line="267"/> + <location filename="../tasks/fimtaskswidget.cpp" line="269"/> + <source>ID</source> + <translation type="unfinished">ID</translation> + </message> + <message> + <location filename="../tasks/fimtaskswidget.cpp" line="269"/> + <source>Title</source> + <translation>Заголовок</translation> + </message> </context> <context> <name>InstructorsAndTraineesWidget</name> <message> - <location filename="../instructorsandtraineeswidget.ui" line="14"/> + <location filename="../instructorsandtraineeswidget.ui" line="20"/> <source>Form</source> <translation>Форма</translation> </message> <message> - <location filename="../instructorsandtraineeswidget.ui" line="27"/> + <location filename="../instructorsandtraineeswidget.ui" line="34"/> <source>Database LMS</source> <translation>База данных СУО</translation> </message> <message> - <location filename="../instructorsandtraineeswidget.ui" line="46"/> - <source>Connection to DB</source> - <translation>Соединиться с БД</translation> + <location filename="../instructorsandtraineeswidget.ui" line="62"/> + <source>Connection to Server</source> + <translation>Подключение к серверу</translation> </message> <message> - <location filename="../instructorsandtraineeswidget.ui" line="75"/> - <location filename="../instructorsandtraineeswidget.cpp" line="55"/> - <location filename="../instructorsandtraineeswidget.cpp" line="215"/> + <location filename="../instructorsandtraineeswidget.ui" line="170"/> + <source>Logged in Instructor: </source> + <translation>Вошедший в систему инструктор: </translation> + </message> + <message> + <location filename="../instructorsandtraineeswidget.ui" line="215"/> + <source>ChangeVersion</source> + <translation>Изменение версии</translation> + </message> + <message> + <location filename="../instructorsandtraineeswidget.ui" line="228"/> + <source>Update StyleSheet</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../instructorsandtraineeswidget.ui" line="242"/> + <source>Tasks</source> + <translation>Задачи</translation> + </message> + <message> + <location filename="../instructorsandtraineeswidget.ui" line="254"/> + <source>AMM</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../instructorsandtraineeswidget.ui" line="264"/> + <source>FIM</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../instructorsandtraineeswidget.ui" line="279"/> + <source>Assign task</source> + <translation>Назначить задачу</translation> + </message> + <message> + <location filename="../instructorsandtraineeswidget.ui" line="116"/> + <location filename="../instructorsandtraineeswidget.cpp" line="136"/> + <location filename="../instructorsandtraineeswidget.cpp" line="236"/> + <location filename="../instructorsandtraineeswidget.cpp" line="261"/> <source>Authorization Instructor</source> <translation>Авторизация инструктора</translation> </message> <message> - <location filename="../instructorsandtraineeswidget.ui" line="111"/> - <source>Logged in instructor:</source> - <translation>Авторизованный инструктор:</translation> - </message> - <message> - <location filename="../instructorsandtraineeswidget.ui" line="118"/> - <location filename="../instructorsandtraineeswidget.cpp" line="236"/> + <location filename="../instructorsandtraineeswidget.ui" line="189"/> + <location filename="../instructorsandtraineeswidget.cpp" line="396"/> <source>none</source> <translation>нет</translation> </message> <message> - <location filename="../instructorsandtraineeswidget.cpp" line="53"/> - <location filename="../instructorsandtraineeswidget.cpp" line="199"/> + <location filename="../instructorsandtraineeswidget.cpp" line="134"/> + <location filename="../instructorsandtraineeswidget.cpp" line="204"/> <source>Deauthorization Instructor</source> <translation>Деавторизация инструктора</translation> </message> <message> - <location filename="../instructorsandtraineeswidget.cpp" line="59"/> <location filename="../instructorsandtraineeswidget.cpp" line="164"/> - <source>Disconnection DB</source> - <translation>Отключение БД</translation> + <source>Attention!</source> + <translation>Внимание!</translation> </message> <message> - <location filename="../instructorsandtraineeswidget.cpp" line="61"/> - <location filename="../instructorsandtraineeswidget.cpp" line="181"/> - <source>Connection DB</source> - <translation>Подключение БД</translation> + <location filename="../instructorsandtraineeswidget.cpp" line="164"/> + <source>The file could not be opened </source> + <translation>Файл не может быть открыт </translation> </message> <message> - <location filename="../instructorsandtraineeswidget.cpp" line="78"/> - <location filename="../instructorsandtraineeswidget.cpp" line="98"/> - <location filename="../instructorsandtraineeswidget.cpp" line="103"/> + <location filename="../instructorsandtraineeswidget.cpp" line="244"/> + <source>Instructor deauthorization</source> + <translation>Деавторизация инструктора</translation> + </message> + <message> + <location filename="../instructorsandtraineeswidget.cpp" line="244"/> + <source>Error!</source> + <translation>Ошибка!</translation> + </message> + <message> + <location filename="../instructorsandtraineeswidget.cpp" line="217"/> + <location filename="../instructorsandtraineeswidget.cpp" line="299"/> <source>Instructor authorization</source> <translation>Авторизация инструктора</translation> </message> <message> - <location filename="../instructorsandtraineeswidget.cpp" line="98"/> - <source>Successfully!</source> - <translation>Успешно!</translation> - </message> - <message> - <location filename="../instructorsandtraineeswidget.cpp" line="103"/> + <location filename="../instructorsandtraineeswidget.cpp" line="217"/> <source>Invalid login or password!</source> <translation>Неправильный логин или пароль!</translation> </message> @@ -318,154 +545,336 @@ Delete anyway?</source> <context> <name>InstructorsView</name> <message> - <location filename="../instructors/instructorsview.cpp" line="92"/> - <location filename="../instructors/instructorsview.cpp" line="105"/> <location filename="../instructors/instructorsview.cpp" line="115"/> + <location filename="../instructors/instructorsview.cpp" line="128"/> + <location filename="../instructors/instructorsview.cpp" line="138"/> <source>yes</source> <translation>да</translation> </message> <message> - <location filename="../instructors/instructorsview.cpp" line="98"/> - <location filename="../instructors/instructorsview.cpp" line="110"/> - <location filename="../instructors/instructorsview.cpp" line="120"/> + <location filename="../instructors/instructorsview.cpp" line="121"/> + <location filename="../instructors/instructorsview.cpp" line="133"/> + <location filename="../instructors/instructorsview.cpp" line="143"/> <source>no</source> <translation>нет</translation> </message> <message> - <location filename="../instructors/instructorsview.cpp" line="147"/> + <location filename="../instructors/instructorsview.cpp" line="177"/> <source>Instructor</source> <translation>Инструктор</translation> </message> <message> - <location filename="../instructors/instructorsview.cpp" line="147"/> + <location filename="../instructors/instructorsview.cpp" line="177"/> <source>Login</source> <translation>Логин</translation> </message> <message> - <location filename="../instructors/instructorsview.cpp" line="147"/> + <location filename="../instructors/instructorsview.cpp" line="177"/> <source>Password</source> <translation>Пароль</translation> </message> <message> - <location filename="../instructors/instructorsview.cpp" line="147"/> + <location filename="../instructors/instructorsview.cpp" line="177"/> <source>Administrator</source> <translation>Администратор</translation> </message> <message> - <location filename="../instructors/instructorsview.cpp" line="147"/> + <location filename="../instructors/instructorsview.cpp" line="177"/> <source>Archived</source> <translation>Архивный</translation> </message> <message> - <location filename="../instructors/instructorsview.cpp" line="147"/> + <location filename="../instructors/instructorsview.cpp" line="177"/> <source>Logged</source> <translation>Залогирован</translation> </message> <message> - <location filename="../instructors/instructorsview.cpp" line="147"/> + <location filename="../instructors/instructorsview.cpp" line="177"/> <source>ID</source> <translation>ID</translation> </message> </context> <context> - <name>TasksWidget</name> + <name>MessangerWidget</name> <message> - <location filename="../tasks/taskswidget.ui" line="14"/> - <location filename="../tasks/taskswidget.ui" line="22"/> - <source>Task manager</source> - <translation>Менеджер задач</translation> + <location filename="../messanger/messangerwidget.ui" line="20"/> + <source>Form</source> + <translation type="unfinished">Форма</translation> + </message> + <message> + <location filename="../messanger/messangerwidget.ui" line="28"/> + <source>Messenger</source> + <translation>Мессенджер</translation> + </message> + <message> + <location filename="../messanger/messangerwidget.ui" line="81"/> + <source>Send</source> + <translation>Отправить</translation> + </message> + <message> + <location filename="../messanger/messangerwidget.ui" line="120"/> + <source>Tab 1</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../messanger/messangerwidget.ui" line="125"/> + <source>Tab 2</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>MsgWidget</name> + <message> + <location filename="../messanger/msgwidget.ui" line="20"/> + <source>Form</source> + <translation type="unfinished">Форма</translation> + </message> + <message> + <location filename="../messanger/msgwidget.ui" line="42"/> + <source>TextLabel</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>NewVersionWidget</name> + <message> + <location filename="../widgets/newversionwidget.ui" line="20"/> + <source>Создать копию...</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../widgets/newversionwidget.ui" line="70"/> + <source>Basic version:</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../widgets/newversionwidget.ui" line="83"/> + <source>TextLabel</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../widgets/newversionwidget.ui" line="121"/> + <source>New name version:</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../widgets/newversionwidget.ui" line="195"/> + <source>Create</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../widgets/newversionwidget.ui" line="224"/> + <source>Cancel</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../widgets/newversionwidget.cpp" line="43"/> + <source>Only Latin letters and numbers</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>NotifyController</name> + <message> + <location filename="../connectorToServer/Core/notifycontroller.cpp" line="14"/> + <source>Error</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>RecognizeSystem</name> + <message> + <location filename="../connectorToServer/Core/recognizesystem.cpp" line="309"/> + <source>Attention!</source> + <translation type="unfinished">Внимание!</translation> + </message> + <message> + <location filename="../connectorToServer/Core/recognizesystem.cpp" line="309"/> + <source>The file could not be opened </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../connectorToServer/Core/recognizesystem.cpp" line="369"/> + <source>You cannot delete the basic version!</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../connectorToServer/Core/recognizesystem.cpp" line="374"/> + <source>You cannot delete the active version</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../connectorToServer/Core/recognizesystem.cpp" line="379"/> + <source>This name already exists</source> + <translation type="unfinished"></translation> </message> </context> <context> <name>TraineesView</name> <message> - <location filename="../trainees/traineesview.cpp" line="114"/> - <location filename="../trainees/traineesview.cpp" line="127"/> + <location filename="../trainees/traineesview.cpp" line="137"/> + <location filename="../trainees/traineesview.cpp" line="151"/> <source>yes</source> <translation>да</translation> </message> <message> - <location filename="../trainees/traineesview.cpp" line="120"/> - <location filename="../trainees/traineesview.cpp" line="132"/> + <location filename="../trainees/traineesview.cpp" line="143"/> + <location filename="../trainees/traineesview.cpp" line="156"/> <source>no</source> <translation>нет</translation> </message> <message> - <location filename="../trainees/traineesview.cpp" line="169"/> + <location filename="../trainees/traineesview.cpp" line="203"/> <source>Trainee</source> <translation>Обучаемый</translation> </message> <message> - <location filename="../trainees/traineesview.cpp" line="169"/> + <location filename="../trainees/traineesview.cpp" line="203"/> <source>Login</source> <translation>Логин</translation> </message> <message> - <location filename="../trainees/traineesview.cpp" line="169"/> + <location filename="../trainees/traineesview.cpp" line="203"/> <source>Password</source> <translation>Пароль</translation> </message> <message> - <location filename="../trainees/traineesview.cpp" line="169"/> + <location filename="../trainees/traineesview.cpp" line="203"/> <source>Class</source> <translation>Класс</translation> </message> <message> - <location filename="../trainees/traineesview.cpp" line="169"/> + <location filename="../trainees/traineesview.cpp" line="203"/> <source>Computer</source> <translation>Компьютер</translation> </message> <message> - <location filename="../trainees/traineesview.cpp" line="169"/> + <location filename="../trainees/traineesview.cpp" line="203"/> <source>IP address</source> <translation>IP адрес</translation> </message> <message> - <location filename="../trainees/traineesview.cpp" line="169"/> + <location filename="../trainees/traineesview.cpp" line="203"/> <source>Archived</source> <translation>Архивный</translation> </message> <message> - <location filename="../trainees/traineesview.cpp" line="169"/> + <location filename="../trainees/traineesview.cpp" line="203"/> <source>Logged</source> <translation>Залогирован</translation> </message> <message> - <location filename="../trainees/traineesview.cpp" line="169"/> - <source>Tasks</source> - <translation>Задачи</translation> - </message> - <message> - <location filename="../trainees/traineesview.cpp" line="169"/> + <location filename="../trainees/traineesview.cpp" line="203"/> <source>ID</source> <translation>ID</translation> </message> </context> +<context> + <name>VersionSelectWidget</name> + <message> + <location filename="../widgets/versionselectwidget.ui" line="20"/> + <source>Form</source> + <translation type="unfinished">Форма</translation> + </message> + <message> + <location filename="../widgets/versionselectwidget.ui" line="59"/> + <source>Available versions on the server:</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../widgets/versionselectwidget.ui" line="106"/> + <source>Create copy</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../widgets/versionselectwidget.ui" line="130"/> + <source>Delete</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../widgets/versionselectwidget.ui" line="150"/> + <source>Change server version</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../widgets/versionselectwidget.ui" line="178"/> + <source>Info:</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../widgets/versionselectwidget.ui" line="188"/> + <source>Double click on the version to see information...</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../widgets/versionselectwidget.ui" line="223"/> + <source>Current server version:</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../widgets/versionselectwidget.ui" line="230"/> + <source>none</source> + <translation type="unfinished">нет</translation> + </message> + <message> + <location filename="../widgets/versionselectwidget.cpp" line="25"/> + <source>Version control</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../widgets/versionselectwidget.cpp" line="47"/> + <source>Version name: </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../widgets/versionselectwidget.cpp" line="48"/> + <source>Created: </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../widgets/versionselectwidget.cpp" line="49"/> + <source>Changeable: </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../widgets/versionselectwidget.cpp" line="50"/> + <source>Author: </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../widgets/versionselectwidget.cpp" line="59"/> + <source>Yes</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../widgets/versionselectwidget.cpp" line="60"/> + <source>No</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../widgets/versionselectwidget.cpp" line="69"/> + <location filename="../widgets/versionselectwidget.cpp" line="84"/> + <location filename="../widgets/versionselectwidget.cpp" line="96"/> + <location filename="../widgets/versionselectwidget.cpp" line="107"/> + <source>Version not selected</source> + <translation type="unfinished"></translation> + </message> +</context> <context> <name>ViewerInstructors</name> <message> - <location filename="../instructors/viewerinstructors.ui" line="19"/> - <location filename="../instructors/viewerinstructors.ui" line="32"/> + <location filename="../instructors/viewerinstructors.ui" line="20"/> + <location filename="../instructors/viewerinstructors.ui" line="33"/> <source>Instructors</source> <translation>Инструкторы</translation> </message> <message> - <location filename="../instructors/viewerinstructors.ui" line="51"/> + <location filename="../instructors/viewerinstructors.ui" line="68"/> <source>Editor of Instructors</source> <translation>Редактор инструкторов</translation> </message> <message> - <location filename="../instructors/viewerinstructors.cpp" line="63"/> - <source>Attention!</source> - <translation>Внимание!</translation> - </message> - <message> - <location filename="../instructors/viewerinstructors.cpp" line="64"/> - <source>Only the Administrator has the right to edit instructors.</source> - <translation>Только Администратор имеет право редактировать инструкторов.</translation> - </message> - <message> - <location filename="../instructors/viewerinstructors.cpp" line="72"/> + <location filename="../instructors/viewerinstructors.cpp" line="54"/> <source>Editor of instructors</source> <translation>Редактор инструкторов</translation> </message> @@ -473,20 +882,28 @@ Delete anyway?</source> <context> <name>ViewerTrainees</name> <message> - <location filename="../trainees/viewertrainees.ui" line="19"/> - <location filename="../trainees/viewertrainees.ui" line="32"/> + <location filename="../trainees/viewertrainees.ui" line="20"/> + <location filename="../trainees/viewertrainees.ui" line="33"/> <source>Trainees</source> <translation>Обучаемые</translation> </message> <message> - <location filename="../trainees/viewertrainees.ui" line="51"/> + <location filename="../trainees/viewertrainees.ui" line="71"/> <source>Editor of Trainees</source> <translation>Редактор обучаемых</translation> </message> <message> - <location filename="../trainees/viewertrainees.cpp" line="92"/> + <location filename="../trainees/viewertrainees.cpp" line="106"/> <source>Editor of trainees</source> <translation>Редактор обучаемых</translation> </message> </context> +<context> + <name>WaitAnimationWidget</name> + <message> + <location filename="../widgets/waitanimationwidget.ui" line="20"/> + <source>Form</source> + <translation type="unfinished">Форма</translation> + </message> +</context> </TS> diff --git a/InstructorsAndTrainees/widgets/newversionwidget.cpp b/InstructorsAndTrainees/widgets/newversionwidget.cpp new file mode 100644 index 0000000..1431ed4 --- /dev/null +++ b/InstructorsAndTrainees/widgets/newversionwidget.cpp @@ -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")); +} diff --git a/InstructorsAndTrainees/widgets/newversionwidget.h b/InstructorsAndTrainees/widgets/newversionwidget.h new file mode 100644 index 0000000..e1696fb --- /dev/null +++ b/InstructorsAndTrainees/widgets/newversionwidget.h @@ -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(); + + diff --git a/InstructorsAndTrainees/widgets/newversionwidget.ui b/InstructorsAndTrainees/widgets/newversionwidget.ui new file mode 100644 index 0000000..928f73b --- /dev/null +++ b/InstructorsAndTrainees/widgets/newversionwidget.ui @@ -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> diff --git a/InstructorsAndTrainees/widgets/versionselectwidget.cpp b/InstructorsAndTrainees/widgets/versionselectwidget.cpp new file mode 100644 index 0000000..93ea715 --- /dev/null +++ b/InstructorsAndTrainees/widgets/versionselectwidget.cpp @@ -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; +} + diff --git a/InstructorsAndTrainees/widgets/versionselectwidget.h b/InstructorsAndTrainees/widgets/versionselectwidget.h new file mode 100644 index 0000000..5b6a9c8 --- /dev/null +++ b/InstructorsAndTrainees/widgets/versionselectwidget.h @@ -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 diff --git a/InstructorsAndTrainees/widgets/versionselectwidget.ui b/InstructorsAndTrainees/widgets/versionselectwidget.ui new file mode 100644 index 0000000..af13a94 --- /dev/null +++ b/InstructorsAndTrainees/widgets/versionselectwidget.ui @@ -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> diff --git a/InstructorsAndTrainees/widgets/waitanimationwidget.cpp b/InstructorsAndTrainees/widgets/waitanimationwidget.cpp new file mode 100644 index 0000000..f226429 --- /dev/null +++ b/InstructorsAndTrainees/widgets/waitanimationwidget.cpp @@ -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; +} diff --git a/InstructorsAndTrainees/widgets/waitanimationwidget.h b/InstructorsAndTrainees/widgets/waitanimationwidget.h new file mode 100644 index 0000000..827ee99 --- /dev/null +++ b/InstructorsAndTrainees/widgets/waitanimationwidget.h @@ -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 diff --git a/InstructorsAndTrainees/widgets/waitanimationwidget.ui b/InstructorsAndTrainees/widgets/waitanimationwidget.ui new file mode 100644 index 0000000..4819a89 --- /dev/null +++ b/InstructorsAndTrainees/widgets/waitanimationwidget.ui @@ -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> diff --git a/ServerLMS/Data/PacketType.h b/ServerLMS/Data/PacketType.h index 2b54672..3b1929d 100644 --- a/ServerLMS/Data/PacketType.h +++ b/ServerLMS/Data/PacketType.h @@ -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, diff --git a/ServerLMS/Data/StreamingVersionData.h b/ServerLMS/Data/StreamingVersionData.h index 3746bd7..20c19c9 100644 --- a/ServerLMS/Data/StreamingVersionData.h +++ b/ServerLMS/Data/StreamingVersionData.h @@ -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 + + + + + + + diff --git a/ServerLMS/Data/typesDataServerClient.h b/ServerLMS/Data/typesDataServerClient.h index 32a19ab..beee14e 100644 --- a/ServerLMS/Data/typesDataServerClient.h +++ b/ServerLMS/Data/typesDataServerClient.h @@ -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: diff --git a/ServerLMS/Systems/Parsers/dbanswerparser.cpp b/ServerLMS/Systems/Parsers/dbanswerparser.cpp index fffd8be..cfce748 100644 --- a/ServerLMS/Systems/Parsers/dbanswerparser.cpp +++ b/ServerLMS/Systems/Parsers/dbanswerparser.cpp @@ -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(); } diff --git a/ServerLMS/Systems/Parsers/dbanswerparser.h b/ServerLMS/Systems/Parsers/dbanswerparser.h index 26300ad..64c53e5 100644 --- a/ServerLMS/Systems/Parsers/dbanswerparser.h +++ b/ServerLMS/Systems/Parsers/dbanswerparser.h @@ -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: diff --git a/ServerLMS/Systems/Parsers/processparser.cpp b/ServerLMS/Systems/Parsers/processparser.cpp index 7a94451..d6706e5 100644 --- a/ServerLMS/Systems/Parsers/processparser.cpp +++ b/ServerLMS/Systems/Parsers/processparser.cpp @@ -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); } diff --git a/ServerLMS/Systems/Parsers/processparser.h b/ServerLMS/Systems/Parsers/processparser.h index 41046a1..2dca25a 100644 --- a/ServerLMS/Systems/Parsers/processparser.h +++ b/ServerLMS/Systems/Parsers/processparser.h @@ -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 diff --git a/ServerLMS/Systems/assetsmanager.cpp b/ServerLMS/Systems/assetsmanager.cpp index ee883e2..b707f58 100644 --- a/ServerLMS/Systems/assetsmanager.cpp +++ b/ServerLMS/Systems/assetsmanager.cpp @@ -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() { diff --git a/ServerLMS/Systems/assetsmanager.h b/ServerLMS/Systems/assetsmanager.h index 559a0f7..dec309c 100644 --- a/ServerLMS/Systems/assetsmanager.h +++ b/ServerLMS/Systems/assetsmanager.h @@ -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 diff --git a/ServerLMS/Systems/commonclienthandler.cpp b/ServerLMS/Systems/commonclienthandler.cpp index e7e769d..2a84c6f 100644 --- a/ServerLMS/Systems/commonclienthandler.cpp +++ b/ServerLMS/Systems/commonclienthandler.cpp @@ -39,7 +39,7 @@ void CommonClientHandler::sendCurrentVersionToAllClient() } } -void CommonClientHandler::slot_AuthChanged() +void CommonClientHandler::slot_ListsInstructorsTraineesChanged() { //Проходим все открытые сокеты foreach(int idSocket, clientsMap->keys()) diff --git a/ServerLMS/Systems/commonclienthandler.h b/ServerLMS/Systems/commonclienthandler.h index 1abf5b8..0348e05 100644 --- a/ServerLMS/Systems/commonclienthandler.h +++ b/ServerLMS/Systems/commonclienthandler.h @@ -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); diff --git a/ServerLMS/Systems/processingsystem.cpp b/ServerLMS/Systems/processingsystem.cpp index ae055ab..f37dd50 100644 --- a/ServerLMS/Systems/processingsystem.cpp +++ b/ServerLMS/Systems/processingsystem.cpp @@ -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; } diff --git a/ServerLMS/Systems/processingsystem.h b/ServerLMS/Systems/processingsystem.h index 22dc00d..cb73318 100644 --- a/ServerLMS/Systems/processingsystem.h +++ b/ServerLMS/Systems/processingsystem.h @@ -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; }; diff --git a/ServerLMS/Systems/recognizesystem.cpp b/ServerLMS/Systems/recognizesystem.cpp index 2389ca6..c524157 100644 --- a/ServerLMS/Systems/recognizesystem.cpp +++ b/ServerLMS/Systems/recognizesystem.cpp @@ -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() { diff --git a/ServerLMS/Systems/recognizesystem.h b/ServerLMS/Systems/recognizesystem.h index 7d5c97c..a28a893 100644 --- a/ServerLMS/Systems/recognizesystem.h +++ b/ServerLMS/Systems/recognizesystem.h @@ -36,7 +36,7 @@ signals: void sigXmlParser(ClientHandler *clientHandler,QByteArray data); void sigChangeVersion(QString versionName); void sigDeleteVersion(QString versionName); - void sigCopyVersion(QString versionName,QString newVersionName); + void sigCopyVersion(QString versionName,QString newVersionName,QString author); private: UpdateController *updateController; @@ -61,5 +61,7 @@ private: void packetTypeInit(PacketType packet,Client *client); void packetTypeInit(PacketType type); QString createFullPath(QString path); + bool checkIsChangeable(); + bool checkNonStaticData(QString path); }; #endif // RECOGNIZESYSTEM_H diff --git a/ServerLMS/Systems/sendsystem.cpp b/ServerLMS/Systems/sendsystem.cpp index 65caacd..7d2140b 100644 --- a/ServerLMS/Systems/sendsystem.cpp +++ b/ServerLMS/Systems/sendsystem.cpp @@ -79,13 +79,13 @@ void SendSystem::sendFileBlock(QString path) countSend++; } - emit sigSendToLogger(Tools::getTime() + " send file " + fileInfo.fileName()); + //emit sigSendToLogger(Tools::getTime() + " send file " + fileInfo.fileName()); } file.close(); countSend = 0; socket->waitForBytesWritten(10); - socket->waitForReadyRead(100); + socket->waitForReadyRead(20); } void SendSystem::sendVersion() diff --git a/ServerLMS/Systems/tools.cpp b/ServerLMS/Systems/tools.cpp index 68de94f..70e2093 100644 --- a/ServerLMS/Systems/tools.cpp +++ b/ServerLMS/Systems/tools.cpp @@ -66,6 +66,11 @@ QString Tools::createStreamingToRealPath(QString path,StreamingVersionData* stre } +QString Tools::createVersionHashFilepath(QString fileName) +{ + return staticDataFolderName + "/" + fileName + "Hash.xml"; +} + QString Tools::createUpdateFilePath(QString path) { //qDebug() << "Full path: " << path; diff --git a/ServerLMS/Systems/tools.h b/ServerLMS/Systems/tools.h index e896664..cd8840f 100644 --- a/ServerLMS/Systems/tools.h +++ b/ServerLMS/Systems/tools.h @@ -15,18 +15,23 @@ static const QString staticDataFolderName = "StaticData"; static const QString applicationFolderName = "Application"; static const QString sharedDataFolderName = "SharedData"; +static const QString additionalFilesFolderName = "RRJ-95NEW-100"; static const QString streamingAssetsFolderName = "StreamingAssets"; +static const QString versionFolderName = "StreamingVersion"; static const QString tempFile = staticDataFolderName + "/save.xml"; static const QString version = staticDataFolderName + "/version.xml"; static const QString versionListFile = staticDataFolderName + "/versionList.xml"; static const QString hashFileName = staticDataFolderName + "/serverHash.xml"; static const QString buildHashName = staticDataFolderName + "/buildHash.xml"; static const QString buildDataPath = "/Application/RRJLoader/RRJ_Data/"; +static const QString tasksAMMfileName = "/tasksAmm.xml"; +static const QString tasksFIMfileName = "/tasksFIM.xml"; static const QString clientHash = staticDataFolderName + "/clientHash.xml"; static const QString baseNameVersion = "base";//может вынести комманды куда нибудь? static const QString commandTryBaseDelete = "BASEDELETETRY"; +static const QString commandTryBaseChange = "TRYBASECHANGE"; static const QString commandTryActiveDelete = "TRYACTIVEDELETE"; static const QString commandTryCopyWithSameNames = "SAMENAMES"; static const QString commandGetServerDataList = "GETSERVERDATALIST"; @@ -35,6 +40,9 @@ static const QString commandReadyClient = "READY"; static const QString commandDisableClient = "DISABLE"; static const QString commandDuplicateVerName = "DUPLICATEVERNAME"; static const QString commandHashCompleteClient = "HASHSENDCOMPLETE"; +static const QString commandCanChangeVersion = "CANCHANGE"; +static const QString commandChangable = "CHANGEABLE"; +static const QString commandUnchangable = "UNCHANGEABLE"; static const QString commandUpdateFilesClient = "update"; class Tools { @@ -48,6 +56,7 @@ public: static QString createSharedPath(QString path); static QString createRealPath(QString path,StreamingVersionData* currentVersionData); static QString createStreamingToRealPath(QString path, StreamingVersionData *streamingVersionData); + static QString createVersionHashFilepath(QString fileName); }; diff --git a/ServerLMS/Systems/updatecontroller.cpp b/ServerLMS/Systems/updatecontroller.cpp index 7b0247a..52264ec 100644 --- a/ServerLMS/Systems/updatecontroller.cpp +++ b/ServerLMS/Systems/updatecontroller.cpp @@ -41,10 +41,10 @@ void UpdateController::changeAssetVersion(QString versionName) commonClientHandler->slot_sendPacketToAllClients(PacketType::FREE); } -void UpdateController::createCopyVersion(QString versionName,QString newVersionName) +void UpdateController::createCopyVersion(QString versionName,QString newVersionName,QString author) { commonClientHandler->slot_sendPacketToAllClients(PacketType::BUSY); - assetManager->createCopyVersion(versionName,newVersionName); + assetManager->createCopyVersion(versionName,newVersionName,author); commonClientHandler->slot_sendPacketToAllClients(PacketType::FREE); } @@ -193,6 +193,11 @@ void UpdateController::setUpCurrentServerHash() saveHash(hashFileName,fileList); } +void UpdateController::setDataInfo(DataInfo *value) +{ + dataInfo = value; +} + QString UpdateController::getCurrentStreamingPath() const { return currentStreamingPath; @@ -362,6 +367,12 @@ QList<FileData>* UpdateController::calculateHash(QString path) return files; } +QString UpdateController::getPathAdditionalFile(QString name) +{ + QString path = Tools::createSharedPath("/" + assetManager->getCurrentVersionData()->getViewName() + "/" + additionalFilesFolderName + name); + return path; +} + QByteArray UpdateController::getLocalHash() { QFile hashFile(hashFileName); @@ -422,7 +433,7 @@ void UpdateController::calculateSharedHash() if (fileInfo.fileName() == "." || fileInfo.fileName() == "..") continue; - QString fileName = staticDataFolderName + "/" + fileInfo.fileName() + "Hash.xml"; + QString fileName = Tools::createVersionHashFilepath(fileInfo.fileName()); fileList = calculateHash(fileInfo.absoluteFilePath()); saveHash(fileName,fileList); @@ -431,74 +442,24 @@ void UpdateController::calculateSharedHash() fileInfo.absoluteFilePath(),fileInfo.fileName(), fileInfo.birthTime(),fileInfo.size()); +// if(fileInfo.fileName() == baseNameVersion) +// { +// version->setIsChangeable(false); +// version->setAuthor(tr("Константа-дизайн")); +// } +// else +// { +// version->setIsChangeable(true); +// } + versionList->append(version); } - createVersionListXmlAnswer(*versionList); - assetManager->setVersionList(versionList); + assetManager->createFirstVersionListXML(*versionList); + //assetManager->setVersionList(versionList); } -void UpdateController::createVersionListXmlAnswer(QList<StreamingVersionData *> version) //TODO: переименовать и перебросить в AssetManager -{ - QList<SXmlAnswerTag> listTag; - - foreach(StreamingVersionData* ver,version) - { - SAttribute attribute1 = {"Version", ver->getViewName()}; - SAttribute attribute2 = {"Created", ver->getCreateData().toString()}; - - QList<SAttribute> listAttr = {attribute1, attribute2}; - SXmlAnswerTag tag = {"VersionData", listAttr}; - - listTag.append(tag); - } - - - QFile file(versionListFile); - 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 UpdateController::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.writeEndElement(); - xmlWriter.writeEndDocument(); - - file.close(); -} - - void UpdateController::sendNewVersionList() { commonClientHandler->sendNewVersionListToAllClient(); @@ -543,6 +504,11 @@ void UpdateController::xmlFileDataParse(QByteArray array) } } +StreamingVersionData* UpdateController::getCurrentVersion() +{ + return assetManager->getCurrentVersionData(); +} + void UpdateController::printFileList(QList<FileData> fileData) { QListIterator<FileData> iterator(fileData); diff --git a/ServerLMS/Systems/updatecontroller.h b/ServerLMS/Systems/updatecontroller.h index efe3aee..0abbbdd 100644 --- a/ServerLMS/Systems/updatecontroller.h +++ b/ServerLMS/Systems/updatecontroller.h @@ -51,12 +51,16 @@ public: DataInfo *getCurrentDataInfo(); void clearCurrentDataInfo(); - void createVersionListXmlAnswer(QList<StreamingVersionData *> version); - void saveVersionToFile(StreamingVersionData *streamingVersion); void xmlFileDataParse(QByteArray array); + + QString getPathAdditionalFile(QString name); + + StreamingVersionData *getCurrentVersion(); + void setDataInfo(DataInfo *value); + public slots: void changeAssetVersion(QString versionName); - void createCopyVersion(QString versionName,QString newVersionName); + void createCopyVersion(QString versionName,QString newVersionName,QString author); void deleteAssetVersion(QString versionName); void setUpCurrentServerHash(); diff --git a/ServerLMS/clienthandler.cpp b/ServerLMS/clienthandler.cpp index 30c8448..5b0604f 100644 --- a/ServerLMS/clienthandler.cpp +++ b/ServerLMS/clienthandler.cpp @@ -103,6 +103,11 @@ void ClientHandler::sendXmlAnswer(QByteArray array, PacketType packetType) emit sigSendXmlAnswer(array, packetType); } +void ClientHandler::sendFileBlock(QString path) +{ + emit sigFileBlock(path); +} + bool ClientHandler::getIsSendStopped() { return emit sigGetIsSendStopped(); diff --git a/ServerLMS/providerdblms.cpp b/ServerLMS/providerdblms.cpp index baca6c3..3372bd6 100644 --- a/ServerLMS/providerdblms.cpp +++ b/ServerLMS/providerdblms.cpp @@ -201,6 +201,63 @@ QString ProviderDBLMS::getNameInstructorByLogin(QString login) return res; } +bool ProviderDBLMS::deAuthorizationAll() +{ + mtxAccess.lock(); + + if(! dbLMS->DBisConnected()) + { + mtxAccess.unlock(); + return false; + } + + Q_EMIT signal_BlockAutorization(true); + + bool res1 = dbLMS->deAuthorizationAllTrainees(); + bool res2 = dbLMS->deAuthorizationAllInstructors(); + + Q_EMIT signal_BlockAutorization(false); + + mtxAccess.unlock(); + return res1 && res2; +} + +int ProviderDBLMS::getIdTraineeByLogin(QString login) +{ + int id_trainee = 0; + qDebug() << "ProviderDBLMS " << QThread::currentThreadId(); + mtxAccess.lock(); + + if(! dbLMS->DBisConnected()) + { + mtxAccess.unlock(); + return id_trainee; + } + + id_trainee = dbLMS->getIdTraineeByLogin(login); + + mtxAccess.unlock(); + return id_trainee; +} + +QString ProviderDBLMS::getLoginTraineeById(int id_trainee) +{ + QString login = ""; + qDebug() << "ProviderDBLMS " << QThread::currentThreadId(); + mtxAccess.lock(); + + if(! dbLMS->DBisConnected()) + { + mtxAccess.unlock(); + return login; + } + + login = dbLMS->getTrainee(id_trainee).getLogin(); + + mtxAccess.unlock(); + return login; +} + QList<Instructor> ProviderDBLMS::GetListAllInstructors() { QList<Instructor> listInstructors; @@ -299,3 +356,69 @@ int ProviderDBLMS::editGroup(Group group) { return dbLMS->editGroup(group); } + +int ProviderDBLMS::newTaskAMM(TaskAmmFim task, int id_trainee) +{ + return dbLMS->newTaskAMM(task, id_trainee); +} + +int ProviderDBLMS::delTaskAMM(int id) +{ + return dbLMS->delTaskAMM(id); +} + +int ProviderDBLMS::editTaskAMM(TaskAmmFim task) +{ + return dbLMS->editTaskAMM(task); +} + +int ProviderDBLMS::newTaskFIM(TaskAmmFim task, int id_trainee) +{ + return dbLMS->newTaskFIM(task, id_trainee); +} + +int ProviderDBLMS::delTaskFIM(int id) +{ + return dbLMS->delTaskFIM(id); +} + +int ProviderDBLMS::editTaskFIM(TaskAmmFim task) +{ + return dbLMS->editTaskFIM(task); +} + +QList<TaskAmmFim> ProviderDBLMS::GetListTasksAMMofTrainee(int id_trainee) +{ + QList<TaskAmmFim> listTasks; + + mtxAccess.lock(); + + if(! dbLMS->DBisConnected()) + { + mtxAccess.unlock(); + return listTasks; + } + + listTasks = dbLMS->getListTasksAMMofTrainee(id_trainee); + + mtxAccess.unlock(); + return listTasks; +} + +QList<TaskAmmFim> ProviderDBLMS::GetListTasksFIMofTrainee(int id_trainee) +{ + QList<TaskAmmFim> listTasks; + + mtxAccess.lock(); + + if(! dbLMS->DBisConnected()) + { + mtxAccess.unlock(); + return listTasks; + } + + listTasks = dbLMS->getListTasksFIMofTrainee(id_trainee); + + mtxAccess.unlock(); + return listTasks; +} diff --git a/ServerLMS/providerdblms.h b/ServerLMS/providerdblms.h index 06fcc75..38f0b37 100644 --- a/ServerLMS/providerdblms.h +++ b/ServerLMS/providerdblms.h @@ -4,6 +4,7 @@ #include <QObject> #include <QDebug> #include "interfacedatabaselms.h" +#include "tasksAmmFim.h" class ProviderDBLMS : public QObject { @@ -24,7 +25,13 @@ public: bool deAuthorizationInstructor(QString login); QString getNameInstructorByLogin(QString login); + //Общая деавторизация + bool deAuthorizationAll(); + // + int getIdTraineeByLogin(QString login); + QString getLoginTraineeById(int id_trainee); + QList<Instructor> GetListAllInstructors(); QList<Trainee> GetListAllTrainees(); QList<Group> GetListAllGroups(); @@ -41,6 +48,17 @@ public: int delGroup(int id); int editGroup(Group group); + int newTaskAMM(TaskAmmFim task, int id_trainee); + int delTaskAMM(int id); + int editTaskAMM(TaskAmmFim task); + + int newTaskFIM(TaskAmmFim task, int id_trainee); + int delTaskFIM(int id); + int editTaskFIM(TaskAmmFim task); + + QList<TaskAmmFim> GetListTasksAMMofTrainee(int id_trainee); + QList<TaskAmmFim> GetListTasksFIMofTrainee(int id_trainee); + Q_SIGNALS: //сигнал о блокировке авторизации void signal_BlockAutorization(bool block); diff --git a/ServerLMS/resources.qrc b/ServerLMS/resources.qrc index e502958..73ebc38 100644 --- a/ServerLMS/resources.qrc +++ b/ServerLMS/resources.qrc @@ -5,5 +5,7 @@ <file>resources/blankXML/ListTrainees.xml</file> <file>resources/icons/switchOff.png</file> <file>resources/icons/switchOn.png</file> + <file>resources/blankXML/ListTasksAMM.xml</file> + <file>resources/blankXML/ListTasksFIM.xml</file> </qresource> </RCC> diff --git a/ServerLMS/resources/blankXML/ListTasksAMM.xml b/ServerLMS/resources/blankXML/ListTasksAMM.xml new file mode 100644 index 0000000..d8ddab9 --- /dev/null +++ b/ServerLMS/resources/blankXML/ListTasksAMM.xml @@ -0,0 +1,3 @@ +<?xml version="1.0" encoding="utf-8"?> +<ListTasksAMM> +</ListTasksAMM> \ No newline at end of file diff --git a/ServerLMS/resources/blankXML/ListTasksFIM.xml b/ServerLMS/resources/blankXML/ListTasksFIM.xml new file mode 100644 index 0000000..c080f12 --- /dev/null +++ b/ServerLMS/resources/blankXML/ListTasksFIM.xml @@ -0,0 +1,3 @@ +<?xml version="1.0" encoding="utf-8"?> +<ListTasksFIM> +</ListTasksFIM> \ No newline at end of file diff --git a/ServerLMS/serverlmswidget.cpp b/ServerLMS/serverlmswidget.cpp index b90a0c3..8dfe6b4 100644 --- a/ServerLMS/serverlmswidget.cpp +++ b/ServerLMS/serverlmswidget.cpp @@ -27,24 +27,19 @@ ServerLMSWidget::ServerLMSWidget(QWidget *parent) : qRegisterMetaType<PacketType>("PacketType"); - ui->comboTasks->addItem("Задача 1"); - ui->comboTasks->addItem("Задача 2"); - ui->comboTasks->addItem("Задача 3"); - ui->comboTasks->addItem("Задача 4"); - ui->comboTasks->addItem("Задача 5"); - ui->btnStopServer->setEnabled(false); ui->btnStartServer->setEnabled(true); // Сделаем первоначальную инициализацию перевода для окна виджета - qtLanguageTranslator.load(QString("translations/ServerLMS_") + QString("ru_RU"), "."); - qApp->installTranslator(&qtLanguageTranslator); + //qtLanguageTranslator.load(QString("translations/ServerLMS_") + QString("en_EN"), "."); + //qApp->installTranslator(&qtLanguageTranslator); updateThread = new QThread; loggerThread = new QThread; providerDBLMS = new ProviderDBLMS(this); providerDBLMS->ConnectionToDB(); + providerDBLMS->deAuthorizationAll(); logger = new Logger(ui->listWidgetLogger); @@ -56,21 +51,21 @@ ServerLMSWidget::ServerLMSWidget(QWidget *parent) : assetsManager = new AssetsManager; assetsManager->moveToThread(updateThread); - processingSystem = new ProcessingSystem(providerDBLMS); + updateController = new UpdateController; + updateController->moveToThread(updateThread); + + processingSystem = new ProcessingSystem(providerDBLMS, updateController); processingSystem->moveToThread(updateThread); dataParser = new DataParser(assetsManager,processingSystem); - updateController = new UpdateController; - updateController->moveToThread(updateThread); - commonClientHandler = new CommonClientHandler; loggerThread->start(); updateThread->start(); commonClientHandler->initialize(&clientsMap,processingSystem,dataParser,logger); - processingSystem->initialize(this,dataParser,commonClientHandler,logger); + processingSystem->initialize(this,dataParser,commonClientHandler,logger,updateController); logger->setTypeLog("widget"); @@ -225,15 +220,6 @@ void ServerLMSWidget::slot_LanguageChanged(QString language) qApp->installTranslator(&qtLanguageTranslator); } - -void ServerLMSWidget::on_btnTaskSet_clicked() -{ - QString fullNameClient = ui->listWidget_Clients->currentItem()->text(); - QString textTask = ui->comboTasks->currentText(); - - commonClientHandler->slot_sendTaskToClient(fullNameClient,textTask); -} - void ServerLMSWidget::slotAddToLog(QString msg) { ui->listWidgetLogger->addItem(msg); @@ -246,7 +232,6 @@ void ServerLMSWidget::on_btnStartServer_clicked() if(startServer()) { QApplication::setOverrideCursor(Qt::WaitCursor); - emit sigCalculateFullHash(); QApplication::restoreOverrideCursor(); ui->btnStartServer->setEnabled(false); diff --git a/ServerLMS/serverlmswidget.h b/ServerLMS/serverlmswidget.h index 545d822..a19d13a 100644 --- a/ServerLMS/serverlmswidget.h +++ b/ServerLMS/serverlmswidget.h @@ -98,7 +98,6 @@ public: private slots: void on_btnStartServer_clicked(); void on_btnStopServer_clicked(); - void on_btnTaskSet_clicked(); private: bool startServer(); diff --git a/ServerLMS/serverlmswidget.ui b/ServerLMS/serverlmswidget.ui index db4997a..db46443 100644 --- a/ServerLMS/serverlmswidget.ui +++ b/ServerLMS/serverlmswidget.ui @@ -28,7 +28,7 @@ <string>Server LMS</string> </property> <layout class="QGridLayout" name="gridLayout_7"> - <item row="4" column="0"> + <item row="3" column="0"> <widget class="QGroupBox" name="groupBox"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Expanding"> @@ -92,7 +92,7 @@ </layout> </widget> </item> - <item row="5" column="0"> + <item row="4" column="0"> <layout class="QHBoxLayout" name="horizontalLayout"> <item> <widget class="QLabel" name="lblOnOff"> @@ -184,33 +184,6 @@ </item> </layout> </item> - <item row="3" column="0"> - <widget class="QGroupBox" name="groupBox_4"> - <property name="title"> - <string>Tasks</string> - </property> - <layout class="QGridLayout" name="gridLayout_6"> - <item row="0" column="0"> - <layout class="QVBoxLayout" name="verticalLayout_4"> - <item> - <layout class="QHBoxLayout" name="horizontalLayout_4"> - <item> - <widget class="QComboBox" name="comboTasks"/> - </item> - <item> - <widget class="QToolButton" name="btnTaskSet"> - <property name="text"> - <string>Set</string> - </property> - </widget> - </item> - </layout> - </item> - </layout> - </item> - </layout> - </widget> - </item> </layout> </widget> </item> diff --git a/TestServerLMS/mainwindow.cpp b/TestServerLMS/mainwindow.cpp index c991e89..5f32e2d 100644 --- a/TestServerLMS/mainwindow.cpp +++ b/TestServerLMS/mainwindow.cpp @@ -10,16 +10,18 @@ MainWindow::MainWindow(QWidget *parent) : { ui->setupUi(this); + //Задаём два пункта с текстом локалей в комбобоксе + ui->cmbLanguage->addItems(QStringList() << "English" << "Русский"); + m_serverLMSWidget = new ServerLMSWidget(this); ui->verticalLayout_1->addWidget(m_serverLMSWidget); connect(this, &MainWindow::signal_LanguageChanged, m_serverLMSWidget, &ServerLMSWidget::slot_LanguageChanged); - //Задаём два пункта с текстом локалей в комбобоксе - ui->cmbLanguage->addItems(QStringList() << "English" << "Русский"); - - this->showMaximized(); + this->move(0, 0); + this->showNormal(); + //this->showMaximized(); } MainWindow::~MainWindow()