Welcome to the Onshape forum! Ask questions and join in the discussions about everything Onshape.

First time visiting? Here are some places to start:
  1. Looking for a certain topic? Check out the categories filter or use Search (upper right).
  2. Need support? Ask a question to our Community Support category.
  3. Please submit support tickets for bugs but you can request improvements in the Product Feedback category.
  4. Be respectful, on topic and if you see a problem, Flag it.

If you would like to contact our Community Manager personally, feel free to send a private message or an email.

Execute FeatureScript using API and Python

Hello,

I am trying to use the Onshape API and Python to evaluate a FeatureScript that I created to process an imported .CSV file.

This is what I have built so far;
# Import Libraries and/or Modules

from    apikey.client   import Client

# Set-Up Stacks
stacks = {
    'cad': 'https://cad.onshape.com'
}

# Create Instance for Onshape Stack
c = Client(stack=stacks['cad'], logging=True)

# Generate New Onshape Document
new_doc = c.new_document(name="input",public=True).json()
did = new_doc['id']
wid = new_doc['defaultWorkspace']['id']

# Get Document Information
details = c.get_document(did)
print 'Document name: ' + details.json()['name']

# Import Dataset
input_file_path = "data/demo_rand_array.csv"
input_file = c.upload_blob(did, wid, filepath=input_file_path)
eid = details.json()['id']<br>
In short:
  1. Created a new document
  2. Uploaded an external file (blob)
I am currently stuck in the implementation/execution of a FeatureScript that I built called "import_data" (working title). So far I have this;
details = c.evaluate_featurescript(did, wid, eid, script_name='import_data')
Where the function evaluate_featurescript() was build on the apikey.client file;
# Evaluate FeatureScript
    #
    # The following function evaluates or executes a specific FeatureScript within the context of a part studio
    def evaluate_featurescript(self, did, wvm, eid, script_name='0'):

        req_headers = {
            'Content-Type': 'application/json'
        }

        payload = {
            'script': script_name
        }
        
        return self._api.request('get', '/api/partstudios/d/' + did + '/[wvm]/' + wvm + '/e/' + eid + '/featurescript', headers=req_headers, body=payload)
I built this following the structure of the other functions, but I did notice some differences.

Now, when I run the program, I actually just get:

request failed, details: {
"moreInfoURL" : "",
"code" : 0,
"message" : "Not found.",
"status" : 404
}
So my guess is that I need to import the FeatureScript to my document, but all the API functions seem to be oriented towards creating one. Do I have to create it every time?

Also, I am currently using "eid" as the element Id of the imported .CSV file (blob). Will this have to be substituted with the "eid" of the FeatureScript?

Can someone provide something close to an example?

Thank you, thank you, thank you!

Comments

  • mark_noyesmark_noyes Member, Onshape Employees, Developers Posts: 21
    Hi, there are several issues to point out here.

    First, to address your question about eid, this is intended to be an elementId, which is expected to be the elementId of a Part Studio element in the document that you want to access. 

    Next, the URL you are constructing is using '[wvm]' as a literal. It is intended to mean "choose one of w or v or m". The wvm argument must then agree with that (i.e. must be a workspaceId or versionId or microversionId). This is why you are getting a 404 response.

    Additionally, the URL that it appears you are trying to call is a POST API only, so once you get the URL to be correct, the GET will result in a 405 response.

    Also, the POST body has a special form to it, and your input isn't shown here, but I suspect it's not in the right form. You can see details on what it should look like in the developer portal help topic titled "Feature list API".

    Perhaps the most important point to make is that this API is used to execute a literal FeatureScript function that executes in a transient context (i.e. no side effects), so it likely won't do what you are hoping for, unless you are just looking for it to return some results to you. It will not modify the document.

    If you have a Feature that you want to exercise, you need to create a FeatureStudio from it, either by manually uploading it or by calling the Create Feature Studio API. If the FeatureScript is static, you can create a feature studio and create a version of the document containing it and then reference it from other documents. To do so, you would create a feature in a part studio (perhaps using the Part Studio Add Feature API). Be sure to see the section of the Feature list API help section titled Custom Features to understand how to set up the inter-document references.
  • fluvio_lobo_fenogliettofluvio_lobo_fenoglietto Member Posts: 36 PRO
    @mark_noyes

    First of all, thank you for the detailed response. It will take me some time to go over everything but I did not want to wait to ask about the issue you brought up.

    Perhaps the most important point to make is that this API is used to execute a literal FeatureScript function that executes in a transient context (i.e. no side effects), so it likely won't do what you are hoping for, unless you are just looking for it to return some results to you. It will not modify the document.

    I have built a FeatureScript that reads data from a .CSV file to generate sketches and loft these as faces. The code does not require any interaction other than gathering the data from the .CSV that I have successfully uploaded using my remote python script. Here is the code, if it helps;

    FeatureScript 701;
    import(path : "onshape/std/geometry.fs", version : "701.0");
    
    // Imports data as top-level constants, which can be accessed through e.g. PROFILE_1::BLOB_DATA.csvData
    import(path : "8349bfa1ec555b8e48de1120", version : "1b5f3508f0dadd9ac02337e1");
    
    annotation { "Feature Type Name" : "import_data" }
    export const import_data = defineFeature(function(context is Context, id is Id, definition is map)
        precondition
        {
            // nothing here for this script...
        }
        { // Processing input data
            var unit                = millimeter;                                               // units
            var input_array         = BLOB_DATA.csvData;                                        // storing csv data into array variable
            var input_array_length  = size(input_array);                                        // determining length of input data
            //println(input_array_length);
            
            var index_array = [];                                                               // initialize index array - index of vertex or data point
            var x_array     = [];                                                               // initialize x coordinate array
            var y_array     = [];                                                               // initialize y coordinate array
            var z_array     = [];                                                               // initialize z coordinate array
            var vertex      = [];                                                               // initialize vertex nx3 array
            
            for (var i = 0; i < input_array_length; i += 1)                                     // populating data arrays
            {
                index_array = append(index_array, input_array[i][0]);
                x_array     = append(x_array,input_array[i][1]);
                y_array     = append(y_array,input_array[i][2]);
                z_array     = append(z_array,input_array[i][3]);
                vertex      = append(vertex, vector([x_array[i], y_array[i]])*unit);            // need to understand their vector structure
            } // end of for-loop
            
            
            var number_of_planes = 5;
            var plane_separation = 10;
            var k = 0;
            var sketches = [];
            
            for (var i = 0; i < number_of_planes; i += 1)                                       // plane/sketch loop - only needs to ad counter to the id of the sketch element
            {
                
                sketches = append(sketches, qCreatedBy(id + i, EntityType.FACE));
                var sketch = newSketchOnPlane(context, id + i, {                           // create sketch on a custom plane
                        "sketchPlane" : plane(vector(0, 0, k) * unit, vector(0, 0, 1))
                });
                /*
                var sketch1 = newSketch(context, id + "sketch1", {                              // sketch creation
                        "sketchPlane" : qCreatedBy(makeId("Top"), EntityType.FACE)
                });*/
                skFitSpline(sketch, "spline1", {
                        "points" : vertex
                });
                skSolve(sketch);
                
                k = k + plane_separation;
            }
            
            println(sketches);
            
            
            opLoft(context, id + "loft1", {
                    "profileSubqueries" : sketches,
            });
            
            
        });
    Essentially, the only thing I want is to execute this script. I don't need to manipulate the program nor take additional input from the user. Is this considered modifying the document?

    I am a little lost on your last paragraph, but perhaps this bit of additional information would help clarify my application. I am not sure what is the difference between a static/dynamic FeatureScript. My script does not change nor I plan to edit it. The only thing that will ever change dynamically (on every execution) would be the input .CSV file that gets imported.

    Let me know if I can provide more information,


  • mark_noyesmark_noyes Member, Onshape Employees, Developers Posts: 21
    As a high-level answer, your feature modifies the document by creating sketches and lofts. The Evaluate FeatureScript api can make calls such as these, but their effect does not persist beyond the end of the function call. I think what you want to do is to add a feature to the part studio, which would cause the sketches and lofts to persist in the part studio.
  • fluvio_lobo_fenogliettofluvio_lobo_fenoglietto Member Posts: 36 PRO
    @mark_noyes

    Mark,

    Once again, thank you for the prompt reply. I think I now understand, but I have two interpretations of your answer.

    Just a few more questions;
    1. When you say add a feature do you mean adding a FeatureScript script/code to the part studio? - I am a little confuse with the wording here. Or, do you mean adding something like an extrusion? 
    2. When you say the effects do not persist, do you mean that the solid is not created within the part studio? - My goal is to export this solid, so I could technically live with a temporary part as long as it can be exported at the end of the function call.
    3. Assuming a feature (= extrusion, cut) has to be created, which is not an issue, could this creation also be made through the FeatureScript or the API? - My ultimate goal is to automate the entire process. My main requirement is: NO MANUAL STEPS.
    Thank you!
  • Jake_RosenfeldJake_Rosenfeld Moderator, Onshape Employees, Developers Posts: 1,646
    @fluvio_lobo_fenoglietto

    What mark is trying to say here is that you cannot use the Evaluate FeatureScript API to create lasting changes to a document.  As soon as your REST request completes, the changes you've made are gone. When Mark says "add a feature" he means adding another feature to the feature list of a part studio.  This can be anything, an extrude, loft, fillet, or (in your case) an instance of the feature you've created.  So what he is saying here is that you should upload your FeatureScript feature as a separate file, do the following:

    1) Make an api call to add an instance of your feature to the part studio
    2) Make an api call to export your solid
    3) (optional - if you want to clean up after yourself) Make an api call to remove the instance of your feature from the part studio.

    In terms of the ui, this is the equivalent of clicking on the button for your custom feature, then exporting a solid, then right clicking on your feature in the feature list and deleting it.


    Mark also mentions versioning the FeatureScript file you upload.  This is important in the context of how custom features work in Onshape. You can upload your feature once in a separate document, then, as long as you version that document, you can create instances of your feature in other documents.

    Although your feature looks like it relies on changing the import path for the CSV file, so maybe its better for you to do this document-by-document, calling the Create Feature Studio API and injecting your FeatureScript (with the import changed), and executing using steps 1, 2, and 3 from above.
    Jake Rosenfeld - Modeling Team
  • fluvio_lobo_fenogliettofluvio_lobo_fenoglietto Member Posts: 36 PRO
    @Jake_Rosenfeld

    Jake,

    Thanks for the explanation. I just want summarized and confirm. I want to make sure I am looking at the right API calls.
    For your 1-2-3 step, would the functions call be:
    1. Add Feature
    2. Export Part Studio to STL - I will use STEP later, I am aware I need to use translation for this
    3. Delete Feature (optional)
    In general, I would not even use Execute FeatureScript.
    If I follow the path on the last paragraph:
    1. Create Feature Studio - Literal parse the raw text/script
    2. Add Feature - Adding the FeatureScript that I just created?
    3. Export Part Studio to STL
    4. Delete Feature (optional)
    If this is correct, my only question is. How does the Add Feature function know the custom feature? Do I have to publish the feature? Is that what you mean with versioning? I have not gone that far with my custom feature (import_data) #N00B

    Thank you for your patience and sorry for the inconvenience,

  • mark_noyesmark_noyes Member, Onshape Employees, Developers Posts: 21
    Yes, creating a version of a document publishes a specific state of the document (including the FeatureScript code) for use by other documents.

    Take a look at the Feature list API section of the dev-portal help. It provides simple examples of how to add features. Be sure to pay special attention to the section titled "Custom Features". It explains how the namespace field in the json body of your Add Feature call identifies where your custom feature code lives. It an be in another tab in the same document or in some other document.
  • saujan_rajbhandarisaujan_rajbhandari Member Posts: 1
    i am getting an error when i run app.py please help.
Sign In or Register to comment.