diff --git a/app/Model/admin.h b/app/Model/admin.h index c9c67e7..20a6e5d 100644 --- a/app/Model/admin.h +++ b/app/Model/admin.h @@ -16,8 +16,9 @@ class AdminTableInfo: public QObject Q_PROPERTY(bool clearable READ clearable NOTIFY dataChanged) public: - explicit AdminTableInfo(QObject* parent = nullptr); - explicit AdminTableInfo(QJsonObject json, QObject* parent = nullptr); + // Constructors + AdminTableInfo(QObject* parent = nullptr); + AdminTableInfo(QJsonObject json, QObject* parent = nullptr); // Getters inline QString section() { return mSection; } @@ -69,7 +70,7 @@ class Admin : public QObject Q_PROPERTY(QVariantList wtTables READ wtTables NOTIFY tablesChanged) public: - explicit Admin(QObject* parent = nullptr); + Admin(QObject* parent = nullptr); // Getters inline bool serverStatusAutoRefresh() { return mServerStatusAutoRefresh; } diff --git a/app/Model/analysis/analyseslistmodel.cpp b/app/Model/analysis/analyseslistmodel.cpp index bd29af6..5faf9d3 100644 --- a/app/Model/analysis/analyseslistmodel.cpp +++ b/app/Model/analysis/analyseslistmodel.cpp @@ -119,7 +119,7 @@ QVariant AnalysesListModel::data(const QModelIndex& index, int role) const else if (role == Status) return analysis->status(); else if (role == UpdateDate) - return analysis->updateDate().toString("yyyy-MM-dd"); + return analysis->updateDate().toString("yyyy-MM-dd HH:mm"); else if (role == SearchField) return analysis->searchField(); return QVariant(); diff --git a/app/Model/analysis/analysesmanager.cpp b/app/Model/analysis/analysesmanager.cpp index 29ddeb9..e762c01 100644 --- a/app/Model/analysis/analysesmanager.cpp +++ b/app/Model/analysis/analysesmanager.cpp @@ -245,7 +245,7 @@ FilteringAnalysis* AnalysesManager::getOrCreateFilteringAnalysis(int id) return mFilteringAnalyses[id]; } // else - FilteringAnalysis* newAnalysis = new FilteringAnalysis(id); + FilteringAnalysis* newAnalysis = new FilteringAnalysis(id, this); mFilteringAnalyses.insert(id, newAnalysis); return newAnalysis; } @@ -257,7 +257,7 @@ PipelineAnalysis* AnalysesManager::getOrCreatePipelineAnalysis(int id) return mPipelineAnalyses[id]; } // else - PipelineAnalysis* newAnalysis = new PipelineAnalysis(id); + PipelineAnalysis* newAnalysis = new PipelineAnalysis(id, this); mPipelineAnalyses.insert(id, newAnalysis); return newAnalysis; } diff --git a/app/Model/analysis/analysesmanager.h b/app/Model/analysis/analysesmanager.h index 88b1c7a..cfc74de 100644 --- a/app/Model/analysis/analysesmanager.h +++ b/app/Model/analysis/analysesmanager.h @@ -18,7 +18,7 @@ class AnalysesManager : public QObject Q_PROPERTY(QString pipelineType READ pipelineType) public: // Constructors - explicit AnalysesManager(QObject* parent=nullptr); + AnalysesManager(QObject* parent=nullptr); // Getters inline PipelineAnalysis* newPipeline() const { return mNewPipeline; } diff --git a/app/Model/analysis/analysis.cpp b/app/Model/analysis/analysis.cpp index 3cb4bf3..2ce1844 100644 --- a/app/Model/analysis/analysis.cpp +++ b/app/Model/analysis/analysis.cpp @@ -63,9 +63,10 @@ QHash Analysis::initStatusAnimatedMap() } -Analysis::Analysis(QObject* parent) : QObject(parent) +Analysis::Analysis(QObject* parent) : RegovarResource(parent) { mMenuModel = new RootMenu(this); + connect(this, &Analysis::dataChanged, this, &Analysis::updateSearchField); } diff --git a/app/Model/analysis/analysis.h b/app/Model/analysis/analysis.h index d1924a3..2cfb060 100644 --- a/app/Model/analysis/analysis.h +++ b/app/Model/analysis/analysis.h @@ -2,19 +2,16 @@ #define ANALYSIS_H #include +#include "Model/framework/regovarresource.h" #include "Model/mainmenu/rootmenu.h" -class Analysis : public QObject +class Analysis : public RegovarResource { Q_OBJECT Q_PROPERTY(RootMenu* menuModel READ menuModel NOTIFY menuModelChanged) - // Regovar resource attributes - Q_PROPERTY(bool loaded READ loaded NOTIFY dataChanged) - Q_PROPERTY(QDateTime updateDate READ updateDate NOTIFY dataChanged) - Q_PROPERTY(QDateTime createDate READ createDate NOTIFY dataChanged) // Analysis attributes Q_PROPERTY(int id READ id WRITE setId NOTIFY dataChanged) Q_PROPERTY(QString name READ name WRITE setName NOTIFY dataChanged) @@ -22,7 +19,7 @@ class Analysis : public QObject Q_PROPERTY(QString type READ type NOTIFY dataChanged) Q_PROPERTY(Project* project READ project WRITE setProject NOTIFY dataChanged) Q_PROPERTY(QString status READ status NOTIFY statusChanged) - Q_PROPERTY(QString searchField READ searchField NOTIFY dataChanged) + // TODO: Indicators public: // enum value returned by the server as analysis type @@ -30,21 +27,17 @@ class Analysis : public QObject static QString PIPELINE; // Constructors - explicit Analysis(QObject *parent = nullptr); + Analysis(QObject *parent = nullptr); // Getters inline RootMenu* menuModel() const { return mMenuModel; } - inline bool loaded() const { return mLoaded; } - inline QDateTime updateDate() const { return mUpdateDate; } - inline QDateTime createDate() const { return mCreateDate; } - - inline int id() { return mId; } - inline QString name() { return mName; } - inline QString comment() { return mComment; } - inline QString type() { return mType; } + + inline int id() const { return mId; } + inline QString name() const { return mName; } + inline QString comment() const { return mComment; } + inline QString type() const { return mType; } inline Project* project() const { return mProject; } inline QString status() const { return mStatus; } - inline QString searchField() const { return mSearchField; } // Setters inline void setId(int id) { mId = id; emit dataChanged(); } @@ -53,18 +46,7 @@ class Analysis : public QObject inline void setProject(Project* project) { mProject = project; emit dataChanged(); } inline void setStatus(QString status) { mStatus = status; emit statusChanged(); } - // Methods - //! Set model with provided json data - Q_INVOKABLE virtual bool loadJson(QJsonObject json, bool full_init=true) = 0; - //! Export model data into json object - Q_INVOKABLE virtual QJsonObject toJson() = 0; - //! Save subject information onto server - Q_INVOKABLE virtual void save() = 0; - //! Load Subject information from server - Q_INVOKABLE virtual void load(bool forceRefresh=true) = 0; - - - + // Static Methods Q_INVOKABLE static QString statusLabel(QString status); Q_INVOKABLE static QString statusIcon(QString status); Q_INVOKABLE static bool statusIconAnimated(QString status); @@ -73,21 +55,14 @@ class Analysis : public QObject Q_SIGNALS: void menuModelChanged(); - void dataChanged(); void statusChanged(); public Q_SLOTS: - virtual void updateSearchField(); - + virtual void updateSearchField() override; protected: RootMenu* mMenuModel = nullptr; - // Regovar resource - bool mLoaded = false; - QDateTime mUpdateDate; - QDateTime mCreateDate; - QDateTime mLastInternalLoad = QDateTime::currentDateTime(); // Analysis attribute int mId = -1; QString mName; @@ -95,7 +70,6 @@ public Q_SLOTS: QString mType; Project* mProject = nullptr; QString mStatus; // status of the analysis (server side) - QString mSearchField = ""; static QHash sStatusLabelMap; static QHash sStatusIconMap; diff --git a/app/Model/analysis/filtering/advancedfilters/advancedfiltermodel.h b/app/Model/analysis/filtering/advancedfilters/advancedfiltermodel.h index 5a6f71f..eec04d8 100644 --- a/app/Model/analysis/filtering/advancedfilters/advancedfiltermodel.h +++ b/app/Model/analysis/filtering/advancedfilters/advancedfiltermodel.h @@ -44,9 +44,9 @@ class AdvancedFilterModel : public QObject Q_ENUM(ConditionType) // Constructor - explicit AdvancedFilterModel(QObject* parent=nullptr); - explicit AdvancedFilterModel(FilteringAnalysis* parent); - explicit AdvancedFilterModel(QJsonArray filterJson, FilteringAnalysis* parent); + AdvancedFilterModel(QObject* parent=nullptr); + AdvancedFilterModel(FilteringAnalysis* parent); + AdvancedFilterModel(QJsonArray filterJson, FilteringAnalysis* parent); // Getters inline QString qmlId() const { return mQmlId; } @@ -129,9 +129,9 @@ class NewAdvancedFilterModel: public AdvancedFilterModel public: // Constructor - explicit NewAdvancedFilterModel(QObject* parent=nullptr); - explicit NewAdvancedFilterModel(FilteringAnalysis* parent); - explicit NewAdvancedFilterModel(QJsonArray filterJson, FilteringAnalysis* parent); + NewAdvancedFilterModel(QObject* parent=nullptr); + NewAdvancedFilterModel(FilteringAnalysis* parent); + NewAdvancedFilterModel(QJsonArray filterJson, FilteringAnalysis* parent); // Getters inline QString fieldType() const { return (mField != nullptr) ? mField->type() : ""; } diff --git a/app/Model/analysis/filtering/advancedfilters/set.h b/app/Model/analysis/filtering/advancedfilters/set.h index fb1f2a1..4a5b723 100644 --- a/app/Model/analysis/filtering/advancedfilters/set.h +++ b/app/Model/analysis/filtering/advancedfilters/set.h @@ -13,8 +13,9 @@ class Set : public QObject Q_PROPERTY(QString label READ label NOTIFY setChanged) public: - explicit Set(QObject* parent = nullptr); - explicit Set(QString type, QString id, QString label); + // Constructors + Set(QObject* parent = nullptr); + Set(QString type, QString id, QString label); // Getters //inline FilteringAnalysis* analysis() const { return mAnalysis; } diff --git a/app/Model/analysis/filtering/annotation.h b/app/Model/analysis/filtering/annotation.h index bb9e2d9..17b6a23 100644 --- a/app/Model/analysis/filtering/annotation.h +++ b/app/Model/analysis/filtering/annotation.h @@ -25,11 +25,10 @@ class Annotation : public QObject Q_PROPERTY(int order READ order) public: - explicit Annotation(QObject* parent = nullptr); - explicit Annotation(QObject* parent, QString uid, QString dbUid, QString name, QString description, - QString type, QJsonObject meta, QString version, QString dbName, int order=-1); -// Annotation(const Annotation &other); -// ~Annotation(); + // Constructors + Annotation(QObject* parent = nullptr); + Annotation(QObject* parent, QString uid, QString dbUid, QString name, QString description, + QString type, QJsonObject meta, QString version, QString dbName, int order=-1); // Getters inline QString uid() { return mUid; } diff --git a/app/Model/analysis/filtering/annotationstreemodel.h b/app/Model/analysis/filtering/annotationstreemodel.h index bffa76b..c07efa5 100644 --- a/app/Model/analysis/filtering/annotationstreemodel.h +++ b/app/Model/analysis/filtering/annotationstreemodel.h @@ -30,7 +30,7 @@ class AnnotationsTreeModel : public TreeModel }; // Constructor - explicit AnnotationsTreeModel(FilteringAnalysis* analysis=nullptr); + AnnotationsTreeModel(FilteringAnalysis* analysis=nullptr); // Getters inline bool isLoading() const { return mIsLoading; } diff --git a/app/Model/analysis/filtering/documentstreeitem.h b/app/Model/analysis/filtering/documentstreeitem.h index 92a16ba..9016421 100644 --- a/app/Model/analysis/filtering/documentstreeitem.h +++ b/app/Model/analysis/filtering/documentstreeitem.h @@ -9,8 +9,8 @@ class DocumentsTreeItem : public TreeItem public: // Constructors - explicit DocumentsTreeItem(TreeItem* parent=nullptr); - explicit DocumentsTreeItem(QHash columnData, TreeItem* parent=nullptr); + DocumentsTreeItem(TreeItem* parent=nullptr); + DocumentsTreeItem(QHash columnData, TreeItem* parent=nullptr); diff --git a/app/Model/analysis/filtering/documentstreemodel.h b/app/Model/analysis/filtering/documentstreemodel.h index 5e25016..52a3429 100644 --- a/app/Model/analysis/filtering/documentstreemodel.h +++ b/app/Model/analysis/filtering/documentstreemodel.h @@ -23,7 +23,7 @@ class DocumentsTreeModel : public TreeModel }; // Constructor - explicit DocumentsTreeModel(FilteringAnalysis* parent=nullptr); + DocumentsTreeModel(FilteringAnalysis* parent=nullptr); QHash roleNames() const override; diff --git a/app/Model/analysis/filtering/fieldcolumninfos.h b/app/Model/analysis/filtering/fieldcolumninfos.h index 61b50d7..ed22c76 100644 --- a/app/Model/analysis/filtering/fieldcolumninfos.h +++ b/app/Model/analysis/filtering/fieldcolumninfos.h @@ -21,8 +21,9 @@ class FieldColumnInfos : public QObject Q_PROPERTY(float width READ width WRITE setWidth NOTIFY widthChanged) public: - explicit FieldColumnInfos(QObject *parent = nullptr); - explicit FieldColumnInfos(Annotation* annotation, bool isDisplayed, QString sortFilter="", QObject *parent = nullptr); + // Constructors + FieldColumnInfos(QObject *parent = nullptr); + FieldColumnInfos(Annotation* annotation, bool isDisplayed, QString sortFilter="", QObject *parent = nullptr); // Getters inline QString uid() { return !mUIUid.isEmpty() ? mUIUid : mAnnotation != nullptr ? mAnnotation->uid() : ""; } diff --git a/app/Model/analysis/filtering/filteringanalysis.cpp b/app/Model/analysis/filtering/filteringanalysis.cpp index 63a1bad..fc6b465 100644 --- a/app/Model/analysis/filtering/filteringanalysis.cpp +++ b/app/Model/analysis/filtering/filteringanalysis.cpp @@ -18,8 +18,7 @@ FilteringAnalysis::FilteringAnalysis(QObject *parent) : Analysis(parent) mLoadingStatus = Empty; - connect(this, SIGNAL(loadingStatusChanged(LoadingStatus,LoadingStatus)), - this, SLOT(asynchLoadingCoordination(LoadingStatus,LoadingStatus))); + connect(this, &FilteringAnalysis::loadingStatusChanged, this, &FilteringAnalysis::asynchLoadingCoordination); // Init model @@ -584,13 +583,13 @@ void FilteringAnalysis::raiseNewInternalLoadingStatus(LoadingStatus newStatus) void FilteringAnalysis::resetSets() { mSets.clear(); - // add samples first + // Add samples first for (Sample* sample: mSamples) { mSets.append(new Set(QString("sample"), QString::number(sample->id()), sample->nickname())); } - // add sample's attributes + // Add sample's attributes for (QObject* o: mAttributes) { Attribute* attribute = qobject_cast(o); @@ -601,23 +600,33 @@ void FilteringAnalysis::resetSets() } } - // add filters + // Add filters for (QObject* o: mFilters) { SavedFilter* filter = qobject_cast(o); mSets.append(new Set("filter", QString::number(filter->id()), filter->name())); } - // add panels - for (QString& panelId: mPanelsUsed) + // Add all panel's head version by default + for (int idx=0; idx < regovar->panelsManager()->panels()->proxy()->rowCount(); idx++) { - PanelVersion* panel = regovar->panelsManager()->getPanelVersion(panelId); - if (panel != nullptr) - { - mSets.append(new Set("panel", panel->id(), panel->fullname() )); - } + QModelIndex i1 = regovar->panelsManager()->panels()->proxy()->getModelIndex(idx); + // TODO: fix get panel sorted by name + QModelIndex i2 = regovar->panelsManager()->panels()->proxy()->mapToSource(i1); + PanelVersion* version =regovar->panelsManager()->panels()->getAt(i1.row()); + mSets.append(new Set("panel", version->id(), version->fullname())); } + // TODO: add panels used +// for (QString& panelId: mPanelsUsed) +// { +// PanelVersion* panel = regovar->panelsManager()->getPanelVersion(panelId); +// if (panel != nullptr) +// { +// mSets.append(new Set("panel", panel->id(), panel->fullname() )); +// } +// } + emit setsChanged(); } @@ -1013,6 +1022,7 @@ void FilteringAnalysis::processPushNotification(QString action, QJsonObject data } + /// Save on local computer, Tableariant columns settings (order of columns displayed and width) void FilteringAnalysis::saveSettings() { diff --git a/app/Model/analysis/filtering/filteringanalysis.h b/app/Model/analysis/filtering/filteringanalysis.h index 9ade8d9..61c1a9d 100644 --- a/app/Model/analysis/filtering/filteringanalysis.h +++ b/app/Model/analysis/filtering/filteringanalysis.h @@ -77,8 +77,8 @@ class FilteringAnalysis : public Analysis }; // Constructor - explicit FilteringAnalysis(QObject* parent = nullptr); - explicit FilteringAnalysis(int id, QObject* parent = nullptr); + FilteringAnalysis(QObject* parent = nullptr); + FilteringAnalysis(int id, QObject* parent = nullptr); // Getters inline QString refName() const { return mRefName; } @@ -129,10 +129,10 @@ class FilteringAnalysis : public Analysis // Analysis abstracts methods overriden - Q_INVOKABLE bool loadJson(QJsonObject json, bool full_init=true); - Q_INVOKABLE QJsonObject toJson(); - Q_INVOKABLE void save(); - Q_INVOKABLE void load(bool forceRefresh=true); + Q_INVOKABLE bool loadJson(QJsonObject json, bool full_init=true) override; + Q_INVOKABLE QJsonObject toJson() override; + Q_INVOKABLE void save() override; + Q_INVOKABLE void load(bool forceRefresh=true) override; // Methods Q_INVOKABLE inline FieldColumnInfos* getColumnInfo(QString uid) { return mAnnotations.contains(uid) ? mAnnotations[uid] : nullptr; } @@ -168,8 +168,6 @@ class FilteringAnalysis : public Analysis void resetSets(); Q_SIGNALS: - void dataChanged(); - void loadingStatusChanged(LoadingStatus oldSatus, LoadingStatus newStatus); void annotationsChanged(); void displayedAnnotationsChanged(); diff --git a/app/Model/analysis/filtering/filteringresultcell.h b/app/Model/analysis/filtering/filteringresultcell.h index 0e986e5..d7303b9 100644 --- a/app/Model/analysis/filtering/filteringresultcell.h +++ b/app/Model/analysis/filtering/filteringresultcell.h @@ -11,7 +11,7 @@ class FilteringResultCell : public QObject public: // Constructors - explicit FilteringResultCell(QObject *parent = nullptr); + FilteringResultCell(QObject *parent = nullptr); // Getters inline QString uid() { return mUid; } diff --git a/app/Model/analysis/filtering/quickfilters/frequencequickfilter.h b/app/Model/analysis/filtering/quickfilters/frequencequickfilter.h index b745e11..898bdba 100644 --- a/app/Model/analysis/filtering/quickfilters/frequencequickfilter.h +++ b/app/Model/analysis/filtering/quickfilters/frequencequickfilter.h @@ -16,7 +16,8 @@ class FrequenceQuickFilter : public QuickFilterBlockInterface Q_PROPERTY(QList exac READ exac) public: - explicit FrequenceQuickFilter(int analysisId); + // Constructors + FrequenceQuickFilter(int analysisId); Q_INVOKABLE bool isVisible() override; Q_INVOKABLE QJsonArray toJson() override; diff --git a/app/Model/analysis/filtering/quickfilters/panelquickfilter.cpp b/app/Model/analysis/filtering/quickfilters/panelquickfilter.cpp index 80c4991..0c34e6e 100644 --- a/app/Model/analysis/filtering/quickfilters/panelquickfilter.cpp +++ b/app/Model/analysis/filtering/quickfilters/panelquickfilter.cpp @@ -2,7 +2,7 @@ #include "Model/regovar.h" #include "Model/analysis/filtering/filteringanalysis.h" -PanelQuickFilter::PanelQuickFilter(int analysisId): QuickFilterBlockInterface() +PanelQuickFilter::PanelQuickFilter(int): QuickFilterBlockInterface() { mOperators.clear(); mOperators.append("∈"); @@ -11,26 +11,41 @@ PanelQuickFilter::PanelQuickFilter(int analysisId): QuickFilterBlockInterface() mPanelsList.clear(); - // Retrieve list of available panel in the analysis settings - FilteringAnalysis* analysis = regovar->analysesManager()->getOrCreateFilteringAnalysis(analysisId); - for (const QString& panelId: analysis->panelsUsed()) + // Retrieve list of head panels + for (int idx=0; idx < regovar->panelsManager()->panels()->proxy()->rowCount(); idx++) { - PanelVersion* version = regovar->panelsManager()->getPanelVersion(panelId); + QModelIndex i1 = regovar->panelsManager()->panels()->proxy()->getModelIndex(idx); + // TODO: fix get panel sorted by name + // QModelIndex i2 = regovar->panelsManager()->panels()->proxy()->mapToSource(i1); + PanelVersion* version =regovar->panelsManager()->panels()->getAt(i1.row()); QuickFilterField* panelFilter = new QuickFilterField( - panelId, + version->id(), version->fullname(), mOperators, "IN", 0, false, this); mPanelsList << panelFilter; } - mIsVisible = mPanelsList.count(); + // TODO: Add specific panels that have been added by users +// FilteringAnalysis* analysis = regovar->analysesManager()->getOrCreateFilteringAnalysis(analysisId); +// if (analysis->panelsUsed().count() > 0) +// { +// for (const QString& panelId: analysis->panelsUsed()) +// { +// PanelVersion* version = regovar->panelsManager()->getPanelVersion(panelId); +// QuickFilterField* panelFilter = new QuickFilterField( +// panelId, +// version->fullname(), +// mOperators, "IN", 0, false, this); +// mPanelsList << panelFilter; +// } +// } } bool PanelQuickFilter::isVisible() { - return mIsVisible; + return true; // mIsVisible; } diff --git a/app/Model/analysis/filtering/quickfilters/phenotypequickfilter.cpp b/app/Model/analysis/filtering/quickfilters/phenotypequickfilter.cpp index b5dd4e2..016d420 100644 --- a/app/Model/analysis/filtering/quickfilters/phenotypequickfilter.cpp +++ b/app/Model/analysis/filtering/quickfilters/phenotypequickfilter.cpp @@ -4,7 +4,7 @@ PhenotypeQuickFilter::PhenotypeQuickFilter(int) : QuickFilterBlockInterface() { - + mHpoList = new HpoDataListModel(this); } diff --git a/app/Model/analysis/filtering/quickfilters/phenotypequickfilter.h b/app/Model/analysis/filtering/quickfilters/phenotypequickfilter.h index 68c34f3..3d0c280 100644 --- a/app/Model/analysis/filtering/quickfilters/phenotypequickfilter.h +++ b/app/Model/analysis/filtering/quickfilters/phenotypequickfilter.h @@ -5,18 +5,17 @@ #include #include #include "quickfilterblockinterface.h" +#include "Model/phenotype/hpodatalistmodel.h" class PhenotypeQuickFilter: public QuickFilterBlockInterface { Q_OBJECT - Q_PROPERTY(QuickFilterField* _1000GAll READ _1000GAll) - Q_PROPERTY(QuickFilterField* exacAll READ exacAll) - Q_PROPERTY(QList _1000G READ _1000G) - Q_PROPERTY(QList exac READ exac) + Q_PROPERTY(HpoDataListModel* hpoList READ hpoList) public: - explicit PhenotypeQuickFilter(int analysisId); + // Constructors + PhenotypeQuickFilter(int analysisId); Q_INVOKABLE bool isVisible() override; Q_INVOKABLE QJsonArray toJson() override; @@ -25,18 +24,12 @@ class PhenotypeQuickFilter: public QuickFilterBlockInterface Q_INVOKABLE void checkAnnotationsDB(QList dbs) override; bool loadJson(QJsonArray filter) override; - inline QList _1000G() { return m1000GFields; } - inline QList exac() { return mExacFields; } - inline QuickFilterField* _1000GAll() { return m1000GAll; } - inline QuickFilterField* exacAll() { return mExacAll; } + inline HpoDataListModel* hpoList() { return mHpoList; } void init(QString _1000gUid, QString exacUid, QStringList _1000g, QStringList exac); private: - QuickFilterField* m1000GAll = nullptr; - QuickFilterField* mExacAll = nullptr; - QList m1000GFields; - QList mExacFields; + HpoDataListModel* mHpoList = nullptr; QString mFilter; QStringList mOperators; QHash mOpMapping; diff --git a/app/Model/analysis/filtering/quickfilters/positionquickfilter.h b/app/Model/analysis/filtering/quickfilters/positionquickfilter.h index ee81fd6..8d3bb51 100644 --- a/app/Model/analysis/filtering/quickfilters/positionquickfilter.h +++ b/app/Model/analysis/filtering/quickfilters/positionquickfilter.h @@ -18,8 +18,10 @@ class PositionQuickFilter : public QuickFilterBlockInterface Q_PROPERTY(QuickFilterField* splice READ splice) public: - explicit PositionQuickFilter(int analysisId); + // Constructors + PositionQuickFilter(int analysisId); + // Methods Q_INVOKABLE bool isVisible() override; Q_INVOKABLE QJsonArray toJson() override; Q_INVOKABLE void setFilter(QString filterId, bool filterActive, QVariant filterValue=QVariant()) override; diff --git a/app/Model/analysis/filtering/quickfilters/quickfilterblockinterface.h b/app/Model/analysis/filtering/quickfilters/quickfilterblockinterface.h index 75e9b22..eaf1f99 100644 --- a/app/Model/analysis/filtering/quickfilters/quickfilterblockinterface.h +++ b/app/Model/analysis/filtering/quickfilters/quickfilterblockinterface.h @@ -74,9 +74,10 @@ class QuickFilterBlockInterface : public QObject Q_PROPERTY(bool isVisible READ isVisible) public: - explicit QuickFilterBlockInterface(QObject *parent = nullptr); - + // Constructors + QuickFilterBlockInterface(QObject *parent = nullptr); + // Methods //! Indicates if this filter shall be displayed in the UI (by example some filter are only for "Trio analysis") Q_INVOKABLE virtual bool isVisible() = 0; //! Return the filter query as Json string that will be concatened with other quickFilters diff --git a/app/Model/analysis/filtering/quickfilters/quickfiltermodel.h b/app/Model/analysis/filtering/quickfilters/quickfiltermodel.h index 018b5be..46be08a 100644 --- a/app/Model/analysis/filtering/quickfilters/quickfiltermodel.h +++ b/app/Model/analysis/filtering/quickfilters/quickfiltermodel.h @@ -11,7 +11,6 @@ class QuickFilterModel : public QObject { Q_OBJECT - Q_PROPERTY(QuickFilterBlockInterface* transmissionFilter READ transmissionFilter NOTIFY filterChanged) Q_PROPERTY(QuickFilterBlockInterface* qualityFilter READ qualityFilter NOTIFY filterChanged) Q_PROPERTY(QuickFilterBlockInterface* positionFilter READ positionFilter NOTIFY filterChanged) @@ -35,10 +34,8 @@ class QuickFilterModel : public QObject PhenotypeFilter }; - explicit QuickFilterModel(QObject *parent = nullptr); - void init(int refId, int analysisId); - Q_INVOKABLE void clear(); - + // Constructor + QuickFilterModel(QObject *parent = nullptr); // Getters inline QuickFilterBlockInterface* transmissionFilter() { return mQuickFilters[TransmissionFilter]; } @@ -50,17 +47,16 @@ class QuickFilterModel : public QObject inline QuickFilterBlockInterface* panelFilter() { return mQuickFilters[PanelFilter]; } inline QuickFilterBlockInterface* phenotypeFilter() { return mQuickFilters[PhenotypeFilter]; } - // Methods + Q_INVOKABLE void clear(); Q_INVOKABLE QJsonArray toJson(); Q_INVOKABLE void checkAnnotationsDB(QList dbs); + void init(int refId, int analysisId); void loadFilter(QJsonArray filter); Q_SIGNALS: void filterChanged(); - - private: QHash mQuickFilters; }; diff --git a/app/Model/analysis/filtering/quickfilters/transmissionquickfilter.h b/app/Model/analysis/filtering/quickfilters/transmissionquickfilter.h index b423c41..faa35b0 100644 --- a/app/Model/analysis/filtering/quickfilters/transmissionquickfilter.h +++ b/app/Model/analysis/filtering/quickfilters/transmissionquickfilter.h @@ -13,7 +13,8 @@ class TransmissionQuickFilter : public QuickFilterBlockInterface public: - explicit TransmissionQuickFilter(int); + // Constructor + TransmissionQuickFilter(int); // QuickFilterBlockInterface implementation Q_INVOKABLE bool isVisible() override; diff --git a/app/Model/analysis/filtering/quickfilters/typequickfilter.h b/app/Model/analysis/filtering/quickfilters/typequickfilter.h index da7792c..57e6b74 100644 --- a/app/Model/analysis/filtering/quickfilters/typequickfilter.h +++ b/app/Model/analysis/filtering/quickfilters/typequickfilter.h @@ -16,8 +16,10 @@ class TypeQuickFilter : public QuickFilterBlockInterface Q_PROPERTY(QuickFilterField* splicing READ splicing) Q_PROPERTY(QuickFilterField* indel READ indel) Q_PROPERTY(QuickFilterField* synonymous READ synonymous) + public: - explicit TypeQuickFilter(int analysisId); + // Constructor + TypeQuickFilter(int analysisId); Q_INVOKABLE bool isVisible() override; Q_INVOKABLE QJsonArray toJson() override; diff --git a/app/Model/analysis/filtering/resultstreeitem.h b/app/Model/analysis/filtering/resultstreeitem.h index 7bd2a37..ecf92b4 100644 --- a/app/Model/analysis/filtering/resultstreeitem.h +++ b/app/Model/analysis/filtering/resultstreeitem.h @@ -13,7 +13,7 @@ class ResultsTreeItem : public TreeItem public: // Constructors - explicit ResultsTreeItem(FilteringAnalysis* analysis=nullptr, TreeItem* parent=nullptr); + ResultsTreeItem(FilteringAnalysis* analysis=nullptr, TreeItem* parent=nullptr); // Getters inline QString uid() { return mUid; } diff --git a/app/Model/analysis/filtering/resultstreemodel.h b/app/Model/analysis/filtering/resultstreemodel.h index 07f47d8..dd0aa2e 100644 --- a/app/Model/analysis/filtering/resultstreemodel.h +++ b/app/Model/analysis/filtering/resultstreemodel.h @@ -17,7 +17,8 @@ class ResultsTreeModel : public TreeModel Q_PROPERTY(QString samplesNames READ samplesNames NOTIFY samplesNamesChanged) public: - explicit ResultsTreeModel(FilteringAnalysis* parent=nullptr); + // Constructors + ResultsTreeModel(FilteringAnalysis* parent=nullptr); // QAbstractItemModel interface - lazy loading methods QHash roleNames() const override; diff --git a/app/Model/analysis/pipeline/pipelineanalysis.cpp b/app/Model/analysis/pipeline/pipelineanalysis.cpp index ebd9063..a60efd7 100644 --- a/app/Model/analysis/pipeline/pipelineanalysis.cpp +++ b/app/Model/analysis/pipeline/pipelineanalysis.cpp @@ -24,7 +24,7 @@ void PipelineAnalysis::addInputs(QList inputs) for (QObject* o1: inputs) { File* file = qobject_cast(o1); - mInputsFiles->add(file); + mInputsFiles->append(file); } } @@ -42,7 +42,7 @@ void PipelineAnalysis::addInputFromWS(QJsonObject json) File* file = regovar->filesManager()->getOrCreateFile(json["id"].toInt()); file->loadJson(json); - mInputsFiles->add(file); + mInputsFiles->append(file); } @@ -88,7 +88,7 @@ bool PipelineAnalysis::loadJson(QJsonObject json, bool full_init) { file->load(true); } - mInputsFiles->add(file); + mInputsFiles->append(file); } } // Outputs files diff --git a/app/Model/analysis/pipeline/pipelineanalysis.h b/app/Model/analysis/pipeline/pipelineanalysis.h index c2cc470..205000a 100644 --- a/app/Model/analysis/pipeline/pipelineanalysis.h +++ b/app/Model/analysis/pipeline/pipelineanalysis.h @@ -23,8 +23,8 @@ class PipelineAnalysis: public Analysis public: // Constructors - explicit PipelineAnalysis(QObject* parent=nullptr); - explicit PipelineAnalysis(int id, QObject* parent=nullptr); + PipelineAnalysis(QObject* parent=nullptr); + PipelineAnalysis(int id, QObject* parent=nullptr); // Getters inline QJsonObject config() const { return mConfig; } @@ -42,13 +42,13 @@ class PipelineAnalysis: public Analysis // Analysis abstracts methods overriden //! Set model with provided json data - Q_INVOKABLE bool loadJson(QJsonObject json, bool full_init=false); + Q_INVOKABLE bool loadJson(QJsonObject json, bool full_init=false) override; //! Export model data into json object - Q_INVOKABLE QJsonObject toJson(); + Q_INVOKABLE QJsonObject toJson() override; //! Save subject information onto server - Q_INVOKABLE void save(); + Q_INVOKABLE void save() override; //! Load Subject information from server - Q_INVOKABLE void load(bool forceRefresh=true); + Q_INVOKABLE void load(bool forceRefresh=true) override; // Methods Q_INVOKABLE void addInputs(QList inputs); diff --git a/app/Model/analysis/remotelogmodel.h b/app/Model/analysis/remotelogmodel.h index 17335cc..69b7cba 100644 --- a/app/Model/analysis/remotelogmodel.h +++ b/app/Model/analysis/remotelogmodel.h @@ -24,8 +24,8 @@ class RemoteLogModel : public QObject public: // Constructor - explicit RemoteLogModel(QObject* parent=nullptr); - explicit RemoteLogModel(QString url, Analysis* parent=nullptr); + RemoteLogModel(QObject* parent=nullptr); + RemoteLogModel(QString url, Analysis* parent=nullptr); // Accessors inline bool autoRefresh() const { return mAutoRefresh; } diff --git a/app/Model/event/event.cpp b/app/Model/event/event.cpp index 4856c34..eb1dde3 100644 --- a/app/Model/event/event.cpp +++ b/app/Model/event/event.cpp @@ -16,7 +16,7 @@ QHash Event::initTypeIconMap() } -Event::Event(QObject* parent) : QObject(parent) +Event::Event(QObject* parent) : RegovarResource(parent) { connect(this, &Event::dataChanged, this, &Event::updateSearchField); } @@ -44,7 +44,7 @@ void Event::updateSearchField() -bool Event::loadJson(QJsonObject json) +bool Event::loadJson(QJsonObject json, bool) { mId = json["id"].toInt(); mType = json["type"].toString(); diff --git a/app/Model/event/event.h b/app/Model/event/event.h index bbdb3f8..9942bc5 100644 --- a/app/Model/event/event.h +++ b/app/Model/event/event.h @@ -2,6 +2,7 @@ #define EVENT_H #include +#include "Model/framework/regovarresource.h" #include "Model/user/user.h" #include "Model/analysis/analysis.h" #include "Model/file/file.h" @@ -10,7 +11,7 @@ #include "Model/subject/sample.h" #include "Model/subject/subject.h" -class Event : public QObject +class Event : public RegovarResource { Q_OBJECT Q_PROPERTY(int id READ id NOTIFY dataChanged) @@ -28,17 +29,14 @@ class Event : public QObject Q_PROPERTY(Subject* subject READ subject NOTIFY dataChanged) Q_PROPERTY(Sample* sample READ sample NOTIFY dataChanged) Q_PROPERTY(File* file READ file NOTIFY dataChanged) - - Q_PROPERTY(QJsonObject messageUI READ messageUI NOTIFY dataChanged) - Q_PROPERTY(QString searchField READ searchField NOTIFY dataChanged) public: // Constructor - explicit Event(QObject* parent=nullptr); - explicit Event(int id, QObject* parent=nullptr); - explicit Event(QJsonObject json, QObject* parent=nullptr); + Event(QObject* parent=nullptr); + Event(int id, QObject* parent=nullptr); + Event(QJsonObject json, QObject* parent=nullptr); // Getters inline int id() const { return mId; } @@ -59,23 +57,20 @@ class Event : public QObject // Methods //! Set model with provided json data - Q_INVOKABLE bool loadJson(QJsonObject json); + Q_INVOKABLE bool loadJson(QJsonObject json, bool full_init=true) override; //! Export model data into json object - Q_INVOKABLE QJsonObject toJson(); + Q_INVOKABLE QJsonObject toJson() override; //! Save event information onto server - Q_INVOKABLE void save(); - //! Load event information and related object from server - Q_INVOKABLE void load(bool forceRefresh=true); + Q_INVOKABLE void save() override; + //! Load event information from server + Q_INVOKABLE void load(bool forceRefresh=true) override; + -Q_SIGNALS: - void dataChanged(); public Q_SLOTS: - void updateSearchField(); + void updateSearchField() override; private: - QDateTime mLastInternalLoad = QDateTime::currentDateTime(); - int mId=-1; QString mMessage; QString mDetails; @@ -90,7 +85,6 @@ public Q_SLOTS: Sample* mSample = nullptr; File* mFile = nullptr; QJsonObject mMessageUI; - QString mSearchField; static QHash mTypeIconMap; static QHash initTypeIconMap(); diff --git a/app/Model/event/eventslistmodel.h b/app/Model/event/eventslistmodel.h index 4f39139..db4b92b 100644 --- a/app/Model/event/eventslistmodel.h +++ b/app/Model/event/eventslistmodel.h @@ -26,8 +26,8 @@ class EventsListModel : public QAbstractListModel public: // Constructors - explicit EventsListModel(QObject* parent = nullptr); - explicit EventsListModel(QString target, QString id, QObject* parent = nullptr); + EventsListModel(QObject* parent = nullptr); + EventsListModel(QString target, QString id, QObject* parent = nullptr); // Getters inline GenericProxyModel* proxy() const { return mProxy; } diff --git a/app/Model/event/eventsmanager.cpp b/app/Model/event/eventsmanager.cpp index c38dc64..a38e2ff 100644 --- a/app/Model/event/eventsmanager.cpp +++ b/app/Model/event/eventsmanager.cpp @@ -44,40 +44,18 @@ Event* EventsManager::getOrCreateEvent(int eventId) void EventsManager::processPushNotification(QString action, QJsonObject data) { -// // Retrieve realtime progress data -// QString status = data["status"].toString(); -// double progressValue = 0.0; -// if (action == "import_vcf_processing" || action == "import_vcf_start") -// { -// progressValue = data["progress"].toDouble(); -// } -// else if (action == "import_vcf_end") -// { -// progressValue = 1.0; -// status = "ready"; -// } - -// // Update sample status -// for (const QJsonValue& json: data["samples"].toArray()) -// { -// QJsonObject obj = json.toObject(); -// int sid = obj["id"].toInt(); - -// Sample* sample = getOrCreateEvent(sid); -// sample->setStatus(status); -// sample->setLoadingProgress(progressValue); -// sample->refreshUIAttributes(); -// } + if (action == "new_event") + { + Event* newEvent = getOrCreateEvent(data["id"].toInt()); + newEvent->loadJson(data); -// // Notify view when new sample import start (import wizard) -// if (action == "import_vcf_start") -// { -// QList ids; -// for (QJsonValue sample: data["samples"].toArray()) -// { -// QJsonObject sampleData = sample.toObject(); -// ids << sampleData["id"].toInt(); -// } -// emit sampleImportStart(data["file_id"].toString().toInt(), ids); -// } + if (newEvent->type() != "technical") + { + mLastEvents->append(newEvent); + emit lastEventsChanged(); + } + mTechnicalEvents->append(newEvent); + emit technicalEventsChanged(); + emit newEventPop(newEvent); + } } diff --git a/app/Model/event/eventsmanager.h b/app/Model/event/eventsmanager.h index 94fcbed..5651013 100644 --- a/app/Model/event/eventsmanager.h +++ b/app/Model/event/eventsmanager.h @@ -13,7 +13,7 @@ class EventsManager : public QObject public: // Constructor - explicit EventsManager(QObject* parent=nullptr); + EventsManager(QObject* parent=nullptr); // Getters inline EventsListModel* lastEvents() const { return mLastEvents; } diff --git a/app/Model/file/file.cpp b/app/Model/file/file.cpp index 5f84376..dd97767 100644 --- a/app/Model/file/file.cpp +++ b/app/Model/file/file.cpp @@ -19,7 +19,7 @@ QStringList File::web = {"html", "htm"}; -File::File(QObject* parent) : QObject(parent) +File::File(QObject* parent) : RegovarResource(parent) { connect(this, &File::dataChanged, this, &File::updateSearchField); } @@ -93,7 +93,7 @@ bool File::loadJson(QJsonDocument json) } -bool File::loadJson(QJsonObject json) +bool File::loadJson(QJsonObject json, bool) { mId = json["id"].toInt(); if (json.contains("name")) mName = json["name"].toString(); diff --git a/app/Model/file/file.h b/app/Model/file/file.h index 17d7b1a..ed2143d 100644 --- a/app/Model/file/file.h +++ b/app/Model/file/file.h @@ -5,15 +5,11 @@ #include #include #include +#include "Model/framework/regovarresource.h" -class File : public QObject +class File : public RegovarResource { Q_OBJECT - - // Regovar resource attribute - Q_PROPERTY(bool loaded READ loaded NOTIFY dataChanged) - Q_PROPERTY(QDateTime updateDate READ updateDate NOTIFY dataChanged) - Q_PROPERTY(QDateTime createDate READ createDate NOTIFY dataChanged) // File attributes Q_PROPERTY(int id READ id) Q_PROPERTY(QString name READ name WRITE setName NOTIFY dataChanged) @@ -31,13 +27,11 @@ class File : public QObject Q_PROPERTY(bool localFileReady READ localFileReady NOTIFY localFileReadyChanged) Q_PROPERTY(qint64 downloadOffset READ downloadOffset WRITE setDownloadOffset NOTIFY dataChanged) Q_PROPERTY(FileStatus localStatus READ localStatus WRITE setLocalStatus NOTIFY dataChanged) - // Property for QML quick display in tableView Q_PROPERTY(QVariant filenameUI READ filenameUI NOTIFY dataChanged) Q_PROPERTY(QVariant statusUI READ statusUI NOTIFY dataChanged) Q_PROPERTY(QString sizeUI READ sizeUI NOTIFY dataChanged) - Q_PROPERTY(QString sourceUI READ sourceUI NOTIFY dataChanged) - Q_PROPERTY(QString searchField READ searchField NOTIFY dataChanged) + Q_PROPERTY(QString sourceUI READ sourceUI NOTIFY dataChanged) public: enum FileStatus @@ -100,13 +94,13 @@ class File : public QObject // Methods //! Set model with provided json data Q_INVOKABLE bool loadJson(QJsonDocument json); - Q_INVOKABLE bool loadJson(QJsonObject json); + Q_INVOKABLE bool loadJson(QJsonObject json, bool full_init=true) override; //! Export model data into json object - Q_INVOKABLE QJsonObject toJson(); - //! Save subject information onto server - Q_INVOKABLE void save(); - //! Load Subject information from server - Q_INVOKABLE void load(bool forceRefresh=true); + Q_INVOKABLE QJsonObject toJson() override; + //! Save event information onto server + Q_INVOKABLE void save() override; + //! Load event information from server + Q_INVOKABLE void load(bool forceRefresh=true) override; //! Dowload the file and put it in cache. When file is ready, the localFileReadyChanged signal is emit Q_INVOKABLE bool downloadLocalFile(); //! Read file content as QString @@ -122,19 +116,14 @@ class File : public QObject Q_SIGNALS: - void dataChanged(); void localFileReadyChanged(); -public Q_SLOTS: - void updateSearchField(); -private: +public Q_SLOTS: + void updateSearchField() override; - bool mLoaded = false; - QDateTime mUpdateDate; - QDateTime mCreateDate; - QDateTime mLastInternalLoad = QDateTime::currentDateTime(); +private: // Attributes int mId = -1; QUrl mUrl; @@ -151,7 +140,6 @@ public Q_SLOTS: bool mLocalFileReady = false; qint64 mDownloadOffset = 0; FileStatus mLocalStatus; - QString mSearchField; // UI all-in-one attributes diff --git a/app/Model/file/fileslistmodel.cpp b/app/Model/file/fileslistmodel.cpp index ac47920..683ac65 100644 --- a/app/Model/file/fileslistmodel.cpp +++ b/app/Model/file/fileslistmodel.cpp @@ -32,7 +32,7 @@ bool FilesListModel::loadJson(QJsonArray json) -bool FilesListModel::add(File* file) +bool FilesListModel::append(File* file) { bool result = false; if (file!= nullptr && !mFileList.contains(file)) @@ -41,6 +41,7 @@ bool FilesListModel::add(File* file) mFileList.append(file); endInsertRows(); emit countChanged(); + emit fileAdded(file->id()); result = true; } return result; @@ -56,6 +57,7 @@ bool FilesListModel::remove(File* file) mFileList.removeAt(pos); endRemoveRows(); emit countChanged(); + emit fileRemoved(file->id()); result = true; } return result; @@ -87,6 +89,13 @@ File* FilesListModel::getAt(int position) return nullptr; } + +bool FilesListModel::contains(File* file) +{ + return mFileList.contains(file); +} + + int FilesListModel::rowCount(const QModelIndex&) const { return mFileList.count(); diff --git a/app/Model/file/fileslistmodel.h b/app/Model/file/fileslistmodel.h index 8085300..3e22f67 100644 --- a/app/Model/file/fileslistmodel.h +++ b/app/Model/file/fileslistmodel.h @@ -27,18 +27,20 @@ class FilesListModel : public QAbstractListModel Q_PROPERTY(GenericProxyModel* proxy READ proxy NOTIFY neverChanged) public: - explicit FilesListModel(QObject* parent = nullptr); + // Constructor + FilesListModel(QObject* parent = nullptr); // Getters inline GenericProxyModel* proxy() const { return mProxy; } // Methods Q_INVOKABLE bool loadJson(QJsonArray json); - Q_INVOKABLE bool add(File* file); + Q_INVOKABLE bool append(File* file); Q_INVOKABLE bool remove(File* file); Q_INVOKABLE bool refresh(); Q_INVOKABLE bool clear(); Q_INVOKABLE File* getAt(int position); + Q_INVOKABLE bool contains(File* file); // QAbstractListModel methods int rowCount(const QModelIndex& parent = QModelIndex()) const; @@ -48,6 +50,8 @@ class FilesListModel : public QAbstractListModel Q_SIGNALS: void neverChanged(); void countChanged(); + void fileAdded(int id); + void fileRemoved(int id); private: //! List of files diff --git a/app/Model/file/filesmanager.cpp b/app/Model/file/filesmanager.cpp index ae4cd87..9fb1c00 100644 --- a/app/Model/file/filesmanager.cpp +++ b/app/Model/file/filesmanager.cpp @@ -5,6 +5,8 @@ FilesManager::FilesManager(QObject *parent) : QObject(parent) { + mUploadsList = new FilesListModel(this); + mRemoteFilesList = new FilesListModel(this); mUploader = new TusUploader(); mUploader->setUploadUrl(regovar->networkManager()->serverUrl().toString() + "/file/upload"); mUploader->setRootUrl(regovar->networkManager()->serverUrl().toString()); @@ -77,7 +79,7 @@ bool FilesManager::deleteFile(int id) // Remove local instance of the file File* file = mFiles[id]; mFiles.remove(id); - mRemoteFilesList.removeAll(file); + mRemoteFilesList->remove(file); file->clearCache(); // Remove the file on the server @@ -112,13 +114,13 @@ void FilesManager::loadFilesBrowser() { if (success) { - mRemoteFilesList.clear(); + mRemoteFilesList->clear(); for (const QJsonValue& data: json["data"].toArray()) { QJsonObject fileData = data.toObject(); File* file = getOrCreateFile(fileData["id"].toInt()); file->loadJson(fileData); - mRemoteFilesList.append(file); + mRemoteFilesList->append(file); } emit remoteListChanged(); } @@ -150,7 +152,7 @@ void FilesManager::filesEnqueued(QHash mapping) int id = pathSplitted[pathSplitted.count()-1].toInt(); File* file = getOrCreateFile(id); file->load(); - mUploadsList.append(file); + mUploadsList->append(file); emit fileUploadEnqueued(key, id); // qDebug() << key << " => " << mapping[key] << id; } @@ -166,10 +168,10 @@ void FilesManager::cancelUploadFile(QList filesId) if (mFiles.contains(id)) { File* file = mFiles[id]; - if (mUploadsList.indexOf(file) != -1) + if (mUploadsList->contains(file)) { mUploader->cancel(QString::number(id)); - mUploadsList.removeAll(file); + mUploadsList->remove(file); } } } @@ -179,7 +181,7 @@ void FilesManager::cancelUploadFile(QList filesId) void FilesManager::clearUploadsList() { - mUploadsList.clear(); + mUploadsList->clear(); } @@ -264,7 +266,7 @@ void FilesManager::processPushNotification(QString action, QJsonObject json) File* file = getOrCreateFile(id); file->loadJson(json); // update global upload progress - if (mUploadsList.indexOf(file) != -1) + if (mUploadsList->contains(file)) { updateUploadProgress(); } @@ -275,18 +277,18 @@ void FilesManager::processPushNotification(QString action, QJsonObject json) void FilesManager::updateUploadProgress() { - if (mUploadsList.count() > 0) + if (mUploadsList->rowCount() > 0) { int totalProgress = 0; - for (QObject* o: mUploadsList) + for (int idx=0; idx < mUploadsList->rowCount(); idx++) { - File* f = qobject_cast(o); + File* f = mUploadsList->getAt(idx); int progress = 0; double s = f->size(); if (s>0) progress = f->uploadOffset() / s * 100; totalProgress += progress; } - mUploadsProgress = totalProgress / mUploadsList.count(); + mUploadsProgress = totalProgress / mUploadsList->rowCount(); } emit uploadsChanged(); } diff --git a/app/Model/file/filesmanager.h b/app/Model/file/filesmanager.h index 8396f91..1376308 100644 --- a/app/Model/file/filesmanager.h +++ b/app/Model/file/filesmanager.h @@ -5,6 +5,7 @@ #include "file.h" #include "filestreemodel.h" #include "tusuploader.h" +#include "fileslistmodel.h" class FilesManager : public QObject { @@ -12,24 +13,25 @@ class FilesManager : public QObject Q_PROPERTY(QString cacheDir READ cacheDir WRITE setCacheDir NOTIFY cacheDirChanged) Q_PROPERTY(qint64 cacheOccupiedSize READ cacheOccupiedSize WRITE setCacheOccupiedSize NOTIFY cacheOccupiedSizeChanged) Q_PROPERTY(int cacheMaxSize READ cacheMaxSize WRITE setCacheMaxSize NOTIFY cacheMaxSizeChanged) - Q_PROPERTY(QList uploadsList READ uploadsList NOTIFY uploadsChanged) + Q_PROPERTY(FilesListModel* uploadsList READ uploadsList NOTIFY uploadsChanged) Q_PROPERTY(int uploadsProgress READ uploadsProgress NOTIFY uploadsChanged) - Q_PROPERTY(QList remoteList READ remoteList NOTIFY remoteListChanged) + Q_PROPERTY(FilesListModel* remoteList READ remoteList NOTIFY remoteListChanged) Q_PROPERTY(FilesTreeModel* filesTree READ filesTree NOTIFY filesTreeChanged) public: - explicit FilesManager(QObject *parent = nullptr); + // Constructor + FilesManager(QObject *parent = nullptr); // Getters inline QString cacheDir() const { return mCacheDir; } inline qint64 cacheOccupiedSize() const { return mCacheOccupiedSize; } inline int cacheMaxSize() const { return mCacheMaxSize; } inline int uploadsProgress() const { return mUploadsProgress; } - inline QList remoteList() const { return mRemoteFilesList; } - inline QList uploadsList() const { return mUploadsList; } + inline FilesListModel* remoteList() const { return mRemoteFilesList; } + inline FilesListModel* uploadsList() const { return mUploadsList; } inline FilesTreeModel* filesTree() const { return mFilesTree; } // Setters @@ -74,9 +76,9 @@ public Q_SLOTS: //! The progress (0 to 100) of all uploads int mUploadsProgress = 0; //! The list of files that are currently uploadling (by this client, add file with enqueueUploadFile method) - QList mUploadsList; + FilesListModel* mUploadsList = nullptr; //! The flat list of files available on the server (load/init with loadFilesBrowser method) - QList mRemoteFilesList; + FilesListModel* mRemoteFilesList = nullptr; //! The tree list of files available on the server (load/init with loadFilesBrowser method) FilesTreeModel* mFilesTree = nullptr; //! The uploader that manage TUS protocol (resumable upload) diff --git a/app/Model/file/filestreemodel.h b/app/Model/file/filestreemodel.h index 0093ff6..fdeb4f3 100644 --- a/app/Model/file/filestreemodel.h +++ b/app/Model/file/filestreemodel.h @@ -26,7 +26,7 @@ class FilesTreeModel : public TreeModel }; // Constructors - explicit FilesTreeModel(QObject* parent=nullptr); + FilesTreeModel(QObject* parent=nullptr); QHash roleNames() const override; // Accessors diff --git a/app/Model/file/tusuploader.h b/app/Model/file/tusuploader.h index 1418d8a..5bc7691 100644 --- a/app/Model/file/tusuploader.h +++ b/app/Model/file/tusuploader.h @@ -25,7 +25,8 @@ class TusUploader : public QObject { Q_OBJECT public: - explicit TusUploader(QObject *parent = nullptr); + // Constructors + TusUploader(QObject *parent = nullptr); ~TusUploader(); // Accessors @@ -50,12 +51,12 @@ class TusUploader : public QObject void emitFileEnqueued(QHash* serverMapping); - Q_SIGNALS: void uploadStarted(TusUploadItem* file); void uploadEnded(TusUploadItem* file); void filesEnqueued(QHash serverMapping); + public Q_SLOTS: //! Try to start or resume uploads in the queue void startNext(); diff --git a/app/Model/framework/dynamicformfieldmodel.h b/app/Model/framework/dynamicformfieldmodel.h index e1042e1..af475e4 100644 --- a/app/Model/framework/dynamicformfieldmodel.h +++ b/app/Model/framework/dynamicformfieldmodel.h @@ -30,8 +30,8 @@ class DynamicFormFieldModel : public QObject public: // Constructor - explicit DynamicFormFieldModel(DynamicFormModel* parent=nullptr); - explicit DynamicFormFieldModel(QJsonObject json, int order, DynamicFormModel* parent=nullptr); + DynamicFormFieldModel(DynamicFormModel* parent=nullptr); + DynamicFormFieldModel(QJsonObject json, int order, DynamicFormModel* parent=nullptr); // Getters inline QString id() const { return mId; } diff --git a/app/Model/framework/dynamicformmodel.h b/app/Model/framework/dynamicformmodel.h index f5e3fa9..839c336 100644 --- a/app/Model/framework/dynamicformmodel.h +++ b/app/Model/framework/dynamicformmodel.h @@ -34,7 +34,8 @@ class DynamicFormModel : public QAbstractListModel Q_PROPERTY(double labelWidth READ labelWidth WRITE setLabelWidth NOTIFY labelWidthChanged) public: - explicit DynamicFormModel(QObject* parent=nullptr); + // Constructor + DynamicFormModel(QObject* parent=nullptr); // Getters inline FilesListModel* inputsFiles() const { return mInputsFiles; } diff --git a/app/Model/framework/genericproxymodel.h b/app/Model/framework/genericproxymodel.h index acf1fa0..2dbefb3 100644 --- a/app/Model/framework/genericproxymodel.h +++ b/app/Model/framework/genericproxymodel.h @@ -8,7 +8,8 @@ class GenericProxyModel: public QSortFilterProxyModel Q_OBJECT public: - explicit GenericProxyModel(QObject* parent=nullptr); + // Constructor + GenericProxyModel(QObject* parent=nullptr); Q_INVOKABLE void setFilterString(QString string); Q_INVOKABLE void setSortOrder(int column, int order); diff --git a/app/Model/framework/jsonlistmodel.cpp b/app/Model/framework/jsonlistmodel.cpp new file mode 100644 index 0000000..bf60ba8 --- /dev/null +++ b/app/Model/framework/jsonlistmodel.cpp @@ -0,0 +1,132 @@ +#include "jsonlistmodel.h" + +JsonListModel::JsonListModel(QObject* parent): QAbstractListModel(parent) +{ + mProxy = new GenericProxyModel(this); + mProxy->setSourceModel(this); + mProxy->setFilterRole(SearchField); + mProxy->setSortRole(Name); +} + + +void JsonListModel::clear() +{ + beginResetModel(); + mJson.clear(); + endResetModel(); + emit countChanged(); +} + +bool JsonListModel::loadJson(QJsonArray json) +{ + beginResetModel(); + mJson.clear(); + for(const QJsonValue& val: json) + { + mJson.append(val.toObject()); + } + endResetModel(); + emit countChanged(); + return true; +} + +bool JsonListModel::append(QJsonObject json) +{ + beginInsertRows(QModelIndex(), rowCount(), rowCount()); + mJson.append(json); + endInsertRows(); + emit countChanged(); + return true; +} + +bool JsonListModel::remove(QJsonObject) +{ + // TODO +// if (mJson.contains(gene)) +// { +// int pos = mJson.indexOf(gene); +// beginRemoveRows(QModelIndex(), pos, pos); +// mJson.removeAll(gene); +// endRemoveRows(); +// emit countChanged(); +// return true; +// } + return false; +} + +QJsonObject JsonListModel::getAt(int idx) +{ + if (idx >= 0 && idx <= mJson.count()) + { + return mJson[idx]; + } + return QJsonObject(); +} + +QString JsonListModel::join(QString separator, QString key) +{ + QString result; + for(QJsonObject json: mJson) + { + result += json.value(key).toString() + separator; + } + return result.mid(0, result.length() - separator.length()); +} + + +int JsonListModel::rowCount(const QModelIndex&) const +{ + return mJson.count(); +} + +QVariant JsonListModel::data(const QModelIndex& index, int role) const +{ + if (index.row() < 0 || index.row() >= mJson.count()) + return QVariant(); + + const QJsonObject json = mJson[index.row()]; + if (role == Name || role == Qt::DisplayRole) + { + if (json.contains("name")) return json["name"]; + else if (json.contains("label")) return json["label"]; + else if (json.contains("title")) return json["title"]; + else if (json.contains("symbol")) return json["symbol"]; + } + else if (role == Id) + { + if (json.contains("id")) return json["id"]; + else if (json.contains("uid")) return json["uid"]; + else if (json.contains("hpo_id")) return json["hpo_id"]; + } + else if (role == Comment) + { + if (json.contains("comment")) return json["comment"]; + else if (json.contains("description")) return json["description"]; + else if (json.contains("details")) return json["details"]; + } + else if (role == Json) + { + return json; + } + else if (role == SearchField) + { + QString searchField = ""; + for(QString key: json.keys()) + { + searchField += json[key].toString() + " "; + } + return searchField; + } + + return QVariant(); +} + +QHash JsonListModel::roleNames() const +{ + QHash roles; + roles[Id] = "id"; + roles[Name] = "name"; + roles[Comment] = "comment"; + roles[SearchField] = "searchField"; + return roles; +} diff --git a/app/Model/framework/jsonlistmodel.h b/app/Model/framework/jsonlistmodel.h new file mode 100644 index 0000000..60f1fa5 --- /dev/null +++ b/app/Model/framework/jsonlistmodel.h @@ -0,0 +1,61 @@ +#ifndef JSONLISTMODEL_H +#define JSONLISTMODEL_H + +#include +#include "Model/framework/genericproxymodel.h" + +class JsonListModel: public QAbstractListModel +{ + enum Roles + { + Id = Qt::UserRole + 1, + Json, + // Adding some usual key found in almost all regovar json object + Name, // Label, Symbol, + Comment, // Description, Details, + SearchField + }; + + Q_OBJECT + Q_PROPERTY(int count READ rowCount NOTIFY countChanged) + Q_PROPERTY(GenericProxyModel* proxy READ proxy NOTIFY neverChanged) + + +public: + // Constructor + JsonListModel(QObject* parent=nullptr); + + // Getters + inline GenericProxyModel* proxy() const { return mProxy; } + + // Methods + //! Remove all entries of the list + Q_INVOKABLE void clear(); + //! Load phenotype list from list of json + Q_INVOKABLE bool loadJson(QJsonArray json); + //! Add the provided gene to the list if not already contains + Q_INVOKABLE bool append(QJsonObject json); + //! Remove a gene from the list if possible + Q_INVOKABLE bool remove(QJsonObject json); + //! Return entry at the requested position in the list + Q_INVOKABLE QJsonObject getAt(int idx); + //! Joins all the string list's strings into a single string with each element separated by the given separator (which can be an empty string). + Q_INVOKABLE QString join(QString separator, QString key="name"); + + // QAbstractListModel methods + int rowCount(const QModelIndex& parent = QModelIndex()) const; + QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const; + QHash roleNames() const; + + +Q_SIGNALS: + void neverChanged(); + void countChanged(); + + +private: + QList mJson; + GenericProxyModel* mProxy = nullptr; +}; + +#endif // JSONLISTMODEL_H diff --git a/app/Model/framework/networkmanager.h b/app/Model/framework/networkmanager.h index 41a09e3..4aded71 100644 --- a/app/Model/framework/networkmanager.h +++ b/app/Model/framework/networkmanager.h @@ -31,7 +31,7 @@ class NetworkManager : public QObject Q_ENUMS(ServerStatus) // Constructors - explicit NetworkManager(QObject* parent=nullptr); + NetworkManager(QObject* parent=nullptr); // Getters diff --git a/app/Model/framework/networktask.h b/app/Model/framework/networktask.h index d2f6c37..f3b542e 100644 --- a/app/Model/framework/networktask.h +++ b/app/Model/framework/networktask.h @@ -30,7 +30,7 @@ class NetworkTask : public QObject Q_ENUMS(TaskType) // Constructors - explicit NetworkTask(QObject* parent=nullptr); + NetworkTask(QObject* parent=nullptr); private: diff --git a/app/Model/framework/regovarresource.cpp b/app/Model/framework/regovarresource.cpp index 9509f10..b12ca3b 100644 --- a/app/Model/framework/regovarresource.cpp +++ b/app/Model/framework/regovarresource.cpp @@ -11,7 +11,7 @@ void RegovarResource::updateSearchField() } //! Set model with provided json data -bool RegovarResource::loadJson(QJsonObject) +bool RegovarResource::loadJson(QJsonObject, bool) { qDebug() << "Warning: calling default implementation of RegovarResource::loadJson(QJsonObject). Nothing done."; return false; diff --git a/app/Model/framework/regovarresource.h b/app/Model/framework/regovarresource.h index 5bd95d7..44ca12c 100644 --- a/app/Model/framework/regovarresource.h +++ b/app/Model/framework/regovarresource.h @@ -13,7 +13,7 @@ class RegovarResource: public QObject public: // Constructors - explicit RegovarResource(QObject* parent=nullptr); + RegovarResource(QObject* parent=nullptr); // Getters inline bool loaded() const { return mLoaded; } @@ -23,7 +23,7 @@ class RegovarResource: public QObject // Methods //! Set model with provided json data - Q_INVOKABLE virtual bool loadJson(QJsonObject json); + Q_INVOKABLE virtual bool loadJson(QJsonObject json, bool full_init=true); //! Export model data into json object Q_INVOKABLE virtual QJsonObject toJson(); //! Save resource information onto server diff --git a/app/Model/framework/treeitem.h b/app/Model/framework/treeitem.h index 1fbf67b..8b83cb6 100644 --- a/app/Model/framework/treeitem.h +++ b/app/Model/framework/treeitem.h @@ -8,9 +8,11 @@ class TreeItem : public QObject { Q_OBJECT + public: - explicit TreeItem(TreeItem* parent=nullptr); - explicit TreeItem(const QHash &data, TreeItem* parent=nullptr); + // Constructors + TreeItem(TreeItem* parent=nullptr); + TreeItem(const QHash &data, TreeItem* parent=nullptr); ~TreeItem(); Q_INVOKABLE void appendChild(TreeItem *child); diff --git a/app/Model/panel/panel.cpp b/app/Model/panel/panel.cpp index 04389d9..9169e8e 100644 --- a/app/Model/panel/panel.cpp +++ b/app/Model/panel/panel.cpp @@ -1,6 +1,7 @@ #include "panel.h" #include "Model/regovar.h" #include "Model/framework/request.h" +#include "Model/phenotype/geneslistmodel.h" @@ -23,7 +24,7 @@ void Panel::updateSearchField() // Load only data for the current panelversion. -bool Panel::loadJson(QJsonObject json) +bool Panel::loadJson(QJsonObject json, bool) { // Loading Panels information @@ -107,6 +108,50 @@ void Panel::addEntry(QJsonObject json) } } +void Panel::addEntriesFromHpo(QString id) +{ + PanelVersion* head = headVersion(); + HpoData* hpo = regovar->phenotypesManager()->getOrCreate(id); + if (head != nullptr) + { + if (hpo->loaded()) + { + importGenesFromHpoData(); + } + else + { + mTmpHpo = hpo; + connect(hpo, &HpoData::dataChanged, this, &Panel::importGenesFromHpoData); + hpo->load(); + } + } + else + { + qDebug() << "WARNING: No panel head version"; + } +} + + +void Panel::importGenesFromHpoData() +{ + PanelVersion* head = headVersion(); + if (mTmpHpo != nullptr) + { + for(int idx=0; idx < mTmpHpo->genes()->rowCount(); idx ++) + { + QJsonObject entry = mTmpHpo->genes()->getAt(idx)->toJson(); + entry.insert("details", tr("From HPO entry: ") + mTmpHpo->label()); + entry.insert("type", "gene"); + head->addEntry(entry); + } + disconnect(mTmpHpo, &HpoData::dataChanged, this, &Panel::importGenesFromHpoData); + mTmpHpo = nullptr; + } +} + + + + void Panel::save() { if (mId.isEmpty()) return; diff --git a/app/Model/panel/panel.h b/app/Model/panel/panel.h index 4b6431c..1cc670e 100644 --- a/app/Model/panel/panel.h +++ b/app/Model/panel/panel.h @@ -5,6 +5,7 @@ #include "Model/framework/genericproxymodel.h" #include "panelversionslistmodel.h" #include "panelversion.h" +#include "Model/phenotype/hpodata.h" class Panel : public RegovarResource { @@ -21,8 +22,8 @@ class Panel : public RegovarResource public: // Constructors - explicit Panel(QObject* parent=nullptr); - explicit Panel(QJsonObject json, QObject* parent=nullptr); + Panel(QObject* parent=nullptr); + Panel(QJsonObject json, QObject* parent=nullptr); // Getters inline QString id() const { return mId; } @@ -40,7 +41,7 @@ class Panel : public RegovarResource // Override ressource methods //! Set model with provided json data - Q_INVOKABLE bool loadJson(QJsonObject json) override; + Q_INVOKABLE bool loadJson(QJsonObject json, bool full_init=true) override; //! Export model data into json object Q_INVOKABLE QJsonObject toJson() override; //! Save panel information onto server @@ -53,6 +54,8 @@ class Panel : public RegovarResource Q_INVOKABLE void reset(Panel* panel=nullptr); //! Add new entry to the current new panel/version Q_INVOKABLE void addEntry(QJsonObject json); + //! Add new (genes) entries to the current new panel/version a HPO disease/phenotype id + Q_INVOKABLE void addEntriesFromHpo(QString id); @@ -63,6 +66,7 @@ class Panel : public RegovarResource public Q_SLOTS: void updateSearchField() override; + void importGenesFromHpoData(); private: @@ -73,6 +77,7 @@ public Q_SLOTS: bool mShared = false; PanelVersionsListModel* mVersions = nullptr; + HpoData* mTmpHpo = nullptr; }; #endif // PANEL_H diff --git a/app/Model/panel/panelentrieslistmodel.h b/app/Model/panel/panelentrieslistmodel.h index 0d9bfbd..5ca4c4c 100644 --- a/app/Model/panel/panelentrieslistmodel.h +++ b/app/Model/panel/panelentrieslistmodel.h @@ -21,7 +21,7 @@ class PanelEntriesListModel: public QAbstractListModel public: // Constructor - explicit PanelEntriesListModel(QObject* parent=nullptr); + PanelEntriesListModel(QObject* parent=nullptr); // Getters inline GenericProxyModel* proxy() const { return mProxy; } diff --git a/app/Model/panel/panelentry.cpp b/app/Model/panel/panelentry.cpp index ece64ec..28605c5 100644 --- a/app/Model/panel/panelentry.cpp +++ b/app/Model/panel/panelentry.cpp @@ -10,7 +10,7 @@ PanelEntry::PanelEntry(QJsonObject json, QObject* parent): PanelEntry(parent) } -bool PanelEntry::loadJson(QJsonObject json) +bool PanelEntry::loadJson(QJsonObject json, bool) { if (json["type"] == "gene") { diff --git a/app/Model/panel/panelentry.h b/app/Model/panel/panelentry.h index a018d8d..a9a6ea1 100644 --- a/app/Model/panel/panelentry.h +++ b/app/Model/panel/panelentry.h @@ -24,7 +24,7 @@ class PanelEntry: public RegovarResource // Override ressource methods //! Set model with provided json data - Q_INVOKABLE bool loadJson(QJsonObject json) override; + Q_INVOKABLE bool loadJson(QJsonObject json, bool full_init=true) override; //! Export model data into json object Q_INVOKABLE QJsonObject toJson() override; diff --git a/app/Model/panel/panelslistmodel.cpp b/app/Model/panel/panelslistmodel.cpp new file mode 100644 index 0000000..6ea7e48 --- /dev/null +++ b/app/Model/panel/panelslistmodel.cpp @@ -0,0 +1,120 @@ +#include "panelslistmodel.h" +#include "Model/regovar.h" + +PanelsListModel::PanelsListModel(QObject* parent): QAbstractListModel(parent) +{ + mProxy = new GenericProxyModel(this); + mProxy->setSourceModel(this); + mProxy->setFilterRole(SearchField); + mProxy->setSortRole(Name); +} + + +void PanelsListModel::clear() +{ + beginResetModel(); + mPanels.clear(); + endResetModel(); + emit countChanged(); +} + +bool PanelsListModel::loadJson(QJsonArray json) +{ + beginResetModel(); + mPanels.clear(); + for(const QJsonValue& val: json) + { + PanelVersion* panel = regovar->panelsManager()->getPanelVersion(val.toString()); + if (panel != nullptr) + { + mPanels.append(panel); + } + } + endResetModel(); + emit countChanged(); + return true; +} + +bool PanelsListModel::append(PanelVersion* panel) +{ + if (panel != nullptr && !mPanels.contains(panel)) + { + beginInsertRows(QModelIndex(), rowCount(), rowCount()); + mPanels.append(panel); + endInsertRows(); + emit countChanged(); + return true; + } + return false; +} + +bool PanelsListModel::remove(PanelVersion* panel) +{ + if (mPanels.contains(panel)) + { + int pos = mPanels.indexOf(panel); + beginRemoveRows(QModelIndex(), pos, pos); + mPanels.removeAll(panel); + endRemoveRows(); + emit countChanged(); + return true; + } + return false; +} + +PanelVersion* PanelsListModel::getAt(int idx) +{ + if (idx >= 0 && idx <= mPanels.count()) + { + return mPanels[idx]; + } + return nullptr; +} + +QString PanelsListModel::join(QString separator) +{ + QString result; + for(PanelVersion* panel: mPanels) + { + result += panel->fullname() + separator; + } + return result.mid(0, result.length() - separator.length()); +} + + +int PanelsListModel::rowCount(const QModelIndex&) const +{ + return mPanels.count(); +} + +QVariant PanelsListModel::data(const QModelIndex& index, int role) const +{ + if (index.row() < 0 || index.row() >= mPanels.count()) + return QVariant(); + + const PanelVersion* panel = mPanels[index.row()]; + if (role == Name || role == Qt::DisplayRole) + return panel->fullname(); + else if (role == Id) + return panel->id(); + else if (role == Comment) + return panel->comment(); + else if (role == CreationDate) + return panel->createDate().toString("yyyy-MM-dd HH:mm"); + else if (role == SearchField) + return panel->searchField(); + + return QVariant(); +} + +QHash PanelsListModel::roleNames() const +{ + QHash roles; + roles[Id] = "id"; + roles[Name] = "name"; + roles[Comment] = "comment"; + roles[CreationDate] = "creationDate"; + roles[SearchField] = "searchField"; + return roles; +} + diff --git a/app/Model/panel/panelslistmodel.h b/app/Model/panel/panelslistmodel.h new file mode 100644 index 0000000..6988414 --- /dev/null +++ b/app/Model/panel/panelslistmodel.h @@ -0,0 +1,61 @@ +#ifndef PANELSLISTMODEL_H +#define PANELSLISTMODEL_H + +#include +#include "Model/framework/genericproxymodel.h" +#include "panelversion.h" + +class PanelsListModel: public QAbstractListModel +{ + enum Roles + { + Id = Qt::UserRole + 1, + Name, + Comment, + CreationDate, + SearchField + }; + + Q_OBJECT + Q_PROPERTY(int count READ rowCount NOTIFY countChanged) + Q_PROPERTY(GenericProxyModel* proxy READ proxy NOTIFY neverChanged) + + +public: + // Constructor + PanelsListModel(QObject* parent=nullptr); + + // Getters + inline GenericProxyModel* proxy() const { return mProxy; } + + // Methods + //! Remove all entries of the list + Q_INVOKABLE void clear(); + //! Load phenotype list from list of json + Q_INVOKABLE bool loadJson(QJsonArray json); + //! Add the provided gene to the list if not already contains + Q_INVOKABLE bool append(PanelVersion* panel); + //! Remove a gene from the list if possible + Q_INVOKABLE bool remove(PanelVersion* panel); + //! Return entry at the requested position in the list + Q_INVOKABLE PanelVersion* getAt(int idx); + //! Joins all the string list's strings into a single string with each element separated by the given separator (which can be an empty string). + Q_INVOKABLE QString join(QString separator); + + // QAbstractListModel methods + int rowCount(const QModelIndex& parent = QModelIndex()) const; + QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const; + QHash roleNames() const; + + +Q_SIGNALS: + void neverChanged(); + void countChanged(); + + +private: + QList mPanels; + GenericProxyModel* mProxy = nullptr; +}; + +#endif // PANELSLISTMODEL_H diff --git a/app/Model/panel/panelsmanager.cpp b/app/Model/panel/panelsmanager.cpp index 246f92f..72aa84d 100644 --- a/app/Model/panel/panelsmanager.cpp +++ b/app/Model/panel/panelsmanager.cpp @@ -6,7 +6,8 @@ PanelsManager::PanelsManager(QObject* parent) : QObject(parent) { mNewPanel = new Panel(this); mNewPanel->versions()->addVersion(new PanelVersion(mNewPanel)); - mPanelsTree = new PanelsTreeModel(); + mPanelsTree = new PanelsTreeModel(this); + mPanelsList = new PanelsListModel(this); mProxy = new GenericProxyModel(this); mProxy->setSourceModel(mPanelsTree); @@ -39,6 +40,7 @@ Panel* PanelsManager::getOrCreatePanel(QString id) // else create new empty panel Panel* newPanel = new Panel(this); mPanels.insert(id, newPanel); + mPanelsList->append(newPanel->headVersion()); return newPanel; } @@ -150,24 +152,14 @@ bool PanelsManager::loadJson(QJsonArray data) { // Refreshing treeModel with provided json will Create/update internal collection of panel mPanelsTree->refresh(data); - // Refresh public model used by QML - updatePanelsLists(); - - return true; -} - - - -void PanelsManager::updatePanelsLists() -{ - mPanelsList.clear(); + // Update list of panel + mPanelsList->clear(); for(Panel* panel: mPanels.values()) { - mPanelsList.append(panel); + mPanelsList->append(panel->headVersion()); } - - emit panelsChanged(); + return true; } diff --git a/app/Model/panel/panelsmanager.h b/app/Model/panel/panelsmanager.h index 8ce097e..877ff00 100644 --- a/app/Model/panel/panelsmanager.h +++ b/app/Model/panel/panelsmanager.h @@ -4,12 +4,13 @@ #include #include "panel.h" #include "panelstreemodel.h" +#include "panelslistmodel.h" #include "Model/framework/genericproxymodel.h" class PanelsManager : public QObject { Q_OBJECT - Q_PROPERTY(QList panels READ panels NOTIFY panelsChanged) + Q_PROPERTY(PanelsListModel* panels READ panels NOTIFY panelsChanged) Q_PROPERTY(PanelsTreeModel* panelsTree READ panelsTree NOTIFY panelsChanged) Q_PROPERTY(Panel* newPanel READ newPanel NOTIFY newPanelChanged) Q_PROPERTY(GenericProxyModel* proxy READ proxy NOTIFY neverChanged) @@ -17,10 +18,10 @@ class PanelsManager : public QObject public: // Constructors - explicit PanelsManager(QObject* parent=nullptr); + PanelsManager(QObject* parent=nullptr); // Getters - inline QList panels() const { return mPanelsList; } + inline PanelsListModel* panels() const { return mPanelsList; } inline Panel* newPanel() const { return mNewPanel; } inline PanelsTreeModel* panelsTree() const { return mPanelsTree; } inline GenericProxyModel* proxy() const { return mProxy; } @@ -48,16 +49,12 @@ class PanelsManager : public QObject QHash mPanels; //! Model for Wizard data when creating new panel Panel* mNewPanel = nullptr; - //! List of all panels - QList mPanelsList; + //! List of all "root" panels + PanelsListModel* mPanelsList = nullptr; //! Treemodel of Panels list. PanelsTreeModel* mPanelsTree = nullptr; //! The QSortFilterProxyModel to use by tree view to browse panel/version of the manager GenericProxyModel* mProxy = nullptr; - - // Methods - //! Refresh list of panel and tree according to the internal model (called by public refresh method) - void updatePanelsLists(); }; #endif // PANELSMANAGER_H diff --git a/app/Model/panel/panelstreemodel.h b/app/Model/panel/panelstreemodel.h index 2e73cc8..05b999d 100644 --- a/app/Model/panel/panelstreemodel.h +++ b/app/Model/panel/panelstreemodel.h @@ -22,7 +22,7 @@ class PanelsTreeModel : public TreeModel }; // Constructor - explicit PanelsTreeModel(QObject* parent=nullptr); + PanelsTreeModel(QObject* parent=nullptr); // Getters inline bool isLoading() { return mIsLoading; } diff --git a/app/Model/panel/panelversion.cpp b/app/Model/panel/panelversion.cpp index 5ff1b5e..40d1a7f 100644 --- a/app/Model/panel/panelversion.cpp +++ b/app/Model/panel/panelversion.cpp @@ -44,7 +44,7 @@ void PanelVersion::emitEntriesChanged() //! Set model with provided json data -bool PanelVersion::loadJson(QJsonObject json) +bool PanelVersion::loadJson(QJsonObject json, bool) { // Load version information mId = json["id"].toString(); @@ -93,9 +93,9 @@ void PanelVersion::save() }); } //! Load Subject information from server -void PanelVersion::load(bool forceRefresh) +void PanelVersion::load(bool) { - + // TODO ? } // Methods diff --git a/app/Model/panel/panelversion.h b/app/Model/panel/panelversion.h index 7b147c9..89a4258 100644 --- a/app/Model/panel/panelversion.h +++ b/app/Model/panel/panelversion.h @@ -21,8 +21,8 @@ class PanelVersion : public RegovarResource public: // Constructors - explicit PanelVersion(Panel* rootPanel=nullptr); - explicit PanelVersion(Panel* rootPanel, QJsonObject json); + PanelVersion(Panel* rootPanel=nullptr); + PanelVersion(Panel* rootPanel, QJsonObject json); // Getters inline QString id() const { return mId; } @@ -41,7 +41,7 @@ class PanelVersion : public RegovarResource // Override ressource methods //! Set model with provided json data - Q_INVOKABLE bool loadJson(QJsonObject json) override; + Q_INVOKABLE bool loadJson(QJsonObject json, bool full_init=true) override; //! Load panel version information from server Q_INVOKABLE void load(bool forceRefresh=true) override; //! Export model data into json object diff --git a/app/Model/panel/panelversionslistmodel.h b/app/Model/panel/panelversionslistmodel.h index 8066fd8..3600f72 100644 --- a/app/Model/panel/panelversionslistmodel.h +++ b/app/Model/panel/panelversionslistmodel.h @@ -26,7 +26,7 @@ class PanelVersionsListModel: public QAbstractListModel public: // Constructor - explicit PanelVersionsListModel(Panel* root); + PanelVersionsListModel(Panel* root); // Getters inline GenericProxyModel* proxy() const { return mProxy; } diff --git a/app/Model/phenotype/disease.cpp b/app/Model/phenotype/disease.cpp index 42b75fe..c7278fe 100644 --- a/app/Model/phenotype/disease.cpp +++ b/app/Model/phenotype/disease.cpp @@ -1,4 +1,6 @@ #include "disease.h" +#include "geneslistmodel.h" +#include "Model/subject/subjectslistmodel.h" Disease::Disease(QObject* parent) : HpoData(parent) @@ -21,7 +23,7 @@ void Disease::updateSearchField() // TODO: add genes, diseases label } -bool Disease::loadJson(QJsonObject json) +bool Disease::loadJson(QJsonObject json, bool) { mId = json["id"].toString(); mPhenotypes->setDiseaseId(mId); @@ -65,11 +67,14 @@ bool Disease::loadJson(QJsonObject json) mDecipher = json["decipher"].toObject(); mLoaded = true; } - if (json.contains("phenotypes")) { mPhenotypes->loadJson(json["phenotypes"].toArray()); } + if (json.contains("subjects")) + { + mSubjects->loadJson(json["subjects"].toArray()); + } emit dataChanged(); return true; diff --git a/app/Model/phenotype/disease.h b/app/Model/phenotype/disease.h index 6c0a318..3e90fb3 100644 --- a/app/Model/phenotype/disease.h +++ b/app/Model/phenotype/disease.h @@ -16,8 +16,8 @@ class Disease: public HpoData public: // Constructor - explicit Disease(QObject* parent = nullptr); - explicit Disease(QString hpoId, QObject* parent = nullptr); + Disease(QObject* parent = nullptr); + Disease(QString hpoId, QObject* parent = nullptr); // Getters inline QJsonObject omim() const { return mOmim; } @@ -26,12 +26,13 @@ class Disease: public HpoData inline PhenotypesListModel* phenotypes() const { return mPhenotypes; } - // HpoData abstracts methods overriden - Q_INVOKABLE bool loadJson(QJsonObject json); + // Methods + //! Set model with provided json data + Q_INVOKABLE bool loadJson(QJsonObject json, bool full_init=true) override; public Q_SLOTS: - virtual void updateSearchField(); + void updateSearchField() override; private: diff --git a/app/Model/phenotype/diseaseslistmodel.h b/app/Model/phenotype/diseaseslistmodel.h index e26c679..22bc539 100644 --- a/app/Model/phenotype/diseaseslistmodel.h +++ b/app/Model/phenotype/diseaseslistmodel.h @@ -22,7 +22,7 @@ class DiseasesListModel: public QAbstractListModel public: // Constructor - explicit DiseasesListModel(QObject* parent=nullptr); + DiseasesListModel(QObject* parent=nullptr); // Getters inline GenericProxyModel* proxy() const { return mProxy; } diff --git a/app/Model/phenotype/gene.cpp b/app/Model/phenotype/gene.cpp index a56f18e..7c5cb4c 100644 --- a/app/Model/phenotype/gene.cpp +++ b/app/Model/phenotype/gene.cpp @@ -7,6 +7,10 @@ Gene::Gene(QObject* parent): RegovarResource(parent) { + mPhenotypes = new PhenotypesListModel(this); + mDiseases = new DiseasesListModel(this); + mPubmed = new JsonListModel(this); + mPanels = new PanelsListModel(this); } Gene::Gene(QJsonObject json, QObject* parent): Gene(parent) { @@ -24,16 +28,54 @@ void Gene::updateSearchField() } -bool Gene::loadJson(QJsonObject json) +bool Gene::loadJson(QJsonObject json, bool) { // Load gene information mSymbol = json["symbol"].toString(); mJson = json; // Panels + if (json.contains("panels")) + { + mPanels->clear(); + for(const QJsonValue& val: json["panels"].toArray()) + { + mPanels->append(regovar->panelsManager()->getPanelVersion(val.toString())); + } + } + // HPO related - // - Diseases - // - Phenotypes + if (json.contains("hpo")) + { + QJsonObject hpo = json["hpo"].toObject(); + mPhenotypes->clear(); + for(const QJsonValue& val: hpo["phenotypes"].toArray()) + { + QJsonObject pheno = val.toObject(); + Phenotype* p = (Phenotype*) regovar->phenotypesManager()->getOrCreate(pheno["id"].toString()); + p->loadJson(pheno); + mPhenotypes->append(p); + } + mDiseases->clear(); + for(const QJsonValue& val: hpo["diseases"].toArray()) + { + QJsonObject disea = val.toObject(); + Disease* d = (Disease*) regovar->phenotypesManager()->getOrCreate(disea["id"].toString()); + d->loadJson(disea); + mDiseases->append(d); + } + } + + // Pubmed + if (json.contains("pubmed")) + { + mPubmed->clear(); + for(const QJsonValue& val: json["pubmed"].toArray()) + { + mPubmed->append(val.toObject()); + } + } + updateSearchField(); emit dataChanged(); return true; diff --git a/app/Model/phenotype/gene.h b/app/Model/phenotype/gene.h index 5903230..aecb8c2 100644 --- a/app/Model/phenotype/gene.h +++ b/app/Model/phenotype/gene.h @@ -3,6 +3,10 @@ #include #include "Model/framework/regovarresource.h" +#include "Model/framework/jsonlistmodel.h" +#include "phenotypeslistmodel.h" +#include "diseaseslistmodel.h" +#include "Model/panel/panelslistmodel.h" class Gene: public RegovarResource { @@ -10,6 +14,11 @@ class Gene: public RegovarResource Q_PROPERTY(QString id READ id NOTIFY dataChanged) Q_PROPERTY(QString symbol READ symbol NOTIFY dataChanged) Q_PROPERTY(QJsonObject json READ json NOTIFY dataChanged) + Q_PROPERTY(PhenotypesListModel* phenotypes READ phenotypes NOTIFY dataChanged) + Q_PROPERTY(DiseasesListModel* diseases READ diseases NOTIFY dataChanged) + Q_PROPERTY(JsonListModel* pubmed READ pubmed NOTIFY dataChanged) + Q_PROPERTY(PanelsListModel* panels READ panels NOTIFY dataChanged) + public: @@ -22,10 +31,14 @@ class Gene: public RegovarResource inline QString id() const { return mHgncId; } inline QString symbol() const { return mSymbol; } inline QJsonObject json() const { return mJson; } + inline PhenotypesListModel* phenotypes() const { return mPhenotypes; } + inline DiseasesListModel* diseases() const { return mDiseases; } + inline JsonListModel* pubmed() const { return mPubmed; } + inline PanelsListModel* panels() const { return mPanels; } // Override ressource methods //! Set model with provided json data - Q_INVOKABLE bool loadJson(QJsonObject json) override; + Q_INVOKABLE bool loadJson(QJsonObject json, bool full_init=true) override; //! Export model data into json object Q_INVOKABLE QJsonObject toJson() override; //! Load resource information from server @@ -39,6 +52,10 @@ public Q_SLOTS: QString mHgncId; QString mSymbol; QJsonObject mJson; + PhenotypesListModel* mPhenotypes = nullptr; + DiseasesListModel* mDiseases = nullptr; + JsonListModel* mPubmed = nullptr; + PanelsListModel* mPanels = nullptr; }; #endif // GENE_H diff --git a/app/Model/phenotype/geneslistmodel.h b/app/Model/phenotype/geneslistmodel.h index c5538de..044da8c 100644 --- a/app/Model/phenotype/geneslistmodel.h +++ b/app/Model/phenotype/geneslistmodel.h @@ -22,7 +22,7 @@ class GenesListModel: public QAbstractListModel public: // Constructor - explicit GenesListModel(QObject* parent=nullptr); + GenesListModel(QObject* parent=nullptr); // Getters inline GenericProxyModel* proxy() const { return mProxy; } diff --git a/app/Model/phenotype/hpodata.cpp b/app/Model/phenotype/hpodata.cpp index 75fca7f..2146d09 100644 --- a/app/Model/phenotype/hpodata.cpp +++ b/app/Model/phenotype/hpodata.cpp @@ -1,22 +1,24 @@ #include "hpodata.h" #include "Model/framework/request.h" #include "Model/regovar.h" +#include "Model/subject/subjectslistmodel.h" +#include "geneslistmodel.h" -HpoData::HpoData(QObject* parent) : QObject(parent) +HpoData::HpoData(QObject* parent) : RegovarResource(parent) { mGenes = new GenesListModel(this); mSubjects = new SubjectsListModel(this); } -bool HpoData::loadJson(QJsonObject) +bool HpoData::loadJson(QJsonObject, bool) { return false; } -bool HpoData::load(bool forceRefresh) +void HpoData::load(bool) { Request* req = Request::get(QString("/search/phenotype/%1").arg(mId)); connect(req, &Request::responseReceived, [this, req](bool success, const QJsonObject& json) @@ -32,5 +34,4 @@ bool HpoData::load(bool forceRefresh) } req->deleteLater(); }); - return true; } diff --git a/app/Model/phenotype/hpodata.h b/app/Model/phenotype/hpodata.h index 6dab72b..34bdb75 100644 --- a/app/Model/phenotype/hpodata.h +++ b/app/Model/phenotype/hpodata.h @@ -2,11 +2,11 @@ #define HPODATA_H #include -#include "geneslistmodel.h" -#include "Model/subject/subjectslistmodel.h" +#include "Model/framework/regovarresource.h" class SubjectsListModel; -class HpoData: public QObject +class GenesListModel; +class HpoData: public RegovarResource { Q_OBJECT Q_PROPERTY(QString id READ id NOTIFY dataChanged) @@ -20,12 +20,9 @@ class HpoData: public QObject Q_PROPERTY(QString category READ category NOTIFY dataChanged) Q_PROPERTY(QJsonObject meta READ meta NOTIFY dataChanged) - Q_PROPERTY(bool loaded READ loaded NOTIFY dataChanged) - Q_PROPERTY(QString searchField READ searchField NOTIFY dataChanged) - public: // Constructor - explicit HpoData(QObject* parent = nullptr); + HpoData(QObject* parent = nullptr); // Getters inline QString id() const { return mId; } @@ -38,17 +35,13 @@ class HpoData: public QObject inline QJsonObject diseasesFreq() const { return mDiseasesFreq; } inline QString category() const { return mCategory; } inline QJsonObject meta() const { return mMeta; } - inline bool loaded() const { return mLoaded; } - inline QString searchField() const { return mSearchField; } // Methods //! Set model with provided json data - Q_INVOKABLE virtual bool loadJson(QJsonObject json); - Q_INVOKABLE virtual bool load(bool forceRefresh=false); - + Q_INVOKABLE bool loadJson(QJsonObject json, bool full_init=true) override; + //! Load Subject information from server + Q_INVOKABLE void load(bool forceRefresh=true) override; -Q_SIGNALS: - void dataChanged(); protected: @@ -62,8 +55,6 @@ class HpoData: public QObject QJsonObject mGenesFreq; QJsonObject mDiseasesFreq; QJsonObject mMeta; - bool mLoaded = false; - QString mSearchField; }; #endif // HPODATA_H diff --git a/app/Model/phenotype/hpodatalistmodel.cpp b/app/Model/phenotype/hpodatalistmodel.cpp index d887e78..323fb99 100644 --- a/app/Model/phenotype/hpodatalistmodel.cpp +++ b/app/Model/phenotype/hpodatalistmodel.cpp @@ -1,5 +1,8 @@ #include "hpodatalistmodel.h" #include "Model/regovar.h" +#include "geneslistmodel.h" +#include "hpodata.h" +#include "Model/subject/subject.h" HpoDataListModel::HpoDataListModel(QObject* parent): QAbstractListModel(parent) { diff --git a/app/Model/phenotype/hpodatalistmodel.h b/app/Model/phenotype/hpodatalistmodel.h index e3bfbb1..15a0f43 100644 --- a/app/Model/phenotype/hpodatalistmodel.h +++ b/app/Model/phenotype/hpodatalistmodel.h @@ -2,9 +2,7 @@ #define HPODATALISTMODEL_H #include -#include "hpodata.h" #include "Model/framework/genericproxymodel.h" -#include "Model/subject/subject.h" class HpoData; class Subject; @@ -31,8 +29,8 @@ class HpoDataListModel: public QAbstractListModel public: // Constructor - explicit HpoDataListModel(QObject* parent=nullptr); - explicit HpoDataListModel(int subjectId, QObject* parent=nullptr); + HpoDataListModel(QObject* parent=nullptr); + HpoDataListModel(int subjectId, QObject* parent=nullptr); // Getters Subject* subject() const; diff --git a/app/Model/phenotype/phenotype.cpp b/app/Model/phenotype/phenotype.cpp index 8e51f59..e41ef82 100644 --- a/app/Model/phenotype/phenotype.cpp +++ b/app/Model/phenotype/phenotype.cpp @@ -1,5 +1,6 @@ #include "phenotype.h" #include "Model/regovar.h" +#include "Model/phenotype/geneslistmodel.h" Phenotype::Phenotype(QObject* parent) : HpoData(parent) @@ -22,7 +23,7 @@ void Phenotype::updateSearchField() // TODO: add genes, diseases label } -bool Phenotype::loadJson(QJsonObject json) +bool Phenotype::loadJson(QJsonObject json, bool) { bool fullLoaded = false; mId = json["id"].toString(); diff --git a/app/Model/phenotype/phenotype.h b/app/Model/phenotype/phenotype.h index 3e2d3c0..b018f11 100644 --- a/app/Model/phenotype/phenotype.h +++ b/app/Model/phenotype/phenotype.h @@ -23,8 +23,8 @@ class Phenotype : public HpoData public: // Constructor - explicit Phenotype(QObject* parent = nullptr); - explicit Phenotype(QString hpo_id, QObject* parent = nullptr); + Phenotype(QObject* parent = nullptr); + Phenotype(QString hpo_id, QObject* parent = nullptr); // Getters inline QString definition() const { return mDefinition; } @@ -32,15 +32,15 @@ class Phenotype : public HpoData inline HpoDataListModel* childs() const { return mChilds; } inline DiseasesListModel* diseases() const { return mDiseases; } - // HpoData abstracts methods overriden - //! Load phenotype from json - Q_INVOKABLE bool loadJson(QJsonObject json); + // Methods + //! Set model with provided json data + Q_INVOKABLE bool loadJson(QJsonObject json, bool full_init=true) override; //! Return the phenotype qualifiers (as human readable string) for the requested disease Q_INVOKABLE QString qualifier(QString diseaseId) const; public Q_SLOTS: - virtual void updateSearchField(); + void updateSearchField() override; private: diff --git a/app/Model/phenotype/phenotypesmanager.h b/app/Model/phenotype/phenotypesmanager.h index 0dd8b54..e9b2dc3 100644 --- a/app/Model/phenotype/phenotypesmanager.h +++ b/app/Model/phenotype/phenotypesmanager.h @@ -13,7 +13,7 @@ class PhenotypesManager : public QObject public: // Constructor - explicit PhenotypesManager(QObject* parent=nullptr); + PhenotypesManager(QObject* parent=nullptr); // Getters inline HpoDataListModel* searchResults() const { return mSearchResults; } diff --git a/app/Model/pipeline/pipeline.cpp b/app/Model/pipeline/pipeline.cpp index 0dd2ef3..849897e 100644 --- a/app/Model/pipeline/pipeline.cpp +++ b/app/Model/pipeline/pipeline.cpp @@ -3,7 +3,7 @@ #include "Model/regovar.h" -Pipeline::Pipeline(QObject* parent) : QObject(parent) +Pipeline::Pipeline(QObject* parent) : RegovarResource(parent) { mConfigForm = new DynamicFormModel(this); connect(this, &Pipeline::dataChanged, this, &Pipeline::updateSearchField); @@ -28,7 +28,7 @@ void Pipeline::updateSearchField() -bool Pipeline::loadJson(QJsonObject json) +bool Pipeline::loadJson(QJsonObject json, bool) { mId = json["id"].toInt(); mType = json["type"].toString(); diff --git a/app/Model/pipeline/pipeline.h b/app/Model/pipeline/pipeline.h index 942e4d4..b653198 100644 --- a/app/Model/pipeline/pipeline.h +++ b/app/Model/pipeline/pipeline.h @@ -2,11 +2,12 @@ #define PIPELINE_H #include +#include "Model/framework/regovarresource.h" #include "Model/framework/dynamicformmodel.h" class DynamicFormModel; -class Pipeline : public QObject +class Pipeline : public RegovarResource { Q_OBJECT Q_PROPERTY(int id READ id NOTIFY dataChanged) @@ -33,9 +34,9 @@ class Pipeline : public QObject public: // Constructor - explicit Pipeline(QObject* parent=nullptr); - explicit Pipeline(int id, QObject* parent=nullptr); - explicit Pipeline(QJsonObject json); + Pipeline(QObject* parent=nullptr); + Pipeline(int id, QObject* parent=nullptr); + Pipeline(QJsonObject json); // Getters inline int id() const { return mId; } @@ -53,7 +54,6 @@ class Pipeline : public QObject inline QString license() const { return mLicense.toString(); } inline QString readme() const { return mReadme.toString(); } inline QString manifest() const { return mManifest.toString(); } - inline QString searchField() const { return mSearchField; } inline DynamicFormModel* configForm() const { return mConfigForm; } Q_INVOKABLE inline QJsonObject manifestJson() const { return mManifestJson; } @@ -64,26 +64,22 @@ class Pipeline : public QObject // Methods //! Set model with provided json data - Q_INVOKABLE bool loadJson(QJsonObject json); + Q_INVOKABLE bool loadJson(QJsonObject json, bool full_init=true) override; //! Export model data into json object - Q_INVOKABLE QJsonObject toJson(); + Q_INVOKABLE QJsonObject toJson() override; + //! Load pipeline information from server + Q_INVOKABLE void load(bool forceRefresh=true) override; //! Ask the server to install this pipeline Q_INVOKABLE void install(); - //! Load pipeline information from server - Q_INVOKABLE void load(bool forceRefresh=true); Q_SIGNALS: void neverChanged(); - void dataChanged(); public Q_SLOTS: - void updateSearchField(); + void updateSearchField() override; private: - bool mLoaded = false; - QDateTime mLastInternalLoad = QDateTime::currentDateTime(); - int mId=-1; bool mStarred = false; QString mName; @@ -101,7 +97,6 @@ public Q_SLOTS: QUrl mLicense; QUrl mReadme; QUrl mManifest; - QString mSearchField; QJsonObject mManifestJson; QJsonObject mFormJson; diff --git a/app/Model/pipeline/pipelineslistmodel.h b/app/Model/pipeline/pipelineslistmodel.h index 6e6bdf3..1b76aa4 100644 --- a/app/Model/pipeline/pipelineslistmodel.h +++ b/app/Model/pipeline/pipelineslistmodel.h @@ -24,7 +24,8 @@ class PipelinesListModel : public QAbstractListModel Q_PROPERTY(GenericProxyModel* proxy READ proxy NOTIFY neverChanged) public: - explicit PipelinesListModel(QObject* parent=nullptr); + // Constructor + PipelinesListModel(QObject* parent=nullptr); // Getters inline GenericProxyModel* proxy() const { return mProxy; } diff --git a/app/Model/pipeline/pipelinesmanager.h b/app/Model/pipeline/pipelinesmanager.h index 4ea8393..bd0440c 100644 --- a/app/Model/pipeline/pipelinesmanager.h +++ b/app/Model/pipeline/pipelinesmanager.h @@ -12,7 +12,8 @@ class PipelinesManager : public QObject Q_PROPERTY(PipelinesListModel* intalledPipes READ intalledPipes NOTIFY intalledPipesChanged) public: - explicit PipelinesManager(QObject* parent=nullptr); + // Constructor + PipelinesManager(QObject* parent=nullptr); // Getters inline PipelinesListModel* availablePipes() const { return mAvailablePipes; } diff --git a/app/Model/project/project.cpp b/app/Model/project/project.cpp index 7eac4c7..f9c0d77 100644 --- a/app/Model/project/project.cpp +++ b/app/Model/project/project.cpp @@ -2,20 +2,23 @@ #include "Model/regovar.h" #include "Model/framework/request.h" #include "Model/analysis/filtering/filteringanalysis.h" +#include "Model/analysis/pipeline/pipelineanalysis.h" -Project::Project(QObject* parent) : QObject(parent) +Project::Project(QObject* parent) : RegovarResource(parent) { + mAnalyses = new AnalysesListModel(this); + mSubjects = new SubjectsListModel(this); } -Project::Project(QJsonObject json, QObject* parent) : QObject(parent) +Project::Project(QJsonObject json, QObject* parent) : Project(parent) { loadJson(json); } -Project::Project(int id, QObject* parent) : QObject(parent) +Project::Project(int id, QObject* parent) : Project(parent) { mId = id; } @@ -24,7 +27,7 @@ Project::Project(int id, QObject* parent) : QObject(parent) -bool Project::loadJson(QJsonObject json) +bool Project::loadJson(QJsonObject json, bool) { mId = json["id"].toInt(); if(json.keys().contains("fullpath")) @@ -39,25 +42,60 @@ bool Project::loadJson(QJsonObject json) mName = json["name"].toString(); // Analyses - mAnalyses.clear(); - for (const QJsonValue& jsonVal: json["analyses"].toArray()) + if (json.contains("analyses")) { - QJsonObject aJson = jsonVal.toObject(); - int id = aJson["id"].toInt(); - FilteringAnalysis* analysis = regovar->analysesManager()->getOrCreateFilteringAnalysis(id); - analysis->loadJson(jsonVal.toObject(), false); - mAnalyses.append(analysis); + mAnalyses->clear(); + for (const QJsonValue& jsonVal: json["analyses"].toArray()) + { + QJsonObject aJson = jsonVal.toObject(); + FilteringAnalysis* analysis = regovar->analysesManager()->getOrCreateFilteringAnalysis(aJson["id"].toInt()); + analysis->loadJson(aJson, false); + mAnalyses->append(analysis); + } + for (const QJsonValue& jsonVal: json["jobs"].toArray()) + { + QJsonObject jJson = jsonVal.toObject(); + PipelineAnalysis* analysis = regovar->analysesManager()->getOrCreatePipelineAnalysis(jJson["id"].toInt()); + analysis->loadJson(jJson, false); + mAnalyses->append(analysis); + } + } + else if (json.contains("analyses_ids")) + { + mAnalyses->clear(); + for (const QJsonValue& aId: json["analyses_ids"].toArray()) + { + FilteringAnalysis* analysis = regovar->analysesManager()->getOrCreateFilteringAnalysis(aId.toInt()); + mAnalyses->append(analysis); + } + for (const QJsonValue& aId: json["jobs_ids"].toArray()) + { + PipelineAnalysis* analysis = regovar->analysesManager()->getOrCreatePipelineAnalysis(aId.toInt()); + mAnalyses->append(analysis); + } } + // Subjects - mSubjects.clear(); - for (const QJsonValue& jsonVal: json["subjects"].toArray()) + if (json.contains("subjects")) + { + mSubjects->clear(); + for (const QJsonValue& jsonVal: json["subjects"].toArray()) + { + QJsonObject sJson = jsonVal.toObject(); + Subject* subject = regovar->subjectsManager()->getOrCreateSubject(sJson["id"].toInt()); + subject->loadJson(sJson); + mSubjects->append(subject); + } + } + else if (json.contains("subjects_ids")) { - QJsonObject aJson = jsonVal.toObject(); - int id = aJson["id"].toInt(); - Subject* subject = regovar->subjectsManager()->getOrCreateSubject(id); - subject->loadJson(jsonVal.toObject()); - mSubjects.append(subject); + mAnalyses->clear(); + for (const QJsonValue& sId: json["subjects_ids"].toArray()) + { + Subject* subject = regovar->subjectsManager()->getOrCreateSubject(sId.toInt()); + mSubjects->append(subject); + } } mLoaded = true; @@ -77,20 +115,12 @@ QJsonObject Project::toJson() { result.insert("parent_id", mParent->id()); } - // Analyses - if (mAnalyses.count() > 0) - { - QJsonArray analyses; - for (QObject* o: mAnalyses) - { - FilteringAnalysis* a = qobject_cast(o); - analyses.append(a->id()); - } - result.insert("analyses_ids", analyses); - } - // TODO: Jobs + // TODO: Indicators + // No need to serialize analyses_ids and jobs_ids. + // To update this information you have to update concerned analyses and jobs + return result; } diff --git a/app/Model/project/project.h b/app/Model/project/project.h index 68e0dda..1ad6eb3 100644 --- a/app/Model/project/project.h +++ b/app/Model/project/project.h @@ -2,14 +2,13 @@ #define PROJECT_H #include +#include "Model/framework/regovarresource.h" +#include "Model/analysis/analyseslistmodel.h" +#include "Model/subject/subjectslistmodel.h" -class Project : public QObject +class Project : public RegovarResource { Q_OBJECT - // Regovar resource attributes - Q_PROPERTY(bool loaded READ loaded NOTIFY dataChanged) - Q_PROPERTY(QDateTime createDate READ createDate NOTIFY dataChanged) - Q_PROPERTY(QDateTime updateDate READ updateDate NOTIFY dataChanged) // project attributes Q_PROPERTY(int id READ id) Q_PROPERTY(Project* parent READ parent WRITE setParent NOTIFY dataChanged) @@ -18,22 +17,19 @@ class Project : public QObject Q_PROPERTY(QString comment READ comment WRITE setComment NOTIFY dataChanged) Q_PROPERTY(QString name READ name WRITE setName NOTIFY dataChanged) Q_PROPERTY(QString fullPath READ fullPath NOTIFY dataChanged) - Q_PROPERTY(QList analyses READ analyses NOTIFY dataChanged) - Q_PROPERTY(QList subjects READ subjects NOTIFY dataChanged) + Q_PROPERTY(AnalysesListModel* analyses READ analyses NOTIFY dataChanged) + Q_PROPERTY(SubjectsListModel* subjects READ subjects NOTIFY dataChanged) public: // Constructors - explicit Project(QObject* parent=nullptr); - explicit Project(int id, QObject *parent = nullptr); - explicit Project(QJsonObject json, QObject *parent = nullptr); + Project(QObject* parent=nullptr); + Project(int id, QObject *parent = nullptr); + Project(QJsonObject json, QObject *parent = nullptr); // Getters - inline bool loaded() const { return mLoaded; } - inline QDateTime createDate() const { return mCreateDate; } - inline QDateTime updateDate() const { return mUpdateDate; } inline int id() const { return mId; } inline Project* parent() const { return mParent; } inline bool isSandbox() const { return mIsSandbox; } @@ -41,8 +37,8 @@ class Project : public QObject inline QString name() const { return mName; } inline QString comment() const { return mComment; } inline QString fullPath() const { return mFullPath; } - inline QList analyses() const { return mAnalyses; } - inline QList subjects() const { return mSubjects; } + inline AnalysesListModel* analyses() const { return mAnalyses; } + inline SubjectsListModel* subjects() const { return mSubjects; } // Setters inline void setParent(Project* parent) { mParent = parent; emit dataChanged(); } @@ -52,30 +48,21 @@ class Project : public QObject // Methods //! Set model with provided json data - Q_INVOKABLE bool loadJson(QJsonObject json); + Q_INVOKABLE bool loadJson(QJsonObject json, bool full_init=true) override; //! Export model data into json object - Q_INVOKABLE QJsonObject toJson(); - //! Save subject information onto server - Q_INVOKABLE void save(); - //! Load Subject information from server - Q_INVOKABLE void load(bool forceRefresh=true); + Q_INVOKABLE QJsonObject toJson() override; + //! Save project information onto server + Q_INVOKABLE void save() override; + //! Load project information from server + Q_INVOKABLE void load(bool forceRefresh=true) override; void buildAnalysis(QJsonObject json); void buildEvent(QJsonObject json); -Q_SIGNALS: - void dataChanged(); - - - private: - bool mLoaded = false; - QDateTime mCreateDate; - QDateTime mUpdateDate; - QDateTime mLastInternalLoad = QDateTime::currentDateTime(); // Attributes int mId = -1; bool mIsSandbox = false; @@ -84,8 +71,8 @@ class Project : public QObject Project* mParent = nullptr; QString mComment; QString mName; - QList mAnalyses; - QList mSubjects; + AnalysesListModel* mAnalyses = nullptr; + SubjectsListModel* mSubjects; // Methods inline void setUpdateDate(QDateTime date) { mUpdateDate = date; emit dataChanged(); } diff --git a/app/Model/project/projectsmanager.h b/app/Model/project/projectsmanager.h index c58f785..a28bbc0 100644 --- a/app/Model/project/projectsmanager.h +++ b/app/Model/project/projectsmanager.h @@ -17,7 +17,7 @@ class ProjectsManager : public QObject public: // Constructor - explicit ProjectsManager(QObject* parent = nullptr); + ProjectsManager(QObject* parent = nullptr); // Getters inline QString searchQuery() const { return mSearchQuery; } diff --git a/app/Model/project/projectstreemodel.cpp b/app/Model/project/projectstreemodel.cpp index 1ebadb7..e807c77 100644 --- a/app/Model/project/projectstreemodel.cpp +++ b/app/Model/project/projectstreemodel.cpp @@ -78,13 +78,13 @@ TreeItem* ProjectsTreeModel::newFolderTreeItem(const QJsonObject& data, TreeItem return result; } -TreeItem* ProjectsTreeModel::newAnalysisTreeItem(const int id, TreeItem* parent) +TreeItem* ProjectsTreeModel::newAnalysisTreeItem(const Analysis* analysis, TreeItem* parent) { - FilteringAnalysis* analysis = regovar->analysesManager()->getOrCreateFilteringAnalysis(id); + //FilteringAnalysis* analysis = regovar->analysesManager()->getOrCreateFilteringAnalysis(id); // add columns info to the item QHash columnData; - columnData.insert(Id, id); + columnData.insert(Id, analysis->id()); columnData.insert(Type, analysis->type()); columnData.insert(AnalysisType, analysis->type()); // todo: for pipeline, give the name-version of the pipe columnData.insert(Name, analysis->name()); @@ -116,17 +116,31 @@ void ProjectsTreeModel::setupModelData(QJsonArray data, TreeItem* parent) parent->appendChild(item); // If project, need to build subtree for analyses and jobs - if (p.contains("analyses") && p["analyses"].toArray().count() > 0) + if (p.contains("analyses_ids") && p["analyses_ids"].toArray().count() > 0) { - for (const QJsonValue& id: p["analyses"].toArray()) + for (const QJsonValue& id: p["analyses_ids"].toArray()) { // Create treeview item with column's data and parent item if (!id.isNull()) { - TreeItem* subItem = newAnalysisTreeItem(id.toInt(), item); + Analysis* a = regovar->analysesManager()->getOrCreateFilteringAnalysis(id.toInt()); + TreeItem* subItem = newAnalysisTreeItem(a, item); + item->appendChild(subItem); + } + } + } + if (p.contains("jobs_ids") && p["jobs_ids"].toArray().count() > 0) + { + for (const QJsonValue& id: p["jobs_ids"].toArray()) + { + if (!id.isNull()) + { + Analysis* a = (Analysis*) regovar->analysesManager()->getOrCreatePipelineAnalysis(id.toInt()); + TreeItem* subItem = newAnalysisTreeItem(a, item); item->appendChild(subItem); } } } + } } diff --git a/app/Model/project/projectstreemodel.h b/app/Model/project/projectstreemodel.h index 65e5ad4..3442d1b 100644 --- a/app/Model/project/projectstreemodel.h +++ b/app/Model/project/projectstreemodel.h @@ -2,6 +2,7 @@ #define PROJECTSTREEMODEL_H #include "Model/framework/treemodel.h" +#include "Model/analysis/analysis.h" class ProjectsTreeModel : public TreeModel { @@ -22,7 +23,7 @@ class ProjectsTreeModel : public TreeModel }; // Constructor - explicit ProjectsTreeModel(QObject* parent=nullptr); + ProjectsTreeModel(QObject* parent=nullptr); // Getters inline bool isLoading() { return mIsLoading; } @@ -35,7 +36,7 @@ class ProjectsTreeModel : public TreeModel bool loadJson(QJsonArray json); void setupModelData(QJsonArray data, TreeItem *parent); TreeItem* newFolderTreeItem(const QJsonObject& data, TreeItem* parent); - TreeItem* newAnalysisTreeItem(const int id, TreeItem* parent); + TreeItem* newAnalysisTreeItem(const Analysis* analysis, TreeItem* parent); Q_SIGNALS: void isLoadingChanged(); diff --git a/app/Model/regovar.cpp b/app/Model/regovar.cpp index 9748e12..e4574ea 100644 --- a/app/Model/regovar.cpp +++ b/app/Model/regovar.cpp @@ -144,7 +144,7 @@ void Regovar::loadConfigData() void Regovar::loadWelcomData() { setWelcomIsLoading(true); - Request* req = Request::get(QString("/")); + Request* req = Request::get(QString("/welcome")); connect(req, &Request::responseReceived, [this, req](bool success, const QJsonObject& json) { if (success) @@ -401,7 +401,7 @@ void Regovar::getPipelineInfo(int pipelineId) } -void Regovar::getGeneInfo(QString symbol, int analysisId) +void Regovar::getGeneInfo(QString symbol, int) { Gene* gene = phenotypesManager()->getGene(symbol); gene->load(); @@ -563,7 +563,7 @@ QString Regovar::formatDate(QDateTime date, bool withTime) if (withTime) { - return date.toString("yyyy-MM-dd hh:mm"); + return date.toString("yyyy-MM-dd HH:mm"); } return date.toString("yyyy-MM-dd"); } diff --git a/app/Model/regovar.h b/app/Model/regovar.h index 025a189..67bf1ca 100644 --- a/app/Model/regovar.h +++ b/app/Model/regovar.h @@ -283,7 +283,9 @@ class RegovarInfo: public QObject Q_PROPERTY(QString welcomMessageType READ welcomMessageType NOTIFY configChanged) public: - explicit RegovarInfo(QObject *parent = nullptr); + // Constructor + RegovarInfo(QObject *parent = nullptr); + // Getters inline QString serverVersion() { return mServerVersion; } inline QString clientVersion() { return mClientVersion; } @@ -292,8 +294,10 @@ class RegovarInfo: public QObject inline QJsonObject release() { return mRelease; } inline QString welcomMessage() const { return mWelcomMessage; } inline QString welcomMessageType() const { return mWelcomMessageType; } + // Setters inline void setRelease(QJsonObject release) { mRelease = release; emit configChanged(); } + // Methods bool loadJson(QJsonObject json); diff --git a/app/Model/subject/attribute.h b/app/Model/subject/attribute.h index 8c5aa71..63ab14c 100644 --- a/app/Model/subject/attribute.h +++ b/app/Model/subject/attribute.h @@ -16,9 +16,9 @@ class Attribute : public QObject public: // Constructors - explicit Attribute(QObject* parent = nullptr); - explicit Attribute(QString name); - explicit Attribute(QJsonObject json); + Attribute(QObject* parent = nullptr); + Attribute(QString name); + Attribute(QJsonObject json); // Getters inline QString name() const { return mName; } diff --git a/app/Model/subject/sample.cpp b/app/Model/subject/sample.cpp index bfab04f..75ac4eb 100644 --- a/app/Model/subject/sample.cpp +++ b/app/Model/subject/sample.cpp @@ -4,7 +4,7 @@ -Sample::Sample(QObject *parent) : QObject(parent) +Sample::Sample(QObject *parent) : RegovarResource(parent) { connect(this, &Sample::dataChanged, this, &Sample::updateSearchField); } @@ -44,7 +44,7 @@ void Sample::setStatus(QString status) setStatus(static_cast(meta.keyToValue(status.toStdString().c_str()))); // T_T .... tout ça pour ça .... } -bool Sample::loadJson(QJsonObject json) +bool Sample::loadJson(QJsonObject json, bool) { // load basic data from json mId = json["id"].toInt(); @@ -70,6 +70,10 @@ bool Sample::loadJson(QJsonObject json) File* source = regovar->filesManager()->getOrCreateFile(json["file_id"].toInt()); setSource(source); + if (json.contains("file")) + { + source->loadJson(json["file"].toObject()); + } setSourceUI(source->filenameUI()); // Retrieve subject @@ -99,6 +103,8 @@ void Sample::refreshUIAttributes() statusInfo.insert("label", statusToLabel(mStatus, mLoadingProgress)); statusInfo.insert("progress", mLoadingProgress); setStatusUI(QVariant::fromValue(statusInfo)); + + emit dataChanged(); } diff --git a/app/Model/subject/sample.h b/app/Model/subject/sample.h index f47b929..6d05cdb 100644 --- a/app/Model/subject/sample.h +++ b/app/Model/subject/sample.h @@ -2,18 +2,15 @@ #define SAMPLE_H #include +#include "Model/framework/regovarresource.h" #include "Model/file/file.h" #include "subject.h" #include "reference.h" - -class Sample : public QObject +class Subject; +class Sample : public RegovarResource { Q_OBJECT - // Regovar resource attribute - Q_PROPERTY(bool loaded READ loaded NOTIFY dataChanged) - Q_PROPERTY(QDateTime updateDate READ updateDate NOTIFY dataChanged) - Q_PROPERTY(QDateTime createDate READ createDate NOTIFY dataChanged) // Sample attributes Q_PROPERTY(int id READ id NOTIFY dataChanged) Q_PROPERTY(QString name READ name WRITE setName NOTIFY dataChanged) @@ -30,7 +27,6 @@ class Sample : public QObject Q_PROPERTY(QVariant nameUI READ nameUI WRITE setNameUI NOTIFY dataChanged) Q_PROPERTY(QVariant statusUI READ statusUI WRITE setStatusUI NOTIFY dataChanged) Q_PROPERTY(QVariant sourceUI READ sourceUI WRITE setSourceUI NOTIFY dataChanged) - Q_PROPERTY(QString searchField READ searchField NOTIFY dataChanged) // Property only used client side by the newAnalysis wizard Q_PROPERTY(bool isIndex READ isIndex WRITE setIsIndex NOTIFY dataChanged) Q_PROPERTY(QString sex READ sex WRITE setSex NOTIFY dataChanged) @@ -47,15 +43,12 @@ class Sample : public QObject }; Q_ENUM(SampleStatus) - - explicit Sample(QObject* parent = nullptr); - explicit Sample(int id, QObject* parent = nullptr); - explicit Sample(QJsonObject json, QObject* parent = nullptr); + // Constructors + Sample(QObject* parent = nullptr); + Sample(int id, QObject* parent = nullptr); + Sample(QJsonObject json, QObject* parent = nullptr); // Getters - inline bool loaded() const { return mLoaded; } - inline QDateTime updateDate() const { return mUpdateDate; } - inline QDateTime createDate() const { return mCreateDate; } inline int id() const { return mId; } inline QString name() const { return mName; } inline QString nickname() const { return mNickname.isEmpty() ? mName : mNickname; } @@ -70,7 +63,6 @@ class Sample : public QObject inline QVariant nameUI() const { return mNameUI; } inline QVariant statusUI() const { return mStatusUI; } inline QVariant sourceUI() const { return mSourceUI; } - inline QString searchField() const { return mSearchField; } inline bool isIndex() const { return mIsIndex; } inline QString sex() const { return mSex; } inline QJsonObject stats() const { return mStats; } @@ -94,31 +86,23 @@ class Sample : public QObject // Methods //! Set model with provided json data - Q_INVOKABLE bool loadJson(QJsonObject json); + Q_INVOKABLE bool loadJson(QJsonObject json, bool full_init=true) override; //! Export model data into json object - Q_INVOKABLE QJsonObject toJson(); + Q_INVOKABLE QJsonObject toJson() override; //! Save subject information onto server - Q_INVOKABLE void save(); + Q_INVOKABLE void save() override; //! Load Subject information from server - Q_INVOKABLE void load(bool forceRefresh=true); + Q_INVOKABLE void load(bool forceRefresh=true) override; //! Convert sample status into a string value QString statusToLabel(SampleStatus status, double progress); void refreshUIAttributes(); -Q_SIGNALS: - void dataChanged(); public Q_SLOTS: - void updateSearchField(); + void updateSearchField() override; private: - - bool mLoaded = false; - QDateTime mUpdateDate; - QDateTime mCreateDate; - QDateTime mLastInternalLoad = QDateTime::currentDateTime(); - int mId = -1; QString mName; QString mNickname; @@ -140,7 +124,6 @@ public Q_SLOTS: QVariant mSourceUI; bool mIsIndex = false; QString mSex; - QString mSearchField; }; #endif // SAMPLE_H diff --git a/app/Model/subject/sampleslistmodel.cpp b/app/Model/subject/sampleslistmodel.cpp new file mode 100644 index 0000000..4315975 --- /dev/null +++ b/app/Model/subject/sampleslistmodel.cpp @@ -0,0 +1,126 @@ +#include "sampleslistmodel.h" +#include "Model/regovar.h" + +SamplesListModel::SamplesListModel(QObject* parent): QAbstractListModel(parent) +{ + mProxy = new GenericProxyModel(this); + mProxy->setSourceModel(this); + mProxy->setFilterRole(SearchField); + mProxy->setSortRole(Name); +} + + +void SamplesListModel::clear() +{ + beginResetModel(); + mSamples.clear(); + endResetModel(); + emit countChanged(); +} + + +bool SamplesListModel::loadJson(QJsonArray json) +{ + beginResetModel(); + mSamples.clear(); + for(const QJsonValue& val: json) + { + QJsonObject data = val.toObject(); + Sample* sample = regovar->samplesManager()->getOrCreateSample(data["id"].toInt()); + sample->loadJson(data, false); + mSamples.append(sample); + } + endResetModel(); + emit countChanged(); + return true; +} + + +bool SamplesListModel::append(Sample* sample) +{ + if (sample!= nullptr && !mSamples.contains(sample)) + { + beginInsertRows(QModelIndex(), rowCount(), rowCount()); + mSamples.append(sample); + endInsertRows(); + emit countChanged(); + return true; + } + return false; +} + +bool SamplesListModel::remove(Sample* sample) +{ + if (mSamples.contains(sample)) + { + int pos = mSamples.indexOf(sample); + beginRemoveRows(QModelIndex(), pos, pos); + mSamples.removeAll(sample); + endRemoveRows(); + emit countChanged(); + return true; + } + return false; +} + + +Sample* SamplesListModel::getAt(int idx) +{ + if (idx >=0 && idx= mSamples.count()) + return QVariant(); + + Sample* sample = mSamples[index.row()]; + if (role == Name || role == Qt::DisplayRole) + return sample->name(); + else if (role == Id) + return sample->id(); + else if (role == IsMosaic) + return sample->isMosaic(); + else if (role == Status) + return sample->statusUI(); + else if (role == Comment) + return sample->comment(); + else if (role == Source) + return sample->sourceUI(); + else if (role == Reference) + return sample->reference()->name(); + else if (role == UpdateDate) + return sample->updateDate().toString("yyyy-MM-dd HH:mm"); + else if (role == SearchField) + return sample->searchField(); + return QVariant(); +} + + +QHash SamplesListModel::roleNames() const +{ + QHash roles; + roles[Id] = "id"; + roles[Name] = "name"; + roles[IsMosaic] = "isMosaic"; + roles[Status] = "status"; + roles[Comment] = "comment"; + roles[Source] = "source"; + roles[Reference] = "reference"; + roles[UpdateDate] = "updateDate"; + roles[SearchField] = "searchField"; + return roles; +} + + diff --git a/app/Model/subject/sampleslistmodel.h b/app/Model/subject/sampleslistmodel.h new file mode 100644 index 0000000..53c8278 --- /dev/null +++ b/app/Model/subject/sampleslistmodel.h @@ -0,0 +1,65 @@ +#ifndef SAMPLESLISTMODEL_H +#define SAMPLESLISTMODEL_H + +#include +#include "Model/framework/genericproxymodel.h" +#include "sample.h" +#include "subject.h" + +class Subject; +class Sample; +class SamplesListModel: public QAbstractListModel +{ + enum Roles + { + Id = Qt::UserRole + 1, + Name, + IsMosaic, + Comment, + Status, + Source, + Reference, + UpdateDate, + SearchField + }; + + Q_OBJECT + Q_PROPERTY(int count READ rowCount NOTIFY countChanged) + Q_PROPERTY(GenericProxyModel* proxy READ proxy NOTIFY neverChanged) + +public: + // Constructors + SamplesListModel(QObject* parent = nullptr); + + // Getters + inline GenericProxyModel* proxy() const { return mProxy; } + + // Methods + //! Remove all entries of the list + Q_INVOKABLE void clear(); + //! Clear list and load entries from json data + Q_INVOKABLE bool loadJson(QJsonArray json); + //! Add the provided sample to the list if not already contains + Q_INVOKABLE bool append(Sample* subject); + //! Remove a sample from the list if possible + Q_INVOKABLE bool remove(Sample* subject); + //! Return entry at the requested position; nullptr if not exists + Q_INVOKABLE Sample* getAt(int idx); + + // QAbstractListModel methods + int rowCount(const QModelIndex& parent = QModelIndex()) const; + QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const; + QHash roleNames() const; + +Q_SIGNALS: + void neverChanged(); + void countChanged(); + +private: + //! List of samples + QList mSamples; + //! The QSortFilterProxyModel to use by table view to browse samples + GenericProxyModel* mProxy = nullptr; +}; + +#endif // SAMPLESLISTMODEL_H diff --git a/app/Model/subject/samplesmanager.h b/app/Model/subject/samplesmanager.h index ccc60f6..8906c3c 100644 --- a/app/Model/subject/samplesmanager.h +++ b/app/Model/subject/samplesmanager.h @@ -29,8 +29,9 @@ class SamplesManager : public QAbstractListModel Q_PROPERTY(GenericProxyModel* proxy READ proxy NOTIFY neverChanged) public: - explicit SamplesManager(QObject* parent = nullptr); - explicit SamplesManager(int refId, QObject* parent = nullptr); + // Constructor + SamplesManager(QObject* parent = nullptr); + SamplesManager(int refId, QObject* parent = nullptr); // Property Get/Set inline int referencialId() const { return mRefId; } diff --git a/app/Model/subject/subject.cpp b/app/Model/subject/subject.cpp index 392ade2..b02adf3 100644 --- a/app/Model/subject/subject.cpp +++ b/app/Model/subject/subject.cpp @@ -6,21 +6,30 @@ #include "Model/phenotype/phenotype.h" #include "Model/phenotype/hpodatalistmodel.h" -Subject::Subject(QObject* parent) : QObject(parent) +Subject::Subject(QObject* parent) : RegovarResource(parent) { mPhenotypes = new HpoDataListModel(this); + mAnalyses = new AnalysesListModel(this); + mSamples = new SamplesListModel(this); + mFiles = new FilesListModel(this); + + connect(mFiles, &FilesListModel::fileAdded, this, &Subject::saveFile); } Subject::Subject(QJsonObject json, QObject* parent) : Subject(parent) { loadJson(json, false); } -Subject::Subject(int id, QObject* parent) : QObject(parent) +Subject::Subject(int id, QObject* parent) : Subject(parent) { mId = id; mPhenotypes = new HpoDataListModel(id, this); } +void Subject::saveFile(int) +{ + save(); +} bool Subject::loadJson(QJsonObject json, bool full_init) @@ -44,12 +53,9 @@ bool Subject::loadJson(QJsonObject json, bool full_init) if (!full_init) return true; mPhenotypes->clear(); - mSamples.clear(); - mAnalyses.clear(); - mProjects.clear(); - mJobs.clear(); - mFiles.clear(); - mIndicators.clear(); + mSamples->clear(); + mAnalyses->clear(); + mFiles->clear(); // samples for (const QJsonValue& val: json["samples"].toArray()) @@ -57,7 +63,7 @@ bool Subject::loadJson(QJsonObject json, bool full_init) QJsonObject sampleData = val.toObject(); Sample* sample = regovar->samplesManager()->getOrCreateSample(sampleData["id"].toInt()); sample->loadJson(sampleData); - mSamples.append(sample); + mSamples->append(sample); } // Phenotype for (const QJsonValue& val: json["hpo"].toArray()) @@ -71,11 +77,36 @@ bool Subject::loadJson(QJsonObject json, bool full_init) } mPhenotypes->append(hpo); } + // Analyses + for (const QJsonValue& val: json["analyses"].toArray()) + { + QJsonObject data = val.toObject(); + Analysis* analysis = (Analysis*) regovar->analysesManager()->getOrCreateFilteringAnalysis(data["id"].toInt()); + analysis->loadJson(data, false); + mAnalyses->append(analysis); + } + for (const QJsonValue& val: json["jobs"].toArray()) + { + QJsonObject data = val.toObject(); + Analysis* analysis = (Analysis*) regovar->analysesManager()->getOrCreatePipelineAnalysis(data["id"].toInt()); + analysis->loadJson(data, false); + mAnalyses->append(analysis); + } + + // Files + for (const QJsonValue& val: json["files"].toArray()) + { + QJsonObject data = val.toObject(); + File* file = regovar->filesManager()->getOrCreateFile(data["id"].toInt()); + file->loadJson(data); + mFiles->append(file); + } + + // TODO: Indicators // Event mEvents->loadJson(json["events"].toArray()); - mLoaded = true; emit dataChanged(); return true; @@ -97,9 +128,9 @@ QJsonObject Subject::toJson() result.insert("sex", mSex == Sex::Male ? "male" : mSex == Sex::Female ? "female" : "unknow"); // Samples QJsonArray samples; - for (QObject* o: mSamples) + for (int idx=0; idx < mSamples->rowCount(); idx++) { - Sample* sample = qobject_cast(o); + Sample* sample = mSamples->getAt(idx); samples.append(sample->id()); } result.insert("samples_ids", samples); @@ -113,7 +144,14 @@ QJsonObject Subject::toJson() result.insert("hpo_ids", hpo); // Files - // Indicators + QJsonArray files; + for (int i=0; irowCount() ; ++i) + { + files.append(mFiles->getAt(i)->id()); + } + result.insert("files_ids", files); + + // TODO: Indicators return result; } @@ -167,32 +205,26 @@ void Subject::load(bool forceRefresh) void Subject::addSample(Sample* sample) { - // Check that sample not already in the list - for (QObject* o: mSamples) + // Add sample to the subject + if(mSamples->append(sample)) { - Sample* s = qobject_cast(o); - if (s->id() == sample->id()) return; + emit dataChanged(); + // add subject to the sample + sample->setSubject(this); + sample->save(); } - - // Add sample to the subject - mSamples.append(sample); - emit dataChanged(); - - // add subject to the sample - sample->setSubject(this); - sample->save(); } void Subject::removeSample(Sample* sample) { - // Check that sample already in the list - mSamples.removeAll(sample); - emit dataChanged(); - - // remove subject to the sample - sample->setSubject(nullptr); - sample->save(); + if(mSamples->remove(sample)) + { + emit dataChanged(); + // remove subject to the sample + sample->setSubject(nullptr); + sample->save(); + } } diff --git a/app/Model/subject/subject.h b/app/Model/subject/subject.h index 05e2cb3..0d4ba1a 100644 --- a/app/Model/subject/subject.h +++ b/app/Model/subject/subject.h @@ -2,19 +2,20 @@ #define SUBJECT_H #include +#include "Model/framework/regovarresource.h" #include "Model/file/file.h" +#include "Model/analysis/analyseslistmodel.h" +#include "Model/file/fileslistmodel.h" +#include "sampleslistmodel.h" class Sample; class EventsListModel; class HpoData; class HpoDataListModel; -class Subject : public QObject +class SamplesListModel; +class Subject : public RegovarResource { Q_OBJECT - // Regovar resource attribute - Q_PROPERTY(bool loaded READ loaded NOTIFY dataChanged) - Q_PROPERTY(QDateTime updateDate READ updateDate NOTIFY dataChanged) - Q_PROPERTY(QDateTime createDate READ createDate NOTIFY dataChanged) // Subject attributes Q_PROPERTY(int id READ id NOTIFY dataChanged) Q_PROPERTY(QString identifier READ identifier WRITE setIdentifier NOTIFY dataChanged) @@ -24,17 +25,14 @@ class Subject : public QObject Q_PROPERTY(Sex sex READ sex WRITE setSex NOTIFY dataChanged) Q_PROPERTY(QDate dateOfBirth READ dateOfBirth WRITE setDateOfBirth NOTIFY dataChanged) Q_PROPERTY(QString familyNumber READ familyNumber WRITE setFamilyNumber NOTIFY dataChanged) - Q_PROPERTY(QList samples READ samples NOTIFY dataChanged) - Q_PROPERTY(QList analyses READ analyses NOTIFY dataChanged) - Q_PROPERTY(QList projects READ projects NOTIFY dataChanged) - Q_PROPERTY(QList jobs READ jobs NOTIFY dataChanged) - Q_PROPERTY(QList files READ files NOTIFY dataChanged) - Q_PROPERTY(QList indicators READ indicators NOTIFY dataChanged) + Q_PROPERTY(SamplesListModel* samples READ samples NOTIFY dataChanged) + Q_PROPERTY(AnalysesListModel* analyses READ analyses NOTIFY dataChanged) + Q_PROPERTY(FilesListModel* files READ files NOTIFY dataChanged) + // TODO: Q_PROPERTY(XXX indicators READ indicators NOTIFY dataChanged) Q_PROPERTY(HpoDataListModel* phenotypes READ phenotypes NOTIFY dataChanged) Q_PROPERTY(EventsListModel* events READ events NOTIFY dataChanged) // Special "shortcut" properties for qml display Q_PROPERTY(QJsonObject subjectUI READ subjectUI NOTIFY dataChanged) - Q_PROPERTY(QString searchField READ searchField NOTIFY dataChanged) public: enum Sex @@ -46,14 +44,11 @@ class Subject : public QObject Q_ENUM(Sex) // Constructors - explicit Subject(QObject* parent = nullptr); - explicit Subject(int id, QObject* parent = nullptr); - explicit Subject(QJsonObject json, QObject* parent = nullptr); + Subject(QObject* parent = nullptr); + Subject(int id, QObject* parent = nullptr); + Subject(QJsonObject json, QObject* parent = nullptr); // Getters - inline bool loaded() const { return mLoaded; } - inline QDateTime updateDate() const { return mUpdateDate; } - inline QDateTime createDate() const { return mCreateDate; } inline int id() const { return mId; } inline QString identifier() const { return mIdentifier; } inline QString firstname() const { return mFirstname; } @@ -62,16 +57,12 @@ class Subject : public QObject inline QString familyNumber() const { return mFamilyNumber; } inline Sex sex() const { return mSex; } inline QDate dateOfBirth() const { return mDateOfBirth; } - inline QList samples() const { return mSamples; } - inline QList analyses() const { return mAnalyses; } - inline QList projects() const { return mProjects; } - inline QList jobs() const { return mJobs; } - inline QList files() const { return mFiles; } - inline QList indicators() const { return mIndicators; } + inline SamplesListModel* samples() const { return mSamples; } + inline AnalysesListModel* analyses() const { return mAnalyses; } + inline FilesListModel* files() const { return mFiles; } inline HpoDataListModel* phenotypes() const { return mPhenotypes; } inline EventsListModel* events() const { return mEvents; } inline QJsonObject subjectUI() const { return mSubjectUI; } - inline QString searchField() const { return mSearchField; } // Setters inline void setIdentifier(QString val) { mIdentifier = val; updateSubjectUI(); emit dataChanged(); } @@ -84,13 +75,13 @@ class Subject : public QObject // Methods //! Set model with provided json data - Q_INVOKABLE bool loadJson(QJsonObject json, bool full_init=true); + Q_INVOKABLE bool loadJson(QJsonObject json, bool full_init=true) override; //! Export model data into json object - Q_INVOKABLE QJsonObject toJson(); + Q_INVOKABLE QJsonObject toJson() override; //! Save subject information onto server - Q_INVOKABLE void save(); + Q_INVOKABLE void save() override; //! Load Subject information from server - Q_INVOKABLE void load(bool forceRefresh=true); + Q_INVOKABLE void load(bool forceRefresh=true) override; //! Associate a sample to the subject Q_INVOKABLE void addSample(Sample* sample); //! Remove the association between the sample and the subject @@ -105,18 +96,12 @@ class Subject : public QObject Q_INVOKABLE QString presence(QString hpoId) const; Q_INVOKABLE QDateTime additionDate(QString hpoId) const; -Q_SIGNALS: - void dataChanged(); public Q_SLOTS: - void updateSearchField(); + void updateSearchField() override; + void saveFile(int fileId); private: - bool mLoaded = false; - QDateTime mUpdateDate; - QDateTime mCreateDate; - QDateTime mLastInternalLoad = QDateTime::currentDateTime(); - int mId = -1; QString mIdentifier = ""; QString mFirstname = ""; @@ -125,16 +110,12 @@ public Q_SLOTS: QString mFamilyNumber = ""; Sex mSex = Sex::Unknow; QDate mDateOfBirth; - QList mSamples; - QList mAnalyses; - QList mProjects; - QList mJobs; - QList mFiles; - QList mIndicators; + SamplesListModel* mSamples = nullptr; + AnalysesListModel* mAnalyses = nullptr; + FilesListModel* mFiles = nullptr; HpoDataListModel* mPhenotypes = nullptr; EventsListModel* mEvents = nullptr; QJsonObject mSubjectUI; - QString mSearchField = ""; QHash mPresence; QHash mAdditionDate; }; diff --git a/app/Model/subject/subjectslistmodel.cpp b/app/Model/subject/subjectslistmodel.cpp index 8abf687..7adbfab 100644 --- a/app/Model/subject/subjectslistmodel.cpp +++ b/app/Model/subject/subjectslistmodel.cpp @@ -105,7 +105,7 @@ QVariant SubjectsListModel::data(const QModelIndex& index, int role) const else if (role == FamilyNumber) return subject->familyNumber(); else if (role == UpdateDate) - return subject->updateDate().toString("yyyy-MM-dd"); + return subject->updateDate().toString("yyyy-MM-dd HH:mm"); else if (role == SearchField) return subject->searchField(); return QVariant(); diff --git a/app/Model/subject/subjectsmanager.h b/app/Model/subject/subjectsmanager.h index c6af450..ae0c08f 100644 --- a/app/Model/subject/subjectsmanager.h +++ b/app/Model/subject/subjectsmanager.h @@ -25,7 +25,7 @@ class SubjectsManager : public QAbstractListModel public: // Constructor - explicit SubjectsManager(QObject* parent = nullptr); + SubjectsManager(QObject* parent = nullptr); // Getters inline GenericProxyModel* proxy() const { return mProxy; } diff --git a/app/Model/tools/tool.h b/app/Model/tools/tool.h index cd7aee6..0c322a9 100644 --- a/app/Model/tools/tool.h +++ b/app/Model/tools/tool.h @@ -20,8 +20,9 @@ class Tool : public QObject }; Q_ENUM(ToolType) - explicit Tool(QObject* parent = nullptr); - explicit Tool(ToolType type, QJsonObject json, QObject* parent = nullptr); + // Constructor + Tool(QObject* parent = nullptr); + Tool(ToolType type, QJsonObject json, QObject* parent = nullptr); // Getters inline QString key() { return mKey; } diff --git a/app/Model/tools/toolparameter.h b/app/Model/tools/toolparameter.h index 31c920b..da1f35a 100644 --- a/app/Model/tools/toolparameter.h +++ b/app/Model/tools/toolparameter.h @@ -18,8 +18,9 @@ class ToolParameter : public QObject Q_PROPERTY(bool required READ required NOTIFY dataChanged) public: - explicit ToolParameter(QObject* parent = nullptr); - explicit ToolParameter(QJsonObject json, QObject* parent = nullptr); + // Constructor + ToolParameter(QObject* parent = nullptr); + ToolParameter(QJsonObject json, QObject* parent = nullptr); // Getters inline QString key() const { return mKey; } diff --git a/app/Model/tools/toolsmanager.h b/app/Model/tools/toolsmanager.h index d07a595..fd9e929 100644 --- a/app/Model/tools/toolsmanager.h +++ b/app/Model/tools/toolsmanager.h @@ -8,14 +8,16 @@ class ToolsManager : public QObject Q_OBJECT Q_PROPERTY(QList exporters READ exporters NOTIFY neverChanged) Q_PROPERTY(QList reporters READ reporters NOTIFY neverChanged) + public: // Constructors - explicit ToolsManager(QObject* parent = nullptr); + ToolsManager(QObject* parent = nullptr); // Getters inline QList exporters() const { return mExporters; } inline QList reporters() const { return mReporters; } + // Methods bool loadJson(QJsonObject json); diff --git a/app/Model/user/user.h b/app/Model/user/user.h index d895fc4..e9a9fa5 100644 --- a/app/Model/user/user.h +++ b/app/Model/user/user.h @@ -27,11 +27,10 @@ class User : public QObject Q_PROPERTY(QString searchField READ searchField NOTIFY dataChanged) public: - // Constructors - explicit User(QObject* parent=nullptr); - explicit User(int id, QObject* parent=nullptr); - explicit User(int id, const QString& firstname, const QString& lastname, QObject* parent=nullptr); + User(QObject* parent=nullptr); + User(int id, QObject* parent=nullptr); + User(int id, const QString& firstname, const QString& lastname, QObject* parent=nullptr); // Getters inline int id() const { return mId; } diff --git a/app/Model/user/userslistmodel.h b/app/Model/user/userslistmodel.h index 67f230c..56206f8 100644 --- a/app/Model/user/userslistmodel.h +++ b/app/Model/user/userslistmodel.h @@ -29,7 +29,7 @@ class UsersListModel : public QAbstractListModel public: // Constructor - explicit UsersListModel(QObject* parent = nullptr); + UsersListModel(QObject* parent = nullptr); // Getters inline GenericProxyModel* proxy() const { return mProxy; } diff --git a/app/Model/user/usersmanager.cpp b/app/Model/user/usersmanager.cpp index 658e60e..e91d06c 100644 --- a/app/Model/user/usersmanager.cpp +++ b/app/Model/user/usersmanager.cpp @@ -40,8 +40,9 @@ User* UsersManager::getOrCreateUser(int userId) } -void UsersManager::processPushNotification(QString action, QJsonObject data) +void UsersManager::processPushNotification(QString, QJsonObject) { + // TODO // // Retrieve realtime progress data // QString status = data["status"].toString(); // double progressValue = 0.0; diff --git a/app/Model/user/usersmanager.h b/app/Model/user/usersmanager.h index fada009..fc1c1a4 100644 --- a/app/Model/user/usersmanager.h +++ b/app/Model/user/usersmanager.h @@ -15,7 +15,7 @@ class UsersManager : public QObject public: // Constructor - explicit UsersManager(QObject* parent=nullptr); + UsersManager(QObject* parent=nullptr); // Getters inline UsersListModel* users() const { return mUsersList; } diff --git a/app/QRegovar.pro b/app/QRegovar.pro index 9b5ad19..e09e4ff 100644 --- a/app/QRegovar.pro +++ b/app/QRegovar.pro @@ -100,7 +100,10 @@ HEADERS += \ Model/phenotype/gene.h \ Model/framework/regovarresource.h \ Model/panel/panelversionslistmodel.h \ - Model/phenotype/geneslistmodel.h + Model/phenotype/geneslistmodel.h \ + Model/subject/sampleslistmodel.h \ + Model/framework/jsonlistmodel.h \ + Model/panel/panelslistmodel.h SOURCES += main.cpp \ Model/framework/treeitem.cpp \ @@ -188,7 +191,10 @@ SOURCES += main.cpp \ Model/phenotype/gene.cpp \ Model/framework/regovarresource.cpp \ Model/panel/panelversionslistmodel.cpp \ - Model/phenotype/geneslistmodel.cpp + Model/phenotype/geneslistmodel.cpp \ + Model/subject/sampleslistmodel.cpp \ + Model/framework/jsonlistmodel.cpp \ + Model/panel/panelslistmodel.cpp # Additional import path used to resolve QML modules in Qt Creator's code model diff --git a/app/UI/Dialogs/SelectSamplesDialog.qml b/app/UI/Dialogs/SelectSamplesDialog.qml index 2e4852d..f01590b 100644 --- a/app/UI/Dialogs/SelectSamplesDialog.qml +++ b/app/UI/Dialogs/SelectSamplesDialog.qml @@ -185,7 +185,7 @@ Dialog text: selectedSamplesTable.statusIcons[styleData.value.status] onTextChanged: { - if (styleData.value.status == 1) // 1 = Loading + if (styleData.value.status === 1) // 1 = Loading { statusIconAnimation.start(); } diff --git a/app/UI/Framework/FilesBrowser.qml b/app/UI/Framework/FilesBrowser.qml index 5df5749..d44d0f6 100644 --- a/app/UI/Framework/FilesBrowser.qml +++ b/app/UI/Framework/FilesBrowser.qml @@ -5,110 +5,145 @@ import Regovar.Core 1.0 import "qrc:/qml/Regovar" import "qrc:/qml/Framework" +import "qrc:/qml/Dialogs" Item { id: root - property alias model: filesTreeView.model + property bool readOnly: false + property File currentFile + property QtObject model + onModelChanged: + { + if(model) + { + model.dataChanged.connect(updateViewFromModel); + } + updateViewFromModel(); + } + Component.onDestruction: + { + model.dataChanged.disconnect(updateViewFromModel); + } + SplitView { id: row anchors.fill: parent - Rectangle + Item { id: leftPanel width: 300 - color: "transparent" - clip: true - - TreeView + ColumnLayout { - id: filesTreeView anchors.fill: parent anchors.rightMargin: 10 + spacing: 10 - onCurrentIndexChanged: + RowLayout { - if (model) + spacing: 10 + ButtonIcon + { + iconTxt: "à" + text: "Add file" + onClicked: fileSelector.open() + visible: !root.readOnly + } + TextField { - var id = model.data(currentIndex, Qt.UserRole+1); - viewer.openFile(id); + id: browserSearch + + Layout.fillWidth: true + iconLeft: "z" + displayClearButton: true + placeholder: qsTr("Search file...") + onTextChanged: browser.model.setFilterString(text) } } - TableViewColumn + + TreeView { - width: 100 - role: "name" - title: "Name" - delegate: Item + id: browser + Layout.fillHeight: true + Layout.fillWidth: true + onCurrentIndexChanged: displayCurrentFile() + + TableViewColumn { - Text + width: 100 + role: "name" + title: "Name" + delegate: Item { - anchors.leftMargin: 5 - anchors.left: parent.left - anchors.verticalCenter: parent.verticalCenter - verticalAlignment: Text.AlignVCenter - horizontalAlignment: styleData.textAlignment - font.pixelSize: Regovar.theme.font.size.normal - font.family: Regovar.theme.icons.name - text: styleData.value.icon - onTextChanged: + Text { - if (styleData.value.icon === "/") // = Loading + anchors.leftMargin: 5 + anchors.left: parent.left + anchors.verticalCenter: parent.verticalCenter + verticalAlignment: Text.AlignVCenter + horizontalAlignment: styleData.textAlignment + font.pixelSize: Regovar.theme.font.size.normal + font.family: Regovar.theme.icons.name + text: styleData.value.icon + onTextChanged: { - statusIconAnimation.start(); + if (styleData.value.icon === "/") // = Loading + { + statusIconAnimation.start(); + } + else + { + statusIconAnimation.stop(); + rotation = 0; + } } - else + NumberAnimation on rotation { - statusIconAnimation.stop(); - rotation = 0; + id: statusIconAnimation + duration: 1500 + loops: Animation.Infinite + from: 0 + to: 360 } } - NumberAnimation on rotation + Text { - id: statusIconAnimation - duration: 1500 - loops: Animation.Infinite - from: 0 - to: 360 + anchors.leftMargin: Regovar.theme.font.boxSize.normal + 5 + anchors.rightMargin: 5 + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + horizontalAlignment: styleData.textAlignment + font.pixelSize: Regovar.theme.font.size.normal + text: styleData.value.filename + elide: Text.ElideRight } } - Text - { - anchors.leftMargin: Regovar.theme.font.boxSize.normal + 5 - anchors.rightMargin: 5 - anchors.left: parent.left - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - horizontalAlignment: styleData.textAlignment - font.pixelSize: Regovar.theme.font.size.normal - text: styleData.value.filename - elide: Text.ElideRight - } + } + TableViewColumn + { + width: 100 + role: "size" + title: "Size" + } + TableViewColumn + { + role: "date" + title: "Date" + } + TableViewColumn + { + role: "comment" + title: "Comment" } } - TableViewColumn - { - width: 100 - role: "size" - title: "Size" - } - TableViewColumn - { - role: "date" - title: "Date" - } - TableViewColumn - { - role: "comment" - title: "Comment" - } - } + } } FileViewer @@ -117,4 +152,37 @@ Item Layout.minimumWidth: 350 } } + + SelectFilesDialog + { + id: fileSelector + onFileSelected: + { + for(var idx in files) + { + var file = files[idx]; + root.model.add(file); + } + } + } + + function updateViewFromModel() + { + if (root.model) + { + browser.model = root.model.proxy; + } + } + + function displayCurrentFile() + { + if (root.model) + { + var idx = root.model.proxy.mapToSource(browser.currentIndex); + var id = root.model.data(idx, 257); // 257 = Qt::UserRole+1 + + root.currentFile = regovar.filesManager.getOrCreateFile(id); + viewer.openFile(id); + } + } } diff --git a/app/UI/InformationPanel/File/FileInformation.qml b/app/UI/InformationPanel/File/FileInformation.qml index ae812cb..c4fd2bb 100644 --- a/app/UI/InformationPanel/File/FileInformation.qml +++ b/app/UI/InformationPanel/File/FileInformation.qml @@ -23,16 +23,19 @@ InformationPanel "icon": "j", "source": "qrc:/qml/InformationPanel/File/InfoPanel.qml" }); - ttt.append({ - "title": qsTr("Relations"), - "icon": "ê", - "source": "qrc:/qml/InformationPanel/Common/RelationsPanel.qml" - }); - ttt.append({ - "title": qsTr("Events"), - "icon": "H", - "source": "qrc:/qml/InformationPanel/Common/EventsPanel.qml" - }); + + // TODO +// ttt.append({ +// "title": qsTr("Relations"), +// "icon": "ê", +// "source": "qrc:/qml/InformationPanel/Common/RelationsPanel.qml" +// }); + // TODO +// ttt.append({ +// "title": qsTr("Events"), +// "icon": "H", +// "source": "qrc:/qml/InformationPanel/Common/EventsPanel.qml" +// }); root.tabsModel = ttt; root.loading = false; diff --git a/app/UI/InformationPanel/Gene/GeneInformation.qml b/app/UI/InformationPanel/Gene/GeneInformation.qml index fb1cad7..40249ef 100644 --- a/app/UI/InformationPanel/Gene/GeneInformation.qml +++ b/app/UI/InformationPanel/Gene/GeneInformation.qml @@ -22,7 +22,7 @@ InformationPanel root.title += data["location"]; // Update tabs - root.tabSharedModel = data; + root.tabSharedModel = model; var ttt = listModel.createObject(root); ttt.append( { "title": qsTr("Information"), @@ -34,21 +34,42 @@ InformationPanel "icon": "è", "source": "qrc:/qml/InformationPanel/Gene/OnlineToolsPanel.qml" }); + var pmCount = model.pubmed.rowCount(); + pmCount = pmCount > 0 ? " (" + pmCount + ")" : ""; ttt.append({ - "title": qsTr("Pubmed") + " (" + data["pubmed"].length + ")", + "title": qsTr("Pubmed") + pmCount, "icon": "Y", "source": "qrc:/qml/InformationPanel/Gene/ReferencePanel.qml" }); + var pCount = model.phenotypes.rowCount(); + pCount = pCount > 0 ? " (" + pCount + ")" : ""; ttt.append({ - "title": qsTr("Phenotype"), + "title": qsTr("Phenotypes") + pCount, "icon": "K", - "source": "qrc:/qml/InformationPanel/Phenotype/InfoPanel.qml" + "source": "qrc:/qml/InformationPanel/Phenotype/PhenotypesPanel.qml" }); + var dCount = model.diseases.rowCount(); + dCount = dCount > 0 ? " (" + dCount + ")" : ""; ttt.append({ - "title": qsTr("Regovar statistics"), - "icon": "í", - "source": "qrc:/qml/InformationPanel/Variant/StatsPanel.qml" + "title": qsTr("Diseases") + dCount, + "icon": "K", + "source": "qrc:/qml/InformationPanel/Phenotype/DiseasesPanel.qml" + }); + var vCount = model.panels.rowCount(); + vCount = vCount > 0 ? " (" + vCount + ")" : ""; + ttt.append({ + "title": qsTr("Panels") + vCount, + "icon": "q", + "source": "qrc:/qml/InformationPanel/Gene/PanelsPanel.qml" }); + + + // TODO +// ttt.append({ +// "title": qsTr("Regovar statistics"), +// "icon": "í", +// "source": "qrc:/qml/InformationPanel/Variant/StatsPanel.qml" +// }); root.tabsModel = ttt; root.loading = false; } diff --git a/app/UI/InformationPanel/Gene/InfoPanel.qml b/app/UI/InformationPanel/Gene/InfoPanel.qml index d2b61da..ce263d4 100644 --- a/app/UI/InformationPanel/Gene/InfoPanel.qml +++ b/app/UI/InformationPanel/Gene/InfoPanel.qml @@ -23,6 +23,7 @@ ScrollView { if (data) { + data = data.json; if ("gene" in data) geneData = data["gene"]; else diff --git a/app/UI/InformationPanel/Gene/OnlineToolsPanel.qml b/app/UI/InformationPanel/Gene/OnlineToolsPanel.qml index 558bb3b..0dc3667 100644 --- a/app/UI/InformationPanel/Gene/OnlineToolsPanel.qml +++ b/app/UI/InformationPanel/Gene/OnlineToolsPanel.qml @@ -19,7 +19,7 @@ ScrollView property var model - property var geneDBData: root.model ? [root.model["entrez_id"], root.model["ensembl_gene_id"], root.model["ucsc_id"]] : [] + property var geneDBData: root.model ? [root.model.json["entrez_id"], root.model.json["ensembl_gene_id"], root.model.json["ucsc_id"]] : [] property var geneDBList: ListModel { ListElement @@ -39,7 +39,7 @@ ScrollView } } - property var nucleotideDBData: root.model ? [root.model["ena"], root.model["refseq_accession"], root.model["ccds_id"]] : [] + property var nucleotideDBData: root.model ? [root.model.json["ena"], root.model.json["refseq_accession"], root.model.json["ccds_id"]] : [] property var nucleotideDBList: ListModel { ListElement @@ -59,7 +59,7 @@ ScrollView } } - property var expresionDBData: root.model ? [root.model["symbol"], root.model["uniprot_ids"][0]] : [] + property var expresionDBData: root.model ? [root.model.json["symbol"], root.model.json["uniprot_ids"][0]] : [] property var expressionDBList: ListModel { @@ -85,7 +85,7 @@ ScrollView } } - property var clinicalDBData: root.model ? [root.model["symbol"], root.model["omim_id"]] : [] + property var clinicalDBData: root.model ? [root.model.json["symbol"], root.model.json["omim_id"]] : [] property var clinicalDBList: ListModel { ListElement @@ -106,7 +106,7 @@ ScrollView } - property var homologsDBData: root.model ? [root.model["mgd_id"], root.model["rgd_id"]] : [] + property var homologsDBData: root.model ? [root.model.json["mgd_id"], root.model.json["rgd_id"]] : [] property var homologsDBList: ListModel { ListElement @@ -121,7 +121,7 @@ ScrollView } } - property var otherDBData: root.model ? [root.model["symbol"], root.model["hgnc_id"], root.model["entrez_id"]] : [] + property var otherDBData: root.model ? [root.model.json["symbol"], root.model.json["hgnc_id"], root.model.json["entrez_id"]] : [] property var otherDBList: ListModel { ListElement diff --git a/app/UI/InformationPanel/Gene/PanelsPanel.qml b/app/UI/InformationPanel/Gene/PanelsPanel.qml new file mode 100644 index 0000000..b3aed91 --- /dev/null +++ b/app/UI/InformationPanel/Gene/PanelsPanel.qml @@ -0,0 +1,122 @@ +import QtQuick 2.9 +import QtQuick.Controls 2.2 +import QtQuick.Controls 1.4 +import QtQuick.Layouts 1.3 +import Regovar.Core 1.0 + +import "qrc:/qml/Regovar" +import "qrc:/qml/Framework" +import "qrc:/qml/InformationPanel/Common" + + +Item +{ + id: root + property var model + onModelChanged: updateFromModel(model) + Component.onCompleted: updateFromModel(model) + + ColumnLayout + { + id: content + anchors.fill: parent + anchors.margins: 10 + visible: false + enabled: false + + TextField + { + id: searchField + Layout.fillWidth: true + iconLeft: "z" + displayClearButton: true + placeholder: qsTr("Quick filter...") + onTextEdited: panelsTable.model.setFilterString(text) + } + + TableView + { + id: panelsTable + Layout.fillWidth: true + Layout.fillHeight: true + + TableViewColumn + { + role: "name" + title: "Name" + delegate: RowLayout + { + anchors.left: parent.left + anchors.leftMargin: 5 + spacing: 10 + + ButtonInline + { + iconTxt: "z" + text: "" + onClicked: regovar.getPanelInfo(model.id) + } + Text + { + Layout.fillWidth: true + font.pixelSize: Regovar.theme.font.size.normal + font.family: Regovar.theme.font.family + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + text: styleData.value + } + } + } + TableViewColumn + { + role: "comment" + title: "Comment" + width: 300 + } + } + + Text + { + id: label + Layout.fillWidth: true + font.pixelSize: Regovar.theme.font.size.small + horizontalAlignment: Text.AlignRight + } + } + + Text + { + id: emptyMessage + anchors.fill: parent + wrapMode: Text.WordWrap + elide: Text.ElideRight + text: qsTr("No panel contains this gene") + color: Regovar.theme.primaryColor.back.normal + font.pixelSize: Regovar.theme.font.size.header + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + + function updateFromModel(data) + { + if (!data) return; + var count = data.panels.rowCount(); + + if (count>0) + { + content.visible = true; + content.enabled = true; + emptyMessage.visible = false; + panelsTable.model = data.panels.proxy; + label.text = count + " panels."; + } + else + { + content.visible = false; + content.enabled = false; + emptyMessage.visible = true; + panelsTable.model = null; + } + } +} diff --git a/app/UI/InformationPanel/Gene/ReferencePanel.qml b/app/UI/InformationPanel/Gene/ReferencePanel.qml index bc34c39..7416360 100644 --- a/app/UI/InformationPanel/Gene/ReferencePanel.qml +++ b/app/UI/InformationPanel/Gene/ReferencePanel.qml @@ -23,6 +23,7 @@ ScrollView function updateFromModel(data) { + data = data.json; if (data && "pubmed" in data) { data = data["pubmed"]; diff --git a/app/UI/InformationPanel/Panel/PanelFormNewGene.qml b/app/UI/InformationPanel/Panel/PanelFormNewGene.qml index 1cef90e..22e900f 100644 --- a/app/UI/InformationPanel/Panel/PanelFormNewGene.qml +++ b/app/UI/InformationPanel/Panel/PanelFormNewGene.qml @@ -142,14 +142,10 @@ Rectangle model: modelData onAdded: { - regovar.panelsManager.newPanel.addEntry({"label" : modelData["label"], "type": "gene", "details": modelData["id"]}); + regovar.panelsManager.newPanel.addEntriesFromHpo(modelData["id"]); enabled = false; } - onShowDetails: - { - phenotypeInfoDialog.open(); - regovar.getPhenotypeInfo(modelData["id"]); - } + onShowDetails: regovar.getPhenotypeInfo(modelData["id"]) } } } diff --git a/app/UI/InformationPanel/Panel/PanelInformation.qml b/app/UI/InformationPanel/Panel/PanelInformation.qml index b88fd36..df87bad 100644 --- a/app/UI/InformationPanel/Panel/PanelInformation.qml +++ b/app/UI/InformationPanel/Panel/PanelInformation.qml @@ -29,11 +29,12 @@ InformationPanel "icon": "o", "source": "qrc:/qml/InformationPanel/Panel/VersionDetailsPanel.qml" }); - ttt.append({ - "title": qsTr("Events"), - "icon": "H", - "source": "qrc:/qml/InformationPanel/Common/EventsPanel.qml" - }); + // TODO +// ttt.append({ +// "title": qsTr("Events"), +// "icon": "H", +// "source": "qrc:/qml/InformationPanel/Common/EventsPanel.qml" +// }); root.tabsModel = ttt; root.loading = false; } diff --git a/app/UI/InformationPanel/Pipeline/InfoPanel.qml b/app/UI/InformationPanel/Pipeline/InfoPanel.qml index 34df60f..3458d66 100644 --- a/app/UI/InformationPanel/Pipeline/InfoPanel.qml +++ b/app/UI/InformationPanel/Pipeline/InfoPanel.qml @@ -8,46 +8,166 @@ import "qrc:/qml/Regovar" import "qrc:/qml/Framework" -ScrollView +Item { id: root - horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff property var model - onModelChanged: if (model) { updateFromModel(model); } - property string varId - onVarIdChanged: + GridLayout { - if (varId) + anchors.fill: parent + anchors.margins: 10 + columns: 2 + columnSpacing: 10 + rowSpacing: 5 + + Rectangle { - // Display loading feedback + Layout.columnSpan: 2 + Layout.fillWidth: true + height: Regovar.theme.font.boxSize.header + color: Regovar.theme.boxColor.back + border.width: 1 + border.color: Regovar.theme.boxColor.border + radius: 2 + + RowLayout + { + anchors.fill: parent + anchors.margins: 5 + + + Text + { + text: qsTr("Starred") + ":" + font.pixelSize: Regovar.theme.font.size.normal + color: Regovar.theme.primaryColor.back.normal + height: Regovar.theme.font.boxSize.normal + verticalAlignment: Text.AlignVCenter + } + Text + { + text: model.starred ? qsTr("Yes") : qsTr("No") + font.pixelSize: Regovar.theme.font.size.normal + color: Regovar.theme.frontColor.normal + height: Regovar.theme.font.boxSize.normal + verticalAlignment: Text.AlignVCenter + } + + Item + { + height: 10 + width: 100 + } + + Text + { + text: qsTr("Type") + ":" + font.pixelSize: Regovar.theme.font.size.normal + color: Regovar.theme.primaryColor.back.normal + height: Regovar.theme.font.boxSize.normal + verticalAlignment: Text.AlignVCenter + } + Text + { + text: model.type + font.pixelSize: Regovar.theme.font.size.normal + color: Regovar.theme.frontColor.normal + height: Regovar.theme.font.boxSize.normal + verticalAlignment: Text.AlignVCenter + } + + Item + { + height: 10 + Layout.fillWidth: true + } + + } + } - // request information + Text + { + text: qsTr("Name") + font.pixelSize: Regovar.theme.font.size.normal + color: Regovar.theme.primaryColor.back.normal + height: Regovar.theme.font.boxSize.normal + verticalAlignment: Text.AlignVCenter } - else + Text { - // Display help message + text: model.name + Layout.fillWidth: true + font.pixelSize: Regovar.theme.font.size.normal + color: Regovar.theme.frontColor.normal + height: Regovar.theme.font.boxSize.normal + verticalAlignment: Text.AlignVCenter } - } - function updateFromModel(data) - { - } + + Text + { + text: qsTr("Version") + font.pixelSize: Regovar.theme.font.size.normal + color: Regovar.theme.primaryColor.back.normal + height: Regovar.theme.font.boxSize.normal + verticalAlignment: Text.AlignVCenter + } + Text + { + text: model.version + Layout.fillWidth: true + font.pixelSize: Regovar.theme.font.size.normal + color: Regovar.theme.frontColor.normal + height: Regovar.theme.font.boxSize.normal + verticalAlignment: Text.AlignVCenter + } + Text + { + text: qsTr("Description") + font.pixelSize: Regovar.theme.font.size.normal + color: Regovar.theme.primaryColor.back.normal + height: Regovar.theme.font.boxSize.normal + verticalAlignment: Text.AlignVCenter + } + Text + { + text: model.description + Layout.fillWidth: true + font.pixelSize: Regovar.theme.font.size.normal + color: Regovar.theme.frontColor.normal + height: Regovar.theme.font.boxSize.normal + verticalAlignment: Text.AlignVCenter + } - Text - { - text: "Stats !" - width: Regovar.theme.font.boxSize.header - height: Regovar.theme.font.boxSize.header - - color: Regovar.theme.primaryColor.front.normal - font.pixelSize: Regovar.theme.font.size.header - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignHCenter - } + Text + { + text: qsTr("Install") + font.pixelSize: Regovar.theme.font.size.normal + color: Regovar.theme.primaryColor.back.normal + height: Regovar.theme.font.boxSize.normal + verticalAlignment: Text.AlignVCenter + } + Text + { + text: regovar.formatDate(model.installationDate) + Layout.fillWidth: true + font.pixelSize: Regovar.theme.font.size.normal + color: Regovar.theme.frontColor.normal + height: Regovar.theme.font.boxSize.normal + verticalAlignment: Text.AlignVCenter + } + + Item + { + Layout.columnSpan: 2 + Layout.fillHeight: true + Layout.fillWidth: true + } + } } diff --git a/app/UI/InformationPanel/Pipeline/PipelineInformation.qml b/app/UI/InformationPanel/Pipeline/PipelineInformation.qml index 9cb2cb4..9d2f7e5 100644 --- a/app/UI/InformationPanel/Pipeline/PipelineInformation.qml +++ b/app/UI/InformationPanel/Pipeline/PipelineInformation.qml @@ -14,11 +14,8 @@ InformationPanel updateFromModel: function updateFromModel(data) { // Update title - root.title = "

" + data["name"] + "

Version: " + data["version"] + ""; - if ("description" in data) - { - root.title += "

" + data["description"]; - } + root.title = "

" + data.name + "

Version: " + data.version + ""; + root.title += "
" + data.description; // Update tabs @@ -26,13 +23,13 @@ InformationPanel var documents = ("documents" in data) ? data["documents"] : {}; var ttt = listModel.createObject(root); - if ("home" in documents) + if (data.aboutPage !== "") { ttt.append( { "title": qsTr("About"), "icon": "j", "source": "qrc:/qml/InformationPanel/Common/WebViewPanel.qml", - "tabModel" : documents["about"] + "tabModel" : data.aboutPage }); } ttt.append( @@ -40,11 +37,19 @@ InformationPanel "icon": "j", "source": "qrc:/qml/InformationPanel/Pipeline/InfoPanel.qml" }); - ttt.append({ - "title": qsTr("Events"), - "icon": "^", - "source": "qrc:/qml/InformationPanel/Pipeline/StatsPanel.qml" - }); + // TODO +// ttt.append({ +// "title": qsTr("Analyses"), +// "icon": "I", +// "source": "qrc:/qml/InformationPanel/Pipeline/Analyses.qml" +// }); + + // TODO +// ttt.append({ +// "title": qsTr("Events"), +// "icon": "^", +// "source": "qrc:/qml/InformationPanel/Pipeline/StatsPanel.qml" +// }); root.tabsModel = ttt; root.loading = false; diff --git a/app/UI/InformationPanel/User/InfoPanel.qml b/app/UI/InformationPanel/User/InfoPanel.qml index 34df60f..01f57da 100644 --- a/app/UI/InformationPanel/User/InfoPanel.qml +++ b/app/UI/InformationPanel/User/InfoPanel.qml @@ -8,46 +8,185 @@ import "qrc:/qml/Regovar" import "qrc:/qml/Framework" -ScrollView +Item { id: root - horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff property var model - onModelChanged: if (model) { updateFromModel(model); } - property string varId - onVarIdChanged: + GridLayout { - if (varId) + anchors.fill: parent + anchors.margins: 10 + columns: 2 + columnSpacing: 10 + rowSpacing: 5 + + Rectangle { - // Display loading feedback + Layout.columnSpan: 2 + Layout.fillWidth: true + height: Regovar.theme.font.boxSize.header + color: Regovar.theme.boxColor.back + border.width: 1 + border.color: Regovar.theme.boxColor.border + radius: 2 + + RowLayout + { + anchors.fill: parent + anchors.margins: 5 + + + Text + { + text: qsTr("Is active") + ":" + font.pixelSize: Regovar.theme.font.size.normal + color: Regovar.theme.primaryColor.back.normal + height: Regovar.theme.font.boxSize.normal + verticalAlignment: Text.AlignVCenter + } + Text + { + text: model.isActive ? qsTr("Yes") : qsTr("No") + font.pixelSize: Regovar.theme.font.size.normal + color: Regovar.theme.frontColor.normal + height: Regovar.theme.font.boxSize.normal + verticalAlignment: Text.AlignVCenter + } + + Item + { + height: 10 + width: 100 + } - // request information + Text + { + text: qsTr("Is admin") + ":" + font.pixelSize: Regovar.theme.font.size.normal + color: Regovar.theme.primaryColor.back.normal + height: Regovar.theme.font.boxSize.normal + verticalAlignment: Text.AlignVCenter + } + Text + { + text: model.isAdmin ? qsTr("Yes") : qsTr("No") + font.pixelSize: Regovar.theme.font.size.normal + color: Regovar.theme.frontColor.normal + height: Regovar.theme.font.boxSize.normal + verticalAlignment: Text.AlignVCenter + } + Item + { + height: 10 + Layout.fillWidth: true + } + + } } - else + + + Text { - // Display help message + text: qsTr("Lastname") + font.pixelSize: Regovar.theme.font.size.normal + color: Regovar.theme.primaryColor.back.normal + height: Regovar.theme.font.boxSize.normal + verticalAlignment: Text.AlignVCenter + } + Text + { + text: model.lastname + Layout.fillWidth: true + font.pixelSize: Regovar.theme.font.size.normal + color: Regovar.theme.frontColor.normal + height: Regovar.theme.font.boxSize.normal + verticalAlignment: Text.AlignVCenter } - } - function updateFromModel(data) - { - } + Text + { + text: qsTr("Firstname") + font.pixelSize: Regovar.theme.font.size.normal + color: Regovar.theme.primaryColor.back.normal + height: Regovar.theme.font.boxSize.normal + verticalAlignment: Text.AlignVCenter + } + Text + { + text: model.firstname + Layout.fillWidth: true + font.pixelSize: Regovar.theme.font.size.normal + color: Regovar.theme.frontColor.normal + height: Regovar.theme.font.boxSize.normal + verticalAlignment: Text.AlignVCenter + } - Text - { - text: "Stats !" - width: Regovar.theme.font.boxSize.header - height: Regovar.theme.font.boxSize.header - - color: Regovar.theme.primaryColor.front.normal - font.pixelSize: Regovar.theme.font.size.header - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignHCenter - } + Text + { + text: qsTr("Email") + font.pixelSize: Regovar.theme.font.size.normal + color: Regovar.theme.primaryColor.back.normal + height: Regovar.theme.font.boxSize.normal + verticalAlignment: Text.AlignVCenter + } + Text + { + text: model.email + Layout.fillWidth: true + font.pixelSize: Regovar.theme.font.size.normal + color: Regovar.theme.frontColor.normal + height: Regovar.theme.font.boxSize.normal + verticalAlignment: Text.AlignVCenter + } + + + Text + { + text: qsTr("Location") + font.pixelSize: Regovar.theme.font.size.normal + color: Regovar.theme.primaryColor.back.normal + height: Regovar.theme.font.boxSize.normal + verticalAlignment: Text.AlignVCenter + } + Text + { + text: model.location + Layout.fillWidth: true + font.pixelSize: Regovar.theme.font.size.normal + color: Regovar.theme.frontColor.normal + height: Regovar.theme.font.boxSize.normal + verticalAlignment: Text.AlignVCenter + } + + Text + { + text: qsTr("Function") + font.pixelSize: Regovar.theme.font.size.normal + color: Regovar.theme.primaryColor.back.normal + height: Regovar.theme.font.boxSize.normal + verticalAlignment: Text.AlignVCenter + } + Text + { + text: model.function + Layout.fillWidth: true + font.pixelSize: Regovar.theme.font.size.normal + color: Regovar.theme.frontColor.normal + height: Regovar.theme.font.boxSize.normal + verticalAlignment: Text.AlignVCenter + } + + Item + { + Layout.columnSpan: 2 + Layout.fillHeight: true + Layout.fillWidth: true + } + } } diff --git a/app/UI/InformationPanel/User/UserInformation.qml b/app/UI/InformationPanel/User/UserInformation.qml index e451515..3c228a6 100644 --- a/app/UI/InformationPanel/User/UserInformation.qml +++ b/app/UI/InformationPanel/User/UserInformation.qml @@ -14,10 +14,8 @@ InformationPanel updateFromModel: function updateFromModel(data) { // Update title - var variant = "chr" + data["chr"] + ":" + data["pos"] + " " + data["ref"] + ">" + data["alt"]; - var gene = data["genename"]; - var ref = data["reference"]; - root.title = "" + variant + "

Ref: " + ref + "   \n\nGene: " + gene; + root.title = "

" + data.firstname + " " + data.lastname + "


"; + root.title += qsTr("Last connection:") + " " + regovar.formatDate(data.lastActivity, true); // Update tabs root.tabSharedModel = data; @@ -25,18 +23,20 @@ InformationPanel ttt.append( { "title": qsTr("Information"), "icon": "j", - "source": "qrc:/qml/InformationPanel/User/InfoPanel.qmll" - }); - ttt.append({ - "title": qsTr("Regovar statistics"), - "icon": "í", - "source": "qrc:/qml/InformationPanel/User/StatsPanel.qml" - }); - ttt.append({ - "title": qsTr("Events"), - "icon": "è", - "source": "qrc:/qml/InformationPanel/Common/EventsPanel.qml" + "source": "qrc:/qml/InformationPanel/User/InfoPanel.qml" }); + // TODO +// ttt.append({ +// "title": qsTr("Regovar statistics"), +// "icon": "í", +// "source": "qrc:/qml/InformationPanel/User/StatsPanel.qml" +// }); + // TODO +// ttt.append({ +// "title": qsTr("Events"), +// "icon": "è", +// "source": "qrc:/qml/InformationPanel/Common/EventsPanel.qml" +// }); root.tabsModel = ttt; root.loading = false; } @@ -50,6 +50,6 @@ InformationPanel Connections { target: regovar - onUserInformationReady: root.model = json + onUserInformationReady: root.model = user; } } diff --git a/app/UI/LoginPage.qml b/app/UI/LoginPage.qml index 8da591f..e7045c1 100644 --- a/app/UI/LoginPage.qml +++ b/app/UI/LoginPage.qml @@ -61,7 +61,7 @@ Rectangle Layout.fillWidth: true placeholder: qsTr("Login") focus: true - Keys.onPressed: { if (event.key == Qt.Key_Return || event.key == Qt.Key_Enter) tryLogin(); } + Keys.onPressed: { if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) tryLogin(); } } TextField @@ -71,7 +71,7 @@ Rectangle placeholder: qsTr("Password") echoMode: TextInput.Password //onTextEdited: regovar.usersManager.login(loginField.text, pwdField.text); - Keys.onPressed: { if (event.key == Qt.Key_Return || event.key == Qt.Key_Enter) tryLogin(); } + Keys.onPressed: { if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) tryLogin(); } } RowLayout @@ -81,8 +81,8 @@ Rectangle Layout.fillWidth: true text: qsTr("Keep me logged in") checked: regovar.usersManager.keepMeLogged - onCheckedChanged: regovar.usersManager.keepMeLogged = checked - Keys.onPressed: { if (event.key == Qt.Key_Return || event.key == Qt.Key_Enter) tryLogin(); } + //onCheckedChanged: regovar.usersManager.keepMeLogged = checked + Keys.onPressed: { if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) tryLogin(); } } Button diff --git a/app/UI/Pages/Analysis/AnalysisPreview.qml b/app/UI/Pages/Analysis/AnalysisPreview.qml new file mode 100644 index 0000000..d4b2724 --- /dev/null +++ b/app/UI/Pages/Analysis/AnalysisPreview.qml @@ -0,0 +1,145 @@ +import QtQuick 2.9 +import QtQuick.Controls 1.4 +import QtQuick.Layouts 1.3 +import Regovar.Core 1.0 + +import "qrc:/qml/Regovar" +import "qrc:/qml/Framework" + +Item +{ + id: root + property Analysis model + + + ColumnLayout + { + anchors.fill: parent + spacing: 10 + + + Rectangle + { + Layout.fillWidth: true + height: Regovar.theme.font.boxSize.header + color: Regovar.theme.backgroundColor.alt + border.width: 1 + border.color: Regovar.theme.boxColor.border + radius: 2 + + RowLayout + { + anchors.fill: parent + anchors.margins: 5 + + // Analysis Name + Text + { + Layout.fillWidth: true + height: Regovar.theme.font.boxSize.header + elide: Text.ElideRight + font.pixelSize: Regovar.theme.font.size.header + verticalAlignment: Text.AlignVCenter + color: Regovar.theme.primaryColor.back.normal + text: model ? model.name : "" + } + Item + { + height: 10 + Layout.fillWidth: true + } + + // Analysis Status + Text + { + height: Regovar.theme.font.boxSize.header + elide: Text.ElideRight + font.pixelSize: Regovar.theme.font.size.header + verticalAlignment: Text.AlignVCenter + color: Regovar.theme.primaryColor.back.normal + font.family: Regovar.theme.icons.name + text: model ? "H" : "" + } + Text + { + height: Regovar.theme.font.boxSize.header + elide: Text.ElideRight + font.pixelSize: Regovar.theme.font.size.header + verticalAlignment: Text.AlignVCenter + color: Regovar.theme.primaryColor.back.normal + text: model ? regovar.formatDate(model.updateDate) : "" + } + Item + { + height: 10 + Layout.fillWidth: true + } + + //Analysis Date + Text + { + height: Regovar.theme.font.boxSize.header + elide: Text.ElideRight + font.pixelSize: Regovar.theme.font.size.header + verticalAlignment: Text.AlignVCenter + color: Regovar.theme.primaryColor.back.normal + font.family: Regovar.theme.icons.name + text: model ? "H" : "" + } + Text + { + height: Regovar.theme.font.boxSize.header + elide: Text.ElideRight + font.pixelSize: Regovar.theme.font.size.header + verticalAlignment: Text.AlignVCenter + color: Regovar.theme.primaryColor.back.normal + text: model ? regovar.formatDate(model.updateDate) : "" + } + } + } + + Rectangle + { + Layout.fillWidth: true + Layout.fillHeight: true + color: Regovar.theme.boxColor.back + border.width: 1 + border.color: Regovar.theme.boxColor.border + radius: 2 + + RowLayout + { + anchors.fill: parent + anchors.margins: 5 + } + } + } + + + GridLayout + { + columns: 3 + rowSpacing: 10 + columnSpacing: 10 + + + + Rectangle + { + color: "transparent" + Layout.fillWidth: true + Layout.fillHeight: true + Layout.columnSpan: 3 + border.width: 1 + border.color: Regovar.theme.boxColor.border + Text + { + anchors.centerIn: parent + text: qsTr("Not yet implemented") + font.pixelSize: Regovar.theme.font.size.normal + color: Regovar.theme.frontColor.disable + verticalAlignment: Text.AlignVCenter + } + } + } +} diff --git a/app/UI/Pages/Analysis/Filtering/AdvancedFilter/FilterFormField.qml b/app/UI/Pages/Analysis/Filtering/AdvancedFilter/FilterFormField.qml index c45fb07..fefb044 100644 --- a/app/UI/Pages/Analysis/Filtering/AdvancedFilter/FilterFormField.qml +++ b/app/UI/Pages/Analysis/Filtering/AdvancedFilter/FilterFormField.qml @@ -24,7 +24,7 @@ Rectangle Component.onDestruction: { model.newConditionModel.resetWizard.disconnect(resetView); - model.loading.resetWizard.disconnect(updateViewFromModel); + model.loadingChanged.disconnect(updateViewFromModel); } onZChanged: { diff --git a/app/UI/Pages/Analysis/Filtering/AdvancedFilter/LogicalBlock.qml b/app/UI/Pages/Analysis/Filtering/AdvancedFilter/LogicalBlock.qml index 399ff63..d983556 100644 --- a/app/UI/Pages/Analysis/Filtering/AdvancedFilter/LogicalBlock.qml +++ b/app/UI/Pages/Analysis/Filtering/AdvancedFilter/LogicalBlock.qml @@ -201,7 +201,7 @@ Rectangle color: "transparent" id: logicalSubItem - property bool isChecked: modelData.enabled + property bool isChecked onIsCheckedChanged: modelData.enabled = isChecked Rectangle diff --git a/app/UI/Pages/Analysis/Filtering/HelpPage.qml b/app/UI/Pages/Analysis/Filtering/HelpPage.qml index 5ea99a3..f5e6e1b 100644 --- a/app/UI/Pages/Analysis/Filtering/HelpPage.qml +++ b/app/UI/Pages/Analysis/Filtering/HelpPage.qml @@ -29,9 +29,9 @@ Rectangle Text { + id: nameLabel anchors.fill: header anchors.margins: 10 - text: model ? model.name : "" font.pixelSize: 20 font.weight: Font.Black color: Regovar.theme.primaryColor.back.dark diff --git a/app/UI/Pages/Analysis/Filtering/Quickfilter/PanelQuickForm.qml b/app/UI/Pages/Analysis/Filtering/Quickfilter/PanelQuickForm.qml index 16a7a45..e03302e 100644 --- a/app/UI/Pages/Analysis/Filtering/Quickfilter/PanelQuickForm.qml +++ b/app/UI/Pages/Analysis/Filtering/Quickfilter/PanelQuickForm.qml @@ -53,12 +53,13 @@ QuickFilterBox // Compute the final checked status of the "All" button var finalCheck = false; - for (var i = 0; i < content.children.length; ++i) + + for (var i = 0; i < container.children.length; ++i) { - var item = content.children[i]; - if (item.objectName == "QuickFilterFieldControl") + var item = container.children[i]; + if (item && item.objectName === "QuickFilterFieldControl") { - finalCheck = finalCheck || item.checked; + finalCheck = finalCheck || item.checked; } } @@ -75,15 +76,15 @@ QuickFilterBox anchors.left: parent.left anchors.right: parent.right - RowLayout { + spacing: 0 width: content.width + Item { height: 10; width: Regovar.theme.font.boxSize.header - 5 } CheckBox { id: panelAll - anchors.left: parent.left - anchors.leftMargin: 30 + Layout.fillWidth: true text: qsTr("All") checked: true onCheckedChanged: @@ -95,57 +96,111 @@ QuickFilterBox for (var i = 0; i < container.children.length; ++i) { var item = container.children[i]; - if (item.objectName == "QuickFilterFieldControl") + if (item && item.objectName === "QuickFilterFieldControl") { item.checked = false; } } + checkFinal(); internalUiUpdate = false; } - - checkFinal(); } } } - Repeater - { - id: panelRepeater - RowLayout + RowLayout + { + spacing: 0 + width: content.width + Item { height: 10; width: Regovar.theme.font.boxSize.header - 5} + Column { + id: container width: content.width - - CheckBox + Repeater { - id: gItem - objectName: "QuickFilterFieldControl" - width: container.width - text: modelData.label - checked: modelData.isActive - onCheckedChanged: + id: panelRepeater + + RowLayout { - modelData.isActive = checked - if (!internalUiUpdate) + id: gItem + width: content.width + CheckBox { - // Update other checkboxes - internalUiUpdate = true; - if (checked) + Layout.fillWidth: true + objectName: "QuickFilterFieldControl" + width: container.width + text: modelData.label + checked: modelData.isActive + onCheckedChanged: { - panelAll.checked = false; + modelData.isActive = checked + if (!internalUiUpdate) + { + // Update other checkboxes + internalUiUpdate = true; + if (checked) + { + panelAll.checked = false; + } + checkFinal(); + internalUiUpdate = false; + } } - checkFinal(); - internalUiUpdate = false; + } + + ButtonInline + { + iconTxt: "z" + text: "" + onClicked: regovar.getPanelInfo(modelData.id) } } } } } - ButtonInline - { - iconTxt: "à" - text: qsTr("Add panel") - } + // TODO: add other panel's version +// Item +// { +// id: newPanelButton +// width: content.width +// height: Regovar.theme.font.boxSize.normal +// property bool hovered: false +// RowLayout +// { +// anchors.fill: parent +// Item { height: 10; width: Regovar.theme.font.boxSize.header } +// Text +// { +// text: "à" +// width: Regovar.theme.font.boxSize.normal +// font.family: Regovar.theme.icons.name +// font.pixelSize: Regovar.theme.font.size.normal +// color: newPanelButton.hovered ? Regovar.theme.secondaryColor.back.normal : Regovar.theme.primaryColor.back.normal +// verticalAlignment: Text.AlignVCenter +// horizontalAlignment: Text.AlignHCenter +// } +// Text +// { +// Layout.fillWidth: true +// text: qsTr("Add panel") +// elide: Text.ElideRight +// font.pixelSize: Regovar.theme.font.size.normal +// color: newPanelButton.hovered ? Regovar.theme.secondaryColor.back.normal : Regovar.theme.primaryColor.back.normal +// verticalAlignment: Text.AlignVCenter +// } +// } + +// MouseArea +// { +// anchors.fill: parent +// hoverEnabled: true +// onEntered: parent.hovered = true +// onExited: parent.hovered = false +// onClicked: console.log("Not yet implemented") +// } +// } } } diff --git a/app/UI/Pages/Analysis/Filtering/Quickfilter/PhenotypeQuickFilter.qml b/app/UI/Pages/Analysis/Filtering/Quickfilter/PhenotypeQuickFilter.qml index 6bd4856..ab2191a 100644 --- a/app/UI/Pages/Analysis/Filtering/Quickfilter/PhenotypeQuickFilter.qml +++ b/app/UI/Pages/Analysis/Filtering/Quickfilter/PhenotypeQuickFilter.qml @@ -15,40 +15,7 @@ QuickFilterBox title : qsTr("Phenotype (HPO)") isExpanded: false - function reset() - { - // force the call of the checkUpdate true for "All" - frqAll.checked = false; - frqAll.checked = true; - } - - property bool internalUiUpdate: false - property real labelWidth: 50 - property real headLabelWidth: 50 - - property bool detailsExpanded: false - - - function checkFinal() - { -// // Compute the final checked status of the "All" button -// var finalCheck = gAll.checked; -// finalCheck = finalCheck || exacAll.checked; - -// for (var i = 0; i < container.children.length; ++i) -// { -// var item = container.children[i]; -// if (item.objectName == "QuickFilterFieldControl") -// { -// finalCheck = finalCheck || item.checked; -// } -// } - -// internalUiUpdate = true; -// frqAll.checked = !finalCheck; -// internalUiUpdate = false; - } onModelChanged: @@ -69,15 +36,36 @@ QuickFilterBox if (model && model.quickfilters && model.quickfilters.phenotypeFilter) { root.enabled = model.quickfilters.phenotypeFilter.isVisible(); + + + panelRepeater.model = model.quickfilters.phenotypeFilter.panelsList; + panelAll.visible = panelRepeater.model.length > 1; + if (!panelAll.visible) + { + panelAll.height = 0; + } } -// gAll.model = model.quickfilters.frequenceFilter._1000GAll; -// exacAll.model = model.quickfilters.frequenceFilter.exacAll; + } -// gRepeater.model = model.quickfilters.frequenceFilter._1000G; -// exacRepeater.model = model.quickfilters.frequenceFilter.exac; -// frqAll.checked = true; -// } + function checkFinal() + { + // Compute the final checked status of the "All" button + var finalCheck = false; + + + for (var i = 0; i < container.children.length; ++i) + { + var item = container.children[i]; + if (item && item.objectName === "QuickFilterFieldControl") + { + finalCheck = finalCheck || item.checked; + } + } + + internalUiUpdate = true; + panelAll.checked = !finalCheck; + internalUiUpdate = false; } content: Column @@ -89,183 +77,135 @@ QuickFilterBox // All - CheckBox - { - id: frqAll - anchors.left: parent.left - anchors.leftMargin: 25 - width: content.width - text: qsTr("All") - checked: true - onCheckedChanged: - { - // Todo - } - } - RowLayout { - width: content.width-30 - + spacing: 0 + width: content.width + Item { height: 10; width: Regovar.theme.font.boxSize.header - 5 } CheckBox { - anchors.left: parent.left - anchors.leftMargin: 25 + id: panelAll Layout.fillWidth: true - width: content.width - text: qsTr("Sample 1") - checked: false - } - - ButtonInline - { - iconTxt: "z" - text: "" + text: qsTr("All") + checked: true + onCheckedChanged: + { + // Update other checkboxes + if (!internalUiUpdate && checked) + { + internalUiUpdate = true; + for (var i = 0; i < container.children.length; ++i) + { + var item = container.children[i]; + if (item && item.objectName === "QuickFilterFieldControl") + { + item.checked = false; + } + } + checkFinal(); + internalUiUpdate = false; + } + } } } RowLayout { - width: content.width-30 - - CheckBox + spacing: 0 + width: content.width + Item { height: 10; width: Regovar.theme.font.boxSize.header - 5} + Rectangle { - anchors.left: parent.left - anchors.leftMargin: 25 Layout.fillWidth: true - width: content.width - text: qsTr("Sample 2") - checked: false - } + height: 200 + border.width: 1 + border.color: Regovar.theme.boxColor.border + color: Regovar.theme.boxColor.back + visible: false - ButtonInline - { - iconTxt: "z" - text: "" - } - } - - RowLayout - { - width: content.width-30 + ListView + { + model: ["OMIM:15454 Disease 1", "Microcephaly"] + anchors.fill: parent + anchors.margins: 5 - CheckBox - { - anchors.left: parent.left - anchors.leftMargin: 25 - Layout.fillWidth: true - width: content.width - text: qsTr("Sample 3") - checked: false - } + delegate: RowLayout + { + width: content.width + CheckBox + { + Layout.fillWidth: true + objectName: "QuickFilterFieldControl" + width: container.width + text: modelData.label + checked: modelData.isActive + onCheckedChanged: + { + modelData.isActive = checked + if (!internalUiUpdate) + { + // Update other checkboxes + internalUiUpdate = true; + if (checked) + { + panelAll.checked = false; + } + checkFinal(); + internalUiUpdate = false; + } + } + } - ButtonInline - { - iconTxt: "z" - text: "" + ButtonInline + { + iconTxt: "z" + text: "" + onClicked: regovar.getPanelInfo(modelData.id) + } + } + } } } - Rectangle + Item { + id: newPanelButton width: content.width height: Regovar.theme.font.boxSize.normal - color: "transparent" - - Text - { - id: collapseIcon - anchors.top: parent.top - anchors.bottom: parent.bottom - anchors.left: parent.left - anchors.leftMargin: 5 - width: Regovar.theme.font.boxSize.normal - height: Regovar.theme.font.boxSize.normal - text: "{" - font.family: Regovar.theme.icons.name - font.pixelSize: Regovar.theme.font.size.normal - color: root.enabled ? Regovar.theme.primaryColor.back.dark : Regovar.theme.frontColor.disable - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignHCenter - - rotation: detailsExpanded ? 90 : 0 - } - - Text + property bool hovered: false + RowLayout { anchors.fill: parent - anchors.leftMargin: 25 - text: qsTr("Search custom terms...") - elide: Text.ElideRight - font.pixelSize: Regovar.theme.font.size.normal - color: root.enabled ? Regovar.theme.primaryColor.back.dark : Regovar.theme.frontColor.disable - verticalAlignment: Text.AlignVCenter - } - - MouseArea - { - enabled: root.enabled - anchors.fill: parent - cursorShape: "PointingHandCursor" - onClicked: + Item { height: 10; width: Regovar.theme.font.boxSize.header } + Text { - detailsExpanded = !detailsExpanded + text: "à" + width: Regovar.theme.font.boxSize.normal + font.family: Regovar.theme.icons.name + font.pixelSize: Regovar.theme.font.size.normal + color: newPanelButton.hovered ? Regovar.theme.secondaryColor.back.normal : Regovar.theme.primaryColor.back.normal + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + } + Text + { + Layout.fillWidth: true + text: qsTr("Add phenotypes or diseases") + elide: Text.ElideRight + font.pixelSize: Regovar.theme.font.size.normal + color: newPanelButton.hovered ? Regovar.theme.secondaryColor.back.normal : Regovar.theme.primaryColor.back.normal + verticalAlignment: Text.AlignVCenter } - } - } - - Column - { - id: container - width: content.width - visible: root.enabled && root.detailsExpanded - - // - TextField - { - id: searchField - anchors.left: parent.left - anchors.leftMargin: 5 - width: content.width - 30 - - iconLeft: "z" - displayClearButton: true - placeholder: qsTr("Enter search terms...") } - Repeater + MouseArea { - id: gRepeater - - QuickFilterFieldControl - { - id: gItem - objectName: "QuickFilterFieldControl" - width: container.width - model: modelData - indentation: 25 - onCheckedChanged: - { - if (!internalUiUpdate) - { - // Update other checkboxes - internalUiUpdate = true; - if (checked) - { - frqAll.checked = false; - } - checkFinal(); - internalUiUpdate = false; - } - } - onLabelWidthChanged: - { - root.labelWidth = Math.max(labelWidth, root.labelWidth); - labelWidth = root.labelWidth; - } - Binding { target: gItem; property: "labelWidth"; value: root.labelWidth; } - } + anchors.fill: parent + hoverEnabled: true + onEntered: parent.hovered = true + onExited: parent.hovered = false + onClicked: console.log("Not yet implemented") } } } diff --git a/app/UI/Pages/Analysis/Filtering/Quickfilter/PositionQuickFilter.qml b/app/UI/Pages/Analysis/Filtering/Quickfilter/PositionQuickFilter.qml index 3da82a9..eaa33c6 100644 --- a/app/UI/Pages/Analysis/Filtering/Quickfilter/PositionQuickFilter.qml +++ b/app/UI/Pages/Analysis/Filtering/Quickfilter/PositionQuickFilter.qml @@ -63,11 +63,11 @@ QuickFilterBox RowLayout { width: content.width + Item { height: 10; width: 25 } CheckBox { id: posAll - anchors.left: parent.left - anchors.leftMargin: 30 + Layout.fillWidth: true text: qsTr("All") checked: true onCheckedChanged: @@ -95,11 +95,11 @@ QuickFilterBox RowLayout { width: content.width + Item { height: 10; width: 25 } CheckBox { id: posExo - anchors.left: parent.left - anchors.leftMargin: 30 + Layout.fillWidth: true text: qsTr("Exonic") checked: false onCheckedChanged: @@ -122,11 +122,11 @@ QuickFilterBox RowLayout { width: content.width + Item { height: 10; width: 25 } CheckBox { id: posIntro - anchors.left: parent.left - anchors.leftMargin: 30 + Layout.fillWidth: true text: qsTr("Intronic") checked: false onCheckedChanged: @@ -149,11 +149,11 @@ QuickFilterBox RowLayout { width: content.width + Item { height: 10; width: 25 } CheckBox { id: posUtr - anchors.left: parent.left - anchors.leftMargin: 30 + Layout.fillWidth: true text: qsTr("UTR") checked: false onCheckedChanged: @@ -176,11 +176,11 @@ QuickFilterBox RowLayout { width: content.width + Item { height: 10; width: 25 } CheckBox { id: posInter - anchors.left: parent.left - anchors.leftMargin: 30 + Layout.fillWidth: true text: qsTr("Intergenic") checked: false onCheckedChanged: @@ -203,11 +203,11 @@ QuickFilterBox RowLayout { width: content.width + Item { height: 10; width: 25 } CheckBox { id: posSpl - anchors.left: parent.left - anchors.leftMargin: 30 + Layout.fillWidth: true text: qsTr("Splice") checked: false onCheckedChanged: @@ -224,7 +224,6 @@ QuickFilterBox internalUiUpdate = false; } } - } } } diff --git a/app/UI/Pages/Analysis/Filtering/Quickfilter/QuickFilterBox.qml b/app/UI/Pages/Analysis/Filtering/Quickfilter/QuickFilterBox.qml index 7173738..e1a1d6e 100644 --- a/app/UI/Pages/Analysis/Filtering/Quickfilter/QuickFilterBox.qml +++ b/app/UI/Pages/Analysis/Filtering/Quickfilter/QuickFilterBox.qml @@ -21,7 +21,7 @@ Rectangle } property FilteringAnalysis model - property real maxHeight: content.height + 30 + property real maxHeight: content.height + Regovar.theme.font.boxSize.header property string title property bool isExpanded property Item content @@ -33,7 +33,7 @@ Rectangle anchors.left: root.left anchors.right: root.right - height: 30 + height: Regovar.theme.font.boxSize.header color: Regovar.theme.backgroundColor.main @@ -44,8 +44,8 @@ Rectangle anchors.top: header.top anchors.bottom: header.bottom anchors.left: header.left - width: 30 - height: 30 + width: Regovar.theme.font.boxSize.header + height: Regovar.theme.font.boxSize.header text: "{" font.family: Regovar.theme.icons.name font.pixelSize: Regovar.theme.font.size.header @@ -61,8 +61,8 @@ Rectangle anchors.top: header.top anchors.bottom: header.bottom anchors.left: header.left - anchors.leftMargin: 30 - anchors.rightMargin: 30 + anchors.leftMargin: Regovar.theme.font.boxSize.header + anchors.rightMargin: Regovar.theme.font.boxSize.header anchors.right: header.right text: title diff --git a/app/UI/Pages/Analysis/Filtering/Quickfilter/QuickFilterFieldControl.qml b/app/UI/Pages/Analysis/Filtering/Quickfilter/QuickFilterFieldControl.qml index 4e5b6c6..f62a0d8 100644 --- a/app/UI/Pages/Analysis/Filtering/Quickfilter/QuickFilterFieldControl.qml +++ b/app/UI/Pages/Analysis/Filtering/Quickfilter/QuickFilterFieldControl.qml @@ -39,11 +39,10 @@ RowLayout } + Item { height: 10; width: root.indentation - 10 } CheckBox { id: fieldCheck - anchors.left: parent.left - anchors.leftMargin: root.indentation onWidthChanged: root.labelWidth = width; text: "" } diff --git a/app/UI/Pages/Analysis/Filtering/Quickfilter/TransmissionQuickForm.qml b/app/UI/Pages/Analysis/Filtering/Quickfilter/TransmissionQuickForm.qml index 4b97e02..8c4e5ab 100644 --- a/app/UI/Pages/Analysis/Filtering/Quickfilter/TransmissionQuickForm.qml +++ b/app/UI/Pages/Analysis/Filtering/Quickfilter/TransmissionQuickForm.qml @@ -72,27 +72,29 @@ QuickFilterBox anchors.left: parent.left anchors.right: parent.right - Text - { - anchors.left: parent.left - anchors.leftMargin: 5 - elide: Text.ElideRight - font.pixelSize: Regovar.theme.font.size.normal - color: Regovar.theme.primaryColor.back.dark - text: qsTr("Mode") + RowLayout + { + width: content.width + Item { height: 10; width: 5 } + Text + { + Layout.fillWidth: true + elide: Text.ElideRight + font.pixelSize: Regovar.theme.font.size.normal + color: Regovar.theme.primaryColor.back.dark + text: qsTr("Mode") + } } - - RowLayout { width: content.width + Item { height: 10; width: 25 } CheckBox { id: modeAll - anchors.left: parent.left - anchors.leftMargin: 30 + Layout.fillWidth: true text: qsTr("All") checked: true onCheckedChanged: @@ -118,11 +120,11 @@ QuickFilterBox RowLayout { width: content.width + Item { height: 10; width: 25 } CheckBox { id: modeDom - anchors.left: parent.left - anchors.leftMargin: 30 + Layout.fillWidth: true text: qsTr("Dominant") checked: false onCheckedChanged: @@ -144,11 +146,11 @@ QuickFilterBox RowLayout { width: content.width + Item { height: 10; width: 25 } CheckBox { id: modeRec - anchors.left: parent.left - anchors.leftMargin: 30 + Layout.fillWidth: true text: qsTr("Recessive") checked: false onCheckedChanged: @@ -177,11 +179,11 @@ QuickFilterBox RowLayout { width: content.width + Item { height: 10; width: 50 } CheckBox { id: modeHtz - anchors.left: parent.left - anchors.leftMargin: 50 + Layout.fillWidth: true text: qsTr("Homozygous") checked: false onCheckedChanged: @@ -204,11 +206,11 @@ QuickFilterBox RowLayout { width: content.width + Item { height: 10; width: 50 } CheckBox { id: modeHtzComp - anchors.left: parent.left - anchors.leftMargin: 50 + Layout.fillWidth: true text: qsTr("Compound heterozygous") checked: false onCheckedChanged: @@ -230,24 +232,27 @@ QuickFilterBox } - Text + RowLayout { - anchors.left: parent.left - anchors.leftMargin: 5 - - elide: Text.ElideRight - font.pixelSize: Regovar.theme.font.size.normal - color: Regovar.theme.primaryColor.back.dark - text: qsTr("Segregation") + width: content.width + Item { height: 10; width: 5 } + Text + { + Layout.fillWidth: true + elide: Text.ElideRight + font.pixelSize: Regovar.theme.font.size.normal + color: Regovar.theme.primaryColor.back.dark + text: qsTr("Segregation") + } } RowLayout { width: content.width + Item { height: 10; width: 25 } CheckBox { id: segAll - anchors.left: parent.left - anchors.leftMargin: 30 + Layout.fillWidth: true text: qsTr("All") checked: true onCheckedChanged: @@ -270,11 +275,11 @@ QuickFilterBox RowLayout { width: content.width + Item { height: 10; width: 25 } CheckBox { id: segDen - anchors.left: parent.left - anchors.leftMargin: 30 + Layout.fillWidth: true text: qsTr("De novo") checked: false onCheckedChanged: @@ -296,11 +301,11 @@ QuickFilterBox RowLayout { width: content.width + Item { height: 10; width: 25 } CheckBox { id: segInh - anchors.left: parent.left - anchors.leftMargin: 30 + Layout.fillWidth: true text: qsTr("Inherited") checked: false onCheckedChanged: @@ -321,24 +326,27 @@ QuickFilterBox } - Text + RowLayout { - anchors.left: parent.left - anchors.leftMargin: 5 - - elide: Text.ElideRight - font.pixelSize: Regovar.theme.font.size.normal - color: Regovar.theme.primaryColor.back.dark - text: qsTr("Localisation") + width: content.width + Item { height: 10; width: 5 } + Text + { + Layout.fillWidth: true + elide: Text.ElideRight + font.pixelSize: Regovar.theme.font.size.normal + color: Regovar.theme.primaryColor.back.dark + text: qsTr("Localisation") + } } RowLayout { width: content.width + Item { height: 10; width: 25 } CheckBox { id: locAll - anchors.left: parent.left - anchors.leftMargin: 30 + Layout.fillWidth: true text: qsTr("All") checked: true onCheckedChanged: @@ -362,11 +370,11 @@ QuickFilterBox RowLayout { width: content.width + Item { height: 10; width: 25 } CheckBox { id: locAut - anchors.left: parent.left - anchors.leftMargin: 30 + Layout.fillWidth: true text: qsTr("Autosomal") checked: false onCheckedChanged: @@ -388,11 +396,11 @@ QuickFilterBox RowLayout { width: content.width + Item { height: 10; width: 25 } CheckBox { id: locXlk - anchors.left: parent.left - anchors.leftMargin: 30 + Layout.fillWidth: true text: qsTr("X-linked") checked: false onCheckedChanged: @@ -414,11 +422,11 @@ QuickFilterBox RowLayout { width: content.width + Item { height: 10; width: 25 } CheckBox { id: locMit - anchors.left: parent.left - anchors.leftMargin: 30 + Layout.fillWidth: true text: qsTr("Mitochondrial") checked: false onCheckedChanged: diff --git a/app/UI/Pages/Analysis/Filtering/Quickfilter/TypeQuickFilter.qml b/app/UI/Pages/Analysis/Filtering/Quickfilter/TypeQuickFilter.qml index 3e7bcaf..0d57729 100644 --- a/app/UI/Pages/Analysis/Filtering/Quickfilter/TypeQuickFilter.qml +++ b/app/UI/Pages/Analysis/Filtering/Quickfilter/TypeQuickFilter.qml @@ -62,11 +62,12 @@ QuickFilterBox RowLayout { width: content.width + + Item { height: 10; width: 25 } CheckBox { id: typAll - anchors.left: parent.left - anchors.leftMargin: 30 + Layout.fillWidth: true text: qsTr("All") checked: true onCheckedChanged: @@ -94,11 +95,11 @@ QuickFilterBox RowLayout { width: content.width + Item { height: 10; width: 25 } CheckBox { id: typMis - anchors.left: parent.left - anchors.leftMargin: 30 + Layout.fillWidth: true text: qsTr("Missense") checked: false onCheckedChanged: @@ -120,11 +121,11 @@ QuickFilterBox RowLayout { width: content.width + Item { height: 10; width: 25 } CheckBox { id: typStp - anchors.left: parent.left - anchors.leftMargin: 30 + Layout.fillWidth: true text: qsTr("Nonsense") checked: false onCheckedChanged: @@ -146,11 +147,11 @@ QuickFilterBox RowLayout { width: content.width + Item { height: 10; width: 25 } CheckBox { id: typSpl - anchors.left: parent.left - anchors.leftMargin: 30 + Layout.fillWidth: true text: qsTr("Splicing") checked: false onCheckedChanged: @@ -172,11 +173,11 @@ QuickFilterBox RowLayout { width: content.width + Item { height: 10; width: 25 } CheckBox { id: typInd - anchors.left: parent.left - anchors.leftMargin: 30 + Layout.fillWidth: true text: qsTr("Indel") checked: false onCheckedChanged: @@ -198,11 +199,11 @@ QuickFilterBox RowLayout { width: content.width + Item { height: 10; width: 25 } CheckBox { id: typSyn - anchors.left: parent.left - anchors.leftMargin: 30 + Layout.fillWidth: true text: qsTr("Synonymous") checked: false onCheckedChanged: diff --git a/app/UI/Pages/Analysis/Filtering/SummaryPage.qml b/app/UI/Pages/Analysis/Filtering/SummaryPage.qml index 58d4722..fe1ef8c 100644 --- a/app/UI/Pages/Analysis/Filtering/SummaryPage.qml +++ b/app/UI/Pages/Analysis/Filtering/SummaryPage.qml @@ -132,7 +132,17 @@ Rectangle Layout.alignment: Qt.AlignTop id: statusButton text: "" - onClicked: computingProgressLog.visible = true + onClicked: + { + if (root.model.status == "empty" || root.model.status == "close") + { + + } + else + { + computingProgressLog.visible = true + } + } ToolTip.text: qsTr("Display details") ToolTip.visible: hovered } diff --git a/app/UI/Pages/Project/AnalysesPage.qml b/app/UI/Pages/Project/AnalysesPage.qml index e16a85e..1b1d410 100644 --- a/app/UI/Pages/Project/AnalysesPage.qml +++ b/app/UI/Pages/Project/AnalysesPage.qml @@ -4,14 +4,27 @@ import QtQuick.Layouts 1.3 import "qrc:/qml/Regovar" import "qrc:/qml/Framework" +import "qrc:/qml/Pages/Analysis" Rectangle { id: root color: Regovar.theme.backgroundColor.main - property QtObject model property var currentAnalysis: null + property QtObject model + onModelChanged: + { + if(model) + { + model.dataChanged.connect(updateViewFromModel); + } + updateViewFromModel(); + } + Component.onDestruction: + { + model.dataChanged.disconnect(updateViewFromModel); + } Rectangle { @@ -35,7 +48,6 @@ Rectangle font.weight: Font.Black color: Regovar.theme.primaryColor.back.dark verticalAlignment: Text.AlignVCenter - text: (model) ? model.name : "" elide: Text.ElideRight } @@ -82,25 +94,7 @@ Rectangle { id: openAnalysis text: qsTr("Open") - onClicked: - { - var analysis = browser.model[browser.currentRow]; - regovar.analysesManager.openAnalysis(analysis.type, analysis.id) - } - } - Button - { - id: playPauseAnalysis - text: qsTr("Pause") - onClicked: console.log("Pause/Play analysis") - enabled: false - } - Button - { - id: deleteAnalysis - text: qsTr("Delete") - onClicked: console.log("Delete analysis") - enabled: false + onClicked: regovar.analysesManager.openAnalysis(currentAnalysis.type, currentAnalysis.id) } } @@ -122,35 +116,29 @@ Rectangle Layout.fillHeight: true color: Regovar.theme.backgroundColor.main + TextField + { + id: browserSearch + anchors.top: topPanel.top + anchors.left: topPanel.left + anchors.right: topPanel.right + anchors.topMargin: 10 + iconLeft: "z" + displayClearButton: true + placeholder: qsTr("Search analyses by names, dates, comments...") + onTextChanged: root.model.analyses.proxy.setFilterString(text) + } + TableView { id: browser - anchors.fill: parent - anchors.margins: 10 - anchors.leftMargin: 0 - model: (root.model) ? root.model.analyses : [] - onDoubleClicked: - { - var analysis = browser.model[currentRow]; - if (analysis) - { - regovar.analysesManager.openAnalysis(analysis.type, analysis.id); - } - } - - // Default delegate for all column - itemDelegate: Item - { - Text - { - anchors.leftMargin: 5 - anchors.fill: parent - verticalAlignment: Text.AlignVCenter - font.pixelSize: Regovar.theme.font.size.normal - text: styleData.value - elide: Text.ElideRight - } - } + anchors.top: browserSearch.bottom + anchors.left: topPanel.left + anchors.right: topPanel.right + anchors.bottom: topPanel.bottom + anchors.bottomMargin: 10 + anchors.topMargin: 10 + onDoubleClicked: regovar.analysesManager.openAnalysis(currentAnalysis.type, currentAnalysis.id) TableViewColumn { @@ -171,14 +159,6 @@ Rectangle { role: "updateDate" title: "Date" - delegate: Text - { - anchors.fill: parent - anchors.margins: 5 - verticalAlignment: Text.AlignVCenter - text: regovar.formatDate(modelData.dateOfBirth, false) - elide: Text.ElideRight - } } TableViewColumn { @@ -187,10 +167,20 @@ Rectangle width: 400 } - onCurrentRowChanged: { - displayCurrentAnalysisPreview(root.model.analyses[currentRow]); + var idx = root.model.analyses.proxy.getModelIndex(browser.currentRow); + var id = root.model.analyses.data(idx, 257); // 257 = Qt::UserRole+1 + var type = root.model.analyses.data(idx, 261); + + if (id && type === "analysis") + { + displayCurrentAnalysisPreview(regovar.analysesManager.getFilteringAnalysis(id)); + } + else if (id && type === "pipeline") + { + displayCurrentAnalysisPreview(regovar.analysesManager.getPipelineAnalysis(id)); + } } } } // end topPanel @@ -202,69 +192,27 @@ Rectangle color: Regovar.theme.backgroundColor.main Layout.minimumHeight: 200 - GridLayout + AnalysisPreview { anchors.fill: parent anchors.margins: 10 anchors.leftMargin: 0 anchors.rightMargin: 0 - columns: 3 - rowSpacing: 10 - columnSpacing: 10 - - Text - { - Layout.fillWidth: true - height: Regovar.theme.font.boxSize.header - elide: Text.ElideRight - font.pixelSize: Regovar.theme.font.size.header - verticalAlignment: Text.AlignVCenter - color: Regovar.theme.primaryColor.back.normal - text: currentAnalysis ? currentAnalysis.name : "" - } - - Text - { - height: Regovar.theme.font.boxSize.header - elide: Text.ElideRight - font.pixelSize: Regovar.theme.font.size.header - verticalAlignment: Text.AlignVCenter - color: Regovar.theme.primaryColor.back.normal - font.family: Regovar.theme.icons.name - text: currentAnalysis ? "H" : "" - } - Text - { - height: Regovar.theme.font.boxSize.header - elide: Text.ElideRight - font.pixelSize: Regovar.theme.font.size.header - verticalAlignment: Text.AlignVCenter - color: Regovar.theme.primaryColor.back.normal - text: currentAnalysis ? regovar.formatDate(currentAnalysis.updateDate) : "" - } - - Rectangle - { - color: "transparent" - Layout.fillWidth: true - Layout.fillHeight: true - Layout.columnSpan: 3 - border.width: 1 - border.color: Regovar.theme.boxColor.border - Text - { - anchors.centerIn: parent - text: qsTr("Not yet implemented") - font.pixelSize: Regovar.theme.font.size.normal - color: Regovar.theme.frontColor.disable - verticalAlignment: Text.AlignVCenter - } - } + model: currentAnalysis } } // end bottomPanel } // end SplitView + function updateViewFromModel() + { + if (root.model) + { + nameLabel.text = root.model.name; + browser.model = root.model.analyses.proxy; + } + } + function displayCurrentAnalysisPreview(analysis) { root.currentAnalysis = analysis; diff --git a/app/UI/Pages/Project/SubjectsPage.qml b/app/UI/Pages/Project/SubjectsPage.qml index e4a6ac3..555723b 100644 --- a/app/UI/Pages/Project/SubjectsPage.qml +++ b/app/UI/Pages/Project/SubjectsPage.qml @@ -73,19 +73,33 @@ Rectangle } + TextField + { + id: browserSearch + anchors.left: root.left + anchors.top: Regovar.helpInfoBoxDisplayed ? helpInfoBox.bottom : header.bottom + anchors.right: root.right + anchors.margins: 10 + iconLeft: "z" + displayClearButton: true + placeholder: qsTr("Search subjects by names, comments...") + onTextChanged: root.model.subjects.proxy.setFilterString(text) + } TableView { - id: subjectsList + id: browser anchors.left: root.left - anchors.top: Regovar.helpInfoBoxDisplayed ? helpInfoBox.bottom : header.bottom + anchors.top: browserSearch.bottom anchors.right: root.right anchors.bottom: root.bottom anchors.margins: 10 onDoubleClicked: { - regovar.subjectsManager.openSubject(subjectsList.model[subjectsList.currentRow].id); + var idx = root.model.subjects.proxy.getModelIndex(browser.currentRow); + var id = root.model.subjects.data(idx, 257); // 257 = Qt::UserRole+1 + regovar.subjectsManager.openSubject(id); } TableViewColumn @@ -93,33 +107,21 @@ Rectangle role: "identifier" title: qsTr("Identifier") } - TableViewColumn { role: "lastname" title: qsTr("Lastname") } - TableViewColumn { role: "firstname" title: qsTr("Firstname") } - TableViewColumn { role: "dateOfBirth" title: qsTr("Date of birth") - delegate: Text - { - anchors.fill: parent - anchors.margins: 5 - verticalAlignment: Text.AlignVCenter - text: regovar.formatDate(modelData.dateOfBirth, false) - elide: Text.ElideRight - } } - TableViewColumn { role: "comment" @@ -132,7 +134,7 @@ Rectangle if (root.model) { nameLabel.text = root.model.name; - subjectsList.model = root.model.subjects; + browser.model = root.model.subjects.proxy; } } } diff --git a/app/UI/Pages/Settings/ProfilePage.qml b/app/UI/Pages/Settings/ProfilePage.qml index a745ac3..cbe6419 100644 --- a/app/UI/Pages/Settings/ProfilePage.qml +++ b/app/UI/Pages/Settings/ProfilePage.qml @@ -104,14 +104,14 @@ Rectangle // USER INFORMATION ======================================================================= - GridLayout - { - Layout.fillWidth: true + GridLayout + { + Layout.fillWidth: true - columns: 2 - rows: 10 - columnSpacing: 30 - rowSpacing: 20 + columns: 2 + rows: 10 + columnSpacing: 30 + rowSpacing: 20 Row { diff --git a/app/UI/Pages/Subject/AnalysesPage.qml b/app/UI/Pages/Subject/AnalysesPage.qml index 09a0177..dfab01e 100644 --- a/app/UI/Pages/Subject/AnalysesPage.qml +++ b/app/UI/Pages/Subject/AnalysesPage.qml @@ -4,14 +4,27 @@ import QtQuick.Layouts 1.3 import "qrc:/qml/Regovar" import "qrc:/qml/Framework" +import "qrc:/qml/Pages/Analysis" Rectangle { id: root color: Regovar.theme.backgroundColor.main - property QtObject model property var currentAnalysis: null + property QtObject model + onModelChanged: + { + if(model) + { + model.dataChanged.connect(updateViewFromModel); + } + updateViewFromModel(); + } + Component.onDestruction: + { + model.dataChanged.disconnect(updateViewFromModel); + } Rectangle { @@ -35,7 +48,6 @@ Rectangle font.weight: Font.Black color: Regovar.theme.primaryColor.back.dark verticalAlignment: Text.AlignVCenter - text: model ? model.identifier + " : " + model.lastname.toUpperCase() + " " + model.firstname : "" elide: Text.ElideRight } @@ -78,25 +90,7 @@ Rectangle { id: openAnalysis text: qsTr("Open") - onClicked: - { - var analysis = browser.model[browser.currentRow]; - regovar.analysesManager.openAnalysis(analysis.type, analysis.id) - } - } - Button - { - id: playPauseAnalysis - text: qsTr("Pause") - onClicked: console.log("Pause/Play analysis") - enabled: false - } - Button - { - id: deleteAnalysis - text: qsTr("Delete") - onClicked: console.log("Delete analysis") - enabled: false + onClicked: regovar.analysesManager.openAnalysis(currentAnalysis.type, currentAnalysis.id) } } @@ -105,6 +99,7 @@ Rectangle anchors.top: Regovar.helpInfoBoxDisplayed ? helpInfoBox.bottom : header.bottom anchors.left: root.left anchors.leftMargin: 10 + anchors.rightMargin: 10 anchors.right: actionsPanel.left anchors.bottom: root.bottom orientation: Qt.Vertical @@ -113,31 +108,33 @@ Rectangle { id: topPanel width: root.width - color: Regovar.theme.backgroundColor.main Layout.minimumHeight: 200 Layout.fillHeight: true + color: Regovar.theme.backgroundColor.main + + TextField + { + id: browserSearch + anchors.top: topPanel.top + anchors.left: topPanel.left + anchors.right: topPanel.right + anchors.topMargin: 10 + iconLeft: "z" + displayClearButton: true + placeholder: qsTr("Search analyses by names, dates, comments...") + onTextChanged: root.model.analyses.proxy.setFilterString(text) + } TableView { id: browser - anchors.fill: parent - anchors.margins: 10 - anchors.leftMargin: 0 - model: (root.model) ? root.model.analyses : [] - - // Default delegate for all column - itemDelegate: Item - { - Text - { - anchors.leftMargin: 5 - anchors.fill: parent - verticalAlignment: Text.AlignVCenter - font.pixelSize: Regovar.theme.font.size.normal - text: styleData.value - elide: Text.ElideRight - } - } + anchors.top: browserSearch.bottom + anchors.left: topPanel.left + anchors.right: topPanel.right + anchors.bottom: topPanel.bottom + anchors.bottomMargin: 10 + anchors.topMargin: 10 + onDoubleClicked: regovar.analysesManager.openAnalysis(currentAnalysis.type, currentAnalysis.id) TableViewColumn { @@ -156,7 +153,7 @@ Rectangle } TableViewColumn { - role: "lastUpdate" + role: "updateDate" title: "Date" } TableViewColumn @@ -166,10 +163,20 @@ Rectangle width: 400 } - onCurrentRowChanged: { - displayCurrentAnalysisPreview(root.model.analyses[currentRow]); + var idx = root.model.analyses.proxy.getModelIndex(browser.currentRow); + var id = root.model.analyses.data(idx, 257); // 257 = Qt::UserRole+1 + var type = root.model.analyses.data(idx, 261); + + if (id && type === "analysis") + { + displayCurrentAnalysisPreview(regovar.analysesManager.getFilteringAnalysis(id)); + } + else if (id && type === "pipeline") + { + displayCurrentAnalysisPreview(regovar.analysesManager.getPipelineAnalysis(id)); + } } } } // end topPanel @@ -181,68 +188,27 @@ Rectangle color: Regovar.theme.backgroundColor.main Layout.minimumHeight: 200 - GridLayout + AnalysisPreview { anchors.fill: parent anchors.margins: 10 anchors.leftMargin: 0 - columns: 3 - rowSpacing: 10 - columnSpacing: 10 - - Text - { - Layout.fillWidth: true - height: Regovar.theme.font.boxSize.header - elide: Text.ElideRight - font.pixelSize: Regovar.theme.font.size.header - verticalAlignment: Text.AlignVCenter - color: Regovar.theme.primaryColor.back.normal - text: currentAnalysis ? currentAnalysis.name : "" - } - - Text - { - height: Regovar.theme.font.boxSize.header - elide: Text.ElideRight - font.pixelSize: Regovar.theme.font.size.header - verticalAlignment: Text.AlignVCenter - color: Regovar.theme.primaryColor.back.normal - font.family: Regovar.theme.icons.name - text: currentAnalysis ? "H" : "" - } - Text - { - height: Regovar.theme.font.boxSize.header - elide: Text.ElideRight - font.pixelSize: Regovar.theme.font.size.header - verticalAlignment: Text.AlignVCenter - color: Regovar.theme.primaryColor.back.normal - text: currentAnalysis ? regovar.formatDate(currentAnalysis.lastUpdate) : "" - } - - Rectangle - { - color: "transparent" - Layout.fillWidth: true - Layout.fillHeight: true - Layout.columnSpan: 3 - border.width: 1 - border.color: Regovar.theme.boxColor.border - Text - { - anchors.centerIn: parent - text: qsTr("Not yet implemented") - font.pixelSize: Regovar.theme.font.size.normal - color: Regovar.theme.frontColor.disable - verticalAlignment: Text.AlignVCenter - } - } + anchors.rightMargin: 0 + model: currentAnalysis } } // end bottomPanel } // end SplitView + function updateViewFromModel() + { + if (root.model) + { + nameLabel.text = root.model.identifier + " : " + root.model.lastname.toUpperCase() + " " + root.model.firstname; + browser.model = root.model.analyses.proxy; + } + } + function displayCurrentAnalysisPreview(analysis) { root.currentAnalysis = analysis; diff --git a/app/UI/Pages/Subject/PhenotypesPage.qml b/app/UI/Pages/Subject/PhenotypesPage.qml index e73ad29..b8b13e1 100644 --- a/app/UI/Pages/Subject/PhenotypesPage.qml +++ b/app/UI/Pages/Subject/PhenotypesPage.qml @@ -13,13 +13,13 @@ Rectangle color: Regovar.theme.backgroundColor.main property Subject model - property bool isInit: false + // property bool isInit: false onModelChanged: { if (model) { phenotypeList.model = model.phenotypes; - root.isInit = true; + // root.isInit = true; } } @@ -93,7 +93,7 @@ Rectangle id: removePhenotype Layout.fillWidth: true text: qsTr("Remove") - onClicked: root.model.phenotypes.removeHpo(phenotypeList.getAt(phenotypeList.currentRow)) + onClicked: root.model.removeHpo(phenotypeList.model.getAt(phenotypeList.currentRow)) } Item { diff --git a/app/UI/Pages/Subject/SamplesPage.qml b/app/UI/Pages/Subject/SamplesPage.qml index e8265fc..fb3e3d1 100644 --- a/app/UI/Pages/Subject/SamplesPage.qml +++ b/app/UI/Pages/Subject/SamplesPage.qml @@ -12,6 +12,18 @@ Rectangle color: Regovar.theme.backgroundColor.main property QtObject model + onModelChanged: + { + if(model) + { + model.dataChanged.connect(updateViewFromModel); + } + updateViewFromModel(); + } + Component.onDestruction: + { + model.dataChanged.disconnect(updateViewFromModel); + } Rectangle { @@ -35,7 +47,6 @@ Rectangle font.weight: Font.Black color: Regovar.theme.primaryColor.back.dark verticalAlignment: Text.AlignVCenter - text: model ? model.identifier + " : " + model.lastname.toUpperCase() + " " + model.firstname : "-" elide: Text.ElideRight } @@ -72,43 +83,23 @@ Rectangle id: tableView Layout.fillWidth: true Layout.fillHeight: true - model: root.model ? root.model.samples : [] - selectionMode: SelectionMode.ExtendedSelection - - onDoubleClicked: - { - var sample = root.model.samples[tableView.currentRow]; - regovar.getSampleInfo(sample.id); - } - - + onDoubleClicked: regovar.getSampleInfo(currentSample().id) property var statusIcons: ["m", "/", "n", "h"] TableViewColumn { title: qsTr("Reference") role: "reference" - - delegate: Item - { - - Text - { - anchors.leftMargin: 5 - anchors.left: parent.left - anchors.verticalCenter: parent.verticalCenter - verticalAlignment: Text.AlignVCenter - horizontalAlignment: styleData.textAlignment - font.pixelSize: Regovar.theme.font.size.normal - text: styleData.value.name - } - } } - TableViewColumn { title: qsTr("Sample"); role: "name"; horizontalAlignment: Text.AlignLeft; } + TableViewColumn + { + title: qsTr("Sample"); + role: "name"; + } TableViewColumn { title: "Status" - role: "statusUI" + role: "status" delegate: Item { @@ -157,11 +148,10 @@ Rectangle } } } - TableViewColumn { title: qsTr("Source") - role: "sourceUI" + role: "source" delegate: Item { Text @@ -190,17 +180,18 @@ Rectangle } } } - TableViewColumn { title: qsTr("Comment"); role: "comment" } + TableViewColumn + { + title: qsTr("Comment"); + role: "comment" + } Rectangle { - id: helpPanel + id: emptyPanel anchors.fill: parent - color: Regovar.theme.backgroundColor.overlay - visible: root.model ? root.model.samples.length == 0 : true - Text { text: qsTr("No sample associated to this subject.\nClick on the opposite button to add it.") @@ -244,11 +235,7 @@ Rectangle { id: editButton text: qsTr("Open sample") - onClicked: - { - var sample = root.model.samples[tableView.currentRow]; - regovar.getSampleInfo(sample.id); - } + onClicked: regovar.getSampleInfo(currentSample().id) Component.onCompleted: actionColumn.maxWidth = Math.max(actionColumn.maxWidth, width) } Button @@ -256,13 +243,7 @@ Rectangle id: remButton text: qsTr("Remove sample") Component.onCompleted: actionColumn.maxWidth = Math.max(actionColumn.maxWidth, width) - onClicked: - { - tableView.selection.forEach( function(rowIndex) - { - root.model.removeSample(root.model.samples[rowIndex]); - }); - } + onClicked: root.model.removeSample(currentSample()) } } } @@ -278,4 +259,21 @@ Rectangle } } } + + function updateViewFromModel() + { + if (root.model) + { + nameLabel.text = root.model.identifier + " : " + root.model.lastname.toUpperCase() + " " + root.model.firstname; + tableView.model = root.model.samples.proxy; + emptyPanel.visible = root.model.samples.length > 0; + } + } + + function currentSample() + { + var idx = root.model.samples.proxy.getModelIndex(tableView.currentRow); + var id = root.model.samples.data(idx, 257); // 257 = Qt::UserRole+1 + return regovar.samplesManager.getOrCreateSample(id); + } } diff --git a/app/UI/Windows/UserInfoWindow.qml b/app/UI/Windows/UserInfoWindow.qml index a09d07f..bf0644f 100644 --- a/app/UI/Windows/UserInfoWindow.qml +++ b/app/UI/Windows/UserInfoWindow.qml @@ -32,6 +32,6 @@ Window { winId = cppWinId; userInfoPanel.model = regovar.getWindowModels(winId); - title = userInfoPanel.model.name; + title = userInfoPanel.model.firstname + " " + userInfoPanel.model.lastname; } } diff --git a/app/UI/qml.qrc b/app/UI/qml.qrc index 400fc27..2af1564 100644 --- a/app/UI/qml.qrc +++ b/app/UI/qml.qrc @@ -233,5 +233,7 @@ Windows/UserInfoWindow.qml Windows/VariantInfoWindow.qml Dialogs/InfoDialog.qml + Pages/Analysis/AnalysisPreview.qml + InformationPanel/Gene/PanelsPanel.qml diff --git a/app/Widgets/VariantsTreeWidget/variantstreewidget.h b/app/Widgets/VariantsTreeWidget/variantstreewidget.h index 6572fd0..e764a5e 100644 --- a/app/Widgets/VariantsTreeWidget/variantstreewidget.h +++ b/app/Widgets/VariantsTreeWidget/variantstreewidget.h @@ -13,7 +13,7 @@ class VariantsTreeWidget : public QQuickPaintedItem public: // Constructors - explicit VariantsTreeWidget(QQuickItem* parent=nullptr); + VariantsTreeWidget(QQuickItem* parent=nullptr); // Getters inline ResultsTreeModel* model() const { return mModel; } diff --git a/app/main.cpp b/app/main.cpp index e060ca5..c24faf4 100644 --- a/app/main.cpp +++ b/app/main.cpp @@ -13,6 +13,7 @@ #include "Model/framework/treeitem.h" #include "Model/framework/genericproxymodel.h" #include "Model/project/project.h" +#include "Model/analysis/analysis.h" #include "Model/analysis/filtering/filteringanalysis.h" #include "Model/analysis/pipeline/pipelineanalysis.h" #include "Model/subject/reference.h" @@ -24,6 +25,8 @@ #include "Model/panel/panel.h" #include "Model/panel/panelversion.h" #include "Model/panel/panelentry.h" +#include "Model/phenotype/geneslistmodel.h" +#include "Model/panel/panelslistmodel.h" #include "Widgets/VariantsTreeWidget/variantstreewidget.h" @@ -44,6 +47,7 @@ int main(int argc, char *argv[]) qmlRegisterType("Regovar.Core", 1, 0, "GenericProxyModel"); qmlRegisterType("Regovar.Core", 1, 0, "AnnotationModel"); + qmlRegisterType("Regovar.Core", 1, 0, "Analysis"); qmlRegisterType("Regovar.Core", 1, 0, "FilteringAnalysis"); qmlRegisterType("Regovar.Core", 1, 0, "PipelineAnalysis"); qmlRegisterType("Regovar.Core", 1, 0, "FieldColumnInfos"); @@ -62,6 +66,8 @@ int main(int argc, char *argv[]) qmlRegisterType("Regovar.Core", 1, 0, "FilesListModel"); qmlRegisterType("Regovar.Core", 1, 0, "FilesTreeModel"); qmlRegisterType("Regovar.Core", 1, 0, "PhenotypesListModel"); + qmlRegisterType("Regovar.Core", 1, 0, "GenesListModel"); + qmlRegisterType("Regovar.Core", 1, 0, "PanelsListModel"); qmlRegisterType("Regovar.Core", 1, 0, "User"); qmlRegisterType("Regovar.Core", 1, 0, "Project");