diff --git a/Modules/Forms/CMakeLists.txt b/Modules/Forms/CMakeLists.txt new file mode 100644 index 0000000000..83366a3827 --- /dev/null +++ b/Modules/Forms/CMakeLists.txt @@ -0,0 +1,7 @@ +mitk_create_module( + INCLUDE_DIRS + PUBLIC include + PRIVATE src + DEPENDS + PUBLIC MitkCore +) diff --git a/Modules/Forms/files.cmake b/Modules/Forms/files.cmake new file mode 100644 index 0000000000..6f8dc23d24 --- /dev/null +++ b/Modules/Forms/files.cmake @@ -0,0 +1,29 @@ +set(H_FILES + include/mitkCheckboxesQuestion.h + include/mitkDropdownQuestion.h + include/mitkForm.h + include/mitkIQuestionFactory.h + include/mitkLinearScaleQuestion.h + include/mitkMultipleChoiceQuestion.h + include/mitkParagraphQuestion.h + include/mitkQuestion.h + include/mitkQuestionWithOptions.h + include/mitkShortAnswerQuestion.h + include/mitkTextQuestion.h +) + +set(CPP_FILES + mitkCheckboxesQuestion.cpp + mitkDropdownQuestion.cpp + mitkForm.cpp + mitkIQuestionFactory.cpp + mitkLinearScaleQuestion.cpp + mitkModuleActivator.cpp + mitkMultipleChoiceQuestion.cpp + mitkParagraphQuestion.cpp + mitkQuestion.cpp + mitkQuestionFactory.cpp + mitkQuestionWithOptions.cpp + mitkShortAnswerQuestion.cpp + mitkTextQuestion.cpp +) diff --git a/Modules/Forms/include/mitkCheckboxesQuestion.h b/Modules/Forms/include/mitkCheckboxesQuestion.h new file mode 100644 index 0000000000..a5df48f5a9 --- /dev/null +++ b/Modules/Forms/include/mitkCheckboxesQuestion.h @@ -0,0 +1,43 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +All rights reserved. + +Use of this source code is governed by a 3-clause BSD license that can be +found in the LICENSE file. + +============================================================================*/ + +#ifndef mitkCheckboxesQuestion_h +#define mitkCheckboxesQuestion_h + +#include + +namespace mitk::Forms +{ + class MITKFORMS_EXPORT CheckboxesQuestion : public QuestionWithOptions + { + public: + ~CheckboxesQuestion() override; + + std::string GetType() const override; + Question* CreateAnother() const override; + + void FromJSON(const nlohmann::ordered_json& j) override; + void ToJSON(nlohmann::ordered_json& j) const override; + + using QuestionWithOptions::AddResponse; + using QuestionWithOptions::RemoveResponse; + using QuestionWithOptions::EnableOtherOption; + using QuestionWithOptions::HasOtherOption; + using QuestionWithOptions::AddOtherResponse; + using QuestionWithOptions::RemoveOtherResponse; + }; + + MITKFORMS_EXPORT void from_json(const nlohmann::ordered_json& j, CheckboxesQuestion& q); + MITKFORMS_EXPORT void to_json(nlohmann::ordered_json& j, const CheckboxesQuestion& q); +} + +#endif diff --git a/Modules/Forms/include/mitkDropdownQuestion.h b/Modules/Forms/include/mitkDropdownQuestion.h new file mode 100644 index 0000000000..bcd4f61d54 --- /dev/null +++ b/Modules/Forms/include/mitkDropdownQuestion.h @@ -0,0 +1,38 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +All rights reserved. + +Use of this source code is governed by a 3-clause BSD license that can be +found in the LICENSE file. + +============================================================================*/ + +#ifndef mitkDropdownQuestion_h +#define mitkDropdownQuestion_h + +#include + +namespace mitk::Forms +{ + class MITKFORMS_EXPORT DropdownQuestion : public QuestionWithOptions + { + public: + ~DropdownQuestion() override; + + std::string GetType() const override; + Question* CreateAnother() const override; + + void FromJSON(const nlohmann::ordered_json& j) override; + void ToJSON(nlohmann::ordered_json& j) const override; + + using QuestionWithOptions::SetResponse; + }; + + MITKFORMS_EXPORT void from_json(const nlohmann::ordered_json& j, DropdownQuestion& q); + MITKFORMS_EXPORT void to_json(nlohmann::ordered_json& j, const DropdownQuestion& q); +} + +#endif diff --git a/Modules/Forms/include/mitkForm.h b/Modules/Forms/include/mitkForm.h new file mode 100644 index 0000000000..de947ede9e --- /dev/null +++ b/Modules/Forms/include/mitkForm.h @@ -0,0 +1,87 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +All rights reserved. + +Use of this source code is governed by a 3-clause BSD license that can be +found in the LICENSE file. + +============================================================================*/ + +#ifndef mitkForm_h +#define mitkForm_h + +#include +#include + +#include +#include +#include + +#include + +namespace mitk::Forms +{ + class Question; + + class MITKFORMS_EXPORT Form + { + public: + class MITKFORMS_EXPORT Section + { + public: + explicit Section(const std::string& title = "", const std::string& description = ""); + ~Section(); + + Section(Section&& other) noexcept; + Section& operator=(Section&& other) noexcept; + + std::string GetTitle() const; + void SetTitle(const std::string& title); + + std::string GetDescription() const; + void SetDescription(const std::string& description); + + std::vector GetQuestions() const; + void AddQuestion(Question* question); + + private: + std::string m_Title; + std::string m_Description; + std::vector> m_Questions; + }; + + explicit Form(const std::string& title = "", const std::string& description = ""); + ~Form(); + + Form(Form&& other) noexcept; + Form& operator=(Form&& other) noexcept; + + Section& AddSection(const std::string& title = "", const std::string& description = ""); + int GetNumberOfSections() const; + + Section& GetSection(int index); + const Section& GetSection(int index) const; + + std::string GetTitle() const; + void SetTitle(const std::string& title); + + std::string GetDescription() const; + void SetDescription(const std::string& description); + + std::vector GetQuestions() const; + void AddQuestion(Question* question); + + void Submit(const fs::path& csvPath) const; + + private: + std::vector
m_Sections; + }; + + MITKFORMS_EXPORT void from_json(const nlohmann::ordered_json& j, Form& f); + MITKFORMS_EXPORT void to_json(nlohmann::ordered_json& j, const Form& f); +} + +#endif diff --git a/Modules/Forms/include/mitkIQuestionFactory.h b/Modules/Forms/include/mitkIQuestionFactory.h new file mode 100644 index 0000000000..7ba8ac78ae --- /dev/null +++ b/Modules/Forms/include/mitkIQuestionFactory.h @@ -0,0 +1,39 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +All rights reserved. + +Use of this source code is governed by a 3-clause BSD license that can be +found in the LICENSE file. + +============================================================================*/ + +#ifndef mitkIQuestionFactory_h +#define mitkIQuestionFactory_h + +#include +#include + +#include + +namespace mitk::Forms +{ + class Question; + + class MITKFORMS_EXPORT IQuestionFactory + { + public: + static IQuestionFactory* GetInstance(); + + virtual ~IQuestionFactory(); + + virtual void Register(Question* question) = 0; + virtual Question* Create(const std::string& type) const = 0; + }; +} + +MITK_DECLARE_SERVICE_INTERFACE(mitk::Forms::IQuestionFactory, "org.mitk.Forms.IQuestionFactory") + +#endif diff --git a/Modules/Forms/include/mitkLinearScaleQuestion.h b/Modules/Forms/include/mitkLinearScaleQuestion.h new file mode 100644 index 0000000000..87b8a84767 --- /dev/null +++ b/Modules/Forms/include/mitkLinearScaleQuestion.h @@ -0,0 +1,58 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +All rights reserved. + +Use of this source code is governed by a 3-clause BSD license that can be +found in the LICENSE file. + +============================================================================*/ + +#ifndef mitkLinearScaleQuestion_h +#define mitkLinearScaleQuestion_h + +#include +#include +#include + +namespace mitk::Forms +{ + class MITKFORMS_EXPORT LinearScaleQuestion : public Question + { + public: + LinearScaleQuestion(); + ~LinearScaleQuestion() override; + + std::string GetType() const override; + Question* CreateAnother() const override; + + void FromJSON(const nlohmann::ordered_json& j) override; + void ToJSON(nlohmann::ordered_json& j) const override; + + std::vector GetResponsesAsStrings() const override; + void ClearResponses() override; + + bool IsComplete() const override; + + std::optional GetResponse() const; + void SetResponse(int response); + + std::pair GetRange() const; + void SetRange(const std::pair& range); + + std::pair GetRangeLabels() const; + void SetRangeLabels(const std::pair& labels); + + private: + std::optional m_Response; + std::pair m_Range; + std::pair m_Labels; + }; + + MITKFORMS_EXPORT void from_json(const nlohmann::ordered_json& j, LinearScaleQuestion& q); + MITKFORMS_EXPORT void to_json(nlohmann::ordered_json& j, const LinearScaleQuestion& q); +} + +#endif diff --git a/Modules/Forms/include/mitkMultipleChoiceQuestion.h b/Modules/Forms/include/mitkMultipleChoiceQuestion.h new file mode 100644 index 0000000000..22af1cc293 --- /dev/null +++ b/Modules/Forms/include/mitkMultipleChoiceQuestion.h @@ -0,0 +1,41 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +All rights reserved. + +Use of this source code is governed by a 3-clause BSD license that can be +found in the LICENSE file. + +============================================================================*/ + +#ifndef mitkMultipleChoiceQuestion_h +#define mitkMultipleChoiceQuestion_h + +#include + +namespace mitk::Forms +{ + class MITKFORMS_EXPORT MultipleChoiceQuestion : public QuestionWithOptions + { + public: + ~MultipleChoiceQuestion() override; + + std::string GetType() const override; + Question* CreateAnother() const override; + + void FromJSON(const nlohmann::ordered_json& j) override; + void ToJSON(nlohmann::ordered_json& j) const override; + + using QuestionWithOptions::SetResponse; + using QuestionWithOptions::EnableOtherOption; + using QuestionWithOptions::HasOtherOption; + using QuestionWithOptions::SetOtherResponse; + }; + + MITKFORMS_EXPORT void from_json(const nlohmann::ordered_json& j, MultipleChoiceQuestion& q); + MITKFORMS_EXPORT void to_json(nlohmann::ordered_json& j, const MultipleChoiceQuestion& q); +} + +#endif diff --git a/Modules/Forms/include/mitkParagraphQuestion.h b/Modules/Forms/include/mitkParagraphQuestion.h new file mode 100644 index 0000000000..700b6a1e6e --- /dev/null +++ b/Modules/Forms/include/mitkParagraphQuestion.h @@ -0,0 +1,36 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +All rights reserved. + +Use of this source code is governed by a 3-clause BSD license that can be +found in the LICENSE file. + +============================================================================*/ + +#ifndef mitkParagraphQuestion_h +#define mitkParagraphQuestion_h + +#include + +namespace mitk::Forms +{ + class MITKFORMS_EXPORT ParagraphQuestion : public TextQuestion + { + public: + ~ParagraphQuestion() override; + + std::string GetType() const override; + Question* CreateAnother() const override; + + void FromJSON(const nlohmann::ordered_json& j) override; + void ToJSON(nlohmann::ordered_json& j) const override; + }; + + MITKFORMS_EXPORT void from_json(const nlohmann::ordered_json& j, ParagraphQuestion& q); + MITKFORMS_EXPORT void to_json(nlohmann::ordered_json& j, const ParagraphQuestion& q); +} + +#endif diff --git a/Modules/Forms/include/mitkQuestion.h b/Modules/Forms/include/mitkQuestion.h new file mode 100644 index 0000000000..83a18f9d30 --- /dev/null +++ b/Modules/Forms/include/mitkQuestion.h @@ -0,0 +1,58 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +All rights reserved. + +Use of this source code is governed by a 3-clause BSD license that can be +found in the LICENSE file. + +============================================================================*/ + +#ifndef mitkQuestion_h +#define mitkQuestion_h + +#include + +#include +#include + +#include + +namespace mitk::Forms +{ + class MITKFORMS_EXPORT Question + { + public: + Question(); + virtual ~Question(); + + std::string GetQuestionText() const; + void SetQuestionText(const std::string& question); + + virtual std::string GetType() const = 0; + virtual Question* CreateAnother() const = 0; + + virtual void FromJSON(const nlohmann::ordered_json& j) = 0; + virtual void ToJSON(nlohmann::ordered_json& j) const = 0; + + virtual std::vector GetResponsesAsStrings() const = 0; + virtual void ClearResponses() = 0; + + virtual std::string GetRequiredText() const; + virtual bool IsComplete() const = 0; + + void SetRequired(bool required = true); + bool IsRequired() const; + + private: + std::string m_QuestionText; + bool m_IsRequired; + }; + + MITKFORMS_EXPORT void from_json(const nlohmann::ordered_json& j, Question& q); + MITKFORMS_EXPORT void to_json(nlohmann::ordered_json& j, const Question& q); +} + +#endif diff --git a/Modules/Forms/include/mitkQuestionWithOptions.h b/Modules/Forms/include/mitkQuestionWithOptions.h new file mode 100644 index 0000000000..c0afb963c4 --- /dev/null +++ b/Modules/Forms/include/mitkQuestionWithOptions.h @@ -0,0 +1,60 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +All rights reserved. + +Use of this source code is governed by a 3-clause BSD license that can be +found in the LICENSE file. + +============================================================================*/ + +#ifndef mitkQuestionWithOptions_h +#define mitkQuestionWithOptions_h + +#include +#include + +namespace mitk::Forms +{ + class MITKFORMS_EXPORT QuestionWithOptions : public Question + { + public: + ~QuestionWithOptions() override; + + std::vector GetResponsesAsStrings() const override; + void ClearResponses() override; + + bool IsComplete() const override; + + void AddOption(const std::string& option); + std::vector GetOptions() const; + + bool HasOtherOption() const; + + protected: + void AddResponse(size_t i); + void RemoveResponse(size_t i); + + void SetResponse(size_t i); + + void EnableOtherOption(); + + void AddOtherResponse(const std::string& response); + void RemoveOtherResponse(); + + void SetOtherResponse(const std::string& response); + + private: + std::vector m_Options; + std::vector m_Responses; + bool m_EnableOtherOption = false; + std::optional m_OtherResponse; + }; + + MITKFORMS_EXPORT void from_json(const nlohmann::ordered_json& j, QuestionWithOptions& q); + MITKFORMS_EXPORT void to_json(nlohmann::ordered_json& j, const QuestionWithOptions& q); +} + +#endif diff --git a/Modules/Forms/include/mitkShortAnswerQuestion.h b/Modules/Forms/include/mitkShortAnswerQuestion.h new file mode 100644 index 0000000000..cd3c79a9c5 --- /dev/null +++ b/Modules/Forms/include/mitkShortAnswerQuestion.h @@ -0,0 +1,36 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +All rights reserved. + +Use of this source code is governed by a 3-clause BSD license that can be +found in the LICENSE file. + +============================================================================*/ + +#ifndef mitkShortAnswerQuestion_h +#define mitkShortAnswerQuestion_h + +#include + +namespace mitk::Forms +{ + class MITKFORMS_EXPORT ShortAnswerQuestion : public TextQuestion + { + public: + ~ShortAnswerQuestion() override; + + std::string GetType() const override; + Question* CreateAnother() const override; + + void FromJSON(const nlohmann::ordered_json& j) override; + void ToJSON(nlohmann::ordered_json& j) const override; + }; + + MITKFORMS_EXPORT void from_json(const nlohmann::ordered_json& j, ShortAnswerQuestion& q); + MITKFORMS_EXPORT void to_json(nlohmann::ordered_json& j, const ShortAnswerQuestion& q); +} + +#endif diff --git a/Modules/Forms/include/mitkTextQuestion.h b/Modules/Forms/include/mitkTextQuestion.h new file mode 100644 index 0000000000..ac88c1254d --- /dev/null +++ b/Modules/Forms/include/mitkTextQuestion.h @@ -0,0 +1,38 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +All rights reserved. + +Use of this source code is governed by a 3-clause BSD license that can be +found in the LICENSE file. + +============================================================================*/ + +#ifndef mitkTextQuestion_h +#define mitkTextQuestion_h + +#include + +namespace mitk::Forms +{ + class MITKFORMS_EXPORT TextQuestion : public Question + { + public: + ~TextQuestion() override; + + std::vector GetResponsesAsStrings() const override; + void ClearResponses() override; + + bool IsComplete() const override; + + std::string GetResponse() const; + void SetResponse(const std::string& response); + + private: + std::string m_Response; + }; +} + +#endif diff --git a/Modules/Forms/src/mitkCheckboxesQuestion.cpp b/Modules/Forms/src/mitkCheckboxesQuestion.cpp new file mode 100644 index 0000000000..dde85eb049 --- /dev/null +++ b/Modules/Forms/src/mitkCheckboxesQuestion.cpp @@ -0,0 +1,55 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +All rights reserved. + +Use of this source code is governed by a 3-clause BSD license that can be +found in the LICENSE file. + +============================================================================*/ + +#include +#include + +using namespace mitk::Forms; +using namespace nlohmann; + +CheckboxesQuestion::~CheckboxesQuestion() = default; + +std::string CheckboxesQuestion::GetType() const +{ + return "Checkboxes"; +} + +Question* CheckboxesQuestion::CreateAnother() const +{ + return new CheckboxesQuestion; +} + +void CheckboxesQuestion::FromJSON(const nlohmann::ordered_json& j) +{ + from_json(j, *this); +} + +void CheckboxesQuestion::ToJSON(nlohmann::ordered_json& j) const +{ + to_json(j, *this); +} + +void mitk::Forms::from_json(const ordered_json& j, CheckboxesQuestion& q) +{ + from_json(j, static_cast(q)); + + if (j.contains("Other") && j["Other"] == true) + q.EnableOtherOption(); +} + +void mitk::Forms::to_json(ordered_json& j, const CheckboxesQuestion& q) +{ + to_json(j, static_cast(q)); + + if (q.HasOtherOption()) + j["Other"] = true; +} diff --git a/Modules/Forms/src/mitkDropdownQuestion.cpp b/Modules/Forms/src/mitkDropdownQuestion.cpp new file mode 100644 index 0000000000..4733262450 --- /dev/null +++ b/Modules/Forms/src/mitkDropdownQuestion.cpp @@ -0,0 +1,48 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +All rights reserved. + +Use of this source code is governed by a 3-clause BSD license that can be +found in the LICENSE file. + +============================================================================*/ + +#include + +using namespace mitk::Forms; +using namespace nlohmann; + +DropdownQuestion::~DropdownQuestion() = default; + +std::string DropdownQuestion::GetType() const +{ + return "Drop-down"; +} + +Question* DropdownQuestion::CreateAnother() const +{ + return new DropdownQuestion; +} + +void DropdownQuestion::FromJSON(const nlohmann::ordered_json& j) +{ + from_json(j, *this); +} + +void DropdownQuestion::ToJSON(nlohmann::ordered_json& j) const +{ + to_json(j, *this); +} + +void mitk::Forms::from_json(const ordered_json& j, DropdownQuestion& q) +{ + from_json(j, static_cast(q)); +} + +void mitk::Forms::to_json(ordered_json& j, const DropdownQuestion& q) +{ + to_json(j, static_cast(q)); +} diff --git a/Modules/Forms/src/mitkForm.cpp b/Modules/Forms/src/mitkForm.cpp new file mode 100644 index 0000000000..bb0b0daf73 --- /dev/null +++ b/Modules/Forms/src/mitkForm.cpp @@ -0,0 +1,286 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +All rights reserved. + +Use of this source code is governed by a 3-clause BSD license that can be +found in the LICENSE file. + +============================================================================*/ + +#include + +#include +#include +#include + +#include + +#include +#include + +using namespace mitk::Forms; +using namespace nlohmann; + +namespace +{ + std::string GetCurrentISO8601DateTime() + { + std::timespec ts; + std::timespec_get(&ts, TIME_UTC); + std::array buffer{}; // YYYY-MM-DDThh:mm:ssZ + + std::strftime(buffer.data(), buffer.size(), "%FT%TZ", std::gmtime(&ts.tv_sec)); + + return buffer.data(); + } +} + +Form::Section::Section(const std::string& title, const std::string& description) + : m_Title(title), + m_Description(description) +{ +} + +Form::Section::~Section() = default; + +Form::Section::Section(Section&& other) noexcept = default; + +Form::Section& Form::Section::operator=(Section&& other) noexcept = default; + +std::string Form::Section::GetTitle() const +{ + return m_Title; +} + +void Form::Section::SetTitle(const std::string& title) +{ + m_Title = title; +} + +std::string Form::Section::GetDescription() const +{ + return m_Description; +} + +void Form::Section::SetDescription(const std::string& description) +{ + m_Description = description; +} + +std::vector Form::Section::GetQuestions() const +{ + std::vector questions; + questions.reserve(m_Questions.size()); + + for (const auto& question : m_Questions) + questions.push_back(question.get()); + + return questions; +} + +void Form::Section::AddQuestion(Question* question) +{ + m_Questions.emplace_back(question); +} + +Form::Form(const std::string& title, const std::string& description) +{ + m_Sections.emplace_back(title, description); +} + +Form::~Form() = default; + +Form::Form(Form&& other) noexcept = default; + +Form& Form::operator=(Form&& other) noexcept = default; + +Form::Section& Form::AddSection(const std::string& title, const std::string& description) +{ + return m_Sections.emplace_back(title, description); +} + +int Form::GetNumberOfSections() const +{ + return static_cast(m_Sections.size()); +} + +Form::Section& Form::GetSection(int index) +{ + return m_Sections.at(index); +} + +const Form::Section& Form::GetSection(int index) const +{ + return m_Sections.at(index); +} + +std::string Form::GetTitle() const +{ + return m_Sections[0].GetTitle(); +} + +void Form::SetTitle(const std::string& title) +{ + m_Sections[0].SetTitle(title); +} + +std::string Form::GetDescription() const +{ + return m_Sections[0].GetDescription(); +} + +void Form::SetDescription(const std::string& description) +{ + m_Sections[0].SetDescription(description); +} + +std::vector Form::GetQuestions() const +{ + return m_Sections[0].GetQuestions(); +} + +void Form::AddQuestion(Question* question) +{ + m_Sections[0].AddQuestion(question); +} + +void Form::Submit(const fs::path& csvPath) const +{ + std::ofstream csvFile; + + if (fs::exists(csvPath)) + { + csvFile.open(csvPath, std::ofstream::app); + + if (!csvFile.is_open()) + mitkThrow() << "Could not open file \"" << csvPath << "\"!"; + } + else + { + csvFile.open(csvPath); + + if (!csvFile.is_open()) + mitkThrow() << "Could not create file \"" << csvPath << "\"!"; + + csvFile << "\"Timestamp\""; + + for (const auto& section : m_Sections) + { + for (const auto* question : section.GetQuestions()) + { + csvFile << ",\"" << question->GetQuestionText() << '"'; + } + } + + csvFile << '\n'; + } + + csvFile << '"' << GetCurrentISO8601DateTime() << '"'; + + for (const auto& section : m_Sections) + { + for (const auto* question : section.GetQuestions()) + { + csvFile << ",\""; + bool isFirstResponse = true; + + for (const auto& response : question->GetResponsesAsStrings()) + { + if (!isFirstResponse) + csvFile << ';'; + + csvFile << response; + + isFirstResponse = false; + } + + csvFile << '"'; + } + } + + csvFile << std::endl; +} + +void mitk::Forms::from_json(const nlohmann::ordered_json& j, Form& f) +{ + if (!j.contains("FileFormat") || j["FileFormat"] != "MITK Form") + mitkThrow() << "Expected \"FileFormat\" field to be \"MITK Form\"!"; + + if (!j.contains("Version") || j["Version"] != 1) + mitkThrow() << "Expected \"Version\" field to be 1!";; + + const auto* questionFactory = IQuestionFactory::GetInstance(); + + if (j.contains("Sections")) + { + bool isFirstSection = true; + + for (const auto& jSection : j["Sections"]) + { + std::string title; + std::string description; + + if (jSection.contains("Title")) + title = jSection["Title"]; + + if (jSection.contains("Description")) + description = jSection["Description"]; + + auto& section = isFirstSection + ? f.GetSection(0) + : f.AddSection(title, description); + + if (isFirstSection) + { + section.SetTitle(title); + section.SetDescription(description); + + isFirstSection = false; + } + + if (jSection.contains("Questions")) + { + for (const auto& jQuestion : jSection["Questions"]) + { + auto question = questionFactory->Create(jQuestion["Type"]); + question->FromJSON(jQuestion); + + section.AddQuestion(question); + } + } + } + } +} + +void mitk::Forms::to_json(nlohmann::ordered_json& j, const Form& f) +{ + j["FileFormat"] = "MITK Form"; + j["Version"] = 1; + + const int numberOfSections = f.GetNumberOfSections(); + + for (int index = 0; index < numberOfSections; ++index) + { + const auto& section = f.GetSection(index); + ordered_json jSection; + + if (auto title = section.GetTitle(); !title.empty()) + jSection["Title"] = title; + + if (auto description = section.GetDescription(); !description.empty()) + jSection["Description"] = description; + + for (const auto* question : section.GetQuestions()) + { + ordered_json jQuestion; + question->ToJSON(jQuestion); + + jSection["Questions"].push_back(jQuestion); + } + + j["Sections"].push_back(jSection); + } +} diff --git a/Modules/Forms/src/mitkIQuestionFactory.cpp b/Modules/Forms/src/mitkIQuestionFactory.cpp new file mode 100644 index 0000000000..e3a6ab5a26 --- /dev/null +++ b/Modules/Forms/src/mitkIQuestionFactory.cpp @@ -0,0 +1,34 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +All rights reserved. + +Use of this source code is governed by a 3-clause BSD license that can be +found in the LICENSE file. + +============================================================================*/ + +#include + +#include +#include +#include + +using namespace mitk::Forms; + +IQuestionFactory* IQuestionFactory::GetInstance() +{ + static auto context = us::GetModuleContext(); + static us::ServiceReference serviceReference; + + if (!serviceReference) + serviceReference = context->GetServiceReference(); + + return serviceReference + ? context->GetService(serviceReference) + : nullptr; +} + +IQuestionFactory::~IQuestionFactory() = default; diff --git a/Modules/Forms/src/mitkLinearScaleQuestion.cpp b/Modules/Forms/src/mitkLinearScaleQuestion.cpp new file mode 100644 index 0000000000..b2c55bc310 --- /dev/null +++ b/Modules/Forms/src/mitkLinearScaleQuestion.cpp @@ -0,0 +1,121 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +All rights reserved. + +Use of this source code is governed by a 3-clause BSD license that can be +found in the LICENSE file. + +============================================================================*/ + +#include +#include + +#include + +using namespace mitk::Forms; +using namespace nlohmann; + +LinearScaleQuestion::LinearScaleQuestion() + : m_Range({1, 5}) +{ +} + +LinearScaleQuestion::~LinearScaleQuestion() = default; + +std::string LinearScaleQuestion::GetType() const +{ + return "Linear scale"; +} + +Question* LinearScaleQuestion::CreateAnother() const +{ + return new LinearScaleQuestion; +} + +void LinearScaleQuestion::FromJSON(const nlohmann::ordered_json& j) +{ + from_json(j, *this); +} + +void LinearScaleQuestion::ToJSON(nlohmann::ordered_json& j) const +{ + to_json(j, *this); +} + +std::vector LinearScaleQuestion::GetResponsesAsStrings() const +{ + std::vector responses; + + if (m_Response.has_value()) + responses.push_back(std::to_string(m_Response.value())); + + return responses; +} + +void LinearScaleQuestion::ClearResponses() +{ + m_Response.reset(); +} + +bool LinearScaleQuestion::IsComplete() const +{ + return m_Response.has_value(); +} + +std::optional LinearScaleQuestion::GetResponse() const +{ + return m_Response; +} + +void LinearScaleQuestion::SetResponse(int response) +{ + m_Response = response; +} + +std::pair LinearScaleQuestion::GetRange() const +{ + return m_Range; +} + +void LinearScaleQuestion::SetRange(const std::pair& range) +{ + if (range.first < 0 || range.first > 1) + mitkThrow() << "Invalid lower bound " << range.first << " in range: must be 0 or 1!"; + + if (range.second < 2 || range.second > 10) + mitkThrow() << "Invalid upper bound " << range.second << " in range: must lie between 2 and 10!"; + + m_Range = range; +} + +std::pair LinearScaleQuestion::GetRangeLabels() const +{ + return m_Labels; +} + +void LinearScaleQuestion::SetRangeLabels(const std::pair& labels) +{ + m_Labels = labels; +} + +void mitk::Forms::from_json(const ordered_json& j, LinearScaleQuestion& q) +{ + from_json(j, static_cast(q)); + + if (j.contains("Range")) + q.SetRange(j["Range"]); + + if (j.contains("Labels")) + q.SetRangeLabels(j["Labels"]); +} + +void mitk::Forms::to_json(ordered_json& j, const LinearScaleQuestion& q) +{ + to_json(j, static_cast(q)); + + j["Range"] = q.GetRange(); + j["Labels"] = q.GetRangeLabels(); +} diff --git a/Modules/Forms/src/mitkModuleActivator.cpp b/Modules/Forms/src/mitkModuleActivator.cpp new file mode 100644 index 0000000000..362454bb27 --- /dev/null +++ b/Modules/Forms/src/mitkModuleActivator.cpp @@ -0,0 +1,50 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +All rights reserved. + +Use of this source code is governed by a 3-clause BSD license that can be +found in the LICENSE file. + +============================================================================*/ + +#include "mitkModuleActivator.h" +#include "mitkQuestionFactory.h" + +#include +#include +#include +#include +#include +#include + +#include + +using namespace mitk::Forms; + +ModuleActivator::ModuleActivator() + : m_QuestionFactory(std::make_unique()) +{ +} + +ModuleActivator::~ModuleActivator() = default; + +void ModuleActivator::Load(us::ModuleContext* context) +{ + context->RegisterService(m_QuestionFactory.get()); + + this->RegisterQuestion(); + this->RegisterQuestion(); + this->RegisterQuestion(); + this->RegisterQuestion(); + this->RegisterQuestion(); + this->RegisterQuestion(); +} + +void ModuleActivator::Unload(us::ModuleContext*) +{ +} + +US_EXPORT_MODULE_ACTIVATOR(ModuleActivator) diff --git a/Modules/Forms/src/mitkModuleActivator.h b/Modules/Forms/src/mitkModuleActivator.h new file mode 100644 index 0000000000..44f22607e2 --- /dev/null +++ b/Modules/Forms/src/mitkModuleActivator.h @@ -0,0 +1,42 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +All rights reserved. + +Use of this source code is governed by a 3-clause BSD license that can be +found in the LICENSE file. + +============================================================================*/ + +#ifndef mitkModuleActivator_h +#define mitkModuleActivator_h + +#include +#include +#include + +namespace mitk::Forms +{ + class ModuleActivator : public us::ModuleActivator + { + public: + ModuleActivator(); + ~ModuleActivator() override; + + void Load(us::ModuleContext* context) override; + void Unload(us::ModuleContext* context) override; + + private: + template + void RegisterQuestion() + { + m_QuestionFactory->Register(new TQuestion); + } + + std::unique_ptr m_QuestionFactory; + }; +} + +#endif diff --git a/Modules/Forms/src/mitkMultipleChoiceQuestion.cpp b/Modules/Forms/src/mitkMultipleChoiceQuestion.cpp new file mode 100644 index 0000000000..7655ccac71 --- /dev/null +++ b/Modules/Forms/src/mitkMultipleChoiceQuestion.cpp @@ -0,0 +1,55 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +All rights reserved. + +Use of this source code is governed by a 3-clause BSD license that can be +found in the LICENSE file. + +============================================================================*/ + +#include +#include + +using namespace mitk::Forms; +using namespace nlohmann; + +MultipleChoiceQuestion::~MultipleChoiceQuestion() = default; + +std::string MultipleChoiceQuestion::GetType() const +{ + return "Multiple choice"; +} + +Question* MultipleChoiceQuestion::CreateAnother() const +{ + return new MultipleChoiceQuestion; +} + +void MultipleChoiceQuestion::FromJSON(const nlohmann::ordered_json& j) +{ + from_json(j, *this); +} + +void MultipleChoiceQuestion::ToJSON(nlohmann::ordered_json& j) const +{ + to_json(j, *this); +} + +void mitk::Forms::from_json(const ordered_json& j, MultipleChoiceQuestion& q) +{ + from_json(j, static_cast(q)); + + if (j.contains("Other") && j["Other"] == true) + q.EnableOtherOption(); +} + +void mitk::Forms::to_json(ordered_json& j, const MultipleChoiceQuestion& q) +{ + to_json(j, static_cast(q)); + + if (q.HasOtherOption()) + j["Other"] = true; +} diff --git a/Modules/Forms/src/mitkParagraphQuestion.cpp b/Modules/Forms/src/mitkParagraphQuestion.cpp new file mode 100644 index 0000000000..6348322d28 --- /dev/null +++ b/Modules/Forms/src/mitkParagraphQuestion.cpp @@ -0,0 +1,48 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +All rights reserved. + +Use of this source code is governed by a 3-clause BSD license that can be +found in the LICENSE file. + +============================================================================*/ + +#include + +using namespace mitk::Forms; +using namespace nlohmann; + +ParagraphQuestion::~ParagraphQuestion() = default; + +std::string ParagraphQuestion::GetType() const +{ + return "Paragraph"; +} + +Question* ParagraphQuestion::CreateAnother() const +{ + return new ParagraphQuestion; +} + +void ParagraphQuestion::FromJSON(const nlohmann::ordered_json& j) +{ + from_json(j, *this); +} + +void ParagraphQuestion::ToJSON(nlohmann::ordered_json& j) const +{ + to_json(j, *this); +} + +void mitk::Forms::from_json(const ordered_json& j, ParagraphQuestion& q) +{ + from_json(j, static_cast(q)); +} + +void mitk::Forms::to_json(ordered_json& j, const ParagraphQuestion& q) +{ + to_json(j, static_cast(q)); +} diff --git a/Modules/Forms/src/mitkQuestion.cpp b/Modules/Forms/src/mitkQuestion.cpp new file mode 100644 index 0000000000..ffc9fba686 --- /dev/null +++ b/Modules/Forms/src/mitkQuestion.cpp @@ -0,0 +1,68 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +All rights reserved. + +Use of this source code is governed by a 3-clause BSD license that can be +found in the LICENSE file. + +============================================================================*/ + +#include +#include + +using namespace mitk::Forms; +using namespace nlohmann; + +Question::Question() + : m_IsRequired(false) +{ +} + +Question::~Question() = default; + +std::string Question::GetQuestionText() const +{ + return m_QuestionText; +} + +void Question::SetQuestionText(const std::string& question) +{ + m_QuestionText = question; +} + +void Question::SetRequired(bool required) +{ + m_IsRequired = required; +} + +bool Question::IsRequired() const +{ + return m_IsRequired; +} + +std::string Question::GetRequiredText() const +{ + return "This is a required question"; +} + +void mitk::Forms::from_json(const ordered_json& j, Question& q) +{ + if (j.contains("Text")) + q.SetQuestionText(j["Text"]); + + if (j.contains("Required")) + q.SetRequired(j["Required"]); +} + +void mitk::Forms::to_json(ordered_json& j, const Question& q) +{ + j["Text"] = q.GetQuestionText(); + + if (q.IsRequired()) + j["Required"] = true; + + j["Type"] = q.GetType(); +} diff --git a/Modules/Forms/src/mitkQuestionFactory.cpp b/Modules/Forms/src/mitkQuestionFactory.cpp new file mode 100644 index 0000000000..26e77969c7 --- /dev/null +++ b/Modules/Forms/src/mitkQuestionFactory.cpp @@ -0,0 +1,47 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +All rights reserved. + +Use of this source code is governed by a 3-clause BSD license that can be +found in the LICENSE file. + +============================================================================*/ + +#include "mitkQuestionFactory.h" + +#include +#include + +using namespace mitk::Forms; + +QuestionFactory::QuestionFactory() = default; + +QuestionFactory::~QuestionFactory() +{ +} + +void QuestionFactory::Register(Question* question) +{ + auto type = question->GetType(); + + if (m_Prototypes.find(type) != m_Prototypes.end()) + mitkThrow() << "A prototype is already registered for questions of type \"" << type << "\"!"; + + m_Prototypes[type].reset(question); +} + +Question* QuestionFactory::Create(const std::string& type) const +{ + try + { + return m_Prototypes.at(type)->CreateAnother(); + } + catch (const std::out_of_range&) + { + } + + return nullptr; +} diff --git a/Modules/Forms/src/mitkQuestionFactory.h b/Modules/Forms/src/mitkQuestionFactory.h new file mode 100644 index 0000000000..30bbd418db --- /dev/null +++ b/Modules/Forms/src/mitkQuestionFactory.h @@ -0,0 +1,38 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +All rights reserved. + +Use of this source code is governed by a 3-clause BSD license that can be +found in the LICENSE file. + +============================================================================*/ + +#ifndef mitkQuestionFactory_h +#define mitkQuestionFactory_h + +#include + +#include +#include +#include + +namespace mitk::Forms +{ + class QuestionFactory : public IQuestionFactory + { + public: + QuestionFactory(); + ~QuestionFactory() override; + + void Register(Question* question) override; + Question* Create(const std::string& type) const override; + + private: + std::unordered_map> m_Prototypes; + }; +} + +#endif diff --git a/Modules/Forms/src/mitkQuestionWithOptions.cpp b/Modules/Forms/src/mitkQuestionWithOptions.cpp new file mode 100644 index 0000000000..f4f499eefb --- /dev/null +++ b/Modules/Forms/src/mitkQuestionWithOptions.cpp @@ -0,0 +1,122 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +All rights reserved. + +Use of this source code is governed by a 3-clause BSD license that can be +found in the LICENSE file. + +============================================================================*/ + +#include +#include + +#include + +using namespace mitk::Forms; +using namespace nlohmann; + +QuestionWithOptions::~QuestionWithOptions() = default; + +void QuestionWithOptions::AddOption(const std::string& option) +{ + m_Options.push_back(option); +} + +std::vector QuestionWithOptions::GetOptions() const +{ + return m_Options; +} + +void QuestionWithOptions::EnableOtherOption() +{ + m_EnableOtherOption = true; +} + +bool QuestionWithOptions::HasOtherOption() const +{ + return m_EnableOtherOption; +} + +void QuestionWithOptions::AddResponse(size_t i) +{ + if (std::find(m_Responses.begin(), m_Responses.end(), i) == m_Responses.end()) + m_Responses.insert(std::lower_bound(m_Responses.begin(), m_Responses.end(), i), i); +} + +void QuestionWithOptions::RemoveResponse(size_t i) +{ + m_Responses.erase(std::remove(m_Responses.begin(), m_Responses.end(), i)); +} + +void QuestionWithOptions::SetResponse(size_t i) +{ + m_OtherResponse.reset(); + m_Responses = { i }; +} + +void QuestionWithOptions::AddOtherResponse(const std::string& response) +{ + if (!m_EnableOtherOption) + mitkThrow() << "Adding \"Other\" response is disallowed!"; + + m_OtherResponse = response; +} + +void QuestionWithOptions::RemoveOtherResponse() +{ + m_OtherResponse.reset(); +} + +void QuestionWithOptions::SetOtherResponse(const std::string& response) +{ + if (!m_EnableOtherOption) + mitkThrow() << "Setting \"Other\" response is disallowed!"; + + m_Responses.clear(); + m_OtherResponse = response; +} + +std::vector QuestionWithOptions::GetResponsesAsStrings() const +{ + std::vector responses; + + for (const auto i : m_Responses) + responses.push_back(m_Options[i]); + + if (m_OtherResponse.has_value()) + responses.push_back(m_OtherResponse.value()); + + return responses; +} + +void QuestionWithOptions::ClearResponses() +{ + m_Responses.clear(); + m_OtherResponse.reset(); +} + +bool QuestionWithOptions::IsComplete() const +{ + return !m_Responses.empty() || m_OtherResponse.has_value(); +} + +void mitk::Forms::from_json(const ordered_json& j, QuestionWithOptions& q) +{ + from_json(j, static_cast(q)); + + if (j.contains("Options")) + { + for (const auto& option : j["Options"]) + q.AddOption(option); + } +} + +void mitk::Forms::to_json(ordered_json& j, const QuestionWithOptions& q) +{ + to_json(j, static_cast(q)); + + j["Options"] = q.GetOptions(); +} diff --git a/Modules/Forms/src/mitkShortAnswerQuestion.cpp b/Modules/Forms/src/mitkShortAnswerQuestion.cpp new file mode 100644 index 0000000000..9931e99c1e --- /dev/null +++ b/Modules/Forms/src/mitkShortAnswerQuestion.cpp @@ -0,0 +1,48 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +All rights reserved. + +Use of this source code is governed by a 3-clause BSD license that can be +found in the LICENSE file. + +============================================================================*/ + +#include + +using namespace mitk::Forms; +using namespace nlohmann; + +ShortAnswerQuestion::~ShortAnswerQuestion() = default; + +std::string ShortAnswerQuestion::GetType() const +{ + return "Short answer"; +} + +Question* ShortAnswerQuestion::CreateAnother() const +{ + return new ShortAnswerQuestion; +} + +void ShortAnswerQuestion::FromJSON(const nlohmann::ordered_json& j) +{ + from_json(j, *this); +} + +void ShortAnswerQuestion::ToJSON(nlohmann::ordered_json& j) const +{ + to_json(j, *this); +} + +void mitk::Forms::from_json(const ordered_json& j, ShortAnswerQuestion& q) +{ + from_json(j, static_cast(q)); +} + +void mitk::Forms::to_json(ordered_json& j, const ShortAnswerQuestion& q) +{ + to_json(j, static_cast(q)); +} diff --git a/Modules/Forms/src/mitkTextQuestion.cpp b/Modules/Forms/src/mitkTextQuestion.cpp new file mode 100644 index 0000000000..4b538eb262 --- /dev/null +++ b/Modules/Forms/src/mitkTextQuestion.cpp @@ -0,0 +1,42 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +All rights reserved. + +Use of this source code is governed by a 3-clause BSD license that can be +found in the LICENSE file. + +============================================================================*/ + +#include + +using namespace mitk::Forms; + +TextQuestion::~TextQuestion() = default; + +std::vector TextQuestion::GetResponsesAsStrings() const +{ + return { m_Response }; +} + +void TextQuestion::ClearResponses() +{ + m_Response.clear(); +} + +bool TextQuestion::IsComplete() const +{ + return !m_Response.empty(); +} + +std::string TextQuestion::GetResponse() const +{ + return m_Response; +} + +void TextQuestion::SetResponse(const std::string& response) +{ + m_Response = response; +} diff --git a/Modules/ModuleList.cmake b/Modules/ModuleList.cmake index 54ad857bcd..427f9145f4 100644 --- a/Modules/ModuleList.cmake +++ b/Modules/ModuleList.cmake @@ -1,59 +1,60 @@ # The entries in the mitk_modules list must be # ordered according to their dependencies. set(MITK_MODULES Log Core + Forms CommandLine CoreCmdApps AppUtil LegacyIO DataTypesExt Annotation LegacyGL AlgorithmsExt MapperExt DICOM DICOMQI DICOMTesting SceneSerializationBase PlanarFigure ImageDenoising ImageExtraction SceneSerialization Gizmo GraphAlgorithms Multilabel Chart ImageStatistics ContourModel SurfaceInterpolation BoundingShape Segmentation QtWidgets QtWidgetsExt ImageStatisticsUI SegmentationUI MatchPointRegistration MatchPointRegistrationUI Classification QtOverlays DICOMUI Remeshing Python QtPython Persistence RT RTUI IOExt XNAT RenderWindowManagerUI CEST BasicImageProcessing ModelFit ModelFitUI Pharmacokinetics PharmacokineticsUI DICOMPM ROI )