ArtDecoCard.purs 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. module LinkedIn.ArtDecoCard where
  2. import Prelude
  3. import Control.Monad.Maybe.Trans (MaybeT)
  4. import Data.Either (Either(..), hush)
  5. import Data.Generic.Rep (class Generic)
  6. import Data.List.NonEmpty (NonEmptyList)
  7. import Data.List.NonEmpty as NEL
  8. import Data.List.Types (List, NonEmptyList)
  9. import Data.Maybe (Maybe(..), fromJust)
  10. import Data.Show.Generic (genericShow)
  11. import Data.Traversable (sequence, traverse)
  12. import Debug (trace)
  13. import Effect (Effect)
  14. import Effect.Exception (try)
  15. import LinkedIn (DetachedNode, toDetached)
  16. import Partial.Unsafe (unsafePartial)
  17. import Web.DOM (Node, ParentNode)
  18. import Web.DOM.Element as E
  19. import Web.DOM.NodeList as NL
  20. import Web.DOM.ParentNode (QuerySelector(..), querySelector, querySelectorAll)
  21. type Parser a = Node → Effect (Either String a)
  22. data ArtDecoPvsEntitySubComponent = ArtDecoPvsEntitySubComponent DetachedNode
  23. derive instance Generic ArtDecoPvsEntitySubComponent _
  24. instance Show ArtDecoPvsEntitySubComponent where
  25. show = genericShow
  26. parseArtDecoPvsEntitySubComponent ∷ Parser ArtDecoPvsEntitySubComponent
  27. parseArtDecoPvsEntitySubComponent n = do
  28. content <- queryAndDetachOne "span[aria-hidden=true]" n
  29. pure $ ado
  30. c <- content
  31. in ArtDecoPvsEntitySubComponent c
  32. data ArtDecoCenterContent = ArtDecoCenterContent (NonEmptyList ArtDecoPvsEntitySubComponent)
  33. derive instance Generic ArtDecoCenterContent _
  34. instance Show ArtDecoCenterContent where
  35. show = genericShow
  36. parseArtDecoCenterContent ∷ Parser ArtDecoCenterContent
  37. parseArtDecoCenterContent n = do
  38. list <- queryManyAndParse ":scope > ul > li" parseArtDecoPvsEntitySubComponent n
  39. pure $ ado
  40. l <- list
  41. in ArtDecoCenterContent l
  42. data ArtDecoCenterHeader = ArtDecoCenterHeader {
  43. bold :: DetachedNode,
  44. normal :: Maybe DetachedNode,
  45. light :: Maybe (NonEmptyList DetachedNode)
  46. }
  47. derive instance Generic ArtDecoCenterHeader _
  48. instance Show ArtDecoCenterHeader where
  49. show = genericShow
  50. parseArtDecoCenterHeader :: Parser ArtDecoCenterHeader
  51. parseArtDecoCenterHeader n = do
  52. bold <- queryAndDetachOne ":scope div.t-bold > span[aria-hidden=true]" n
  53. normal <- queryAndDetachOne ":scope span.t-normal:not(t-black--light) > span[aria-hidden=true]" n
  54. light <- queryAndDetachMany ":scope span.t-black--light > span[aria-hidden=true]" n
  55. pure $ ado
  56. b <- bold
  57. in ArtDecoCenterHeader {bold: b, normal: hush normal, light: hush light}
  58. data ArtDecoCenter = ArtDecoCenter {
  59. header :: ArtDecoCenterHeader,
  60. content :: ArtDecoCenterContent
  61. }
  62. derive instance Generic ArtDecoCenter _
  63. instance Show ArtDecoCenter where
  64. show = genericShow
  65. parseArtDecoCenter :: Parser ArtDecoCenter
  66. parseArtDecoCenter n = do
  67. header <- queryOneAndParse ":scope > div" parseArtDecoCenterHeader n
  68. content <- queryOneAndParse ":scope > div ~ div" parseArtDecoCenterContent n
  69. pure $ ado
  70. h <- header
  71. c <- content
  72. in ArtDecoCenter {header: h, content: c}
  73. data ArtDecoPvsEntity = ArtDecoPvsEntity {
  74. side :: Unit,
  75. center :: ArtDecoCenter
  76. }
  77. derive instance Generic ArtDecoPvsEntity _
  78. instance Show ArtDecoPvsEntity where
  79. show = genericShow
  80. parseArtDecoPvsEntity :: Parser ArtDecoPvsEntity
  81. parseArtDecoPvsEntity n = do
  82. center <- queryOneAndParse ":scope > div.display-flex" parseArtDecoCenter n
  83. pure $ ado
  84. c <- center
  85. in ArtDecoPvsEntity {side: unit, center: c}
  86. data ArtDecoCardElement = ArtDecoCardElement {
  87. pvs_entity :: ArtDecoPvsEntity
  88. }
  89. derive instance Generic ArtDecoCardElement _
  90. instance Show ArtDecoCardElement where
  91. show = genericShow
  92. parseArtDecoCard :: Parser ArtDecoCardElement
  93. parseArtDecoCard n = do
  94. pvs <- queryOneAndParse ":scope div.pvs-entity--padded" parseArtDecoPvsEntity n
  95. pure $ ado
  96. p <- pvs
  97. in ArtDecoCardElement {pvs_entity: p}
  98. toParentNode' :: Node -> ParentNode
  99. toParentNode' n =
  100. unsafePartial $ fromJust $ intoParentNode' n where
  101. intoParentNode' :: Node -> Maybe ParentNode
  102. intoParentNode' node = do
  103. he <- E.fromNode node
  104. pure $ E.toParentNode he
  105. queryOne :: String -> Node -> Effect (Maybe Node)
  106. queryOne selector n = do
  107. found <- querySelector (QuerySelector selector) $ toParentNode' n
  108. pure case found of
  109. Nothing -> Nothing
  110. Just el -> Just $ E.toNode el
  111. queryAll :: String -> Node -> Effect (Maybe (NonEmptyList Node))
  112. queryAll selector n = do
  113. found <- querySelectorAll (QuerySelector selector) $ toParentNode' n
  114. liftA1 NEL.fromFoldable $ NL.toArray found
  115. queryAndDetachOne ∷ String -> Parser DetachedNode
  116. queryAndDetachOne selector n = do
  117. node <- queryOne selector n
  118. case node of
  119. Nothing -> pure $ Left $ "Could not find node with selector " <> selector
  120. Just node -> do
  121. node <- toDetached node
  122. pure $ Right node
  123. queryAndDetachMany ∷ String -> Parser (NonEmptyList DetachedNode)
  124. queryAndDetachMany selector n = do
  125. nodes <- queryAll selector n
  126. case nodes of
  127. Nothing -> pure $ Left $ "Did not find any node with selector " <> selector
  128. Just nodes -> do
  129. nodes <- traverse toDetached nodes
  130. pure $ Right nodes
  131. queryOneAndParse ∷ ∀ a. String → Parser a → Parser a
  132. queryOneAndParse selector parser n = do
  133. node <- queryOne selector n
  134. case node of
  135. Nothing -> pure $ Left $ "Could not find node with selector " <> selector
  136. Just node -> parser node
  137. queryManyAndParse ∷ ∀ a. String → Parser a → Parser (NonEmptyList a)
  138. queryManyAndParse selector parser n = do
  139. nodes <- queryAll selector n
  140. case nodes of
  141. Nothing -> pure $ Left $ "Did not find any node with selector " <> selector
  142. Just nodes -> do
  143. nodes <- sequence $ map parser nodes :: Effect (NonEmptyList((Either String a)))
  144. pure $ sequence nodes