Skip to content

Commit

Permalink
Merge pull request #868 from 2i2c-org/fundraising-strategy
Browse files Browse the repository at this point in the history
Add fundraising strategy document
  • Loading branch information
jnywong authored Jun 19, 2024
2 parents a36a640 + 80ce215 commit 289bab3
Show file tree
Hide file tree
Showing 8 changed files with 120 additions and 35 deletions.
9 changes: 8 additions & 1 deletion engineering/workflow/index.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
# Workflow

```{toctree}
:hidden:
default-workflow
1-week-iteration-workflow
```

This document outlines the delivery workflow for the engineering team.

There are currently two workflows:
1. [The default 2-week iteration process](./default-workflow) (paused)
1. [The hyper-focused 1-week iteration process](./1-week-iteration-worklflow) (active)
1. [The hyper-focused 1-week iteration process](./1-week-iteration-workflow) (active)

## Why the need for 'The hyper-focused 1-week iteration process'
This is a derivation of the default workflow and relies on many of its artifacts. It arose in direct response to the need to better support the grant writing process and short-term funding goals identified in our 2024 team retreat.
2 changes: 1 addition & 1 deletion operations/onboarding.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,4 +85,4 @@ Here are the right places to learn more about our workflow:
Different teams each interpret these in a different way, but this covers the basics.

Beyond these, your workflow will likely depend on the functional area where you work.
For example, [the engineering team](../engineering/workflow.md) tends to work heavily on GitHub and uses many GitHub Issues tickets to track its work.
For example, [the engineering team](../engineering/workflow/default-workflow.md) tends to work heavily on GitHub and uses many GitHub Issues tickets to track its work.
50 changes: 18 additions & 32 deletions partnerships/community_success/hub-activity.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,19 @@ kernelspec:
name: python3
---

+++ {"editable": true, "slideshow": {"slide_type": ""}, "user_expressions": []}
+++ {"editable": true, "slideshow": {"slide_type": ""}}

# Track Hub Usage with Grafana

+++ {"user_expressions": []}
+++

## Overview

Grafana is an open source analytics and interactive visualization web application. Prometheus is an open-source monitoring and alerting platform that collects and stores metrics as time-series data, which feeds into Grafana as a data source.

Grafana dashboard deployments for 2i2c hubs (k8s+JupyterHub) follow the templates outlined in the GitHub repository https://github.com/jupyterhub/grafana-dashboards. Note that Prometheus data is retained for 1 year on 2i2c hubs.

+++ {"user_expressions": []}
+++

## Prerequisites

Expand All @@ -46,7 +46,7 @@ See [Grafana docs – Service Accounts](https://grafana.com/docs/grafana/latest/

See [](../../reference/documentation/secrets.md) for a general guide to configuring access to the Grafana Token in a local development environment, or while deploying documentation with GitHub actions or Read the Docs.

+++ {"user_expressions": []}
+++

(hub-activity:python-packages)=
### Python packages
Expand All @@ -58,7 +58,7 @@ We require the following Python packages to run the code in this guide:
- `prometheus-pandas` – query Prometheus and format into Pandas data structures
- `plotly` – visualize interactive plots

+++ {"user_expressions": []}
+++

### Javascript

Expand All @@ -75,7 +75,7 @@ https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.4/require.min.js"

## Import packages and define functions

+++ {"user_expressions": []}
+++

Import packages and set the Pandas plotting backend to the Plotly engine.

Expand All @@ -84,7 +84,6 @@ import os
import re
import requests
from dotenv import load_dotenv
from urllib.parse import urlencode
from datetime import datetime
from dateparser import parse as dateparser_parse
from prometheus_pandas.query import Prometheus
Expand All @@ -94,17 +93,13 @@ import plotly.graph_objects as go
pd.options.plotting.backend = "plotly"
```

+++ {"user_expressions": []}

Load the Grafana token as an environment variable from the `.env` file or GitHub/Read the Docs secret.

```{code-cell} ipython3
load_dotenv()
GRAFANA_TOKEN = os.environ["GRAFANA_TOKEN"]
```

+++ {"user_expressions": []}

Define a `get_default_prometheus_uid` function to get the unique id of the Prometheus data source.

```{code-cell} ipython3
Expand Down Expand Up @@ -139,8 +134,6 @@ def get_prometheus_datasources(grafana_url: str, grafana_token: str) -> str:
```

+++ {"user_expressions": []}

Define the `get_pandas_prometheus` function that creates and Prometheus client and formats the result into a pandas dataframe.

```{code-cell} ipython3
Expand All @@ -167,20 +160,16 @@ def get_pandas_prometheus(grafana_url: str, grafana_token: str, prometheus_uid:
return Prometheus(proxy_url, session)
```

+++ {"user_expressions": []}

## Execute the main program

+++ {"user_expressions": []}
+++

Fetch all available data sources from Prometheus.

```{code-cell} ipython3
datasources = get_prometheus_datasources("https://grafana.pilot.2i2c.cloud", GRAFANA_TOKEN)
```

+++ {"user_expressions": []}

Define a query for the data source using [PromQL](https://prometheus.io/docs/prometheus/latest/querying/basics/), formatted as a string. The query below finds the maximum number of unique users over the last 24 hour period and aggregrates by hub name.

```{code-cell} ipython3
Expand All @@ -191,22 +180,27 @@ query = """
"""
```

+++ {"user_expressions": []}

:::{note}

Writing efficient PromQL queries is important to make sure that the query actually completes, especially over large periods of time. However, most queries users of JupyterHub are bound to make are fairly simple, and you don't need to be a PromQL expert.

You can borrow a lot of useful queries from the GitHub repository [jupyterhub/grafana-dashboards](https://github.com/jupyterhub/grafana-dashboards), from inside the `jsonnet` files. The primary thing you may need to modify is getting rid of the `$hub` template parameter from queries.
:::

+++ {"user_expressions": []}
+++

Loop over each datasource and call the `get_pandas_prometheus()` function to create a Prometheus client for querying the server with the API. Evaluate the query from the last month to now with a step size of 1 day and output the results to a pandas dataframe. Save each output into an `activity` list item and then concatenate the results together at the end.
Loop over each datasource, test the connection to the hub and then call the `get_pandas_prometheus()` function to create a Prometheus client for querying the server with the API. Evaluate the query from the last month to now with a step size of 1 day and output the results to a pandas dataframe. Save each output into an `activity` list item and then concatenate the results together at the end.

```{code-cell} ipython3
activity=[]
for prometheus_uid in datasources['uid']:
# Test connection to hub
try:
r = requests.get(datasources.loc[datasources['uid']==prometheus_uid, 'url'].values[0])
except requests.exceptions.RequestException as err:
print(f"{datasources.loc[datasources['uid']==prometheus_uid, 'name'].values[0]}: Error{err}: ")
continue
# Query Prometheus server
prometheus = get_pandas_prometheus("https://grafana.pilot.2i2c.cloud", GRAFANA_TOKEN, prometheus_uid)
df = prometheus.query_range(
query,
Expand All @@ -218,39 +212,31 @@ for prometheus_uid in datasources['uid']:
df = pd.concat(activity)
```

+++ {"user_expressions": []}

## Pre-process and visualize the results

+++ {"user_expressions": []}
+++

Round the datetime index to nearest calendar day.

```{code-cell} ipython3
df.index = df.index.floor('D')
```

+++ {"user_expressions": []}

Rename the hubs from the raw data, `{namespace="<hub_name>"}`, to a human readable format using regex to extract the `<hub_name>` from the `"` double-quotes.

```{code-cell} ipython3
df.columns = [re.findall(r'[^"]+', col)[1] for col in df.columns]
```

+++ {"user_expressions": []}

Sort hubs by most number of unique users over time.

```{code-cell} ipython3
df = df.reindex(df.sum().sort_values(ascending=False).index, axis=1)
```

+++ {"user_expressions": []}

### Unique users in the last 24 hours

+++ {"user_expressions": []}
+++

Plot the data! 📊

Expand Down
90 changes: 90 additions & 0 deletions partnerships/fundraising.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# Fundraising strategy

:::{admonition} Our funding goal for 2024
_By **August 7th 2024**_

We have a **projected** expected monthly revenue of $180,000 in October 2024.
:::

## Primary funding strategy

This is the primary funding strategy that will guide Partnerships and Engineering through October 2024:

:::{epigraph}
Raise funding for _networks_ of communities that share the same workflows, domains, or impact.
:::

Here's a rough idea for how to operationalize this strategy:

1. Identify collections of communities that share similar goals or workflows.
2. Identify community leaders that can collaborate with us on a proposal.
3. Identify funders that care about the outcomes from these communities.
4. Raise funds for the entire network at-once.

```{figure} images/fundraising-diagram.png
An overview of the network of service and funder relationships we aim to create with fundraising.
```

This strikes a balance between "scalable and very simple hubs" (which require a lot of administrative toil) and "fully bespoke hubs" (which are expensive and unscalable). By using a single funding contract to serve many communities, we can reduce the amount of sales and contracting toil for each community. This will let us raise funds more quickly, and focus more of our time on delivering service.

### Engineering tactics

- **Define a list of hub "ingredients" that we can deploy.** These ingredients give our Partnerships teams options for what functionality they can offer _out-of-the-box_.
- **Make it efficient to deploy these quickly for a hub.** This will let the Partnerships team be agile in responding to opportunities from the market.

### Partnership creation tactics

- **Identify qualified networks of communities.** These communities share workflows and interests, and would be well-served by a hub "distribution" for them.
See [](#qualifying-communities) for guidelines on how to qualify them.
- **Define a mission / impact statement for the network.** What is the collective problem that connects these communities?
- **Define one to three champions for the network.** These will be leaders that we can partner with in helping grow the community and potentially fundraise.
- **Identify a funder to fund this mission.** Fund the entire group at once from a funder (foundation, agency, large buyer, etc).
- **Develop and deliver service once funding arrives.** Once contracts begin, we will have time to develop the capabilities needed to deliver hub service, and identify communities to use the service.


### Community success tactics

- **Use hub ingredients to define a base hub for the network.** Do this in collaboration with hub champions. This provides the basic hub that any community in the network will get.
- **Offer learning material for workflows on a hub network.** This gives community leaders a starting point for how to make the hub theirs and encourage specific workflows around it.
- **Develop community leaders through guidance and support.** Work with community leaders throughout the project so that they are equipped to guide their communities.

(qualifying-communities)=
## Qualifying a network of communities

The following guidelines help us identify potential networks of communities that are a good fit for this network.
Use this information to decide if it is worth investing more time into a community network.

+ A _community_ is a group of 10 to 1000 people. (Example: People that work in the engine room on a cruise ship.)
+ A _prospective community_ is a community with the potential to benefit from cloud-hosted interactive computing and open science social innovation. (Example: People who want to understand the urban <> ecology interactions in estuaries and marshes in San Francisco bay.)
+ A _qualified prospective community_ is a prospective community 2i2c has more fully described by filling out the community's qualification metadata:

| Name | Value |
| ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| Community | What is the name of this community? |
| Mission | What is the mission of the statement for this community?<br>What **value** does the community seek? |
| Leadership | Who is the champion for this community?<br>Is there a person who can serve as technical lead?<br>What is the leadership capacity for this community? |
| Demographics | Describe the community |
| Data | Describe the data used by this community. |
| Compute | What kind of hardware does this community need? |
| Environment | What kind of software tools does this community need? |
| Funder | What organization has funded or is likely to fund this community? |
| Plan | What is the plan? |


## Secondary strategies

These are opportunities to pursue only if there’s a natural opportunity.

**Development-focused grants.** These are grants designed to expand technical capabilities of some aspect of our hubs service. We should only pursue them if they are (1) Substantial in size, (2) Clearly aligned with our product strategy.

**Strategic and core support grants.** This is a 2i2c-wide grant that funds our vision and intended impact, and is not attached to any specific service. It funds the core roles needed to execute on our mission, and is similar to a "Series A" round of funding for a non-profit.

## Appendix: Options available to us

- **Simple hubs that scale to many hundreds of communities.** These require extreme growth to achieve sustainability. Small contracts, large N of contracts.
- **Complex hubs that scale to tens of communities within a domain.** These build on a base hub deployment, with predefined options for customization to a specific use-case. Medium contracts, medium N of contracts.
- **Fully bespoke hubs.** that require full customization and are very expensive. Large contracts, low N of contracts.

## References

- [Team brainstorm on fundraising strategy](https://docs.google.com/document/d/1wiOZSStLIxFyUPtymQXo9fkTco_apKXLqLHCNmUk2fo/edit).
Binary file added partnerships/images/fundraising-diagram.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions partnerships/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ This includes partnerships involving paid contracts, as well as via informal and
overview.md
structure.md
workflow.md
fundraising.md
../communication/index.md
community_success/freshdesk.md
community_success/hub-activity.ipynb
Expand Down
3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,5 @@ slack-sdk
python-dotenv
dateparser
prometheus-pandas
plotly
plotly
tomlkit

0 comments on commit 289bab3

Please sign in to comment.