浏览代码

pushing dashboard to opengogs

darraghb 5 年之前
当前提交
a1d7dae775
共有 100 个文件被更改,包括 8958 次插入0 次删除
  1. 339 0
      LICENSE.txt
  2. 14 0
      Luzzu Dashboard/.editorconfig
  3. 2 0
      Luzzu Dashboard/.env
  4. 23 0
      Luzzu Dashboard/.gitignore
  5. 68 0
      Luzzu Dashboard/README.md
  6. 72 0
      Luzzu Dashboard/package.json
  7. 0 0
      Luzzu Dashboard/public/assets/.gitkeep
  8. 二进制
      Luzzu Dashboard/public/assets/img/favicon.png
  9. 二进制
      Luzzu Dashboard/public/favicon.ico
  10. 51 0
      Luzzu Dashboard/public/index.html
  11. 15 0
      Luzzu Dashboard/public/manifest.json
  12. 32 0
      Luzzu Dashboard/src/App.js
  13. 11 0
      Luzzu Dashboard/src/App.scss
  14. 9 0
      Luzzu Dashboard/src/App.test.js
  15. 32 0
      Luzzu Dashboard/src/_nav.js
  16. 二进制
      Luzzu Dashboard/src/assets/cloud-upload.png
  17. 3 0
      Luzzu Dashboard/src/assets/img/brand/icon.svg
  18. 3 0
      Luzzu Dashboard/src/assets/img/brand/logo.svg
  19. 24 0
      Luzzu Dashboard/src/config/dashboardProperties.js
  20. 45 0
      Luzzu Dashboard/src/config/exceptionInfoIconDetails.js
  21. 32 0
      Luzzu Dashboard/src/config/metricKnowledgeBaseMapping.js
  22. 30 0
      Luzzu Dashboard/src/config/metricViewMapping.js
  23. 30 0
      Luzzu Dashboard/src/containers/DefaultLayout/DefaultFooter.js
  24. 45 0
      Luzzu Dashboard/src/containers/DefaultLayout/DefaultHeader-Working.js
  25. 41 0
      Luzzu Dashboard/src/containers/DefaultLayout/DefaultHeader.js
  26. 90 0
      Luzzu Dashboard/src/containers/DefaultLayout/DefaultLayout.js
  27. 5 0
      Luzzu Dashboard/src/containers/DefaultLayout/index.js
  28. 6 0
      Luzzu Dashboard/src/containers/DefaultLayout/package.json
  29. 5 0
      Luzzu Dashboard/src/containers/index.js
  30. 1 0
      Luzzu Dashboard/src/index.css
  31. 21 0
      Luzzu Dashboard/src/index.js
  32. 44 0
      Luzzu Dashboard/src/polyfill.js
  33. 26 0
      Luzzu Dashboard/src/routes.js
  34. 110 0
      Luzzu Dashboard/src/scss/_custom.scss
  35. 11 0
      Luzzu Dashboard/src/scss/_ie-fix.scss
  36. 1 0
      Luzzu Dashboard/src/scss/_variables.scss
  37. 14 0
      Luzzu Dashboard/src/scss/style.scss
  38. 0 0
      Luzzu Dashboard/src/scss/vendors/.gitkeep
  39. 4 0
      Luzzu Dashboard/src/scss/vendors/_variables.scss
  40. 127 0
      Luzzu Dashboard/src/serviceWorker.js
  41. 166 0
      Luzzu Dashboard/src/services/datasetConfigDetails/datasetConfigDetails.js
  42. 40 0
      Luzzu Dashboard/src/services/luzzuFrameWorkAPIs/cancelAssessment.js
  43. 30 0
      Luzzu Dashboard/src/services/luzzuFrameWorkAPIs/getAllMetricsAvailableForAssessment.js
  44. 30 0
      Luzzu Dashboard/src/services/luzzuFrameWorkAPIs/getStatusUpdate.js
  45. 64 0
      Luzzu Dashboard/src/services/luzzuFrameWorkAPIs/triggerAssessment.js
  46. 40 0
      Luzzu Dashboard/src/services/tripleStoreAPIs/deleteGraphFromTripleStore.js
  47. 33 0
      Luzzu Dashboard/src/services/tripleStoreAPIs/readFromTripleStore.js
  48. 28 0
      Luzzu Dashboard/src/services/tripleStoreAPIs/sparql/QueryListOfAssessedDatasets.js
  49. 23 0
      Luzzu Dashboard/src/services/tripleStoreAPIs/sparql/checkDatasetExists.js
  50. 24 0
      Luzzu Dashboard/src/services/tripleStoreAPIs/sparql/checkDatasetPLDExists.js
  51. 21 0
      Luzzu Dashboard/src/services/tripleStoreAPIs/sparql/fetchDistinctResourceCount.js
  52. 22 0
      Luzzu Dashboard/src/services/tripleStoreAPIs/sparql/fetchListOfSimilarResource.js
  53. 21 0
      Luzzu Dashboard/src/services/tripleStoreAPIs/sparql/fetchListOfSimilarTriples.js
  54. 22 0
      Luzzu Dashboard/src/services/tripleStoreAPIs/sparql/fetchTriplesForResource.js
  55. 40 0
      Luzzu Dashboard/src/services/tripleStoreAPIs/sparql/get1SpatialAssessmentData.js
  56. 26 0
      Luzzu Dashboard/src/services/tripleStoreAPIs/sparql/get1SpatialAssessmentMetrics.js
  57. 39 0
      Luzzu Dashboard/src/services/tripleStoreAPIs/sparql/get1SpatialDimensions.js
  58. 20 0
      Luzzu Dashboard/src/services/tripleStoreAPIs/sparql/get1SpatialMappings.js
  59. 27 0
      Luzzu Dashboard/src/services/tripleStoreAPIs/sparql/getAllAssessmentDates.js
  60. 43 0
      Luzzu Dashboard/src/services/tripleStoreAPIs/sparql/getAssessmentQuality.js
  61. 21 0
      Luzzu Dashboard/src/services/tripleStoreAPIs/sparql/getGraphList.js
  62. 27 0
      Luzzu Dashboard/src/services/tripleStoreAPIs/sparql/getRecentAssessmentDates.js
  63. 50 0
      Luzzu Dashboard/src/services/tripleStoreAPIs/sparql/getRecentAssessmentQuality.js
  64. 41 0
      Luzzu Dashboard/src/services/tripleStoreAPIs/sparql/problemReport/getErrorReportForModelDefault-Working.js
  65. 41 0
      Luzzu Dashboard/src/services/tripleStoreAPIs/sparql/problemReport/getErrorReportForModelDefault.js
  66. 34 0
      Luzzu Dashboard/src/services/tripleStoreAPIs/sparql/problemReport/getErrorReportForModelProblematicTriple.js
  67. 35 0
      Luzzu Dashboard/src/services/tripleStoreAPIs/sparql/problemReport/getErrorReportForQuad.js
  68. 31 0
      Luzzu Dashboard/src/services/tripleStoreAPIs/sparql/problemReport/getErrorReportForResource.js
  69. 56 0
      Luzzu Dashboard/src/services/tripleStoreAPIs/sparql/problemReport/listOfAllMetricsFailedForResource-Working.js
  70. 51 0
      Luzzu Dashboard/src/services/tripleStoreAPIs/sparql/problemReport/listOfAllMetricsFailedForResource.js
  71. 34 0
      Luzzu Dashboard/src/services/tripleStoreAPIs/statusUpdate.js
  72. 43 0
      Luzzu Dashboard/src/services/tripleStoreAPIs/uploadToTripleStore.js
  73. 15 0
      Luzzu Dashboard/src/setupTests.js
  74. 100 0
      Luzzu Dashboard/src/store/connectDatasetReducer.js
  75. 148 0
      Luzzu Dashboard/src/store/datasetDetailsCacheReducer.js
  76. 39 0
      Luzzu Dashboard/src/views/Dashboard/ConnectDataSet/ConnectDataSet.js
  77. 526 0
      Luzzu Dashboard/src/views/Dashboard/ConnectDataSet/DatasetConnectConfigModelWindow.js
  78. 739 0
      Luzzu Dashboard/src/views/Dashboard/ConnectDataSet/DatasetConnectOptionsModelWindow.js
  79. 17 0
      Luzzu Dashboard/src/views/Dashboard/ConnectDataSet/fileLargeError.js
  80. 53 0
      Luzzu Dashboard/src/views/Dashboard/ConnectDataSet/problemsModel.js
  81. 378 0
      Luzzu Dashboard/src/views/Dashboard/Dashboard.js
  82. 351 0
      Luzzu Dashboard/src/views/Dashboard/DataSetLarge/DataSetLarge.js
  83. 14 0
      Luzzu Dashboard/src/views/Dashboard/loading.js
  84. 17 0
      Luzzu Dashboard/src/views/Dashboard/loadingFailed.js
  85. 6 0
      Luzzu Dashboard/src/views/Dashboard/package.json
  86. 18 0
      Luzzu Dashboard/src/views/Dashboard/tripleStoreUploadError.js
  87. 400 0
      Luzzu Dashboard/src/views/DataSetDetails/AnalyticsWidget/AnalysisWidget-Working.js
  88. 473 0
      Luzzu Dashboard/src/views/DataSetDetails/AnalyticsWidget/AnalysisWidget.js
  89. 317 0
      Luzzu Dashboard/src/views/DataSetDetails/AnalyticsWidget/DetailsModels/ProblematicThingViewDetails.js
  90. 393 0
      Luzzu Dashboard/src/views/DataSetDetails/AnalyticsWidget/DetailsModels/ResourceViewDetails.js
  91. 58 0
      Luzzu Dashboard/src/views/DataSetDetails/AnalyticsWidget/MetricFilter.js
  92. 407 0
      Luzzu Dashboard/src/views/DataSetDetails/AnalyticsWidget/PerformAnalysis-Working.js
  93. 480 0
      Luzzu Dashboard/src/views/DataSetDetails/AnalyticsWidget/PerformAnalysis.js
  94. 50 0
      Luzzu Dashboard/src/views/DataSetDetails/AnalyticsWidget/ProblematicThingList.js
  95. 50 0
      Luzzu Dashboard/src/views/DataSetDetails/AnalyticsWidget/ResourceList.js
  96. 131 0
      Luzzu Dashboard/src/views/DataSetDetails/AnalyticsWidget/SummaryReport.js
  97. 423 0
      Luzzu Dashboard/src/views/DataSetDetails/DataQualityOverTime.js
  98. 135 0
      Luzzu Dashboard/src/views/DataSetDetails/DataSetDetails.js
  99. 282 0
      Luzzu Dashboard/src/views/DataSetDetails/DimensionDetails/DimensionDetails.js
  100. 329 0
      Luzzu Dashboard/src/views/DataSetDetails/DimensionDetails/ErrorReport-Working.js

+ 339 - 0
LICENSE.txt

@@ -0,0 +1,339 @@
+GNU GENERAL PUBLIC LICENSE
+                       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc., <http://fsf.org/>
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                            NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    {description}
+    Copyright (C) {year}  {fullname}
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  {signature of Ty Coon}, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.

+ 14 - 0
Luzzu Dashboard/.editorconfig

@@ -0,0 +1,14 @@
+# Editor configuration, see http://editorconfig.org
+root = true
+
+[*]
+charset = utf-8
+end_of_line = lf
+indent_size = 2
+indent_style = space
+insert_final_newline = true
+trim_trailing_whitespace = true
+
+[*.md]
+max_line_length = off
+trim_trailing_whitespace = false

+ 2 - 0
Luzzu Dashboard/.env

@@ -0,0 +1,2 @@
+PORT=3000
+CHOKIDAR_USEPOLLING=true

+ 23 - 0
Luzzu Dashboard/.gitignore

@@ -0,0 +1,23 @@
+# See https://help.github.com/ignore-files/ for more about ignoring files.
+
+# dependencies
+/node_modules
+package-lock.json
+
+# testing
+/coverage
+
+# production
+/build
+
+# misc
+.DS_Store
+.idea
+.env.local
+.env.development.local
+.env.test.local
+.env.production.local
+
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*

+ 68 - 0
Luzzu Dashboard/README.md

@@ -0,0 +1,68 @@
+This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
+
+## Available Scripts
+
+In the project directory, you can run:
+
+### `npm start`
+
+Runs the app in the development mode.<br>
+Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
+
+The page will reload if you make edits.<br>
+You will also see any lint errors in the console.
+
+### `npm test`
+
+Launches the test runner in the interactive watch mode.<br>
+See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
+
+### `npm run build`
+
+Builds the app for production to the `build` folder.<br>
+It correctly bundles React in production mode and optimizes the build for the best performance.
+
+The build is minified and the filenames include the hashes.<br>
+Your app is ready to be deployed!
+
+See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
+
+### `npm run eject`
+
+**Note: this is a one-way operation. Once you `eject`, you can’t go back!**
+
+If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
+
+Instead, it will copy all the configuration files and the transitive dependencies (Webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
+
+You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
+
+## Learn More
+
+You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
+
+To learn React, check out the [React documentation](https://reactjs.org/).
+
+### Code Splitting
+
+This section has moved here: https://facebook.github.io/create-react-app/docs/code-splitting
+
+### Analyzing the Bundle Size
+
+This section has moved here: https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size
+
+### Making a Progressive Web App
+
+This section has moved here: https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app
+
+### Advanced Configuration
+
+This section has moved here: https://facebook.github.io/create-react-app/docs/advanced-configuration
+
+### Deployment
+
+This section has moved here: https://facebook.github.io/create-react-app/docs/deployment
+
+### `npm run build` fails to minify
+
+This section has moved here: https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify

+ 72 - 0
Luzzu Dashboard/package.json

@@ -0,0 +1,72 @@
+{
+  "name": "luzzu-dashboard",
+  "version": "0.1.0",
+  "private": true,
+  "dependencies": {
+    "@coreui/coreui": "^2.1.9",
+    "@coreui/coreui-plugin-chartjs-custom-tooltips": "^1.2.0",
+    "@coreui/icons": "0.3.0",
+    "@coreui/react": "~2.1.5",
+    "axios": "0.18.0",
+    "basic-auth": "^2.0.1",
+    "bootstrap": "^4.4.1",
+    "chart.js": "^2.8.0",
+    "chartjs-plugin-annotation": "^0.5.7",
+    "classnames": "^2.2.6",
+    "core-js": "^2.6.5",
+    "enzyme": "^3.9.0",
+    "enzyme-adapter-react-16": "^1.11.2",
+    "flag-icon-css": "^3.3.0",
+    "font-awesome": "^4.7.0",
+    "lodash": "^4.17.11",
+    "node-sass": "^4.11.0",
+    "prop-types": "^15.7.2",
+    "query-string": "6.2.0",
+    "react": "^16.8.5",
+    "react-app-polyfill": "^0.2.2",
+    "react-bootstrap": "^1.0.0-beta.16",
+    "react-bootstrap-switch": "^15.5.3",
+    "react-chartjs-2": "^2.7.4",
+    "react-circular-progressbar": "^1.0.0",
+    "react-dom": "^16.8.5",
+    "react-fileupload-progress": "^0.4.2",
+    "react-lineto": "^3.1.3",
+    "react-rangeslider": "^2.2.0",
+    "react-redux": "^6.0.1",
+    "react-router-config": "^4.4.0-beta.8",
+    "react-router-dom": "~4.3.1",
+    "react-switch": "^5.0.1",
+    "react-test-renderer": "^16.8.5",
+    "reactstrap": "^7.1.0",
+    "redux": "^4.0.1",
+    "simple-line-icons": "^2.4.1"
+  },
+  "devDependencies": {
+    "react-scripts": "^2.1.8"
+  },
+  "scripts": {
+    "start": "react-scripts start",
+    "build": "react-scripts build",
+    "test": "react-scripts test",
+    "test:cov": "react-scripts test --coverage",
+    "test:debug": "react-scripts --inspect-brk test --runInBand",
+    "eject": "react-scripts eject"
+  },
+  "eslintConfig": {
+    "extends": "react-app"
+  },
+  "browserslist": [
+    ">0.2%",
+    "not dead",
+    "not ie <= 9",
+    "not op_mini all"
+  ],
+  "jest": {
+    "collectCoverageFrom": [
+      "src/**/*.{js,jsx}",
+      "!**/*index.js",
+      "!src/serviceWorker.js",
+      "!src/polyfill.js"
+    ]
+  }
+}

+ 0 - 0
Luzzu Dashboard/public/assets/.gitkeep


二进制
Luzzu Dashboard/public/assets/img/favicon.png


二进制
Luzzu Dashboard/public/favicon.ico


+ 51 - 0
Luzzu Dashboard/public/index.html

@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="utf-8">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge">
+    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
+    <meta name="description" content="Dashboard for Luzzu Assessment Tool">
+    <meta name="author" content="Ramneesh Vaidyambath">
+    <title>Luzzu Dashboard</title>
+
+    <link rel="manifest" href="%PUBLIC_URL%/manifest.json">
+    <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
+    <!--
+      Notice the use of %PUBLIC_URL% in the tags above.
+      It will be replaced with the URL of the `public` folder during the build.
+      Only files inside the `public` folder can be referenced from the HTML.
+
+      Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
+      work correctly both with client-side routing and a non-root public URL.
+      Learn how to configure a non-root public URL by running `npm run build`.
+    -->
+    <!-- Global site tag (gtag.js) - Google Analytics -->
+    <script async src="https://www.googletagmanager.com/gtag/js?id=UA-118965717-3"></script>
+    <script>
+      window.dataLayer = window.dataLayer || [];
+      function gtag(){dataLayer.push(arguments);}
+      gtag('js', new Date());
+      // Shared ID
+      gtag('config', 'UA-118965717-3');
+      // React.js ID
+      gtag('config', 'UA-118965717-6');
+    </script>
+  </head>
+
+  <body>
+    <noscript>
+      You need to enable JavaScript to run this app.
+    </noscript>
+    <div id="root"></div>
+    <!--
+      This HTML file is a template.
+      If you open it directly in the browser, you will see an empty page.
+
+      You can add webfonts, meta tags, or analytics to this file.
+      The build step will place the bundled scripts into the <body> tag.
+
+      To begin the development, run `npm start` or `yarn start`.
+      To create a production bundle, use `npm run build` or `yarn build`.
+    -->
+  </body>
+</html>

+ 15 - 0
Luzzu Dashboard/public/manifest.json

@@ -0,0 +1,15 @@
+{
+  "short_name": "Luzzu Dashboard",
+  "name": "Luzzu Dashboard",
+  "icons": [
+    {
+      "src": "./assets/img/favicon.png",
+      "sizes": "100x100",
+      "type": "image/png"
+    }
+  ],
+  "start_url": ".",
+  "display": "standalone",
+  "theme_color": "#000000",
+  "background_color": "#ffffff"
+}

+ 32 - 0
Luzzu Dashboard/src/App.js

@@ -0,0 +1,32 @@
+//© 2019 Dublin City University, Trinity College Dublin. All rights reserved. This material may not be reproduced, displayed, modified or distributed without the express prior written permission of the copyright holder.
+import React, { Component } from 'react';
+import { BrowserRouter } from 'react-router-dom';
+// import { renderRoutes } from 'react-router-config';
+import './App.scss';
+
+const loading = () => <div className="animated fadeIn pt-3 text-center">Loading...</div>;
+
+// Containers
+const DefaultLayout = React.lazy(() => import('./containers/DefaultLayout'));
+
+
+
+class App extends Component {
+
+  componentDidMount =() =>
+  {
+
+  }
+
+    render() {
+        return (
+            <BrowserRouter>
+                <React.Suspense fallback={loading()}>
+                    <DefaultLayout />
+                </React.Suspense>
+            </BrowserRouter>
+        );
+    }
+}
+
+export default App;

+ 11 - 0
Luzzu Dashboard/src/App.scss

@@ -0,0 +1,11 @@
+// Styles
+// CoreUI Icons Set
+@import '~@coreui/icons/css/coreui-icons.css';
+// Import Flag Icons Set
+@import '~flag-icon-css/css/flag-icon.min.css';
+// Import Font Awesome Icons Set
+@import '~font-awesome/css/font-awesome.min.css';
+// Import Simple Line Icons Set
+@import '~simple-line-icons/css/simple-line-icons.css';
+// Import Main styles for this application
+@import './scss/style.scss';

+ 9 - 0
Luzzu Dashboard/src/App.test.js

@@ -0,0 +1,9 @@
+import React from 'react';
+import {shallow} from 'enzyme/build';
+import App from './App';
+
+
+it('mounts without crashing', () => {
+  const wrapper = shallow(<App />);
+  wrapper.unmount()
+});

+ 32 - 0
Luzzu Dashboard/src/_nav.js

@@ -0,0 +1,32 @@
+//© 2019 Dublin City University, Trinity College Dublin. All rights reserved. This material may not be reproduced, displayed, modified or distributed without the express prior written permission of the copyright holder.
+export default {
+  items: [
+
+    {
+      title: true,
+      name: 'LUZZU DATA QUALITY',
+      wrapper: {            // optional wrapper object
+        element: '',        // required valid HTML5 element tag
+        attributes: {}        // optional valid JS object with JS API naming ex: { className: "my-class", style: { fontFamily: "Verdana" }, id: "my-id"}
+      },
+      class: ''             // optional class names space delimited list for title item ex: "text-center"
+    },
+    {
+      name: 'Pipeline',
+      url: '/pipeline',
+      icon: 'icon-home',
+
+    },
+    {
+      name: 'Linked Data',
+      url: '/linkeddata',
+      icon: 'icon-grid',
+
+    },
+    {
+      name: 'Reporting',
+      url: '/reporting',
+      icon: 'icon-chart',
+    }
+  ],
+};

二进制
Luzzu Dashboard/src/assets/cloud-upload.png


文件差异内容过多而无法显示
+ 3 - 0
Luzzu Dashboard/src/assets/img/brand/icon.svg


文件差异内容过多而无法显示
+ 3 - 0
Luzzu Dashboard/src/assets/img/brand/logo.svg


+ 24 - 0
Luzzu Dashboard/src/config/dashboardProperties.js

@@ -0,0 +1,24 @@
+//© 2019 Dublin City University, Trinity College Dublin. All rights reserved. This material may not be reproduced, displayed, modified or distributed without the express prior written permission of the copyright holder.
+
+export default {
+	luzzuFramework: {
+		//host: "35.240.120.25",
+		host: "localhost:8080",
+		port: "8080"
+	},
+	tripleStore: {
+		//host: "35.240.120.25",
+		host: "localhost:3030",
+		port: 3030,
+		datastore: "fusekiserver",
+		userName: "admin",
+		password: "pw"
+	},
+	wrapperAPI: {
+		//host: "35.240.120.25",
+		host: "localhost:5000",
+		port : 5000
+	},
+	dataQualityOverTimeBarCount : 6,
+	supportedFileFormats: [{fileFormat:"RDF/XML",extension:"rdf"},{fileFormat:"TURTLE",extension:"ttl"},{fileFormat:"NQUAD",extension:"nq"},{fileFormat:"NTRIPLES",extension:"nt"},{fileFormat:"JSON-LD",extension:"jsonld"}]
+};

+ 45 - 0
Luzzu Dashboard/src/config/exceptionInfoIconDetails.js

@@ -0,0 +1,45 @@
+//© 2019 Dublin City University, Trinity College Dublin. All rights reserved. This material may not be reproduced, displayed, modified or distributed without the express prior written permission of the copyright holder.
+
+export default {
+	infoIcon:
+    {
+      "UndefinedClass" : "The class identifier (i.e URI) which is used to state the resource as an instance of a class is undefined in its respective schema."
+    ,
+
+      "UndefinedProperty" : "The property identifier (i.e URI) which is used to describe a charateristic of resource is undefined in its respective schema."
+    ,
+
+      "NoHumanReadableLabel" : "Resource does not have a Human Readable Label. i.e No "
+    ,
+
+      "ResourceReplica" : "The referred resource has a replica resource with a different id."
+    ,
+
+      "IncorrectRange" : "The object doesn't fall under the range type category of the property."
+    ,
+
+      "IncorrectDomain" : "The subject doesn't fall under the domain type category of the property."
+    ,
+
+      "UnknownType" : "The resource type is unknown."
+    ,
+
+      "MultiTypedResourceWithDisjointedClasses" : "The referred resource is multi-typed with disjoined classes."
+		,
+		"MisplacedClass" : "The referred resource, used a defined class instead of a property, in the property's position."
+	,
+
+		"MisplacedProperty" : "The referred resource, used a defined property instead of a class, in an rdf:type statement object's position."
+	,
+
+		"MisusedObjectProperty" : "The referred resource used an object property at the predicate position instead of a datatype property."
+	,
+
+		"MisusedDatatypeProperty" : "The referred resource used a datatype property at the predicate position instead of an Object Property."
+	,
+
+		"dt-unknown-dt" : "Data type value of the object literal doesn't satisfy the datatype conditions."
+
+    }
+
+};

+ 32 - 0
Luzzu Dashboard/src/config/metricKnowledgeBaseMapping.js

@@ -0,0 +1,32 @@
+//© 2019 Dublin City University, Trinity College Dublin. All rights reserved. This material may not be reproduced, displayed, modified or distributed without the express prior written permission of the copyright holder.
+
+export default {
+	knowledgeBaseMapping:
+    {
+      "UndefinedClassesAndPropertiesMetric" : {"UndefinedClass":["Object"],"UndefinedProperty":["Predicate"]}
+    ,
+
+      "HumanReadableLabellingMetric" : {"NoHumanReadableLabel":["Subject"]}
+    ,
+
+      "ExtensionalConcisenessMetric" : {"ResourceReplica":["Subject"]}
+    ,
+
+      "UsageOfIncorrectDomainOrRangeDatatypesMetric" : {"IncorrectRange":["Predicate","Object"],"IncorrectDomain":["Subject","Predicate"],"UnknownType":["Subject","Object"]}
+    ,
+
+      "EntitiesAsMembersOfDisjointClassesMetric" : {"MultiTypedResourceWithDisjointedClasses":["Subject"]}
+    ,
+
+      "MisplacedClassesOrPropertiesMetric" : {"MisplacedProperty":["Object"],"MisplacedClass":["Predicate"]}
+    ,
+
+      "MisusedOwlDatatypeOrObjectPropertiesMetric" : {"MisusedObjectProperty":["Predicate","Object"],"MisusedDatatypeProperty":["Predicate","Object"]}
+    ,
+
+      "CompatibleDatatype" : {"dt-unknown-dt":["Object"]}
+		,
+		  "MachineReadableLicenseMetric" : {"NoValidLicenseInDataset":["Subject"], "NotRecommendedLicenseInDataset":["Subject"]}
+    }
+
+};

+ 30 - 0
Luzzu Dashboard/src/config/metricViewMapping.js

@@ -0,0 +1,30 @@
+//© 2019 Dublin City University, Trinity College Dublin. All rights reserved. This material may not be reproduced, displayed, modified or distributed without the express prior written permission of the copyright holder.
+
+export default {
+	viewMapping:
+    {
+      "UndefinedClassesAndPropertiesMetric" : ["ProblematicThing","Resource"]
+    ,
+
+      "HumanReadableLabellingMetric" : ["Resource"]
+    ,
+
+      "ExtensionalConcisenessMetric" : ["Resource"]
+    ,
+
+      "UsageOfIncorrectDomainOrRangeDatatypesMetric" : ["ProblematicThing"]
+    ,
+
+      "EntitiesAsMembersOfDisjointClassesMetric" : ["ProblematicThing"]
+    ,
+
+      "MisplacedClassesOrPropertiesMetric" : ["ProblematicThing"]
+    ,
+
+      "MisusedOwlDatatypeOrObjectPropertiesMetric" : ["ProblematicThing"]
+    ,
+
+      "CompatibleDatatype" : ["Resource"]
+    }
+
+};

+ 30 - 0
Luzzu Dashboard/src/containers/DefaultLayout/DefaultFooter.js

@@ -0,0 +1,30 @@
+//© 2019 Dublin City University, Trinity College Dublin. All rights reserved. This material may not be reproduced, displayed, modified or distributed without the express prior written permission of the copyright holder.
+
+import React, { Component } from 'react';
+import PropTypes from 'prop-types';
+
+const propTypes = {
+  children: PropTypes.node,
+};
+
+const defaultProps = {};
+
+class DefaultFooter extends Component {
+  render() {
+
+    // eslint-disable-next-line
+    const { children, ...attributes } = this.props;
+
+    return (
+      <React.Fragment>
+        <span><a href="https://luzzu.adaptcentre.ie/">Luzzu Framework</a></span>
+        <span className="ml-auto">Developed by <a href="mailto: ramneesh.vaidyambath2@mail.dcu.ie">Ramneesh Vaidyambath</a></span>
+      </React.Fragment>
+    );
+  }
+}
+
+DefaultFooter.propTypes = propTypes;
+DefaultFooter.defaultProps = defaultProps;
+
+export default DefaultFooter;

+ 45 - 0
Luzzu Dashboard/src/containers/DefaultLayout/DefaultHeader-Working.js

@@ -0,0 +1,45 @@
+//© 2019 Dublin City University, Trinity College Dublin. All rights reserved. This material may not be reproduced, displayed, modified or distributed without the express prior written permission of the copyright holder.
+
+import React, { Component } from 'react';
+import { NavLink } from 'react-router-dom';
+import { Badge, Nav, NavItem } from 'reactstrap';
+import PropTypes from 'prop-types';
+
+import { AppNavbarBrand, AppSidebarToggler } from '@coreui/react';
+import logo from '../../assets/img/brand/logo.svg'
+import sygnet from '../../assets/img/brand/icon.svg'
+
+const propTypes = {
+    children: PropTypes.node,
+};
+
+const defaultProps = {};
+
+class DefaultHeader extends Component {
+    render() {
+
+        // eslint-disable-next-line
+        const { children, ...attributes } = this.props;
+
+        return (
+            <React.Fragment>
+                <AppSidebarToggler className="d-lg-none" display="md" mobile />
+                <AppNavbarBrand
+                    full={{ src: logo, width: 89, height: 25, alt: 'Luzzu Logo' }}
+                    minimized={{ src: sygnet, width: 30, height: 30, alt: 'Luzzu Logo' }}/>
+                <AppSidebarToggler className="d-md-down-none" display="lg" />
+
+                <Nav className="ml-auto" navbar>
+                    <NavItem className="d-md-down-none">
+                        <NavLink to="#" className="nav-link"><i className="icon-bell"></i><Badge pill color="danger">5</Badge></NavLink>
+                    </NavItem>
+                </Nav>
+            </React.Fragment>
+        );
+    }
+}
+
+DefaultHeader.propTypes = propTypes;
+DefaultHeader.defaultProps = defaultProps;
+
+export default DefaultHeader;

+ 41 - 0
Luzzu Dashboard/src/containers/DefaultLayout/DefaultHeader.js

@@ -0,0 +1,41 @@
+//© 2019 Dublin City University, Trinity College Dublin. All rights reserved. This material may not be reproduced, displayed, modified or distributed without the express prior written permission of the copyright holder.
+
+import React, { Component } from 'react';
+import {  Nav} from 'reactstrap';
+import PropTypes from 'prop-types';
+
+import { AppNavbarBrand, AppSidebarToggler } from '@coreui/react';
+import logo from '../../assets/img/brand/logo.svg'
+import sygnet from '../../assets/img/brand/icon.svg'
+
+const propTypes = {
+    children: PropTypes.node,
+};
+
+const defaultProps = {};
+
+class DefaultHeader extends Component {
+    render() {
+
+        // eslint-disable-next-line
+        const { children, ...attributes } = this.props;
+
+        return (
+            <React.Fragment>
+                <AppSidebarToggler className="d-lg-none" display="md" mobile />
+                <AppNavbarBrand
+                    full={{ src: logo, width: 89, height: 25, alt: 'Luzzu Logo' }}
+                    minimized={{ src: sygnet, width: 30, height: 30, alt: 'Luzzu Logo' }}/>
+                <AppSidebarToggler className="d-md-down-none" display="lg" />
+
+                <Nav className="ml-auto" navbar>
+                </Nav>
+            </React.Fragment>
+        );
+    }
+}
+
+DefaultHeader.propTypes = propTypes;
+DefaultHeader.defaultProps = defaultProps;
+
+export default DefaultHeader;

+ 90 - 0
Luzzu Dashboard/src/containers/DefaultLayout/DefaultLayout.js

@@ -0,0 +1,90 @@
+//© 2019 Dublin City University, Trinity College Dublin. All rights reserved. This material may not be reproduced, displayed, modified or distributed without the express prior written permission of the copyright holder.
+
+import React, { Component, Suspense } from 'react';
+import { Redirect, Route, Switch } from 'react-router-dom';
+import { Container } from 'reactstrap';
+
+import {
+  AppBreadcrumb,
+  AppFooter,
+  AppHeader,
+  AppSidebar,
+  AppSidebarFooter,
+  AppSidebarForm,
+  AppSidebarHeader,
+  AppSidebarMinimizer,
+  AppSidebarNav,
+} from '@coreui/react';
+// sidebar nav config
+import navigation from '../../_nav';
+// routes config
+import routes from '../../routes';
+
+const DefaultFooter = React.lazy(() => import('./DefaultFooter'));
+const DefaultHeader = React.lazy(() => import('./DefaultHeader'));
+
+class DefaultLayout extends Component {
+  constructor(props) {
+    super(props);
+    this.state = {
+    };
+  }
+
+
+  loading = () => <div className="animated fadeIn pt-1 text-center">Loading...</div>
+
+
+  render() {
+    return (
+      <div className="app">
+        <AppHeader fixed>
+          <Suspense  fallback={this.loading()}>
+            <DefaultHeader />
+          </Suspense>
+        </AppHeader>
+        <div className="app-body">
+          <AppSidebar fixed display="lg">
+            <AppSidebarHeader />
+            <AppSidebarForm />
+            <Suspense>
+            <AppSidebarNav navConfig={navigation} {...this.props} />
+            </Suspense>
+            <AppSidebarFooter />
+            <AppSidebarMinimizer />
+          </AppSidebar>
+          <main className="main">
+            <AppBreadcrumb appRoutes={routes}/>
+
+            <Container fluid>
+              <Suspense fallback={this.loading()}>
+                <Switch>
+                  {routes.map((route, idx) => {
+                    return route.component ? (
+                      <Route
+                        key={idx}
+                        path={route.path}
+                        exact={route.exact}
+                        name={route.name}
+                        render={props => (
+                          <route.component {...props} />
+                        )} />
+                    ) : (null);
+                  })}
+                  <Redirect from="/" to="/login" />
+                </Switch>
+              </Suspense>
+            </Container>
+          </main>
+
+        </div>
+        <AppFooter>
+          <Suspense fallback={this.loading()}>
+            <DefaultFooter />
+          </Suspense>
+        </AppFooter>
+      </div>
+    );
+  }
+}
+
+export default DefaultLayout;

+ 5 - 0
Luzzu Dashboard/src/containers/DefaultLayout/index.js

@@ -0,0 +1,5 @@
+//© 2019 Dublin City University, Trinity College Dublin. All rights reserved. This material may not be reproduced, displayed, modified or distributed without the express prior written permission of the copyright holder.
+
+import DefaultLayout from './DefaultLayout';
+
+export default DefaultLayout;

+ 6 - 0
Luzzu Dashboard/src/containers/DefaultLayout/package.json

@@ -0,0 +1,6 @@
+{
+  "name": "DefaultLayout",
+  "version": "0.0.0",
+  "private": true,
+  "main": "./DefaultLayout.js"
+}

+ 5 - 0
Luzzu Dashboard/src/containers/index.js

@@ -0,0 +1,5 @@
+//© 2019 Dublin City University, Trinity College Dublin. All rights reserved. This material may not be reproduced, displayed, modified or distributed without the express prior written permission of the copyright holder.
+
+import DefaultLayout from './DefaultLayout';
+
+export { DefaultLayout };

+ 1 - 0
Luzzu Dashboard/src/index.css

@@ -0,0 +1 @@
+

+ 21 - 0
Luzzu Dashboard/src/index.js

@@ -0,0 +1,21 @@
+import 'react-app-polyfill/ie9'; // For IE 9-11 support
+import 'react-app-polyfill/ie11'; // For IE 11 support
+import './polyfill'
+import React from 'react';
+import ReactDOM from 'react-dom';
+import { createStore} from 'redux';
+import { Provider } from 'react-redux';
+import './index.css';
+import App from './App';
+import * as serviceWorker from './serviceWorker';
+import datasetDetailsCacheReducer from './store/datasetDetailsCacheReducer';
+const store = createStore(datasetDetailsCacheReducer);
+//import connectDatasetReducer from './store/connectDatasetReducer';
+//const store = createStore(connectDatasetReducer);
+
+ReactDOM.render(<Provider store={store}><App /></Provider>, document.getElementById('root'));
+
+// If you want your app to work offline and load faster, you can change
+// unregister() to register() below. Note this comes with some pitfalls.
+// Learn more about service workers: http://bit.ly/CRA-PWA
+serviceWorker.unregister();

+ 44 - 0
Luzzu Dashboard/src/polyfill.js

@@ -0,0 +1,44 @@
+/*
+* required polyfills
+*/
+
+/** IE9, IE10 and IE11 requires all of the following polyfills. **/
+// import 'core-js/es6/symbol'
+// import 'core-js/es6/object'
+// import 'core-js/es6/function'
+// import 'core-js/es6/parse-int'
+// import 'core-js/es6/parse-float'
+// import 'core-js/es6/number'
+// import 'core-js/es6/math'
+// import 'core-js/es6/string'
+// import 'core-js/es6/date'
+import 'core-js/es6/array'
+// import 'core-js/es6/regexp'
+import 'core-js/es6/map'
+// import 'core-js/es6/weak-map'
+import 'core-js/es6/set'
+import 'core-js/es7/object'
+
+/** IE10 and IE11 requires the following for the Reflect API. */
+// import 'core-js/es6/reflect'
+
+/** Evergreen browsers require these. **/
+// Used for reflect-metadata in JIT. If you use AOT (and only Angular decorators), you can remove.
+// import 'core-js/es7/reflect'
+
+// CustomEvent() constructor functionality in IE9, IE10, IE11
+(function () {
+
+  if ( typeof window.CustomEvent === "function" ) return false
+
+  function CustomEvent ( event, params ) {
+    params = params || { bubbles: false, cancelable: false, detail: undefined }
+    var evt = document.createEvent( 'CustomEvent' )
+    evt.initCustomEvent( event, params.bubbles, params.cancelable, params.detail )
+    return evt
+  }
+
+  CustomEvent.prototype = window.Event.prototype
+
+  window.CustomEvent = CustomEvent
+})()

+ 26 - 0
Luzzu Dashboard/src/routes.js

@@ -0,0 +1,26 @@
+//© 2019 Dublin City University, Trinity College Dublin. All rights reserved. This material may not be reproduced, displayed, modified or distributed without the express prior written permission of the copyright holder.
+import React from 'react';
+
+const Dashboard = React.lazy(() => import('./views/Dashboard'));
+const Pipeline = React.lazy(() => import('./views/Pipeline'));
+const Login = React.lazy(() => import('./views/Login'));
+const Reporting = React.lazy(() => import('./views/Reports/ReportList'));
+const DataSetDetails = React.lazy(() => import('./views/DataSetDetails'));
+const OneSpatialDetails = React.lazy(() => import('./views/DataSetDetails/OneSpatialDetails/OneSpatialDetails'));
+const OneSpatialDimensionDetails = React.lazy(() => import('./views/DataSetDetails/OneSpatialDetails/OneSpatialDimensionDetails'));
+const DimensionDetails = React.lazy(() => import('./views/DataSetDetails/DimensionDetails'));
+
+// https://github.com/ReactTraining/react-router/tree/master/packages/react-router-config
+const routes = [
+  { path: '/', exact: true, name: 'Home' },
+  { path: '/login', exact: true, name: 'Login', component: Login },
+  { path: '/pipeline', exact: true, name: 'Pipeline', component: Pipeline },
+  { path: '/linkeddata', exact: true, name: 'Linked Data', component: Dashboard },
+  { path: '/reporting', exact: true, name: 'Reporting', component: Reporting },
+  { path: '/onespatial/:datasetID', exact: true, name: '1Spatial', component: OneSpatialDetails },
+  { path: '/linkeddata/:datasetID', exact : true, name: 'Assessment Details', component: DataSetDetails },
+  { path: '/linkeddata/:datasetID/:dimension', exact : true, name: 'Dimension Details', component: DimensionDetails },
+  { path: '/onespatial/:datasetID/:dimension', exact : true, name: '1Spatial Dimension Details', component: OneSpatialDimensionDetails }
+];
+
+export default routes;

+ 110 - 0
Luzzu Dashboard/src/scss/_custom.scss

@@ -0,0 +1,110 @@
+
+//© 2019 Dublin City University, Trinity College Dublin. All rights reserved. This material may not be reproduced, displayed, modified or distributed without the express prior written permission of the copyright holder.
+
+// Here you can add other styles
+.connectDataSet:hover * {
+    background: #f8f9fa;
+    cursor: pointer;
+}
+
+.dataSetTiles:hover * {
+  //background: #6cc5e0;
+    cursor: pointer;
+}
+
+.pipelineTiles {
+    min-width: 200px;
+    min-height: 300px;
+    max-width: 350px;
+    position: absolute;
+    z-index: 5;
+}
+
+.pipelineTiles:hover * {
+  //background: #6cc5e0;
+    cursor: pointer;
+}
+
+.dimensionContainer:hover * {
+  background: rgba(220,220,220,0.5);
+    cursor: pointer;
+}
+
+.front {
+    min-width: 78%;
+    max-height: 60px;
+    min-height: 50px;
+    position: absolute;
+    left: 5%;
+    top: 55%;
+    margin-top: 100px;
+    z-index: 1;
+}
+
+.nav-dims {
+    position: relative;
+    left: -3%;
+    top: -69px;
+}
+
+.align-right {
+    position: relative;
+    left: 20%;
+}
+
+.sidebar-fixed .sidebar {
+    position: fixed;
+    z-index: 1019;
+    width: 210px;
+    height: 100vh;
+}
+
+.sidebar > .sidebar-navs {
+    overflow-x: hidden;
+    overflow-y: auto;
+}
+
+.sidebar .sidebar-navs {
+    position: relative;
+    flex: 1 1;
+    width: 220px;
+}
+
+.sidebar .nav-links {
+    display: block;
+    padding: 0.3rem 1.4rem;
+    color: #fff;
+    text-decoration: none;
+    background: transparent;
+    max-width: 230px;
+}
+
+.standard-switch{
+    padding: 10px;
+    float: left;
+    text-align: left;
+}
+
+.dim-bar-custom-height{
+    height: 390px;
+}
+
+.cardTextDown{
+    position: relative;
+    top: 100px;
+    font-size: 30px;
+}
+
+@media all and (min-width: 480px) {
+  .Login {
+    padding: 60px 0;
+  }
+
+  .Login form {
+    margin: 0 auto;
+    max-width: 320px;
+  }
+}
+
+
+

+ 11 - 0
Luzzu Dashboard/src/scss/_ie-fix.scss

@@ -0,0 +1,11 @@
+html body .app.flex-row.align-items-center {
+  height: 100vh;
+}
+
+// ie11 floating footer temp fix, react only
+@media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) {
+  #root {
+    display: flex;
+    flex-direction: column;
+  }
+}

+ 1 - 0
Luzzu Dashboard/src/scss/_variables.scss

@@ -0,0 +1 @@
+// Variable overrides

+ 14 - 0
Luzzu Dashboard/src/scss/style.scss

@@ -0,0 +1,14 @@
+// If you want to override variables do it here
+@import "variables";
+
+// Import styles
+@import "~@coreui/coreui/scss/coreui.scss";
+
+// Temp fix for reactstrap
+@import '~@coreui/coreui/scss/_dropdown-menu-right.scss';
+
+// If you want to add something do it here
+@import "custom";
+
+// ie fixes
+@import "ie-fix";

+ 0 - 0
Luzzu Dashboard/src/scss/vendors/.gitkeep


+ 4 - 0
Luzzu Dashboard/src/scss/vendors/_variables.scss

@@ -0,0 +1,4 @@
+// Override Boostrap variables
+@import "../variables";
+@import "~bootstrap/scss/mixins";
+@import "~@coreui/coreui/scss/variables";

+ 127 - 0
Luzzu Dashboard/src/serviceWorker.js

@@ -0,0 +1,127 @@
+// In production, we register a service worker to serve assets from local cache.
+
+// This lets the app load faster on subsequent visits in production, and gives
+// it offline capabilities. However, it also means that developers (and users)
+// will only see deployed updates on the "N+1" visit to a page, since previously
+// cached resources are updated in the background.
+
+// To learn more about the benefits of this model, read https://goo.gl/KwvDNy.
+// This link also includes instructions on opting out of this behavior.
+
+const isLocalhost = Boolean(
+  window.location.hostname === 'localhost' ||
+    // [::1] is the IPv6 localhost address.
+    window.location.hostname === '[::1]' ||
+    // 127.0.0.1/8 is considered localhost for IPv4.
+    window.location.hostname.match(
+      /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
+    )
+);
+
+export function register(config) {
+  if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
+    // The URL constructor is available in all browsers that support SW.
+    const publicUrl = new URL(process.env.PUBLIC_URL, window.location);
+    if (publicUrl.origin !== window.location.origin) {
+      // Our service worker won't work if PUBLIC_URL is on a different origin
+      // from what our page is served on. This might happen if a CDN is used to
+      // serve assets; see https://github.com/facebook/create-react-app/issues/2374
+      return;
+    }
+
+    window.addEventListener('load', () => {
+      const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
+
+      if (isLocalhost) {
+        // This is running on localhost. Let's check if a service worker still exists or not.
+        checkValidServiceWorker(swUrl, config);
+
+        // Add some additional logging to localhost, pointing developers to the
+        // service worker/PWA documentation.
+        navigator.serviceWorker.ready.then(() => {
+          console.log(
+            'This web app is being served cache-first by a service ' +
+              'worker. To learn more, visit https://goo.gl/SC7cgQ'
+          );
+        });
+      } else {
+        // Is not local host. Just register service worker
+        registerValidSW(swUrl, config);
+      }
+    });
+  }
+}
+
+function registerValidSW(swUrl, config) {
+  navigator.serviceWorker
+    .register(swUrl)
+    .then(registration => {
+      registration.onupdatefound = () => {
+        const installingWorker = registration.installing;
+        installingWorker.onstatechange = () => {
+          if (installingWorker.state === 'installed') {
+            if (navigator.serviceWorker.controller) {
+              // At this point, the old content will have been purged and
+              // the fresh content will have been added to the cache.
+              // It's the perfect time to display a "New content is
+              // available; please refresh." message in your web app.
+              console.log('New content is available; please refresh.');
+
+              // Execute callback
+              if (config.onUpdate) {
+                config.onUpdate(registration);
+              }
+            } else {
+              // At this point, everything has been precached.
+              // It's the perfect time to display a
+              // "Content is cached for offline use." message.
+              console.log('Content is cached for offline use.');
+
+              // Execute callback
+              if (config.onSuccess) {
+                config.onSuccess(registration);
+              }
+            }
+          }
+        };
+      };
+    })
+    .catch(error => {
+      console.error('Error during service worker registration:', error);
+    });
+}
+
+function checkValidServiceWorker(swUrl, config) {
+  // Check if the service worker can be found. If it can't reload the page.
+  fetch(swUrl)
+    .then(response => {
+      // Ensure service worker exists, and that we really are getting a JS file.
+      if (
+        response.status === 404 ||
+        response.headers.get('content-type').indexOf('javascript') === -1
+      ) {
+        // No service worker found. Probably a different app. Reload the page.
+        navigator.serviceWorker.ready.then(registration => {
+          registration.unregister().then(() => {
+            window.location.reload();
+          });
+        });
+      } else {
+        // Service worker found. Proceed as normal.
+        registerValidSW(swUrl, config);
+      }
+    })
+    .catch(() => {
+      console.log(
+        'No internet connection found. App is running in offline mode.'
+      );
+    });
+}
+
+export function unregister() {
+  if ('serviceWorker' in navigator) {
+    navigator.serviceWorker.ready.then(registration => {
+      registration.unregister();
+    });
+  }
+}

+ 166 - 0
Luzzu Dashboard/src/services/datasetConfigDetails/datasetConfigDetails.js

@@ -0,0 +1,166 @@
+//© 2019 Dublin City University, Trinity College Dublin. All rights reserved. This material may not be reproduced, displayed, modified or distributed without the express prior written permission of the copyright holder.
+
+import axios from 'axios';
+import properties from '../../config/dashboardProperties';
+
+const host = properties.wrapperAPI.host;
+const port = properties.wrapperAPI.port;
+
+/*-------------config-------------*/
+const config ={
+    //baseURL: 'http://'+host+':'+port+'/api/v1/',
+    baseURL: 'http://'+host+'/api/v1/',
+    timeout: 10000,
+    headers: {'Accept': 'application/json'}
+  };
+
+axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
+/*-----------------------------------*/
+
+export const getStatus = () => axios.get('status',config)
+.then((response) => {
+    return response;})
+.catch((err) => {
+  if (!err.response) {
+              // network error
+            console.log('Error: Network Error. Unable to connect to '+ host+":"+port);
+  } else {
+            console.log(err.response.data.message);
+  }
+    return err;
+});
+
+export const getAllDataset = () => axios.get('getAllDataset/',config)
+.then((response) => {
+    return response;})
+.catch((err) => {
+  if (!err.response) {
+              // network error
+            console.log('Error: Network Error. Unable to connect to '+ host+":"+port);
+  } else {
+            console.log(err.response.data.message);
+  }
+    return err;
+});
+
+export const getDataset = (parameters) =>
+  axios.get('getDataset/',{...config, params : parameters})
+.then((response) => {
+  console.log(parameters)
+
+    return response;})
+.catch((err) => {
+  console.log(parameters)
+
+  if (!err.response) {
+              // network error
+            console.log('Error: Network Error. Unable to connect to '+ host+":"+port);
+  } else {
+            console.log(err.response.data.message);
+  }
+    return err;
+});
+
+export const removeDataset = (datasetID) =>
+  axios.get('removeDataset/',{...config, params : {"datasetID": datasetID}})
+.then((response) => {
+    return response;})
+.catch((err) => {
+  if (!err.response) {
+              // network error
+            console.log('Error: Network Error. Unable to connect to '+ host+":"+port);
+  } else {
+            console.log(err.response.data.message);
+  }
+    return err;
+});
+
+export const addDataset = (payLoad) =>
+
+  axios.post('addDataset', payLoad, config)
+  .then((response) => {
+      return response;})
+  .catch((err) => {
+    if (!err.response) {
+                // network error
+              console.log('Error: Network Error. Unable to connect to '+ host+":"+port);
+    } else {
+              console.log(err.response.data.message);
+    }
+      return err;
+  });
+
+  export const updateDataset = (request,datasetID) => {
+    //let request = {
+    //    "lastAssessmentRequestID" : payLoad,
+    //}
+
+  return  axios.post('updateDataset', request, {...config, params : {"datasetID": datasetID}})
+      .then((response) => {
+          return response;})
+      .catch((err) => {
+        if (!err.response) {
+                    // network error
+                  console.log('Error: Network Error. Unable to connect to '+ host+":"+port);
+        } else {
+                  console.log(err.response.data.message);
+        }
+          return err;
+      });
+  }
+
+
+  export const generateKnowledgeBase = (metrics, qualityGraph, dataGraph, lastAssessmentDate,isSparqlEndPoint, knowledgeBaseMapping) => {
+
+let request = {
+  "metrics" : metrics,
+  "qualityGraph" : qualityGraph,
+  "dataGraph" : dataGraph,
+  "tripleStoreEndPoint" : "http://"+properties.tripleStore.host+":"+properties.tripleStore.port+"/"+properties.tripleStore.datastore+"/query",
+  "lastAssessmentDate" : lastAssessmentDate,
+  "isSparqlEndPoint" : isSparqlEndPoint.toString(),
+  "knowledgeBaseMapping": knowledgeBaseMapping
+}
+  return  axios.post('knowledgeBase', request, config)
+      .then((response) => {
+          return response;})
+      .catch((err) => {
+        if (!err.response) {
+                    // network error
+                  console.log('Error: Network Error. Unable to connect to '+ host+":"+port);
+        } else {
+                  console.log(err.response.data.message);
+        }
+          return err;
+      });
+  }
+
+  export const getKnowledgeBaseStatus = (knowledgeBaseID) =>
+    axios.get('knowledgeBase',{...config, params : {"knowledgeBaseID": knowledgeBaseID}})
+  .then((response) => {
+      return response;})
+  .catch((err) => {
+    if (!err.response) {
+                // network error
+              console.log('Error: Network Error. Unable to connect to '+ host+":"+port);
+    } else {
+              console.log(err.response.data.message);
+    }
+      return err;
+  });
+
+  export const getKnowledgeBase= (knowledgeBaseID) =>
+    axios.get('getKnowledgeBase',{...config, params : {"knowledgeBaseID": knowledgeBaseID}})
+  .then((response) => {
+      return response;})
+  .catch((err) => {
+    if (!err.response) {
+                // network error
+              console.log('Error: Network Error. Unable to connect to '+ host+":"+port);
+    } else {
+              console.log(err.response.data.message);
+    }
+      return err;
+  });
+
+export default {getAllDataset,getDataset,removeDataset,addDataset, updateDataset, generateKnowledgeBase, getKnowledgeBaseStatus, getKnowledgeBase, getStatus};

+ 40 - 0
Luzzu Dashboard/src/services/luzzuFrameWorkAPIs/cancelAssessment.js

@@ -0,0 +1,40 @@
+//© 2019 Dublin City University, Trinity College Dublin. All rights reserved. This material may not be reproduced, displayed, modified or distributed without the express prior written permission of the copyright holder.
+
+import axios from 'axios';
+import properties from '../../config/dashboardProperties';
+import queryString from 'query-string';
+
+const host = properties.luzzuFramework.host;
+const port = properties.luzzuFramework.port;
+/*-------------config-------------*/
+const config ={
+    //baseURL: 'http://'+host+':'+port+'/Luzzu/v4/',
+    baseURL: 'http://'+host+'/Luzzu/v4/',
+    timeout: 5000,
+    headers: {'Accept': 'application/json','Content-Type':'application/x-www-form-urlencoded'}
+  };
+
+  axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
+/*-----------------------------------*/
+
+export const cancelAssessment = (lastAssessmentRequestID) => {
+
+  let request = {
+      "Request-ID":lastAssessmentRequestID
+  }
+
+  return axios.post('assessment/cancel',queryString.stringify(request), config)
+    .then((response) => {
+        return response;})
+    .catch((err) => {
+      if (!err.response) {
+                  // network error
+                console.log('Error: Network Error. Unable to connect to '+ host+":"+port);
+      } else {
+                console.log(err.response.data.message);
+      }
+        return err;
+    });
+}
+
+export default {cancelAssessment};

+ 30 - 0
Luzzu Dashboard/src/services/luzzuFrameWorkAPIs/getAllMetricsAvailableForAssessment.js

@@ -0,0 +1,30 @@
+//© 2019 Dublin City University, Trinity College Dublin. All rights reserved. This material may not be reproduced, displayed, modified or distributed without the express prior written permission of the copyright holder.
+
+import axios from 'axios';
+import properties from '../../config/dashboardProperties';
+
+const host = properties.luzzuFramework.host;
+const port = properties.luzzuFramework.port;
+/*-------------config-------------*/
+const config ={
+    //baseURL: 'http://'+host+':'+port+'/Luzzu/v4/',
+    baseURL: 'http://'+host+'/Luzzu/v4/',
+    timeout: 5000,
+    headers: {'Accept': 'application/json','Content-Type':'application/x-www-form-urlencoded'}
+  };
+/*-----------------------------------*/
+
+export const allMetricsForAssessment = () => axios.get('framework/available-metrics/',config)
+                .then((response) => {
+                    return response.data;})
+                .catch((err) => {
+                  if (!err.response) {
+                              // network error
+                            console.log('Error: Network Error. Unable to connect to '+ host+":"+port);
+                  } else {
+                            console.log(err.response.data.message);
+                  }
+                });
+
+
+export default { allMetricsForAssessment };

+ 30 - 0
Luzzu Dashboard/src/services/luzzuFrameWorkAPIs/getStatusUpdate.js

@@ -0,0 +1,30 @@
+//© 2019 Dublin City University, Trinity College Dublin. All rights reserved. This material may not be reproduced, displayed, modified or distributed without the express prior written permission of the copyright holder.
+
+import axios from 'axios';
+import properties from '../../config/dashboardProperties';
+
+const host = properties.luzzuFramework.host;
+const port = properties.luzzuFramework.port;
+/*-------------config-------------*/
+const config ={
+    //baseURL: 'http://'+host+':'+port+'/Luzzu/v4/',
+    baseURL: 'http://'+host+'/Luzzu/v4/',
+    timeout: 5000,
+    headers: {'Accept': 'application/json','Content-Type':'application/x-www-form-urlencoded'}
+  };
+/*-----------------------------------*/
+
+export const getStatusUpdate = (lastAssessmentRequestID) => axios.get('assessment/status/'+lastAssessmentRequestID,config)
+                .then((response) => {
+                    return response.data;})
+                .catch((err) => {
+                  if (!err.response) {
+                              // network error
+                            console.log('Error: Network Error. Unable to connect to '+ host+":"+port);
+                  } else {
+                            console.log(err.response.data.message);
+                  }
+                });
+
+
+export default {getStatusUpdate};

+ 64 - 0
Luzzu Dashboard/src/services/luzzuFrameWorkAPIs/triggerAssessment.js

@@ -0,0 +1,64 @@
+//© 2019 Dublin City University, Trinity College Dublin. All rights reserved. This material may not be reproduced, displayed, modified or distributed without the express prior written permission of the copyright holder.
+
+import axios from 'axios';
+import properties from '../../config/dashboardProperties';
+import queryString from 'query-string';
+const host = properties.luzzuFramework.host;
+const port = properties.luzzuFramework.port;
+/*-------------config-------------*/
+const config ={
+    //baseURL: 'http://'+host+':'+port+'/Luzzu/v4/',
+    baseURL: 'http://'+host+'/Luzzu/v4/',
+    timeout: 5000,
+    headers: {'Accept': 'application/json','Content-Type':'application/x-www-form-urlencoded'}
+  };
+
+axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
+/*-----------------------------------*/
+
+export const triggerAssessment = (props) => {
+  let datasetLocation="";
+  if(props.isSparqlEndPoint===true)
+  {
+    datasetLocation=props.sparqlEndPoint;
+  }
+  else {
+    datasetLocation=props.fileName;
+  }
+  let metrics =[];
+
+  metrics = props.assessmentMetrics.filter((metric) => {if(metric.assess & metric.javaPackageName!==""){return true;} return false;}).map((metric)=> {return (metric.javaPackageName);});
+  let MetricConfiguration = {
+    "@context": {
+      "lmi": "http://purl.org/eis/vocab/lmi#",
+      "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
+      "rdfs": "http://www.w3.org/2000/01/rdf-schema#",
+      "xsd": "http://www.w3.org/2001/XMLSchema#" },
+      "@id": "_:ub104bL6C1",
+      "@type": "lmi:MetricConfiguration",
+      "lmi:metric": [...metrics]}
+
+  let request = {
+      "Dataset-Location" : datasetLocation,
+      "Quality-Report-Required" : true,
+      "Dataset-PLD" : props.datasetPLD,
+      "Is-Sparql-Endpoint" : props.isSparqlEndPoint,
+      "Metrics-Configuration" :JSON.stringify(MetricConfiguration)
+
+  }
+return axios.post('assessment/compute',queryString.stringify(request), config)
+  .then((response) => {
+      return response;})
+  .catch((err) => {
+    if (!err.response) {
+                // network error
+              console.log('Error: Network Error. Unable to connect to '+ host+":"+port);
+    } else {
+              console.log(err.response.data.message);
+    }
+      return err;
+  });
+
+};
+
+export default {triggerAssessment};

+ 40 - 0
Luzzu Dashboard/src/services/tripleStoreAPIs/deleteGraphFromTripleStore.js

@@ -0,0 +1,40 @@
+//© 2019 Dublin City University, Trinity College Dublin. All rights reserved. This material may not be reproduced, displayed, modified or distributed without the express prior written permission of the copyright holder.
+import axios from 'axios';
+import queryString from 'query-string';
+import properties from '../../config/dashboardProperties';
+
+
+const host = properties.tripleStore.host;
+const port = properties.tripleStore.port;
+const dataStore = properties.tripleStore.datastore;
+/*-------------config-------------*/
+const config ={
+    timeout: 10000,
+    headers: {'Accept': 'application/sparql-results+json', 'Content-Type':'application/x-www-form-urlencoded'},
+    withCredentials: true,
+    auth: {username: properties.tripleStore.userName, password: properties.tripleStore.password}
+  };
+/*-----------------------------------*/
+
+let url = "http://"+host+"/"+dataStore+"/update";
+
+
+
+export const deleteGraph = (graphName) => {
+              let query = "DROP GRAPH <http://"+host+":"+port+"/"+dataStore+"/data/"+graphName+">";
+              //console.log(query);
+                return  axios.post(url, queryString.stringify({update: query}), config)
+                    .then((response) => {
+                        return response.data;})
+                    .catch((err) => {
+                      if (!err.response) {
+                                  // network error
+                                console.log('Error: Network Error. Unable to connect to '+ host+":"+port);
+                      } else {
+                                console.log(err.response.data.message);
+                      }
+                        return err;
+                    });
+                }
+
+export default {deleteGraph};

+ 33 - 0
Luzzu Dashboard/src/services/tripleStoreAPIs/readFromTripleStore.js

@@ -0,0 +1,33 @@
+//© 2019 Dublin City University, Trinity College Dublin. All rights reserved. This material may not be reproduced, displayed, modified or distributed without the express prior written permission of the copyright holder.
+import axios from 'axios';
+import queryString from 'query-string';
+import properties from '../../config/dashboardProperties';
+
+const host = properties.tripleStore.host;
+const port = properties.tripleStore.port;
+const dataStore = properties.tripleStore.datastore;
+/*-------------config-------------*/
+const config ={
+    timeout: 10000,
+    headers: {'Accept': 'application/sparql-results+json', 'Content-Type':'application/x-www-form-urlencoded'},
+  };
+/*-----------------------------------*/
+
+let url = "http://"+host+"/"+dataStore+"/query";
+
+export const read = (query) => axios.post(url, queryString.stringify({query: query}), config)
+                .then((response) => {
+
+                    return response.data;
+                })
+                .catch((err) => {
+                  if (!err.response) {
+                              // network error
+                            console.log('Error: Network Error. Unable to connect to '+ host+":"+port);
+                  } else {
+                            console.log(err.response.data.message);
+                  }
+                    //return err;
+                })
+
+export default {read};

+ 28 - 0
Luzzu Dashboard/src/services/tripleStoreAPIs/sparql/QueryListOfAssessedDatasets.js

@@ -0,0 +1,28 @@
+//© 2019 Dublin City University, Trinity College Dublin. All rights reserved. This material may not be reproduced, displayed, modified or distributed without the express prior written permission of the copyright holder.
+//Query to get all datasets
+'use strict'
+
+class AssessDataSetLists{
+    constructor(){
+        this.prefix = `
+            PREFIX daq: <http://purl.org/eis/vocab/daq#>
+            PREFIX sdma: <http://purl.org/linked-data/sdmx/2009/dimension#>
+        `;
+    }
+
+
+    getAssessedDataSetDetails(){
+        this.query = `
+            SELECT DISTINCT ?QualityGraph ?computedOn ?timePeriod WHERE {
+                ?QualityGraph ?predicate daq:QualityGraph.
+                    {GRAPH ?subject {?Observation daq:computedOn ?computedOn} }
+                 UNION
+                    {GRAPH ?subject {?Observation sdma:timePeriod ?timePeriod} }
+            }
+        `;
+
+        return this.prefix + this.query;
+    }
+}
+
+export default AssessDataSetLists

+ 23 - 0
Luzzu Dashboard/src/services/tripleStoreAPIs/sparql/checkDatasetExists.js

@@ -0,0 +1,23 @@
+//© 2019 Dublin City University, Trinity College Dublin. All rights reserved. This material may not be reproduced, displayed, modified or distributed without the express prior written permission of the copyright holder.
+//Check if the dataset exists in the triplestore
+const  prefix = `
+PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
+        `;
+
+
+
+export const  checkDatasetExists = (dataGraph) =>{
+        let query = `
+        SELECT DISTINCT ?DataGraph
+    		FROM NAMED <${dataGraph}>
+    		WHERE
+    		{
+      		{GRAPH ?DataGraph {?Subject ?Predicate ?Object}}
+    		}
+        `;
+
+        return prefix + query;
+    }
+
+
+export default {checkDatasetExists};

+ 24 - 0
Luzzu Dashboard/src/services/tripleStoreAPIs/sparql/checkDatasetPLDExists.js

@@ -0,0 +1,24 @@
+//© 2019 Dublin City University, Trinity College Dublin. All rights reserved. This material may not be reproduced, displayed, modified or distributed without the express prior written permission of the copyright holder.
+//Checks to see if the PLD of the dataset exists in the triplestore
+const  prefix = `
+PREFIX daq: <http://purl.org/eis/vocab/daq#>
+PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
+        `;
+
+
+
+export const  checkDatasetPLDExists = (datasetPLD) =>{
+        let query = `
+        SELECT  ?QualityGraph
+      	WHERE {
+        ?QualityGraph ?predicate daq:QualityGraph.
+        {GRAPH ?QualityGraph {?x daq:computedOn ?ComputedOn} }.
+        FILTER(str(?ComputedOn) = "${datasetPLD}").
+      }
+        `;
+
+        return prefix + query;
+    }
+
+
+export default {checkDatasetPLDExists};

+ 21 - 0
Luzzu Dashboard/src/services/tripleStoreAPIs/sparql/fetchDistinctResourceCount.js

@@ -0,0 +1,21 @@
+//© 2019 Dublin City University, Trinity College Dublin. All rights reserved. This material may not be reproduced, displayed, modified or distributed without the express prior written permission of the copyright holder.
+//Gets the number of all unique resources
+
+
+
+export const  fetchDistinctResourceCount = (graphType, dataGraph) =>{
+        let query = `
+        SELECT (COUNT(DISTINCT ?Resource) AS ?ResourceCount)
+        FROM NAMED <${dataGraph}>
+      	WHERE {
+          {${graphType} <${dataGraph}>
+  	           {?Resource ?Predicate ?Object}.
+            }
+      }
+        `;
+
+        return query;
+    }
+
+
+export default {fetchDistinctResourceCount};

+ 22 - 0
Luzzu Dashboard/src/services/tripleStoreAPIs/sparql/fetchListOfSimilarResource.js

@@ -0,0 +1,22 @@
+//© 2019 Dublin City University, Trinity College Dublin. All rights reserved. This material may not be reproduced, displayed, modified or distributed without the express prior written permission of the copyright holder.
+
+
+
+
+export const  fetchListOfSimilarResource = (graphType, dataGraph, queryPattern,resource) =>{
+        let query = `
+        SELECT DISTINCT ?Resource
+        FROM NAMED <${dataGraph}>
+      	WHERE {
+          {${graphType} <${dataGraph}>
+  	           {${queryPattern}}.
+            }
+             FILTER(?Resource!=${resource}).
+      }
+        `;
+
+        return query;
+    }
+
+
+export default {fetchListOfSimilarResource};

+ 21 - 0
Luzzu Dashboard/src/services/tripleStoreAPIs/sparql/fetchListOfSimilarTriples.js

@@ -0,0 +1,21 @@
+//© 2019 Dublin City University, Trinity College Dublin. All rights reserved. This material may not be reproduced, displayed, modified or distributed without the express prior written permission of the copyright holder.
+
+
+
+
+export const  fetchListOfSimilarTriples = (graphType, dataGraph, queryPattern) =>{
+        let query = `
+        SELECT *
+        FROM NAMED <${dataGraph}>
+      	WHERE {
+          {${graphType} <${dataGraph}>
+  	           {${queryPattern}}.
+            }
+      }
+        `;
+
+        return query;
+    }
+
+
+export default {fetchListOfSimilarTriples};

+ 22 - 0
Luzzu Dashboard/src/services/tripleStoreAPIs/sparql/fetchTriplesForResource.js

@@ -0,0 +1,22 @@
+//© 2019 Dublin City University, Trinity College Dublin. All rights reserved. This material may not be reproduced, displayed, modified or distributed without the express prior written permission of the copyright holder.
+
+
+
+
+export const  fetchTriplesForResource = (graphType, dataGraph, resource) =>{
+        let query = `
+        SELECT *
+        FROM NAMED <${dataGraph}>
+      	WHERE {
+          {${graphType} <${dataGraph}>
+  	           {?Resource ?Predicate ?Object}.
+            }
+             FILTER(?Resource=${resource}).
+      }
+        `;
+
+        return query;
+    }
+
+
+export default {fetchTriplesForResource};

+ 40 - 0
Luzzu Dashboard/src/services/tripleStoreAPIs/sparql/get1SpatialAssessmentData.js

@@ -0,0 +1,40 @@
+//© 2019 Dublin City University, Trinity College Dublin. All rights reserved. This material may not be reproduced, displayed, modified or distributed without the express prior written permission of the copyright holder.
+const  prefix = `
+PREFIX daq: <http://purl.org/eis/vocab/daq#>
+PREFIX sdma: <http://purl.org/linked-data/sdmx/2009/dimension#>
+PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
+PREFIX qpro: <http://purl.org/eis/vocab/qpro#>        
+PREFIX da: <https://www.wowman.org/index.php?id=1&type=get#>
+        `;
+
+
+
+export const  get1SpatialAssessmentData = (dataGraph) =>{
+        let query = `
+          SELECT  ?ObservationURI ?TimePeriod ?Value ?MetricUri ?maxTimePeriod ?onBuildings
+                   FROM NAMED  
+              <${dataGraph}> 
+              WHERE {
+
+                    {GRAPH ?QualityGraph {?ObservationURI a daq:Observation} }.
+                    {GRAPH ?QualityGraph {?ObservationURI daq:computedOn ?ComputedOn} }.
+                    {GRAPH ?QualityGraph {?ObservationURI sdma:timePeriod ?TimePeriod} }.
+                  {GRAPH ?QualityGraph {?ObservationURI daq:metric ?MetricUri} }.
+                    #{GRAPH ?QualityGraph {?onBuildings <http://purl.org/eis/vocab/daq#computedOn>    <http://ontologies.adaptcentre.ie/dataset-hierarchy#BUILDING>} }.
+                    {GRAPH ?QualityGraph {?ObservationURI daq:value ?Value} }.
+                    {
+                    SELECT  (max(?TimePeriod) AS ?maxTimePeriod)
+                    WHERE {
+                    {GRAPH ?QualityGraph {?x daq:computedOn ?ComputedOn} }.
+                    {GRAPH ?QualityGraph {?a sdma:timePeriod ?TimePeriod} }.
+                  }
+                }.
+                    FILTER (?TimePeriod =  ?maxTimePeriod).
+
+                  }
+        `;
+
+        return prefix + query;
+    }
+
+export default {get1SpatialAssessmentData};

+ 26 - 0
Luzzu Dashboard/src/services/tripleStoreAPIs/sparql/get1SpatialAssessmentMetrics.js

@@ -0,0 +1,26 @@
+//© 2019 Dublin City University, Trinity College Dublin. All rights reserved. This material may not be reproduced, displayed, modified or distributed without the express prior written permission of the copyright holder.
+//Query to get all metrics 
+
+const  prefix = `
+        `;
+
+
+
+export const  get1SpatialAssessmentMetrics = () =>{
+        let query = `
+          SELECT  DISTINCT ?metric ?label ?comment ?datatype ?metric ?dimension
+           WHERE {
+                 ?metric  
+                    <http://www.w3.org/2000/01/rdf-schema#subClassOf> <http://purl.org/eis/vocab/daq#Metric> ;
+                     <http://www.w3.org/2000/01/rdf-schema#label> ?label;
+                     <http://www.w3.org/2000/01/rdf-schema#comment> ?comment;
+                     <http://purl.org/eis/vocab/daq#expectedDataType> ?datatype.
+                     ?property  <http://www.w3.org/2000/01/rdf-schema#range> ?metric;
+                  <http://www.w3.org/2000/01/rdf-schema#domain> ?dimension.
+           }
+        `;
+
+        return prefix + query;
+    }
+
+export default {get1SpatialAssessmentMetrics};

+ 39 - 0
Luzzu Dashboard/src/services/tripleStoreAPIs/sparql/get1SpatialDimensions.js

@@ -0,0 +1,39 @@
+//© 2019 Dublin City University, Trinity College Dublin. All rights reserved. This material may not be reproduced, displayed, modified or distributed without the express prior written permission of the copyright holder.
+//Query fro the 1Spatial dimension and related metrics
+
+const  prefix = `
+PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> 
+PREFIX daq: <http://purl.org/eis/vocab/daq#>
+PREFIX sdma: <http://purl.org/linked-data/sdmx/2009/dimension#>
+PREFIX cube: <http://purl.org/linked-data/cube#>
+PREFIX prov: <http://www.w3.org/ns/prov#>
+PREFIX sd: <http://www.w3.org/ns/sparql-service-description#>
+PREFIX sdm: <http://standard.k-history.kr/resource/>
+PREFIX pr: <http://purl.org/ontology/prv/core#>
+PREFIX pro: <http://purl.org/hpi/patchr#>
+        `;
+
+
+
+export const  get1SpatialDimensions = (dataGraph) =>{
+        let query = `
+SELECT DISTINCT ?cat ?ObservationURI ?Metric ?MetricURI ?dimInstance ?dim ?TimePeriod
+        FROM NAMED  
+        <${dataGraph}>
+        WHERE {
+           {GRAPH ?QualityGraph{?ObservationURI a daq:Observation}}.
+        {GRAPH ?QualityGraph {?ObservationURI daq:metric ?Metric} }.
+  {GRAPH ?QualityGraph {?cat a  <http://data.example.com/category/Oracle>} }.
+  {GRAPH ?QualityGraph {?cat ?p  ?dimInstance} }.
+  {GRAPH ?QualityGraph {?dimInstance a ?dim} }.
+  {GRAPH ?QualityGraph {?dimInstance ?dimProp ?Metric} }.
+       {GRAPH ?QualityGraph {?Metric a ?MetricURI} }.
+        #{GRAPH ?QualityGraph {?ObservationURI rdfs?domain> ?Value} }.
+        {GRAPH ?QualityGraph {?ObservationURI sdma:timePeriod ?TimePeriod} }.
+      }
+        `;
+
+        return prefix + query;
+    }
+
+export default {get1SpatialDimensions};

+ 20 - 0
Luzzu Dashboard/src/services/tripleStoreAPIs/sparql/get1SpatialMappings.js

@@ -0,0 +1,20 @@
+//© 2019 Dublin City University, Trinity College Dublin. All rights reserved. This material may not be reproduced, displayed, modified or distributed without the express prior written permission of the copyright holder.
+//Query to get all metrics 
+
+const  prefix = `
+        `;
+
+
+
+export const  get1SpatialMappings = () =>{
+        let query = `
+          SELECT ?onespatial ?linked
+          WHERE {
+            ?onespatial <http://open.vocab.org/terms/similarTo> ?linked
+          }
+        `;
+
+        return prefix + query;
+    }
+
+export default {get1SpatialMappings};

+ 27 - 0
Luzzu Dashboard/src/services/tripleStoreAPIs/sparql/getAllAssessmentDates.js

@@ -0,0 +1,27 @@
+//© 2019 Dublin City University, Trinity College Dublin. All rights reserved. This material may not be reproduced, displayed, modified or distributed without the express prior written permission of the copyright holder.
+//Query for all assessments and their time
+
+const  prefix = `
+          PREFIX daq: <http://purl.org/eis/vocab/daq#>
+          PREFIX sdma: <http://purl.org/linked-data/sdmx/2009/dimension#>
+        `;
+
+
+
+export const  getAllAssessmentDates = (computedOn) =>{
+        let query = `
+          SELECT  DISTINCT ?QualityGraph ?ComputedOn ?TimePeriod
+          WHERE {
+            ?QualityGraph ?predicate daq:QualityGraph.
+            {GRAPH ?QualityGraph {?x daq:computedOn ?ComputedOn} }.
+            {GRAPH ?QualityGraph {?a sdma:timePeriod ?TimePeriod} }.
+            FILTER(str(?ComputedOn) = "${computedOn}").
+          }
+          ORDER BY ASC(?TimePeriod)
+        `;
+
+        return prefix + query;
+    }
+
+
+export default {getAllAssessmentDates};

+ 43 - 0
Luzzu Dashboard/src/services/tripleStoreAPIs/sparql/getAssessmentQuality.js

@@ -0,0 +1,43 @@
+//© 2019 Dublin City University, Trinity College Dublin. All rights reserved. This material may not be reproduced, displayed, modified or distributed without the express prior written permission of the copyright holder.
+//Query to get all metric quality values
+
+const  prefix = `
+PREFIX daq: <http://purl.org/eis/vocab/daq#>
+PREFIX sdma: <http://purl.org/linked-data/sdmx/2009/dimension#>
+PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
+PREFIX qpro: <http://purl.org/eis/vocab/qpro#>
+        `;
+
+
+
+export const  getAssessmentQuality = (computedOn,timePeriod) =>{
+        let query = `
+        SELECT   ?Value ?Metric
+        WHERE {
+          ?QualityGraph ?predicate daq:QualityGraph.
+          {GRAPH ?QualityGraph {?ObservationURI a daq:Observation} }.
+          {GRAPH ?QualityGraph {?ObservationURI daq:computedOn ?ComputedOn} }.
+          {GRAPH ?QualityGraph {?MetricURI daq:hasObservation ?ObservationURI} }.
+          {GRAPH ?QualityGraph {?MetricURI a ?Metric} }.
+          {GRAPH ?QualityGraph {?DimensionURI ?hasMetric ?MetricURI} }.
+          {GRAPH ?QualityGraph {?DimensionURI a ?Dimension} }.
+          {GRAPH ?QualityGraph {?CategoryURI ?hasDimension ?DimensionURI} }.
+          {GRAPH ?QualityGraph {?CategoryURI a ?Category} }.
+          {GRAPH ?QualityGraph {?ObservationURI sdma:timePeriod ?TimePeriod} }.
+          {GRAPH ?QualityGraph {?ObservationURI daq:value ?Value} }.
+          OPTIONAL{ {GRAPH ?ProblemGraph {?QualityProblem qpro:generatedBy ?Observation;
+                                      qpro:problemStructure ?ProblemStructure} }.
+                                      FILTER(?Observation IN (?ObservationURI)).
+                                    }.
+          FILTER(str(?ComputedOn) = "${computedOn}").
+          FILTER (?Dimension != daq:Observation).
+          FILTER (?TimePeriod =  "${timePeriod}"^^xsd:dateTime).
+        }
+
+        `;
+
+        return prefix + query;
+    }
+
+
+export default {getAssessmentQuality};

+ 21 - 0
Luzzu Dashboard/src/services/tripleStoreAPIs/sparql/getGraphList.js

@@ -0,0 +1,21 @@
+//© 2019 Dublin City University, Trinity College Dublin. All rights reserved. This material may not be reproduced, displayed, modified or distributed without the express prior written permission of the copyright holder.
+const  prefix = `
+PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
+        `;
+
+
+
+export const  getGraphList = () =>{
+        let query = `
+        SELECT DISTINCT ?DataGraph
+    		WHERE
+    		{
+      		{GRAPH ?DataGraph {?Subject ?Predicate ?Object}}
+    		}
+        `;
+
+        return prefix + query;
+    }
+
+
+export default {getGraphList};

+ 27 - 0
Luzzu Dashboard/src/services/tripleStoreAPIs/sparql/getRecentAssessmentDates.js

@@ -0,0 +1,27 @@
+//© 2019 Dublin City University, Trinity College Dublin. All rights reserved. This material may not be reproduced, displayed, modified or distributed without the express prior written permission of the copyright holder.
+//Query to get all recent assessment times
+import React from 'react';
+
+const  prefix = `
+          PREFIX daq: <http://purl.org/eis/vocab/daq#>
+          PREFIX sdma: <http://purl.org/linked-data/sdmx/2009/dimension#>
+        `;
+
+
+
+export const  getRecentAssessmentDates = (computedOn) =>{
+        let query = `
+          SELECT  (max(?TimePeriod) AS ?maxTimePeriod)
+          WHERE {
+            ?QualityGraph ?predicate daq:QualityGraph.
+            {GRAPH ?QualityGraph {?x daq:computedOn ?ComputedOn} }.
+            {GRAPH ?QualityGraph {?a sdma:timePeriod ?TimePeriod} }.
+            FILTER(str(?ComputedOn) = "${computedOn}").
+          }
+        `;
+
+        return prefix + query;
+    }
+
+
+export default {getRecentAssessmentDates};

+ 50 - 0
Luzzu Dashboard/src/services/tripleStoreAPIs/sparql/getRecentAssessmentQuality.js

@@ -0,0 +1,50 @@
+//© 2019 Dublin City University, Trinity College Dublin. All rights reserved. This material may not be reproduced, displayed, modified or distributed without the express prior written permission of the copyright holder.
+//Query to get all recent assessment quality values and related data
+const  prefix = `
+PREFIX daq: <http://purl.org/eis/vocab/daq#>
+PREFIX sdma: <http://purl.org/linked-data/sdmx/2009/dimension#>
+PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
+PREFIX qpro: <http://purl.org/eis/vocab/qpro#>
+        `;
+
+
+
+export const  getRecentAssessmentQuality = (computedOn) =>{
+        let query = `
+        SELECT  ?ObservationURI ?TimePeriod ?Value ?Metric ?Dimension ?Category ?ProblemGraph ?QualityProblem ?ProblemStructure
+        WHERE {
+        	?QualityGraph ?predicate daq:QualityGraph.
+          {GRAPH ?QualityGraph {?ObservationURI a daq:Observation} }.
+          {GRAPH ?QualityGraph {?ObservationURI daq:computedOn ?ComputedOn} }.
+          {GRAPH ?QualityGraph {?MetricURI daq:hasObservation ?ObservationURI} }.
+          {GRAPH ?QualityGraph {?MetricURI a ?Metric} }.
+          {GRAPH ?QualityGraph {?DimensionURI ?hasMetric ?MetricURI} }.
+          {GRAPH ?QualityGraph {?DimensionURI a ?Dimension} }.
+          {GRAPH ?QualityGraph {?CategoryURI ?hasDimension ?DimensionURI} }.
+          {GRAPH ?QualityGraph {?CategoryURI a ?Category} }.
+          {GRAPH ?QualityGraph {?ObservationURI sdma:timePeriod ?TimePeriod} }.
+          {GRAPH ?QualityGraph {?ObservationURI daq:value ?Value} }.
+          {
+          SELECT  (max(?TimePeriod) AS ?maxTimePeriod)
+        	WHERE {
+          ?QualityGraph ?predicate daq:QualityGraph.
+          {GRAPH ?QualityGraph {?x daq:computedOn ?ComputedOn} }.
+          {GRAPH ?QualityGraph {?a sdma:timePeriod ?TimePeriod} }.
+          FILTER(str(?ComputedOn) = "${computedOn}").
+        }
+      }.
+      OPTIONAL{ {GRAPH ?ProblemGraph {?QualityProblem qpro:generatedBy ?Observation;
+                                        qpro:problemStructure ?ProblemStructure} }.
+                                        FILTER(?Observation IN (?ObservationURI)).
+                                      }.
+          FILTER (?Dimension != daq:Observation).
+          FILTER (?TimePeriod =  ?maxTimePeriod).
+        }
+
+        `;
+
+        return prefix + query;
+    }
+
+
+export default {getRecentAssessmentQuality};

+ 41 - 0
Luzzu Dashboard/src/services/tripleStoreAPIs/sparql/problemReport/getErrorReportForModelDefault-Working.js

@@ -0,0 +1,41 @@
+//© 2019 Dublin City University, Trinity College Dublin. All rights reserved. This material may not be reproduced, displayed, modified or distributed without the express prior written permission of the copyright holder.
+
+const  prefix = `
+PREFIX qpro: <http://purl.org/eis/vocab/qpro#>
+PREFIX rdf:     <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
+PREFIX dqm-prob: <http://www.diachron-fp7.eu/dqm-prob#>
+PREFIX dqm:	<http://purl.org/eis/vocab/dqm#>
+        `;
+
+
+
+export const  getErrorReportForModelDefault = (graph,observationURI, exceptions, hasExceptions) =>{
+        let query = `
+        SELECT  (COALESCE(IF(BOUND(?HasProblematicThing), ?ProblematicThingResourceRef,?Empty)) AS ?ProblematicThingResource) ?Exception (COALESCE(?HasProblematicThing, ?ProblematicThingResourceRef) as ?ProblematicThing)
+		FROM NAMED <${graph}>
+WHERE {
+{GRAPH ?QualityGraph {?QualityProblem qpro:generatedBy ?Observation;
+                                     qpro:isDescribedBy ?IsDescribedBy;
+                                     qpro:problemStructure qpro:ModelContainer;
+                                     qpro:problematicThing ?ProblematicThingResourceRef} }.
+
+
+{
+   {GRAPH ?QualityGraph {?ProblematicThingResourceRef ?Description ?Exception }}.
+   FILTER(?Exception IN (${exceptions})).
+}.
+
+
+OPTIONAL    {
+ {GRAPH ?QualityGraph {?ProblematicThingResourceRef ?hasException ?HasProblematicThing }}.
+  FILTER(?hasException IN (${hasExceptions})).
+}.
+
+ FILTER(str(?Observation) = "${observationURI}").
+}        `;
+
+        return prefix + query;
+    }
+
+
+export default {getErrorReportForModelDefault};

+ 41 - 0
Luzzu Dashboard/src/services/tripleStoreAPIs/sparql/problemReport/getErrorReportForModelDefault.js

@@ -0,0 +1,41 @@
+//© 2019 Dublin City University, Trinity College Dublin. All rights reserved. This material may not be reproduced, displayed, modified or distributed without the express prior written permission of the copyright holder.
+
+const  prefix = `
+PREFIX qpro: <http://purl.org/eis/vocab/qpro#>
+PREFIX rdf:     <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
+PREFIX dqm-prob: <http://www.diachron-fp7.eu/dqm-prob#>
+PREFIX dqm:	<http://purl.org/eis/vocab/dqm#>
+        `;
+
+
+
+export const  getErrorReportForModelDefault = (graph,observationURI, exceptions, hasExceptions) =>{
+        let query = `
+        SELECT  DISTINCT (COALESCE(IF(BOUND(?HasProblematicThing), ?ProblematicThingResourceRef,?Empty)) AS ?ProblematicThingResource) ?Exception (COALESCE(?HasProblematicThing, ?ProblematicThingResourceRef) as ?ProblematicThing)
+		FROM NAMED <${graph}>
+WHERE {
+{GRAPH ?QualityGraph {?QualityProblem qpro:generatedBy ?Observation;
+                                     qpro:isDescribedBy ?IsDescribedBy;
+                                     qpro:problemStructure qpro:ModelContainer;
+                                     qpro:problematicThing ?ProblematicThingResourceRef} }.
+
+
+{
+   {GRAPH ?QualityGraph {?ProblematicThingResourceRef ?Description ?Exceptions }}.
+   FILTER(?Exceptions IN (${exceptions})).
+}.
+
+
+OPTIONAL    {
+ {GRAPH ?QualityGraph {?ProblematicThingResourceRef ?hasException ?HasProblematicThing }}.
+  FILTER(?hasException IN (${hasExceptions})).
+}.
+ BIND (COALESCE(IF((CONTAINS( STR(?hasException), "has" ) && BOUND(?hasException)), URI(CONCAT( STRBEFORE( STR(?hasException), "has"  ), STRAFTER( STR(?hasException), "has"  ))) ,?Exceptions)) AS ?Exception).
+ FILTER(str(?Observation) = "${observationURI}").
+}        `;
+
+        return prefix + query;
+    }
+
+
+export default {getErrorReportForModelDefault};

+ 34 - 0
Luzzu Dashboard/src/services/tripleStoreAPIs/sparql/problemReport/getErrorReportForModelProblematicTriple.js

@@ -0,0 +1,34 @@
+//© 2019 Dublin City University, Trinity College Dublin. All rights reserved. This material may not be reproduced, displayed, modified or distributed without the express prior written permission of the copyright holder.
+
+const  prefix = `
+PREFIX qpro: <http://purl.org/eis/vocab/qpro#>
+PREFIX rdf:     <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
+PREFIX dqm-prob: <http://www.diachron-fp7.eu/dqm-prob#>
+PREFIX dqm:	<http://purl.org/eis/vocab/dqm#>
+        `;
+
+
+
+export const  getErrorReportForModelProblematicTriple = (graph,observationURI) =>{
+        let query = `
+        SELECT  ?ProblematicThing ?ProblematicThingResource ?Exception
+		FROM NAMED <${graph}>
+WHERE {
+  {GRAPH ?QualityGraph {?QualityProblem qpro:generatedBy ?Observation;
+                                       qpro:isDescribedBy ?IsDescribedBy;
+                                       qpro:problemStructure qpro:ModelContainer;
+                                       qpro:problematicThing ?ProblematicThingRef} }.
+  {GRAPH ?QualityGraph {?ProblematicThingRef ?Description ?Exception;
+                                          dqm-prob:problematicTriple ?ProblematicTriple}}.
+  {GRAPH ?QualityGraph {?ProblematicTriple rdf:subject ?ProblematicThingResource;
+                                           rdf:predicate ?ProblematicThing}}.
+ FILTER(str(?Observation) = "${observationURI}").
+ FILTER(?Description = rdf:type).
+}
+        `;
+
+        return prefix + query;
+    }
+
+
+export default {getErrorReportForModelProblematicTriple};

+ 35 - 0
Luzzu Dashboard/src/services/tripleStoreAPIs/sparql/problemReport/getErrorReportForQuad.js

@@ -0,0 +1,35 @@
+//© 2019 Dublin City University, Trinity College Dublin. All rights reserved. This material may not be reproduced, displayed, modified or distributed without the express prior written permission of the copyright holder.
+
+const  prefix = `
+PREFIX qpro: <http://purl.org/eis/vocab/qpro#>
+PREFIX rdf:     <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
+PREFIX dqm-prob: <http://www.diachron-fp7.eu/dqm-prob#>
+PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
+PREFIX dqm: <http://purl.org/eis/vocab/dqm#>
+        `;
+
+
+
+export const  getErrorReportForQuad = (graph,observationURI) =>{
+        let query = `
+SELECT (COALESCE(IF(?predicate=qpro:exceptionDescription, ?Subject, ?Object)) AS ?ProblematicThing)  (?Subject AS ?ProblematicThingResource) ?Exception
+		FROM NAMED <${graph}>
+WHERE {
+	{GRAPH ?QualityGraph {?QualityProblem qpro:generatedBy ?Observation;
+    										qpro:isDescribedBy ?IsDescribedBy;
+              								qpro:problemStructure qpro:QuadContainer;
+                      						qpro:problematicThing ?ProblematicThings}}.
+  {GRAPH ?QualityGraph {?ProblematicThings ?Sequence ?ProblematicResource}}.
+  {GRAPH ?QualityGraph {?ProblematicResource rdf:object ?Object;
+                                             rdf:predicate ?predicate;
+                                             rdf:subject ?Subject}}.
+	 FILTER(str(?Observation) = "${observationURI}").
+   BIND(IF(?IsDescribedBy=dqm:CompatibleDatatype, xsd:dt-unknown-dt, ?Object) AS ?Exception).
+}
+`;
+
+        return prefix + query;
+    }
+
+
+export default {getErrorReportForQuad};

+ 31 - 0
Luzzu Dashboard/src/services/tripleStoreAPIs/sparql/problemReport/getErrorReportForResource.js

@@ -0,0 +1,31 @@
+//© 2019 Dublin City University, Trinity College Dublin. All rights reserved. This material may not be reproduced, displayed, modified or distributed without the express prior written permission of the copyright holder.
+
+const  prefix = `
+PREFIX qpro: <http://purl.org/eis/vocab/qpro#>
+PREFIX rdf:     <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
+PREFIX dqm-prob: <http://www.diachron-fp7.eu/dqm-prob#>
+PREFIX dqm: <http://purl.org/eis/vocab/dqm#>
+        `;
+
+
+
+export const  getErrorReportForResource = (graph,observationURI) =>{
+        let query = `
+SELECT  ?ProblematicThing (?ProblematicThing AS?ProblematicThingResource) ?Exception
+		FROM NAMED <${graph}>
+WHERE {
+	{GRAPH ?QualityGraph {?QualityProblem qpro:generatedBy ?Observation;
+                                       	qpro:isDescribedBy ?IsDescribedBy;
+                                        qpro:problemStructure qpro:ResourceContainer;
+                                        qpro:problematicThing ?Problems} }.
+	{GRAPH ?QualityGraph {?Problems ?Sequence ?ProblematicThing } }.
+	 FILTER(str(?Observation) = "${observationURI}").
+  	 FILTER (?Sequence != rdf:type).
+     BIND(IF(?IsDescribedBy=dqm:HumanReadableLabellingMetric, dqm-prob:NoHumanReadableLabel, "Unknown") AS ?Exception).
+}        `;
+
+        return prefix + query;
+    }
+
+
+export default {getErrorReportForResource};

+ 56 - 0
Luzzu Dashboard/src/services/tripleStoreAPIs/sparql/problemReport/listOfAllMetricsFailedForResource-Working.js

@@ -0,0 +1,56 @@
+//© 2019 Dublin City University, Trinity College Dublin. All rights reserved. This material may not be reproduced, displayed, modified or distributed without the express prior written permission of the copyright holder.
+
+const  prefix = `
+PREFIX qpro: <http://purl.org/eis/vocab/qpro#>
+PREFIX rdf:     <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
+PREFIX dqm-prob: <http://www.diachron-fp7.eu/dqm-prob#>
+PREFIX dqm: <http://purl.org/eis/vocab/dqm#>
+        `;
+
+
+
+export const  listOfAllMetricsFailedForResource = (graph,resource) =>{
+        let query = `
+        SELECT  ?IsDescribedBy ?Exception
+        FROM NAMED <${graph}>
+        WHERE
+        {
+        		{{GRAPH ?QualityGraph {?QualityProblemRef qpro:problematicThing ?Problems;
+        												qpro:problemStructure ?ProblemStructure;
+                                                       	qpro:isDescribedBy ?IsDescribedBy}}.
+        						{GRAPH ?QualityGraph {?Problems ?Sequence ?ProblematicThing } }.
+        		}
+            UNION
+        		{{GRAPH ?QualityGraph {?QualityProblemRef qpro:problematicThing ?ProblematicThing;
+        												qpro:problemStructure ?ProblemStructure;
+                                                       	qpro:isDescribedBy ?IsDescribedBy}}.
+        		{GRAPH ?QualityGraph {?ProblematicThing qpro:exceptionDescription ?Exception}}.
+        		}
+            UNION
+        		{{{GRAPH ?QualityGraph {?QualityProblemRef qpro:problematicThing ?ProblematicThingRef;
+        												qpro:problemStructure ?ProblemStructure;
+        												qpro:isDescribedBy ?IsDescribedBy} }.
+        		{GRAPH ?QualityGraph {?ProblematicThingRef ?ProblemDescription ?ProblemDetails;
+        												a ?Exception}}.
+        		{GRAPH ?QualityGraph {?ProblemDetails rdf:subject ?ProblematicThing}}.
+        			FILTER(?ProblemDescription = dqm-prob:problematicTriple).}
+        		}
+        	UNION
+        		{{{GRAPH ?QualityGraph {?QualityProblemRef qpro:problematicThing ?ProblematicThingRef;
+        												qpro:problemStructure ?ProblemStructure;
+                    									qpro:isDescribedBy ?IsDescribedBy} }.
+        		{GRAPH ?QualityGraph {?ProblematicThingRef ?Sequence ?ProblematicResource}}.
+        		{GRAPH ?QualityGraph {?ProblematicResource rdf:subject ?ProblematicThing;
+        													rdf:predicate ?Predicate;
+        													rdf:object ?Exception}}.
+        			FILTER(?Sequence != dqm-prob:problematicTriple).}
+                 }
+            FILTER(str(?ProblematicThing) =  "${resource}").
+        }
+        `;
+
+        return prefix + query;
+    }
+
+
+export default {listOfAllMetricsFailedForResource};

+ 51 - 0
Luzzu Dashboard/src/services/tripleStoreAPIs/sparql/problemReport/listOfAllMetricsFailedForResource.js

@@ -0,0 +1,51 @@
+//© 2019 Dublin City University, Trinity College Dublin. All rights reserved. This material may not be reproduced, displayed, modified or distributed without the express prior written permission of the copyright holder.
+
+const  prefix = `
+PREFIX qpro: <http://purl.org/eis/vocab/qpro#>
+PREFIX rdf:     <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
+PREFIX dqm-prob: <http://www.diachron-fp7.eu/dqm-prob#>
+PREFIX dqm: <http://purl.org/eis/vocab/dqm#>
+        `;
+
+
+
+export const  listOfAllMetricsFailedForResource = (graph,resource, modelContainerQuery) =>{
+        let query = `
+        SELECT  ?IsDescribedBy ?Exception
+        FROM NAMED <${graph}>
+        WHERE
+        {
+        		{{GRAPH ?QualityGraph {?QualityProblemRef qpro:problematicThing ?Problems;
+        												qpro:problemStructure ?ProblemStructure;
+                                                       	qpro:isDescribedBy ?IsDescribedBy}}.
+        						{GRAPH ?QualityGraph {?Problems ?Sequence ?ProblematicThing } }.
+        		}
+            ${modelContainerQuery}
+            UNION
+        		{{{GRAPH ?QualityGraph {?QualityProblemRef qpro:problematicThing ?ProblematicThingRef;
+        												qpro:problemStructure ?ProblemStructure;
+        												qpro:isDescribedBy ?IsDescribedBy} }.
+        		{GRAPH ?QualityGraph {?ProblematicThingRef ?ProblemDescription ?ProblemDetails;
+        												a ?Exception}}.
+        		{GRAPH ?QualityGraph {?ProblemDetails rdf:subject ?ProblematicThing}}.
+        			FILTER(?ProblemDescription = dqm-prob:problematicTriple).}
+        		}
+        	UNION
+        		{{{GRAPH ?QualityGraph {?QualityProblemRef qpro:problematicThing ?ProblematicThingRef;
+        												qpro:problemStructure ?ProblemStructure;
+                    									qpro:isDescribedBy ?IsDescribedBy} }.
+        		{GRAPH ?QualityGraph {?ProblematicThingRef ?Sequence ?ProblematicResource}}.
+        		{GRAPH ?QualityGraph {?ProblematicResource rdf:subject ?ProblematicThing;
+        													rdf:predicate ?Predicate;
+        													rdf:object ?Exception}}.
+        			FILTER(?Sequence != dqm-prob:problematicTriple).}
+                 }
+            FILTER(str(?ProblematicThing) =  "${resource}").
+        }
+        `;
+
+        return prefix + query;
+    }
+
+
+export default {listOfAllMetricsFailedForResource};

+ 34 - 0
Luzzu Dashboard/src/services/tripleStoreAPIs/statusUpdate.js

@@ -0,0 +1,34 @@
+//© 2019 Dublin City University, Trinity College Dublin. All rights reserved. This material may not be reproduced, displayed, modified or distributed without the express prior written permission of the copyright holder.
+import axios from 'axios';
+import properties from '../../config/dashboardProperties';
+
+const host = properties.tripleStore.host;
+const port = properties.tripleStore.port;
+const dataStore = properties.tripleStore.datastore;
+/*-------------config-------------*/
+const config ={
+    baseURL: 'http://'+host+'/$/stats/',
+    timeout: 10000,
+    headers: {'Accept': 'application/json','Content-Type':'application/x-www-form-urlencoded'},
+    withCredentials: true,
+    auth: {username: properties.tripleStore.userName, password: properties.tripleStore.password}
+  };
+/*-----------------------------------*/
+
+
+export const statusUpdate = (query) => axios.get(dataStore, config)
+                .then((response) => {
+
+                    return response;
+                })
+                .catch((err) => {
+                  if (!err.response) {
+                              // network error
+                            console.log('Error: Network Error. Unable to connect to '+ host+":"+port);
+                  } else {
+                            console.log(err.response.data.message);
+                  }
+                    //return err;
+                })
+
+export default {statusUpdate};

+ 43 - 0
Luzzu Dashboard/src/services/tripleStoreAPIs/uploadToTripleStore.js

@@ -0,0 +1,43 @@
+//© 2019 Dublin City University, Trinity College Dublin. All rights reserved. This material may not be reproduced, displayed, modified or distributed without the express prior written permission of the copyright holder.
+import axios from 'axios';
+import properties from '../../config/dashboardProperties';
+
+/*-------------config-------------*/
+const config ={
+    timeout: 10000,
+    headers: {'Accept': 'application/json','Content-Type':'application/x-www-form-urlencoded'},
+    withCredentials: true,
+    auth: {username: properties.tripleStore.userName, password: properties.tripleStore.password}
+  };
+
+/*-----------------------------------*/
+
+
+const host = properties.tripleStore.host;
+const port = properties.tripleStore.port;
+const dataStore = properties.tripleStore.datastore;
+let url = "http://"+host+"/"+dataStore+"/data";
+
+
+export const uploadToTripleStore = (fileToUpload, fileName) => {
+  const formData = new FormData();
+  formData.append('file', fileToUpload);
+
+return  axios.post(url, formData, {...config, params : {"graph": fileName}})
+  .then((response) => {
+      return response;})
+  .catch((err) => {
+    if (!err.response) {
+                // network error
+              console.log('Error: Network Error. Unable to connect to '+ host+":"+port);
+    } else {
+              console.log(err.response.data.message);
+    }
+      return err;
+  });
+
+
+}
+
+
+export default {uploadToTripleStore};

+ 15 - 0
Luzzu Dashboard/src/setupTests.js

@@ -0,0 +1,15 @@
+import { configure } from 'enzyme';
+import Adapter from 'enzyme-adapter-react-16';
+
+configure({ adapter: new Adapter() });
+
+if (global.document) {
+  document.createRange = () => ( {
+    setStart: () => {},
+    setEnd: () => {},
+    commonAncestorContainer: {
+      nodeName: 'BODY',
+      ownerDocument: document,
+    },
+  });
+}

+ 100 - 0
Luzzu Dashboard/src/store/connectDatasetReducer.js

@@ -0,0 +1,100 @@
+//© 2019 Dublin City University, Trinity College Dublin. All rights reserved. This material may not be reproduced, displayed, modified or distributed without the express prior written permission of the copyright holder.
+const initialState = {
+  modalConfigOpen: false,
+  modalOptionsOpen : false,
+  datasetName: "",
+  isSparqlEndPoint: false,
+  sparqlEndPointURL: "",
+  datasetPLD: "",
+  fileToUpload: null,
+  historyFileToUpload: null,
+  datasetNameValid: true,
+  datasetNameDisabled : false,
+  datasetPLDValid: true,
+  datasetPLDDisabled : false,
+  datasetPLDMandatoryValid: true,
+  datasetPLDPrefixValid: true,
+  sparqlEndPointURLValid: true,
+  fileUploadValid: true,
+  fileUploadMandatory: true,
+  fileFormatValid: true,
+  fileUploadProgress: 0,
+  showFileUploadProgress: false,
+  fileUploadCompleted : false,
+  historyFileUploadCompleted : false,
+  fileUploadSuccessfull: false,
+  historyFileUploadSuccessfull: false,
+  fileUploadedName : "",
+  fileUploadedPath : "",
+  dimensionsToAssess : [],
+  assessmentDetails : [],
+  prevAssessmentDetails : [],
+  datasetID : "",
+  displayUseExistingFileCheckBox : false,
+  useExistingFile : false,
+  qualityMetrics : []
+
+}
+
+const connectDatasetReducer = (state = initialState, action) => {
+
+  switch (action.type){
+    case "OPEN_CONFIG_MODEL":
+    return({
+      ...state,
+      modalConfigOpen:true,
+      modalOptionsOpen : false
+    });
+    case "REOPEN_CONFIG_MODEL":
+    let hasExistingFile=false;
+    if(action.payLoad.fileName!=="")
+    {
+      hasExistingFile=true;
+    }
+    return({
+      ...state,
+      datasetID : action.payLoad.datasetID,
+      datasetName:action.payLoad.datasetName,
+      datasetPLD:action.payLoad.datasetPLD,
+      isSparqlEndPoint:action.payLoad.isSparqlEndPoint,
+      sparqlEndPointURL:action.payLoad.sparqlEndPoint,
+      prevAssessmentDetails : [...action.payLoad.assessmentMetrics],
+      datasetNameDisabled : true,
+      datasetPLDDisabled : true,
+      fileUploadedPath : (action.payLoad.fileName).substring(0, (action.payLoad.fileName).lastIndexOf("/") + 1),
+      fileUploadedName : (action.payLoad.fileName).substring((action.payLoad.fileName).lastIndexOf("/") + 1, (action.payLoad.fileName).length),
+      displayUseExistingFileCheckBox : hasExistingFile,
+      useExistingFile : hasExistingFile,
+      modalConfigOpen:true,
+      modalOptionsOpen : false
+    });
+    case "OPEN_OPTION_MODEL":
+    return({
+      ...action.payLoad,
+      modalConfigOpen:false,
+      modalOptionsOpen : true
+    });
+    case "BACK_CONFIG_MODEL":
+    return({
+      ...action.payLoad,
+      modalConfigOpen:true,
+      modalOptionsOpen : false
+    });
+    case "CLOSE_CONFIG_MODEL":
+    return({
+      ...initialState
+    });
+    case "CLOSE_OPTION_MODEL":
+    return({
+      ...initialState
+    });
+    default:
+    return (
+      {...state,
+      modalConfigOpen:false,
+      modalOptionsOpen : false
+    });
+    }
+};
+
+export default connectDatasetReducer;

+ 148 - 0
Luzzu Dashboard/src/store/datasetDetailsCacheReducer.js

@@ -0,0 +1,148 @@
+//© 2019 Dublin City University, Trinity College Dublin. All rights reserved. This material may not be reproduced, displayed, modified or distributed without the express prior written permission of the copyright holder.
+const initialState = {
+  datasetDetailsCache : [],
+  pipelineCache : [],
+  loggedIn: false
+}
+
+const datasetDetailsCacheReducer = (state = initialState, action) => {
+  let fromSessionStore = JSON.parse(sessionStorage.getItem("DATASETCACHE"));
+  let pipelineStore = JSON.parse(sessionStorage.getItem("PIPELINECACHE"));
+
+  switch (action.type){
+    case "CREATE_DATASET_DETAILS_CACHE":
+    //console.log("CREATE_DATASET_DETAILS_CACHE");
+    //console.log(action.payLoad);
+    sessionStorage.setItem("DATASETCACHE",JSON.stringify(action.payLoad) );
+    return({
+      datasetDetailsCache : [...action.payLoad]
+    });
+    case "ADD_DATASET_DETAILS_CACHE":
+    //console.log("ADD_DATASET_DETAILS_CACHE");
+    //console.log(action.payLoad);
+    //console.log(fromSessionStore);
+    fromSessionStore.push(action.payLoad);
+    //console.log(fromSessionStore);
+    sessionStorage.setItem("DATASETCACHE",JSON.stringify(fromSessionStore) );
+    return({
+      datasetDetailsCache : [...fromSessionStore]
+    });
+    case "UPDATE_DATASET_DETAILS_CACHE":
+    //console.log("UPDATE_DATASET_DETAILS_CACHE");
+    //console.log(action.payLoad);
+    //console.log(action.payLoad.datasetID);
+    //console.log(fromSessionStore);
+    let storeToUpdate = [...fromSessionStore];
+    let indexOfDatasetToUpdate = storeToUpdate.findIndex((datasetDetails)=>{return datasetDetails.datasetID===action.payLoad.datasetID});
+    //console.log(indexOfDatasetToUpdate);
+    //console.log(storeToUpdate[indexOfDatasetToUpdate]);
+    let updateData = {
+      ...storeToUpdate[indexOfDatasetToUpdate],
+      ...action.payLoad
+    }
+    //console.log(updateData);
+    storeToUpdate[indexOfDatasetToUpdate]=updateData;
+    //console.log(storeToUpdate);
+    sessionStorage.setItem("DATASETCACHE",JSON.stringify(storeToUpdate));
+    return({
+      datasetDetailsCache : [...storeToUpdate]
+    });
+
+    case "UPDATE_METRIC_ERROR_REPORT_CACHE":
+    //console.log("UPDATE_METRIC_ERROR_REPORT_CACHE");
+    //console.log(action.payLoad);
+    //console.log(action.payLoad.datasetID);
+    //console.log(fromSessionStore);
+    let storeToUpdateErrorReport = [...fromSessionStore];
+    let indexOfDatasetToUpdateErrorReport = storeToUpdateErrorReport.findIndex((datasetDetails)=>{ return datasetDetails.datasetID===action.payLoad.datasetID});
+    //console.log(indexOfDatasetToUpdateErrorReport);
+    storeToUpdateErrorReport[indexOfDatasetToUpdateErrorReport].lastAssessmentMetrics[action.payLoad.metricIndex]= {...storeToUpdateErrorReport[indexOfDatasetToUpdateErrorReport].lastAssessmentMetrics[action.payLoad.metricIndex], errorReport:action.payLoad.errorReport};
+    //console.log(storeToUpdateErrorReport[indexOfDatasetToUpdateErrorReport]);
+    sessionStorage.setItem("DATASETCACHE",JSON.stringify(storeToUpdateErrorReport));
+    return({
+      datasetDetailsCache : [...storeToUpdateErrorReport]
+    });
+
+    case "REMOVE_DATASET_DETAILS_CACHE":
+    //console.log("REMOVE_DATASET_DETAILS_CACHE");
+    //console.log(action.payLoad);
+    let afterRemovedDataset = fromSessionStore.filter((datasetDetails)=>{return datasetDetails.datasetID!==action.payLoad.datasetID});
+    //console.log(afterRemovedDataset);
+    sessionStorage.setItem("DATASETCACHE",JSON.stringify(afterRemovedDataset));
+    return({
+      datasetDetailsCache : [...afterRemovedDataset]
+    });
+
+    case "CREATE_PIPELINE_CACHE":
+    //console.log("CREATE_DATASET_DETAILS_CACHE");
+    //console.log(action.payLoad);
+    sessionStorage.setItem("PIPELINECACHE",JSON.stringify(action.payLoad) );
+    return({
+      pipelineCache : [...action.payLoad]
+    });
+    case "ADD_PIPELINE_CACHE":
+    //console.log("ADD_DATASET_DETAILS_CACHE");
+    //console.log(action.payLoad);
+    //console.log(fromSessionStore);
+    pipelineStore.push(action.payLoad);
+    sessionStorage.setItem("PIPELINECACHE",JSON.stringify(pipelineStore) );
+    return({
+      pipelineCache : [...pipelineStore]
+    });
+
+    case "SET_IS_LOGGED_IN":
+    console.log(action.payLoad);
+    return({
+      loggedIn: action.payload
+    });
+
+    case "UPDATE_HISTORIC_ASSESSMENT_DETAILS_CACHE":
+    //console.log("UPDATE_HISTORIC_ASSESSMENT_DETAILS_CACHE");
+    //console.log(action.payLoad);
+    let prevStore = [...fromSessionStore];
+    let index = prevStore.findIndex((datasetDetails)=>{return datasetDetails.datasetID===action.payLoad.datasetID});
+    let modifyDataset=null;
+    //console.log(prevStore[index]);
+    //console.log(prevStore[index].historicAssessmentData);
+    if(typeof prevStore[index].historicAssessmentData==="undefined")
+    {
+        modifyDataset={
+          ...prevStore[index],
+          historicAssessmentData : [action.payLoad.historicAssessmentData]
+        };
+    }
+    else {
+      modifyDataset={
+        ...prevStore[index],
+        historicAssessmentData : [...prevStore[index].historicAssessmentData,action.payLoad.historicAssessmentData]
+      };
+    }
+    //console.log(modifyDataset);
+    prevStore[index]=modifyDataset;
+    //console.log(prevStore);
+    sessionStorage.setItem("DATASETCACHE",JSON.stringify(prevStore) );
+    return({
+      datasetDetailsCache : [...prevStore]
+    });
+
+    default:
+    //console.log("DEFAULT");
+    if(fromSessionStore!==null)
+    {
+      //console.log("NOT EMPTY");
+      //console.log(fromSessionStore);
+      return (
+        {
+          datasetDetailsCache:[...fromSessionStore],
+          pipelineCache:[...pipelineStore]
+      });
+    }
+
+    return (
+      {
+        ...state
+    });
+    }
+};
+
+export default datasetDetailsCacheReducer;

+ 39 - 0
Luzzu Dashboard/src/views/Dashboard/ConnectDataSet/ConnectDataSet.js

@@ -0,0 +1,39 @@
+//© 2019 Dublin City University, Trinity College Dublin. All rights reserved. This material may not be reproduced, displayed, modified or distributed without the express prior written permission of the copyright holder.
+import React, { Component } from 'react';
+import {
+    Col,
+    Container,
+    Card,
+    CardBody,
+    CardText,
+    CardTitle,
+    CardSubtitle,
+    Media
+} from 'reactstrap';
+import uploadImage from '../../../assets/cloud-upload.png'
+
+class ConnectDataSet extends Component {
+
+    render() {
+        return (
+            <Col xs="12" sm="6" lg="4" onClick={this.props.tileClicked}>
+                <Container style={{ margin: 'auto' }} className=" w-100 h-90 connectDataSet">
+                    <Card className="text-white bg-light text-center w-100 h-90 ">
+                        <CardBody>
+                            <CardTitle></CardTitle>
+                            <CardSubtitle></CardSubtitle>
+                            <Media style={{ height: '150px', width: '150px', margin: 'auto' }} src={uploadImage} alt="Upload Image" />
+                            <CardText className="text-value text-dark">CONNECT</CardText>
+                            <CardText className="text-dark">a dataset</CardText>
+                        </CardBody>
+                        <CardBody className="pb-0">
+                            <div style={{ height: '20px' }} />
+                        </CardBody>
+                    </Card>
+                </Container>
+            </Col>
+        );
+    }
+}
+
+export default ConnectDataSet;

+ 526 - 0
Luzzu Dashboard/src/views/Dashboard/ConnectDataSet/DatasetConnectConfigModelWindow.js

@@ -0,0 +1,526 @@
+//© 2019 Dublin City University, Trinity College Dublin. All rights reserved. This material may not be reproduced, displayed, modified or distributed without the express prior written permission of the copyright holder.
+import React, { Component } from 'react';
+import {
+    Modal,
+    ModalHeader,
+    ModalBody,
+    ModalFooter,
+    Button,
+    FormGroup,
+    Form,
+    Label,
+    Input,
+    FormFeedback,
+    Progress
+} from 'reactstrap';
+
+import axios from 'axios';
+import properties from '../../../config/dashboardProperties';
+import FileTooLarge from './fileLargeError';
+
+const host = properties.luzzuFramework.host;
+const port = properties.luzzuFramework.port;
+const supportedFileFormats = properties.supportedFileFormats;
+
+const initialState = {
+  datasetName: "",
+  isSparqlEndPoint: false,
+  sparqlEndPointURL: "",
+  datasetPLD: "",
+  fileToUpload: null,
+  historyFileToUpload: null,
+  datasetNameValid: true,
+  datasetNameDisabled : false,
+  datasetPLDValid: true,
+  datasetPLDDisabled : false,
+  datasetPLDMandatoryValid: true,
+  datasetPLDPrefixValid: true,
+  sparqlEndPointURLValid: true,
+  fileUploadValid: true,
+  fileUploadMandatory: true,
+  fileFormatValid: true,
+  fileUploadProgress: 0,
+  showFileUploadProgress: false,
+  fileUploadCompleted : false,
+  historyFileUploadCompleted : false,
+  fileUploadSuccessfull: false,
+  historyFileUploadSuccessfull: false,
+  fileUploadedName : "",
+  fileUploadedPath : "",
+  dimensionsToAssess : [],
+  assessmentDetails : [],
+  prevAssessmentDetails : [],
+  datasetID : "",
+  displayUseExistingFileCheckBox : false,
+  useExistingFile : false,
+  qualityMetrics : [],
+  lastAssessmentRequestID:"",
+  fileToolarge: false
+}
+
+class DatasetConnectConfigModelWindow extends Component {
+
+    state = {
+
+    };
+
+    componentDidMount() {
+      //console.log(supportedFileFormats);
+        this.setState(
+          {
+            ...initialState,
+            ...this.props.currentState
+          }
+        );
+
+    }
+
+    triggerSubmitValidation = () => {
+
+        let datasetNameValid = false;
+        let datasetPLDValid = false;
+        let datasetPLDMandatoryValid = false;
+        let datasetPLDPrefixValid = false;
+        let sparqlEndPointURLValid = false;
+        let fileUploadValid = false;
+        let fileUploadMandatory = false;
+        let fileFormatValid = false;
+        let fileToUpload = null;
+        let fileUploadCompleted = false;
+        //Dataset Name is mandatory
+        if (this.state.datasetName === "") {
+            datasetNameValid = false;
+        }
+        else {
+            datasetNameValid = true;
+        }
+
+        //Dataset PLD is mandatory
+        if (this.state.datasetPLD === "") {
+            datasetPLDValid = false;
+            datasetPLDMandatoryValid = false;
+            datasetPLDPrefixValid = true;
+
+        }
+        else {
+            if (this.state.datasetPLD.startsWith("uri:") || this.state.datasetPLD.startsWith("http://") || this.state.datasetPLD.startsWith("https://")) {
+                datasetPLDValid = true;
+                datasetPLDMandatoryValid = true;
+                datasetPLDPrefixValid = true;
+
+            }
+            else {
+
+                datasetPLDValid = false;
+                datasetPLDMandatoryValid = true;
+                datasetPLDPrefixValid = false;
+
+            }
+
+        }
+        //SPARQL End-Point is mandatory if Is SPARQL is selected as Yes
+        if (this.state.isSparqlEndPoint && this.state.sparqlEndPointURL === "") {
+            sparqlEndPointURLValid = false;
+
+        }
+        else {
+            sparqlEndPointURLValid = true;
+        }
+        //File Upload
+        if (!this.state.isSparqlEndPoint && this.state.fileToUpload === null) {
+
+            fileUploadValid = false;
+            fileUploadMandatory = false;
+            fileFormatValid = true;
+
+        }
+        else {
+            if (this.state.isSparqlEndPoint) {
+                //No need to validate file if it is a SPARQL End-Point. So set state of fileUploadMandatory and fileFormatValid TRUE so that no validation is called.
+
+                fileToUpload = null;
+                fileUploadValid = true;
+                fileUploadMandatory = true;
+                fileFormatValid = true;
+
+            }
+            else {
+                fileToUpload = this.state.fileToUpload;
+                let fileName = this.state.fileToUpload.name;
+                let period = fileName.lastIndexOf(".");
+                let fileExtension = fileName.substring(period + 1);
+                let fileFormatSupported = false;
+                fileFormatSupported=supportedFileFormats.findIndex((fileFormats)=>{return fileFormats.extension===fileExtension});
+                if (fileFormatSupported>=0) {
+                    fileUploadValid = true;
+                    fileUploadMandatory = true;
+                    fileFormatValid = true;
+
+                }
+                else {
+
+                    fileUploadValid = false;
+                    fileUploadMandatory = true;
+                    fileFormatValid = false;
+
+                }
+            }
+        }
+
+        if(this.state.useExistingFile)
+        {
+          fileUploadMandatory=true;
+        }
+        if (this.state.fileUploadCompleted && !this.state.fileUploadSuccessfull) {
+            fileUploadCompleted = false;
+        }
+        else if (this.state.fileUploadCompleted && this.state.fileUploadSuccessfull) {
+            fileUploadCompleted = true;
+        }
+
+        this.setState(
+            {
+                datasetNameValid: datasetNameValid,
+                datasetPLDValid: datasetPLDValid,
+                datasetPLDMandatoryValid: datasetPLDMandatoryValid,
+                datasetPLDPrefixValid: datasetPLDPrefixValid,
+                sparqlEndPointURLValid: sparqlEndPointURLValid,
+                fileUploadValid: fileUploadValid,
+                fileUploadMandatory: fileUploadMandatory,
+                fileFormatValid: fileFormatValid,
+                fileToUpload: fileToUpload,
+                fileUploadCompleted: fileUploadCompleted
+
+            }, () => { this.triggerSubmitAction(); }
+        );
+
+
+    }
+
+    triggerSubmitAction = () => {
+      if(this.state.datasetNameValid && this.state.datasetPLDValid && !this.state.isSparqlEndPoint && this.state.useExistingFile){
+        this.props.onNext(this.state);
+      }
+      else {
+        if (this.state.datasetNameValid && this.state.datasetPLDValid && !this.state.isSparqlEndPoint && this.state.fileUploadValid) {
+            if (!this.state.fileUploadCompleted && !this.state.fileUploadSuccessfull) {
+
+
+                if(this.state.fileToUpload.size<=52428800)
+                {
+                  this.fileUploadHandler();
+                }
+                else {
+                  this.setState({
+                    fileToolarge:true
+                  });
+                }
+
+            }
+            else {
+                  this.props.onNext(this.state);
+            }
+        }
+        else if (this.state.datasetNameValid && this.state.datasetPLDValid && this.state.isSparqlEndPoint && this.state.sparqlEndPointURLValid) {
+              this.props.onNext(this.state);
+        }
+      }
+
+
+    }
+
+    fileUploadHandler = () => {
+        const formData = new FormData();
+        formData.append('file', this.state.fileToUpload, this.state.fileToUpload.name);
+        axios.post('http://'+host+':'+port+'/Luzzu/v4/import', formData, {
+            onUploadProgress: (progressEvent) => {
+                this.setState({
+                    showFileUploadProgress: true,
+                    fileUploadProgress: Math.round((progressEvent.loaded / progressEvent.total) * 100),
+                });
+            }, headers: {'Accept': 'application/json','Content-Type':'multipart/form-data'}
+        })
+            .then((res) => {
+                if (res.status === 200) {
+                    this.setState({
+
+                        fileUploadCompleted: true,
+                        historyFileUploadCompleted : true,
+                        fileUploadSuccessfull: true,
+                        historyFileUploadSuccessfull: true,
+                        fileUploadedName: res.data.fileName,
+                        fileUploadedPath: res.data.filePath,
+                        historyFileToUpload : this.state.fileToUpload,
+                        displayUseExistingFileCheckBox : false
+                    });
+                    const sleep = (milliseconds) => {
+                      return new Promise(resolve => setTimeout(resolve, milliseconds))
+                    }
+                    sleep(1000).then(() => {
+                      this.props.onNext(this.state);
+                    });
+
+                }
+                else {
+                    this.setState({
+                        showFileUploadProgress: false,
+                        fileUploadProgress: 0,
+                        fileUploadCompleted: true,
+                        fileUploadSuccessfull: false,
+                        fileUploadedName: "",
+                        fileUploadedPath: ""
+                    });
+                }
+
+            })
+            .catch((err) => {
+                if (!err.response) {
+                    // network error
+                    //console.log('Error: Network Error. Unable to connect to Server');
+                } else {
+                    //console.log(err.response.data.message);
+                }
+                this.setState({
+                    showFileUploadProgress: false,
+                    fileUploadProgress: 0,
+                    fileUploadCompleted: true,
+                    fileUploadSuccessfull: false,
+                    fileUploadedName: "",
+                    fileUploadedPath: ""
+                });
+
+            });
+    }
+
+
+    datasetNameChange = (event) => {
+      let regExpr = /[^a-zA-Z0-9-_ ]/g;
+        this.setState(
+            { datasetName: event.target.value.replace(regExpr, "") }
+        );
+    }
+
+    sparqlEndPointURLChange = (event) => {
+        this.setState(
+            { sparqlEndPointURL: event.target.value }
+        );
+    }
+
+    useExistingFileChange = (event) => {
+      if(this.state.useExistingFile)
+      {
+        this.setState({
+          useExistingFile : false,
+          fileToUpload: this.state.historyFileToUpload,
+          fileUploadSuccessfull:this.state.historyFileUploadSuccessfull,
+          fileUploadCompleted:this.state.historyFileUploadCompleted
+        });
+      }
+      else {
+        this.setState({
+          useExistingFile : true,
+          fileToUpload: null
+        });
+      }
+
+    }
+
+    isSparqlEndPointChange = (event) => {
+        let isSparqlEndPoint = false;
+        if (event.target.checked) {
+            if (event.target.value === "true") {
+                if (document.getElementById("uploadFileSelector")) {
+                    document.getElementById("uploadFileSelector").value = "";
+                }
+
+                isSparqlEndPoint = true;
+                this.setState(
+                    {
+                        isSparqlEndPoint: isSparqlEndPoint,
+                        fileToUpload: null,
+                        fileUploadValid: true,
+                        fileUploadMandatory: true,
+                        fileFormatValid: true
+                    }
+                );
+            }
+            else {
+                this.setState(
+                    {
+                        isSparqlEndPoint: isSparqlEndPoint,
+                        sparqlEndPointURL: "",
+                        sparqlEndPointURLValid: true,
+                        fileToUpload : this.state.historyFileToUpload,
+                        fileUploadSuccessfull:this.state.historyFileUploadSuccessfull,
+                        fileUploadCompleted:this.state.historyFileUploadCompleted
+                    }
+                );
+            }
+        }
+    }
+
+    is1SpatialChange = (event) => {
+        let is1Spatial = false;
+        if (event.target.checked) {
+            if (event.target.value === "true") {
+                if (document.getElementById("uploadFileSelector")) {
+                    document.getElementById("uploadFileSelector").value = "";
+                }
+
+                is1Spatial = true;
+                this.setState(
+                    {
+                        is1Spatial: is1Spatial
+                    }
+                );
+            }
+            else {
+                this.setState(
+                    {
+                        is1Spatial: is1Spatial
+                    }
+                );
+            }
+        }
+    }
+
+    datasetPLDChange = (event) => {
+        this.setState(
+            { datasetPLD: event.target.value }
+        );
+    }
+
+    closeFileLargeError = () =>{
+      this.setState({
+        fileToolarge:false
+      });
+    }
+
+    fileSelectionOnChange = (event) => {
+        if (event.target.files.length > 0) {
+            this.setState(
+                {
+                    fileToUpload: event.target.files[0],
+                    fileUploadValid: true,
+                    fileUploadMandatory: true,
+                    fileFormatValid: true,
+                    fileUploadCompleted : false,
+                    fileUploadSuccessfull : false
+                }
+            );
+
+        } else {
+            this.setState(
+                {
+                    fileToUpload: this.state.historyFileToUpload,
+                    fileUploadValid: true,
+                    fileUploadMandatory: true,
+                    fileFormatValid: true,
+                    fileUploadSuccessfull:this.state.historyFileUploadSuccessfull,
+                    fileUploadCompleted:this.state.historyFileUploadCompleted
+                }
+            );
+        }
+
+        if (this.state.fileUploadCompleted && !this.state.fileUploadSuccessfull) {
+            this.setState(
+                {
+
+                    fileUploadCompleted: false
+                }
+            );
+        }
+    }
+
+
+    render() {
+      //console.log(this.state);
+      let formats = supportedFileFormats.reduce((prev,next)=>{
+        if(prev.fileFormat!==undefined)
+        {
+          return (prev.fileFormat + " " + next.fileFormat);
+        }
+        else
+        {
+          return (prev + " " + next.fileFormat);
+        }
+       });
+        return (
+
+            <Modal isOpen={true} className="modal-dialog-centered">
+                <ModalHeader style={{ margin: 'auto' }}>Connect a Dataset</ModalHeader>
+                <ModalBody>
+                    <Form>
+                        <FormGroup>
+                            <Label >Dataset Name:</Label>
+                            {this.state.datasetNameValid ? <Input type="text" name="datasetName" id="datasetName" placeholder="Type dataset name here" onChange={this.datasetNameChange} value={this.state.datasetName} disabled={this.state.datasetNameDisabled}/> : <Input invalid type="text" name="datasetName" id="datasetName" placeholder="Type dataset name here" onChange={this.datasetNameChange} value={this.state.datasetName} />}
+                            <FormFeedback invalid>Dataset Name is Mandatory!!!</FormFeedback>
+                        </FormGroup>
+                        <FormGroup tag="onespatial">
+                            <Label>Is it a 1Spatial dataset?</Label>
+                            <FormGroup check>
+                                <Label check>
+                                    <Input type="radio" name="is1Spatial" value="true" checked={this.state.is1Spatial} onChange={this.is1SpatialChange} />{' '}
+                                    Yes
+                                    </Label>
+                            </FormGroup>
+                            <FormGroup check>
+                                <Label check>
+                                    <Input type="radio" name="is1Spatial" value="false" checked={!this.state.is1Spatial} onChange={this.is1SpatialChange} />{' '}
+                                    No
+                                </Label>
+                            </FormGroup>
+                        </FormGroup>
+                        <FormGroup tag="fieldset">
+                            <Label>Is it a SPARQL end-point?</Label>
+                            <FormGroup check>
+                                <Label check>
+                                    <Input type="radio" name="sparqlendpoint" value="true" checked={this.state.isSparqlEndPoint} onChange={this.isSparqlEndPointChange} />{' '}
+                                    Yes
+                                    </Label>
+                            </FormGroup>
+                            <FormGroup check>
+                                <Label check>
+                                    <Input type="radio" name="sparqlendpoint" value="false" checked={!this.state.isSparqlEndPoint} onChange={this.isSparqlEndPointChange} />{' '}
+                                    No
+                                </Label>
+                            </FormGroup>
+                        </FormGroup>
+                        <FormGroup>
+                            <Label >SPARQL End-Point:</Label>
+                            {this.state.sparqlEndPointURLValid ? <Input type="text" name="sparqlEndPoint" id="sparqlEndPoint" placeholder="Type SPARQL URL here" disabled={!this.state.isSparqlEndPoint} onChange={this.sparqlEndPointURLChange} value={this.state.sparqlEndPointURL} /> : <Input invalid type="text" name="sparqlEndPoint" id="sparqlEndPoint" placeholder="Type SPARQL URL here" disabled={!this.state.isSparqlEndPoint} onChange={this.sparqlEndPointURLChange} value={this.state.sparqlEndPointURL} />}
+                            {this.state.isSparqlEndPoint ? <FormFeedback invalid>SPARQL End-Point is Mandatory!!!</FormFeedback> : null}
+                        </FormGroup>
+                        <FormGroup>
+                            <Label >Dataset PLD:</Label>
+                            {this.state.datasetPLDValid ? <Input type="text" name="datasetPLD" id="datasetPLD" placeholder="Type Dataset PLD here" onChange={this.datasetPLDChange} value={this.state.datasetPLD} disabled={this.state.datasetPLDDisabled}/> : <Input invalid type="text" name="datasetPLD" id="datasetPLD" placeholder="Type Dataset PLD here" onChange={this.datasetPLDChange} value={this.state.datasetPLD} />}
+                            {this.state.datasetPLDMandatoryValid ? null : <FormFeedback invalid>Dataset PLD is Mandatory!!!</FormFeedback>}
+                            {this.state.datasetPLDPrefixValid ? null : <FormFeedback invalid>Dataset PLD should be prefixed with either 'uri:' or 'http://' or 'https://'</FormFeedback>}
+                        </FormGroup>
+
+                        <FormGroup>
+                            <Label >Upload Assessment File:</Label>
+                            {this.state.displayUseExistingFileCheckBox & !this.state.isSparqlEndPoint ? <div><FormGroup check><Label check><Input id="useexistingfile" type="checkbox" name="useexistingfile" checked={this.state.useExistingFile} onClick={this.useExistingFileChange} /> Use existing dump file</Label></FormGroup><br/></div>: null}
+                            {this.state.fileUploadValid ? <Input id="uploadFileSelector" type="file" disabled={this.state.isSparqlEndPoint | this.state.useExistingFile} onChange={this.fileSelectionOnChange} /> : <Input invalid id="uploadFileSelector" type="file" disabled={this.state.isSparqlEndPoint | this.state.useExistingFile} onChange={this.fileSelectionOnChange} />}
+                            {this.state.fileUploadMandatory ? null : <FormFeedback invalid>Please select a file!!!</FormFeedback>}
+                            {this.state.fileFormatValid ? null : <FormFeedback invalid>Only following file formats are allowed.<div>({formats})</div></FormFeedback>}
+                        </FormGroup>
+                        <FormGroup>
+                            {this.state.showFileUploadProgress ? <Progress value={this.state.fileUploadProgress}><small class="justify-content-center d-flex position-absolute w-100"><font size="2" color="black">{this.state.fileUploadProgress}%</font></small></Progress> : null}
+                            {this.state.fileUploadCompleted && !this.state.fileUploadSuccessfull ? <div className="text-danger">File Upload Failed. Server did not respond!!!</div> : null}
+                            {this.state.fileUploadCompleted && this.state.fileUploadSuccessfull ? <div className="text-success">File Upload Successfull.</div> : null}
+                        </FormGroup>
+                    </Form>
+                </ModalBody>
+                <ModalFooter>
+                    <Button color="secondary" onClick={this.props.onClose}>Discard</Button>
+                    <Button color="primary" onClick={this.triggerSubmitValidation}>Next</Button>{' '}
+                </ModalFooter>
+                {this.state.fileToolarge?<FileTooLarge clickHandler={this.closeFileLargeError}/>:null}
+            </Modal>
+        );
+    }
+}
+
+
+export default DatasetConnectConfigModelWindow;

+ 739 - 0
Luzzu Dashboard/src/views/Dashboard/ConnectDataSet/DatasetConnectOptionsModelWindow.js

@@ -0,0 +1,739 @@
+//© 2019 Dublin City University, Trinity College Dublin. All rights reserved. This material may not be reproduced, displayed, modified or distributed without the express prior written permission of the copyright holder.
+import React, { Component } from 'react';
+import {
+  Modal,
+  ModalHeader,
+  ModalBody,
+  ModalFooter,
+  Button,
+  FormGroup,
+  Form,
+  Label,
+  Input,
+  Col,
+  Card,
+  CardHeader,
+  CardBody,
+  Table,
+  ListGroup,
+  CustomInput
+} from 'reactstrap';
+import Slider from 'react-rangeslider'
+import 'react-rangeslider/lib/index.css'
+import LoadingSpinner from '../loading';
+import LoadingFailed from '../loadingFailed';
+import ProblemsModel from './problemsModel';
+import { allMetricsForAssessment } from '../../../services/luzzuFrameWorkAPIs/getAllMetricsAvailableForAssessment';
+import { checkDatasetPLDExists } from '../../../services/tripleStoreAPIs/sparql/checkDatasetPLDExists';
+import { read } from '../../../services/tripleStoreAPIs/readFromTripleStore';
+import { deleteGraph } from '../../../services/tripleStoreAPIs/deleteGraphFromTripleStore';
+import { addDataset,getDataset, updateDataset, getStatus } from '../../../services/datasetConfigDetails/datasetConfigDetails';
+import { uploadToTripleStore } from '../../../services/tripleStoreAPIs/uploadToTripleStore';
+import { get1SpatialAssessmentMetrics } from '../../../services/tripleStoreAPIs/sparql/get1SpatialAssessmentMetrics';
+import { statusUpdate } from '../../../services/tripleStoreAPIs/statusUpdate';
+import properties from '../../../config/dashboardProperties';
+import metricExceptionMapping from '../../../config/metricKnowledgeBaseMapping';
+
+class DatasetConnectOptionsModelWindow extends Component {
+  state = {
+    ...this.props.currentState
+  };
+
+
+  componentDidMount() {
+    let definedMetrics = Object.keys(metricExceptionMapping.knowledgeBaseMapping);
+    if (this.state.qualityMetrics.length === 0 && !this.state.is1Spatial) {
+      this.setState({ isloading: true }, () => {
+        allMetricsForAssessment().then((response) => {
+          if (response) {
+            let graphs = response['@graph'];
+            this.setState({
+              isloading: false,
+              loadFailed: false
+            }, () => {
+              let qualityMetrics = [];
+              console.log(graphs)
+              graphs.forEach((graph) => {
+                let label = graph.label;
+                let javaPackageName = graph.javaPackageName;
+                let comment = graph.comment;
+                let metricURI = graph.referTo;
+                let dataType = (graph.expectedDataType).split("#")[1];
+                let category = (graph.javaPackageName).split(".")[5];
+                let dimension = (graph.javaPackageName).split(".")[6];
+                if(definedMetrics.indexOf(metricURI.split("#")[1])>=0)
+                {
+                  if(metricURI.split("#")[1]==="UsageOfIncorrectDomainOrRangeDatatypesMetric")
+                  {
+                    label="Incorrect Domain or Range Datatypes";
+                  }
+                  qualityMetrics.push(
+                    {
+                      label: label,
+                      comment: comment,
+                      javaPackageName: javaPackageName,
+                      category: category.charAt(0).toUpperCase() + category.slice(1),
+                      dimension: dimension.charAt(0).toUpperCase() + dimension.slice(1),
+                      metricURI: metricURI,
+                      dataType: dataType
+                    }
+                  );
+                }
+
+              });
+              this.setState(
+                {
+                  qualityMetrics: qualityMetrics
+                }, () => {
+                  let dimensions = [...new Set(this.state.qualityMetrics.map((metric) => metric.dimension))];
+                  this.setState(
+                    {
+                      dimensionsToAssess: dimensions
+                    }, () => { this.prepareMetricDetails(); }
+                  );
+
+                }
+              );
+
+            });
+          }
+          else {
+            this.setState({
+              isloading: false,
+              loadFailed: true
+            });
+          }
+        })
+      });
+    }
+    if (this.state.qualityMetrics.length === 0 && this.state.is1Spatial) {
+      this.setState({ isloading: true }, () => {
+        this.fetch1SpatialMetricsFromTripleStore().then((response) => {
+          if (response) {
+            console.log(response)
+            let graphs = response;
+            this.setState({
+              isloading: false,
+              loadFailed: false
+            }, () => {
+              let qualityMetrics = [];
+              graphs.forEach((graph) => {
+                let label = graph.result.label.value;
+                let javaPackageName = "";
+                let comment = graph.result.comment.value;
+                let metricURI = graph.result.metric.value;
+                let dataType = (graph.result.datatype.value).split("#")[1];
+                let category = "";
+                let dimension = graph.result.dimension.value;
+                if(definedMetrics.indexOf(metricURI.split("#")[1])>=0)
+                {
+                  if(metricURI.split("#")[1]==="UsageOfIncorrectDomainOrRangeDatatypesMetric")
+                  {
+                    label="Incorrect Domain or Range Datatypes";
+                  }
+                  qualityMetrics.push(
+                    {
+                      label: label,
+                      comment: comment,
+                      javaPackageName: javaPackageName,
+                      category: category,
+                      dimension: dimension,
+                      metricURI: metricURI,
+                      dataType: dataType
+                    }
+                  );
+                }
+
+              });
+              this.setState(
+                {
+                  qualityMetrics: qualityMetrics
+                }, () => {
+                  let dimensions = [...new Set(this.state.qualityMetrics.map((metric) => metric.dimension))];
+                  this.setState(
+                    {
+                      dimensionsToAssess: dimensions
+                    }, () => { this.prepareMetricDetails(); }
+                  );
+
+                }
+              );
+
+            });
+          }
+          else {
+            this.setState({
+              isloading: false,
+              loadFailed: true
+            });
+          }
+        })
+      });
+    }
+  }
+
+  fetch1SpatialMetricsFromTripleStore = () =>
+  {
+      return new Promise((resolve)=>{
+        let spatialmetrics = [];
+        read(get1SpatialAssessmentMetrics())
+          .then((response) => {
+            //console.log(response);
+            if (response) {
+              if (response.results.bindings.length > 0) {
+                spatialmetrics = response.results.bindings.map((result,index) => {
+                  return ({result:result});
+                })
+              }
+            }
+            resolve(spatialmetrics);
+        });
+
+      });
+  }
+
+  prepareMetricDetails =  () => {
+    if (this.state.assessmentDetails.length === 0) {
+      this.setState({
+        assessmentDetails: this.state.qualityMetrics.map((metric, index) => {
+          let type = "double";
+          let target = 1.0;
+          let canAssess = true;
+          if (metric.dataType === "boolean") {
+            type = "boolean";
+            target = 0.0;
+          }
+
+          if (this.state.prevAssessmentDetails.length !== 0) {
+            this.state.prevAssessmentDetails.forEach((existingMetric) => {
+              if (existingMetric.metric === metric.metricURI & existingMetric.javaPackageName === metric.javaPackageName) {
+                target = existingMetric.target;
+                canAssess = existingMetric.assess;
+              }
+            });
+          }
+          return { id: "metricID_" + index, javapackagename: metric.javaPackageName, label: metric.label, dimension: metric.dimension, category: metric.category, metricURI: metric.metricURI, comment: metric.comment, assess: canAssess, target: target, prevtarget: target, type: type }
+        })
+      });
+    }
+
+  }
+
+  metrictoggleChange = (index) => (event) => {
+    let prevAssessmentDetails = [...this.state.assessmentDetails];
+    let changeAssessmentDetails = prevAssessmentDetails[index];
+
+    if (changeAssessmentDetails.target === 1.0) {
+      changeAssessmentDetails.target = 0;
+    }
+    else {
+      changeAssessmentDetails.target = 1.0;
+    }
+    prevAssessmentDetails[index] = changeAssessmentDetails;
+
+    this.setState(
+      {
+        prevAssessmentDetails
+      }
+    );
+  }
+
+  assessMetricChange = (index) => (event) => {
+    let prevAssessmentDetails = [...this.state.assessmentDetails];
+    let changeAssessmentDetails = prevAssessmentDetails[index];
+
+    if (event.target.value === "false") {
+      changeAssessmentDetails.assess = false;
+      changeAssessmentDetails.prevtarget = changeAssessmentDetails.target;
+      changeAssessmentDetails.target = 0;
+    }
+    else {
+      changeAssessmentDetails.assess = true;
+      changeAssessmentDetails.target = changeAssessmentDetails.prevtarget;
+    }
+    prevAssessmentDetails[index] = changeAssessmentDetails;
+
+    this.setState(
+      {
+        prevAssessmentDetails
+      }
+    );
+  }
+
+
+  metricValueChange = (index) => (value) => {
+    let prevAssessmentDetails = [...this.state.assessmentDetails];
+    let changeAssessmentDetails = prevAssessmentDetails[index];
+
+    if (changeAssessmentDetails.assess === true) {
+      changeAssessmentDetails.target = value;
+      prevAssessmentDetails[index] = changeAssessmentDetails;
+
+      this.setState(
+        {
+          prevAssessmentDetails
+        }
+      );
+    }
+  }
+
+  checkExistingdatasetPLDInTripleStore = (datasetPLD) =>
+  {
+    let exists = false;
+
+  return  new Promise((resolve)=>
+    {
+      this.setState({isloading:true},()=>{
+        read(checkDatasetPLDExists(datasetPLD)).then((response)=>{
+          //console.log(response);
+          this.setState({isloading:false},()=>{
+            if(response)
+            {
+                if(response.results.bindings.length>0)
+                {
+                  exists = true;
+                  resolve(exists);
+                }
+                else
+                {
+                  exists=false;
+                  resolve(exists);
+                }
+            }
+            else
+            {
+              exists=false;
+              resolve(exists);
+            }
+          });
+
+        });
+      });
+
+
+    }
+  );
+  }
+
+  checkExistingDatasetPLDInConfigFile = (datasetPLD) =>
+  {
+    let exists = false;
+
+  return  new Promise((resolve)=>
+    {
+      this.setState({isloading:true},()=>{
+
+        let parameter = {
+          datasetPLD: datasetPLD
+        };
+
+        getDataset(parameter).then((response)=>{
+          //console.log(response);
+          this.setState({isloading:false},()=>{
+            if(response.status===200)
+            {
+              if(response.data.datasetDetails.length>0)
+              {
+                exists=true;
+                resolve(exists);
+              }
+              else
+              {
+                exists=false;
+                resolve(exists);
+              }
+            }
+            else
+            {
+              exists=false;
+              resolve(exists);
+            }
+          });
+
+        });
+      });
+    }
+  );
+  }
+
+  checkWrapperServiceIsReachable =()=>
+  {
+    let isReachable = false;
+
+  return  new Promise((resolve)=>
+    {
+      this.setState({isloading:true},()=>{
+        getStatus().then((response)=>{
+          this.setState({isloading:false},()=>{
+            if(response.status===200)
+            {
+              if(response.data.status==="OK")
+              {
+                isReachable=true;
+                resolve(isReachable);
+              }
+              else
+              {
+                isReachable=false;
+                resolve(isReachable);
+              }
+            }
+            else
+            {
+              isReachable=false;
+              resolve(isReachable);
+            }
+          });
+        });
+      });
+    }
+  );
+  }
+
+  checkTripleStoreIsReachable =()=>
+  {
+    let isReachable = false;
+
+  return  new Promise((resolve)=>
+    {
+      this.setState({isloading:true},()=>{
+        statusUpdate().then((response)=>{
+          this.setState({isloading:false},()=>{
+            if(response)
+            {
+
+              if(response.status===200)
+              {
+                isReachable=true;
+                resolve(isReachable);
+              }
+              else {
+                isReachable=false;
+                resolve(isReachable);
+              }
+            }
+            else
+            {
+              isReachable=false;
+              resolve(isReachable);
+            }
+          });
+        });
+      });
+    }
+  );
+  }
+
+  checkMetricsWithSameURI = (assessmentMetrics) => {
+    //console.log(assessmentMetrics);
+    return new Promise((resolve, reject) => {
+      let problems = [];
+      if (assessmentMetrics.length > 0) {
+        let dictionary = {};
+        let metricsToAssess = 0;
+        assessmentMetrics.forEach((metric) => {
+          if (metric.assess === true) {
+            metricsToAssess += 1;
+            if (dictionary[metric.metric] === undefined) {
+              dictionary[metric.metric] = new Array();
+            }
+            dictionary[metric.metric].push(metric.label);
+          }
+        });
+        let keys = Object.keys(dictionary);
+
+        keys.forEach((key) => {
+          if (dictionary[key].length > 1) {
+            problems.push({
+              [key]: [...dictionary[key]]
+            });
+          }
+        });
+
+        if (metricsToAssess === 0) {
+          problems.push({
+            NoMetricSelected: ["Please select alteast one Metric for assessment"]
+          });
+        }
+        //console.log(problems);
+          return resolve(problems);
+      }
+      else {
+        problems.push({
+          NoMetricSelected: ["Please select alteast one Metric for assessment"]
+        });
+        return resolve(problems);
+      }
+    });
+  }
+
+
+  triggerSubmitValidation = async () => {
+    let fileIsUploadedToTripleStore = false;
+    let graphName = "";
+    const targets = this.state.assessmentDetails.filter((metric) => { if (metric.assess) { return true; } return false; }).map((metric) => { if (metric.type === "boolean") { return (1); } else { return (metric.target); } });
+    let expectedProgress = 0.0;
+    if (targets.length > 0) {
+      expectedProgress = targets.reduce((previous, current) => current += previous) / targets.length;
+    }
+
+    let assessmentMetrics = [];
+    assessmentMetrics = this.state.assessmentDetails.map((metric) => { return ({ javaPackageName: metric.javapackagename, label: metric.label, comment: metric.comment, category: metric.category, dimension: metric.dimension, metric: metric.metricURI, assess: metric.assess, type: metric.type, target: Number(parseFloat(metric.target).toFixed(2)) }); });
+
+    let tripleStoreIsReachable = true;
+    let wrapperServiceIsReachable = true;
+    let existingDatasetPLDInConfigFile=false;
+    let existingdatasetPLDInTripleStore=false;
+    let metricSelectionProblems = [];
+
+    //tripleStoreIsReachable=await this.checkTripleStoreIsReachable();
+    wrapperServiceIsReachable = await this.checkWrapperServiceIsReachable();
+    metricSelectionProblems = await this.checkMetricsWithSameURI(assessmentMetrics);
+    if (!wrapperServiceIsReachable)
+    {
+      metricSelectionProblems.push({WrapperServiceNotAvailable: "Host:"+properties.wrapperAPI.host+", Port:"+properties.wrapperAPI.port});
+    }
+
+    if(wrapperServiceIsReachable & this.state.datasetID === "")
+    {
+      existingDatasetPLDInConfigFile = await this.checkExistingDatasetPLDInConfigFile(this.state.datasetPLD);
+
+    }
+    /*
+    if(tripleStoreIsReachable & this.state.datasetID === "")
+    {
+      existingdatasetPLDInTripleStore = await this.checkExistingdatasetPLDInTripleStore(this.state.datasetPLD);
+    }
+    */
+
+          if (existingDatasetPLDInConfigFile | existingdatasetPLDInTripleStore)
+          {
+            metricSelectionProblems.push({datasetPLDAlreadyExists: this.state.datasetPLD});
+          }
+
+    if(metricSelectionProblems.length===0)
+    {
+      let fileName = "";
+      if (!this.state.isSparqlEndPoint) {
+        fileName = this.state.fileUploadedPath + this.state.fileUploadedName;
+      }
+      if (this.state.datasetID === "") {
+        //Functionality to Add new Dataset
+        let requestBody = {
+          datasetID: Math.floor(Date.now()),
+          datasetName: this.state.datasetName,
+          fileName: fileName,
+          datasetPLD: this.state.datasetPLD,
+          isSparqlEndPoint: this.state.isSparqlEndPoint,
+          sparqlEndPoint: this.state.sparqlEndPointURL,
+          lastAssessmentRequestID: "",
+          knowledgeBaseID: "",
+          expectedProgress: Number(parseFloat(expectedProgress).toFixed(2)),
+          assessmentMetrics: [...assessmentMetrics]
+        }
+        this.setState({ isloading: true }, () => {
+          addDataset(requestBody).then(async (res) => {
+            //Upload File to Triple Store
+            if (!this.state.isSparqlEndPoint & !fileIsUploadedToTripleStore) {
+              graphName = this.state.fileUploadedName.split('.').slice(0, -1).join('.');
+              await uploadToTripleStore(this.state.fileToUpload, graphName).then((response) => {
+                if (response) {
+                  if (response.status === 201) {
+                    fileIsUploadedToTripleStore = true;
+                  }
+                }
+              });
+            }
+            else {
+              fileIsUploadedToTripleStore = true;
+            }
+
+            if (res.status === 201) {
+              this.setState({
+                isloading: false,
+                loadFailed: false
+              }, () => {
+                this.props.onSubmit(fileIsUploadedToTripleStore, graphName);
+                this.props.whenAddNew(requestBody);
+              });
+            }
+            else {
+              this.setState({
+                isloading: false,
+                loadFailed: true
+              });
+            }
+          });
+
+        });
+      }
+      else {
+        //Functionality to Update existing Dataset
+        let requestBody = {
+          datasetID: this.state.datasetID,
+          datasetName: this.state.datasetName,
+          fileName: fileName,
+          datasetPLD: this.state.datasetPLD,
+          isSparqlEndPoint: this.state.isSparqlEndPoint,
+          sparqlEndPoint: this.state.sparqlEndPointURL,
+          knowledgeBaseID: this.state.knowledgeBaseID,
+          lastAssessmentRequestID: this.state.lastAssessmentRequestID,
+          expectedProgress: Number(parseFloat(expectedProgress).toFixed(2)),
+          assessmentMetrics: [...assessmentMetrics]
+        }
+
+        this.setState({ isloading: true }, () => {
+          updateDataset(requestBody, this.state.datasetID).then(async (res) => {
+            //Upload File to Triple Store
+            //console.log(this.state.fileToUpload);
+            if (!this.state.isSparqlEndPoint & !fileIsUploadedToTripleStore & this.state.fileToUpload !== null) {
+              graphName = this.state.fileUploadedName.split('.').slice(0, -1).join('.');
+              //Delete Old Graph
+              if(this.state.historyFileToUpload!==null)
+              {
+
+                await deleteGraph(this.state.graphName).then((response)=>{
+                  //console.log(response);
+                });
+              }
+              await uploadToTripleStore(this.state.fileToUpload, graphName).then((response) => {
+                if (response) {
+                  if (response.status === 201) {
+                    fileIsUploadedToTripleStore = true;
+                  }
+                }
+              });
+            }
+            else {
+              fileIsUploadedToTripleStore = true;
+            }
+
+            if (res.status === 200) {
+              this.setState({
+                isloading: false,
+                loadFailed: false
+              }, () => {
+
+                this.props.onSubmit(fileIsUploadedToTripleStore, graphName);
+                this.props.whenUpdateExisting(requestBody);
+              });
+            }
+            else {
+              this.setState({
+                isloading: false,
+                loadFailed: true
+              });
+            }
+          });
+
+        });
+      }
+    }
+    else
+    {
+      //Has Problem
+      this.setState(
+        {
+          hasProblems: true,
+          assessmentProblems: metricSelectionProblems
+        }
+      );
+    }
+
+  }
+
+  toggleAlertModel = () => {
+    this.setState({
+      loadFailed: false
+    });
+  }
+
+  toggleProblemsModel = () => {
+    this.setState({
+      hasProblems: false,
+      assessmentProblems: []
+    });
+  }
+
+  triggerBackAction = () => {
+    this.props.onBack(this.state);
+  }
+
+  render() {
+    let metricTable = null;
+    metricTable = (this.state.dimensionsToAssess.map((dimension) => {
+      return <Card key={dimension}><CardHeader><b style={{ 'textTransform': 'uppercase' }}>{dimension}</b></CardHeader><CardBody><Table striped responsive size="sm"><thead><tr><th style={{ width: '60%' }}>METRIC NAME</th><th style={{ width: '15%' }}>ASSESS?</th><th style={{ width: '25%' }}>SET TARGET</th></tr></thead><tbody>
+        {this.state.assessmentDetails.map((metric, index) => {
+          if (metric.dimension === dimension) {
+            let slider = true;
+            let switchChecked = true;
+            let switchLabel = "True";
+            let assessDisabled = false;
+            if (metric.type === "boolean") {
+              slider = false;
+              if (metric.target === 1.0) {
+                switchChecked = true;
+                switchLabel = "True";
+              }
+              else {
+                switchChecked = false
+                switchLabel = "False";
+              }
+            }
+
+            if (metric.assess === false) {
+              assessDisabled = true;
+            }
+
+            return <tr key={metric.id} id={metric.id}>
+              <td><b>{metric.label}</b><br />{metric.comment}</td>
+              <td>
+                <FormGroup row>
+                  <FormGroup check inline>
+                    <Input className="form-check-input" type="radio" id={metric.id + "_radio1"} name={metric.id + "_radio1"} checked={metric.assess} value="true" onChange={this.assessMetricChange(index)} />
+                    <Label className="form-check-label" check htmlFor={metric.id + "_radio1"}>Yes</Label>
+                  </FormGroup>
+                  <FormGroup check inline>
+                    <Input className="form-check-input" type="radio" id={metric.id + "_radio2"} name={metric.id + "_radio1"} checked={!metric.assess} value="false" onChange={this.assessMetricChange(index)} />
+                    <Label className="form-check-label" check htmlFor={metric.id + "_radio1"}>No</Label>
+                  </FormGroup>
+                </FormGroup>
+              </td>
+              <td>
+                {slider ? <div><Slider id={metric.id + "_slider"} value={metric.target} min={0} max={1} step={0.01} tooltip={false} onChange={this.metricValueChange(index)} disabled={assessDisabled} /><p>{Math.round(metric.target * 100) + "%"}</p></div> : <CustomInput type="switch" id={metric.id + "_switch"} label={switchLabel} onChange={this.metrictoggleChange(index)} checked={switchChecked} disabled={assessDisabled} />}
+              </td></tr>
+          }
+        })}
+      </tbody></Table></CardBody></Card>
+    }));
+
+    return (
+      <Modal isOpen={true} className="modal-dialog-centered modal-lg">
+        <ModalHeader style={{ margin: 'auto' }}>Configure Metrics for Test Dataset</ModalHeader>
+        <ModalBody>
+          {this.state.isloading ? <LoadingSpinner /> : null}
+          {this.state.loadFailed ? <LoadingFailed clickHandler={this.toggleAlertModel} /> : null}
+          {this.state.hasProblems ? <ProblemsModel clickHandler={this.toggleProblemsModel} assessmentProblems={this.state.assessmentProblems} /> : null}
+          <Form>
+            <FormGroup>
+              <div data-spy="scroll" data-target="#metric-list">
+                <ListGroup id="metric-list">
+                  <Col xs={'auto'}>
+                    {metricTable}
+                  </Col>
+                </ListGroup>
+              </div>
+            </FormGroup>
+          </Form>
+        </ModalBody>
+        <ModalFooter>
+          <Button color="secondary" onClick={this.triggerBackAction}>Back</Button>
+          <Button color="primary" onClick={this.triggerSubmitValidation}>Next</Button>{' '}
+        </ModalFooter>
+      </Modal>
+    );
+  }
+}
+
+
+export default DatasetConnectOptionsModelWindow;

+ 17 - 0
Luzzu Dashboard/src/views/Dashboard/ConnectDataSet/fileLargeError.js

@@ -0,0 +1,17 @@
+//© 2019 Dublin City University, Trinity College Dublin. All rights reserved. This material may not be reproduced, displayed, modified or distributed without the express prior written permission of the copyright holder.
+import React from 'react';
+import { Modal, ModalBody,ModalFooter, Button} from 'reactstrap';
+
+const FileLargeError = (props) => (
+  <Modal isOpen={true} className="modal-dialog-centered">
+    <ModalBody>
+      <div className="animated fadeIn pt-1 text-center">File is too large. Please publish the dataset and then provide the SPARQL end-point.</div>
+    </ModalBody>
+    <ModalFooter>
+        <Button color="secondary" onClick={props.clickHandler}>Ok</Button>
+    </ModalFooter>
+  </Modal>
+);
+
+
+export default FileLargeError;

+ 53 - 0
Luzzu Dashboard/src/views/Dashboard/ConnectDataSet/problemsModel.js

@@ -0,0 +1,53 @@
+//© 2019 Dublin City University, Trinity College Dublin. All rights reserved. This material may not be reproduced, displayed, modified or distributed without the express prior written permission of the copyright holder.
+import React from 'react';
+import { Modal, ModalBody,ModalHeader,ModalFooter, Button, ListGroup, ListGroupItem, ListGroupItemHeading, ListGroupItemText} from 'reactstrap';
+
+const ProblemsModel = (props) => {
+  let problems=null;
+  problems =(
+    props.assessmentProblems.map((problem,index)=>{
+      let title="";
+      title=(Object.keys(problem))[0].split("#")[1];
+      let details = "";
+      let additionalDetails = "";
+      //console.log(title);
+      if (title===undefined) {
+        title=(Object.keys(problem))[0];
+        details = problem[Object.keys(problem)];
+      }
+      else {
+        title = "Please select any one Metric for : " + (Object.keys(problem))[0].split("#")[1];
+        details=problem[Object.keys(problem)].join(', ');
+      }
+      if(title==="WrapperServiceNotAvailable")
+      {
+        additionalDetails="Please ensure that Wrapper API Service endpoint is defined correctly and running.";
+      }
+      if(title==="datasetPLDAlreadyExists")
+      {
+        additionalDetails="Please enter a different Dataset PLD in Configuration Screen.";
+      }
+      return (<ListGroupItem key={index}><ListGroupItemHeading>{title}</ListGroupItemHeading><ListGroupItemText>{details}</ListGroupItemText><ListGroupItemText>{additionalDetails}</ListGroupItemText></ListGroupItem>)
+    })
+  );
+
+  //console.log(props.assessmentProblems);
+  return (
+    <Modal isOpen={true} className="modal-dialog-centered">
+    <ModalHeader style={{ margin: 'auto' }}>List of Issues</ModalHeader>
+      <ModalBody>
+        <div className="animated fadeIn pt-1 text-center">Please correct the following issue(s):</div>
+        <ListGroup>
+        {problems}
+        </ListGroup>
+      </ModalBody>
+      <ModalFooter>
+          <Button color="secondary" onClick={props.clickHandler}>Ok</Button>
+      </ModalFooter>
+    </Modal>
+  );
+
+}
+
+
+export default ProblemsModel;

+ 378 - 0
Luzzu Dashboard/src/views/Dashboard/Dashboard.js

@@ -0,0 +1,378 @@
+//© 2019 Dublin City University, Trinity College Dublin. All rights reserved. This material may not be reproduced, displayed, modified or distributed without the express prior written permission of the copyright holder.
+import React, { Component } from 'react';
+import { connect } from 'react-redux';
+import { Row, Col, Dropdown, DropdownToggle, DropdownMenu, DropdownItem, InputGroup, Input } from 'reactstrap';
+import ConnectDataSet from './ConnectDataSet/ConnectDataSet';
+import DataSetLarge from './DataSetLarge/DataSetLarge'
+import { getAllDataset, removeDataset,getDataset } from '../../services/datasetConfigDetails/datasetConfigDetails';
+import { deleteGraph } from '../../services/tripleStoreAPIs/deleteGraphFromTripleStore';
+import DatasetConnectConfigModelWindow from './ConnectDataSet/DatasetConnectConfigModelWindow';
+import DatasetConnectOptionsModelWindow from './ConnectDataSet/DatasetConnectOptionsModelWindow';
+import LoadingSpinner from './loading';
+import LoadingFailed from './loadingFailed';
+import properties from '../../config/dashboardProperties';
+import TripleStoreUploadError from './tripleStoreUploadError';
+
+const limit = properties.dataQualityOverTimeBarCount;
+
+class Dashboard extends Component {
+
+  constructor(props) {
+    super(props);
+    if (sessionStorage.getItem("LIMIT")===null) {
+      sessionStorage.setItem("LIMIT",limit );
+    }
+
+  }
+
+    state = {
+        datasetDetails: [],
+        dropdownOpen: false,
+        selectedView: "Large Title View",
+        searchString: "",
+        showConfigModal: false,
+        showOptionModel: false,
+        connectDataSet: null
+    };
+
+
+    toggleDropDown = () => {
+        this.setState(prevState => ({
+            dropdownOpen: !prevState.dropdownOpen
+        }));
+    }
+
+    toggleAlertModel = () => {
+        this.setState({
+            loadFailed: false
+        });
+    }
+
+    toggleBetweenConfigAndOptionModel = (modelState) =>{
+      this.setState(prevState => ({
+          showConfigModal: !prevState.showConfigModal,
+          showOptionModel: !prevState.showOptionModel,
+          connectDataSet : modelState
+      }));
+    }
+
+    toggleConfigModel = () => {
+      if(this.state.showConfigModal){
+        //If it is already open, then close it and set connectDataSet as null
+        this.setState(prevState => ({
+            showConfigModal: !prevState.showConfigModal,
+            connectDataSet : null
+        }));
+      }
+      else {
+        this.setState(prevState => ({
+            showConfigModal: !prevState.showConfigModal
+        }));
+      }
+    }
+
+    toggleOptionModel = (uploadError,graphName) => {
+      //console.log(uploadError);
+      if(this.state.showOptionModel){
+        //If it is already open, then close it and set connectDataSet as null
+        this.setState(prevState => ({
+            showOptionModel: !prevState.showOptionModel,
+            connectDataSet : null
+        }));
+      }
+      else {
+        this.setState(prevState => ({
+            showOptionModel: !prevState.showOptionModel
+        }));
+      }
+
+      if(!uploadError)
+      {
+        this.setState({
+          showTripleStoreError:true,
+          graphName:graphName
+        });
+      }
+
+    }
+
+    toggleTripleStoreErrorModel = () => {
+        this.setState({
+            showTripleStoreError: false,
+            graphName:""
+        });
+    }
+
+    componentWillUnmount(){
+    }
+
+    componentDidMount() {
+      //If there are no datasetDetails in Cache, then Call API to fetch the data
+      //and the update the sessionStorage Cache.
+
+      if(this.props.datasetDetailsCache){
+        this.setState({
+            datasetDetails: [...this.props.datasetDetailsCache]
+        });
+      }
+    }
+
+    componentDidUpdate() {
+
+    }
+
+    removeDataSetComponent = (dataSetID, graphName) => {
+        //This function will remove delete the DataSet details from the dataset config file
+        this.setState({ isloading: true }, () =>{
+          removeDataset(dataSetID).then((response) => {
+          if (response.status === 200) {
+            this.setState({
+                isloading: false,
+                //loadFailed: false
+            }, async ()=>{
+              await this.props.removeDatasetDetailsCache(dataSetID);
+              if(graphName!=="")
+              {
+                await deleteGraph(graphName).then((response)=>{
+                  //console.log(response);
+                });
+              }
+              //console.log(this.props.datasetDetailsCache);
+              this.setState(
+                {
+                  datasetDetails: [...this.props.datasetDetailsCache]
+                }
+              );
+              if(this.state.searchString!=="")
+              {
+                this.searchHandler();
+              }
+
+            });
+        }
+        else {
+
+          let parameter = {
+            datasetID: dataSetID
+          };
+          getDataset(parameter).then((response)=>{
+            if(response.status===404)
+            {
+              this.setState({
+                  isloading: false,
+                  //loadFailed: true
+              },async ()=>{
+                await this.props.removeDatasetDetailsCache(dataSetID);
+                if(graphName!=="")
+                {
+                  await deleteGraph(graphName).then((response)=>{
+                    //console.log(response);
+                  });
+                }
+
+                //console.log(this.props.datasetDetailsCache);
+                this.setState(
+                  {
+                    datasetDetails: [...this.props.datasetDetailsCache]
+                  }
+                );
+                if(this.state.searchString!=="")
+                {
+                  this.searchHandler();
+                }
+
+              });
+            }
+            else {
+              this.setState({
+                  isloading: false,
+                  //loadFailed: true
+              });
+            }
+          });
+
+        }
+      });
+      });
+    }
+
+    addDataSetComponent = async (dataSetDetails) => {
+
+      await this.props.addDatasetDetailsCache(dataSetDetails);
+      //console.log(this.props.datasetDetailsCache);
+      this.setState(
+        {
+          datasetDetails: [...this.props.datasetDetailsCache]
+        }
+      );
+      if(this.state.searchString!=="")
+      {
+        this.searchHandler();
+      }
+    }
+
+    updateDataSetComponent = async (dataSetDetails) => {
+
+      await this.props.updateDatasetDetailsCache(dataSetDetails);
+      //console.log(this.props.datasetDetailsCache);
+      this.setState(
+        {
+          datasetDetails: [...this.props.datasetDetailsCache]
+        }
+      );
+      if(this.state.searchString!=="")
+      {
+        this.searchHandler();
+      }
+    }
+
+    changeView = (event) => {
+        this.setState({
+            selectedView: event.target.innerText
+        }
+        );
+    }
+
+    searchHandler = (event) => {
+        let searchString="";
+        let regExpr = /[^a-zA-Z0-9-_ ]/g;
+        if(typeof event==="undefined")
+        {
+          searchString = this.state.searchString.replace(regExpr, "");
+        }
+        else {
+          searchString = event.target.value.replace(regExpr, "");
+        }
+
+        if (searchString === "") {
+            this.setState(
+                {
+                    searchString: searchString,
+                    datasetDetails: [...this.props.datasetDetailsCache]
+                }
+            );
+        }
+        else {
+            let tempList = [];
+            if (this.props.datasetDetailsCache.length > 0) {
+                tempList = this.props.datasetDetailsCache.filter((dataset) => {
+                    let tempDataSetName = (dataset.datasetName).toUpperCase();
+                    if (tempDataSetName.search(searchString.toUpperCase()) >= 0) {
+                        return true;
+                    }
+                    return false;
+                })
+            }
+            if (typeof tempList[0] == 'undefined') {
+                tempList = [];
+            }
+            this.setState(
+                {
+                    searchString: searchString,
+                    datasetDetails: tempList
+                }
+            );
+        }
+
+    }
+
+openReConfigPopup = (datasetDetails) =>
+{
+  let hasExistingFile=false;
+  if(datasetDetails.fileName!=="")
+  {
+    hasExistingFile=true;
+  }
+this.setState({
+  connectDataSet : {
+    datasetID : datasetDetails.datasetID,
+    datasetName:datasetDetails.datasetName,
+    datasetPLD:datasetDetails.datasetPLD,
+    isSparqlEndPoint:datasetDetails.isSparqlEndPoint,
+    sparqlEndPointURL:datasetDetails.sparqlEndPoint,
+    knowledgeBaseID: datasetDetails.knowledgeBaseID,
+    prevAssessmentDetails : [...datasetDetails.assessmentMetrics],
+    datasetNameDisabled : true,
+    datasetPLDDisabled : true,
+    fileUploadedPath : (datasetDetails.fileName).substring(0, (datasetDetails.fileName).lastIndexOf("/") + 1),
+    fileUploadedName : (datasetDetails.fileName).substring((datasetDetails.fileName).lastIndexOf("/") + 1, (datasetDetails.fileName).length),
+    displayUseExistingFileCheckBox : hasExistingFile,
+    useExistingFile : hasExistingFile,
+    lastAssessmentRequestID: datasetDetails.lastAssessmentRequestID,
+    graphName : (datasetDetails.fileName).substring((datasetDetails.fileName).lastIndexOf("/") + 1, (datasetDetails.fileName).lastIndexOf("."))
+  }
+}, ()=> {this.toggleConfigModel()});
+}
+
+    loading = () => <div className="animated fadeIn pt-1 text-center">Loading...</div>
+
+    render() {
+        let datasetList = null;
+        if (this.state.datasetDetails.length > 0) {
+            datasetList = (
+                this.state.datasetDetails.map((dataset) => {
+                  if(dataset.datasetID != 1){
+                    let graphName = (dataset.fileName).substring((dataset.fileName).lastIndexOf("/") + 1, (dataset.fileName).lastIndexOf("."));
+                    return <DataSetLarge key={dataset.datasetID} dataSetDetails={dataset} onRemoveClick={this.removeDataSetComponent.bind(this, dataset.datasetID, graphName)} onConfigClick={this.openReConfigPopup} whenUpdated={this.updateDataSetComponent}/>
+                  }
+                })
+            );
+        }
+        return (
+
+            <div className="animated fadeIn">
+                {this.state.isloading ? <LoadingSpinner /> : null}
+                {this.state.loadFailed ? <LoadingFailed clickHandler={this.toggleAlertModel} /> : null}
+                {this.state.showTripleStoreError?<TripleStoreUploadError clickHandler={this.toggleTripleStoreErrorModel} graphName={this.state.graphName} />:null}
+                <InputGroup>
+                    <Input placeholder="Search" onChange={this.searchHandler} value={this.state.searchString} />
+                </InputGroup>
+                <br />
+
+
+
+                <Row >
+                    <Col>
+                        <Dropdown isOpen={this.state.dropdownOpen} toggle={this.toggleDropDown} className="float-right">
+                            <DropdownToggle caret>
+                                {this.state.selectedView}
+                            </DropdownToggle>
+                            <DropdownMenu>
+                                {this.state.selectedView === "Large Title View" ? <DropdownItem className="active" onClick={this.changeView}>Large Title View</DropdownItem> : <DropdownItem onClick={this.changeView}>Large Title View</DropdownItem>}
+                                {this.state.selectedView === "Medium Title View" ? <DropdownItem className="active" onClick={this.changeView}>Medium Title View</DropdownItem> : <DropdownItem onClick={this.changeView}>Medium Title View</DropdownItem>}
+                                {this.state.selectedView === "Small Title View" ? <DropdownItem className="active" onClick={this.changeView}>Small Title View</DropdownItem> : <DropdownItem onClick={this.changeView}>Small Title View</DropdownItem>}
+                                {this.state.selectedView === "List" ? <DropdownItem className="active" onClick={this.changeView}>List</DropdownItem> : <DropdownItem onClick={this.changeView}>List</DropdownItem>}
+                            </DropdownMenu>
+                        </Dropdown>
+                    </Col>
+                </Row>
+                <br />
+                {this.state.showConfigModal ? <DatasetConnectConfigModelWindow onClose={this.toggleConfigModel} onNext={this.toggleBetweenConfigAndOptionModel} currentState={this.state.connectDataSet}/> : null}
+                {this.state.showOptionModel ? <DatasetConnectOptionsModelWindow onBack={this.toggleBetweenConfigAndOptionModel} onSubmit={this.toggleOptionModel} currentState={this.state.connectDataSet} whenUpdateExisting={this.updateDataSetComponent} whenAddNew={this.addDataSetComponent}/> : null}
+                <Row>
+                    <ConnectDataSet tileClicked={this.toggleConfigModel} />
+                    {datasetList}
+                </Row>
+            </div>
+        );
+    }
+}
+
+const mapStateToProps = (state) => {
+    return (
+        {
+            datasetDetailsCache: state.datasetDetailsCache
+        }
+    );
+}
+
+const mapDispatchToProps = (dispatch) => {
+    return ({
+        updateDatasetDetailsCache: (datasetDetails) => { return dispatch({ type: 'UPDATE_DATASET_DETAILS_CACHE', payLoad: datasetDetails }); },
+        addDatasetDetailsCache: (datasetDetails) => { return dispatch({ type: 'ADD_DATASET_DETAILS_CACHE', payLoad: datasetDetails }); },
+        removeDatasetDetailsCache: (datasetID) => { return dispatch({ type: 'REMOVE_DATASET_DETAILS_CACHE', payLoad: {datasetID:datasetID} }); },
+        createDatasetDetailsCache: (datasetDetails) => { return dispatch({ type: 'CREATE_DATASET_DETAILS_CACHE', payLoad: datasetDetails }); }
+    }
+    );
+}
+export default connect(mapStateToProps, mapDispatchToProps)(Dashboard);

+ 351 - 0
Luzzu Dashboard/src/views/Dashboard/DataSetLarge/DataSetLarge.js

@@ -0,0 +1,351 @@
+//© 2019 Dublin City University, Trinity College Dublin. All rights reserved. This material may not be reproduced, displayed, modified or distributed without the express prior written permission of the copyright holder.
+import React, { Component } from 'react';
+import {
+  Col,
+  Container,
+  Button,
+  ButtonDropdown,
+  ButtonGroup,
+  Card,
+  CardBody,
+  CardText,
+  CardTitle,
+  CardSubtitle,
+  DropdownItem,
+  DropdownMenu,
+  DropdownToggle,
+  Media
+} from 'reactstrap';
+import { withRouter, Link } from 'react-router-dom';
+import CircularProgressbar from 'react-circular-progressbar';
+import 'react-circular-progressbar/dist/styles.css';
+import { getRecentAssessmentQuality } from '../../../services/tripleStoreAPIs/sparql/getRecentAssessmentQuality';
+import { read } from '../../../services/tripleStoreAPIs/readFromTripleStore';
+import { triggerAssessment } from '../../../services/luzzuFrameWorkAPIs/triggerAssessment';
+import { cancelAssessment } from '../../../services/luzzuFrameWorkAPIs/cancelAssessment';
+import LoadingSpinner from '../loading';
+import LoadingFailed from '../loadingFailed';
+import { updateDataset, getStatus } from '../../../services/datasetConfigDetails/datasetConfigDetails';
+import { getStatusUpdate } from '../../../services/luzzuFrameWorkAPIs/getStatusUpdate';
+
+const noteReadyToPublish = "rgba(255,0,0,0.6)";
+const readyToPublish = "rgba(34,139,34,0.8)";
+
+class DataSetLarge extends Component {
+
+  state = {
+    dropdownOpen: false,
+    dataSetLastAssessed: "Not Assessed Yet",
+    canAssess: true,
+    lastAssessmentQuality: 0.0,
+    isloading: false,
+    loadFailed: false,
+    lastAssessmentMetrics: [],
+    datasetID:this.props.dataSetDetails.datasetID
+  };
+
+  recall = 0;
+
+  toggle = () => {
+    this.setState({
+      dropdownOpen: !this.state.dropdownOpen,
+    });
+  }
+
+
+  componentDidMount() {
+    if (this.props.dataSetDetails.lastAssessmentRequestID !== "") {
+      getStatusUpdate(this.props.dataSetDetails.lastAssessmentRequestID).then((response) => {
+
+        if (typeof response !== 'undefined') {
+          if (response.Status === "INPROGRESS") {
+            this.setState(
+              {
+                canAssess: false
+              }, () => { this.statusUpdate(); }
+            );
+          }
+        }
+      });
+    }
+    this.getLatestAssessmentQuality();
+  }
+
+  componentWillUnmount() {
+    //console.log("Component Will  Unmount");
+    //console.log(this.recall);
+    clearInterval(this.recall);
+    //console.log(this.recall);
+  }
+
+
+  checkWrapperServiceIsReachable =()=>
+  {
+    let isReachable = false;
+
+  return new Promise((resolve)=>
+    {
+      this.setState({isloading:true},()=>{
+        getStatus().then((response)=>{
+          this.setState({isloading:false},()=>{
+            if(response.status===200)
+            {
+              if(response.data.status==="OK")
+              {
+                isReachable=true;
+                resolve(isReachable);
+              }
+              else
+              {
+                isReachable=false;
+                resolve(isReachable);
+              }
+            }
+            else
+            {
+              isReachable=false;
+              resolve(isReachable);
+            }
+          });
+        });
+      });
+    }
+  );
+  }
+
+  handleAssessClick = async () => {
+    let wrapperServiceIsReachable=false;
+    wrapperServiceIsReachable = await this.checkWrapperServiceIsReachable();
+
+    if(wrapperServiceIsReachable)
+    {
+      this.setState({ isloading: true }, () => {
+        triggerAssessment(this.props.dataSetDetails).then((response) => {
+          console.log(this.props.dataSetDetails)
+          console.log(response)
+          if (response.status === 200) {
+            this.setState(prevState => ({
+              isloading: false,
+              loadFailed: false,
+              canAssess: false
+            }), () => {
+              let knowledgeBaseCache = JSON.parse(sessionStorage.getItem(this.props.dataSetDetails.knowledgeBaseID));
+              if(knowledgeBaseCache!==null)
+              {
+                sessionStorage.removeItem(this.props.dataSetDetails.knowledgeBaseI);
+              }
+
+              this.statusUpdate();
+              this.setState({ isloading: true }, () => {
+                updateDataset({"lastAssessmentRequestID":response.data['Request-ID'], "knowledgeBaseID":""}, this.props.dataSetDetails.datasetID).then((response) => {
+                  console.log(response)
+                  if (response.status === 200) {
+                    this.setState({
+                      isloading: false,
+                      //loadFailed: false
+                    });
+                  }
+                  else {
+                    this.setState({
+                      isloading: false,
+                      //loadFailed: true
+                    });
+                  }
+                });
+              });
+            });
+            this.props.whenUpdated({...this.props.dataSetDetails, lastAssessmentRequestID:response.data['Request-ID'],knowledgeBaseID:"" });
+          }
+          else {
+            this.setState({
+              isloading: false,
+              loadFailed: true
+            });
+          }
+        })
+      }
+      );
+    }
+    else
+    {
+      this.setState({
+        loadFailed: true
+      });
+    }
+
+
+  }
+
+  getLatestAssessmentQuality = () => {
+    read(getRecentAssessmentQuality(this.props.dataSetDetails.datasetPLD)).then((response) => {
+      let assessmentValues = [];
+      let assessmentDate = "Not Assessed Yet";
+      let calculateValue = [];
+      let actualValue = 0.0;
+      if (response) {
+        if (response.results.bindings.length > 0) {
+            calculateValue = response.results.bindings.map((metricDetails) => {
+            let metricValueType = (metricDetails.Value.datatype).split("#")[1]
+            let metricValue = 0.0;
+            assessmentDate = metricDetails.TimePeriod.value;
+            if (metricValueType === "boolean") {
+              let metricValueTemp = metricDetails.Value.value
+              if (metricValueTemp === "true") {
+                metricValue = 1.0;
+              }
+            }
+            else {
+              metricValue = Number.parseFloat(metricDetails.Value.value).toFixed(2)
+            }
+
+            assessmentValues.push(
+              metricDetails
+            );
+
+            return (this.props.dataSetDetails.assessmentMetrics.filter((metric) => { if (metric.assess === true & metric.metric === metricDetails.Metric.value) { return true; } return false }).map((metric) => {if (metricValueType === "boolean") { if(metricValue===metric.target){return parseFloat(1.0, 10);}else{return parseFloat(0.0, 10);}}else{return parseFloat(metricValue, 10);}}));
+          });
+        }
+      }
+      let filteredArray = calculateValue.flat();
+      if (filteredArray.length >0) {
+
+        actualValue = filteredArray.reduce((previous, current) => current += previous) / filteredArray.length;
+      }
+
+      this.setState(
+        {
+          dataSetLastAssessed: (assessmentDate).split(".")[0],
+          lastAssessmentQuality: Number(parseFloat(actualValue).toFixed(4)),
+          lastAssessmentMetrics: [...assessmentValues]
+        }, ()=> {   //console.log(this.state.lastAssessmentQuality);
+          this.props.whenUpdated({...this.props.dataSetDetails, dataSetLastAssessed:assessmentDate, lastAssessmentQuality:this.state.lastAssessmentQuality,lastAssessmentMetrics:[...this.state.lastAssessmentMetrics] });}
+      );
+    });
+  }
+
+  statusUpdate = async () => {
+    this.recall = setInterval(async () => {
+          //console.log("Status Update");
+      await getStatusUpdate(this.props.dataSetDetails.lastAssessmentRequestID).then((response) => {
+        if (typeof response === 'undefined') {
+          this.setState(
+            {
+              canAssess: true
+            });
+          clearInterval(this.recall);
+        }
+        else {
+          if (response.Status !== "INPROGRESS") {
+            this.setState(
+              {
+                canAssess: true
+              }
+            );
+            clearInterval(this.recall);
+            this.getLatestAssessmentQuality();
+          }
+        }
+      });
+    }, 5000);
+  }
+
+  toggleAlertModel = () => {
+    this.setState({
+      loadFailed: false
+    });
+  }
+
+  cancelAssessment =()=>
+  {
+      this.setState({ isloading: true }, () => {
+        cancelAssessment(this.props.dataSetDetails.lastAssessmentRequestID).then((response)=>{
+          //console.log(response);
+          if (response.status === 200  ) {
+            if(response.data.Status==="CANCELLED")
+          {
+            this.setState({
+              isloading: false,
+              loadFailed: false,
+              canAssess: true}, ()=>{
+                clearInterval(this.recall);
+              });
+          }
+          else {
+            this.setState({
+              isloading: false,
+              loadFailed: true});
+          }
+          }
+            else {
+              this.setState({
+                isloading: false,
+                loadFailed: true});
+            }
+
+        });
+      });
+  }
+
+  openConfig = () => {
+    this.props.onConfigClick(this.props.dataSetDetails);
+  }
+
+
+  render() {
+    let camelize = (str) => {
+      return str.replace(/(?:^\w|[A-Z]|\b\w)/g, (word, index) => {
+        return index === 0 ? word.toLowerCase() : word.toUpperCase();
+      }).replace(/\s+/g, '');
+    }
+
+    let percentageValue = 0;
+    percentageValue = Number.parseFloat(this.state.lastAssessmentQuality * 100).toFixed(2);
+    //console.log(percentageValue);
+    let canPublish = "Not Ready to Publish";
+    let displayColour = noteReadyToPublish;
+    let canPublishClass = "text-left font-weight-bold";
+
+    if (this.state.lastAssessmentQuality >= this.props.dataSetDetails.expectedProgress) {
+      canPublish = "Ready to Publish";
+      displayColour = readyToPublish;
+      canPublishClass = "text-left font-weight-bold";
+    }
+
+    return (
+      <Col xs="12" sm="6" lg="4">
+        <Container style={{ margin: 'auto' }} className=" w-100 h-90 dataSetTiles" >
+          <Card className="text-white bg-info text-center w-100 h-90 " >
+            <CardBody className="pb-0">
+              <ButtonGroup className="float-right">
+                <ButtonDropdown id='card1' isOpen={this.state.dropdownOpen} toggle={this.toggle}>
+                  <DropdownToggle caret className="p-0" color="transparent">
+                    <Media className="icon-settings"></Media>
+                  </DropdownToggle>
+                  <DropdownMenu right>
+                    <DropdownItem onClick={this.props.onRemoveClick}>Remove</DropdownItem>
+                    <DropdownItem onClick={this.openConfig}>Config</DropdownItem>
+                    {!this.state.canAssess?<DropdownItem onClick={this.cancelAssessment}>Cancel Assessment</DropdownItem>:null}
+                  </DropdownMenu>
+                </ButtonDropdown>
+              </ButtonGroup>
+              <Link to={{ pathname: this.props.location.pathname + "/" + (this.props.dataSetDetails.datasetID) }}>
+                <CardTitle className="text-value font-weight-bold text-dark text-left text-truncate">{this.props.dataSetDetails.datasetName}</CardTitle>
+                <CardSubtitle className={canPublishClass} style={{ color: displayColour }}>{canPublish}</CardSubtitle>
+                <div style={{ width: '150px', margin: 'auto' }}>
+                  <CircularProgressbar percentage={percentageValue} text={`${percentageValue}%`} background backgroundPadding={6} styles={{ background: { fill: '#3e98c7' }, text: { fill: displayColour }, path: { stroke: displayColour }, trail: { stroke: 'transparent' } }} />
+                </div>
+                <br />
+              </Link>
+              {this.state.canAssess ? <Button block color="secondary" aria-pressed="true" onClick={this.handleAssessClick} >Run Assessment</Button> : <Button disabled block color="secondary" aria-pressed="true" onClick={this.handleAssessClick} >Run Assessment</Button>}
+              <CardText className="text-dark">Last Assessed : {this.state.dataSetLastAssessed}</CardText>
+            </CardBody>
+          </Card>
+        </Container>
+        {this.state.isloading ? <LoadingSpinner /> : null}
+        {this.state.loadFailed ? <LoadingFailed clickHandler={this.toggleAlertModel} /> : null}
+      </Col>
+    );
+  }
+}
+
+export default withRouter(DataSetLarge);

+ 14 - 0
Luzzu Dashboard/src/views/Dashboard/loading.js

@@ -0,0 +1,14 @@
+//© 2019 Dublin City University, Trinity College Dublin. All rights reserved. This material may not be reproduced, displayed, modified or distributed without the express prior written permission of the copyright holder.
+import React from 'react';
+import { Modal, ModalBody } from 'reactstrap';
+
+const LoadingSpinner = () => (
+  <Modal isOpen={true} className="modal-dialog-centered">
+    <ModalBody style={{ margin: 'auto', display:'inline'}}>
+      <div className="fa fa-spinner fa-spin"></div>
+    </ModalBody>
+  </Modal>
+);
+
+
+export default LoadingSpinner;

+ 17 - 0
Luzzu Dashboard/src/views/Dashboard/loadingFailed.js

@@ -0,0 +1,17 @@
+//© 2019 Dublin City University, Trinity College Dublin. All rights reserved. This material may not be reproduced, displayed, modified or distributed without the express prior written permission of the copyright holder.
+import React from 'react';
+import { Modal, ModalBody,ModalFooter, Button} from 'reactstrap';
+
+const LoadingFailed = (props) => (
+  <Modal isOpen={true} className="modal-dialog-centered">
+    <ModalBody>
+      <div className="animated fadeIn pt-1 text-center">Error: Server Failed to Respond</div>
+    </ModalBody>
+    <ModalFooter>
+        <Button color="secondary" onClick={props.clickHandler}>Ok</Button>
+    </ModalFooter>
+  </Modal>
+);
+
+
+export default LoadingFailed;

+ 6 - 0
Luzzu Dashboard/src/views/Dashboard/package.json

@@ -0,0 +1,6 @@
+{
+  "name": "Dashboard",
+  "version": "0.0.0",
+  "private": true,
+  "main": "./Dashboard.js"
+}

+ 18 - 0
Luzzu Dashboard/src/views/Dashboard/tripleStoreUploadError.js

@@ -0,0 +1,18 @@
+//© 2019 Dublin City University, Trinity College Dublin. All rights reserved. This material may not be reproduced, displayed, modified or distributed without the express prior written permission of the copyright holder.
+import React from 'react';
+import { Modal, ModalBody,ModalFooter,Button } from 'reactstrap';
+
+const TripleStoreUploadError = (props) => (
+  <Modal isOpen={true} className="modal-dialog-centered">
+  <ModalBody>
+    <div className="animated fadeIn pt-1 text-center">Upload to triple store failed.</div>
+    <div className="animated fadeIn pt-1 text-center">Please upload the dataset to triple store manually with graph name "{props.graphName}" to support Root Case Analysis.</div>
+  </ModalBody>
+  <ModalFooter>
+      <Button color="secondary" onClick={props.clickHandler}>Ok</Button>
+  </ModalFooter>
+  </Modal>
+);
+
+
+export default TripleStoreUploadError;

+ 400 - 0
Luzzu Dashboard/src/views/DataSetDetails/AnalyticsWidget/AnalysisWidget-Working.js

@@ -0,0 +1,400 @@
+//© 2019 Dublin City University, Trinity College Dublin. All rights reserved. This material may not be reproduced, displayed, modified or distributed without the express prior written permission of the copyright holder.
+import React, { Component } from 'react';
+import {
+  FormGroup,
+  Form,
+  Label,
+  Input,
+  Row,
+  Col,
+  Card,
+  ListGroup,
+  CardBody,
+  CardText
+} from 'reactstrap';
+import MetricFilter from './MetricFilter';
+import ResourceList from './ResourceList';
+import ProblematicThingList from './ProblematicThingList';
+import ResourceViewDetails from './DetailsModels/ResourceViewDetails';
+import ProblematicThingViewDetails from './DetailsModels/ProblematicThingViewDetails';
+import viewMapping from '../../../config/metricViewMapping';
+
+class AnalysisWidget extends Component {
+  constructor(props) {
+    //console.log("constructor");
+
+    super(props);
+    this.state = {
+      resourceView: false,
+      problematicThingView: false,
+      viewChanged: false,
+      analysisMetrics: [],
+      currentMetricFilter: [],
+      resourceWiseList: [],
+      problematicWiseList: [],
+      showDetailsModel: false,
+      showDetails: null,
+      modelLinkNavigation: []
+    }
+    //console.log(this.props);
+  }
+
+  componentDidUpdate() {
+    //console.log("componentDidUpdate");
+  }
+
+  componentDidMount() {
+    //console.log("componentDidMount");
+  }
+
+  initialize = () => {
+    //console.log("initialize");
+
+    if (this.state.viewChanged) {
+      //console.log("Changing Filter");
+      let knowledgeBaseCache = JSON.parse(sessionStorage.getItem(this.props.knowledgeBaseID));
+      if (knowledgeBaseCache !== null) {
+        //console.log("Not Null");
+        let tempMetricList = [...new Set(knowledgeBaseCache.map((knowledgeBase) => (knowledgeBase.FailedMertic)))];
+        let viewType = "";
+        if (this.state.problematicThingView) {
+          viewType = "ProblematicThing";
+        }
+        else if (this.state.resourceView) {
+          viewType = "Resource";
+        }
+        if (viewType !== "") {
+          //tempMetricList = tempMetricList.filter((metric)=>(viewMapping.viewMapping[metric]===viewType));
+          tempMetricList = tempMetricList.filter((metric) => {
+            if (viewMapping.viewMapping[metric].indexOf(viewType) >= 0) {
+              return metric;
+            }
+          });
+
+          //console.log(tempMetricList);
+          this.setState({
+            analysisMetrics: tempMetricList,
+            currentMetricFilter: [],
+            viewChanged: false
+          });
+        }
+      }
+    }
+  }
+
+  changeView = (event) => {
+    //console.log(event);
+    if (event.target.value === "resource") {
+      this.setState({
+        resourceView: true,
+        problematicThingView: false,
+        viewChanged: true,
+        analysisMetrics: [],
+        currentMetricFilter: []
+      }, () => {
+        this.generateResourceWiseList();
+      });
+    }
+    else if (event.target.value === "problematicThing") {
+      this.setState({
+        resourceView: false,
+        problematicThingView: true,
+        viewChanged: true,
+        analysisMetrics: [],
+        currentMetricFilter: []
+      }, () => {
+        this.generateProblematicThingWiseList();
+      });
+    }
+  }
+
+  generateResourceWiseList = () => {
+    //console.log("generateResourceWiseList");
+    let knowledgeBaseCache = JSON.parse(sessionStorage.getItem(this.props.knowledgeBaseID));
+    let resourceDictionary = {};
+    //knowledgeBaseCache.forEach((knowledgeBase)=>{
+    for (const knowledgeBase of knowledgeBaseCache) {
+      if (this.state.currentMetricFilter.indexOf(knowledgeBase.FailedMertic) >= 0) {
+        let resource = knowledgeBase.Subject;
+        if (resourceDictionary[resource] === undefined) {
+          resourceDictionary[resource] = { exception: [], resourceReplica: [], disjointClass: [] };
+        }
+
+        //resourceDictionary[resource].exception = [...resourceDictionary[resource].exception, ...knowledgeBase.resourceRelatedExceptions];
+        resourceDictionary[resource].exception = [...resourceDictionary[resource].exception, ...knowledgeBase.SubjectRelatedException];
+
+        if (knowledgeBase.Exception === "ResourceReplica") {
+          //if(resourceDictionary[resource].resourceReplica===null)
+          //{
+          //  resourceDictionary[resource].resourceReplica = [];
+          //  resourceDictionary[resource].resourceReplica.push(knowledgeBase.Object);
+          //}
+          //else
+          //{
+          if (resourceDictionary[resource].resourceReplica.indexOf(knowledgeBase.Object) < 0) {
+            resourceDictionary[resource].resourceReplica.push(knowledgeBase.Object);
+          }
+          //}
+        }
+
+        if (knowledgeBase.Exception === "MultiTypedResourceWithDisjointedClasses") {
+          //if(resourceDictionary[resource].disjointClass===null)
+          //{
+          //  resourceDictionary[resource].disjointClass = [];
+          //  resourceDictionary[resource].disjointClass.push(knowledgeBase.Object);
+          //}
+          //else
+          //{
+          if (resourceDictionary[resource].disjointClass.indexOf(knowledgeBase.Object) < 0) {
+            resourceDictionary[resource].disjointClass.push(knowledgeBase.Object);
+          }
+          //}
+        }
+
+      }
+    }
+    //});
+
+    let resourceArray = [];
+    for (const [key, value] of Object.entries(resourceDictionary)) {
+      //console.log(key, value);
+      value.exception = [...new Set(value.exception.map((exception) => (exception)))];
+      resourceArray.push({ [key]: value })
+    }
+    resourceArray.sort((resourcePrev, resourceNext) => {
+      if (resourcePrev[Object.keys(resourcePrev)].resourceReplica !== null & resourceNext[Object.keys(resourceNext)].resourceReplica !== null & resourcePrev[Object.keys(resourcePrev)].resourceReplica.length > resourceNext[Object.keys(resourceNext)].resourceReplica.length) return -1;
+      if (resourcePrev[Object.keys(resourcePrev)].resourceReplica !== null & resourceNext[Object.keys(resourceNext)].resourceReplica !== null & resourcePrev[Object.keys(resourcePrev)].resourceReplica.length < resourceNext[Object.keys(resourceNext)].resourceReplica.length) return 1;
+      if (resourcePrev[Object.keys(resourcePrev)].disjointClass !== null & resourceNext[Object.keys(resourceNext)].disjointClass !== null & resourcePrev[Object.keys(resourcePrev)].disjointClass.length > resourceNext[Object.keys(resourceNext)].disjointClass.length) return -1;
+      if (resourcePrev[Object.keys(resourcePrev)].disjointClass !== null & resourceNext[Object.keys(resourceNext)].disjointClass !== null & resourcePrev[Object.keys(resourcePrev)].disjointClass.length < resourceNext[Object.keys(resourceNext)].disjointClass.length) return 1;
+      if (resourcePrev[Object.keys(resourcePrev)].exception.length > resourceNext[Object.keys(resourceNext)].exception.length) return -1;
+      if (resourcePrev[Object.keys(resourcePrev)].exception.length < resourceNext[Object.keys(resourceNext)].exception.length) return 1;
+    });
+    //console.log(resourceArray);
+    this.setState({
+      resourceWiseList: resourceArray
+    });
+  }
+
+
+  generateProblematicThingWiseList = () => {
+    //console.log("generateProblematicThingWiseList");
+    let knowledgeBaseCache = JSON.parse(sessionStorage.getItem(this.props.knowledgeBaseID));
+    let problematicThingDictionary = {};
+    //knowledgeBaseCache.forEach((knowledgeBase)=>{
+    for (const knowledgeBase of knowledgeBaseCache) {
+      if (this.state.currentMetricFilter.indexOf(knowledgeBase.FailedMertic) >= 0) {
+        for (const problematicThing of knowledgeBase.ProblematicThing) {
+          let problem = "";
+          if (problematicThing === "Subject") {
+            problem = "Subject-> " + knowledgeBase.Subject;
+          }
+          else if (problematicThing === "Predicate") {
+            problem = "Predicate-> " + knowledgeBase.Predicate;
+          }
+          else if (problematicThing === "Object") {
+            problem = "Object-> " + knowledgeBase.Object;
+          }
+
+          if (problem !== "") {
+            if (problematicThingDictionary[problem] === undefined) {
+              problematicThingDictionary[problem] = { exception: [], effectedResources: [], linkedProblematicThings: [] };
+            }
+            //problematicThingDictionary[problem].exception = [...problematicThingDictionary[problem].exception, ...knowledgeBase.problematicThingRelatedExceptions, knowledgeBase.Exception];
+            if (problematicThing === "Subject") {
+              problematicThingDictionary[problem].exception = [...problematicThingDictionary[problem].exception, ...knowledgeBase.SubjectRelatedException];
+            }
+            else if (problematicThing === "Predicate") {
+              problematicThingDictionary[problem].exception = [...problematicThingDictionary[problem].exception, ...knowledgeBase.PredicateRelatedException];
+            }
+            else if (problematicThing === "Object") {
+              problematicThingDictionary[problem].exception = [...problematicThingDictionary[problem].exception, ...knowledgeBase.ObjectRelatedException];
+            }
+
+            problematicThingDictionary[problem].effectedResources = [...problematicThingDictionary[problem].effectedResources, knowledgeBase.Subject];
+
+            for (const linkedProblematicThing of knowledgeBase.ProblematicThing) {
+              if (linkedProblematicThing !== problematicThing) {
+                problematicThingDictionary[problem].linkedProblematicThings = [...problematicThingDictionary[problem].linkedProblematicThings, linkedProblematicThing + "-> " + knowledgeBase[linkedProblematicThing]];
+              }
+            }
+          }
+        }
+      }
+    }
+    //});
+    let problematicThingArray = [];
+    for (const [key, value] of Object.entries(problematicThingDictionary)) {
+      //console.log(key, value);
+      value.exception = [...new Set(value.exception.map((exception) => (exception)))];
+      value.effectedResources = [...new Set(value.effectedResources.map((effectedResource) => (effectedResource)))];
+      value.linkedProblematicThings = [...new Set(value.linkedProblematicThings.map((linkedProblematicThing) => (linkedProblematicThing)))];
+      problematicThingArray.push({ [key]: value })
+    }
+
+    problematicThingArray.sort((problematicThingPrev, problematicThingNext) => {
+      if (problematicThingPrev[Object.keys(problematicThingPrev)].effectedResources.length > problematicThingNext[Object.keys(problematicThingNext)].effectedResources.length) return -1;
+      if (problematicThingPrev[Object.keys(problematicThingPrev)].effectedResources.length < problematicThingNext[Object.keys(problematicThingNext)].effectedResources.length) return 1;
+      if (problematicThingPrev[Object.keys(problematicThingPrev)].linkedProblematicThings.length > problematicThingNext[Object.keys(problematicThingNext)].linkedProblematicThings.length) return -1;
+      if (problematicThingPrev[Object.keys(problematicThingPrev)].linkedProblematicThings.length < problematicThingNext[Object.keys(problematicThingNext)].linkedProblematicThings.length) return 1;
+      if (problematicThingPrev[Object.keys(problematicThingPrev)].exception.length > problematicThingNext[Object.keys(problematicThingNext)].exception.length) return -1;
+      if (problematicThingPrev[Object.keys(problematicThingPrev)].exception.length < problematicThingNext[Object.keys(problematicThingNext)].exception.length) return 1;
+
+    });
+    //console.log(problematicThingArray);
+    this.setState({
+      problematicWiseList: problematicThingArray
+    });
+  }
+
+  updateMetricFilter = (event) => {
+    //console.log(event.target.value);
+    //console.log(event.target.checked);
+    if (event.target.checked) {
+      let tempFilter = this.state.currentMetricFilter;
+      tempFilter.push(event.target.value);
+      this.setState({
+        currentMetricFilter: tempFilter
+      }, () => {
+        if (this.state.resourceView) {
+          this.generateResourceWiseList();
+        }
+        else if (this.state.problematicThingView) {
+          this.generateProblematicThingWiseList();
+        }
+      });
+    }
+    else {
+      let tempFilter = this.state.currentMetricFilter.filter((metric) => { return metric !== event.target.value });
+      this.setState({
+        currentMetricFilter: tempFilter
+      }, () => {
+        if (this.state.resourceView) {
+          this.generateResourceWiseList();
+        }
+        else if (this.state.problematicThingView) {
+          this.generateProblematicThingWiseList();
+        }
+      });
+    }
+  }
+
+  OpenLinkedProblematicThingModel = (problematicThing) => {
+    //console.log(problematicThing);
+    let indexFound = -1;
+    for (const [index, value] of this.state.problematicWiseList.entries()) {
+      if (Object.keys(value)[0] === problematicThing) {
+        indexFound = index;
+        break;
+      }
+    }
+    if (indexFound >= 0) {
+      let modelLinkNavigation = [...this.state.modelLinkNavigation];
+      modelLinkNavigation.push(indexFound);
+      this.setState({
+        showDetailsModel: false,
+        showDetails: null,
+        modelLinkNavigation: [...modelLinkNavigation]
+      }, () => {
+        this.showDetails(indexFound);
+      });
+    }
+  }
+
+  closeShowDetails = () => {
+    this.setState({
+      showDetailsModel: false,
+      showDetails: null,
+      modelLinkNavigation: []
+    });
+  }
+
+  showDetails = (index) => {
+    //console.log("Showing Details : " + index);
+
+    if (this.state.resourceView) {
+      //console.log(this.state.resourceWiseList[index]);
+      //console.log(this.props);
+      //Fetch Complete Triples from the datastore
+      //Single tab, list all the triples and mark the triple with exception
+      //If there is resourcereplica exception then in another tab, list all the resources which are replica
+      this.setState({
+        showDetailsModel: true,
+        showDetails: this.state.resourceWiseList[index]
+      });
+    }
+    else if (this.state.problematicThingView) {
+      //console.log(this.state.problematicWiseList[index]);
+      if (this.state.modelLinkNavigation.length > 0) {
+        this.setState({
+          showDetailsModel: true,
+          showDetails: this.state.problematicWiseList[index]
+        });
+      }
+      else {
+        this.setState({
+          showDetailsModel: true,
+          showDetails: this.state.problematicWiseList[index],
+          modelLinkNavigation: [index]
+        });
+      }
+    }
+  }
+
+  render() {
+    //console.log("Render");
+    //console.log(this.state);
+    this.initialize();
+
+    return (
+      <React.Fragment>
+        <Row className="row justify-content-md-center">
+          <Col xs="4">
+          </Col>
+          <Col className="align-self-center" xs="8">
+            <Form className="form-inline justify-content-center">
+              <FormGroup check inline >
+                <Input className="form-check-input" type="radio" name="inlineRadioOptions" id="resource" value="resource" checked={this.state.resourceView} onChange={this.changeView} />
+                <Label className="form-check-label" for="inlineRadio1">Resource</Label>
+              </FormGroup>
+              <FormGroup check inline>
+                <Input className="form-check-input" type="radio" name="inlineRadioOptions" id="problematicThing" value="problematicThing" checked={this.state.problematicThingView} onChange={this.changeView} />
+                <Label className="form-check-label" for="inlineRadio2">Problematic Thing</Label>
+              </FormGroup>
+            </Form>
+          </Col>
+        </Row>
+        <Row>
+          <Col xs="4">
+            {!this.state.problematicThingView & !this.state.resourceView ?
+              <Card className="w-100 text-center" style={{ height: '30rem' }}>
+                <p className="text-center font-weight-bold">Metrics</p>
+                <CardBody>
+                  <CardText>Please select either Resource View or Problematic View option to list metric filters.</CardText>
+                  <CardText><small className="text-muted">If the required metric is not listed, then try switching the view option.</small></CardText>
+                </CardBody>
+              </Card>
+              :
+              <Card className="w-100" style={{ overflow: 'scroll', height: '30rem' }}>
+                <p className="text-center font-weight-bold">Metrics</p>
+                <ListGroup id="metricList" role="tablist" className="flex-column align-items-start">
+                  <MetricFilter metrics={this.state.analysisMetrics} updateFilter={this.updateMetricFilter} datasetID={this.props.datasetID} />
+                </ListGroup>
+              </Card>}
+          </Col>
+          <Col xs="8">
+            <Card className="w-100" style={{ overflow: 'scroll', height: '30rem' }}>
+              <ListGroup id="metricList" role="tablist" className="flex-column align-items-start">
+                {this.state.resourceView ? <ResourceList resourceList={this.state.resourceWiseList} showDetails={this.showDetails} /> : null}
+                {this.state.problematicThingView ? <ProblematicThingList problematicThingList={this.state.problematicWiseList} showDetails={this.showDetails} /> : null}
+              </ListGroup>
+            </Card>
+          </Col>
+        </Row>
+        {this.state.showDetailsModel & this.state.resourceView ? <ResourceViewDetails resourceDetails={this.state.showDetails} resourceList={this.state.resourceWiseList} closeModel={this.closeShowDetails} knowledgeBaseID={this.props.knowledgeBaseID} datasetID={this.props.datasetID} /> : null}
+        {this.state.showDetailsModel & this.state.problematicThingView ? <ProblematicThingViewDetails problematicThingDetails={this.state.showDetails} closeModel={this.closeShowDetails} knowledgeBaseID={this.props.knowledgeBaseID} datasetID={this.props.datasetID} linkNavigation={this.OpenLinkedProblematicThingModel} currentMetrics={this.state.currentMetricFilter}/> : null}
+      </React.Fragment>
+    );
+  }
+}
+
+export default AnalysisWidget;

+ 473 - 0
Luzzu Dashboard/src/views/DataSetDetails/AnalyticsWidget/AnalysisWidget.js

@@ -0,0 +1,473 @@
+//© 2019 Dublin City University, Trinity College Dublin. All rights reserved. This material may not be reproduced, displayed, modified or distributed without the express prior written permission of the copyright holder.
+import React, { Component } from 'react';
+import {
+  FormGroup,
+  Form,
+  Label,
+  Input,
+  Row,
+  Col,
+  Card,
+  ListGroup,
+  CardBody,
+  CardText
+} from 'reactstrap';
+import MetricFilter from './MetricFilter';
+import SummaryReport from './SummaryReport';
+import ResourceList from './ResourceList';
+import ProblematicThingList from './ProblematicThingList';
+import ResourceViewDetails from './DetailsModels/ResourceViewDetails';
+import ProblematicThingViewDetails from './DetailsModels/ProblematicThingViewDetails';
+import viewMapping from '../../../config/metricViewMapping';
+import LoadingSpinner from '../loading';
+import LoadingFailed from '../loadingFailed';
+
+class AnalysisWidget extends Component {
+  constructor(props) {
+    //console.log("constructor");
+
+    super(props);
+    this.state = {
+      resourceView: false,
+      problematicThingView: false,
+      viewChanged: false,
+      analysisMetrics: [],
+      currentMetricFilter: [],
+      resourceWiseList: [],
+      problematicWiseList: [],
+      showDetailsModel: false,
+      showDetails: null,
+      modelLinkNavigation: [],
+      resourceCount : [],
+      showSummary : false,
+      isloading: false
+    }
+    //console.log(this.props);
+  }
+
+  componentDidUpdate() {
+    //console.log("componentDidUpdate");
+  }
+
+  componentDidMount() {
+    //console.log("componentDidMount");
+  }
+
+  initialize = () => {
+    //console.log("initialize");
+
+    if (this.state.viewChanged) {
+      //console.log("Changing Filter");
+      //let knowledgeBaseCache = JSON.parse(sessionStorage.getItem(this.props.knowledgeBaseID));
+      let knowledgeBaseCache = this.props.knowledgeBaseCache;
+      if (knowledgeBaseCache !== null) {
+        //console.log("Not Null");
+        let tempMetricList = [...new Set(knowledgeBaseCache.map((knowledgeBase) => (knowledgeBase.FailedMertic)))];
+        let viewType = "";
+        if (this.state.problematicThingView) {
+          viewType = "ProblematicThing";
+        }
+        else if (this.state.resourceView) {
+          viewType = "Resource";
+        }
+        if (viewType !== "") {
+          //tempMetricList = tempMetricList.filter((metric)=>(viewMapping.viewMapping[metric]===viewType));
+          tempMetricList = tempMetricList.filter((metric) => {
+            if (viewMapping.viewMapping[metric].indexOf(viewType) >= 0) {
+              return metric;
+            }
+          });
+
+          //console.log(tempMetricList);
+          this.setState({
+            analysisMetrics: tempMetricList,
+            currentMetricFilter: [],
+            viewChanged: false
+          });
+        }
+      }
+    }
+  }
+
+  changeView = (event) => {
+    //console.log(event);
+    if (event.target.value === "resource") {
+      this.setState({
+        resourceView: true,
+        problematicThingView: false,
+        viewChanged: true,
+        analysisMetrics: [],
+        currentMetricFilter: []
+      }, () => {
+        this.generateResourceWiseList();
+      });
+    }
+    else if (event.target.value === "problematicThing") {
+      this.setState({
+        resourceView: false,
+        problematicThingView: true,
+        viewChanged: true,
+        analysisMetrics: [],
+        currentMetricFilter: []
+      }, () => {
+        this.generateProblematicThingWiseList();
+      });
+    }
+  }
+
+  generateResourceWiseList = () => {
+    //console.log("generateResourceWiseList");
+    //let knowledgeBaseCache = JSON.parse(sessionStorage.getItem(this.props.knowledgeBaseID));
+    let knowledgeBaseCache = this.props.knowledgeBaseCache;
+    let resourceDictionary = {};
+    let resourceCount = {};
+    //knowledgeBaseCache.forEach((knowledgeBase)=>{
+    for (const knowledgeBase of knowledgeBaseCache) {
+      if (this.state.currentMetricFilter.indexOf(knowledgeBase.FailedMertic) >= 0) {
+        if (resourceCount[knowledgeBase.FailedMertic] === undefined) {
+          resourceCount[knowledgeBase.FailedMertic] = [];
+        }
+        resourceCount[knowledgeBase.FailedMertic].push(knowledgeBase.Subject);
+
+        let resource = knowledgeBase.Subject;
+        if (resourceDictionary[resource] === undefined) {
+          resourceDictionary[resource] = { exception: [], resourceReplica: [], disjointClass: [] };
+        }
+
+        //resourceDictionary[resource].exception = [...resourceDictionary[resource].exception, ...knowledgeBase.resourceRelatedExceptions];
+        resourceDictionary[resource].exception = [...resourceDictionary[resource].exception, ...knowledgeBase.SubjectRelatedException];
+
+        if (knowledgeBase.Exception === "ResourceReplica") {
+          //if(resourceDictionary[resource].resourceReplica===null)
+          //{
+          //  resourceDictionary[resource].resourceReplica = [];
+          //  resourceDictionary[resource].resourceReplica.push(knowledgeBase.Object);
+          //}
+          //else
+          //{
+          if (resourceDictionary[resource].resourceReplica.indexOf(knowledgeBase.Object) < 0) {
+            resourceDictionary[resource].resourceReplica.push(knowledgeBase.Object);
+          }
+          //}
+        }
+
+        if (knowledgeBase.Exception === "MultiTypedResourceWithDisjointedClasses") {
+          //if(resourceDictionary[resource].disjointClass===null)
+          //{
+          //  resourceDictionary[resource].disjointClass = [];
+          //  resourceDictionary[resource].disjointClass.push(knowledgeBase.Object);
+          //}
+          //else
+          //{
+          if (resourceDictionary[resource].disjointClass.indexOf(knowledgeBase.Object) < 0) {
+            resourceDictionary[resource].disjointClass.push(knowledgeBase.Object);
+          }
+          //}
+        }
+
+      }
+    }
+    //});
+
+    let resourceArray = [];
+    for (const [key, value] of Object.entries(resourceDictionary)) {
+      //console.log(key, value);
+      value.exception = [...new Set(value.exception.map((exception) => (exception)))];
+      resourceArray.push({ [key]: value })
+    }
+
+    let resourceCountArray = [];
+    for (const [key, value] of Object.entries(resourceCount))
+    {
+      //console.log(key, value);
+      value = [...new Set(value.map((resource) => (resource)))]
+      resourceCountArray.push({ [key]: value })
+    }
+    //console.log(resourceCountArray);
+
+    resourceArray.sort((resourcePrev, resourceNext) => {
+      if (resourcePrev[Object.keys(resourcePrev)].resourceReplica !== null & resourceNext[Object.keys(resourceNext)].resourceReplica !== null & resourcePrev[Object.keys(resourcePrev)].resourceReplica.length > resourceNext[Object.keys(resourceNext)].resourceReplica.length) return -1;
+      if (resourcePrev[Object.keys(resourcePrev)].resourceReplica !== null & resourceNext[Object.keys(resourceNext)].resourceReplica !== null & resourcePrev[Object.keys(resourcePrev)].resourceReplica.length < resourceNext[Object.keys(resourceNext)].resourceReplica.length) return 1;
+      if (resourcePrev[Object.keys(resourcePrev)].disjointClass !== null & resourceNext[Object.keys(resourceNext)].disjointClass !== null & resourcePrev[Object.keys(resourcePrev)].disjointClass.length > resourceNext[Object.keys(resourceNext)].disjointClass.length) return -1;
+      if (resourcePrev[Object.keys(resourcePrev)].disjointClass !== null & resourceNext[Object.keys(resourceNext)].disjointClass !== null & resourcePrev[Object.keys(resourcePrev)].disjointClass.length < resourceNext[Object.keys(resourceNext)].disjointClass.length) return 1;
+      if (resourcePrev[Object.keys(resourcePrev)].exception.length > resourceNext[Object.keys(resourceNext)].exception.length) return -1;
+      if (resourcePrev[Object.keys(resourcePrev)].exception.length < resourceNext[Object.keys(resourceNext)].exception.length) return 1;
+    });
+    //console.log(resourceArray);
+    this.setState({
+      resourceWiseList: resourceArray,
+      resourceCount: resourceCountArray,
+      isloading:false
+    });
+    //console.log("generateResourceWiseList-Completed");
+  }
+
+
+  generateProblematicThingWiseList = () => {
+    //console.log("generateProblematicThingWiseList");
+    //let knowledgeBaseCache = JSON.parse(sessionStorage.getItem(this.props.knowledgeBaseID));
+    let knowledgeBaseCache = this.props.knowledgeBaseCache;
+    let problematicThingDictionary = {};
+    let resourceCount = {};
+    //knowledgeBaseCache.forEach((knowledgeBase)=>{
+    for (const knowledgeBase of knowledgeBaseCache) {
+      if (this.state.currentMetricFilter.indexOf(knowledgeBase.FailedMertic) >= 0) {
+        if (resourceCount[knowledgeBase.FailedMertic] === undefined) {
+          resourceCount[knowledgeBase.FailedMertic] = [];
+        }
+        resourceCount[knowledgeBase.FailedMertic].push(knowledgeBase.Subject);
+
+        for (const problematicThing of knowledgeBase.ProblematicThing) {
+          let problem = "";
+          if (problematicThing === "Subject") {
+            problem = "Subject-> " + knowledgeBase.Subject;
+          }
+          else if (problematicThing === "Predicate") {
+            problem = "Predicate-> " + knowledgeBase.Predicate;
+          }
+          else if (problematicThing === "Object") {
+            problem = "Object-> " + knowledgeBase.Object;
+          }
+
+          if (problem !== "") {
+            if (problematicThingDictionary[problem] === undefined) {
+              problematicThingDictionary[problem] = { exception: [], effectedResources: [], linkedProblematicThings: [] };
+            }
+            //problematicThingDictionary[problem].exception = [...problematicThingDictionary[problem].exception, ...knowledgeBase.problematicThingRelatedExceptions, knowledgeBase.Exception];
+            if (problematicThing === "Subject") {
+              //console.log(knowledgeBase.SubjectRelatedException);
+              problematicThingDictionary[problem].exception = [...problematicThingDictionary[problem].exception, ...knowledgeBase.SubjectRelatedException];
+            }
+            else if (problematicThing === "Predicate") {
+              //console.log(knowledgeBase.PredicateRelatedException);
+              problematicThingDictionary[problem].exception = [...problematicThingDictionary[problem].exception, ...knowledgeBase.PredicateRelatedException];
+            }
+            else if (problematicThing === "Object") {
+              //console.log(knowledgeBase.ObjectRelatedException);
+              problematicThingDictionary[problem].exception = [...problematicThingDictionary[problem].exception, ...knowledgeBase.ObjectRelatedException];
+            }
+
+            problematicThingDictionary[problem].effectedResources = [...problematicThingDictionary[problem].effectedResources, knowledgeBase.Subject];
+
+            for (const linkedProblematicThing of knowledgeBase.ProblematicThing) {
+              if (linkedProblematicThing !== problematicThing) {
+                problematicThingDictionary[problem].linkedProblematicThings = [...problematicThingDictionary[problem].linkedProblematicThings, linkedProblematicThing + "-> " + knowledgeBase[linkedProblematicThing]];
+              }
+            }
+          }
+        }
+      }
+    }
+    //});
+    let problematicThingArray = [];
+    for (const [key, value] of Object.entries(problematicThingDictionary)) {
+      //console.log(key, value);
+      value.exception = [...new Set(value.exception.map((exception) => (exception)))];
+      value.effectedResources = [...new Set(value.effectedResources.map((effectedResource) => (effectedResource)))];
+      value.linkedProblematicThings = [...new Set(value.linkedProblematicThings.map((linkedProblematicThing) => (linkedProblematicThing)))];
+      problematicThingArray.push({ [key]: value })
+    }
+
+    let resourceCountArray = [];
+    for (const [key, value] of Object.entries(resourceCount))
+    {
+      //console.log(key, value);
+      value = [...new Set(value.map((resource) => (resource)))]
+      resourceCountArray.push({ [key]: value })
+    }
+    //console.log(resourceCountArray);
+
+    problematicThingArray.sort((problematicThingPrev, problematicThingNext) => {
+      if (problematicThingPrev[Object.keys(problematicThingPrev)].effectedResources.length > problematicThingNext[Object.keys(problematicThingNext)].effectedResources.length) return -1;
+      if (problematicThingPrev[Object.keys(problematicThingPrev)].effectedResources.length < problematicThingNext[Object.keys(problematicThingNext)].effectedResources.length) return 1;
+      if (problematicThingPrev[Object.keys(problematicThingPrev)].linkedProblematicThings.length > problematicThingNext[Object.keys(problematicThingNext)].linkedProblematicThings.length) return -1;
+      if (problematicThingPrev[Object.keys(problematicThingPrev)].linkedProblematicThings.length < problematicThingNext[Object.keys(problematicThingNext)].linkedProblematicThings.length) return 1;
+      if (problematicThingPrev[Object.keys(problematicThingPrev)].exception.length > problematicThingNext[Object.keys(problematicThingNext)].exception.length) return -1;
+      if (problematicThingPrev[Object.keys(problematicThingPrev)].exception.length < problematicThingNext[Object.keys(problematicThingNext)].exception.length) return 1;
+
+    });
+    //console.log(problematicThingArray);
+    this.setState({
+      problematicWiseList: problematicThingArray,
+      resourceCount: resourceCountArray,
+      isloading:false
+    });
+    //console.log("generateProblematicThingWiseList-Completed");
+  }
+
+
+  updateMetricFilter = (event) => {
+    const sleep = ms => new Promise(resolve => setTimeout(() => resolve(), ms));
+    //console.log(event.target.value);
+    //console.log(event.target.checked);
+    //console.log("Clicked");
+    if (event.target.checked) {
+      let tempFilter = this.state.currentMetricFilter;
+      tempFilter.push(event.target.value);
+      this.setState({
+        currentMetricFilter: tempFilter,
+        isloading:true
+      }, async () => {
+        await sleep(500);
+        if (this.state.resourceView) {
+          //console.log(this.state.isloading);
+          this.generateResourceWiseList();
+        }
+        else if (this.state.problematicThingView) {
+          //console.log(this.state.isloading);
+          this.generateProblematicThingWiseList();
+        }
+      });
+    }
+    else {
+      let tempFilter = this.state.currentMetricFilter.filter((metric) => { return metric !== event.target.value });
+      this.setState({
+        currentMetricFilter: tempFilter,
+        isloading:true
+      }, async () => {
+        await sleep(500);
+        if (this.state.resourceView) {
+          //console.log(this.state.isloading);
+          this.generateResourceWiseList();
+        }
+        else if (this.state.problematicThingView) {
+          //console.log(this.state.isloading);
+          this.generateProblematicThingWiseList();
+        }
+      });
+    }
+  }
+
+  OpenLinkedProblematicThingModel = (problematicThing) => {
+    //console.log(problematicThing);
+    let indexFound = -1;
+    for (const [index, value] of this.state.problematicWiseList.entries()) {
+      if (Object.keys(value)[0] === problematicThing) {
+        indexFound = index;
+        break;
+      }
+    }
+    if (indexFound >= 0) {
+      let modelLinkNavigation = [...this.state.modelLinkNavigation];
+      modelLinkNavigation.push(indexFound);
+      this.setState({
+        showDetailsModel: false,
+        showDetails: null,
+        modelLinkNavigation: [...modelLinkNavigation]
+      }, () => {
+        this.showDetails(indexFound);
+      });
+    }
+  }
+
+  closeShowDetails = () => {
+    this.setState({
+      showDetailsModel: false,
+      showDetails: null,
+      modelLinkNavigation: []
+    });
+  }
+
+  showDetails = (index) => {
+    //console.log("Showing Details : " + index);
+
+    if (this.state.resourceView) {
+      this.setState({
+        showDetailsModel: true,
+        showDetails: this.state.resourceWiseList[index]
+      });
+    }
+    else if (this.state.problematicThingView) {
+      //console.log(this.state.problematicWiseList[index]);
+      if (this.state.modelLinkNavigation.length > 0) {
+        this.setState({
+          showDetailsModel: true,
+          showDetails: this.state.problematicWiseList[index]
+        });
+      }
+      else {
+        this.setState({
+          showDetailsModel: true,
+          showDetails: this.state.problematicWiseList[index],
+          modelLinkNavigation: [index]
+        });
+      }
+    }
+  }
+
+  showSummaryDetails=()=>{
+    this.setState({
+      showSummary:true
+    });
+  }
+
+  closeSummaryDetails =()=>{
+    this.setState({
+      showSummary:false
+    });
+  }
+
+  render() {
+    //console.log("Render");
+    //console.log(this.state.isloading);
+    //console.log(this.state);
+    this.initialize();
+
+    return (
+      <React.Fragment>
+      {this.state.isloading ? <LoadingSpinner /> : null}
+        <Row className="row justify-content-md-center">
+          <Col xs="4">
+          <p className="text-center font-weight-bold">
+          {this.state.currentMetricFilter.length>0?<button style={{ color: "#069", fontSize: "16px", textDecoration: "underline", background:"none",padding:"0", border:"none", cursor:"ponter" }} onClick={() => this.showSummaryDetails()}>Click here to view Summary</button>:null}
+          </p>
+          </Col>
+          <Col className="align-self-center" xs="8">
+            <Form className="form-inline justify-content-center">
+              <FormGroup check inline >
+                <Input className="form-check-input" type="radio" name="inlineRadioOptions" id="resource" value="resource" checked={this.state.resourceView} onChange={this.changeView} />
+                <Label className="form-check-label" for="inlineRadio1">Resource</Label>
+              </FormGroup>
+              <FormGroup check inline>
+                <Input className="form-check-input" type="radio" name="inlineRadioOptions" id="problematicThing" value="problematicThing" checked={this.state.problematicThingView} onChange={this.changeView} />
+                <Label className="form-check-label" for="inlineRadio2">Problematic Thing</Label>
+              </FormGroup>
+            </Form>
+          </Col>
+        </Row>
+        <Row>
+          <Col xs="4">
+            {!this.state.problematicThingView & !this.state.resourceView ?
+              <Card className="w-100 text-center" style={{ height: '30rem' }}>
+                <p className="text-center font-weight-bold">Metrics</p>
+                <CardBody>
+                  <CardText>Please select either Resource View or Problematic View option to list metric filters.</CardText>
+                  <CardText><small className="text-muted">If the required metric is not listed, then try switching the view option.</small></CardText>
+                </CardBody>
+              </Card>
+              :
+              <Card className="w-100" style={{ overflow: 'scroll', height: '30rem' }}>
+                <p className="text-center font-weight-bold">Metrics</p>
+                <ListGroup id="metricList" role="tablist" className="flex-column align-items-start">
+                  <MetricFilter metrics={this.state.analysisMetrics} updateFilter={this.updateMetricFilter} datasetID={this.props.datasetID} />
+                </ListGroup>
+              </Card>}
+          </Col>
+          <Col xs="8">
+            <Card className="w-100" style={{ overflow: 'scroll', height: '30rem' }}>
+              <ListGroup id="metricList" role="tablist" className="flex-column align-items-start">
+                {this.state.resourceView ? <ResourceList resourceList={this.state.resourceWiseList} showDetails={this.showDetails} /> : null}
+                {this.state.problematicThingView ? <ProblematicThingList problematicThingList={this.state.problematicWiseList} showDetails={this.showDetails} /> : null}
+              </ListGroup>
+            </Card>
+          </Col>
+        </Row>
+        {this.state.showSummary?<SummaryReport listOfMetrics={this.state.resourceCount} onCloseButtonClick={this.closeSummaryDetails} datasetID={this.props.datasetID}/>:null}
+        {this.state.showDetailsModel & this.state.resourceView ? <ResourceViewDetails resourceDetails={this.state.showDetails} resourceList={this.state.resourceWiseList} closeModel={this.closeShowDetails} knowledgeBaseID={this.props.knowledgeBaseID} datasetID={this.props.datasetID} knowledgeBaseCache={this.props.knowledgeBaseCache}/> : null}
+        {this.state.showDetailsModel & this.state.problematicThingView ? <ProblematicThingViewDetails problematicThingDetails={this.state.showDetails} closeModel={this.closeShowDetails} knowledgeBaseID={this.props.knowledgeBaseID} datasetID={this.props.datasetID} linkNavigation={this.OpenLinkedProblematicThingModel} currentMetrics={this.state.currentMetricFilter} knowledgeBaseCache={this.props.knowledgeBaseCache}/> : null}
+      </React.Fragment>
+    );
+  }
+}
+
+export default AnalysisWidget;

+ 317 - 0
Luzzu Dashboard/src/views/DataSetDetails/AnalyticsWidget/DetailsModels/ProblematicThingViewDetails.js

@@ -0,0 +1,317 @@
+//© 2019 Dublin City University, Trinity College Dublin. All rights reserved. This material may not be reproduced, displayed, modified or distributed without the express prior written permission of the copyright holder.
+import React, { Component } from 'react';
+import { Nav, NavItem, NavLink, TabContent, UncontrolledTooltip, Table, TabPane, Modal, Col, Row, ModalBody, ModalHeader, ModalFooter, Button } from 'reactstrap';
+import classnames from 'classnames';
+import exceptionInfoIconDetails from '../../../../config/exceptionInfoIconDetails';
+import { connect } from 'react-redux';
+import LoadingSpinner from '../../loading';
+import LoadingFailed from '../../loadingFailed';
+import { read } from '../../../../services/tripleStoreAPIs/readFromTripleStore';
+import { fetchListOfSimilarTriples } from '../../../../services/tripleStoreAPIs/sparql/fetchListOfSimilarTriples';
+import properties from '../../../../config/dashboardProperties';
+
+class ProblematicThingViewDetails extends Component {
+  constructor(props) {
+    //console.log("ProblematicThingViewDetails constructor");
+
+    super(props);
+    this.state = {
+      problematicThing: "",
+      activeTab: "",
+      navigationTabs: [],
+      triples: [],
+      isloading: false,
+      dataGraph: "",
+      graphType: ""
+    }
+    //console.log(this.props);
+  }
+
+
+  toggleTab = (tabID) => {
+    //console.log(typeof tabID);
+    if (this.state.activeTab !== tabID) {
+      this.setState({
+
+        activeTab: tabID
+      });
+    }
+  }
+
+  componentDidMount() {
+    //console.log("componentDidMount");
+    this.setState({
+      isloading: true
+    }, () => {
+      this.initialize().then((results) => {
+        this.setState({
+          problematicThing: results[0],
+          activeTab: results[1],
+          navigationTabs: results[2],
+          dataGraph: results[3],
+          graphType: results[4],
+          triples: results[5],
+          isloading: false
+        });
+      }
+      );
+    });
+
+  }
+
+  initialize = async () => {
+    //console.log("initialize");
+    let problematicThing = Object.keys(this.props.problematicThingDetails)[0];
+    let activeTab = "Default";
+    let navigationTabs = [];
+    navigationTabs.push("Default");
+    /*
+    if (this.props.problematicThingDetails[problematicThing].effectedResources.length > 0) {
+      navigationTabs.push("Impacted Resources");
+    }*/
+    if (this.props.problematicThingDetails[problematicThing].linkedProblematicThings.length > 0) {
+      navigationTabs.push("Linked Problematic Things");
+    }
+    navigationTabs.sort();
+
+    let dataGraph = "";
+    let graphType = "";
+    if (this.props.datasetDetailsCache[0].isSparqlEndPoint) {
+      dataGraph = this.props.datasetDetailsCache[0].sparqlEndPoint;
+      graphType = "SERVICE";
+    }
+    else {
+      let dataGraphName = this.props.datasetDetailsCache[0].fileName.replace(/^.*[\\/]/, '').split('.').slice(0, -1).join('.');
+      dataGraph = "http://" + properties.tripleStore.host + ":" + properties.tripleStore.port + "/" + properties.tripleStore.datastore + "/data/" + dataGraphName;
+      graphType = "GRAPH";
+    }
+    let query = await this.prepareQuery(problematicThing);
+    //console.log(query);
+    let triples = await this.fetchListOfTriples(dataGraph, graphType, query, problematicThing);
+    //console.log(triples);
+    return [problematicThing, activeTab, navigationTabs, dataGraph, graphType, triples];
+  }
+
+
+  fetchListOfTriples_NEW = (dataGraph, graphType, query, problematicThing) => {
+    let triples = [];
+    let problem = problematicThing.split("-> ")[1];
+    let problematicPart = problematicThing.split("-> ")[0];
+
+    return new Promise((resolve) => {
+      //let knowledgeBaseCache = JSON.parse(sessionStorage.getItem(this.props.knowledgeBaseID));
+      //let knowledgeBaseCache = this.props.knowledgeBaseCache;
+      for (const knowledgeBase of this.props.knowledgeBaseCache) {
+        if ((this.props.currentMetrics.indexOf(knowledgeBase.FailedMertic)>=0) & knowledgeBase[problematicPart]===problem) {
+          triples.push({
+            Subject: knowledgeBase.Subject,
+            Predicate: knowledgeBase.Predicate,
+            Object: knowledgeBase.Object,
+            Exception: []
+          });
+        }
+      }
+      for (const triple of triples) {
+        //console.log("Inside Triple Array : "+tripleArray);
+        for (const knowledgeBase of this.props.knowledgeBaseCache) {
+          if (knowledgeBase.Subject === triple.Subject & knowledgeBase.Predicate === triple.Predicate & knowledgeBase.Object === triple.Object) {
+            triple.Exception.push(knowledgeBase.Exception);
+          }
+        }
+      }
+
+      resolve(triples);
+    });
+  }
+
+
+
+  fetchListOfTriples = (dataGraph, graphType, query, problematicThing) => {
+    let triples = [];
+    let problem = problematicThing.split("-> ")[1];
+    return new Promise((resolve) => {
+      read(fetchListOfSimilarTriples(graphType, dataGraph, query)).then((response) => {
+        //console.log(response);
+        if (response) {
+          if (response.results.bindings.length > 0) {
+            for (const result of response.results.bindings) {
+              let subject = "";
+              let predicate = "";
+              let object = "";
+
+              if (result.Subject === undefined) {
+                subject = problem;
+              }
+              else {
+                subject = "<" + result.Subject.value + ">";
+              }
+
+              if (result.Predicate === undefined) {
+                predicate = problem;
+              }
+              else {
+                predicate = "<" + result.Predicate.value + ">";
+              }
+
+              if (result.Object === undefined) {
+                object = problem;
+              }
+              else {
+                if (result.Object.type === "uri") {
+                  object += "<" + result.Object.value + ">";
+                }
+                else {
+                  if (result.Object["xml:lang"] !== undefined) {
+                    object += "\"\"\"" + result.Object.value + "\"\"\"@" + result.Object["xml:lang"];
+                  }
+                  else if (result.Object["datatype"] !== undefined) {
+                    let value = result.Object.value.replace(/("|')/g, '\\$1');
+                    object += "\"\"\"" + value + "\"\"\"" + "^^<" + result.Object["datatype"] + ">";
+                  }
+                  else {
+                    object += "\"\"\"" + result.Object.value + "\"\"\"";
+                  }
+                }
+              }
+              triples.push({
+                Subject: subject,
+                Predicate: predicate,
+                Object: object,
+                Exception: []
+              });
+            }
+
+            //let knowledgeBaseCache = JSON.parse(sessionStorage.getItem(this.props.knowledgeBaseID));
+            //let knowledgeBaseCache = this.props.knowledgeBaseCache;
+            let tripleArray = 0;
+            for (const triple of triples) {
+              //console.log("Inside Triple Array : "+tripleArray);
+              for (const knowledgeBase of this.props.knowledgeBaseCache) {
+                if (knowledgeBase.Subject === triple.Subject & knowledgeBase.Predicate === triple.Predicate & knowledgeBase.Object === triple.Object) {
+                  triple.Exception.push(knowledgeBase.Exception);
+                }
+              }
+              tripleArray += 1;
+            }
+            //console.log("Returning Similar Resources");
+            //console.log(similarResourcesFound);
+            resolve(triples);
+          }
+          else {
+            resolve(triples);
+          }
+        }
+        else {
+          resolve(triples);
+        }
+      });
+    });
+  }
+
+  prepareQuery = (problematicThing) => {
+    //console.log("prepareQuery");
+    let problematicPart = problematicThing.split("-> ")[0];
+    let problem = problematicThing.split("-> ")[1];
+    let query = "";
+    return new Promise((resolve) => {
+      if (problematicPart === "Subject") {
+        query = problem + " ?Predicate ?Object";
+      }
+      else if (problematicPart === "Predicate") {
+        query = "?Subject " + problem + " ?Object";
+      }
+      else if (problematicPart === "Object") {
+        query = "?Subject ?Predicate " + problem;
+      }
+      resolve(query);
+    });
+  }
+
+  renderDetails = (tabID) => {
+    switch (tabID) {
+      case "Default":
+        return (<React.Fragment>
+          <div className="text-center">
+          <h6 className="mb-1">{this.state.problematicThing}</h6>
+          <p style={{ fontSize: "14px" }}>Exception(s) Related to this Problematic Thing : {this.props.problematicThingDetails[this.state.problematicThing].exception.map((exception, indexException) => { return <React.Fragment key={indexException}><i>{exception} </i><i className="fa fa-info-circle" id={exception + "_toolTipIcon"}>  </i><UncontrolledTooltip placement="right" target={exception + "_toolTipIcon"}>{exceptionInfoIconDetails.infoIcon[exception]}</UncontrolledTooltip>&nbsp;</React.Fragment> })}</p>
+          </div>
+          <font size="2"  >
+            <Table hover bordered><thead><tr><th>Subject</th><th>Predicate</th><th>Object</th><th>Exception(s)</th></tr></thead><tbody>
+              {this.state.triples.map((triple, indexInside) => {
+                if (triple.Exception.length > 0) {
+                  return <tr key={indexInside} style={{ 'backgroundColor': 'rgba(255,255,102,0.5)' }} ><td>{triple.Subject}</td><td>{triple.Predicate}</td><td>{triple.Object}</td><td>{triple.Exception.join(', ')}</td></tr>
+                }
+                return <tr key={indexInside}><td>{triple.Subject}</td><td>{triple.Predicate}</td><td>{triple.Object}</td><td></td></tr>
+              })}
+            </tbody></Table></font>
+        </React.Fragment>);
+
+      case "Impacted Resources":
+        return (<React.Fragment><Table hover bordered striped><thead><tr><th>Resource</th></tr></thead><tbody>
+          {this.props.problematicThingDetails[this.state.problematicThing].effectedResources.map((resource, indexInside) => {
+            return <tr key={indexInside}><td>{resource}</td></tr>
+          })}
+        </tbody></Table>
+        </React.Fragment>);
+
+      case "Linked Problematic Things":
+        return (<React.Fragment><Table hover bordered striped><thead><tr><th>Problematic Thing <small>(Click on the link to navigate)</small></th></tr></thead><tbody>
+          {this.props.problematicThingDetails[this.state.problematicThing].linkedProblematicThings.map((link, indexInside) => {
+            return <tr key={indexInside}><td onClick={() => this.props.linkNavigation(link)}>{link}</td></tr>
+          })}
+        </tbody></Table>
+        </React.Fragment>);
+    }
+  }
+
+  render() {
+    //console.log(this.state);
+    let navigationTabs = null;
+    let displayPane = null;
+    navigationTabs = (this.state.navigationTabs.map((tabID, index) => {
+      return <NavItem key={index}><NavLink className={classnames({ active: this.state.activeTab === tabID })} onClick={() => { this.toggleTab(tabID); }}>{tabID}</NavLink></NavItem>
+    }));
+
+    displayPane = (this.state.navigationTabs.map((tabID, index) => {
+      return <TabPane key={index} tabId={tabID} style={{ overflow: 'scroll', height: '30rem' }}><Row><Col>
+        {
+          this.renderDetails(tabID)
+        }
+      </Col></Row></TabPane>
+    }));
+
+
+    return (
+      <React.Fragment>
+        <Modal isOpen={true} className="modal-dialog-centered mw-100 w-100" >
+          <ModalHeader style={{ margin: 'auto' }}>Problematic Thing Details</ModalHeader>
+          <ModalBody>
+            <Nav tabs>
+              {navigationTabs}
+            </Nav>
+            <TabContent activeTab={this.state.activeTab}>
+              {displayPane}
+            </TabContent>
+          </ModalBody>
+          <ModalFooter>
+            <Button color="secondary" onClick={this.props.closeModel}>Ok</Button>
+          </ModalFooter>
+          {this.state.isloading ? <LoadingSpinner /> : null}
+        </Modal>
+      </React.Fragment>
+    );
+  }
+}
+
+
+const mapStateToProps = (state, ownProps) => {
+  let datasetID = Number(ownProps.datasetID);
+  let datasetDetails = state.datasetDetailsCache.filter((dataset) => { if (dataset.datasetID === datasetID) { return dataset; } return null; });
+  return (
+    {
+      datasetDetailsCache: datasetDetails
+    }
+  );
+}
+
+export default connect(mapStateToProps)(ProblematicThingViewDetails);

+ 393 - 0
Luzzu Dashboard/src/views/DataSetDetails/AnalyticsWidget/DetailsModels/ResourceViewDetails.js

@@ -0,0 +1,393 @@
+//© 2019 Dublin City University, Trinity College Dublin. All rights reserved. This material may not be reproduced, displayed, modified or distributed without the express prior written permission of the copyright holder.
+import React, { Component } from 'react';
+import { Nav, NavItem, NavLink, TabContent, UncontrolledTooltip, Card, Table, TabPane, Modal, Col, Row, ModalBody, ModalHeader, ModalFooter, Button, ListGroupItem } from 'reactstrap';
+import classnames from 'classnames';
+import properties from '../../../../config/dashboardProperties';
+import exceptionInfoIconDetails from '../../../../config/exceptionInfoIconDetails';
+import { connect } from 'react-redux';
+import { read } from '../../../../services/tripleStoreAPIs/readFromTripleStore';
+import { fetchTriplesForResource } from '../../../../services/tripleStoreAPIs/sparql/fetchTriplesForResource';
+import { fetchListOfSimilarResource } from '../../../../services/tripleStoreAPIs/sparql/fetchListOfSimilarResource';
+import LoadingSpinner from '../../loading';
+import LoadingFailed from '../../loadingFailed';
+
+class ResourceViewDetails extends Component {
+  constructor(props) {
+    //console.log("ResourceViewDetails constructor");
+    super(props);
+    this.state = {
+      resource: "",
+      activeTab: "",
+      navigationTabs: [],
+      triples: [],
+      isloading: false,
+      dataGraph: "",
+      graphType: "",
+      similarResources: [],
+      showSimilarResourcePopup: false,
+      similarResourceTriple: [],
+      similarResourceTripleException: []
+    }
+    //console.log(this.props);
+  }
+
+  toggleTab = (tabID) => {
+    //console.log(typeof tabID);
+    if (this.state.activeTab !== tabID) {
+      this.setState({
+        activeTab: tabID
+      });
+    }
+  }
+
+  initialize = async () => {
+    let resource = Object.keys(this.props.resourceDetails)[0];
+    let navigationTabs = [...new Set(this.props.resourceDetails[resource].exception.map((exception) => {
+      if (exception === "ResourceReplica") {
+        return "ResourceReplica";
+      }
+      return "Default";
+    }))];
+
+    let activeTab = "Default";
+    if (!(navigationTabs.indexOf("Default") >= 0)) {
+      navigationTabs.push("Default");
+    }
+    //navigationTabs.push("Similar Resources");
+    navigationTabs.sort();
+
+    let dataGraph = "";
+    let graphType = "";
+    if (this.props.datasetDetailsCache[0].isSparqlEndPoint) {
+      dataGraph = this.props.datasetDetailsCache[0].sparqlEndPoint;
+      graphType = "SERVICE";
+    }
+    else {
+      let dataGraphName = this.props.datasetDetailsCache[0].fileName.replace(/^.*[\\/]/, '').split('.').slice(0, -1).join('.');
+      dataGraph = "http://" + properties.tripleStore.host + ":" + properties.tripleStore.port + "/" + properties.tripleStore.datastore + "/data/" + dataGraphName;
+      graphType = "GRAPH";
+    }
+    //console.log("Going to fetch for : " +graphType+ ":"+dataGraph+":"+resource);
+    let triples = await this.fetchDetailsAboutResource(graphType, dataGraph, resource);
+    let [query, distinctProperties] = await this.prepareQueryToFindSimilarResources(triples);
+    let similarResources = await this.findSimilarResources(graphType, dataGraph, query, resource);
+    //console.log(query);
+    //console.log(distinctProperties);
+    //console.log(similarResources);
+    let actualListOfSimilarResource = [];
+    /*
+    //What happens if there are 100 or more similar resources??
+    for (const distinctResource of similarResources) {
+      let triplesOfDistinctResource = await this.fetchDetailsAboutResource(graphType, dataGraph, distinctResource);
+      let [queryOfDistinctResource, distinctPropertiesOfDistinctResource] = await this.prepareQueryToFindSimilarResources(triplesOfDistinctResource);
+      let matching = await this.matchProperties(distinctProperties, distinctPropertiesOfDistinctResource);
+      if (matching) {
+        actualListOfSimilarResource.push(distinctResource);
+      }
+    }
+    */
+    return [resource, navigationTabs, activeTab, dataGraph, graphType, triples, actualListOfSimilarResource]
+  }
+
+  matchProperties = (distinctProperties, distinctPropertiesOfDistinctResource) => {
+    let similar = false;
+    return new Promise((resolve) => {
+
+      if (distinctProperties.length === distinctPropertiesOfDistinctResource.length) {
+        distinctProperties.sort();
+        distinctPropertiesOfDistinctResource.sort();
+        for (const [index, property] of distinctProperties.entries()) {
+          if (property !== distinctPropertiesOfDistinctResource[index]) {
+            similar = false;
+            resolve(similar);
+          }
+        }
+        similar = true;
+        resolve(similar);
+      }
+      else {
+        resolve(similar);
+      }
+    });
+  }
+
+  findSimilarResources = (graphType, dataGraph, query, resource) => {
+    let similarResourcesFound = [];
+    return new Promise((resolve) => {
+
+      read(fetchListOfSimilarResource(graphType, dataGraph, query, resource)).then((response) => {
+
+        //console.log(response);
+        if (response) {
+          if (response.results.bindings.length > 0) {
+            for (const result of response.results.bindings) {
+              similarResourcesFound.push("<" + result.Resource.value + ">");
+            }
+            //console.log("Returning Similar Resources");
+            //console.log(similarResourcesFound);
+            resolve(similarResourcesFound);
+          }
+          else {
+            resolve(similarResourcesFound);
+          }
+        }
+        else {
+          resolve(similarResourcesFound);
+        }
+      });
+    });
+  }
+
+  prepareQueryToFindSimilarResources = (triples) => {
+    let query = "";
+    let addedProperties = [];
+    return new Promise((resolve) => {
+      for (const [index, triple] of triples.entries()) {
+        if (addedProperties.indexOf(triple.Predicate) < 0) {
+          if (triple.Predicate !== "<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>") {
+            addedProperties.push(triple.Predicate);
+          }
+          if (index === 0) {
+            query = "?Resource " + triple.Predicate + " ?Object_" + index;
+          }
+          else {
+            query += triple.Predicate + " ?Object_" + index;
+          }
+          if ((index + 1) !== triples.length) {
+            query += "; "
+          }
+        }
+      }
+      resolve([query, addedProperties]);
+    });
+  }
+
+  fetchDetailsAboutResource = (graphType, dataGraph, resource) => {
+    let triples = [];
+
+    return new Promise((resolve) => {
+      read(fetchTriplesForResource(graphType, dataGraph, resource)).then((response) => {
+        //console.log(response);
+        if (response) {
+          if (response.results.bindings.length > 0) {
+            let responseArray = 0;
+            for (const triple of response.results.bindings) {
+              //console.log("Respone Array : " + responseArray);
+              let object = "";
+              if (triple.Object.type === "uri") {
+                object += "<" + triple.Object.value + ">";
+              }
+              else {
+                if (triple.Object["xml:lang"] !== undefined) {
+                  object += "\"\"\"" + triple.Object.value + "\"\"\"@" + triple.Object["xml:lang"];
+                }
+                else if (triple.Object["datatype"] !== undefined) {
+                  let value = triple.Object.value.replace(/("|')/g, '\\$1');
+                  object += "\"\"\"" + value + "\"\"\"" + "^^<" + triple.Object["datatype"] + ">";
+                }
+                else {
+                  object += "\"\"\"" + triple.Object.value + "\"\"\"";
+                }
+              }
+              triples.push({
+                Subject: "<" + triple.Resource.value + ">",
+                Predicate: "<" + triple.Predicate.value + ">",
+                Object: object,
+                Exception: []
+              });
+              responseArray += 1;
+            }
+            //let knowledgeBaseCache = JSON.parse(sessionStorage.getItem(this.props.knowledgeBaseID));
+            //let knowledgeBaseCache = this.props.knowledgeBaseCache;
+            let tripleArray = 0;
+            for (const triple of triples) {
+              //console.log("Inside Triple Array : "+tripleArray);
+              for (const knowledgeBase of this.props.knowledgeBaseCache) {
+                if (knowledgeBase.Subject === triple.Subject & knowledgeBase.Predicate === triple.Predicate & knowledgeBase.Object === triple.Object) {
+                  triple.Exception.push(knowledgeBase.Exception);
+                }
+                if(knowledgeBase.Object === triple.Subject & knowledgeBase.Exception==="ResourceReplica")
+                {
+                  triple.Exception.push(knowledgeBase.Exception);
+                }
+              }
+              tripleArray += 1;
+            }
+            //console.log("Returning Triples");
+            //console.log(triples);
+            resolve(triples);
+          }
+          else {
+            resolve(triples);
+          }
+        }
+        else {
+          resolve(triples);
+        }
+      });
+    });
+  }
+
+  componentDidMount() {
+
+    this.setState({
+      isloading: true
+    }, () => {
+      this.initialize().then((results) => {
+        this.setState({
+          resource: results[0],
+          navigationTabs: results[1],
+          activeTab: results[2],
+          dataGraph: results[3],
+          graphType: results[4],
+          triples: results[5],
+          similarResources: results[6],
+          isloading: false
+        });
+      }
+      );
+    });
+  }
+
+  closeSimilarResourcePopup = () => {
+    this.setState({
+      showSimilarResourcePopup: false,
+      similarResourceTriple: [],
+      similarResourceTripleException: []
+    });
+  }
+
+  showSimilarResource = async (resource) => {
+    //console.log(resource);
+    let triples = await this.fetchDetailsAboutResource(this.state.graphType, this.state.dataGraph, resource);
+    let tempExceptionsOfSimilarResource = [];
+    for (const triple of triples) {
+      tempExceptionsOfSimilarResource=[...tempExceptionsOfSimilarResource,...triple.Exception]
+    }
+      let exceptionsOfSimilarResource=[...new Set(tempExceptionsOfSimilarResource.map((exception) => exception))];
+    this.setState({
+      showSimilarResourcePopup: true,
+      similarResourceTriple: triples,
+      similarResourceTripleException: exceptionsOfSimilarResource
+    });
+  }
+
+  renderDetails = (tabID) => {
+    switch (tabID) {
+      case "Default":
+        return (<React.Fragment>
+          <p style={{ fontSize: "14px" }} className="text-center">Exception(s) Related to this Resource : {this.props.resourceDetails[this.state.resource].exception.map((exception, indexException) => { return <React.Fragment key={indexException}><i>{exception} </i><i className="fa fa-info-circle" id={exception + "_toolTipIcon"}>  </i><UncontrolledTooltip placement="right" target={exception + "_toolTipIcon"}>{exceptionInfoIconDetails.infoIcon[exception]}</UncontrolledTooltip><span>&nbsp;</span></React.Fragment> })}</p>
+          <font size="2"  >
+            <Table hover bordered><thead><tr><th>Subject</th><th>Predicate</th><th>Object</th><th>Exception(s)</th></tr></thead><tbody>
+              {this.state.triples.map((triple, indexInside) => {
+                if (triple.Exception.length > 0) {
+                  return <tr key={indexInside} style={{ 'backgroundColor': 'rgba(255,255,102,0.5)' }} ><td>{triple.Subject}</td><td>{triple.Predicate}</td><td>{triple.Object}</td><td>{triple.Exception.join(', ')}</td></tr>
+                }
+                return <tr key={indexInside}><td>{triple.Subject}</td><td>{triple.Predicate}</td><td>{triple.Object}</td><td></td></tr>
+              })}
+            </tbody></Table></font>
+        </React.Fragment>);
+
+      case "Similar Resources":
+        return (<React.Fragment>
+          {this.state.similarResources.length > 0 ?
+            <Table hover bordered striped><thead><tr><th>Similar Resources <small>(Click on the link to view)</small></th></tr></thead><tbody>
+              {this.state.similarResources.map((resource, indexInside) => {
+                return <tr key={indexInside}><td onClick={() => this.showSimilarResource(resource)}>{resource}</td></tr>
+              })}
+            </tbody></Table>
+            :
+            <p style={{ fontSize: "14px" }} className="text-center">No similar resource(s) found</p>
+          }
+        </React.Fragment>);
+
+      case "ResourceReplica":
+        return (<React.Fragment>
+          {this.props.resourceDetails[this.state.resource].resourceReplica.length>0?
+            this.props.resourceDetails[this.state.resource].resourceReplica.map((resourceReplica, indexInside) => {
+              return <ListGroupItem key={indexInside} className="border-0">{resourceReplica}</ListGroupItem>
+            })
+            :
+            <p style={{ fontSize: "14px" }} className="text-center">Please select ExtensionalConcisenessMetric from the filter list.</p>
+          }
+        </React.Fragment>);
+    }
+  }
+
+  render() {
+    //console.log(this.state);
+    let navigationTabs = null;
+    let displayPane = null;
+    navigationTabs = (this.state.navigationTabs.map((tabID, index) => {
+      return <NavItem key={index}><NavLink className={classnames({ active: this.state.activeTab === tabID })} onClick={() => { this.toggleTab(tabID); }}>{tabID}</NavLink></NavItem>
+    }));
+
+    displayPane = (this.state.navigationTabs.map((tabID, index) => {
+      return <TabPane tabId={tabID} style={{ overflow: 'scroll', height: '30rem' }} key={index}><Row><Col>
+        {
+          this.renderDetails(tabID)
+        }
+      </Col></Row></TabPane>
+    }));
+    let similarResourcePopup = null;
+    similarResourcePopup = (
+      <React.Fragment>
+        <Modal isOpen={true} className="modal-dialog-centered mw-100 w-75" >
+          <ModalHeader style={{ margin: 'auto' }}>Similar Resource Details</ModalHeader>
+          <ModalBody>
+            <Card style={{ overflow: 'scroll', height: '30rem' }} ><Row><Col>
+              <p style={{ fontSize: "14px" }} className="text-center">Exception(s) Related to this Resource : {this.state.similarResourceTripleException.map((exception, indexException) => { return <React.Fragment key={indexException}><i>{exception} </i><i className="fa fa-info-circle" id={exception + "_toolTipIcon_similar"}>  </i><UncontrolledTooltip placement="right" target={exception + "_toolTipIcon_similar"}>{exceptionInfoIconDetails.infoIcon[exception]}</UncontrolledTooltip><span>&nbsp;</span></React.Fragment> })}</p>
+              <font size="2"  >
+                <Table hover bordered><thead><tr><th>Subject</th><th>Predicate</th><th>Object</th><th>Exception(s)</th></tr></thead><tbody>
+                  {this.state.similarResourceTriple.map((triple, indexInside) => {
+                    if (triple.Exception.length > 0) {
+                      return <tr key={indexInside} style={{ 'backgroundColor': 'rgba(255,255,102,0.5)' }} ><td>{triple.Subject}</td><td>{triple.Predicate}</td><td>{triple.Object}</td><td>{triple.Exception.join(', ')}</td></tr>
+                    }
+                    return <tr key={indexInside}><td>{triple.Subject}</td><td>{triple.Predicate}</td><td>{triple.Object}</td><td></td></tr>
+                  })}
+                </tbody></Table>
+              </font>
+            </Col></Row></Card>
+          </ModalBody>
+          <ModalFooter>
+            <Button color="secondary" onClick={this.closeSimilarResourcePopup}>Ok</Button>
+          </ModalFooter>
+        </Modal>
+      </React.Fragment>
+    );
+
+    return (
+      <React.Fragment>
+        <Modal isOpen={true} className="modal-dialog-centered mw-100 w-100" >
+          <ModalHeader style={{ margin: 'auto' }}>Resource Details</ModalHeader>
+          <ModalBody>
+            <Nav tabs>
+              {navigationTabs}
+            </Nav>
+            <TabContent activeTab={this.state.activeTab}>
+              {displayPane}
+            </TabContent>
+          </ModalBody>
+          <ModalFooter>
+            <Button color="secondary" onClick={this.props.closeModel}>Ok</Button>
+          </ModalFooter>
+          {this.state.isloading ? <LoadingSpinner /> : null}
+          {this.state.showSimilarResourcePopup ? similarResourcePopup : null}
+        </Modal>
+      </React.Fragment>
+    );
+  }
+}
+
+
+const mapStateToProps = (state, ownProps) => {
+  let datasetID = Number(ownProps.datasetID);
+  let datasetDetails = state.datasetDetailsCache.filter((dataset) => { if (dataset.datasetID === datasetID) { return dataset; } return null; });
+  return (
+    {
+      datasetDetailsCache: datasetDetails
+    }
+  );
+}
+
+export default connect(mapStateToProps)(ResourceViewDetails);

+ 58 - 0
Luzzu Dashboard/src/views/DataSetDetails/AnalyticsWidget/MetricFilter.js

@@ -0,0 +1,58 @@
+//© 2019 Dublin City University, Trinity College Dublin. All rights reserved. This material may not be reproduced, displayed, modified or distributed without the express prior written permission of the copyright holder.
+import React, { Component } from 'react';
+import {
+  ListGroupItem, FormGroup, Input, Label, UncontrolledTooltip, Card, CardBody, CardText
+} from 'reactstrap';
+import { connect } from 'react-redux';
+
+class MetricFilter extends Component {
+
+  componentDidMount() {
+    //console.log(this.props);
+  }
+
+  render() {
+    let metrics = "";
+    metrics = (this.props.metrics.map((metric, index) => {
+      var toolTip = this.props.datasetDetailsCache[0].assessmentMetrics.find((metricDetails) => {
+        if (metricDetails.metric.split("#")[1] === metric) {
+          return metricDetails.comment;
+        }
+      });
+      //console.log(toolTip);
+      return <ListGroupItem key={index} className="border-0">
+        <FormGroup check inline>
+          <Input className="form-check-input" type="checkbox" id={metric} name={metric} value={metric} onChange={this.props.updateFilter} defaultChecked={false} />
+          <Label className="form-check-label" check htmlFor={metric}>{metric} <i className="fa fa-info-circle" id={metric + "_toolTipIcon"}></i><UncontrolledTooltip placement="right" target={metric + "_toolTipIcon"}>{toolTip.comment}</UncontrolledTooltip></Label>
+        </FormGroup>
+      </ListGroupItem>
+    }));
+    //console.log(metrics);
+    if (metrics.length === 0) {
+      //console.log("EMPTY");
+      metrics = (<Card className="w-100 text-center" style={{ border: "none" }} >
+        <CardBody>
+          <CardText>No Metrics to list. Please select a different view.</CardText>
+        </CardBody>
+      </Card>);
+    }
+    return (
+      <React.Fragment>
+        {metrics}
+      </React.Fragment>
+    );
+  }
+}
+
+
+const mapStateToProps = (state, ownProps) => {
+  let datasetID = Number(ownProps.datasetID);
+  let datasetDetails = state.datasetDetailsCache.filter((dataset) => { if (dataset.datasetID === datasetID) { return dataset; } return null; });
+  return (
+    {
+      datasetDetailsCache: datasetDetails
+    }
+  );
+}
+
+export default connect(mapStateToProps)(MetricFilter);

+ 407 - 0
Luzzu Dashboard/src/views/DataSetDetails/AnalyticsWidget/PerformAnalysis-Working.js

@@ -0,0 +1,407 @@
+//© 2019 Dublin City University, Trinity College Dublin. All rights reserved. This material may not be reproduced, displayed, modified or distributed without the express prior written permission of the copyright holder.
+import React, { Component } from 'react';
+import {
+  Button
+} from 'reactstrap';
+import { connect } from 'react-redux';
+import LoadingSpinner from '../loading';
+import LoadingFailed from '../loadingFailed';
+import { getKnowledgeBaseStatus, generateKnowledgeBase, getKnowledgeBase } from '../../../services/datasetConfigDetails/datasetConfigDetails';
+import { updateDataset, getStatus } from '../../../services/datasetConfigDetails/datasetConfigDetails';
+import { read } from '../../../services/tripleStoreAPIs/readFromTripleStore';
+import { statusUpdate } from '../../../services/tripleStoreAPIs/statusUpdate';
+import { checkDatasetExists } from '../../../services/tripleStoreAPIs/sparql/checkDatasetExists';
+import properties from '../../../config/dashboardProperties';
+import ProblemsModel from '../problemsModel'
+import AnalysisWidget from './AnalysisWidget'
+import mapping from '../../../config/metricKnowledgeBaseMapping';
+import axios from 'axios';
+import queryString from 'query-string';
+
+/*-------------config-------------*/
+const headers = { 'Accept': 'application/sparql-results+json', 'Content-Type': 'application/x-www-form-urlencoded' };
+/*-----------------------------------*/
+
+
+class PerformAnalysis extends Component {
+
+  constructor(props) {
+    super(props);
+    this.state = {
+      hasKnowledgeBase: false,
+      knowledgeBaseGenerationInProgress: false,
+      isloading: false,
+      loadFailed: false,
+      problemsNoticed: [],
+      knowledgeBaseCache:null
+    }
+  }
+
+  componentDidMount() {
+    this.initialize();
+  }
+
+  initialize = () => {
+    //console.log("Initialize");
+    //console.log(mapping.knowledgeBaseMapping);
+    if (this.props.datasetDetailsCache[0].knowledgeBaseID !== "" & this.props.datasetDetailsCache[0].knowledgeBaseID !== undefined) {
+      //console.log("knowledgeBaseID Present");
+      //Check in session cache
+      let knowledgeBaseCache = JSON.parse(sessionStorage.getItem(this.props.datasetDetailsCache[0].knowledgeBaseID));
+      if (knowledgeBaseCache === null) {
+        getKnowledgeBaseStatus(this.props.datasetDetailsCache[0].knowledgeBaseID).then((response) => {
+          //console.log("Got Status");
+          if (response) {
+            if (response.status === 200) {
+              if (response.data.status === "COMPLETED") {
+                //console.log("COMPLETED");
+                this.setState({
+                  isloading: true,
+                  hasKnowledgeBase: true,
+                  knowledgeBaseGenerationInProgress: false
+                }, async () => {
+                  await getKnowledgeBase(this.props.datasetDetailsCache[0].knowledgeBaseID).then((responseKB) => {
+                    //console.log("FETCHED");
+                    let knowledgeBaseFetched = responseKB.data.knowledgeBase;
+                    sessionStorage.setItem(this.props.datasetDetailsCache[0].knowledgeBaseID, JSON.stringify(knowledgeBaseFetched));
+                    this.setState({
+                      isloading: false
+                    });
+                    //console.log("STORED");
+                  });
+                });
+              }
+              else if (response.data.status === "FAILED") {
+                this.setState({
+                  hasKnowledgeBase: false,
+                  knowledgeBaseGenerationInProgress: false
+                });
+              }
+              else {
+                this.setState({
+                  hasKnowledgeBase: false,
+                  knowledgeBaseGenerationInProgress: true
+                });
+              }
+            }
+          }
+        });
+      }
+      else {
+        //console.log("Inside ELSE");
+        this.setState({
+          hasKnowledgeBase: true,
+          knowledgeBaseGenerationInProgress: false
+        });
+      }
+    }
+  }
+
+  checkTripleStoreIsReachable = () => {
+    let isReachable = false;
+
+    return new Promise((resolve) => {
+      this.setState({ isloading: true }, () => {
+        statusUpdate().then((response) => {
+          this.setState({ isloading: false }, () => {
+            if (response) {
+              if (response.status === 200) {
+                isReachable = true;
+                resolve(isReachable);
+              }
+              else {
+                isReachable = false;
+                resolve(isReachable);
+              }
+            }
+            else {
+              isReachable = false;
+              resolve(isReachable);
+            }
+          });
+        });
+      });
+    }
+    );
+  }
+
+  checkDataGraphExistsInTripleStore = () => {
+    let exists = false;
+    let hasMultiple = false;
+    let dataGraph = "";
+
+    return new Promise((resolve) => {
+      let dataGraphName = this.props.datasetDetailsCache[0].fileName.replace(/^.*[\\/]/, '').split('.').slice(0, -1).join('.');
+      dataGraph = "http://" + properties.tripleStore.host + ":" + properties.tripleStore.port + "/" + properties.tripleStore.datastore + "/data/" + dataGraphName;
+      this.setState({
+        isloading: true,
+        //loadFailed: false
+      }, () => {
+        read(checkDatasetExists(dataGraph)).then((response) => {
+          this.setState({
+            isloading: false
+          }, () => {
+            //console.log(response);
+            if (response) {
+              if (response.results.bindings.length === 1) {
+                exists = true;
+                resolve([exists, hasMultiple, dataGraph]);
+              }
+              else if (response.results.bindings.length > 1) {
+                exists = true;
+                hasMultiple = true;
+                resolve([exists, hasMultiple, dataGraph]);
+              }
+              else {
+                exists = false;
+                resolve([exists, hasMultiple, dataGraph]);
+              }
+            }
+            else {
+              resolve([exists, hasMultiple, dataGraph]);
+            }
+          });
+        });
+      });
+    });
+  }
+
+  checkSparqlEndPointIsReachable = () => {
+    let isReachable = false;
+    let query = "SELECT DISTINCT  (COUNT(?s) AS ?count) WHERE   {   { ?s  ?p  ?o }     UNION       { GRAPH ?g           { ?s  ?p  ?o }       }   }";
+    return new Promise((resolve) => {
+      //isReachable=true;
+      this.setState({
+        isloading: true,
+        //loadFailed: false
+      }, () => {
+
+        axios.post(this.props.datasetDetailsCache[0].sparqlEndPoint, queryString.stringify({ query: query }), headers)
+          .then((response) => {
+            this.setState({ isloading: false }, () => {
+              //console.log(response);
+              if (response.status === 200) {
+                isReachable = true;
+                resolve(isReachable);
+              }
+              else {
+                isReachable = false;
+                resolve(isReachable);
+              }
+            });
+          })
+          .catch((err) => {
+            this.setState({ isloading: false }, () => {
+              isReachable = false;
+              resolve(isReachable);
+            });
+          })
+      }
+      );
+    });
+  }
+
+  checkWrapperServiceIsReachable = () => {
+    let isReachable = false;
+
+    return new Promise((resolve) => {
+      this.setState({ isloading: true }, () => {
+        getStatus().then((response) => {
+          this.setState({ isloading: false }, () => {
+            //console.log(response);
+            if (response.status === 200) {
+              if (response.data.status === "OK") {
+                isReachable = true;
+                resolve(isReachable);
+              }
+              else {
+                isReachable = false;
+                resolve(isReachable);
+              }
+            }
+            else {
+              isReachable = false;
+              resolve(isReachable);
+            }
+          });
+        });
+      });
+    }
+    );
+  }
+
+  checkKnowledgeBaseDBIsReachable = () => {
+    let isReachable = false;
+
+    return new Promise((resolve, reject) => {
+      isReachable = true;
+      resolve(isReachable);
+    }
+    );
+  }
+
+
+  toggleAlertModel = () => {
+    this.setState({
+      loadFailed: false
+    });
+  }
+
+  triggerKBGeneration = async () => {
+    let problemsNoticedBeforeTriggering = [];
+    let qualityGraph = [...new Set(this.props.datasetDetailsCache[0].lastAssessmentMetrics.map((metric) => { if (metric.ProblemGraph !== undefined) { return metric.ProblemGraph.value } }))].filter((graph) => { return graph !== undefined });
+    let metrics = this.props.datasetDetailsCache[0].lastAssessmentMetrics.filter((metric) => { return metric.ProblemGraph !== undefined }).map((metric) => {
+
+      let valueToReturn = {
+        metric: metric.Metric.value.split("#")[1],
+        observationURI: metric.ObservationURI.value
+      };
+      return valueToReturn;
+    });
+    let dataGraphExists = false;
+    let hasMultipleDataGraph = false;
+    let dataGraph = "";
+    let wrapperServiceAvailable = false;
+    let knowledgeBaseDBAvailable = false;
+    let sparqlEndPointIsReachable = false;
+    let isSparqlEndPoint = false;
+    let tripleStoreIsReachable = false;
+
+    wrapperServiceAvailable = await this.checkWrapperServiceIsReachable();
+    knowledgeBaseDBAvailable = await this.checkKnowledgeBaseDBIsReachable();
+
+    if (!this.props.datasetDetailsCache[0].isSparqlEndPoint) {
+      //console.log("Not Sparql");
+      tripleStoreIsReachable = await this.checkTripleStoreIsReachable();
+      if (tripleStoreIsReachable) {
+        [dataGraphExists, hasMultipleDataGraph, dataGraph] = await this.checkDataGraphExistsInTripleStore();
+      }
+      //console.log(dataGraphExists);
+      //console.log(hasMultipleDataGraph);
+      //console.log(dataGraph);
+      isSparqlEndPoint = false;
+    }
+    else {
+      sparqlEndPointIsReachable = await this.checkSparqlEndPointIsReachable();
+      isSparqlEndPoint = true;
+      dataGraph = this.props.datasetDetailsCache[0].sparqlEndPoint;
+    }
+
+    //Create Problem List
+    if (!isSparqlEndPoint & !tripleStoreIsReachable) {
+      problemsNoticedBeforeTriggering.push({ TripleStoreNotReachable: "Host:" + properties.tripleStore.host + ", Port:" + properties.tripleStore.port + ", Dataset:" + properties.tripleStore.datastore });
+    }
+    if (!isSparqlEndPoint & !dataGraphExists & tripleStoreIsReachable) {
+      problemsNoticedBeforeTriggering.push({ DataSetGraphNotFound: dataGraph });
+    }
+    if (!isSparqlEndPoint & hasMultipleDataGraph & tripleStoreIsReachable) {
+      problemsNoticedBeforeTriggering.push({ MultipleGraphsFound: dataGraph });
+    }
+    if (!wrapperServiceAvailable) {
+      problemsNoticedBeforeTriggering.push({ WrapperServiceNotAvailable: "Host:" + properties.wrapperAPI.host + ", Port:" + properties.wrapperAPI.port });
+    }
+    if (!knowledgeBaseDBAvailable) {
+      problemsNoticedBeforeTriggering.push({ KnowledgeBaseDBNotAvailable: "Host:" + properties.knowledgeBase.host + ", Port:" + properties.knowledgeBase.port });
+    }
+    if (isSparqlEndPoint & !sparqlEndPointIsReachable) {
+      problemsNoticedBeforeTriggering.push({ SPARQLEndPointNotReachable: this.props.datasetDetailsCache[0].sparqlEndPoint });
+    }
+
+    if (problemsNoticedBeforeTriggering.length === 0) {
+      this.setState({
+        isloading: true,
+        loadFailed: false
+      }, () => {
+        generateKnowledgeBase(metrics, qualityGraph[0], dataGraph, this.props.datasetDetailsCache[0].dataSetLastAssessed, isSparqlEndPoint, mapping.knowledgeBaseMapping).then((response) => {
+          //console.log(response);
+          if (response) {
+            if (response.status === 201) {
+              let detailsToUpdate = {
+                datasetID: this.props.datasetDetailsCache[0].datasetID,
+                knowledgeBaseID: response.data.knowledgeBaseID
+              }
+
+              this.props.updateDatasetDetailsCache(detailsToUpdate);
+              this.setState({
+                knowledgeBaseGenerationInProgress: true
+
+              }, () => {
+                updateDataset({ "knowledgeBaseID": response.data.knowledgeBaseID }, this.props.datasetDetailsCache[0].datasetID).then((response) => {
+                  if (response.status === 200) {
+                    this.setState({
+                      isloading: false,
+                      //loadFailed: false
+                    });
+                  }
+                  else {
+                    this.setState({
+                      isloading: false,
+                      //loadFailed: true
+                    });
+                  }
+                });
+              });
+            }
+          }
+          else {
+            this.setState({
+              isloading: false,
+              //loadFailed: true
+            });
+          }
+        });
+      });
+    }
+    else {
+      //Has problems. Dont Continue. Show error popup.
+      this.setState({
+        problemsNoticed: problemsNoticedBeforeTriggering
+      });
+    }
+  }
+
+  problemModelClosed = () => {
+    this.setState({
+      problemsNoticed: []
+    });
+  }
+
+  render() {
+    //console.log(this.state);
+    //console.log(this.props);
+
+    //console.log("Render");
+    let displayGenerateKBButton = true;
+    if (this.props.datasetDetailsCache[0].dataSetLastAssessed === "Not Assessed Yet") {
+      displayGenerateKBButton = false;
+    }
+    return (
+      <React.Fragment>
+        {displayGenerateKBButton & !this.state.hasKnowledgeBase ? <Button block color="secondary" aria-pressed="true" disabled={this.state.knowledgeBaseGenerationInProgress} onClick={this.triggerKBGeneration}>Perform Analysis</Button> : null}
+        {this.state.isloading ? <LoadingSpinner /> : null}
+        {this.state.loadFailed ? <LoadingFailed clickHandler={this.toggleAlertModel} /> : null}
+        {this.state.problemsNoticed.length > 0 ? <ProblemsModel clickHandler={this.problemModelClosed} triggerProblems={this.state.problemsNoticed} /> : null}
+        {this.state.hasKnowledgeBase ? <AnalysisWidget knowledgeBaseID={this.props.datasetDetailsCache[0].knowledgeBaseID} datasetID={this.props.datasetDetailsCache[0].datasetID} /> : null}
+      </React.Fragment>
+    );
+  }
+}
+
+const mapStateToProps = (state, ownProps) => {
+  let datasetID = Number(ownProps.datasetID);
+  let datasetDetails = state.datasetDetailsCache.filter((dataset) => { if (dataset.datasetID === datasetID) { return dataset; } return null; });
+  return (
+    {
+      datasetDetailsCache: datasetDetails
+    }
+  );
+}
+
+
+const mapDispatchToProps = (dispatch) => {
+  return ({
+    updateDatasetDetailsCache: (datasetDetails) => { return dispatch({ type: 'UPDATE_DATASET_DETAILS_CACHE', payLoad: datasetDetails }); }
+  }
+  );
+}
+export default connect(mapStateToProps, mapDispatchToProps)(PerformAnalysis);

+ 480 - 0
Luzzu Dashboard/src/views/DataSetDetails/AnalyticsWidget/PerformAnalysis.js

@@ -0,0 +1,480 @@
+//© 2019 Dublin City University, Trinity College Dublin. All rights reserved. This material may not be reproduced, displayed, modified or distributed without the express prior written permission of the copyright holder.
+import React, { Component } from 'react';
+import {
+  Button, Progress
+} from 'reactstrap';
+import { connect } from 'react-redux';
+import LoadingSpinner from '../loading';
+import LoadingFailed from '../loadingFailed';
+import { getKnowledgeBaseStatus, generateKnowledgeBase, getKnowledgeBase } from '../../../services/datasetConfigDetails/datasetConfigDetails';
+import { updateDataset, getStatus } from '../../../services/datasetConfigDetails/datasetConfigDetails';
+import { read } from '../../../services/tripleStoreAPIs/readFromTripleStore';
+import { statusUpdate } from '../../../services/tripleStoreAPIs/statusUpdate';
+import { checkDatasetExists } from '../../../services/tripleStoreAPIs/sparql/checkDatasetExists';
+import properties from '../../../config/dashboardProperties';
+import ProblemsModel from '../problemsModel'
+import AnalysisWidget from './AnalysisWidget'
+import metricExceptionMapping from '../../../config/metricKnowledgeBaseMapping';
+import axios from 'axios';
+import queryString from 'query-string';
+
+/*-------------config-------------*/
+const headers = { 'Accept': 'application/sparql-results+json', 'Content-Type': 'application/x-www-form-urlencoded' };
+/*-----------------------------------*/
+
+
+class PerformAnalysis extends Component {
+
+  constructor(props) {
+    super(props);
+    this.state = {
+      hasKnowledgeBase: false,
+      knowledgeBaseGenerationInProgress: false,
+      isloading: false,
+      loadFailed: false,
+      problemsNoticed: [],
+      knowledgeBaseCache:null,
+      percentage:0
+    }
+  }
+
+  componentDidMount() {
+    this.initialize();
+  }
+
+  recall = 0;
+
+  performAnalysisStatusUpdate = async () => {
+    this.recall = setInterval(async () => {
+      //console.log("Call Status Update");
+      getKnowledgeBaseStatus(this.props.datasetDetailsCache[0].knowledgeBaseID).then((response) => {
+        //console.log("Got Status");
+        if (response) {
+          //console.log(response);
+          if (response.status === 200) {
+            if (response.data.status !== "INPROGRESS"  ) {
+              clearInterval(this.recall);
+              this.initialize();
+            }
+            else {
+              this.setState({
+                percentage: Number.parseFloat(response.data.percentage * 100).toFixed(2)
+              });
+            }
+
+          }
+          else {
+            //console.log("ERROR");
+            clearInterval(this.recall);
+            this.initialize();
+          }
+        }
+        else {
+          //console.log("ERROR");
+          clearInterval(this.recall);
+          this.initialize();
+        }
+
+      });
+
+
+    }, 5000);
+  }
+
+  componentWillUnmount() {
+    //console.log("Component Will  Unmount");
+    //console.log(this.recall);
+    clearInterval(this.recall);
+    //console.log(this.recall);
+  }
+
+  initialize = () => {
+    //console.log("Initialize");
+    //console.log(metricExceptionMapping.knowledgeBaseMapping);
+    if (this.props.datasetDetailsCache[0].knowledgeBaseID !== "" & this.props.datasetDetailsCache[0].knowledgeBaseID !== undefined) {
+      //console.log("knowledgeBaseID Present");
+      //Check in session cache
+      //let knowledgeBaseCache = JSON.parse(sessionStorage.getItem(this.props.datasetDetailsCache[0].knowledgeBaseID));
+      if (this.state.knowledgeBaseCache === null) {
+        getKnowledgeBaseStatus(this.props.datasetDetailsCache[0].knowledgeBaseID).then((response) => {
+          //console.log("Got Status");
+          if (response) {
+            if (response.status === 200) {
+              if (response.data.status === "COMPLETED") {
+                //console.log("COMPLETED");
+                clearInterval(this.recall);
+                this.setState({
+                  isloading: true
+                }, async () => {
+                  await getKnowledgeBase(this.props.datasetDetailsCache[0].knowledgeBaseID).then((responseKB) => {
+                    //console.log("FETCHED");
+                    let knowledgeBaseFetched = responseKB.data.knowledgeBase;
+                    //sessionStorage.setItem(this.props.datasetDetailsCache[0].knowledgeBaseID, JSON.stringify(knowledgeBaseFetched));
+                    this.setState({
+                      isloading: false,
+                      knowledgeBaseCache: knowledgeBaseFetched,
+                      hasKnowledgeBase: true,
+                      knowledgeBaseGenerationInProgress: false,
+                      percentage:0
+                    });
+                    //console.log("STORED");
+                  });
+                });
+              }
+              else if (response.data.status === "FAILED") {
+                clearInterval(this.recall);
+                this.setState({
+                  hasKnowledgeBase: false,
+                  knowledgeBaseGenerationInProgress: false,
+                  percentage:0
+                });
+              }
+              else {
+                //console.log(response);
+                this.performAnalysisStatusUpdate();
+                this.setState({
+                  hasKnowledgeBase: false,
+                  knowledgeBaseGenerationInProgress: true,
+                  percentage: Number.parseFloat(response.data.percentage * 100).toFixed(2)
+                });
+              }
+            }
+            else {
+              clearInterval(this.recall);
+              this.setState({
+                hasKnowledgeBase: false,
+                knowledgeBaseGenerationInProgress: false,
+                percentage:0
+              });
+            }
+          }
+          else {
+            clearInterval(this.recall);
+            this.setState({
+              hasKnowledgeBase: false,
+              knowledgeBaseGenerationInProgress: false,
+              percentage:0
+            });
+          }
+        });
+      }
+      else {
+        //console.log("Inside ELSE");
+        this.setState({
+          hasKnowledgeBase: true,
+          knowledgeBaseGenerationInProgress: false
+        });
+      }
+    }
+  }
+
+  checkTripleStoreIsReachable = () => {
+    let isReachable = false;
+
+    return new Promise((resolve) => {
+      this.setState({ isloading: true }, () => {
+        statusUpdate().then((response) => {
+          this.setState({ isloading: false }, () => {
+            if (response) {
+              if (response.status === 200) {
+                isReachable = true;
+                resolve(isReachable);
+              }
+              else {
+                isReachable = false;
+                resolve(isReachable);
+              }
+            }
+            else {
+              isReachable = false;
+              resolve(isReachable);
+            }
+          });
+        });
+      });
+    }
+    );
+  }
+
+  checkDataGraphExistsInTripleStore = () => {
+    let exists = false;
+    let hasMultiple = false;
+    let dataGraph = "";
+
+    return new Promise((resolve) => {
+      let dataGraphName = this.props.datasetDetailsCache[0].fileName.replace(/^.*[\\/]/, '').split('.').slice(0, -1).join('.');
+      dataGraph = "http://" + properties.tripleStore.host + ":" + properties.tripleStore.port + "/" + properties.tripleStore.datastore + "/data/" + dataGraphName;
+      this.setState({
+        isloading: true,
+        //loadFailed: false
+      }, () => {
+        read(checkDatasetExists(dataGraph)).then((response) => {
+          this.setState({
+            isloading: false
+          }, () => {
+            //console.log(response);
+            if (response) {
+              if (response.results.bindings.length === 1) {
+                exists = true;
+                resolve([exists, hasMultiple, dataGraph]);
+              }
+              else if (response.results.bindings.length > 1) {
+                exists = true;
+                hasMultiple = true;
+                resolve([exists, hasMultiple, dataGraph]);
+              }
+              else {
+                exists = false;
+                resolve([exists, hasMultiple, dataGraph]);
+              }
+            }
+            else {
+              resolve([exists, hasMultiple, dataGraph]);
+            }
+          });
+        });
+      });
+    });
+  }
+
+  checkSparqlEndPointIsReachable = () => {
+    let isReachable = false;
+    let query = "SELECT DISTINCT  (COUNT(?s) AS ?count) WHERE   {   { ?s  ?p  ?o }     UNION       { GRAPH ?g           { ?s  ?p  ?o }       }   }";
+    return new Promise((resolve) => {
+      //isReachable=true;
+      this.setState({
+        isloading: true,
+        //loadFailed: false
+      }, () => {
+
+        axios.post(this.props.datasetDetailsCache[0].sparqlEndPoint, queryString.stringify({ query: query }), headers)
+          .then((response) => {
+            this.setState({ isloading: false }, () => {
+              //console.log(response);
+              if (response.status === 200) {
+                isReachable = true;
+                resolve(isReachable);
+              }
+              else {
+                isReachable = false;
+                resolve(isReachable);
+              }
+            });
+          })
+          .catch((err) => {
+            this.setState({ isloading: false }, () => {
+              isReachable = false;
+              resolve(isReachable);
+            });
+          })
+      }
+      );
+    });
+  }
+
+  checkWrapperServiceIsReachable = () => {
+    let isReachable = false;
+
+    return new Promise((resolve) => {
+      this.setState({ isloading: true }, () => {
+        getStatus().then((response) => {
+          this.setState({ isloading: false }, () => {
+            //console.log(response);
+            if (response.status === 200) {
+              if (response.data.status === "OK") {
+                isReachable = true;
+                resolve(isReachable);
+              }
+              else {
+                isReachable = false;
+                resolve(isReachable);
+              }
+            }
+            else {
+              isReachable = false;
+              resolve(isReachable);
+            }
+          });
+        });
+      });
+    }
+    );
+  }
+
+  checkKnowledgeBaseDBIsReachable = () => {
+    let isReachable = false;
+
+    return new Promise((resolve, reject) => {
+      isReachable = true;
+      resolve(isReachable);
+    }
+    );
+  }
+
+
+  toggleAlertModel = () => {
+    this.setState({
+      loadFailed: false
+    });
+  }
+
+  triggerKBGeneration = async () => {
+    let problemsNoticedBeforeTriggering = [];
+    let qualityGraph = [...new Set(this.props.datasetDetailsCache[0].lastAssessmentMetrics.map((metric) => { if (metric.ProblemGraph !== undefined) { return metric.ProblemGraph.value } }))].filter((graph) => { return graph !== undefined });
+    let metrics = this.props.datasetDetailsCache[0].lastAssessmentMetrics.filter((metric) => { return metric.ProblemGraph !== undefined }).map((metric) => {
+
+      let valueToReturn = {
+        metric: metric.Metric.value.split("#")[1],
+        observationURI: metric.ObservationURI.value
+      };
+      return valueToReturn;
+    });
+    let dataGraphExists = false;
+    let hasMultipleDataGraph = false;
+    let dataGraph = "";
+    let wrapperServiceAvailable = false;
+    let knowledgeBaseDBAvailable = false;
+    let sparqlEndPointIsReachable = false;
+    let isSparqlEndPoint = false;
+    let tripleStoreIsReachable = false;
+
+    wrapperServiceAvailable = await this.checkWrapperServiceIsReachable();
+    knowledgeBaseDBAvailable = await this.checkKnowledgeBaseDBIsReachable();
+
+    if (!this.props.datasetDetailsCache[0].isSparqlEndPoint) {
+      //console.log("Not Sparql");
+      tripleStoreIsReachable = await this.checkTripleStoreIsReachable();
+      if (tripleStoreIsReachable) {
+        [dataGraphExists, hasMultipleDataGraph, dataGraph] = await this.checkDataGraphExistsInTripleStore();
+      }
+      //console.log(dataGraphExists);
+      //console.log(hasMultipleDataGraph);
+      //console.log(dataGraph);
+      isSparqlEndPoint = false;
+    }
+    else {
+      sparqlEndPointIsReachable = await this.checkSparqlEndPointIsReachable();
+      isSparqlEndPoint = true;
+      dataGraph = this.props.datasetDetailsCache[0].sparqlEndPoint;
+    }
+
+    //Create Problem List
+    if (!isSparqlEndPoint & !tripleStoreIsReachable) {
+      problemsNoticedBeforeTriggering.push({ TripleStoreNotReachable: "Host:" + properties.tripleStore.host + ", Port:" + properties.tripleStore.port + ", Dataset:" + properties.tripleStore.datastore });
+    }
+    if (!isSparqlEndPoint & !dataGraphExists & tripleStoreIsReachable) {
+      problemsNoticedBeforeTriggering.push({ DataSetGraphNotFound: dataGraph });
+    }
+    if (!isSparqlEndPoint & hasMultipleDataGraph & tripleStoreIsReachable) {
+      problemsNoticedBeforeTriggering.push({ MultipleGraphsFound: dataGraph });
+    }
+    if (!wrapperServiceAvailable) {
+      problemsNoticedBeforeTriggering.push({ WrapperServiceNotAvailable: "Host:" + properties.wrapperAPI.host + ", Port:" + properties.wrapperAPI.port });
+    }
+    if (!knowledgeBaseDBAvailable) {
+      problemsNoticedBeforeTriggering.push({ KnowledgeBaseDBNotAvailable: "Host:" + properties.knowledgeBase.host + ", Port:" + properties.knowledgeBase.port });
+    }
+    if (isSparqlEndPoint & !sparqlEndPointIsReachable) {
+      problemsNoticedBeforeTriggering.push({ SPARQLEndPointNotReachable: this.props.datasetDetailsCache[0].sparqlEndPoint });
+    }
+
+    if (problemsNoticedBeforeTriggering.length === 0) {
+      this.setState({
+        isloading: true,
+        loadFailed: false
+      }, () => {
+        generateKnowledgeBase(metrics, qualityGraph[0], dataGraph, this.props.datasetDetailsCache[0].dataSetLastAssessed, isSparqlEndPoint, metricExceptionMapping.knowledgeBaseMapping).then((response) => {
+          //console.log(response);
+          if (response) {
+            if (response.status === 201) {
+              let detailsToUpdate = {
+                datasetID: this.props.datasetDetailsCache[0].datasetID,
+                knowledgeBaseID: response.data.knowledgeBaseID
+              }
+
+              this.props.updateDatasetDetailsCache(detailsToUpdate);
+              this.performAnalysisStatusUpdate();
+              this.setState({
+                knowledgeBaseGenerationInProgress: true
+
+              }, () => {
+                updateDataset({ "knowledgeBaseID": response.data.knowledgeBaseID }, this.props.datasetDetailsCache[0].datasetID).then((response) => {
+                  if (response.status === 200) {
+                    this.setState({
+                      isloading: false,
+                      //loadFailed: false
+                    });
+                  }
+                  else {
+                    this.setState({
+                      isloading: false,
+                      //loadFailed: true
+                    });
+                  }
+                });
+              });
+            }
+          }
+          else {
+            this.setState({
+              isloading: false,
+              //loadFailed: true
+            });
+          }
+        });
+      });
+    }
+    else {
+      //Has problems. Dont Continue. Show error popup.
+      this.setState({
+        problemsNoticed: problemsNoticedBeforeTriggering
+      });
+    }
+  }
+
+  problemModelClosed = () => {
+    this.setState({
+      problemsNoticed: []
+    });
+  }
+
+  render() {
+    //console.log(this.state);
+    //console.log(this.props);
+
+    //console.log("Render");
+    let displayGenerateKBButton = true;
+    if (this.props.datasetDetailsCache[0].dataSetLastAssessed === "Not Assessed Yet") {
+      displayGenerateKBButton = false;
+    }
+    return (
+      <React.Fragment>
+        {displayGenerateKBButton & !this.state.hasKnowledgeBase ? <Button block color="secondary" aria-pressed="true" disabled={this.state.knowledgeBaseGenerationInProgress} onClick={this.triggerKBGeneration}>Perform Analysis</Button> : null}
+        {displayGenerateKBButton & !this.state.hasKnowledgeBase & this.state.knowledgeBaseGenerationInProgress? <Progress className="progress-xs" color="info" style={{ 'height': '20px' }} value={this.state.percentage} ><small className="justify-content-center d-flex position-absolute w-100"><font size="2" color="black">Completed : {this.state.percentage}%</font></small></Progress> : null}
+        {this.state.isloading ? <LoadingSpinner /> : null}
+        {this.state.loadFailed ? <LoadingFailed clickHandler={this.toggleAlertModel} /> : null}
+        {this.state.problemsNoticed.length > 0 ? <ProblemsModel clickHandler={this.problemModelClosed} triggerProblems={this.state.problemsNoticed} /> : null}
+        {this.state.hasKnowledgeBase ? <AnalysisWidget knowledgeBaseID={this.props.datasetDetailsCache[0].knowledgeBaseID} datasetID={this.props.datasetDetailsCache[0].datasetID} knowledgeBaseCache={this.state.knowledgeBaseCache}/> : null}
+      </React.Fragment>
+    );
+  }
+}
+
+const mapStateToProps = (state, ownProps) => {
+  let datasetID = Number(ownProps.datasetID);
+  let datasetDetails = state.datasetDetailsCache.filter((dataset) => { if (dataset.datasetID === datasetID) { return dataset; } return null; });
+  return (
+    {
+      datasetDetailsCache: datasetDetails
+    }
+  );
+}
+
+
+const mapDispatchToProps = (dispatch) => {
+  return ({
+    updateDatasetDetailsCache: (datasetDetails) => { return dispatch({ type: 'UPDATE_DATASET_DETAILS_CACHE', payLoad: datasetDetails }); }
+  }
+  );
+}
+export default connect(mapStateToProps, mapDispatchToProps)(PerformAnalysis);

+ 50 - 0
Luzzu Dashboard/src/views/DataSetDetails/AnalyticsWidget/ProblematicThingList.js

@@ -0,0 +1,50 @@
+//© 2019 Dublin City University, Trinity College Dublin. All rights reserved. This material may not be reproduced, displayed, modified or distributed without the express prior written permission of the copyright holder.
+import React, { Component } from 'react';
+import {
+  ListGroupItem
+} from 'reactstrap';
+
+class ProblematicThingList extends Component {
+  constructor(props) {
+    //console.log("constructor");
+    super(props);
+    this.state = {
+    }
+    //console.log(this.props);
+  }
+
+  componentDidMount() {
+    //console.log("componentDidMount");
+  }
+
+  render() {
+    //console.log("Render");
+    //console.log(this.state);
+    let returnList = null;
+    returnList = this.props.problematicThingList.map((problematicThing, index) => {
+      let problematicThingKey = Object.keys(problematicThing);
+      return <ListGroupItem key={index} className="list-group-item list-group-item-action flex-column align-items-start" >
+        <h5 className="mb-1">Problematic Thing:</h5>
+        <div className="d-flex w-100 justify-content-between">
+          <h6 className="mb-1">{problematicThingKey[0]}</h6>
+        </div>
+        <p className="mb-1"></p>
+        <p style={{ fontSize: "14px" }}>{problematicThing[problematicThingKey].effectedResources.length} Resource(s) are impacted by this Problematic Thing.</p>
+        <p style={{ fontSize: "14px" }}>{problematicThing[problematicThingKey].exception.length} Exception(s) : {problematicThing[problematicThingKey].exception.join(', ')} </p>
+        <div className="d-flex w-100 justify-content-between">
+          <p className="mb-1">
+            {problematicThing[problematicThingKey].linkedProblematicThings.length > 0 ? <small>Also has '{problematicThing[problematicThingKey].linkedProblematicThings.length}' linked Problematic Things.</small> : null}
+          </p>
+          <button style={{ color: "#069", fontSize: "16px", textDecoration: "underline", background:"none",padding:"0", border:"none", cursor:"ponter" }} onClick={() => this.props.showDetails(index)}>Click here for more details..</button>
+        </div>
+      </ListGroupItem>
+    });
+    return (
+      <React.Fragment>
+        {returnList}
+      </React.Fragment>
+    );
+  }
+}
+
+export default ProblematicThingList;

+ 50 - 0
Luzzu Dashboard/src/views/DataSetDetails/AnalyticsWidget/ResourceList.js

@@ -0,0 +1,50 @@
+//© 2019 Dublin City University, Trinity College Dublin. All rights reserved. This material may not be reproduced, displayed, modified or distributed without the express prior written permission of the copyright holder.
+import React, { Component } from 'react';
+import {
+  ListGroupItem
+} from 'reactstrap';
+
+class ResourceList extends Component {
+  constructor(props) {
+    //console.log("constructor");
+    super(props);
+    this.state = {
+    }
+    //console.log(this.props);
+  }
+
+  componentDidMount() {
+    //console.log("componentDidMount");
+  }
+
+  render() {
+    //console.log("Render");
+    //console.log(this.state);
+    let returnList = null;
+    returnList = this.props.resourceList.map((resource, index) => {
+      let resourceKey = Object.keys(resource);
+      return <ListGroupItem key={index} className="list-group-item list-group-item-action flex-column align-items-start" >
+        <h5 className="mb-1">Resource:</h5>
+        <div className="d-flex w-100 justify-content-between">
+          <h6 className="mb-1">{resourceKey[0]}</h6>
+        </div>
+        <p className="mb-1"></p>
+        <p style={{ fontSize: "14px" }}>{resource[resourceKey].exception.length} Exception(s) Related to this Resource : {resource[resourceKey].exception.join(', ')}</p>
+        <div className="d-flex w-100 justify-content-between">
+          <p className="mb-1">
+            {resource[resourceKey].resourceReplica.length > 0 ? <small>Also has '{resource[resourceKey].resourceReplica.length}' Resource Replica </small> : null}
+            {resource[resourceKey].disjointClass.length > 0 ? <small>and '{resource[resourceKey].disjointClass.length}' Disjoint Classes.</small> : null}
+          </p>
+          <button style={{ color: "#069", fontSize: "16px", textDecoration: "underline", background:"none",padding:"0", border:"none", cursor:"ponter" }} onClick={() => this.props.showDetails(index)}>Click here for more details..</button>
+        </div>
+      </ListGroupItem>
+    });
+    return (
+      <React.Fragment>
+        {returnList}
+      </React.Fragment>
+    );
+  }
+}
+
+export default ResourceList;

+ 131 - 0
Luzzu Dashboard/src/views/DataSetDetails/AnalyticsWidget/SummaryReport.js

@@ -0,0 +1,131 @@
+//© 2019 Dublin City University, Trinity College Dublin. All rights reserved. This material may not be reproduced, displayed, modified or distributed without the express prior written permission of the copyright holder.
+import React, { Component } from 'react';
+import {
+  Modal,
+  ModalHeader,
+  ModalBody,
+  ModalFooter,
+  Button,
+  Row,
+  Col,
+  Card,
+  CardBody,
+  CardText
+} from 'reactstrap';
+import LoadingSpinner from '../loading';
+import LoadingFailed from '../loadingFailed';
+import properties from '../../../config/dashboardProperties';
+import { connect } from 'react-redux';
+import { read } from '../../../services/tripleStoreAPIs/readFromTripleStore';
+import { fetchDistinctResourceCount } from '../../../services/tripleStoreAPIs/sparql/fetchDistinctResourceCount';
+
+
+class SummaryReport extends Component {
+
+  constructor(props) {
+    super(props);
+    this.state = {
+      isloading: false,
+      loadFailed: false,
+      totalNumberOfResources: 0
+    }
+  }
+
+  componentDidMount() {
+    let dataGraph = "";
+    let graphType = "";
+    if (this.props.datasetDetailsCache[0].isSparqlEndPoint) {
+      dataGraph = this.props.datasetDetailsCache[0].sparqlEndPoint;
+      graphType = "SERVICE";
+    }
+    else {
+      let dataGraphName = this.props.datasetDetailsCache[0].fileName.replace(/^.*[\\/]/, '').split('.').slice(0, -1).join('.');
+      dataGraph = "http://" + properties.tripleStore.host + ":" + properties.tripleStore.port + "/" + properties.tripleStore.datastore + "/data/" + dataGraphName;
+      graphType = "GRAPH";
+    }
+
+    this.setState({
+      isloading: true,
+      //loadFailed: false
+    }, () => {
+      read(fetchDistinctResourceCount(graphType,dataGraph)).then((response) => {
+        this.setState({
+          isloading: false
+        }, () => {
+          if (response) {
+            if (response.results.bindings.length > 0) {
+              this.setState({
+                totalNumberOfResources: response.results.bindings[0].ResourceCount.value
+              });
+            }
+            else {
+
+            }
+          }
+          else {
+
+          }
+        });
+      });
+    });
+  }
+
+
+  render() {
+    //console.log(this.state);
+    let listOfMetricsToDisplay = null;
+    listOfMetricsToDisplay = this.props.listOfMetrics.map((metric, index) => {
+      let metricKey = Object.keys(metric);
+      return <CardText key={index} className="text-center"><p className="text-center font-weight-normal" >{metricKey[0]} : {metric[metricKey[0]].length}</p></CardText>
+    });
+
+    let countOfTotalResource = null;
+    countOfTotalResource =(
+      <CardBody className="text-center">
+        <p className="text-center font-weight-bold" ><u>Total number of Resource(s) in assessed dataset : {this.state.totalNumberOfResources}</u></p>
+      </CardBody>
+    );
+
+
+    return (
+      <Modal isOpen={true} className="modal-dialog-centered modal-md">
+        <ModalHeader style={{ margin: 'auto' }}>Analysis Summary</ModalHeader>
+        <ModalBody>
+          <Row>
+            <Col xs="12">
+              <Row>
+                <Card className="w-100 ">
+                {this.state.totalNumberOfResources>0? countOfTotalResource :null}
+                  <p className="text-center font-weight-bold" ><u>Number of Resource(s) impacted by each Metric(s)</u></p>
+                  <CardBody className="text-center">
+                    {listOfMetricsToDisplay}
+                  </CardBody>
+                </Card>
+              </Row>
+            </Col>
+          </Row>
+        </ModalBody>
+        {this.state.isloading ? <LoadingSpinner /> : null}
+        <ModalFooter>
+          <Button color="secondary" onClick={this.props.onCloseButtonClick}>Close</Button>
+        </ModalFooter>
+      </Modal>
+
+    );
+  }
+
+}
+
+
+
+const mapStateToProps = (state, ownProps) => {
+  let datasetID = Number(ownProps.datasetID);
+  let datasetDetails = state.datasetDetailsCache.filter((dataset) => { if (dataset.datasetID === datasetID) { return dataset; } return null; });
+  return (
+    {
+      datasetDetailsCache: datasetDetails
+    }
+  );
+}
+
+export default connect(mapStateToProps)(SummaryReport);

+ 423 - 0
Luzzu Dashboard/src/views/DataSetDetails/DataQualityOverTime.js

@@ -0,0 +1,423 @@
+//© 2019 Dublin City University, Trinity College Dublin. All rights reserved. This material may not be reproduced, displayed, modified or distributed without the express prior written permission of the copyright holder.
+import React, { Component} from 'react';
+import { Bar } from 'react-chartjs-2';
+import {
+  Card,
+  CardBody,
+  CardHeader,
+  ButtonDropdown,
+  ButtonGroup,
+  DropdownItem,
+  DropdownMenu,
+  DropdownToggle,
+  Media,
+  Modal,
+  ModalHeader,
+  ModalBody,
+  ModalFooter,
+  Button,
+  FormGroup,
+  Form,
+  Label,
+  Input,
+} from 'reactstrap';
+import { connect } from 'react-redux';
+import { CustomTooltips } from '@coreui/coreui-plugin-chartjs-custom-tooltips';
+import { getAllAssessmentDates } from '../../services/tripleStoreAPIs/sparql/getAllAssessmentDates';
+import { getAssessmentQuality } from '../../services/tripleStoreAPIs/sparql/getAssessmentQuality';
+import { read } from '../../services/tripleStoreAPIs/readFromTripleStore';
+import LoadingSpinner from './loading';
+import LoadingFailed from './loadingFailed';
+
+
+const options = {
+  tooltips: {
+    enabled: false,
+    custom: CustomTooltips
+  },
+  scales: {
+    xAxes: [{
+        barThickness: 40,
+        scaleLabel: {
+        display: true,
+        labelString: "Date/Time"
+
+      }
+    }],
+    yAxes:[
+      {
+        ticks :{
+          max: 100,
+          min: 0,
+          stepSize: 20
+        },
+        scaleLabel: {
+        display: true,
+        labelString: "Data Quality"
+
+      }
+      }
+    ]
+},
+legend: {
+    display: false
+},
+
+  maintainAspectRatio: false
+}
+
+class DataQualityOverTime extends Component{
+
+  constructor(props) {
+    super(props);
+    this.state = {
+      assessmentDates:[],
+      assessmentQualities:[],
+      qualityStatus :[],
+      isloading : false,
+      loadFailed :  false,
+      dropdownOpen: false,
+      popupOpen: false,
+      tempLimit: sessionStorage.getItem("LIMIT")
+    };
+
+
+  }
+
+  togglePopup = () => {
+      this.setState(prevState => ({
+          popupOpen: !prevState.popupOpen
+      }));
+  }
+
+  onChangeLimit = (event) =>
+  {
+    this.setState(
+        { tempLimit: event.target.value }
+    );
+  }
+
+  saveLimit = () =>
+  {
+    //console.log(this.state.tempLimit);
+    sessionStorage.setItem("LIMIT",this.state.tempLimit );
+    this.togglePopup();
+  }
+
+componentDidMount(){
+
+  //console.log("componentDidMount");
+  //console.log(this.props)
+  var linkedDataset = this.props.datasetDetailsCache[0]
+  if(this.props.is1Spatial){
+    var sessionStore = JSON.parse(sessionStorage.getItem("PIPELINECACHE"));
+    var index = 0
+    sessionStore.forEach((store, index)=>{
+      if(this.props.datasetID === store.datasetID)
+        index = index
+    })
+    console.log(index)
+    linkedDataset = sessionStore[index]
+  }
+
+  this.setState({is1Spatial: this.props.is1Spatial})
+  if (this.props.is1Spatial || this.props.datasetDetailsCache.length>0) {
+    this.setState({ isloading: true }, () => {
+      this.setup(linkedDataset)
+      .then(()=>{
+        let assessmentDate =[];
+        let assessmentQuality = [];
+        let assessmentColor =[];
+        if(typeof linkedDataset.historicAssessmentData!=="undefined")
+        {
+          if(linkedDataset.historicAssessmentData.length>0)
+          {
+              assessmentDate = linkedDataset.historicAssessmentData.map((result,index) => {
+                return ({index: index, result:Object.keys(result)[0]});
+              });
+          }
+        }
+        assessmentDate.forEach((date)=>{
+          //console.log(date.result);
+            let quality=0;
+            let status="rgba(255,99,132,0.2)";
+            let tempTotal=[];
+            var cache = linkedDataset.historicAssessmentData[date.index][date.result]
+            tempTotal = cache.map((metricDetails) => {
+              let metricValueType = (metricDetails.Value.datatype).split("#")[1]
+              let metricValue = 0.0;
+              if (metricValueType === "boolean") {
+                let metricValueTemp = metricDetails.Value.value
+                if (metricValueTemp === "true") {
+                  metricValue = 1.0;
+                }
+              }
+              else {
+                metricValue = Number.parseFloat(metricDetails.Value.value).toFixed(2)
+              }
+              tempTotal.push(
+                {
+                  /* metric: (metricDetails.Metric.value).split("#")[1], */
+                  metric: metricDetails.Metric.value,
+                  value: metricValue
+                }
+              );
+                if(!this.state.is1Spatial)
+                  return (linkedDataset.assessmentMetrics.filter((metric) => { if (metric.assess === true & metric.metric === metricDetails.Metric.value) { return true; } return false }).map((metric) => {if (metricValueType === "boolean") { if(metricValue===metric.target){return parseFloat(1.0, 10);}else{return parseFloat(0.0, 10);}}else{return parseFloat(metricValue, 10);}}));
+                else{
+                  if(metricValue != -Infinity){
+                    return parseFloat(metricValue, 10)
+                  }
+                  else{
+                    return 0
+                  }
+                }
+            });
+            if (tempTotal.length > 0) {
+              let filteredArray = tempTotal.flat();
+              if(filteredArray.length>1)
+              {
+                quality = filteredArray.reduce((previous, current) => current += previous) / filteredArray.length;
+              }
+              else {
+                quality = filteredArray[0];
+              }
+              if (quality >= linkedDataset.expectedProgress) {
+                status="rgba(0,255,0,0.2)";
+              }
+              quality = Number(parseFloat(quality).toFixed(4))*100;
+            }
+
+
+            assessmentQuality.push(
+              {
+                index : date.index,
+                quality : Number.parseFloat(quality).toFixed(2)
+              }
+            );
+            assessmentColor.push(
+              {
+                index : date.index,
+                status : status
+              }
+            );
+
+          });
+          this.setState(
+            {
+              assessmentDates : assessmentDate,
+              assessmentQualities : assessmentQuality,
+              qualityStatus : assessmentColor,
+              isloading: false
+            }
+          );
+      });
+    });
+
+}
+}
+
+ setup = async (props) =>
+{
+
+  return;
+}
+
+
+fetchHistoricDateFromCache = (historicAssessmentDataFromCache)=>
+{
+  return new Promise((resolve)=>{
+      let cachedHistoricDates = [];
+      if (typeof historicAssessmentDataFromCache!=="undefined")
+      {
+        historicAssessmentDataFromCache.forEach((assessmentData)=>{
+          cachedHistoricDates.push(Object.keys(assessmentData));
+        });
+      }
+      //console.log(cachedHistoricDates.flat());
+      resolve(cachedHistoricDates.flat());
+
+  });
+}
+
+fetchHistoricDateFromTripleStore = (datasetPLD) =>
+{
+    return new Promise((resolve)=>{
+      let historicDates = [];
+      read(getAllAssessmentDates(datasetPLD))
+        .then((response) => {
+          //console.log(response);
+          if (response) {
+            if (response.results.bindings.length > 0) {
+              historicDates = response.results.bindings.map((result,index) => {
+                return ({result:result.TimePeriod.value});
+              });
+            }
+          }
+          //console.log(historicDates);
+          resolve(historicDates);
+      });
+
+    });
+}
+
+refreshCacheWithHistoricAssessmentData = (cachedHistoricDates, date, datasetID,datasetPLD) =>
+{
+    return new Promise((resolve)=>{
+
+        //console.log(cachedHistoricDates.indexOf(date.result));
+        if(cachedHistoricDates.indexOf(date.result) >= 0)
+        {
+          //console.log("Yes");
+          resolve();
+        }
+        else
+        {
+          //console.log("No");
+          read(getAssessmentQuality(datasetPLD,date.result)).then((response) => {
+
+              let assessmentResult = {
+                [date.result] : [...response.results.bindings]
+              }
+              //console.log(assessmentResult);
+              this.props.updateHistoricAssessmentDetailsToCache(assessmentResult,datasetID);
+              resolve();
+            }
+          );
+        }
+    });
+}
+
+
+
+toggleAlertModel = () => {
+    this.setState({
+        loadFailed: false
+    });
+}
+
+toggle = () => {
+  this.setState({
+    dropdownOpen: !this.state.dropdownOpen,
+  });
+}
+
+
+render ()
+{
+  //console.log(this.props.datasetDetailsCache[0]);
+  console.log(this.state.assessmentDates)
+  let assessmentCount = this.state.assessmentDates.length;
+  var dateTime = ''
+  var dates = ''
+  var time = ''
+  if(this.state.is1Spatial){
+     dateTime = this.state.assessmentDates.map((date)=>{return((date.result));});
+     dates = dateTime.map((date)=>{return((date).split(" ")[0]);});
+     time = dateTime.map((date)=>{return((date).split(" ")[1]);});
+  }else{
+     dateTime = this.state.assessmentDates.map((date)=>{return((date.result).split(".")[0]);});
+     dates = dateTime.map((date)=>{return((date).split("T")[0]);});
+     time = dateTime.map((date)=>{return((date).split("T")[1]);});
+  }
+  let backgroundColour = this.state.qualityStatus.sort((a, b) => parseInt(a.index) - parseInt(b.index));
+  let assessData = this.state.assessmentQualities.sort((a, b) => parseInt(a.index) - parseInt(b.index));
+  if(assessmentCount>sessionStorage.getItem("LIMIT"))
+  {
+    let startIndex=assessmentCount-sessionStorage.getItem("LIMIT");
+    dateTime=dateTime.slice(startIndex);
+    backgroundColour=backgroundColour.slice(startIndex);
+    assessData=assessData.slice(startIndex);
+  }
+  let mergedDateTime=[];
+  let size = dateTime.length;
+  let index=0;
+  for (index=0; index<size;index++)
+  {
+    mergedDateTime.push([dates[index],time[index]]);
+  }
+
+  const bar = {
+    labels: [...mergedDateTime],
+    datasets: [
+      {
+        label: this.state.datasetPLD,
+        backgroundColor: backgroundColour.map((status)=> {return(status.status)}),
+
+        data:  assessData.map((data)=>{return(data.quality)}) ,
+      },
+    ],
+
+  };
+
+  let model = (<Modal isOpen={true} className="modal-dialog-centered">
+                  <ModalHeader style={{ margin: 'auto' }}>Configure Settings</ModalHeader>
+                  <ModalBody>
+                      <Form>
+                          <FormGroup>
+                              <Label>Dataset Name:</Label>
+                               <Input type="text" name="limitInput" id="limitInput" ref="limitInput" value={this.state.tempLimit} onChange={this.onChangeLimit} />
+                          </FormGroup>
+                          </Form>
+                      </ModalBody>
+                      <ModalFooter>
+                          <Button color="secondary" onClick={this.togglePopup}>Close</Button>
+                          <Button color="primary" onClick={this.saveLimit}>Save</Button>{' '}
+                      </ModalFooter>
+                  </Modal>
+                        );
+
+return(
+  <Card className="w-75">
+  {this.state.popupOpen?model:null}
+    <CardHeader>
+      Data Quality over Time
+      <ButtonGroup className="float-right">
+        <ButtonDropdown id='card1' isOpen={this.state.dropdownOpen} toggle={this.toggle}>
+          <DropdownToggle caret className="p-0" color="black">
+            <Media className="icon-settings"></Media>
+          </DropdownToggle>
+          <DropdownMenu right>
+            <DropdownItem onClick={this.togglePopup}>Config</DropdownItem>
+          </DropdownMenu>
+        </ButtonDropdown>
+      </ButtonGroup>
+    </CardHeader>
+    <CardBody>
+
+    {this.state.isloading ? <LoadingSpinner /> : null}
+    {this.state.loadFailed ? <LoadingFailed clickHandler={this.toggleAlertModel} /> : null}
+        <Bar data={bar} options={options} />
+    </CardBody>
+  </Card>
+);
+
+}
+
+}
+
+
+const mapStateToProps = (state,ownProps) => {
+
+  let datasetID = ownProps.datasetID;
+  let datasetDetails = null
+  if(ownProps.is1Spatial && state.pipelineCache){
+    datasetDetails = state.pipelineCache.filter((dataset)=>{if(dataset.datasetID===datasetID){return dataset;} return null; });
+  }else{
+    datasetDetails = state.datasetDetailsCache.filter((dataset)=>{if(dataset.datasetID===datasetID){return dataset;} return null; });
+  }
+
+    return (
+        {
+            datasetDetailsCache: datasetDetails
+        }
+    );
+}
+
+const mapDispatchToProps = (dispatch) => {
+    return ({
+        updateHistoricAssessmentDetailsToCache: (historicAssessmentData,datasetID) => { return dispatch({ type: 'UPDATE_HISTORIC_ASSESSMENT_DETAILS_CACHE', payLoad: {historicAssessmentData:historicAssessmentData, datasetID:datasetID} }); }
+    }
+    );
+}
+export default connect(mapStateToProps, mapDispatchToProps)(DataQualityOverTime);

+ 135 - 0
Luzzu Dashboard/src/views/DataSetDetails/DataSetDetails.js

@@ -0,0 +1,135 @@
+//© 2019 Dublin City University, Trinity College Dublin. All rights reserved. This material may not be reproduced, displayed, modified or distributed without the express prior written permission of the copyright holder.
+import React, { Component } from 'react';
+import {
+  Card,
+  CardBody,
+  CardHeader,
+  Row,
+  CardSubtitle
+} from 'reactstrap';
+import { connect } from 'react-redux';
+import { Redirect } from 'react-router-dom';
+import DataQualityOverTime from './DataQualityOverTime';
+import DimensionsQualityDetails from './DimensionsQualityDetails';
+import PerformAnalysis from './AnalyticsWidget/PerformAnalysis';
+import CircularProgressbar from 'react-circular-progressbar';
+import 'react-circular-progressbar/dist/styles.css';
+const noteReadyToPublish = "rgba(255,0,0,0.6)";
+const readyToPublish = "rgba(34,139,34,0.8)";
+
+
+class DataSetAssessmentDetails extends Component {
+
+  constructor(props) {
+    super(props);
+    this.state = {
+        analysisDropdownOpen: false,
+        radioSelected: 2,
+      }
+  }
+
+  analysisToggle = () => {
+    this.setState({
+      analysisDropdownOpen: !this.state.analysisDropdownOpen,
+    });
+  }
+
+  loading = () => <div className="animated fadeIn pt-1 text-center">Loading...</div>
+
+  render() {
+    let datasetDetails = this.props.datasetDetailsCache;
+    let percentageValue = 0;
+    let display = null;
+    if(datasetDetails.length>0){
+      percentageValue = Number.parseFloat(datasetDetails[0].lastAssessmentQuality * 100).toFixed(2);
+      let canPublish = "Not Ready to Publish";
+      let displayColour = noteReadyToPublish;
+      let canPublishClass = "text-left font-weight-bold";
+
+      if (datasetDetails[0].lastAssessmentQuality >= datasetDetails[0].expectedProgress) {
+        canPublish = "Ready to Publish";
+        displayColour = readyToPublish;
+        canPublishClass = "text-left font-weight-bold";
+      }
+
+      display = (    <div className="animated fadeIn">
+      <Row >
+      <Card className="w-100" >
+        <CardHeader>
+        <span className="h3">{datasetDetails[0].datasetName}</span>
+
+        </CardHeader>
+        </Card>
+      </Row>
+
+            <Row >
+              <Card className="w-25" >
+                <CardHeader>
+                  Current Data Quality
+                </CardHeader>
+                <CardBody className="pb-0">
+                  <CardSubtitle className={canPublishClass} style={{ color: displayColour }}>{canPublish}</CardSubtitle>
+                  <p/>
+                  <div style={{ width: '150px', margin: 'auto' }}>
+                    <CircularProgressbar percentage={percentageValue} text={`${percentageValue}%`} styles={{ text: { fill: displayColour }, path: { stroke: displayColour } }} />
+                  </div>
+                  <div style={{ height: '20px' }} />
+                </CardBody>
+              </Card>
+              <DataQualityOverTime datasetID={datasetDetails[0].datasetID} is1Spatial={false} />
+            </Row>
+
+            <Row>
+              <Card className="w-100">
+                <CardHeader>
+                  Dimensions Measured
+                  </CardHeader>
+                <CardBody>
+                  <Row>
+                    <DimensionsQualityDetails lastAssessmentMetrics={datasetDetails[0].lastAssessmentMetrics} assessmentMetrics={datasetDetails[0].assessmentMetrics}/>
+
+                  </Row>
+                </CardBody>
+              </Card>
+            </Row>
+
+            <Row>
+              <Card className="w-100">
+                <CardHeader>
+                  Analysis
+                  </CardHeader>
+                <CardBody>
+
+                  <PerformAnalysis datasetID={this.props.match.params.datasetID}/>
+
+
+                </CardBody>
+              </Card>
+            </Row>
+          </div>);
+    }
+    else {
+      display =(<Redirect to="/dashboard" />);
+    }
+
+
+    return (
+      <React.Fragment>{display}</React.Fragment>
+
+    );
+  }
+}
+
+
+const mapStateToProps = (state,ownProps) => {
+  let datasetID = Number(ownProps.match.params.datasetID);
+  let datasetDetails = state.datasetDetailsCache.filter((dataset)=>{if(dataset.datasetID===datasetID){return dataset;} return null; });
+    return (
+        {
+            datasetDetailsCache: datasetDetails
+        }
+    );
+}
+
+
+export default connect(mapStateToProps)(DataSetAssessmentDetails);

+ 282 - 0
Luzzu Dashboard/src/views/DataSetDetails/DimensionDetails/DimensionDetails.js

@@ -0,0 +1,282 @@
+//© 2019 Dublin City University, Trinity College Dublin. All rights reserved. This material may not be reproduced, displayed, modified or distributed without the express prior written permission of the copyright holder.
+import React, { Component } from 'react';
+import {
+  Card,
+  CardBody,
+  CardHeader,
+  Row,
+  CardSubtitle,
+  Col,
+  Progress,
+  Container
+} from 'reactstrap';
+import { connect } from 'react-redux';
+import { Redirect } from 'react-router-dom';
+import CircularProgressbar from 'react-circular-progressbar';
+import 'react-circular-progressbar/dist/styles.css';
+import { Link } from 'react-router-dom';
+import ErrorReport from './ErrorReport';
+import SummaryReport from './SummaryReport';
+const noteReadyToPublish = "rgba(255,0,0,0.6)";
+const readyToPublish = "rgba(34,139,34,0.8)";
+
+
+class DimensionDetails extends Component {
+
+  constructor(props) {
+    super(props);
+    this.state = {
+      dimensionActual: 0,
+      dimensionExpected: 0,
+      dimension: this.props.match.params.dimension,
+      metricValues: [],
+      showErrorPopup: false,
+      showErrorReportFor: null,
+      showSummaryPopup: false,
+      showSummaryReportFor: null
+    }
+  }
+
+  componentDidMount() {
+    if (this.props.datasetDetailsCache.length > 0) {
+      this.calculateDimensionActualAndExpected(this.state.dimension);
+    }
+  }
+
+  calculateDimensionActualAndExpected = (dimension) => {
+
+    let tempArray = [];
+    tempArray = this.props.datasetDetailsCache[0].assessmentMetrics.map((metric) => {
+      return (this.props.datasetDetailsCache[0].lastAssessmentMetrics.map((assessed) => {
+        if (metric.metric === assessed.Metric.value & metric.assess & assessed.Dimension.value.split("#")[1] === dimension) {
+          let assessValue = 0;
+          let problemStrucure = "";
+          let qualityProblem = "";
+          if (assessed.Value.datatype.split("#")[1] === "boolean") {
+            if (assessed.Value.value === "true") {
+              assessValue = 1.0;
+            }
+          }
+          else {
+            assessValue = Number(parseFloat(assessed.Value.value).toFixed(4));
+          }
+
+          if (assessed.QualityProblem !== undefined) {
+            qualityProblem = assessed.QualityProblem.value;
+          }
+
+          if (assessed.ProblemStructure !== undefined) {
+            problemStrucure = assessed.ProblemStructure.value;
+          }
+
+          return ({
+            metric: assessed.Metric.value, observationURI: assessed.ObservationURI.value, actual: assessValue, target: metric.target, label: metric.label, comment: metric.comment, type: (assessed.Value.datatype).split("#")[1],
+            qualityProblem: qualityProblem, problemStructure: problemStrucure
+          });
+        }
+      }));
+    });
+
+    this.setState(
+      {
+        metricValues: [...tempArray.flat().filter(item => item)]
+      }, () => {
+        let tempActual = 0.0;
+        let tempExpected = 0.0;
+        this.state.metricValues.forEach((values) => {
+          if (values.type === "boolean") {
+            tempExpected += 1;
+            if (Number(parseFloat(values.actual).toFixed(4)) === Number(parseFloat(values.target).toFixed(4))) {
+              tempActual += 1;
+            }
+            else {
+              tempActual += 0;
+            }
+          }
+          else {
+            tempActual += Number(parseFloat(values.actual).toFixed(4));
+            tempExpected += Number(parseFloat(values.target).toFixed(4));
+          }
+        });
+
+        if (this.state.metricValues.length > 0) {
+          tempActual = tempActual / this.state.metricValues.length;
+          tempExpected = tempExpected / this.state.metricValues.length;
+        }
+
+        this.setState({
+          dimensionActual: tempActual,
+          dimensionExpected: tempExpected,
+        });
+      }
+    );
+  }
+
+  showErrorReport = (metricURI) => {
+    this.setState(
+      {
+        showErrorPopup: true,
+        showErrorReportFor: metricURI
+      }
+    );
+  }
+
+  closeErrorReport = () => {
+    this.setState(
+      {
+        showErrorPopup: false,
+        showErrorReportFor: null
+      }
+    );
+  }
+
+
+    showErrorSummary = (metricURI) => {
+      this.setState(
+        {
+          showSummaryPopup: true,
+          showSummaryReportFor: metricURI
+        }
+      );
+    }
+
+    closeErrorSummary = () => {
+      this.setState(
+        {
+          showSummaryPopup: false,
+          showSummaryReportFor: null
+        }
+      );
+    }
+
+  render() {
+    console.log(this.props)
+    if ((this.props.location.hash !== "" & this.state.showErrorPopup === false) | (this.props.location.hash !== "" & this.state.showSummaryPopup === false))
+    {
+      return <Redirect push to={this.props.location.pathname} />;
+    }
+    let canPublish = "Not Ready to Publish";
+    let displayColour = noteReadyToPublish;
+    let canPublishClass = "text-left font-weight-bold";
+    let percentageValue = (this.state.dimensionActual * 100).toFixed(2);
+    if (this.state.dimensionActual >= this.state.dimensionExpected)
+    {
+      canPublish = "Ready to Publish";
+      displayColour = readyToPublish;
+      canPublishClass = "text-left font-weight-bold";
+    }
+
+    let metrics = null;
+    metrics = (
+      this.state.metricValues.map((metricDetails, index) =>
+      {
+        let actual = 0;
+        let expected = 0;
+        let actualLabel = "";
+        let expectedLabel = "";
+        if (metricDetails.type === "boolean")
+        {
+          expected = 100;
+          expectedLabel = "True";
+          if (metricDetails.target === 0)
+          {
+            expectedLabel = "False";
+          }
+          actual = 0;
+          actualLabel = "False";
+          if (metricDetails.actual === 1)
+          {
+            actualLabel = "True";
+          }
+          if (metricDetails.actual === metricDetails.target)
+          {
+            actual = 100;
+          }
+        }
+        else {
+          actual = Number(parseFloat(metricDetails.actual).toFixed(4)) * 100;
+          actualLabel = "Observed : " + actual.toString() + "%";
+          expected = Number(parseFloat(metricDetails.target).toFixed(4)) * 100;
+          expectedLabel = "Threshold : " + expected.toString() + "%"
+        }
+
+        let color = "danger";
+        if (actual >= expected) {
+          color = "success";
+        }
+        return <Card className="w-100" key={index}><CardHeader><b>{metricDetails.label}</b></CardHeader><CardBody><Col>
+          <Container ><Card ><CardBody><div className="progress-group-bars"><Progress className="progress-xs" color="info" style={{ 'height': '20px' }} value={expected} ><small className="justify-content-center d-flex position-absolute w-100"><font size="2" color="black">{expectedLabel}</font></small></Progress><Progress className="progress-xs" style={{ 'height': '20px' }} color={color} value={actual}><small className="justify-content-center d-flex position-absolute w-100"><font size="2" color="black">{actualLabel}</font></small></Progress></div></CardBody></Card>
+          </Container>
+        </Col>
+          {metricDetails.qualityProblem !== "" ? <Row className="justify-content-md-center"><Col className="col-md-auto"><b><Link to={{ pathname: this.props.location.pathname, hash: "#" + metricDetails.metric.split("#")[1] }} onClick={() => this.showErrorReport(metricDetails.metric)}>SHOW PROBLEM REPORT</Link></b></Col><Col className="col-md-auto"><b><Link to={{ pathname: this.props.location.pathname, hash: "#" + metricDetails.metric.split("#")[1] }} onClick={() => this.showErrorSummary(metricDetails.metric)}>SHOW PROBLEM SUMMARY</Link></b></Col></Row> : null}
+        </CardBody></Card>
+      })
+    );
+
+    let display = null;
+    if (this.props.datasetDetailsCache.length > 0) {
+      display = (<div className="animated fadeIn">
+        <Row >
+          <Card className="w-100" >
+            <CardHeader>
+              <span className="h3">{this.props.datasetDetailsCache[0].datasetName} / {this.state.dimension}</span>
+            </CardHeader>
+          </Card>
+        </Row>
+        <Row >
+          <Col className="col-3" >
+            <Card >
+              <CardHeader>
+                {this.state.dimension} Dimension
+            </CardHeader>
+              <CardBody className="pb-0">
+                <CardSubtitle className={canPublishClass} style={{ color: displayColour }}>{canPublish}</CardSubtitle>
+                <p />
+                <div style={{ width: '150px', margin: 'auto' }}>
+                  <CircularProgressbar percentage={percentageValue} text={`${percentageValue}%`} styles={{ text: { fill: displayColour }, path: { stroke: displayColour } }} />
+                </div>
+                <div style={{ height: '20px' }} />
+              </CardBody>
+            </Card>
+          </Col>
+          <Col className="col-9">
+            <Card >
+              <CardHeader>
+                Metrics Measured
+            </CardHeader>
+              <CardBody>
+                <Row>
+                  {metrics}
+                  {this.state.showErrorPopup ? <ErrorReport datasetID={this.props.match.params.datasetID} metricURI={this.state.showErrorReportFor} onCloseButtonClick={this.closeErrorReport} /> : null}
+                  {this.state.showSummaryPopup ? <SummaryReport datasetID={this.props.match.params.datasetID} metricURI={this.state.showSummaryReportFor} onCloseButtonClick={this.closeErrorSummary} /> : null}
+                </Row>
+              </CardBody>
+            </Card>
+          </Col>
+        </Row>
+      </div>
+      );
+    }
+    else {
+      display = (<Redirect to="/dashboard" />);
+    }
+
+    return (
+      <React.Fragment>
+        {display}
+      </React.Fragment>
+    );
+  }
+}
+
+const mapStateToProps = (state, ownProps) => {
+  let datasetID = Number(ownProps.match.params.datasetID);
+  let datasetDetails = state.datasetDetailsCache.filter((dataset) => { if (dataset.datasetID === datasetID) { return dataset; } return null; });
+  return (
+    {
+      datasetDetailsCache: datasetDetails
+    }
+  );
+}
+
+export default connect(mapStateToProps)(DimensionDetails);

+ 329 - 0
Luzzu Dashboard/src/views/DataSetDetails/DimensionDetails/ErrorReport-Working.js

@@ -0,0 +1,329 @@
+//© 2019 Dublin City University, Trinity College Dublin. All rights reserved. This material may not be reproduced, displayed, modified or distributed without the express prior written permission of the copyright holder.
+import React, { Component } from 'react';
+import {
+  Modal,
+  ModalHeader,
+  ModalBody,
+  ModalFooter,
+  Button,
+  Row,
+  Col,
+  ListGroup,
+  TabContent,
+  Card,
+  CardBody
+} from 'reactstrap';
+import { connect } from 'react-redux';
+import { read } from '../../../services/tripleStoreAPIs/readFromTripleStore';
+import { getErrorReportForModelDefault } from '../../../services/tripleStoreAPIs/sparql/problemReport/getErrorReportForModelDefault';
+import { getErrorReportForModelProblematicTriple } from '../../../services/tripleStoreAPIs/sparql/problemReport/getErrorReportForModelProblematicTriple';
+import { getErrorReportForResource } from '../../../services/tripleStoreAPIs/sparql/problemReport/getErrorReportForResource';
+import { getErrorReportForQuad } from '../../../services/tripleStoreAPIs/sparql/problemReport/getErrorReportForQuad';
+import ListOfProblems from './ListOfProblems';
+import ResourcesFailedForProblem from './ResourcesFailedForProblem';
+import LoadingSpinner from '../loading';
+import LoadingFailed from '../loadingFailed';
+import metricExceptionMapping from '../../../config/metricKnowledgeBaseMapping';
+
+
+class ErrorReport extends Component {
+
+  constructor(props) {
+    super(props);
+    this.state = {
+      isloading: false,
+      loadFailed: false,
+      totalNumberOfTriplesWithProblem: 0,
+      listOfExceptionsWithCount: [],
+      listOfProblematicThings: [],
+      listOfRelatedFailedMetrics:[],
+      activeTab: 0
+    }
+  }
+
+  toggleActiveTab = (tab) => {
+    if (this.state.activeTab !== tab) {
+      this.setState({
+        activeTab: tab,
+        listOfRelatedFailedMetrics:[]
+      });
+    }
+  }
+
+  componentDidMount() {
+    let indexOfMetric = this.props.datasetDetailsCache[0].lastAssessmentMetrics.findIndex((assessment) => { return assessment.Metric.value === this.props.metricURI });
+    if (this.props.datasetDetailsCache[0].lastAssessmentMetrics[indexOfMetric].errorReport === undefined) {
+      this.setState({ isloading: true }, () => {
+        this.prepareErrorReportCache(indexOfMetric, this.props.datasetDetailsCache[0].lastAssessmentMetrics[indexOfMetric].ProblemStructure.value.split("#")[1], this.props.datasetDetailsCache[0].lastAssessmentMetrics[indexOfMetric].Metric.value.split("#")[1]).then(() => {
+          this.setState(
+            {
+              isloading: false
+            }, () => {
+              if (this.props.datasetDetailsCache[0].lastAssessmentMetrics[indexOfMetric].errorReport !== undefined) {
+                this.prepareUI(indexOfMetric);
+              }
+            }
+          );
+        })
+      });
+    }
+    else {
+      if (this.props.datasetDetailsCache[0].lastAssessmentMetrics[indexOfMetric].errorReport !== undefined) {
+        this.prepareUI(indexOfMetric);
+      }
+    }
+  }
+
+  prepareUI = async (indexOfMetric) => {
+    let totalNumberOfTriplesWithProblem = await this.prepareTotalNumberOfTriplesWithProblem(indexOfMetric);
+    let listOfExceptionsWithCount = await this.prepareListOfExceptionsWithCount(indexOfMetric);
+    let listOfProblematicThings = await this.prepareListOfProblematicThings(indexOfMetric);
+    this.setState({
+      totalNumberOfTriplesWithProblem: totalNumberOfTriplesWithProblem,
+      listOfExceptionsWithCount: listOfExceptionsWithCount,
+      listOfProblematicThings: listOfProblematicThings
+    });
+  }
+
+  prepareTotalNumberOfTriplesWithProblem = (indexOfMetric) => {
+    let exceptions = Object.keys(this.props.datasetDetailsCache[0].lastAssessmentMetrics[indexOfMetric].errorReport);
+    let count = 0;
+
+    for (let exception of exceptions) {
+      count += this.props.datasetDetailsCache[0].lastAssessmentMetrics[indexOfMetric].errorReport[exception].count;
+    }
+    return count;
+  }
+
+  prepareListOfExceptionsWithCount = (indexOfMetric) => {
+    let exceptions = Object.keys(this.props.datasetDetailsCache[0].lastAssessmentMetrics[indexOfMetric].errorReport);
+    let listOfExceptions = [];
+    for (let exception of exceptions) {
+      listOfExceptions.push({
+        [exception]: this.props.datasetDetailsCache[0].lastAssessmentMetrics[indexOfMetric].errorReport[exception].count
+      });
+    }
+    return listOfExceptions;
+  }
+
+  prepareListOfProblematicThings = (indexOfMetric) => {
+    let exceptions = Object.keys(this.props.datasetDetailsCache[0].lastAssessmentMetrics[indexOfMetric].errorReport);
+    let listOfProblematicThings = [];
+    for (let exception of exceptions) {
+      let problematicThings = Object.keys(this.props.datasetDetailsCache[0].lastAssessmentMetrics[indexOfMetric].errorReport[exception].ProblematicThing);
+      for (let problematicThing of problematicThings) {
+        listOfProblematicThings.push({
+          [problematicThing]: [(this.props.datasetDetailsCache[0].lastAssessmentMetrics[indexOfMetric].errorReport[exception].ProblematicThing[problematicThing].filter(resource => resource !== "")).length,exception]
+        });
+      }
+    }
+    return listOfProblematicThings;
+  }
+
+  prepareErrorReportCache = async (index, problemStrucure, metricURI) => {
+    let errorReport = null;
+
+    if (problemStrucure === "ModelContainer") {
+      let functionToCall = getErrorReportForModelDefault;
+      let listOfExceptions = "";
+      let listOfHasExceptions = "";
+      switch (metricURI) {
+        case "UsageOfIncorrectDomainOrRangeDatatypesMetric":
+          functionToCall = getErrorReportForModelProblematicTriple;
+          break;
+        default:
+          functionToCall = getErrorReportForModelDefault;
+          if(metricExceptionMapping.knowledgeBaseMapping[metricURI]!==undefined)
+          {
+            let exceptions = Object.keys(metricExceptionMapping.knowledgeBaseMapping[metricURI]).map((exception)=> "dqm-prob:"+exception);
+            listOfExceptions = exceptions.join(", ") ;
+            let hasExceptions = Object.keys(metricExceptionMapping.knowledgeBaseMapping[metricURI]).map((exception)=> "dqm-prob:has"+exception);
+            listOfHasExceptions = hasExceptions.join(", ");
+          }
+
+      }
+
+      if(listOfExceptions==="")
+      {
+        errorReport = await read(functionToCall(this.props.datasetDetailsCache[0].lastAssessmentMetrics[index].ProblemGraph.value, this.props.datasetDetailsCache[0].lastAssessmentMetrics[index].ObservationURI.value)).then((response) => {
+          if (response) {
+            if (response.results.bindings.length > 0) {
+              return response;
+            }
+          }
+          return null;
+        });
+      }
+      else
+      {
+        errorReport = await read(functionToCall(this.props.datasetDetailsCache[0].lastAssessmentMetrics[index].ProblemGraph.value, this.props.datasetDetailsCache[0].lastAssessmentMetrics[index].ObservationURI.value, listOfExceptions, listOfHasExceptions)).then((response) => {
+          if (response) {
+            if (response.results.bindings.length > 0) {
+              return response;
+            }
+          }
+          return null;
+        });
+      }
+
+    }
+    else if (problemStrucure === "ResourceContainer") {
+
+      errorReport = await read(getErrorReportForResource(this.props.datasetDetailsCache[0].lastAssessmentMetrics[index].ProblemGraph.value, this.props.datasetDetailsCache[0].lastAssessmentMetrics[index].ObservationURI.value)).then((response) => {
+        if (response) {
+          if (response.results.bindings.length > 0) {
+            return response;
+          }
+        }
+        return null;
+      });
+    }
+    else if (problemStrucure === "QuadContainer") {
+
+      errorReport = await read(getErrorReportForQuad(this.props.datasetDetailsCache[0].lastAssessmentMetrics[index].ProblemGraph.value, this.props.datasetDetailsCache[0].lastAssessmentMetrics[index].ObservationURI.value)).then((response) => {
+        if (response) {
+          if (response.results.bindings.length > 0) {
+            return response;
+          }
+        }
+        return null;
+      });
+    }
+
+    if (errorReport !== null) {
+      let cache = {};
+      for (let error of errorReport.results.bindings) {
+        let exception = error.Exception.value;
+        let problematicThing = "";
+        if (error.ProblematicThing !== undefined) {
+          problematicThing = error.ProblematicThing.value;
+        }
+
+        let problematicThingResource = "";
+        if (error.ProblematicThingResource !== undefined) {
+          problematicThingResource = error.ProblematicThingResource.value;
+        }
+
+        //Exception Key doesn't exists
+        if (!(exception in cache)) {
+          cache[exception] = {
+            count: 1,
+            ProblematicThing: {
+              [problematicThing]: [problematicThingResource]
+            }
+          };
+        }
+        else {
+          cache[exception].count += 1;
+          //Exception Key exists but problematicThing doesn't exists
+          if (!(problematicThing in cache[exception].ProblematicThing)) {
+            cache[exception].ProblematicThing = {
+              ...cache[exception].ProblematicThing,
+              [problematicThing]: [problematicThingResource]
+            };
+          }
+          else {
+            cache[exception].ProblematicThing[problematicThing].push(problematicThingResource);
+          }
+        }
+      }
+      await this.props.updateErrorReportCache(cache, Number(this.props.datasetID), index);
+    }
+
+    return;
+  }
+
+  listRelatedProblems=(listOfRelatedMetrics)=>{
+    this.setState({
+      listOfRelatedFailedMetrics:listOfRelatedMetrics
+    });
+  }
+
+  render() {
+    //console.log(this.state);
+    let listOfRelatedFailedMetricsToDisplay=null;
+    if(this.state.listOfRelatedFailedMetrics.length>0)
+    {
+      if(this.state.listOfRelatedFailedMetrics.length===1 & this.state.listOfRelatedFailedMetrics[0]==="")
+      {
+        //Nothing to do
+      }
+      else {
+        listOfRelatedFailedMetricsToDisplay= this.state.listOfRelatedFailedMetrics.map((metric, index) => {
+          if(metric!=="")
+          {
+            return <li key={index}>{metric}</li>
+          }
+
+        });
+      }
+    }
+    return (
+      <Modal isOpen={true} className="modal-dialog-centered modal-lg">
+
+        <ModalHeader style={{ margin: 'auto' }}>Problem Report</ModalHeader>
+        <ModalBody>
+          <Row>
+            <Col xs="4">
+
+            <Card className="w-100" style={{overflow: 'scroll', height:'43rem'}}>
+<p className="text-center font-weight-bold">List of Problematic Instances</p>
+              <ListGroup id="problemList" role="tablist" className="flex-column align-items-start">
+                <ListOfProblems toggle={this.toggleActiveTab} activeTab={this.state.activeTab} listOfProblems={this.state.listOfProblematicThings} />
+              </ListGroup>
+              </Card>
+
+            </Col>
+            <Col xs="8">
+
+              <Row>
+
+                <Card className="w-100">
+                <p className="text-center font-weight-bold">Problematic Instance Details</p>
+                    <TabContent activeTab={this.state.activeTab}>
+                      <ResourcesFailedForProblem listOfProblems={this.state.listOfProblematicThings} datasetID={this.props.datasetDetailsCache[0].datasetID} metricURI={this.props.metricURI} activeTab={this.state.activeTab} callBack={this.listRelatedProblems}/>
+                    </TabContent>
+
+                </Card>
+              </Row>
+              <Row>
+                <Card className="w-100" style={{overflow: 'scroll', height:'10rem'}}>
+                  <p className="text-center font-weight-bold">All Metric(s) failed for the selected Resource</p>
+                  <CardBody>
+                  {listOfRelatedFailedMetricsToDisplay}
+                  </CardBody>
+                </Card>
+              </Row>
+            </Col>
+          </Row>
+        </ModalBody>
+        {this.state.isloading ? <LoadingSpinner /> : null}
+        <ModalFooter>
+          <Button color="secondary" onClick={this.props.onCloseButtonClick}>Close</Button>
+        </ModalFooter>
+      </Modal>
+
+    );
+  }
+
+}
+
+
+
+const mapStateToProps = (state, ownProps) => {
+  let datasetID = Number(ownProps.datasetID);
+  let datasetDetails = state.datasetDetailsCache.filter((dataset) => { if (dataset.datasetID === datasetID) { return dataset; } return null; });
+  return (
+    {
+      datasetDetailsCache: datasetDetails
+    }
+  );
+}
+
+const mapDispatchToProps = (dispatch) => {
+  return ({
+    updateErrorReportCache: (errorReport, datasetID, metricIndex) => { return dispatch({ type: 'UPDATE_METRIC_ERROR_REPORT_CACHE', payLoad: { errorReport: errorReport, datasetID: datasetID, metricIndex: metricIndex } }); }
+  }
+  );
+}
+export default connect(mapStateToProps, mapDispatchToProps)(ErrorReport);

部分文件因为文件数量过多而无法显示