Mercurial FTP extension

If you use mercurial for your version control and you need to deploy continuously to a FTP site you should have a look at the extension developed by André Klitzing.

http://mercurial.selenic.com/wiki/FTPExtension

It will the first time upload all your files to your host and then only the missing committed changes, it’s really great instead or uploading all or remembering the 35 files that you changed everywhere you can simply do:

cd yourdirectory
hg ftp yourseveralias --upload
(Check the instructions for how to add your server alias,user,pwd)

Now unfortunately in my hosting the username MUST use @ like : user@site.com, so the URL is parsed wrongly by the python functions and the upload fails

I add this very naïve fix (New code in green and  DON’T forget to add proper padding in your editor)

Around line 31:
// Check if  there's more than one @ if so replace the first one with some arbitrary  characters that are hard to find in a username
path = self.ui.config('paths', url)
 if path:
   if path.find('@') != path.rfind('@'):
      path = path.replace('@','{!!',1)
   self.url = urlparse.urlsplit(path)
 else:
   self.url = urlparse.urlsplit(url)Around line 144 on the _ftp function BEFORE the login function
// Replace back the arbitrary characters '{!!' for the first @
 if user:
    user = user.replace('{!!','@',1)
 ftp.login(user, psw)

Hope it helps someone else!

Zend Framework and the future of PHP

I always saw PHP as a great language, is really easy to learn, it adapts itself to many levels of coders from total beginners coming from HTML to consummated developers creating advanced frameworks and huge applications, even better the final results are fast running applications that you can deploy in pretty much any hosting service.

PHP is not anymore a new cool proposal but a well established language and as I’m studying Python and RoR I can help but comparing and wondering about the future of the language specially now that I see so many applications based on the Zend framework.

While I love the new functionality in the Zend framework I’m not sure if that’s a step forward or backwards, it seems to tell the world: “thanks for all this years developing code on PHP but you guys haven’t done anything useful yet so now we will come with our advanced Java style coded classes to fill the huge gaps this language has.”

Does PHP lacks a collaborative community capable of really making the language evolve or complementing it?

How’s possible that other languages have now high quality, fully integrated and tested modules to do pretty much everything? Need to connect to Facebook, use a special service, parse data? There’s a package for it, there’s a way to downloading and install it.

The community have put a unified effort in making the languages powerful and complete, as a result developing in those languages is now faster than doing it on PHP and startups and companies looking for Agile style development are opting for those languages.

What happened to Pear? Where are those PHP well coded and tested classes that we all can use to work better and faster?

Why not betting for simplicity?

Zend framework seems to be trying to move PHP to a more complicated Java coding style instead of following the recent trend of super easy languages.

Don’t get me wrong in here I love Java and I know that thanks to Android is more alive than ever, still I wouldn’t recommend it for nearly any company or project over any other technology.

Why? Takes longer to develop, there’s no many qualified people around and the final results are not much better in comparison to other technologies (if they are at all).

So why is Zend using a coding style that makes PHP more inaccessible instead of trying to go for simple, intuitive and powerful ala Ruby or Python? Why not at least keep the PHP tradition of offering powerful functionality encapsulated in simple functions?

Is this the end of lightweight applications?

I remember how much I use to like Visual Basic 5-6, clean, easy, powerful and lightweight, once compiled you got a 200-300k application that you could use in pretty much any windows machine, it was awesome! Then .Net came and the applications become huge you either bundled the .Net framework with the installer or you were not sure that your application will run in your target computer.

PHP applications also use to be so light! You could get great applications in a couple of megabytes but now that’s gone, by the time you finish bundling the Zend framework your application is 35-40 megabytes, ridiculous! Sure you can just write it as a requirement or try to minimize the classes you need but at the end you’ll end up with a huge upload.

Conclusion

Certainly no one is obliged to use any classes or framework and it will be crazy to deny that the framework comes with really good functionality, but couldn’t it have been done in a more PHP’ish way?

And do we really need Zend to come to ‘save us’? Is it true that no valuable code ‘as good as’ was created before to fulfill those needs?

  • If it wasn’t it’s quite sad and perhaps it’s time to look for brighter horizons where smart coders exists (I don’t think so)
  • If it was it’s also sad that the community and the PHP makers didn’t find a way to canalize all that energy in an organized way so that we all could now enjoy  better ways to build even greater things on top.

Perhaps I’m getting all wrong but I keep getting this feeling that years and years of great coders work have been lost and that Zend framework recent efforts are going against their base users, the ones that like a simple powerful language to deliver good results.

Time for Mac?

It was just yesterday that the new MacBook Air was announced and I’m really feeling tempted to betray myself and run to get one, while I still don’t like the apple business ways it hard not to recognize that they are making the hottest gadgets in the market.

What’s happening to the rest of the companies! Cannot come with amazing things yourselves? Is it really possible that there’s only a single company capable of creating perfect beautiful gadgets?

So hating myself I came on-line today and did a quick googling about Mac for developers and unfortunately it seems like all I need is available on a Mac:
PHP, Python, RoR, MySql, Apache, Android, Netbeans, Eclipse etc..

On top of that, the normal suspects that have sadly been missing in my ubuntu for the last year: All the adobe applications and a decent MSOffice.

Is this the time to quit my awesome Ubuntu?

Learning Python – Making a SVCD GUI

I was making a VCD the other day for my kid and rediscover how hard it can be to make one of those – in Ubuntu-. I love the OS but every application that I tried was from useless to lousy, as often happen in the Linux world I discover the absolute best tools are on the command line.

While I don’t mind playing with the terminal, the truth is there’s no way I’ll ever remember all those special flags, parameters and other strange things you need to re-encode videos, so I decided to try my freshly acquired Python knowledge to make a small tool to pass my commands to the terminal and encode the videos using mencoder.

I was harder than expected and the final result could be better, but it’s not bad for a first hands on!

To develop this project I used:

  • Geany. For coding
  • Glade. To design the interface

Functionalities:

  • Can convert a single video to VCD or SVCD
  • Can crop a single video (Time start – Time End) < Both in seconds
  • Can convert all videos in a directory to VCD or SVCD

Problems:

  • The progress bar is useless, it never seems to update
  • Better to be run from terminal so you can see the actual progress of the task

The source:
This code include the glade file, the final .xml to define the interface and the python file (Change the format after downloading)
VCDmaker.zip

The python code.

  1. #!/usr/bin/env python
  2.  
  3. # gtk-builder-convert tutorial.glade vcdmaker.xml
  4. # Then save this file as tutorial.py and make it executable using this command:
  5. # chmod a+x vcdtrigger.py
  6. # And execute it:
  7. # ./vcdtrigger.py
  8.  
  9. import pygtk
  10. pygtk.require("2.0")
  11. import gtk
  12. import os
  13. import subprocess
  14.  
  15. class vcd(object):      
  16.     def __init__(self):
  17.         builder = gtk.Builder()
  18.         builder.add_from_file("vcdmaker.xml")
  19.         builder.connect_signals(self)
  20.         self.window = builder.get_object("Video Maker")
  21.         self.saveas = builder.get_object("txt_saveas")
  22.         self.filesource = builder.get_object("file_source")
  23.         self.dirsource = builder.get_object("dirchooser")
  24.         self.dirsource.set_action(gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER)
  25.         self.pbar = builder.get_object("progressbar1")
  26.         self.type = builder.get_object("sel_target")
  27.         self.type.set_active(1)
  28.         self.output = builder.get_object("txtoutput")
  29.         self.ini = builder.get_object("spin_start")
  30.         self.end = builder.get_object("spin_end")
  31.         self.window.show()
  32.  
  33.     def on_window_destroy(self, widget, data=None):
  34.         gtk.main_quit()
  35.    
  36.     def target_name(self,filename):
  37.         parts = os.path.split(filename)
  38.         name = parts[1]
  39.         name = name.replace(" ","_")
  40.         name = name.lower()
  41.         ext = os.path.splitext(name)[1]
  42.         ext = ext[1:]
  43.         ext = ext.lower()
  44.         videoformats = [‘mpg’,‘avi’,‘mpeg’, ‘flv’]
  45.         if (ext in videoformats):
  46.             return(parts[0] + os.sep + "VCD" + os.sep + "VCD_" + os.path.splitext(name)[0]+".mpg")
  47.         else:
  48.             return False
  49.    
  50.     def on_dirchooser_file_set(self, dir_chooser):
  51.         self.saveas.set_text("Multiple")
  52.    
  53.     def on_file_source_file_set(self, file_chooser):
  54.         if (self.filesource.get_filename() != None):
  55.             saveas = self.target_name(self.filesource.get_filename())
  56.             if (saveas != False):
  57.                 self.saveas.set_text(saveas)
  58.             else:
  59.                 self.error_message ("Please select a video file")
  60.    
  61.     def on_btnstart_clicked(self, button):
  62.         if (self.saveas.get_text() == ""):
  63.             print("Please select a file first")
  64.             self.error_message("Please select a file first")
  65.         elif self.saveas.get_text() == "Multiple":
  66.             print self.dirsource.get_filename()
  67.             self.convert_all(self.dirsource.get_filename(), self.type.get_active())
  68.         else:
  69.             targetdir = os.path.split(self.saveas.get_text())
  70.             print (targetdir)
  71.             self.check_target_dir(targetdir[0])
  72.             c = converter()
  73.             if self.type.get_active() == 1:
  74.                 c.svcd(self.filesource.get_filename(), self.saveas.get_text(), self.ini.get_value_as_int(), self.end.get_value_as_int())
  75.             else:
  76.                 c.vcd(self.filesource.get_filename(), self.saveas.get_text(), self.ini.get_value_as_int(), self.end.get_value_as_int())
  77.  
  78.  
  79.     def check_target_dir(self, dirname):
  80.         print (dirname)
  81.         if not os.path.isdir(dirname):
  82.             os.makedirs(dirname)
  83.  
  84.    
  85.     def convert_all(self, dir, format):
  86.         print ("dir: %s" % dir)
  87.         c = converter()
  88.         self.check_target_dir(dir + os.sep + "VCD")
  89.         x=0
  90.         for directory, subdirectories, files in os.walk(dir):
  91.             file_count = len(files)
  92.             if (x==0):
  93.                 self.pbar.set_text("Processing %d" % file_count)
  94.             for file in files:
  95.                 x +=1
  96.                 print ("Processing file %d of %d " % (x, file_count))
  97.                 self.pbar.set_fraction( x / file_count)
  98.                 print (x/file_count)
  99.                 source = os.path.join(directory,file)
  100.                 target = self.target_name(source)
  101.                 if self.type.get_active() ==1:
  102.                     c.svcd(source, target, 0, 0)
  103.                 else:
  104.                     c.vcd(source, target, 0, 0)
  105.  
  106.     def error_message(self, message):
  107.         print message
  108.         dialog = gtk.MessageDialog(None, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, message)
  109.         dialog.run()
  110.         dialog.destroy()
  111.        
  112. class converter:
  113.     def test(self):
  114.         print ("test ok")
  115.        
  116.     def get_common_cmd(self, source, ini, end):
  117.         command = [‘mencoder’, source]
  118.         if (ini != 0 ):
  119.             command.append("-ss")
  120.             command.append("%d" % ini)
  121.         if (end!=0):
  122.             command.append("-endpos")
  123.             command.append("%d " % end)
  124.         return command
  125.        
  126.     def vcd(self, source, target, ini=0, end=0):
  127.         command = self.get_common_cmd(source, ini,end)
  128.         command += [‘-oac’ ,‘lavc’ ,‘-ovc’ ,‘lavc’ ,‘-of’ ,‘mpeg’ ,‘-mpegopts’ ,‘format=xsvcd’ ,‘-vf’ ,‘scale=480:576,harddup’ ,‘-srate’ ,‘44100’ ,‘-af’ ,‘lavcresample=44100’ ,‘-lavcopts’  ,‘vcodec=mpeg2video:mbd=2:keyint=15:vrc_buf_size=917:vrc_minrate=600:vbitrate=2500:vrc_maxrate=2500:acodec=mp2:abitrate=224:aspect=16/9’ ,‘-ofps’ ,’25’ ,‘-o’, target]
  129.         self.convert(command)
  130.  
  131.     def svcd(self,source, target, ini=0, end=0):       
  132.         command = self.get_common_cmd(source, ini,end)
  133.         command += [‘-oac’, ‘lavc’ ,‘-ovc’, ‘lavc’ ,‘-of’ ,‘mpeg’ ,‘-mpegopts’ ,‘format=xvcd’ ,‘-vf’ ,‘scale=352:288,harddup’ ,‘-srate’, ‘44100’ ,‘-af’ ,‘lavcresample=44100’ ,‘-lavcopts’ ,‘vcodec=mpeg1video:keyint=15:vrc_buf_size=327:vrc_minrate=1152:vbitrate=1152:vrc_maxrate=1152:acodec=mp2:abitrate=224:aspect=16/9’ ,‘-ofps’ ,’25’ ,‘-o’, target]       
  134.         self.convert(command)
  135.        
  136.     def convert (self, command):
  137.         print (command)
  138.         #Execute the actual command and put result in text
  139.         #os.execl ("mencoder", command)
  140.         subprocess.call(command)
  141.        
  142. if __name__ == "__main__":
  143.     app = vcd()
  144.     gtk.main()
  145.  

Searching strings in a database and files

I always find cumbersome to work or maintain someone else systems, to make a small changes you normally lose hours trying to find out where that little string or that link that you have to modify.

Searching in files

For searching in files you can sometimes download all the site and simply use netbeans or other tool to search in files.

If you are lucky and you are in Linux you can SSH to the server and use the system tools. Check this fantastic link on how to do it

http://www.liamdelahunty.com/tips/linux_find_string_files.php

Searching in the database

Now what happens when you discover that what you are looking for is contained in the database?

Sometimes you can just browse the database content or dump the database and search as file but this can be hard with big databases.

So I decided for a very simple solution, create a PHP script that will check for a string in every available string field in all tables.  It will echo the name of the tables that have matches or you can go further and echo the ID’s or even further and echo the whole content of the row.

Hope someone else find it useful!

(This script is v.0.2, I updated after Andrew commented on the wrong sql construction) Please let me know of you find bugs.

<?php
/**
* Simple tool to search in a mysql database in all string fields
*
* Browse to search.php?term=yourterm[detailed=1/2]
* searchdb.php?action=search&term=yourterm[&detailed=1/2][&in=table1,table2,...][&notin=table1,table2,...]
*
* To use this class set $dbase to the database you want to use, 
* You might also need to modify line 70 to enter your own server info
* (I left that info hardcoded as it failed to use this info using variables in one server)
*
* If you need it, would be easy to add a web interface, just create one field for every parameter and send the form via GET or change the safeget function to read the POST or REQUEST var instead 
*
* Please let me know of any errors
*
* @author itzco 
* @version 0.2
*/

error_reporting(E_ALL);
set_time_limit(120);
// 1) SET YOUR DB NAME HERE
$dbase = 'yourdbnamehere';

if (empty($_GET)) {
    echo "Please enter a method (or action=help)";
    die();
}

echo "<pre>";
$method =safeget('action');


switch($method) {
    case 'getdbpwd':
    // This method to get the user/pwd when it's set in php.ini
        $hk = new db();
        $tmp = $hk->getdata();
        echo 'User: '.$tmp['user'].'Pwd:'. $tmp['pwd'];
        break;
    case 'search':
        $finder = new dbsearch($dbase);
        $finder->exclude(safeget('notin'));
        $finder->useonly(safeget('in'));
        $finder->find(safeget('term'),safeget('detailed'));
        break;
    case 'help':

        echo "<br>Methods:getdbpwd/search<p>getdbpwd<br>?action=getdbpwd&domain=xxx.domain.nl</p>";
        echo "<p>search<br>?action=search&term=<i>yourterm</i>[&detailed=1/2][&in=table1,table2,...][&notin=table1,table2,...]</p>";
        echo "<p>While in/notin are not mutually exclusive setting one is enough";
        break;
}
function safeget($key,$default=false)
{
    return isset($_GET[$key]) ? $_GET[$key] : $default;
}

class db {
    private $link;
    public function getdata() {
        return array('user'=>ini_get('mysql.default_user'), 'pwd' =>ini_get('mysql.default_password'));

    }
    public function connect($dbase)
    {
        $user = ini_get('mysql.default_user');
        $pwd  = ini_get('mysql.default_password');
        if ($user & $pwd)
        {
           $this->link = @mysql_connect();
        }
        else
        {
            echo  '<br>Warning: ini_get user and pwd are not set';
            // 2) Enter here your user pwd and server if required
            $this->link = @mysql_connect('localhost', 'root', 'root');
        }
        if ($this->link === FALSE)
    {
            die ('Not able to connect to the database');
        }
    else
    {
            $db_selected = mysql_select_db ($dbase, $this->link);
            if (!$db_selected) {
                die ('Can\'t use database : ' . mysql_error());
            }
    }

    }
    function query($query)
    {

        if (false === ($this->res = mysql_query($query, $this->link))) {
            echo sprintf('MySQL error #%d: %s.', mysql_errno(), mysql_error());
        } else {
            $this->error = null;
            $result = array();
            while ($row = mysql_fetch_assoc($this->res)) {
                $result[] = $row;
            }
            return $result;
        }
    }
}



class dbsearch
{
    private $db;
    private $targettypes = array('varchar','char','text','tinytext','mediumtext');
    private $dbname;
    private $include=array();
    private $exclude=array();
    public function __construct($dbase)
    {
        $this->db = new db();
        $this->db->connect($dbase);
        $this->dbname = $dbase;
    }
    public function exclude($tables)
    {
        if (!$tables) return;
        $this->exclude = explode(',',$tables);
    }
    public function useonly($tables)
    {
        if (!$tables) return;
        $this->include = explode(',',$tables);
    }
    public function find($string, $detailed=false)
    {
        if (!$string || strlen($string)<3)
        {
            die ("<br>Please enter a search term (3 chars min)");
        }
        $tables = $this->db->query('SHOW TABLES');

        $tblfn = 'Tables_in_'.$this->dbname;
        foreach ($tables as $tablerow)
        {
            $table = $tablerow[$tblfn];
            $use = ($this->include ? in_array($table, $this->include) : true);
            $notuse = ($this->exclude ? in_array($table, $this->exclude) : false);
            if ($use && !$notuse)
            {
            $qryfields = array();
            $key='';

            echo ".";
            $msql = 'SHOW FIELDS IN '. $table;
            //echo "<br>$msql<br>";
            $fields = $this->db->query($msql);
            // use char,varchar and text tinytext
            //print_r($fields);
            foreach($fields as $field)
            {
                $mysqltype = preg_replace('/\s*\(\d+\)\s*/','', $field['Type']);
        $mysqltype = preg_replace('/\s*\(\d+,\d+\)\s*/','', $mysqltype);
                //echo $mysqltype."<br>";
                if ($field['Key'] == 'PRI' && !$key)
                {
                    $key = $field['Field'];
                }
                if (in_array($mysqltype, $this->targettypes ))
                {
                    $qryfields[] = '`'.$field['Field']."` like '%$string%'";
                }
            }
            //echo "<br> PRI: $key";print_r($qryfields);
            if ($qryfields)
            {
                //print_r($table);
                $mSql = "SELECT count(*) as e FROM  `$table` WHERE " . implode(' OR ', $qryfields);
                //echo $mSql."<br>";
                $result = $this->db->query($mSql);
                if ($result[0]['e'] >0)
                {
                    echo "<br>$table (".$result[0]['e'].')';
                    if ($detailed)
                    {
                         $mSql = "SELECT ".($detailed==1 ? $key : '*')." FROM  `$table` WHERE " . implode(' OR ', $qryfields);
                         echo '<div style="border:1px solid #ccc; height:200px;overflow:auto;">';
                         print_r($this->db->query($mSql));
                         echo "</div>";
                    }
                }

            }
        }//if
        }
        echo "Done.";
    }
}

?>

Working with Mercurial .hgignore

We’re currently using Mercurial as the base tequila repository and we frequently have to work with .hgignore files

After some fighting with this regex here are some rules that seems to work

syntax: glob

syntax: regexp
#^gateway.php$ Ignores a top level file called gateway

# ignores a directory
^includes/app$

# ignores another directory
^includes/packages/Zend$

#ignores cache.php
^temp/cache\.*\.php$
# ignores cache.inc.php
^temp/cache\.inc\.php/
cache\.inc\.php$

#ignores logs
log.*\.txt

Interestingly I cannot manage to ignore the file temp/cache.inc.php no matter what I do! When I have the answer I’ll modify this post!

PureMVC Templates

Enjoying and suffering still this PureMVC learning, must recognize that it’s a really verbose framework, isn’t there any possibility to the same without having all those boring strings around :)?

Anyway, as I consider it crazy to type like an idiot I just made some snippets to use in Flex Builder.

To use this templates:
Follow instructions to install support on Flex builder:
http://cookbooks.adobe.com/post_Handling_snippets_in_Flex_Builder-13507.html

Download the attached file (Change extension to .xml) Download: snippets

Import the attached file from the Snippets window

Enjoy!

Templates are mainly taken from:
http://forums.puremvc.org/index.php?topic=1527.msg6930#msg6930

& my own first experiment.