Skip to content
Merged
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
9 changes: 5 additions & 4 deletions answers-script.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ library
filepath,
lens,
text,
cmark,
cmark-gfm,
containers,
directory,
cryptohash-sha1,
Expand All @@ -86,8 +86,9 @@ library
transformers,
monad-loops,
aeson,
aeson-pretty,
attoparsec,
cmark-lens
cmark-gfm-lens

-- Directories containing source files.
hs-source-dirs: src
Expand Down Expand Up @@ -155,6 +156,6 @@ test-suite answers-script-test
bytestring,
lens,
attoparsec,
cmark,
cmark-lens,
cmark-gfm,
cmark-gfm-lens,
directory
8 changes: 7 additions & 1 deletion cabal.project
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,10 @@ source-repository-package
type: git
location: https://github.com/ingun37/cmark-lens.git
tag: 4456f10deccb61419ce28811db448c266931190f
--sha256: 0s1zzpd1bgap403awkjar365qxsysw4lwq6i23whjydqj3whqdhb
--sha256: 0s1zzpd1bgap403awkjar365qxsysw4lwq6i23whjydqj3whqdhb

source-repository-package
type: git
location: https://github.com/ingun37/cmark-gfm-lens.git
tag: 7c57b38c7ddb8888df8b3b92800a42a1ac0ac359
--sha256: 0ssznk105b7ai59pr2d6i1if7h4s4f00vs810afpyby6mvfvbvfy
8 changes: 4 additions & 4 deletions src/MatlabMark.hs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@

module MatlabMark (generateMatlabAnswersDB, readMatlabMD) where

import CMark
import CMark.Lens
import CMarkGFM
import CMarkGFM.Lens
import Control.Lens
import Data.Attoparsec.Text qualified as A
import Data.Text qualified as T
Expand Down Expand Up @@ -38,7 +38,7 @@ parseVersion = do
generateMatlabAnswersDB :: FilePath -> Node -> IO ()
generateMatlabAnswersDB outputDirPath node =
let (intro, groups) = groupByProblems node
toDocText = CMark.nodeToCommonmark [] Nothing . Node Nothing DOCUMENT
toDocText = nodeToCommonmark [] Nothing . Node Nothing DOCUMENT
writeMD name nodes = do
D.createDirectory $ outputDirPath F.</> name
TIO.writeFile (outputDirPath F.</> name F.</> "a.md") (toDocText nodes)
Expand All @@ -47,4 +47,4 @@ generateMatlabAnswersDB outputDirPath node =
mapM_ (uncurry writeMD) groups

readMatlabMD :: FilePath -> IO Node
readMatlabMD mdFilePath = CMark.commonmarkToNode [] . changeMatlabMarkdownDelimeters <$> TIO.readFile mdFilePath
readMatlabMD mdFilePath = commonmarkToNode [] [] . changeMatlabMarkdownDelimeters <$> TIO.readFile mdFilePath
8 changes: 5 additions & 3 deletions src/MyLib.hs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@

module MyLib (someFunc) where

import CMark qualified
import CMarkGFM qualified as CMark
import Control.Lens
import Control.Monad qualified as Monad
import Crypto.Hash.SHA1 qualified as SHA (hash)
import Data.Aeson as Json
import Data.Aeson.Encode.Pretty qualified as PrettyJson
import Data.ByteString.Base16 qualified as B16 (encode)
import Data.ByteString.Char8 qualified as C8
import Data.ByteString.Lazy qualified as B
import Data.Foldable qualified as Foldable
import Data.List qualified as List
import Data.List.NonEmpty qualified as NE
Expand Down Expand Up @@ -153,7 +155,7 @@ theWriter source destination prefix (parentPathComponents, item) = do
putStrLn $ " Compiling " ++ src ++ " -> " ++ dst
let safePrefix = List.dropWhileEnd (== '/') $ dropWhile (== '/') prefix
let finalPrefix = List.intercalate "/" (filter (not . null) [safePrefix, "resources", _hash])
TIO.writeFile dst (CMark.nodeToHtml [] $ MyMark.prefixImageUrl finalPrefix $ CMark.commonmarkToNode [] _content)
TIO.writeFile dst (CMark.nodeToHtml [CMark.optUnsafe] [] $ MyMark.prefixImageUrl finalPrefix $ CMark.commonmarkToNode [] [CMark.extTable] _content)

let writeFileType key =
\case
Expand Down Expand Up @@ -193,7 +195,7 @@ someFunc prefixPath source destination = do
let pageDatas = Tree.foldTree folder tree
let pagesDir = destination File.</> "pages"
Dir.createDirectoryIfMissing True pagesDir
let writePageData pg = Json.encodeFile (pagesDir File.</> (pg ^. pageContent . hash) ++ ".json") pg
let writePageData pg = B.writeFile (pagesDir File.</> (pg ^. pageContent . hash) ++ ".json") (PrettyJson.encodePretty pg)
Monad.forM_ pageDatas writePageData
return pageDatas

Expand Down
16 changes: 14 additions & 2 deletions src/MyMark.hs
Original file line number Diff line number Diff line change
@@ -1,17 +1,29 @@
{-# LANGUAGE OverloadedStrings #-}

module MyMark (prefixImageUrl) where

import CMark
import CMarkGFM
import Data.Text qualified as T

tableCellToHTML :: Node -> T.Text
tableCellToHTML (Node _ _ nodes) = "<td>\n" <> T.intercalate "" (map (CMarkGFM.nodeToHtml [] []) (workOnInlineMath nodes)) <> "\n</td>"

tableRowToHTML :: Node -> T.Text
tableRowToHTML (Node _ _ nodes) = "<tr>\n" <> T.intercalate "\n" (map tableCellToHTML nodes) <> "\n</tr>"

tableToInlineHTML :: [TableCellAlignment] -> [Node] -> Node
tableToInlineHTML _ nodes = Node Nothing (HTML_BLOCK $ "<table>\n" <> T.intercalate "\n" (map tableRowToHTML nodes) <> "\n</table>") []

prefixImageUrl :: String -> Node -> Node
prefixImageUrl prefix node =
let safePrefix = T.pack ("/" ++ prefix ++ "/")
replaceUrl url = if T.isPrefixOf (T.pack "http") url then url else safePrefix <> T.dropWhile (== '/') url
recurse (Node posInfo nodeType nodes) =
recurse (Node _ nodeType nodes) =
case nodeType of
IMAGE url title -> Node Nothing (IMAGE (replaceUrl url) title) nodes
PARAGRAPH -> Node Nothing PARAGRAPH $ workOnInlineMath (map (prefixImageUrl prefix) nodes)
CODE_BLOCK info text -> if info == T.pack "math" then mathBlock text else Node Nothing nodeType nodes
TABLE aligns -> tableToInlineHTML aligns nodes
_ -> Node Nothing nodeType (recurse <$> nodes)
in recurse node

Expand Down
40 changes: 19 additions & 21 deletions test/Main.hs
Original file line number Diff line number Diff line change
@@ -1,22 +1,20 @@
{-# LANGUAGE OverloadedStrings #-}

module Main (main) where

import Control.Lens
import Control.Monad
import Data.ByteString qualified as B
import Data.Either qualified as E
import Data.Text qualified as T
import Data.Text.IO qualified as TIO
import Data.Text.Encoding qualified as Encoding
import MyLib qualified
import Data.Text.IO qualified as TIO
import MatlabMark qualified
import System.Directory.Tree qualified as DT
import MyLib qualified
import System.Directory qualified as D
import System.Directory.Tree qualified as DT
import System.FilePath qualified as F
import Test.Hspec
import Control.Lens
import CMark qualified
import CMark.Lens
import Data.Attoparsec.Text qualified as A

main :: IO ()
main = hspec $ do
Expand All @@ -27,6 +25,13 @@ main = hspec $ do
it "asset build test" $ do
testCase

isEmptyDir :: DT.DirTree a -> Bool
isEmptyDir (DT.Dir _ xs) = null xs
isEmptyDir _ = False

makeComparable :: DT.AnchoredDirTree a -> [DT.DirTree a]
makeComparable = filter (not . isEmptyDir) . DT.flattenDir . set DT._name "" . view DT._dirTree

testCase :: IO ()
testCase =
do
Expand All @@ -37,25 +42,18 @@ testCase =
let reader x = do
b <- B.readFile x
return $ E.fromRight (T.pack $ F.takeBaseName x ++ ": " ++ show (B.length b)) $ Encoding.decodeUtf8' b
a <- DT.readDirectoryWith reader dst
let a' = a ^.DT._dirTree
let a'' = DT.flattenDir (set DT._name "" a')
b <- DT.readDirectoryWith reader expect
let b' = b ^.DT._dirTree
let b'' = DT.flattenDir (set DT._name "" b')
zipWithM_ shouldBe a'' b''
a <- makeComparable <$> DT.readDirectoryWith reader dst
b <- makeComparable <$> DT.readDirectoryWith reader expect
zipWithM_ shouldBe a b

matlab :: IO ()
matlab =
do
node <- MatlabMark.readMatlabMD $ "test" F.</> "matlab-short.md"
let dst = "test" F.</> "matlab-dst"
D.removeDirectoryRecursive dst
D.createDirectoryIfMissing True dst
MatlabMark.generateMatlabAnswersDB dst node
a <- DT.readDirectoryWith TIO.readFile dst
let a' = a ^.DT._dirTree
let a'' = DT.flattenDir (set DT._name "" a')
b <- DT.readDirectoryWith TIO.readFile ("test" F.</> "matlab-expect")
let b' = b ^.DT._dirTree
let b'' = DT.flattenDir (set DT._name "" b')
zipWithM_ shouldBe a'' b''
a <- makeComparable <$> DT.readDirectoryWith TIO.readFile dst
b <- makeComparable <$> DT.readDirectoryWith TIO.readFile ("test" F.</> "matlab-expect")
zipWithM_ shouldBe a b
17 changes: 17 additions & 0 deletions test/expect/pages/0af5b76897bd59b64b3d577b459c75a1b5b6b3ca.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"_childPageContents": [
{
"_answers": 0,
"_attributes": {},
"_hash": "ba45627f4d5702a2d14eaced7927fd3798c60167",
"_pageTitle": "11. Equilibrium and Elasticity"
}
],
"_pageContent": {
"_answers": 0,
"_attributes": {},
"_hash": "0af5b76897bd59b64b3d577b459c75a1b5b6b3ca",
"_pageTitle": "University Physics with Modern Physics"
},
"_parentHash": "2aed5404c83f7a46aa249e0a6328af756b19d513"
}
31 changes: 30 additions & 1 deletion test/expect/pages/2125c437aaac928d5852224ff00a83f9d9776dda.json
Original file line number Diff line number Diff line change
@@ -1 +1,30 @@
{"_pageContent":{"_pageTitle":"5.1 Continuous Mappings","_hash":"2125c437aaac928d5852224ff00a83f9d9776dda","_attributes":{},"_answers":1},"_parentHash":"6c11e3a31b4b8bd07bdef3f87887ab202a568679","_childPageContents":[{"_pageTitle":"8","_hash":"3e8d61043ed9bdb6b3cd4f197f0203bbdcb13db1","_attributes":{"a.md":{"_time":"2020-10-18T14:48:51+09:00","_attributeFile":{"_content":"I'll refer $`\\mathcal{T}`$ as $`\\mathcal{T}_X`$, $`\\mathcal{T}_1`$ as $`\\mathcal{T}_Y`$, $`\\mathcal{T}_2`$ as $`\\mathcal{T}_A`$, $`\\mathcal{T}_3`$ as $`\\mathcal{T}_B`$.\n\nLet's define $`x \\in X`$ and $`U \\in \\mathcal{T}_Y`$ such that $`f(x) \\in U`$. By definition of continuous mapping, there must exists a $`V`$ such that $`x \\in V \\in \\mathcal{T}_X`$ and $`f(x) \\in fV \\subseteq U`$.\n\nIf for any $`x \\in A`$ and any B-induced open set $`U_B \\in \\mathcal{T}_B`$, which must imply existance of $`U \\in \\mathcal{T}_Y`$, such that $`g(x) \\in U_B`$,\n\n![](IMG_44AF9D4BED6C-1.jpeg)\n\n...there exists an A-induced open set $`V_A`$, which must imply existance of $`V \\in \\mathcal{T}_A`$, such that the image $`gV_A`$ satisfies $`g(x) \\in gV_A \\subseteq U_B`$, then $`g`$ must be continuous. What we have to know is that if the image $`gV_A`$is subset of $`U_B`$, in other words, every $`x \\in V_A`$ will satisfiy $`g(x) \\in U_B`$. Let's proove it.\n\n![](IMG_E7FB922B8E70-1.jpeg)\n\n```math\n\\begin{aligned}\n & x \\in V_A \\\\\n & \\rightarrow x \\in V \\\\\n & \\rightarrow g(x) \\in gV \\\\\n & \\rightarrow g(x) \\in \\text{ some } U & \\text{ since } f \\text{ is continuous} \\\\\n & \\rightarrow g(x) \\in U \\cap B & \\text{ since codomain of } g \\text{ is } B \\\\\n & \\rightarrow g(x) \\in U_B\n\\end{aligned}\n```"}},"q.md":{"_time":"2020-04-12T01:08:01+09:00","_attributeFile":{"_content":"Let $`(X,\\mathcal{T})`$ and $`(Y,\\mathcal{T}_1)`$ be topological spaces and $`f:(X,\\mathcal{T}) \\rightarrow (Y,\\mathcal{T}_1)`$ a continuous mapping. Let $`A`$ be a subset of $`X`$, $`\\mathcal{T}_2`$ the induced topology on $`A`$, $`B = f(A)`$, $`\\mathcal{T}_3`$ the induced topology on $`B`$ and $`g:(A,\\mathcal{T}_2) \\rightarrow (B,\\mathcal{T}_3)`$ the restriction of $`f`$ to $`A`$. Prove that $`g`$ is continuous."}}},"_answers":1}]}
{
"_childPageContents": [
{
"_answers": 1,
"_attributes": {
"a.md": {
"_attributeFile": {
"_content": "I'll refer $`\\mathcal{T}`$ as $`\\mathcal{T}_X`$, $`\\mathcal{T}_1`$ as $`\\mathcal{T}_Y`$, $`\\mathcal{T}_2`$ as $`\\mathcal{T}_A`$, $`\\mathcal{T}_3`$ as $`\\mathcal{T}_B`$.\n\nLet's define $`x \\in X`$ and $`U \\in \\mathcal{T}_Y`$ such that $`f(x) \\in U`$. By definition of continuous mapping, there must exists a $`V`$ such that $`x \\in V \\in \\mathcal{T}_X`$ and $`f(x) \\in fV \\subseteq U`$.\n\nIf for any $`x \\in A`$ and any B-induced open set $`U_B \\in \\mathcal{T}_B`$, which must imply existance of $`U \\in \\mathcal{T}_Y`$, such that $`g(x) \\in U_B`$,\n\n![](IMG_44AF9D4BED6C-1.jpeg)\n\n...there exists an A-induced open set $`V_A`$, which must imply existance of $`V \\in \\mathcal{T}_A`$, such that the image $`gV_A`$ satisfies $`g(x) \\in gV_A \\subseteq U_B`$, then $`g`$ must be continuous. What we have to know is that if the image $`gV_A`$is subset of $`U_B`$, in other words, every $`x \\in V_A`$ will satisfiy $`g(x) \\in U_B`$. Let's proove it.\n\n![](IMG_E7FB922B8E70-1.jpeg)\n\n```math\n\\begin{aligned}\n & x \\in V_A \\\\\n & \\rightarrow x \\in V \\\\\n & \\rightarrow g(x) \\in gV \\\\\n & \\rightarrow g(x) \\in \\text{ some } U & \\text{ since } f \\text{ is continuous} \\\\\n & \\rightarrow g(x) \\in U \\cap B & \\text{ since codomain of } g \\text{ is } B \\\\\n & \\rightarrow g(x) \\in U_B\n\\end{aligned}\n```"
},
"_time": "2020-10-18T14:48:51+09:00"
},
"q.md": {
"_attributeFile": {
"_content": "Let $`(X,\\mathcal{T})`$ and $`(Y,\\mathcal{T}_1)`$ be topological spaces and $`f:(X,\\mathcal{T}) \\rightarrow (Y,\\mathcal{T}_1)`$ a continuous mapping. Let $`A`$ be a subset of $`X`$, $`\\mathcal{T}_2`$ the induced topology on $`A`$, $`B = f(A)`$, $`\\mathcal{T}_3`$ the induced topology on $`B`$ and $`g:(A,\\mathcal{T}_2) \\rightarrow (B,\\mathcal{T}_3)`$ the restriction of $`f`$ to $`A`$. Prove that $`g`$ is continuous."
},
"_time": "2020-04-12T01:08:01+09:00"
}
},
"_hash": "3e8d61043ed9bdb6b3cd4f197f0203bbdcb13db1",
"_pageTitle": "8"
}
],
"_pageContent": {
"_answers": 1,
"_attributes": {},
"_hash": "2125c437aaac928d5852224ff00a83f9d9776dda",
"_pageTitle": "5.1 Continuous Mappings"
},
"_parentHash": "6c11e3a31b4b8bd07bdef3f87887ab202a568679"
}
51 changes: 50 additions & 1 deletion test/expect/pages/2aed5404c83f7a46aa249e0a6328af756b19d513.json
Original file line number Diff line number Diff line change
@@ -1 +1,50 @@
{"_pageContent":{"_pageTitle":"books","_hash":"2aed5404c83f7a46aa249e0a6328af756b19d513","_attributes":{"q.md":{"_time":"2022-09-25T22:34:28+09:00","_attributeFile":{"_content":"# DON'T PANIC\n\nAlthough it has many omissions and contains much that is apocryphal, or at least wildly inaccurate, but it scores over the other answers over the internet in few important respects. First, the way it is written is very subjective, and second, it has the words DON'T PANIC inscribed in large friendly letters on its home."}}},"_answers":3},"_parentHash":"da39a3ee5e6b4b0d3255bfef95601890afd80709","_childPageContents":[{"_pageTitle":"Topology Without Tears","_hash":"4c1513c92422dc16b3c5f13bd03d34ba0feeb6df","_attributes":{"author.txt":{"_time":"2022-09-25T22:34:28+09:00","_attributeFile":{"_content":"Sidney A. Morris"}}},"_answers":1},{"_pageTitle":"Category Theory For Programmers","_hash":"b614f31d04b3bc2b3d23ee4337475251429e5a9f","_attributes":{"author.txt":{"_time":"2022-09-25T22:34:28+09:00","_attributeFile":{"_content":"Bartosz Milewski"}}},"_answers":2}]}
{
"_childPageContents": [
{
"_answers": 0,
"_attributes": {},
"_hash": "0af5b76897bd59b64b3d577b459c75a1b5b6b3ca",
"_pageTitle": "University Physics with Modern Physics"
},
{
"_answers": 1,
"_attributes": {
"author.txt": {
"_attributeFile": {
"_content": "Sidney A. Morris"
},
"_time": "2022-09-25T22:34:28+09:00"
}
},
"_hash": "4c1513c92422dc16b3c5f13bd03d34ba0feeb6df",
"_pageTitle": "Topology Without Tears"
},
{
"_answers": 2,
"_attributes": {
"author.txt": {
"_attributeFile": {
"_content": "Bartosz Milewski"
},
"_time": "2022-09-25T22:34:28+09:00"
}
},
"_hash": "b614f31d04b3bc2b3d23ee4337475251429e5a9f",
"_pageTitle": "Category Theory For Programmers"
}
],
"_pageContent": {
"_answers": 3,
"_attributes": {
"q.md": {
"_attributeFile": {
"_content": "# DON'T PANIC\n\nAlthough it has many omissions and contains much that is apocryphal, or at least wildly inaccurate, but it scores over the other answers over the internet in few important respects. First, the way it is written is very subjective, and second, it has the words DON'T PANIC inscribed in large friendly letters on its home."
},
"_time": "2022-09-25T22:34:28+09:00"
}
},
"_hash": "2aed5404c83f7a46aa249e0a6328af756b19d513",
"_pageTitle": "books"
},
"_parentHash": "da39a3ee5e6b4b0d3255bfef95601890afd80709"
}
Loading