]> gitweb.michael.orlitzky.com - dead/htsn-import.git/blobdiff - src/TSN/DbImport.hs
Rewrite TSN.DbImport.run_dbmigrate for groundhog-0.7.
[dead/htsn-import.git] / src / TSN / DbImport.hs
index 3aeb29f26a87756e51a49f0773bf1fd53b5b499e..066b5a0c36e7e8ec2a72dfc687224a03ff3b1b79 100644 (file)
@@ -1,63 +1,80 @@
 -- | Definition of the DbImport typeclass.
-module TSN.DbImport
+--
+--   When we parse an XML tree, there are two functions that we would
+--   like to call on the result independent of its type. First, we
+--   would like to be able to run the database migrations for that
+--   type. The migrations are kept separate from insertion because, at
+--   some later point, it make make sense to disable automatic
+--   migrations.
+--
+--   Next we want to import the thing.
+--
+--   Neither of these should depend on the type -- we should just be
+--   able to call 'dbmigrate' followed by 'dbimport' on the
+--   datastructure and have the right thing happen. That is the
+--   purpose of the 'DbImport' typeclass. It allows the XML types to
+--   define their own \"migrate me\" and \"insert me\" functions that
+--   the rest of the application doesn't have to care about.
+--
+module TSN.DbImport (
+  DbImport(..),
+  ImportResult(..),
+  run_dbmigrate )
 where
 
-import Control.Monad.IO.Class ( MonadIO )
-import Database.Groundhog (
-  defaultMigrationLogger,
-  insert,
-  migrate,
-  runMigration )
-import Database.Groundhog.Core ( PersistBackend, PersistEntity )
-import Text.XML.HXT.Core (
-  XmlPickler,
-  XmlTree,
-  unpickleDoc,
-  xpickle )
-
-
--- | The type that will be returned from every file import attempt. If
---   there was an error, its description will be wrapped in an Err. If
---   we successfully imported records, the number of records imported
---   will be wrapped in a Succ.
---
---   Anything else will be wrapped in a "Info" constructor;
---   i.e. somewhere between success and failure. This is like an
---   'Either' with three choices. A "Info" return value means that
---   the XML document *was* processed, so it should be removed.
+-- System imports
+import Control.Monad ( forM_ )
+import Control.Monad.IO.Class ( MonadIO( liftIO ) )
+import qualified Data.Map as Map ( elems )
+import Database.Groundhog ( executeRaw )
+import Database.Groundhog.Core ( NamedMigrations )
+import Database.Groundhog.Generic (
+  createMigration,
+  getQueries,
+  mergeMigrations )
+import Database.Groundhog.Core ( Migration, PersistBackend )
+import Network.Services.TSN.Report ( report_info )
+
+
+-- | The type that will be returned from every file import attempt.
 --
 data ImportResult =
   ImportFailed String -- ^ Failure with an error message.
-    | ImportSkipped String -- ^ We processed the file, but didn't import it.
-                           --   The reason is contained in the second field.
-    | ImportSucceeded Int  -- ^ We did import records, and here's how many.
-    | ImportUnsupported String -- ^ We didn't know how to process this file.
-                               --   The second field should contain info.
-
--- | Instances of this type know how to insert themselves into a
---   Groundhog database.
+
+  | ImportSkipped String -- ^ We processed the file, but didn't import it.
+                         --   The reason is contained in the second field.
+
+  | ImportSucceeded      -- ^ We did import records.
+
+  | ImportUnsupported String -- ^ We didn't know how to process this file.
+                             --   The second field should contain info.
+
+
+-- | Instances of this type know how to run their own database
+--   migrations and insert themselves into a database.
+--
 class DbImport a where
-  dbimport :: (MonadIO m, PersistBackend m)
-           => a
-           -> XmlTree
-           -> m ImportResult
+  -- | Import an instance of type @a@.
+  dbimport :: (PersistBackend m) => a -> m ImportResult
+
+  -- | This must migrate *all* stuffs that can potentially be
+  --   created/used by the type @a@.
+  dbmigrate :: (MonadIO m, PersistBackend m) => a -> m ()
 
 
--- | We put the 'Configuration' and 'XmlTree' arguments last so that
--- it's easy to eta reduce all of the import_foo functions that call
--- this.
+-- | A migration runner that will use our normal info reporting
+--   mechanism. The top-level code was stolen from 'runMigration' in
+--   "Data.Groundhog.Generic" and the 'execute_pretty' code was stolen
+--   from 'executeMigration'' in the same module.
 --
-import_generic :: (XmlPickler a, MonadIO m, PersistEntity b, PersistBackend m)
-               => (a -> [b]) -- ^ listings getter
-               -> b          -- ^ Dummy Listing instance needed for 'migrate'
-               -> XmlTree
-               -> m ImportResult -- ^ Return the number of records inserted.
-import_generic g dummy xml = do
-  runMigration defaultMigrationLogger $ migrate dummy
-  let root_element = unpickleDoc xpickle xml
-  case root_element of
-    Nothing -> return $
-                 ImportFailed "Could not unpickle document in import_generic."
-    Just elt  -> do
-      ids <- mapM insert (g elt)
-      return $ ImportSucceeded (length ids)
+run_dbmigrate :: (MonadIO m, PersistBackend m) => Migration m -> m ()
+run_dbmigrate migration = createMigration migration >>= execute_pretty
+  where
+    execute_pretty :: (PersistBackend m, MonadIO m) => NamedMigrations -> m ()
+    execute_pretty m = do
+      let migs = getQueries False $ mergeMigrations $ Map.elems m
+      case migs of
+        Left errs -> fail $ unlines errs
+        Right qs -> forM_ qs $ \q -> do
+                         liftIO $ report_info ("Migration: " ++ q ++ ";")
+                         executeRaw False q []