<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="assets/xml/rss.xsl" media="all"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Felix Schwarz – Software-Entwicklung und Beratung</title><link>https://www.schwarz.eu/</link><description></description><atom:link href="https://www.schwarz.eu/rss.xml" rel="self" type="application/rss+xml"></atom:link><language>de</language><lastBuildDate>Tue, 24 Sep 2019 19:52:13 GMT</lastBuildDate><generator>Nikola (getnikola.com)</generator><docs>http://blogs.law.harvard.edu/tech/rss</docs><item><title>GEGLs "Mono Mixer" mit Python verwenden</title><link>https://www.schwarz.eu/posts/2019/gegl-monomixer-mit-python/</link><dc:creator>Felix Schwarz</dc:creator><description>&lt;p&gt;
&lt;strong&gt;Eine Lobeshymne auf GEGL, gobject-introspection und Fedora&lt;/strong&gt;
&lt;/p&gt;
&lt;h3 id="Hintergrund:Rotfilter"&gt;Hintergrund: Rotfilter&lt;/h3&gt;
&lt;p&gt;
Diese Woche konnte ich mich endlich einem Problem widmen, welches ich schon längere Zeit im Hinterkopf hatte: Einer meiner Kunden muss regelmäßig Text aus gescannten Rezepten der gesetzlichen Krankenversicherung extrahieren. Im Prinzip eine relativ einfache OCR-Aufgabe.
&lt;/p&gt;
&lt;p&gt;
Allerdings enthält bereits der Blankobeleg einige Texte wie z.B. die Beschriftung der einzelnen Felder. Die Arzt- bzw. Apothekenbedruckung kann durchaus über die vorhandenen Texte gedruckt werden. Um der OCR die Aufgabe zu erleichtern, werden für die Texte des Blankobelegs Rottöne verwendet, während Arzt und Apotheker in Schwarz drucken.
&lt;/p&gt;
&lt;p&gt;
Mittels eines Rotfilters können daher alle verdefinierten Texte des Belegs entfernt werden, so dass die OCR nur noch den eigentlichen Arzt- bzw. Apothekendruck erkennt. Heraus kommt dann ein Bild, welches nur noch Grautöne enthält. Bislang wird dies direkt durch die "Hardware"/Spezialscanner des Kunden durchgeführt, die dafür auch gut getunte Algorithmen verwenden.
&lt;/p&gt;
&lt;p&gt;
Allerdings gibt es dabei wie immer ein paar Nachteile:
&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;Durch den Rotfilter gehen auch Informationen verloren, so dass eine eventuell nötige manuelle Nacharbeit schwieriger wird. 
&lt;/li&gt;&lt;li&gt;In Einzelfällen werden auch kleinere Scanner benutzt, die über keinen Rotfilter verfügen.
&lt;/li&gt;&lt;li&gt;Der wichtigste Punkt für mich ist aber die Unflexibilität des Hardwarefilters: Arzt- und der Apothekendruck können sehr unterschiedlich sein, da es sich ja um zwei verschiedene Drucker handelt. Manchmal ist einer der Drucke nur schwach zu erkennen (nahezu erschöpfte Farbbänder/Tonerkartuschen), während der andere kräftig und klar ist. Je nach Schwellwert kann es sein, dass der Rotfilter den schwachen Druck komplett entfernt. Ist der Schwellwert aber zu niedrig eingestellt, bleibt zu viel Text der Blankovorlage erhalten. Ich würde also gerne auf dem farbigen Scan bestimmte Bereiche mit unterschiedlichen Schwellwerten versehen (ggf. auch in Abhängigkeit der OCR-Ergebnisse).
&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;
Ich habe bereits vor Jahren einen primitiven Filter selbst geschrieben. Dieser funktioniert meistens „gut genug“, ist aber sehr langsam und ist nur schlecht parametrisierbar.
&lt;/p&gt;
&lt;h3 id="GEGLMonoMixer"&gt;GEGL Mono Mixer&lt;/h3&gt;
&lt;p&gt;
&lt;a class="ext-link" href="http://www.gegl.org/"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;GEGL&lt;/a&gt; („Generic Graphics Library“) ist eine Bildbearbeitungsbibliothek, die ursprünglich entwickelt wurde, um Grafikoperationen für &lt;a class="ext-link" href="https://www.gimp.org/"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;GIMP&lt;/a&gt; bereitzustellen. Allerdings ist GEGL in keiner Weise an GIMP gebunden, sondern kann auch völlig unabhängig davon verwendet werden.
&lt;/p&gt;
&lt;p&gt;
Insbesondere stellt GEGL eine Operation namens &lt;a class="ext-link" href="http://gegl.org/operations/gegl-mono-mixer.html"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;Mono Mixer&lt;/a&gt; („Monochrome channel mixer“) bereit. Diese Funktion ist genau der gesuchte Rotfilter. 
&lt;/p&gt;
&lt;p&gt;
&lt;a style="padding:0; border:none" href="https://www.schwarz.eu/posts/2019/gegl-colorfilter/colorfilter-result.png"&gt;&lt;img width="500px" alt="/posts/2019/gegl-colorfilter/colorfilter-result.png" title="/posts/2019/gegl-colorfilter/colorfilter-result.png" style="margin-right:auto; display:block; margin-left:auto" src="https://www.schwarz.eu/posts/2019/gegl-colorfilter/colorfilter-result.png"&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;
Allerdings ist GEGL größtenteils in C geschrieben, während die OCR ansonsten mit Python gesteuert wird. Allerdings wurde im Umfeld des GNOME-Projekts die großartige &lt;a class="ext-link" href="https://gi.readthedocs.io/"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;gobject-introspection&lt;/a&gt; Bibliothek entwickelt. Damit ist es möglich, entsprechend annotierten C-Code aus verschiedensten Sprachen wie Python oder JavaScript aufzurufen, ohne dafür spezielle C-Anbindungen schreiben zu müssen.
&lt;/p&gt;
&lt;p&gt;
Der größte Nachteil ist aus meiner Sicht, dass es für GEGL nur wenig Dokumentation gibt. Insofern ist der folgende Abschnitt vielleicht auch für andere interessant :-)
&lt;/p&gt;
&lt;h3 id="Code:RotfiltermitGEGLundPython"&gt;Code: Rotfilter mit GEGL und Python&lt;/h3&gt;
&lt;div class="wiki-code"&gt;&lt;div class="code"&gt;&lt;pre&gt;&lt;span class="ch"&gt;#!/usr/bin/env python3&lt;/span&gt;
&lt;span class="c1"&gt;# Licensed under the Creative Commons Zero v1.0 Universal&lt;/span&gt;
&lt;span class="c1"&gt;# https://creativecommons.org/publicdomain/zero/1.0/&lt;/span&gt;
&lt;span class="c1"&gt;# SPDX-License-Identifier: CC0-1.0&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;gi&lt;/span&gt;

gi&lt;span class="o"&gt;.&lt;/span&gt;require_version&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Gegl'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'0.4'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;gi.repository&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; Gegl
Gegl&lt;span class="o"&gt;.&lt;/span&gt;init&lt;span class="p"&gt;()&lt;/span&gt;

graph &lt;span class="o"&gt;=&lt;/span&gt; Gegl&lt;span class="o"&gt;.&lt;/span&gt;Node&lt;span class="p"&gt;();&lt;/span&gt;
gegl_img &lt;span class="o"&gt;=&lt;/span&gt; graph&lt;span class="o"&gt;.&lt;/span&gt;create_child&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'gegl:load'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
gegl_img&lt;span class="o"&gt;.&lt;/span&gt;set_property&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'path'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'input.jpg'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

colorfilter &lt;span class="o"&gt;=&lt;/span&gt; graph&lt;span class="o"&gt;.&lt;/span&gt;create_child&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'gegl:mono-mixer'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
colorfilter&lt;span class="o"&gt;.&lt;/span&gt;set_property&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'preserve-luminosity'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
colorfilter&lt;span class="o"&gt;.&lt;/span&gt;set_property&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'red'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
colorfilter&lt;span class="o"&gt;.&lt;/span&gt;set_property&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'green'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
colorfilter&lt;span class="o"&gt;.&lt;/span&gt;set_property&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'blue'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
gegl_img&lt;span class="o"&gt;.&lt;/span&gt;link&lt;span class="p"&gt;(&lt;/span&gt;colorfilter&lt;span class="p"&gt;)&lt;/span&gt;

sink &lt;span class="o"&gt;=&lt;/span&gt; graph&lt;span class="o"&gt;.&lt;/span&gt;create_child&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'gegl:jpg-save'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
sink&lt;span class="o"&gt;.&lt;/span&gt;set_property&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'path'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'output.jpg'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
colorfilter&lt;span class="o"&gt;.&lt;/span&gt;link&lt;span class="p"&gt;(&lt;/span&gt;sink&lt;span class="p"&gt;)&lt;/span&gt;

sink&lt;span class="o"&gt;.&lt;/span&gt;process&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="c1"&gt;# not calling "Gegl.exit()" to suppress some warnings, see also&lt;/span&gt;
&lt;span class="c1"&gt;# - GEGL issue 142: "GEGL warning und buffer leak after loading a file"&lt;/span&gt;
&lt;span class="c1"&gt;#     https://gitlab.gnome.org/GNOME/gegl/issues/142&lt;/span&gt;
&lt;span class="c1"&gt;# Gegl.exit()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;h3 id="Ergebnisse"&gt;Ergebnisse&lt;/h3&gt;
&lt;p&gt;
Der obige Farbfilter (mit angepassten RGB-Parametern) liefert bei mir exzellente Ergebnisse. Der neue Code ist zudem etwa 4-5x schneller und viel besser parametrisierbar, so dass die Erkennungsrate auch bei schwachem Druck besser ist.
&lt;/p&gt;
&lt;p&gt;
Außerdem will ich an dieser Stelle noch mal Fedora loben: Alle nötigen Pakete sind mit einem einfachen &lt;code&gt;dnf install gegl04 python3-gobject-base&lt;/code&gt; installiert und einsatzbereit. Fedora 29 bietet derzeit (März 2019) die relativ aktuelle GEGL-Version 0.4.12 an (veröffentlicht im Oktober 2019). Die neueste Upstream-Version ist 0.4.14, die erst vor drei Wochen (am 01. März 2019) veröffentlicht wurde.
&lt;/p&gt;</description><guid>https://www.schwarz.eu/posts/2019/gegl-monomixer-mit-python/</guid><pubDate>Fri, 22 Mar 2019 20:04:00 GMT</pubDate></item><item><title>Ascendos – A New Hope?</title><link>https://www.schwarz.eu/posts/2011/ascendos-a-new-hope/</link><dc:creator>Felix Schwarz</dc:creator><description>&lt;p&gt;
Last week I came across a new Open Source project which looks very promising 
to me (and at the same time got almost no media coverage yet). Therefore I 
decided to blog about this right now: &lt;a class="ext-link" href="http://www.ascendos.org"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;Ascendos&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;
Ascendos &lt;del&gt;is&lt;/del&gt; will be a free rebuild of Red Hat Enterprise Linux (RHEL), 
technically very similar to &lt;a class="ext-link" href="http://www.centos.org"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;CentOS&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;
I started to write a bit of technical background 
&lt;a href="https://www.schwarz.eu/posts/2011/why-you-should-consider-rhel-clones-for-your-linux-machines.html"&gt;what is so interesting about RHEL&lt;/a&gt;
but it became to long so you find that in a separate blog post.
&lt;/p&gt;
&lt;p&gt;
Why is Ascendos so exciting to me:
&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;The &lt;a href="https://www.schwarz.eu/posts/2011/the-downfall-of-centos.html"&gt;CentOS project has severe management problems&lt;/a&gt; and there are no signs
of even acknowledging the problem.
&lt;/li&gt;&lt;li&gt;From the mission statement on the web site it looks like Ascendos will work
in a way that I always wanted from CentOS.
&lt;/li&gt;&lt;li&gt;At least one &lt;a class="ext-link" href="http://www.ascendos.org/forum/index.php/topic,2.msg11.html#msg11"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;core member of Scientific Linux participates in Ascendos&lt;/a&gt; (even though on personal time) and Scientific Linux is generally more &lt;a class="ext-link" href="https://www.scientificlinux.org/distributions/6x/build/problembyrpm"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;open about their "secret sauce"&lt;/a&gt;.
&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;
So it looks like the time has finally come for a serious CentOS contender. 
Currently it's all vaporware but I'm hoping for the best.
&lt;/p&gt;</description><guid>https://www.schwarz.eu/posts/2011/ascendos-a-new-hope/</guid><pubDate>Sat, 23 Jul 2011 22:08:01 GMT</pubDate></item><item><title>The Downfall of CentOS</title><link>https://www.schwarz.eu/posts/2011/the-downfall-of-centos/</link><dc:creator>Felix Schwarz</dc:creator><description>&lt;h2 id="WhatfeaturesmadeCentOSsuccessful"&gt;What features made CentOS successful&lt;/h2&gt;
&lt;p&gt;
As you likely know, &lt;a class="ext-link" href="http://www.centos.org"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;CentOS&lt;/a&gt; is a free rebuild of Red Hat 
Enterprise (RHEL). I wrote a &lt;a href="https://www.schwarz.eu/posts/2011/why-you-should-consider-rhel-clones-for-your-linux-machines.html"&gt;praise on RHEL&lt;/a&gt; which pretty much explains why RHEL is great. 
&lt;/p&gt;
&lt;p&gt;
I don't think it'll surprise you that there are a couple of RHEL rebuilds:
&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;CentOS
&lt;/li&gt;&lt;li&gt;&lt;a class="ext-link" href="http://www.scientificlinux.org"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;Scientific Linux&lt;/a&gt;
&lt;/li&gt;&lt;li&gt;&lt;a class="ext-link" href="http://www.oracle.com/us/technologies/linux/index.html"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;Oracle Linux&lt;/a&gt;
&lt;/li&gt;&lt;li&gt;&lt;a class="ext-link" href="http://linux.startcom.org/"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;Startcom Linux&lt;/a&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;
These rebuilds ("clones" as they are sometimes named) generally take the source
rpms published by Red Hat, remove Red Hat's branding (required by Red Hat legal)
and recompile the packages. The end result is usually free to use.
&lt;/p&gt;
&lt;p&gt;
However even "just" recompiling is quite some work even if it can be done by
individuals which enough time and skill. CentOS gained a lot of users quickly
and always had a couple (about 2-6) core maintainers who did a lot of work. As a
consequence the creators of Whitebox Linux and Tao Linux (both were RHEL clones 
as well) recommended switching to CentOS when they discontinued their work.
&lt;/p&gt;
&lt;p&gt;
CentOS always had the major advantage that they promised the same support period
as RHEL and strived for maximum compatibility with RHEL. CentOS did not add 
additional packages or patches unless it was unavoidable (e.g. adding yum to
update a system in CentOS 4 instead of using the Red Hat Network). 
&lt;/p&gt;
&lt;p&gt;
And last but not least CentOS had a community behind so the project was present
on all major Open Source conferences and fares which might have increased its
popularity as well.
&lt;/p&gt;
&lt;h2 id="ChroniclesofaDownfall"&gt;Chronicles of a Downfall&lt;/h2&gt;
&lt;p&gt;
However even with all that popularity problems began to mount up. CentOS' downfall
did not start due to a major catastrophe but was more a slow but steady process.
During that time the CentOS core team members failed to react appropriately and 
so things got worse.
&lt;/p&gt;
&lt;h4 id="DagWieersleaving"&gt;Dag Wieers leaving&lt;/h4&gt;
&lt;p&gt;
In June 2009 &lt;a class="ext-link" href="http://dag.wieers.com/blog/leaving-centos-team-not-centos-community"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;Dag Wieers left the CentOS project&lt;/a&gt;
because of his level of frustration with the CentOS development team. This in 
itself didn't have a big impact on the project. However it was a clear warning 
sign. When a highly skilled, well known contributor leaves a project not for 
the lack of time but because they are not satisfied with the project itself, it
means that there is something seriously wrong.
&lt;/p&gt;
&lt;p&gt;
Dag is an experienced RPM packager, providing additional RPMs for Fedora and
CentOS for a long time (he's also one of the guys behind &lt;a class="ext-link" href="http://rpmfusion.org"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;RPM Fusion&lt;/a&gt;.
From personal experience I can say that he's a nice guy, easy to deal with and
for sure someone with good 'social' skills.
&lt;/p&gt;
&lt;p&gt;
Now while there were no changes in the project, some of the problems Dag was 
frustrated about became public just a month later…
&lt;/p&gt;
&lt;h4 id="DisruptedcommunicationwithintheCentOScoreteam"&gt;Disrupted communication within the CentOS core team&lt;/h4&gt;
&lt;p&gt;
CentOS was founded by Lance Davis in 2004. However Lance stopped contributing
a few years afterwards. This resulted in an exceptional situation of an 
&lt;a class="ext-link" href="http://lists.centos.org/pipermail/centos/2009-July/079767.html"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;Open Letter to Lance Davis&lt;/a&gt; 
in July 2009.
&lt;/p&gt;
&lt;p&gt;
The public learned that Lance was the sole owner of the CentOS.org domain as 
well as the CentOS Paypal account. For several years he did not even disclose to
other core developers how much money was available and the actual project 
("project" being the people doing marketing and engineering) did not benefit at
all from monetary donations.
&lt;/p&gt;
&lt;p&gt;
Lance did not answer neither email nor calls even from the inner circle of 
CentOS developers. Tim Verhoeven even &lt;a class="ext-link" href="http://lists.centos.org/pipermail/centos/2009-July/079803.html"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;wrote&lt;/a&gt;: 
"We tried to contact Lance multiple times over a period of more then a year."
&lt;/p&gt;
&lt;p&gt;
Ralph Angenent, another CentOS core member, &lt;a class="ext-link" href="http://lestighaniker.de/static/2009/07/30/#open-letter-to-lance-davis"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;wrote&lt;/a&gt;:
"This means that the project depends on one person in too many ways. Add to 
that a person who doesn’t answer calls, isn't available as meetings, doesn't 
publish things he promised to do - we have a problem. And this is unacceptable. 
We as a project have to be more transparent. And this is one of the things 
blocking this."
&lt;/p&gt;
&lt;p&gt;
As many users mentioned this is unacceptable and not only a failure of Lance 
but IMHO also a project failure because keeping quiet for long means that 
problems are just brushed under the carpet.
&lt;/p&gt;
&lt;p&gt;
Also &lt;a class="ext-link" href="http://dag.wieers.com/blog/the-burden-of-keeping-things-private"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;Dag Wieer's comment on the whole story&lt;/a&gt; 
is an interesting read and he disclosed that Lance's position and absence was 
one of the things which drove him to leave the CentOS project.
&lt;/p&gt;
&lt;p&gt;
End of the story was that Lance showed up in a IRC meeting two days later and
the issues were resolved. However at least to me as an outsider transparency
did not really get better…
&lt;/p&gt;
&lt;h4 id="Releasedelaysandmissingsecurityupdates"&gt;Release delays and missing security updates&lt;/h4&gt;
&lt;p&gt;
RHEL clones can publish their updates only after Red Hat naturally. For CentOS
regular updates are done in a quite timely manner. However every few months 
Red Hat releases 'minor updates' (similar to Windows service packages) like 5.1, 5.2, …
Many CentOS pay a lot of attention to the time it takes CentOS to get their 
release out as the update also contains security fixes (comparions with release 
dates of Scientific Linux are misleading as the latter project continues to
publish security fixes even when the full update is not yet released).
&lt;/p&gt;
&lt;p&gt;
Wikipedia has the complete &lt;a class="ext-link" href="http://en.wikipedia.org/w/index.php?title=CentOS&amp;amp;oldid=439094191#Release_history"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;release history with the slack time relative to RHEL&lt;/a&gt;.
Generall you can see that the slack time increases over time both for CentOS 4 
and 5 which means CentOS became slower to follow up on Red Hat (the quick release
of CentOS 4.9 is likely because Red Hat doesn't change much anymore for RHEL 4.x
at this point in the release cycle).
&lt;/p&gt;
&lt;p&gt;
This culminated in a delay of 85 days to release CentOS 5.6 which is 
&lt;strong&gt;almost 3 months without security updates&lt;/strong&gt; for a big install base! How is 
that for an &lt;em&gt;enterprise&lt;/em&gt; (C&lt;em&gt;ent&lt;/em&gt;OS) distribution?
&lt;/p&gt;
&lt;p&gt;
Actually Karanbir Singh mentioned that &lt;a class="ext-link" href="http://lists.centos.org/pipermail/centos-devel/2011-February/006916.html"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;critical/important security fixes will be published for CentOS 5.5 as updates&lt;/a&gt; but in that
case he failed by his own standards:
&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;Firefox and Thunderbird: &lt;a class="ext-link" href="https://www.redhat.com/security/data/cve/CVE-2011-0051.html"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;CVE-2011-0051&lt;/a&gt;, &lt;a class="ext-link" href="https://www.redhat.com/security/data/cve/CVE-2011-0053.html"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;CVE-2011-0053&lt;/a&gt;, &lt;a class="ext-link" href="https://www.redhat.com/security/data/cve/CVE-2011-0054.html"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;CVE-2011-0053&lt;/a&gt;, &lt;a class="ext-link" href="https://www.redhat.com/security/data/cve/CVE-2011-0055.html"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;CVE-2011-0055&lt;/a&gt;, &lt;a class="ext-link" href="https://www.redhat.com/security/data/cve/CVE-2011-0056.html"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;CVE-2011-0056&lt;/a&gt;, &lt;a class="ext-link" href="https://www.redhat.com/security/data/cve/CVE-2011-0057.html"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;CVE-2011-0057&lt;/a&gt;, &lt;a class="ext-link" href="https://www.redhat.com/security/data/cve/CVE-2011-0058.html"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;CVE-2011-0058&lt;/a&gt;, &lt;a class="ext-link" href="https://www.redhat.com/security/data/cve/CVE-2011-0061.html"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;CVE-2011-0061&lt;/a&gt;, &lt;a class="ext-link" href="https://www.redhat.com/security/data/cve/CVE-2011-0062.html"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;CVE-2011-0062&lt;/a&gt;
&lt;/li&gt;&lt;li&gt;libtiff: &lt;a class="ext-link" href="https://www.redhat.com/security/data/cve/CVE-2011-0192.html"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;CVE-2011-0192&lt;/a&gt;, &lt;a class="ext-link" href="https://www.redhat.com/security/data/cve/CVE-2011-0282.html"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;CVE-2011-0282&lt;/a&gt;
&lt;/li&gt;&lt;li&gt;krb5: &lt;a class="ext-link" href="https://www.redhat.com/security/data/cve/CVE-2011-0281.html"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;CVE-2011-0281&lt;/a&gt;
&lt;/li&gt;&lt;li&gt;glibc &lt;a class="ext-link" href="https://www.redhat.com/security/data/cve/CVE-2011-0536.html"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;CVE-2011-0536&lt;/a&gt;
&lt;/li&gt;&lt;li&gt;…
&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;
As you can see on the CentOS archive &lt;a class="ext-link" href="http://vault.centos.org/5.5/updates/x86_64/RPMS/"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;there were no updates after the release of RHEL 5.6&lt;/a&gt; (13.01.2011). There was even a LWN article about that: &lt;a class="ext-link" href="https://lwn.net/Articles/429364/"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;CentOS 5, RHEL 5.6, and security updates&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;
Compared to that the 242 days of delay to publish CentOS 6.0 is not that 
significant because there were no existing CentOS 6 users waiting for security 
updates. Though the enormous time tells something about CentOS' capacities.
&lt;/p&gt;
&lt;h4 id="LackofCollaboration"&gt;Lack of Collaboration&lt;/h4&gt;
&lt;p&gt;
Red Hat publishes their source RPMs which is really nice because makes 
rebuilding RHEL possible. However rebuilding is still not trivial because of
Red Hat's packaging errors (missing build requirements for example) and 
non-obvious build system requirements. &lt;a class="ext-link" href="https://www.scientificlinux.org/distributions/6x/build/problem"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;Scientific Linux documented the rebuild problems&lt;/a&gt; they encountered. Also rebuilding from 
scratch (and keeping compatibility with RHEL) requires to follow a specific,
non-documented build order.
&lt;/p&gt;
&lt;p&gt;
But despite of that core members just deny that there is any "secret sauce" (&lt;a class="ext-link" href="http://lists.centos.org/pipermail/centos-devel/2011-January/006468.html"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;1&lt;/a&gt;, &lt;a class="ext-link" href="http://lists.centos.org/pipermail/centos-devel/2011-February/006967.html"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;2&lt;/a&gt;, &lt;a class="ext-link" href="http://lists.centos.org/pipermail/centos-devel/2011-March/007087.html"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;3&lt;/a&gt;, &lt;a class="ext-link" href="http://lists.centos.org/pipermail/centos-devel/2011-March/007114.html"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;4&lt;/a&gt;, …).
&lt;/p&gt;
&lt;p&gt;
Questions about privately rebuilding RHEL are frowned upon on the CentOS devel 
mailing list (for example &lt;a class="ext-link" href="http://lists.centos.org/pipermail/centos-devel/2010-November/006089.html"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;1&lt;/a&gt;)
or people are put of with superficial answers (&lt;a class="ext-link" href="http://lists.centos.org/pipermail/centos-devel/2011-February/006814.html"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;1&lt;/a&gt;, &lt;a class="ext-link" href="http://lists.centos.org/pipermail/centos-devel/2011-February/006712.html"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;2&lt;/a&gt;).
&lt;/p&gt;
&lt;p&gt;
In the end there is a lot of &lt;a class="ext-link" href="http://lists.centos.org/pipermail/centos-devel/2011-February/006858.html"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;fear&lt;/a&gt; (of unofficial rebuilds) and of giving away their own position:
&lt;/p&gt;
&lt;blockquote class="citation"&gt;
&lt;p&gt;
Red Hat did not tell me how to build it. (...) Why should I tell someone how 
to build a replacement OS to CentOS.&lt;em&gt;
&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;
(&lt;a class="ext-link" href="http://lists.centos.org/pipermail/centos-devel/2011-March/007081.html"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;Johnny Hughes&lt;/a&gt;).
Also &lt;a class="ext-link" href="http://lists.centos.org/pipermail/centos-devel/2011-February/006839.html"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;FUD tactics&lt;/a&gt; are used for excuses not to publish things.
&lt;/p&gt;
&lt;p&gt;
There is &lt;a class="ext-link" href="http://lists.centos.org/pipermail/centos-devel/2011-March/007118.html"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;one statement which summarizes the philosophy pretty well&lt;/a&gt;:
&lt;/p&gt;
&lt;blockquote class="citation"&gt;
&lt;p&gt;
Our goal is not a reproducable system so YOU can build software, it is for 
US to produce software.  If you are looking for a distribution that teaches 
YOU to build things, get Gentoo or Linux From Scratch."
&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;
Uh, wait. I always though that sharing knowledge and enabling people to do 
things on their own was at the very heart of the Free Software Movement? Well,
this brings us to the next problem…
&lt;/p&gt;
&lt;h4 id="CentOSisonlyacommunityofusers"&gt;CentOS is only a &lt;em&gt;community of users&lt;/em&gt;&lt;/h4&gt;
&lt;p&gt;
Many people critizing the CentOS core team because of their attitude towards the
community don't understand how Karanbir and Johnny see the CentOS 'community'.
&lt;/p&gt;
&lt;p&gt;
&lt;a class="ext-link" href="http://lists.centos.org/pipermail/centos-devel/2011-January/006422.html"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;Karanbir, January 2011&lt;/a&gt;:
&lt;/p&gt;
&lt;blockquote class="citation"&gt;
&lt;p&gt;
what do you think is your association with CentOS ? do you use it ? if so, 
you are a part of the community already. None ever said it was BUILT by a 
bunch of random driveby community members. Its built for a community and 
around a community. 
&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;
&lt;a class="ext-link" href="http://lists.centos.org/pipermail/centos-devel/2011-March/007101.html"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;Johnny&lt;/a&gt;:
&lt;/p&gt;
&lt;blockquote class="citation"&gt;
&lt;p&gt;
Community because all the QA team, the forum moderators, the graphics
team, the mailing lists and the wiki are all members of the community
providing help. (…)
CentOS is for the community ... it is not BUILT buy the community.
&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;
As you can see, even though CentOS is officially the 'Community Enterprise OS',
the term 'community' only means a community of mere users. 
&lt;/p&gt;
&lt;h4 id="Mywayorthehighwayattitude-failuretounderstandtheessenceofvoluntarycontributions"&gt;"My way or the highway" attitude - failure to understand the essence of voluntary contributions&lt;/h4&gt;
&lt;p&gt;
&lt;a class="ext-link" href="http://lists.centos.org/pipermail/centos-devel/2010-November/006103.html"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;Karanbir Singh, November 2010&lt;/a&gt;:
&lt;/p&gt;
&lt;blockquote class="citation"&gt;
&lt;p&gt;
Lots of people will argue that open source works in a way where people 
do what they want to do, so you cant tell them what needs doing - and 
they will do what they want, when they want. Its what many imagine is 
the 'fun' in the open source way. Fortunately, or unfortunately we dont 
have that luxury. What comes down the pipe needs to be addressed, 
sometimes its what we want to do - and sometimes its what needs doing 
because that's the issue on hand. The process we have in place is mostly 
finite, with a specified origin and a specified delivery expectation. We 
need to join those dots. And if people don't want to help with that 
joining-the-dots effort, they are never going to be a part of the process.
&lt;/p&gt;
&lt;p&gt;
So when people imply that there are lots of potential-contributers who 
would want to get involved and help etc : What fantasy world are they 
looking at ? I, or one, would like to get in on that action please.
&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;
To me this post illustrates a severe lack of misunderstanding how voluntary
contributions work. The first two sentences are mostly correct. However the 
conclusions he draws from this ("if people don't want to help with that 
joining-the-dots effort, they are never going to be a part of the process.")
are just shortsighted.
&lt;/p&gt;
&lt;p&gt;
The essence of voluntary work is motivation. Potential contributors will have 
all kinds of motivation when the first try to help. It is crucial to allow them
to work how they want initially. Over time some will stick with the project and
develop a sense of duty so they care also about non-fun stuff. Others will 
contribute only one small thing but even the little things will add up as they
were shared.
&lt;/p&gt;
&lt;p&gt;
However if you set a process in stone and throw some more or less boring tasks 
with much explanation over the fence (as Karanbir did initially) it's very 
unlikely to attract new contributors. So in a way Karanbir is right: There are 
not many potential contributors – for his definition of potential contributor 
as someone who is willing to even boring tasks without much questioning. 
&lt;/p&gt;
&lt;p&gt;
This ties in "nicely" with an attitude that many people offering help want to 
do their own/&lt;a class="ext-link" href="http://lists.centos.org/pipermail/centos-devel/2011-February/006837.html"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;don't care for "the project"&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;
That attitude is absolutely killing. Getting new volunteers in a free software
project is usually quite hard. The project members have to work hard (in a 
social sense, not technically!) to get new developers constantly. Any obstactles
in that process will stop people from contributing very quickly.
&lt;/p&gt;
&lt;p&gt;
Well, so it does come as a surprise that there is very few attempts are being
make to speed up the release process with the help of outsiders 
(&lt;a class="ext-link" href="http://lists.centos.org/pipermail/centos-devel/2011-February/006704.html"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;1&lt;/a&gt;) 
and that there are &lt;a class="ext-link" href="http://lists.centos.org/pipermail/centos-devel/2011-February/006815.html"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;still significant delays in the release process&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;
At the same time I find interesting that &lt;a class="ext-link" href="http://lists.centos.org/pipermail/centos-devel/2011-April/007371.html"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;Dag doubts that QA is the main bottleneck&lt;/a&gt;. Which in turn means getting more people involved in actually building CentOS might be a good thing…
&lt;/p&gt;
&lt;p&gt;
Also CentOS chose a way of working which &lt;a class="ext-link" href="http://lists.centos.org/pipermail/centos-devel/2011-February/006735.html"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;requires extra work to pass on knowledge&lt;/a&gt; about the build process. As Johnny wrote even in case of a missing build requirement they don't modify the source rpm.
&lt;/p&gt;
&lt;p&gt;
While I certainly like that CentOS tries not to change the upstream sources I think this is a clear exaggeration: In the end the missing package is use in the build one way or another. The alternative (add the missing BuildRequires line which Red Hat should've added) is quicker (no need to change the build root/restart the build process), passes the knowledge to others (as it is recorded in the source rpm) and looks just like the right approach to the problem. Of course filing bugs in the Red Hat Bugzilla should always be done.
&lt;/p&gt;
&lt;p&gt;
Another limiting factor is that CentOS is very strict about not publishing 
anything before they are sure that all branding was removed (&lt;a class="ext-link" href="http://lists.centos.org/pipermail/centos-devel/2010-November/006138.html"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;Karanbir's statement&lt;/a&gt;)
unlike Scientific Linux which also publishes betas and the like.
&lt;/p&gt;
&lt;p&gt;
A good example for the lack of understanding is a &lt;a class="ext-link" href="http://lists.centos.org/pipermail/centos-devel/2010-November/006135.html"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;conversation between Douglas McClendon and Karanbir Singh&lt;/a&gt; 
where Karanbir does not accept any of Douglas' more significant proposals 
related to feedback and generally dismisses most of his ideas pretty strictly 
("if you believe that, you are about as far away from CentOS as can be.", "No, 
that's a serious waste of time and effort.", "Whats so hard to understand about 
that ?").
&lt;/p&gt;
&lt;h2 id="Analysisofthefailures"&gt;Analysis of the failures&lt;/h2&gt;
&lt;p&gt;
I think the current situation can be attributed to the three following project 
failures:
&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Showing the secret sauce only to core members&lt;/strong&gt; Details of the 
development process are completely intransparent: How packages
are rebuilt, which special handholding is required for which package is 
not documented because of an attitude "this process works, we need help 
elsewhere". Many people and companies are interested in this (so there they
are potential contributors) but the project doesn't grab them.
&lt;/li&gt;&lt;li&gt;&lt;strong&gt;No strategic community building&lt;/strong&gt;: There is no strategic effort to
recruit new team members constantly which is required to keep a project alive
in the long run.
&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Lack of trust and fear of uncontrolled work&lt;/strong&gt;: You have to complete 
predefined QA tasks or something similar before you can even start thinking 
about fixing (maybe very small) issues. You can't even work on your own and
just submit the results because you lack information from the very beginning
unless you are a CentOS team member.
&lt;/li&gt;&lt;/ul&gt;&lt;h2 id="IsthistheEndofCentOS"&gt;Is this the End of CentOS?&lt;/h2&gt;
&lt;p&gt;
Now even with all my ranting about the CentOS project I don't think it is dead
already. Certainly some users switched while waiting on CentOS 5.6/6.0 (mostly
to Scientific Linux I think). The bad news got some media coverage which has 
some impact on CentOS' image but the install base is still huge. There are still
people doing fares and conferences for CentOS. Karanbir Singh is still doing 
package rebuilding.
&lt;/p&gt;
&lt;p&gt;
&lt;strong&gt;So the project is as alive as a year ago. Its weaknesses are just more obvious 
than before.&lt;/strong&gt;
&lt;/p&gt;
&lt;p&gt;
The most important thing keeping CentOS strong at the moment is that
there is no real competitor right now. What is needed is a real community-based,
open and transparent RHEL clone which does not add any features (like CentOS, 
even better if there is no such thing as "CentOS extras").
&lt;/p&gt;
&lt;p&gt;
Even with such a contender CentOS will remain an important distribution as long
as the key people still invest time to maintain CentOS. So currently it's are
bit premature to talk about a 'downfall' of CentOS but there is for sure an
eclipse…
&lt;/p&gt;</description><guid>https://www.schwarz.eu/posts/2011/the-downfall-of-centos/</guid><pubDate>Sat, 23 Jul 2011 22:07:05 GMT</pubDate></item><item><title>Why you should consider RHEL+clones for your Linux machines</title><link>https://www.schwarz.eu/posts/2011/why-you-should-consider-rhel-clones-for-your-linux-machines/</link><dc:creator>Felix Schwarz</dc:creator><description>&lt;p&gt;
If you're not familiar with the Red Hat Linux world, you might ask why you 
should bother at all looking at this enterprisy Linux. After all Ubuntu is for
sure the most successful desktop Linux distribution which even has 'long-term' 
support (LTS releases). Alternatively Debian might be seen as a stable 
distribution without too much commercial influence.
&lt;/p&gt;
&lt;p&gt;
Well, Red Hat enterprise Linux beats these alternatives single handed in my use cases: 
&lt;/p&gt;
&lt;p&gt;
&lt;strong&gt;7 years of security support for every major version (4.x, 5.x, 6.x) for all packages&lt;/strong&gt;&lt;br&gt;
This is already a significant advantange over Ubuntu's LTS for me because 
Ubuntu's LTS does not really have "long-term" support:
&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;Packages from the universe/multiverse repositories don't have any long-term
support, sometimes they are more like some quickly created packages without
a community of packagers maintaining them.
&lt;/li&gt;&lt;li&gt;desktop packages are only supported for three years, afterwards you're left 
alone.
&lt;/li&gt;&lt;li&gt;And last but not least it's only 5 years support even for server packages.
&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;
I run a couple of Linux servers and even today (July 2011) I'm running 3-4 
CentOS 4 boxes. CentOS 4 was released initially in 2005 and these servers now 
run for more than six years now without problems.
&lt;/p&gt;
&lt;p&gt;
Unlike Debian installs I did not have to upgrade my install (with the need to
adapt configurations because of new major versions being incompatible with the
previously installed versions). All I had to do is to install updates coming in
though yum.
&lt;/p&gt;
&lt;p&gt;
Red Hat invests a significant amount of time to update all the old components 
(RHEL 4 comes with Linux kernel 2.6.9) in order to minimize disruptions. Which
means I can plan very well when I do a major upgrade of my servers. Currently
I'm planning to retire to the old CentOS 4 boxes but I had more than two years
for the actual migration which really helps!
&lt;/p&gt;
&lt;p&gt;
&lt;strong&gt;Fedora as a Technology Preview&lt;/strong&gt;&lt;br&gt;
At the same time I can run Fedora on my desktop or on servers which really need
the "lastest and greatest" technology. As RHEL is based on (older) Fedora 
releases the administration is very much the same. I can create private RPM 
packages for Fedora and reuse them with minimal effort on RHEL/CentOS.
&lt;/p&gt;
&lt;p&gt;
&lt;strong&gt;Fedora EPEL provides all other packages&lt;/strong&gt;&lt;br&gt;
&lt;a class="ext-link" href="https://fedoraproject.org/wiki/EPEL"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;Fedora EPEL&lt;/a&gt; is a Fedora subproject which
provides more high-quality RPM packages for RHEL/CentOS in a transparent, 
reliable way. It is based on Fedora's standards, policies, and technologies so
there are a lot of good packages which are even updated constantly. Usually it's
quite easy to jump in and fix things and with co-maintainers it's not so much of
a problem if an individual packager has no time anymore to update packages.
&lt;/p&gt;
&lt;p&gt;
The high quality of these packages is also demonstrated by the fact that Red Hat
often blesses EPEL packages as 'official' and distributes them as part of RHEL.
&lt;/p&gt;
&lt;p&gt;
&lt;strong&gt;Red Hat/Fedora really take packaging seriously&lt;/strong&gt;&lt;br&gt;
It's a small point but comes in handy more often than most people think:
&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;Each RPM has a changelog section where each packaging change is listed.
&lt;/li&gt;&lt;li&gt;Usually packages have only the absolutely required dependencies with 
subpackages for specialized functionality which requires additional packages.
On the other hand I found Debian sometimes to be too granular which makes it
hard just installing some meaningful functionality.
&lt;/li&gt;&lt;li&gt;Package installation/updating can be done completely unattended. Fedora's 
packaging policy mandates that there is no user interaction. This might be 
bad for a "Desktop installer"-style installation with initial configuration 
but it ensures that you can set up systems completely unattended.
&lt;/li&gt;&lt;li&gt;Services are not restarted automatically after updates. I was burned once on
Suse Enterprise Linux 9 when I just updated some packages while waited for
all user to log of so I could start the actual maintenance. Unfortunately
several services were restarted automatically which caused TCP connections to
reset which in turn caused data loss for some users.
&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;
&lt;strong&gt;Red Hat and Fedora take Free Software and community seriously&lt;/strong&gt;&lt;br&gt;
There is very little "secret sauce", most of the stuff is done in the open. I 
don't remember Red Hat dictating big changes, new features is introduced through 
Fedora and people with enough merits can influence these decisions. Most (all?) 
software from Red Hat is released under a free software license.
&lt;/p&gt;
&lt;p&gt;
Also Red Hat provides source RPMs publicly and does not make any attempts to 
prevent free "clones" like CentOS and Scientific Linux: &lt;a class="ext-link" href="https://lwn.net/Articles/430098/"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;merging all kernel patches into one big patch&lt;/a&gt; 
is &lt;a class="ext-link" href="https://lwn.net/Articles/432012/"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;not a GPL violation&lt;/a&gt; and does not affect 
the community clones but rather Oracle as a commercial free-rider.
&lt;/p&gt;
&lt;p&gt;
So that's why &lt;a class="ext-link" href="http://investors.redhat.com/releasedetail.cfm?ReleaseID=586876"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;Red Hat earns more money every year&lt;/a&gt; even if they 'just' publish a Linux distribution.
&lt;/p&gt;</description><guid>https://www.schwarz.eu/posts/2011/why-you-should-consider-rhel-clones-for-your-linux-machines/</guid><pubDate>Wed, 20 Jul 2011 10:03:31 GMT</pubDate></item><item><title>MediaCore – an extensible video platform</title><link>https://www.schwarz.eu/posts/2011/mediacore-an-extensible-video-platform/</link><dc:creator>Felix Schwarz</dc:creator><description>&lt;p&gt;
In the last five years, web videos became more and more popular. Obviously YouTube has a big market share and provides a lot of functionality for the user. However many companies, associations and organizations want to host their videos on a dedicated platform. The reasons I hear most often are:
&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;Pages should blend in the main company site (styles, integration)
&lt;/li&gt;&lt;li&gt;Show custom ads to make money
&lt;/li&gt;&lt;li&gt;More flexibility, the ability to add custom features whenever you like
&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;
&lt;a style="padding:0; border:none" href="https://www.schwarz.eu/posts/2011/mediacore/france.png"&gt;&lt;img src="https://www.schwarz.eu/posts/2011/mediacore/france.png" alt="/posts/2011/mediacore/france.png" title="/posts/2011/mediacore/france.png"&gt;&lt;/a&gt; Merci à Quentin Theuret pour avoir &lt;a href="https://www.schwarz.eu/fr/posts/2011/mediacore-une-plateforme-video-extensible.html"&gt;traduit cet article en français&lt;/a&gt;.
&lt;/p&gt;
&lt;h2 id="AboutMediaCore"&gt;About MediaCore&lt;/h2&gt;
&lt;p&gt;
&lt;a class="ext-link" href="http://www.getmediacore.com"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;MediaCore&lt;/a&gt; is an open source video and podcast hosting software, first published in January 2010. Even the stock distribution package contains a lot of interesting functionality:
&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;web videos using Flash or "HTML5", including popular players like &lt;a class="ext-link" href="http://flowplayer.org/"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;Flowplayer&lt;/a&gt; and &lt;a class="ext-link" href="http://www.longtailvideo.com/players/"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;JW Player&lt;/a&gt;
&lt;/li&gt;&lt;li&gt;embed videos from YouTube, Vimeo or Google Video
&lt;/li&gt;&lt;li&gt;Support for podcasts
&lt;/li&gt;&lt;li&gt;sophisticated storage backends (e.g. store media in the Amazon S3 cloud)
&lt;/li&gt;&lt;li&gt;Social media integration (sharing on Facebook, twitter etc)
&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;
&lt;a style="padding:0; border:none" href="https://www.schwarz.eu/posts/2011/mediacore/MediaCore_Website.png"&gt;&lt;img src="https://www.schwarz.eu/posts/2011/mediacore/MediaCore_Website.png" alt="/posts/2011/mediacore/MediaCore_Website.png" title="/posts/2011/mediacore/MediaCore_Website.png"&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;
The main development work is done by &lt;a class="ext-link" href="http://simplestation.com/"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;Simple Station&lt;/a&gt;, a Canadian design studio. However development happens transparently with a &lt;a class="ext-link" href="https://github.com/simplestation/mediacore"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;github repository&lt;/a&gt;, a &lt;a class="ext-link" href="http://getmediacore.com/community/"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;user forum&lt;/a&gt; and a &lt;a class="ext-link" href="http://groups.google.com/group/mediacore-developers"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;developer mailing list&lt;/a&gt;. 
&lt;/p&gt;
&lt;p&gt;
One thing to keep in mind with MediaCore is that it is currently &lt;strong&gt;not a turn-key solution for everyone&lt;/strong&gt;. Some commonly requested features which are not implemented yet include:
&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;automated video encoding, e.g. encode uploaded videos to a format suitable for web viewing)
&lt;/li&gt;&lt;li&gt;user permission system, restrict uploading ability, show videos only to some (paying?) users
&lt;/li&gt;&lt;li&gt;In-video advertising support
&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;
Also installation on very cheap web space without shell access is quite hard, sometimes even impossible. Many of these problems will be solved by plugins after the release of MediaCore 0.9 (mid March 2011) or will be worked on in later releases. It's just not there yet.
&lt;/p&gt;
&lt;h2 id="Customization"&gt;Customization&lt;/h2&gt;
&lt;p&gt;
So far MediaCore customizations required always changes to the actual source code which makes updates much harder because you need to change the source code for every new release. With the upcoming 0.9 release MediaCore supports plugins so you can do a lot more customization without actually modifying the MediaCore source code. Examples for plugins:
&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;Change the overall style in arbitrary ways by overriding templates
&lt;/li&gt;&lt;li&gt;Add new players
&lt;/li&gt;&lt;li&gt;Trigger custom code after certain events (e.g. "new video uploaded")
&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;
With MediaCore 0.9 you can develop custom changes as plugins so it's way easier to update the stock MediaCore without having to change your code. See the next section for technical details.
&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="MediaCoreplugins"&gt;MediaCore plugins&lt;/h2&gt;
&lt;p&gt;
Currently (March 06 2011) there is no public documentation how to write MediaCore plugins so I decided to share my experience with you. However this won't/can't be a complete guide for newcomers. I assume that you're familiar with Python basics.
&lt;/p&gt;
&lt;p&gt;
Generally plugins are developed using Python (backend code), &lt;a class="ext-link" href="http://genshi.edgewall.org"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;Genshi&lt;/a&gt; (templating) and the usual web technologies (HTML, CSS, Javascript) for the frontend. For the Python side, you also might want to check the homepage of &lt;a class="ext-link" href="http://pylonshq.com/"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;Pylons&lt;/a&gt; which is the underlying framework used in MediaCore. There is also &lt;a class="ext-link" href="http://www.amazon.de/gp/product/1590599349"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;The Definitive Guide to Pylons&lt;/a&gt; (&lt;a class="ext-link" href="http://pylonsbook.com/"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;online version&lt;/a&gt;) by James Gardner.
&lt;/p&gt;
&lt;h3 id="Structure"&gt;Structure&lt;/h3&gt;
&lt;p&gt;
First of all, let's see the structure of a typical plugin (you can &lt;a href="https://www.schwarz.eu/posts/2011/mediacore/plugin_skeleton.tar.gz"&gt;download this skeleton&lt;/a&gt; as well):
&lt;/p&gt;
&lt;p&gt;
&lt;a style="padding:0; border:none" href="https://www.schwarz.eu/posts/2011/mediacore/mediacore_plugin_structure.png"&gt;&lt;img src="https://www.schwarz.eu/posts/2011/mediacore/mediacore_plugin_structure.png" alt="/posts/2011/mediacore/mediacore_plugin_structure.png" title="/posts/2011/mediacore/mediacore_plugin_structure.png"&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;em&gt;sample&lt;/em&gt; is the name of your Python module, so you can change that to any valid Python name. 
&lt;/li&gt;&lt;li&gt;&lt;em&gt;mediacore_plugin.py&lt;/em&gt; is an arbitrary name but I like to use that as a central entry point where I import all my code.
&lt;/li&gt;&lt;li&gt;If your plugin needs static resources like Javascript files, style sheets or additional images, you need a folder named &lt;em&gt;public&lt;/em&gt; below "sample" (or however you named your module). Obviously the static resources can have arbitrary names, I just added a CSS file named &lt;em&gt;sample-style.css&lt;/em&gt;.
&lt;/li&gt;&lt;li&gt;If you want to add new pages to your MediaCore, create a folder &lt;em&gt;templates&lt;/em&gt; below "sample" (or however you named your module). Inside you put your Genshi templates, again &lt;em&gt;my-page.html&lt;/em&gt; is just an example.
&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;
Every folder of your plugin should contain an (empty) file &lt;code&gt;__init__.py&lt;/code&gt; (this is because of how Python's setuptools/zipimport works). In the root folder, you need a &lt;em&gt;setup.py&lt;/em&gt; file.
&lt;/p&gt;
&lt;h3 id="Example:AddingaNewPage"&gt;Example: Adding a New Page&lt;/h3&gt;
&lt;p&gt;
To add a new page, you need a controller, a specific &lt;em&gt;exposed&lt;/em&gt; method in that controller and a template. Let's see an example
&lt;/p&gt;
&lt;div class="wiki-code"&gt;&lt;div class="code"&gt;&lt;pre&gt;&lt;span class="c1"&gt;# File: sample/mediacore_plugin.py&lt;/span&gt;

&lt;span class="c1"&gt;# ------------------------------------------------------------&lt;/span&gt;
&lt;span class="c1"&gt;# -*- coding: UTF-8 -*-&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;mediacore.lib.base&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; BaseController
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;mediacore.lib.decorators&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; expose

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyController&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;BaseController&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nd"&gt;@expose&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'sample/my_page.html'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;my_page&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; tag_name&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;kwargs&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# do your backend work here&lt;/span&gt;
        &lt;span class="c1"&gt;# …&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;'tag_name'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; tag_name&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;# ------------------------------------------------------------&lt;/span&gt;

__controller__ &lt;span class="o"&gt;=&lt;/span&gt; MyController
&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;
If you use this example code (and fill in the missing pieces) in your plugin, your new page would be available as &lt;code&gt;http://&amp;lt;mediacore&amp;gt;/sample/my_page?tag_name=Something&lt;/code&gt;. Please note that the &lt;em&gt;sample&lt;/em&gt; part of the URL is depend on your settings in setup.py (&lt;a href="https://www.schwarz.eu/wiki%3A#InstallingyourPlugin"&gt;see section about installation&lt;/a&gt; below), not the name of your Python module (however it makes sense to keep them the same).
&lt;/p&gt;
&lt;p&gt;
In your template you can reference your static resources using &lt;code&gt;${h.url_for('/sample/public/&amp;lt;filename&amp;gt;')&lt;/code&gt;}.
&lt;/p&gt;
&lt;p&gt;
If I remember correctly, every plugin can have only one controller. However a controller can have multiple exposed functions of course.
&lt;/p&gt;
&lt;h3 id="Example:AddingaNewPlayer"&gt;Example: Adding a New Player&lt;/h3&gt;
&lt;p&gt;
MediaCore supports different types of players, especially HTML5 players (using the &amp;lt;video&amp;gt; tag and Javascript) and Flash players. The two important classes to check are &lt;code&gt;mediacore.lib.players.AbstractHTML5Player&lt;/code&gt; and &lt;code&gt;mediacore.lib.players.AbstractFlashPlayer&lt;/code&gt;.
&lt;/p&gt;
&lt;p&gt;
I'll show you an example how to build a custom player based on the FlowPlayer:
&lt;/p&gt;
&lt;div class="wiki-code"&gt;&lt;div class="code"&gt;&lt;pre&gt;&lt;span class="c1"&gt;# File: sample/player.py&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;mediacore.lib.players&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; FlowPlayer

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyPlayer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;FlowPlayer&lt;span class="p"&gt;):&lt;/span&gt;
    name &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;u&lt;/span&gt;&lt;span class="s1"&gt;'my_player'&lt;/span&gt;
    display_name &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;u&lt;/span&gt;&lt;span class="s1"&gt;'Custom Player based on Flowplayer'&lt;/span&gt;

    &lt;span class="c1"&gt;# you could override flashvars() for example to add player some options&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;
Afterwards you need to register your class, so add this to your &lt;code&gt;mediacore_plugin.py&lt;/code&gt;:
&lt;/p&gt;
&lt;div class="wiki-code"&gt;&lt;div class="code"&gt;&lt;pre&gt;
&lt;span class="c1"&gt;# …&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;mediacore.lib.players&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; AbstractFlashPlayer
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;sample.player&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; MyPlayer
AbstractFlashPlayer&lt;span class="o"&gt;.&lt;/span&gt;register&lt;span class="p"&gt;(&lt;/span&gt;MyPlayer&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# …&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;
If you &lt;a href="https://www.schwarz.eu/wiki%3A#InstallingyourPlugin"&gt;installed&lt;/a&gt; your plugin &lt;em&gt;after&lt;/em&gt; you initialized the main MediaCore database (&lt;code&gt;paster development.ini setup-app&lt;/code&gt;, you need to register your player via SQL:
&lt;/p&gt;
&lt;div class="wiki-code"&gt;&lt;div class="code"&gt;&lt;pre&gt;&lt;span class="k"&gt;insert&lt;/span&gt; &lt;span class="k"&gt;into&lt;/span&gt; players &lt;span class="p"&gt;(&lt;/span&gt;name&lt;span class="p"&gt;,&lt;/span&gt; enabled&lt;span class="p"&gt;,&lt;/span&gt; priority&lt;span class="p"&gt;,&lt;/span&gt; created_on&lt;span class="p"&gt;,&lt;/span&gt; modified_on&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'my_player'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;priority&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; now&lt;span class="p"&gt;(),&lt;/span&gt; now&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="s1"&gt;'{}'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;
Now your player should be visible in the admin settings ("Players" section) and you just need to activate it. All videos will use that player (if possible).
&lt;/p&gt;
&lt;h3 id="InstallingyourPlugin"&gt;Installing your Plugin&lt;/h3&gt;
&lt;p&gt;
MediaCore uses setuptools to find plugins. All found plugins are enabled automatically, there is no way to disable a plugin (currently). 
&lt;/p&gt;
&lt;p&gt;
So that's why your plugin needs a setup.py file which looks like this (roughly):
&lt;/p&gt;
&lt;div class="wiki-code"&gt;&lt;div class="code"&gt;&lt;pre&gt;&lt;span class="ch"&gt;#!/usr/bin/env python&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;setuptools&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; setup&lt;span class="p"&gt;,&lt;/span&gt; find_packages

setup&lt;span class="p"&gt;(&lt;/span&gt;
      name&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'SamplePlugin'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      version&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'1.0'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

      author&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'Felix Schwarz'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      author_email&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'info@schwarz.eu'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      license&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'GPL v3 or later'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        
      packages&lt;span class="o"&gt;=&lt;/span&gt;find_packages&lt;span class="p"&gt;(),&lt;/span&gt;
      entry_points &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="s1"&gt;'mediacore.plugin'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
              &lt;span class="s1"&gt;'sample = sample.mediacore_plugin'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;
Besides some meta information, The interesting point is the entry_point 'mediacore.plugin':
&lt;/p&gt;
&lt;ol&gt;&lt;li&gt;The first part ('sample = …') defines the URL prefix where your new pages/resources are found (&lt;code&gt;http://&amp;lt;mediacore&amp;gt;/sample/…&lt;/code&gt;.
&lt;/li&gt;&lt;li&gt;Besides that the line tells MediaCore where to look for the plugin (&lt;code&gt;sample/mediacore_plugin.py&lt;/code&gt;). This file is executed when MediaCore loads its plugin, and you may use the &lt;code&gt;__controller__&lt;/code&gt; attribute here to add new pages. 
&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;
Before you can use your new plugin, you need to install it (don't forget to activate your virtualenv before!):
&lt;/p&gt;
&lt;pre class="wiki"&gt;python setup.py install
&lt;/pre&gt;&lt;p&gt;
For development you'll likely want to use the 'develop' mode instead of 'install' so that you don't have to install your plugin every time you made a code change.
&lt;/p&gt;
&lt;p&gt;
That's my very rough primer on MediaCore plugins. I just left out one topic: Events. That's because I think the API design makes them basically pointless. 
&lt;/p&gt;
&lt;p&gt;
I hope you liked this paragraph anyway. In case of errors, please send an email. Also remember, you can &lt;a href="https://www.schwarz.eu/posts/2011/mediacore/plugin_skeleton.tar.gz"&gt;download the basic skeleton&lt;/a&gt;.
&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="MyContributionsandAdaptations"&gt;My Contributions and Adaptations&lt;/h2&gt;
&lt;p&gt;
I contributed several patches to MediaCore, for example:
&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;Basic internationalization (i18n) infrastructure and large parts of the German translation
&lt;/li&gt;&lt;li&gt;Support for Python 2.6
&lt;/li&gt;&lt;li&gt;Better diagnostics for plugin problems
&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;
These patches were created while I modified MediaCore according to the needs of my customers.
&lt;/p&gt;
&lt;h2 id="CommercialCustomizationExamples"&gt;Commercial Customization Examples&lt;/h2&gt;
&lt;p&gt;
While I can't share all the commercial customizations I did for customers, I like to give two examples for modifications to give you an idea what can be done:
&lt;/p&gt;
&lt;p&gt;
&lt;a style="padding:0; border:none" href="https://www.schwarz.eu/posts/2011/mediacore/One_Goal.png"&gt;&lt;img src="https://www.schwarz.eu/posts/2011/mediacore/One_Goal.png" alt="/posts/2011/mediacore/One_Goal.png" class="seu-blog-image-text-left" title="/posts/2011/mediacore/One_Goal.png"&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;
A &lt;strong&gt;German NGO&lt;/strong&gt; launched a campaign during the Soccer World Cup 2010 in South Africa. The wanted a web platform where users (mostly schools) can &lt;strong&gt;upload videos and images&lt;/strong&gt;. I needed a German site and the ability to manage images in a MediaCore instance.
&lt;/p&gt;
&lt;br clear="all"&gt;
&lt;div style="margin-top: 2em"&gt;&lt;/div&gt;
&lt;p&gt;
&lt;a style="padding:0; border:none" href="https://www.schwarz.eu/posts/2011/mediacore/carousel_with_ads.png"&gt;&lt;img src="https://www.schwarz.eu/posts/2011/mediacore/carousel_with_ads.png" alt="/posts/2011/mediacore/carousel_with_ads.png" class="seu-blog-image-text-right" title="/posts/2011/mediacore/carousel_with_ads.png"&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;
A &lt;strong&gt;commercial portal&lt;/strong&gt; hosts a lot of videos with MediaCore but lacked the ability to &lt;strong&gt;display in-video advertisements using OpenX&lt;/strong&gt;. I developed a custom MediaCore player which shows a pre-roll advertisement. Also I developed a video carousel which can be embedded into other sites (also showing the ads). All of this functionality is contained in plugins so MediaCore can be updated easily. 
&lt;/p&gt;
&lt;br clear="all"&gt;
&lt;hr&gt;
&lt;h2 id="FurtherQuestions"&gt;Further Questions?&lt;/h2&gt;
&lt;p&gt;
As always, if you have more questions about MediaCore, check out the &lt;a class="ext-link" href="http://getmediacore.com/community/"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;user forum&lt;/a&gt;. If you want to ask about commercial support, remote installation and modifications, just send an email to &lt;a class="mail-link" href="mailto:info@schwarz.eu"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;info@schwarz.eu&lt;/a&gt;. 
&lt;/p&gt;</description><guid>https://www.schwarz.eu/posts/2011/mediacore-an-extensible-video-platform/</guid><pubDate>Sun, 06 Mar 2011 19:44:00 GMT</pubDate></item><item><title>Free Software is no silver bullet for niche markets</title><link>https://www.schwarz.eu/posts/2008/free-software-is-no-silver-bullet-for-niche-markets/</link><dc:creator>Felix Schwarz</dc:creator><description>&lt;p&gt;
Many people think of solid application which causes not too much trouble when 
they talk about &lt;a class="ext-link" href="http://www.fsf.org/licensing/essays/free-sw.html"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;Free Software&lt;/a&gt;.
Most Free Software is given away for free, everyone can extend it as (s)he likes
(presuming the necessary knowlegde) and there is no marketing department which
urges for premature 'final' releases. Administrators often like that Free 
Software does not require any license management (if you just use it) and
bug reports are openly shared because of the abundance of company politics which
may try to maintain a zero-defect illusion even if the product is buggy like hell.
&lt;/p&gt;
&lt;p&gt;
But the article's head line is 'Free Software is no silver bullet for niche markets' 
so you probably already expect that I will give you some counter-examples to the
cliches above.
&lt;/p&gt;
&lt;h3 id="Pipedreamsmeetreallive"&gt;Pipe dreams meet real live&lt;/h3&gt;
&lt;p&gt;
So today's example is &lt;strong&gt;&lt;a class="ext-link" href="http://www.lx-office.org"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;Lx-Office&lt;/a&gt;&lt;/strong&gt; 
(&lt;a class="ext-link" href="http://de.wikipedia.org/wiki/Lx-Office"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;Wikipedia&lt;/a&gt;, sorry no English version
available). Lx-Office is a web-based 
&lt;a class="ext-link" href="http://en.wikipedia.org/wiki/Enterprise_resource_planning"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;ERP&lt;/a&gt; and 
&lt;a class="ext-link" href="http://en.wikipedia.org/wiki/Customer_relationship_management"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;CRM&lt;/a&gt; system
written in Perl and PHP and distributed under the GNU General Public License.
It includes a module for financial accounting in companies.
 
The ERP/accounting module of Lx-Office originated in 
&lt;a class="ext-link" href="http://www.sql-ledger.org/"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;SQL-Ledger&lt;/a&gt; (&lt;a class="ext-link" href="http://en.wikipedia.org/wiki/SQL-Ledger"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;Wikipedia&lt;/a&gt;).
After the fork from SQL-Ledger in 2003, Lx-Office was modified to meet German
regulatory requirements.
&lt;/p&gt;
&lt;p&gt;
In Germany, the majority of all tax advisors 
(&lt;a class="ext-link" href="http://www.ftd.de/karriere_management/408313.html"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;approximately 80%&lt;/a&gt; according
to Financial Times Germany) uses software produced by a German cooperative named 
&lt;a class="ext-link" href="http://www.datev.com/"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;DATEV&lt;/a&gt; (&lt;a class="ext-link" href="http://de.wikipedia.org/wiki/DATEV"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;Wikipedia&lt;/a&gt;).
This software is really the heart of a tax advisor's services, different 
versions support almost everything from financial accounting and tax calculation
to payroll accounting.
&lt;/p&gt;
&lt;p&gt;
So you can imagine that for a serious accounting software, data exchange with 
tax advisors is really a must-have. Though DATEV does only offer properietary 
software (which only runs on MS Windows), they sell a 
&lt;a class="ext-link" href="http://en.wikipedia.org/wiki/Software_development_kit"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;SDK&lt;/a&gt; which contains the
complete specification of the KNE format ("Postversandformat"). With that format
you can share accounting information with your tax advisor (e.g. the routine 
accounting is done in-house but the tax advisor should assemble the balance 
sheet at the end of the year).
&lt;/p&gt;
&lt;p&gt;
The KNE format is an 'old-style' binary file format with a long track record 
(if you include the predecessor, the OBE format which was introduced in the late
'80s). When I wrote &lt;strong&gt;libkne&lt;/strong&gt;, a pure Python library to create and parse 
these binary KNE files, I tested my implementation against several real-world 
implementations. However, when I used the DATEV export using Lx-Office's demo
server I was very surprised to see that the exported files where not valid 
according to the specification!
&lt;/p&gt;
&lt;h3 id="Exportbugknownforseveralyears"&gt;Export bug known for several years&lt;/h3&gt;
&lt;p&gt;
It turned out that the problem is caused by different date string formatting.
The export module &lt;a class="ext-link" href="https://lx-office.linet-services.de/svn/trunk/unstable/SL/DATEV.pm"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;DATEV.pm&lt;/a&gt;
transforms a string which is presumed to contain a date ('25.12.2008', which is 
2008-12-25 in ISO notation) into the KNE binary date format ('251208' - yes, the
KNE format is not y2k-safe). As soon you use a different date format (like 
'12/25/2008'), the export just puts in the date string '12/25/2008', ignoring 
the size restrictions (6 bytes) and the format specification ('DDMMYY') 
completely.
&lt;/p&gt;
&lt;p&gt;
What really puzzled me was that this &lt;strong&gt;bug is known for 2 1/2 years&lt;/strong&gt; and 
nothing happened so far. In June 2006 there was a 
&lt;a class="ext-link" href="http://forum.lx-office.org/forum_entry.php?id=2646"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;post in the user forum&lt;/a&gt; which
explained the problem and even spotted the bug in the source code. Only thirty
minutes later, another forum reader filed a 
&lt;a class="ext-link" href="https://lx-office.linet-services.de/bugzilla/show_bug.cgi?id=367"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;bug report&lt;/a&gt; with
a reference to the post. Six months later, a developer requested more 
information (which looks stupid to me, all necessary information was given) from 
the submitter but no further action was taken until now (December 2008). That 
means that the DATEV export of Lx-Office is non-functional for 2 1/2 years if 
you don't use one specific date format!
&lt;/p&gt;
&lt;h3 id="Lookingatthesource"&gt;Looking at the source&lt;/h3&gt;
&lt;p&gt;
As always, we can have a look at the source to reveal the issue:
&lt;/p&gt;
&lt;div class="wiki-code"&gt;&lt;div class="code"&gt;&lt;pre&gt;&lt;span class="c1"&gt;# Code taken from SL/DATEV.pm (r3482)&lt;/span&gt;
&lt;span class="c1"&gt;# https://lx-office.linet-services.de/svn/trunk/unstable/SL/DATEV.pm&lt;/span&gt;
&lt;span class="c1"&gt;# Licensed under the GPL v2+&lt;/span&gt;

&lt;span class="k"&gt;sub&lt;/span&gt; &lt;span class="nf"&gt;datetofour&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nv"&gt;$&lt;/span&gt;&lt;span class="nn"&gt;main::&lt;/span&gt;&lt;span class="nv"&gt;lxdebug&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;enter_sub&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$six&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;@_&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$day&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$month&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$year&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/\./&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$date&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$day&lt;/span&gt; &lt;span class="o"&gt;=~&lt;/span&gt;&lt;span class="sr"&gt; /^0/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$day&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;substr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$day&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;length&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$month&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$month&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0"&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$month&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;length&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$year&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$year&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;substr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$year&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$six&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$day&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$month&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$year&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$day&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$month&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nv"&gt;$&lt;/span&gt;&lt;span class="nn"&gt;main::&lt;/span&gt;&lt;span class="nv"&gt;lxdebug&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;leave_sub&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$date&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;
So the &lt;em&gt;datetofour&lt;/em&gt; function gets a date string $date and a boolean flag $six
and returns the KNE date string. The problem is that the function assumes a 
certain date format in line 10 in the snippet (line 481 in original file as 
of r3482): 'split(/\./, $date)' But this date formatting can be configured by the user so the 
function should query the configuration for the format to use.
&lt;/p&gt;
&lt;h3 id="Conclusions"&gt;Conclusions&lt;/h3&gt;
&lt;p&gt;
I presented you one example of a Free Software application which has a serious
known bug that was not fixed for more that 2 1/2 years (though it is probably 
only a matter of one or two lines). The project itself is 
&lt;a class="ext-link" href="http://www.lx-office.org/index.php?id=about"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;backed by two small companies&lt;/a&gt; and 
the software is used by quite a lot of people (Sourceforge counted 
4468 downloads for Lx-Office ERP beta1 between 2008-08-12 and 2008-12-25). The
project follows a traditional 'open source' development approach (public bug
tracker and source control repository, community support through web forums) and
uses a well-known license (GPL 2+). So this is clearly an example of a 'real'
Free Software project.
&lt;/p&gt;
&lt;p&gt;
Nevertheless the project failed to fix a simple bug so that users can not use 
functionality which is quite important in many enterprise use cases if you 
consider the extensive German regulatory policies. Obviously this functionality
is not important enough for most users of Lx-Office. Probably (speculation!) 
many of them are in the 'low budget' segment which does not regularly exchange
financial data with their tax advisors.
&lt;/p&gt;
&lt;p&gt;
Admittedly financial accounting is really a niche use case (highly dependend on
the country you're operating in, laws and policies change on a regular basis)
so the user and developer number is quite small (compared to other software 
like Linux, Mozilla Firefox or OpenOffice). But it still gives evidence to my 
initial thesis that &lt;strong&gt;Free Software is not necessarily a solution for you&lt;/strong&gt;. 
While Free Software in very common use cases can excel their proprietary 
competitors (e.g. Apache HTTP), you may find that you should evaluate Free 
Software in niche markets very carefully.
&lt;/p&gt;
&lt;h3 id="Briefremark:Batteriesincludedistherightapproachforclasslibraries"&gt;Brief remark: 'Batteries included' is the right approach for class libraries&lt;/h3&gt;
&lt;p&gt;
While I described what code caused the bug on a technical level, the real 
(technical) problem for me is found below: In Perl 5 there is no built-in data
type to represent dates. So the Lx-Office developers had to fall back on using
strings to pass dates around. If Perl shipped some included module for date
representation and manipulation, probably the bug would have never occured 
because things like date formatting would only influence the visual presentation.
&lt;/p&gt;
&lt;p&gt;
So I think that on a class library level Python's approach of 
&lt;a class="ext-link" href="http://www.python.org/about/"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;batteries included&lt;/a&gt; is the right one. A 
decent(tm) programming language needs to ship modules for date manipulation 
(even a less-than-optimal API like in Python is better than a completely 
missing one). Just having 
&lt;a class="ext-link" href="http://search.cpan.org/dist/Date-Simple/lib/Date/Simple.pm"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;add-on modules&lt;/a&gt; for 
that is not sufficient because not all developers use them and it's hard to use 
multiple independently developed third-party modules with each other because 
they probably will use incompatible date representations...
&lt;/p&gt;</description><guid>https://www.schwarz.eu/posts/2008/free-software-is-no-silver-bullet-for-niche-markets/</guid><pubDate>Thu, 25 Dec 2008 20:05:20 GMT</pubDate></item><item><title>How to make input validation really complicated</title><link>https://www.schwarz.eu/posts/2008/how-to-make-input-validation-really-complicated/</link><dc:creator>Felix Schwarz</dc:creator><description>&lt;p&gt;
Thanks to Vera Djuraskovic there is a &lt;a class="ext-link" href="http://science.webhostinggeeks.com/kako-napraviti-validaciju"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;Serbo-Croatian translation&lt;/a&gt; of this article available.
&lt;/p&gt;
&lt;p&gt;
In every web application you need to validate your input data &lt;em&gt;very carefully&lt;/em&gt;. Data validation is a very common task and so it's surprising that there are several validation libraries in Python (like &lt;a class="ext-link" href="http://www.formencode.org"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;formencode&lt;/a&gt;) which include validators for common tasks. However, &lt;a class="ext-link" href="http://trac.edgewall.org"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;Trac&lt;/a&gt; does not integrate any of these libraries so every plugin developer has to write their own validation code.
&lt;/p&gt;
&lt;p&gt;
Now look how complicated you can check if a string does only contain the characters a-z, hyphens ('-'), underscore ('_'):
&lt;/p&gt;
&lt;div class="wiki-code"&gt;&lt;div class="code"&gt;&lt;pre&gt;&lt;span class="c1"&gt;# Only alphanumeric characters (and [-_]) allowed for custom fieldname&lt;/span&gt;
&lt;span class="c1"&gt;# Note: This is not pretty, but it works... Anyone have an easier way of checking ???&lt;/span&gt;
f_name&lt;span class="p"&gt;,&lt;/span&gt; f_type &lt;span class="o"&gt;=&lt;/span&gt; customfield&lt;span class="p"&gt;[&lt;/span&gt;Key&lt;span class="o"&gt;.&lt;/span&gt;NAME&lt;span class="p"&gt;],&lt;/span&gt; customfield&lt;span class="p"&gt;[&lt;/span&gt;Key&lt;span class="o"&gt;.&lt;/span&gt;TYPE&lt;span class="p"&gt;]&lt;/span&gt;
match &lt;span class="o"&gt;=&lt;/span&gt; re&lt;span class="o"&gt;.&lt;/span&gt;search&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"[a-z0-9-_]+"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; f_name&lt;span class="p"&gt;)&lt;/span&gt;
length_matched_name &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; match &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    match_span &lt;span class="o"&gt;=&lt;/span&gt; match&lt;span class="o"&gt;.&lt;/span&gt;span&lt;span class="p"&gt;()&lt;/span&gt;
    length_matched_name &lt;span class="o"&gt;=&lt;/span&gt; match_span&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; match_span&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
namelen &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;f_name&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;length_matched_name &lt;span class="o"&gt;!=&lt;/span&gt; namelen&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; TracError&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Only alphanumeric characters allowed for custom field name (a-z or 0-9 or -_)."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;
Please note how deep the author digged into &lt;a class="ext-link" href="http://www.python.org/doc/2.5.2/lib/module-re.html"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;Python's re library&lt;/a&gt; to find the &lt;em&gt;span()&lt;/em&gt; method. So he first looks for an acceptable substring, computes the position of this substring, derives the length of the substring from that and checks if the length of the substring equals the length of the whole string. 
&lt;/p&gt;
&lt;p&gt;
At least the author had some doubts if his solution is the most elegant one (see the second line of the snippet above). So a simpler method of checking could be:
&lt;/p&gt;
&lt;div class="wiki-code"&gt;&lt;div class="code"&gt;&lt;pre&gt;&lt;span class="c1"&gt;# Only alphanumeric characters (and [-_]) allowed for custom fieldname&lt;/span&gt;
f_name&lt;span class="p"&gt;,&lt;/span&gt; f_type &lt;span class="o"&gt;=&lt;/span&gt; customfield&lt;span class="p"&gt;[&lt;/span&gt;Key&lt;span class="o"&gt;.&lt;/span&gt;NAME&lt;span class="p"&gt;],&lt;/span&gt; customfield&lt;span class="p"&gt;[&lt;/span&gt;Key&lt;span class="o"&gt;.&lt;/span&gt;TYPE&lt;span class="p"&gt;]&lt;/span&gt;
match &lt;span class="o"&gt;=&lt;/span&gt; re&lt;span class="o"&gt;.&lt;/span&gt;search&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"([a-z0-9-_]+)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; f_name&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;match &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;match&lt;span class="o"&gt;.&lt;/span&gt;group&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; f_name&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; TracError&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Only alphanumeric characters allowed for custom field name (a-z or 0-9 or -_)."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;
So now we got rid of all the index stuff. But still, you can do much easier than that by properly using regular expressions:
&lt;/p&gt;
&lt;div class="wiki-code"&gt;&lt;div class="code"&gt;&lt;pre&gt;&lt;span class="c1"&gt;# Only alphanumeric characters (and [-_]) allowed for custom fieldname&lt;/span&gt;
f_name&lt;span class="p"&gt;,&lt;/span&gt; f_type &lt;span class="o"&gt;=&lt;/span&gt; customfield&lt;span class="p"&gt;[&lt;/span&gt;Key&lt;span class="o"&gt;.&lt;/span&gt;NAME&lt;span class="p"&gt;],&lt;/span&gt; customfield&lt;span class="p"&gt;[&lt;/span&gt;Key&lt;span class="o"&gt;.&lt;/span&gt;TYPE&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; re&lt;span class="o"&gt;.&lt;/span&gt;search&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'^[a-z0-9-_]+$'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; f_name&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; TracError&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Only alphanumeric characters allowed for custom field name (a-z or 0-9 or -_)."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;
Of course there is re.match but I try to avoid it due to personal issues with the method - I produced some bugs when using re.match previously.
&lt;/p&gt;
&lt;p&gt;
You wonder which software does ship this mess? &lt;a class="ext-link" href="http://trac-hacks.org/browser/customfieldadminplugin/0.11/customfieldadmin/api.py?rev=2388#L55"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;This code&lt;/a&gt; is part of a plugin which helps you to manage custom fields for your Trac tickets (&lt;a class="ext-link" href="http://trac-hacks.org/wiki/CustomFieldAdminPlugin"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;CustomFieldAdminPlugin&lt;/a&gt;). Also the overall code quality of the module is quite poor so if you like to spend some time to learn refactoring, this is a good place to start.
&lt;/p&gt;</description><guid>https://www.schwarz.eu/posts/2008/how-to-make-input-validation-really-complicated/</guid><pubDate>Sat, 29 Nov 2008 12:18:13 GMT</pubDate></item><item><title>TurboMail 3.0 Alpha1 released</title><link>https://www.schwarz.eu/posts/2008/turbomail-3.0-alpha1-released/</link><dc:creator>Felix Schwarz</dc:creator><description>&lt;p&gt;
For quite a while no new versions of TurboMail were released and indeed TurboMail 2 just worked well for many users. But now there is an alpha release of the upcoming TurboMail 3.0 which is nearly a complete rewrite of TurboMail 2. With the new code structure we were able to add some nice features:
&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;No strong coupling to &lt;a class="ext-link" href="http://www.turbogears.org"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;TurboGears&lt;/a&gt; anymore (use TurboMail in command line scripts if you like!)
&lt;/li&gt;&lt;li&gt;Different delivery methods like SMTP, local mailbox etc.
&lt;/li&gt;&lt;li&gt;Different delivery strategies (queuing and immediate delivery)
&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;
To get more people involved, we decided to release an &lt;strong&gt;alpha version&lt;/strong&gt;. That means the most important functionality is already in, but some things are missing right now. Furthermore there are some small known bugs (sending pre-formatted messages won't work) but we can work on that later. Besides that, I'm using a this alpha (with some custom patches) on production servers since several months and it works really great so far.
&lt;/p&gt;
&lt;h4 id="Download"&gt;Download&lt;/h4&gt;
&lt;ul&gt;&lt;li&gt;&lt;a class="ext-link" href="http://www.schwarz.eu/opensource/misc/2008/turbomail/3.0-alpha1/TurboMail-3.0.dev-r117.tar.gz"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;Source&lt;/a&gt;
&lt;/li&gt;&lt;li&gt;&lt;a class="ext-link" href="http://www.schwarz.eu/opensource/misc/2008/turbomail/3.0-alpha1/TurboMail-3.0.dev_r117-py2.3.egg"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;Python 2.3&lt;/a&gt; (not tested yet!)
&lt;/li&gt;&lt;li&gt;&lt;a class="ext-link" href="http://www.schwarz.eu/opensource/misc/2008/turbomail/3.0-alpha1/TurboMail-3.0.dev_r117-py2.4.egg"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;Python 2.4&lt;/a&gt;
&lt;/li&gt;&lt;li&gt;&lt;a class="ext-link" href="http://www.schwarz.eu/opensource/misc/2008/turbomail/3.0-alpha1/TurboMail-3.0.dev_r117-py2.5.egg"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;Python 2.5&lt;/a&gt;
&lt;/li&gt;&lt;li&gt;Python 2.6: sorry, no such version available here.
&lt;/li&gt;&lt;/ul&gt;&lt;h4 id="Documentation"&gt;Documentation&lt;/h4&gt;
&lt;p&gt;
I tried to built comprehensive documentation for every feature. This is still work in progress, but at least some docs are there:
&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;a class="ext-link" href="http://www.schwarz.eu/opensource/misc/2008/turbomail/html/"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;HTML&lt;/a&gt;
&lt;/li&gt;&lt;li&gt;&lt;a class="ext-link" href="http://www.schwarz.eu/opensource/misc/2008/turbomail/TurboMail.pdf"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;PDF&lt;/a&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;
If you have questions, patches or just want to rant about the alpha, please use the &lt;a class="ext-link" href="http://groups.google.com/group/turbomail-devel"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;mailing list&lt;/a&gt;. The &lt;a class="ext-link" href="http://www.python-turbomail.org/roadmap"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;roadmap page&lt;/a&gt; shows the missing tasks. I'm currently writing a Python MTA to get real SMTP tests in again.
&lt;/p&gt;</description><guid>https://www.schwarz.eu/posts/2008/turbomail-3.0-alpha1-released/</guid><pubDate>Sun, 16 Nov 2008 14:46:18 GMT</pubDate></item><item><title>Migrating data from MS Access to MySQL easily</title><link>https://www.schwarz.eu/posts/2008/migrating-data-from-ms-access-to-mysql-easily/</link><dc:creator>Felix Schwarz</dc:creator><description>&lt;p&gt;
&lt;em&gt;MySQL AB: If we are too slow, your bug must be invalid&lt;/em&gt;
Most people will know the &lt;a class="ext-link" href="http://www.mysql.com"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;MySQL&lt;/a&gt; database. MySQL AB
(&lt;a class="ext-link" href="http://www.mysql.com/news-and-events/sun-to-acquire-mysql.html"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;recently bought by Sun&lt;/a&gt;)
is the company behind that open source database and pays most developers
working on the code.
&lt;/p&gt;
&lt;p&gt;
Some years ago MySQL AB released a really nice tool which they named 
&lt;a class="ext-link" href="http://www.mysql.com/products/tools/migration-toolkit/"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;MySQL Migration Toolkit&lt;/a&gt;.
Its a Java GUI application which can be used to copy data from other 
databases (like Microsoft Access) to a MySQL database. The tool even generates
a &lt;a class="ext-link" href="http://www.lua.org/"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;lua&lt;/a&gt; script file so that you can automate the migration
process afterwards.
&lt;/p&gt;
&lt;p&gt;
In a recent project I was hired to build a replacement for a custom Access
application. Part of the work was to create a new database schema. Of course the
old data must be migrated so that it isn't lost. The chosen production database
was &lt;a class="ext-link" href="http://www.postgresql.org/"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;PostgreSQL&lt;/a&gt; due to its solid track record
when it comes to SQL features like transactions, referential integrity and
all this boring stuff.
&lt;/p&gt;
&lt;p&gt;
I figured out that it would be much easier for me converting data in the old 
database to an open source database and do the structure conversion in a second
step with custom tools.
&lt;/p&gt;
&lt;p&gt;
This was when the Migration Toolkit came into play: It copied the data from
Access to MySQL and I wrote some scripts to get that data into Postgres. It 
worked really great after a very short time and everyone was happy. Sure there
were some initial problems (like Migration Toolkit won't work with Java 1.6 but
only 1.5) but these were solved quickly.
&lt;/p&gt;
&lt;p&gt;
Some weeks later I discovered that some data in my new database was different
from the Access data: In Access the value was 0.075, MySQL stored 0.08.
This happened for all columns which were specified as "Single" with automatic
precision in MS Access. The MySQL bugtracker revealed that others had the same
problem: &lt;a class="ext-link" href="http://bugs.mysql.com/bug.php?id=17880"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;Access singles are converted to MySQL 7,2 double&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;
Unfortunately this bug was marked as 'invalid' when I found it two years after
its creation (because the MySQL developer investigating it did not read the
bug description correctly so he was not able to reproduce it). Bugs are far
from unusual and so I was quite confident that I could jump in and help fixing
it. Unfortunately, the bug was closed and therefore I was sure that no MySQL
developer would ever look at it again. But their bug tracker won't let me
open the bug as a normal user.
&lt;/p&gt;
&lt;p&gt;
I pinged another MySQL dev directly (Susanne Ebrecht) and she reopened the bug :-)
I followed her advise and tried to reproduce the problem which newer versions
of MS Access. Clearly, the bug was still there...
&lt;/p&gt;
&lt;p&gt;
Unfortunately, no MySQL developer had enough time to look at the report again
and so an automatic process closed the bug again:
&lt;/p&gt;
&lt;pre class="wiki"&gt;No feedback was provided for this bug for over a month, so it is
being suspended automatically. If you are able to provide the
information that was originally requested, please do so and change
the status of the bug back to "Open".
&lt;/pre&gt;&lt;p&gt;
Maybe I'm just too stupid but I was not able to re-open it. Maybe only the 
original reporter can. But the main problem is the crappy configuration of the
MySQL bug tracker which closes bugs which are marked as 'need more info' after
30 days which is quite short. Of course you need some sort of 'timeout' in a
bug tracker because else you get swamped with useless bug reports no one is able
to reproduce so they stay open forever. 
&lt;/p&gt;
&lt;p&gt;
Other bug trackers implement that better: If a bug has the NEEDINFO status in 
&lt;a class="ext-link" href="http://www.bugzilla.org/"&gt;&lt;span class="icon"&gt;​&lt;/span&gt;Bugzilla&lt;/a&gt; there is a checkbox "I am providing the 
requested information for this bug." and afterwards the status is being set back
to normal.
&lt;/p&gt;
&lt;p&gt;
The problem with MySQL was that I provided all necessary information and &lt;strong&gt;the
bug was closed only because MySQL AB worked too slowly&lt;/strong&gt;!
&lt;/p&gt;
&lt;p&gt;
This is an example how to not handle bug reports! If you don't have enough
time to triage all bugs in a short period of time, then don't set the timeout
to 30 days!
&lt;/p&gt;
&lt;p&gt;
Which left me alone with the rounding problem... Luckily I was a able to use some
kind of "dirty hack" to work around the whole issue: The problem is that the 
created MySQL database schemes are too imprecise. The rounding problem occurs
not until the real data is copied which happens at a later stage.
&lt;/p&gt;
&lt;p&gt;
So I extended the lua migration script to call a Python script of mine. This 
script just sets all columns with the type 'double(7,2) to 'DECIMAL(7,4)'. And
this fixed the rounding errors for me. Of course it would be nicer if a proper
fix was included in the Migration Toolkit itself but this seems to be out of 
reach...
&lt;/p&gt;
&lt;p&gt;
Maybe my workaround can save you some trouble or time so I put my dirty 
hack online. Help yourself!
&lt;/p&gt;
&lt;p&gt;
My lua adaptation:
&lt;/p&gt;
&lt;div class="wiki-code"&gt;&lt;div class="code"&gt;&lt;pre&gt;&lt;span class="p"&gt;(...)&lt;/span&gt;
  &lt;span class="c1"&gt;-- create database objects online&lt;/span&gt;
  &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Create database objects."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  
  grtM&lt;span class="p"&gt;.&lt;/span&gt;callFunction&lt;span class="p"&gt;(&lt;/span&gt;
    grtV&lt;span class="p"&gt;.&lt;/span&gt;toLua&lt;span class="p"&gt;(&lt;/span&gt;targetConn&lt;span class="p"&gt;.&lt;/span&gt;modules&lt;span class="p"&gt;.&lt;/span&gt;TransformationModule&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s2"&gt;"executeSqlStatements"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="p"&gt;{&lt;/span&gt;targetConn&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"global::/migration/targetCatalog"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"global::/migration/creationLog"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
  grt&lt;span class="p"&gt;.&lt;/span&gt;exitOnError&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"The SQL create script cannot be executed."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kr"&gt;end&lt;/span&gt;
  
&lt;span class="c1"&gt;-- added this line to execute my Python script&lt;/span&gt;
&lt;span class="nb"&gt;os.execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"C:&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;Python24&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;python.exe &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;c:&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;migration&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;fix_access_single_migration.py&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  
&lt;span class="c1"&gt;-- ------------------------------------------------------------------&lt;/span&gt;
&lt;span class="c1"&gt;-- checkpoint 5&lt;/span&gt;
&lt;span class="c1"&gt;-- Bulk data transfer&lt;/span&gt;
&lt;span class="p"&gt;(...)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;
And this is my Python script (fix_access_single_migration.py):
&lt;/p&gt;
&lt;div class="wiki-code"&gt;&lt;div class="code"&gt;&lt;pre&gt;&lt;span class="ch"&gt;#!/usr/bin/env python&lt;/span&gt;
&lt;span class="c1"&gt;# License: MIT licee&lt;/span&gt;
&lt;span class="c1"&gt;# Copyright (c) 2008 Felix Schwarz &amp;lt;felix schwarz AT oss schwarz eu&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;span class="c1"&gt;# Permission is hereby granted, free of charge, to any person obtaining a copy&lt;/span&gt;
&lt;span class="c1"&gt;# of this software and associated documentation files (the "Software"), to deal&lt;/span&gt;
&lt;span class="c1"&gt;# in the Software without restriction, including without limitation the rights&lt;/span&gt;
&lt;span class="c1"&gt;# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell&lt;/span&gt;
&lt;span class="c1"&gt;# copies of the Software, and to permit persons to whom the Software is&lt;/span&gt;
&lt;span class="c1"&gt;# furnished to do so, subject to the following conditions:&lt;/span&gt;
&lt;span class="c1"&gt;# &lt;/span&gt;
&lt;span class="c1"&gt;# The above copyright notice and this permission notice shall be included in&lt;/span&gt;
&lt;span class="c1"&gt;# all copies or substantial portions of the Software.&lt;/span&gt;
&lt;span class="c1"&gt;# &lt;/span&gt;
&lt;span class="c1"&gt;# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR&lt;/span&gt;
&lt;span class="c1"&gt;# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,&lt;/span&gt;
&lt;span class="c1"&gt;# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE&lt;/span&gt;
&lt;span class="c1"&gt;# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER&lt;/span&gt;
&lt;span class="c1"&gt;# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,&lt;/span&gt;
&lt;span class="c1"&gt;# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN&lt;/span&gt;
&lt;span class="c1"&gt;# THE SOFTWARE.&lt;/span&gt;
 
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;MySQLdb&lt;/span&gt;
 
hostname &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'localhost'&lt;/span&gt;
user &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'root'&lt;/span&gt;
password &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;
db &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_all_tables&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;cursor&lt;span class="p"&gt;):&lt;/span&gt;
    tables &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    cursor&lt;span class="o"&gt;.&lt;/span&gt;execute&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'show tables'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    rows &lt;span class="o"&gt;=&lt;/span&gt; cursor&lt;span class="o"&gt;.&lt;/span&gt;fetchall&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; row &lt;span class="ow"&gt;in&lt;/span&gt; rows&lt;span class="p"&gt;:&lt;/span&gt;
        tables&lt;span class="o"&gt;.&lt;/span&gt;append&lt;span class="p"&gt;(&lt;/span&gt;row&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; tables

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;find_double_fields&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;cursor&lt;span class="p"&gt;,&lt;/span&gt; tables&lt;span class="p"&gt;):&lt;/span&gt;
    problematic_fields &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; table &lt;span class="ow"&gt;in&lt;/span&gt; tables&lt;span class="p"&gt;:&lt;/span&gt;
        cursor&lt;span class="o"&gt;.&lt;/span&gt;execute&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;u&lt;/span&gt;&lt;span class="s1"&gt;'show fields from `&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt;`'&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; table&lt;span class="p"&gt;)&lt;/span&gt;
        rows &lt;span class="o"&gt;=&lt;/span&gt; cursor&lt;span class="o"&gt;.&lt;/span&gt;fetchall&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; row &lt;span class="ow"&gt;in&lt;/span&gt; rows&lt;span class="p"&gt;:&lt;/span&gt;
            col_name &lt;span class="o"&gt;=&lt;/span&gt; row&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            col_type &lt;span class="o"&gt;=&lt;/span&gt; row&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; col_type &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sa"&gt;u&lt;/span&gt;&lt;span class="s2"&gt;"double(7,2)"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                problematic_fields&lt;span class="o"&gt;.&lt;/span&gt;append&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="s2"&gt;"table"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; table&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"col_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; col_name&lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; problematic_fields

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fix_fields&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;cursor&lt;span class="p"&gt;,&lt;/span&gt; problems&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; problem &lt;span class="ow"&gt;in&lt;/span&gt; problems&lt;span class="p"&gt;:&lt;/span&gt;
        table &lt;span class="o"&gt;=&lt;/span&gt; problem&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"table"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        col_name &lt;span class="o"&gt;=&lt;/span&gt; problem&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"col_name"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        sql &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;u&lt;/span&gt;&lt;span class="s1"&gt;'ALTER TABLE `&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt;` CHANGE COLUMN `&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt;` `&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt;` DECIMAL(7,4)'&lt;/span&gt;
        cursor&lt;span class="o"&gt;.&lt;/span&gt;execute&lt;span class="p"&gt;(&lt;/span&gt;sql_template &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;table&lt;span class="p"&gt;,&lt;/span&gt; col_name&lt;span class="p"&gt;,&lt;/span&gt; col_name&lt;span class="p"&gt;))&lt;/span&gt;

conn &lt;span class="o"&gt;=&lt;/span&gt; MySQLdb&lt;span class="o"&gt;.&lt;/span&gt;connect&lt;span class="p"&gt;(&lt;/span&gt;host&lt;span class="o"&gt;=&lt;/span&gt;hostname&lt;span class="p"&gt;,&lt;/span&gt; user&lt;span class="o"&gt;=&lt;/span&gt;user&lt;span class="p"&gt;,&lt;/span&gt;
                       passwd&lt;span class="o"&gt;=&lt;/span&gt;password&lt;span class="p"&gt;,&lt;/span&gt; db&lt;span class="o"&gt;=&lt;/span&gt;db&lt;span class="p"&gt;,&lt;/span&gt;
                       use_unicode&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; charset&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"cp1250"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
conn&lt;span class="o"&gt;.&lt;/span&gt;begin&lt;span class="p"&gt;()&lt;/span&gt;

cursor &lt;span class="o"&gt;=&lt;/span&gt; conn&lt;span class="o"&gt;.&lt;/span&gt;cursor&lt;span class="p"&gt;()&lt;/span&gt;
tables &lt;span class="o"&gt;=&lt;/span&gt; get_all_tables&lt;span class="p"&gt;(&lt;/span&gt;cursor&lt;span class="p"&gt;)&lt;/span&gt;
problems &lt;span class="o"&gt;=&lt;/span&gt; find_double_fields&lt;span class="p"&gt;(&lt;/span&gt;cursor&lt;span class="p"&gt;,&lt;/span&gt; tables&lt;span class="p"&gt;)&lt;/span&gt;
fix_fields&lt;span class="p"&gt;(&lt;/span&gt;cursor&lt;span class="p"&gt;,&lt;/span&gt; problems&lt;span class="p"&gt;)&lt;/span&gt;

conn&lt;span class="o"&gt;.&lt;/span&gt;commit&lt;span class="p"&gt;()&lt;/span&gt;
conn&lt;span class="o"&gt;.&lt;/span&gt;close&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;</description><guid>https://www.schwarz.eu/posts/2008/migrating-data-from-ms-access-to-mysql-easily/</guid><pubDate>Sun, 22 Jun 2008 14:49:24 GMT</pubDate></item></channel></rss>