2017-09-12 15:22:18 +00:00
< ? php
namespace service ;
2017-09-14 17:56:24 +00:00
use \database\core\DatabaseDriver ;
2017-09-14 13:54:41 +00:00
use \service\Tesseract ;
2017-09-13 13:03:36 +00:00
2017-09-12 15:22:18 +00:00
class CalendarExtractor {
/* [ 1 ] Attributes
=========================================================*/
private $start_d = null ; // 1st day date
private $img_url = null ; // image url
2017-09-13 13:03:36 +00:00
private $d_uid = null ; // diplome uid
2017-09-12 15:22:18 +00:00
private $img_res = null ; // image resource
private $event = []; // events
/* [ 2 ] Constants
=========================================================*/
public static $start_y = 79 ; // start y
public static $stop_y = 699 ; // stop y
2017-09-16 16:03:10 +00:00
public static $start_h = 6 ; // start hour (GMT)
public static $stop_h = 18 ; // stop hour (GMT)
2017-09-12 15:22:18 +00:00
/* ( 1 ) Builds a calendar extractor
*
2017-09-13 13:03:36 +00:00
* @ d_uid < String > UID of the diplome
2017-09-12 15:22:18 +00:00
* @ img_url < String > URL of the string to extract from
* @ start_d < String > Date of the first day
*
* @ return instance < CalendarExtractor > Instance
*
---------------------------------------------------------*/
2017-09-13 13:03:36 +00:00
public function __construct ( $d_uid = null , $img_url = null , $start_d = null ){
2017-09-12 15:22:18 +00:00
/* [ 1 ] Check arguments
=========================================================*/ {
/* (1) Check type */
if ( ! is_string ( $img_url ) || ! is_string ( $start_d ) )
2017-09-13 13:03:36 +00:00
throw new \Exception ( " CalendarExtractor.__construct(<String>, <String>, <String>) expected but CalendarExtractor.__construct(< " . gettype ( $d_uid ) . " >, < " . gettype ( $img_url ) . " >, < " . gettype ( $start_d ) . " >) received " );
2017-09-12 15:22:18 +00:00
/* (2) Check @img_url link availability */
if ( ! ( $img_head =@ get_headers ( $img_url )) )
2017-09-13 13:03:36 +00:00
throw new \Exception ( " CalendarExtractor.__construct(<String>, <URL>, <String>) received but cannot reach <URL> " );
2017-09-12 15:22:18 +00:00
if ( ! preg_match ( '@HTTP.+200@' , $img_head [ 0 ]) )
2017-09-13 13:03:36 +00:00
throw new \Exception ( " CalendarExtractor.__construct(<String>, <URL>, <String>) received but cannot reach <URL> " );
2017-09-12 15:22:18 +00:00
/* (3) Check @start_d format */
2017-09-16 16:03:10 +00:00
if ( ! preg_match ( " @^ \ d { 3,}- \ d { 1,2}- \ d { 1,2} $ @ " , $start_d ) )
2017-09-13 13:03:36 +00:00
throw new \Exception ( " CalendarExtractor.__construct(<String>, <String>, <DATE>) received <DATE> has not the correct format " );
/* (4) Check @d_uid format */
if ( ! preg_match ( " @^T \ d+ $ @ " , $d_uid ) )
throw new \Exception ( " CalendarExtractor.__construct(<UID>, <String>, <String>) received <UID> has not the correct format " );
2017-09-12 15:22:18 +00:00
}
/* [ 2 ] Fetch file
=========================================================*/ {
/* (1) Try to open file with GD */
$this -> img_res = @ imagecreatefrompng ( $img_url );
/* (2) Manage error */
if ( ! $this -> img_res )
throw new \Exception ( " URL is not a JPEG image, or is unreachable " );
/* (3) Register data */
$this -> img_url = $img_url ;
2017-09-13 13:03:36 +00:00
$this -> d_uid = $d_uid ;
2017-09-12 15:22:18 +00:00
$this -> start_d = $start_d ;
}
}
/* ( 2 ) Extracts calendar data from image
*
* @ return error < bool > FALSE on error
*
---------------------------------------------------------*/
public function process (){
/* [ 1 ] Global variables
=========================================================*/ {
/* (1) Image size */ {
// {1} Request for size //
$img_siz = @ getimagesize ( $this -> img_url );
// {2} If error //
if ( ! $img_siz )
throw new \Exception ( " Cannot get image size " );
$img_siz = [
'w' => $img_siz [ 0 ],
'h' => $img_siz [ 1 ]
];
}
}
/* [ 2 ] Extract day limits
=========================================================*/ {
/* (1) Will contain the column+1 w index */
$col_ind = [];
/* (2) Extract column indexes */
for ( $x = 0 ; $x < $img_siz [ 'w' ] ; $x ++ ){
if ( $this -> getColor ( $x , 62 ) <= 10 )
$col_ind [] = $x + 1 ;
}
2017-09-12 15:53:49 +00:00
2017-09-12 15:22:18 +00:00
}
/* [ 3 ] For each day -> get events
=========================================================*/ {
2017-09-13 14:55:49 +00:00
$uid = 0 ;
2017-09-12 15:22:18 +00:00
/* (1) For each day */
2017-09-13 14:55:49 +00:00
for ( $day_n = 0 ; $day_n < count ( $col_ind ) - 1 ; $day_n ++ ){
2017-09-13 14:15:54 +00:00
$col_x = $col_ind [ $day_n ];
2017-09-12 15:22:18 +00:00
/* (2) For each y pixel -> exctract event */
for ( $y = self :: $start_y ; $y < self :: $stop_y ; $y ++ ){
/* (3) Get current color + next */
$p = $this -> getColor ( $col_x , $y );
$p1 = $this -> getColor ( $col_x , $y + 1 );
2017-09-14 17:56:24 +00:00
$color = '#' . str_pad ( dechex ( $p1 ), 6 , '0' , STR_PAD_LEFT );
2017-09-12 15:22:18 +00:00
/* (4) If on black pixel and next not white */
if ( $p == 0 && $p1 != 0xffffff ){
// {1} calculate time //
2017-09-13 14:15:54 +00:00
$start_y = $y ;
2017-09-16 16:03:10 +00:00
$time_start = $this -> yToTime ( $day_n , $start_y );
$date_start = date ( 'Ymd\THis\Z' , $time_start );
2017-09-12 15:22:18 +00:00
2017-09-13 14:55:49 +00:00
// {2} Incr uid //
$uid ++ ;
2017-09-12 15:53:49 +00:00
2017-09-12 15:22:18 +00:00
// {3} Store event start //
2017-09-16 16:03:10 +00:00
$this -> event [ $uid ][ $date_start ] = [];
2017-09-12 15:22:18 +00:00
// {4} Seek end of event //
$y ++ ;
while ( $y < self :: $stop_y && $this -> getColor ( $col_x , $y ) != 0 )
$y ++ ;
2017-09-13 14:55:49 +00:00
2017-09-12 15:22:18 +00:00
// {5} If end reached //
2017-09-16 16:03:10 +00:00
$time_stop = $this -> yToTime ( $day_n , $y );
$this -> event [ $uid ][ $date_start ] = [ date ( 'Ymd\THis\Z' , $time_stop ) ];
2017-09-12 15:22:18 +00:00
2017-09-13 14:15:54 +00:00
// {6} Exctract event's image //
2017-09-16 16:03:10 +00:00
$ev = $this -> extractEvent ( " $date_start - $uid " , [ $col_x , $start_y + 1 ], [ $col_ind [ $day_n + 1 ] - 1 , $y ]);
2017-09-14 17:56:24 +00:00
2017-09-15 18:04:04 +00:00
/* {7} Check @event if already exists */ {
2017-09-13 14:15:54 +00:00
2017-09-15 18:04:04 +00:00
$read_name = is_null ( $ev [ 'name' ]) ? '?' : $ev [ 'name' ];
2017-09-14 17:56:24 +00:00
2017-09-15 18:04:04 +00:00
$sqlr = DatabaseDriver :: getPDO () -> prepare ( " SELECT id_event FROM event WHERE basename = :bn AND color = :c AND id_diplome = :idd " );
$sqlr -> execute ([ ':bn' => $read_name , ':c' => $color , ':idd' => $this -> d_uid ]);
$fetched = $sqlr -> fetch ();
// {7.1} If not found in db -> insert //
if ( ! $fetched ){
// {7.2} Insert new event //
$sqlr = DatabaseDriver :: getPDO () -> prepare ( " INSERT INTO event(id_event,basename,color,id_diplome,name) VALUES(DEFAULT,:bn,:c,:idd,:n) " );
$sqlr -> execute ([ ':c' => $color , ':idd' => $this -> d_uid , ':bn' => $read_name , ':n' => $read_name ]);
// {7.3} Store id in current event //
$event_id = DatabaseDriver :: getPDO () -> lastInsertId ();
} else
$event_id = $fetched [ 'id_event' ];
}
/* (8) Check @location if already exists */ {
$read_location = is_null ( $ev [ 'location' ]) ? '?' : $ev [ 'location' ];
$sqlr = DatabaseDriver :: getPDO () -> prepare ( " SELECT id_location FROM location WHERE basename = :bn AND id_event = :ide " );
$sqlr -> execute ([ ':bn' => $read_location , ':ide' => $event_id ]);
$fetched = $sqlr -> fetch ();
// {8.1} If not found in db -> insert //
if ( ! $fetched ){
// {8.2} Insert new location //
$sqlr = DatabaseDriver :: getPDO () -> prepare ( " INSERT INTO location(id_location,id_event,basename,name) VALUES(DEFAULT,:ide,:bn,:n) " );
$sqlr -> execute ([ ':ide' => $event_id , ':bn' => $read_location , ':n' => $read_location ]);
// {8.3} Store id in current location //
$location_id = DatabaseDriver :: getPDO () -> lastInsertId ();
} else
$location_id = $fetched [ 'id_location' ];
2017-09-14 17:56:24 +00:00
}
2017-09-13 14:15:54 +00:00
2017-09-16 16:03:10 +00:00
/* (9) Check @course if already exists */ {
$date = [
'start' => date ( 'Y-m-d H:i:s' , $time_start ),
'stop' => date ( 'Y-m-d H:i:s' , $time_stop )
];
$sqlr = DatabaseDriver :: getPDO () -> prepare ( " SELECT c.id_course as idc FROM course as c, event as e, location as l WHERE c.id_event = e.id_event AND c.id_location = l.id_location AND e.name = :ename AND l.name = :lname AND e.id_event = :ide AND l.id_location = :idl AND c.start_date = :startd AND c.stop_date = :stopd " );
$sqlr -> execute ([ ':ename' => $read_name , ':lname' => $read_location , ':ide' => $event_id , ':idl' => $location_id , ':startd' => $date [ 'start' ], ':stopd' => $date [ 'stop' ] ]);
$fetched = $sqlr -> fetch ();
// {8.1} If not found in db -> insert //
if ( ! $fetched ){
// {8.2} Insert new location //
$sqlr = DatabaseDriver :: getPDO () -> prepare ( " INSERT INTO course(id_course,id_event,id_location,start_date,stop_date) VALUES(DEFAULT,:ide,:idl,:startd,:stopd) " );
$sqlr -> execute ([ ':ide' => $event_id , ':idl' => $location_id , ':startd' => $date [ 'start' ], ':stopd' => $date [ 'stop' ] ]);
// {8.3} Store id in current location //
$course_id = DatabaseDriver :: getPDO () -> lastInsertId ();
} else
$course_id = $fetched [ 'idc' ];
}
2017-09-15 18:04:04 +00:00
/* (9) Store local data for ics */
2017-09-16 16:03:10 +00:00
$this -> event [ $uid ][ $date_start ][ 1 ] = $location_id ;
2017-09-15 18:04:04 +00:00
2017-09-12 15:22:18 +00:00
}
}
}
}
2017-09-13 13:03:36 +00:00
2017-09-12 15:22:18 +00:00
}
2017-09-13 15:24:07 +00:00
/* ( 3 ) Exctracts an event using OCR
2017-09-13 14:15:54 +00:00
*
2017-09-13 14:55:49 +00:00
* @ uid < String > Image uid
2017-09-13 14:15:54 +00:00
* @ start < Array > Start rect ( x , y )
* @ stop < Array > Stop rect ( x , y )
*
---------------------------------------------------------*/
2017-09-13 14:55:49 +00:00
public function extractEvent ( $uid , $start = null , $stop = null ){
2017-09-13 15:24:07 +00:00
$link = __ROOT__ . " /tmp/ $uid .jpeg " ;
2017-09-13 16:40:15 +00:00
$width = $stop [ 0 ] - $start [ 0 ];
$height = $stop [ 1 ] - $start [ 1 ];
2017-09-14 13:54:41 +00:00
$resize_factor = 2 ;
2017-09-13 15:24:07 +00:00
2017-09-13 14:55:49 +00:00
/* [ 1 ] Get the right clip
2017-09-13 15:24:07 +00:00
=========================================================*/ {
2017-09-14 13:54:41 +00:00
2017-09-13 15:24:07 +00:00
/* (1) Create clipped copy */
2017-09-14 13:54:41 +00:00
$clip = \imagecreatetruecolor ( $width * $resize_factor , $height * $resize_factor );
2017-09-13 15:24:07 +00:00
$copied = \imagecopyresized (
2017-09-14 13:54:41 +00:00
$clip , // destin img
$this -> img_res , // source img
0 , // dest x
0 , // dest y
$start [ 0 ], // src x
$start [ 1 ], // src y
$width * $resize_factor , // dest w
$height * $resize_factor , // dest h
$width , // src w
$height // src h
2017-09-13 15:24:07 +00:00
);
/* (2) Manage copy error */
if ( ! $copied )
2017-09-15 18:04:04 +00:00
return [ 'name' => null , 'location' => null ];
2017-09-13 15:24:07 +00:00
2017-09-14 13:54:41 +00:00
/* (3) Save to jpeg */
2017-09-13 15:24:07 +00:00
\imagesavealpha ( $clip , true );
2017-09-14 13:54:41 +00:00
// ob_start();
\imagejpeg ( $clip , $link );
// $image_data = \base64_encode(ob_get_contents());
// ob_end_clean();
}
/* [ 2 ] Apply Tesseract
=========================================================*/ {
/* (1) Load image with tesseract */
try {
$tesseract = new Tesseract ( $link );
$read = $tesseract -> read ();
/* (2) Manage error */
} catch ( \Exception $e ){
2017-09-15 21:35:34 +00:00
// Remove file //
unlink ( $link );
2017-09-15 18:04:04 +00:00
return [ 'name' => null , 'location' => null ];
2017-09-14 13:54:41 +00:00
}
2017-09-13 15:24:07 +00:00
}
2017-09-13 14:55:49 +00:00
2017-09-13 16:40:15 +00:00
2017-09-14 13:54:41 +00:00
/* [ 3 ] End procedure
=========================================================*/
2017-09-15 21:35:34 +00:00
/* (1) Remove file */
unlink ( $link );
/* (2) Return read value */
2017-09-14 13:54:41 +00:00
return $read ;
2017-09-13 14:15:54 +00:00
}
2017-09-12 15:22:18 +00:00
/* ( 3 ) Get a pixel ' s color
*
* @ x < int > X coordinate
* @ y < int > Y coordinate
*
* @ return color < u_int32 > Raw color
*
---------------------------------------------------------*/
private function getColor ( $x , $y ){
return imagecolorat ( $this -> img_res , $x , $y );
}
/* ( 4 ) Get time from a y - coordinate
*
* @ day_n < int > Day relative index
* @ y < int > y Coordinate
*
2017-09-16 16:03:10 +00:00
* @ return time < int > GMT timestamp
2017-09-12 15:22:18 +00:00
*
---------------------------------------------------------*/
private function yToTime ( $day_n , $y ){
/* [ 1 ] Get the date
=========================================================*/ {
/* (1) Get the day's date */
$day_ts = strtotime ( $this -> start_d . " + $day_n days " );
/* (2) Format it */
2017-09-12 15:53:49 +00:00
$day = date ( 'Ymd' , $day_ts );
2017-09-12 15:22:18 +00:00
}
/* [ 2 ] Get the time
=========================================================*/ {
/* (1) Get the time */
$time = self :: $start_h + ( self :: $stop_h - self :: $start_h ) * ( $y - self :: $start_y ) / ( self :: $stop_y - self :: $start_y );
/* (2) Calculate hour form time */
$hour = floor ( $time );
/* (3) Calculate min from time */
$min = round ( 60 * ( $time - $hour ) );
/* (4) Round minutes to 10min */
2017-09-12 15:53:49 +00:00
$min = round ( $min / 10 ) * 10 ;
2017-09-12 15:22:18 +00:00
/* (5) Format to 2-digit */
$hour = ( $hour < 10 ) ? " 0 $hour " : $hour ;
$min = ( $min < 10 ) ? " 0 $min " : $min ;
}
2017-09-12 19:57:08 +00:00
2017-09-12 20:15:51 +00:00
/* [ 3 ] Convert to GMT ( UTC + 0 )
2017-09-12 19:57:08 +00:00
=========================================================*/
2017-09-12 20:30:30 +00:00
/* (1) Set fixed timezone offset */
2017-09-16 16:03:10 +00:00
// $tz_offset = +2;
2017-09-12 19:57:08 +00:00
2017-09-12 20:30:30 +00:00
/* (2) Get GMT (UTC+0) timestamp */
2017-09-16 16:03:10 +00:00
$ts = strtotime ( " ${ day } $hour : $min :00 " );
2017-09-12 20:30:30 +00:00
/* (3) Return GMT date */
2017-09-16 16:03:10 +00:00
return $ts ;
2017-09-12 15:22:18 +00:00
}
/* ( 5 ) Generate ICS output
*
* @ return ics < String > ICS Representation of the events
*
---------------------------------------------------------*/
2017-09-12 16:09:51 +00:00
public function toIcs (){
$RAW = " " ;
2017-09-13 13:03:36 +00:00
2017-09-14 17:56:24 +00:00
/* [ 1 ] For each event
2017-09-12 16:09:51 +00:00
=========================================================*/
2017-09-13 13:03:36 +00:00
foreach ( $this -> event as $event_col => $events ){
2017-09-12 16:09:51 +00:00
/* ( 2 ) For each event of each type
---------------------------------------------------------*/
2017-09-13 16:40:15 +00:00
foreach ( $events as $start_t => $data ){
2017-09-14 17:56:24 +00:00
2017-09-15 18:04:04 +00:00
/* (1) If a name -> Search if there is a correction in the database */
2017-09-15 18:19:45 +00:00
$sqlr = DatabaseDriver :: getPDO () -> prepare ( " SELECT e.id_event as ide, l.id_location as idl, l.id_location as idl, e.name as ename, l.name as lname FROM event as e, location as l WHERE e.id_event = l.id_event AND l.id_location= :idl " );
2017-09-15 18:04:04 +00:00
$sqlr -> execute ([ ':idl' => $data [ 1 ] ]);
/* (2) Default values */
$ide = - 1 ;
$idl = - 1 ;
$name = '?' ;
$location = '?' ;
/* (2) If a match found -> set it */
if ( ( $fetched = $sqlr -> fetch ()) ){
$ide = $fetched [ 'ide' ];
$idl = $fetched [ 'idl' ];
$name = $fetched [ 'ename' ];
$location = $fetched [ 'lname' ];
}
2017-09-14 17:56:24 +00:00
/* (3) Build ICS event */
2017-09-12 16:09:51 +00:00
$RAW .= " BEGIN:VEVENT \n " ;
2017-09-12 21:12:21 +00:00
$RAW .= " DTSTAMP: " . gmdate ( " Ymd \T His \ Z " , time ()) . " \n " ; // required
$RAW .= " DTSTART: ${ start_t } \n " ;
2017-09-13 16:40:15 +00:00
$RAW .= " DTEND: ${ data[0] } \n " ;
2017-09-14 17:56:24 +00:00
$RAW .= " UID: $start_t\n " ; // required
2017-09-15 18:04:04 +00:00
$RAW .= " DESCRIPTION:event@ $ide\n " ; // event@id_event
$RAW .= " SUMMARY: $name\n " ;
$RAW .= " DESCRIPTION:location@ $idl\n " ; // location@id_location
$RAW .= " LOCATION: $location\n " ;
2017-09-12 21:28:10 +00:00
$RAW .= " CATEGORIES: UPPA Calendar \n " ;
$RAW .= " END:VEVENT \n " ;
2017-09-12 16:09:51 +00:00
}
}
return $RAW ;
}
2017-09-12 15:22:18 +00:00
}