data-manipulation 0.36__tar.gz → 0.37__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.36 → data_manipulation-0.37}/LICENSE +4 -4
- {data_manipulation-0.36 → data_manipulation-0.37}/PKG-INFO +11 -1
- {data_manipulation-0.36 → data_manipulation-0.37}/data_manipulation/__init__.py +3 -0
- {data_manipulation-0.36 → data_manipulation-0.37}/data_manipulation/_version.py +3 -3
- {data_manipulation-0.36 → data_manipulation-0.37}/data_manipulation/base.py +23 -0
- data_manipulation-0.37/data_manipulation/boto3_.py +82 -0
- data_manipulation-0.37/data_manipulation/django_.py +123 -0
- {data_manipulation-0.36 → data_manipulation-0.37}/data_manipulation/pandas_.py +49 -5
- data_manipulation-0.37/data_manipulation/prometheus_.py +26 -0
- {data_manipulation-0.36 → data_manipulation-0.37}/data_manipulation/sqlalchemy_.py +4 -4
- {data_manipulation-0.36 → data_manipulation-0.37}/data_manipulation.egg-info/PKG-INFO +11 -1
- {data_manipulation-0.36 → data_manipulation-0.37}/data_manipulation.egg-info/SOURCES.txt +1 -0
- data_manipulation-0.36/data_manipulation/django_.py +0 -48
- data_manipulation-0.36/data_manipulation/prometheus_.py +0 -27
- {data_manipulation-0.36 → data_manipulation-0.37}/MANIFEST.in +0 -0
- {data_manipulation-0.36 → data_manipulation-0.37}/README.md +0 -0
- {data_manipulation-0.36 → data_manipulation-0.37}/data_manipulation/beautifulsoup_.py +0 -0
- {data_manipulation-0.36 → data_manipulation-0.37}/data_manipulation/cryptography_.py +0 -0
- {data_manipulation-0.36 → data_manipulation-0.37}/data_manipulation/flask_.py +0 -0
- {data_manipulation-0.36 → data_manipulation-0.37}/data_manipulation/geopandas_.py +0 -0
- {data_manipulation-0.36 → data_manipulation-0.37}/data_manipulation/kerberos_.py +0 -0
- {data_manipulation-0.36 → data_manipulation-0.37}/data_manipulation/mysql_connector_python_.py +0 -0
- {data_manipulation-0.36 → data_manipulation-0.37}/data_manipulation/openldap_.py +0 -0
- {data_manipulation-0.36 → data_manipulation-0.37}/data_manipulation/psycopg2_.py +0 -0
- {data_manipulation-0.36 → data_manipulation-0.37}/data_manipulation/pyspark_.py +0 -0
- {data_manipulation-0.36 → data_manipulation-0.37}/data_manipulation/smtplib_.py +0 -0
- {data_manipulation-0.36 → data_manipulation-0.37}/data_manipulation.egg-info/dependency_links.txt +0 -0
- {data_manipulation-0.36 → data_manipulation-0.37}/data_manipulation.egg-info/requires.txt +0 -0
- {data_manipulation-0.36 → data_manipulation-0.37}/data_manipulation.egg-info/top_level.txt +0 -0
- {data_manipulation-0.36 → data_manipulation-0.37}/setup.cfg +0 -0
- {data_manipulation-0.36 → data_manipulation-0.37}/setup.py +0 -0
- {data_manipulation-0.36 → data_manipulation-0.37}/versioneer.py +0 -0
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
BSD 3-Clause License
|
|
2
2
|
|
|
3
|
-
Copyright (c) 2020, Ng
|
|
3
|
+
Copyright (c) 2020, Shawn Ng (shawnngtq)
|
|
4
4
|
All rights reserved.
|
|
5
5
|
|
|
6
6
|
Redistribution and use in source and binary forms, with or without modification,
|
|
7
7
|
are permitted provided that the following conditions are met:
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
- Redistributions of source code must retain the above copyright notice, this
|
|
10
10
|
list of conditions and the following disclaimer.
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
- Redistributions in binary form must reproduce the above copyright notice, this
|
|
13
13
|
list of conditions and the following disclaimer in the documentation and/or
|
|
14
14
|
other materials provided with the distribution.
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
- Neither the name of data_manipulation nor the names of its
|
|
17
17
|
contributors may be used to endorse or promote products derived from this
|
|
18
18
|
software without specific prior written permission.
|
|
19
19
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: data_manipulation
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.37
|
|
4
4
|
Summary: Powerful data manipulation
|
|
5
5
|
Home-page: https://github.com/shawnngtq/data-manipulation
|
|
6
6
|
Author: Shawn Ng
|
|
@@ -19,6 +19,16 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
19
19
|
Classifier: Programming Language :: Cython
|
|
20
20
|
Classifier: Topic :: Scientific/Engineering
|
|
21
21
|
License-File: LICENSE
|
|
22
|
+
Requires-Dist: beautifulsoup4
|
|
23
|
+
Requires-Dist: django
|
|
24
|
+
Requires-Dist: lxml
|
|
25
|
+
Requires-Dist: matplotlib
|
|
26
|
+
Requires-Dist: mysql-connector-python
|
|
27
|
+
Requires-Dist: pandas
|
|
28
|
+
Requires-Dist: psycopg2
|
|
29
|
+
Requires-Dist: pyspark
|
|
30
|
+
Requires-Dist: tqdm
|
|
31
|
+
Requires-Dist: xlsxwriter
|
|
22
32
|
|
|
23
33
|
|
|
24
34
|
Data Manipulation is a Python package providing powerful utility functions.
|
|
@@ -8,11 +8,11 @@ import json
|
|
|
8
8
|
|
|
9
9
|
version_json = '''
|
|
10
10
|
{
|
|
11
|
-
"date": "2023-
|
|
11
|
+
"date": "2023-12-30T10:16:23+0800",
|
|
12
12
|
"dirty": false,
|
|
13
13
|
"error": null,
|
|
14
|
-
"full-revisionid": "
|
|
15
|
-
"version": "0.
|
|
14
|
+
"full-revisionid": "6f824ae7f46ddf5b2f60190bacfc2e4118d7f80f",
|
|
15
|
+
"version": "0.37"
|
|
16
16
|
}
|
|
17
17
|
''' # END VERSION_JSON
|
|
18
18
|
|
|
@@ -109,6 +109,7 @@ def get_country_name_variation() -> dict:
|
|
|
109
109
|
"SLAVONIC": "SK",
|
|
110
110
|
"SLOVAK REPUBLIC": "SK",
|
|
111
111
|
"TURKEY": "TR",
|
|
112
|
+
"TURKIYE": "TR",
|
|
112
113
|
"UNITED STATES": "US",
|
|
113
114
|
}
|
|
114
115
|
return variations
|
|
@@ -363,6 +364,28 @@ def list_to_file(filepath: str, list_: list, newline: bool = True) -> None:
|
|
|
363
364
|
f.close()
|
|
364
365
|
|
|
365
366
|
|
|
367
|
+
# URLLIB
|
|
368
|
+
def create_encode_url(url: str, query_params: dict = {}) -> str:
|
|
369
|
+
"""
|
|
370
|
+
Create encoded url
|
|
371
|
+
|
|
372
|
+
Parameters
|
|
373
|
+
----------
|
|
374
|
+
url : str
|
|
375
|
+
base url
|
|
376
|
+
query_params : dict, optional
|
|
377
|
+
dictionary of url query parameters, by default {}
|
|
378
|
+
|
|
379
|
+
Returns
|
|
380
|
+
-------
|
|
381
|
+
str
|
|
382
|
+
encoded url
|
|
383
|
+
"""
|
|
384
|
+
from urllib.parse import urlencode
|
|
385
|
+
|
|
386
|
+
return f"{url}{urlencode(query_params)}"
|
|
387
|
+
|
|
388
|
+
|
|
366
389
|
# SYSTEM
|
|
367
390
|
def parse_ps_aux(ps_aux_commands: str) -> List[list]:
|
|
368
391
|
"""
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
def send_aws_ses_email(
|
|
2
|
+
sender: str,
|
|
3
|
+
recipient: list,
|
|
4
|
+
subject: str,
|
|
5
|
+
body_text: str,
|
|
6
|
+
body_type: str,
|
|
7
|
+
ses_client,
|
|
8
|
+
attachment: str = None,
|
|
9
|
+
):
|
|
10
|
+
"""
|
|
11
|
+
Send AWS SES email
|
|
12
|
+
|
|
13
|
+
Parameters
|
|
14
|
+
----------
|
|
15
|
+
sender : str
|
|
16
|
+
sender email
|
|
17
|
+
recipient : list
|
|
18
|
+
list of recipient emails
|
|
19
|
+
subject : str
|
|
20
|
+
email subject
|
|
21
|
+
body_text : str
|
|
22
|
+
email body
|
|
23
|
+
body_type : str
|
|
24
|
+
email body type
|
|
25
|
+
ses_client : _type_
|
|
26
|
+
aws ses client
|
|
27
|
+
attachment : str, optional
|
|
28
|
+
attachment path, by default None
|
|
29
|
+
|
|
30
|
+
Returns
|
|
31
|
+
-------
|
|
32
|
+
str
|
|
33
|
+
aws ses client email response or none
|
|
34
|
+
"""
|
|
35
|
+
import os
|
|
36
|
+
from email.mime.application import MIMEApplication
|
|
37
|
+
from email.mime.multipart import MIMEMultipart
|
|
38
|
+
from email.mime.text import MIMEText
|
|
39
|
+
|
|
40
|
+
from botocore.exceptions import BotoCoreError, ClientError
|
|
41
|
+
|
|
42
|
+
msg = MIMEMultipart()
|
|
43
|
+
msg["Subject"] = subject
|
|
44
|
+
msg["From"] = sender
|
|
45
|
+
msg["To"] = ", ".join(recipient)
|
|
46
|
+
|
|
47
|
+
part = MIMEText(body_text, body_type)
|
|
48
|
+
msg.attach(part)
|
|
49
|
+
|
|
50
|
+
if attachment:
|
|
51
|
+
try:
|
|
52
|
+
with open(attachment, "rb") as f:
|
|
53
|
+
part = MIMEApplication(f.read())
|
|
54
|
+
part.add_header(
|
|
55
|
+
"Content-Disposition",
|
|
56
|
+
"attachment",
|
|
57
|
+
filename=os.path.basename(attachment),
|
|
58
|
+
)
|
|
59
|
+
msg.attach(part)
|
|
60
|
+
except FileNotFoundError:
|
|
61
|
+
print(f"{attachment} not found")
|
|
62
|
+
return
|
|
63
|
+
|
|
64
|
+
try:
|
|
65
|
+
response = ses_client.send_raw_email(
|
|
66
|
+
Source=sender,
|
|
67
|
+
Destinations=recipient,
|
|
68
|
+
RawMessage={
|
|
69
|
+
"Data": msg.as_string(),
|
|
70
|
+
},
|
|
71
|
+
)
|
|
72
|
+
except (BotoCoreError, ClientError) as error:
|
|
73
|
+
print(f"Error: {error}")
|
|
74
|
+
return
|
|
75
|
+
else:
|
|
76
|
+
return response
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
if __name__ == "__main__":
|
|
80
|
+
import doctest
|
|
81
|
+
|
|
82
|
+
doctest.testmod()
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
def init_django(django_dir: str, project_name: str) -> None:
|
|
2
|
+
"""
|
|
3
|
+
Setup Django in Jupyter. Reference from https://gist.github.com/codingforentrepreneurs/76e570d759f83d690bf36a8a8fa4cfbe
|
|
4
|
+
|
|
5
|
+
Parameters
|
|
6
|
+
----------
|
|
7
|
+
django_dir : str
|
|
8
|
+
Django project location
|
|
9
|
+
project_name : str
|
|
10
|
+
Django project name
|
|
11
|
+
"""
|
|
12
|
+
import os
|
|
13
|
+
import sys
|
|
14
|
+
|
|
15
|
+
import django
|
|
16
|
+
|
|
17
|
+
# django_dir = os.getenv("PWD")
|
|
18
|
+
# os.chdir(django_dir)
|
|
19
|
+
|
|
20
|
+
project_name = project_name or os.environ.get("DJANGO_PROJECT") or None
|
|
21
|
+
if not project_name:
|
|
22
|
+
raise Exception(
|
|
23
|
+
"""Set an environment variable: `DJANGO_PROJECT=your_project_name` or call `init_django(your_project_name)`"""
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
sys.path.insert(0, django_dir)
|
|
27
|
+
os.environ.setdefault("DJANGO_SETTINGS_MODULE", f"{project_name}.settings")
|
|
28
|
+
os.environ["DJANGO_ALLOW_ASYNC_UNSAFE"] = "true"
|
|
29
|
+
django.setup()
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def get_django_countries_dict():
|
|
33
|
+
"""
|
|
34
|
+
Get django_countries package's dictionaries
|
|
35
|
+
code_name - "SG": "SINGAPORE"
|
|
36
|
+
name_code - "SINGAPORE": "SG"
|
|
37
|
+
"""
|
|
38
|
+
from django_countries import countries
|
|
39
|
+
|
|
40
|
+
code_name = {k: v.upper() for k, v in dict(countries).items()}
|
|
41
|
+
name_code = {v: k for k, v in code_name.items()}
|
|
42
|
+
return code_name, name_code
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def django_validate_email(email: str) -> str | None:
|
|
46
|
+
"""
|
|
47
|
+
Django validate email
|
|
48
|
+
|
|
49
|
+
Parameters
|
|
50
|
+
----------
|
|
51
|
+
email : str
|
|
52
|
+
email to check
|
|
53
|
+
|
|
54
|
+
Returns
|
|
55
|
+
-------
|
|
56
|
+
str | None
|
|
57
|
+
email or none
|
|
58
|
+
"""
|
|
59
|
+
from django.core.validators import validate_email
|
|
60
|
+
|
|
61
|
+
try:
|
|
62
|
+
validate_email(email)
|
|
63
|
+
return email
|
|
64
|
+
except Exception as e:
|
|
65
|
+
return
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def django_validate_url(url: str) -> str | None:
|
|
69
|
+
"""
|
|
70
|
+
Django validate url
|
|
71
|
+
|
|
72
|
+
Parameters
|
|
73
|
+
----------
|
|
74
|
+
url : str
|
|
75
|
+
url to validate
|
|
76
|
+
|
|
77
|
+
Returns
|
|
78
|
+
-------
|
|
79
|
+
str | None
|
|
80
|
+
url or None
|
|
81
|
+
"""
|
|
82
|
+
from django.core.validators import URLValidator
|
|
83
|
+
|
|
84
|
+
validator = URLValidator()
|
|
85
|
+
try:
|
|
86
|
+
validator(url)
|
|
87
|
+
return url
|
|
88
|
+
except Exception as e:
|
|
89
|
+
return
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def django_validate_phone(phone: str, region=None) -> str | None:
|
|
93
|
+
"""
|
|
94
|
+
Django validate phone
|
|
95
|
+
|
|
96
|
+
Parameters
|
|
97
|
+
----------
|
|
98
|
+
phone : str
|
|
99
|
+
phone to check
|
|
100
|
+
region : _type_, optional
|
|
101
|
+
phone region, by default None
|
|
102
|
+
|
|
103
|
+
Returns
|
|
104
|
+
-------
|
|
105
|
+
str | None
|
|
106
|
+
parsed phone or none
|
|
107
|
+
"""
|
|
108
|
+
from phonenumber_field.phonenumber import PhoneNumber
|
|
109
|
+
|
|
110
|
+
try:
|
|
111
|
+
return PhoneNumber.from_string(phone).as_e164
|
|
112
|
+
except:
|
|
113
|
+
try:
|
|
114
|
+
return PhoneNumber.from_string(phone, region=region).as_e164
|
|
115
|
+
except Exception as e:
|
|
116
|
+
print(e)
|
|
117
|
+
return
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
if __name__ == "__main__":
|
|
121
|
+
import doctest
|
|
122
|
+
|
|
123
|
+
doctest.testmod()
|
|
@@ -364,15 +364,56 @@ def split_left_merged_dataframe(dataframe, dataframe2, columns):
|
|
|
364
364
|
return df_both, df_left
|
|
365
365
|
|
|
366
366
|
|
|
367
|
+
## AGGREGATE
|
|
368
|
+
def aggregate_set_without_none(column: pd.Series, nested_set: bool = False) -> set:
|
|
369
|
+
"""
|
|
370
|
+
Use in pandas groupby agg, create a set without none based on input column
|
|
371
|
+
|
|
372
|
+
Parameters
|
|
373
|
+
----------
|
|
374
|
+
column : pd.Series
|
|
375
|
+
columns to set
|
|
376
|
+
nested_set : bool, optional
|
|
377
|
+
nested set within set, by default False
|
|
378
|
+
|
|
379
|
+
Returns
|
|
380
|
+
-------
|
|
381
|
+
set
|
|
382
|
+
set without None
|
|
383
|
+
"""
|
|
384
|
+
if nested_set:
|
|
385
|
+
output = set()
|
|
386
|
+
for value in column:
|
|
387
|
+
if isinstance(value, set):
|
|
388
|
+
output.update(value)
|
|
389
|
+
# todo, what if there is valid list / dict / tuple / complex object?
|
|
390
|
+
elif value:
|
|
391
|
+
output.add(value)
|
|
392
|
+
return output
|
|
393
|
+
else:
|
|
394
|
+
return {value for value in column if value is not None}
|
|
395
|
+
|
|
396
|
+
|
|
367
397
|
# DATA STRUCTURE
|
|
368
|
-
def clean_none(
|
|
398
|
+
def clean_none(
|
|
399
|
+
dataframe: pd.DataFrame,
|
|
400
|
+
nan_to_none: bool = True,
|
|
401
|
+
clean_variation: bool = True,
|
|
402
|
+
none_variations: list = [],
|
|
403
|
+
) -> pd.DataFrame:
|
|
369
404
|
"""
|
|
370
405
|
Return a dataframe from given dataframe with standardized None. Deprecated as of pandas 1.3.0.
|
|
371
406
|
|
|
372
407
|
Parameters
|
|
373
408
|
----------
|
|
374
|
-
dataframe :
|
|
375
|
-
|
|
409
|
+
dataframe : pd.DataFrame
|
|
410
|
+
_description_
|
|
411
|
+
nan_to_none : bool, optional
|
|
412
|
+
replace np.NaN with None, by default True
|
|
413
|
+
clean_variation : bool, optional
|
|
414
|
+
clean custom variables, by default True
|
|
415
|
+
none_variations : list, optional
|
|
416
|
+
list of none variations, by default []
|
|
376
417
|
|
|
377
418
|
Examples
|
|
378
419
|
--------
|
|
@@ -410,12 +451,15 @@ def clean_none(dataframe, clean_variation=True, non_variations=[]):
|
|
|
410
451
|
|
|
411
452
|
Returns
|
|
412
453
|
-------
|
|
413
|
-
|
|
454
|
+
pd.DataFrame
|
|
455
|
+
_description_
|
|
414
456
|
"""
|
|
415
457
|
df = dataframe.copy()
|
|
416
458
|
df = df.replace(r"^\s*$", np.nan, regex=True)
|
|
459
|
+
if nan_to_none:
|
|
460
|
+
df = df.replace(np.NaN, None, inplace=True)
|
|
417
461
|
if clean_variation:
|
|
418
|
-
df = df.replace(
|
|
462
|
+
df = df.replace(none_variations, np.nan)
|
|
419
463
|
df = df.where(pd.notnull(df), None)
|
|
420
464
|
return df
|
|
421
465
|
|
|
@@ -0,0 +1,26 @@
|
|
|
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()
|
|
@@ -45,7 +45,7 @@ def create_sqlalchemy_url(
|
|
|
45
45
|
return url
|
|
46
46
|
|
|
47
47
|
|
|
48
|
-
def
|
|
48
|
+
def create_sqlalchemy_engine(
|
|
49
49
|
drivername: str,
|
|
50
50
|
host: str,
|
|
51
51
|
dbname: str,
|
|
@@ -54,7 +54,7 @@ def check_connection_valid(
|
|
|
54
54
|
port=3306,
|
|
55
55
|
) -> sqlalchemy.engine.base.Engine:
|
|
56
56
|
"""
|
|
57
|
-
|
|
57
|
+
Create SQLalchemy engine
|
|
58
58
|
|
|
59
59
|
Parameters
|
|
60
60
|
----------
|
|
@@ -87,9 +87,9 @@ def check_connection_valid(
|
|
|
87
87
|
engine = sqlalchemy.create_engine(url)
|
|
88
88
|
try:
|
|
89
89
|
engine.connect()
|
|
90
|
-
logging.info("
|
|
90
|
+
logging.info("create_sqlalchemy_engine: True")
|
|
91
91
|
except Exception as e:
|
|
92
|
-
logging.error(f"
|
|
92
|
+
logging.error(f"create_sqlalchemy_engine: False ({e})")
|
|
93
93
|
return engine
|
|
94
94
|
|
|
95
95
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: data-manipulation
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.37
|
|
4
4
|
Summary: Powerful data manipulation
|
|
5
5
|
Home-page: https://github.com/shawnngtq/data-manipulation
|
|
6
6
|
Author: Shawn Ng
|
|
@@ -19,6 +19,16 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
19
19
|
Classifier: Programming Language :: Cython
|
|
20
20
|
Classifier: Topic :: Scientific/Engineering
|
|
21
21
|
License-File: LICENSE
|
|
22
|
+
Requires-Dist: beautifulsoup4
|
|
23
|
+
Requires-Dist: django
|
|
24
|
+
Requires-Dist: lxml
|
|
25
|
+
Requires-Dist: matplotlib
|
|
26
|
+
Requires-Dist: mysql-connector-python
|
|
27
|
+
Requires-Dist: pandas
|
|
28
|
+
Requires-Dist: psycopg2
|
|
29
|
+
Requires-Dist: pyspark
|
|
30
|
+
Requires-Dist: tqdm
|
|
31
|
+
Requires-Dist: xlsxwriter
|
|
22
32
|
|
|
23
33
|
|
|
24
34
|
Data Manipulation is a Python package providing powerful utility functions.
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
def init_django(django_dir: str, project_name: str) -> None:
|
|
2
|
-
"""
|
|
3
|
-
Setup Django in Jupyter. Reference from https://gist.github.com/codingforentrepreneurs/76e570d759f83d690bf36a8a8fa4cfbe
|
|
4
|
-
|
|
5
|
-
Parameters
|
|
6
|
-
----------
|
|
7
|
-
django_dir : str
|
|
8
|
-
Django project location
|
|
9
|
-
project_name : str
|
|
10
|
-
Django project name
|
|
11
|
-
"""
|
|
12
|
-
import os
|
|
13
|
-
import sys
|
|
14
|
-
|
|
15
|
-
import django
|
|
16
|
-
|
|
17
|
-
# django_dir = os.getenv("PWD")
|
|
18
|
-
# os.chdir(django_dir)
|
|
19
|
-
|
|
20
|
-
project_name = project_name or os.environ.get("DJANGO_PROJECT") or None
|
|
21
|
-
if not project_name:
|
|
22
|
-
raise Exception(
|
|
23
|
-
"""Set an environment variable: `DJANGO_PROJECT=your_project_name` or call `init_django(your_project_name)`"""
|
|
24
|
-
)
|
|
25
|
-
|
|
26
|
-
sys.path.insert(0, django_dir)
|
|
27
|
-
os.environ.setdefault("DJANGO_SETTINGS_MODULE", f"{project_name}.settings")
|
|
28
|
-
os.environ["DJANGO_ALLOW_ASYNC_UNSAFE"] = "true"
|
|
29
|
-
django.setup()
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
def get_django_countries_dict():
|
|
33
|
-
"""
|
|
34
|
-
Get django_countries package's dictionaries
|
|
35
|
-
code_name - "SG": "SINGAPORE"
|
|
36
|
-
name_code - "SINGAPORE": "SG"
|
|
37
|
-
"""
|
|
38
|
-
from django_countries import countries
|
|
39
|
-
|
|
40
|
-
code_name = {k: v.upper() for k, v in dict(countries).items()}
|
|
41
|
-
name_code = {v: k for k, v in code_name.items()}
|
|
42
|
-
return code_name, name_code
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
if __name__ == "__main__":
|
|
46
|
-
import doctest
|
|
47
|
-
|
|
48
|
-
doctest.testmod()
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import logging
|
|
2
|
-
import subprocess
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
def ldapsearch(
|
|
6
|
-
search_base: str,
|
|
7
|
-
ldap_uri: str,
|
|
8
|
-
bind_dn: str,
|
|
9
|
-
password: str,
|
|
10
|
-
filter: str,
|
|
11
|
-
) -> subprocess.CompletedProcess:
|
|
12
|
-
cmd = (
|
|
13
|
-
"ldapsearch -b {search_base} -H {ldap_uri} -D {bind_dn} -w {password} {filter}"
|
|
14
|
-
)
|
|
15
|
-
output = subprocess.run(
|
|
16
|
-
cmd,
|
|
17
|
-
capture_output=True,
|
|
18
|
-
shell=True,
|
|
19
|
-
text=True,
|
|
20
|
-
)
|
|
21
|
-
return output
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
if __name__ == "__main__":
|
|
25
|
-
import doctest
|
|
26
|
-
|
|
27
|
-
doctest.testmod()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{data_manipulation-0.36 → data_manipulation-0.37}/data_manipulation/mysql_connector_python_.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{data_manipulation-0.36 → data_manipulation-0.37}/data_manipulation.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|