Skip to content

Commit

Permalink
Merge pull request #67 from QAInsights/feature/telemetry
Browse files Browse the repository at this point in the history
Add: Feature/telemetry
  • Loading branch information
QAInsights authored Dec 5, 2023
2 parents 8ddb26e + 454d70c commit 5d84672
Show file tree
Hide file tree
Showing 11 changed files with 174 additions and 20 deletions.
Binary file modified .DS_Store
Binary file not shown.
14 changes: 5 additions & 9 deletions .github/workflows/release-workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,17 @@ jobs:

runs-on: ${{ matrix.os }}
env:
RELEASE_TAG: 0.1.0
RELEASE_TAG: 0.2.0
INTEL_DMG: Hamster-x86-64-intel.dmg
ARM64_DMG: Hamster-arm64-silicon.dmg
MIXPANEL_TOKEN: ${{ secrets.MIXPANEL_TOKEN }}

strategy:
matrix:
# os: [ windows-latest ]
# arch: [ 'X86' ]
os: [ self-hosted, macos, windows-latest ]
arch: [ 'X64', 'ARM64', 'X86' ]

steps:
# - name: Checkout feature/windows branch
# uses: actions/checkout@v3
# with:
# ref: feature/windows

- uses: actions/checkout@v3
- name: Set up Python 3.10.11
uses: actions/setup-python@v3
Expand All @@ -45,19 +39,21 @@ jobs:

- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install --upgrade pip
pip install py2app
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
if: matrix.os == 'macos' || matrix.os == 'self-hosted'

- name: Generate app for intel architecture
run: |
export MIXPANEL_TOKEN=${{ env.MIXPANEL_TOKEN }}
python setup.py py2app
mv dist dist-intel
if: matrix.os == 'macos' && matrix.arch == 'X64'

- name: Generate app for arm64 architecture
run: |
export MIXPANEL_TOKEN=${{ env.MIXPANEL_TOKEN }}
python setup.py py2app
mv dist dist-arm64
if: matrix.os == 'self-hosted' && matrix.arch == 'ARM64'
Expand Down
35 changes: 34 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ An app that puts your recent JMeter test plans just a click away from your Mac m
- [Build](#-build)
- [Install](#-install)
- [Points to Note](#-points-to-note)
- [Telemetry](#-telemetry)
- [License](#-license)
- [Sponsor](#-sponsor)

Expand Down Expand Up @@ -68,9 +69,41 @@ An app that puts your recent JMeter test plans just a click away from your Mac m
## 🎯 Points to Note

- **Experimental App**: Please note that this application is experimental and may contain bugs. ⚠️
- **macOS**: This application is only for macOS. 🍎
- **macOS**: This application is only for macOS and Windows.
- **JMeter**: This application requires JMeter to be installed on your machine. 📥

## 🔍 Telemetry

- Hamster collects anonymous usage data to improve the app. 📊
- The data collected is the menu clicks only.
- No personal information or entities are collected.
- The data collected is not shared with any third-party.
- The data collected is not used for any marketing purposes.
- The data is being collected using [Mixpanel](https://mixpanel.com).
- You can opt out of telemetry by configuring `false` in `~/.hamster_app.properties` file and restart the app.
- ```commandline
[TELEMETRY]
enabled = false
```
- You can opt in of telemetry by configuring `true` in `~/.hamster_app.properties` file and restart the app.
- ```commandline
[TELEMETRY]
enabled = true
```

## 🧩 Windows Vs Mac app features

| Features | Windows | Mac |
|---------------------------------|---------|-----|
| Launch JMeter |||
| Launch JMeter Recent Test Plans |||
| Set JMeter Home |||
| View Config |||
| Refresh |||
| Telemetry |||
| Updates |||


## 🗒️ License
- Apache 2 License

Expand Down
Binary file modified assets/Hamster.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions hamster/.hamster_app.properties
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
[JMETER]
home = /Users/naveenkumar/Tools/apache-jmeter-5.6.1

[TELEMETRY]
enabled = true
2 changes: 1 addition & 1 deletion hamster/__version__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
__version_info__ = (0, 1, 0)
__version_info__ = (0, 2, 0)
__version__ = '.'.join(map(str, __version_info__))
16 changes: 16 additions & 0 deletions hamster/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,16 @@
import getpass
import re
import shutil
import uuid
from pathlib import Path

from __version__ import __version__


def create_log_dir():
Path(app_config.log_dir).mkdir(parents=True, exist_ok=True)


class AppConfig:
def __init__(self):
self.app_title = 'Hamster'
Expand All @@ -17,6 +24,9 @@ def __init__(self):
self.buy_me_a_coffee_url = 'https://www.buymeacoffee.com/QAInsights'
self.authors = ['NaveenKumar Namachivayam', 'Leela Prasad Vadla']
self.about_website = 'https://QAInsights.com'
self.app_uuid = str(uuid.uuid4())
self.log_dir = os.path.join(os.path.expanduser("~"), 'hamster_logs')


@property
def authors_str(self):
Expand Down Expand Up @@ -68,6 +78,9 @@ def help_text(self):
jmeter_plist = f"/Users/{username}/Library/Preferences/org.apache.jmeter.plist"

app_config = AppConfig()
create_log_dir()

uuid = app_config.app_uuid


def jmeter_path():
Expand All @@ -77,3 +90,6 @@ def jmeter_path():
jmeter_home = jmeter_home.rstrip('/')
jmeter_bin = jmeter_home + '/bin/jmeter'
return jmeter_home, jmeter_bin



59 changes: 57 additions & 2 deletions hamster/menu.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,57 @@
import os
import webbrowser
import logging

from functools import wraps

import rumps
import subprocess
from utils import update_properties, show_splash_screen, prechecks, get_recent_jmeter_test_plans, sleep
from utils import update_properties, show_splash_screen, prechecks, get_recent_jmeter_test_plans, sleep, \
get_telemetry_config
from config import jmeter_path, icon_path, properties_file_path, config_parser, jmeter_plist
from config import app_config
from config import app_config, uuid

from mixpanel import Mixpanel

mp = Mixpanel(os.environ.get('MIXPANEL_TOKEN'))

# Create a logger
logger = logging.getLogger(__name__)

# Set the log level
logger.setLevel(logging.DEBUG)

# Create a file handler
handler = logging.FileHandler(os.path.join(app_config.log_dir, 'hamster.log'))

# Create a logging format
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)

# Add the handlers to the logger
logger.addHandler(handler)


def track(ids, menu_item):
telemetry_enabled = get_telemetry_config()

def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
try:
if telemetry_enabled:
mp.track(ids, menu_item)
logger.info(f"Clicked {menu_item}")
return func(*args, **kwargs)
else:
logger.info(f"Telemetry is disabled. Not tracking {menu_item}")
return func(*args, **kwargs)
except Exception as e:
logger.error(e)

return wrapper

return decorator


class DynamicMenuApp(rumps.App):
Expand Down Expand Up @@ -56,20 +103,23 @@ def refresh_test_plans(self, delay=5):
recent_test_plans_menu.add(rumps.MenuItem(test_plan, callback=self.menu_callback))

@rumps.clicked("Refresh")
@track(uuid, "Refresh")
def refresh(self, _):
"""
Refreshes Test Plans
"""
self.refresh_test_plans(1)

@rumps.clicked("Help")
@track(uuid, "Help")
def help(self, _):
"""
Displays the help screen.
"""
show_splash_screen()

@rumps.clicked("View Config")
@track(uuid, "View Config")
def view_config(self, _):
"""
Displays the configuration of the application.
Expand All @@ -81,6 +131,7 @@ def view_config(self, _):
rumps.alert("Error", e)

@rumps.clicked("Edit JMETER_HOME")
@track(uuid, "Edit JMETER_HOME")
def edit_home_path(self, _):
"""
Allows the user to edit the JMETER_HOME path.
Expand All @@ -102,6 +153,7 @@ def edit_home_path(self, _):
except Exception as e:
rumps.alert("Error", e)

@track(uuid, "Recent Test Plans")
def menu_callback(self, sender):
"""
Callback function for menu items.
Expand All @@ -114,6 +166,7 @@ def menu_callback(self, sender):
rumps.alert("Error", e)

@rumps.clicked("Launch JMeter")
@track(uuid, "Launch JMeter")
def just_jmeter(self, _):
"""
Launches JMeter without any test plan.
Expand All @@ -124,13 +177,15 @@ def just_jmeter(self, _):
rumps.alert("Error", e)

@rumps.clicked("Buy me a Coffee")
@track(uuid, "Buy me a Coffee")
def sponsor(self, _):
"""
Displays information about the application.
"""
webbrowser.open_new_tab(app_config.buy_me_a_coffee_url)

@rumps.clicked("About")
@track(uuid, "About")
def about(self, _):
"""
Displays information about the application.
Expand Down
43 changes: 38 additions & 5 deletions hamster/utils.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,28 @@
import logging
import plistlib
import time
import rumps

import os
from config import app_config
from pathlib import Path
from config import properties_file_path, config_parser, jmeter_plist, pattern, icon_path

# Create a logger
logger = logging.getLogger(__name__)

# Set the log level
logger.setLevel(logging.DEBUG)

# Create a file handler
handler = logging.FileHandler(os.path.join(app_config.log_dir, 'hamster.log'))

# Create a logging format
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)

# Add the handlers to the logger
logger.addHandler(handler)


def sleep(delay=1):
time.sleep(delay)
Expand All @@ -28,7 +46,7 @@ def get_recent_jmeter_test_plans():
a single string indicating that no recent JMeter test plans files were found.
"""
config_parser.read(properties_file_path)

recent_files = []

p = Path(jmeter_plist)
Expand All @@ -37,16 +55,17 @@ def get_recent_jmeter_test_plans():
try:
with open(jmeter_plist, 'rb') as fp:
pl = plistlib.load(fp)
recent_files = {k: v for k, v in pl['/org/apache/jmeter/']["gui/"]["action/"].items() if pattern.match(k)}
recent_files = {k: v for k, v in pl['/org/apache/jmeter/']["gui/"]["action/"].items() if
pattern.match(k)}

# escape file names with spaces
recent_files = {k: v for k, v in recent_files.items()}
recent_files = dict(sorted(recent_files.items()))
recent_files = list(recent_files.values())

except Exception as e:
rumps.alert("Error", e)

return recent_files


Expand Down Expand Up @@ -96,3 +115,17 @@ def update_properties(properties):

with open(properties_file_path, 'w') as config_file:
config_parser.write(config_file)


def get_telemetry_config():
"""
Returns the telemetry configuration.
"""

try:
config_parser.read(properties_file_path)
return config_parser.getboolean('TELEMETRY', 'enabled')
except Exception as e:
logging.error(f"{e} Telemetry config not found. Setting telemetry to False.")
return False

21 changes: 20 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,23 @@
certifi==2023.11.17
cffi==1.16.0
chardet==5.2.0
charset-normalizer==3.3.2
cryptography==41.0.7
Deprecated==1.2.14
idna==3.6
mixpanel==4.10.0
psutil==5.9.6
pycparser==2.21
PyGithub==2.1.1
PyJWT==2.8.0
PyNaCl==1.5.0
pyobjc-core==10.0
pyobjc-framework-Cocoa==10.0
python-dateutil==2.8.2
requests==2.31.0
rumps==0.4.0
psutil
sentry-sdk==1.38.0
six==1.16.0
typing_extensions==4.8.0
urllib3==2.1.0
wrapt==1.16.0
2 changes: 1 addition & 1 deletion windows/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__VERSION__ = "0.1.0"
__VERSION__ = "0.2.0"

0 comments on commit 5d84672

Please sign in to comment.