fullscript.spellbook 2.1.2__tar.gz → 2.1.3__tar.gz

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.
Files changed (42) hide show
  1. {fullscript_spellbook-2.1.2 → fullscript_spellbook-2.1.3}/.gitlab-ci.yml +3 -7
  2. {fullscript_spellbook-2.1.2 → fullscript_spellbook-2.1.3}/PKG-INFO +11 -2
  3. {fullscript_spellbook-2.1.2 → fullscript_spellbook-2.1.3}/README.md +9 -0
  4. fullscript_spellbook-2.1.3/documentation/db_magic.md +98 -0
  5. fullscript_spellbook-2.1.3/documentation/drive_magic.md +77 -0
  6. fullscript_spellbook-2.1.3/documentation/sheet_magic.md +138 -0
  7. fullscript_spellbook-2.1.3/documentation/snowflake_key_pair_setup.md +35 -0
  8. fullscript_spellbook-2.1.3/documentation/stats_magic.md +154 -0
  9. {fullscript_spellbook-2.1.2 → fullscript_spellbook-2.1.3}/fullscript.spellbook.egg-info/PKG-INFO +11 -2
  10. {fullscript_spellbook-2.1.2 → fullscript_spellbook-2.1.3}/fullscript.spellbook.egg-info/SOURCES.txt +6 -0
  11. {fullscript_spellbook-2.1.2 → fullscript_spellbook-2.1.3}/pyproject.toml +1 -1
  12. fullscript_spellbook-2.1.3/requirements.txt +252 -0
  13. {fullscript_spellbook-2.1.2 → fullscript_spellbook-2.1.3}/spellbook/db_magic.py +29 -9
  14. {fullscript_spellbook-2.1.2 → fullscript_spellbook-2.1.3}/spellbook/utils.py +31 -1
  15. {fullscript_spellbook-2.1.2 → fullscript_spellbook-2.1.3}/.DS_Store +0 -0
  16. {fullscript_spellbook-2.1.2 → fullscript_spellbook-2.1.3}/.gitignore +0 -0
  17. {fullscript_spellbook-2.1.2 → fullscript_spellbook-2.1.3}/.python-version +0 -0
  18. {fullscript_spellbook-2.1.2 → fullscript_spellbook-2.1.3}/LICENSE +0 -0
  19. {fullscript_spellbook-2.1.2 → fullscript_spellbook-2.1.3}/MANIFEST.in +0 -0
  20. {fullscript_spellbook-2.1.2 → fullscript_spellbook-2.1.3}/fullscript.spellbook.egg-info/dependency_links.txt +0 -0
  21. {fullscript_spellbook-2.1.2 → fullscript_spellbook-2.1.3}/fullscript.spellbook.egg-info/requires.txt +0 -0
  22. {fullscript_spellbook-2.1.2 → fullscript_spellbook-2.1.3}/fullscript.spellbook.egg-info/top_level.txt +0 -0
  23. {fullscript_spellbook-2.1.2 → fullscript_spellbook-2.1.3}/setup.cfg +0 -0
  24. {fullscript_spellbook-2.1.2 → fullscript_spellbook-2.1.3}/spellbook/.DS_Store +0 -0
  25. {fullscript_spellbook-2.1.2 → fullscript_spellbook-2.1.3}/spellbook/__init__.py +0 -0
  26. {fullscript_spellbook-2.1.2 → fullscript_spellbook-2.1.3}/spellbook/drive_magic.py +0 -0
  27. {fullscript_spellbook-2.1.2 → fullscript_spellbook-2.1.3}/spellbook/sheet_magic.py +0 -0
  28. {fullscript_spellbook-2.1.2 → fullscript_spellbook-2.1.3}/spellbook/stats_magic.py +0 -0
  29. {fullscript_spellbook-2.1.2 → fullscript_spellbook-2.1.3}/spellbook/wizards/__init__.py +0 -0
  30. {fullscript_spellbook-2.1.2 → fullscript_spellbook-2.1.3}/spellbook/wizards/wizard1.txt +0 -0
  31. {fullscript_spellbook-2.1.2 → fullscript_spellbook-2.1.3}/spellbook/wizards/wizard10.txt +0 -0
  32. {fullscript_spellbook-2.1.2 → fullscript_spellbook-2.1.3}/spellbook/wizards/wizard11.txt +0 -0
  33. {fullscript_spellbook-2.1.2 → fullscript_spellbook-2.1.3}/spellbook/wizards/wizard12.txt +0 -0
  34. {fullscript_spellbook-2.1.2 → fullscript_spellbook-2.1.3}/spellbook/wizards/wizard2.txt +0 -0
  35. {fullscript_spellbook-2.1.2 → fullscript_spellbook-2.1.3}/spellbook/wizards/wizard3.txt +0 -0
  36. {fullscript_spellbook-2.1.2 → fullscript_spellbook-2.1.3}/spellbook/wizards/wizard4.txt +0 -0
  37. {fullscript_spellbook-2.1.2 → fullscript_spellbook-2.1.3}/spellbook/wizards/wizard5.txt +0 -0
  38. {fullscript_spellbook-2.1.2 → fullscript_spellbook-2.1.3}/spellbook/wizards/wizard6.txt +0 -0
  39. {fullscript_spellbook-2.1.2 → fullscript_spellbook-2.1.3}/spellbook/wizards/wizard7.txt +0 -0
  40. {fullscript_spellbook-2.1.2 → fullscript_spellbook-2.1.3}/spellbook/wizards/wizard8.txt +0 -0
  41. {fullscript_spellbook-2.1.2 → fullscript_spellbook-2.1.3}/spellbook/wizards/wizard9.txt +0 -0
  42. {fullscript_spellbook-2.1.2 → fullscript_spellbook-2.1.3}/spellbook_config.yaml +0 -0
@@ -1,15 +1,11 @@
1
1
  include:
2
- - component: $CI_SERVER_FQDN/devops/ci-cd/build-python/template@0.0.2
3
- - component: $CI_SERVER_FQDN/devops/ci-cd/publish-python-package/template@initial-impl
2
+ - component: $CI_SERVER_FQDN/devops/ci-cd/build-python/template@0a65e20ffcfb1ffd12dd922860666f93bf322556
3
+ - component: $CI_SERVER_FQDN/devops/ci-cd/publish-python-package/template@0.1.1
4
4
  inputs:
5
- public_python_package_image_tag: 87fa387a0fe221b8a630f7c0e78fbbaf6c23e9a1
6
5
  artifacts_job_name: build_spellbook
7
- - component: $CI_SERVER_FQDN/devops/ci-cd/publish-python-package/template@initial-impl
6
+ - component: $CI_SERVER_FQDN/devops/ci-cd/publish-python-package/template@0.1.1
8
7
  inputs:
9
- public_python_package_image_tag: 87fa387a0fe221b8a630f7c0e78fbbaf6c23e9a1
10
8
  upload_target: pypi
11
- pypi_rules_config:
12
- - when: always
13
9
  artifacts_job_name: build_spellbook
14
10
  - project: devops/ci-cd/workflow-skip-duplicate-pipelines
15
11
  file: template.yaml
@@ -1,10 +1,10 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: fullscript.spellbook
3
- Version: 2.1.2
3
+ Version: 2.1.3
4
4
  Summary: A simple package to make your life easier.
5
5
  Author-email: Thomas Edwards <thomas.edwards@fullscript.com>, Gui Chagas <gui.chagas@fullscript.com>, David King <david.king@fullscript.com>, Meinhard Mare <meinhard.mare@fullscript.com>, Karsten Melchers <karsten.melchers@fullscript.com>
6
6
  License: MIT
7
- Project-URL: homepage, https://git.fullscript.io/data/spellbook
7
+ Project-URL: homepage, https://github.com/Fullscript/spellbook
8
8
  Requires-Python: <3.12,>=3.10
9
9
  Description-Content-Type: text/markdown
10
10
  License-File: LICENSE
@@ -136,6 +136,7 @@ Spellbook is built to simplify your workflows and supercharge your data explorat
136
136
  - [Features](#features)
137
137
  - [Installation](#installation)
138
138
  - [Configuration](#configuration)
139
+ - [Documentation](#documentation)
139
140
  - [Contributing](#contributing)
140
141
  - [License](#license)
141
142
 
@@ -190,6 +191,14 @@ google_accounts:
190
191
  service_account: "your_base64_encoded_service_account_credentials_here" # Add your Base64-encoded service account JSON here
191
192
  ```
192
193
 
194
+ ## Documentation
195
+ For detailed documentation on how to use Spellbook, please refer to the [Documentation](documentation) folder in the repository.
196
+ **Functionality:**
197
+ - [Database Magic](documentation/db_magic.md)
198
+ - [Drive Magic](documentation/drive_magic.md)
199
+ - [Sheet Magic](documentation/sheet_magic.md)
200
+ - [Stat Magic](documentation/stat_magic.md)
201
+
193
202
  ## Contributing
194
203
 
195
204
  We welcome contributions! Please follow these steps:
@@ -17,6 +17,7 @@ Spellbook is built to simplify your workflows and supercharge your data explorat
17
17
  - [Features](#features)
18
18
  - [Installation](#installation)
19
19
  - [Configuration](#configuration)
20
+ - [Documentation](#documentation)
20
21
  - [Contributing](#contributing)
21
22
  - [License](#license)
22
23
 
@@ -71,6 +72,14 @@ google_accounts:
71
72
  service_account: "your_base64_encoded_service_account_credentials_here" # Add your Base64-encoded service account JSON here
72
73
  ```
73
74
 
75
+ ## Documentation
76
+ For detailed documentation on how to use Spellbook, please refer to the [Documentation](documentation) folder in the repository.
77
+ **Functionality:**
78
+ - [Database Magic](documentation/db_magic.md)
79
+ - [Drive Magic](documentation/drive_magic.md)
80
+ - [Sheet Magic](documentation/sheet_magic.md)
81
+ - [Stat Magic](documentation/stat_magic.md)
82
+
74
83
  ## Contributing
75
84
 
76
85
  We welcome contributions! Please follow these steps:
@@ -0,0 +1,98 @@
1
+ A magical wrapper to perform work with databases.
2
+
3
+
4
+ ##### Key Features
5
+ * **Unified Interface**: Abstracts the complexities of connecting and interacting with Snowflake and MySQL databases.
6
+ * **Seamless Data Handling**: Fetch, manipulate, and write data with Pandas DataFrames.
7
+ * **Flexibility**: Supports secure credentials management via configuration files and environment variables.
8
+
9
+ ```python
10
+ from spellbook import db_magic
11
+ ```
12
+
13
+ ### Methods Overview
14
+
15
+
16
+ ##### get_data(db_name, query)
17
+
18
+ Fetches data from a specified database and returns it as a Pandas DataFrame. This method abstracts the connection and retrieval process for both Snowflake and MySQL databases, allowing you to focus on the SQL query.
19
+
20
+ Parameters:
21
+ * `db_name` (str): The database name defined in the configuration file.
22
+ * `query` (str): The SQL query to execute.
23
+
24
+ Returns:
25
+ * A Pandas DataFrame containing the result of the query.
26
+
27
+ ```python
28
+ # Fetch data from Snowflake
29
+ # Option 1:
30
+ query = "SELECT * FROM my_table LIMIT 10;"
31
+ df = db_magic.get_data("snowflake_db", query)
32
+
33
+ # Option 2:
34
+ df = db_magic.get_data("snowflake_db", 'my_sql_query.sql')
35
+
36
+ # Option 3:
37
+ df = db_magic.get_data("snowflake_db", 'user/project/my_query.sql')
38
+
39
+
40
+ # Fetch data from MySQL
41
+ query = "SELECT * FROM users WHERE is_active = 1;"
42
+ df = db_magic.get_data("mysql_db", query)
43
+
44
+ # Display results
45
+ print(df.head())
46
+ ```
47
+
48
+ ##### write_data(db_name, schema, df, table_name, overwrite=True, chunk_size=1000, **kwargs)
49
+
50
+ Writes a Pandas DataFrame to a table in Snowflake. It supports creating tables automatically and overwriting existing tables.
51
+
52
+ Parameters:
53
+ * `db_name` (str): The database name defined in the configuration file.
54
+ * `schema` (str): The schema where the table is located.
55
+ * `df` (Pandas DataFrame): The data to write.
56
+ * `table_name` (str): The name of the destination table.
57
+ * `overwrite` (bool): Whether to overwrite the table if it exists (default: True).
58
+ * `chunk_size` (int): Number of rows to write per chunk (default: 1000).
59
+
60
+ ```python
61
+ import pandas as pd
62
+
63
+ # Create a sample DataFrame
64
+ data = {"id": [1, 2, 3], "name": ["Alice", "Bob", "Charlie"]}
65
+ df = pd.DataFrame(data)
66
+
67
+ # Write data to Snowflake
68
+ db_magic.write_data("snowflake_db", "my_schema", df, "my_table", overwrite=True)
69
+ ```
70
+
71
+ ##### run_sql(db_name, query)
72
+
73
+ Executes a SQL query directly on a specified database without returning results. Ideal for operations like creating tables, updating rows, or running stored procedures.
74
+
75
+ Parameters:
76
+ * `db_name` (str): The database name defined in the configuration file.
77
+ * `query` (str): The SQL query to execute.
78
+
79
+ Returns:
80
+ * `None`
81
+
82
+ ```python
83
+
84
+ # Execute SQL in Snowflake
85
+ create_table_query = """
86
+ CREATE TABLE my_table (
87
+ id INT,
88
+ name STRING
89
+ );
90
+ """
91
+ db_magic.run_sql("snowflake_db", create_table_query)
92
+
93
+ # Execute SQL in MySQL
94
+ update_query = "UPDATE users SET is_active = 1 WHERE last_login > '2024-01-01';"
95
+ db_magic.run_sql("mysql_db", update_query)
96
+
97
+ print("SQL executed successfully!")
98
+ ```
@@ -0,0 +1,77 @@
1
+ ## Different Authentication Methods
2
+
3
+ This package can use 2 different authentication methods:
4
+
5
+ - Service Account: The same method we currently use in `sheet_magic`
6
+ - OAuth: Method where the user has to allow the script to use their credentials
7
+
8
+ Both methods have their pros and cons:
9
+
10
+ - Service Account:
11
+ - It's the easiest to use as one probably already has the setup ready.
12
+ - It can only access folders shared with the service account email (similarly as it can only access sheets we share with it)
13
+ - Because we're not managers of the data team's shared folders, we cannot share those folders with this account. This is a road block for now.
14
+ - OAuth:
15
+ - The user has to grant access constantly (every time the scope changes or the authentication token expires)
16
+ - This method can access any folder shared with you as the script will be using your identity (this includes shared folders where you're not the owner)
17
+
18
+ ## OAuth Setup
19
+
20
+ Step-by-step:
21
+
22
+ 1 - Access this link and follow the [OAuth client ID credentials](https://developers.google.com/workspace/guides/create-credentials#oauth-client-id) process
23
+
24
+ 2 - Probably you'll be asked to setup the OAuth Consent Screen. At the second page of this part ("Scopes"), be sure to add all Google Drive API scopes.
25
+
26
+ 3 - In the "Client ID for Web Application" page, add `http://localhost:3000/` as an "Authorized redirect URIs"
27
+
28
+ 4 - From the same page, download the client secret as a JSON file.
29
+
30
+ 5 - Now the process is similar to what is done for `'GS_SERVICE_ACCOUNT'`. Copy the contents of the JSON file.
31
+
32
+ 6 - Go to the terminal and use `pbpaste | base64 | pbcopy` to paste, encode, and copy the contents again.
33
+
34
+ 7 - Store the encoded contents as an envvar called `'GS_CLIENT_SECRETS'`
35
+
36
+ ## Initiate
37
+
38
+ ```python
39
+ from spellbook import drive_magic as dm
40
+ ```
41
+
42
+ ## Usage examples
43
+
44
+ ##### Getting service
45
+ ```python
46
+ scopes = ['https://www.googleapis.com/auth/drive.metadata.readonly',
47
+ 'https://www.googleapis.com/auth/drive.file',
48
+ 'https://www.googleapis.com/auth/drive.readonly',
49
+ 'https://www.googleapis.com/auth/drive',
50
+ 'https://www.googleapis.com/auth/drive.file',
51
+ 'https://www.googleapis.com/auth/drive.appdata']
52
+
53
+ # service = dm.get_service(scopes=scopes, auth_type='service_account')
54
+ service = dm.get_service(scopes=scopes, auth_type='oauth')
55
+ del scopes
56
+ ```
57
+
58
+ ##### Listing contents of a folder
59
+ ```python
60
+ folder_url = 'https://drive.google.com/drive/u/0/folders/1zgsK7EkEcYsvmiQ0InT04Bg5yBdG6b-D'
61
+ df_files = dm.list_folder_contents(service, folder_url)
62
+ ```
63
+
64
+ ##### Uploading a file
65
+ ```python
66
+ dm.upload_file(service, folder_url, 'samples/01_sample_pdf.pdf')
67
+ ```
68
+
69
+ ##### Downloading a file
70
+ ```python
71
+ temp_file_id = '1JAYgOUztUNYrx5qJispiEM-B2oXdLQzO'
72
+ dm.download_file(service, temp_file_id, '02_sample_picture_v2.jpeg')
73
+ ```
74
+ ##### Deleting / thrashing a file
75
+ ```python
76
+ dm.delete_file(service, temp_file_id)
77
+ ```
@@ -0,0 +1,138 @@
1
+ ### Key Features
2
+
3
+ 1. **Sheet Management**:
4
+ * Create, delete, or clear sheets with ease.
5
+ * Access sheets by name or index.
6
+ 2. **Data Handling**:
7
+ * Read and write Pandas DataFrames.
8
+ * Target specific ranges or clear entire sheets.
9
+ 3. **Flexibility**:
10
+ * Automatically create sheets if they don’t exist.
11
+ * Handle trailing empty cells in row counting.
12
+ 4. **Seamless Google Sheets Integration**:
13
+ * Authenticate with Google Service Account credentials.
14
+ * Perform operations with minimal setup.
15
+
16
+ # Setup
17
+
18
+ ## Create a Google Service account
19
+ To use this feature, you must create a Google service account. Instructions on how to do this are available on the Google [IAM documentation](https://cloud.google.com/iam/docs/service-accounts-create#iam-service-accounts-create-console)
20
+
21
+ ## Saving Your `GS_SERVICE_ACCOUNT` Environment Variable
22
+ How to save your 'GS_SERVICE_ACCOUNT' env-var to use this code
23
+
24
+ To use Google Service Account functionality with this code, you need to save your Google Service Account JSON as an encrypted string in an environment variable. Follow these steps:
25
+ 1.Convert the JSON File Content to a Base64-Encoded String
26
+ * Copy the content of your service account JSON file to your clipboard.
27
+ * Open your terminal and run the following command to encode it:
28
+ ```bash
29
+ pbpaste | base64 | pbcopy
30
+ ```
31
+ * This command encrypts the JSON content using Base64 and places the encoded string back into your clipboard.
32
+
33
+ 2.Save the Encoded String as an Environment Variable
34
+ * Add the encoded string as an environment variable.
35
+ * Use a name like GS_SERVICE_ACCOUNT for consistency.
36
+
37
+ For example, add the following line to your environment file (e.g., .env):
38
+ ```bash
39
+ GS_SERVICE_ACCOUNT=your_base64_encoded_string
40
+ ```
41
+ 3.Why Base64 Encoding?
42
+ * Base64 ensures the JSON content is securely represented as a single string, making it compatible with environment variables.
43
+
44
+ Once set up, your code will securely retrieve the service account details directly from the environment variable.
45
+
46
+ ## Initialize
47
+
48
+ To begin using sheet_magic, initialize the handler with the URL of the Google Sheet you want to work with.
49
+
50
+ ```python
51
+ from spellbook import sheet_magic
52
+
53
+ # Replace with your actual Google Sheet URL
54
+ sheet_url = 'https://docs.google.com/spreadsheets/d/your_sheet_id/edit'
55
+ working_sheet = sheet_magic.GSheetsHandler(gsheet_url=sheet_url, service_account_name='my_google_service_account')
56
+ ```
57
+
58
+ <div style="border-left: 6px solid #f39c12; padding: 10px; background-color: #fcf8e3;">
59
+
60
+ ⚠️ **Important Notice:** In order to use sheet_magic, you need to add the Google service account to the sheet you are working on
61
+
62
+ </div>
63
+
64
+ ### Usage Examples
65
+
66
+ 1. Create a New Sheet
67
+
68
+ Add a new sheet to your Google Sheets document.
69
+ ```python
70
+ # Create a new sheet named 'test_sheet'
71
+ working_sheet.create_sheet(worksheet='test_sheet')
72
+ ```
73
+
74
+ 2. Delete an Existing Sheet
75
+
76
+ Delete a sheet by its name or index.
77
+ ```python
78
+ # Delete a sheet by its name
79
+ working_sheet.delete_sheet(worksheet='test_sheet')
80
+
81
+ # Delete a sheet by its index
82
+ working_sheet.delete_sheet(worksheet=1)
83
+ ```
84
+
85
+ 3. Write Data to a Sheet
86
+
87
+ Write a Pandas DataFrame to a specified sheet. You can specify a starting position, and the sheet will be resized or extended as needed.
88
+
89
+ ```python
90
+ import pandas as pd
91
+
92
+ # Sample DataFrame
93
+ data = {'Name': ['Alice', 'Bob'], 'Age': [25, 30]}
94
+ df_test = pd.DataFrame(data)
95
+
96
+ # Write DataFrame to a sheet by name
97
+ working_sheet.write_to_sheet(input_dataframe=df_test, worksheet='data_sheet')
98
+
99
+ # Write DataFrame to a sheet by index
100
+ working_sheet.write_to_sheet(input_dataframe=df_test, worksheet=2)
101
+ ```
102
+
103
+ 4. Count Rows in a Sheet
104
+
105
+ Count the number of rows in a specified column, with an option to include trailing empty cells.
106
+ ```python
107
+ # Count rows in column 1 of the sheet 'data_sheet'
108
+ row_count = working_sheet.count_rows(worksheet='data_sheet')
109
+
110
+ # Count rows, including empty trailing cells
111
+ row_count_with_empty = working_sheet.count_rows(worksheet='data_sheet', include_tailing_empty=True)
112
+
113
+ print(f"Rows in column 1: {row_count}")
114
+ ```
115
+
116
+ 5. Clear a Range
117
+
118
+ Clear the contents of a specific range or the entire sheet.
119
+ ```python
120
+ # Clear the entire sheet
121
+ working_sheet.clear_range(worksheet='data_sheet')
122
+
123
+ # Clear a specific range (e.g., B2:D4)
124
+ working_sheet.clear_range(worksheet='data_sheet', clear_range_str='B2:D4')
125
+ ```
126
+
127
+ 6. Read Data from a Sheet
128
+
129
+ Read data from a sheet into a Pandas DataFrame. Optionally, specify a target range.
130
+ ```python
131
+ # Read all data from the sheet 'data_sheet'
132
+ df_read = working_sheet.read_sheet(worksheet='data_sheet')
133
+
134
+ # Read a specific range (e.g., B1:C4) from the sheet
135
+ df_read_partial = working_sheet.read_sheet(worksheet='data_sheet', target_range_str='B1:C4')
136
+
137
+ print(df_read.head())
138
+ ```
@@ -0,0 +1,35 @@
1
+ A step-by-step guide to establishing a connection to Snowflake using Snowpark for Python with key-pair authentication.
2
+
3
+ ### 1. Generate Key Pair:
4
+ * **Private Key**: Create an unencrypted private key using OpenSSL:
5
+ ```bash
6
+ openssl genrsa 2048 | openssl pkcs8 -topk8 -inform PEM -out rsa_key.p8 -nocrypt
7
+ ```
8
+ > Note: If you want a passphrase for additional security, remove the `-nocrypt` flag.
9
+
10
+ * **Public Key**: Derive the public key from the private key:
11
+ ```bash
12
+ openssl rsa -in rsa_key.p8 -pubout -out rsa_key.pub
13
+ ```
14
+
15
+ ### 2. Assign Public Key to Snowflake User (will require admin access):
16
+ * In Snowflake, associate the generated public key with your user account:
17
+ ```sql
18
+ ALTER USER <username> SET rsa_public_key='<contents_of_rsa_key.pub>';
19
+ ```
20
+
21
+ * Replace `<username>` with your actual Snowflake username and `<contents_of_rsa_key.pub>` with the content of your public key file.
22
+
23
+
24
+ ### 3. Base64 Encode the Private Key:
25
+ Since private keys are typically multi-line strings, encoding them in Base64 ensures they can be stored as single-line environment variables.
26
+ ```bash
27
+ openssl base64 -in rsa_key.p8 -out rsa_key_base64.txt
28
+ ```
29
+ This command reads your rsa_key.p8 file and outputs a Base64-encoded version to rsa_key_base64.txt.
30
+
31
+ ### 4. Set the Environment Variable:
32
+ Once you have the Base64-encoded key, set it as an environment variable.
33
+ ```bash
34
+ export PRIVATE_KEY_BASE64='your_base64_encoded_key_here'
35
+ ```
@@ -0,0 +1,154 @@
1
+ The stats_magic module provides a suite of statistical tests and tools for analyzing datasets. It simplifies the process of conducting normality tests, variance analysis, ANOVA, and various other statistical methods. The module supports multi-group comparisons as well as two-group analyses, making it ideal for exploratory data analysis and hypothesis testing.
2
+
3
+ ### Key Features
4
+
5
+ 1. **Comprehensive Statistical Tests:**
6
+ * Covers normality, variance, mean, and median comparisons.
7
+ * Includes both parametric and non-parametric methods.
8
+ 2. **Multi-Group Analysis:**
9
+ * Tools like ANOVA, Tukey’s, and Dunn’s tests for multi-level datasets.
10
+ 3. **Two-Group Analysis:**
11
+ * Student’s t-test, Welch’s t-test, and Mann-Whitney U test for pairwise comparisons.
12
+ 4. **Detailed Outputs:**
13
+ * Returns results as Pandas DataFrames for easy analysis.
14
+ * Supports verbose mode for detailed explanations of results.
15
+
16
+ # Setup
17
+
18
+ ## Import the Module
19
+ To begin using stats_magic, import it into your Python environment.
20
+ ```python
21
+ from spellbook import stats_magic
22
+ ```
23
+
24
+ ### Multi-Level Tests
25
+
26
+ These tests are designed for datasets with more than two groups, allowing comparisons across multiple categories.
27
+
28
+ 1. **Shapiro-Wilk Normality Test**
29
+
30
+ Tests if the data in each group follows a normal distribution.
31
+ ```python
32
+ shapiro_df = stats_magic.shapiro_wilk_normality_test(
33
+ df=df_penguins,
34
+ group_column='species',
35
+ values_column='flipper_length_mm',
36
+ alpha_level=0.05,
37
+ verbose=True
38
+ )
39
+ ```
40
+ Key Parameters:
41
+ * `group_column`: The column defining groups (e.g., species).
42
+ * `values_column`: The column containing numeric values to test.
43
+ * `alpha_level`: Significance level (default 0.05).
44
+ * `verbose`: Print detailed output (default False).
45
+
46
+ 2. **Levene’s Variance Test**
47
+
48
+ Tests if the variances across groups are equal.
49
+ ```python
50
+ levene_df = stats_magic.levene_variance_test(
51
+ df=df_penguins,
52
+ group_column='species',
53
+ values_column='flipper_length_mm',
54
+ alpha_level=0.05,
55
+ center_method='mean',
56
+ verbose=True
57
+ )
58
+ ```
59
+ Key Parameters:
60
+ * `center_method`: Method to calculate central tendency (mean, median, or trimmed).
61
+
62
+ 3. **ANOVA**
63
+
64
+ Performs an analysis of variance to test if means across groups are significantly different.
65
+ ```python
66
+ anova_df = stats_magic.anova(
67
+ df=df_penguins,
68
+ group_column='species',
69
+ values_column='flipper_length_mm',
70
+ alpha_level=0.05,
71
+ verbose=True
72
+ )
73
+ ```
74
+
75
+ 4. **Tukey’s Test**
76
+
77
+ Conducts pairwise comparisons between group means.
78
+ ```python
79
+ tukey_test_df = stats_magic.tukey_test(
80
+ df=df_penguins,
81
+ group_column='species',
82
+ values_column='flipper_length_mm',
83
+ alpha_level=0.05,
84
+ verbose=True
85
+ )
86
+ ```
87
+
88
+ 5. **Mood’s Median Test**
89
+
90
+ Tests if the medians across groups are significantly different.
91
+ ```python
92
+ mood_median_test_df = stats_magic.mood_median_test(
93
+ df=df_penguins,
94
+ group_column='species',
95
+ values_column='flipper_length_mm',
96
+ alpha_level=0.05,
97
+ verbose=True
98
+ )
99
+ ```
100
+
101
+ 6. **Dunn’s Test**
102
+
103
+ Performs pairwise comparisons between groups, adjusting p-values to account for multiple tests.
104
+ ```python
105
+ dunn_test_df = stats_magic.dunn_test(
106
+ df=df_penguins,
107
+ group_column='species',
108
+ values_column='flipper_length_mm',
109
+ alpha_level=0.05,
110
+ verbose=True
111
+ )
112
+ ```
113
+
114
+ ### Two-Level Tests
115
+
116
+ These tests are designed for datasets with exactly two groups, allowing direct comparisons between them.
117
+
118
+ **Setup: Create a Two-Group Dataset**
119
+ Filter the dataset to include only two groups.
120
+
121
+ ```python
122
+ two_group_df = df_penguins[
123
+ (df_penguins['species'] == 'Adelie') | (df_penguins['species'] == 'Gentoo')
124
+ ]
125
+ ```
126
+
127
+ 1. **Student’s t-test / Welch’s t-test**
128
+
129
+ Tests if the means of two groups are significantly different.
130
+ ```python
131
+ t_welch_test_df = stats_magic.t_welch_test(
132
+ df=two_group_df,
133
+ group_column='species',
134
+ values_column='flipper_length_mm',
135
+ equal_variances=True,
136
+ alpha_level=0.05,
137
+ verbose=True
138
+ )
139
+ ```
140
+ Key Parameters:
141
+ * `equal_variances`: Set to False to use Welch’s t-test for unequal variances.
142
+
143
+ 2. **Mann-Whitney U Test**
144
+
145
+ Tests if the distributions of two groups are significantly different.
146
+ ```python
147
+ mann_whitney_u_test_df = stats_magic.mann_whitney_u_test(
148
+ df=two_group_df,
149
+ group_column='species',
150
+ values_column='flipper_length_mm',
151
+ alpha_level=0.05,
152
+ verbose=True
153
+ )
154
+ ```
@@ -1,10 +1,10 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: fullscript.spellbook
3
- Version: 2.1.2
3
+ Version: 2.1.3
4
4
  Summary: A simple package to make your life easier.
5
5
  Author-email: Thomas Edwards <thomas.edwards@fullscript.com>, Gui Chagas <gui.chagas@fullscript.com>, David King <david.king@fullscript.com>, Meinhard Mare <meinhard.mare@fullscript.com>, Karsten Melchers <karsten.melchers@fullscript.com>
6
6
  License: MIT
7
- Project-URL: homepage, https://git.fullscript.io/data/spellbook
7
+ Project-URL: homepage, https://github.com/Fullscript/spellbook
8
8
  Requires-Python: <3.12,>=3.10
9
9
  Description-Content-Type: text/markdown
10
10
  License-File: LICENSE
@@ -136,6 +136,7 @@ Spellbook is built to simplify your workflows and supercharge your data explorat
136
136
  - [Features](#features)
137
137
  - [Installation](#installation)
138
138
  - [Configuration](#configuration)
139
+ - [Documentation](#documentation)
139
140
  - [Contributing](#contributing)
140
141
  - [License](#license)
141
142
 
@@ -190,6 +191,14 @@ google_accounts:
190
191
  service_account: "your_base64_encoded_service_account_credentials_here" # Add your Base64-encoded service account JSON here
191
192
  ```
192
193
 
194
+ ## Documentation
195
+ For detailed documentation on how to use Spellbook, please refer to the [Documentation](documentation) folder in the repository.
196
+ **Functionality:**
197
+ - [Database Magic](documentation/db_magic.md)
198
+ - [Drive Magic](documentation/drive_magic.md)
199
+ - [Sheet Magic](documentation/sheet_magic.md)
200
+ - [Stat Magic](documentation/stat_magic.md)
201
+
193
202
  ## Contributing
194
203
 
195
204
  We welcome contributions! Please follow these steps:
@@ -6,7 +6,13 @@ LICENSE
6
6
  MANIFEST.in
7
7
  README.md
8
8
  pyproject.toml
9
+ requirements.txt
9
10
  spellbook_config.yaml
11
+ documentation/db_magic.md
12
+ documentation/drive_magic.md
13
+ documentation/sheet_magic.md
14
+ documentation/snowflake_key_pair_setup.md
15
+ documentation/stats_magic.md
10
16
  fullscript.spellbook.egg-info/PKG-INFO
11
17
  fullscript.spellbook.egg-info/SOURCES.txt
12
18
  fullscript.spellbook.egg-info/dependency_links.txt
@@ -16,7 +16,7 @@ authors = [
16
16
  {name = "Meinhard Mare", email = "meinhard.mare@fullscript.com"},
17
17
  {name = "Karsten Melchers", email = "karsten.melchers@fullscript.com"}
18
18
  ]
19
- urls = {homepage = "https://git.fullscript.io/data/spellbook"}
19
+ urls = {homepage = "https://github.com/Fullscript/spellbook"}
20
20
  dependencies = [
21
21
  "appnope==0.1.4",
22
22
  "asn1crypto==1.5.1",
@@ -0,0 +1,252 @@
1
+ #
2
+ # This file is autogenerated by pip-compile with Python 3.11
3
+ # by the following command:
4
+ #
5
+ # pip-compile
6
+ #
7
+ appnope==0.1.4
8
+ # via
9
+ # fullscript.spellbook (pyproject.toml)
10
+ # ipykernel
11
+ asn1crypto==1.5.1
12
+ # via
13
+ # fullscript.spellbook (pyproject.toml)
14
+ # snowflake-connector-python
15
+ asttokens==2.4.1
16
+ # via
17
+ # fullscript.spellbook (pyproject.toml)
18
+ # stack-data
19
+ certifi==2024.8.30
20
+ # via
21
+ # fullscript.spellbook (pyproject.toml)
22
+ # requests
23
+ # snowflake-connector-python
24
+ cffi==1.17.1
25
+ # via
26
+ # cryptography
27
+ # fullscript.spellbook (pyproject.toml)
28
+ # snowflake-connector-python
29
+ charset-normalizer==3.4.0
30
+ # via
31
+ # fullscript.spellbook (pyproject.toml)
32
+ # requests
33
+ # snowflake-connector-python
34
+ cloudpickle==2.2.1
35
+ # via
36
+ # fullscript.spellbook (pyproject.toml)
37
+ # snowflake-snowpark-python
38
+ comm==0.2.2
39
+ # via
40
+ # fullscript.spellbook (pyproject.toml)
41
+ # ipykernel
42
+ cryptography==43.0.3
43
+ # via
44
+ # fullscript.spellbook (pyproject.toml)
45
+ # pyopenssl
46
+ # snowflake-connector-python
47
+ debugpy==1.8.8
48
+ # via
49
+ # fullscript.spellbook (pyproject.toml)
50
+ # ipykernel
51
+ decorator==5.1.1
52
+ # via
53
+ # fullscript.spellbook (pyproject.toml)
54
+ # ipython
55
+ executing==2.1.0
56
+ # via
57
+ # fullscript.spellbook (pyproject.toml)
58
+ # stack-data
59
+ filelock==3.16.1
60
+ # via
61
+ # fullscript.spellbook (pyproject.toml)
62
+ # snowflake-connector-python
63
+ idna==3.10
64
+ # via
65
+ # fullscript.spellbook (pyproject.toml)
66
+ # requests
67
+ # snowflake-connector-python
68
+ ipykernel==6.29.5
69
+ # via fullscript.spellbook (pyproject.toml)
70
+ ipython==8.29.0
71
+ # via
72
+ # fullscript.spellbook (pyproject.toml)
73
+ # ipykernel
74
+ jedi==0.19.2
75
+ # via
76
+ # fullscript.spellbook (pyproject.toml)
77
+ # ipython
78
+ jupyter-client==8.6.3
79
+ # via
80
+ # fullscript.spellbook (pyproject.toml)
81
+ # ipykernel
82
+ jupyter-core==5.7.2
83
+ # via
84
+ # fullscript.spellbook (pyproject.toml)
85
+ # ipykernel
86
+ # jupyter-client
87
+ matplotlib-inline==0.1.7
88
+ # via
89
+ # fullscript.spellbook (pyproject.toml)
90
+ # ipykernel
91
+ # ipython
92
+ nest-asyncio==1.6.0
93
+ # via
94
+ # fullscript.spellbook (pyproject.toml)
95
+ # ipykernel
96
+ numpy==2.1.3
97
+ # via
98
+ # fullscript.spellbook (pyproject.toml)
99
+ # pandas
100
+ packaging==24.2
101
+ # via
102
+ # fullscript.spellbook (pyproject.toml)
103
+ # ipykernel
104
+ # snowflake-connector-python
105
+ pandas==2.2.3
106
+ # via fullscript.spellbook (pyproject.toml)
107
+ parso==0.8.4
108
+ # via
109
+ # fullscript.spellbook (pyproject.toml)
110
+ # jedi
111
+ pexpect==4.9.0
112
+ # via
113
+ # fullscript.spellbook (pyproject.toml)
114
+ # ipython
115
+ platformdirs==4.3.6
116
+ # via
117
+ # fullscript.spellbook (pyproject.toml)
118
+ # jupyter-core
119
+ # snowflake-connector-python
120
+ prompt-toolkit==3.0.48
121
+ # via
122
+ # fullscript.spellbook (pyproject.toml)
123
+ # ipython
124
+ protobuf==5.28.3
125
+ # via
126
+ # fullscript.spellbook (pyproject.toml)
127
+ # snowflake-snowpark-python
128
+ psutil==6.1.0
129
+ # via
130
+ # fullscript.spellbook (pyproject.toml)
131
+ # ipykernel
132
+ ptyprocess==0.7.0
133
+ # via
134
+ # fullscript.spellbook (pyproject.toml)
135
+ # pexpect
136
+ pure-eval==0.2.3
137
+ # via
138
+ # fullscript.spellbook (pyproject.toml)
139
+ # stack-data
140
+ pyarrow==18.0.0
141
+ # via fullscript.spellbook (pyproject.toml)
142
+ pycparser==2.22
143
+ # via
144
+ # cffi
145
+ # fullscript.spellbook (pyproject.toml)
146
+ pygments==2.18.0
147
+ # via
148
+ # fullscript.spellbook (pyproject.toml)
149
+ # ipython
150
+ pyjwt==2.9.0
151
+ # via
152
+ # fullscript.spellbook (pyproject.toml)
153
+ # snowflake-connector-python
154
+ pyopenssl==24.2.1
155
+ # via
156
+ # fullscript.spellbook (pyproject.toml)
157
+ # snowflake-connector-python
158
+ python-dateutil==2.9.0.post0
159
+ # via
160
+ # fullscript.spellbook (pyproject.toml)
161
+ # jupyter-client
162
+ # pandas
163
+ pytz==2024.2
164
+ # via
165
+ # fullscript.spellbook (pyproject.toml)
166
+ # pandas
167
+ # snowflake-connector-python
168
+ pyyaml==6.0.2
169
+ # via
170
+ # fullscript.spellbook (pyproject.toml)
171
+ # snowflake-snowpark-python
172
+ pyzmq==26.2.0
173
+ # via
174
+ # fullscript.spellbook (pyproject.toml)
175
+ # ipykernel
176
+ # jupyter-client
177
+ requests==2.32.3
178
+ # via
179
+ # fullscript.spellbook (pyproject.toml)
180
+ # snowflake-connector-python
181
+ six==1.16.0
182
+ # via
183
+ # asttokens
184
+ # fullscript.spellbook (pyproject.toml)
185
+ # python-dateutil
186
+ snowflake-connector-python==3.12.3
187
+ # via
188
+ # fullscript.spellbook (pyproject.toml)
189
+ # snowflake-snowpark-python
190
+ # snowflake-sqlalchemy
191
+ snowflake-snowpark-python==1.25.0
192
+ # via fullscript.spellbook (pyproject.toml)
193
+ snowflake-sqlalchemy==1.7.0
194
+ # via fullscript.spellbook (pyproject.toml)
195
+ sortedcontainers==2.4.0
196
+ # via
197
+ # fullscript.spellbook (pyproject.toml)
198
+ # snowflake-connector-python
199
+ sqlalchemy==2.0.36
200
+ # via
201
+ # fullscript.spellbook (pyproject.toml)
202
+ # snowflake-sqlalchemy
203
+ stack-data==0.6.3
204
+ # via
205
+ # fullscript.spellbook (pyproject.toml)
206
+ # ipython
207
+ tomlkit==0.13.2
208
+ # via
209
+ # fullscript.spellbook (pyproject.toml)
210
+ # snowflake-connector-python
211
+ tornado==6.4.1
212
+ # via
213
+ # fullscript.spellbook (pyproject.toml)
214
+ # ipykernel
215
+ # jupyter-client
216
+ traitlets==5.14.3
217
+ # via
218
+ # comm
219
+ # fullscript.spellbook (pyproject.toml)
220
+ # ipykernel
221
+ # ipython
222
+ # jupyter-client
223
+ # jupyter-core
224
+ # matplotlib-inline
225
+ typing-extensions==4.12.2
226
+ # via
227
+ # fullscript.spellbook (pyproject.toml)
228
+ # ipython
229
+ # snowflake-connector-python
230
+ # snowflake-snowpark-python
231
+ # sqlalchemy
232
+ tzdata==2024.2
233
+ # via
234
+ # fullscript.spellbook (pyproject.toml)
235
+ # pandas
236
+ tzlocal==5.2
237
+ # via
238
+ # fullscript.spellbook (pyproject.toml)
239
+ # snowflake-snowpark-python
240
+ urllib3==2.2.3
241
+ # via
242
+ # fullscript.spellbook (pyproject.toml)
243
+ # requests
244
+ wcwidth==0.2.13
245
+ # via
246
+ # fullscript.spellbook (pyproject.toml)
247
+ # prompt-toolkit
248
+ wheel==0.45.1
249
+ # via snowflake-snowpark-python
250
+
251
+ # The following packages are considered to be unsafe in a requirements file:
252
+ # setuptools
@@ -5,7 +5,7 @@ import pandas as pd
5
5
  from cryptography.hazmat.backends import default_backend
6
6
  from cryptography.hazmat.primitives import serialization
7
7
  import base64
8
- from spellbook.utils import load_and_parse_config, get_wizard
8
+ from spellbook.utils import load_and_parse_config, get_wizard, load_sql_file
9
9
 
10
10
 
11
11
  # Needs a very specific pip install
@@ -108,7 +108,9 @@ def read_mysql(db_name, query):
108
108
  if con is None:
109
109
  return pd.DataFrame()
110
110
  try:
111
- df = pd.read_sql(query, con)
111
+ df = pd.read_sql(
112
+ load_sql_file(query),
113
+ con)
112
114
  df.columns = df.columns.str.lower()
113
115
  return df
114
116
  except Exception as e:
@@ -129,7 +131,9 @@ def execute_mysql(query, dbname):
129
131
  # Establish a connection and run the query
130
132
  con = mysql_connect(dbname)
131
133
  mycursor = con.connection.cursor()
132
- mycursor.execute(query)
134
+ mycursor.execute(
135
+ load_sql_file(query)
136
+ )
133
137
  except Exception as e:
134
138
  # Handle errors
135
139
  print('An error was encountered executing mysql, ', e)
@@ -151,7 +155,9 @@ def execute_snowflake(db_name, query):
151
155
  return
152
156
 
153
157
  # Execute query
154
- session.sql(query).collect()
158
+ session.sql(
159
+ load_sql_file(query)
160
+ ).collect()
155
161
  print("Query executed successfully.")
156
162
 
157
163
  except Exception as e:
@@ -177,7 +183,9 @@ def read_snowflake(db_name, query):
177
183
  return pd.DataFrame()
178
184
 
179
185
  # Execute query and fetch data
180
- df = session.sql(query).to_pandas()
186
+ df = (session.sql(
187
+ load_sql_file(query)
188
+ ).to_pandas())
181
189
  df.columns = df.columns.str.lower() # Normalize column names
182
190
  return df
183
191
 
@@ -238,10 +246,16 @@ def get_data(db_name, query, **kwargs):
238
246
  # Call the correct database read function
239
247
  if db_config['type'] == 'snowflake':
240
248
  print(get_wizard())
241
- df = read_snowflake(db_name, query)
249
+ df = read_snowflake(
250
+ db_name,
251
+ load_sql_file(query)
252
+ )
242
253
  elif db_config['type'] == 'mysql':
243
254
  print(get_wizard())
244
- df = read_mysql(db_name, query)
255
+ df = read_mysql(
256
+ db_name,
257
+ load_sql_file(query)
258
+ )
245
259
  else:
246
260
  print('Valid database types are, snowflake and mysql')
247
261
  return df
@@ -258,9 +272,15 @@ def run_sql(db_name, query, **kwargs):
258
272
  # Call the correct database run function
259
273
  if db_config['type'] == 'snowflake':
260
274
  print(get_wizard())
261
- execute_snowflake(db_name, query)
275
+ execute_snowflake(
276
+ db_name,
277
+ load_sql_file(query)
278
+ )
262
279
  elif db_config['type'] == 'mysql':
263
280
  print(get_wizard())
264
- execute_mysql(db_name, query)
281
+ execute_mysql(
282
+ db_name,
283
+ load_sql_file(query)
284
+ )
265
285
  else:
266
286
  print('Valid database types are, snowflake and mysql')
@@ -90,4 +90,34 @@ def get_wizard():
90
90
  print('An error was encountered loading the wizard:', e)
91
91
  wizard = None
92
92
 
93
- return wizard
93
+ return wizard
94
+
95
+
96
+ def load_sql_file(query_file):
97
+ """
98
+ Load the contents of a SQL file or return the SQL string directly.
99
+
100
+ :param query_file: str, path to the SQL file or raw SQL query string
101
+ :return: str, SQL query
102
+ :raises ValueError: if the file path is invalid or not a .sql file
103
+ """
104
+ if isinstance(query_file, str) and os.path.exists(query_file):
105
+ # Ensure it's a .sql file
106
+ if not query_file.lower().endswith('.sql'):
107
+ raise ValueError(f"The file {query_file} is not a valid SQL file.")
108
+
109
+ # Use a context manager to read the file safely
110
+ try:
111
+ with open(query_file, 'r', encoding='utf-8') as file:
112
+ query_str = file.read()
113
+ except Exception as e:
114
+ raise ValueError(f"Error reading the file {query_file}: {e}")
115
+ else:
116
+ # Assume it's a raw SQL query string
117
+ query_str = query_file
118
+
119
+ # Check for empty SQL content
120
+ if not query_str.strip():
121
+ raise ValueError("The SQL query is empty. Please provide valid SQL content.")
122
+
123
+ return query_str