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.

Django Signals

Django includes a “signal dispatcher” which helps allow decoupled applications get notified when actions occur elsewhere in the framework. In a nutshell, signals allow certain senders to notify a set of receivers that some action has taken place. They’re especially useful when many pieces of code may be interested in the same events.

Django provides a set of built-in signals that let user code get notified by Django itself of certain actions. They may be useful when you’d like to take certain actions pre/post saving/deleting of objects. These include some useful notifications:

  • django.db.models.signals.pre_save & django.db.models.signals.post_save
  • django.db.models.signals.pre_delete & django.db.models.signals.post_delete

See the built-in signal documentation for a complete list, and a complete explanation of each signal.

Listening to signals
To receive a signal, you need to register a receiver function that gets called when the signal is sent by using the Signal.connect() method:

Signal.connect(receiver, sender=None, weak=True, dispatch_uid=None)

Receiver functions
First, we need to define a receiver function. A receiver can be any Python function or method:

def my_callback(sender, **kwargs):
    print("Request finished!")

Example of logging changes in Django application

Log changes on pre_save of object

def log_change_to_admin(sender, instance, **kwargs):
    '''
    Custom audit trail for any model.  This adds an entry into the django admin logs.  We'll get two entries per edit but at least we'll have the changes.
    Handles cases where the user adds or edits a model
    '''
    try:
        #if you want to override the log object do it as a _log_obj method on the instance, returning an object
        log_obj = instance._log_obj()
    except:
        log_obj = instance
    try:
        #if you want to override the excluded fields do it as a _log_excluded_fields method on the instance, returning a list
        excluded_fields = instance._log_excluded_fields()
    except:
        excluded_fields = []

    try:

        log_message = None

        #the "raw" check is to make sure we are not loading fixtures
        if not kwargs["raw"]:

            #if there is an id then we are in an edit situation
            if instance.id:
                #get the old object from the database
                old = sender.objects.get(pk=instance.id)

                #figure out what changed
                changed = []
                if old._meta and old._meta.fields:
                    for f in [field for field in old._meta.fields if field.name not in excluded_fields]:
                        if getattr(instance, f.name) != getattr(old, f.name):
                            old_value = "'%s'" % getattr(old, f.name)
                            new_value = "'%s'" % getattr(instance, f.name)
                            changed.append((f.name, old_value, new_value))

                #only log stuff if it has changed.
                if changed:
                    log_message = "Changes to %s: " % instance.__unicode__()
                    log_message += ', '.join(["%s from %s to %s" % (x[0], x[1], x[2]) for x in changed])
                    flag = CHANGE

            #since there was no id then we are in an add situation
            else:
                log_message = "Added %s: %s" % (sender.__name__, instance.__dict__)
                flag = ADDITION

        if log_message:
            u = 1 #default to root user if the request object is not attached.

            #add to the django log
            LogEntry.objects.log_action(
                user_id = u,
                content_type_id = ContentType.objects.get_for_model(log_obj).id,  #content_type_id for the account object
                object_id = log_obj.id,
                object_repr = log_obj.__unicode__(),
                change_message = log_message.rstrip(),
                action_flag = flag,
                )
    except:
        print "Failed to log action."

#connect log_change_to_admin to the pre_save signal for Installations and associated children
models.signals.pre_save.connect(log_change_to_admin, sender=Installation)

Log changes on pre_delete of object

def log_delete_to_admin(sender, instance, **kwargs):
    '''
    Custom audit trail for models.  This adds an entry into the django admin logs.  We'll get two entries per edit but at least we'll have the changes.
    Handles cases where the user deletes a model
    '''
    try:
        #if you want to override the log object do it as a _log_obj method on the instance, returning an object
        log_obj = instance._log_obj()
    except:
        log_obj = instance

    try:

        log_message = "Deleting %s: %s" % (instance.__unicode__(), instance.__dict__)

        u = 1 #default to root user if the request object is not attached.

        #add to the django log
        LogEntry.objects.log_action(
            user_id = u,
            content_type_id = ContentType.objects.get_for_model(log_obj).id,  #content_type_id for the account object
            object_id = log_obj.id,
            object_repr = log_obj.__unicode__(),
            change_message = log_message.rstrip(),
            action_flag = DELETION,
            )
    except:
        print "Failed to log action."

#connect log_delete_to_admin to the pre_delete signal for Installations and associated children
models.signals.pre_delete.connect(log_delete_to_admin, sender=Installation)

You can define your own custom rules and attach them to signals. e.g. I wanted to conditionally do some synchronization with SFDC after the object was saved. I created a post_save signal and hooked it up with a receiver that did the job.

# receiver to work on the SFDC changes for incoming request
def syncAccountToSfdc(sender, instance, **kwargs):
    if instance.isCustomer() and instance.account.salesforce_type != instance.account.getComputedSfdcAccountType() \
    and instance.account.salesforce_type in [SfdcAccountType.CUSTOMER, SfdcAccountType.CUSTOMER_IN_DANGER, SfdcAccountType.FORMER_CUSTOMER]:
        instance.account.update_salesforce()

# post_save signal to sync to SFDC if required
models.signals.post_save.connect(syncAccountToSfdc, sender=Installation)

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']

Install/Uninstall PostgreSQL on a Mac

Use the installer from Enterprise DB website to install on a mac. There are installers available from BigSQL but they aren’t quite standard so uninstallation could be a little more work. Here are instructions to install and uninstall PostgreSQL on/from your Mac.

Install postgres
1. Download the installer from the following link and click through the installation process

https://www.enterprisedb.com/downloads/postgres-postgresql-downloads#macosx

2. Follow instructions to execute psql from commandline on Mac:

https://varunver.wordpress.com/2016/09/26/executing-psql-from-command-prompt-on-mac-osx-after-installing-postgresql/

Uninstall process
1. If you used the above link to install postgres on your system, it would have been installed under /Library/PostgreSQL. Navigate to the following directory from your terminal as root (The version 9.6 may differ based on the version that was installed on your system):

cd /Library/PostgreSQL/9.6/

2. Open the uninstaller and follow instructions to remove the application

open uninstall-postgresql.app

3. Remove the PostgreSQL and data folders. The Wizard will notify you that these were not removed.

rm -rf /Library/PostgreSQL

4. Remove the configuration file that keeps track of the database version you are using:

rm /etc/postgres-reg.ini

5. Remove the PostgreSQL user using System Preferences -> Users & Groups.

That should be all! The uninstall wizard would have removed all icons and start-up applications files so you don’t have to worry about those.

Run queries like count, distinct, etc from a list of CSV, Excel column on a DB

Someone recently sent me an Excel with a set of data and claimed that one of the rows that stored the ID’s (unique) had set of duplicates. One way to read this data in an excel is to sort and scroll through to see if you can find any duplicates. The other way that I found out to be cooler and easier for a developer was to get the data as a view in a SQL editor and run queries on top of it. Here are the steps to use the dataset in SQL

1. Get the Raw data that you need to query upon, e.g. 10 rows below for simplicity

7
8
9
12
15
16
17
19
21
23

2. Use your favorite text editor to convert the data to something like this:

SELECT 7
UNION SELECT 8
UNION SELECT 9
UNION SELECT 12
UNION SELECT 15
UNION SELECT 16
UNION SELECT 17
UNION SELECT 19
UNION SELECT 21

3. Now convert this to a sub-query that can be used in SQL to query the data

SELECT depid FROM
(SELECT 7
UNION SELECT 8
UNION SELECT 9
UNION SELECT 12
UNION SELECT 15
UNION SELECT 16
UNION SELECT 17
UNION SELECT 19
UNION SELECT 21) deployments(depid)

4. Now in order to get the duplicates, all I had to do was to get a count for all records and get a count for the duplicate records. If that didn’t match, I could have done grouping and got the defaulters

SELECT DISTINCT depid FROM
(SELECT 7
UNION SELECT 8
UNION SELECT 9
UNION SELECT 12
UNION SELECT 15
UNION SELECT 16
UNION SELECT 17
UNION SELECT 19
UNION SELECT 21) deployments(depid)

Create tooltip image that changes on hover

Created a grey image that isn’t in your face but when you hover over it, it turns into a color image. And then you can set tooltip property on hover.

CSS

/* Tooltips */
.infotip {
    width: 30px;
    height: 30px;
    display: inline-block;
    vertical-align: bottom;
    background: url('info-grey.png');
    background-size: 15px 15px;
    background-repeat: no-repeat;
}

.infotip:hover {
    background-image: url('info.png');
}

HTML

<span class="infotip"></span>