Skip to main content

nurtelecom_gras_library

Official Python toolkit for the NurTelecom GRAS data team. It bundles the everyday building blocks for data work into one package:

  • Oracle access — query into pandas, bulk insert/upsert, export to file
  • Geospatial — read WKT/SDO geometries straight into a GeoDataFrame
  • Secrets — pull credentials from HashiCorp Vault
  • SQL generation — build CREATE TABLE DDL (with partitioning) from a DataFrame
  • Notifications — Telegram, SMS, and e-mail (plain + HTML)
  • Jira — create/update issues and Service Desk requests
  • Tableau — export views to PNG / PDF / CSV / XLSX

Installation

pip install nurtelecom_gras_library

Spatial features (OracleGeoDataImporter, voronoi_split) need extra, heavier dependencies. Install them only if you need them:

pip install nurtelecom_gras_library[geo]      # geopandas, shapely, scipy
pip install nurtelecom_gras_library[smb]      # smbprotocol (network shares)
pip install nurtelecom_gras_library[tableau]  # tableauserverclient

The core install does not pull in geopandas/shapely/tableauserverclient; you'll get a clear error pointing you to the right extra if you use a feature that needs one.


Quick start

A complete, runnable version of the flow below lives in example.py — copy it and fill in your details.

import logging
from nurtelecom_gras_library import (
    get_db_connection,
    get_all_cred_dict,
    make_table_query_from_pandas,
)

# Optional: the library logs (doesn't print) — show INFO to see progress.
logging.basicConfig(level=logging.INFO)

# 1. Pull credentials from Vault. AppRole (role_id + secret_id) is recommended;
#    any argument left out is read from the matching env var (see Credentials).
all_cred_dict = get_all_cred_dict(
    vault_url="https://vault.example.com",
    role_id="your-approle-role-id",
    secret_id="your-approle-secret-id",
    path_to_secret="path/to/secret",
    mount_point="your_kv_mount",
)

# 2. Open a connection for a given user + database (context manager closes it).
with get_db_connection("my_login", "DWH", all_cred_dict) as db:
    # 3. Query into a pandas DataFrame.
    df = db.get_data("SELECT 1 AS one FROM dual")

    # 4. Generate and run a CREATE TABLE from the DataFrame, then load it.
    ddl = make_table_query_from_pandas(df=df, table_name="my_new_table")
    db.execute(ddl)
    db.upload_pandas_df_to_oracle(df, "my_new_table")

Database access (Oracle)

get_db_connection(user, database, all_cred_dict=None, geodata=False, vso_paths=None) returns an OracleDataRetriever (or an OracleGeoDataImporter when geodata=True). It looks up {USER}_{DATABASE}, {DATABASE}_IP, {DATABASE}_SERVICE_NAME, and {DATABASE}_PORT from the credentials dict. If the dict contains {DATABASE}_DSN (an Easy Connect string <IP>:<PORT>/<SERVICE_NAME>, e.g. 10.0.0.1:1521/DWH), it is used instead of the separate IP/PORT/SERVICE_NAME keys. You can also instantiate the class directly:

from nurtelecom_gras_library import OracleDataRetriever

db = OracleDataRetriever(
    user="user", password="pass",
    host="192.168.1.1", port="1521", service_name="DWH",
)

# or from a DSN (all methods work the same)
db = OracleDataRetriever(user="user", password="pass", dsn="192.168.1.1:1521/DWH")
db = OracleDataRetriever.from_dsn("user", "pass", "192.168.1.1:1521/DWH")

Connections are context managers — use with to release the engine/pool automatically (or call db.close() yourself):

with get_db_connection("my_login", "DWH", all_cred_dict) as db:
    df = db.get_data("SELECT 1 FROM dual")

Oracle Client / Thick mode

On import the library tries to initialize Thick mode (it loads the Oracle Client libraries), falling back silently to Thin mode if the client isn't found. Thick mode is required for accounts using older password verifiers — otherwise you'll see DPY-3015: password verifier type ... not supported ... in thin mode.

If the client isn't on the default loader path you'll get DPI-1047: Cannot locate ... Oracle Client library. Point the library at it via either:

export ORACLE_CLIENT_LIB_DIR=/opt/oracle/instantclient_21_13   # used on import
# or set LD_LIBRARY_PATH before launching Python
from nurtelecom_gras_library import enable_thick_mode
enable_thick_mode(lib_dir="/opt/oracle/instantclient_21_13")    # explicit

Set NURTELECOM_THICK_MODE=0 to skip the auto-init and stay in Thin mode.

Reading

df = db.get_data("SELECT * FROM my_table", remove_na=True, show_logs=True)

Writing

# Bulk insert (batched executemany)
db.upload_pandas_df_to_oracle(df, table_name="my_table")

# Parallel bulk insert using an internal connection pool
db.upload_pandas_df_to_oracle_parallel(df, "my_table", num_threads=4, batch_size=10000)

# MERGE / upsert on key columns (optionally summing some columns)
db.upsert_from_pandas_df(
    df, table_name="my_table",
    list_of_keys=["id"],
    sum_update_columns=["counter"],
)

Exporting & maintenance

db.export_to_file("SELECT * FROM big_table", "out.csv")          # chunked via pandas
db.export_to_file_oracle("SELECT * FROM big_table", "out.csv")   # chunked via oracledb
db.truncate_table("my_table")
db.execute("BEGIN my_proc(); END;")

Geospatial

OracleGeoDataImporter (returned by get_db_connection(..., geodata=True)) parses geometry columns into a geopandas.GeoDataFrame. Have your query return geometry as a standard interchange format. WKB (binary) is preferred — compact, fast, and lossless:

SELECT SDO_UTIL.TO_WKBGEOMETRY(SDO_CS.TRANSFORM(t.geometry, 4326)) AS geometry
FROM my_geo_table t

WKT (text) is also supported:

SELECT SDO_UTIL.TO_WKTGEOMETRY(SDO_CS.TRANSFORM(t.geometry, 4326)) AS geometry
FROM my_geo_table t
# geom_format defaults to "auto" (detects WKB bytes vs WKT text per value);
# pass "wkb" or "wkt" to force one.
gdf = geo_db.get_data(query, geom_columns_list=["geometry"], crid="EPSG:4326")

NULL geometries become None, CLOB/BLOB columns are read correctly, and the first column in geom_columns_list is set as the active geometry (so it can be named anything).

Geometry can also be written back via the geometry_cols/srid arguments on the upload methods (using SDO_GEOMETRY).


SQL generation from DataFrames

from nurtelecom_gras_library import make_table_query_from_pandas

ddl = make_table_query_from_pandas(
    df,
    table_name="events",
    list_num_columns=["amount"],
    list_date_columns=["event_date"],
    list_clob_columns=["payload"],
    # Optional partitioning
    partition_column="event_date",
    partition_type="RANGE",
    partition_granularity="MONTH",
    partition_start="2024-01-01",
    partition_end="2025-01-01",
)

Supports RANGE (manual or interval), LIST, and HASH partitioning.


Credentials (Vault)

from nurtelecom_gras_library import get_all_cred_dict

# AppRole auth (recommended): role_id + secret_id
creds = get_all_cred_dict(
    vault_url=url, role_id="your-approle-role-id", secret_id="your-approle-secret-id",
    path_to_secret="path/to/secret", mount_point="your_kv_mount",
)

# Token auth
creds = get_all_cred_dict(vault_url=url, vault_token=token, path_to_secret=..., mount_point=...)

AppRole takes precedence when both role_id and secret_id are supplied. Values are used as-is (raw) — vault_url must include the scheme (https://…). If any argument is omitted, it is read verbatim from the corresponding environment variable: VAULT_LINK_URL, VAULT_TKN, VAULT_ROLE_ID, VAULT_SECRET_ID, PATH_TO_SECRET_VLT, MOUNT_POINT_VLT. The function raises ValueError if no credentials are given and RuntimeError if Vault auth fails. Helpers pass_encoder / pass_decoder still provide base64 encode/decode if you want them (note: base64 is encoding, not encryption).

Vault Secrets Operator (Kubernetes)

When VSO syncs Vault secrets into Kubernetes Secrets mounted as volumes (one file per key), read them with get_all_cred_dict_vso. Several secrets mounted at different paths are merged into one dict:

volumeMounts:
  - { name: main, mountPath: /etc/secrets/main, readOnly: true }
  - { name: db,   mountPath: /etc/secrets/db,   readOnly: true }
from nurtelecom_gras_library import get_all_cred_dict_vso, get_db_connection

creds = get_all_cred_dict_vso(["/etc/secrets/main", "/etc/secrets/db"])
db = get_db_connection("my_login", "DWH", creds)

# or in one step
db = get_db_connection("my_login", "DWH", vso_paths=["/etc/secrets/main", "/etc/secrets/db"])

If secret_paths is omitted, paths are read from the VSO_SECRET_PATHS environment variable (plain text, comma-separated). Kubernetes bookkeeping entries (..data etc.) and VSO's _raw key are skipped; trailing newlines are stripped. A key present in more than one directory raises ValueError.


Notifications

Telegram

from nurtelecom_gras_library import (
    send_msg_via_telegram, send_photo_via_telegram, send_file_via_telegram,
)

send_msg_via_telegram(token, chat_id, "Hello")
send_photo_via_telegram(token, chat_id, "chart.png", captions="Daily chart")
send_file_via_telegram(token, chat_id, "report.xlsx")

send_telegram_msg(payload, receiver, db) and send_sms(payload, receiver, db) route messages through Oracle stored procedures using a DB connection.

E-mail

from nurtelecom_gras_library import send_email, send_email_html

# Plain text + attachments (single path, or a whole directory)
send_email(
    send_to=["a@x.kg", "b@x.kg"], send_from="bot@x.kg",
    subject="Report", host="smtp.host", content="See attached",
    file_to_attach="report.pdf", cc_to="boss@x.kg",
)

# HTML body with a plain-text fallback
send_email_html(
    send_to="a@x.kg", send_from="bot@x.kg", subject="Report",
    host="smtp.host", html="<h1>Hi</h1>", text="Hi",
)

send_to/cc_to accept either a string or a list/tuple of addresses.


Jira

from nurtelecom_gras_library import JiraClient

jira = JiraClient(base_url="https://jira.example", token="PAT")
# or: JiraClient(base_url=..., username=..., password=...)

# Standard projects
key = jira.create_project_issue("OPTM", "Task", {"summary": "New task"})
jira.update_project_issue_fields(key, {"description": "..."})
jira.add_comment_to_issue(key, "Working on it")
jira.add_attachment_to_issue(key, "log.txt")
df = jira.get_issues_df(jira.fetch_all_issues("project = OPTM"), field_map={"Key": "Key", "summary": None})

# Service Desk
req = jira.create_service_desk_request(service_desk_id, request_type_id, fields)
jira.add_comment_to_request(req, "Update", public=True)

Includes discovery helpers: list_available_projects, list_issue_types_for_project, get_project_issue_fields, get_request_types, get_request_fields, check_portal_access.


Tableau

from nurtelecom_gras_library import TableauServerManager

tab = TableauServerManager(login="user", password="pass", host="https://tableau.host")

# Export a view directly from its browser URL (PNG/PDF/CSV/XLSX).
# Resolves the workbook from the URL slug — no full-site enumeration needed.
status, file_path = tab.export_view(
    "https://tableau.host/#/views/MyWorkbook/MyView", format="png",
)

The server REST API version is auto-detected by default; pin it with server_version="3.9" if needed. Exported files are written to an exported_files/ folder.


Utilities

Function Purpose
measure_time Decorator that prints a function's runtime
get_list_of_objects(path, is_dir=False) List files (or dirs) in a path
get_a_copy(src, dst) Copy a file
value_extractor(pattern, path) Extract a numeric value from a text file
merge_clob_maker(string, num_of_charr=25000) Split a long string into TO_CLOB(...) chunks for Oracle
voronoi_split(poly, coords, ...) Voronoi partition of a polygon (requires [geo])
register_smb_session(user, password, server) Open an SMB session (requires [smb])

License

MIT License — see LICENSE.

Contact

For questions or support, contact the NurTelecom GRAS team.

Metadata

Release files for nurtelecom-gras-library 2.3.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for nurtelecom-gras-library 2.3.2
File Size Uploaded
nurtelecom_gras_library-2.3.2.tar.gz 46.7 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for nurtelecom-gras-library 2.3.2
File Interpreter ABI Platform
nurtelecom_gras_library-2.3.2-py3-none-any.whl Python 3 none any Details

Total release size: 87.2 kB

Release files / nurtelecom_gras_library-2.3.2.tar.gz

Download URL nurtelecom_gras_library-2.3.2.tar.gz
Size 46.7 kB
Tags Source
SHA-256 checksum
How to use checksums
ff0589af6ae1411224e4f82f7e41d7173c9ff0bc6b283690563a4fe0537336d7
BLAKE2b-256 checksum
How to use checksums
3fd833a9bf72937d3234621b44a6f8757de71d73f2615e7c7d690f652ecaa9d3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.10.11

Release files / nurtelecom_gras_library-2.3.2-py3-none-any.whl

Download URL nurtelecom_gras_library-2.3.2-py3-none-any.whl
Size 40.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
7422da8df3ecbd9f8cf8195f7669dbe08039fd185a59684379471630b77afbc2
BLAKE2b-256 checksum
How to use checksums
b054c190801671d3dfb39d10ad9f4e58a3c5e37fda26e2d56436dca645772e70
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.10.11

Release history Release notifications | RSS feed

This release

2.3.2 This release

2 release files

2.3.1

2 release files

2.3.0

2 release files

2.2.1

2 release files

2.2.0

2 release files

2.1.9

2 release files

2.1.8

2 release files

2.1.7

2 release files

2.1.6

2 release files

2.1.5

2 release files

2.1.4

2 release files

2.1.3

2 release files

2.1.2

2 release files

2.1.1

2 release files

2.1.0

2 release files

2.0.1

2 release files

2.0.0

2 release files

1.5.1

2 release files

1.5.0

2 release files

1.4.0

2 release files

1.3.9

2 release files

1.3.8

2 release files

1.3.7

2 release files

1.3.6

2 release files

1.3.5

2 release files

1.3.4

2 release files

1.3.3

2 release files

1.3.2

2 release files

1.3.1

2 release files

1.3.0

2 release files

1.2.4

2 release files

1.2.3

1 release file

1.2.2

1 release file

1.2.1

1 release file

1.2.0

1 release file

1.1.9

1 release file

1.1.8

1 release file

1.1.7

1 release file

1.1.6

1 release file

1.1.5

1 release file

1.1.4

1 release file

1.1.3

1 release file

1.1.2

1 release file

1.1.1

1 release file

1.1.0

1 release file

1.0.9

1 release file

1.0.8

1 release file

1.0.7

1 release file

1.0.6

1 release file

1.0.5

1 release file

1.0.4

1 release file

1.0.3

1 release file

1.0.2

1 release file

1.0.1

1 release file

1.0.0

1 release file

0.3.11

1 release file

0.3.10

1 release file

0.3.9

1 release file

0.3.8

1 release file

0.3.7

1 release file

0.3.6

1 release file

0.3.5

1 release file

0.3.4

1 release file

0.3.3

1 release file

0.3.2

1 release file

0.3.1

1 release file

0.3

1 release file

0.2

1 release file

0.1

1 release file

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page