Automate download of CAD files to send to Supplier

Good evening, my Purchasing team is constantly getting Suppliers to quote building a certain assembly. In order to prepare the quote, the Supplier needs all of the SolidWorks CAD files from Aras. This means the parent SLDASM and all related child SLDPRT files. Currently, the Purchasing team is downloading these files 1-by-1 from Aras, before emailing them to the Supplier.

This seems like a fairly common industry task. Has anyone done any development to automate this, specifically the recursive file download? I've seen a GitHub project to download multiple file attachments via a single action, but this is a slight different use case.

Thanks in advance!

Paul Sheehy

Parents
  • Hi Paul,

    The Download Multiple Files community project works by downloading the files a user has selected in the grid, but you can use the same general process for downloading multiple files from a structure.

    You can split this into a couple smaller problems.

    1. Do a recursive call to get all of the items in the CAD Structure
    2. Get a list of Files that need to be downloaded
    3. Download all of the Files in the list to the server

    The first problem of performing a recursive call can be solved by using the GetItemRepeatConfig action described in this blog post. We use the Part -> Part BOM structure as the example in that blog post, but you can easily rewrite the AML to work with CAD -> CAD Structure.

    The second is to get a list of all of the Files that need to be downloaded. You can do this pretty simply by using an XPath selector like //native_file/text() to avoid manually iterating through the entire structure. Depending on your exact AML query, this will get you a list of IDs for the Files that exist in your CAD Structure.

    For the last step of downloading all the files, you can look at the Create Zip on Server method of the Download Multiple Files project for some sample code that uses the Aras.IOME.CheckoutManager class to download a collection of File items to a location on the server. You can find a full reference of this class in the API guide available by logging into Aras Innovator as an admin and selecting Admin > API Reference (.NET) in the user menu. Once the files are in a common location on the server, you can package them up however you want to deliver them to the supplier.


    Chris

    Christopher Gillis

    Aras Labs Software Engineer

Reply
  • Hi Paul,

    The Download Multiple Files community project works by downloading the files a user has selected in the grid, but you can use the same general process for downloading multiple files from a structure.

    You can split this into a couple smaller problems.

    1. Do a recursive call to get all of the items in the CAD Structure
    2. Get a list of Files that need to be downloaded
    3. Download all of the Files in the list to the server

    The first problem of performing a recursive call can be solved by using the GetItemRepeatConfig action described in this blog post. We use the Part -> Part BOM structure as the example in that blog post, but you can easily rewrite the AML to work with CAD -> CAD Structure.

    The second is to get a list of all of the Files that need to be downloaded. You can do this pretty simply by using an XPath selector like //native_file/text() to avoid manually iterating through the entire structure. Depending on your exact AML query, this will get you a list of IDs for the Files that exist in your CAD Structure.

    For the last step of downloading all the files, you can look at the Create Zip on Server method of the Download Multiple Files project for some sample code that uses the Aras.IOME.CheckoutManager class to download a collection of File items to a location on the server. You can find a full reference of this class in the API guide available by logging into Aras Innovator as an admin and selecting Admin > API Reference (.NET) in the user menu. Once the files are in a common location on the server, you can package them up however you want to deliver them to the supplier.


    Chris

    Christopher Gillis

    Aras Labs Software Engineer

Children
  • Chris, thanks for the great feedback! I've got the AML query working to get all the child items:

    <Item type='CAD' select='item_number, native_file' action='GetItemRepeatConfig' id='{CAD parent id here}'>

    <Relationships>

    <Item type='CAD Structure' select='related_id' repeatProp='related_id' repeatTimes='0'></Item>

    </Relationships>

    </Item>

    But my xPath skills aren't very good. In trying to take those results and get a list of FileIDs, via:

    Item fileIds = qryResults.getItemsByXPath("//Item[@type='CAD']/native_file/text()");

    I get an error that the xPath does not resolve to Item nodes. Do I use another function instead of getItemsByXPath?

    Thanks!

    Paul

  • Where you ever able to figure this out?  I am trying to do the same thing.

  • 0 オフライン in reply to Nathan H.

    Hi Nathan

    Try below server method. This need to be configured in the Scheduler if you want to run and download the files automatically. Also you need to add the condition to pass the CAD item ID

    Innovator innovator = this.getInnovator();
    Item cadItem = innovator.newItem("CAD", "GetItemRepeatConfig");
    cadItem.setAttribute("select", "item_number,native_file");
    cadItem.setID("BF3D2A06F8234971988E063527E486D0");
    Item cadStructure = innovator.newItem("CAD Structure", "get");
    cadStructure.setAttribute("select", "related_id");
    cadStructure.setAttribute("repeatProp", "related_id");
    cadStructure.setAttribute("repeatTimes", "0");
    cadItem.addRelationship(cadStructure);
    cadItem = cadItem.apply();
    if(cadItem.isError())
    {
    return cadItem;
    }
    Item queryItem = cadItem.getItemsByXPath("//Item[@type='CAD']");
    var fileItemId = new List <string>();
    for(int i = 0; i < queryItem.getItemCount(); i++)
    {
    if(!string.IsNullOrEmpty(queryItem.getItemByIndex(i).getProperty("native_file")))
    {
    fileItemId.Add(queryItem.getItemByIndex(i).getProperty("native_file"));
    }
    }
    if(fileItemId.Any())
    {
    string folderPath = @"C:\Temp\Aras\MultipleFileDownload\" + innovator.getUserID();
    string filePath = folderPath + "\\Files";
    string zipPath = folderPath + "\\Zip";
    if (System.IO.Directory.Exists(filePath))
    {
    System.IO.Directory.Delete(filePath, true);
    }
    System.IO.DirectoryInfo di = System.IO.Directory.CreateDirectory(filePath);
    System.IO.DirectoryInfo zdi = System.IO.Directory.CreateDirectory(zipPath);
    Item files = innovator.newItem("File", "get");
    files.setAttribute("idlist", String.Join(",", fileItemId.ToArray()));
    files = files.apply();
    if (files.isError())
    {
    return files;
    }
    Aras.IOME.CheckoutManager cm = new Aras.IOME.CheckoutManager(files);
    cm.DownloadFiles(filePath, 1);
    string currentDateString = DateTime.Now.ToString(@"yyMMddHHmmss", CultureInfo.CreateSpecificCulture("en-US"));
    string zipName = string.Format(CultureInfo.InvariantCulture, "ZipFiles_{0}.zip", currentDateString);
    string fullZipPath = String.Format("{0}\\{1}", zipPath, zipName);
    try
    {
    System.IO.Compression.ZipFile.CreateFromDirectory(filePath, fullZipPath);
    if (System.IO.Directory.Exists(filePath))
    {
    System.IO.Directory.Delete(filePath, true);
    }
    }
    catch (InvalidDataException)
    {
    return innovator.newError("Could not create .zip archive");
    }
    }
    return this;

    This will download the file to C:\Temp\Aras\MultipleFileDownload\AD30A6D8D3B642F5A2AFED1A4B02BEFA\Zip location

  • 0 オフライン in reply to Gopikrishnan

    Gopikrishnan,

    Thank you for this information.  The big problem I have is the JavaScript method to pass the information.  I am trying to set this up as an Action.  Thank you again for this help.

    Nate

  • 0 オフライン in reply to Gopikrishnan

    Gopikrishnan,

    Your code works great.  I can pass a CAD ID to it and get the files zipped up.  I just can't get the rest of the JavaScript to download the file.  From what I am seeing, the "var fileItem" line is empty/null (used an Alert to try to see what it returns).  The JavaScript errors out on "top.aras.downloadFile(fileItem, "AllFiles.zip");".  I believe this is because the fileItem is empty.  Thanks for any help you can provide.

    var dir = "";
    
    var userItem = top.aras.getLoggedUserItem();
    var workingDirNode = userItem.selectSingleNode('./working_directory');
    
    if (workingDirNode) 
    {
        var logDir = workingDirNode.text;
    }
    
    // Create a zip on the server and upload it as a new file item to the vault
    var res = aras.applyMethod("File_Download-CAD2", "<file_ids>" + this.getID() + "</file_ids>");
    if (!res)
    {
        alert("Error when zipping files.");
        return;
    }
    
    dom = top.aras.createXMLDocument();
    dom.loadXML(res);
    
    // Download the uploaded zip to the user's working directory
    var fileItem = aras.getItemById("File", dom.firstChild.getAttribute("id"));
    top.aras.downloadFile(fileItem, "AllFiles.zip"); // TODO: Make this name relevant to the item this is called from
    
    // Lastly, delete the file from the server
    // NOTE: We're doing this in a timeout because otherwise, the file is deleted from the server before the client has a chance to download it.
    setTimeout(function(){
        var res = aras.applyMethod("Delete Zip from Server", "<id>" + dom.firstChild.getAttribute("id") + "</id>");
    
        if (!res)
        {
            alert("Error cleaning up files");
        }
    }, 3000);
    
    return;

  • +1 オフライン in reply to Nathan H.

    Dear Nathan

    One small modification in your server method should work I guess. Give a try and let me know if it works.

    Innovator innovator = this.getInnovator();
    Item cadItem = innovator.newItem("CAD", "GetItemRepeatConfig");
    cadItem.setAttribute("select", "item_number,native_file");
    cadItem.setID(this.getProperty("file_ids"));
    Item cadStructure = innovator.newItem("CAD Structure", "get");
    cadStructure.setAttribute("select", "related_id");
    cadStructure.setAttribute("repeatProp", "related_id");
    cadStructure.setAttribute("repeatTimes", "0");
    cadItem.addRelationship(cadStructure);
    cadItem = cadItem.apply();
    if(cadItem.isError())
    {
    	return cadItem;
    }
    Item queryItem = cadItem.getItemsByXPath("//Item[@type='CAD']");
    var fileItemId = new List <string>();
    for(int i = 0; i < queryItem.getItemCount(); i++)
    {
    	if(!string.IsNullOrEmpty(queryItem.getItemByIndex(i).getProperty("native_file")))
    	{
    		fileItemId.Add(queryItem.getItemByIndex(i).getProperty("native_file"));
    	}
    }
    if(fileItemId.Any())
    {
    	string folderPath = @"C:\Temp\Aras\MultipleFileDownload\" + innovator.getUserID();
    	string filePath = folderPath + "\\Files";
    	string zipPath = folderPath + "\\Zip";
    	if (System.IO.Directory.Exists(folderPath))
    	{
    		System.IO.Directory.Delete(folderPath, true);
    	}
    	System.IO.DirectoryInfo di = System.IO.Directory.CreateDirectory(filePath);
    	System.IO.DirectoryInfo zdi = System.IO.Directory.CreateDirectory(zipPath);
    	Item files = innovator.newItem("File", "get");
    	files.setAttribute("idlist", String.Join(",", fileItemId.ToArray()));
    	files = files.apply();
    	if (files.isError())
    	{
    		return files;
    	}
    	Aras.IOME.CheckoutManager cm = new Aras.IOME.CheckoutManager(files);
    	cm.DownloadFiles(filePath, 1);
    	string currentDateString = DateTime.Now.ToString(@"yyMMddHHmmss", CultureInfo.CreateSpecificCulture("en-US"));
    	string zipName = string.Format(CultureInfo.InvariantCulture, "ZipFiles_{0}.zip", currentDateString);
    	string fullZipPath = String.Format("{0}\\{1}", zipPath, zipName);
    	try
    	{
    	    System.IO.Compression.ZipFile.CreateFromDirectory(filePath, fullZipPath);
        	Item zipFile = innovator.newItem("File", "add");
        	zipFile.setProperty("filename", zipName);
        	zipFile.attachPhysicalFile(fullZipPath);
        	zipFile = zipFile.apply();
        	if (System.IO.Directory.Exists(folderPath))
    	    {
    		    System.IO.Directory.Delete(folderPath, true);
    	    }
        	return zipFile;
    	}
    	catch (InvalidDataException)
    	{
    		return innovator.newError("Could not create .zip archive");
    	}
    }
    return this;

  • 0 オフライン in reply to Gopikrishnan

    Gopikrishnan,

    Thank you for your help with this.  It works now!  Here is the code I used in case anyone needs it in the future.

    File_Download-CAD (JavaScript)

    var dir = "";
    
    var userItem = top.aras.getLoggedUserItem();
    var workingDirNode = userItem.selectSingleNode('./working_directory');
    
    if (workingDirNode) 
    {
        var logDir = workingDirNode.text;
    }
    
    // Create a zip on the server and upload it as a new file item to the vault
    var res = aras.applyMethod("DownloadAllFilesInZip-CAD", "<file_ids>" + this.getID() + "</file_ids>");
    
    if (!res)
    {
        return;
    }
    
    dom = top.aras.createXMLDocument();
    dom.loadXML(res);
    
    // Download the uploaded zip to the user's working directory
    var fileItem = aras.getItemById("File", dom.firstChild.getAttribute("id"));
    
    // top.aras.downloadFile errors out, I believe this is due to no information in fileItem
    top.aras.downloadFile(fileItem, "AllFiles.zip"); // TODO: Make this name relevant to the item this is called from
    
    // Lastly, delete the file from the server
    // NOTE: We're doing this in a timeout because otherwise, the file is deleted from the server before the client has a chance to download it.
    setTimeout(function(){
        var res = aras.applyMethod("Delete Zip from Server", "<id>" + dom.firstChild.getAttribute("id") + "</id>");
    
        if (!res)
        {
            alert("Error cleaning up files");
        }
    }, 3000);
    
    return;

    DownloadAllFilesInZip-CAD (C#)

    //  Called by File_Download-CAD Method and CAD_Download Action.
    
    Innovator innovator = this.getInnovator();
    
    Item cadItem = innovator.newItem("CAD", "GetItemRepeatConfig");
        cadItem.setAttribute("select", "item_number,native_file");
        cadItem.setID(this.getProperty("file_ids"));
    
    Item cadStructure = innovator.newItem("CAD Structure", "get");
        cadStructure.setAttribute("select", "related_id");
        cadStructure.setAttribute("repeatProp", "related_id");
        cadStructure.setAttribute("repeatTimes", "0");
        cadItem.addRelationship(cadStructure);
        cadItem = cadItem.apply();
    
    if(cadItem.isError())
    {
        return cadItem;
    }
    
    Item queryItem = cadItem.getItemsByXPath("//Item[@type='CAD']");
    
    var fileItemId = new List <string>();
    
    for(int i = 0; i < queryItem.getItemCount(); i++)
    {
        if(!string.IsNullOrEmpty(queryItem.getItemByIndex(i).getProperty("native_file")))
        {
            fileItemId.Add(queryItem.getItemByIndex(i).getProperty("native_file"));
        }
    }
    
    if(fileItemId.Any())
    {
        string folderPath = @"D:\aras_vault\CM_InnovatorSMORS\Vault\TDP Download" + innovator.getUserID();
        string filePath = folderPath + "\\Files";
        string zipPath = folderPath + "\\Zip";
    
        if (System.IO.Directory.Exists(folderPath))
        {
            System.IO.Directory.Delete(folderPath, true);
        }
    
        System.IO.DirectoryInfo di = System.IO.Directory.CreateDirectory(filePath);
        System.IO.DirectoryInfo zdi = System.IO.Directory.CreateDirectory(zipPath);
    
        Item files = innovator.newItem("File", "get");
            files.setAttribute("idlist", String.Join(",", fileItemId.ToArray()));
            files = files.apply();
    
        if (files.isError())
        {
            return files;
        }
    
        // Create a checkout manager to download all of the files to the server
        Aras.IOME.CheckoutManager cm = new Aras.IOME.CheckoutManager(files);
        cm.DownloadFiles(filePath, 1);
    
        string currentDateString = DateTime.Now.ToString(@"yyMMddHHmmss", CultureInfo.CreateSpecificCulture("en-US"));
        string zipName = string.Format(CultureInfo.InvariantCulture, "ZipFiles_{0}.zip", currentDateString);
        string fullZipPath = String.Format("{0}\\{1}", zipPath, zipName);
    
        try
        {
            System.IO.Compression.ZipFile.CreateFromDirectory(filePath, fullZipPath);
            Item zipFile = innovator.newItem("File", "add");
                zipFile.setProperty("filename", zipName);
                zipFile.attachPhysicalFile(fullZipPath);
                zipFile = zipFile.apply();
    
            if (System.IO.Directory.Exists(folderPath))
            {
                System.IO.Directory.Delete(folderPath, true);
            }
            return zipFile;
        }
        catch (InvalidDataException)
        {
            return innovator.newError("Could not create .zip archive");
        }
    }
    
    return this;

  • 0 オフライン in reply to Nathan H.

    Glad to hear and thanks for sharing the final code. If in case any one looking for Delete Zip from Server method can get it from  https://github.com/ArasLabs/download-multiple-files