Browse Source

priorities feature upboats intutively with echelons

main
deicidus 3 years ago
parent
commit
d3010163a7
  1. 3
      README.md
  2. 3
      package.json
  3. 10
      scripts/api.js
  4. 8
      scripts/cards.js
  5. 82
      scripts/connect.js
  6. 5
      scripts/features/index.js
  7. 84
      scripts/priority.js
  8. 1
      scripts/services.js

3
README.md

@ -32,8 +32,9 @@ These features work right now:
* Detects your OS, with support for Debian/Ubuntu, Arch/Manjaro, and Fedora (MacOS planned)
* Wraps the functionality of (some of) Zen's Alchemy suite of scripts (system configuration, AO installation)
* Add `ao` alias for `ao-cli` (under Features→ao-cli)
* Enchant your 'cd' command to narrate your travels through the UNIX filesystem (under Features→ao-cli) (less annoying than it sounds, easy to disable)
* Optionally enchant your 'cd' command to narrate your travels through the UNIX filesystem and occasionally remind you of your top priority (under Features→ao-cli)
* Easily add your existing systemctl services to the Features list so you can start and stop them from the AO Features menu
* Pedagogical codebase designed for teaching novice users. Code written to be read, with relevant contextual information mentioned in comments. Browse or download the code for ao-cli at the Repository link above.
## Upcoming Features

3
package.json

@ -21,7 +21,8 @@
"autonomous",
"autonomy",
"DAO",
"CIC"
"CIC",
"pedagogy"
],
"author": "Coalition of Invisible Colleges",
"license": "AGPL-3.0-or-later",

10
scripts/api.js

@ -65,7 +65,6 @@ export async function postRequest(endpoint, payload = null, verbose = true) {
// Performs a post request to the /event endpoint, sending the given JSON object as the event
export async function postEvent(event, verbose) {
console.log('about to post event')
return await postRequest('/events', event, verbose)
}
@ -473,14 +472,17 @@ export async function dropPile(taskId) {
}
// Priority feature
export async function prioritizeCard(taskId, inId, position = 0) {
return await postEvent({
export async function prioritizeCard(taskId, inId, position = 0, echelon = null) {
const act = {
type: 'task-prioritized',
taskId: taskId,
inId: inId,
position: position,
...(echelon && { echelon: echelon }),
blame: currentMemberId
})
}
console.log(act)
return await postEvent(act)
}
export async function prioritizePile(inId) {

8
scripts/cards.js

@ -3,6 +3,7 @@ import inquirer from 'inquirer'
import { getCardByName, createCard, prioritizeCard } from './api.js'
import { headerStyle } from './styles.js'
import { prioritiesMenu } from './priority.js'
import { aoEnv } from './settings.js'
// The card menu is complex and so has been split into this separate file
export async function cardMenu() {
@ -52,7 +53,8 @@ async function browseMenu() {
}
// Ask the user to create a card, checks if it already exists, and then creates it if it doesn't
async function createCardInteractive(prioritized = true) {
export async function createCardInteractive(prioritized = true) {
console.log('createcardinteractive')
const memberId = aoEnv('AO_CLI_SESSION_MEMBERID')
if(!memberId) {
console.log('Not logged in.')
@ -74,9 +76,9 @@ async function createCardInteractive(prioritized = true) {
}
if(prioritized) {
console.log('Card already exists, prioritizing.')
const prioritizeResult = prioritizeCard(fetchedCards[0].taskId, memberId)
const prioritizeResult = await prioritizeCard(fetchedCards[0].taskId, memberId)
if(!prioritizeResult.ok) {
console.log('May have failed to prioritize card.')
console.log('May have failed to prioritize card:', prioritizeResult)
}
}
return false

82
scripts/connect.js

@ -131,85 +131,3 @@ async function connectInteractive() {
console.log('result is', result.body)
return true
}
// Fetches a list of your AO server's p2p connections and displays it
async function connectionsMenu() {
console.log(`\n${headerStyle('AO P2P Connections')}`)
let connectionsChoices = []
const memberId = aoEnv('AO_CLI_SESSION_MEMBERID')
if(!memberId) {
console.log('Not logged in.')
return false
}
const fetchedCards = await getCard(taskId, 'priorities')
if(!fetchedCards || fetchedCards.length < 1) {
console.log('Failed to fetch member card, this is bad.')
return false
}
const card = fetchedCards[0]
const priorityCards = fetchedCards.slice(1) // First card is member card itself
let priorities = card.priorities.slice()
priorities.reverse()
console.log('You have', priorityCards.length, 'priorities:')
prioritiesChoices = priorities.map((priorityTaskId, i) => {
const priorityCard = priorityCards.find(p => p.taskId === priorityTaskId)
if(!priorityCard) {
return 'Missing card, repair your database'
}
return {
name: priorityCard.name,
value: { index: i, card: priorityCard },
short: priorityCard.name.substring(0, 70) + priorityCard.name.length >= 70 ? '...' : ''
}
})
prioritiesChoices.push(
{ name: 'Create priority', value: 'create_here', short: 'new priority' },
{ name: 'Back to Deck', value: false, short: 'back' }
)
let answer
try {
answer = await inquirer.prompt({
name: 'priorities_menu',
type: 'rawlist',
message: 'Please choose:',
choices: prioritiesChoices,
loop: false
})
} catch(error) {
if (error === 'EVENT_INTERRUPTED') {
console.log('\nESC')
return false
}
}
switch(answer.priorities_menu) {
case false:
return false
case 'create_here':
let previousCardCreatedText
do {
console.log('previousCardCreatedText is', previousCardCreatedText)
previousCardCreatedText = await createCardInteractive()
} while(previousCardCreatedText != '\n')
return true
case 'Missing card, repair your database':
console.log('Database repair yet implemented, sorry.')
return true
}
let chosenTask = answer.priorities_menu.card
const chosenTaskId = chosenTask.taskId
let previousAnswer
do {
previousAnswer = await priorityCardMenu(chosenTask, answer.priorities_menu.index)
if(previousAnswer) {
const fetchedCards = await getCard(chosenTaskId, false)
if(!fetchedCards || fetchedCards.length < 1) {
console.log('The card has disappeared. Maybe it was deleted, or cards held by no one are automatically cleaned up every five minutes.')
return false
}
chosenTask = fetchedCards[0]
}
} while(previousAnswer !== false)
console.log('Card menu not yet implemented.')
return true
}

5
scripts/features/index.js

@ -123,10 +123,11 @@ function oneFeatureMenuChoices(name, feature, status, isCustom = false) {
return null
}
const running = typeof feature.isRunning === 'function' ? feature.isRunning() : feature.hasOwnProperty('isRunning') ? feature.isRunning : false
const installed = typeof feature.isInstall === 'function' ? feature.isInstalled : feature.hasOwnProperty('isInstalled') ? feature.isInstalled : status !== 'off'
const running = installed && typeof feature.isRunning === 'function' ? feature.isRunning() : feature.hasOwnProperty('isRunning') ? feature.isRunning : false
if(running && typeof feature.stop === 'function') {
featureChoices.push({ name: 'Stop ' + name, value: () => feature.stop() })
} else if(!running && typeof feature.start === 'function') {
} else if(installed && !running && typeof feature.start === 'function') {
featureChoices.push({ name: 'Start ' + name, value: () => feature.start() })
}

84
scripts/priority.js

@ -3,6 +3,7 @@ import inquirer from 'inquirer'
import { headerStyle } from './styles.js'
import { aoEnv } from './settings.js'
import { getCard, prioritizeCard, completeCard, uncheckCard, refocusCard } from './api.js'
import { createCardInteractive } from './cards.js'
// Prints the text (.name) of the first card prioritized in the logged-in users member card
export async function getTopPriorityText() {
@ -46,9 +47,17 @@ export async function prioritiesMenu(taskId = null) {
return false
}
const card = fetchedCards[0]
const priorityCards = fetchedCards.slice(1) // First card is member card itself
let priorityCards = fetchedCards.slice(1) // First card is member card itself
let priorities = card.priorities.slice()
priorities.reverse()
// Fix order - incoming card order is absolute order on server, not order within this card
priorityCards = priorities.map((priorityTaskId, i) => {
const priorityCard = priorityCards.find(p => p.taskId === priorityTaskId)
if(!priorityCard) {
return 'Missing card, repair your database'
}
return priorityCard
})
console.log('You have', priorityCards.length, 'priorities:')
prioritiesChoices = priorities.map((priorityTaskId, i) => {
const priorityCard = priorityCards.find(p => p.taskId === priorityTaskId)
@ -61,6 +70,19 @@ export async function prioritiesMenu(taskId = null) {
short: priorityCard.name.substring(0, 70) + priorityCard.name.length >= 70 ? '...' : ''
}
})
let firstIndexEchelonDecreases
let firstEchelon = priorityCards.length >= 1 ? priorityCards[0].echelon : null
if(firstEchelon) {
priorityCards.some((pc, i) => {
if(!pc.echelon || pc.echelon !== firstEchelon) {
firstIndexEchelonDecreases = i
return true
}
})
if(!isNaN(firstIndexEchelonDecreases)) {
prioritiesChoices.splice(firstIndexEchelonDecreases, 0, new inquirer.Separator('───'))
}
}
prioritiesChoices.push(
{ name: 'Create priority', value: 'create_here', short: 'new priority' },
{ name: 'Back to Deck', value: false, short: 'back' }
@ -68,11 +90,11 @@ export async function prioritiesMenu(taskId = null) {
let answer
try {
answer = await inquirer.prompt({
name: 'priorities_menu',
type: 'rawlist',
message: 'Please choose:',
choices: prioritiesChoices,
loop: false
name: 'priorities_menu',
type: 'rawlist',
message: 'Please choose:',
choices: prioritiesChoices,
loop: false
})
} catch(error) {
if (error === 'EVENT_INTERRUPTED') {
@ -86,7 +108,6 @@ export async function prioritiesMenu(taskId = null) {
case 'create_here':
let previousCardCreatedText
do {
console.log('previousCardCreatedText is', previousCardCreatedText)
previousCardCreatedText = await createCardInteractive()
} while(previousCardCreatedText != '\n')
return true
@ -98,7 +119,7 @@ export async function prioritiesMenu(taskId = null) {
const chosenTaskId = chosenTask.taskId
let previousAnswer
do {
previousAnswer = await priorityCardMenu(chosenTask, answer.priorities_menu.index)
previousAnswer = await priorityCardMenu(chosenTask, answer.priorities_menu.index, priorityCards)
if(previousAnswer) {
const fetchedCards = await getCard(chosenTaskId, false)
if(!fetchedCards || fetchedCards.length < 1) {
@ -114,7 +135,7 @@ export async function prioritiesMenu(taskId = null) {
// Short action-oriented menu for cards in the priorities list
// Index is the position of the card in the list that it is in, used for fencepost case to display upboat contextually
async function priorityCardMenu(card, index) {
async function priorityCardMenu(card, index, allPriorities) {
if(!card) {
console.log('priorityCardMenu: card is required.')
return false
@ -161,7 +182,49 @@ async function priorityCardMenu(card, index) {
}
break
case 'upboat':
await prioritizeCard(taskId, memberId)
console.log('upboat')
let firstEchelonScore
let newEchelonScore
let newPosition = 0
console.log('upboat2')
console.log('card is', card)
//console.log(allPriorities)
breakHere:
for(let i = 0; i < allPriorities.length; i++) {
console.log('upboat3')
const priority = allPriorities[i]
console.log('priority is', priority)
if(i === 0) {
console.log('upboat3.1')
firstEchelonScore = priority.echelon
console.log('upboat3.11115', priority.name, priority.echelon, typeof priority.echelon)
if(isNaN(firstEchelonScore)) {
console.log('upboat3.2')
newEchelonScore = 1
break breakHere
}
if(!card.echelon || card.echelon < priority.echelon) {
console.log('upboat3.3')
newEchelonScore = priority.echelon
} else if(card.echelon && priority.echelon && card.echelon === priority.echelon) {
console.log('upboat3.4')
newEchelonScore = priority.echelon + 1
break breakHere
} else if(card.echelon && priority.echelon && card.echelon > priority.echelon) {
console.log('upboat3.5')
break breakHere
}
}
console.log('upboat4')
if(priority.echelon !== firstEchelonScore) {
newPosition = i
break breakHere
}
console.log('upboat5')
}
console.log('upboat6')
console.log('newPosition is', newPosition, 'and newEchelonScore is', newEchelonScore)
await prioritizeCard(taskId, memberId, newPosition, newEchelonScore)
return false
case 'downboat':
await refocusCard(taskId, memberId)
@ -171,5 +234,6 @@ async function priorityCardMenu(card, index) {
default:
return false
}
console.log('broken')
return true
}

1
scripts/services.js

@ -114,6 +114,7 @@ export default class SystemServiceManager {
willBeUninstall(verbose = true) {
this.assertInitialized()
console.log('Deleting service file. You may be asked for you sudo password.')
this.stop()
try {
execSync('sudo rm ' + this.servicePath())
if(verbose) console.log("Deleted service file.")

Loading…
Cancel
Save