Freitag, 11. Dezember 2009

Creating a history table using SQL triggers

Just for the record and for posterity, this is how to insert a row into table B after a row has been inserted in table a (e.g. something like a foreign key) using a sqlite3 trigger (the trigger would probably work on other databases as well). Add this to a file - call the file "trigger.txt" or something like that:

CREATE TRIGGER mytrigger AFTER INSERT ON table_a
    BEGIN
        INSERT INTO table_b VALUES(NULL,new.primary_id)
END;

Easy, non? Now you have to import the trigger into the sqlite database. Assuming you have already got a database called mydb.db, use this command:
sqlite3 mydb.db < trigger.txt



Now, when you enter a row into table a, a row should automagically be appended to table b.

Montag, 7. Dezember 2009

python-perlmodule in the openSUSE build service

If you've ever wanted to embed perl code directly in python code, or execute perl code or functions directly from python, then python-perlmodule may be exactly what you are looking for. This package basically compiles and embeds the perl interpreter inside python. Once installed, it is possible to directly access perl by doing (e.g.):

import perl
x = perl.eval('3+3')
print x
6
print type(x)
'int'

Neat, eh? Theoretically, you could paste any long string of perl code into the perl.eval() function and have it executed as perl. The return value is a perl object, with python attributes. Sounds strange, huh? Well, consider this code:

import perl
x = perl.eval('[12,23,34,45,{ foo=>999,bar=>666},56,67]')
print x

for i in x: print i
...
12
23
34
45

56
67
>>> print x[4]['foo']
999


Quite handy, non? You can also access perl modules using syntax such as:
import perl
perl.require('Digest::MD5')

You can download the rpm directly from my openSUSE build service repository.

Montag, 30. November 2009

TurboGears 2.1a3 is packaged and ready for installation on openSUSE!
I finally got some time over the weekend to look into packaging all the components for TurboGears2 into rpm format. Now that everything has finally successfully built, you can 1-click install TurboGears2 on openSUSE using a simple

zypper in python-tg-devtools
(once you have the repository added as a zypper source, of course. If you haven't you need to try this first:
zypper ar http://download.opensuse.org/repositories/home:/babelworx:/tg21/devel_languages_python_openSUSE_11.22/ home:babelworx:tg21
).Or you can use the button below (on openSUSE).

TurboGears2 now (mostly) packaged as noarch
Given that the packaging took place on an openSUSE 11.2 machine, I was able to package most of the packages as noarch (for older versions of openSUSE it was not really possible - at least not without signifant headache). Some packages are still architecture dependent. For example python-simplejson uses C optimisations, which cannot be build as noarch. Overall, having the repository available mostly as noarch should make installation and updates smoother. Older versions used to install e.g. i586 packages when the x86_64 version wasn't yet available, causing unforeseen application import problems. That should more or less be sorted out now.

Donnerstag, 17. September 2009

Handling multiple file uploads in Turbogears2

Just as a reminder to myself, here's the trick needed in order to handle multiple file uploads using the same name. What does "using the same name" mean? Well, if you use javascript to make your web app's user happy - take http://www.fyneworks.com/jquery/multiple-file-upload/ for example, then you would write something like this in your html:

<form action="u" enctype="multipart/form-data" method="POST">
<input class="multi" maxlength="2" name="doc" type="file" />
<input type="submit" value="Upload" />
</form>

Using trickery like the jquery.MultiFile (from the link above) - i.e. sending multiple files using the same input field, what will happen is that all the files will arrive through request.POST['doc'] - i.e. you don't automatically get a list of files such as request.POST['doc'] = [FileStorage(), FileStorage()] - which would probably be exactly what you want in order to iterate through. Instead, you have to use the request.POST.getall('doc') to generate a list object containing all of the uploaded files.

Samstag, 22. August 2009

I've been doing a lot of work on Turbogears 2.0 recently. One of the things that really impressed me with Turbogears was how easy it is to re-use form components using e.g. Toscawidgets. Seeing as I also want to use the jquery javascript library to add coolness (yes, and usability), I was even more impressed to see that there is an official Toscawidgets jquery module - tw.jquery. However, it was a bit of a downer to see that there weren't all that many plugins packaged in there. Particularly, the one I needed wasn't there: http://plugins.jquery.com/project/comboselect

The way I see it, with Toscawidgets and Turbogears, you are either using widgets or you are not. Trying to sometimes include widgets, or to have only some widget form fields just doesn't work properly - it might be doable, but it's a pain. Especially when it comes to form validation. Thus, I thought it was time to bite into the sour apple and try to package the comboselect plugin myself. After some research and having a look at how other people accomplished this with jquery plugins, I managed to whack together something that works for me. Here's the class:

from tw.api import Widget, JSLink, CSSLink, js_function, js_callback
from tw.forms.fields import MultipleSelectField
from tw.jquery.base import *

jQuery = js_function('$')

jquery_selso_js = JSLink(
filename='static/javascript/jquery.selso.js',
javascript=[jquery_js],
modname=__name__)

jquery_comboselect_js = JSLink(
filename='static/javascript/jquery.comboselect.js',
javascript=[jquery_selso_js],
modname=__name__)

jquery_comboselect_css = CSSLink(
filename='static/css/jquery.comboselect.css',
modname=__name__)

class ComboSelect(MultipleSelectField):
javascript=[jquery_comboselect_js]
css=[jquery_comboselect_css]
def update_params(self,d):
super(ComboSelect,self).update_params(d)
self.update_attrs(d, "size")
d['attrs']['multiple'] = True
self.add_call("$(function(){$('#%s').comboselect({ sort: 'both', addbtn: '+',  rembtn: '-' });});"%d.id)


I added the above code to a new file "comboselect.py" in /usr/lib64/python2.6/site-packages/tw/jquery/ directory (yes, I edited this directly - I know, not elegant).

These additions were necessary to the /usr/lib64/python2.6/site-packages/tw/jquery/static/javascript/ directory:
This change was necessary to the /usr/lib64/python2.6/site-packages/tw/jquery/static/css directory:
When this is all done, you can create a widget in the normal manner, as per the standard TurboGears documentation:
"""Movie Form"""

from tw.api import WidgetsList,CSSLink
from tw.forms import TableForm, CalendarDatePicker, SingleSelectField, TextField, TextArea
from tg import url
from tw.jquery.comboselect import ComboSelect

class MovieForm(TableForm):

class fields(WidgetsList):
title = TextField()
year = TextField()
#release_date = CalendarDatePicker()
genre_options = enumerate((
'Action & Adventure', 'Animation', 'Comedy',
'Documentary', 'Drama', 'Sci-Fi & Fantasy'))
genre = ComboSelect(options=genre_options)
description = TextArea()

movie_form = MovieForm("create_movie_form")
Follow the rest of the documentation on the TurboGears documentation page referenced above and you should get something like this:

Samstag, 7. März 2009

How much energy does that steak have?

While studying for the second part of the LPIC 1 Certificate(LPI102), I started messing about with bash scripts. I still way prefer python, but bash is handy to whip together semi useful stuff quickly.
Anyhow, this script quite ineffeciently gets the kilocalorific (kcal) value of (some) foods:
#!/bin/bash
searchterm=$1
data=$(curl -# 'http://www.med-surfer.de/kalorientabelle/kalorientabelle.php3')
echo $data | egrep -i "
.*
" | sed -e "s/<\/td>/<\/td>\n/g" | egrep -i "value='.*kcal.*'>" | sed -e "s/.*value='//g" | sed -e "s/'><\/td>$//g" | sed -e "s/\.\+/ /g" | sed -e "s/kcal/kcal pro 100g hat/g" | iconv -f iso-8859-1 | grep -i $searchterm
Of course, you can see immediately that this is limited to the html formatting on the page above - which in turn means you have to enter your query auf deutsch. Furthermore, you could probably reduce the number of times that sed is called significantly. And you could grep more than just one foodstuff quite easily either. But this at least helped to get me up to speed for the 102.
By the way, everything after the echo $data above is on one single line - the blog format might split it up. Otherwise, do:
  1. wget http://www.foxhall.de/downloads/kal.sh
  2. chmod +x kal.sh
  3. ./kal.sh apfel
I'd be interested in hearing how I could improve it - not what the script does, but how it does it - e.g. reducing the numbers of grep and sed calls.

Montag, 2. März 2009

LPI 102 Objectives by weight

LPI 102 Objectives, sorted by weight

1
1.107.2 Manage printers and print queues
1.107.3 Print files
1.107.4 Install and configure local and remote printers
1.108.5 Notify users on system-related issues
1.114.3 Setup user level security

3
1.105.2 Reconfigure, build and install a custom kernel and kernel modules
1.106.1 Boot the system
1.106.2 Change runlevels and shutdown or reboot system
1.108.2 Find Linux documentation on the Internet
1.109.2 Customize or write simple scripts
1.111.3 Configure and use system log files to meet admin and security needs
1.111.5 Maintain an effective data backup strategy
1.112.4 Configure Linux as a PPP client
1.114.2 Setup host security

4
1.105.1 Manage/Query kernel and kernel modules at runtime
1.108.1 Use and manage local system documentation
1.111.1 Manage users and group accounts and related system files
1.111.2 Tune the user environment and system environment variables
1.111.4 Automate system admin tasks by scheduling jobs to run in the future
1.111.6 Maintain system time
1.112.1 Fundamentals of TCP/IP
1.113.1 Configure and manage xinetd, inetd and related services
1.113.2 Operate and perform basic configuration of Mail Tranfer Agent (MTA)
1.113.3 Operate and perform basic configuration of Apache
1.113.4 Properly manage the NFS and SAMBA daemons
1.113.5 Setup and configure basic DNS services
1.113.7 Set up secure shell (OpenSSH)
1.114.1 Perform security administration tasks

5
1.109.1 Customize and use the shell environment

7
1.112.3 TCP/IP configuration and troubleshooting

Sonntag, 18. Januar 2009

LPI 101 Objectives, sorted by weight

Weight 8

  • Use Debian package manager 101

  • Use RPM package manager 101


Weight 7

  • TCP/IP configuration and troubleshooting 102


Weight 6

  • Process text streams using filters 101


Weight 5

  • Design harddisk layout 101

  • Make and install programs from source 101

  • Work on the commandline 101

  • Use streams, pipes and redirects 101

  • Create, monitor and kill processes 101

  • Use file permissions to control access to files 101

  • Find system files and put them in their correct locations 101

  • Install and configure X 102

  • Install and customise window manager environment 102

  • Customise and use shell environment 102


Weight 4

  • Manage/query kernel and kernel modules at runtime 102

  • Use and manage local system documentation 102

  • Manage user/group accounts and associated files 102

  • Automate system administration by scheduling future jobs 102

  • Maintain system time 102

  • Fundamentals of TCP/IP 102

  • Configure and manage xinetd, inetd etc 102

  • Operate and perform basic configuration of Mail Transfer Agent 102

  • Operate and perform basic configuration of Apache 102

  • Properly manage NFS and SAMBA daemons 102

  • Setup and configure DNS services 102

  • Setup SSH (openSSH) 102

  • Perform security administration tasks 102


Weight 3

  • Setup different PC expansion cards 101

  • Manage shared libraries 101

  • Perform basic file management 101

  • Modify process execution priorities 101

  • Search files using regular expressions 101

  • Create partitions and file systems 101

  • Maintain integrity of file systems 101

  • Control mount/umount of file systems 101

  • Manage disk quotas 101

  • Setup a display manager 101

  • Reconfigure, build, install custom kernel and kernel modules 102

  • Boot the system 102

  • Change runlevels, shutdown, reboot 102

  • Find Linux documentation on the Internet 102

  • Customise or write simple scripts 102

  • Tune user environment and system environment variables 102

  • Configure and use system logs to meet admin and security needs 102

  • Maintain effective data backup strategy 102

  • Configure Linux as PPP client 102

  • Setup host security 102


Weight 1

  • Configure fundamental BIOS settings 101

  • Configure modem and soundcard 101

  • Setup non-IDE devices 101

  • Configure communication devices 101

  • Configure USB devices 101

  • Install boot manager 101

  • Basic file editing with vi 101

  • Manage file ownership 101

  • Create and change hard links and symbolic links 101

  • Manage printers and print queues 102

  • Print files 102

  • Install and configure local and remote printers 102

  • Notify users on system related issues 102

  • Setup user level security 102