Is there a working Bulk Submission script?

I’ve tried a few versions of submission scripts and none of them seem to work in the latest lab. I’m trying to find a working one to “borrow/steal” the “Submit” part to meld with a Mutate with Random Barcode variant. Unfortunately I can’t find any scripts where the Submit part works. I get several different errors from different scripts.

Sorry I can’t help but thanks for spending so much time on the project.

While I don’t know what the state of the art is (beyond the current “curated” mutation/submission booster being the latest “official” version), if there’s specific issues you’re running into that you can’t figure out, I can try to help debug (given details on the issue, reproduction scenario, etc).

I am having trouble with the curated Mutation/Submission script. It returns an error: “Cannot read properties of undefined”. I don’t seem to find the script by searching for it’s name, so perhaps it is an “unlinked” script? (What’s it’s scriptID?)

Replicate the problem:

Take an existing submission. Select two paired bases in the barcode. Open the Mutation/Submission booster. Hit Mutate, then Prune, then Goto. There should be 6 or less variants less. Try to Submit.

It should popup an error confirmer.

Ah. I think I found its script ID using the Javascript console: script 8069673

https://eternagame.org/web/script/8069673/

It appears to be the following line in BLv4Lines.prototype.postDesign that is causing the problem:

outln(id+cst);

When I comment that out the script works to submit designs. Perhaps left over from development/testing?

Probably. I’ve updated script 8069673 commenting out that line.

OK. I think https://eternagame.org/web/script/11657048/ should work for the current lab. When asked to “mutate” it generates random 13-base barcodes with some constraints to avoid long runs of similar bases as well as some patterns that tend to pair with the fixed tail. There still should be over 2 million possibilities. It does not guarantee that the barcode has not been used but the probability is low. It can also be used to check off-line generated sequences, in which case it does not randomize the barcode but expects that you have already generated a unique one (in case you want to optimize the design in conjunction with the barcode stem and hairpin, which I usually do). Even with a full 90K submissions there should be a less than 0.5% chance of a barcode collision for any given design, so it probably will suffice for most uses.

MasterStormer was already working on a variant of the mutation booster, I don’t know if he already got unused barcode checks working yet, but I know he had started on it or at least planned to Eterna - Invent Medicine.

I tried starting from MasterStormer’s script but could not get it working. I had problems with calling async functions from non-async functions and it seemed to be a “turtles all the way down” problem I could not figure out. Perhaps @MasterStormer or @LFP6 could figure it out but it’s above my JavaScript skillset. So I settled for just randomized barcodes. I left the async functions in my script but don’t call them, so if there is a way to untangle that mess perhaps someone with more JavasScript savvy can figure it out. In any case I have it working well enough for my needs for now.

One thing I did notice was that the use of Lib.randomSequence and Lib.nextSequence did not work very well for generating random barcodes. The nextSequence function tended to create longer runs of bases and generated the same sequence multiple times in a batch. I generated my own randSeq function and call it repeatedly instead. It should spread out the barcodes more across the available range.

Yeah if you use async/await, you can only await async calls from within an async function, and that does indeed “turtle”. :slight_smile: If you’re ok with not waiting for an asynchronouos operation to complete before continuing, you can use .then on the return of the async function (which will be a Promise) instead. If you’re familiar with coroutines, same idea!

Alas I’m NOT familiar with JavaScript’s implementation of co-routines. Generators with yield is the max of my comfort level and that’s pushing it. Is there any way to “wait” for a Promise to complete? Is that what the .then does and what would the usage syntax be like? I.e. assuming an async function getUnusedBarcode() is there any way to meaningfully call it and resolve the promise from a “normal” function? Would getUnusedBarcode.next or getUnusedBarcode.next() return a result for me to use?

As far as I can tell it is not possible to “wait” for a Promise any normal sense, so I don’t know how to integrate my code with “await” style functions.

Quick primer:

async getNumber() { return 5 }

If I call getNumber(), the return will be a Promise object that resolves to 5, ie once it completes (though in this case it can complete immediately and always returns a static value) its resulting value is 5. You can wait for the result of the coroutine and do something with it in two ways:

  1. Write an async function and await it.
async function g() { let x = await f(); console.log(x) }
  1. Register a callback on the promise
f().then((x) => console.log(x))

In the case of option 1, execution of the async function stops until the call is done. In the case of option 2, it says “run this code once the callback is complete”, and just continues on executing whatever code comes next.

You could also call f() within a non-async function without .then, which will kick off execution of it and then continue, but throw out the resulting value. You can also do things like Promise.all(<array of promises) to get a promise that resolves with an array of the results of all the promises provided once they all complete.

Javascript does not allow you to synchronously wait on a promise - in general you don’t want to do that anyways since JS is singlethreaded and it means the browser window hangs while doing whatever it is (in this case, waiting on a network request). So to review, your two options are either to trace back your functions to make them all async so that you can “pause” to wait for the value to come back, or at some point along the chain, you just register a callback and continue on.


Separately, there is one important point I just pinged Guy about and will also record here. The regex he uses to get the current puzzle ID from the URL will not work in the mobile app. The regex expression I had come up with previously looked like this: href.match(/(Application\/[^\/]+\/)?[^\d]+(\d{6,})/)[2];

1 Like

My understanding is that it must be “async function g() {…}” not “function g() {…}” aka turtles all the way down.

I might be able to get away with “f().then((x) => foo(x))” assuming I can wrap my head around it.

Edited:

Actually I tried the equivalent of the “f().then((x) => foo(x))” syntax and the booster failed to load but I also tried “asyncf(x).then(callbackg)” where I defined the equivalent of “callbackg = function(x) {…}” and that worked so I now have a way to call an async function and pass its result to a non-asynchronous callback.

Thanks.

Yep you’re right, g would be async. Edited.

As a mater of practice, my preference is to opt to make the entire codepath/stack async unless I truly want the async operation to be concurrent. That helps to clarify and ensure correct behavior for order of execution, and avoids lots of callbacks/extra scopes/indirection. Of course that said, whatever works!

@LFP6 Version 3 https://eternagame.org/web/script/11657566/ of the script Mutation with Random Barcode Booster with Check/Prune/Delete/Submit for OpenKnot Round 1 (v3.0) checks the barcodes against the set of used barcodes at mutation time. It’s possible although unlikely that someone else could pick the same barcode before submission. It uses async functions although not as fully and cleanly as if it had been designed that way from the start. But at least it seems to work and does not just rely on assuming that random barcodes are unique.

Update: Alas the “Check” function does not work at the moment although mutate, prune, delete, and submit work. Also it only seems to be using 12 of the 13 barcode bases. I’ll have to try and figure what that is about.

@jandersonlee Thank you for updating the script for players. I submitted a series using your first version yesterday and the custom target structure may not have carried over. Jonathan will look into it. My plan is to take a known pknot and enter the known target structure, then mutate a few bases to test if the pknot still forms. I won’t be able to Prune in many cases because the known pknot often is not predicted by EFoldThresh. I’ll manually Delete mispairs instead.

@DigitalEmbrace @LFP6 Version 3.1 https://eternagame.org/web/script/11657798/ of the script has a few bugfixes and removes the “Check” button which wasn’t working quite as expected. Probably better to have a non-mutating bulk check/upload script anyways. You can still sort of use the script to manually check and submit off-line generated sequences, you just need to manually step through them and use delete. I don’t recommend using the Mutate button with the NuPACK energy model enabled. It’s VERY SLOW and times out periodically.

For the moment the PUZZLE_ID and BARCODE_SIZE is hardcoded into the script. If there is a way to grab those values from the applet the script might be made more general.

Let me know if there are any other minor tweaks you can think of.