Git Product home page Git Product logo

meetupr's Introduction

meetupr Zane Dax @StarTrek_Lt

Logo by Zane Dax @StarTrek_Lt

⚠️ ALERT TO USERS:

Meetup has deprecated the REST API that’s used in this package. We are working to add support for their new API but unfortnately that means that the package is not currently functional. If you’d like to help with this transition in any way (we could use help with testing and documentation in particular), please comment on the this issue.

R-CMD-check Codecov test coverage

R interface to the Meetup API (v3)

Installation

To install the development version from GitHub:

# install.packages("remotes")
remotes::install_github("rladies/meetupr")

A released version will be on CRAN soon.

Usage

Authentication

API key? No

As of August 15, 2019, Meetup.com switched from an API key based authentication system to OAuth 2.0, so we added support for OAuth.

OAuth? Yes

Meetup API and this package recommend using OAuth for authentication. We’ve abstracted part of the difficulty but it’s still a bit more complex than storing a simple API key as secret.

With OAuth you need

  • an OAuth app. There’s one shipped in with the package! If you prefer you can bring your own app by setting the meetupr.consumer_key and meetupr.consumer_secret options.

  • an access token. It’s an httr object and it can be saved to disk. It expires but can be refreshed. It contains secrets so it’s a sensitive file! For creating one you will be prompted to log into your meetup.com account in the browser. But then if you cache the token to disk, you won’t need to do that again. This means you can create a token on your computer locally and use it on a server (if the server is public, encrypting the token).

Let’s go through workflows and ways to control how your token is created and cached.

If you don’t tweak anything, the first time you run a meetupr function, you’ll be prompted to go into your browser and a token will be created. It will be saved to disk in an app directory as determined by rappdirs::user_data_dir("meetupr", "meetupr")

And all the times you use meetupr again, this token will be used, and refreshed and re-saved as needed.

This is, we hope, a sensible default.

Now if you want to have a different behavior you either tweak options (in your .Rprofile so for all sessions in the future, or just in the current session), or call the meetup_auth() function directly.

  • Don’t want to cache the token to disk? Use the cache argument, to be set to FALSE.
  • Don’t want to use an app dir? Use the use_appdir argument, to be set to FALSE. If it is false, the token will be cached to .httr-oauth (unless cache is FALSE too, of course)
  • Want to save the token to somewhere you choose? No way to use an option. Use the token_path argument of meetup_auth().
  • Want to use a token that was created elsewhere? Save it to disk, keep it secret, and refer to it via the token argument of meetup_auth() that can be either a token or the path to a token.

Functions

See the pkgdown reference.

For example, the following code will get all upcoming events for the R-Ladies San Francisco meetup.

library(meetupr)

urlname <- "rladies-san-francisco"
events <- get_events(urlname)
dplyr::arrange(events, desc(time))
#> # A tibble: 63 × 18
#>    id        title    link  status durat…¹ going waiting descr…² venue…³ venue…⁴
#>    <chr>     <chr>    <chr> <chr>  <chr>   <int>   <int> <chr>   <chr>     <dbl>
#>  1 280404371 Data St… http… PAST   PT1H30M    61       0 "=====… 269060…   -8.52
#>  2 277757076 Buildin… http… PAST   PT2H       70       0 "Build… 269060…   -8.52
#>  3 273036282 A conve… http… PAST   PT2H       30       0 "Fires… 269060…   -8.52
#>  4 272407729 Tangibl… http… PAST   PT1H30M    45       0 "Tangi… 269060…   -8.52
#>  5 267967077 R-Ladie… http… PAST   PT2H       95       0 "Just … 260586…   37.8 
#>  6 266395328 Decembe… http… PAST   PT2H30M    98       0 "Join … 261907…   37.8 
#>  7 265100301 Worksho… http… PAST   PT2H       40       0 "\"Tra… 266458…   37.8 
#>  8 263230714 August … http… PAST   PT2H       58       0 "1. Bu… 265200…   37.6 
#>  9 262717409 R-Ladie… http… PAST   PT3H       18       0 "Come … 260979…   37.8 
#> 10 262662040 Bayesia… http… PAST   PT2H       57       0 "MAIN … 260597…   37.8 
#> # … with 53 more rows, 8 more variables: venue_lon <dbl>, venue_name <chr>,
#> #   venue_address <chr>, venue_city <chr>, venue_state <chr>, venue_zip <chr>,
#> #   venue_country <chr>, time <dttm>, and abbreviated variable names ¹​duration,
#> #   ²​description, ³​venue_id, ⁴​venue_lat
#> # ℹ Use `print(n = ...)` to see more rows, and `colnames()` to see all variable names

We can also search for groups with free text.

groups <- find_groups("R-Ladies")
dplyr::arrange(groups, desc(created))
#> # A tibble: 189 × 17
#>    id       name             urlname latit…¹ longi…² city  state membe…³ members
#>    <chr>    <chr>            <chr>     <dbl>   <dbl> <chr> <chr> <chr>     <int>
#>  1 36221103 R-Ladies Honolu… rladie…    21.3 -158.   Hono… "HI"  LEADER       11
#>  2 36221061 R-Ladies Rabat   rladie…    34.0   -6.83 Rabat ""    LEADER      145
#>  3 36155475 R-Ladies Morelia rladie…    19.7 -101.   More… ""    LEADER       97
#>  4 36155463 R-Ladies Ciudad… rladie…    27.5 -110.   Ciud… ""    LEADER        2
#>  5 36128420 R-Ladies Rome    rladie…    41.9   12.5  Roma  "RM"  LEADER       25
#>  6 36128390 R-Ladies Oxford… rladie…    34.4  -89.5  Oxfo… "MS"  LEADER        3
#>  7 35897820 R-Ladies Gaboro… rladie…   -24.6   25.9  Gabo… ""    LEADER      490
#>  8 35897809 R-Ladies Cologne rladie…    50.9    6.96 Colo… ""    LEADER      178
#>  9 35897790 R-Ladies West L… rladie…    40.5  -87.0  West… "IN"  LEADER        9
#> 10 35897779 R-Ladies Villah… rladie…    18.0  -92.9  Vill… ""    LEADER      116
#> # … with 179 more rows, 8 more variables: created <dttm>, timezone <chr>,
#> #   join_mode <chr>, who <chr>, isPrivate <lgl>, category_id <chr>,
#> #   category_name <chr>, country <chr>, and abbreviated variable names
#> #   ¹​latitude, ²​longitude, ³​membershipMetadata.status
#> # ℹ Use `print(n = ...)` to see more rows, and `colnames()` to see all variable names

How can you contribute?

We are looking for new people to join the list of contributors! Please take a look at the open issues, file a new issue, contribute tests, or improve the documentation. We are also looking to expand the set of functions to include more endpoints from the Meetup API. Lastly, we’d also love to hear about any applications of the meetupr package, so we can compile a list of demos!

Please note that the this project is released with a Contributor Code of Conduct. By contributing to this project, you agree to abide by its terms.

meetupr's People

Contributors

benubah avatar brooklynbagel avatar cvitolo avatar drmowinckels avatar gdequeiroz avatar gregsutcliffe avatar hfrick avatar jennybc avatar ledell avatar lucymcgowan avatar maelle avatar malcolmbarrett avatar missaugustina avatar olgamie avatar rickpack avatar romainfrancois avatar schloerke avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

meetupr's Issues

Add fields argument to find_groups() to retrieve optional fields

Add fields argument to find_groups() to retrieve optional fields from the response. I am using this package to work on a project this summer and found out that I needed a count of past events for each R-Ladies group. Thankfully, past_event_count is an optional field within the response of find/groups API method.

For now, I suggest just adding fields to find_groups() and the call to .fetch_results() as shown below:

find_groups <- function(text = NULL, topic_id = NULL, radius = "global", 
                                            fields = NULL, api_key = NULL) {
  api_method <- "find/groups"
  res <- .fetch_results(api_method = api_method,
                        api_key = api_key,
                        text = text,
                        topic_id = topic_id,
                        fields = fields,
                        radius = radius)

An example usage could be:

# add 'past_event_count' and 'upcoming_event_count' to the query parameters using 'fields' argument; 
#these two are among several optional fields provided by the API: https://www.meetup.com/meetup_api/docs/find/groups/
all_rladies_groups <- find_groups(text = "r-ladies",
                       fields="past_event_count, upcoming_event_count", api_key = "key here")

# Cleanup
rladies_groups <- all_rladies_groups[grep(pattern = "rladies|r-ladies|r ladies", 
                                          x = all_rladies_groups$name,
                                          ignore.case = TRUE), ]

#for now, we could easily retrieve the values for these two fields with:
pastevent_count <- purrr::map_dbl(rladies_groups$resource, "past_event_count", .default = 0)
upcomingevent_count <-  purrr::map_dbl(rladies_groups$resource, "upcoming_event_count", .default = 0)

Integration test that make real API calls (Travis/PR, not unit)

Tests are mocked in Issue #31

We should have some integration tests that check our client against the Meetup API.

  • These should not be run as part of testthat by default
  • They should only be run by Travis
  • They should only be run on PR, not with every update
  • They should not depend on fragile aspects of the API response (eg, expecting a certain number of columns or responses)
  • They're function is to check essential compatibility with the Meetup API (authentication, parameters being honored, etc)

Bug in internals.R?

My work using this package to explore R User groups based on a modified version of find_groups() (provided within this pull request #48 ) that retrieves additional fields has revealed an unexpected behavior.

Consider the code below:

all_ruser_groups <- find_groups(text = "r-project-for-statistical-computing", fields = "past_event_count, upcoming_event_count, last_event, topics", api_key = meetup_api_key)

There are 481 records returned for this query. For records, between 0 - 199, everything is fine. But for records between 200 - 481, there are no entries for upcoming_event_count, last_event, topics. Only the first additional field past_event_count is returned.

I guessed that the problem could be from the following portion of .fetch_results() function within internals.R :

# If you have not yet retrieved all records, calculate the # of remaining calls required
  extra_calls <- ifelse(
    (length(records) < total_records) & !is.null(res$headers$link),
    floor(total_records/length(records)),
    0)
  if (extra_calls > 0) {
    all_records <- list(records)
    for (i in seq(extra_calls)) {
      # Keep making API requests with an increasing offset value until you get all the records
      # TO DO: clean this strsplit up or replace with regex
      next_url <- strsplit(strsplit(res$headers$link, split = "<")[[1]][2], split = ">")[[1]][1]
      res <- .quick_fetch(next_url, api_key, event_status)
      all_records[[i + 1]] <- res$result
    }
    records <- unlist(all_records, recursive = FALSE)
  }

I re-wrote this portion and got a working solution for my work. Possibly could send a pull request soon.

@ledell @LucyMcGowan Any thoughts?

Add OAuth support

Here's an announcement from meetup.com. We need to add support for OAuth, deprecate api_key (I guess we should not remove it from the functions because it will break people's code but we should tell people that it's not active anymore), update the docs and re-test everything. :-(

Changes to our API access

  • API Keys will be replaced by OAuth: We will be removing API keys on August 15, 2019 and requiring you to authenticate with OAuth.
  • Move to OAuth soon for continued free API access: Until August 15, members will be able to apply for OAuth access free of charge. After August 15, anyone who wants to apply for API access through OAuth will need to have a Meetup Pro account in order to do so.
  • We’re removing API version 2: Before August 15, you will need to switch to API version 3. API version 2 will no longer be available. You can find more information on our API updates and answers to common questions on our Help Center.

Build failing on Travis CI

The build has been failing for 6 months: https://travis-ci.org/github/rladies/meetupr/branches

I don't see any issues with the DESCRIPTION file though..

Building with: R CMD build 
0.26s$ R CMD build  .
* checking for file ‘./DESCRIPTION’ ... OK
* preparing ‘meetupr’:
* checking DESCRIPTION meta-information ... ERROR

Malformed Depends or Suggests or Imports or Enhances field.

Offending entries:
  tibble
knitr

Entries must be names of packages optionally followed by '<=' or '>=',
white space, and a valid version number in parentheses.
See section 'The DESCRIPTION file' in the 'Writing R Extensions'
manual.

The command "R CMD build  ." failed and exited with 1 during .

Your build has been stopped.

get_events() throws an error when no events found for a query

Via Athanasia

Hi folks! I’m playing around with the meetupr package we have on github.
I was wondering whether theget_events function should throw an error when no events are found for a query? Is it really an error if there are no upcoming events? Should it not rather be a warning?

I am looking to see if our code is doing this or if its something from the Meetup API.

default OAuth key and secret shipped with meetupr not working?

The documentation of meetupr suggests that, in a situation where the user has not provided a token, the packages uses a default token that is shipped with it here: https://github.com/rladies/meetupr/blob/master/R/zzz.R

However, I tried to run a simple call using the package without providing a token, but I am redirected to a browser to Log in, and that seems to be the only way to obtain a token. But once I have logged in once and the authentication is complete, I can now make as many API calls without needing to re-authenticate for several months due to the cache set to true.

To reproduce this scenario, I created a brand new project on RStudio and tried to run the following code:

groups <- find_groups(text = "r-ladies", api_key = "")

Result: I was redirected to a browser for authentication.
Expected behaviour: I expected this to work automatically with the default token shipped with the package.

Add Mocking to Unit tests so they don't make For Realz api calls

The unit tests are currently making "IRL" api calls which can result in failures in the event of no network access. Additionally, these tests may result in errors beyond the scope of what is actually being tested. Ultimately, at least for these tests, we just want to know if our library code is working, we don't care about the api key or httr.

That said, other end-to-end continuous integration tests should be developed that do call the API from TravisCi but that's beyond the scope of this issue. I'll file it as a second one and mention this one to link it.

If there are no events, return empty tibble instead of an error

I think it would be more user-friendly for the get_events() function to return an empty (zero rows) tibble instead of an error when there's no results. We could convert the error message into a warning.

I ran into this error when writing a function to get and rbind all R-Ladies events into a single tibble, and my function died in the middle because of this error, rather than just rbinding an empty frame.

Note: This repro will only work if there are still no upcoming events for the Sao Paulo chapter (check here).

events <- get_events(urlname = "rladies-sao-paulo", event_status = "upcoming")

When there are no results, it will give the following error:

Error: Zero records match your filter. Nothing to return.

Right now, we have to wrap in a tryCatch() to be able to use get_events() inside another function w/o potential errors:

events <- tryCatch(get_events(urlname = "rladies-sao-paulo",
                              event_status = "upcoming"),
                   error = function(e) {
                     message("No events found.")
                   })

TO DO: Check if this is the case in other functions and if so, change this ticket to cover all the functions and make the changes all at once.

api_key argument does not default to NULL in get_boards

I set my API key using Sys.setenv(MEETUP_KEY="mykey"). I can then use all functions without specifying the api_key argument because all functions set it to NULL as default and .fetch_results will then check for NULL and draw it from the Sys env.

The get_boards function does not specify NULL as default for api_key, hence resulting in a missing argument error.


library(meetupr)
get_boards(urlname = "rladies-san-francisco")
#> Error in .fetch_results(api_method, api_key): argument "api_key" is missing, with no default

Deprecate API key methods

Now that we added OAuth support and the API key authentication scheme is no longer functional, we can deprecate the API key methods.

Current plan is to remove the api_key argument and add ... to swallow any api_key args that appear in old/outdated scripts.

Consider adding a column for each field specified in the `fields` parameter

The PR which was just merged which adds the fields parameter to find_groups() will add the extra fields to the list (raw data), but the user still has to extract those fields and add them as extra columns to the returned tibble. Should we just add those fields automatically as columns, since it's almost 100% likely that the user will want to do that anyway, since they are explicitly requesting that data?

The other thing I was considering, if we do this, is to make the fields parameter more generic (possibly rename to extra_fields) and then we could allow the user to specify which extra fields they want to add to the returned tibble as extra columns. In this case, those fields could just be extra ones that we already have in the default resource raw list object but are not exposed as columns by default, or they could be the extra ones that must be passed via the API "fields" parameter to the /find/groups endpoint.

Lastly, since it seems like a lot of the endpoints support the "fields" parameter, we could consider adding this functionality to all of the functions. e.g. get_members()

"joined" date for membership in groups

I have been working a little with getting some dat for a presentation, and I realised that the "joined" date, as retrieved by the get_members() function, is the date a person joined meetup, not the date they joined the group.

We might not have the date of joining group (I'm looking into the resource we get and see if I can parse it out), but i'm writing it down here so checking this is not forgotten.

And perhaps the "joined" column should be renamed to "joined_meetup", and if we find group joining, it could be named "joined_group"?

Add Contributing, CoC, Issue Template

These files are essential when other people want to contribute to the package.

To generate this, we can use. the usethis package.

library(usethis)
use_code_of_conduct()
use_tidy_contributing()
use_tidy_issue_template()

get_members() returns meetup join date instead group join date

find_groups('R-ladies Paris')
returns
id name urlname created members status organizer lat lon city
1 2.04e⁷ R-Ladi… rladies-… 2016-09-19 17:39:51 298 active Gabriela… 48.9 2.34 Paris

however
get_members('rladies-paris')
returns members with join dates prior to group create date!
id name bio status joined city country state lat lon
1 8.37e⁶ Sarah… I miss skii… active 2008-11-15 22:13:36 Paris fr <NA> 48.9 2.34
2 1.45e⁷ Gabri… <NA> active 2011-04-20 00:57:39 San … us CA 37.8 -122

Restrict character input to limited set of values

Sometimes we don’t list all the options for an argument with a fixed value set, so let's find all the cases of this and add the correct set of values.

Here's an example:

get_events(urlname, event_status = "upcoming")

The default value for event_status, as written in the function definition should be restricted to the set of allowable values (so that R can check if an invalid string is passed and throw an error):

get_events(urlname, event_status = c("upcoming", "cancelled", "draft", 
                                     "past", "proposed", "suggested"))

The actual default value for event_status is the first element of this character vector.

Use ratelimitr to not get 429 errors?

Poke @kierisi

ratelimitr

The docs say "You can know your current rate limit status by reading X-RateLimit HTTP headers included in responses. The following table indicates their name and meaning. " so I guess this issue is related to #9.

I imagine the code would need to make a call at the start of each session to determine what to put in the ratelimitr call 🤔

Retrieve `localized_country_name` in `find_groups()`

I guess that retrieving the full country name (localized_country_name in the meetup API) could be more useful than just the country code (country in the meetup API). You might want to retrieve both.


Using the following code retrieves this for me in the find_group() function.

country = purrr::map_chr(res, "localized_country_name"),

get_events More descriptive error message than "Result 1 is not a length 1 atomic vector"

I hope to explore this more carefully but get_events returns:
Error: Result 1 is not a length 1 atomic vector

when one submits:
grp_events <- get_events("tangoutopia", event_status = "past")

I do not yet know why. There are plenty of past Meetups in https://www.meetup.com/tangoutopia/events/past/ and indeed the function prints:
Downloading 1021 record(s)...

EDIT Can someone see if you also experience this error? I wonder if this is due to me hitting the API too frequently in a given expanse of time.

Submit to CRAN

I think we're probably ready to do our first CRAN submission!

List of demos or projects using meetupr

Please add any demos or references to the meetupr package that you see on the internet as a comment below. Once there is a short list, we can make this into a markdown file called demos.md (or similar).

Problem installing package

I get the following message when I run the code:

devtools::install_github("rladies/meetupr")
Downloading GitHub repo rladies/meetupr@master
Error in process_initialize(self, private, command, args, stdin, stdout, :
Command not found

Relax group admin constraints in `get_event_attendees()`

There is a blog post by Rick Pack about trying to use the meetupr::get_event_attendees() function and he pointed out that this currently requires the user to be an admin of that group.

It looks like he was able to get around that constraint in his code so we should figure out if we can update the get_event_attendees() to not require group admin rights.

Here's an example:

> past_events <- get_events(urlname = "rladies-nashville", event_status = "past")
> past_events
# A tibble: 9 x 21
         id                                            name
      <chr>                                           <chr>
1 234968855                     R-Ladies Nashville Kickoff!
2 235652800             December Meetup: data.table package
3 235757913              January Meetup: R Resources & Help
4 237055230 February Meetup: RStudio Conference Highlights!
5 237055481                    March Meetup: Intro to Shiny
6 239935025                           An R + Github Journey
7 242098493           Working with Pipes %>% + GIS Tutorial
8 243331077                           Introduction to purrr
9 243331084      Play in the snow, then clean up your mess!
# ... with 19 more variables: created <dttm>, status <chr>,
#   time <dttm>, local_date <date>, local_time <chr>,
#   waitlist_count <int>, yes_rsvp_count <int>, venue_id <int>,
#   venue_name <chr>, venue_lat <dbl>, venue_lon <dbl>,
#   venue_address_1 <chr>, venue_city <chr>, venue_state <chr>,
#   venue_zip <chr>, venue_country <chr>, description <chr>,
#   link <chr>, resource <list>
> get_event_attendees(urlname, event_id = 234968855)
Error in .quick_fetch(api_url = api_url, api_key = api_key, event_status = event_status,  : 
  Bad Request (HTTP 400).

We also need to add some tests to check for this type of thing.

Support more API query parameters in find_groups()

To find R-Ladies meetups that aren't using the topic, I do a text search and limit to a category and then apply a regex to the result. We should support category as a parameter.

(TODO: make a reprex)

groups_url <- "https://api.meetup.com/find/groups"

  groups_query_params <- list(
    key=params$meetup_api_key, 
    sign=TRUE,
    page=200,
    radius="global")
  
  # by topic
  meetup_groups_topic <- get_meetups(groups_url, append(groups_query_params, c(topic_id=1513883, order="members")))
  
  # by text + category
  meetup_groups_text <- get_meetups(groups_url, append(groups_query_params, c(text="r-ladies", category=34)))
  
  meetup_groups_aggr <- bind_rows(meetup_groups_topic, meetup_groups_text %>% anti_join(meetup_groups_topic, by="id"))
  meetup_groups <- meetup_groups_aggr %>% filter(str_detect(name, "[Rr]([ -]?)[Ll]adies"))`

Design question - do we want to explicitly support parameters or do we want to consider dynamic arguments?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.