/** * Contains the [[DateFolder]] class * This tracks data stored in dated folders and sub-folders * */ import * as debug from '../debug' import { store } from 'solid-logic-jss' import ns from '../ns' import * as $rdf from 'rdflib' // pull in first avoid cross-refs export async function emptyLeaf (leafDocument) { await store.fetcher.load(leafDocument) // files can have seealso links. skip ones with no leafObjects with a date return !( store.statementsMatching(null, ns.dct('created'), null, leafDocument) .length > 0 ) } /** * Track back through the YYYY/MM/DD tree to find the previous/next day */ export class DateFolder { constructor (rootThing, leafFileName, membershipProperty) { this.root = rootThing this.rootFolder = rootThing.dir() this.leafFileName = leafFileName || 'index.ttl' // typically chat.ttl this.membershipProperty = membershipProperty || ns.wf('leafObject') } /* Generate the leaf document (rdf object) from date * @returns: - document */ leafDocumentFromDate (date) { // debug.log('incoming date: ' + date) const isoDate = date.toISOString() // Like "2018-05-07T17:42:46.576Z" let path = isoDate.split('T')[0].replace(/-/g, '/') // Like "2018/05/07" path = this.root.dir().uri + path + '/' + this.leafFileName return store.sym(path) } /* Generate a date object from the leaf file name */ dateFromLeafDocument (doc) { const head = this.rootFolder.uri.length const str = doc.uri.slice(head, head + 10).replace(/\//g, '-') // let date = new Date(str + 'Z') // GMT - but fails in FF - invalid format :-( const date = new Date(str) // not explicitly UTC but is assumed so in spec // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/parse // debug.log('Date for ' + doc + ':' + date.toISOString()) return date } async loadPrevious (date, backwards) { async function previousPeriod (file, level) { function younger (x) { if (backwards ? x.uri >= file.uri : x.uri <= file.uri) return false // later than we want or same -- looking for different return true } function suitable (x) { const tail = x.uri .slice(0, -1) .split('/') .slice(-1)[0] if (!'0123456789'.includes(tail[0])) return false // not numeric return true } function lastOrFirst (siblings) { siblings = siblings.filter(suitable) siblings.sort() // chronological order if (!backwards) siblings.reverse() return siblings.pop() // date folder } // debug.log(' previousPeriod level' + level + ' file ' + file) const parent = file.dir() try { await store.fetcher.load(parent) let siblings = store.each(parent, ns.ldp('contains')) siblings = siblings.filter(younger) const folder = lastOrFirst(siblings) if (folder) return folder // debug.log(' parent no suitable offspring ' + parent) } catch (err) { if (err.response && err.response.status && err.response.status === 404) { debug.log('Error 404 for chat parent file ' + parent) } else { debug.log('*** Error NON 404 for chat parent file ' + parent) // statusTR.appendChild(widgets.errorMessageBlock(dom, err, 'pink')) throw (new Error(`*** ${err.message} for chat folder ${parent}`)) } } if (level === 0) { // debug.log('loadPrevious: returning as level is zero') return null // 3:day, 2:month, 1: year 0: no } const uncle = await previousPeriod(parent, level - 1) if (!uncle) { // debug.log(' previousPeriod: nothing left before. ', parent) return null // reached first ever } await store.fetcher.load(uncle) const cousins = store.each(uncle, ns.ldp('contains')) const result = lastOrFirst(cousins) // debug.log(' previousPeriod: returning cousins at level ' + level, cousins) // debug.log(' previousPeriod: returning result at level ' + level, result) return result } // previousPeriod let folder = this.leafDocumentFromDate(date).dir() while (true) { const found = await previousPeriod(folder, 3) if (found) { const leafDocument = store.sym(found.uri + this.leafFileName) const nextDate = this.dateFromLeafDocument(leafDocument) if (!await emptyLeaf(leafDocument)) { return nextDate } else { // debug.log(' loadPrevious: skipping empty ' + leafDocument) date = nextDate folder = this.leafDocumentFromDate(date).dir() // debug.log(' loadPrevious: moved back to ' + folder) } } else { return null // no more left } } } // loadPrevious async firstLeaf (backwards) { // backwards -> last leafObject const folderStore = $rdf.graph() const folderFetcher = new $rdf.Fetcher(folderStore) async function earliestSubfolder (parent) { function suitable (x) { const tail = x.uri .slice(0, -1) .split('/') .slice(-1)[0] if (!'0123456789'.includes(tail[0])) return false // not numeric return true } // debug.log(' parent ' + parent) delete folderFetcher.requested[parent.uri] // try { await folderFetcher.load(parent, { force: true }) // Force fetch as will have changed // }catch (err) { // } let kids = folderStore.each(parent, ns.ldp('contains')) kids = kids.filter(suitable) if (kids.length === 0) { throw new Error(' @@@ No children to parent2 ' + parent) } kids.sort() if (backwards) kids.reverse() return kids[0] } const y = await earliestSubfolder(this.root.dir()) const month = await earliestSubfolder(y) const d = await earliestSubfolder(month) const leafDocument = $rdf.sym(d.uri + 'chat.ttl') await folderFetcher.load(leafDocument) const leafObjects = folderStore.each( this.root, this.membershipProperty, null, leafDocument ) if (leafObjects.length === 0) { const msg = ' INCONSISTENCY -- no chat leafObject in file ' + leafDocument debug.trace(msg) throw new Error(msg) } const sortMe = leafObjects.map(leafObject => [ folderStore.any(leafObject, ns.dct('created')), leafObject ]) sortMe.sort() if (backwards) sortMe.reverse() /* debug.log( (backwards ? 'Latest' : 'Earliest') + ' leafObject is ' + sortMe[0][1] ) */ return sortMe[0][1] } // firstleafObject } // class