Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
- Remove unused `getTimetableImage` function in `Export/GetImages.hs`
- Refactored various backend text functions and tests to avoid `String` data in favour of `Text` when feasible
- Removed unused files
- Refactored `returnPDF`, `exportTimetablePDFResponse`, and `graphImageResponse` to use stdin instead of temporary `.tex` and `.svg` files

## [0.7.2] - 2025-12-10

Expand Down
17 changes: 7 additions & 10 deletions app/Controllers/Graph.hs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ module Controllers.Graph (graphResponse, index, getGraphJSON, graphImageResponse
import Control.Monad.IO.Class (liftIO)
import Data.Aeson (decode, object, (.=))
import Data.Maybe (fromMaybe)
import Export.ImageConversion (createImageFile)
import Export.ImageConversion (withImageFile)
import Happstack.Server (Response, ServerPart, look, lookBS, lookText', ok, toResponse)
import MasterTemplate (masterTemplate)
import Scripts (graphScripts)
import System.IO (hClose)
import System.IO.Temp (withSystemTempFile)
import System.FilePath ((</>))
import System.IO.Temp (withSystemTempDirectory)
import Text.Blaze ((!))
import qualified Text.Blaze.Html5 as H
import qualified Text.Blaze.Html5.Attributes as A
Expand Down Expand Up @@ -54,13 +54,10 @@ getGraphJSON = do
graphImageResponse :: ServerPart Response
graphImageResponse = do
graphInfo <- look "JsonLocalStorageObj"
liftIO $ withSystemTempFile "graph.svg" $ \svgPath svgHandle -> do
withSystemTempFile "graph.png" $ \pngPath pngHandle -> do
hClose pngHandle
writeActiveGraphImage graphInfo svgHandle
hClose svgHandle
createImageFile svgPath pngPath
readImageData pngPath
liftIO $ withSystemTempDirectory "graph-image" $ \tempDir -> do
let pngPath = tempDir </> "graph.png"
withImageFile pngPath (writeActiveGraphImage graphInfo)
readImageData pngPath

-- | Inserts SVG graph data into Texts, Shapes, and Paths tables
saveGraphJSON :: ServerPart Response
Expand Down
19 changes: 7 additions & 12 deletions app/Controllers/Timetable.hs
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,14 @@ import Data.Time.Calendar.OrdinalDate (fromMondayStartWeek, mondayStartWeek)
import Database.Persist.Sqlite (entityKey, entityVal, selectList, (==.))
import Database.Tables
import Export.GetImages (getActiveTimetable, writeActiveGraphImage)
import Export.ImageConversion (createImageFile)
import Export.ImageConversion (withImageFile)
import Export.LatexGenerator
import Export.PdfGenerator
import Happstack.Server
import MasterTemplate
import Models.Meeting (returnMeeting)
import Scripts
import System.FilePath ((</>))
import System.IO (IOMode (WriteMode), withFile)
import System.IO.Temp (withSystemTempDirectory)
import Text.Blaze ((!))
import qualified Text.Blaze.Html5 as H
Expand Down Expand Up @@ -60,12 +59,8 @@ exportTimetablePDFResponse = do
graphInfo <- look "JsonLocalStorageObj"

liftIO $ withSystemTempDirectory "timetable-pdf" $ \tempDir -> do
let graphSvgPath = tempDir </> "graph.svg"
graphPngPath = tempDir </> "graph.png"

withFile graphSvgPath WriteMode $ \graphSvgHandle ->
writeActiveGraphImage graphInfo graphSvgHandle
createImageFile graphSvgPath graphPngPath
let graphPngPath = tempDir </> "graph.png"
withImageFile graphPngPath (writeActiveGraphImage graphInfo)
fallPngPath <- getActiveTimetable selectedCourses "Fall" tempDir
springPngPath <- getActiveTimetable selectedCourses "Spring" tempDir
pdfName <- returnPDF graphPngPath fallPngPath springPngPath tempDir
Expand All @@ -81,10 +76,10 @@ returnPdfBS pdfFilename = do
-- and springTimetableImg generated in tempDir.
returnPDF :: String -> String -> String -> FilePath -> IO String
returnPDF graphImg fallTimetableImg springTimetableImg tempDir = do
let texName = tempDir </> "timetable.tex"
pdfName = tempDir </> "timetable.pdf"
generateTex [graphImg, fallTimetableImg, springTimetableImg] texName -- generate a temporary TEX file
createPDF texName -- create PDF using TEX
let timetableName = "timetable"
pdfName = tempDir </> (timetableName ++ ".pdf")
texText <- generateTex [graphImg, fallTimetableImg, springTimetableImg]
createPDF texText tempDir timetableName -- create PDF using TEX
return pdfName


Expand Down
16 changes: 14 additions & 2 deletions app/Export/ImageConversion.hs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,22 @@
Description : Includes functions for converting SVG files to PNG.
-}
module Export.ImageConversion
(createImageFile) where
(createImageFile, withImageFile) where

import GHC.IO.Handle.Types
import System.Process (ProcessHandle, createProcess, shell, waitForProcess)
import System.IO (hClose)
import System.Process (ProcessHandle, StdStream (CreatePipe), createProcess, proc, shell, std_in,
waitForProcess)

-- | Opens a new process to convert an SVG read from stdin to a PNG (outName)
withImageFile :: String -> (Handle -> IO ()) -> IO ()
withImageFile outName writeAction = do
(Just hin, _, _, pid) <- createProcess (proc "magick" ["svg:-", outName]) { std_in = CreatePipe }
writeAction hin
hClose hin
putStrLn "Waiting for process..."
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I think you can delete these logging calls (throughout)

_ <- waitForProcess pid
putStrLn "Process Complete"

-- | Opens a new process to convert an SVG (inName) to a PNG (outName)
-- Note: hGetContents can be used to read Handles. Useful when trying to read from
Expand Down
9 changes: 5 additions & 4 deletions app/Export/LatexGenerator.hs
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@ import Text.LaTeX.Packages.Fancyhdr
import Text.LaTeX.Packages.Geometry
import Text.LaTeX.Packages.Graphicx

-- | Create a TEX file named texName that includes all of the images in
-- imageNames
generateTex :: [String] -> String -> IO ()
generateTex imageNames texName = execLaTeXT (buildTex imageNames) >>= renderFile texName
-- | Generates a TEX text that includes all of the images in imageNames
generateTex :: [String] -> IO Text
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that this function is not writing to a file, I think we should be able to make it pure ([String] -> Text)

generateTex imageNames = do
texText <- execLaTeXT (buildTex imageNames)
return $ render texText

-- | Combine the preamble and the document text into a single block of latex
-- code. The document text contains code to insert all of the images in
Expand Down
50 changes: 27 additions & 23 deletions app/Export/PdfGenerator.hs
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,34 @@
module Export.PdfGenerator
(createPDF) where

import Data.List.Utils (replace)
import Data.Text (Text)
import qualified Data.Text.IO as TIO
import GHC.IO.Handle.Types
import System.Directory (removeFile)
import System.Process (ProcessHandle, createProcess, shell, waitForProcess)
import System.IO (hClose)
import System.Process (ProcessHandle, StdStream (CreatePipe), createProcess, proc, std_in,
waitForProcess)

-- | Opens a new process to create a PDF from a TEX (texName) and deletes
-- the tex file and extra files created by pdflatex
createPDF :: String -> IO ()
createPDF texName = do
(_, _, _, pid) <- convertTexToPDF texName
putStrLn "Waiting for a process..."
_ <- waitForProcess pid
let auxFile = replace ".tex" ".aux" texName
logFile = replace ".tex" ".log" texName
mapM_ removeFile [auxFile, logFile, texName]
putStrLn "Process Complete"
-- | Opens a new process to create a PDF from a TEX
createPDF :: Text -> FilePath -> String -> IO ()
createPDF texText outDir jobName = do
(Just hin, _, _, pid) <- convertTexToPDF outDir jobName
TIO.hPutStr hin texText
hClose hin
putStrLn "Waiting for a process..."
_ <- waitForProcess pid
putStrLn "Process Complete"

-- | Create a process to use the pdflatex program to create a PDF from a TEX
-- file (texName). The process is run in nonstop mode and so it will not block
-- if an error occurs. The resulting PDF will have the same filename as texName.
convertTexToPDF :: String -> IO
(Maybe Handle,
Maybe Handle,
Maybe Handle,
ProcessHandle)
convertTexToPDF texName =
createProcess $ shell $ "pdflatex -interaction=nonstopmode " ++ texName
-- read from stdin. The process is run in nonstop mode and so it will not block
-- if an error occurs. The resulting PDF will have the same filename as jobName.
convertTexToPDF :: FilePath -> String -> IO
(Maybe Handle,
Maybe Handle,
Maybe Handle,
ProcessHandle)
convertTexToPDF outDir jobName =
createProcess (proc "pdflatex"
[ "-interaction=nonstopmode"
, "-output-directory=" ++ outDir
, "-jobname=" ++ jobName
]) { std_in = CreatePipe }