source: extensions/ColorStat/cstat_aip.class.inc.php @ 6176

Last change on this file since 6176 was 6176, checked in by grum, 14 years ago

Improve algorythm for colors analysis + use GPCRequestBuilder interface instead of a local interface

  • Property svn:executable set to *
File size: 20.7 KB
Line 
1<?php
2/* -----------------------------------------------------------------------------
3  Plugin     : ColorStat
4  Author     : Grum
5    email    : grum@piwigo.org
6    website  : http://photos.grum.fr
7
8    << May the Little SpaceFrog be with you ! >>
9  ------------------------------------------------------------------------------
10  See main.inc.php for release information
11
12  CStat_AIP : classe to manage plugin admin pages
13
14  --------------------------------------------------------------------------- */
15
16if(!defined('PHPWG_ROOT_PATH')) die('Hacking attempt!');
17
18include_once('cstat_root.class.inc.php');
19include_once(PHPWG_PLUGINS_PATH.'GrumPluginClasses/classes/GPCTables.class.inc.php');
20include_once(PHPWG_PLUGINS_PATH.'GrumPluginClasses/classes/GPCTabSheet.class.inc.php');
21include_once(PHPWG_PLUGINS_PATH.'GrumPluginClasses/classes/GPCRequestBuilder.class.inc.php');
22include_once(PHPWG_ROOT_PATH.'admin/include/tabsheet.class.php');
23
24class CStat_AIP extends CStat_root
25{
26  protected $tabsheet;
27
28  public function __construct($prefixeTable, $filelocation)
29  {
30    parent::__construct($prefixeTable, $filelocation);
31    $this->checkRequest();
32
33    $this->loadConfig();
34    $this->initEvents();
35
36    $this->tabsheet = new tabsheet();
37    $this->tabsheet->add('database',
38                          l10n('cstat_database'),
39                          $this->getAdminLink()."&amp;f_tabsheet=database");
40    $this->tabsheet->add('stat',
41                          l10n('cstat_stat'),
42                          $this->getAdminLink()."&amp;f_tabsheet=stat");
43    $this->tabsheet->add('search',
44                          l10n('cstat_search'),
45                          $this->getAdminLink()."&amp;f_tabsheet=search");
46    $this->tabsheet->add('config',
47                          l10n('cstat_config'),
48                          $this->getAdminLink()."&amp;f_tabsheet=config");
49  }
50
51  public function __destruct()
52  {
53    unset($this->tabsheet);
54    parent::__destruct();
55  }
56
57  /*
58    initialize events call for the plugin
59  */
60  public function initEvents()
61  {
62    parent::initEvents();
63    if($_REQUEST['f_tabsheet']=='search')
64    {
65      // load request builder JS only on the search page
66      GPCRequestBuilder::loadJSandCSS();
67    }
68    add_event_handler('loc_end_page_header', array(&$this->css, 'applyCSS'));
69    GPCCss::applyGpcCss();
70  }
71
72  /*
73    display administration page
74  */
75  public function manage()
76  {
77    global $template;
78
79    $this->returnAjaxContent();
80
81    $template->set_filename('plugin_admin_content', dirname(__FILE__)."/admin/cstat_admin.tpl");
82
83
84    switch($_REQUEST['f_tabsheet'])
85    {
86      case 'database':
87        $this->displayDatabasePage();
88        break;
89      case 'stat':
90        $this->displayStatPage();
91        break;
92      case 'search':
93        $this->displaySearchPage();
94        break;
95      case 'config':
96        $this->displayConfigPage();
97        break;
98    }
99
100    $this->tabsheet->select($_REQUEST['f_tabsheet']);
101    $this->tabsheet->assign();
102    $selected_tab=$this->tabsheet->get_selected();
103    $template->assign($this->tabsheet->get_titlename(), "[".$selected_tab['caption']."]");
104
105    $template_plugin["CSTAT_VERSION"] = "<i>".$this->getPluginName()."</i> ".l10n('cstat_release').CSTAT_VERSION;
106
107    $template->assign('plugin', $template_plugin);
108    $template->assign_var_from_handle('ADMIN_CONTENT', 'plugin_admin_content');
109  }
110
111
112  /*
113    return ajax content
114  */
115  protected function returnAjaxContent()
116  {
117    global $ajax, $template;
118
119    if(isset($_REQUEST['ajaxfct']))
120    {
121      //$this->debug("AJAXFCT:".$_REQUEST['ajaxfct']);
122      $result="<p class='errors'>An error has occured</p>";
123      switch($_REQUEST['ajaxfct'])
124      {
125        case 'updateDatabaseGetStatus':
126          $result=$this->ajax_cstat_updateDatabaseGetStatus();
127          break;
128        case 'updateDatabaseGetList':
129          $result=$this->ajax_cstat_updateDatabaseGetList($_REQUEST['selectMode'], $_REQUEST['numOfItems']);
130          break;
131        case 'updateDatabaseDoAnalyze':
132          $result=$this->ajax_cstat_updateDatabaseDoAnalyze($_REQUEST['imagesList']);
133          break;
134        case 'updateDatabaseConsolidation':
135          $result=$this->ajax_cstat_updateDatabaseConsolidation();
136          break;
137        case 'showStatsGetListColors':
138          $result=$this->ajax_cstat_showStatsGetListColors($_REQUEST['orderType']);
139          break;
140      }
141      GPCAjax::returnResult($result);
142    }
143  }
144
145  /**
146   * check the $_REQUEST values and set default values
147   *
148   */
149  protected function checkRequest()
150  {
151    if(!isset($_REQUEST['f_tabsheet'])) $_REQUEST['f_tabsheet']='stat';
152
153
154    if(!($_REQUEST['f_tabsheet']=='database' or
155         $_REQUEST['f_tabsheet']=='stat' or
156         $_REQUEST['f_tabsheet']=='search' or
157         $_REQUEST['f_tabsheet']=='config')) $_REQUEST['f_tabsheet']='stat';
158
159
160    if(isset($_REQUEST['ajaxfct']))
161    {
162      if($_REQUEST['ajaxfct']=='updateDatabaseGetList')
163      {
164        if(!isset($_REQUEST['selectMode'])) $_REQUEST['selectMode']='caddieAdd';
165        if(!isset($_REQUEST['numOfItems'])) $_REQUEST['numOfItems']=$this->config['analyze_itemPerRequest'];
166
167        if(!($_REQUEST['selectMode']=='notAnalyzed' or
168             $_REQUEST['selectMode']=='all' or
169             $_REQUEST['selectMode']=='caddieAdd' or
170             $_REQUEST['selectMode']=='caddieReplace')) $_REQUEST['selectMode']='caddieAdd';
171
172        if($_REQUEST['numOfItems'] <=0 or $_REQUEST['numOfItems']>100) $_REQUEST['numOfItems']=10;
173      }
174
175
176      if($_REQUEST['ajaxfct']=='updateDatabaseDoAnalyze')
177      {
178        if(!isset($_REQUEST['imagesList'])) $_REQUEST['imagesList']='';
179      }
180
181      if($_REQUEST['ajaxfct']=='showStatsGetListColors')
182      {
183        if(!isset($_REQUEST['orderType'])) $_REQUEST['orderType']='img';
184
185        if(!($_REQUEST['orderType']=='color' or
186             $_REQUEST['orderType']=='img' or
187             $_REQUEST['orderType']=='pixels')) $_REQUEST['orderType']=='img';
188      }
189
190    }
191
192
193  }
194
195  /**
196   * display the database page
197   */
198  protected function displayDatabasePage()
199  {
200    global $template;
201
202    $template->set_filename('body_page',
203                dirname($this->getFileLocation()).'/admin/cstat_database.tpl');
204
205    $datas=Array(
206      'urlRequest' => $this->getAdminLink(),
207      'numberOfItemsPerRequest' => $this->config['analyze_itemPerRequest']
208    );
209    $template->assign('datas', $datas);
210    $template->assign_var_from_handle('CSTAT_BODY_PAGE', 'body_page');
211  } //displayDatabasePage
212
213  /**
214   * display the stat page
215   */
216  protected function displayStatPage()
217  {
218    global $template;
219
220    $template->set_filename('body_page',
221                dirname($this->getFileLocation()).'/admin/cstat_stat.tpl');
222
223    $colorTable=CStat_functions::getColorTableWithStat();
224
225
226    $datas=Array(
227      //'themeconf' => Array('name' => $template->get_themeconf('name')),
228      'colorTable' => CStat_functions::htmlColorTable(
229                        $colorTable,
230                        ($this->config['analyze_colorTable']=='small')?19:10,
231                        "",
232                        "color0px",
233                        "<br>"
234                      ),
235      'urlRequest' => $this->getAdminLink(),
236      'config_GetListColors_OrderType' => $this->config['display_stat_orderType'],
237    );
238    $template->assign('datas', $datas);
239    $template->assign_var_from_handle('CSTAT_BODY_PAGE', 'body_page');
240  } //displayStatPage
241
242
243  /**
244   * display search page
245   */
246  protected function displaySearchPage()
247  {
248    global $template, $lang;
249
250    $template->set_filename('body_page',
251                dirname($this->getFileLocation()).'/admin/cstat_search.tpl');
252
253    $template->assign('cstat_search_page', GPCRequestBuilder::displaySearchPage());
254
255    $template->assign_var_from_handle('CSTAT_BODY_PAGE', 'body_page');
256  } //displaySearchPage
257
258
259  /**
260   * manage display of config page & save config
261   */
262  protected function displayConfigPage()
263  {
264    $tmpPct=$this->config['stat_minPct'];
265
266    if(!$this->adviser_abort())
267    {
268      if(isset($_POST['submit_save_config']))
269      {
270        foreach($this->config as $key => $val)
271        {
272          if(isset($_REQUEST['f_'.$key]))
273          {
274            $this->config[$key] = $_REQUEST['f_'.$key];
275          }
276        }
277        $this->displayResult(l10n('cstat_save_config'), $this->saveConfig());
278      }
279    }
280
281    if($tmpPct!=$this->config['stat_minPct'])
282    {
283      $this->updateDatabaseConsolidation();
284    }
285
286    $this->displayConfig();
287  }
288
289  /**
290   * display config page
291   */
292  protected function displayConfig()
293  {
294    global $template, $lang;
295
296    $configTabs=new GPCTabSheet('configTabsheet', $this->tabsheet->get_titlename(), 'tabsheet2 gcBorder', 'itab2');
297    $configTabs->add('database',
298                      l10n('cstat_database'),
299                      '', true, "displayConfig('database');");
300    $configTabs->add('statsearch',
301                      l10n('cstat_stat_and_search'),
302                      '', false, "displayConfig('statsearch');");
303    $configTabs->add('display',
304                      l10n('cstat_gallery_integration'),
305                      '', false, "displayConfig('display');");
306    $configTabs->assign();
307
308    $template->set_filename('body_page',
309                dirname($this->getFileLocation()).'/admin/cstat_config.tpl');
310
311    $datas=Array(
312      'minPct' => $this->config['stat_minPct'],
313      'showColors' => $this->config['display_gallery_showColors']
314    );
315
316    $template->assign('datas', $datas);
317
318    $template->assign_var_from_handle('CSTAT_BODY_PAGE', 'body_page');
319  } //displayConfig
320
321
322  /**
323   * manage adviser profile
324   * return true if user is adviser
325   */
326  protected function adviser_abort()
327  {
328    if(is_adviser())
329    {
330      $this->displayResult(l10n("cstat_adviser_not_allowed"), false);
331      return(true);
332    }
333    return(false);
334  }
335
336
337  /* ---------------------------------------------------------------------------
338    function to manage database manipulation
339  --------------------------------------------------------------------------- */
340  protected function analyzeImageFile($fileName, $imageId, $colorTable)
341  {
342    // set the picture to the 'try to analyze' statut
343    $sql="UPDATE ".$this->tables['images']." SET analyzed='t'
344          WHERE image_id=".$imageId.";";
345    pwg_query($sql);
346
347    $colors=ColorStat::getFileColors(
348      $fileName,
349      $colorTable,
350      Array(
351        'quality' => 8,
352        'numColors' => 16,
353        'maxTime' => $this->config['analyze_maxTime'],
354        'pps' => $this->config['analyze_pps'],
355      )
356    );
357
358    if($colors!==false and
359       ColorStat::$fileColorsStat['colors']>0 and
360       ColorStat::$fileColorsStat['analyzed']>0)
361    {
362      $sql="UPDATE ".$this->tables['images']."
363              SET analyzed='y',
364                  num_colors='".ColorStat::$fileColorsStat['colors']."',
365                  num_pixels='".ColorStat::$fileColorsStat['pixels']."',
366                  analyzed_pixels='".ColorStat::$fileColorsStat['analyzed']."',
367                  pps='".ColorStat::$fileColorsStat['pps']."',
368                  time='".ColorStat::$fileColorsStat['time']."',
369                  quality='".ColorStat::$fileColorsStat['quality']."'
370            WHERE image_id=".$imageId.";";
371      pwg_query($sql);
372      $sql="";
373      foreach($colors as $key=>$val)
374      {
375        /*
376         * $key => RGB color #RRGGBB
377         * $val => Array (
378         *            'hsv' => Array ('H', 'S', 'V')
379         *            'num' => integer
380         *            'pct' => float
381         *          )
382         */
383
384        $sql.=(($sql=="")?"":", ")."
385              ('".$imageId."', '".$key."', ".$val['pct'].", ".$val['num'].")";
386      }
387      $sql="REPLACE INTO ".$this->tables['images_colors']."
388                VALUES ".$sql;
389      pwg_query($sql);
390
391      return($imageId.'='.ColorStat::$fileColorsStat['colors'].';');
392    }
393    else
394    {
395      return($imageId.'=KO;');
396    }
397  }
398
399
400  /**
401   * make consolidation for the color_table :
402   *  - count number of images using a color
403   *  - count number of pixels of a color
404   */
405  protected function updateDatabaseConsolidation()
406  {
407    $sql="UPDATE ".$this->tables['color_table']." cct
408          SET cct.num_images=0,
409              cct.num_pixels=0;";
410    pwg_query($sql);
411
412    $sql="UPDATE ".$this->tables['color_table']." cct,
413                 (SELECT color_id,
414                         count(image_id) AS num_images,
415                         sum(num_pixels) AS num_pixels
416                  FROM ".$this->tables['images_colors']."
417                  WHERE pct >= ".$this->config['stat_minPct']."
418                  GROUP BY color_id) cic
419          SET cct.num_images=cic.num_images,
420              cct.num_pixels=cic.num_pixels
421          WHERE cct.color_id=cic.color_id;";
422    pwg_query($sql);
423
424    $sql="UPDATE ".$this->tables['images']." pci
425          SET pci.colors = '',
426              pci.colors_pct = ''";
427    pwg_query($sql);
428
429    $sql="UPDATE ".$this->tables['images']." pci,
430          (SELECT image_id,
431                  GROUP_CONCAT(color_id ORDER BY pct DESC SEPARATOR ',') AS colors,
432                  GROUP_CONCAT(pct ORDER BY pct DESC SEPARATOR ',') AS colors_pct
433           FROM ".$this->tables['images_colors']."
434           WHERE pct >= ".$this->config['stat_minPct']."
435           GROUP BY image_id) pcic
436          SET pci.colors = pcic.colors,
437              pci.colors_pct = pcic.colors_pct
438          WHERE pci.image_id = pcic.image_id;";
439    pwg_query($sql);
440  }
441
442  /* ---------------------------------------------------------------------------
443    ajax functions
444  --------------------------------------------------------------------------- */
445
446  /**
447   * returns a list of formated string, separated with a semi-colon :
448   *  - number of current analyzed pictures
449   *  - number of pictures not analyzed + number of picture in error
450   *
451   * @return String
452   */
453  private function ajax_cstat_updateDatabaseGetStatus()
454  {
455    $numOfPictures=0;
456    $numOfPicturesNotAnalyzed=0;
457    $numOfPicturesInError=0;
458
459    $sql="SELECT COUNT(image_id), analyzed FROM ".$this->tables['images']."
460            GROUP BY analyzed;";
461    $result=pwg_query($sql);
462    if($result)
463    {
464      while($row=pwg_db_fetch_row($result))
465      {
466        switch($row[1])
467        {
468          case 'n': //no
469            $numOfPicturesNotAnalyzed=$row[0];
470            break;
471          case 'y': //yes
472            $numOfPictures=$row[0];
473            break;
474          case 't': //tried to be analyzed, but not finished
475            $numOfPicturesInError=$row[0];
476            break;
477        }
478      }
479    }
480
481    return(sprintf(l10n("cstat_numberOfAnalyzedPictures"), $numOfPictures).";".
482           sprintf(l10n("cstat_numberOfNotAnalyzedPictures"), $numOfPicturesNotAnalyzed).";".
483           sprintf(l10n("cstat_numberOfPicturesInError"), $numOfPicturesInError));
484  }
485
486
487  /**
488   * return a list of picture Id
489   *
490   * picture id are separated with a space " "
491   * picture id are grouped in blocks of 'NumberOfItemsPerRequest' items and
492   * are separated with a semi-colon ";"
493   *
494   * client side just have to split blocks, and transmit it to the server
495   *
496   * There is four mode to determine the pictures being analyzed :
497   *  - "all"           : analyze all the images (add & replace)
498   *  - "notAnalyzed"   : analyze only the images not yet analyzed (add)
499   *  - "caddieAdd"     : analyze all the images of the caddie (add)
500   *  - "caddieReplace" : analyze all the images of the caddie (add & replace)
501   *
502   * @param String $mode
503   * @param Integer $nbOfItems : number of items per request
504   * @return String : list of image id to be analyzed, separated with a space
505   *                      "23 78 4523 5670"
506   */
507  private function ajax_cstat_updateDatabaseGetList($mode, $nbOfItems)
508  {
509    global $user;
510
511    $returned="";
512
513    $sql="SELECT cit.image_id FROM ".$this->tables['images']." cit";
514    if($mode=="notAnalyzed")
515    {
516      $sql.=" WHERE cit.analyzed='n'";
517    }
518    elseif($mode=="caddieAdd" or $mode=="caddieReplace")
519    {
520
521      $sql.=" LEFT JOIN ".CADDIE_TABLE." ct ON cit.image_id = ct.element_id
522            WHERE ct.user_id = ".$user['id']." ";
523      if($mode=="caddieAdd") $sql.=" AND cit.analyzed='n'";
524    }
525
526    if($mode=="all" or $mode=="caddieReplace")
527    {
528      pwg_query("UPDATE ".$this->tables['images']."
529                  SET analyzed='n',
530                      num_colors=0,
531                      num_pixels=0,
532                      analyzed_pixels=0,
533                      pps=0,
534                      time=0,
535                      quality=0
536                  WHERE analyzed<>'t'");
537      pwg_query("DELETE FROM ".$this->tables['images_colors']);
538      pwg_query("UPDATE ".$this->tables['color_table']."
539                 SET num_pixels=0, num_images=0;");
540    }
541
542    $result=pwg_query($sql);
543    if($result)
544    {
545      $i=0;
546      while($row=pwg_db_fetch_row($result))
547      {
548        $returned.=$row[0];
549        $i++;
550        if($i>=$nbOfItems)
551        {
552          $returned.=";";
553          $i=0;
554        }
555        else
556        {
557          $returned.=" ";
558        }
559      }
560    }
561    return(trim($returned).";");
562  }
563
564
565
566  /**
567   * extract metadata from images
568   *
569   * @param String $imageList : list of image id to be analyzed, separated with
570   *                            a space
571   *                                "23 78 4523 5670"
572   * @return String : list of the analyzed pictures, with number of colors found
573   *                  for each picture
574   *                    "23=0;78=66;4523=33;5670=91;"
575   */
576  private function ajax_cstat_updateDatabaseDoAnalyze($imagesList)
577  {
578    $list=explode(" ", trim($imagesList));
579
580    $returned="";
581
582    if(count($list)>0 and trim($imagesList)!='')
583    {
584      // $path = path of piwigo's on the server filesystem
585      $path=dirname(dirname(dirname(__FILE__)));
586
587      $sql="SELECT id, path FROM ".IMAGES_TABLE." WHERE id IN (".implode(", ", $list).")";
588      $result=pwg_query($sql);
589      if($result)
590      {
591        $colorTable=ColorStat::getColorTable(
592          CStat_root::$colorTableSize[$this->config['analyze_colorTable']][0],
593          CStat_root::$colorTableSize[$this->config['analyze_colorTable']][1]
594        );
595
596        while($row=pwg_db_fetch_assoc($result))
597        {
598          /*
599           * in some case (in a combination of some pictures), when there is too
600           * much pictures to analyze in the same request, a fatal error occurs
601           * with the message : "Allowed memory size of XXXXX bytes exhausted"
602           *
603           *
604           * tracking memory leak is not easy... :-(
605           *
606           */
607          //echo "analyzing:".$row['id']."\n";
608          //$mem1=memory_get_usage();
609          //echo "memory before analyze:".$mem1."\n";
610          $returned.=$this->analyzeImageFile($path."/".$row['path'], $row['id'], $colorTable);
611          //echo $returned."\n";
612          //$mem2=memory_get_usage();
613          //echo "memory after analyze:".$mem2." (".($mem2-$mem1).")\n";
614        }
615      }
616    }
617    return($returned);
618  }
619
620
621  /**
622   * make consolidation for the color_table :
623   *  - count number of images using a color
624   *  - count number of pixels of a color
625   */
626  private function ajax_cstat_updateDatabaseConsolidation()
627  {
628    $this->updateDatabaseConsolidation();
629    return("ok");
630  }
631
632
633
634  /**
635   * return a formatted <table> (using the template "cstat_stat_show_iListColors")
636   * of used tag with, for each tag, the number and the percentage of pictures
637   * where the tag was found
638   *
639   * @param String $orderType : order for the list (by color 'color' or by number
640   *                            of pictures 'img' or by number of pixels 'pixels')
641   * @return String
642   */
643  private function ajax_cstat_showStatsGetListColors($orderType)
644  {
645    global $template;
646
647    $this->config['display_stat_orderType'] = $orderType;
648    $this->saveConfig();
649
650    $local_tpl = new Template(AMD_PATH."admin/", "");
651    $local_tpl->set_filename('body_page',
652                  dirname($this->getFileLocation()).'/admin/cstat_stat_show_iListColors.tpl');
653
654    $generalStats=CStat_functions::getGeneralStats();
655
656    $sql="SELECT color_id, num_images, num_pixels
657          FROM ".$this->tables['color_table']."
658          WHERE num_images > 0 ";
659    if($orderType=='color')
660    {
661      $sql.=" ORDER BY hue ASC, saturation ASC, value DESC";
662    }
663    elseif($orderType=='img')
664    {
665      $sql.=" ORDER BY num_images DESC, num_pixels DESC ";
666    }
667    elseif($orderType=='pixels')
668    {
669      $sql.=" ORDER BY num_pixels DESC, num_images DESC ";
670    }
671
672    $datas=Array();
673    $result=pwg_query($sql);
674    if($result)
675    {
676      while($row=pwg_db_fetch_assoc($result))
677      {
678        $row['pct_images']=sprintf('%.2f', round(100*$row['num_images']/$generalStats['nbImages'],2));
679        $row['pct_pixels']=sprintf('%.2f', round(100*$row['num_pixels']/$generalStats['pixelsAnalyzedSum'],2));
680        $datas[]=$row;
681      }
682    }
683
684    $local_tpl->assign('themeconf', Array('name' => $template->get_themeconf('name')));
685    $local_tpl->assign('datas', $datas);
686
687    return($local_tpl->parse('body_page', true));
688  }
689
690} //class
691
692
693?>
Note: See TracBrowser for help on using the repository browser.