Time remaining on a delegation/at home before going on a delegation

Intro

I am an IT consultant. As like any consultant I am working nearshore by the customer. Lately my wife asked me, how much more time is till I will be back home. I said 4 days. It wasn’t so exactly. It was about 4 and half day. And that is why I did some application, which will count back those times, how long I am on the project, how much more time I have to stay here, and how long I will be at home (when I am already at home).

What is needed for starting building this application?

Linux, Python, PySide, Gedit

What was pretty challenging?

Petterns (see utils.py for more details), recalculating seconds into human readable format (see utils.py for more details) – it is a little bit kinky, but it works (although this method is not looking good, so if you have any sugestions about changing it, please be so kind and add a comment to this entry)…

What left to do?

Well, a lot of things, for example : in countdown.py (odliczanie.py for Polish version) the form shouldn’t be made per hand, but with QTDesigner and then it should be converted and used in appropriate way in this file…this is next thing, I will do, when I get some more time for it. Creation of the periods should be redesigned, but I haven’t got enought time…so maybe in a later term…when the time at work will not be so stressful, as it is now…

How does it look like ?

It’s simple application, that shows some times for example, for current time (when I was writting this blog) it was looking like this :

Conclusion

Well, no conclusion at all… If you are an consultant, you can tell now exactly how much time have you left till end of the delegation :)

Source code

You can download source code of the scripts from : countdown.tar.gz

Gedit/Python/Zenity – zewnętrzne własne narzędzie do szyfrowania/deszyfrowania plików

Intro

Ostatnio pisałem jakiś dokument *.txt, który potem chciałem zaszyfrować, tak, by ktoś inny, kto ewentualnie dostanie się do mojego pendrive’a, nie mógł odczytać tego dokumentu. Wiem, że są zewnętrzne aplikacje, które mogą takie pliki zaszyfrować, ale chciałem spróbować sam napisać jakiś mały skrypt, który będzie szyfrował plik i potem go deszyfrował. Potrzebne narzędzia :

  • python 2.x (ja pisałem w 2.6.5)
  • gedit 2.x (ja korzystam ze sprawdzonego 2.30.3)
  • gnupg oraz biblioteki do tego dla pythona (można sciągnąć ze strony http://code.google.com/p/python-gnupg/ )
  • zenity (standardowo zainstalowana w ubuntu 10.04 LTS)

I już można zaczynać.

Konfiguracja gnupg

W ubuntu najlepiej jest ściągnąć gnupg poprzez synaptica. Wybieramy następujące pakiety :

  • gnupg (ewentualnie też gnupg2)
  • python-gnnupginterface

Reszta powiązań zostanie automatycznie dodana. Po zainstalowaniu gnupg zostaje nam napisanie prostych dwóch skryptów do szyfrowania i deszyfrowania.

Skrypt szyfrujący

Oto prosty skrypt szyfrujący wykorzystany jako narzędzie zewnętrzne w gedit :

#!/usr/bin/python
#encoding=utf8
import gnupg
import os.path
import sys

if os.name == 'nt' :
	gpg = gnupg.GPG(gnupghome='C:\Dokumenty\max')
elif os.name == 'posix' :
	gpg = gnupg.GPG(gnupghome='/home/max')

if len(sys.argv) > 1 and len(sys.argv[1]) > 0 :
	plikDoZapisania = sys.argv[1]
else :
	plikDoZapisania = raw_input("Podaj plik do encrypcji : ");
	
password = sys.argv[2]

if ((len(password) > 0)):
	if(os.path.isfile(plikDoZapisania)) :
		stream = open(plikDoZapisania, "rb")
		encrypted_data = gpg.encrypt_file(stream, "None", symmetric="true", passphrase=password, output=plikDoZapisania + ".gpg")
		if(os.path.isfile(plikDoZapisania + ".gpg")) :
			print "Szyfrowanie powiodlo sie"
			stream.close()
			os.remove(plikDoZapisania)		
		else :
			print "Nieudane szyfrowanie"

		stream.close()
	else :
		print "Nie ma pliku do zaszyfrowania."
else :
	print "Nie podano hasla. Plik nie zostanie zaszyfrowany."

Zapiszmy go w katalogu ~/bin jako encfile.py. I nadajmy mu prawo wykonywania (chmod +x encfile.py).

Skrypt deszyfrujący

A oto skrypt deszyfrujący wykorzystywany jako narzędzie zewnętrzne w gedit :

#!/usr/bin/python
#encoding=utf8
import gnupg
import os.path
import sys
"""
Programik do deszyfrowania plików zakodowanych przy pomocy gpg. Niestety trzeba podac haslo :) 
"""

if os.name == 'nt' :
	gpg = gnupg.GPG(gnupghome='C:\Dokumenty\max')
elif os.name == 'posix' :
	gpg = gnupg.GPG(gnupghome='/home/max')

if len(sys.argv) > 1 and len(sys.argv[1]) > 0 :
	plikDoDeszyfrowania = sys.argv[1]
else :
	plikDoDeszyfrowania = raw_input("Podaj nazwe pliku do deszyfrowania : ")
	
password = sys.argv[2]
if len(password) > 0 :
	if(os.path.isfile(plikDoDeszyfrowania)) :
		stream = open(plikDoDeszyfrowania, "rb")
		decrypted_data = gpg.decrypt_file(stream, passphrase=password, output=plikDoDeszyfrowania[:-4])
		if(os.path.isfile(plikDoDeszyfrowania[:-4])) :
			print "Szyfrowanie powiodlo sie."
			stream.close()
			os.remove(plikDoDeszyfrowania)		
		else :
			print "Nieudane odszyfrowanie. Prawdopodobnie zle haslo. "

		stream.close()
	else :
		print "Nie ma pliku do odszyfrowania."
else :
	print "Musisz podac prawidlowe haslo."

Zapiszmy go w katalogu ~/bin jako decfile.py. I nadajmy mu prawo wykonywania (chmod +x decfile.py).

Konfiguracja gedit

W gedit musimy uaktywnić (o ile jeszcze tego nie zrobiliśmy) narzędzia zewnętrzne. Edycja -> Preferencje -> Zakładka "wtyczki" -> zaznaczamy "Narzędzia zewnętrzne".

Teraz musimy jeszcze przygotować odpowiednie narzędzia zewnętrzne. Wchodzimy w Narzędzia -> Zarządzaj narzędziami zewnętrznymi.

Dodajemy nowy wpis np: Szyfrowanie pliku. Dodajemy skrót np. Shift+Control+s i edytujemy skrypt po prawej. Wpisujemy tam następujący kod :

#!/bin/sh
pass1=$(zenity --entry --title="Podaj haslo" --text="Haslo :" --hide-text);
pass2=$(zenity --entry --title="Powtorz" --text="Haslo :" --hide-text);
if [ "$pass1" = "$pass2" ]
then
	python ~/bin/encfile.py $GEDIT_CURRENT_DOCUMENT_NAME $pass1
	exit 1
else
	echo "Hasla nie zgadzaja sie. Prosze sprobowac jeszcze raz."
fi

Teraz dodajemy nowy wpis np: Deszyfrowanie pliku. Dodajemy skrót np. Shift + Control + d i edytujemy skrypt po prawej. Wpisujemy tam następujący kod :

#!/bin/sh
FILE=$(zenity --file-selection --title="Wybierz plik do deszyfrowania");
pass1=$(zenity --entry --title="Podaj haslo" --text="Haslo :" --hide-text);
if [ -z $pass1 ]
then
	echo "Haslo nie moze byc puste."
else
	python ~/bin/decfile.py $FILE $pass1
	exit 1
fi

I gotowe.

Dosyć ciekawe jest to, że musi być otwarte jakieś okno (nawet z pustym dokumentem) w gedit, żeby skróty działały i żeby odpalały się te narzędzia zewnętrzne. Miłego eksperymentowania.

Konsolowe wersje plików

Konsolowe wersje plików wymagają dodatkowo modułu pythona getpass w celu ukrycia wpisywanego hasła.

Plik szyfrujący

#!/usr/bin/python
#encoding=utf8
import gnupg
import os.path
import getpass
import sys

if os.name == 'nt' :
	gpg = gnupg.GPG(gnupghome='C:\Dokumenty\max')
elif os.name == 'posix' :
	gpg = gnupg.GPG(gnupghome='/home/max')

if len(sys.argv) > 1 and len(sys.argv[1]) > 0 :
	plikDoZapisania = sys.argv[1]
else :
	plikDoZapisania = raw_input("Podaj plik do encrypcji : ");
	
password = getpass.getpass("Podaj haslo : ")
password2 = getpass.getpass("Powtorz haslo : ")
if ((len(password) > 0) and (password == password2)):
	if(os.path.isfile(plikDoZapisania)) :
		stream = open(plikDoZapisania, "rb")
		encrypted_data = gpg.encrypt_file(stream, "None", symmetric="true", passphrase=password, output=plikDoZapisania + ".gpg")
		if(os.path.isfile(plikDoZapisania + ".gpg")) :
			print "Szyfrowanie powiodlo sie"
			stream.close()
			os.remove(plikDoZapisania)		
		else :
			print "Nieudane szyfrowanie"

		stream.close()
	else :
		print "Nie ma pliku do zaszyfrowania."
else :
	print "Hasla nie sa identyczne. Plik nie zostal zaszyfrowany."

Plik deszyfrujący

#!/usr/bin/python
#encoding=utf8
import gnupg
import os.path
import getpass
import sys
"""
Programik do deszyfrowania plików zakodowanych przy pomocy gpg. Niestety trzeba podac haslo :) 
"""

if os.name == 'nt' :
	gpg = gnupg.GPG(gnupghome='C:\Dokumenty\max')
elif os.name == 'posix' :
	gpg = gnupg.GPG(gnupghome='/home/max')

if len(sys.argv) > 1 and len(sys.argv[1]) > 0 :
	plikDoDeszyfrowania = sys.argv[1]
else :
	plikDoDeszyfrowania = raw_input("Podaj nazwe pliku do deszyfrowania : ")
	
password = getpass.getpass("Podaj haslo : ")
if len(password) > 0 :
	if(os.path.isfile(plikDoDeszyfrowania)) :
		stream = open(plikDoDeszyfrowania, "rb")
		decrypted_data = gpg.decrypt_file(stream, passphrase=password, output=plikDoDeszyfrowania[:-4])
		if(os.path.isfile(plikDoDeszyfrowania[:-4])) :
			print "Szyfrowanie powiodlo sie."
			stream.close()
			os.remove(plikDoDeszyfrowania)		
		else :
			print "Nieudane odszyfrowanie. Prawdopodobnie zle haslo. "

		stream.close()
	else :
		print "Nie ma pliku do odszyfrowania."
else :
	print "Musisz podac prawidlowe haslo."


SQL Alchemy and database tables reflection

Intro

Well, I was working on some small application, which was using sqlite databases. And whole problem was I had to create definitions for objects representing the tables. I was looking for some ORM framework for this application. Django gives such framework, and it can be injected in not-django application, but I found sqlalchemy after a little research in the net. And it really rocks. It is pretty easy to configure python script to create appropriate python files with appropriate models from the tables. So, let’s have a closer look.

Initial setup and assumptions

Let’s say we are using sqlite database for our small aplication.

We have to import appropriate modules :

from sqlalchemy import *
from datetime import *

Let’s say our future model objects will reside in directory /home/max/pythonprojects/slowniki/domainobjects and our databases reside in /home/max/pythonprojects/slowniki/dbs. So define this as TARGET_DIRECTORY and SOURCE_DB_DIRECTORY like this :

TARGET_DIRECTORY="/home/max/pythonprojects/slowniki/domainobjects"
SOURCE_DB_DIRECTORY="/home/max/pythonprojects/slowniki/dbs"

Now, let’s define that our databases are : ustawienia.db and edictgerman.db and engine is sqlite. Define this like this :

databasetuples = ('ustawienia.db', 'edictgerman.db')
enginename = "sqlite:///"

Generating "header" for the new files

Well, let’s define now a method for generating "header" of the files, that will be autmatically generated :

def generujKomentarze(nazwapliku, dbname, file) :
	d = datetime.now()
	commentstuples = (
	'# -*- coding: utf-8 -*-',
	'#',
	'#       %s' % nazwapliku,
	'#',
	'# Plik wygenerowany przy pomocy narzędzia tablereflector.py z modułu utils ',
	'#',
	'#',
	'# Data wygenerowania : %s ' %d ,
	'#',
	'# Należy sprawdzić, czy któraś z kolumn nie jest Booleanem, w sqlite',
	'# Boolean jest zapisywany jako Integer.',
	'#',
	'# Baza danych z której pochodzi tablica : %s ' % dbname,
	'\n'
	)
	for string in commentstuples :
		file.write(string + "\n")

Here we define the comments for the new file, with date of the generation – Data wygenerowania – which we take from datetime.now().

Generating imports for the new files

def generujImporty(file) :
	importsTuples = (
	'from sqlalchemy.ext.declarative import declarative_base',
	'from sqlalchemy import Table, Column, Integer, String, MetaData, ForeignKey',
	'from sqlalchemy.dialects.sqlite import BOOLEAN',
	''
	)
	for string in importsTuples :
		file.write(string + "\n")

This are simply needed imports for the objects, which will be instantiated later on.

Generating base class and constructor

def generujKod(tname, columnsArray, file) :
	basedeclarative = "Base = declarative_base()"
	arrayOfCode = [
	'def %s(Base) :' % changeCaseInWord(tname, 0),
	'	__tablename__ = "%s"' %tname
	]
	
	if columnsArray is not None and len(columnsArray) > 0 :
		for stringline in columnsArray:
			arrayOfCode.append(stringline)
	
	for string in arrayOfCode:
		file.write(string + "\n")	

Here will be constructor generated. columnsArray are simply strings, which are generated by method generujPojedynczaLinie – generateSimpleLine, this method (generujPojedynczaLinie) is called every time, when we are receiving metadata for the columns from table, this we will se a little bit later.

Now let’s see how the simple line of constructor may be generated :

Generating simple lines

def generujPojedynczaLinie(nazwa, typ, primaryKey = False) :
	line = "	%s = Column(%s" % (nazwa, typ)
	line = line.replace("TEXT", "String")
	line = line.replace("INTEGER", "Integer")
	if primaryKey is True :
		line = line + ", primary_key=True)"
	else :
		line = line +")"
	return line

Now, let’s take a while in here. Sqlite has a few data types. We use simply use TEXT and INTEGER, but, for the python we have to change TEXT into String and INTEGER into Integer. That’s why there is a replace call. Additionally, there is also a check, if the column is primary key. By default it is False, but if this gonna be primary key, it is added to appropriate property definition.

Generating init method

def generujMetodeInit(columnsNames, file) :
	initDefinitionArray=[]
	line00 = " "
	initDefinitionArray.append(line00)
	lineInit = "	def __init__(self"
	if columnsNames is not None and len(columnsNames) > 0 :
		for string in columnsNames :
			lineInit = lineInit + ", %s" % string

	lineInit = lineInit + "):"
	initDefinitionArray.append(lineInit)

	if columnsNames is not None and len(columnsNames) > 0 :
		for string in columnsNames :
			initDefinitionArray.append("		self.%s = %s" % (string, string))
	
	for string in initDefinitionArray:
		file.write(string + "\n")

This method goes through columnsNames and generates init method. Here are appropriate lines added (generated earlier with generujPojedynczaLinie method (see above, previous section).

Generate "__repr__" method

def generujMetodeRepr(tname, columnsNames, file):
	reprDefinitionArray = []
	line00 = " "
	reprDefinitionArray.append(line00)
	lineInit = "	def __repr__(self):"
	reprDefinitionArray.append(lineInit)
	lineEnd = "		return \"<%s(" % changeCaseInWord(tname, 0)
	if columnsNames is not None and len(columnsNames) > 0:
		for string in columnsNames :
			if columnsNames.index(string) < len(columnsNames) - 1:
				lineEnd = lineEnd + "'%s',"
			else :
				lineEnd = lineEnd + "'%s'"
	
	lineEnd = lineEnd + ")>\" % ("
	if columnsNames is not None and len(columnsNames) > 0:
		for string in columnsNames :
			if columnsNames.index(string) < len(columnsNames) - 1 :
				lineEnd = lineEnd + "self.%s, " %string
			else :
				lineEnd = lineEnd + "self.%s" %string
				
	lineEnd += ")"
	reprDefinitionArray.append(lineEnd)
	
	for string in reprDefinitionArray :
		file.write(string + "\n")

This method gives us the repr method which is called to represent our new object. It can be used in place of str method. But if it is your wish, you can add str method too. It simply returns a string, with the fields of the object.

Defining helper method for changing one letter in the word to upper or lower case

'''
Zmienia literkę pod podanym numerem na dużą, lub małą
w zależności od flagi toUpper.
Reszta literek w słowie zostaje bez zmiany,
domyślnie toUpper ustawiony jest na True
'''
def changeCaseInWord(s, numerliterki, toUpper=True):
	if numerliterki == 0:
		if toUpper == True:
			return s.upper()[0] + s[1:]
		else :
			return s.lower()[0] + s[1:]
	elif numerliterki <= len(s) :
		if toUpper is True :
			return s[:numerliterki] + s.upper()[numerliterki] + s[numerliterki+1:]
		else :
			return s[:numerliterki] + s.lower()[numerliterki] + s[numerliterki+1:]
	else :
		return "Numer literki jest poza słowem..."

This method is used to change first letter of the table (during creation of the base class for the table) to upper case, but it can be used to change any selected letter in a word to upper or lower case.

Finally the main method of the generation script

if __name__ == "__main__" :
	for db in databasetuples :
		nazwa_enginea = enginename + SOURCE_DB_DIRECTORY + "/" + db
		engine = create_engine(nazwa_enginea)
		meta = MetaData()
		meta.bind = engine
		meta.reflect()
		for tname in meta.tables:
			file = open(TARGET_DIRECTORY + "/%s.py" % tname, "w")
			T = meta.tables[tname]
			generujKomentarze(tname + ".py", db, file)
			generujImporty(file)
			columnsArray = []
			columnsNames = []
			for c in T.columns :
				columnsArray.append(generujPojedynczaLinie(c.name, c.type, c.primary_key))
				columnsNames.append(c.name)
			generujKod(tname, columnsArray, file)
			generujMetodeInit(columnsNames, file)
			generujMetodeRepr(tname, columnsNames, file)
			file.close()

Here we initialise everything and run the script, which creates our files. Isn’t it easy? The result of running so constructed script is a folder with generated models (every model has it’s own file).

PySide QTimer and GUI application

As I was looking for some informations about licenses for PyQT, I found an pretty interesting alternative (yes, I know, this alternative was there already a while, but everyone is still learning, so am I). Well, alternative to PyQT (in case one want to sell it’s software, which growed into a big project :) ) is PySide from Nokia Corporation. Well, I thought that it is worth to give it a try, so let’s have a closer look at it.

In my little tiny application I was using QTimer. And QTimer needs to be initialised (define) and then just start it. Following code is showing a sample, which was working with PyQT :

def create_timer(self):
    self.timer = QTimer()
    self.timer.timeout.connect(self.update)
    self.timer.start(1000)

This was called in a constructor and was doing it’s job (calling every 1 second update method).

After changing imports in main python script from PyQT to PySide I encountered, that this code is not calling update method every 1 second. It wasn’t calling it at all. This slot self.update was never run. I’ve looked over the google multiple combinations to find out what a f*** is going on there. Is QTimer not working in PySide, am I stupid or something? (well, the answer for the last question was not found in google…so I suppose I’m not so stupid at all). Somewhere on some kind of kinky forum I found something like : to use QTimer you have to call QCoreApplication… what… heh ?! What about GUI applications? My QTimer object was running smoothly with PyQT, so why it is not with PySide? I’ve tried different combinations, with no success…For example : self.connect(self.timer, SIGNAL("timeout()"), self.update) but it just didn’t started to count the time.

I was pretty confused…and…already desparate, and I must say angry…What a f**& am I doing wrong…So, I just changed the name of the method called from timer from self.update to self.updateGUI…And guess what – it suddenly started to work…heh?

def create_timer(self):
    self.timer = QTimer()
    self.timer.timeout.connect(self.updateGui)
    self.timer.start(1000)

Above code works in both versions of Qt Python, PyQT and PySide. I was looking in QDialog documentation some kind of tips, or something, maybe slot update() is in some way reserved, I don’t know. Didn’t found anything, what could really answer that question. Well, it is working now. 2 hours of investigations lost, but a lesson learned.

Netherless, I want stopping on python. It’s a nice tool for some speciall tasks.

Instalacja django i sterownika db2

Założenie jest, że zainstalowany jest już db2, czy to lokalnie, czy nie (z dodatkowym pakietem programistycznym)

1. Instalacja django

Z Synaptica instalujemy pakiet django

2. Instalowanie dodatkowych pakietów pythona

Z Synaptica instalujemy pakiet python-dev

3. Instalacja narzędzia easy_install

$ sudo apt-get install python-setuptools

4. Dodanie bibliotek db2 do patha

$ nano .bashrc

na końcu pliku dodajemy :

export IBM_DB_DIR=~/sqllib
export IBM_DB_LIB=~/sqllib/lib

zapisujemy plik i wychodzimy

odpalamy :
$ source ~/.bashrc

5. Zmiana uprawnień dla pakietów pythona

Dodajemy uprawnienia dla użytkownika, na którego się zalogowaliśmy :

$ sudo chown locke /usr/local/lib/python2.6/dist-packages

6. Instalacja django db2

$ easy_install ibm_db_django

Pakiety ściągają się i instalują. I jak wszystko poszło dobrze można już korzystać z django i db2.

Follow

Get every new post delivered to your Inbox.