## Calculating the Age of a Blog Post with CURRENT_DATE in Google Data Studio

Google Data Studio recently introduced a new function called CURRENT_DATE in September of 2020. Hallelujah! Now calculating the age of a blog post is easy. To start calculating the age of a blog post, the post's permalink must have a year and month embedded in it. The format of a blog post permalink URL should be

`http://mydomain.com/YEAR/MONTH/nameofpost.html`

where `YEAR` is a 4 digit year and `MONTH` is a 2 digit month.

To extract the year and month of a blog post, you can use the following calculated field formula, where Page is the page path Google Analytics dimension of a link (without the domain name).

Month of Blog Post
`substr(Page,7,2)`

Year of Blog Post
`substr(Page,2,4)`

To calculate the age of the blog post we use this monster of a calculated field. We will break down the nested formula functions piece by piece:

`floor(`
`date_diff(`
`current_date(), parse_date('%Y%m',`
`  concat(``Year of Blog Post,``Month of Blog Post``)`
`                          )`
`         )`
`       / 31`
`      )`

First let’s concatenate Year of Blog Post and Month of Blog Post with the concat() function. This will create a year and month string that can be parsed into a date.

For example, a blog post with /2010/10 in the URL will have 201010 passed into the parse_date() function. The parse_date() function uses the format mask %Y%m to convert this date string into a proper DATE type. Without a day of month component the default day is the first of the month ( 1 ). Therefore the parse_date() function will return October 1, 2010 for 201010.

To calculate the difference between today's date and the date of the blog post let's use the date_diff() function along with the brand new current_date() function. The date_diff() function will return the difference between two dates in days. To get the age of the blog post in months let's divide the date difference by 31, the number of days in most months.

Lastly, let's use the floor() function to round down the age in months. And voila! Using the URL you can accurately compute the age of a blog post. This calculated field can now be used with a session or page view count for each URL to compute the lasting traffic generation capability of a post. Blog posts that continue to generate traffic long after they have been posted indeed are well optimized for searches.

## Making Better Decisions with Real-Time Analytics Using the Wild Apricot Reports Manager

This is a companion blog post to the online presentation "Making Better Decisions with Real-Time Analytics Using the Wild Apricot Reports Manager" at the PersoniFest: A Virtual Experience. The presentation will be made available April 23, at 12:30pm CDT / 1:30 pm EDT. NewPath Consulting will be also hosting a virtual watch party by Zoom alongside this presentation.  See details on how to join the watch party here,

To enter the special prize giveaway, see the end of this post for a link!

The Wild Apricot Reports Manager (aka WARM) is NewPath Consulting's solution to extending Wild Apricot's reporting functionality. All Wild Apricot administrators can create “dashboards” to gauge goals, performance metrics and or even report across a network of Wild Apricot sites. Best of all the solution is completely free!

WARM is powered by Google Data Studio, a free business reporting and visualization solution that helps administrators securely summarize, transform and visualize Wild Apricot data and turn data into actionable information. The Wild Apricot Reports Manager can also offer a high degree of visibility across a network of Wild Apricot chapter or club sites. With WARM you can see how your organization or network is growing as well as understand how data can help to make better decisions.

This video and text walkthrough will demonstrate how quickly you can build your first report with Wild Apricot Reports Manager.

### Step 1. Go to Google Data Studio.

Click the create button in the upper left corner
Select Data Source to create a data source
Type in WARM or Wild Apricot to find the WARM data connector. Click the box under Partner Connectors..

### Step 2. Switch to your Wild Apricot administrative dashboard to create an authorized application. This will enable Google Data Studio to read information from your Wild Apricot database. The WARM connector uses the Wild Apricot API.

Go to settings and find the Integration section. Click Authorized applications.

Click the Authorize application button
Select Server application and click Continue
Name your application and provide Read only access permissions. Click the Save button

### Step 3. Now copy and paste the API key from the authorized application you created in Step 2 into the API key in the WARM configuration. This is a secret key that you should not share with anyone.

Select the Contacts object. Click the NEXT button to configure the connector. NOTE: The API key is blurred for security.
To configure the Contacts object for WARM, select page size. For contact databases that have less than 2,000 records set page size to 2,000 or more. For larger databases set page size to 1,000 or less. Click NEXT button.
Once you click NEXT you will see a message "Click "CONNECT" to continue. Click the CONNECT button in the upper right corner.

### Step 4. You will now see the Contacts dimensions. You may find some of these fields familiar from your Contacts database.

Click Create Report to create the report template

### Step 5. After a few seconds or minutes, depending on the size of your contact database you will see your report template. This report can be shared for read or edit access like any Google Doc. You can also schedule a snapshot of this data to be delivered in PDF format. This is the best way to create snapshots of your membership data automatically.

To schedule the report click the down arrow near the Share button and select Schedule email delivery.

### Step 6. Schedule the email delivery.

Add the email addresses that will receive the PDF report. You can create a custom schedule to deliver the report every month, for example.

## Beta Testing of WARM v2 is Open!

We are proud to opening the beta testing of Wild Apricot Reports Manager (WARM) v2!

The features of WARM v2 include:

v1.97 supports:

• support for accounts that have tens of thousands of contacts (v2 is has been tested for over 10,000 contacts)
• custom field support for Contacts
• added support for Sent Emails (automatic and manual emails and analytics)
• support the \$filter and \$count capabilities of API to improve filtering subsets of the Contact, Events and Sent Email data sets
• added support for Payments (payments recorded against invoices and manually)
• sample report to view capabilities of WARM

Known limitations:

• When accessing large data sets (more than 2,000 records) use the paging feature to retrieve data in pages of 1,000 records or less
• API rate limit may occur if you create reports with too many report gadgets (wait for a few minutes to retry)

All outstanding issues, enhancements and ongoing roadmap for projects for WARM are on GitHub. If you want to participate please login with your own GitHub account.

Current plans are to release v2 in production by late spring 2020.

## 11/15/2019

Google Tag Manager enables a website administrator to setup 'tags' which enable tracking of just about any event on a website including clicking on a file download, or clicking a Pay Online button on an event registration or even how far someone has scrolled on a page. Event tracking with Google Tag Manager is surprisingly effective to track how someone is using your website, or abandoning a particular workflow midstream.

To track file downloads we have created a sample page with 3 files in Wild Apricot. These links have been created with a content gadget.

Click to enlarge

This article will help you create a new set of  GA Behavior reports under the Behavior>Events. The new reports will have the category "Download" and will contain the file type (PDF, Word, Graphic, Excel, PowerPoint, etc) as well as the name of the file that was clicked on.

Event Reports in GA

When you click on the Event Category Download you will "drill down" to a report that describes the various file types that have been downloaded, listed under the Event Action column.

File Types are displayed under Event Action

There are 6 main steps in setting up the Google Tag Manager tag required to track file downloads. Of course you can modify this tag to suit, but we recommend you get this tag working before you start making any changes to it. We are assuming you have successfully configured Google Analytics in Wild Apricot as well as added added the necessary Google Tag Manager scripts to Wild Apricot. You must make sure GTM is configured and working properly with your website in "Preview" mode, so do that first before continuing with this tutorial.

The Download Files tag is described below with each step outlined under the image.

### Step 1. Create the tag

When creating the tag Download Files, select the Track Type "Event" which will allow you to add the category, action, label and the GA settings.

### Step 2.  Assign a Category

For Category we are using the word "Download" although you can name it whatever you like. This will appear as the category on the GA Event reports.

### Step 3. Create the Event Action

For the Event Action we have created a RegEx (regular expression) Table, a custom variable in GTM. The purpose of this custom variable is to convert the download link into a set of file type categories (eg PDF).

The RegEx Table works by converting the link of the download (stored in {{Click URL}} data layer variable) and testing to see what the URL ends with. We have 11 mappings here but you can add more if you'd like to your RegEx Table. To add another file extension just make sure you separate each extension with a pipe character (|).

### Output

`http[*\s]+pdf`

`PDF`

`http[*\s]+(doc|docm|docx|dot|dotm|dotx)` `Word`
`http[*\s]+(xls|xlsx|xlt|xltm|xltx|xlsm)` `Excel`
`http[*\s]+(pptx|pot|potx|ppt|pps|ppsm|ppsx|pptm)` `PowerPoint`
`http[*\s]+(txt|rtf)` `Text`
`http[*\s]+(bmp|gif|png|jpg|jpeg|tif|tiff)` `Graphic`
`http[*\s]+(htm|html)` `HTML Document`
`http[*\s]+xml` `XML Document `
`http[*\s]+(mp4|mpeg|wmv)` `Video `
`http[*\s]+mp3` `Audio`
`http[*\s]+(zip|gzip|tar)` `Compressed Archive`

### Step 4. Create the Event Label

The event label will store the file name of the downloaded document. In order for GTM to extract this information from the URL clicked, we create a Custom JavaScript variable. The Custom JavaScript GTM variable is a powerful mechanism to manipulate any of the click stream data (known as the data layer) passed to GTM while the visit is being tracked.

The JavaScript function is:

`function() {`

`    var filepath = {{Click Element}}.pathname.split("/");`

`// splits the pathname into an array of strings, using / as a delimiter`

`    var filename = filepath.pop();`

`// grab the last element of the array (ie the filename)`

`return filename.indexOf(".") > -1?decodeURI(filename):'N/A'`

`// is there is a period (.) in the filename?      ``// if so, return a clean file name, otherwise N/A`
`}`

### Step 5. Assign the Google Analytics ID

Each event tag must have a Google Analytics ID assigned to it. The  ID is a string that starts with UA- in your Google Analytics installation script.

We have created a custom variable of type "Google Analytics Settings" and stored our GA ID there. This way we can reuse this ID in other tags without having to remember and retype the Google Analytics ID.

Google Analytics ID configured as a Variable

### Step 6. Create the Firing Trigger for the Tag

Finally we setup the firing trigger using the "Click - Just Links" trigger type. We will fire the trigger to record the data only when the URL contains one of our file download extensions. We do not want to fire this tag on all links!

The regular expression for Click URL is

`(pdf|doc|docm|docx|dot|dotm|dotx|xls|xlsx|xlt|xltm|xltx|xlsm|pptx|pot|potx`

`|ppt|pps|ppsm|ppsx|pptm|txt|rtf|bmp|gif|png|jpg|jpeg|tif|tiff|htm|html|`

`xml|mp4|mpeg|wmv|mp3)\$`

Again, this regex can be modified in case you wish to track other file types. This is a concatenation of all the file extensions defined in the regex table in step 4.

### Step 7. Test the event with Realtime reports in GA

Once the tag has been created, you can "Preview" the GTM container on your website. You will see a GTM "Debug" screen overlaid on the bottom of your site where you can check if the tag is firing properly by clicking on one of the links to download it. Once the tag is properly being triggeredyou can see Realtime events in Google Analytics when you download a few files.

NOTE: Before testing your GTM tags, you will need to click the "Preview" button each time you make a change in GTM and reload your Wild Apricot site to make sure it picks up any changes you have made to the tag configuration.

When you click on the Download Event category you will see the various file names you have clicked on.

### Step 8. Publish the GTM container

Finally, when this is working you can publish the container. Use the blue "Publish" button at the top right of the GTM window. At this point all click traffic that download files will be recorded for posterity and available for reporting just like any other type of traffic under the Behavior>Events reports.

## Lessons Learned: Building a Google Data Studio Partner Connector

We built an approved Partner connector called Wild Apricot Reports Manager to get familiar with the capabilities of Google Data Studio and the Wild Apricot API. Community connectors are very powerful as they let you use data that is stored in any REST-API enabled app including thousands of SaaS applications available today. Community connectors can also fetch data through JDBC APIs, flat files (e.g. CSV, JSON, XML), and Google Apps Script Services. Community connectors work like a "data pipeline" between Google Data Studio and your favorite web service or app. Google Data Studio sends a request to your SaaS app through the community connector and the connector returns the requested data, so you can analyze and visualize it in Google Data Studio.

Google Data Studio Community Connector Overview (click to enlarge)

You can build and deploy community connectors using Google Apps Script, which is JavaScript-based, rapid application development platform that makes it fast and easy to create business applications that integrate with G Suite. Google Apps Script is Google's solution to scripting and automating their online suite of products. The best analogy is Visual Basic for Applications, the Microsoft-designed programming language from the 1990s that automates Microsoft Office and the rest of their desktop suite of products. Google Apps Script is designed and maintained for all of Google's apps and services, so it it very "online" friendly and can automate much of Google's web-based products.

This blog post will cover the things we learned between the summer of 2018 to fall 2019 about building a partner connector:

## Understand how a REST API works for your app or service

To analyze data with Google Data Studio, you need to understanding the REST API of the app whose data you wish to analyze. The REST API's underlying data structures will be used extensively by your connector. Wild Apricot's API, like any other REST API, is intended for use by developers with technical expertise, so it is important to get the technical documentation for your app's REST API. The Wild Apricot API help documentation is an excellent example. Doing simple tests on your app's REST API can help you see much more clearly what your connector will see.

You will also need to understand and implement the correct API authentication mechanisms for your REST API. Thankfully Wild Apricot supports a simple API key so that is what our connector uses for now. Most partner connectors require a more secure authentication method (such as oAuth 2) that does not pass the API key as a configuration parameter.

Here are the most common approaches to REST API authentication:

No Authentication

Some APIs don't require any authentication so you can simply call the endpoint ('url' in the call below). This is common for open data sets that are non-proprietary or don't contain any private information about people.

`var response = UrlFetchApp.fetch(url);`

Authentication in the query parameter

A web app that stores any personal data or transactional information will have an authentication step to access the API. The simplest protocol to use is the API key in a query parameter of the REST API endpoint URL. Here's an example code snippet where the API_KEY variable contains a uniquely created API key. You can think of this key as a password to your API, and it should not be stored in the code or in plain text. This key is generated (and can be revoked) in the web app. Wild Apricot supports the creation of one or more API keys.

`var response = UrlFetchApp.fetch(url + '&api_key=' + API_KEY);`

Another REST API authentication method involves passing the API key in the HTTP header, rather than in the URL. This can be considered more secure especially when the calls are done via SSL. Some REST APIs only allow authentication via HTTP headers. The code snipped below passes the API key  inside the advanced parameters of the Url Fetch App call.

`var params = {`
`    'headers': {`
`      'Authorization': 'Basic ' + API_KEY`
`    }`
`  };`

`var response = UrlFetchApp.fetch(url, params);`

oAuth Authentication

Finally, the most secure and supported REST API authentication method uses the oAuth 2 protocol. This is a multi-step process where you first validate your credentials and are granted an access token to use access the API.

Google has published an Apps Script oAuth 2 library to handle a lot of the mechanics of this process.

`var headers = {`
`    "Authorization": "Bearer " + service.getAccessToken()`
`  };`

## Create and test the basic community connector code

Currently there are many code samples that can be good starting points on the Google Data Studio Community Connectors github repository. You will be using the Google Apps Script editor for editing the set of files that comprise your connector. A community connector uses 4 function calls to drive the functionality -- `getAuthType(), getConfig(), getSchema() and getData()`. Learn them and love them! Remember you have complete access to JavaScript for manipulating the REST API data and doing basically anything you want before you return them to your community connector. The code of the connector can be used validate configuration inputs to the connector, create the schema and any extra dimensions or metrics beyond what the REST API returns as well as much else.

We published the code under an open-source licence for the Wild Apricot Reports Manager (WARM) on GitHub so you can use it as a starting point. You can see how we did some of the more interesting things like validate the configuration parameters and use date ranges for the Invoices and Auditlog endpoints. There is also a way to call multiple endpoints and combine the data in one set of Google Data Studio dimensions and metrics.

The purpose of the connector is to authorize access to your API (if required), collect any configuration parameters,  expose the appropriate dimensions and metrics (the schema) and populate the scheme with real-time data from your app into Google Data Studio. The configuration parameters along with the manifest file allow you to collect a variety of information like API keys, which end point you wish to use and other configuration information. There is a mechanism to allow the configuration parameters to be changed once they have been added to a report as well using the .SetAllowOverride option. This can be very useful especially when you wish to use alternate API keys from one data source!

We started our development from the Accounts API endpoint. It doesn't have any custom fields  and describes the Wild Apricot account information like the Account ID, domain name and name. This endpoint is a very simple introduction to the Wild Apricot API.

Here's a redacted JSON response from this call which we can expose to Google Data Studio. Every API response in JSON format will provide a very similar structure of name/value key pairs. In certain cases there will be nested arrays in this structure. It is really important to understand that every successful REST API call should respond with JSON like this:

`[`
` {`
`"Id": 221748,`
`"Url": "https://api.wildapricot.org/v2.1/Accounts/221748",`
`"PrimaryDomainName": "NewpathConsulting.wildapricot.org",`

`...`

`"Currency": {`
`"Name": "Canadian Dollar",`
`"Code": "CAD",`
`"Symbol": "\$"`
`            },`

`...`

`"Name": "NewPath Consulting",`
`"ContactLimitInfo": {`
`"CurrentContactsCount": 100,`
`"BillingPlanContactsLimit": 100`
`                    }`
` }`
`]`

## Identify the dimensions and metrics in each API endpoint

There are only 2 kinds of data fields in Google Data Studio: dimensions and metrics. Dimensions are things you want to measure, or that serve as ways to categorize your data. Metrics are numbers that measure the things contained in dimensions. A Google Data Studio report is simply a combination of visualizations that are comprised of one or more dimensions and zero or more metrics. Your connector must identify which part of the REST API is a dimension and which is a metric, so you can make your data source readily usable by your reporting analysts. In Wild Apricot an invoice number is a dimension, where as the invoice total amount is a metric.

Here's a sample piece of code in the Wild Apricot community connector that defines the Account schema. Note that the conceptType key/value pair defines whether the data field will be a dimension or metric. In this case there are 3 dimensions: Account Number, Account Domain and Account Name that will be available to any report.

`account: [`
`{`
`name: "Id",`
`label: "Account Number",`
`dataType: "NUMBER",`
`semantics: {`
`conceptType: "DIMENSION"`
`           }`
`},`
`{`
`name: "PrimaryDomainName",`
`label: "Account Domain",`
`dataType: "STRING",`
`semantics: {`
`conceptType: "DIMENSION",`
`semanticType: "URL"`
`           }`
`},`
`{`
`name: "Name",`
`label: "Account Name",`
`dataType: "STRING",`
`semantics: {`
`conceptType: "DIMENSION"`
`           }`
`}`
`]`

## Test and deploy your community connector

When you test your community connector you will be creating data sources and reports. A community connector has to be "installed" as a data source before it can be used. The deployment mechanism in Google Apps Script is used to create links that can be used to create a data source. Note that the Google Data Studio developer docs assume you know how to use the Google Apps Script development environment. Each community connector must have a manifest contained in the appsscript.json file. This file defines a lot of the elements of the connectors meta data including the support contact for the connector, the logo and other descriptive information.

To publish a connector for testing go to Publish --> Deploy from manifest... in the Apps Script editor.

By building and managing deployments, you can control the exact code version your users use. You can also maintain multiple deployments for production, testing and development.

You can create a new deployment with the red Create button. A new deployment is essentially a "fork" of your connector that can be deployed for use through a link. Within each deployment you can manage a "snapshot" of your code base that can be deployed for testing or to the public. For WARM we have a UAT (user acceptance testing) version that we use internally for testing. When we are ready to publish a production version we create a new Production version.

To create a new version just click on "Edit" next to the deployment you wish to update and select New under the Version drop down. You can also add some notes to the version.

To install create a new data source click on the Google Data Studio icon to the right of Get ID, which will reveal a link that can be linked (and even shared) for other Google Data Studio users to try. This is how a community connector is distributed. You can just select the deployment you wish to install and deploy, and you can install multiple versions of your data connectors under the same Google Account.

Debugging can be done in various ways but we normally use the console.log function call to print debugging messages out to the console or to Google's Stackdriver logging system.  You can see the output of various messages from the execution of the connector in the My Executions section in the G Suite Developer Hub

Note that once a connector has been deployed to your account you should see the connector appear in your Partner Connectors list, although your account will be the only one that sees community connectors deployed this way. See the partner connector submission process below to publish your connector to the public without needing to deploy using a link. In the screenshot below we have both a Production and a UAT deployment installed for use.

## Use the Community Connector API reference

We started to build the Wild Apricot connector in summer 2018 from the Google Data Studio community connector repository before the Community Connector API reference was published. In early 2019 we started to redesign the connector code with the Community Connector API reference and will redesign the connector using this approach because it is more compact, easier to read and exposes a variety of interesting features. The Community Connector API reference will also be supported by Google to add functionality to connectors as the platform evolves. It will also make it easier for us to develop a custom fields functionality to dynamically load any custom common and membership fields for the Members endpoint and any other future endpoints that have custom data that can be defined by the Wild Apricot administrator (such as event registration form fields).

## The partner connector review process is rigorous

Partner connectors are built and supported by  Google Data Studio developer partners and are tested and evaluated by Google's partner program for inclusion in the partner data connector list. One main benefit of partner connectors is that they are usable by anyone who tries Google Data Studio without having to install a community connector by link. To promote a community connector into a partner connector your connector must review and pass the requirements through the partner connector submission process. Here are a few gotchas that we had to ensure we built into the connector to pass the review process:

• the connector name must be 28 characters or less
• provide a complete the project manifest file (appsscript.json) with a valid set of key value pairs (tip: here is the WARM manifest file)
• get an approved pull request/merge of a new organization and source into the Google Data Studio official registry of community connectors.
• get an approved oAuth consent screen in a Google Cloud Platform project, which will allow your connector to be installed by any Google user without security warnings
• any template report, if provided in a the manifest file, must operate without any errors
• the connector code must validate any configuration parameters that are provided and present any errors as appropriate
• you may be asked to submit a screencast of your production connector being added to a new Google account (not the one you used to develop the connector) and show all the functionality of your connector including oAuth authorization

ps. The Google Data Studio Resources dashboard (built in Google Data Studio!) is a fantastic way to search through hundreds of GDS articles, videos and other resources.

## Introducing the Wild Apricot Reports Manager

We have worked with many Wild Apricot customers over the years and one consistent complaint comes up over and over: customers want better reporting capabilities to understand more about their organization's performance and membership demographics.

But the reporting capabilities for reporting in Wild Apricot are somewhat limited. Along side the membership summary there are only 5 reports in Wild Apricot. Wild Apricot administrators want to be able to analyze membership demographics, do cross-event reporting and trend membership data over time. Association executives also want to be able to report key performance indicators like revenue growth. When it comes time to do in-depth analysis using custom dashboards, a reporting system more powerful than Excel is required.

But why create reports at all? One reason is to help set goals. The report below shows a marked increase in traffic to NewPath Consulting's website in 2019, which can be attributed to the number of blog posts NewPath published in 2019 and an increase in high quality external links to the website. It's also clear that by attracting more blog traffic, that translates to more traffic to the rest of the site. Without long-term tracking of blog vs web page traffic it would be hard to know if our blogging efforts are translating to more traffic to the rest of the website. And more traffic has meant more qualified leads and business!

The Wild Apricot Reports Manager (aka WARM) is NewPath's custom reporting solution for Wild Apricot administrators who want an increased level of reporting flexibility and analytical power.

The Wild Apricot Reports Manager can:

1. Access your Wild Apricot database securely, in real-time without copy and paste or manual data extracts
2. Prepare tables, graphs and visualizations for one site or a network of Wild Apricot sites
3. Analyze Wild Apricot account, event, contact/member, invoice and audit log data with instant data refreshes
4. Report on data summaries, aggregations, comparisons and even calculated fields to create new insights
6. Share and distribute reports and dashboards with your board or staff, with on-demand or scheduled email delivery
7. WARM is an approved Partner Connector, part of Google Data Studio, a free reporting and visualization solution

Watch this short video to see how easy it is to create your first custom Wild Apricot report in Google Data Studio with WARM.

If you want to have a look at the code or improve on the data connector:

## Wild Apricot Custom Reporting Services

We have worked with many Wild Apricot customers over the years and one consistent requirement that comes up frequently: better reporting capabilities that can help inform the board and chapter leaders on key performance indicators, event performance and membership demographics across one Wild Apricot site or a network of Wild Apricot sites.

Starting at USD\$2,000*

The Wild Apricot Reports Manager (WARM) is a cost-effective way to deliver custom data extracts and analytical dashboards. With this service NewPath creates a completely custom reporting dashboard for your organization.

• Access your Wild Apricot database in real-time without copy and paste or manual data extracts
• Prepare tables, graphs and visualizations for one site or a network of Wild Apricot sites
• Analyze Wild Apricot data with instant data refreshes
• Report on data summaries, aggregations, comparisons and even calculated fields to create new insights
• Combine data from hundreds of other data services including Google Analytics, Amazon, Google Ads, Facebook, Twitter and more! You can even integrate freely available data sets from sources like Kaggle.
• WARM is an approved Partner Connector available in Google Data Studio, a free reporting and visualization solution
• *Discretionary discounts are available for military, public service and very small organizations.