DataQualityOverTime.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423
  1. //© 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.
  2. import React, { Component} from 'react';
  3. import { Bar } from 'react-chartjs-2';
  4. import {
  5. Card,
  6. CardBody,
  7. CardHeader,
  8. ButtonDropdown,
  9. ButtonGroup,
  10. DropdownItem,
  11. DropdownMenu,
  12. DropdownToggle,
  13. Media,
  14. Modal,
  15. ModalHeader,
  16. ModalBody,
  17. ModalFooter,
  18. Button,
  19. FormGroup,
  20. Form,
  21. Label,
  22. Input,
  23. } from 'reactstrap';
  24. import { connect } from 'react-redux';
  25. import { CustomTooltips } from '@coreui/coreui-plugin-chartjs-custom-tooltips';
  26. import { getAllAssessmentDates } from '../../services/tripleStoreAPIs/sparql/getAllAssessmentDates';
  27. import { getAssessmentQuality } from '../../services/tripleStoreAPIs/sparql/getAssessmentQuality';
  28. import { read } from '../../services/tripleStoreAPIs/readFromTripleStore';
  29. import LoadingSpinner from './loading';
  30. import LoadingFailed from './loadingFailed';
  31. const options = {
  32. tooltips: {
  33. enabled: false,
  34. custom: CustomTooltips
  35. },
  36. scales: {
  37. xAxes: [{
  38. barThickness: 40,
  39. scaleLabel: {
  40. display: true,
  41. labelString: "Date/Time"
  42. }
  43. }],
  44. yAxes:[
  45. {
  46. ticks :{
  47. max: 100,
  48. min: 0,
  49. stepSize: 20
  50. },
  51. scaleLabel: {
  52. display: true,
  53. labelString: "Data Quality"
  54. }
  55. }
  56. ]
  57. },
  58. legend: {
  59. display: false
  60. },
  61. maintainAspectRatio: false
  62. }
  63. class DataQualityOverTime extends Component{
  64. constructor(props) {
  65. super(props);
  66. this.state = {
  67. assessmentDates:[],
  68. assessmentQualities:[],
  69. qualityStatus :[],
  70. isloading : false,
  71. loadFailed : false,
  72. dropdownOpen: false,
  73. popupOpen: false,
  74. tempLimit: sessionStorage.getItem("LIMIT")
  75. };
  76. }
  77. togglePopup = () => {
  78. this.setState(prevState => ({
  79. popupOpen: !prevState.popupOpen
  80. }));
  81. }
  82. onChangeLimit = (event) =>
  83. {
  84. this.setState(
  85. { tempLimit: event.target.value }
  86. );
  87. }
  88. saveLimit = () =>
  89. {
  90. //console.log(this.state.tempLimit);
  91. sessionStorage.setItem("LIMIT",this.state.tempLimit );
  92. this.togglePopup();
  93. }
  94. componentDidMount(){
  95. //console.log("componentDidMount");
  96. //console.log(this.props)
  97. var linkedDataset = this.props.datasetDetailsCache[0]
  98. if(this.props.is1Spatial){
  99. var sessionStore = JSON.parse(sessionStorage.getItem("PIPELINECACHE"));
  100. var index = 0
  101. sessionStore.forEach((store, index)=>{
  102. if(this.props.datasetID === store.datasetID)
  103. index = index
  104. })
  105. console.log(index)
  106. linkedDataset = sessionStore[index]
  107. }
  108. this.setState({is1Spatial: this.props.is1Spatial})
  109. if (this.props.is1Spatial || this.props.datasetDetailsCache.length>0) {
  110. this.setState({ isloading: true }, () => {
  111. this.setup(linkedDataset)
  112. .then(()=>{
  113. let assessmentDate =[];
  114. let assessmentQuality = [];
  115. let assessmentColor =[];
  116. if(typeof linkedDataset.historicAssessmentData!=="undefined")
  117. {
  118. if(linkedDataset.historicAssessmentData.length>0)
  119. {
  120. assessmentDate = linkedDataset.historicAssessmentData.map((result,index) => {
  121. return ({index: index, result:Object.keys(result)[0]});
  122. });
  123. }
  124. }
  125. assessmentDate.forEach((date)=>{
  126. //console.log(date.result);
  127. let quality=0;
  128. let status="rgba(255,99,132,0.2)";
  129. let tempTotal=[];
  130. var cache = linkedDataset.historicAssessmentData[date.index][date.result]
  131. tempTotal = cache.map((metricDetails) => {
  132. let metricValueType = (metricDetails.Value.datatype).split("#")[1]
  133. let metricValue = 0.0;
  134. if (metricValueType === "boolean") {
  135. let metricValueTemp = metricDetails.Value.value
  136. if (metricValueTemp === "true") {
  137. metricValue = 1.0;
  138. }
  139. }
  140. else {
  141. metricValue = Number.parseFloat(metricDetails.Value.value).toFixed(2)
  142. }
  143. tempTotal.push(
  144. {
  145. /* metric: (metricDetails.Metric.value).split("#")[1], */
  146. metric: metricDetails.Metric.value,
  147. value: metricValue
  148. }
  149. );
  150. if(!this.state.is1Spatial)
  151. 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);}}));
  152. else{
  153. if(metricValue != -Infinity){
  154. return parseFloat(metricValue, 10)
  155. }
  156. else{
  157. return 0
  158. }
  159. }
  160. });
  161. if (tempTotal.length > 0) {
  162. let filteredArray = tempTotal.flat();
  163. if(filteredArray.length>1)
  164. {
  165. quality = filteredArray.reduce((previous, current) => current += previous) / filteredArray.length;
  166. }
  167. else {
  168. quality = filteredArray[0];
  169. }
  170. if (quality >= linkedDataset.expectedProgress) {
  171. status="rgba(0,255,0,0.2)";
  172. }
  173. quality = Number(parseFloat(quality).toFixed(4))*100;
  174. }
  175. assessmentQuality.push(
  176. {
  177. index : date.index,
  178. quality : Number.parseFloat(quality).toFixed(2)
  179. }
  180. );
  181. assessmentColor.push(
  182. {
  183. index : date.index,
  184. status : status
  185. }
  186. );
  187. });
  188. this.setState(
  189. {
  190. assessmentDates : assessmentDate,
  191. assessmentQualities : assessmentQuality,
  192. qualityStatus : assessmentColor,
  193. isloading: false
  194. }
  195. );
  196. });
  197. });
  198. }
  199. }
  200. setup = async (props) =>
  201. {
  202. return;
  203. }
  204. fetchHistoricDateFromCache = (historicAssessmentDataFromCache)=>
  205. {
  206. return new Promise((resolve)=>{
  207. let cachedHistoricDates = [];
  208. if (typeof historicAssessmentDataFromCache!=="undefined")
  209. {
  210. historicAssessmentDataFromCache.forEach((assessmentData)=>{
  211. cachedHistoricDates.push(Object.keys(assessmentData));
  212. });
  213. }
  214. //console.log(cachedHistoricDates.flat());
  215. resolve(cachedHistoricDates.flat());
  216. });
  217. }
  218. fetchHistoricDateFromTripleStore = (datasetPLD) =>
  219. {
  220. return new Promise((resolve)=>{
  221. let historicDates = [];
  222. read(getAllAssessmentDates(datasetPLD))
  223. .then((response) => {
  224. //console.log(response);
  225. if (response) {
  226. if (response.results.bindings.length > 0) {
  227. historicDates = response.results.bindings.map((result,index) => {
  228. return ({result:result.TimePeriod.value});
  229. });
  230. }
  231. }
  232. //console.log(historicDates);
  233. resolve(historicDates);
  234. });
  235. });
  236. }
  237. refreshCacheWithHistoricAssessmentData = (cachedHistoricDates, date, datasetID,datasetPLD) =>
  238. {
  239. return new Promise((resolve)=>{
  240. //console.log(cachedHistoricDates.indexOf(date.result));
  241. if(cachedHistoricDates.indexOf(date.result) >= 0)
  242. {
  243. //console.log("Yes");
  244. resolve();
  245. }
  246. else
  247. {
  248. //console.log("No");
  249. read(getAssessmentQuality(datasetPLD,date.result)).then((response) => {
  250. let assessmentResult = {
  251. [date.result] : [...response.results.bindings]
  252. }
  253. //console.log(assessmentResult);
  254. this.props.updateHistoricAssessmentDetailsToCache(assessmentResult,datasetID);
  255. resolve();
  256. }
  257. );
  258. }
  259. });
  260. }
  261. toggleAlertModel = () => {
  262. this.setState({
  263. loadFailed: false
  264. });
  265. }
  266. toggle = () => {
  267. this.setState({
  268. dropdownOpen: !this.state.dropdownOpen,
  269. });
  270. }
  271. render ()
  272. {
  273. //console.log(this.props.datasetDetailsCache[0]);
  274. console.log(this.state.assessmentDates)
  275. let assessmentCount = this.state.assessmentDates.length;
  276. var dateTime = ''
  277. var dates = ''
  278. var time = ''
  279. if(this.state.is1Spatial){
  280. dateTime = this.state.assessmentDates.map((date)=>{return((date.result));});
  281. dates = dateTime.map((date)=>{return((date).split(" ")[0]);});
  282. time = dateTime.map((date)=>{return((date).split(" ")[1]);});
  283. }else{
  284. dateTime = this.state.assessmentDates.map((date)=>{return((date.result).split(".")[0]);});
  285. dates = dateTime.map((date)=>{return((date).split("T")[0]);});
  286. time = dateTime.map((date)=>{return((date).split("T")[1]);});
  287. }
  288. let backgroundColour = this.state.qualityStatus.sort((a, b) => parseInt(a.index) - parseInt(b.index));
  289. let assessData = this.state.assessmentQualities.sort((a, b) => parseInt(a.index) - parseInt(b.index));
  290. if(assessmentCount>sessionStorage.getItem("LIMIT"))
  291. {
  292. let startIndex=assessmentCount-sessionStorage.getItem("LIMIT");
  293. dateTime=dateTime.slice(startIndex);
  294. backgroundColour=backgroundColour.slice(startIndex);
  295. assessData=assessData.slice(startIndex);
  296. }
  297. let mergedDateTime=[];
  298. let size = dateTime.length;
  299. let index=0;
  300. for (index=0; index<size;index++)
  301. {
  302. mergedDateTime.push([dates[index],time[index]]);
  303. }
  304. const bar = {
  305. labels: [...mergedDateTime],
  306. datasets: [
  307. {
  308. label: this.state.datasetPLD,
  309. backgroundColor: backgroundColour.map((status)=> {return(status.status)}),
  310. data: assessData.map((data)=>{return(data.quality)}) ,
  311. },
  312. ],
  313. };
  314. let model = (<Modal isOpen={true} className="modal-dialog-centered">
  315. <ModalHeader style={{ margin: 'auto' }}>Configure Settings</ModalHeader>
  316. <ModalBody>
  317. <Form>
  318. <FormGroup>
  319. <Label>Dataset Name:</Label>
  320. <Input type="text" name="limitInput" id="limitInput" ref="limitInput" value={this.state.tempLimit} onChange={this.onChangeLimit} />
  321. </FormGroup>
  322. </Form>
  323. </ModalBody>
  324. <ModalFooter>
  325. <Button color="secondary" onClick={this.togglePopup}>Close</Button>
  326. <Button color="primary" onClick={this.saveLimit}>Save</Button>{' '}
  327. </ModalFooter>
  328. </Modal>
  329. );
  330. return(
  331. <Card className="w-75">
  332. {this.state.popupOpen?model:null}
  333. <CardHeader>
  334. Data Quality over Time
  335. <ButtonGroup className="float-right">
  336. <ButtonDropdown id='card1' isOpen={this.state.dropdownOpen} toggle={this.toggle}>
  337. <DropdownToggle caret className="p-0" color="black">
  338. <Media className="icon-settings"></Media>
  339. </DropdownToggle>
  340. <DropdownMenu right>
  341. <DropdownItem onClick={this.togglePopup}>Config</DropdownItem>
  342. </DropdownMenu>
  343. </ButtonDropdown>
  344. </ButtonGroup>
  345. </CardHeader>
  346. <CardBody>
  347. {this.state.isloading ? <LoadingSpinner /> : null}
  348. {this.state.loadFailed ? <LoadingFailed clickHandler={this.toggleAlertModel} /> : null}
  349. <Bar data={bar} options={options} />
  350. </CardBody>
  351. </Card>
  352. );
  353. }
  354. }
  355. const mapStateToProps = (state,ownProps) => {
  356. let datasetID = ownProps.datasetID;
  357. let datasetDetails = null
  358. if(ownProps.is1Spatial && state.pipelineCache){
  359. datasetDetails = state.pipelineCache.filter((dataset)=>{if(dataset.datasetID===datasetID){return dataset;} return null; });
  360. }else{
  361. datasetDetails = state.datasetDetailsCache.filter((dataset)=>{if(dataset.datasetID===datasetID){return dataset;} return null; });
  362. }
  363. return (
  364. {
  365. datasetDetailsCache: datasetDetails
  366. }
  367. );
  368. }
  369. const mapDispatchToProps = (dispatch) => {
  370. return ({
  371. updateHistoricAssessmentDetailsToCache: (historicAssessmentData,datasetID) => { return dispatch({ type: 'UPDATE_HISTORIC_ASSESSMENT_DETAILS_CACHE', payLoad: {historicAssessmentData:historicAssessmentData, datasetID:datasetID} }); }
  372. }
  373. );
  374. }
  375. export default connect(mapStateToProps, mapDispatchToProps)(DataQualityOverTime);