data-manipulation 0.37__tar.gz → 0.40__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.
- {data_manipulation-0.37 → data_manipulation-0.40}/PKG-INFO +6 -5
- {data_manipulation-0.37 → data_manipulation-0.40}/README.md +1 -10
- {data_manipulation-0.37 → data_manipulation-0.40}/data_manipulation/__init__.py +1 -0
- {data_manipulation-0.37 → data_manipulation-0.40}/data_manipulation/_version.py +3 -3
- {data_manipulation-0.37 → data_manipulation-0.40}/data_manipulation/base.py +5 -1
- {data_manipulation-0.37 → data_manipulation-0.40}/data_manipulation/beautifulsoup_.py +5 -5
- {data_manipulation-0.37 → data_manipulation-0.40}/data_manipulation/boto3_.py +14 -5
- {data_manipulation-0.37 → data_manipulation-0.40}/data_manipulation/cryptography_.py +5 -2
- {data_manipulation-0.37 → data_manipulation-0.40}/data_manipulation/django_.py +7 -1
- {data_manipulation-0.37 → data_manipulation-0.40}/data_manipulation/kerberos_.py +6 -3
- {data_manipulation-0.37 → data_manipulation-0.40}/data_manipulation/mysql_connector_python_.py +7 -4
- {data_manipulation-0.37 → data_manipulation-0.40}/data_manipulation/openldap_.py +0 -1
- {data_manipulation-0.37 → data_manipulation-0.40}/data_manipulation/pandas_.py +6 -2
- data_manipulation-0.40/data_manipulation/prometheus_.py +0 -0
- {data_manipulation-0.37 → data_manipulation-0.40}/data_manipulation/psycopg2_.py +7 -4
- data_manipulation-0.40/data_manipulation/psycopg_.py +119 -0
- {data_manipulation-0.37 → data_manipulation-0.40}/data_manipulation/pyspark_.py +0 -3
- {data_manipulation-0.37 → data_manipulation-0.40}/data_manipulation/sqlalchemy_.py +5 -2
- {data_manipulation-0.37 → data_manipulation-0.40}/data_manipulation.egg-info/PKG-INFO +7 -6
- {data_manipulation-0.37 → data_manipulation-0.40}/data_manipulation.egg-info/SOURCES.txt +1 -0
- {data_manipulation-0.37 → data_manipulation-0.40}/data_manipulation.egg-info/requires.txt +5 -4
- {data_manipulation-0.37 → data_manipulation-0.40}/setup.py +5 -4
- data_manipulation-0.37/data_manipulation/prometheus_.py +0 -26
- {data_manipulation-0.37 → data_manipulation-0.40}/LICENSE +0 -0
- {data_manipulation-0.37 → data_manipulation-0.40}/MANIFEST.in +0 -0
- {data_manipulation-0.37 → data_manipulation-0.40}/data_manipulation/flask_.py +0 -0
- {data_manipulation-0.37 → data_manipulation-0.40}/data_manipulation/geopandas_.py +0 -0
- {data_manipulation-0.37 → data_manipulation-0.40}/data_manipulation/smtplib_.py +0 -0
- {data_manipulation-0.37 → data_manipulation-0.40}/data_manipulation.egg-info/dependency_links.txt +0 -0
- {data_manipulation-0.37 → data_manipulation-0.40}/data_manipulation.egg-info/top_level.txt +0 -0
- {data_manipulation-0.37 → data_manipulation-0.40}/setup.cfg +0 -0
- {data_manipulation-0.37 → data_manipulation-0.40}/versioneer.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: data_manipulation
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.40
|
|
4
4
|
Summary: Powerful data manipulation
|
|
5
5
|
Home-page: https://github.com/shawnngtq/data-manipulation
|
|
6
6
|
Author: Shawn Ng
|
|
@@ -20,15 +20,16 @@ Classifier: Programming Language :: Cython
|
|
|
20
20
|
Classifier: Topic :: Scientific/Engineering
|
|
21
21
|
License-File: LICENSE
|
|
22
22
|
Requires-Dist: beautifulsoup4
|
|
23
|
+
Requires-Dist: boto3
|
|
23
24
|
Requires-Dist: django
|
|
24
|
-
Requires-Dist:
|
|
25
|
-
Requires-Dist:
|
|
25
|
+
Requires-Dist: flask
|
|
26
|
+
Requires-Dist: geopandas
|
|
26
27
|
Requires-Dist: mysql-connector-python
|
|
27
28
|
Requires-Dist: pandas
|
|
29
|
+
Requires-Dist: psycopg
|
|
28
30
|
Requires-Dist: psycopg2
|
|
29
31
|
Requires-Dist: pyspark
|
|
30
|
-
Requires-Dist:
|
|
31
|
-
Requires-Dist: xlsxwriter
|
|
32
|
+
Requires-Dist: sqlalchemy
|
|
32
33
|
|
|
33
34
|
|
|
34
35
|
Data Manipulation is a Python package providing powerful utility functions.
|
|
@@ -25,16 +25,7 @@ pip install data-manipulation
|
|
|
25
25
|
|
|
26
26
|
## Dependencies
|
|
27
27
|
|
|
28
|
-
The dependencies will be installed automatically along with this package
|
|
29
|
-
|
|
30
|
-
- beautifulsoup4
|
|
31
|
-
- django
|
|
32
|
-
- numpy
|
|
33
|
-
- pandas
|
|
34
|
-
- ipython
|
|
35
|
-
- requests
|
|
36
|
-
- psycopg2
|
|
37
|
-
- pyspark
|
|
28
|
+
The dependencies will be installed automatically along with this package, in `setup.py`
|
|
38
29
|
|
|
39
30
|
## License
|
|
40
31
|
|
|
@@ -8,11 +8,11 @@ import json
|
|
|
8
8
|
|
|
9
9
|
version_json = '''
|
|
10
10
|
{
|
|
11
|
-
"date": "
|
|
11
|
+
"date": "2024-05-26T15:48:20+0800",
|
|
12
12
|
"dirty": false,
|
|
13
13
|
"error": null,
|
|
14
|
-
"full-revisionid": "
|
|
15
|
-
"version": "0.
|
|
14
|
+
"full-revisionid": "805e0463f2f7f444a10afa22b658bc31ea864fed",
|
|
15
|
+
"version": "0.40"
|
|
16
16
|
}
|
|
17
17
|
''' # END VERSION_JSON
|
|
18
18
|
|
|
@@ -1,6 +1,10 @@
|
|
|
1
|
+
import logging
|
|
1
2
|
import subprocess
|
|
2
3
|
from typing import List
|
|
3
4
|
|
|
5
|
+
logging.basicConfig(level=logging.INFO)
|
|
6
|
+
logger = logging.getLogger(__file__)
|
|
7
|
+
|
|
4
8
|
|
|
5
9
|
# DATA STRUCTURE
|
|
6
10
|
def clean_string(
|
|
@@ -336,7 +340,7 @@ def remove_path_file(path: str, keyword: str, n: int = 2) -> None:
|
|
|
336
340
|
to_delete = get_path_files(path=path, keywords=[keyword])[:-n]
|
|
337
341
|
for file in to_delete:
|
|
338
342
|
os.remove(f"{path}/{file}")
|
|
339
|
-
|
|
343
|
+
logger.info(f"{path}/{file} deleted ...")
|
|
340
344
|
|
|
341
345
|
|
|
342
346
|
def list_to_file(filepath: str, list_: list, newline: bool = True) -> None:
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import logging
|
|
1
2
|
from typing import Optional
|
|
2
3
|
|
|
3
|
-
|
|
4
|
+
logging.basicConfig(level=logging.INFO)
|
|
5
|
+
logger = logging.getLogger(__file__)
|
|
4
6
|
|
|
5
7
|
|
|
6
8
|
def preprocess(html: str) -> Optional[str]:
|
|
@@ -37,9 +39,7 @@ def preprocess(html: str) -> Optional[str]:
|
|
|
37
39
|
return html
|
|
38
40
|
|
|
39
41
|
|
|
40
|
-
def build_soup(
|
|
41
|
-
url: str, features: str = "lxml", to_preprocess: str = True
|
|
42
|
-
) -> Optional[BeautifulSoup]:
|
|
42
|
+
def build_soup(url: str, features: str = "lxml", to_preprocess: str = True):
|
|
43
43
|
"""
|
|
44
44
|
Return Beautifulsoup object from given url
|
|
45
45
|
|
|
@@ -74,7 +74,7 @@ def build_soup(
|
|
|
74
74
|
soup = BeautifulSoup(response.text, features=features)
|
|
75
75
|
return soup
|
|
76
76
|
else:
|
|
77
|
-
|
|
77
|
+
logger.error(f"response status code: {response.status_code}")
|
|
78
78
|
|
|
79
79
|
|
|
80
80
|
if __name__ == "__main__":
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
logging.basicConfig(level=logging.INFO)
|
|
4
|
+
logger = logging.getLogger(__file__)
|
|
5
|
+
|
|
6
|
+
|
|
1
7
|
def send_aws_ses_email(
|
|
2
8
|
sender: str,
|
|
3
9
|
recipient: list,
|
|
@@ -29,8 +35,12 @@ def send_aws_ses_email(
|
|
|
29
35
|
|
|
30
36
|
Returns
|
|
31
37
|
-------
|
|
32
|
-
|
|
38
|
+
dict
|
|
33
39
|
aws ses client email response or none
|
|
40
|
+
|
|
41
|
+
Reference
|
|
42
|
+
---------
|
|
43
|
+
- https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/ses/client/send_raw_email.html
|
|
34
44
|
"""
|
|
35
45
|
import os
|
|
36
46
|
from email.mime.application import MIMEApplication
|
|
@@ -58,7 +68,7 @@ def send_aws_ses_email(
|
|
|
58
68
|
)
|
|
59
69
|
msg.attach(part)
|
|
60
70
|
except FileNotFoundError:
|
|
61
|
-
|
|
71
|
+
logger.error(f"{attachment} not found")
|
|
62
72
|
return
|
|
63
73
|
|
|
64
74
|
try:
|
|
@@ -69,11 +79,10 @@ def send_aws_ses_email(
|
|
|
69
79
|
"Data": msg.as_string(),
|
|
70
80
|
},
|
|
71
81
|
)
|
|
82
|
+
return response
|
|
72
83
|
except (BotoCoreError, ClientError) as error:
|
|
73
|
-
|
|
84
|
+
logger.error(f"Error: {error}")
|
|
74
85
|
return
|
|
75
|
-
else:
|
|
76
|
-
return response
|
|
77
86
|
|
|
78
87
|
|
|
79
88
|
if __name__ == "__main__":
|
|
@@ -2,6 +2,9 @@ import logging
|
|
|
2
2
|
|
|
3
3
|
from cryptography.fernet import Fernet
|
|
4
4
|
|
|
5
|
+
logging.basicConfig(level=logging.INFO)
|
|
6
|
+
logger = logging.getLogger(__file__)
|
|
7
|
+
|
|
5
8
|
|
|
6
9
|
def generate_fernet_key(output_directory: str, output_filename: str) -> bytes:
|
|
7
10
|
"""
|
|
@@ -29,9 +32,9 @@ def generate_fernet_key(output_directory: str, output_filename: str) -> bytes:
|
|
|
29
32
|
f = open(filepath, "wb")
|
|
30
33
|
f.write(key)
|
|
31
34
|
f.close()
|
|
32
|
-
|
|
35
|
+
logger.info(f"generate_fernet_key: True")
|
|
33
36
|
except Exception as e:
|
|
34
|
-
|
|
37
|
+
logger.error(f"generate_fernet_key: False ({e})")
|
|
35
38
|
return key
|
|
36
39
|
|
|
37
40
|
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
logging.basicConfig(level=logging.INFO)
|
|
4
|
+
logger = logging.getLogger(__file__)
|
|
5
|
+
|
|
6
|
+
|
|
1
7
|
def init_django(django_dir: str, project_name: str) -> None:
|
|
2
8
|
"""
|
|
3
9
|
Setup Django in Jupyter. Reference from https://gist.github.com/codingforentrepreneurs/76e570d759f83d690bf36a8a8fa4cfbe
|
|
@@ -113,7 +119,7 @@ def django_validate_phone(phone: str, region=None) -> str | None:
|
|
|
113
119
|
try:
|
|
114
120
|
return PhoneNumber.from_string(phone, region=region).as_e164
|
|
115
121
|
except Exception as e:
|
|
116
|
-
|
|
122
|
+
logger.error(e)
|
|
117
123
|
return
|
|
118
124
|
|
|
119
125
|
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
import subprocess
|
|
3
3
|
|
|
4
|
+
logging.basicConfig(level=logging.INFO)
|
|
5
|
+
logger = logging.getLogger(__file__)
|
|
6
|
+
|
|
4
7
|
|
|
5
8
|
def keytab_valid(
|
|
6
9
|
keytab_filepath: str,
|
|
@@ -29,11 +32,11 @@ def keytab_valid(
|
|
|
29
32
|
)
|
|
30
33
|
try:
|
|
31
34
|
if output.returncode == 0:
|
|
32
|
-
|
|
35
|
+
logger.info("keytab_valid: True")
|
|
33
36
|
else:
|
|
34
|
-
|
|
37
|
+
logger.info("keytab_valid: False")
|
|
35
38
|
except Exception as e:
|
|
36
|
-
|
|
39
|
+
logger.error(f"keytab_valid: False ({e})")
|
|
37
40
|
return output
|
|
38
41
|
|
|
39
42
|
|
{data_manipulation-0.37 → data_manipulation-0.40}/data_manipulation/mysql_connector_python_.py
RENAMED
|
@@ -3,6 +3,9 @@ from typing import Optional, Union
|
|
|
3
3
|
|
|
4
4
|
import mysql.connector
|
|
5
5
|
|
|
6
|
+
logging.basicConfig(level=logging.INFO)
|
|
7
|
+
logger = logging.getLogger(__file__)
|
|
8
|
+
|
|
6
9
|
|
|
7
10
|
def create_connection(
|
|
8
11
|
host: str,
|
|
@@ -43,9 +46,9 @@ def create_connection(
|
|
|
43
46
|
password=password,
|
|
44
47
|
port=port,
|
|
45
48
|
)
|
|
46
|
-
|
|
49
|
+
logger.info("MySQL database connected ...")
|
|
47
50
|
except mysql.connector.errors.Error as e:
|
|
48
|
-
|
|
51
|
+
logger.error(f"{e}")
|
|
49
52
|
return connection
|
|
50
53
|
|
|
51
54
|
|
|
@@ -79,12 +82,12 @@ def execute_query(
|
|
|
79
82
|
cursor.execute(sql_query, data)
|
|
80
83
|
if commit:
|
|
81
84
|
connection.commit()
|
|
82
|
-
|
|
85
|
+
logger.info("MySQL committed ...")
|
|
83
86
|
id = cursor.lastrowid
|
|
84
87
|
cursor.close()
|
|
85
88
|
return id
|
|
86
89
|
except mysql.connector.errors.Error as e:
|
|
87
|
-
|
|
90
|
+
logger.error(f"{e}")
|
|
88
91
|
|
|
89
92
|
|
|
90
93
|
if __name__ == "__main__":
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import logging
|
|
1
2
|
import subprocess
|
|
2
3
|
from itertools import combinations
|
|
3
4
|
|
|
@@ -5,6 +6,9 @@ import numpy as np
|
|
|
5
6
|
import pandas as pd
|
|
6
7
|
from IPython.display import display
|
|
7
8
|
|
|
9
|
+
logging.basicConfig(level=logging.INFO)
|
|
10
|
+
logger = logging.getLogger(__file__)
|
|
11
|
+
|
|
8
12
|
|
|
9
13
|
# CONFIG
|
|
10
14
|
def config_pandas_display():
|
|
@@ -226,7 +230,7 @@ def compare_dataframes(dataframe, dataframe2):
|
|
|
226
230
|
try:
|
|
227
231
|
pd_df2 = dataframe2[dataframe2[column].notnull()]
|
|
228
232
|
except KeyError:
|
|
229
|
-
|
|
233
|
+
logger.error(f"df2 {column}: does not exist")
|
|
230
234
|
|
|
231
235
|
if pd_df2 is not None:
|
|
232
236
|
print(f"df2 non-null {column}: {len(pd_df2)}")
|
|
@@ -722,7 +726,7 @@ def print_dataframe_overview(dataframe, stats=False):
|
|
|
722
726
|
print(f"Maximum: {max(dataframe[column])}")
|
|
723
727
|
display(series_count(dataframe[column]).head(20))
|
|
724
728
|
except:
|
|
725
|
-
|
|
729
|
+
logger.error(f"Unable to get value_counts of {column} ...\n")
|
|
726
730
|
|
|
727
731
|
|
|
728
732
|
def series_count(series):
|
|
File without changes
|
|
@@ -4,6 +4,9 @@ from typing import Optional, Union
|
|
|
4
4
|
import pandas as pd
|
|
5
5
|
import psycopg2
|
|
6
6
|
|
|
7
|
+
logging.basicConfig(level=logging.INFO)
|
|
8
|
+
logger = logging.getLogger(__file__)
|
|
9
|
+
|
|
7
10
|
|
|
8
11
|
def create_connection(
|
|
9
12
|
host: str,
|
|
@@ -39,9 +42,9 @@ def create_connection(
|
|
|
39
42
|
f"host={host} port={port} dbname={dbname} user={user} password={password}"
|
|
40
43
|
)
|
|
41
44
|
connection = psycopg2.connect(connection_string)
|
|
42
|
-
|
|
45
|
+
logger.info("PostgreSQL database connected ...")
|
|
43
46
|
except psycopg2.OperationalError as e:
|
|
44
|
-
|
|
47
|
+
logger.error(f"{e}")
|
|
45
48
|
return connection
|
|
46
49
|
|
|
47
50
|
|
|
@@ -70,9 +73,9 @@ def execute_sql(
|
|
|
70
73
|
cursor.execute(sql_query, data)
|
|
71
74
|
if commit:
|
|
72
75
|
connection.commit()
|
|
73
|
-
|
|
76
|
+
logger.info("PostgreSQL committed ...")
|
|
74
77
|
except psycopg2.OperationalError as e:
|
|
75
|
-
|
|
78
|
+
logger.error(f"{e}")
|
|
76
79
|
|
|
77
80
|
|
|
78
81
|
def query_to_pandas(
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import Optional, Union
|
|
3
|
+
|
|
4
|
+
import pandas as pd
|
|
5
|
+
import psycopg
|
|
6
|
+
|
|
7
|
+
logging.basicConfig(level=logging.INFO)
|
|
8
|
+
logger = logging.getLogger(__file__)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def create_connection(
|
|
12
|
+
host: str,
|
|
13
|
+
dbname: str,
|
|
14
|
+
user: str,
|
|
15
|
+
password: str,
|
|
16
|
+
port=5432,
|
|
17
|
+
) -> psycopg.Connection:
|
|
18
|
+
"""
|
|
19
|
+
Return psycopg connection (https://www.psycopg.org/)
|
|
20
|
+
|
|
21
|
+
Parameters
|
|
22
|
+
----------
|
|
23
|
+
host : str
|
|
24
|
+
Database host address
|
|
25
|
+
dbname : str
|
|
26
|
+
Database name
|
|
27
|
+
user : str
|
|
28
|
+
User used to authenticate
|
|
29
|
+
password : str
|
|
30
|
+
Password used to authenticate
|
|
31
|
+
port : int, optional
|
|
32
|
+
Port number, by default 5432
|
|
33
|
+
|
|
34
|
+
Returns
|
|
35
|
+
-------
|
|
36
|
+
psycopg.Connection
|
|
37
|
+
Connection object
|
|
38
|
+
"""
|
|
39
|
+
connection = None
|
|
40
|
+
try:
|
|
41
|
+
connection_string = (
|
|
42
|
+
f"host={host} port={port} dbname={dbname} user={user} password={password}"
|
|
43
|
+
)
|
|
44
|
+
connection = psycopg.connect(connection_string)
|
|
45
|
+
logger.info("PostgreSQL database connected ...")
|
|
46
|
+
except psycopg.OperationalError as e:
|
|
47
|
+
logger.error(f"{e}")
|
|
48
|
+
return connection
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def execute_sql(
|
|
52
|
+
connection: psycopg.Connection,
|
|
53
|
+
sql_query: str,
|
|
54
|
+
data: Union[dict, tuple],
|
|
55
|
+
commit=True,
|
|
56
|
+
) -> None:
|
|
57
|
+
"""
|
|
58
|
+
Execute and commit PostgreSQL query
|
|
59
|
+
|
|
60
|
+
Parameters
|
|
61
|
+
----------
|
|
62
|
+
connection : psycopg.Connection
|
|
63
|
+
_description_
|
|
64
|
+
sql_query : str
|
|
65
|
+
SQL query
|
|
66
|
+
data : Union[dict, tuple]
|
|
67
|
+
Data
|
|
68
|
+
commit : bool, optional
|
|
69
|
+
Make database change persistent, by default True
|
|
70
|
+
|
|
71
|
+
Reference
|
|
72
|
+
---------
|
|
73
|
+
When you insert data key-value with dict type, error
|
|
74
|
+
ProgrammingError: cannot adapt type 'dict' using placeholder '%s' (format: AUTO)
|
|
75
|
+
"""
|
|
76
|
+
cursor = connection.cursor()
|
|
77
|
+
try:
|
|
78
|
+
cursor.execute(sql_query, data)
|
|
79
|
+
if commit:
|
|
80
|
+
connection.commit()
|
|
81
|
+
logger.info("PostgreSQL committed ...")
|
|
82
|
+
except psycopg.OperationalError as e:
|
|
83
|
+
logger.error(f"{e}")
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def query_to_pandas(
|
|
87
|
+
connection: psycopg.Connection,
|
|
88
|
+
sql_query: str,
|
|
89
|
+
) -> pd.DataFrame:
|
|
90
|
+
"""
|
|
91
|
+
Return Pandas dataframe from result of given SQL query
|
|
92
|
+
|
|
93
|
+
Parameters
|
|
94
|
+
----------
|
|
95
|
+
connection : psycopg.Connection
|
|
96
|
+
_description_
|
|
97
|
+
sql_query : str
|
|
98
|
+
SQL query
|
|
99
|
+
|
|
100
|
+
Examples
|
|
101
|
+
--------
|
|
102
|
+
>>> sql_query = '''SELECT * FROM users;'''
|
|
103
|
+
|
|
104
|
+
Returns
|
|
105
|
+
-------
|
|
106
|
+
pd.DataFrame
|
|
107
|
+
_description_
|
|
108
|
+
"""
|
|
109
|
+
|
|
110
|
+
return pd.read_sql(
|
|
111
|
+
sql=sql_query,
|
|
112
|
+
con=connection,
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
if __name__ == "__main__":
|
|
117
|
+
import doctest
|
|
118
|
+
|
|
119
|
+
doctest.testmod()
|
|
@@ -2,8 +2,6 @@ import pandas as pd
|
|
|
2
2
|
import pyspark
|
|
3
3
|
import pyspark.sql.functions as F
|
|
4
4
|
|
|
5
|
-
spark = pyspark.sql.SparkSession.builder.master("local").getOrCreate()
|
|
6
|
-
|
|
7
5
|
|
|
8
6
|
# CONFIG
|
|
9
7
|
def config_spark_local(autoset=True):
|
|
@@ -84,7 +82,6 @@ def config_spark_local(autoset=True):
|
|
|
84
82
|
print(f"spark_default_parallelism: {spark_default_parallelism}")
|
|
85
83
|
|
|
86
84
|
if autoset:
|
|
87
|
-
global spark
|
|
88
85
|
spark = (
|
|
89
86
|
pyspark.sql.SparkSession.builder.master("local")
|
|
90
87
|
.config("spark.executor.cores", str(spark_executor_cores))
|
|
@@ -2,6 +2,9 @@ import logging
|
|
|
2
2
|
|
|
3
3
|
import sqlalchemy
|
|
4
4
|
|
|
5
|
+
logging.basicConfig(level=logging.INFO)
|
|
6
|
+
logger = logging.getLogger(__file__)
|
|
7
|
+
|
|
5
8
|
|
|
6
9
|
def create_sqlalchemy_url(
|
|
7
10
|
drivername: str,
|
|
@@ -87,9 +90,9 @@ def create_sqlalchemy_engine(
|
|
|
87
90
|
engine = sqlalchemy.create_engine(url)
|
|
88
91
|
try:
|
|
89
92
|
engine.connect()
|
|
90
|
-
|
|
93
|
+
logger.info("create_sqlalchemy_engine: True")
|
|
91
94
|
except Exception as e:
|
|
92
|
-
|
|
95
|
+
logger.error(f"create_sqlalchemy_engine: False ({e})")
|
|
93
96
|
return engine
|
|
94
97
|
|
|
95
98
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
|
-
Name:
|
|
3
|
-
Version: 0.
|
|
2
|
+
Name: data_manipulation
|
|
3
|
+
Version: 0.40
|
|
4
4
|
Summary: Powerful data manipulation
|
|
5
5
|
Home-page: https://github.com/shawnngtq/data-manipulation
|
|
6
6
|
Author: Shawn Ng
|
|
@@ -20,15 +20,16 @@ Classifier: Programming Language :: Cython
|
|
|
20
20
|
Classifier: Topic :: Scientific/Engineering
|
|
21
21
|
License-File: LICENSE
|
|
22
22
|
Requires-Dist: beautifulsoup4
|
|
23
|
+
Requires-Dist: boto3
|
|
23
24
|
Requires-Dist: django
|
|
24
|
-
Requires-Dist:
|
|
25
|
-
Requires-Dist:
|
|
25
|
+
Requires-Dist: flask
|
|
26
|
+
Requires-Dist: geopandas
|
|
26
27
|
Requires-Dist: mysql-connector-python
|
|
27
28
|
Requires-Dist: pandas
|
|
29
|
+
Requires-Dist: psycopg
|
|
28
30
|
Requires-Dist: psycopg2
|
|
29
31
|
Requires-Dist: pyspark
|
|
30
|
-
Requires-Dist:
|
|
31
|
-
Requires-Dist: xlsxwriter
|
|
32
|
+
Requires-Dist: sqlalchemy
|
|
32
33
|
|
|
33
34
|
|
|
34
35
|
Data Manipulation is a Python package providing powerful utility functions.
|
|
@@ -19,6 +19,7 @@ data_manipulation/openldap_.py
|
|
|
19
19
|
data_manipulation/pandas_.py
|
|
20
20
|
data_manipulation/prometheus_.py
|
|
21
21
|
data_manipulation/psycopg2_.py
|
|
22
|
+
data_manipulation/psycopg_.py
|
|
22
23
|
data_manipulation/pyspark_.py
|
|
23
24
|
data_manipulation/smtplib_.py
|
|
24
25
|
data_manipulation/sqlalchemy_.py
|
|
@@ -23,15 +23,16 @@ CLASSIFIERS = [
|
|
|
23
23
|
|
|
24
24
|
DEPENDENCIES = [
|
|
25
25
|
"beautifulsoup4",
|
|
26
|
+
"boto3",
|
|
26
27
|
"django",
|
|
27
|
-
"
|
|
28
|
-
"
|
|
28
|
+
"flask",
|
|
29
|
+
"geopandas",
|
|
29
30
|
"mysql-connector-python",
|
|
30
31
|
"pandas",
|
|
32
|
+
"psycopg",
|
|
31
33
|
"psycopg2",
|
|
32
34
|
"pyspark",
|
|
33
|
-
"
|
|
34
|
-
"xlsxwriter",
|
|
35
|
+
"sqlalchemy",
|
|
35
36
|
]
|
|
36
37
|
|
|
37
38
|
setup(
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
# import logging
|
|
2
|
-
# import subprocess
|
|
3
|
-
|
|
4
|
-
# def ldapsearch(
|
|
5
|
-
# search_base: str,
|
|
6
|
-
# ldap_uri: str,
|
|
7
|
-
# bind_dn: str,
|
|
8
|
-
# password: str,
|
|
9
|
-
# filter: str,
|
|
10
|
-
# ) -> subprocess.CompletedProcess:
|
|
11
|
-
# cmd = (
|
|
12
|
-
# "ldapsearch -b {search_base} -H {ldap_uri} -D {bind_dn} -w {password} {filter}"
|
|
13
|
-
# )
|
|
14
|
-
# output = subprocess.run(
|
|
15
|
-
# cmd,
|
|
16
|
-
# capture_output=True,
|
|
17
|
-
# shell=True,
|
|
18
|
-
# text=True,
|
|
19
|
-
# )
|
|
20
|
-
# return output
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
# if __name__ == "__main__":
|
|
24
|
-
# import doctest
|
|
25
|
-
|
|
26
|
-
# doctest.testmod()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{data_manipulation-0.37 → data_manipulation-0.40}/data_manipulation.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|