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

Last change on this file since 6107 was 6107, checked in by grum, 10 years ago

Plugin is now in a usable state (color analysis is not yet tuned)

  • Property svn:executable set to *
File size: 21.4 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=$this->getColorTableWithStat();
224
225
226    $datas=Array(
227      //'themeconf' => Array('name' => $template->get_themeconf('name')),
228      'colorTable' => $this->htmlColorTable(
229                        $colorTable,
230                        ($this->config['analyze_colorTable']=='small')?19:10,
231                        "",
232                        "color0px"
233                      ),
234      'urlRequest' => $this->getAdminLink(),
235      'config_GetListColors_OrderType' => $this->config['display_stat_orderType'],
236    );
237    $template->assign('datas', $datas);
238    $template->assign_var_from_handle('CSTAT_BODY_PAGE', 'body_page');
239  } //displayStatPage
240
241
242  /**
243   * display search page
244   */
245  protected function displaySearchPage()
246  {
247    global $template, $lang;
248
249    $template->set_filename('body_page',
250                dirname($this->getFileLocation()).'/admin/cstat_search.tpl');
251
252    $datas=Array(
253      'dialogBox' => $this->dialogBoxColor(),
254      'themeName' => $template->get_themeconf('name'),
255    );
256
257    $template->assign('datas', $datas);
258
259    $template->assign_var_from_handle('CSTAT_BODY_PAGE', 'body_page');
260  } //displaySearchPage
261
262
263  /**
264   * manage display of config page & save config
265   */
266  protected function displayConfigPage()
267  {
268    $tmpPct=$this->config['stat_minPct'];
269
270    if(!$this->adviser_abort())
271    {
272      if(isset($_POST['submit_save_config']))
273      {
274        foreach($this->config as $key => $val)
275        {
276          if(isset($_REQUEST['f_'.$key]))
277          {
278            $this->config[$key] = $_REQUEST['f_'.$key];
279          }
280        }
281        $this->displayResult(l10n('cstat_save_config'), $this->saveConfig());
282      }
283    }
284
285    if($tmpPct!=$this->config['stat_minPct'])
286    {
287      $this->updateDatabaseConsolidation();
288    }
289
290    $this->displayConfig();
291  }
292
293  /**
294   * display config page
295   */
296  protected function displayConfig()
297  {
298    global $template, $lang;
299
300    $template->set_filename('body_page',
301                dirname($this->getFileLocation()).'/admin/cstat_config.tpl');
302
303    $datas=Array(
304      'minPct' => $this->config['stat_minPct'],
305      'showColors' => $this->config['display_gallery_showColors']
306    );
307
308    $template->assign('datas', $datas);
309
310    $template->assign_var_from_handle('CSTAT_BODY_PAGE', 'body_page');
311  } //displayConfig
312
313
314  /**
315   * manage adviser profile
316   * return true if user is adviser
317   */
318  protected function adviser_abort()
319  {
320    if(is_adviser())
321    {
322      $this->displayResult(l10n("cstat_adviser_not_allowed"), false);
323      return(true);
324    }
325    return(false);
326  }
327
328
329  /* ---------------------------------------------------------------------------
330    function to manage database manipulation
331  --------------------------------------------------------------------------- */
332  protected function analyzeImageFile($fileName, $imageId, $colorTable)
333  {
334    // set the picture to the 'try to analyze' statut
335    $sql="UPDATE ".$this->tables['images']." SET analyzed='t'
336          WHERE image_id=".$imageId.";";
337    pwg_query($sql);
338
339    $colors=ColorStat::getFileColors(
340      $fileName,
341      $colorTable,
342      Array(
343        'quality' => 8,
344        'numColors' => 16,
345        'maxTime' => $this->config['analyze_maxTime'],
346        'pps' => $this->config['analyze_pps'],
347      )
348    );
349
350    if($colors!==false and
351       ColorStat::$fileColorsStat['colors']>0 and
352       ColorStat::$fileColorsStat['analyzed']>0)
353    {
354      $sql="UPDATE ".$this->tables['images']."
355              SET analyzed='y',
356                  num_colors='".ColorStat::$fileColorsStat['colors']."',
357                  num_pixels='".ColorStat::$fileColorsStat['pixels']."',
358                  analyzed_pixels='".ColorStat::$fileColorsStat['analyzed']."',
359                  pps='".ColorStat::$fileColorsStat['pps']."',
360                  time='".ColorStat::$fileColorsStat['time']."',
361                  quality='".ColorStat::$fileColorsStat['quality']."'
362            WHERE image_id=".$imageId.";";
363      pwg_query($sql);
364      $sql="";
365      foreach($colors as $key=>$val)
366      {
367        /*
368         * $key => RGB color #RRGGBB
369         * $val => Array (
370         *            'hsv' => Array ('H', 'S', 'V')
371         *            'num' => integer
372         *            'pct' => float
373         *          )
374         */
375
376        $sql.=(($sql=="")?"":", ")."
377              ('".$imageId."', '".$key."', ".$val['pct'].", ".$val['num'].")";
378      }
379      $sql="REPLACE INTO ".$this->tables['images_colors']."
380                VALUES ".$sql;
381      pwg_query($sql);
382
383      return($imageId.'='.ColorStat::$fileColorsStat['colors'].';');
384    }
385    else
386    {
387      return($imageId.'=KO;');
388    }
389  }
390
391
392  protected function getGeneralStats()
393  {
394    $returned=Array(
395      'nbImages' => 0,
396      'totalTime' => 0,
397      'pixelsAnalyzedMax' => 0,
398      'pixelsAnalyzedMin' => 0,
399      'pixelsAnalyzedAvg' => 0,
400      'pixelsAnalyzedSum' => 0,
401      'totalPixels' => 0,
402      'ppsMax' => 0,
403      'ppsMin' => 0,
404      'ppsAvg' => 0,
405      'qualityMax' => 0,
406      'qualityMin' => 0,
407      'qualityAvg' => 0,
408    );
409    $sql="SELECT COUNT(image_id) AS nbImages,
410                 SUM(time) AS totalTime,
411                 MAX(analyzed_pixels) AS pixelsAnalyzedMax,
412                 MIN(analyzed_pixels) AS pixelsAnalyzedMin,
413                 AVG(analyzed_pixels) AS pixelsAnalyzedAvg,
414                 SUM(analyzed_pixels) AS pixelsAnalyzedSum,
415                 SUM(num_pixels) AS totalPixels,
416                 MAX(pps) AS ppsMax,
417                 MIN(pps) AS ppsMin,
418                 AVG(pps) AS ppsAvg,
419                 MAX(quality) AS qualityMax,
420                 MIN(quality) AS qualityMin,
421                 AVG(quality) AS qualityAvg
422          FROM ".$this->tables['images']."
423          WHERE analyzed='y';";
424    $result=pwg_query($sql);
425    if($result)
426    {
427      while($row=pwg_db_fetch_assoc($result))
428      {
429        $returned=$row;
430      }
431    }
432    return($returned);
433  }
434
435
436  /**
437   * make consolidation for the color_table :
438   *  - count number of images using a color
439   *  - count number of pixels of a color
440   */
441  protected function updateDatabaseConsolidation()
442  {
443    $sql="UPDATE ".$this->tables['color_table']." cct
444          SET cct.num_images=0,
445              cct.num_pixels=0;";
446    pwg_query($sql);
447
448    $sql="UPDATE ".$this->tables['color_table']." cct,
449                 (SELECT color_id,
450                         count(image_id) AS num_images,
451                         sum(num_pixels) AS num_pixels
452                  FROM ".$this->tables['images_colors']."
453                  WHERE pct >= ".$this->config['stat_minPct']."
454                  GROUP BY color_id) cic
455          SET cct.num_images=cic.num_images,
456              cct.num_pixels=cic.num_pixels
457          WHERE cct.color_id=cic.color_id;";
458    pwg_query($sql);
459
460    $sql="UPDATE ".$this->tables['images']." pci
461          SET pci.colors = '',
462              pci.colors_pct = ''";
463    pwg_query($sql);
464
465    $sql="UPDATE ".$this->tables['images']." pci,
466          (SELECT image_id,
467                  GROUP_CONCAT(color_id ORDER BY pct DESC SEPARATOR ',') AS colors,
468                  GROUP_CONCAT(pct ORDER BY pct DESC SEPARATOR ',') AS colors_pct
469           FROM ".$this->tables['images_colors']."
470           WHERE pct >= ".$this->config['stat_minPct']."
471           GROUP BY image_id) pcic
472          SET pci.colors = pcic.colors,
473              pci.colors_pct = pcic.colors_pct
474          WHERE pci.image_id = pcic.image_id;";
475    pwg_query($sql);
476  }
477
478  /* ---------------------------------------------------------------------------
479    ajax functions
480  --------------------------------------------------------------------------- */
481
482  /**
483   * returns a list of formated string, separated with a semi-colon :
484   *  - number of current analyzed pictures
485   *  - number of pictures not analyzed + number of picture in error
486   *
487   * @return String
488   */
489  private function ajax_cstat_updateDatabaseGetStatus()
490  {
491    $numOfPictures=0;
492    $numOfPicturesNotAnalyzed=0;
493    $numOfPicturesInError=0;
494
495    $sql="SELECT COUNT(image_id), analyzed FROM ".$this->tables['images']."
496            GROUP BY analyzed;";
497    $result=pwg_query($sql);
498    if($result)
499    {
500      while($row=pwg_db_fetch_row($result))
501      {
502        switch($row[1])
503        {
504          case 'n': //no
505            $numOfPicturesNotAnalyzed=$row[0];
506            break;
507          case 'y': //yes
508            $numOfPictures=$row[0];
509            break;
510          case 't': //tried to be analyzed, but not finished
511            $numOfPicturesInError=$row[0];
512            break;
513        }
514      }
515    }
516
517    return(sprintf(l10n("cstat_numberOfAnalyzedPictures"), $numOfPictures).";".
518           sprintf(l10n("cstat_numberOfNotAnalyzedPictures"), $numOfPicturesNotAnalyzed).";".
519           sprintf(l10n("cstat_numberOfPicturesInError"), $numOfPicturesInError));
520  }
521
522
523  /**
524   * return a list of picture Id
525   *
526   * picture id are separated with a space " "
527   * picture id are grouped in blocks of 'NumberOfItemsPerRequest' items and
528   * are separated with a semi-colon ";"
529   *
530   * client side just have to split blocks, and transmit it to the server
531   *
532   * There is four mode to determine the pictures being analyzed :
533   *  - "all"           : analyze all the images (add & replace)
534   *  - "notAnalyzed"   : analyze only the images not yet analyzed (add)
535   *  - "caddieAdd"     : analyze all the images of the caddie (add)
536   *  - "caddieReplace" : analyze all the images of the caddie (add & replace)
537   *
538   * @param String $mode
539   * @param Integer $nbOfItems : number of items per request
540   * @return String : list of image id to be analyzed, separated with a space
541   *                      "23 78 4523 5670"
542   */
543  private function ajax_cstat_updateDatabaseGetList($mode, $nbOfItems)
544  {
545    global $user;
546
547    $returned="";
548
549    $sql="SELECT cit.image_id FROM ".$this->tables['images']." cit";
550    if($mode=="notAnalyzed")
551    {
552      $sql.=" WHERE cit.analyzed='n'";
553    }
554    elseif($mode=="caddieAdd" or $mode=="caddieReplace")
555    {
556
557      $sql.=" LEFT JOIN ".CADDIE_TABLE." ct ON cit.image_id = ct.element_id
558            WHERE ct.user_id = ".$user['id']." ";
559      if($mode=="caddieAdd") $sql.=" AND cit.analyzed='n'";
560    }
561
562    if($mode=="all" or $mode=="caddieReplace")
563    {
564      pwg_query("UPDATE ".$this->tables['images']."
565                  SET analyzed='n',
566                      num_colors=0,
567                      num_pixels=0,
568                      analyzed_pixels=0,
569                      pps=0,
570                      time=0,
571                      quality=0
572                  WHERE analyzed<>'t'");
573      pwg_query("DELETE FROM ".$this->tables['images_colors']);
574      pwg_query("UPDATE ".$this->tables['color_table']."
575                 SET num_pixels=0, num_images=0;");
576    }
577
578    $result=pwg_query($sql);
579    if($result)
580    {
581      $i=0;
582      while($row=pwg_db_fetch_row($result))
583      {
584        $returned.=$row[0];
585        $i++;
586        if($i>=$nbOfItems)
587        {
588          $returned.=";";
589          $i=0;
590        }
591        else
592        {
593          $returned.=" ";
594        }
595      }
596    }
597    return(trim($returned).";");
598  }
599
600
601
602  /**
603   * extract metadata from images
604   *
605   * @param String $imageList : list of image id to be analyzed, separated with
606   *                            a space
607   *                                "23 78 4523 5670"
608   * @return String : list of the analyzed pictures, with number of colors found
609   *                  for each picture
610   *                    "23=0;78=66;4523=33;5670=91;"
611   */
612  private function ajax_cstat_updateDatabaseDoAnalyze($imagesList)
613  {
614    $list=explode(" ", trim($imagesList));
615
616    $returned="";
617
618    if(count($list)>0 and trim($imagesList)!='')
619    {
620      // $path = path of piwigo's on the server filesystem
621      $path=dirname(dirname(dirname(__FILE__)));
622
623      $sql="SELECT id, path FROM ".IMAGES_TABLE." WHERE id IN (".implode(", ", $list).")";
624      $result=pwg_query($sql);
625      if($result)
626      {
627        $colorTable=ColorStat::getColorTable(
628          $this->colorTableSize[$this->config['analyze_colorTable']][0],
629          $this->colorTableSize[$this->config['analyze_colorTable']][1]
630        );
631
632        while($row=pwg_db_fetch_assoc($result))
633        {
634          /*
635           * in some case (in a combination of some pictures), when there is too
636           * much pictures to analyze in the same request, a fatal error occurs
637           * with the message : "Allowed memory size of XXXXX bytes exhausted"
638           *
639           *
640           * tracking memory leak is not easy... :-(
641           *
642           */
643          //echo "analyzing:".$row['id']."\n";
644          //$mem1=memory_get_usage();
645          //echo "memory before analyze:".$mem1."\n";
646          $returned.=$this->analyzeImageFile($path."/".$row['path'], $row['id'], $colorTable);
647          //echo $returned."\n";
648          //$mem2=memory_get_usage();
649          //echo "memory after analyze:".$mem2." (".($mem2-$mem1).")\n";
650        }
651      }
652    }
653    return($returned);
654  }
655
656
657  /**
658   * make consolidation for the color_table :
659   *  - count number of images using a color
660   *  - count number of pixels of a color
661   */
662  private function ajax_cstat_updateDatabaseConsolidation()
663  {
664    $this->updateDatabaseConsolidation();
665    return("ok");
666  }
667
668
669
670  /**
671   * return a formatted <table> (using the template "cstat_stat_show_iListColors")
672   * of used tag with, for each tag, the number and the percentage of pictures
673   * where the tag was found
674   *
675   * @param String $orderType : order for the list (by color 'color' or by number
676   *                            of pictures 'img' or by number of pixels 'pixels')
677   * @return String
678   */
679  private function ajax_cstat_showStatsGetListColors($orderType)
680  {
681    global $template;
682
683    $this->config['display_stat_orderType'] = $orderType;
684    $this->saveConfig();
685
686    $local_tpl = new Template(AMD_PATH."admin/", "");
687    $local_tpl->set_filename('body_page',
688                  dirname($this->getFileLocation()).'/admin/cstat_stat_show_iListColors.tpl');
689
690    $generalStats=$this->getGeneralStats();
691
692    $sql="SELECT color_id, num_images, num_pixels
693          FROM ".$this->tables['color_table']."
694          WHERE num_images > 0 ";
695    if($orderType=='color')
696    {
697      $sql.=" ORDER BY hue ASC, saturation ASC, value DESC";
698    }
699    elseif($orderType=='img')
700    {
701      $sql.=" ORDER BY num_images DESC, num_pixels DESC ";
702    }
703    elseif($orderType=='pixels')
704    {
705      $sql.=" ORDER BY num_pixels DESC, num_images DESC ";
706    }
707
708    $datas=Array();
709    $result=pwg_query($sql);
710    if($result)
711    {
712      while($row=pwg_db_fetch_assoc($result))
713      {
714        $row['pct_images']=sprintf('%.2f', round(100*$row['num_images']/$generalStats['nbImages'],2));
715        $row['pct_pixels']=sprintf('%.2f', round(100*$row['num_pixels']/$generalStats['pixelsAnalyzedSum'],2));
716        $datas[]=$row;
717      }
718    }
719
720    $local_tpl->assign('themeconf', Array('name' => $template->get_themeconf('name')));
721    $local_tpl->assign('datas', $datas);
722
723    return($local_tpl->parse('body_page', true));
724  }
725
726} //class
727
728
729?>
Note: See TracBrowser for help on using the repository browser.