packages feed

dawdle 0.1.0.1 → 0.1.0.2

raw patch · 7 files changed

+108/−44 lines, 7 filesdep ~dawdlePVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependency ranges changed: dawdle

API changes (from Hackage documentation)

- Database.Dawdle.PrettyPrint: ddl :: String -> [String] -> [CellType] -> Doc
- Database.Dawdle.PrettyPrint: prettyTypes :: Bool -> CellType -> Doc
- Database.Dawdle.Types: applyNullIfNeeded :: Bool -> CellType -> CellType
- Database.Dawdle.Types: removeNull :: CellType -> CellType
- Database.Dawdle.Utils: getFstIfJust :: (a, Maybe b) -> Maybe a
+ Database.Dawdle.Types: addNullable :: CellType -> CellType
+ Database.Dawdle.Types: removeNullable :: CellType -> CellType

Files

dawdle.cabal view
@@ -2,7 +2,7 @@ -- documentation, see http://haskell.org/cabal/users-guide/
 
 name:                dawdle
-version:             0.1.0.1
+version:             0.1.0.2
 synopsis:            Generates DDL suggestions based on a CSV file
 description:         Generates DDL suggestions based on a CSV file
 homepage:            https://github.com/arnons1/dawdle
@@ -47,5 +47,5 @@                        pretty >= 1.0 && < 1.2,
                        text >= 0.11.1.13 && < 1.3,
                        filepath >= 1.4.0.0,
-                       dawdle == 0.1.0.1
+                       dawdle == 0.1.0.2
   ghc-options:         -Wall -O3
src/Database/Dawdle/Dawdle.lhs view
@@ -18,7 +18,10 @@ > import           Data.Time.Calendar
 > import           Text.Read (readMaybe)
 
-> analyzeFile :: Options -> [[String]] -> Either String [CellType]
+> -- | Analyze a file and return a list of types, or an error.
+> analyzeFile :: Options -- ^ Options from 'getOpts' (or 'defaultOptions') in "Database.Dawdle.Options"
+>             -> [[String]] -- ^ Table containing parsed CSV text, one element per cell
+>             -> Either String [CellType] -- ^ Returned type is either error message or list of types, each item representing one column
 > analyzeFile o mtrx = do
 >   let cols = transpose mtrx
 >   -- sanity on structure
@@ -33,12 +36,12 @@ 
 > analyzeCell :: CellType -> String -> Either String CellType
 > analyzeCell mp s
->  | null s || isNullText s = Right $ composeMaxTypesWithNulls mp $ Nullable Unknown
+>  | null s || isNullText s = Right $ addNullable $ composeMaxTypesWithNulls mp Unknown
 >  | b <- map toLower s
 >  , b `elem` ["false","true"]
->  , removeNull mp `elem` [CTBool,Unknown] =
+>  , removeNullable mp `elem` [CTBool,Unknown] =
 >      Right $ composeMaxTypesWithNulls mp CTBool
->  | isDateOrUnknown $ removeNull mp
+>  | isDateOrUnknown $ removeNullable mp
 >  , Just fFmt <- msum
 >       [ ptm fmt s | fmt <- iso8601DateFormat Nothing : 
 >                            ["%Y%m%d" | length s == 8] ] = 
@@ -46,7 +49,7 @@ >        Just oFmt |
 >          oFmt /= fFmt -> return $ composeMaxTypesWithNulls mp $ CTChar (length s)
 >        _ -> return $ composeMaxTypesWithNulls mp $ CTDate fFmt
->  | isDateTimeOrUnknown $ removeNull mp
+>  | isDateTimeOrUnknown $ removeNullable mp
 >  , Just fFmt <- msum
 >       [ ptm fmt s | fmt <- [iso8601DateFormat $ Just "%H:%M:%S"
 >                                      ,"%Y-%m-%d %H:%M:%S%Q"
@@ -57,11 +60,11 @@ >          oFmt /= fFmt -> return $ composeMaxTypesWithNulls mp $ CTChar (length s)
 >        _ -> return $ composeMaxTypesWithNulls mp $ CTDateTime fFmt
 >  | isInt s
->  , removeNull mp < CTDateTime ""
+>  , removeNullable mp < CTDateTime ""
 >  , Right s' <- maybeToEither "Can't read integer as Integer (??)" (readMaybe s :: Maybe Integer) =
 >       composeMaxTypesWithNulls mp <$> intType' s'
 >  | (not . isInt) s && isCTNumber s
->  , removeNull mp < CTChar 1
+>  , removeNullable mp < CTChar 1
 >  , Right s' <- maybeToEither "Can't read float type as Double (??)" (readMaybe s :: Maybe Double) =
 >      composeMaxTypesWithNulls mp <$> floatType s'
 >   | otherwise = Right $ composeMaxTypesWithNulls mp $ CTChar (length s)
src/Database/Dawdle/Options.lhs view
@@ -8,16 +8,32 @@ >
 > import           System.Console.GetOpt
 > import           Text.Read ( readMaybe )
->
+
+> -- | Options structure for passing to the main module's 'analyzeFile'
 > data Options = Options
->  { optVerbose     :: Bool
->  , optInput       :: Maybe FilePath
->  , optStopAfter   :: Maybe Int
->  , optWithHeader  :: Bool
->  , optSepChar     :: Char
->  , optVerifyIntegrity :: Bool
+>  { optVerbose     :: Bool -- ^ Verbose mode
+>  , optInput       :: Maybe FilePath -- ^ stdin (Nothing) or file to read (Just FilePath)
+>  , optStopAfter   :: Maybe Int -- ^ Threshold for stopping (Just Int)
+>                                --   or consume entire file (Nothing)
+>  , optWithHeader  :: Bool -- ^ Is the first line a header? (Boolean)
+>  , optSepChar     :: Char -- ^ Separator character for CSV (Default ',')
+>  , optVerifyIntegrity :: Bool -- ^ Verify integrity of CSV file first? (Default True)
 >  } deriving Show
 
+> -- | Default set of options:
+> --
+> --   * Verbosity: False
+> --
+> --   * Input: Nothing (stdin)
+> --
+> --   * Stop after threshold: Just 10000
+> --
+> --   * With header: False
+> --
+> --   * Separator char: ,
+> --
+> --   * Verify integrity: True
+> --
 > defaultOptions :: Options
 > defaultOptions    = Options
 >  { optVerbose     = False
@@ -54,6 +70,8 @@ > headErr' (a:_) = a
 > headErr' _ = error "Head error in Options.lhs"
 
+> -- | getOpts constructs a set of flags based on the user's input
+> --   and the default options set in the 'defaultOptions' structure
 > getOpts :: [String] -> IO (Options, [String])
 > getOpts argv =
 >   case getOpt Permute options argv of
src/Database/Dawdle/Parser.lhs view
@@ -10,7 +10,12 @@ > import Data.Text.Lazy (Text)
 > import Text.Parsec
 
-> parseCsv :: Char -> String -> Text -> Either ParseError [[String]]
+> -- | Parses a CSV file and returns a 2D list of parsed strings
+> parseCsv :: Char -- ^ Separator character for CSV
+>          -> String -- ^ Source of the text (stdin/file name)
+>          -> Text -- ^ "Data.Text.Lazy"'s 'Text' of the source
+>          -> Either ParseError [[String]] -- ^ Either "Text.Parsec"'s 'ParseError'
+>                                          --   or a 2D list of parsed strings
 > parseCsv sepChar = parse (csvFile sepChar)
 
 > newLines :: String
src/Database/Dawdle/PrettyPrint.lhs view
@@ -4,19 +4,28 @@ >              FlexibleContexts, RankNTypes #-}
 
 > module Database.Dawdle.PrettyPrint
+>        (pretty)
 > where
 > import Database.Dawdle.Types
 > import Text.PrettyPrint
 
-> pretty :: String -> [String] -> [CellType] -> String
+TODO: Add different syntax support (ANSI/Oracle/Microsoft/PostgreSQL,...)
+
+> -- | PrettyPrint the types as a Create Table statement
+> pretty :: String -- ^ Source of the data (stdin / filename)
+>        -> [String] -- ^ Column names to assign
+>        -> [CellType] -- ^ List of types
+>        -> String -- ^ create table statement returned
 > pretty fn colns = render . ddl fn colns
 
+> -- | Create a "Text.PrettyPrint" 'Doc' to be rendered
 > ddl :: String -> [String] -> [CellType] -> Doc
 > ddl fn colns cts = text "create table" <+> text fn <+> body
 >   where
 >     body = parens $ nest 3 $ vcat $ punctuate comma clnms
 >     clnms = zipWith (\a b -> text a <+> b) colns $ map (prettyTypes True) cts
 
+> -- | Prettyprint the types.
 > prettyTypes :: Bool -> CellType -> Doc
 > prettyTypes isNullPass = \case
 >   Nullable c -> prettyTypes False c <+> text "null"
src/Database/Dawdle/Types.lhs view
@@ -5,9 +5,9 @@ >        (SParser
 >        ,CellType(..)
 >        ,isNullable
->        ,applyNullIfNeeded
+>        ,addNullable
 >        ,getDateFormatFromCT
->        ,removeNull
+>        ,removeNullable
 >        ,composeNull
 >        ,composeMaxTypesWithNulls
 >        ,isDateOrUnknown
@@ -17,42 +17,66 @@ > import Text.Parsec
 
 > type SParser a = forall s u (m :: * -> *). Stream s m Char => ParsecT s u m a
-> data CellType = Nullable CellType | Unknown | 
->   CTBool | CTInt8 | CTInt16 | CTInt32 | CTInt64 | CTFloat | CTDouble | CTDate String | CTDateTime String | CTChar Int
+> -- ^ Used to prettify the type signatures
+
+> -- | The CellType contains the types available for inferrence.
+> --   The order matters, as it represents the order in which the
+> --   algorithm will try to infer.
+> data CellType =
+>                 Nullable CellType -- ^ Nullable cell (Recursive definition)
+>               | Unknown -- ^ Initial type is unknown
+>               | CTBool
+>               | CTInt8
+>               | CTInt16
+>               | CTInt32
+>               | CTInt64
+>               | CTFloat
+>               | CTDouble
+>               | CTDate String -- ^ The string represents the format
+>               | CTDateTime String -- ^ The string represents the format
+>               | CTChar Int -- ^ String with size
 >   deriving (Eq,Show,Ord,Data,Typeable)
 
+> -- | Is this cell nullable?
 > isNullable :: CellType -> Bool
-> isNullable (Nullable _) = True
+> isNullable Nullable{} = True
 > isNullable _ = False
 
-> applyNullIfNeeded :: Bool -> CellType -> CellType
-> applyNullIfNeeded True r@Nullable{} = r
-> applyNullIfNeeded True r = Nullable r
-> applyNullIfNeeded False r = r
-
-> getDateFormatFromCT :: CellType -> Maybe String
-> getDateFormatFromCT (CTDateTime s) = Just s
-> getDateFormatFromCT (CTDate s) = Just s
-> getDateFormatFromCT _ = Nothing
+> -- | Add nullability to a cell
+> addNullable :: CellType -> CellType
+> addNullable r@Nullable{} = r
+> addNullable r = Nullable r
 
-> removeNull :: CellType -> CellType
-> removeNull (Nullable x) = x
-> removeNull x = x
+> -- | Remove nullability from a cell
+> removeNullable :: CellType -> CellType
+> removeNullable (Nullable x) = x
+> removeNullable x = x
 
+> -- | Compose second cell with first cell's nullability
 > composeNull :: CellType -> CellType -> CellType
 > composeNull _ r@Nullable{} = r
 > composeNull Nullable{} r = Nullable r
 > composeNull _ r = r
 
+> -- | Select the maximum type between first and second cell, and apply
+> --   nullability if needed (according to first cell)
 > composeMaxTypesWithNulls :: CellType -> CellType -> CellType
 > composeMaxTypesWithNulls mp nt =
->   composeNull mp (maximum $ map removeNull [mp,nt])
+>   composeNull mp (maximum $ map removeNullable [mp,nt])
 
+> -- | Pull out format from date or datetime cells
+> getDateFormatFromCT :: CellType -> Maybe String
+> getDateFormatFromCT (CTDateTime s) = Just s
+> getDateFormatFromCT (CTDate s) = Just s
+> getDateFormatFromCT _ = Nothing
+
+> -- | Is this cell a Date or Unknown?
 > isDateOrUnknown :: CellType -> Bool
 > isDateOrUnknown CTDate{} = True
 > isDateOrUnknown Unknown = True
 > isDateOrUnknown _ = False
 
+> -- | Is this cell a DateTime or Unknown?
 > isDateTimeOrUnknown :: CellType -> Bool
 > isDateTimeOrUnknown CTDateTime{} = True
 > isDateTimeOrUnknown Unknown = True
src/Database/Dawdle/Utils.lhs view
@@ -1,7 +1,6 @@ 
 > module Database.Dawdle.Utils
->  (getFstIfJust
->  ,isCTNumber
+>  (isCTNumber
 >  ,isInt
 >  ,maybeToEither
 >  ,allTheSame
@@ -11,24 +10,30 @@ > import Data.Char
 > import Data.List
 
-> getFstIfJust :: (a, Maybe b) -> Maybe a
-> getFstIfJust (a,Just _) = Just a
-> getFstIfJust _ = Nothing
-
+> -- | Is this a number or a float(y) number? (Can give incorrect
+> --   results, for example with version numbers like 0.5.1.1)
 > isCTNumber :: String -> Bool
 > isCTNumber = all (\x -> isNumber x || x `elem` ['-','.'])
 
+> -- | Is this number an int? (Similar to 'isCTNumber', but without a decimal point)
 > isInt :: String -> Bool
 > isInt = all (\x -> isNumber x || x =='-')
 
-> maybeToEither :: String -> Maybe a -> Either String a
+> -- | Promote a Maybe to an Either with an error string
+> maybeToEither :: String -- ^ Error string
+>               -> Maybe a -- ^ Maybe value to check
+>               -> Either String a -- ^ Either Error string or the Just value from the maybe
 > maybeToEither _ (Just x) = Right x
 > maybeToEither s Nothing = Left s
 
+> -- | Check that all the items in a list are identical. This is used to verify lengths in the algorithm.
 > allTheSame :: (Eq a) => [a] -> Bool
 > allTheSame xs = all (== head xs) (tail xs)
 
-> normalizeNames :: [String] -> [String]
+> -- | Normalize names by removing special characters, replacing
+> --   spaces with underscores and lowercasing everything.
+> normalizeNames :: [String] -- ^ List of strings to normalize
+>                -> [String] -- ^ List of normalized strings
 > normalizeNames = map normalize
 >  where
 >    normalize = map toLower . filter (\x -> x=='_' || isAlphaNum x) . intercalate "_" . words