Graphalyze-0.5: Data/Graph/Analysis/Reporting.hs
{- |
Module : Data.Graph.Analysis.Reporting
Description : Graphalyze Types and Classes
Copyright : (c) Ivan Lazar Miljenovic 2008
License : 2-Clause BSD
Maintainer : Ivan.Miljenovic@gmail.com
This module defines the report framework used.
-}
module Data.Graph.Analysis.Reporting
( -- * Document representation
-- $document
Document(..),
DocumentGenerator(..),
Location(..),
DocElement(..),
DocInline(..),
DocGraph,
-- * Helper functions
-- $utilities
today,
tryCreateDirectory,
createGraph,
createSize,
unDotPath
) where
import Data.GraphViz
import Data.GraphViz.Attributes
import Data.Graph.Analysis.Visualisation
import Data.Maybe
import Data.Time
import Control.Exception
import System.Directory
import System.FilePath
import System.Locale
-- -----------------------------------------------------------------------------
{- $document
'Document' is the simplified representation of a document. Note
that this just specifies a document layout, and not an
implementation. To actually create a \"physical\" document,
you must use an instance of 'DocumentGenerator'.
-}
{- | Representation of a document. The document is to be stored in
the directory 'rootDirectory', and the main file is to have a
filename of @'fileFront' '<.>' ('docExtension' dg)@, where @dg@ is an
instance of 'DocumentGenerator'.
-}
data Document = Doc { -- | Document location
rootDirectory :: FilePath,
fileFront :: String,
-- | The sub-directory of 'rootDirectory',
-- where graphs are to be created.
graphDirectory :: FilePath,
-- | Pre-matter
title :: DocInline,
author :: String,
date :: String,
-- | Main-matter
content :: [DocElement]
}
-- | Represents the class of document generators.
class DocumentGenerator dg where
-- | Convert idealised 'Document' values into actual documents,
-- returning the document file created.
createDocument :: dg -> Document -> IO (Maybe FilePath)
-- | The extension of all document-style files created. Note that
-- this doesn't preclude the creation of other files, e.g. images.
docExtension :: dg -> String
-- | Representation of a location, either on the internet or locally.
data Location = URL String | File FilePath
instance Show Location where
show (URL url) = url
show (File fp) = fp
-- | Elements of a document.
data DocElement = Section DocInline [DocElement]
| Paragraph [DocInline]
| Enumeration [DocElement]
| Itemized [DocElement]
| Definition DocInline DocElement
| GraphImage DocGraph
-- | Inline elements of a document.
data DocInline = Text String
| BlankSpace
| Grouping [DocInline]
| Bold DocInline
| Emphasis DocInline
| DocLink DocInline Location
| DocImage DocInline Location
-- | Let the 'DocumentGenerator' instance apply extra settings, such as size.
type AttrsToGraph = [Attribute] -> DotGraph
type DocGraph = (FilePath,DocInline,AttrsToGraph)
-- -----------------------------------------------------------------------------
{- $utilities
Utility functions to help with document creation.
-}
-- | Return today's date as a string, e.g. \"Monday 1 January, 2000\".
-- This arbitrary format is chosen as there doesn't seem to be a way
-- of determining the correct format as per the user's locale settings.
today :: IO String
today = do zoneT <- getZonedTime
let localT = zonedTimeToLocalTime zoneT
return $ formatTime locale fmt localT
where
locale = defaultTimeLocale
fmt = "%A %e %B, %Y"
-- | Attempts to create the specified directly, returning @True@
-- if successful (or if the directory already exists), @False@
-- if an error occurred.
tryCreateDirectory :: FilePath -> IO Bool
tryCreateDirectory fp = do r <- try $ mkDir fp
return (isRight r)
where
mkDir = createDirectoryIfMissing True
isRight (Right _) = True
isRight _ = False
-- | Attempts to creates a png file (with the given filename in the
-- given directory) from the graph using the given attributes.
-- If the second set of attributes is not 'Nothing', then the first
-- image links to the second. The whole result is wrapped in a
-- 'Paragraph'.
createGraph :: FilePath -> FilePath -> [Attribute] -> Maybe [Attribute]
-> DocGraph -> IO (Maybe DocElement)
createGraph fp gfp as mas (fn,inl,ag)
= do eImg <- gI as DocImage fn inl Nothing
if (isJust eImg)
then case mas of
Nothing -> rt eImg
(Just as') -> do rt =<< gI as' DocLink fn' (toImg eImg) eImg
else return Nothing
where
fn' = fn ++ "-large"
i2e i = Just (i,Paragraph [i])
rt = return . fmap snd
toImg = fst . fromJust
gI a ln nm lb fl = do mImg <- graphImage fp gfp a ln (nm,lb,ag)
case mImg of
Nothing -> return fl
(Just img) -> return $ i2e img
-- | Create the inline image/link from the given DocGraph.
graphImage :: FilePath -> FilePath -> [Attribute]
-> (DocInline -> Location -> DocInline)
-> DocGraph -> IO (Maybe DocInline)
graphImage fp gfp as link (fn,inl,ag)
= do created <- runGraphviz dg output filename'
if created
then return (Just img)
else return Nothing
where
dg = ag as
fn' = unDotPath fn
ext = "png"
output = Png
filename = gfp </> fn' <.> ext
filename' = fp </> filename
loc = File filename
img = link inl loc
-- | Create the "Data.GraphViz" 'Size' 'Attribute' using the given width
-- and a 6:4 width:height ratio.
createSize :: Double -> Attribute
createSize w = Size w (w*4/6)
-- | Replace all @.@ with @-@ in the given 'FilePath', since some output
-- formats (e.g. LaTeX) don't like extraneous @.@'s in the filename.
unDotPath :: FilePath -> FilePath
unDotPath = map replace
where
replace '.' = '-'
replace c = c