mmsyn4 0.2.0.0 → 0.3.0.0
raw patch · 5 files changed
+142/−161 lines, 5 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
- MMSyn4: isWindows :: Bool
+ MMSyn4: getFormat :: String -> String
- MMSyn4: process2 :: IO ()
+ MMSyn4: process2 :: String -> String -> String -> IO ()
Files
- ChangeLog.md +7/−0
- MMSyn4.hs +88/−129
- Main.hs +23/−25
- README.markdown +23/−6
- mmsyn4.cabal +1/−1
ChangeLog.md view
@@ -41,3 +41,10 @@ additional module MMSyn4 with almost all functions in it (they mostly are not exported because of their specific usage). Added possibility to specify another basic file to work with except 1.csv. Added a possibility to specify an output graphics format. Some code and documentation improvements.++## 0.3.0.0 -- 2020-05-16++* Third version. The code is almost fully rewritten. Fixed issues with wrong processing and formatting of the file. Fixed issue with double +printed prompt message in the MMSyn4 module. Changed the exported and imported functions and dependencies for modules. Added the possibility +to specify three additional command line arguments to specify the command to be executed to create the visualization file and its format. +So, now the program behavior can be basically controlled with command line arguments.
MMSyn4.hs view
@@ -6,15 +6,16 @@ Maintainer : olexandr543@yahoo.com Stability : Experimental -A program @mmsyn4@ converts a specially formated @.csv@ file with a colon as a field separator obtained from the electronic teble +A program @mmsyn4@ converts a specially formated @.csv@ file with a colon as a field separator obtained from the electronic table into a visualized by GraphViz graph in the one of the supported by GraphViz graphics format. The proper GraphViz installation is required. -} -module MMSyn4 (isWindows,process2) where+module MMSyn4 (getFormat,process2)+ where +import GHC.Base (mconcat) import Data.List (nub) import System.Info (os)-import System.Directory import System.CPUTime (getCPUTime) import System.Process (callCommand) import CaseBi (getBFst')@@ -32,7 +33,7 @@ divideString :: (Char -> Bool) -> String -> [String] divideString p xs | null xs = []- | otherwise = let (zs,ys) = break p xs in zs:divideString p (drop 1 ys)+ | otherwise = let (zs,ys) = break p xs in zs:(if null ys then [""] else divideString p (drop 1 ys)) isEscapeChar :: Char -> Bool isEscapeChar x = x == '\n' || x == '\r'@@ -43,133 +44,82 @@ | let ts = dropWhile isSep ys in all isEscapeChar ts || null ts = dropEmptyLines yss | otherwise = ys:dropEmptyLines yss -cells :: String -> [[String]]-cells = map (divideString isSep) . dropEmptyLines . lines--changeCell :: String -> String -> String -> String-changeCell xs ys zs - | null zs = ys- | otherwise = xs--isTruncated :: String -> String -> Bool-isTruncated [] (v:_) = True-isTruncated _ _ = False--toBoolList :: [String] -> [Bool]-toBoolList yss = zipWith isTruncated yss ([]:yss)- -countChanged :: [Bool] -> Int-countChanged z = length (takeWhile not z)--createSecondLine :: [String] -> [String] -> [String]-createSecondLine xss yss = take (countChanged (toBoolList yss)) tss- where tss = zipWith3 changeCell xss yss uss- uss = tail yss ++ [""]- -lineN :: Int -> [[String]] -> [String]-lineN n xss = last $! take n xss--lineN' :: Int -> [[String]] -> [String]-lineN' n xss = last $! take (n-1) xss--createNthLine :: [[String]] -> Int -> [String]-createNthLine xss n - | n < 1 || n > length xss = error "Undefined line!"- | n == 1 = concat . take 1 $ xss- | otherwise = createSecondLine (lineN' n xss) (lineN n xss)- -fillEmptyCells :: [[String]] -> [[String]]-fillEmptyCells xss = map (createNthLine xss) [1..length xss]--changeNthLine :: [String] -> String-changeNthLine xs = "\"" ++ concatMap (++"\"->\"") xs ++ endOfLineGv--dropLast :: String -> String-dropLast xs - | isWindows = if drop (length xs - 5) xs == "->\"\r\n" - then dropLast (take (length xs - 5) xs) - else xs ++ ";\r\n"- | drop (length xs - 4) xs == "->\"\n" = dropLast (take (length xs - 4) xs) - | otherwise = xs ++ ";\n"+cells :: String -> V.Vector [String]+cells = V.map (divideString isSep) . V.fromList . dropEmptyLines . map (\rs -> if drop (length rs - 1) rs == "\r" then init rs else rs) . lines -dropDouble :: String -> String-dropDouble (x:y:xs) - | x == '\"' && y == '\"' = dropDouble (y:xs)- | otherwise = x:dropDouble (y:xs)-dropDouble xs = xs- -dropNull :: [String] -> [String]-dropNull = filter (not . null)+processCells :: String -> V.Vector [String] -> String+processCells xs v = makeRecordGv xs . convertElemsToStringGv . filterNeeded . changeNeededCells $ v -processCellsA :: String -> [[String]]-processCellsA = fillEmptyCells . cells+processCellsG :: String -> String -> String+processCellsG xs = processCells xs . cells -processCellsZ :: [[String]] -> [String]-processCellsZ = map (dropDouble . dropLast . changeNthLine)+-- | Do not change the lengths of element lists+changeNeededCells :: V.Vector [String] -> V.Vector [String]+changeNeededCells v = V.generate (V.length v) (\i -> changeLine i v) -takeColumn :: Int -> [[String]] -> [String]-takeColumn n xss - | null xss = error "Empty list in takeColumn!"- | if n < 1 then True else n > (length . head $ xss) = error "Undefined column!"- | otherwise = map (concat . take 1 . drop (n-1)) xss+-- | Changes every line by changing (if needed) one empty String to the needed one non-empty. It is necessary for this to find the parent cell for the +-- line in the previous elements of the 'V.Vector'. The contents of the cell (if exist) are substituted instead of the empty 'String' in the line being +-- processed. Afterwards, drops all the preceding empty strings in the line. The length of the line now is not constant.+changeLine :: Int -> V.Vector [String] -> [String]+changeLine i v = + let n = length . takeWhile null . V.unsafeIndex v $ i+ xs = parentCellContents n i v in if null xs then drop n . V.unsafeIndex v $ i else xs:(drop n . V.unsafeIndex v $ i) --- | m is a column number of the cell, which 'findParentCell' function returns, n is a line number of the cell, which calls 'findParentCell' function-findParentCell :: Int -> Int -> [[String]] -> String-findParentCell m n xss - | if n < 2 then True else n > length xss = error "Undefined column!"- | otherwise = last . dropNull . take (n - 1) . takeColumn m $ xss+parentCellContents :: Int -> Int -> V.Vector [String] -> String+parentCellContents n i v + | n == 0 || i == 0 = []+ | otherwise = V.unsafeLast . V.filter (not . null) . (\v1 -> V.unsafeSlice 0 i v1) . V.map (!! (n - 1)) $ v -createNthLine2 :: [[String]] -> Int -> [String]-createNthLine2 x n - | if n < 1 then True else n > length x = error "Undefined line!"- | n == 1 = dropNull . head $ x- | null . head . lineN n $ x = findParentCell (length . takeWhile null . lineN n $ x) n x:dropNull (lineN n x)- | otherwise = dropNull (lineN n x)- -fillEmptyCells2 :: [[String]] -> [[String]]-fillEmptyCells2 xss = map (createNthLine2 xss) [length xss,length xss - 1..1]+-- | Change the lengths of element lists by dropping the last empty strings in every element.+filterNeeded :: V.Vector [String] -> V.Vector [String]+filterNeeded = V.map (takeWhile (not . null)) -beginsWithAtSign :: String -> Bool-beginsWithAtSign xs = if take 1 xs == "@" then True else take 2 xs == "\"@"+-- | Makes conversion for every line +convertElemsToStringGv :: V.Vector [String] -> V.Vector String+convertElemsToStringGv v = (V.map convertLineToStrGv v) V.++ (findAndMakeFilledWithClr v) -findFilledWithColor :: [[String]] -> [String]-findFilledWithColor = concatMap (filter beginsWithAtSign)+convertLineToStrGv :: [String] -> String+convertLineToStrGv xss = "\"" ++ (let ys = concatMap (++"\"->\"") xss in take (length ys - 3) ys) ++ endOfLineGv endOfLineGv :: String endOfLineGv | isWindows = "\r\n" | otherwise = "\n" -makeFilledWithColor :: [String] -> String-makeFilledWithColor xss = concat (nub . zipWith (++) xss $ (repeat (" [style=filled, fillcolor=\"#ffffba\"];" ++ endOfLineGv)))+findAndMakeFilledWithClr :: V.Vector [String] -> V.Vector String+findAndMakeFilledWithClr = + V.singleton . concatMap (('\"':) . (++ "\" [style=filled, fillcolor=\"#ffffba\"];" ++ endOfLineGv)) . nub . mconcat . V.toList . V.map lineWithAtSign -processCells :: String -> String-processCells = concat . nub . processCellsZ . fillEmptyCells2 . processCellsA+-- | In every list (representing a line) returns only those strings that begin with at-sign.+lineWithAtSign :: [String] -> [String]+lineWithAtSign = filter beginsWithAtSign -combineCells :: String -> String -> String-combineCells x0 x = let (y,z) = (x,x) in concat ["strict digraph 1 {", endOfLineGv, "overlap=false", endOfLineGv, "splines=", - getBFst' ("true", V.fromList [("0", "false"), ("1", "true"), ("2", "ortho"), ("3", "polyline")]) x0, endOfLineGv, processCells z, - makeFilledWithColor . findFilledWithColor . fillEmptyCells2 . processCellsA $ y, endOfLineGv, "}", endOfLineGv]+beginsWithAtSign :: String -> Bool+beginsWithAtSign xs = if take 1 xs == "@" then True else take 2 xs == "\"@" --- | Process a \"1.csv\" file. -process2 :: IO ()-process2 = do - xs <- readFile "1.csv"- if length xs > 0 - then do+-- | Makes all needed additions and synthesizes into a single String ready to be recorded to the .gv file.+makeRecordGv :: String -> V.Vector String -> String+makeRecordGv xs v = mconcat ["strict digraph 1 {", endOfLineGv, "overlap=false", endOfLineGv, "splines=", + getBFst' ("true", V.fromList [("0", "false"), ("1", "true"), ("2", "ortho"), ("3", "polyline")]) xs, endOfLineGv, + mconcat . V.toList $ v, "}", endOfLineGv]++-- | Processes the given text (the first 'String' argument). The second one is used to get a name of the command to be +-- executed to obtain a visualization file. The third argument is used for the 'getFormat'. +process2 :: String -> String -> String -> IO ()+process2 text xxs yys + | length text > 0 = do ts <- getCPUTime [zs,x2,remAt] <- processCtrl- let x0 = take 1 x2+ let xs = take 1 x2 if take 1 remAt == "y" then do - let ys = filter (/='@') . combineCells x0 $ xs in writeFile ("new." ++ show ts ++ "." ++ zs ++ ".gv") ys+ let ys = filter (/='@') . processCellsG xs $ text in writeFile (show ts ++ "." ++ zs ++ ".gv") ys putStrLn "The visualization will be created without the at-sign."- processFile 'n' ts zs- removeFile $ show ts ++ "." ++ zs ++ ".csv"+ processFile 'n' ts zs xxs yys else do - let ys = combineCells x0 xs in writeFile (show ts ++ "." ++ zs ++ ".gv") ys+ let ys = processCellsG xs text in writeFile ("at." ++ show ts ++ "." ++ zs ++ ".gv") ys putStrLn "The visualization will be created with the at-sign preserved."- processFile 'a' ts zs- else error "Epmty file 1.csv!"+ processFile 'a' ts zs xxs yys+ | otherwise = error "Empty text to be processed! " processCtrl :: IO [String] processCtrl = mapM procCtrl [1..3]@@ -186,24 +136,30 @@ getLine procCtrl _ = putStrLn "Would you like to remove all \'@\' signs from the visualization file?" >> getLine -processFile :: Char -> Integer -> String -> IO ()-processFile w ts zs = do- renameFile "1.csv" (show ts ++ "." ++ zs ++ ".csv")+processFile :: Char -> Integer -> String -> String -> String -> IO ()+processFile w t zs xxs yys = do if all (isJust . showE) ["fdp","twopi","circo","neato","sfdp","dot","patchwork","osage"]- then do - putStrLn "Please, specify the GraphViz command: "- mapM_ printGraphFilter ["d","f","t","c","n","s","p","o"]- putStrLn "otherwise there will be used the default sfdp"- [vs,spec] <- mapM getFormat1 [1,2]- let u = take 1 vs in if null u || u == "\n" || u == "\x0000" - then error "Please, specify the needed character" - else do - let temp = getBFst' (fromJust (showE "sfdp"), V.fromList (map (\(x, y) -> (x, fromJust y)) [("c", showE "circo"), ("d", showE "dot"), - ("f", showE "fdp"), ("n", showE "neato"), ("o",showE "osage"), ("p", showE "patchwork"), ("s", showE "sfdp"), ("t", showE "twopi")]))- q = getFormat spec - callCommand $ temp u ++ (if w == 'n' then " -T" ++ q ++ " new." else " -T" ++ q ++ " ") ++ show ts ++ "." ++ zs ++ ".gv -O "- else error "Please, install the GraphViz so that its executables are in the directories mentioned in the variable PATH!"- + then processFile1 w t zs xxs yys+ else error "MMSyn4.processFile: Please, install the GraphViz so that its executables are in the directories mentioned in the variable PATH!"++processFile1 :: Char -> Integer -> String -> String -> String -> IO ()+processFile1 w t zs xxs yys = do + [vs,spec] <- proc2Params xxs yys+ let u = take 1 vs + if null u || u == "\n" || u == "\x0000" + then error "MMSyn4.processFile1: Please, specify the needed character." + else do + let temp = getBFst' (fromJust (showE "sfdp"), V.fromList (map (\(x, y) -> (x, fromJust y)) [("c", showE "circo"), ("d", showE "dot"), + ("f", showE "fdp"), ("n", showE "neato"), ("o",showE "osage"), ("p", showE "patchwork"), ("s", showE "sfdp"), ("t", showE "twopi")]))+ q = getFormat spec + callCommand $ temp u ++ (if w == 'n' then " -T" ++ q ++ " " else " -T" ++ q ++ " at.") ++ show t ++ "." ++ zs ++ ".gv -O "++proc2Params :: String -> String -> IO [String]+proc2Params xxs yys + | null xxs = if null yys then mapM getFormat1 [1,2] else do { vs <- getFormat1 1 ; return [vs,yys] }+ | null yys = do { spec <- getFormat1 2 ; return [xxs,spec] }+ | otherwise = return [xxs,yys]+ specFormatFile :: IO String specFormatFile = do putStrLn "Please, specify the GraphViz output format for the file: "@@ -217,16 +173,19 @@ mapM_ printGraphFilter ["d","f","t","c","n","s","p","o"] putStrLn "otherwise there will be used the default sfdp" getLine-getFormat1 2 = specFormatFile - +getFormat1 _ = specFormatFile ++-- | For the given argument (usually of two characters) return the full form of the file format to be generated by GraphViz and @mmsyn4@. The default one +-- is \"svg\". getFormat :: String -> String getFormat = getBFst' ("svg",V.fromList [("cm", "cmapx"),("do", "dot"),("fi", "fig"),("gi", "gif"),("im", "imap"),- ("je", "jpeg"),("js", "json"),("jp", "jpg"),("pd", "pdf"),("pn", "png"),("ps", "ps"),("sv", "svg"),("sz", "svgz"),("xd", "xdot")])+ ("je", "jpeg"),("jp", "jpg"),("js", "json"),("pd", "pdf"),("pn", "png"),("ps", "ps"),("sv", "svg"),("sz", "svgz"),("xd", "xdot")]) printFormF :: String -> IO () printFormF xs = putStrLn $ show xs ++ " -- for -T" ++ getBFst' ("svg",V.fromList [("cm", "cmapx"),("do", "dot"),("fi", "fig"),("gi", "gif"),("im", "imap"),- ("je", "jpeg"),("js", "json"),("jp", "jpg"),("pd", "pdf"),("pn", "png"),("ps", "ps"),("sv", "svg"),("sz", "svgz"),("xd", "xdot")]) xs ++ "\""+ ("je", "jpeg"),("jp", "jpg"),("js", "json"),("pd", "pdf"),("pn", "png"),("ps", "ps"),("sv", "svg"),("sz", "svgz"),("xd", "xdot")]) xs ++ "\"" printGraphFilter :: String -> IO () printGraphFilter xs = putStrLn $ show (take 1 xs) ++ " -- for " ++ getBFst' ("sfdp", V.fromList [("c", "circo"), ("d", "dot"), ("f", "fdp"), ("n", "neato"), ("o", "osage"), ("p", "patchwork"), ("s", "sfdp"), ("t", "twopi")]) (take 1 xs)+
Main.hs view
@@ -6,7 +6,7 @@ Maintainer : olexandr543@yahoo.com Stability : Experimental -A program @mmsyn4@ converts a specially formated @.csv@ file with a colon as a field separator obtained from the electronic teble +A program @mmsyn4@ converts a specially formated @.csv@ file with a colon as a field separator obtained from the electronic table into a visualized by GraphViz graph in the one of the supported by GraphViz format. The proper GraphViz installation is required. -} @@ -15,8 +15,10 @@ import MMSyn4 (process2) import System.Environment (getArgs) import System.Directory+import Data.List (isPrefixOf) -{-| Usage+{-| Usage with only the first command line+ 1. After installation the executable mmsyn4 is created. Afterwards, it is used to process files. So, open an office spreadsheet program, e. g. LibreOffice Calc. 2. Begin to enter the text in the cells. You can use Unicode characters. No quotation marks should be used, instead use some @@ -32,40 +34,36 @@ It must be located above the text on the new line or even further to the right above. Otherwise, the program will produce no reasonably useful output. 9. After entering all the text, export the sheet as a \"\*.csv\" file using colons (\':\') as separator in the working directory. Otherwise, the program won’t work.-10. Run the apprapriate executable mmsyn4 in the terminal or from the command line while being in the directory with the created .csv file. +10. Run the appropriate executable mmsyn4 in the terminal or from the command line while being in the directory with the created .csv file. Specify as a command line argument its name. While executing a program enter a word name of the .csv file to be saved. DO use alphanumeric symbols and dashes if needed. Then specify the needed visualization scheme by specifying the appropriate character in the terminal and the format of the resulting visualization file (refer to GraphViz documentation for the default list of formats). 11. Your first visualization is then created. 12. Save the spreadsheet document as a spreadsheet file (if you worked with spreadsheets, otherwise this step can be omitted). 13. Repeat the steps from 2 to 12 as needed to produce more visualizations. -14. Afterwards, you have a list of graphics files, a list of .gv files -- source files for Graphviz -- and a list of csv files, and a saved spreadsheet file. +14. Afterwards, you have a list of graphics files, a list of .gv files -- source files for Graphviz -- and a saved spreadsheet file. Then you can use the produced visualizations for some other documents. +Usage of the next command line arguments after the first one++Since the version 0.3.0.0 mmsyn4 supports the following further command lines arguments (given after the first one -- see above):++-c... -- dots are instead of one letter to specify the first character of the GraphViz command (e. g. \'n\' -- for \'neato\')++-f... -- dots are instead of two letters to specify the format (according to the 'getFormat') of the GraphViz command (e. g. \'jp\' -- for \'jpg\')++They can be given in any combinations (if needed) or omitted. In the latter one case the program will prompt you the needed information. -} main :: IO () main = do args <- getArgs let arg0 = concat . take 1 $ args- if arg0 == "1.csv" + arggs = drop 1 args+ xxs = concatMap (take 1 . drop 2) . filter ("-c" `isPrefixOf`) $ arggs+ yys = concatMap (take 2 . drop 2) . filter ("-f" `isPrefixOf`) $ arggs+ exI <- doesFileExist arg0+ if exI then do - exi <- doesFileExist arg0- if exi - then do- copyFile "1.csv" "1.csv.saved"- process2- else putStrLn "Please, create the named file and run the program again! "- else do - exi <- doesFileExist arg0- if exi - then do- exi2 <- doesFileExist "1.csv"- if exi2 - then do - copyFile "1.csv" "1.csv.saved"- copyFile arg0 "1.csv"- process2- else do- copyFile arg0 "1.csv"- process2- else putStrLn "Please, create the named file and run the program again! "+ text2 <- readFile arg0+ process2 text2 xxs yys+ else putStrLn "No file specified exists in the current directory! "
README.markdown view
@@ -40,11 +40,11 @@ in the working directory. Otherwise, the program won’t work. -10. Run the apprapriate executable mmsyn4 in the terminal +10. Run the appropriate executable mmsyn4 in the terminal or from the command line while being in the directory with the created .csv file. Specify as a command line argument its name. While executing a program enter - a word name of the .csv file to be saved. DO use + a basic name of the file to be saved. DO use alphanumeric symbols and dashes if needed. Then specify the needed visualization scheme by specifying the appropriate character @@ -63,7 +63,24 @@ 13. Repeat the steps from 2 to 12 as needed to produce more visualizations. -14. Afterwards, you have a list of svg files, a list of .gv - files as source files for Graphviz, and a list of csv- files, and a saved spreadsheet file. Then you can use- the produced visualizations for some other documents.+14. Afterwards, you have a list of graphics files, a list of .gv + files as source files for Graphviz, and a saved spreadsheet file. + Then you can use the produced visualizations for some other + documents.++ ***** Usage of the Next Command Line Arguments after the First One *****+ ------------------------------------------------------------------------++Since the version 0.3.0.0 mmsyn4 supports the following further +command lines arguments (given after the first one -- see above):++-c... -- dots are instead of one letter to specify the first character + of the GraphViz command (e. g. \'n\' -- for \'neato\')++-f... -- dots are instead of two letters to specify the format (according to + the 'getFormat') of the GraphViz command (e. g. \'jp\' -- for \'jpg\')++They can be given in any combinations (if needed) or omitted. In the latter +one case the program will prompt you the needed information (but this is +not the case for a separator, which must be specified in such a way to be +used instead).
mmsyn4.cabal view
@@ -2,7 +2,7 @@ -- documentation, see http://haskell.org/cabal/users-guide/ name: mmsyn4-version: 0.2.0.0+version: 0.3.0.0 synopsis: The "glue" between electronic tables and GraphViz description: The program mmsyn4 converts a specially formated .csv file with a colon as a field separator obtained from the electronic table into a visualized by GraphViz graph. homepage: https://hackage.haskell.org/package/mmsyn4