Python – Read from CSV file

Here’s what you can use to read from a CSV file, row by row:

import csv
import tempfile, os

def release_script():
    file_path = os.path.join(tempfile.gettempdir(), 'customer_since.csv')
    print file_path # Refers to %temp% on Windows and /tmp/ on Linux
    csv_file = open(file_path, 'rb')
    reader = csv.DictReader(csv_file)
    for row in reader:
        sfid = row['Account Number']
        customer_since = row['Customer Since']
        print "%s : %s" % (sfid, customer_since,)
Advertisements

Django – Create temporary CSV file and Email as Attachment

An interesting challenge that I faced today. I wanted to create a CSV file on my Django project (fairly easy, huh) and email it as attachment. My first challenge was to save the file in a temp directory that is OS independent since I do my development on Windows and the application runs on a linux server. I came across this really cool python module called tempfile, but the drawback to me for using this module was that:

  1. It would create these random filenames
  2. I didn’t want to have the recipient see a random filename and think of it as spam
  3. The files would stay in the temp directory until manually cleaned up. Since the files need to be attached after creations, I didn’t want to delete them after creation which is the default behavior for tempfile (s)

So, the solution I found was to use tempfile module to get the location of the temp directory. Then use that location and write a file using standard I/O with a specific name. Every time the job runs, it would overwrite the old file and I won’t have a ton of files sitting in the temp directory. Here’s the complete solution to the problem:

from datetime import datetime, timedelta
import csv
import tempfile, os

file_path = os.path.join(tempfile.gettempdir(), 'expired_deployments.csv')

email_from = "YOUR-EMAIL@DOMAIN.COM"
email_to = ["TO-EMAIL@DOMAIN.COM"]
email_subject = "YOUR EMAIL SUBJECT"
email_body = "YOUR EMAIL BODY..."

# Create CSV file
f = open(file_path, 'wb')
file_writer = csv.writer(f, quotechar='"', quoting=csv.QUOTE_MINIMAL)
# Write headers to CSV file
file_header = ['Product Type', 'Company Name', 'SFID', 'DeploymentID', 'Status', 'License Expiration', 'Support Expiration']
file_writer.writerow(file_header)
file_writer.writerow(['XYZ', 'ABC In.c', 12345, 123, 'Active', '5/31/2017', '12/31/2017'])
f.close()

# Read the file and send as attachment
fr = open(f.name, "rb")
mail = CustomEmailMessage(email_subject, email_body, email_from, to=email_to, bcc=[]) 
mail.attach(os.path.basename(f.name), fr.read(), "application/octet-stream")
mail.send()
fr.close()

Note: The piece of code that sends out the email only works on Django. If you’re looking to send a file using standard python libraries, check out this other article about using smtplib to send out email attachments.

Here’s the custom class CustomEmailMessage that overrides that Django class EmailMessage. This is useful to stop email messages going out to actual users when you’re working on a development system. Must always use CustomEmailMessage to send out email notification and it would take care of things.

from django.core.mail.message import EmailMessage

class CustomEmailMessage(EmailMessage):
    # Over writting the send function so that in development and eval mode, emails are not sent to all.
    def send(self, fail_silently=False):
        """Sends the email message."""
        if settings.ENVIRONMENT == 'development' or settings.ENVIRONMENT == 'eval': # Staging's settings is same as production.
            self.to = [self.from_email,]
        if not self.recipients():
            # Don't bother creating the network connection if there's nobody to
            # send to.
            return 0
        return self.get_connection(fail_silently).send_messages([self])

Python – Export to CSV

Exporting data to a CSV file:

import csv

# Create CSV file
f = open('file_name.csv', 'w')
file_writer = csv.writer(f, quotechar='"', quoting=csv.QUOTE_MINIMAL)

# Write headers to CSV file
volume_header = ['col1', 'col2', 'col3', 'col4', 'col5', 'col6', 'col7']
file_writer.writerow(volume_header)

# Simplifying the code assuming you have an array of data
for i in array:
	file_writer.writerow([i.val1, i.val2, i.val3, i.val4, i.val5, i.val6, i.val7])
f.close()

Python smtplib – Send email with attachments

Here’s how you can send attachments from email using smtplib

#!/usr/lib/python
import smtplib
import mimetypes
from email.mime.multipart import MIMEMultipart
from email import encoders
from email.message import Message
from email.mime.audio import MIMEAudio
from email.mime.base import MIMEBase
from email.mime.image import MIMEImage
from email.mime.text import MIMEText

emailfrom = "FROM-EMAIL@DOMAIN.COM"
emailto = ["TO-EMAIL1@DOMAIN.COM", "TO-EMAIL2@DOMAIN.COM"]
fileToSend = "UPLOAD-FILE.csv"

msg = MIMEMultipart()
msg["From"] = emailfrom
msg["To"] = ",".join(emailto)
msg["Subject"] = "YOUR EMAIL SUBJECT"
msg.preamble = "YOUR EMAIL SUBJECT"

ctype, encoding = mimetypes.guess_type(fileToSend)
if ctype is None or encoding is not None:
    ctype = "application/octet-stream"

maintype, subtype = ctype.split("/", 1)

if maintype == "text":
    print 'text'
    fp = open(fileToSend)
    # Note: we should handle calculating the charset
    attachment = MIMEText(fp.read(), _subtype=subtype)
    fp.close()
elif maintype == "image":
    print 'image'
    fp = open(fileToSend, "rb")
    attachment = MIMEImage(fp.read(), _subtype=subtype)
    fp.close()
elif maintype == "audio":
    print 'audio'
    fp = open(fileToSend, "rb")
    attachment = MIMEAudio(fp.read(), _subtype=subtype)
    fp.close()
else:
    print 'else'
    fp = open(fileToSend, "rb")
    attachment = MIMEBase(maintype, subtype)
    attachment.set_payload(fp.read())
    fp.close()
    encoders.encode_base64(attachment)
attachment.add_header("Content-Disposition", "attachment", filename=fileToSend)
msg.attach(attachment)

server = smtplib.SMTP("EMAIL-SERVER.DOMAIN.COM")
server.set_debuglevel(0)
server.sendmail(emailfrom, emailto, msg.as_string())
server.quit()

Python – Iterate over a list and check if it’s not Empty

I have a list that I wanted to iterate over. When I used a list comprehension, it would fail if the list was None. But it would if the list was empty [].

The old and easy way of doing this was:

if tags:
	for t in tags:
		# Do stuff with t

The pythonic way of merging the if statement within the list comprehension is:

for t in [t for t in (tags or [])]:
	# Do stuff with t

If you had a dictionary, you could use a ternary condition within list comprehension:

([k for k, v in tags] if tags is not None else [])

OR, You can embed the ternary condition in the comprehension as well:

[k for k, v in (tags if tags is not None else [])]

The original stack overflow post.

Python Class used like an Enum

Although Enums are available in Python, I just like the idea or am used to using Classes instead which allows me to have more functionality in future if I need to attach additional methods to the class. Here’s how I have typically created a Class to store AWS Regions and have a method all() that would return all the region values – basically hard-coded.

class AwsRegion():
	'''
	Class to define AWS Regions
	'''
	OHIO = 'us-east-2'
	NORTH_VIRGINIA = 'us-east-1'
	NORTH_CALIFORNIA = 'us-west-1'
	OREGON = 'us-west-2'
	MUMBAI = 'ap-south-1'
	SEOUL = 'ap-northeast-2'
	SINGAPORE = 'ap-southeast-1'
	SYDNEY = 'ap-southeast-2'
	TOKYO = 'ap-northeast-1'
	FRANKFURT = 'eu-central-1'
	IRELAND = 'eu-west-1'
	LONDON = 'eu-west-2'
	SAO_PAULO = 'sa-east-1'

	@classmethod
	def all(cls, ):
		return [
			AwsRegion.OHIO,
			AwsRegion.NORTH_VIRGINIA,
			AwsRegion.NORTH_CALIFORNIA, 
			AwsRegion.OREGON,
			AwsRegion.MUMBAI,
			AwsRegion.SEOUL,
			AwsRegion.SINGAPORE,
			AwsRegion.SYDNEY,
			AwsRegion.TOKYO,
			AwsRegion.FRANKFURT,
			AwsRegion.IRELAND,
			AwsRegion.LONDON,
			AwsRegion.SAO_PAULO
			]

But I wanted to make the method all() more dynamic so every time I add new regions to the class, I don’t have to add the region to the all method return list. I found a better way to implement the all() method.

@classmethod
def all(cls, ):
	return [value for name, value in vars(cls).items()
				if not name.startswith('__')
					and not callable(getattr(cls,name))]

The original stack overflow post.

Updating Nulls in SFDC using salesforce-python-toolkit

I had been looking for a long time to update Null into a SFDC field. I wanted to set the date value to current date or reset it to blank based on a condition. I was able to set a date without issues, but clearing the date wouldn’t work. I tried to use ”, ‘null’ and None while trying to blank out the value but nothing worked. I was sure it was a straightforward solution but just didn’t know what it was. I got in touch with the author of the toolkit and he responded back in no time with the solution. I’m sharing the working solution here:

data = {}
if condition==True:
    data['Termination_Date__c'] = '2017-05-12'
else:
    data['fieldsToNull'] = ['Termination_Date__c']