darcswatch-0.4: src/Darcs/Watch/GenerateOutput.hs
{-
Copyright (C) 2008 Joachim Breitner
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; see the file COPYING. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
-}
module Darcs.Watch.GenerateOutput where
import Control.Monad
import Control.Concurrent
import Control.Applicative
import System.Environment (getArgs)
import System.Directory
import System.Posix.Files
import System.Time
import System.IO
import System.Terminal.Concurrent
import qualified Data.Map as M
import qualified Data.Set as S
import qualified MultiMap as MM
import MultiMap ((!!!!))
import Data.Char
import Data.List
import Data.Time
import Data.Digest.OpenSSL.MD5 (md5sum)
import Data.Maybe
import Data.Monoid
import System.FilePath
import qualified Data.ByteString.Char8 as B
import Data.ByteString.Char8 (ByteString)
-- Darcs stuff
import Darcs
import Darcs.Watch.Storage
import Darcs.Watch.Data
-- Web ouput
import HTML
generateOutput config patchNew = do
nowStamp <- getCurrentTime
let outputStampFile = cData config </> "output.stamp"
ex <- doesFileExist outputStampFile
lastStamp <- if ex then read . B.unpack <$> B.readFile outputStampFile
else return $ UTCTime (ModifiedJulianDay 0) 0
date <- getClockTime >>= toCalendarTime
putStrLn "Reading name mapping list..."
nameMapping <- readNameMapping (cData config)
putStrLn "Reading bundle list lists.."
bundleLists <- getBundleListList (cData config)
-- Split bundle lists by type
let (repos, authors) = mconcat $ map (\bl -> case bl of
RepositoryBundleList repo -> ([repo],[])
AuthorBundleList author -> ([],[author])
_ -> mempty) bundleLists
let getBundleInfos bundleHash = do
bundle <- getBundle (cData config) bundleHash
history <- getBundleHistory (cData config) bundleHash
let bundleFileName = "bundles" </> bundleBaseName bundleHash
return (BundleInfo bundleHash bundle bundleFileName history)
putStrLn "Writing output (repo pages)..."
forM_ repos $ \r -> do
(bundleHashes,_) <- readBundleList (cData config) (RepositoryBundleList r)
bundleInfos <- mapM getBundleInfos bundleHashes
repoInfo <- getRepositoryInfo (cData config) r
writeFile (cOutput config ++ "/" ++ repoFile r) $
repoPage date nameMapping r repoInfo bundleInfos
putStrLn "Writing output (user pages)..."
forM_ authors $ \u -> do
(bundleHashes,_) <- readBundleList (cData config) (AuthorBundleList u)
bundleInfos <- mapM getBundleInfos bundleHashes
writeFile (cOutput config ++ "/" ++ userFile u) $
userPage date nameMapping u bundleInfos
putStrLn "Writing output (main page)..."
bundleHashes <- listBundles (cData config)
patches <- mconcat <$> forM bundleHashes (\bundleHash -> do
(ps,_) <- getBundle (cData config) bundleHash
return (M.fromList ps)
)
let patchCount = M.size patches
let bundleCount = length bundleHashes
let strict5 f a b c d e = ((((f $! a) $! b) $! c) $! d ) $! e
let strict6 f a b c d e g = (((((f $! a) $! b) $! c) $! d ) $! e) $! g
repoData <- forM repos $ \r -> do
(bundleHashes,_) <- readBundleList (cData config) (RepositoryBundleList r)
bundleInfos <- mapM getBundleInfos bundleHashes
inv <- readRepository (cData config) r
return $ strict6 (,,,,,)
r
(length inv)
(length $ bundleInfoFilter Applied bundleInfos)
(length $ bundleInfoFilter Applicable bundleInfos)
(length $ bundleInfoFilter Obsoleted bundleInfos)
(length $ bundleInfoFilter Rejected bundleInfos)
userData <- forM authors $ \u -> do
(bundleHashes,_) <- readBundleList (cData config) (AuthorBundleList u)
bundleInfos <- mapM getBundleInfos bundleHashes
return $ strict5 (,,,,)
u
(length bundleInfos)
(length (bundleInfoFilter Applicable bundleInfos))
(length (bundleInfoFilter Obsoleted bundleInfos))
(length (bundleInfoFilter Rejected bundleInfos))
writeFile (cOutput config ++ "/index.html") $
mainPage date nameMapping patchCount bundleCount repoData userData
putStrLn "Writing output (diffs)..."
forM_ (M.toList patches) $ \(p,d) -> do
let filename = cOutput config ++ "/" ++ patchDiffFile p
ex <- doesFileExist filename
unless ex $ do
putStrLn $ "Writing new patch file " ++ filename
B.writeFile filename d
ex <- doesDirectoryExist (cOutput config </> "bundles")
unless ex $
createSymbolicLink (bundleDir (cData config)) (cOutput config </> "bundles")
B.writeFile outputStampFile (B.pack (show nowStamp))
getDirectoryFiles dir' = getDirectoryContents dir >>=
return . (map (dir++)) >>=
return . filter ((/= '.') . head) >>=
filterM doesFileExist >>=
filterM ((readable `fmap`) . getPermissions)
where dir = addSlash dir'
maxBy f v1 v2 = if f v1 >= f v2 then v1 else v2
minBy f v1 v2 = if f v1 <= f v2 then v1 else v2
addSlash filename | last filename == '/' = filename
| otherwise = filename ++ "/"
-- not in ghc6.6
infixl 0 `on`
on :: (b -> b -> c) -> (a -> b) -> a -> a -> c
(*) `on` f = \x y -> f x * f y