- Monday, August 6: Chapter 16 hosted by Charlottesville Haskell Book Reading Group in Charlottesville, va, USA
- Monday, August 6: Combinating - The Weekly Function hosted by Orange Combinator - Functional Programming In OC in Irvine, CA
- Tuesday, August 7: Techniques in Functional JavaScript hosted by NoFUN - New Orleans Functional Programming in New Orleans, USA
- Tuesday, August 7: Lightning talks / Show and tell hosted by Milwaukee Functional Programming User Group in Milwaukee, WI, USA
- Tuesday, August 7: Hack Night hosted by λ cleveland -> func in Westlake, oh, USA
- Tuesday, August 7: Go Beyond Higher Order Functions: A Journey into Functional Programming hosted by Atlanta Functional Programming Meetup in Atlanta, GA, USA
- Tuesday, August 7: Introduction to ReasonML hosted by Functional First Phoenix in Tempe, AZ, USA
- Wednesday, August 8: Programação Funcional em Fortaleza hosted by Programação Funcional em Fortaleza in Fortaleza, Brazil
- Wednesday, August 8: Code & Coffee hosted by Code & Coffee (Leeds) in Leeds, United Kingdom
- Wednesday, August 8: Haskell Book reading group (22th Ed. with Revisions) hosted by Budapest Haskell User Group in Budapest, Hungary
- Wednesday, August 8: Weekly Low-Pressure Social Get Together hosted by Asheville Coders League in Asheville, NC, USA
- Wednesday, August 8: Monthly Get Together hosted by Tucson Functional Programmers in Tucson, AZ, USA
- Wednesday, August 8: (Scala and Haskell) Functional Programming Techniques Level 1: Type Classes hosted by FP Academy Ams in Amsterdam, Netherlands
- Wednesday, August 8: Haskell Wednesday hosted by Berlin Haskell Users Group in Berlin, Germany
- Wednesday, August 8: Open Programming Session (Wednesday) hosted by East Bay Functional Programming Study Group in Berkeley, CA
- Wednesday, August 8: Hack Night: Learn, Explore, Do hosted by FunctionalTO in Toronto, ON, Canada
- Wednesday, August 8: Bound for Greatness hosted by Boulder Haskell Programmers in Boulder, USA
- Thursday, August 9: Morning Haskell coding session hosted by Durham Haskell Meetup in Durham, nc, USA
- Thursday, August 9: (hack . yack) hosted by Raleigh Haskell Meetup in Raleigh, NC, USA
- Thursday, August 9: hack night hosted by Madhackers in Madison, WI
- Thursday, August 9: Functional Programming Meetup hosted by Las Vegas Functional Programming User Group in Las Vegas, NV, USA
- Thursday, August 9: Category Theory for Programmers Part 3 hosted by Seattle Area Haskell Users' Group in Seattle, USA
- Thursday, August 9: Our initial Meetup hosted by Lambda Pi in Hamburg, Germany
- Friday, August 10: Going through Software Foundations by Benjamin Pierce et al hosted by Austin Types, Theorems, and Programming Languages in Austin, TX, USA
- Saturday, August 11: Weekly Haskell Coding Meetup hosted by BC Haskell Users Group in Chilliwack, BC, Canada
- Saturday, August 11: Hang out, chat FP, work on some code hosted by Weekly Functional Programming Meetup in Boston, ma, USA
- Saturday, August 11: Diving deeper into software development discussions hosted by San Diego Software Development Deep Dive in San Diego , CA, USA
- Sunday, August 12: Applicative in Haskell hosted by Bangalore Functional Programmers Meetup in Bangalore, India
- Monday, August 13: Combinating - The Weekly Function hosted by Orange Combinator - Functional Programming In OC in Irvine, CA
- Monday, August 13: Hack Night! hosted by Pittsburgh Functional Programming Meetup in Pittsburgh, PA, USA
- Monday, August 13: RustBelt: securing the foundations of the Rust programming language hosted by Sydney Type Theory in Sydney, Australia
- Monday, August 13: Exact Calculations in Haskell by Petrus Potgieter hosted by Lambda Luminaries in Sandton, South Africa
Last active
August 9, 2018 05:49
-
-
Save tfausak/c275143017f8ab4999a4f36ea82cfce6 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env stack | |
-- stack --resolver lts-12.0 script | |
module Main | |
( main | |
) | |
where | |
import qualified Control.Arrow | |
import qualified Data.Aeson | |
import qualified Data.Aeson.Types | |
import qualified Data.List | |
import qualified Data.Maybe | |
import qualified Data.Text | |
import qualified Data.Text.Encoding | |
import qualified Data.Time | |
import qualified Network.HTTP.Client | |
import qualified Network.HTTP.Client.TLS | |
import qualified Network.HTTP.Types | |
import qualified System.Environment | |
main :: IO () | |
main = do | |
key <- System.Environment.getEnv "MEETUP_API_KEY" | |
today <- Data.Time.utctDay <$> Data.Time.getCurrentTime | |
url <- makeUrl | |
"https://api.meetup.com/find/upcoming_events" | |
[ ("end_date_range", formatDay $ Data.Time.addDays 8 today) | |
, ("key", key) | |
, ("oder", "time") | |
, ("radius", "global") | |
, ("start_date_range", formatDay $ Data.Time.addDays (-1) today) | |
, ("text", "haskell") | |
] | |
request <- Network.HTTP.Client.parseUrlThrow url | |
manager <- Network.HTTP.Client.TLS.newTlsManager | |
response <- Network.HTTP.Client.httpLbs request manager | |
payload <- | |
either fail pure | |
. Data.Aeson.eitherDecode | |
$ Network.HTTP.Client.responseBody response | |
mapM_ printEvent | |
. Data.List.sortOn (eventLocalDate Control.Arrow.&&& eventYesRsvpCount) | |
$ payloadEvents payload | |
printEvent :: Event -> IO () | |
printEvent event = putStrLn $ concat | |
[ "- " | |
, formatDate $ eventLocalDate event | |
, ": [" | |
, eventName event | |
, "](" | |
, eventLink event | |
, ") hosted by [" | |
, groupName $ eventGroup event | |
, "](https://www.meetup.com/" | |
, groupUrlname $ eventGroup event | |
, "/) in [" | |
, eventLocation event | |
, "](https://www.openstreetmap.org/?mlat=" | |
, show $ eventLatitude event | |
, "&mlon=" | |
, show $ eventLongitude event | |
, ")" | |
] | |
makeUrl :: String -> [(String, String)] -> IO String | |
makeUrl url query = | |
either (fail . show) (pure . Data.Text.unpack) | |
. Data.Text.Encoding.decodeUtf8' | |
$ Data.Text.Encoding.encodeUtf8 (Data.Text.pack url) | |
<> Network.HTTP.Types.renderQuery True (Network.HTTP.Types.toQuery query) | |
formatDay :: Data.Time.Day -> String | |
formatDay = Data.Time.formatTime Data.Time.defaultTimeLocale "%FT00:00:00" | |
formatDate :: Date -> String | |
formatDate (Date day) = | |
Data.Time.formatTime Data.Time.defaultTimeLocale "%A, %B %-d" day | |
newtype Payload = Payload | |
{ payloadEvents :: [Event] | |
} | |
instance Data.Aeson.FromJSON Payload where | |
parseJSON = Data.Aeson.withObject "Payload"$ \object -> Payload | |
<$> required object "events" | |
data Event = Event | |
{ eventGroup :: Group | |
, eventLink :: String | |
, eventLocalDate :: Date | |
, eventName :: String | |
, eventVenue :: Maybe Venue | |
, eventYesRsvpCount :: Integer | |
} | |
instance Data.Aeson.FromJSON Event where | |
parseJSON = Data.Aeson.withObject "Event" $ \object -> Event | |
<$> required object "group" | |
<*> required object "link" | |
<*> required object "local_date" | |
<*> required object "name" | |
<*> optional object "venue" | |
<*> required object "yes_rsvp_count" | |
eventLocation :: Event -> String | |
eventLocation event = | |
maybe | |
(groupLocalizedLocation $ eventGroup event) | |
(\venue -> Data.List.intercalate ", " $ Data.Maybe.catMaybes | |
[ Just $ venueCity venue | |
, venueState venue | |
, Just $ venueLocalizedCountryName venue | |
] | |
) | |
$ eventVenue event | |
eventLatitude :: Event -> Double | |
eventLatitude event = | |
maybe (groupLat $ eventGroup event) venueLat $ eventVenue event | |
eventLongitude :: Event -> Double | |
eventLongitude event = | |
maybe (groupLon $ eventGroup event) venueLon $ eventVenue event | |
data Group = Group | |
{ groupLat :: Double | |
, groupLon :: Double | |
, groupName :: String | |
, groupLocalizedLocation :: String | |
, groupUrlname :: String | |
} | |
instance Data.Aeson.FromJSON Group where | |
parseJSON = Data.Aeson.withObject "Group" $ \object -> Group | |
<$> required object "lat" | |
<*> required object "lon" | |
<*> required object "name" | |
<*> required object "localized_location" | |
<*> required object "urlname" | |
data Venue = Venue | |
{ venueCity :: String | |
, venueLat :: Double | |
, venueLocalizedCountryName :: String | |
, venueLon :: Double | |
, venueState :: Maybe String | |
} | |
instance Data.Aeson.FromJSON Venue where | |
parseJSON = Data.Aeson.withObject "Venue" $ \object -> Venue | |
<$> required object "city" | |
<*> required object "lat" | |
<*> required object "localized_country_name" | |
<*> required object "lon" | |
<*> optional object "state" | |
required | |
:: Data.Aeson.FromJSON a | |
=> Data.Aeson.Object | |
-> String | |
-> Data.Aeson.Types.Parser a | |
required object key = object Data.Aeson..: Data.Text.pack key | |
optional | |
:: Data.Aeson.FromJSON a | |
=> Data.Aeson.Object | |
-> String | |
-> Data.Aeson.Types.Parser (Maybe a) | |
optional object key = object Data.Aeson..:? Data.Text.pack key | |
newtype Date | |
= Date Data.Time.Day | |
deriving (Eq, Ord) | |
instance Data.Aeson.FromJSON Date where | |
parseJSON value = do | |
string <- Data.Aeson.parseJSON value | |
Date <$> Data.Time.parseTimeM False Data.Time.defaultTimeLocale "%F" string |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment