packages feed

hydra-0.15.0: src/main/haskell/Hydra/Demos/Genpg/Demo.hs

module Hydra.Demos.GenPG.Demo where

import Hydra.Kernel
import qualified Hydra.Show.Errors as ShowError
import Hydra.Demos.GenPG.Examples.Sales.DatabaseSchema
import Hydra.Demos.GenPG.Examples.Sales.GraphSchema
import Hydra.Demos.GenPG.Examples.Sales.Mapping
import Hydra.Demos.GenPG.Examples.Health.DatabaseSchema
import Hydra.Demos.GenPG.Examples.Health.GraphSchema
import Hydra.Demos.GenPG.Examples.Health.Mapping
import Hydra.Dsl.Pg.Mappings
import Hydra.Lib.Literals
import Hydra.Pg.Graphson.Utils
import qualified Hydra.Demos.Genpg.Transform as Transform
import Hydra.Pg.Utils (lazyGraphToElements)
import Hydra.Sources.Kernel.Types.Core
import qualified Hydra.Json.Writer as JsonWriter
import qualified Hydra.Pg.Model as Pg
import qualified Hydra.Tabular as Tab

import qualified Control.Monad as CM
import qualified Data.List as L
import qualified Data.Map as M
import System.IO (hFlush, stdout)


generateSalesGraphSON :: IO ()
generateSalesGraphSON = generateGraphSON
  "demos/genpg/data/sources/sales"
  salesTableSchemas
  salesGraph
  "demos/genpg/output/sales.jsonl"

generateHealthGraphSON :: IO ()
generateHealthGraphSON = generateGraphSON
  "demos/genpg/data/sources/health"
  healthTableSchemas
  healthGraph
  "demos/genpg/output/health.jsonl"

generateGraphSON :: FilePath -> [TableType] -> Pg.LazyGraph Term -> FilePath -> IO ()
generateGraphSON sourceRoot tableSchemas graphMapping outputPath = do
  log $ "Reading CSV files from " ++ sourceRoot ++ "/"
  log $ "  Tables: " ++ L.intercalate ", " (fmap (unRelationName . tableTypeName) tableSchemas)
  g <- transformTables sourceRoot tableSchemas graphMapping
  let els = lazyGraphToElements g
  let (vertices, edges) = L.partition Transform.elementIsVertex els
  log $ "Transforming to property graph..."
  log $ "  Vertices: " ++ show (L.length vertices)
  log $ "  Edges: " ++ show (L.length edges)
  log $ "Writing GraphSON to " ++ outputPath
  jsonResult <- case pgElementsToGraphson encodeTermValue els of
    Left ic -> fail (ShowError.error ic)
    Right v -> return v
  writeFile outputPath (jsonValuesToString jsonResult)
  log $ "Done. Output written to " ++ outputPath
  where
    log msg = putStrLn msg >> hFlush stdout
    jsonValuesToString vals = L.intercalate "\n" (fmap JsonWriter.printJson vals) ++ "\n"
    unRelationName (RelationName n) = n


--------------------------------------------------------------------------------
-- Table transformation (I/O)

-- | Transform a table by reading from a file and applying vertex/edge specifications
transformTable :: TableType -> FilePath -> [Pg.Vertex Term] -> [Pg.Edge Term] -> IO ([Pg.Vertex Term], [Pg.Edge Term])
transformTable tableType@(TableType (RelationName tableName) _) path vspecs especs = do
    (Table _ rows) <- decodeTableIo tableType path
    case Transform.transformTableRows emptyContext hydraCoreGraph vspecs especs tableType rows of
      Left ic -> fail $ "Error transforming " ++ tableName ++ ": " ++ ShowError.error ic
      Right v -> return v

-- | Transform multiple tables according to a graph mapping specification
transformTables :: FilePath -> [TableType] -> Pg.LazyGraph Term -> IO (Pg.LazyGraph Term)
transformTables fileRoot tableTypes spec = do
    transform <- case (Transform.elementSpecsByTable spec) of
      Left err -> fail $ "Error in mapping specification: " ++ err
      Right t -> return t
    pairs <- CM.mapM forTable $ M.toList transform
    let (vertices, edges) = L.foldl Transform.concatPairs ([], []) pairs
    return $ Transform.makeLazyGraph vertices edges
  where
    forTable (tname, (vspecs, especs)) = case M.lookup (RelationName tname) tblTypesByName of
        Nothing -> fail $ "Table specified in mapping does not exist: " ++ tname
        Just tableType -> transformTable tableType path vspecs especs
      where
        path = fileRoot ++ "/" ++ tname
    tblTypesByName = Transform.tableTypesByName tableTypes


--------------------------------------------------------------------------------
-- Table reading

decodeTableIo :: TableType -> FilePath -> IO (Table Term)
decodeTableIo tableType path = do
    rawLines <- fmap lines $ readFile path
    table <- case Transform.parseTableLines True rawLines of
      Left err -> fail $ "CSV read error in " ++ show path ++ ": " ++ err
      Right t -> return t
    case Transform.decodeTable tableType table of
      Left err -> fail err
      Right t -> return t