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.

Geometric Variants using API, onshape-client

fluvio_lobo_fenogliettofluvio_lobo_fenoglietto Member Posts: 36 PRO
edited September 3 in Using Onshape
@ethan_keller924

I am finally trying to get back to this project we started about a year ago!
https://forum.onshape.com/discussion/11860/geometric-variants-derivatives-using-onshapepy-api

I was trying to get familiar with the onshape-client repo., but I am afraid I may be a little rusty. :sweat_smile:

Our code accesses a document, changes some configurations, and exports an .STL
I want to make sure we can do the same thing with onshape_clients.

Here are a few sections of the module where you could, perhaps, just point our the basic changes.
Here is the function we created to define the client
def connect_to_sketch( self, args ):
    '''
    CONNECT TO ONSHAPE DOCUMENT
    '''
    
    print('[{:0.6f}] Connecting to Onshape document'.format(current_time( self )))
    
    if( len(self.did) != 24 or                                              # Ensure inputted IDs are valid
        len(self.wid) != 24 or                                              # ...
        len(self.eid) != 24 ):                                              # ...
        raise ValueError( "Document, workspace, and element IDs must each be 24 characters in length" )
    else:
        part_URL    = "https://cad.onshape.com/documents/{}/w/{}/e/{}".format( self.did, self.wid, self.eid )
        self.myPart = Part( part_URL )                                      # Connect to part for modification
        self.c      = Client()                                              # Create instance of the onshape client for exporting
    
Here is our function for exporting the STL
def export_stl( self ):
    '''
    EXPORT STL OF GENERATED PART/VARIANT
    '''

    print('[{:0.6f}] Export new configurations from Onshape'.format(current_time( self )))

    variant_iter                            = self.variant_iter
    partname                                = self.partname
    dest                                    = self.dst

    stl = self.c._api.request('get','/api/partstudios/d/{}/w/{}/e/{}/stl'.format(self.did, self.wid, self.eid))

    stl_filename = ('{}{}_var{}.stl'.format(dest, partname, variant_iter))
    
    with open( stl_filename, 'w' ) as f:                                                                                            # Write STL to file
        f.write( stl.text )

    self.stl_filename                       = stl_filename 

Your help is much appreciated!

Comments

  • fluvio_lobo_fenogliettofluvio_lobo_fenoglietto Member Posts: 36 PRO
    edited August 13
    Made some small progress here but there are a few steps that are unclear to me from the api. Here is my codel;
    from onshape_client import OnshapeElement, Client
    
    # variables
    config_filename = '.onshape_client_config.yaml'
    # onshape variables
    did = '4106f8fea9cf4607edeba1db'
    wid = 'c11cf0ae6ab5e6297d09562d'
    wvmid = '54baa80481e28eb15207e067'
    eid = '3340d6f3b50b6e32e22d9a3b'
    
    # do things
    client = Client( config_filename ) # create client
    client.part_studios_api.export_stl1(did, wid, wvmid, eid, _preload_content=False)
    When I execute this, I get the following error;
    onshape_client.oas.exceptions.ApiException: (404)Reason: Not FoundHTTP response headers: HTTPHeaderDict({'Server': 'nginx', 'Date': 'Thu, 13 Aug 2020 15:50:13 GMT', 'Content-Type': 'application/json;charset=utf-8', 'Content-Length': '84', 'Connection': 'keep-alive', 'Vary': 'Accept-Encoding', 'On-Version': '1.117.25799.ec2b15afb7f4', 'On-Request-Id': '38e3bed0508d9712b49878ed', 'Strict-Transport-Security': 'max-age=31536000; includeSubDomains', 'X-Content-Type-Options': 'nosniff', 'X-XSS-Protection': '1; mode=block'})HTTP response body: b'{\n  "moreInfoUrl" : "",\n  "message" : "Not found.",\n  "status" : 404,\n  "code" : 0\n}'
    What is the best way of creating an OnshapeElement so that I can pass the right structure to the "part_studio_api" functions?
  • pmdpmd Member, Developers Posts: 43 PRO
    The signature for export_stl1 is (did, wvm, wvmid, eid, ...) where wvm means one of "w", "v" or "m" so pass in "w" as your wid parameter.

  • fluvio_lobo_fenogliettofluvio_lobo_fenoglietto Member Posts: 36 PRO
    Thank you @pmd!

    I was able to correct this on my program.
    I also found another forum post that discussed how to use OnshapeElement() so my latest program looks like this;

    # modules, libraries
    from onshape_client import OnshapeElement, Client
    
    # variables
    config_filename = '.onshape_client_config.yaml'
    # onshape variables
    did = '4106f8fea9cf4607edeba1db'
    wid = 'c11cf0ae6ab5e6297d09562d'
    eid = '3340d6f3b50b6e32e22d9a3b'
    
    # do things
    client = Client( config_filename ) # create client
    element = OnshapeElement('https://cad.onshape.com/documents/{}/w/{}/e/{}'.format(did,wid,eid))
    
    response = client.part_studios_api.export_stl1(element.did,
                                        element.wvm,
                                        element.wvmid,
                                        element.eid,
                                        _preload_content=False)

    Now (of course) I have different problems;

    If I run the code above, I get this error
    raise ApiException(http_resp=r)
    onshape_client.oas.exceptions.ApiException: (404)
    Which I believe has to do with a mismatch between the base url on the config. file (.yaml) and the url I use for the OnshapeElement function.
    To solve this, I test and "fix" this I changed the address on the config file to;
    prod_api_keys:
      base_url: "https://cad.onshape.com/documents"
      secret_key: ---censored---
      access_key: ---censored---
    default_stack: prod_api_keys
    The code then ran without errors, but nothing exports and writing the .STL data from the response does not generate a valid geometry file.
    I am using this code for writing the response into a .STL file
    file = 'out.stl'
    with open(file, 'wb') as f:
        f.write(response.data)

    So I am stuck again  :'(

  • jakeramsleyjakeramsley Member, Moderator, Onshape Employees, Developers Posts: 617
    Hi fluvio_lobo_fenoglietto,

    I believe the issue is your base_url should be "https://cad.onshape.com" instead of "https://cad.onshape.com/documents".  The end point for STL export is "https://cad.onshape.com/api/partstudios/d/{did}/{wvm}/{wvmid}/e/{eid}/stl".  The way you have it written now I believe will make a call to "https://cad.onshape.com/documents/api/partstudios/d/{did}/{wvm}/{wvmid}/e/{eid}/stl" which I believe will return 404 html page.

    I'm no python export, so I may be misinterpreting the code, but the STL export endpoint requires a redirect to download.  What I am assuming is happening is the malformed link is giving null for a redirect URL, which the python script is following and getting an empty response.
    Jake Ramsley

    Director of Quality Engineering & Release Manager              onshape.com
  • fluvio_lobo_fenogliettofluvio_lobo_fenoglietto Member Posts: 36 PRO
    @jakeramsley

    Thanks for the response and sorry for the delay.

    Correct me if I am wrong, but I think there are two ways of solving this issue. I just want to understand both so that I can 'scale-up'

    First, the semi-hardcoded way, where I just have python construct the URL and send the request via the webbrowser library.
    # modules
    import webbrowser
    
    # onshape variables
    did = '4106f8fea9cf4607edeba1db'
    wid = 'c11cf0ae6ab5e6297d09562d'
    eid = '3340d6f3b50b6e32e22d9a3b'
    
    # do things
    webbrowser.open_new('https://cad.onshape.com/api/partstudios/d/{}/w/{}/e/{}/stl?'.format(did,wid,eid))
    Here I do not need to use the API functions at all.
    However, can anyone else run this script and download the STL?
    Do other users need to have an API key?

    Second, the api way, where I actually use the Onshape API to do this, is where I am having most of my issues... Here is what I have so far;

    # modules
    from onshape_client import OnshapeElement, Client
    
    # variables
    config_filename = '.onshape_client_config.yaml'
    did = '4106f8fea9cf4607edeba1db'
    wid = 'c11cf0ae6ab5e6297d09562d'
    eid = '3340d6f3b50b6e32e22d9a3b'
    
    # do things
    client = Client( config_filename ) # create client
    
    element = OnshapeElement('https://cad.onshape.com/documents/{}/w/{}/e/{}'.format(did,wid,eid)) # create onshape element
    
    response = client.part_studios_api.export_stl1(element.did,
                                        element.wvm,
                                        element.wvmid,
                                        element.eid,
                                        _preload_content=False)

    I think my problem is understanding the need and proper use of the OnshapeElement and client.part_studios_api.export_stl1() functions.
    If I OnshapeElement as shown above and print the values element.did, wvm, ...etc, I get the right values.
    Still, when I use the client.part_studios_api.export_stl1() function, I now get this error;

    onshape_client.oas.exceptions.ApiException: (404)
    Reason: 
    HTTP response headers: HTTPHeaderDict({'Server': 'nginx', 'Date': 'Thu, 20 Aug 2020 15:45:39 GMT', 'Content-Type': 'text/html;charset=utf-8', 'Content-Length': '789', 'Connection': 'keep-alive', 'Vary': 'Accept-Encoding', 'Last-Modified': 'Mon, 17 Aug 2020 16:27:55 GMT', 'ETag': 'W/"eQBDZHccMAEeQBCFIojmew"', 'Cache-Control': 'no-cache', 'Strict-Transport-Security': 'max-age=31536000; includeSubDomains', 'X-Content-Type-Options': 'nosniff', 'X-XSS-Protection': '1; mode=block'})
    HTTP response body: b'<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">\n\n<html>\n<head>\n  <meta http-equiv="Refresh" content="5; url=/">\n  <title>www.onshape.com</title>\n  <link rel="shortcut icon" type="image/ico" href="/favicon.ico" />\n</head>\n<body class="page-rec">\n  <div id="wrapper">\n    <div id="content" style="padding:100px;">\n      <div style="padding: 10px; -moz-border-radius: 5px 5px 5px 5px; background: -moz-linear-gradient(center top, rgb(255, 249, 204) 0%, rgb(255, 237, 116) 100% ) repeat scroll 0pt 0pt transparent; font-size: 26pt; font-family: \'QuicksandBook\', Helvetica, Arial, sans-serif; text-shadow: 1px 1px 1px rgb(255, 255, 255); line-height: 1.2em; width: 730px;">\n
    Access denied. Click <a href="/">here</a> to go to home page.\n      </div>\n    </div>\n  </div>\n</body>\n</html>\n'

    What is the proper way of using these functions?



  • pmdpmd Member, Developers Posts: 43 PRO
    It looks like you are doing the same as the test suite for onshape_clients does:

    https://github.com/onshape-public/onshape-clients/blob/0a3fa9c73e64bf6c2451a98a4c0677a0e222eca6/python/test/test_part_studios_api.py#L48

    so given the error is "Access denied" I would check if your credentials match the user who has access to that document. Note that "export" permission has not been granted on that doc to anyone other than the owner as far as I can see.

    Personally I found that onshape_clients was too complex to understand when debugging and unfinished (_preload_content=False.....) and just wrote my own wrapper to do the few API calls I needed using requests.


  • fluvio_lobo_fenogliettofluvio_lobo_fenoglietto Member Posts: 36 PRO
    @pmd

    Can you elaborate on this;
    so given the error is "Access denied" I would check if your credentials match the user who has access to that document. Note that "export" permission has not been granted on that doc to anyone other than the owner as far as I can see.
    As far as I know, the file is public and can be shared/exported by all onshape users... (see below)


    Also,

    Would you mind sharing an example function for how you handle requests directly?
    I was trying using client.api_client.request() earlier, with 'POST' and URL as the inputs, but I get the a different error.

    Here is my call;
    client.api_client.request('POST','https://cad.onshape.com/api/partstudios/d/{}/w/{}/e/{}/stl?'.format(did,wid,eid))

    With the error;
    Traceback (most recent call last):
      File "C:\Users\WOLF512\AppData\Local\Programs\Python\Python38-32\lib\urllib\parse.py", line 905, in urlencode
        if len(query) and not isinstance(query[0], tuple):
    TypeError: object of type 'NoneType' has no len()

    Thank you!
  • jakeramsleyjakeramsley Member, Moderator, Onshape Employees, Developers Posts: 617
    edited August 21
    @pmd

    Can you elaborate on this;
    so given the error is "Access denied" I would check if your credentials match the user who has access to that document. Note that "export" permission has not been granted on that doc to anyone other than the owner as far as I can see.

    It's likely something wrong in your config yaml file.  Either the base_url is not set to `https://cad.onshape.com` or there is a problem with your key/secret pair.

    Can you sign into Onshape then go to `https://cad.onshape.com/api/users/apikeys` and validate that the API key you have set there is showing?  If not, then you will have to generate/switch out the key/secret pair.

    NOTE:
    I know you haven't, but even while triaging issues like this, under no circumstance ever post your secret key and make sure all important data is sanitized before posting here.

    Also,

    Would you mind sharing an example function for how you handle requests directly?
    I was trying using client.api_client.request() earlier, with 'POST' and URL as the inputs, but I get the a different error.

    Here is my call;
    client.api_client.request('POST','https://cad.onshape.com/api/partstudios/d/{}/w/{}/e/{}/stl?'.format(did,wid,eid))

    With the error;
    Traceback (most recent call last):
      File "C:\Users\WOLF512\AppData\Local\Programs\Python\Python38-32\lib\urllib\parse.py", line 905, in urlencode
        if len(query) and not isinstance(query[0], tuple):
    TypeError: object of type 'NoneType' has no len()

    Thank you!
    This call is a `GET` not a `POST`.  The error looks like a check that you are doing a `POST` with no body.  If you do a `GET`, you should be getting back a redirect URL to follow to the get the STL.
    Jake Ramsley

    Director of Quality Engineering & Release Manager              onshape.com
  • pmdpmd Member, Developers Posts: 43 PRO
    @fluvio_lobo_fenoglietto  When I went to the URL and right clicked on Part1 there was no "Export" option. I just tried it again and it now has an export option so do not know what happened.

    I can't share any code at current since not in a useful (standalone) state - sorry.

     
  • fluvio_lobo_fenogliettofluvio_lobo_fenoglietto Member Posts: 36 PRO
    @jakeramsley

    My configuration file is now;
    prod_api_keys:
      base_url: "https://cad.onshape.com/"
      secret_key: ----
      access_key: ----
    default_stack: prod_api_keys
    I just created a new set of keys.

    When I follow the link https://cad.onshape.com/api/users/apikeys, the accessKey is displayed and the secretKey is shown as null

    Either way, I get the same error... which I think refers to how I am structuring the request...?
    Traceback (most recent call last):
      File "C:\Users\WOLF512\AppData\Local\Programs\Python\Python38-32\lib\urllib\parse.py", line 905, in urlencode
        if len(query) and not isinstance(query[0], tuple):
    TypeError: object of type 'NoneType' has no len()
    ...

    I also took a side step and tried using the python requests module. I think there is something fundamental that I am not understanding about requests (in general). The following code is, again, an attempt to send the same STL request...

    # modules
    import requests
    import json
    from onshape_client import OnshapeElement, Client
    
    # variables
    config_filename = '.onshape_client_config.yaml'
    did = '4106f8fea9cf4607edeba1db'
    wid = 'c11cf0ae6ab5e6297d09562d'
    eid = '3340d6f3b50b6e32e22d9a3b'
    
    
    # do things
    client = Client( config_filename )
    
    response = requests.get(    'https://cad.onshape.com/api/partstudios/d/{}/w/{}/e/{}/stl'.format(did,wid,eid),
                                params=client.configuration.api_key )
    Here I am using onshape-client so I can extract the api_keys and pass them as payload in the GET request...
    But I still get a 401 response...
    {'message': 'Unauthenticated API request', 'status': 401}
    Using the requests module, what is the best way of sending credentials to be able to execute the request?



  • Ethan_KEthan_K Member, Onshape Employees Posts: 52
    @fluvio_lobo_fenoglietto Here is a working test that shows how to export the PS to STL: https://github.com/onshape-public/onshape-clients/blob/master/python/test/test_export.py#L39 . Does that help? This runs as part of our test suite.
  • fluvio_lobo_fenogliettofluvio_lobo_fenoglietto Member Posts: 36 PRO
    Thank you all for the help,

    Here is the working code:
    # modules
    from onshape_client import OnshapeElement, Client
    
    # variables
    config_file = '.onshape_client_config.yaml'                                                     # configuration file
    did = '4106f8fea9cf4607edeba1db'                                                                # did
    wid = 'c11cf0ae6ab5e6297d09562d'                                                                # wid
    eid = '3340d6f3b50b6e32e22d9a3b'                                                                # eid
    
    # do things
    client = Client( keys_file=config_file )                                                        # create client using the specified configuration file
    element = OnshapeElement('https://cad.onshape.com/documents/{}/w/{}/e/{}'.format(did,wid,eid))  # create onshape element
    response = client.part_studios_api.export_stl1(did, 'w', wid, eid, _preload_content=False)
    
    # writing STL file
    with open( '{}.stl'.format(element.name), 'wb' ) as file:
        file.write(response.data)
    file.close()

    The program is not completely optimized and a little redundant, but it works!
    The issue I was having was resolved by the _preload_content = False input.
    I was not aware that the "redirect warning" was just a "warning" instead of an error  :p



Sign In or Register to comment.