three99

web development

Octopress Pagination

| Comments

In most cases pagination is better than traditional “previous – next” navigation as it offers visitors a more quick and convenient navigation through the site. It’s not a must, but a useful nice-to-have-feature.

So here is a gist to add pagination to your Octopress blog. All you need to do is to replace the pagination section in /source/index.html.

Python Grandfather Father Son Snapshots - Backup for EC2

| Comments

If you need to keep backups of your instance in EC2, then you can set a cron job to take a snapshot every hour and then run a python script for removing the unwanted snapshots.

Here is the script for creating snapshots:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import boto
import iso8601
import sys
import datetime
from boto.ec2.connection import EC2Connection


AWS_ACCESS_KEY_ID = 'AAAAAAAAAAAAAATQ'
AWS_SECRET_ACCESS_KEY = 'MAAAAAAAAAAAAAAAAAAAAn'
AWS_ACCOUNT_ID = "322222222226"

START_TIME = str(datetime.datetime.now())
arguments_length = len(sys.argv)

if arguments_length != 3:
  print "You need to supply 2 arguments at least. The first is the AWS volume_id and the second is the log path"
  print "For example:"
  
  sys.exit()


volume_id = sys.argv[1]
log_location = sys.argv[2]

conn = EC2Connection(AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY)

description = 'Hourly automatic backup'

rval = conn.create_snapshot(volume_id, description = description)
END_TIME = str(datetime.datetime.now())
f = open(log_location, 'a+')
s = 'start:\t' + START_TIME + '\tend:\t' + END_TIME + '\tstatus: ' + str(rval)  + ' for backup of volume ' + volume_id +'\n'
f.write(s)
f.close()

Here is the script for cleaning the snapshot list and keeping only the ones that you need:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
import boto
import iso8601
import sys
import datetime
from boto.ec2.connection import EC2Connection

TO_BE_KEPT_FOR_HOURLY = 'TO BE KEPT FOR HOURLY'
TO_BE_KEPT_FOR_DAILY  = 'TO BE KEPT FOR DAILY'
TO_BE_KEPT_FOR_WEEKLY = 'TO BE KEPT FOR WEEKLY'
TO_BE_DELETED         = 'TO BE DELETED'
DRY_RUN                   = 'dry'
LIVE_RUN              = 'live'

AWS_ACCESS_KEY_ID     = 'ABCDEFGHIJKLMONQ'                # CHANGE  TO YOUR OWN
AWS_SECRET_ACCESS_KEY = 'MASASDFASDFASDFSADFASDFASDASDrn' # CHANGE  TO YOUR OWN
AWS_ACCOUNT_ID            = "31232312316"                     # CHANGE  TO YOUR OWN

NUM_HOURLY    = 24 * 7
NUM_DAILY = 7 * 5
NUM_WEEKLY    = 30

arguments_length = len(sys.argv)

if arguments_length != 4:
  print "You need to supply 4 arguments at least. The first is the AWS volume_id, the second is the log path, and the 3rd is whether this is a dry run or not"
  print "The last argument should be either 'dry' or 'live'."
  sys.exit()

volume_id     = sys.argv[1]
log_location  = sys.argv[2]
if sys.argv[3] == LIVE_RUN:
  dry_run_status  = LIVE_RUN
else:
  dry_run_status  = DRY_RUN


NOW = datetime.datetime.now()

LAST_HOURLY   = NOW           - datetime.timedelta(hours  = NUM_HOURLY)
LAST_DAILY    = LAST_HOURLY   - datetime.timedelta(days   = NUM_DAILY)
LAST_WEEKLY   = LAST_DAILY    - datetime.timedelta(days   = 7 * NUM_WEEKLY)


f = open(log_location, 'a+')
snaps=[]
easy_snap_array = [] 

conn = EC2Connection(AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY)

all_snaps=conn.get_all_snapshots(owner = AWS_ACCOUNT_ID)

# Keep just the snapshots for the volume of interest
for snap in all_snaps:
  if snap.volume_id == volume_id:
      snaps.append(snap)

if len(snaps) == 0:

  f.write('\n\nNOW: ' + str(NOW) + '\tDid not find any snapshots for volume_id: %s' + volume_id)
  f.close()
  sys.exit()

for snap in snaps:

  snap_dict = dict()
  snap_time = iso8601.parse_date(snap.start_time)
  snap_time = snap_time.replace(tzinfo=None) # remove the timezone
  time_delta = snap_time.now() - snap_time
  snap_dict['snap_object'] = snap
  snap_dict['time_start'] = snap_time.replace(tzinfo=None)
  snap_dict['days_delta'] = time_delta.days
  snap_dict['weeks_delta'] = time_delta.days / 7
  snap_dict['hours_delta'] = time_delta.days * 24 + time_delta.seconds / 3600
  snap_dict['status'] = 'NOT DEFINED'
  easy_snap_array.append(snap_dict)

f.write('\n\nNOW: ' + str(NOW) + '*************' + dry_run_status + '********************\n\n')
f.write('LAST_HOURLY:\t\t' + str(LAST_HOURLY) +'\n')
f.write('LAST_DAILY:\t\t' + str(LAST_DAILY) +'\n')
f.write('LAST_WEEKLY:\t\t' + str(LAST_WEEKLY) +'\n')

snap_day_dict = dict()
snap_week_dict = dict()



# reverse=False return in ascending, True in descending time_start 
for easy_snap in sorted(easy_snap_array,  key=lambda k: k['time_start'],reverse=True):

  if easy_snap['time_start'] > LAST_HOURLY:
      easy_snap['status'] = TO_BE_KEPT_FOR_HOURLY

  if LAST_HOURLY > easy_snap['time_start'] > LAST_DAILY:
      day_key = str(easy_snap['time_start'].date())
      if day_key not in snap_day_dict:
          snap_day_dict[day_key] = easy_snap['snap_object']
          easy_snap['status'] = TO_BE_KEPT_FOR_DAILY
      else:
          easy_snap['status'] = TO_BE_DELETED

  if LAST_DAILY > easy_snap['time_start'] > LAST_WEEKLY:
      week_key = str(easy_snap['time_start'].isocalendar()[0]) +'-'+\
          str(easy_snap['time_start'].isocalendar()[1])
      if week_key not in snap_week_dict:
          snap_week_dict[week_key] = easy_snap['snap_object']
          easy_snap['status'] = TO_BE_KEPT_FOR_WEEKLY
      else:
          easy_snap['status'] = TO_BE_DELETED

  if LAST_WEEKLY > easy_snap['time_start']:
      easy_snap['status'] = TO_BE_DELETED

  s = 'start_time: '  + str(easy_snap['time_start'])              + '\t' +\
      'hours_delta: ' + str(easy_snap['hours_delta'])             + '\t' +\
      'days_delta: '  + str(easy_snap['days_delta'])              + '\t' +\
      'weeks_delta: ' + str(easy_snap['weeks_delta'])             + '\t' +\
      'volume_id: '   + str(easy_snap['snap_object'].volume_id)   + '\t' +\
      'snapshot_id: ' + str(easy_snap['snap_object'].id)          + '\t' +\
      'status: '      + str(easy_snap['status'])                  + '\t' +\
      '\n'

  f.write(s)

if dry_run_status == DRY_RUN:
  pass
elif dry_run_status == LIVE_RUN:
  # delete all that snapshots marked for deletion
  for easy_snap in easy_snap_array:
      if easy_snap['status'] == TO_BE_DELETED:
          result = conn.delete_snapshot(easy_snap['snap_object'].id)
          f.write("Deleted snapshot with id: " + easy_snap['snap_object'].id + "\tRETURN STATUS: "+str(result)+"\n")
else:
  pass

f.close()

How to Install PIL on Ubuntu With JPEG Support

| Comments

I was trying to get PIL to work on my ubuntu box but it failed to handle JPEGs. The system responded with the dreaded “IOError: decoder jpeg not available”. Here is what I did to solve it…

I am running my pylons project through a virtual environment. All the libraries are installed with pip, always run from within the virtual environment. When I tried to use PIL on my MAC I had no problems, but the same code just failed on Ubuntu (I was trying to generate some thumbnails from JPEGs).

The reason was that PIP was not installed properly. It required for the installation some libraries for dealing with JPEGs which it failed to find. What you have to do is tell it where to get it from. Before doing that make sure that your system has the necessary libraries. This seems to be enough for most cases:

1
sudo apt-get install libjpeg62-dev

If this does not do it, then try all the other possible libjpeg libraries. Now follow the steps:

Step 1: Download the code of PIL with pip (or rather uninstall PIP first - pip uninstall PIL)

1
/myEnv/bin/pip install --no-install PIL

This should download the source in the build directory of your environment.

1
cd /myEnv/build/PIL

Step 2: Edit the setup.py file

Find this line:

1
JPEG_ROOT = None

and change it to:

1
JPEG_ROOT = libinclude("/usr/lib")

Have a look at the setup.py in case you need to specify separately the lib and the include directory.

Step 3: Finish the installation

1
/myEnv/bin/pip install PIL

If all went well then you should see at the end of the installation something like this (see the JPEG line):

1
2
3
4
5
6
7
 -------------------------------------------------------------------
    *** TKINTER support not available
    --- JPEG support available
    --- ZLIB (PNG/ZIP) support available
    --- FREETYPE2 support available
    *** LITTLECMS support not available
--------------------------------------------------------------------

Mako Syntax Highlighting for Sublime Text 2

| Comments

If you by chance have to work with python mako templates in Sublime Text 2, I have some good news for you… There is a workaround to have enable syntax highlighting.

There is a Mako bundle for TextMate at the URL below which also works for Sublime Text 2, my favorite editor.

http://www.makotemplates.org/trac/attachment/wiki/WikiStart/Mako.tmbundle.zip

Download and extract the file locally and then move the extracted folder under ~/.config/sublime-text-2/Packages/. At least that’s the path on my Ubuntu machine.

In order to make it work I had to remove the code below from the HTML (Mako).tmLanguage file because it was throwing errors.

1
2
3
4
 <dict>
      <key>include</key>
      <string>#embedded-code</string>
 </dict>

You need to search for the #embedded-code string as the same code exists more than once and has to be removed throughout the file.

jQuery Plugin - Highlight HTML Table Columns

| Comments

A very simple jQuery plugin which adds a CSS class in the HTML table columns when the user mouseovers their column heading.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
$.fn.hiColumns = function(options){
  var defaults = {
      select: 'th',
      klass: 'highlighted'
  },
  options = $.extend({}, defaults, options);

  return this.each(function(){
      var elem = $(this);

      $(options.select, this).hover(
          function(){
              var i = $(this).index();

              elem.find('td').filter(function(){
                  return $(this).index() == i;
              }).addClass(options.klass);
          },
          function(){
              elem.find('.' + options.klass).removeClass(options.klass);
          }
      )
  })
}

Usage

1
2
3
4
5
6
7
8
9
10
<script type="text/javascript" src="/path/to/jquery-latest.js"></script> 
<script type="text/javascript" src="/path/to/jquery.hiColumns.js"></script>
<script>
$(function(){
     $('table').hiColumns();
})
</script>
<style>
     td.highlighted{background:gold;}
</style>

Options

1
2
3
4
5
6
7
8
<script>
$(function(){
     $('table').hiColumns({
          select: 'th',
          klass: 'highlighted'
     });
})
</script>

Download from GitHub