Pages

Monday, March 21, 2016

Alphabetize CSV Column Headers With Python

Today I need to dump a 200-column CSV out of a "central university" Salesforce org, give it to a business user, and ask her which columns she needs me to set up a "landing spot" for in our "subschool" org.

The least I can do is have it in alphabetical order for her.

Here's a Python script that can do just that (plus move ID to the front), after dataloader.io exported the columns in a seemingly random order.

If you're doing inter-data-store integration and you're not using Python (especially the "CSV" & "Pandas" modules) to help you with the CSV files, try it. You'll like it.

import pandas
import csv

inpath = 'C:\\WHEREVER_YOU_PUT_YOUR_FILE\\'
infilename = 'WHATEVER_YOU_NAMED_YOUR_FILE.csv'
infilepath = inpath + infilename
outpath = 'C:\\WHEREVER_YOU_WANT_YOUR_NEW_FILE\\'
outfilename = 'NAME_FOR_YOUR_NEW_FILE.csv'
outfilepath = outpath + outfilename

# CSV to Pandas:
df = pandas.read_csv(infilepath, encoding='latin-1',  header=0)

# Make a list of alphabetized column headers (with ID at the front):
sortedColumnList = sorted(df.columns)
sortedColumnList.insert(0, sortedColumnList.pop(sortedColumnList.index('Id')))

# Make a new Pandas object arranged alphabetically:
df2 = df.reindex_axis(sortedColumnList, axis=1)

# Write the new Pandas object to disk:
df2.to_csv(outfilepath, index=False, quoteing=csv.QUOTE_ALL)
You can replace the last line of code with this code if you want to remove columns where all of the rows are empty, anyway:
# Drops blank columns.  (Nothing to do with alphabetization.)
df3 = df2.dropna(axis=1, how='all')
# Write the new Pandas object to disk
df3.to_csv(outfilepath, index=False, quoteing=csv.QUOTE_ALL)

Actually, I think you can just add ".dropna(axis=1, how='all')" to the end of the "reindex_axis()" call and still print df2 - and actually, you can also just set df to equal itself with something done to it and print df (e.g. "df = df.reindex_axis().dropna()"), but I decided to favor legibility over style.

To execute this, you can just paste it into a plain-text file you call alphabetizecolumnheaders.py, install WinPython on your computer, bring up your ".py" file in the "Spyder" python IDE, and click "Run."

Credit to StackExchange here & here & here.

Tuesday, March 15, 2016

Metadata API Low-Overhead Assistance Sought (For Custom Object and Custom Field Creation)

This "Metadata API" thing is so frustrating. Why?!

I have about a week to:

  1. Rebuild 4 custom objects and about 25% of their 100-or-so-per-object custom fields from one Salesforce org in another - sometimes with slight name changes to avoid conflicts (both are sandboxes, but of unrelated production orgs)
  2. Hook up the "source" Salesforce org's data to the new objects/fields in the "target" Salesforce org via Jitterbit
  3. Write a bazillion triggers to post-process the data once it lands in the "target" org

I am not looking forward to step 1.

Thank goodness for student workers, because so far I've wasted 3 hours trying to figure out just how to take the following XML file and throw it at a Salesforce org w/o a "zzzTestSmallObject__c" custom object and get it to create one (along with "BoolCustField__c" custom field).

Any step-by-step advice on the fastest way to do this?

All "intros" to manipulating metadata with the API seem geared at getting people set up who need a lot of power.

I just need to take little XML files like this and create objects/fields from them on an ad-hoc basis.

And then I never want to think about the Metadata API again.

<?xml version="1.0" encoding="UTF-8"?>
<CustomObject xmlns="http://soap.sforce.com/2006/04/metadata">
    <actionOverrides>
        <actionName>Accept</actionName>
        <type>Default</type>
    </actionOverrides>
    <actionOverrides>
        <actionName>CancelEdit</actionName>
        <type>Default</type>
    </actionOverrides>
    <actionOverrides>
        <actionName>Clone</actionName>
        <type>Default</type>
    </actionOverrides>
    <actionOverrides>
        <actionName>Delete</actionName>
        <type>Default</type>
    </actionOverrides>
    <actionOverrides>
        <actionName>Edit</actionName>
        <type>Default</type>
    </actionOverrides>
    <actionOverrides>
        <actionName>Follow</actionName>
        <type>Default</type>
    </actionOverrides>
    <actionOverrides>
        <actionName>List</actionName>
        <type>Default</type>
    </actionOverrides>
    <actionOverrides>
        <actionName>New</actionName>
        <type>Default</type>
    </actionOverrides>
    <actionOverrides>
        <actionName>SaveEdit</actionName>
        <type>Default</type>
    </actionOverrides>
    <actionOverrides>
        <actionName>Tab</actionName>
        <type>Default</type>
    </actionOverrides>
    <actionOverrides>
        <actionName>View</actionName>
        <type>Default</type>
    </actionOverrides>
    <compactLayoutAssignment>SYSTEM</compactLayoutAssignment>
    <deploymentStatus>Deployed</deploymentStatus>
    <description>zzzTestSmallObjectHasADescription</description>
    <enableActivities>false</enableActivities>
    <enableBulkApi>true</enableBulkApi>
    <enableFeeds>false</enableFeeds>
    <enableHistory>false</enableHistory>
    <enableReports>false</enableReports>
    <enableSharing>true</enableSharing>
    <enableStreamingApi>true</enableStreamingApi>
    <fields>
        <fullName>BoolCustField__c</fullName>
        <defaultValue>false</defaultValue>
        <description>ThisIsABoolCustField</description>
        <externalId>false</externalId>
        <inlineHelpText>ThisIsABoolCustField</inlineHelpText>
        <label>BoolCustField</label>
        <trackTrending>false</trackTrending>
        <type>Checkbox</type>
    </fields>
    <label>zzzTestSmallObject</label>
    <nameField>
        <label>zzzTestSmallObject Name</label>
        <type>Text</type>
    </nameField>
    <pluralLabel>zzzTestSmallObjects</pluralLabel>
    <searchLayouts/>
    <sharingModel>ReadWrite</sharingModel>
</CustomObject>