Roll20 uses cookies to improve your experience on our site. Cookies enable you to enjoy certain features, social sharing functionality, and tailor message and display ads to your interests on our site and others. They also help us understand how our site is being used. By continuing to use our site, you consent to our use of cookies. Update your cookie preferences .
Advertisement Create a free account Compendium in Game, Join Today

[Sheet Author] replace/rename existing attribute

1589352209
Vince
Pro
Sheet Author
Couple of use cases I'm looking at; Let's say "someone" created an attribute on a sheet, but later(sheet's been live for quite some time) realized it was misspelled and would like to correct said attribute with a properly spelled attribute. Sheet has numerous mixed case attribute names , but I would like to change them to all lowercase for consistency.  Wondering if attributes could simply be renamed to lowercase without issue...? Sheet is using the same attribute name in two locations.  One is located within a repeating section and represents spell @{level} and the other is used outside the repeating section to represent class @{Level} .  I know that you're supposed to use unique attribute names. Note there is a difference in case and one of the attribute is used within a repeating section which "might" make it unique even with the same name...?   I'm thinking I need to replace the new attribute name in the html, then get the value of the old attribute(getattrs) and set the new attribute equal to the old attribute using a sheetworker triggered by a sheet version check when opening the sheet?  Sound about right?  Thoughts? TIA 
1589354299
GiGs
Pro
Sheet Author
API Scripter
For #2, you can go ahead and change them without issue - roll20 doesnt care about case.  the names Attribute, attribute, ATTRIBUTE, and aTtRiBuTe are all the same attribute. The only place where case matters is in the fieldset class of a repeating section, and that should always be entirely lower case. For #3, this primarily matters with sheet workers. If you have an attribute called level, and repeating section called repeating_spells which also has level, any sheet worker checking for the change of level will trigger on both of them. It might not do anything, or it might silently crash, but it will still trigger. This is why although its clunky, it's always a good idea to give names inside a repeating section a unique suffix. So I'd recommend changing the name inside the repeating section, and if you're doing that, you may as well correct the misspelling at the same time. I'm thinking I need to replace the new attribute name in the html, then get the value of the old attribute(getattrs) and set the new attribute equal to the old attribute using a sheetworker triggered by a sheet version check when opening the sheet?  Sound about right?  Thoughts? TIA  Yes, thats right. You want to create a new attribute, call it something like codeversion, set its value to 0. Then create a sheet:opened worker, which checks for version value, and if its below, say 1, you launch a function. In that function, you have the getAttrs for the old attributes, transfer them to the new attributes, and also set the codeversion attribute to 1. It's not important for this function, but if you plan on having multiple version changes, you want to include in the setAttrs callback a call back to the version checking function, so that multiple updates can be done in sequence, without overlapping.
1589356764

Edited 1589375632
GiGs
Pro
Sheet Author
API Scripter
Here's some code that should do what you need, with some name tweaks. I;ll post an explanation of each part, then the completed thing at the bottom. So first you need the sheet opened: on('sheet:opened', () => {     // log statement that you are checking version     getAttrs(['codeversion'], v => {         versionator(parseInt(v.codeversion) || 0);     });     //log statement that version check is complete }); This checks the attribute you have named for the version checking. It can be the same as your normal version attribute. This calls a separate function to do the actual version checking. This is important because sometimes your version checking will call it again, so it needs to be separated from the sheet: opened event. So, then the version checking function. There's more than one way to build this. It could look like this: const versionator = (codeversion) => {     if(codeversion < 1) {         version_0_to_1();     } else if(codeversion < 2) {         version_1_to_2();     } else if(codeversion < 3) {         version_2_to_3();     } }; or this: const versionator = (codeversion) => {     switch(true) {         case (codeversion <1):             version_0_to_1();             break;         case (codeversion <2):             version_1_to_2();             break;         case (codeversion <3):             version_2_to_3();             break;         default:             // log statement that no change is needed?     } }; Or the order could be reversed, going from highest version to low (and reversing the equality comparison). All these versions of the function do exactly the same thing. They accept a version parameter, and then go through steps to see if the version is less than a threshold, and if so run a version upgrade function. Think about what happens if you r sheets code is up to version 3, and someone has never opened their sheet since version 0. The versionator will run, find they are below version 1, and so run the code that upgrades it to version 1, and then stop. (I'll explain why it is important that it stops after one and only upgrade, below.) So in the function that upgrades to version 1, we have to run versionator again. So we would have a function that looks at base like this: const version_0_to_1 = () => {     getAttrs(['some', 'attributes', 'to', 'change'] = (v) => {         // create an object to hold the new attributes         const update = {};         // transfer to old attributes into the new attributes         update.new_some = v.some;         update.new_attributes = v.attributes         update.new_to = v.to;         update.new_change  = v.change;         // add the version attribute         const codeversion = 1;         update.codeversion = version         setAttrs(update, // send the new attributes to the sheet             {silent:true}, // you probably want it not to trigger sheet workers             versionator(codeversion)); // and run the versionator function again     }); }; So some things to note here: you add the new version number to the updated attributes. and you include a new call to the versionator function inside the setAttrs  function. This is really important. It means that it only runs after  the sheet attributes have been updated.     And you include the version number in the paremeters. So now, the sheet has been upgraded to version 1, and goes back to versionator. The first check - is it below 1? - is run again, and it passes that. It reaches the second check - is it below 2? Since it isnt, that function now runs. And when it ends, it send back to the versionator, and so on. So, this seems a little convoluted. It would be easier to run the versionator without stopping after each upgrade, just let it run and complete all 3 upgrades (in this example). But setAttrs is asynchronous - it starts to work but doesnt finish immediately. The rest of the code continues simultaneously, without waiting for the setAttrs process to finish. So you could easily have the version 3 upgrade running at the same time as the version 1 upgrade - and the version 3 upgrade might complete  before   the version 1 upgrade does. Sometimes, the order things happen can lead to different results. When doing sheet updates it's especially important that upgrades complete properly. With the code being constructed this way, you can update a sheet multiple times, and be confident that each update is fully complete before the next one starts.  So, with that explanation out of the way, here's code that will serve your purpose: on('sheet:opened', () => {     // log statement that you are checking version     getAttrs(['codeversion'], v => {         versionator(parseInt(v.codeversion) ||0);     });     //log statement that version check is complete }); const versionator = (codeversion) => {     if(codeversion < 1) {         version_0_to_1();     } }; const version_0_to_1 = () => {     // log statement that you are updating to version 1     getSectionIDs('repeating_spells', idarray => {         const fieldnames = [];         idarray.forEach(id => fieldnames.push(`repeating_spells_${id}_level`));         getAttrs(['NAMEWITHTYPO', ...fieldnames], v => {             const update = {};             const codeversion = 1;             update.codeversion = codeversion;             update['NAMEWITHOUTTYPO'] = v['NAMEWITHTYPO'];             idarray.forEach(id => {                 update[`repeating_spells_${id}_spell_level`] = v[`repeating_spells_${id}_level`];             });             // log statement that you have completed updating to version 1             setAttrs(update,{silent:true}, versionator(codeversion));         });     }); } Change each instance of NAMEWITHTYPO and NAMEWITHOUTTYPO to what they should be. Likewise I've used repeating_spells for your section name, and spell_level for your new spell level name. Change them to what you need them to be.
1589438915
Vince
Pro
Sheet Author
Thanks GiGs.  I have a version checking function on the sheet(ad&d1e and forbidden lands) already which includes some previous code updates based on the sheet's version. I'll see if I can figure out out how to use your code to work with the existing versioning code.  Although, your versionator model looks very tempting.
1589440959
GiGs
Pro
Sheet Author
API Scripter
I looked at the forbidden lands sheet. Does the version checker do anything other than set announcements? I also looked at the AD&D version code and recoiled in horror. Picture me rocking in the corner whispering "no no no no"... If Scott saw this code, I fear for his sanity. More seriously, it looks like you have combined two functions which should be separate:  Actual version checking, where there is a change to the sheets code that if a sheet worker isnt run to fix it, will destroy user data minor sheet updates, where the sheet's function hasnt changed in a way that needs sheet worker code to fix Incidentally this is why i suggested using a code_version attribute, so you can have it separate from sheet_version, which manages whether to show announcements and normal sheet updates. The code_version never needs displaying to users, btw. It's only for internal use. If you want help to streamline and optimise this code, we can take it to PM.
1589461453
GiGs
Pro
Sheet Author
API Scripter
Okay I figured out what each step of the AD&amp;D version checker was doing, and I've rewritten it to match the versionator I provided on the forum, though i have rewritten it a touch. You can find it here <a href="https://gist.github.com/G-G-G/52b96cff0b827c42f9d04ab411fb42e8" rel="nofollow">https://gist.github.com/G-G-G/52b96cff0b827c42f9d04ab411fb42e8</a> I recommend starting a new campaign, and commenting out parts of the versionator before opening any sheets, and test it with values of 0.1, 1.2, and 1.5 in sequence to make sure each of the old version functions are converted properly. So, the versionator looks like this const&nbsp;versionator&nbsp;=&nbsp;(old_version,&nbsp;new_version)&nbsp;=&gt;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;set&nbsp;the&nbsp;sheet&nbsp;version&nbsp;you&nbsp;are&nbsp;upgrading&nbsp;sheet&nbsp;to,&nbsp;and&nbsp;set&nbsp;show_announcement&nbsp;to&nbsp;1&nbsp;if&nbsp;you&nbsp;want&nbsp;to&nbsp;force&nbsp;an&nbsp;announcement&nbsp;to&nbsp;be&nbsp;shown.&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;dont&nbsp;set&nbsp;a&nbsp;code_version;&nbsp;we&nbsp;use&nbsp;the&nbsp;switch&nbsp;below&nbsp;for&nbsp;that. &nbsp;&nbsp;&nbsp;&nbsp;if(old_version&nbsp;&lt;&nbsp;0.1)&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dmgSwap(0.1,&nbsp;new_version); &nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;else&nbsp;if&nbsp;(old_version&nbsp;&lt;&nbsp;1.2)&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;maxSwap(1.2,&nbsp;new_version); &nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;else&nbsp;if&nbsp;(old_version&nbsp;&lt;&nbsp;1.5)&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;nwpMacroColorUpdate(1.5,&nbsp;new_version); &nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;else&nbsp;if&nbsp;(old_version&nbsp;&lt;&nbsp;1.6)&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;spelllNameFix(1.6,&nbsp;new_version); &nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;else&nbsp;if(old_version&nbsp;&lt;&nbsp;new_version)&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setAttrs({ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sheet_version:&nbsp;new_version &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}); &nbsp;&nbsp;&nbsp;&nbsp;} }; On your first run comment out as follows: const&nbsp;versionator&nbsp;=&nbsp;(old_version,&nbsp;new_version)&nbsp;=&gt;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;set&nbsp;the&nbsp;sheet&nbsp;version&nbsp;you&nbsp;are&nbsp;upgrading&nbsp;sheet&nbsp;to,&nbsp;and&nbsp;set&nbsp;show_announcement&nbsp;to&nbsp;1&nbsp;if&nbsp;you&nbsp;want&nbsp;to&nbsp;force&nbsp;an&nbsp;announcement&nbsp;to&nbsp;be&nbsp;shown.&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;dont&nbsp;set&nbsp;a&nbsp;code_version;&nbsp;we&nbsp;use&nbsp;the&nbsp;switch&nbsp;below&nbsp;for&nbsp;that. &nbsp;&nbsp;&nbsp;&nbsp;if(old_version&nbsp;&lt;&nbsp;0.1)&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dmgSwap(0.1,&nbsp;new_version); /* =========================================== &nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;else&nbsp;if&nbsp;(old_version&nbsp;&lt;&nbsp;1.2)&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;maxSwap(1.2,&nbsp;new_version); &nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;else&nbsp;if&nbsp;(old_version&nbsp;&lt;&nbsp;1.5)&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;nwpMacroColorUpdate(1.5,&nbsp;new_version); &nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;else&nbsp;if&nbsp;(old_version&nbsp;&lt;&nbsp;1.6)&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;spelllNameFix(1.6,&nbsp;new_version); ============================================= */ &nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;else&nbsp;if(old_version&nbsp;&lt;&nbsp;new_version)&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setAttrs({ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sheet_version:&nbsp;new_version &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}); &nbsp;&nbsp;&nbsp;&nbsp;} }; So it just does the first one. Then try on a different character with const&nbsp;versionator&nbsp;=&nbsp;(old_version,&nbsp;new_version)&nbsp;=&gt;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;set&nbsp;the&nbsp;sheet&nbsp;version&nbsp;you&nbsp;are&nbsp;upgrading&nbsp;sheet&nbsp;to,&nbsp;and&nbsp;set&nbsp;show_announcement&nbsp;to&nbsp;1&nbsp;if&nbsp;you&nbsp;want&nbsp;to&nbsp;force&nbsp;an&nbsp;announcement&nbsp;to&nbsp;be&nbsp;shown.&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;dont&nbsp;set&nbsp;a&nbsp;code_version;&nbsp;we&nbsp;use&nbsp;the&nbsp;switch&nbsp;below&nbsp;for&nbsp;that. &nbsp;&nbsp;&nbsp;&nbsp;if(old_version&nbsp;&lt;&nbsp;0.1)&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dmgSwap(0.1,&nbsp;new_version); &nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;else&nbsp;if&nbsp;(old_version&nbsp;&lt;&nbsp;1.2)&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;maxSwap(1.2,&nbsp;new_version); /* =========================================== &nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;else&nbsp;if&nbsp;(old_version&nbsp;&lt;&nbsp;1.5)&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;nwpMacroColorUpdate(1.5,&nbsp;new_version); &nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;else&nbsp;if&nbsp;(old_version&nbsp;&lt;&nbsp;1.6)&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;spelllNameFix(1.6,&nbsp;new_version); ============================================= */ &nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;else&nbsp;if(old_version&nbsp;&lt;&nbsp;new_version)&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setAttrs({ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sheet_version:&nbsp;new_version &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}); &nbsp;&nbsp;&nbsp;&nbsp;} }; and so on since you've tested each section. Let me know if any aren't working properly. I added your new version change at version 1.6, so you can raise it to there to see it in action. But it has some edits you need to do first: the NAMEWITHTYPO and NAMEWITHOUTTYPO attributes, and the repeating section name "spell_level", if you want it to be something else, you'll need to change it. I also included a blank template for future upgrades which includes all the bits that are essential for the versionator to work. It looks like this: const blankUpdateTemplate = function (old_version, new_version) { // have this line near the start const update = {}; // do your stuff = geSectionIDs, getAttrs, all the various calculations // then end the worker update.sheet_version = old_version; setAttrs(update, { silent: true }, versionator(old_version, new_version)); // dont forget any close brackets for getyttrs, etc. } I do get carried away sometimes...
1589511436

Edited 1589522803
Vince
Pro
Sheet Author
Picture me rocking in the corner whispering "no no no no"...&nbsp; Lol.&nbsp; GiGs, I'm always willing to incorporate your suggestions.&nbsp; Nearly all of the sheetworker code on the sheet was done by me (non-coder) years ago.&nbsp; I know a little more now, thanks to your tutelage and forum posts. ;-)&nbsp; I really appreciate any help that you are willing to offer.&nbsp; I'll change over to your updated versioning function as well as use it to implement a couple attribute updates I should make to the sheet.&nbsp; Thank you.
1589520219
GiGs
Pro
Sheet Author
API Scripter
hehe, great.
1589538806
ᐰndreas J.
Forum Champion
Sheet Author
I added a link to here on the <a href="https://wiki.roll20.net/Sheet_Author_Tips" rel="nofollow">https://wiki.roll20.net/Sheet_Author_Tips</a> page, so there is now a section for "sheet versioning" to look up or reference in the future.