diff --git a/VERSIONS_WIP/102219 b/VERSIONS_WIP/102219
new file mode 100644
index 0000000000000000000000000000000000000000..4b833839e89298ef440836cb695c4e705df1bdb2
--- /dev/null
+++ b/VERSIONS_WIP/102219
@@ -0,0 +1 @@
+ - ticket #102219 : Ussel : Reprise des données albums cartes postales
\ No newline at end of file
diff --git a/library/Class/AlbumRessource.php b/library/Class/AlbumRessource.php
index a616f3ac7fa1358ef3120c5c408c48baf94109ed..11c9656976b31627ca3fb5fa92aad4fa83b02abd 100644
--- a/library/Class/AlbumRessource.php
+++ b/library/Class/AlbumRessource.php
@@ -451,6 +451,17 @@ class Class_AlbumRessource extends Storm_Model_Abstract {
   }
 
 
+  /**
+   * @param $name string input file name
+   * @param $mover Class_UploadMover_Abstract
+   * @return Class_AlbumRessource
+   */
+  public function setUploadMover($name, $mover) {
+    $this->getUploadHandler($name)->setUploadMover($mover);
+    return $this;
+  }
+
+
   /**
    * @return Class_Folder_Manager
    */
diff --git a/library/Class/UploadMover/Abstract.php b/library/Class/UploadMover/Abstract.php
index 6ab0b63dd76e061dc6a6dfe9e619d5bf5f031c37..48a70e2f3fc1ecf164fd1049f792254320cdd5f8 100644
--- a/library/Class/UploadMover/Abstract.php
+++ b/library/Class/UploadMover/Abstract.php
@@ -16,10 +16,12 @@
  *
  * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
  * along with BOKEH; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA 
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
  */
 
 abstract class Class_UploadMover_Abstract {
+  use Trait_Translator;
+
   /** @var string */
   protected $_error;
 
@@ -30,7 +32,23 @@ abstract class Class_UploadMover_Abstract {
    * @param string $destination
    * @return bool
    */
-  abstract public function moveTo($source, $destination);
+  public function moveTo($source, $destination) {
+    if ($this->_realMoveTo($source, $destination))
+      return true;
+
+    $this->setError($this->_('Impossible d\'écrire le fichier sur le serveur au chemin [%s]',
+                             $destination));
+    return false;
+  }
+
+
+  /**
+   * @codeCoverageIgnore
+   * @param string $source
+   * @param string $destination
+   * @return bool
+   */
+  abstract protected function _realMoveTo($source, $destination);
 
 
   /**
@@ -52,5 +70,3 @@ abstract class Class_UploadMover_Abstract {
     return $this->_error;
   }
 }
-
-?>
\ No newline at end of file
diff --git a/library/Class/UploadMover/HttpPost.php b/library/Class/UploadMover/HttpPost.php
index 37798ac9c50184c605c197a901672705d08e175b..644935f106da5b8a22361efed40ad5cab9a08aad 100644
--- a/library/Class/UploadMover/HttpPost.php
+++ b/library/Class/UploadMover/HttpPost.php
@@ -20,22 +20,15 @@
  */
 
 class Class_UploadMover_HttpPost extends Class_UploadMover_Abstract {
-  use Trait_Translator,
-    Trait_StaticFileWriter;
+  use Trait_StaticFileWriter;
+
   /**
    * @codeCoverageIgnore
    * @param string $source
    * @param string $destination
    * @return bool
    */
-  public function moveTo($source, $destination) {
-    if (!$this->getFileWriter()->moveUploadedFile($source, $destination)) {
-      $this->setError(sprintf($this->_('Impossible d\'écrire le fichier sur le serveur au chemin [%s]'),  $destination));
-      return false;
-    }
-
-    return true;
+  protected function _realMoveTo($source, $destination) {
+    return $this->getFileWriter()->moveUploadedFile($source, $destination);
   }
 }
-
-?>
\ No newline at end of file
diff --git a/library/Class/UploadMover/LocalFile.php b/library/Class/UploadMover/LocalFile.php
index f238097f2975cbf02ce777c6bee4d8ba6b5467c6..906222111415ec19b5db9b211b8c30a8898e7dc7 100644
--- a/library/Class/UploadMover/LocalFile.php
+++ b/library/Class/UploadMover/LocalFile.php
@@ -16,26 +16,18 @@
  *
  * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
  * along with BOKEH; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA 
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
  */
 
 class Class_UploadMover_LocalFile extends Class_UploadMover_Abstract {
-  use Trait_Translator;
   /**
    * @codeCoverageIgnore
    * @param string $source
    * @param string $destination
    * @return bool
    */
-  public function moveTo($source, $destination) {
+  protected function _realMoveTo($source, $destination) {
     @rename($source, $destination);
-    if (!file_exists($destination)) {
-      $this->setError(sprintf($this->_('Impossible d\'écrire le fichier sur le serveur au chemin [%s]'),  $destination));
-      return false;
-    }
-
-    return true;
+    return file_exists($destination);
   }
 }
-
-?>
\ No newline at end of file
diff --git a/scripts/import_ussel.php b/scripts/import_ussel.php
new file mode 100644
index 0000000000000000000000000000000000000000..737e5046d84d0b51157fe4f320e04be0ff1063e0
--- /dev/null
+++ b/scripts/import_ussel.php
@@ -0,0 +1,256 @@
+<?php
+error_reporting(E_ALL^E_DEPRECATED);
+ini_set('display_startup_errors', 1);
+ini_set('display_errors', 1);
+
+require __DIR__ . '/../console.php';
+
+mkdir('/tmp/ussel');
+
+
+class UsselAlbumReportItem extends Class_Entity {
+  public static function newWith($record, $attributes = []) {
+    $ils_id = $record->get_subfield('001');
+    return new static(array_merge(['BokehId' => $record->getId(),
+                                   'IlsId' => $ils_id ? $ils_id[0] : 'n/a'],
+                                  $attributes));
+  }
+
+
+  public static function description() {
+    return (new Class_TableDescription('ussel'))
+      ->addColumn('Bokeh id', 'bokeh_id')
+      ->addColumn('ILS id', 'ils_id')
+      ;
+  }
+}
+
+
+
+class UsselAlbumImporter {
+  use Trait_MemoryCleaner;
+
+  protected
+    $_base_path = '/home/webmaster/stl/php/bokeh/SAV_HCC',
+    $_base_category,
+    $_frbr_type,
+    $_handled,
+    $_no_links,
+    $_file_not_exists,
+    $_no_files,
+    $_all_files,
+    $_view;
+
+  public function __construct() {
+    symlink($this->_base_path.'/La Courtine', $this->_base_path.'/LaCourtine');
+    symlink($this->_base_path.'/Meymac/Mey656 .jpg', $this->_base_path.'/Meymac/Mey656.jpg');
+    symlink($this->_base_path.'/Meymac/MEY658 .JPG', $this->_base_path.'/Meymac/MEY658.jpg');
+    $this->_view = new ZendAfi_Controller_Action_Helper_View();
+    $command = new Class_Testing_Command();
+    $command->exec('find "'.$this->_base_path.'"');
+    $this->_all_files = implode("\n", $command->getOutput());
+    if (!trim($this->_all_files)) {
+      echo 'no files';
+      exit;
+    }
+  }
+
+
+  public function run() {
+    $this->_handled = 0;
+    $this->_no_links = [];
+    $this->_file_not_exists = [];
+    $this->_no_files = [];
+
+    if ( !$this->_base_category = Class_AlbumCategorie::getOrCreateRootCategory('Cartes Postales')) {
+      echo 'Base category not found';
+      return;
+    }
+
+    if (!$this->_frbr_type = Class_FRBR_LinkType::find(1)) {
+      echo 'FRBR link type not found';
+      return;
+    }
+
+    $page = 0;
+    while($records = Class_Notice::findAllBy(['type_doc' => 7,
+                                              'order' => 'id_notice',
+                                              'limitPage' => [$page, 100]])) {
+      $this->_runPage($records);
+      $this->_cleanMemory();
+      $page++;
+    }
+
+    return $this;
+  }
+
+
+  protected function _runPage($records) {
+    foreach($records as $record)
+      $this->_importRecord($record);
+  }
+
+
+  public function runPageTest() {
+    foreach (Class_Notice::findAllBy(['type_doc' => 7,
+                                      'order' => 'id_notice',
+                                      'limitPage' => [0, 5000]]) as $record) {
+      $existing_files = $this->_existingFilesOf($record);
+    }
+  }
+
+
+  public function report() {
+    echo implode("\n",
+                 ['', '',
+                  $this->_handled . ' notices traitées',
+                  count($this->_no_links) . ' notices sans zones de liens',
+                  count($this->_file_not_exists) . ' liens sans fichier correspondant',
+                  count($this->_no_files) . ' notices sans aucun fichier correspondant',
+                  '', '']);
+
+    $this->_reportFile('ussel_no_links',
+                       UsselAlbumReportItem::description(),
+                       $this->_no_links);
+
+    $this->_reportFile('ussel_file_not_exists',
+                       UsselAlbumReportItem::description()->addColumn('Path', 'path'),
+                       $this->_file_not_exists);
+
+    $this->_reportFile('ussel_no_files',
+                       UsselAlbumReportItem::description(),
+                       $this->_no_files);
+  }
+
+
+  protected function _reportFile($name, $description, $data) {
+    file_put_contents($name . '.csv',
+                      $this->_view->renderCsv($description, $data));
+  }
+
+
+  protected function _importRecord($record) {
+    $this->_handled++;
+    echo '.';
+
+    $existing_files = $this->_existingFilesOf($record);
+    if ($existing_files->isEmpty()) {
+      $this->_no_files[] = UsselAlbumReportItem::newWith($record);
+      return;
+    }
+
+    $album = Class_Album::newInstance(['categorie' => $this->_categoryFor($existing_files->first()),
+                                       'titre' => $record->getTitrePrincipal(),
+                                       'visible' => false,
+                                       'description' => $record->getResume(),
+                                       'matiere' => $this->_matersOf($record)]);
+    $album->beValidated()->save();
+
+    $this->_linkRecordToAlbum($record, $album);
+
+    foreach($existing_files as $file)
+      $this->_addResourceTo($file, $album);
+  }
+
+
+  protected function _addResourceTo($file, $album) {
+    $ressource = Class_AlbumRessource::newInstance(['album' => $album,
+                                                    'titre' => '',
+                                                    'description' => '']);
+
+    $ressource->save();
+    $pathinfo = pathinfo($file);
+    $filename = $pathinfo['basename'];
+    $tmp_name = '/tmp/ussel/'.$filename;
+
+    copy($file, $tmp_name);
+    $_FILES['fichier'] = ['size' => filesize($file),
+                          'name' => $filename,
+                          'tmp_name' => $tmp_name];
+
+    $ressource->setUploadMover('fichier', new Class_UploadMover_LocalFile);
+    if ($ressource->receiveFiles())
+      return;
+
+    echo $ressource->getErrors()['fichier'];
+    $ressource->delete();
+  }
+
+
+  protected function _linkRecordToAlbum($record, $album) {
+    $source = Class_Url::absolute((new Class_Notice_Permalink())->paramsFor($record), null, true);
+    $target = Class_Url::absolute(array_merge(['id' => $album->getId()],
+                                              $album->getPermalink()), null, true);
+
+    return Class_FRBR_Link::newInstance(['source' => $source,
+                                         'target' => $target,
+                                         'type' => $this->_frbr_type])
+      ->save();
+  }
+
+
+  protected function _categoryFor($path) {
+    $path = str_replace($this->_base_path, '', $path);
+    if (!$parts = array_filter(explode('/', pathinfo($path)['dirname'])))
+      return $this->_base_category;
+
+    $parent = $this->_base_category;
+    foreach($parts as $part)
+      $parent = $this->_ensureSubCategory($parent, $part);
+
+    return $parent;
+  }
+
+
+  protected function _ensureSubCategory($parent, $label) {
+    $attributes = ['parent_id' => $parent->getId(), 'libelle' => $label];
+    if (!$category = Class_AlbumCategorie::findFirstBy($attributes)) {
+      $category = Class_AlbumCategorie::newInstance($attributes);
+      $category->save();
+    }
+
+    return $category;
+  }
+
+
+  protected function _matersOf($record) {
+    return implode(';', Class_Notice_Facette::facetsValuesFromRecordFilteredBy($record, 'isMatter'));
+  }
+
+
+  protected function _existingFilesOf($record) {
+    if (!$links = $record->get_subfield('856', 'u')) {
+      $this->_no_links[] = UsselAlbumReportItem::newWith($record);
+      return new Storm_Collection([]);
+    }
+
+    return (new Storm_Collection($links))
+      ->collect(function($each) { return $this->_base_path . $each; })
+      ->collect(function($each) use($record)
+                {
+                  if ($path = $this->_fileExists($each))
+                    return $path;
+
+                  $path = str_replace($this->_base_path, '', $each);
+                  $this->_file_not_exists[] = UsselAlbumReportItem::newWith($record,
+                                                                            ['Path' => $path]);
+                  return null;
+                })
+      ->select(function($each) { return $each; });
+  }
+
+
+  protected function _fileExists($path) {
+    if (file_exists($path))
+      return $path;
+
+    $matches = [];
+    if (preg_match('|^' . preg_quote($path) . '$|im', $this->_all_files, $matches))
+      return $matches[0];
+  }
+}
+
+
+(new UsselAlbumImporter)
+  ->run()
+  ->report();