rdxz2-utill 0.0.1__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.

Potentially problematic release.


This version of rdxz2-utill might be problematic. Click here for more details.

@@ -0,0 +1,168 @@
1
+ Metadata-Version: 2.4
2
+ Name: rdxz2-utill
3
+ Version: 0.0.1
4
+ Summary: A small example package
5
+ Author-email: Richard Dharmawan <richard.dharmawan@gmail.com>
6
+ License: MIT License
7
+
8
+ Copyright (c) 2024 Richard Dharmawan
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+ Classifier: Programming Language :: Python :: 3
28
+ Classifier: Programming Language :: Python :: 3.12
29
+ Classifier: Programming Language :: Python :: 3 :: Only
30
+ Classifier: License :: OSI Approved :: MIT License
31
+ Classifier: Operating System :: OS Independent
32
+ Requires-Python: >=3.12
33
+ Description-Content-Type: text/markdown
34
+ License-File: LICENSE
35
+ Requires-Dist: click==8.1.8
36
+ Requires-Dist: duckdb==1.1.3
37
+ Requires-Dist: humanize==4.11.0
38
+ Requires-Dist: loguru==0.7.3
39
+ Requires-Dist: paramiko==3.5.0
40
+ Requires-Dist: pydantic-settings==2.7.1
41
+ Requires-Dist: pydantic==2.10.6
42
+ Requires-Dist: requests==2.32.3
43
+ Requires-Dist: sshtunnel==0.4.0
44
+ Provides-Extra: google-cloud
45
+ Requires-Dist: google-cloud-bigquery==3.29.0; extra == "google-cloud"
46
+ Requires-Dist: google-cloud-storage==3.0.0; extra == "google-cloud"
47
+ Provides-Extra: postgresql
48
+ Requires-Dist: psycopg==3.2.2; extra == "postgresql"
49
+ Provides-Extra: pdf
50
+ Requires-Dist: PyPDF2==3.0.1; extra == "pdf"
51
+ Dynamic: license-file
52
+
53
+ # Using this library
54
+
55
+ Installation
56
+
57
+ ```sh
58
+ pip install utill
59
+ ```
60
+
61
+ Usage syntax
62
+
63
+ ```py
64
+ from utill.__MODULE__ import __OBJECT__
65
+ ```
66
+
67
+ Example
68
+
69
+ ```py
70
+ # Using the string module
71
+ from utill.my_string import generate_random_string
72
+
73
+ print(generate_random_string(16))
74
+ ```
75
+
76
+ ## Initial set up
77
+
78
+ This package contains CLI command
79
+
80
+ ```sh
81
+ utill conf init
82
+ ```
83
+
84
+ # Additional extensions
85
+
86
+ Syntax
87
+
88
+ ```sh
89
+ pip install utill[__EXTENSION_NAME__]
90
+ ```
91
+
92
+ Extension list:
93
+
94
+ - google-cloud
95
+ - postgresql
96
+ - pdf
97
+
98
+ # Per module usages
99
+
100
+ ## my_bq
101
+
102
+ Executing a query
103
+
104
+ ```py
105
+ from utill.my_bq import BQ
106
+
107
+ # Initialize BigQuery client
108
+ bq = BQ()
109
+
110
+ # Execute a query, returns iterable QueryJob
111
+ job = bq.execute_query('...')
112
+
113
+ # Convert into list for quick data conversion
114
+ results = list(job)
115
+
116
+ # Iterate the results
117
+ for row in job:
118
+ # Do anything with the row
119
+ ```
120
+
121
+ Uploading CSV file into BigQuery table
122
+
123
+ ```py
124
+ from utill.my_bq import BQ, Dtype, LoadStrategy
125
+
126
+ # Initialize BigQuery client
127
+ bq = BQ()
128
+
129
+ # Load the data
130
+ filename = '/path/to/file.csv' # Your local CSV file location
131
+ bq_table_fqn = 'project.dataset.table' # An FQN (fully qualified name) of a BigQuery table to export
132
+ columns = {
133
+ 'col1': Dtype.INT64,
134
+ 'col2': Dtype.STRING,
135
+ 'col3': Dtype.DATE,
136
+ ...
137
+ }
138
+ partition_col = 'col3' # Optional, for performance and cost optimization
139
+ cluster_cols = ['col1'] # Optional, for performance and cost optimization
140
+ load_strategy = LoadStrategy.APPEND # Optional, default to APPEND
141
+ bq.upload_csv(filename, bq_table_fqn, columns, partition_col, cluster_cols, load_strategy)
142
+ ```
143
+
144
+ Exporting query into CSV
145
+
146
+ ```py
147
+ from utill.my_bq import BQ
148
+
149
+ # Initialize BigQuery client
150
+ bq = BQ()
151
+
152
+ query = 'SELECT * FROM `project.dataset.table`' # The query to export
153
+ filename = '/path/to/file.csv' # Destination CSV file location
154
+ bq.download_csv(query, filename)
155
+ ```
156
+
157
+ Exporting table into XLSX
158
+
159
+ ```py
160
+ from utill.my_bq import BQ
161
+
162
+ # Initialize BigQuery client
163
+ bq = BQ()
164
+
165
+ bq_table_fqn = 'project.dataset.table' # An FQN (fully qualified name) of a BigQuery table to export
166
+ filename = '/path/to/file.xlsx' # Destination XLSX file location
167
+ bq.download_xlsx(src_table_fqn, dst_filename)
168
+ ```
@@ -0,0 +1,34 @@
1
+ rdxz2_utill-0.0.1.dist-info/licenses/LICENSE,sha256=PF9CUvzP8XFYopEAzrMzSCovF7RdBdscPqJCDC6KjPc,1073
2
+ utill/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
+ utill/my_bq.py,sha256=ZsjRbrbczGQ70CavXfyeAPaWT1OW5LyRFBxyusNZ0cc,14701
4
+ utill/my_const.py,sha256=88dOqn6NPQ5-hfRqdkew5POoAIyO91XXOGvN76oNsdo,251
5
+ utill/my_csv.py,sha256=76Q7IM7T_WYF5SybObN_MbkPqycirX4FnLd0DX3Kdyg,2742
6
+ utill/my_datetime.py,sha256=KEZTplLk3tgVqqC3wClXFcsF_zo40fma_rtPg4kSJHc,2125
7
+ utill/my_dict.py,sha256=jPaPfdn4WYpm0uIBPiYFinpHhx1jXpFVDJ9npmvxGZQ,391
8
+ utill/my_encryption.py,sha256=SCF7PPur39cW4RHidsRhw-9BZP-ymUH-6LZ9nAHJDsY,2105
9
+ utill/my_env.py,sha256=mREys72Ybg2p9p2s7ApOt0s_6F5-qxR8FyYEcSJ8pmU,2093
10
+ utill/my_file.py,sha256=H3QmIOwubQCUMoOuk7jwf6AnqsljWZIuM7OjelyZby4,1865
11
+ utill/my_gcs.py,sha256=VY2CXQbzBUhX-HunvAZ_y4E19eiuZ1b3TF33bUkJfp4,3953
12
+ utill/my_input.py,sha256=OyKLoutXpwISReltuL_Gw2oojv16tYWJqQpqabBOQx4,350
13
+ utill/my_json.py,sha256=WgW6mavGhfs4h1N5XbhsDnRk2dbh_ttJWdJUj4iWDN4,1473
14
+ utill/my_mb.py,sha256=3_A5kXHgnkxGbd38vK5t5MfFcj84lohjS7C2OtlSo30,14841
15
+ utill/my_pg.py,sha256=OlZQ7svTnGALUluuj-LlHa3_eEW7sIIYvpfGKX9TQ88,6640
16
+ utill/my_queue.py,sha256=hINP4_yjmboSjHgo1J3CtPm2X9SE3HfczyED3ip7nfk,1930
17
+ utill/my_string.py,sha256=pINYFR1ligTyVZYzV8P_FolCsZQwYE1jaFNTuQ3XS_8,833
18
+ utill/my_style.py,sha256=Wy6j4WL9RgGeX6cS9hhlOrufc9UC4UPTQ5UJa0ZJ3Yo,900
19
+ utill/my_tunnel.py,sha256=uCpGtiG8AcRYiaN7rLnTulsZI4iFTRM8EHxwyAAfDrE,1292
20
+ utill/my_xlsx.py,sha256=YcQRp6DC9girSS1fkUPVKsHspyQpr8JC8GymSSnRV-w,729
21
+ utill/cmd/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
+ utill/cmd/_bq.py,sha256=MQGLIv_WBUBl2tf18bfYrAszx0Koa5kdTW1c8A5HDDg,520
23
+ utill/cmd/_conf.py,sha256=hNdkApzRhPloRSe4RyxbWuLoyeqkK7Yx9g44kcvKOEM,1800
24
+ utill/cmd/_enc.py,sha256=DBy3Iwa5DTtww7lgHPRLEilrYPrWDG1vRv5PO-YzNO8,997
25
+ utill/cmd/_main.py,sha256=UJ_XTIGDO9XPIypgHhS81SJQ_8qy8JOyw98Or0Nb2x8,273
26
+ utill/cmd/_pg.py,sha256=RVxEiSifyIwMDYDM69vt6WSLdVDr1cMzY6r4T2PzNRA,492
27
+ utill/cmd/utill.py,sha256=TlHfiwOUcK1m58PrRCjX9sARiPYZUsoTk-KOTCOz1vM,3558
28
+ utill/templates/mb.json,sha256=BPnVhMG2FgcxnThYp04Vn5zSQI0G-yQv99qTPNvmSok,44
29
+ utill/templates/pg.json,sha256=49c8AoGznP-omKGEgWlIWFpj7qIjeOC5Nf5k0DxlbHE,256
30
+ rdxz2_utill-0.0.1.dist-info/METADATA,sha256=_dteJKI8L6lzpMz_FoUC7cUYwEuIwx98hjICiQb8E9Q,4487
31
+ rdxz2_utill-0.0.1.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
32
+ rdxz2_utill-0.0.1.dist-info/entry_points.txt,sha256=9n5NWz5Wi9jDvYhB_81_4icgT5xABZ-QivHD8ibcafg,47
33
+ rdxz2_utill-0.0.1.dist-info/top_level.txt,sha256=tuAYZoCsr02JYbpZj7I6fl1IIo53v3GG0uoj-_fINVk,6
34
+ rdxz2_utill-0.0.1.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (78.1.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ utill = utill.cmd.utill:main
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Richard Dharmawan
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 @@
1
+ utill
utill/__init__.py ADDED
File without changes
utill/cmd/__init__.py ADDED
File without changes
utill/cmd/_bq.py ADDED
@@ -0,0 +1,12 @@
1
+ def _upload_csv(src_filename: str, dst_table_fqn: str, cols: list[tuple[str, str]], partition_col: str = None, cluster_cols: list[str] = None, project: str = None):
2
+ from ..my_bq import BQ
3
+
4
+ bq = BQ(project)
5
+ bq.upload_csv(src_filename, dst_table_fqn, {col: dtype for col, dtype in cols}, partition_col, cluster_cols)
6
+
7
+
8
+ def _download_table(src_table_fqn: str, dst_filename: str, project: str):
9
+ from ..my_bq import BQ
10
+
11
+ bq = BQ(project)
12
+ bq.download_csv(f'SELECT * FROM {src_table_fqn}', dst_filename)
utill/cmd/_conf.py ADDED
@@ -0,0 +1,60 @@
1
+ def _init(mode: str):
2
+ from loguru import logger
3
+
4
+ from ..my_env import envs, init_pg_file, init_mb_file
5
+
6
+ match mode:
7
+ case 'google-cloud':
8
+ setattr(envs, 'GCP_PROJECT_ID', input('GCP_PROJECT_ID: '))
9
+ setattr(envs, 'GCS_BUCKET', input('GCS_BUCKET: '))
10
+ envs.write()
11
+ logger.info('Google cloud configuration initialized')
12
+ case 'postgresql':
13
+ init_pg_file()
14
+ case 'metabase':
15
+ init_mb_file()
16
+ case _:
17
+ logger.warning(f'Mode \'{mode}\' not recognized')
18
+
19
+
20
+ def _list(module: str = None):
21
+ import json
22
+ import os
23
+
24
+ from loguru import logger
25
+
26
+ from ..my_env import envs, PG_FILENAME, MB_FILENAME
27
+ from ..my_string import mask
28
+
29
+ match module:
30
+ case 'postgresql':
31
+ if not os.path.exists(PG_FILENAME):
32
+ logger.error('PostgreSQL configuraiton not exists')
33
+ return
34
+
35
+ config: dict = json.loads(open(PG_FILENAME, 'r').read())
36
+ for k, v in config.items():
37
+ print(k)
38
+ for k2, v2 in v.items():
39
+ print(f'\t{k2} = {mask(str(v2)) if k2 in ("password", ) else v2}')
40
+
41
+ case 'metabase':
42
+ if not os.path.exists(MB_FILENAME):
43
+ logger.error('Metabase configuration not exists')
44
+ return
45
+
46
+ config: dict = json.loads(open(MB_FILENAME, 'r').read())
47
+ for k, v in config.items():
48
+ print(f'{k} = {mask(str(v)) if k in ("api_key", ) else v}')
49
+ case _:
50
+ for env in envs.model_fields:
51
+ print(f'{env} = {getattr(envs, env)}')
52
+
53
+
54
+ def _set(vars: list[tuple[str, str]]):
55
+ from ..my_env import envs
56
+
57
+ for k, v in vars:
58
+ setattr(envs, k, v)
59
+
60
+ envs.write()
utill/cmd/_enc.py ADDED
@@ -0,0 +1,26 @@
1
+ def _encrypt(src: str, password: str, output: str = None, force: bool = False):
2
+ from pathlib import Path
3
+
4
+ # Get the password string from file, if exists
5
+ path_password = Path(password).expanduser()
6
+ if path_password.exists():
7
+ if not path_password.is_file():
8
+ raise ValueError(f'Password path is not a file: {password}')
9
+ else:
10
+ password = open(path_password.as_posix(), 'r').read().strip()
11
+
12
+ path_src = Path(src).expanduser()
13
+ if path_src.exists():
14
+ if path_src.is_dir():
15
+ raise ValueError(f'Source file is a directory: {src}')
16
+
17
+ # Do encryption
18
+ from ..my_encryption import encrypt_file
19
+ if output:
20
+ encrypt_file(path_src.as_posix(), password, dst_filename=output, overwrite=force)
21
+ else:
22
+ print(encrypt_file(path_src.as_posix(), password))
23
+ else:
24
+ # Do encryption
25
+ from ..my_encryption import encrypt_string
26
+ print(encrypt_string(src, password))
utill/cmd/_main.py ADDED
@@ -0,0 +1,8 @@
1
+ def _random(length: int, alphanum: bool):
2
+ from ..my_string import generate_random_string
3
+
4
+ print(generate_random_string(length, alphanum))
5
+
6
+
7
+ def _unique(strings: list[str], sort: bool = True):
8
+ [print(x) for x in (sorted(set(strings)) if sort else set(strings))]
utill/cmd/_pg.py ADDED
@@ -0,0 +1,15 @@
1
+ def _pg_to_pg(src_profile: str, src_table: str, dst_profile: str, dst_table: str, columns: str):
2
+ from ..my_pg import PG
3
+
4
+ columns = ','.join([f"{x}" for x in columns.split(',')]) if columns != '*' else None
5
+ pg_src = PG(src_profile)
6
+ pg_dst = PG(dst_profile)
7
+
8
+ pg_src.pg_to_pg(pg_dst, src_table, dst_table, columns)
9
+
10
+
11
+ def _upload_csv(profile: str, src_filename: str, dst_table: str):
12
+ from ..my_pg import PG
13
+
14
+ pg = PG(profile)
15
+ pg.upload_csv(src_filename, dst_table)
utill/cmd/utill.py ADDED
@@ -0,0 +1,90 @@
1
+ import click
2
+
3
+
4
+ @click.group()
5
+ def main(): pass
6
+
7
+
8
+ # Conf
9
+
10
+
11
+ @main.group('conf', help='Configure this library')
12
+ def main__conf(): pass
13
+ @main__conf.command('init', help='Initialize env files')
14
+ @click.argument('mode', type=click.Choice(['google-cloud', 'postgresql', 'metabase']))
15
+ def main__conf__init(**kwargs): from ._conf import _init; _init(**kwargs)
16
+ @main__conf.command('list', help='List all configs')
17
+ @click.option('-m', 'module', type=click.Choice(['postgresql', 'metabase']), help='List config for a specific modules')
18
+ def main__conf__list(**kwargs): from ._conf import _list; _list(**kwargs)
19
+ @main__conf.command('set', help='Set configuration variables')
20
+ @click.option('-e', 'vars', type=(str, str), multiple=True, required=True, help='Variables -> K V')
21
+ def main__conf__set(**kwargs): from ._conf import _set; _set(**kwargs)
22
+
23
+
24
+ # PG
25
+
26
+
27
+ @main.group('pg', help='PostgreSQL utility')
28
+ def main__pg(): pass
29
+ @main__pg.command('pg-to-pg', help='Copy table from one PG instance to another')
30
+ @click.argument('src_profile', type=str)
31
+ @click.argument('src_table', type=str)
32
+ @click.argument('dst_profile', type=str)
33
+ @click.argument('dst_table', type=str)
34
+ @click.option('-c', '--columns', type=str, default='*', help='Columns to copy')
35
+ def main__pg__pg_to_pg(**kwargs): from ._pg import _pg_to_pg; _pg_to_pg(**kwargs)
36
+ @main__pg.command('upload-csv', help='Upload CSV file into PG table')
37
+ @click.argument('profile', type=str)
38
+ @click.argument('src_filename', type=click.Path())
39
+ @click.argument('dst_table', type=str)
40
+ def main__pg__upload_csv(**kwargs): from ._pg import _upload_csv; _upload_csv(**kwargs)
41
+
42
+
43
+ # BQ
44
+
45
+
46
+ @main.group('bq', help='BigQuery utility')
47
+ def main__bq(): pass
48
+ @main__bq.command('upload-csv', help='Upload CSV file into BQ table')
49
+ @click.argument('src_filename', type=click.Path())
50
+ @click.argument('dst_table_fqn', type=str)
51
+ @click.option('-c', 'columns', type=(str, str), required=True, multiple=True, help='Columns -> Name DataType')
52
+ @click.option('--partition-col', 'partition_col', type=str, help='Partition column')
53
+ @click.option('--cluster-col', 'cluster_cols', type=str, multiple=True, help='Cluster column(s)')
54
+ @click.option('--project', type=str, help='Billing project')
55
+ def main__bq__upload_csv(**kwargs): from ._bq import _upload_csv; _upload_csv(**kwargs)
56
+ @main__bq.command('download-table', help='Download a BQ table into CSV file')
57
+ @click.argument('src_table_fqn', type=str)
58
+ @click.argument('dst_filename', type=str)
59
+ @click.option('--project', type=str, help='Billing project')
60
+ def main__bq__download_table(**kwargs): from ._bq import _download_table; _download_table(**kwargs)
61
+
62
+
63
+ # Encyrption
64
+
65
+
66
+ @main.group('enc', help='Encryption utility')
67
+ def main__enc(): pass
68
+ @main__enc.command('encrypt', help='Encrypt a string / file')
69
+ @click.argument('src', type=str)
70
+ @click.option('-p', 'password', type=str, required=True, help='The password')
71
+ def main__enc__encrypt(**kwargs): from ._enc import _encrypt; _encrypt(**kwargs)
72
+
73
+
74
+ # Other utilities
75
+
76
+
77
+ @main.command('random', help='Generate random string')
78
+ @click.option('-l', 'length', type=int, default=32, help='Length of the random string')
79
+ @click.option('-a', 'alphanum', is_flag=True, default=False, help='Use alphanumeric only (a-Z, 0-9)')
80
+ def main__random(**kwargs): from ._main import _random; _random(**kwargs)
81
+
82
+
83
+ @main.command('unique', help='Get unique values')
84
+ @click.argument('strings', nargs=-1)
85
+ @click.option('-s', 'sort', type=bool, is_flag=True, help='Sort the output')
86
+ def main__unique(**kwargs): from ._main import _unique; _unique(**kwargs)
87
+
88
+
89
+ if __name__ == '__main__':
90
+ main()