├── README.md ├── index.html └── index.js /README.md: -------------------------------------------------------------------------------- 1 | # TagRename 2 | Rename Hypothesis tags 3 | 4 | More Hypothesis tools. 5 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Rename Hypothesis tags 6 | 7 | 8 | 36 | 37 | 38 | 39 | 40 | 42 | 43 | view/edit/reply 44 | 45 | 48 | 51 | 52 | 53 | 54 | 55 |

Rename Hypothesis tags

56 | 57 |

58 | This tool lists your Hypothesis tags, and enables you to rename one or more of them. 59 | If you have many annotations to search, it'll take a while to build the list. You can 60 | shortcut that process by specifying a particular tag you want to rename. 61 | 62 |

63 |
64 |
65 |
66 |
67 |
reset API token
68 |
69 |
70 | 71 |
72 | 73 |
74 | 75 |
76 |
77 |

To see all your tags, increase the max to a number greater than your total # of annotations.

78 |

Please do make a safe copy your annotations first. And proceed with care. There a way to lose information without any technical malfunction. Suppose you are using three tags, A, B, and C, to classify annotations into three buckets. Then you rename B to C. Now bucket B is gone. There is only A, unchanged. and C, which includes what was in B. You can't reverse the arrow of entropy and reconstitute the set of annotations that were in B! 79 | 80 | More Hypothesis tools. 81 |

82 | 83 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | let allTags = {} 2 | 3 | const externalLinkStyle = `style="display:inline;width:.6em;height:.6em;margin-left:2px;margin-top:3px;"` 4 | 5 | hlib.createUserInputForm(hlib.getById('userContainer'), 'Your Hypothesis username') 6 | hlib.createTagInputForm(hlib.getById('tagContainer'), 'Leave empty to search all tags') 7 | hlib.createMaxInputForm(hlib.getById('maxContainer'), 'Max annotations to search') 8 | hlib.createApiTokenInputForm(hlib.getById('tokenContainer')) 9 | hlib.getById('searchButton').onclick = search 10 | 11 | function initializeTagDiv(tag) { 12 | const externalLink = renderIcon('icon-external-link', externalLinkStyle) 13 | const facetUrl = `https://jonudell.info/h/facet?user=${getUser()}&tag=${tag}&expanded=true&exactTagSearch=true` 14 | return ` 15 |
16 | ${externalLink} 17 | ${tag} 18 | ${allTags[tag]} 19 |
` 20 | } 21 | 22 | function getUser() { 23 | return hlib.getSettings().user 24 | } 25 | 26 | function getTag() { 27 | return hlib.getSettings().tag 28 | } 29 | 30 | function renderIcon(iconClass, style) { 31 | const _style = style ? style : `style="display:block"` 32 | return `` 33 | } 34 | 35 | function addRenameUX(tag) { 36 | if (hlib.getById(`_${tag}`)) { 37 | return 38 | } 39 | let tagDivs = Array.from(document.querySelectorAll('.tag')) 40 | let tags = tagDivs.map(tagDiv => { 41 | return tagDiv.innerText 42 | }) 43 | tags.forEach(_tag => { 44 | try { 45 | cancelSetup(_tag) 46 | } catch (e) { 47 | console.log(e) 48 | } 49 | }) 50 | let element = hlib.getById(tag) 51 | element.querySelector('a').setAttribute('onclick', null) 52 | element.innerHTML += ` 53 | 54 | 55 | ` 56 | } 57 | 58 | // rename an individual tag 59 | function rename(tag) { 60 | if (hlib.getById(`_${tag}`).value === '') { 61 | alert('Cannot rename to nothing') 62 | return 63 | } 64 | let fromTag = tag 65 | let params = { 66 | user: getUser(), 67 | max: hlib.getSettings().max, 68 | tag: fromTag 69 | } 70 | hlib.search(params) 71 | .then( data => { 72 | processRenameResults(data[0], data[1]) 73 | }) 74 | } 75 | 76 | function cancelSetup(tag) { 77 | let element = hlib.getById(tag) 78 | element.outerHTML = initializeTagDiv(tag) 79 | } 80 | 81 | function tokenReset() { 82 | localStorage.setItem('h_token', '') 83 | } 84 | 85 | function processSearchResults(annos, replies) { 86 | annos = annos.concat(replies) 87 | const _tag = getTag() 88 | annos.forEach(anno => { 89 | for (let i = 0; i < anno.tags.length; i++) { 90 | let tag = anno.tags[i] 91 | if (_tag && tag !== _tag) { 92 | continue 93 | } 94 | tag = tag.replace(/"/g,'').trim() 95 | if (!allTags.hasOwnProperty(tag)) { 96 | allTags[tag] = 1 97 | } else { 98 | allTags[tag] += 1 99 | } 100 | } 101 | }) 102 | let tagList = Object.keys(allTags).sort(function (a, b) { 103 | return a.toLowerCase().localeCompare(b.toLowerCase()) 104 | }) 105 | tagList = tagList.map(tag => { 106 | return initializeTagDiv(tag) 107 | }) 108 | hlib.getById('tags').innerHTML += tagList.join('\n') 109 | hlib.getById('progress').innerHTML = '' 110 | } 111 | 112 | function processRenameResults(annos, replies) { 113 | annos = annos.concat(replies) 114 | let fromTag = document.querySelector('.renamer').parentElement.id 115 | let toTag = document.querySelector('.renamer').value 116 | for (i = 0; i < annos.length; i++ ) { 117 | let anno = annos[i] 118 | let tags = anno.tags 119 | let _tags = [] 120 | tags.forEach(tag => { 121 | if (tag === fromTag) { 122 | tag = toTag 123 | } 124 | _tags.push(tag) 125 | }) 126 | let payload = { 127 | tags: _tags 128 | } 129 | let payloadJson = JSON.stringify(payload) 130 | hlib.updateAnnotation(anno.id, hlib.getToken(), payloadJson) 131 | .then(data => { 132 | console.log(data) 133 | if (i == annos.length) { 134 | hlib.getById(fromTag).innerHTML = `${fromTag} ${toTag}` 135 | } 136 | }) 137 | } 138 | } 139 | 140 | async function search() { 141 | if (!getUser()) { 142 | alert('Please provide the Hypothesis username corresponding to the API token') 143 | return 144 | } 145 | allTags = {} 146 | hlib.getById('tags').innerHTML = '' 147 | let params = { 148 | user: getUser(), 149 | max: hlib.getSettings().max 150 | } 151 | const tag = getTag() 152 | if (tag) { 153 | params.tag = tag 154 | } 155 | const data = await hlib.search(params, 'progress') 156 | processSearchResults(data[0], data[1]) 157 | } 158 | 159 | setTimeout(_ => { 160 | hlib.manageTokenDisplayAndReset() 161 | }, 200) 162 | 163 | --------------------------------------------------------------------------------