class: center, middle, inverse, title-slide .title[ # Adobe R Packages Update ] .author[ ### Ben Woodard ] .institute[ ### Search Discovery ] .date[ ### 2022-04-01 (updated: 2022-07-28) ] --- class: agenda * adobeanalyticsr * Authentication * Pulling Data * Building Segments * cjar * Authentication * Key Differences * Wrap Up * Use Cases * What's Next --- class: keypoint  --- class: keypoint #Created By Analysts For Analysts --- class: titlebody <img src="pres.demo_files/figure-html/pkgdownloads-1.png" style="display: block; margin: auto;" /> <!--  --> --- class: maintitle # Authentication --- class: titlebody # What are the options? .col-md-6[ ### Oauth *Open Authentication* * Based on your profile access * Less control * Interactive authentication flow * Setup is really fast ] .col-md-6[ ### JWT *JASON Web Token* * Based on the JSON Web Token access * More control * Non-interactive authentication flow * Harder to setup ] --- class: titlebody # Creating the Project 1. Navigate to the following URL: https://console.adobe.io/integrations. 2. Click the Create New Project button and select Empty Project 3. Click the Add API button. 4. Select the Experience Cloud product icon and then choose Adobe Analytics and click Next. .center[  ] *The next series of steps depends on your choice of authorization.* --- class: titlebody # OAuth Setup Process 1. Select the OAuth option and then click Next. 2. Select Web as the platform where you want the integration. 3. Add Default redirect URI https://adobeanalyticsr.com/token_result.html*. 4. Add Redirect URI pattern https://adobeanalyticsr\.com/token_result\.html*.  --- class: titlebody # JWT Setup Process 1. Select the Service Account (JWT) options and then click Next. 2. Click on the Generate a Key Pair button. A config file will download onto your desktop. 3. Select the product profiles to be included in the access and click Save configured API. {width=50%} --- class: titlebody # Setup Your R Environment Variables The `.Renviron` file is an easy way to manage global variables. Global variables are mostly used in packages that rely on some sort of authentication process such as `adobeanalyticsr` or `GoogleAnaltyicsR`. ``` usethis::edit_r_environ('user') ``` Add the following variables to the file, save it, and restart your R session. (command/ctrl + shift + F10) -- .col-md-6[ ### Oauth ``` AW_CLIENT_ID = "[OAuth client ID]" AW_CLIENT_SECRET = "[OAuth client secret]" ``` ] -- .col-md-6[ ### JWT ``` AW_AUTH_FILE = "[auth_file.json]" AW_PRIVATE_KEY = "[private.key]" ``` ] --- class: titlebody #
**Hot Tip 1** Non-mandatory global environment variables. Assigning these two optional variables in the `.Renviron` file can save you a lot of time. If you are working with different companies and report suites then assigning it in the project itself is going to be the best option. Saving them as global variables could cause confusion when sharing reports. .col-md-4[ ``` AW_COMPANY_ID = "[Company ID]" AW_REPORTSUITE_ID = "[RSID]" ``` ] .col-md-8[  ] --- class: subsection # Finally! Let's Authenticate --- class: titlebody # Oauth Authentication ```r library(adobeanalyticsr) #load the package into the session aw_auth_with('oauth') #define the type of authentication aw_auth() #run the authentication function ``` ``` ## Successfully authenticated with OAuth ``` This function will initiate the authentication dance. -- .pull-left-3col[  ] -- .pull-left-3col[  ] -- .pull-left-3col[  ] --- class: titlebody # JWT Authentication ```r library(adobeanalyticsr) #load the package into the session aw_auth('jwt') #define the auth type in the authentication function ``` ``` ## Successfully authenticated with JWT: access token valid until 2022-07-27 14:18:09 ``` -- No dance on this one!  --- class: titlebody # Authentication is complete! .col-md-6[ When using **OAuth** authentication, I have access to 12 different accounts. ```r aw_auth('oauth') ``` ] -- .col-md-6[ When using **JWT** authentication, I only have access to 1 account, Search Discovery, because that is the account the token was setup under. ```r aw_auth('jwt') get_me() ``` ``` ## globalCompanyId companyName ## 1 search9 Search Discovery ``` ] --- class: subsection # Getting Started Functions --- class: titlebody # Define the Company ID Now that we have authenticated, we need to define the **Company ID** and **Report Suite** we are going to pull data from.
**Hot Tip 2:** The easiest way to find the Company ID is to use the name you find in Workspace on the top right. By filtering results by this name you can pull the `globalCompanyId` and save it as an object to be used in future API calls. .col-md-7[ ```r company_id <- get_me() %>% filter(companyName == 'Search Discovery') %>% pull(globalCompanyId) company_id ``` ``` ## [1] "search9" ``` ] .col-md-5.mw-75[  ] ??? Using the `get_me()` function I find the `company_id` associated with "Search Discovery". Usually this is pretty easy to find but when starting on a new account, you may need to clarify with someone just to make sure you are looking at the right company account. Some companies have multiple accounts like GSK. --- class: titlebody # Define the Report Suite Next, you will need to identify the ID for the appropriate Report Suite. Use the `aw_get_reportsuites()` function to help you find this.
**Hot Tip 3:** The easiest way to find the report suite id (RSID) is to pull all the report suites in the comapny and then filter using the `grepl()` function. .col-md-7[ ```r id <- aw_get_reportsuites(company_id = company_id, expansion = 'name', limit = 1000) %>% filter(grepl('SDI Website 2020 Production', name)) %>% pull(id) id ``` ``` ## [1] "ageo1xxpnwsdi2020prod" ``` ] .col-md-5.mw-75[  ] --- class: titlebody #
Super Hot Tip 4 .col-md-4[ I often find that new accounts/engagements have at least 1 relevant project created already. This allows me to use the debugger in the Workspace Project to find all the account information I need. ] .col-md-4[  ] -- .col-md-4[  ] -- .col-md-12[  ] ??? The last REALLY hot tip is where to find the rsid, company id, and all the variable ids to be use in the function to pull data. The debugger is a great way to cut some corners in the development of your analysis. --- class: maintitle # Pulling Data --- class: titlebody # Metrics ```r aw_get_metrics() ``` |id |name |type | |:----------------------|:------------------------------------|:-------| |averagepagedepth |Average Page Depth |int | |averagetimespentonpage |Average Time Spent on Page (seconds) |int | |averagetimespentonsite |Average Time Spent on Site (seconds) |int | |averagevisitdepth |Average Visit Depth |int | |bouncerate |Bounce Rate |percent | |bounces |Bounces |int | --- class: titlebody # Dimensions ```r aw_get_dimensions() ``` |id |title |description | |:---------------------|:-----------------------------|:-----------| |averagepagetime |Time Spent on Page - Bucketed |NA | |browser |Browser |NA | |browserheight |Browser Height - Granular |NA | |browserheightbucketed |Browser Height - Bucketed |NA | |browsertype |Browser Type |NA | |browserwidth |Browser Width - Granular |NA | --- class: titlebody # Calculated Metrics Calculated metrics have really long id names which make it hard to find. Since names can be identical across different calculated metrics, this function is a big help in identifying the correct calculated metrics you are looking for. ```r aw_get_calculatedmetrics() ``` |rsid |id |name | |:-----------------|:------------------------------------|:-------------------------------| |sdptnradamtest1 |cm300003965_557fc577e4b07e827d177ad0 |Crash Rate (Mobile) | |sdptnradamtest1 |cm300003965_557fc577e4b07e827d177ad3 |Average Session Length (Mobile) | |sdptnregan |cm300003965_557fc578e4b0094eea4f5201 |Single Access (Calculated) | |sdptnregan |cm300003965_557fc578e4b0094eea4f5204 |Crash Rate (Mobile) | |sdptnregan |cm300003965_557fc578e4b0416196926008 |Average Session Length (Mobile) | |sdptnr-coala-prod |cm300003965_557fc656e4b0adadba08f337 |Avg. Order Value | --- class: titlebody # Segments More to come on this topic but keep in mind that segments and calculated metrics are a key part to many of our analysis projects. It would do you well to get really good at understanding segments. ```r aw_get_segments(expansion = 'definition', rsids = Sys.getenv('AW_REPORTSUITE_ID'), limit = 100) ``` |name |definition |id | |:-----------------------------------|:----------|:-----------------------------------| |Adobe Test1 |visitors |53e4e60ee4b0acd00fa0ce9c | |Visits from United States |hits |53f634a6e4b0acd00fa0eec0 | |Barneys |hits |5516c646e4b060c760327bd2 | |Neenah Packaging |hits |5516d0bce4b060c760327bd4 | |Senior Manager of Analytics |hits |5527eed9e4b060c760329dde | |Site Section = blog |hits |55551fdbe4b08d109cd584ca | |Blog Visitor |visitors |s300003965_560aa300e4b0ff9bcc064a38 | |Form Viewers |visitors |s300003965_560aaa89e4b0298c6791a0b1 | |Recency - Last 90 days (full Range) |visitors |s300003965_565e4e0be4b0a1e1c628e8f8 | |Recency - Last 60 Days |visitors |s300003965_565e4ff0e4b080610428494c | --- class: subsection # Requesting Data --- class: titlebody # Simple Data Request There is convenient `prettynames` argument that allows you to ahve report ready column names. .col-md-6[ ```r #top 5 pages in the last 30 days by visits df <- aw_freeform_table(dimensions = 'evar1', metrics = 'visits', prettynames = F) knitr::kable(df) ``` |evar1 | visits| |:-----------------------------|------:| |/thanks-for-joining-us/ | 4622| |/how-we-help/partners/google/ | 3184| |/ | 2488| |/ga4-migration-assessment/ | 1226| |/how-we-help/services/ | 894| ] .col-md-6[ ```r #pretty names df <- aw_freeform_table(dimensions = 'evar1', metrics = 'visits', prettynames = T) knitr::kable(df) ``` |Page Path (v1) | Visits| |:-----------------------------|------:| |/thanks-for-joining-us/ | 4622| |/how-we-help/partners/google/ | 3184| |/ | 2488| |/ga4-migration-assessment/ | 1226| |/how-we-help/services/ | 894| ] --- class: titlebody # Pulling a Date Range Report You can choose either 'daterangehour', 'daterangeday', 'daterangeweek', 'daterangemonth', 'daterangequarter', 'daterangeyear' ```r df <- aw_freeform_table(date_range = c(Sys.Date() - 30, Sys.Date() - 1), dimensions = c('daterangeyear', 'daterangequarter', 'daterangemonth', 'daterangeday', 'daterangehour'), metrics = c("visits"), top = c(1, 1, 2, 0, 0) ) ``` |daterangeyear |daterangequarter |daterangemonth |daterangeday |daterangehour | visits| |:-------------|:-------------------|:--------------|:------------|:-------------------|------:| |2022 |Jul 2022 - Sep 2022 |Jul 2022 |2022-07-25 |2022-07-25 23:00:00 | 5| |2022 |Jul 2022 - Sep 2022 |Jul 2022 |2022-07-25 |2022-07-25 22:00:00 | 12| |2022 |Jul 2022 - Sep 2022 |Jul 2022 |2022-07-25 |2022-07-25 21:00:00 | 8| |2022 |Jul 2022 - Sep 2022 |Jul 2022 |2022-07-25 |2022-07-25 20:00:00 | 13| |2022 |Jul 2022 - Sep 2022 |Jul 2022 |2022-07-25 |2022-07-25 19:00:00 | 11| |2022 |Jul 2022 - Sep 2022 |Jul 2022 |2022-07-25 |2022-07-25 18:00:00 | 17| --- class: titlebody # Include Calculated Metrics ```r cm1 <- 'cm300003965_60200cc865d8fb7e012b1db6' aw_freeform_table(dimensions = 'daterangeday', metrics = cm1, prettynames = T) ``` |Day | Page Views / Visit| |:----------|------------------:| |2022-07-05 | 1.817877| |2022-06-28 | 1.654546| |2022-07-06 | 1.648107| |2022-06-29 | 1.616519| |2022-07-13 | 1.555678| --- class: titlebody # Multi-Dimensional Data Requests Unlimited Dimensions is now a thing! ```r #multilevel dimension df <- aw_freeform_table(date_range = c('2020-10-01', '2020-10-01'), dimensions = c('daterangeday','page','mobiledevicetype','browser','geocity'), metrics = 'visits', top = c(2)) knitr::kable(df) ``` |daterangeday |page |mobiledevicetype |browser |geocity | visits| |:------------|:------------------------------------------------|:----------------|:--------------------|:-----------------------------------------|------:| |2020-10-01 |Engage|Blog |Other |Google Chrome 85.0 |Delhi (Delhi, India) | 4| |2020-10-01 |Engage|Blog |Other |Google Chrome 85.0 |Glencoe (Illinois, United States) | 3| |2020-10-01 |Engage|Blog |Other |Google Chrome 84.0 |Suwanee (Georgia, United States) | 1| |2020-10-01 |Engage|Blog |Other |Google Chrome 84.0 |Stillwater (Minnesota, United States) | 1| |2020-10-01 |Engage|Blog |Mobile Phone |Safari |Portsmouth (New Hampshire, United States) | 2| |2020-10-01 |Engage|Blog |Mobile Phone |Safari |Atlanta (Georgia, United States) | 2| |2020-10-01 |Engage|Blog |Mobile Phone |Chrome Mobile 85.0 |Cebu (Cebu, Philippines) | 1| |2020-10-01 |Engage|Blog |Mobile Phone |Chrome Mobile 85.0 |Brooklyn (New York, United States) | 1| |2020-10-01 |Engage|Search Discovery Education Community |Other |Google Chrome 85.0 |Woodbridge (Virginia, United States) | 2| |2020-10-01 |Engage|Search Discovery Education Community |Other |Google Chrome 85.0 |Madrid (Madrid, Spain) | 2| |2020-10-01 |Engage|Search Discovery Education Community |Other |Mozilla Firefox 79.0 |Bronx (New York, United States) | 2| |2020-10-01 |Engage|Search Discovery Education Community |Mobile Phone |Safari |Suwanee (Georgia, United States) | 1| |2020-10-01 |Engage|Search Discovery Education Community |Mobile Phone |Safari |Scotts Valley (California, United States) | 1| |2020-10-01 |Engage|Search Discovery Education Community |Mobile Phone |Chrome Mobile 85.0 |Gurgaon (Haryana, India) | 1| |2020-10-01 |Engage|Search Discovery Education Community |Mobile Phone |Chrome Mobile 85.0 |Pune (Maharashtra, India) | 1| --- class: titlebody # Filter By Segments ```r #filtering all the segments I pulled earlier blog <- segs %>% filter(grepl('blog', name)) %>% pull(id) #segment id added aw_freeform_table(date_range = c('2020-10-01', '2020-12-31'), dimensions = c('page'), metrics = 'visits', top = 100, ##inlcude the segment id segmentId = blog ) ``` |page | visits| |:----------------------------|------:| |About | 1| |How We Help | 1| |Terms of Use | 1| |Engage|Blog | 1| |How We Help|Technology | 1| |Stay Current|Events | 1| |Engage|Blog|Search | 1| --- class: titlebody # Search Added Search argument works for R <v4.0 ```r #segment id added aw_freeform_table(date_range = c('2020-10-01', '2020-12-31'), dimensions = c('page'), metrics = 'visits', top = 10, search = "(CONTAINS 'blog')" ) ``` |page | visits| |:--------------------------------------------------------------------------------|------:| |Engage|Blog | 5956| |https://www.searchdiscovery.com/blog/google-analytics-app-plus-web | 447| |Engage|Blog|Search | 245| |https://www.searchdiscovery.com/blog/creating-checkout-funnel-google-data-studio | 168| |https://www.searchdiscovery.com/blog/adobe-analytics-new-features | 117| |https://www.searchdiscovery.com/blog/dynamic-url-parameters-in-adwords | 102| |https://www.searchdiscovery.com/blog/integrating-onetrust-with-adobe-launch | 64| |https://www.searchdiscovery.com/blog/bounce-rate-in-google-analytics-4 | 59| |https://www.searchdiscovery.com/blog/what-is-google-analytics-4 | 58| |https://www.searchdiscovery.com/blog/instagram-contest | 50| --- class: titlebody # Segment Freeform Table .blink_me[**New Function!**] This is the equivalent of a freeform table with segments as the row components. This type of table offers a few components that aw_freeform_table does not. For example, this function does not require (or allow) dimensions to be included in the breakdown. Segment IDs are automatically translated into their human-readable names. ```r seg1 <- 's300003965_5f89910f2b368719693edf6e' seg2 <- 's300003965_5eab136b34d05f0ff1c318ff' aw_segment_table(segmentIds = c(seg1, seg2), metrics = c('visits', 'pageviews')) ``` |name |id | visits| pageviews| |:-------------------------|:-----------------------------------|------:|---------:| |Careers Site Section Hits |s300003965_5f89910f2b368719693edf6e | 636| 904| |Blog Views |s300003965_5eab136b34d05f0ff1c318ff | 5657| 6817| --- class: maintitle # Building Segments --- class: subsection # New Function Introduction Junction --- class: titlebody # Verbs This data function will return a list of all available verbs for building segments. They are divided up into 3 different categories: 'Exists', 'Strings', and 'Numbers'. You can determine what category of verb to use by the object you are going to be using. For instance, if the object is going to be a number then the verb must be a number verb. ``` seg_verbs ``` |type |class |verb |description | |:------|:------|:-------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------| |string |string |streq |Equals | |string |string |not-streq |Not Equals | |string |string |strlt |Less Than | |string |string |strgt |Greater Than | |string |string |strle |Less Than or Equals | |string |string |strge |Greater Than or Equals | |string |list |streq-in |Match a string to any of the values in the parameter | |string |list |not-streq-in |Ensure a string doesn't match any of the values in the parameter | |string |string |contains |Ensure a string matches or contains the value in the parameter | |string |string |not-contains |Ensure a string doesn't match or contains the value in the parameter | |string |list |contains-any-of |Ensure a string contains any of the values in the parameter. Case-insensitive. | |string |list |contains-all-of |Ensure a string contains all of the values in the parameter. Case-insensitive. | |string |list |not-contains-any-of |Ensure a string doesn't contain at least one of the values in the parameter. Case-insensitive. | |string |list |not-contains-all-of |Ensure a string doesn't contain any of the values in the parameter. Case-insensitive. | |string |string |starts-with |Ensure a string starts with the value in the parameter. Case-insensitive. | |string |string |ends-with |Ensure a string ends with the value in the parameter. Case-insensitive. | |string |string |not-starts-with |Ensure a string doesn't start with the value in the parameter. Case-insensitive. | |string |string |not-ends-with |Ensure a string doesn't end with the value in the parameter. Case-insensitive. | |string |glob |matches |Ensure a string matches the glob parameter. A glob parameter uses a '' character to match any sequence of characters. A literal '' is expressed with '*'. | |string |glob |not-matches |Ensure a string doesn't match the glob parameter. A glob parameter uses a '' character to match any sequence of characters. A literal '' is expressed with '*'. | |number |number |eq |Equals | |number |number |not-eq |Not equals | |number |number |gt |Greater than | |number |number |lt |Less than | |number |number |ge |Greater than or equal to | |number |number |le |Less than | |number |list |eq-any-of |Equal to any of the values provided | |number |list |not-eq-any-of |Not equal to any of the values provided | |number |list |eq-in |Equal to any of the values provided | |number |list |not-eq-in |Not equal to any of the values provided | |string |exists |exists |Tests if an attribute has been set to a value. | |string |exists |not-exists |Tests if an attribute has never been set to a value. | |number |exists |event-exists |Tests if an attribute has been set to a number value. | |number |exists |not-event-exists |Tests if an attribute has never been set to a number value. | --- class: titlebody # New Functions 1. `seg_rule()` - This function creates the simple rule of a segment. `seg_rule(dimension, metric, verb, object)` 2. `seg_then()` - This function creates a then list object which restricts the time constraint of a segment to be added to a sequence segment. `seg_then(limit = "within", count = 1, unit = "year")` 3. `seg_con()` - This function combines rules into a container. `seg_con(context = "hits", conjunction = "and", rules = NULL, exclude = FALSE)` 4. `seg_seq()` - This function combines rules into a sequence container. `seg_seq(context = "visits", rules = NULL, sequence = "in_order")` 5. `seg_build()` - This function combines rules, containers and/or sequences into a single JSON string and can then make the post call to create the segment in Adobe Analytics or return the json string for use in other api calls or for validation. `seg_build(name, description, rules, context = "hits", conjunction = "and", create_seg = FALSE)` 6. `seg_val()` - Returns a segment validation response for a segment contained in a json string object. `seg_val(segment_body)` --- class: titlebody # Basic Segment Example ```r rule1 = seg_rule(dimension = 'daterangehour', verb = 'eq', object = c('2021-11-02', '1600')) rule2 = seg_rule(dimension = 'page', verb = 'contains', object = 'home') built <- seg_build('Basic Segment For My Test', 'This was created by Ben Woodard as a test', conjunction = 'and', rules = list(rule1, rule2), context = 'visitors') seg_val(built) ``` ``` ## [1] "The segment is valid." ``` --- class: titlebody # Adding containers to build the segment ```r rule1 = seg_rule(dimension = 'daterangehour', verb = 'eq', object = c('2021-11-02', '1600')) rule2 = seg_rule(dimension = 'page', verb = 'contains', object = 'home', description = 'something') rule3 = seg_rule(metric = 'visits', verb = 'exists') con1 = seg_con(context = 'hits', conjunction = 'or', rules = list(seg_rule(dimension = 'page', verb = 'contains', object = 'home', description = 'something'), rule3)) built <- seg_build('This is a Basic combined Segment For My Test', 'This was created by Ben Woodard as a test', conjunction = 'and', containers = list(rule1, con1), context = 'visits', create_seg = F) seg_val(built) ``` --- class: titlebody # Non-repeating Instance and Repeating Instance ```r rule1 <- seg_rule(dimension = 'page', verb = 'streq', object = 'Home', attribution = 'repeating') #default rule2 <- seg_rule(dimension = 'page', verb = 'streq', object = 'Home', attribution = 'nonrepeating', attribution_context = 'visits') rule3 <- seg_rule(dimension = 'visitnumber', verb = 'eq', object = 1, attribution = 'instance') built <- seg_build(name = 'Instance and Non-Repeating Instance example', description = 'This is the example of the repeating and non-repeating instance', rules = list(rule1, rule2, rule3), context = 'hits', conjunction = 'or', create_seg = F) seg_val(built) ``` --- class: titlebody # Count Distinct Dimension Segment ```r rule1 <- seg_rule(dimension = 'page', is_distinct = TRUE, verb = 'ge', #using number verb because the count distinct argument is true object = 5) built <- seg_build(name = "Count Distinct Segment", description = "Unique Counts", context = 'visitors', rules = list(rule1) ) seg_val(built) ``` --- class: titlebody # Excluding Containers in a segment ```r #create the rule to define the page view rule <- seg_rule(dimension = 'page', verb = 'streq', object = 'Home') #create the 2 page view limite per visitor con2 <- seg_seq(context = 'visitors', rules = list(rule, rule), sequence = 'in_order') #this uses the conjunction 'then' #create the container including either visitors with 1 or 2 pageviews con2_in <- seg_con(context = 'visitors', conjunction = 'or', rules = list(rule, con2)) #create the container exclude visitors with 3 pageviews (would automatically exclude more than 3 because they had 3 if they had 4.) con3_x <- seg_seq(context = 'visitors', rules = list(rule, rule, rule), sequence = 'in_order', exclude = TRUE) #create the segment in Adobe built <- seg_build(name = "Exclude 2 Home Pageview Visitors", description = "Written as an example", context = 'visitors', rules = list(con2_in, con3_x), conjunction = 'and') seg_val(built) ``` --- class: subsection # Sequence Segments --- class: titlebody # Basic Sequence ```r rule1 <- seg_rule(dimension = 'daterangeday', verb = 'eq', object = '2021-09-13') rule2 <- seg_rule(dimension = 'page', verb = 'contains', object = 'blog') built <- seg_build('Visited on 9/13 and then visited the blog', 'This was created by Ben Woodard as a test', sequences = list(rule1, rule2), sequence = 'in_order', context = 'visitors') seg_val(built) ``` --- class: titlebody # Basic Context Change Sequence ```r rule1 <- seg_rule(dimension = 'daterangeday', verb = 'eq', object = '2021-09-13') then <- seg_then(limit = 'after', count = 2, unit = 'hit') rule2 <- seg_rule(dimension = 'page', verb = 'contains', object = 'blog') built <- seg_build('Visited on 9/13 and then after 2 hits visited the blog', 'This was created by Ben Woodard as a test', sequences = list(rule1, then, rule2), sequence_context = 'visits', sequence = 'in_order', context = 'visitors') seg_val(built) ``` --- class: titlebody # Time Restriction (then) Sequence Adding a time restriction using the `seg_then()` function In the UI there is a clock after the "then" term. when clicked there are 2 options that are shown: `after` and `while`. You can choose any of the different ```r rule1 <- seg_rule(dimension = 'daterangeday', verb = 'eq', object = '2021-09-13') thenit <- seg_then('after', 1, 'hit') rule2 <- seg_rule(dimension = 'page', verb = 'contains', object = 'blog') sequ <- seg_seq(context = 'visits', rules = list(rule1, thenit, rule2)) built <- seg_build('Visited on 9/13 and then visited the blog', 'This was created by Ben Woodard as a test', containers = list(sequ), context = 'visits', sequence_context = 'visitors') seg_val(built) ``` --- class: titlebody # Within + After Time Restriction Sequence You can also add the 'after' and 'within' time restrictions together just like in the UI. The function will automatically put the proper one first but you still want to be cognizant of the time restriction you are placing on your function. ```r red1 <- seg_rule(dimension = 'daterangeday', verb = 'eq', object = '2021-09-13') thenit <- seg_then(limit = c('within', 'after'), count = c(1, 2), unit = c('week', 'visit')) #unit is always singular rule2 <- seg_rule(dimension = 'page', verb = 'exists') sequ <- seg_seq(context = 'visits', rules = list(red1, thenit, rule2)) built <- seg_build('Compound then statement', 'This was created by Ben Woodard as a test', containers = list(sequ), context = 'visitors') seg_val(built) ``` --- class: titlebody # Within + After Time Restriction & Standard Container This segment will combine the sequence container and basic container into a segment. .col-md-6[ ```r rule1 <- seg_rule(dimension = 'page', verb = 'streq', object = 'Home') thenit <- seg_then(limit = c('after', 'within'), count = c(1, 1), unit = c('hit', 'visit')) #unit is always singular rule2 <- seg_rule(dimension = 'page', verb = 'contains', object = 'Blog') rule3 <- seg_rule(dimension = 'visitnumber', verb = 'eq', object = 4) rule4 <- seg_rule(dimension = 'page', verb = 'exists') ``` ] .col-md-6[ ```r ## make the different containers (sequence container and basic container) sequ1 <- seg_seq(context = 'visits', rules = list(rule1, thenit, rule2, rule1)) con2 <- seg_con(context = 'hits', rules = list(rule3, rule4)) built <- seg_build(name = 'Compound then statement', description = 'This was created by Ben Woodard as a test', context = 'visitors', sequence_context = 'visits', sequences = list(sequ1, con2)) seg_val(built) ``` ] --- class: titlebody # Inline Segment Building ```r ## The basic flow of these objects can be looked at in this way. built <- seg_build(name = 'Compound then statement', description = 'This was created by Ben Woodard as a test', context = 'visitors', sequence_context = 'visits', sequences = list(seg_seq(context = 'visits', rules = list(seg_rule(dimension = 'page',verb = 'streq',object = 'Home'), seg_then(limit = c('after', 'within'), count = c(1, 1), unit = c('hit', 'visit')), seg_rule(dimension = 'page', verb = 'contains', object = 'Blog'), seg_rule(dimension = 'page',verb = 'streq',object = 'Home'))), seg_con(context = 'hits', rules = list(seg_rule(dimension = 'visitnumber', verb = 'eq', object = 4), seg_rule(dimension = 'page', verb = 'exists')))) ) ``` --- class: titlebody # In Order, Before, and After Sequence Sequence prefix and suffix will enable the user to define if the segment should only include those after or before the sequence of events. .col-md-6[ ```r rule1 <- seg_rule(dimension = 'page', verb = 'streq', object = 'Home') thenit <- seg_then(limit = c('after', 'within'), count = c(1, 1), unit = c('hit', 'visit')) #unit is always singular rule2 <- seg_rule(dimension = 'page', verb = 'contains', object = 'Blog') rule3 <- seg_rule(dimension = 'visitnumber', verb = 'eq', object = 4) rule4 <- seg_rule(dimension = 'page', verb = 'exists') ``` ] .col-md-6[ ```r ## make the different containers (sequence container and basic container) sequ1 <- seg_seq(context = 'visitors', rules = list(rule1, thenit, rule2), sequence = 'before') ## adding the sequence to include only visitors before con2 <- seg_con(context = 'hits', rules = list(rule3, rule4)) built <- seg_build('Using the sequence Before', 'This was created by Ben Woodard as a test', sequences = list(sequ1, con2), sequence_context = 'visits', sequence = 'after', ##Include only those visits after the sequence context = 'visitors') seg_val(built) ``` ] --- class: titlebody # Exclude checkpoints in a sequence ```r rule1 <- seg_rule(dimension = 'page', verb = 'streq', object = 'Home') rule2 <- seg_rule(dimension = 'page', verb = 'contains', object = 'blog') rule3 <- seg_rule(dimension = 'visitnumber', verb = 'gt', object = 3) seq1 <- seg_seq(context = 'visits', rules = list(rule1, rule2, rule3), sequence = 'in_order', exclude = F, exclude_checkpoint = 2) built <- seg_build(name = 'Exclude a checkpoint in a sequence', description = 'defined checkpoint 2 to be excluded from the sequence', containers = list(seq1), context = 'visits') seg_val(built) ``` --- class: maintitle # cjar --- class: keypoint  --- class: keypoint # Just Published on March 3, 2022 --- class: titlebody <img src="pres.demo_files/figure-html/unnamed-chunk-30-1.png" style="display: block; margin: auto;" /> --- class: titlebody # Authentication Very similar to adobeanalyticsr in a lot of ways. On catch, only JWT authentication is available at this time. So when you get your first client on CJA, make sure to ask for the JWT json file and private key file. ```r library(cjar) cjar::cja_auth('jwt') ``` ``` ## Successfully authenticated with JWT: access token valid until 2022-07-27 14:19:06 ``` --- class: subsection # Key Differences --- class: titlebody # Metric IDs Are Long The IDs for metrics and dimensions are a lot longer than what we are familiar with in Adobe Analytics. ```r mets <- cja_get_metrics() knitr::kable( head(mets$id) ) ``` |x | |:--------------------------------------------------------------------| |productListItems._experience.analytics.event601to700.event616.value | |productListItems._experience.analytics.event901to1000.event953.value | |_experience.analytics.event601to700.event678.value | |_experience.analytics.event201to300.event229.value | |_experience.analytics.event1to100.event9.value | |_experience.analytics.event501to600.event549.value | --- class: titlebody # Dataviews Instead Of Report Suites ```r dvs <- cja_get_dataviews() knitr::kable( head(dvs) ) ``` |name |id |systemUserOwned | |:------------------------------------|:---------------------------|:---------------| |ForgeDX IdeaCloud |dv_5f244913c5e4fb0e66545d73 |NA | |Demo System Data View |dv_5f31a113e2bc180e66186089 |NA | |Combined CJA Lab Action bgeorge |dv_5f3cabee5788290e6707196e |NA | |Stoke Salesforce Integration Demo v1 |dv_5f3e6cddeaf4860e6a8b4b1b |NA | |Stoke Salesforce Integration Demo v2 |dv_5f3ed4e98e7ce20e65688253 |NA | |Google Analytics Demo Data View |dv_5f4f1e2572ea4e09073ce262 |NA | --- class: titlebody # Filters Instead of Segments ```r filters <- cja_get_filters() knitr::kable( head(filters) ) ``` |name |description |organization |id |owner |dataId | |:--------------------|:-----------|:---------------------------------|:-----------------------------------------------------------|:-------------------------------------------------|:------| |GA: Not New People | |EDA8E66E593730510A495EFA@AdobeOrg |sEDA8E66E593730510A495EFA@AdobeOrg_5f89ceea63f04c09def28a9d |C6B17ECB62DD8D840A495FD1@b9bf09c062dd315c495e9d.e |NA | |Unique | |EDA8E66E593730510A495EFA@AdobeOrg |sEDA8E66E593730510A495EFA@AdobeOrg_5f89ceeaede7f509dec1d0e8 |CE73768462DD8D8F0A495E5F@b9bf09c062dd315c495e9d.e |NA | |Single Page Sessions | |EDA8E66E593730510A495EFA@AdobeOrg |sEDA8E66E593730510A495EFA@AdobeOrg_5f89ceed63f04c09def28a9e |C6B17ECB62DD8D840A495FD1@b9bf09c062dd315c495e9d.e |NA | |Single Touch | |EDA8E66E593730510A495EFA@AdobeOrg |sEDA8E66E593730510A495EFA@AdobeOrg_5f89ceeed08dac09dffc8dfe |C6B17ECB62DD8D840A495FD1@b9bf09c062dd315c495e9d.e |NA | |New Sessions | |EDA8E66E593730510A495EFA@AdobeOrg |sEDA8E66E593730510A495EFA@AdobeOrg_5f89ceeeede7f509dec1d0e9 |C6B17ECB62DD8D840A495FD1@b9bf09c062dd315c495e9d.e |NA | |Two Touch | |EDA8E66E593730510A495EFA@AdobeOrg |sEDA8E66E593730510A495EFA@AdobeOrg_5f89ceeeede7f509dec1d0ea |C6B17ECB62DD8D840A495FD1@b9bf09c062dd315c495e9d.e |NA | --- class: maintitle # Wrap Up --- class: titlebody # Current Use Case Examples 1. Automated Adobe Analytics Big Query + Data Studio Data Flow - As the cool kids say "AAABQ+DSDF" - There was a data flow setup sending data from AA (old API) > Domo > Google Sheets (via appscript) > Data Studio - The new Big Query integration with DS takes about 5 seconds to load vs 90+ seconds with Sheets 2. 1,506 Rule Segment Build - Needed to define a segment using 1,506 different rules - The Adobe system choked on the number of rules but it did validate and was trying to run - Discovered "contains any" along the way but the limit is 500 unique items. 3. Code First Segment Definitions and Maintenance for Reporting - Built 13 different complex segments for an HVA Analysis - Found out we were missing an exclusion rule - Created the exclusion rule, applied the object to the existing definitions - Reran the code to rebuild the segments - Less than 15 minutes - Delivered transparent segment definitions to the client in the report --- class: titlebody # What's Next? 1. **Projects** - The projects endpoint was just release publicly - Apollo has been dynamically creating projects via the API for over a year - Pull a project from an existing account, make edits to the dimensions, metrics, color scheme, and push it live in a brand new account. 2. **Annotations** - Announced during Adobe Summit last week - API has not been made public yet - Github repo for Adobe Analytics and CJA both got a PR merge yesterday with the new annotation endpoints - Compile information around annotations that are missing from the reports, format a spreadsheet of annotations and then push list to report suite 3. **Calculated Metrics** - Create a series of functions to build Calculated Metrics using code 4. **Segments as Global Filter** - There is a way to add a segment to the freeform table using the definition - This would aleviate the issue with having to create segments first and possibly overburdening the system with many duplicate segments. --- class: keypoint_spike # Questions?