Files can now have mirrors. Implemented SMOMatcher service to attempt to automaticall...
authorCameron Ball <cameron@getapproved.com.au>
Thu, 27 Nov 2014 07:51:19 +0000 (15:51 +0800)
committerCameron Ball <cameron@getapproved.com.au>
Thu, 27 Nov 2014 07:51:19 +0000 (15:51 +0800)
13 files changed:
Controllers/SimfileController.php
Domain/Entities/File.php
Domain/Entities/FileBuilder.php
Domain/Entities/FileFactory.php
Domain/Entities/FileStepByStepBuilder.php
Domain/Entities/IFile.php
Domain/Entities/IFileBuilder.php
Domain/VOs/FileMirror.php [new file with mode: 0644]
Domain/VOs/IFileMirror.php [new file with mode: 0644]
Services/ISMOMatcher.php [new file with mode: 0644]
Services/SMOMatcher.php [new file with mode: 0644]
config/DI.php
config/DataMaps.php

index 28a3061..d73527d 100644 (file)
@@ -7,11 +7,13 @@ use Services\Http\IHttpResponse;
 use Services\Uploads\IUploadManager;\r
 use Services\IUserSession;\r
 use Services\IZipParser;\r
+use Services\ISMOMatcher;\r
 use DataAccess\StepMania\ISimfileRepository;\r
 use DataAccess\StepMania\IPackRepository;\r
 use DataAccess\IFileRepository;\r
 use Domain\Entities\StepMania\ISimfile;\r
-use Domain\Entities\StepMania\IPack;\r
+use Domain\Entities\IFile;\r
+use Domain\VOs\IFileMirror;\r
 \r
 class SimfileController implements IDivineController\r
 {\r
@@ -22,6 +24,7 @@ class SimfileController implements IDivineController
     private $_uploadManager;\r
     private $_userSession;\r
     private $_zipParser;\r
+    private $_smoMatcher;\r
     \r
     public function __construct(\r
         IHttpResponse $response,\r
@@ -30,7 +33,8 @@ class SimfileController implements IDivineController
         IPackRepository $packRepository,\r
         IFileRepository $fileRepository,\r
         IUserSession $userSession,\r
-        IZipParser $zipParser\r
+        IZipParser $zipParser,\r
+        ISMOMatcher $smoMatcher\r
     ) {\r
         $this->_response = $response;\r
         $this->_uploadManager = $uploadManager;\r
@@ -39,6 +43,7 @@ class SimfileController implements IDivineController
         $this->_fileRepository = $fileRepository;\r
         $this->_userSession = $userSession;\r
         $this->_zipParser = $zipParser;\r
+        $this->_smoMatcher = $smoMatcher;\r
     }\r
     \r
     public function indexAction() {\r
@@ -67,11 +72,21 @@ class SimfileController implements IDivineController
                 $packSimfiles[] = $this->simfileToArray($simfile);\r
             }\r
 \r
+            $packMirrors = array();\r
+            if($pack->getFile()->getMirrors())\r
+            {\r
+                foreach($pack->getFile()->getMirrors() as $mirror)\r
+                {\r
+                    $packMirrors = $mirror->getUri();\r
+                }\r
+            }\r
+            \r
             $packArray[] = array(\r
                 'title'=> $pack->getTitle(),\r
                 'contributors' => $pack->getContributors(),\r
                 'simfiles' => $packSimfiles,\r
-                'banner' => $pack->getBanner() ? 'files/banner/' . $pack->getBanner()->getHash() : 'files/banner/default'\r
+                'banner' => $pack->getBanner() ? 'files/banner/' . $pack->getBanner()->getHash() : 'files/banner/default',\r
+                'mirrors' => $packMirrors\r
             );\r
         }\r
         \r
@@ -93,8 +108,9 @@ class SimfileController implements IDivineController
         {\r
             $zipParser = $this->_zipParser;\r
             $zipParser->parse($file);\r
-            \r
+                        \r
             //save the actual zip in the db\r
+            $this->findAndAddSmoMirror($file);\r
             $this->_fileRepository->save($file);  \r
             \r
             if($zipParser->isPack())\r
@@ -118,6 +134,18 @@ class SimfileController implements IDivineController
         }\r
     }\r
     \r
+    private function findAndAddSmoMirror(IFile $file)\r
+    {\r
+        $basename = pathinfo($file->getFilename(), PATHINFO_FILENAME);\r
+        $match = $this->_smoMatcher->match($basename, $file->getSize());\r
+        \r
+        //XXX: Direct instantiation of FileMirror bad?\r
+        if($match && $match['confidence'] > 90)\r
+        {\r
+            $file->addMirror(new \Domain\VOs\FileMirror($match['href']));\r
+        }\r
+    }\r
+    \r
     private function simfileToArray(ISimfile $simfile)\r
     {\r
         $singleSteps = array();\r
index 3b70b35..8f6e1a3 100644 (file)
@@ -3,6 +3,7 @@
 namespace Domain\Entities;
 
 use Domain\Entities\AbstractEntity;
+use Domain\VOs\IFileMirror;
 
 class File extends AbstractEntity implements IFile
 {    
@@ -12,6 +13,7 @@ class File extends AbstractEntity implements IFile
     private $_mimetype;
     private $_size;
     private $_uploadDate;
+    private $_mirrors;
     
     public function __construct(
         $hash,
@@ -19,7 +21,8 @@ class File extends AbstractEntity implements IFile
         $filename,
         $mimetype,
         $size,
-        $uploadDate
+        $uploadDate,
+        array $mirrors = null
     ) {
         $this->_hash = $hash;
         $this->_path = $path;
@@ -27,6 +30,17 @@ class File extends AbstractEntity implements IFile
         $this->_mimetype = $mimetype;
         $this->_size = $size;
         $this->_uploadDate = $uploadDate;
+        
+        if($mirrors)
+        {
+            foreach($mirrors as $mirror) {
+                if(!$mirror instanceof IFileMirror) {
+                    throw new InvalidStepChartException(sprintf('Invalid FileMirror array. All array elements must be an instance of IFileMirror.'));
+                }
+            }
+
+            $this->_mirrors = $mirrors;
+        }
     }
     
     public function getFilename() 
@@ -58,4 +72,14 @@ class File extends AbstractEntity implements IFile
     {
         return $this->_uploadDate;
     }
+    
+    public function getMirrors()
+    {
+        return $this->_mirrors;
+    }
+    
+    public function addMirror(IFileMirror $mirror)
+    {
+        $this->_mirrors[] = $mirror;
+    }
 }
\ No newline at end of file
index d67447b..407e14e 100644 (file)
@@ -13,6 +13,7 @@ class FileBuilder implements IFileBuilder
     private $_mimetype;
     private $_size;
     private $_date;
+    private $_mirrors;
     
     public function __construct(IFileFactory $fileFactory)
     {
@@ -49,6 +50,11 @@ class FileBuilder implements IFileBuilder
         $this->_date = $date;
     }
     
+    public function With_Mirrors(array $mirrors = null)
+    {
+        $this->_mirrors = $mirrors;
+    }
+    
     public function build()
     {
         return $this->_fileFactory
@@ -58,6 +64,7 @@ class FileBuilder implements IFileBuilder
                             $this->_filename,
                             $this->_mimetype,
                             $this->_size,
-                            $this->_date);
+                            $this->_date,
+                            $this->_mirrors);
     }
 }
\ No newline at end of file
index dd66810..ce9cb22 100644 (file)
@@ -12,7 +12,8 @@ interface IFileFactory
         $filename,
         $mimetype,
         $size,
-        $uploadDate
+        $uploadDate,
+        array $mirrors = null
     );
 }
 
@@ -24,15 +25,17 @@ class FileFactory implements IFileFactory
         $filename,
         $mimetype,
         $size,
-        $uploadDate
+        $uploadDate,
+        array $mirrors = null
     ) {
         return new File(
-        $hash,
-        $path,
-        $filename,
-        $mimetype,
-        $size,
-        $uploadDate
+            $hash,
+            $path,
+            $filename,
+            $mimetype,
+            $size,
+            $uploadDate,
+            $mirrors
         );
     }
 }
index 1f22a6b..a76dfd6 100644 (file)
@@ -36,6 +36,7 @@ interface IFileStepByStepBuilder_With_Size
 
 interface IFileStepByStepBuilder_With_UploadDate
 {
+    public function With_Mirrors(array $mirrors = null);
     public function build();
 }
 
@@ -100,6 +101,11 @@ class FileStepByStepBuilder_With_Size extends AbstractFileStepByStepBuilder impl
 
 class FileStepByStepBuilder_With_UploadDate extends AbstractFileStepByStepBuilder implements IFileStepByStepBuilder_With_UploadDate
 {
+    public function With_Mirrors(array $mirrors = null) {
+        $this->_fileBuilder->With_Mirrors($mirrors);
+        return new FileStepByStepBuilder_With_UploadDate($this->_fileBuilder);
+    }
+    
     public function build() {
         return $this->_fileBuilder
                     ->build();
index 4ce49c3..458b359 100644 (file)
@@ -2,6 +2,8 @@
 
 namespace Domain\Entities;
 
+use Domain\VOs\IFileMirror;
+
 interface IFile extends IDivineEntity
 {
     public function getPath();
@@ -10,4 +12,6 @@ interface IFile extends IDivineEntity
     public function getMimetype();
     public function getSize();
     public function getUploadDate();
+    public function getMirrors();
+    public function addMirror(IFileMirror $mirror);
 }
\ No newline at end of file
index 95b7ab4..b014b5f 100644 (file)
@@ -10,5 +10,6 @@ interface IFileBuilder
     public function With_Mimetype($mimetype);
     public function With_Size($size);
     public function With_UploadDate($date);
+    public function With_Mirrors(array $mirrors = null);
     public function build();
 }
\ No newline at end of file
diff --git a/Domain/VOs/FileMirror.php b/Domain/VOs/FileMirror.php
new file mode 100644 (file)
index 0000000..6e991f0
--- /dev/null
@@ -0,0 +1,21 @@
+<?php
+
+namespace Domain\VOs;
+
+use Domain\VOs\IFileMirror;
+
+class FileMirror implements IFileMirror
+{
+    private $_uri;
+    
+    public function __construct($uri)
+    {
+        $this->_uri = $uri;
+    }
+    
+    public function getUri()
+    {
+        return $this->_uri;
+    }
+}
+
diff --git a/Domain/VOs/IFileMirror.php b/Domain/VOs/IFileMirror.php
new file mode 100644 (file)
index 0000000..3892be4
--- /dev/null
@@ -0,0 +1,8 @@
+<?php
+
+namespace Domain\VOs;
+
+interface IFileMirror
+{
+    public function getUri();
+}
\ No newline at end of file
diff --git a/Services/ISMOMatcher.php b/Services/ISMOMatcher.php
new file mode 100644 (file)
index 0000000..0b00ffb
--- /dev/null
@@ -0,0 +1,8 @@
+<?php
+
+namespace Services;
+
+interface ISMOMatcher
+{
+    public function match($filename, $filesize);
+}
\ No newline at end of file
diff --git a/Services/SMOMatcher.php b/Services/SMOMatcher.php
new file mode 100644 (file)
index 0000000..20fe9bf
--- /dev/null
@@ -0,0 +1,74 @@
+<?php
+
+namespace Services;
+
+use Services\ISMOMatcher;
+use DOMDocument;
+use DOMXPath;
+use DOMNodeList;
+
+class SMOMatcher implements ISMOMatcher
+{
+    private $_records;
+    
+    public function match($title, $filesize)
+    {
+        if(!$this->scrapeSmo()) return null;
+
+        $most_likely = array('confidence' => 0);
+        foreach($this->_records as $rowNum => $row) {
+            $cells = $row->getElementsByTagName('td');
+    
+            if($rowNum > 0) {
+                $candidate = $this->cellToCandidateArray($cells);
+                $this->setCandidateSimilarty($title, $filesize, $candidate);
+                $most_likely = $candidate['confidence'] > $most_likely['confidence'] ? $candidate : $most_likely;
+            }
+        }
+        
+        return $most_likely;
+    }
+    
+    private function scrapeSmo()
+    {
+        if($this->_records) return true;
+        
+        $c = curl_init('http://stepmaniaonline.net/index.php?page=downloads');
+        curl_setopt($c, CURLOPT_RETURNTRANSFER, true);
+        //curl_setopt(... other options you want...)
+        
+        $html = curl_exec($c);
+        
+        if (curl_error($c)) return false;
+
+        // Get the status code
+        $status = curl_getinfo($c, CURLINFO_HTTP_CODE);
+
+        curl_close($c);
+        //$html = file_get_contents('smo.html');
+        $dom = new DOMDocument();
+        @$dom->loadHTML($html);
+        $xpath = new DOMXPath($dom);
+
+        $table = $xpath->query("/html/body/div[@class='container']/div[@class='mid']/div[@class='content']/div[3]/div[@class='blockcontent']/table")->item(0);
+        $this->_records = $table->getElementsByTagName("tr");
+        
+        return true;
+    }
+    
+    private function cellToCandidateArray(DOMNodeList $cells)
+    {
+        return array(
+            'href' => $cells->item(0)->getElementsByTagName('a')->item(0)->getAttribute('href'),
+            'title' => $cells->item(0)->nodeValue,
+            'filesize' => strpos($cells->item(1)->nodeValue, 'Gb') === true ? ($cells->item(1)->nodeValue)*1024*1024*1024 : ($cells->item(1)->nodeValue)*1024*1024            
+        );
+    }
+    
+    private function setCandidateSimilarty($title, $filesize, array &$candidate)
+    {
+        similar_text($title, $candidate['title'], $percent);
+        $r = $candidate['filesize'] > $filesize ? $filesize/$candidate['filesize'] : $candidate['filesize']/$filesize;
+        $candidate['confidence'] = $percent*$r;
+    }
+}
\ No newline at end of file
index 574d4c1..3d117d0 100644 (file)
@@ -37,6 +37,7 @@ return [
     'Services\ISimfileParser'                             => DI\object('Services\SimfileParser'),\r
     'Services\IZipParser'                                 => DI\object('Services\ZipParser'),\r
     'Services\IBannerExtracter'                           => DI\object('Services\BannerExtracter'),\r
+    'Services\ISMOMatcher'                                => DI\object('Services\SMOMatcher'),\r
     \r
     //DA\r
     'DataAccess\StepMania\ISimfileRepository'             => DI\object('DataAccess\StepMania\SimfileRepository'),\r
index a647632..358ccc6 100644 (file)
@@ -137,7 +137,16 @@ return [
             'filename' => DataAccess\Varchar('filename'),\r
             'mimetype' => DataAccess\Varchar('mimetype'),\r
             'size' => DataAccess\Int('size'),\r
-            'uploadDate' => DataAccess\Int('uploaded', 'getUploadDate')\r
+            'uploadDate' => DataAccess\Int('uploaded', 'getUploadDate'),\r
+            'mirrors' => DataAccess\VOArray('FileMirror', 'getMirrors')\r
+        ]\r
+    ],\r
+    \r
+    'FileMirror' => [\r
+        'class' => 'Domain\VOs\FileMirror',\r
+        'table' => 'mirrors',\r
+        'maps' => [\r
+            'uri' => DataAccess\Varchar('uri')\r
         ]\r
     ]\r
 ];\r