Sunday, August 23, 2015

Phonetic search of arabic names latin spelled

The goal of this article is to give an approach to search record on a latin database of recrods containing arab names / surnames,  of course, there is no problem if the records were written in arabic letter encoded in a charset that supports this language such ar8mswin1256 charset recommended for oracle databases, but when we phonetic translate an arabic name and spell it in latin chars, that's another kind of non bijective job,
Let's take a look:

Mohammed (salla Allah Alyhi wa sallam) can be spelled :
   1- Mohamed
   2- Muhamed
   3- Mouhamed
   4- Mouhammed
   5- Med (Abusive spelling in north Afraica countries ex french occupied  countries)
   6- Mohd(Same a 5)

you must note that in arabic theres there is often a successive double consonant, and those who don't respect phonetic translation can omit the second char, even if the word become incorrect in certain case
eg:
 'S' between two vowels that become 'Z'

Prohet's name is a very frequent name in Muslim countries, we can say then that's a particular case?
Let's get another look


i'll take now my name and others
Wassim, can be spelled:
   1- Ouassim
   2- Wessim
   3- Ouessim
   4- Wassime
   5- Ouassime
   6- Ouessime

And that for all names with 'W' char, we can replace the W by ou and we have the same name.
We can note also that the 'E' char at the end of the word often doesn't metter

Tayeb can be spelled:
   1-Taieb
We can note that 'Y' char and 'I' (also 'EE' in middle east culture ) have the same phonetic effect.
then we can spell
Sami-->Samy (don't forget Sammy -double consonant - evoked earlier)
Rym -->Rim

etcetera etcetera...

Let's now comeback to the approach that can be adapted to resolve the problem.

First of all, we must establish our names dictionary, not a real dictionary because arabic language has the must vast dictionary with 12 000 000 word vs 600 000 word in english **, but let's say that we will build a function that will transform an arabic name on another word that we can exploit later by a simple matching with the result of the same function executed on the searched for name.

behind sentences
We'll build function f
for each name of our database
 f(name) = name' stored somewhere

select from database rows with condition ; f(searchedForName) = name'

 f(name) = name' stored somewhere ??? why??
storing the result will be very useful because the result is always the same for records stored in our database, we win a time and money by ding that until the change of our function f we must re-execute
the function on our population.

function f(name):
Considering our parameter name which is a String, an intelligent algorithm will consider that string a char sequence, other ways we will be in the case **.We'll transform that char sequence to another one which we will respect our universal rules, and unlike ** the cardinality of  rules set is an infinitesimal (x 10) in front of arabic words set count (12 000 000)
Our algorithm will take a decision and return a string that can be blank if needed for all incoming letters in our world: Typical recursive behavior, the normalized name will be the concatenation of results of recursive call on the word.

We remark also that the pseudo strings generated during recursive call, needs some extra information:
specially previous char 'in consonant case (double char) or other cases' end sometimes we need the next char if it exists.


ALGORITHM String getArabicPhoneticEquivalent (String name , Int depth) RETURN String

DECLARE
   String toReturn;
   char nextChar,currentChar;
BEGIN

IF StringLength(name) == depth THEN //Condition to break recursivity
    return "";
END IF;

toReturn =""; // Must be set to empty string

IF depth == 0 THEN
     name =  StringLowerCase(name);//Affects lowercase to input
     name =  cleanSpecialChars(name); //Transfom some unsupported letters
     toReturn  = getSpecialNames(name);// Tests for special names
END IF;


IF toReturn != "" THEN // Case we have special name, no need to continue
   return toReturn;
END IF;

currentChar = name[depth];

IF StringLength(name) > depth THEN
     nextChar = name[depth+1];
ELSE
     nextChar = ' ';
END IF;
depth = depth + 1;
IF nextChar == currentChar THEN //Double letter simulated to one
    return  getArabicPhoneticEquivalent (name , depth);
END IF;

IF consonant(name[depth]) THEN
   
    IF name[depth] == 'h' THEN //Skip h after a consonant
         IF depth > 1 AND consonant(name[depth-1]) THEN
              return  getArabicPhoneticEquivalent (name , depth);
         END IF;
     END IF;
     return StringConcat(getConsonantEquivalent(name[depth],nextChar) , getArabicPhoneticEquivalent (name , depth));
END IF;

IF vowel(name[depth]) THEN
       return StringConcat(getVowelEquivalent(name[depth],nextChar) , getArabicPhoneticEquivalent (name , depth));
END IF;

END ALGORITHM;    

ALGORITHM skipSpecialChars(String name) RETURN String
BEGIN
  name = StringReplaceAllSequences("w","ou");
  name = StringReplaceAllSequences("ï","i");
  name = StringReplaceAllSequences("î","i");
  name = StringReplaceAllSequences("ô","o");
  name = StringReplaceAllSequences("é","e");
  name = StringReplaceAllSequences("è","e");
  name = StringReplaceAllSequences("ê","e");
  name = StringReplaceAllSequences("à","a");
  name = StringReplaceAllSequences("ç","c");
  return name;
END ALGORITHM;

ALGORITHM getSpecialNames(String name) RETURN String
BEGIN
   IF name == "med" OR name =="mohd" THEN
       return 'mouhamed';
   END IF;

   return "";
END ALGORITHM;

ALGORITHM getConsonantEquivalent(char currentChar, char nextChar) RETURN String
BEGIN
IF currentChar == 'c' THEN
    IF  consonant(nextChar)THEN
          return "k";
    ELSEIF vowel(nextChar) THEN
          return "s"
    END IF;
END IF;
return currentChar+"";
END ALGORITHM

ALGORITHM getVowelEquivalent(char currentChar, char nextChar) RETURN String
BEGIN
IF currentChar == 'y' THEN
     return "i";
END IF;
IF currentChar == 'e' THEN
     return "a";
END IF;
return currentChar+"";
END ALGORITHM;

Note that we can make this algorithm more performant by adding additional controls
such as:
The abreviation of ben 'b'
The 'el' similar to 'al'

In the PLSQL code below i've added these controls and some others
'Code may be more up te date than algorithm ( no worry the spirit is kept)'

 
create or replace 
package body PKG_SEARCH is
  -------------------------------------------------------------
  -- Teste les voyelles
  -- @return boolean
  -------------------------------------------------------------
  function f_is_vowel(c in char) return boolean is
  begin
     if c in ('a','e','i','o','y','u') then 
        return true;
     else
        return false;
     end if;   
  end;
    -------------------------------------------------------------
  -- Teste les consonnes
  -- @return boolean
  -------------------------------------------------------------
  function f_is_consonent(c in char) return boolean is
  begin
     if c in ('b','c','d','f','g','h','j','k','l','m','n','p','q','r','s','t','v','w','x','z') then      
        return true;
     else
        return false;
     end if;     
  end;
  -------------------------------------------------------------
  -- Teste les consonnes
  -- @return boolean
  -------------------------------------------------------------
  function  f_special_chars(a_name in out varchar) return varchar is
  begin    
    a_name := replace(a_name,'ï','i');
    a_name := replace(a_name,'î','i');
    a_name := replace(a_name,'ô','o');
    a_name := replace(a_name,'é','e');
    a_name := replace(a_name,'è','e');
    a_name := replace(a_name,'ê','e');
    a_name := replace(a_name,'à','a');
    a_name := replace(a_name,'ç','c');    
    a_name := replace(a_name,'w','o');    
    a_name := replace(a_name,'u','o');   
    a_name := replace(a_name,'y','i');   
    a_name := replace(a_name,' b ','ben');   
    a_name := replace(a_name,' b','ben');   
    a_name := replace(a_name,' al ',' al');   
    if (length(a_name) >1 and substr(a_name,1,2) = 'b ') or (length(a_name) >3 and substr(a_name,1,2) = 'ben ')  then
          a_name := 'ben '|| substr(a_name,2);
    end if;
    if length(a_name) >2 and substr(a_name,1,2) = 'el' then
          a_name := 'al'|| substr(a_name,3);
    end if;
    return a_name;  
  end;
  -------------------------------------------------------------
  -- Traite les noms speciaux
  -- @return boolean
  -------------------------------------------------------------
  function f_special_names(a_name in varchar) return  varchar is
  begin
     if a_name = 'med' or a_name ='mohd' then
       return 'mouhamed';
     end if;  
     return '';
  end;
  -------------------------------------------------------------
  -- Retourne la chaine de caractère qui correspond à la consonne en question
  -- @return boolean
  -------------------------------------------------------------
function f_consonant_equivalent(currentChar in char, nextChar in char) return  varchar is
  begin
  if currentChar = 'c' THEN
      if  f_is_consonent(nextChar)THEN 
            return 'k';
      elsif f_is_vowel(nextChar) THEN
            return 's';
      end if;
  end if;
  if currentChar = 'd' THEN
        if  f_is_consonent(nextChar)THEN
          return '';
        end if;
  end if;  
  return currentChar;
end;
  -------------------------------------------------------------
  -- Retourne la chaine de caractère qui correspond à la voyelle en question
  -- @return boolean
  -------------------------------------------------------------
function f_vowel_equivalent(currentChar in char, nextChar in char) return varchar is
begin  
  if currentChar = 'e' then
      if  (nextChar=' ') then
          return '';
      else    
          return 'a';
      end if;
  end if;    
  return currentChar;
end; 

  -------------------------------------------------------------
  -- Retourne l'equivalent phonetic
  -- Main function 
  -- @return boolean
  -------------------------------------------------------------
function f_arabic_phonetic_aquivalent (a_name in out varchar, depth in out number) return varchar is
   toReturn varchar(256);
   nextChar char;
   currentChar char;
   nextDepth number;
BEGIN

IF depth = 1 THEN
     a_name :=  lower(a_name);
     a_name :=  f_special_chars(a_name);  
END IF;

toReturn  := f_special_names(a_name);
IF toReturn <> '' THEN 
   return toReturn;
END IF;

IF length(a_name) < depth THEN 
    return '';
END IF;

toReturn :=''; 
nextDepth := depth + 1;

currentChar := substr(a_name,depth,1);
IF currentChar = ' ' THEN
    return  ' '||f_arabic_phonetic_aquivalent (a_name , nextDepth);
END IF;
IF length(a_name) > depth THEN
     nextChar := substr(a_name,nextDepth,1);      
ELSE
     nextChar := ' ';
END IF;

IF nextChar = currentChar THEN 
    return  f_arabic_phonetic_aquivalent (a_name , nextDepth);
END IF;

IF f_is_consonent(substr(a_name,depth,1)) THEN    
    IF substr(a_name,depth,1) = 'h' THEN 
         IF depth > 1 AND f_is_consonent(substr(a_name,depth-1,1)) THEN
              return  f_arabic_phonetic_aquivalent (a_name , nextDepth);
         END IF;
     END IF;
     return concat(f_consonant_equivalent(substr(a_name,depth,1),nextChar) , f_arabic_phonetic_aquivalent (a_name ,nextDepth));
END IF;

IF f_is_vowel(substr(a_name,depth,1)) THEN
       return concat(f_vowel_equivalent(substr(a_name,depth,1),nextChar) , f_arabic_phonetic_aquivalent (a_name , nextDepth));
END IF;
return NULL;
END ; 
----------------------------------------------------------------
function f_pkg_runner (a_name in out varchar) return varchar is
toReturn varchar2(256);
depth number;
begin
   depth :=1;
   toReturn := f_arabic_phonetic_aquivalent (a_name, depth);
   toReturn := replace(toReturn,' mohd ','mouhamad');
   toReturn := replace(toReturn,' med ','mouhamad');
   if toReturn = 'mohd' or toReturn = 'med'then
      return 'mouhamad';
   end if;   
   return toReturn;
end;
----------------------------------------------------------------
function f_pkg_tester() return varchar is
sampleSurname varchar(256);

begin
      sampleSurname  := 'wassim';
      sampleSurname   := PKG_SEARCH .f_pkg_runner(sampleSurname);
      dbms_output.put_line(sampleSurname);
      return sampleSurname;
 
end;
end PKG_SEARCH ;
This will output oasim

Saturday, May 2, 2015

Prestashop minimum quantity product by customer group

I'm working on prestashop 1.5 that invalidates quantities under 10 for each product for professionals, note that profesionnals are customers linked to groups with ID 4 or 5.

the best solution i'did is:

1- Override CartController under override/controllers/front/CartController
2- Added this little snipet: (where 4 and 5 are groups ids) (target method is processChangeProductInCart then add your controle)
  $arr = $this->context->customer->getGroups();
                $minQuantity = -1;
                if(in_array(4,$arr) || in_array(5,$arr)) $minQuantity=10;
                if($qty_to_check<$minQuantity)
                   $this->errors[] = Tools::displayError('Minimum order quantity is 10 for profesionnals');

3- be careful to add this code out of an if surrount that does not cover all scenarios, in our cas i've added it just after stock controle.
4- you can remark that later in the controller there's another quantity check made by prestashop using var called minimum_quantity, i didn't altered that code, i think it'sb not quiet careful to do it, since custom code have to be clear and separated from original code to avoid unwanted results

i hop it helps

Sunday, April 19, 2015

Wordpress: posts linked to many categories at the same time

in some search cases, there a need to get posts linked to many categories at the same time,
this query is useful
       $whereS = "12,13,22";//Here searched for categories ids
        query_posts( array('category__and'=>preg_split('/[\s,]+/',$whereS)));
next you can use loop to iterate through records
if (have_posts()) : while (have_posts()) : the_post();
  /*custom code*/ 
endWhile;
Same as 'give me posts that ara linked at the same time to categories 12, 13 and 22'
that's an intersection.

Note if you want to search for posts linked to 2 or more categories ; in this case we are talking about union and not intersection, we can use simply this little code
       $whereS = "12,13,22";
        query_posts( 'cat=12,13,22');
Same as 'give me posts in the categories 12, 13 and 22'

Thursday, January 22, 2015

Generic dao class for wordpress

metaDefinition = $metaDefinition;
        $this->tableName = $tableName;
    }
    /**
     * Insert a new row
     * @global type $wpdb
     * @param type $pairs
     * @return type last inserted id
     */
    function insertRow($pairs){
            global $wpdb;          
            $prototypes = array();
             foreach ($pairs as $colName => $dataType) {
                 $prototypes[] = $this->metaDefinition[$colName];
             }
            $wpdb->insert($this->tableName, $pairs,$prototypes);
            return $wpdb->insert_id;
    }
    /**
     * Select all rows
     * @global type $wpdb
     * @param type $orderingExpr
     * @param type $limit
     * @return type
     */
    function  selectAllRows($orderingExpr=" ",$limit=""){
            global $wpdb;
            $sql = "select * from $this->tableName ".$orderingExpr.$limit;
            return $wpdb->get_results($sql);
    }
    /**
     * Query table
     * @global type $wpdb
     * @param type $where
     * @param type $orderingExpr
     * @param type $limit
     * @return type
     */
    function  selectRows($where,$orderingExpr=" ",$limit=""){
            global $wpdb;
            $sql = "select * from $this->tableName where ".$where.' '.$orderingExpr.' '.$limit;
            return $wpdb->get_results($sql);
    }
    /**
     * Select rows count
     * @global type $wpdb
     * @return type
     */
    function selectRowsCount(){
            global $wpdb;
            $sql = "select count(*)as count from $this->tableName ";
            return $wpdb->get_results($sql);
    }
    /**
     * Delete row
     * @global type $wpdb
     * @param type $pairs array of paris (columns, new values)
     * @param type $where
     * @param type $whereFormat
     * @return type
     */
    function updateRow($pairs,$where,$whereFormat=array("%d")){
            global $wpdb;
             $prototypes = array();
             foreach ($pairs as $colName => $dataType) {
                 $prototypes[] = $this->metaDefinition[$colName];
             }
             return $wpdb->update( $this->tableName, $pairs, $where, $prototypes, $whereFormat );
    }
        
    function deleteRow($where,$whereFormat=array("%d")){
            global $wpdb;
            return $wpdb->delete( $this->tableName, $where,$whereFormat);
    }
    

}
?>

An implementation of this genric class is this little code:
Supposing that we have this table :
CREATE  TABLE IF NOT EXISTS `wordpress_categ_type_poste` (
  `ctgtp_id` INT NOT NULL AUTO_INCREMENT ,
  `ctgtp_lib` VARCHAR(45) NOT NULL ,
  PRIMARY KEY (`ctgtp_id`) )
ENGINE = InnoDB;
then an extension of this class can be
'%d','ctgtp_lib'=>'%s'),$wpdb->prefix . "categ_type_poste");
    }
  }
?>
once you have finished that class, it's recommended to build your service class exposing some or all functions of the dao class.

Friday, January 9, 2015

Jquery get longitude and latitude from address using google api

A little js code to retrieve long and lat from adress using google api
    function getLtLgFromAdresse(address,apiKey){
         var lng;
         var lt;
         jQuery.ajax({
                    type: "GET",
                    url: "https://maps.googleapis.com/maps/api/geocode/xml?address="+address+"&key="+apiKey,
                    dataType: "xml",
                    success: function(xml) {      
                          if(jQuery(xml).find('status').text() == 'OK'){
                              var locationNode = jQuery(xml).find('result:first'). find('geometry').find('location'); 
                              lng = locationNode.find('lng').text();
                              lt =  locationNode.find('lat').text();
                              alert("lng: "+lng+" lat:"+lt) 
                          }    else{
                                alert('Error!');          
                          }
                       
                    }                
            });
    }
apiKey is your google key, read this for more info
https://developers.google.com/maps/documentation/geocoding/

Note that here we show only the first occurence "using the :first near to the selector result.

Friday, January 2, 2015

Primefaces responsive theme

In order to render a mobile content for primefaces framework bases web application:
just follow these 2 steps:
  1. in faces config.xmln add

      
         
           
             org.primefaces.mobile.application.MobileNavigationHandler
           
         
         
         PRIMEFACES_MOBILE
    "
  2. at the top of your xhtml or jsf or ... whatever extesion choosen for files that will be processed by jsf servlet  add this taglib:
    xmlns:pm="http://primefaces.org/mobile
    "
pm taglib offers many possibilities such as view pagination like :
 
   
 
  

to define inside the same view many pages