ga4-cli 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,253 @@
1
+ Metadata-Version: 2.4
2
+ Name: ga4-cli
3
+ Version: 0.1.0
4
+ Summary: Command-line interface for Google Analytics 4
5
+ Home-page: https://github.com/sulimanbenhalim/ga-cli
6
+ Author: Suliman Ben Halim
7
+ Author-email: suliman.benhalim@binary.ly
8
+ License: MIT
9
+ Project-URL: Bug Reports, https://github.com/sulimanbenhalim/ga-cli/issues
10
+ Project-URL: Source, https://github.com/sulimanbenhalim/ga-cli
11
+ Project-URL: Documentation, https://github.com/sulimanbenhalim/ga-cli#readme
12
+ Keywords: google-analytics ga4 cli command-line
13
+ Classifier: Development Status :: 3 - Alpha
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.8
19
+ Classifier: Programming Language :: Python :: 3.9
20
+ Classifier: Programming Language :: Python :: 3.10
21
+ Classifier: Programming Language :: Python :: 3.11
22
+ Requires-Python: >=3.8
23
+ Description-Content-Type: text/markdown
24
+ License-File: LICENSE
25
+ Requires-Dist: click<9.0.0,>=8.0.0
26
+ Requires-Dist: google-analytics-admin<1.0.0,>=0.27.0
27
+ Requires-Dist: google-auth<3.0.0,>=2.0.0
28
+ Requires-Dist: rich<14.0.0,>=13.0.0
29
+ Requires-Dist: pytz>=2023.3
30
+ Dynamic: author
31
+ Dynamic: author-email
32
+ Dynamic: classifier
33
+ Dynamic: description
34
+ Dynamic: description-content-type
35
+ Dynamic: home-page
36
+ Dynamic: keywords
37
+ Dynamic: license
38
+ Dynamic: license-file
39
+ Dynamic: project-url
40
+ Dynamic: requires-dist
41
+ Dynamic: requires-python
42
+ Dynamic: summary
43
+
44
+ # GA CLI - Google Analytics Command Line Interface
45
+
46
+ A command-line interface tool for managing Google Analytics 4 properties, accounts, and data streams.
47
+
48
+ ## Features
49
+
50
+ - List and manage Google Analytics accounts
51
+ - Create and manage GA4 properties
52
+ - Manage data streams and retrieve measurement IDs
53
+ - Beautiful table output with Rich
54
+ - JSON output support
55
+ - Authentication via service account credentials
56
+
57
+ ## Installation
58
+
59
+ ### From source
60
+
61
+ ```bash
62
+ cd ga-cli
63
+ pip install -e .
64
+ ```
65
+
66
+ ### Using pip (when published)
67
+
68
+ ```bash
69
+ pip install ga-cli
70
+ ```
71
+
72
+ ## Setup
73
+
74
+ ### 1. Get Google Service Account Credentials
75
+
76
+ 1. Go to [Google Cloud Console](https://console.cloud.google.com/)
77
+ 2. Create or select a project
78
+ 3. Enable the Google Analytics Admin API
79
+ 4. Create a service account with Analytics Admin permissions
80
+ 5. Download the JSON credentials file
81
+
82
+ ### 2. Initialize GA CLI
83
+
84
+ ```bash
85
+ ga-cli config init
86
+ ```
87
+
88
+ This will prompt you for the path to your service account JSON file and test the credentials.
89
+
90
+ Alternatively, set the environment variable:
91
+
92
+ ```bash
93
+ export GOOGLE_APPLICATION_CREDENTIALS=/path/to/credentials.json
94
+ ```
95
+
96
+ ## Usage
97
+
98
+ ### Configuration
99
+
100
+ ```bash
101
+ # Initialize with credentials
102
+ ga-cli config init
103
+
104
+ # Show current configuration
105
+ ga-cli config show
106
+ ```
107
+
108
+ ### Accounts
109
+
110
+ ```bash
111
+ # List all accounts
112
+ ga-cli accounts list
113
+
114
+ # Get account details
115
+ ga-cli accounts get <account-id>
116
+
117
+ # Output as JSON
118
+ ga-cli accounts list --format json
119
+ ```
120
+
121
+ ### Properties
122
+
123
+ ```bash
124
+ # List properties for an account
125
+ ga-cli properties list <account-id>
126
+
127
+ # Get property details
128
+ ga-cli properties get <property-id>
129
+
130
+ # Create a new property
131
+ ga-cli properties create <account-id> --name "My Website" --timezone "America/New_York" --currency "USD"
132
+
133
+ # Delete a property
134
+ ga-cli properties delete <property-id>
135
+ ```
136
+
137
+ ### Data Streams
138
+
139
+ ```bash
140
+ # List data streams for a property
141
+ ga-cli datastreams list <property-id>
142
+
143
+ # Get data stream details (including measurement ID)
144
+ ga-cli datastreams get <property-id> <stream-id>
145
+
146
+ # Create a new web data stream
147
+ ga-cli datastreams create <property-id> --name "Main Website" --url "https://example.com"
148
+ ```
149
+
150
+ ## Examples
151
+
152
+ ### Quick workflow to create a new GA4 property
153
+
154
+ ```bash
155
+ # 1. List your accounts to get the account ID
156
+ ga-cli accounts list
157
+
158
+ # 2. Create a new property
159
+ ga-cli properties create 123456789 --name "BOTCHA" --timezone "Africa/Tripoli"
160
+
161
+ # 3. Create a web data stream
162
+ ga-cli datastreams create 987654321 --name "BOTCHA Website" --url "https://botcha.example.com"
163
+
164
+ # 4. Get the measurement ID
165
+ ga-cli datastreams get 987654321 111222333
166
+ ```
167
+
168
+ ### Get measurement ID quickly
169
+
170
+ ```bash
171
+ # If you know your property and stream IDs
172
+ ga-cli datastreams get <property-id> <stream-id> | grep "Measurement ID"
173
+ ```
174
+
175
+ ## Command Reference
176
+
177
+ ### Global Options
178
+
179
+ - `--credentials PATH` - Path to service account credentials file
180
+ - `--version` - Show version
181
+ - `--help` - Show help message
182
+
183
+ ### Output Formats
184
+
185
+ Most list and get commands support:
186
+ - `--format table` (default) - Beautiful table output
187
+ - `--format json` - JSON output
188
+
189
+ ## Development
190
+
191
+ ### Setup development environment
192
+
193
+ ```bash
194
+ # Clone the repository
195
+ git clone <repository-url>
196
+ cd ga-cli
197
+
198
+ # Create virtual environment
199
+ python -m venv venv
200
+ source venv/bin/activate # On Windows: venv\Scripts\activate
201
+
202
+ # Install in development mode
203
+ pip install -e .
204
+ ```
205
+
206
+ ### Run tests
207
+
208
+ ```bash
209
+ pytest tests/ -v
210
+ ```
211
+
212
+ ## Project Structure
213
+
214
+ ```
215
+ ga-cli/
216
+ ├── ga_cli/
217
+ │ ├── __init__.py
218
+ │ ├── cli.py # Main CLI entry point
219
+ │ ├── auth.py # Authentication manager
220
+ │ ├── config.py # Configuration manager
221
+ │ ├── commands/
222
+ │ │ ├── accounts.py # Account commands
223
+ │ │ ├── properties.py # Property commands
224
+ │ │ ├── datastreams.py # Data stream commands
225
+ │ │ └── config.py # Config commands
226
+ │ └── formatters/
227
+ │ ├── table.py # Table formatter
228
+ │ └── json.py # JSON formatter
229
+ ├── tests/
230
+ ├── setup.py
231
+ ├── requirements.txt
232
+ └── README.md
233
+ ```
234
+
235
+ ## Requirements
236
+
237
+ - Python 3.7+
238
+ - Click 8.0+
239
+ - google-analytics-admin 0.27.0+
240
+ - google-auth 2.0+
241
+ - rich 13.0+
242
+
243
+ ## License
244
+
245
+ MIT License
246
+
247
+ ## Contributing
248
+
249
+ Contributions are welcome! Please feel free to submit a Pull Request.
250
+
251
+ ## Support
252
+
253
+ For issues and questions, please open an issue on GitHub.
@@ -0,0 +1,32 @@
1
+ ga4_cli-0.1.0.dist-info/licenses/LICENSE,sha256=9vweInHjCIGx8SsP1v0L3seMYTVersqfyZZtgD0P3ho,1076
2
+ ga_cli/__init__.py,sha256=zmVdMQWUJADbrUqT9Rt8ok0RDO5L2oZnPFw6XAJsRN0,85
3
+ ga_cli/auth.py,sha256=7D_QL4FOs1SeT3Xhjn6v6D4fBu34zk9Owel1ayLUnU8,1892
4
+ ga_cli/cli.py,sha256=gqojzERWqVwZjdI5_H36Flfbpr6oUPvnp9y34GDPF_M,954
5
+ ga_cli/config.py,sha256=PSRwRJPVhH5eCTM9Hy6KR65klpeeIrOb0HXEy4sTJu8,3040
6
+ ga_cli/decorators.py,sha256=Qn4LfV0Xs9680KrV0lcIt73dRAiTLXEprXOVJOVBYA4,3269
7
+ ga_cli/errors.py,sha256=7Qkw_vjJjVdApoG8IaPe5pStHCt4Ps8sELDRAtaDkGs,1612
8
+ ga_cli/logging_config.py,sha256=DqW5x7-pR-LTjXH88N7TPRt4RrqG0kzTTZlEjJXE-T4,937
9
+ ga_cli/output.py,sha256=JyOafS8DMqLvcjfqXf9xb1YAXb5nkw9LLhGclzcPISA,1581
10
+ ga_cli/retry.py,sha256=WFfdGw7N5ohElUlKqOpdw2F1Y2w5uvx42pZRVkHzGy4,1432
11
+ ga_cli/validators.py,sha256=eDmZ_lnPInn3DNDkH-W-QUiv2C7N8qK33eZVxwEkooU,1457
12
+ ga_cli/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
+ ga_cli/commands/accounts.py,sha256=WIS4liWgnmfWWh_OHO-CboheZKbCQzRt-jqOgr9EUDk,2648
14
+ ga_cli/commands/config.py,sha256=fjxRB8wpJ_0a01R9whThkWhO7lBN8XMiOCsa0NRgeBc,2034
15
+ ga_cli/commands/datastreams.py,sha256=ps_HomGWPwlAgYXt9ZLNg5R90fbUmL0VJFVk6-7yv0M,5671
16
+ ga_cli/commands/properties.py,sha256=tnGe4OZujpCNwWUfVseGXRThlL3mXMyUz9Zjpkka4Js,5545
17
+ ga_cli/formatters/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
+ ga_cli/formatters/json.py,sha256=C1a-ghd-k7Sr7_Cmg9up96xcFueC4PgF2931pOhCcHM,145
19
+ ga_cli/formatters/table.py,sha256=HSwcH92wQSzgFebzr9z82NWrh27SEeD4mqj6bknhhg8,569
20
+ tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
21
+ tests/conftest.py,sha256=jrTHupz_iYC6kdm2dpPKt7zzjwu6Udq-yqL_RUWeXRg,2618
22
+ tests/test_accounts.py,sha256=-7xgnqYwzj24RhZldykUkj0MEGcOlfUnd5tpPkU_1qQ,1671
23
+ tests/test_auth.py,sha256=UhtpUZOKWXRA6ty_3N9GHPouuZNJB1kt_2oD3BphdiM,2939
24
+ tests/test_cli.py,sha256=2bbcFPON-vYfdylXawIULFh-Tgs6CzsfmpubD1-OBvQ,1539
25
+ tests/test_config.py,sha256=Ide0_EMkVX58HIFxPuvkw3toHBKQ6n5yqjRsGVEpv9M,5406
26
+ tests/test_errors.py,sha256=zANsghozyAoRf4MNoIEgqlbmO_HGjBwhg_AB9hJ-eE4,3197
27
+ tests/test_validators.py,sha256=LO9wBHawTcn8eZ_zTIxBV7WCOSRU_Wq1UD7szwJIj0o,5176
28
+ ga4_cli-0.1.0.dist-info/METADATA,sha256=MUpWxfDqb3HxhVjD5FzAfEVAEP1gE7D_tiq_jeJBTnU,5909
29
+ ga4_cli-0.1.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
30
+ ga4_cli-0.1.0.dist-info/entry_points.txt,sha256=RgZTBWAkc21O2EnGi13mqHuy-ClVEffLAKW7MZ5cpdU,42
31
+ ga4_cli-0.1.0.dist-info/top_level.txt,sha256=8h9UkbqhP9WgaX0PfFMR5XjE1v5CcsswRYUB6ErXLE8,13
32
+ ga4_cli-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.10.2)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ ga-cli = ga_cli.cli:cli
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 GA CLI Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,2 @@
1
+ ga_cli
2
+ tests
ga_cli/__init__.py ADDED
@@ -0,0 +1,3 @@
1
+ """Google Analytics CLI - Manage GA4 from the command line"""
2
+
3
+ __version__ = "0.1.0"
ga_cli/auth.py ADDED
@@ -0,0 +1,59 @@
1
+ """Authentication manager for Google Analytics Admin API"""
2
+
3
+ from google.analytics.admin import AnalyticsAdminServiceClient
4
+ from google.oauth2 import service_account
5
+ import os
6
+
7
+
8
+ class AuthManager:
9
+ """Manages authentication for Google Analytics Admin API
10
+
11
+ This class supports context manager protocol for proper resource cleanup.
12
+
13
+ Usage:
14
+ # Standard usage
15
+ auth = AuthManager(credentials_path)
16
+ client = auth.get_client()
17
+
18
+ # With context manager
19
+ with AuthManager(credentials_path) as client:
20
+ # Use client
21
+ pass
22
+ """
23
+
24
+ def __init__(self, credentials_path=None):
25
+ self.credentials_path = credentials_path or os.getenv('GOOGLE_APPLICATION_CREDENTIALS')
26
+ self._client = None
27
+
28
+ def __enter__(self):
29
+ """Context manager entry"""
30
+ return self.get_client()
31
+
32
+ def __exit__(self, exc_type, exc_val, exc_tb):
33
+ """Context manager exit - clean up client resources"""
34
+ if self._client:
35
+ # Close client connection
36
+ self._client = None
37
+ return False
38
+
39
+ def get_client(self, timeout=30):
40
+ """Get authenticated Analytics Admin API client
41
+
42
+ Args:
43
+ timeout: Request timeout in seconds (default: 30)
44
+
45
+ Returns:
46
+ AnalyticsAdminServiceClient: Authenticated client instance
47
+ """
48
+ if self._client is None:
49
+ if self.credentials_path:
50
+ credentials = service_account.Credentials.from_service_account_file(
51
+ self.credentials_path
52
+ )
53
+ self._client = AnalyticsAdminServiceClient(
54
+ credentials=credentials,
55
+ client_options={'api_endpoint': 'analyticsadmin.googleapis.com'}
56
+ )
57
+ else:
58
+ self._client = AnalyticsAdminServiceClient()
59
+ return self._client
ga_cli/cli.py ADDED
@@ -0,0 +1,35 @@
1
+ """Main CLI entry point"""
2
+
3
+ import click
4
+ from ga_cli import __version__
5
+ from ga_cli.commands.accounts import accounts
6
+ from ga_cli.commands.properties import properties
7
+ from ga_cli.commands.datastreams import datastreams
8
+ from ga_cli.commands.config import config
9
+ from ga_cli.config import ConfigManager
10
+
11
+
12
+ @click.group()
13
+ @click.version_option(version=__version__)
14
+ @click.option('--credentials', envvar='GOOGLE_APPLICATION_CREDENTIALS',
15
+ help='Path to service account credentials file')
16
+ @click.pass_context
17
+ def cli(ctx, credentials):
18
+ """Google Analytics CLI - Manage GA4 from the command line"""
19
+ ctx.ensure_object(dict)
20
+
21
+ if not credentials:
22
+ config_manager = ConfigManager()
23
+ credentials = config_manager.get_credentials_path()
24
+
25
+ ctx.obj['credentials'] = credentials
26
+
27
+
28
+ cli.add_command(accounts)
29
+ cli.add_command(properties)
30
+ cli.add_command(datastreams)
31
+ cli.add_command(config)
32
+
33
+
34
+ if __name__ == '__main__':
35
+ cli()
File without changes
@@ -0,0 +1,81 @@
1
+ """Account management commands"""
2
+
3
+ import click
4
+ from ga_cli.decorators import with_client
5
+ from ga_cli.formatters.table import format_table
6
+ from ga_cli.formatters.json import format_json
7
+ from ga_cli.validators import validate_account_id
8
+ from ga_cli.logging_config import logger
9
+ from ga_cli.retry import retry_on_transient_error
10
+
11
+
12
+ @click.group()
13
+ def accounts():
14
+ """Manage Google Analytics accounts"""
15
+ pass
16
+
17
+
18
+ @accounts.command()
19
+ @click.option('--format', type=click.Choice(['table', 'json']), default='table')
20
+ @click.pass_context
21
+ @with_client
22
+ def list(ctx, format):
23
+ """List all accounts"""
24
+ client = ctx.obj['client']
25
+ logger.info("Listing Google Analytics accounts")
26
+
27
+ accounts_data = []
28
+ for account in _list_accounts_with_retry(client):
29
+ accounts_data.append({
30
+ 'id': account.name.split('/')[-1] if '/' in account.name else account.name,
31
+ 'name': account.display_name or 'N/A',
32
+ 'region': account.region_code or 'N/A',
33
+ 'create_time': str(account.create_time).split('.')[0] if account.create_time else 'N/A',
34
+ })
35
+
36
+ logger.info(f"Found {len(accounts_data)} accounts")
37
+
38
+ if format == 'json':
39
+ format_json(accounts_data)
40
+ else:
41
+ format_table(accounts_data, title="Google Analytics Accounts")
42
+
43
+
44
+ @accounts.command()
45
+ @click.argument('account_id', callback=validate_account_id)
46
+ @click.option('--format', type=click.Choice(['table', 'json']), default='table')
47
+ @click.pass_context
48
+ @with_client
49
+ def get(ctx, account_id, format):
50
+ """Get account details"""
51
+ client = ctx.obj['client']
52
+ logger.info(f"Getting account details for: {account_id}")
53
+
54
+ account = _get_account_with_retry(client, account_id)
55
+
56
+ account_data = {
57
+ 'id': account.name.split('/')[-1] if '/' in account.name else account.name,
58
+ 'name': account.display_name or 'N/A',
59
+ 'region': account.region_code or 'N/A',
60
+ 'create_time': str(account.create_time).split('.')[0] if account.create_time else 'N/A',
61
+ 'update_time': str(account.update_time).split('.')[0] if account.update_time else 'N/A',
62
+ }
63
+
64
+ logger.info(f"Retrieved account: {account.display_name}")
65
+
66
+ if format == 'json':
67
+ format_json(account_data)
68
+ else:
69
+ format_table([account_data], title=f"Account: {account.display_name}")
70
+
71
+
72
+ @retry_on_transient_error()
73
+ def _list_accounts_with_retry(client):
74
+ """List accounts with retry logic"""
75
+ return list(client.list_accounts())
76
+
77
+
78
+ @retry_on_transient_error()
79
+ def _get_account_with_retry(client, account_id):
80
+ """Get account with retry logic"""
81
+ return client.get_account(name=f"accounts/{account_id}")
@@ -0,0 +1,65 @@
1
+ """Configuration commands"""
2
+
3
+ import click
4
+ import os
5
+ from ga_cli.config import ConfigManager
6
+ from ga_cli.auth import AuthManager
7
+
8
+
9
+ @click.group()
10
+ def config():
11
+ """Manage CLI configuration"""
12
+ pass
13
+
14
+
15
+ @config.command()
16
+ @click.option('--credentials', prompt='Path to service account JSON',
17
+ help='Path to Google service account credentials file')
18
+ def init(credentials):
19
+ """Initialize GA CLI with credentials"""
20
+ try:
21
+ credentials_path = os.path.expanduser(credentials)
22
+
23
+ if not os.path.exists(credentials_path):
24
+ click.echo(f"Error: Credentials file not found at {credentials_path}", err=True)
25
+ raise click.Abort()
26
+
27
+ click.echo("Testing credentials...")
28
+ auth = AuthManager(credentials_path)
29
+ client = auth.get_client()
30
+
31
+ accounts = list(client.list_accounts())
32
+
33
+ if accounts:
34
+ click.echo(f"Credentials valid! Found {len(accounts)} account(s)")
35
+
36
+ config_manager = ConfigManager()
37
+ config_manager.set_credentials_path(credentials_path)
38
+
39
+ click.echo(f"Configuration saved to {config_manager.config_file}")
40
+ click.echo("\nYou can now use ga-cli commands without specifying credentials")
41
+ else:
42
+ click.echo("Warning: Credentials are valid but no accounts found", err=True)
43
+
44
+ except Exception as e:
45
+ click.echo(f"Error: {str(e)}", err=True)
46
+ raise click.Abort()
47
+
48
+
49
+ @config.command()
50
+ def show():
51
+ """Show current configuration"""
52
+ try:
53
+ config_manager = ConfigManager()
54
+ credentials_path = config_manager.get_credentials_path()
55
+
56
+ if credentials_path:
57
+ click.echo("Configuration:")
58
+ click.echo(f" Credentials: {credentials_path}")
59
+ click.echo(f" Config file: {config_manager.config_file}")
60
+ else:
61
+ click.echo("No configuration found. Run 'ga-cli config init' to set up.")
62
+
63
+ except Exception as e:
64
+ click.echo(f"Error: {str(e)}", err=True)
65
+ raise click.Abort()