Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refresh logic and mitigation of situations with inconsistent config file #34

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# Wrap TTL files into JS files for bundling with library

,all : wf.js trackerSettingsForm.js trackerInstancesForm.js ui.js
# was trackerInstancesForm.js

,all : wf.js trackerSettingsForm.js ui.js

#individualForm.js : individualForm.ttl
# (echo 'module.exports = `' ; cat individualForm.ttl; echo '`') > individualForm.js
Expand Down
30 changes: 8 additions & 22 deletions issue.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export function renderIssueCard (issue, context) {
const backgroundColor = getBackgroundColor() || 'white'
card.style.backgroundColor = backgroundColor
editButton.style.backgroundColor = backgroundColor // Override white from style sheet
widgets.setImage(img, issue) // react to a change of class of the object
}
const dom = context.dom
const uncategorized = !getBackgroundColor() // This is a suspect issue. Prompt to delete it
Expand All @@ -52,7 +53,8 @@ export function renderIssueCard (issue, context) {
card.style = 'border-radius: 0.4em; border: 0.05em solid grey; margin: 0.3em;'

const img = card.firstChild.firstChild.firstChild.firstChild // div/table/tr/td/img
img.setAttribute('src', icons.iconBase + 'noun_Danger_1259514.svg') // override
// img.setAttribute('src', icons.iconBase + 'noun_Danger_1259514.svg') // override

// Add a button for viewing the whole issue in overlay
const buttonsCell = card.firstChild.firstChild.children[2] // right hand part of card
const editButton = widgets.button(dom, icons.iconBase + 'noun_253504.svg', 'edit', async _event => {
Expand Down Expand Up @@ -262,22 +264,6 @@ export function renderIssue (issue, context) {
complainIfBad
)

// Descriptions can be long and are stored local to the issue
/*
issueDiv.appendChild(
widgets.makeDescription(
dom,
kb,
issue,
ns.wf('description'),
store,
function (ok, body) {
if (ok) setModifiedDate(store, kb, store)
else console.log('Failed to change description:\n' + body)
}
)
) */

// Assigned to whom?

const assignments = kb.statementsMatching(issue, ns.wf('assignee'))
Expand All @@ -301,8 +287,8 @@ export function renderIssue (issue, context) {
const devGroups = kb.each(issue, ns.wf('assigneeGroup'))
for (let i = 0; i < devGroups.length; i++) {
const group = devGroups[i]
await kb.fetcher.load()
devs = devs.concat(kb.each(group, ns.vcard('member')))
await kb.fetcher.load(group)
devs = devs.concat(kb.each(group, ns.vcard('hasMember')))
}
// Anyone who is a developer of any project which uses this tracker
const proj = kb.any(null, ns.doap('bug-database'), tracker) // What project?
Expand All @@ -321,9 +307,9 @@ export function renderIssue (issue, context) {

getPossibleAssignees().then(devs => {
if (devs.length) {
devs.forEach(function (person) {
kb.fetcher.lookUpThing(person)
}) // best effort async for names etc
// devs.map(function (person) {
// kb.fetcher.lookUpThing(person)
// }) // best effort async for names etc
const opts = {
// 'mint': '** Add new person **',
nullLabel: '(unassigned)'
Expand Down
161 changes: 91 additions & 70 deletions issuePane.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,13 +94,13 @@ export default {
try {
await updateMany([], ins)
} catch (err) {
return UI.widgets.complain(context, 'Error writing tracker configuration: ' + err)
return widgets.complain(context, 'Error writing tracker configuration: ' + err)
}
/*
try {
await kb.updater.updateMany([], kb.statementsMatching(undefined, undefined, undefined, stateStore))
} catch (err) {
return UI.widgets.complain(context, 'Error writing tracker state file: ' + err)
return widgets.complain(context, 'Error writing tracker state file: ' + err)
}
*/
const dom = context.dom
Expand All @@ -118,15 +118,22 @@ export default {
},

render: function (subject, context) {
function refreshPane () {
for (const ele of refreshables) {
widgets.refreshTree(ele)
}
}
const dom = context.dom

const paneDiv = dom.createElement('div')
context.paneDiv = paneDiv
paneDiv.setAttribute('class', 'issuePane')
var refreshables = [] // Separate widgets to be refreshed
paneDiv.refresh = refreshPane

function complain (message) {
console.warn(message)
paneDiv.appendChild(UI.widgets.errorMessageBlock(dom, message))
paneDiv.appendChild(widgets.errorMessageBlock(dom, message))
}

function complainIfBad (ok, message) {
Expand All @@ -137,50 +144,63 @@ export default {

/** Infer subclass from disjoint Union
**
** This is would not be needed if our quey language
** allowed is to query ardf Collection membership.
** This would not be needed if our query language
** allowed us to query RDF Collection membership.
*/
async function fixSubClasses (kb, tracker) {
async function checkOneSuperclass (klass) {
const collection = kb.any(klass, ns.owl('disjointUnionOf'), null, doc)
function checkSubClasses (kb, tracker) {
function checkOneSuperclass (klass) {
const collection = kb.any(klass, ns.owl('disjointUnionOf'), null, klass.doc())
if (!collection) throw new Error(`Classification ${klass} has no disjointUnionOf`)
if (!collection.elements) throw new Error(`Classification ${klass} has no array`)
const needed = new Set(collection.elements.map(x => x.uri))
const existing = new Set(kb.each(null, ns.rdfs('subClassOf'), klass, doc)
const existing = new Set(kb.each(null, ns.rdfs('subClassOf'), klass, klass.doc())
.map(x => x.uri))
for (const sub of existing) {
if (!needed.has(sub)) {
deletables.push($rdf.st(kb.sym(sub), ns.rdfs('subClassOf'), klass, doc))
deletables.push($rdf.st(kb.sym(sub), ns.rdfs('subClassOf'), klass, klass.doc()))
}
}
for (const sub of needed) {
if (!existing.has(sub)) {
insertables.push($rdf.st(kb.sym(sub), ns.rdfs('subClassOf'), klass, doc))
insertables.push($rdf.st(kb.sym(sub), ns.rdfs('subClassOf'), klass, klass.doc()))
}
}
}
const doc = tracker.doc()
// const doc = tracker.doc()
const states = kb.any(tracker, ns.wf('issueClass'))
const cats = kb.each(tracker, ns.wf('issueCategory'))
var insertables = []
var deletables = []
cats.push(states)
for (const klass of cats) {
await checkOneSuperclass(klass)
checkOneSuperclass(klass)
}
const damage = insertables.length + deletables.length
if (damage) {
alert(`Internal error: s${damage} subclasses inconsistences!`)
/*
if (confirm(`Fix ${damage} inconsistent subclasses in tracker config?`)) {
await kb.updater.update(deletables, insertables)
*/
// const damage = insertables.length + deletables.length
return [deletables, insertables]
}

async function subClassFixButton (kb, tracker) {
const [deletables, insertables] = checkSubClasses(kb, tracker)
const button = widgets.button(dom, UI.icons.iconBase + 'noun_344563.svg', 'Adjust', async () => {
const [deletables, insertables] = checkSubClasses(kb, tracker)
try {
await updater.update(deletables, insertables)
alert('(Config file adjusted)')
} catch (err) {
const msg = 'Config file cannot be adjusted: ' + err
alert(msg)
console.error(msg)
}
})
if (deletables.length === 0 && insertables.length === 0) {
button.setAttribute('disabled', 'yes')
}
return button
}

/** /////////////////////////// Board
*/
function renderBoard (tracker, klass) {
async function renderBoard (tracker, klass) {
const states = kb.any(tracker, ns.wf('issueClass'))
klass = klass || states // default to states
const doingStates = klass.sameTerm(states)
Expand Down Expand Up @@ -210,9 +230,11 @@ export default {
[$rdf.st(issue, ns.rdf('type'), currentState, stateStore)],
[$rdf.st(issue, ns.rdf('type'), newState, stateStore)])
} catch (err) {
UI.widgets.complain(context, 'Unable to change issue state: ' + err)
widgets.complain(context, 'Unable to change issue state: ' + err)
return
}
boardDiv.refresh() // reorganize board to match the new reality
paneDiv.refresh() // Propagate up to whole pane in fact
}

function isOpen (issue) {
Expand Down Expand Up @@ -242,12 +264,12 @@ export default {
alert(err)
return
}
UI.widgets.refreshTree(tableDiv)
widgets.refreshTree(tableDiv)
})
return refreshButton
}

function renderTable (tracker) {
async function renderTable (tracker) {
function newOptionalClause () {
const clause = new $rdf.IndexedFormula()
query.pat.optional.push(clause)
Expand Down Expand Up @@ -327,14 +349,15 @@ export default {
}

// Allow user to create new things within the folder
function renderCreationControl (refreshTarget) {
// @@ move this into common code
function renderCreationControl (refreshTarget, noun) {
const creationDiv = dom.createElement('div')
const me = UI.authn.currentUser()
const creationContext = {
// folder: subject,
div: creationDiv,
dom: dom,
noun: 'tracker',
noun: noun,
statusArea: creationDiv,
me: me,
refreshTarget: refreshTarget
Expand All @@ -344,25 +367,19 @@ export default {
UI.create.newThingUI(creationContext, context, relevantPanes) // Have to pass panes down newUI
return creationDiv
}

function renderInstances (theClass) {
async function renderInstances (theClass, noun) {
const instancesDiv = dom.createElement('div')
const context = { dom, div: instancesDiv, noun: 'tracker' }
UI.authn.registrationList(context, { public: true, private: true, type: theClass }).then(_context2 => {
instancesDiv.appendChild(renderCreationControl(instancesDiv))
/* // keep this code in case we need a form
const InstancesForm = ns.wf('TrackerInstancesForm')
const text = trackerInstancesFormText
$rdf.parse(text, kb, InstancesForm.doc().uri, 'text/turtle')
widgets.appendForm(dom, instancesDiv, {}, tracker, InstancesForm,
tracker.doc(), complainIfBad)
*/
})
const context = { dom, div: instancesDiv, noun: noun }

const _context2 = await UI.authn.registrationList(context, { public: true, private: true, type: theClass })
instancesDiv.appendChild(renderCreationControl(instancesDiv, noun))

return instancesDiv
}
function renderSettings (tracker) {

async function renderSettings (tracker) {
const settingsDiv = dom.createElement('div')
// A registration control allows the to record this tracker in their type index
// A registration control allows the user to record this tracker in their type index
const context = { dom, div: settingsDiv, noun: 'tracker' }
UI.authn.registrationControl(context, tracker, ns.wf('Tracker')).then(_context2 => {
const settingsForm = ns.wf('TrackerSettingsForm')
Expand All @@ -371,26 +388,30 @@ export default {
widgets.appendForm(dom, settingsDiv, {}, tracker, settingsForm,
tracker.doc(), complainIfBad)
})
const fixButton = await subClassFixButton(kb, tracker)
settingsDiv.append(fixButton)
return settingsDiv
}

function renderTabsTableAndBoard () {
function renderMain (ele, object) {
/* Renderthe tab system
*/
async function renderTabsTableAndBoard () {
async function renderMain (ele, object) {
ele.innerHTML = '' // Clear out "loading message"
if (object.sameTerm(boardView)) {
ele.appendChild(renderBoard(tracker))
ele.appendChild(await renderBoard(tracker))
} else if (object.sameTerm(tableView)) {
ele.appendChild(renderTable(tracker))
ele.appendChild(await renderTable(tracker))
} else if (object.sameTerm(settingsView)) {
ele.appendChild(renderSettings(tracker))
ele.appendChild(await renderSettings(tracker))
} else if (object.sameTerm(instancesView)) {
ele.appendChild(renderInstances(ns.wf('Tracker')))
ele.appendChild(await renderInstances(ns.wf('Tracker'), 'tracker'))
} else if ((kb.holds(tracker, ns.wf('issueCategory'), object)) ||
(kb.holds(tracker, ns.wf('issueClass'), object))) {
ele.appendChild(renderBoard(tracker, object))
ele.appendChild(await renderBoard(tracker, object))
} else {
throw new Error('Unexpected tab type: ' + object)
}
refreshables.push(ele.lastChild)
}
const states = kb.any(tracker, ns.wf('issueClass'))
const items = [instancesView, tableView, states]
Expand All @@ -411,14 +432,14 @@ export default {

async function renderTracker () {
function showNewIssue (issue) {
UI.widgets.refreshTree(paneDiv)
widgets.refreshTree(paneDiv)
exposeOverlay(issue, context)
b.disabled = false // https://stackoverflow.com/questions/41176582/enable-disable-a-button-in-pure-javascript
}
tracker = subject

try {
await fixSubClasses(kb, tracker)
await checkSubClasses(kb, tracker)
} catch (err) {
console.log('@@@ Error fixing subclasses in config: ' + err)
}
Expand Down Expand Up @@ -462,24 +483,24 @@ export default {
// Table of issues - when we have the main issue list
// We also need the ontology loaded
//
context.session.store.fetcher
.load([stateStore])
.then(function (_xhrs) {
const tableDiv = renderTabsTableAndBoard(tracker)
// const tableDiv = renderTable(tracker) // was
paneDiv.appendChild(tableDiv)

if (tableDiv.refresh) {
// Refresh function
} else {
console.log('No table refresh function?!')
}
paneDiv.appendChild(newTrackerButton(subject))
updater.addDownstreamChangeListener(stateStore, tableDiv.refresh) // Live update
})
.catch(function (err) {
return console.log('Cannot load state store: ' + err)
})
try {
await context.session.store.fetcher.load(stateStore)
} catch (err) {
const msg = 'Cannot load state store: ' + err
alert(msg)
return console.log(msg)
}
const tabsDiv = await renderTabsTableAndBoard(tracker)

paneDiv.appendChild(tabsDiv)

if (tabsDiv.refresh) {
// Refresh function
} else {
console.log('No table refresh function?!')
}
paneDiv.appendChild(newTrackerButton(subject, context))
updater.addDownstreamChangeListener(stateStore, paneDiv.refresh) // Live update
// end of Tracker instance
} // render tracker

Expand Down Expand Up @@ -531,7 +552,7 @@ export default {
}
paneDiv.appendChild(renderIssue(subject, context))
updater.addDownstreamChangeListener(stateStore, function () {
UI.widgets.refreshTree(paneDiv)
widgets.refreshTree(paneDiv)
}) // Live update
}
)
Expand Down
Loading