JobsUnifiedTopCard.purs 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. module LinkedIn.JobsUnifiedTopCard where
  2. import Control.Alt
  3. import Prelude
  4. import Data.Either (Either(..), hush)
  5. import Data.Generic.Rep (class Generic)
  6. import Data.List.Types (NonEmptyList)
  7. import Data.Maybe (Maybe)
  8. import Data.Show.Generic (genericShow)
  9. import LinkedIn (DetachedNode(..))
  10. import LinkedIn.Types (ParseError(..), Parser)
  11. import LinkedIn.Utils (detachNonEmptyTextChild, parseDetachedNode, queryAndDetachMany, queryAndDetachOne, queryManyAndParse, queryOneAndParse)
  12. data JobsUnifiedTopCardElement a = JobsUnifiedTopCardElement {
  13. header :: a,
  14. primaryDescription :: TopCardPrimaryDescription a,
  15. insights :: Maybe (NonEmptyList (TopCardInsight a)),
  16. actions :: Maybe (NonEmptyList (TopCardAction a))
  17. }
  18. data TopCardPrimaryDescription a = TopCardPrimaryDescription {
  19. link :: a,
  20. text :: a,
  21. tvmText :: Maybe (NonEmptyList a)
  22. }
  23. data TopCardInsight a = TopCardInsight {
  24. icon :: a,
  25. content :: TopCardInsightContent a
  26. }
  27. data TopCardInsightContent a =
  28. TopCardInsightContentSingle a
  29. | TopCardInsightContentSecondary {primary :: a, secondary :: NonEmptyList (TopCardSecondaryInsight a)}
  30. | TopCardInsightContentButton a
  31. data TopCardSecondaryInsight a =
  32. TopCardSecondaryInsightNested a
  33. | TopCardSecondaryInsightPlain a
  34. -- External application : <button id="ember74" class="jobs-apply-button artdeco-button artdeco-button--3 artdeco-button--primary ember-view artdeco-button--icon-right" role="link">
  35. -- LinkedIn Applcation : <button id="ember115" class="jobs-apply-button artdeco-button artdeco-button--3 artdeco-button--primary ember-view" data-job-id="3786945580">
  36. data TopCardAction a = TopCardActionApplyButton a
  37. derive instance Generic (JobsUnifiedTopCardElement a) _
  38. derive instance Eq a => Eq (JobsUnifiedTopCardElement a)
  39. instance Show a => Show (JobsUnifiedTopCardElement a) where
  40. show = genericShow
  41. instance Functor JobsUnifiedTopCardElement where
  42. map f (JobsUnifiedTopCardElement {header, primaryDescription, insights, actions}) =
  43. JobsUnifiedTopCardElement {
  44. header: f header,
  45. primaryDescription: map f primaryDescription,
  46. insights: map (map (map f)) insights,
  47. actions: map (map (map f)) actions
  48. }
  49. derive instance Generic (TopCardPrimaryDescription a) _
  50. derive instance Eq a => Eq (TopCardPrimaryDescription a)
  51. instance Show a => Show (TopCardPrimaryDescription a) where
  52. show = genericShow
  53. instance Functor TopCardPrimaryDescription where
  54. map f (TopCardPrimaryDescription {link, text, tvmText}) =
  55. TopCardPrimaryDescription {link: f link, text: f text, tvmText: map (map f) tvmText}
  56. derive instance Generic (TopCardInsight a) _
  57. derive instance Eq a => Eq (TopCardInsight a)
  58. instance Show a => Show (TopCardInsight a) where
  59. show = genericShow
  60. instance Functor TopCardInsight where
  61. map f (TopCardInsight {icon, content}) =
  62. TopCardInsight {icon: f icon, content: map f content}
  63. derive instance Generic (TopCardInsightContent a) _
  64. derive instance Eq a => Eq (TopCardInsightContent a)
  65. instance Show a => Show (TopCardInsightContent a) where
  66. show = genericShow
  67. instance Functor TopCardInsightContent where
  68. map f (TopCardInsightContentSingle c) = TopCardInsightContentSingle (f c)
  69. map f (TopCardInsightContentSecondary {primary, secondary}) =
  70. TopCardInsightContentSecondary {primary: f primary, secondary: map (map f) secondary}
  71. map f (TopCardInsightContentButton c) = TopCardInsightContentButton (f c)
  72. derive instance Generic (TopCardSecondaryInsight a) _
  73. derive instance Eq a => Eq (TopCardSecondaryInsight a)
  74. instance Show a => Show (TopCardSecondaryInsight a) where
  75. show = genericShow
  76. instance Functor TopCardSecondaryInsight where
  77. map f (TopCardSecondaryInsightNested c) = TopCardSecondaryInsightNested (f c)
  78. map f (TopCardSecondaryInsightPlain c) = TopCardSecondaryInsightPlain (f c)
  79. derive instance Generic (TopCardAction a) _
  80. derive instance Eq a => Eq (TopCardAction a)
  81. instance Show a => Show (TopCardAction a) where
  82. show = genericShow
  83. instance Functor TopCardAction where
  84. map f (TopCardActionApplyButton c) = TopCardActionApplyButton (f c)
  85. parseTopCardAction :: Parser (TopCardAction DetachedNode)
  86. parseTopCardAction n = do
  87. self <- parseDetachedNode n
  88. pure $ ado
  89. s <- self
  90. in TopCardActionApplyButton s
  91. parseTopCardSecondaryInsight :: Parser (TopCardSecondaryInsight DetachedNode)
  92. parseTopCardSecondaryInsight n = do
  93. nested <- queryAndDetachOne ":scope span[aria-hidden=true]" n
  94. plain <- parseDetachedNode n
  95. pure $ case nested, plain of
  96. Right p@(DetachedElement _), _ -> Right $ TopCardSecondaryInsightNested p
  97. _, Right p@(DetachedElement _) -> Right $ TopCardSecondaryInsightPlain p
  98. _, _ -> Left TextNotFoundError
  99. parseTopCardInsightContent :: Parser (TopCardInsightContent DetachedNode)
  100. parseTopCardInsightContent n = do
  101. primary <- queryAndDetachOne ":scope > span:first-child span[aria-hidden=true]" n
  102. secondary <- queryManyAndParse
  103. ":scope > span.job-details-jobs-unified-top-card__job-insight-view-model-secondary"
  104. parseTopCardSecondaryInsight
  105. n
  106. self <- parseDetachedNode n
  107. pure $ case primary, secondary, self of
  108. _, _, Right b@(DetachedElement {tag: "BUTTON"}) -> Right $ TopCardInsightContentButton b
  109. Right p@(DetachedElement _), Right s, _ -> Right $ TopCardInsightContentSecondary {primary: p, secondary: s}
  110. _, _, Right el@(DetachedElement _) -> Right $ TopCardInsightContentSingle el
  111. _, _, _ -> Left TextNotFoundError
  112. parseTopCardInsight :: Parser (TopCardInsight DetachedNode)
  113. parseTopCardInsight n = do
  114. icon <- queryAndDetachOne ":scope li-icon" n
  115. svg <- queryAndDetachOne ":scope svg" n
  116. content <- queryOneAndParse ":scope > span" parseTopCardInsightContent n
  117. actionButton <- queryOneAndParse ":scope > button" parseTopCardInsightContent n
  118. pure $ ado
  119. i <- icon <|> svg
  120. c <- content <|> actionButton
  121. in TopCardInsight {icon: i, content: c}
  122. parseTopCardPrimaryDescription :: Parser (TopCardPrimaryDescription DetachedNode)
  123. parseTopCardPrimaryDescription n = do
  124. link <- queryAndDetachOne ":scope > a" n
  125. text <- detachNonEmptyTextChild n
  126. tvmText <- queryAndDetachMany "span.tvm__text" n
  127. pure $ ado
  128. l <- link
  129. t <- text
  130. in TopCardPrimaryDescription {link: l, text: t, tvmText: hush tvmText}
  131. parseJobsUnifiedTopCardElement :: Parser (JobsUnifiedTopCardElement DetachedNode)
  132. parseJobsUnifiedTopCardElement n = do
  133. h1 <- queryAndDetachOne "h1.job-details-jobs-unified-top-card__job-title" n
  134. primary <- queryOneAndParse
  135. "div.job-details-jobs-unified-top-card__primary-description-container > div"
  136. parseTopCardPrimaryDescription
  137. n
  138. insights <- queryManyAndParse
  139. "li.job-details-jobs-unified-top-card__job-insight"
  140. parseTopCardInsight
  141. n
  142. actions <- queryManyAndParse
  143. ".mt5 button"
  144. parseTopCardAction
  145. n
  146. pure $ ado
  147. h <- h1
  148. p <- primary
  149. in JobsUnifiedTopCardElement {
  150. header: h,
  151. primaryDescription: p,
  152. insights: hush insights,
  153. actions: hush actions
  154. }
  155. toHeader ∷ forall a. JobsUnifiedTopCardElement a → a
  156. toHeader (JobsUnifiedTopCardElement {header}) = header
  157. toPrimaryDescriptionLink ∷ forall a. JobsUnifiedTopCardElement a → a
  158. toPrimaryDescriptionLink (JobsUnifiedTopCardElement {
  159. primaryDescription: TopCardPrimaryDescription {link}
  160. }) = link
  161. toPrimaryDescriptionText ∷ forall a. JobsUnifiedTopCardElement a → a
  162. toPrimaryDescriptionText (JobsUnifiedTopCardElement {
  163. primaryDescription: TopCardPrimaryDescription {text}
  164. }) = text