Jul 21 2008

OpsWeb Printlog

Filed under: What I Did Today

I wrote the printlog page some time ago, but the server that was hosting OpsWeb didn't have the GD libraries installed. Now that we have moved I can show the app in its full glory with it graph.

screen shot

I am using the JpGraph library to render a line graph of each days print total and display all together on one graph.

Jun 27 2008

Adding a Parser in PHP

Filed under: OpsWeb, What I Did Today

What is Parsing? (You can click this link to see the Wikipedia article)
In short parsing is taking chunks of data and replacing them with something else, based on the chunk you took out.

So why is this useful you may ask. Lets take a look at a problem the users of OpsWeb (The Intranet site I develop) had. One of the largest parts of OpsWeb is the article section. There are over 600 technical articles and more being added every week. A lot of times users will want to link from one article to another. To do this they had to add a normal HTML link. Even thou my user population is made of IT staff and programmers most of them have little or no experience with HTML. This made linking hard, and it should be easy. We want lots of links to help connect information together.

Here is the solution:
From now on to add a link to an article you do not use HTML. You now write the link as [[article#]], or [[article#:link text]].

Examples:
[[530]] When the page is displayed it is shown as OpsWeb Improvements and Changes
[[530:List of Changes]] -> List of Changes

Both of the examples make a link to article 530, and the only thing the user needs to know is the number of the article. (The users are very aware of the article numbers as they are used as a short hand reference on process flowcharts and emails. Example "Hay Bob i need you to preform the reset procedure from article 126")

How to write a simple text parser in PHP

Enough into on to the good stuff. Lets look at some code.

 
public function body_parser($text){
//paser to prossess body text of an article
$result = preg_replace_callback("/\[\[(\d+)\]\]/", array($this, 'p_format_link'), $text);
$result = preg_replace_callback("/\[\[(\d+):(.*)\]\]/", array($this, 'p_format_link_with_text'), $result);
return $result;
}
public function p_format_link($given){
return
"<a href="http://egondev.com/opsweb/articles/articles_control.php?action=display_one&amp;record_id=">"
.$this-&gt;get_title_from_id_DB($given[1])
."</a>";
}
public function p_format_link_with_text($given){
return
"<a href="http://egondev.com/opsweb/articles/articles_control.php?action=display_one&amp;record_id=">"
.$given[2]
."</a>";
}

The Function body_parser finds all of our link tags (the [[article#]] bits) using regular expressions and then pass the links to one of the other 2 functions to be formatted in to HTML. Then it puts the formatted text into the body in place of our link tag.
Example:
$body = "hello I am a short article with a link to [[530]]";
$body = body_parser($body);
echo $body;

>> "hello I am a short article with a link to OpsWeb Improvements and Changes"

The heart of this thing is the preg_replace_callback function. What it does is it searches a string using a regular expression. When it finds a match in that string it calls the function given as the second param and pass the regular expression match to the function. Then it will replace the matched part of the sting with the return of the function.

mixed preg_replace_callback ( mixed $pattern , callback $callback , mixed $subject [, int $limit [, int &$count ]] )

Lets take a look at one of the preg_replace_callback lines from my parser
$result = preg_replace_callback("/\[\[(\d+)\]\]/", array($this, 'p_format_link'), $text);

  • "/\[\[(\d+)\]\]/" will match any chunk of text starting with [[ followed by 1 or more digits and ending with ]]
  • The (\d+) part of the expression will give us just the digits as a sub part of the matched text.
  • array($this, 'p_format_link') this is the call back function. The syntax looks odd because all 3 of these functions are in a class to reference the call back function correctly you need to use this form. With out a class structure it would just be the name of the function you want to pass the match to. No ()s.
  • $text is the sting we a passing to the preg_replace_callback function.
  • $result is the new string with the matches subsited with the return form the call back function.
Jun 03 2008

1 Step Unbuntu Flash and Codexs

Filed under: What I Did Today

One easy step set up of the medibuntu repositories and install of stuff you will want on all your computers.

Daily Gyan
http://www.dailygyan.com/2008/04/how-to-have-dvd-playback-adobe-flash.html

Jun 02 2008

Where do you start with OO PHP, and the MCV pattern

Filed under: What I Did Today

php books
PHP-Objects-Patterns-Practice-Second
PHP-Nutshell-OReilly-Paul-Hudson
these 2 are the best and will cover most of what you need

php modules you will want to look at
adodb  db abstraction
smarty  template

simple explanation of mcv pattern

controller:

* This the php script that the users will call
* Made up of a case statement or other logic that will trigger the desired functions and display the correct template to the user.

example:

 
//import the required files
require_once('model.class.php');
require_once('templater.php');
//create object from model
$blog = new blog;
//create object form templater
$tlp = new templater;
//import the post and get vars
import_request_variables("p", "post_");
import_request_variables("g", "get_");
//find and preform actions
switch($get_action){
case "input":   //if the $get_action is set to input
  if (get_form == 'save'){   //if the request includes a flag to save
    if ($blog-&gt;clean($post_input) == false){   //try clean the user input from the form, verifacation escaping...
      $tlp-&gt;assign('vars', $blog-&gt;vars);   //pass the user input back to the form so the user doesn't need yo reenter it
      $tlp-&gt;assign('errors' $blog-&gt;errors);   //pass the error the cleaning function generated back to the form
      $tlp-&gt;render('form.tlp');   //render the form for the user
    }else{
      $blog-&gt;save();   //save the no cleaned data in the database
      $tlp-&gt;render(home_page.tlp);   //generate the html for the home page
    }
  }else{
    $tlp-&gt;render('form.tlp');   //render the input form
  }
break;
default:   //if no action is set
  $tlp-&gt;assign('entries', $blog-&gt;get_entries());   //pass the blog entries to the home page template
  $tlp-&gt;render('home_page.tlp');  // generate the html for the home page
break;
?&gt;
 

model:

* Data model and application methods

example:

 
class blog{
//data vars
private $title;
private $body;
 //methods for common tasks
public function clean($vars){
  $this-&gt;title = trim(strip_tags($vars['title']));
  $this-&gt;body = trim(strip_tags($vars['body']));
  return true;
}
public function save(){
  connect to DB
  make query
  return true
}
public function get_entries(){
  connect to DB
  make query
  return array of entries
}
}
 

view/template:

* The interface for the user
* Normal a template of some kind to be filled with variables passed to it by the controller

example:

{include('head.html')}
my blog
{foreach entries as entry}
  {title}
  {entry}
{/foreach}
{include('foot.html')}
May 27 2008

Wieghted Searches in MySQL

Filed under: OpsWeb, What I Did Today

MySQL has the Match Against syntax for preforming full text searches, but what if you have multiple fields and would like to weight there values differently. A key word in a title has more meaning then a key word in the body right. Below is the function I am using now on the article component of OpsWeb, the intranet site I develop. I will step you through how it works.

 
public function search($search_string){
	//gets the adodb wrapper if it hasn't been loaded already
	require_once($this-&gt;paths['adodb_path']-&gt;get_primary().'adodb.inc.php');
	$DB = &amp;ADONewConnection($this-&gt;DNS); 
 
	$query = "
	SELECT
		ROWID
		,title
		,description
		,MATCH (title) AGAINST('$search_string' )*3
		+ MATCH (description) AGAINST('$search_string' )*2
		+ MATCH (body) AGAINST('$search_string' ) score
	FROM articles
	WHERE  MATCH (title, description, body) AGAINST('$search_string' IN BOOLEAN MODE)
	AND retired != '1'
	ORDER BY score DESC;
	";
 
	$results = $DB-&gt;Execute($query);
 
	if ($results){
		return $results-&gt;GetRows();
	}else{
		return array(array(description =&gt; "No Results Found for '$search_string'"));
	}
}

In the query string you can see that I am retrieving normal 3 fields and one computed field called score. You can learn more about the match against syntax here. Simply put the the server looks in the match field for things that look like the against string and returns a score based on how well the string matched and how unique the words are. All I am doing here is adding some multipliers to give fields different weights, title worth more points the the body and such. This gives me my weighted score.

The next part of the query string to note is the where clause. Here I have another match against statement. This one is using the boolean mode switch. Boolean searches have a lot of additional options you can add to a search to narrow down your results. The problem is that MySQL doesn't return a score for boolean searches. All matches have a score of 1 if that row is a match. By adding this extra match against statement to the where clause we preform the weighted search only against a sub set of rows that match the boolean search, and keep that ability to narrow the search while still giving well scored results.

With this new search the results returned have become a lot better.

May 21 2008

Test of ScribeFire

Filed under: What I Did Today

I have set up ScribeFire for Firefox. Which was pretty pain less. Now I am just giving it a test.

Hay what do you know it worked on the first try.

May 21 2008

Test of ScribeFire

Filed under: What I Did Today

I have set up ScribeFire for Firefox. Which was pretty pain less. Now I am just giving it a test.
May 02 2008

Project Euler: Problem 14

Filed under: Project Euler

The following iterative sequence is defined for the set of positive integers:

nn/2 (n is even)
n → 3n + 1 (n is odd)

Using the rule above and starting with 13, we generate the following sequence:

13 → 40 → 20 → 10 → 5 → 16 → 8 → 4 → 2 → 1

It can be seen that this sequence (starting at 13 and finishing at 1) contains 10 terms. Although it has not been proved yet (Collatz Problem), it is thought that all starting numbers finish at 1.

Which starting number, under one million, produces the longest chain?



   1	def genChain(foo):

   2	    chain = list()

   3	    chain.append(foo)

   4	    while not foo == 1:

   5	        foo = genChainNum(foo)

   6	        chain.append(foo)

   7	    return chain

   8	def genChainNum(foo):

   9	    #print foo

  10	    if foo % 2 == 0:

  11	        return foo/2

  12	    else:

  13	        return foo*3+1

  14

  15	if __name__ == "__main__":

  16	    foo = 1

  17	    longestChain = list()

  18	    while foo <= 1000000:

  19	        #print foo

  20	        bar = genChain(foo)

  21	        if len(longestChain) < len(bar):

  22	            longestChain = bar

  23	            longestChainSeed = foo

  24	        foo = foo +1

  25	    print "The longest chain %s" % "-->".join(["%s"% (s) for s in longestChain])

  26	    print len(longestChain)

  27	    print longestChainSeed

  28	    print "The longest chain has %i numbers, and has a seed of %i" % (len(longestChain), longestChainSeed)

May 02 2008

Python Decorators

Filed under: What I Did Today

I read a blog page today that had some interesting python info.  It talks about python decorators and the memorize pattern to catch function calls.

memorize pattern

if function has been called with args before return the previous results

else preform the function with the args and save results

Looks pretty handy for speeding up algorithms that may have a repetitive calls.

http://avinashv.net/2008/04/python-decorators-syntactic-sugar/

May 01 2008

PowerBook bites the dust

Filed under: What I Did Today

The loyal PowerBook G4 I had been using for a dev computer at work die today. Well not exactly. Networking turned off the port it was on and forgot to tell me. That is until I had finished setting up my new dev computer. (insert sound of hand hitting forehead)

Good news is the new computer is a p4 with twice the ram, and since it isn't a power pc chip I have the newest version of Xubuntu to play with.