Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added: collection of usage statistics using Plausible #78

Merged
merged 2 commits into from
Sep 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,16 @@ further info.
- Drop us a line at hello@vizzuhq.com
- Follow us on Twitter: [VizzuHQ](https://twitter.com/VizzuHQ)

## Usage Statistics

`ipyvizzu-story` collects aggregate usage statistics by default to follow the
progress and overall trends of our library. This feature is optional, and users
can choose to opt-out. However, we do not track, collect, or store any personal
data or personally identifiable information. Please note that even when this
feature is enabled, publishing anything made with `ipyvizzu-story` remains GDPR
compatible. For more details, please visit
[Analytics chapter](https://ipyvizzu-story.vizzuhq.com/latest/tutorial/initialization/#analytics).

## License

Copyright © 2022-2023 [Vizzu Inc](https://vizzuhq.com).
Expand Down
28 changes: 25 additions & 3 deletions docs/tutorial/initialization.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,29 @@ story.set_size(width="800px", aspect_ratio=16 / 9)

## Story properties

### vizzu
### Analytics

The usage statistics feature in `ipyvizzu-story` allows aggregate usage data
collection using [Plausible](https://plausible.io/)'s algorithm. Enabling this
feature helps us follow the progress and overall trends of our library, allowing
us to focus our resources effectively and better serve our users.

We do not track, collect, or store any personal data or personally identifiable
information. All data is isolated to a single day, a single site, and a single
device only.

Usage statistics feature is optional, and by default, it is enabled (default
value: `True`). Users can choose to opt-out if they prefer not to participate in
data collection. Please note that even when this feature is enabled, publishing
anything made with `ipyvizzu-story` remains GDPR compatible.

To disable usage statistics feature, set `analytics` property to `False`.

```python
story.analytics = False
```

### Vizzu

`ipyvizzu-story` requires and downloads the
[Vizzu](https://github.com/vizzuhq/vizzu-lib) `JavaScript`/`C++`
Expand All @@ -77,7 +99,7 @@ story.vizzu = "<url>/vizzu.min.js"
The default value of `vizzu` property is `None`, because the default version
of `Vizzu` is stored in the `vizzu-story` package.

### vizzu_story
### Vizzu-Story

`ipyvizzu-story` requires and downloads the
[Vizzu-Story](https://github.com/vizzuhq/vizzu-ext-js-story) `JavaScript`
Expand All @@ -88,7 +110,7 @@ but you can also use a different or self-hosted version of it.
story.vizzu_story = "<url>/vizzu-story.min.js"
```

### start_slide
### Start slide

You can start the story on a specific slide via the `start_slide` property. You
can also use negative numbers, where `-1` means the last slide.
Expand Down
36 changes: 33 additions & 3 deletions src/ipyvizzustory/storylib/story.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
DISPLAY_TEMPLATE,
DISPLAY_INDENT,
)
from ipyvizzustory.__version__ import __version__


class Step(dict):
Expand Down Expand Up @@ -345,6 +346,7 @@ def __init__(self, data: Data, style: Optional[Style] = None):

super().__init__()

self._analytics = True
self._vizzu: Optional[str] = None
self._vizzu_story: str = VIZZU_STORY
self._start_slide: Optional[int] = None
Expand All @@ -365,6 +367,32 @@ def __init__(self, data: Data, style: Optional[Style] = None):

self["slides"] = []

@property
def analytics(self) -> bool:
"""
A property for enabling/disabling the usage statistics feature.

The usage statistics feature allows aggregate usage data collection
using Plausible's algorithm.
Enabling this feature helps us follow the progress and overall trends of our library,
allowing us to focus our resources effectively and better serve our users.

We do not track, collect, or store any personal data or personally identifiable information.
All data is isolated to a single day, a single site, and a single device only.

Please note that even when this feature is enabled,
publishing anything made with `ipyvizzu-story` remains GDPR compatible.

Returns:
The value of the property (default `True`).
"""

return self._analytics

@analytics.setter
def analytics(self, analytics: Optional[bool]):
self._analytics = bool(analytics)

@property
def vizzu(self) -> Optional[str]:
"""
Expand Down Expand Up @@ -510,12 +538,14 @@ def to_html(self) -> str:
vizzu_player_data = f"{json.dumps(self, cls=RawJavaScriptEncoder)}"
return DISPLAY_TEMPLATE.format(
id=uuid.uuid4().hex[:7],
vizzu_attribute=f'vizzu-url="{self._vizzu}"' if self._vizzu else "",
version=__version__,
analytics=str(self._analytics).lower(),
vizzu=f'vizzu-url="{self._vizzu}"' if self._vizzu else "",
vizzu_story=self._vizzu_story,
vizzu_player_data=vizzu_player_data,
start_slide=f'start-slide="{self._start_slide}"'
if self._start_slide
else "",
vizzu_story=self._vizzu_story,
vizzu_player_data=vizzu_player_data,
chart_size=self._size.style,
chart_features=f"\n{DISPLAY_INDENT * 3}".join(self._features),
chart_events=f"\n{DISPLAY_INDENT * 3}".join(self._events),
Expand Down
47 changes: 46 additions & 1 deletion src/ipyvizzustory/storylib/template.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,55 @@

DISPLAY_TEMPLATE: str = """
<div>
<vizzu-player id="{id}" {vizzu_attribute} {start_slide} controller></vizzu-player>
<vizzu-player id="{id}" {vizzu} {start_slide} controller></vizzu-player>
<script type="module">
import VizzuPlayer from "{vizzu_story}";

class IpyvizzuStory {{
static version = "{version}";
static analytics = undefined;

static changeAnalyticsTo(analytics) {{
if (IpyvizzuStory.analytics !== analytics) {{
console.log("ipyvizzu-story gather usage stats:", analytics);
IpyvizzuStory.analytics = analytics;
}}
if (analytics) {{
IpyvizzuStory._addHeadScript();
}} else {{
IpyvizzuStory._removeScript("ipyvizzu-story-analytics-head");
}}
}}

static _addHeadScript() {{
const scriptId = "ipyvizzu-story-analytics-head";
if (!IpyvizzuStory._isScriptAppended(scriptId)) {{
const script = document.createElement("script");
script.defer = true;
script.src = "https://plausible.io/js/script.local.js";
script.dataset.domain = "usage.ipyvizzu-story.com";
script.id = scriptId;
document.getElementsByTagName("head")[0].appendChild(script);
}}
}}

static _isScriptAppended(id) {{
return document.querySelector(`script[id="${{id}}"]`) !== null;
}}

static _removeScript(id) {{
const script = document.getElementById(id);
if (script) script.remove();
}}
}}

if (IpyvizzuStory.version !== window.IpyvizzuStory?.version) {{
window.IpyvizzuStory = IpyvizzuStory;
console.log(`ipyvizzu-story ${{IpyvizzuStory.version}}`);
}}

window.IpyvizzuStory?.changeAnalyticsTo({analytics});

const vp = document.getElementById("{id}");
import(vp.vizzuUrl).then(vizzuLoaded => {{
const lib = vizzuLoaded.default;
Expand Down
39 changes: 34 additions & 5 deletions tests/test_storylib.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
DISPLAY_TEMPLATE,
DISPLAY_INDENT,
)
from ipyvizzustory.__version__ import __version__


class TestHtml(ABC):
Expand Down Expand Up @@ -43,20 +44,24 @@ def get_vpd(self) -> str:

def get_html(
self,
vizzu_attribute="",
start_slide="",
version=__version__,
analytics=True,
vizzu="",
vizzu_story=VIZZU_STORY,
start_slide="",
chart_size="",
chart_features="",
chart_events="",
) -> str:
# pylint: disable=too-many-arguments
return DISPLAY_TEMPLATE.format(
id="1234567",
vizzu_attribute=vizzu_attribute,
start_slide=start_slide,
version=version,
analytics=str(analytics).lower(),
vizzu=vizzu,
vizzu_story=vizzu_story,
vizzu_player_data=self.get_vpd(),
start_slide=start_slide,
chart_size=chart_size,
chart_features=chart_features,
chart_events=chart_events,
Expand Down Expand Up @@ -139,7 +144,7 @@ def test_vizzu(self) -> None:
story.vizzu = vizzu
self.assertEqual(
story.to_html(),
self.get_html(vizzu_attribute=f'vizzu-url="{vizzu}"'),
self.get_html(vizzu=f'vizzu-url="{vizzu}"'),
)

def test_vizzu_story_default(self) -> None:
Expand Down Expand Up @@ -377,3 +382,27 @@ def test_to_html_with_event(self) -> None:
)
),
)

def test_to_html_analytics(self) -> None:
with unittest.mock.patch(
"ipyvizzustory.storylib.story.uuid.uuid4", return_value=self
):
story = self.get_story()
self.assertEqual(
story.analytics,
True,
)
self.assertEqual(
story.to_html(),
self.get_html(analytics=True),
)

story.analytics = False
self.assertEqual(
story.analytics,
False,
)
self.assertEqual(
story.to_html(),
self.get_html(analytics=False),
)