dynamic-tables 0.1.0__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.
- dynamic_tables/__init__.py +5 -0
- dynamic_tables/dynamic_tables.py +224 -0
- dynamic_tables-0.1.0.dist-info/LICENSE +0 -0
- dynamic_tables-0.1.0.dist-info/METADATA +139 -0
- dynamic_tables-0.1.0.dist-info/RECORD +7 -0
- dynamic_tables-0.1.0.dist-info/WHEEL +5 -0
- dynamic_tables-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,224 @@
|
|
1
|
+
# this is dynamic_tables.py
|
2
|
+
|
3
|
+
import psycopg2, re
|
4
|
+
from psycopg2 import sql
|
5
|
+
|
6
|
+
class DynamicTables:
|
7
|
+
|
8
|
+
def __init__(self):
|
9
|
+
"""Initialize the class by calling self.initialize() to set up default values."""
|
10
|
+
self.initialize()
|
11
|
+
|
12
|
+
def initialize(self):
|
13
|
+
"""Sets up default attributes for the table structure."""
|
14
|
+
self.column_list = []
|
15
|
+
self.column_dynamic = ''
|
16
|
+
self.table_prefix = 'dtbl_'
|
17
|
+
self.table_name_dynamic = ''
|
18
|
+
|
19
|
+
def connection_open(self, dbname, user, password, host):
|
20
|
+
"""Opens a connection to the PostgreSQL database and initializes the cursor."""
|
21
|
+
self.conn = psycopg2.connect(
|
22
|
+
dbname=dbname,
|
23
|
+
user=user,
|
24
|
+
password=password,
|
25
|
+
host=host
|
26
|
+
)
|
27
|
+
self.cur = self.conn.cursor()
|
28
|
+
|
29
|
+
def connectHC(self, dbname, user, password, host):
|
30
|
+
"""Helper function to open a connection using hardcoded credentials."""
|
31
|
+
self.connection_open(dbname, user, password, host)
|
32
|
+
|
33
|
+
def connectJSON(self):
|
34
|
+
"""Opens a database connection using credentials from a JSON config file."""
|
35
|
+
import json
|
36
|
+
with open("config.json") as config_file:
|
37
|
+
config = json.load(config_file)
|
38
|
+
self.conn = psycopg2.connect(**config)
|
39
|
+
self.cur = self.conn.cursor()
|
40
|
+
|
41
|
+
def connectENVS(self):
|
42
|
+
"""Opens a database connection using environment variables."""
|
43
|
+
import os
|
44
|
+
self.conn = psycopg2.connect(
|
45
|
+
host=os.getenv("DTABLES_ENVS_PGSQL_HOST"),
|
46
|
+
user=os.getenv("DTABLES_ENVS_PGSQL_USER"),
|
47
|
+
password=os.getenv("DTABLES_ENVS_PGSQL_PASSWORD"),
|
48
|
+
database=os.getenv("DTABLES_ENVS_PGSQL_DATABASE")
|
49
|
+
)
|
50
|
+
self.cur = self.conn.cursor()
|
51
|
+
|
52
|
+
def connection_close(self):
|
53
|
+
"""Closes the database cursor and connection."""
|
54
|
+
self.cur.close()
|
55
|
+
self.conn.close()
|
56
|
+
|
57
|
+
def close(self):
|
58
|
+
"""Alias for connection_close() to improve readability."""
|
59
|
+
self.connection_close()
|
60
|
+
|
61
|
+
def format_table_name(self, input_column):
|
62
|
+
"""Formats an input column name to create a valid table name."""
|
63
|
+
cleaned = re.sub(r'[^a-zA-Z0-9_]', '', input_column)
|
64
|
+
return self.table_prefix + cleaned
|
65
|
+
|
66
|
+
def input(self, *args):
|
67
|
+
"""Handles data insertion by dynamically determining the table name and inserting values."""
|
68
|
+
column_dict = {}
|
69
|
+
self.table_name_dynamic = ''
|
70
|
+
|
71
|
+
for column_number, (column_name, _) in enumerate(self.column_list):
|
72
|
+
if column_name == self.column_dynamic:
|
73
|
+
self.table_name_dynamic = self.format_table_name(args[column_number])
|
74
|
+
column_dict[column_name] = args[column_number]
|
75
|
+
else:
|
76
|
+
column_dict[column_name] = args[column_number]
|
77
|
+
|
78
|
+
if self.table_name_dynamic:
|
79
|
+
self.create_table(self.table_name_dynamic, self.column_list)
|
80
|
+
|
81
|
+
self.insert_data(self.table_name_dynamic, column_dict)
|
82
|
+
|
83
|
+
def insert_data(self, table_name, data_dict):
|
84
|
+
"""Inserts data into a dynamically created table."""
|
85
|
+
columns = data_dict.keys()
|
86
|
+
values = list(data_dict.values())
|
87
|
+
|
88
|
+
query = sql.SQL("""
|
89
|
+
INSERT INTO {} ({})
|
90
|
+
VALUES ({});
|
91
|
+
""").format(
|
92
|
+
sql.Identifier(table_name),
|
93
|
+
sql.SQL(', ').join(map(sql.Identifier, columns)),
|
94
|
+
sql.SQL(', ').join(sql.Placeholder() for _ in columns)
|
95
|
+
)
|
96
|
+
|
97
|
+
self.cur.execute(query, values)
|
98
|
+
self.conn.commit()
|
99
|
+
|
100
|
+
def create_table(self, table_name, columns):
|
101
|
+
"""Creates a table dynamically if it does not already exist."""
|
102
|
+
column_definitions = sql.SQL(", ").join(
|
103
|
+
sql.SQL("{} {}").format(sql.Identifier(col_name), sql.SQL(col_type))
|
104
|
+
for col_name, col_type in columns
|
105
|
+
)
|
106
|
+
|
107
|
+
query = sql.SQL("""
|
108
|
+
CREATE TABLE IF NOT EXISTS {} (
|
109
|
+
id SERIAL PRIMARY KEY,
|
110
|
+
{}
|
111
|
+
);
|
112
|
+
""").format(sql.Identifier(table_name), column_definitions)
|
113
|
+
|
114
|
+
self.cur.execute(query)
|
115
|
+
self.conn.commit()
|
116
|
+
|
117
|
+
def show_db(self):
|
118
|
+
"""Lists all available databases."""
|
119
|
+
self.cur.execute("SELECT datname FROM pg_database WHERE datistemplate = false;")
|
120
|
+
for row in self.cur.fetchall():
|
121
|
+
print(row)
|
122
|
+
|
123
|
+
def show_tables(self):
|
124
|
+
"""Fetch and return a list of all table names."""
|
125
|
+
self.cur.execute("SELECT table_name FROM information_schema.tables WHERE table_schema = 'public';")
|
126
|
+
tables = self.cur.fetchall()
|
127
|
+
|
128
|
+
if tables:
|
129
|
+
table_list = [table[0] for table in tables] # Convert to a simple list of table names
|
130
|
+
print("\n".join(table_list)) # Print for debugging
|
131
|
+
return table_list
|
132
|
+
else:
|
133
|
+
print("No tables found.")
|
134
|
+
return [] # Return an empty list instead of None
|
135
|
+
|
136
|
+
def show_columns(self, table_name):
|
137
|
+
"""Displays all columns and their data types for a given table."""
|
138
|
+
try:
|
139
|
+
query = sql.SQL("""
|
140
|
+
SELECT column_name, data_type
|
141
|
+
FROM information_schema.columns
|
142
|
+
WHERE table_name = %s
|
143
|
+
ORDER BY ordinal_position;
|
144
|
+
""")
|
145
|
+
self.cur.execute(query, (table_name,))
|
146
|
+
columns = self.cur.fetchall()
|
147
|
+
|
148
|
+
if columns:
|
149
|
+
print(f"Columns in table '{table_name}':")
|
150
|
+
for column_name, data_type in columns:
|
151
|
+
print(f" - {column_name}: {data_type}")
|
152
|
+
else:
|
153
|
+
print(f"No columns found for table '{table_name}' or the table does not exist.")
|
154
|
+
except Exception as e:
|
155
|
+
print(f"Error retrieving columns for '{table_name}': {e}")
|
156
|
+
|
157
|
+
def show_columns_all(self):
|
158
|
+
"""Lists column details for all tables."""
|
159
|
+
self.cur.execute("SELECT table_name FROM information_schema.tables WHERE table_schema = 'public';")
|
160
|
+
for table in self.cur.fetchall():
|
161
|
+
self.show_columns(table[0])
|
162
|
+
|
163
|
+
def select_table(self, table_name):
|
164
|
+
"""Retrieves and prints all rows from the specified table."""
|
165
|
+
query = sql.SQL("SELECT * FROM {}").format(sql.Identifier(table_name))
|
166
|
+
self.cur.execute(query)
|
167
|
+
rows = self.cur.fetchall()
|
168
|
+
|
169
|
+
if rows:
|
170
|
+
for row in rows:
|
171
|
+
print(f"Row: {row}")
|
172
|
+
else:
|
173
|
+
print(f"Table '{table_name}' exists but has no data.")
|
174
|
+
|
175
|
+
return rows
|
176
|
+
|
177
|
+
def delete_tables(self):
|
178
|
+
"""Deletes all tables that match the current table prefix."""
|
179
|
+
query = """
|
180
|
+
SELECT table_name FROM information_schema.tables
|
181
|
+
WHERE table_schema = 'public' AND table_name LIKE %s;
|
182
|
+
"""
|
183
|
+
prefix_pattern = self.table_prefix + "%" # Dynamic prefix
|
184
|
+
print("prefix_pattern:" + prefix_pattern) # Debugging output
|
185
|
+
self.cur.execute(query, (prefix_pattern,))
|
186
|
+
|
187
|
+
tables = self.cur.fetchall()
|
188
|
+
print("DEBUG - Found tables to delete:", tables) # Debugging output
|
189
|
+
|
190
|
+
for table in tables:
|
191
|
+
table_name = table[0]
|
192
|
+
print(f"Dropping table: {table_name}")
|
193
|
+
|
194
|
+
drop_query = sql.SQL("DROP TABLE IF EXISTS {} CASCADE;").format(sql.Identifier(table_name))
|
195
|
+
self.cur.execute(drop_query)
|
196
|
+
|
197
|
+
self.conn.commit()
|
198
|
+
|
199
|
+
def set_columns(self, input_columns):
|
200
|
+
"""Sets the columns for table creation based on user input."""
|
201
|
+
self.column_list = []
|
202
|
+
for column in input_columns.split(','):
|
203
|
+
column = column.strip()
|
204
|
+
parts = column.split(maxsplit=1)
|
205
|
+
if len(parts) == 2:
|
206
|
+
self.column_list.append((parts[0], parts[1]))
|
207
|
+
else:
|
208
|
+
print(f"Invalid column format: '{column}'")
|
209
|
+
|
210
|
+
def set_table_prefix(self, input_prefix):
|
211
|
+
"""Defines a custom table name prefix."""
|
212
|
+
self.table_prefix = input_prefix
|
213
|
+
|
214
|
+
def set_dynamic_column(self, input_column):
|
215
|
+
"""Sets the column that determines dynamic table names."""
|
216
|
+
self.column_dynamic = input_column
|
217
|
+
|
218
|
+
def status(self):
|
219
|
+
"""Displays the currently configured column settings."""
|
220
|
+
print('\nConfigured Columns:')
|
221
|
+
for column in self.column_list:
|
222
|
+
print(column)
|
223
|
+
print('\nDynamic Column:\n', self.column_dynamic, '\n')
|
224
|
+
|
File without changes
|
@@ -0,0 +1,139 @@
|
|
1
|
+
Metadata-Version: 2.1
|
2
|
+
Name: dynamic-tables
|
3
|
+
Version: 0.1.0
|
4
|
+
Summary: A dynamic table creation and management library for PostgreSQL
|
5
|
+
Home-page: https://github.com/scottrodeo/dynamic_tables
|
6
|
+
Author: Scott Rodeo
|
7
|
+
Author-email: signcactus@gmail.com
|
8
|
+
License: UNKNOWN
|
9
|
+
Project-URL: Author Website, https://patreon.com/scottrodeo
|
10
|
+
Platform: UNKNOWN
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
13
|
+
Classifier: Operating System :: OS Independent
|
14
|
+
Requires-Python: >=3.6
|
15
|
+
Description-Content-Type: text/markdown
|
16
|
+
Requires-Dist: psycopg2
|
17
|
+
|
18
|
+
# Dynamic Tables (Python)
|
19
|
+
A Python library for dynamically creating and managing PostgreSQL tables based on input data.
|
20
|
+
|
21
|
+
## 🚀 Features
|
22
|
+
- Automatically creates tables based on incoming data.
|
23
|
+
- Supports dynamic naming conventions.
|
24
|
+
- Provides easy database connectivity with PostgreSQL.
|
25
|
+
- Includes helper functions for querying and deleting tables.
|
26
|
+
|
27
|
+
---
|
28
|
+
|
29
|
+
## 📥 Installation
|
30
|
+
### **1️⃣ Install via GitHub (Recommended for Development)**
|
31
|
+
Clone the repository and install in editable mode:
|
32
|
+
```bash
|
33
|
+
git clone https://github.com/scottrodeo/dynamic-tables-python.git
|
34
|
+
cd dynamic-tables-python
|
35
|
+
pip install -e .
|
36
|
+
```
|
37
|
+
|
38
|
+
### **2️⃣ Install Directly via `pip`**
|
39
|
+
The package is available on PyPI, you can install it with:
|
40
|
+
```bash
|
41
|
+
pip install dynamic-tables
|
42
|
+
```
|
43
|
+
|
44
|
+
---
|
45
|
+
|
46
|
+
## 🏃♂️ Running the Example
|
47
|
+
### **1️⃣ Quick Run (Without Installation)**
|
48
|
+
If you don't want to install the package, you can directly run the example script:
|
49
|
+
```bash
|
50
|
+
python3 examples/example.py
|
51
|
+
```
|
52
|
+
💡 *This works because the script dynamically adjusts `sys.path`.*
|
53
|
+
|
54
|
+
### **2️⃣ Recommended (After Installation)**
|
55
|
+
If you've installed the package (`pip install -e .`), simply run:
|
56
|
+
```bash
|
57
|
+
python3 examples/example.py
|
58
|
+
```
|
59
|
+
|
60
|
+
---
|
61
|
+
|
62
|
+
## 📌 Example Usage
|
63
|
+
Once installed, you can use `dynamic_tables` in your Python scripts:
|
64
|
+
|
65
|
+
```python
|
66
|
+
from dynamic_tables import DynamicTables
|
67
|
+
|
68
|
+
# Initialize the dynamic table manager
|
69
|
+
tables = DynamicTables()
|
70
|
+
|
71
|
+
# Example: Creating and inserting data dynamically
|
72
|
+
tables.set_table_prefix("dtbl_")
|
73
|
+
tables.set_columns("domain TEXT, category TEXT, lang TEXT")
|
74
|
+
tables.set_dynamic_column("domain")
|
75
|
+
|
76
|
+
tables.input("wikipedia.org", "cats", "en")
|
77
|
+
tables.input("wikipedia.org", "dogs", "en")
|
78
|
+
|
79
|
+
# Show all tables
|
80
|
+
tables.show_tables()
|
81
|
+
```
|
82
|
+
|
83
|
+
---
|
84
|
+
|
85
|
+
## 🛠️ Available Functions
|
86
|
+
| Function | Description |
|
87
|
+
|----------|-------------|
|
88
|
+
| `set_columns("name TYPE, age TYPE")` | Define table schema |
|
89
|
+
| `set_table_prefix("prefix_")` | Set a custom table prefix |
|
90
|
+
| `set_dynamic_column("column_name")` | Set the column that determines dynamic table names |
|
91
|
+
| `input(value1, value2, ...)` | Insert a new row dynamically |
|
92
|
+
| `show_tables()` | List all dynamically created tables |
|
93
|
+
| `show_columns("table_name")` | Show column details for a specific table |
|
94
|
+
| `select_table("table_name")` | Retrieve all rows from a table |
|
95
|
+
| `delete_tables()` | Drop all tables matching the prefix |
|
96
|
+
|
97
|
+
---
|
98
|
+
|
99
|
+
## ⚡ Development
|
100
|
+
### **Running Tests**
|
101
|
+
To run the test suite:
|
102
|
+
```bash
|
103
|
+
pytest tests/
|
104
|
+
```
|
105
|
+
|
106
|
+
### **Linting**
|
107
|
+
Ensure your code follows best practices:
|
108
|
+
```bash
|
109
|
+
flake8 dynamic_tables/
|
110
|
+
```
|
111
|
+
|
112
|
+
---
|
113
|
+
|
114
|
+
## 🤝 Contributing
|
115
|
+
Contributions are welcome! If you'd like to improve `dynamic_tables`, follow these steps:
|
116
|
+
1. Fork the repository.
|
117
|
+
2. Create a new branch (`git checkout -b feature-branch`).
|
118
|
+
3. Commit your changes (`git commit -m "Added new feature"`).
|
119
|
+
4. Push to the branch (`git push origin feature-branch`).
|
120
|
+
5. Open a pull request.
|
121
|
+
|
122
|
+
---
|
123
|
+
|
124
|
+
## 📄 License
|
125
|
+
This project is licensed under the **MIT License**. See the [LICENSE](LICENSE) file for details.
|
126
|
+
|
127
|
+
---
|
128
|
+
|
129
|
+
## 🌎 Links
|
130
|
+
- **GitHub Repository:** [Dynamic Tables (Python)](https://github.com/scottrodeo/dynamic-tables-python)
|
131
|
+
- **Documentation:** *(To be added)*
|
132
|
+
- **Issue Tracker:** [Report Issues](https://github.com/scottrodeo/dynamic-tables-python/issues)
|
133
|
+
|
134
|
+
---
|
135
|
+
|
136
|
+
### **🚀 Happy Coding!**
|
137
|
+
|
138
|
+
|
139
|
+
|
@@ -0,0 +1,7 @@
|
|
1
|
+
dynamic_tables/__init__.py,sha256=ttyVGUaIlALPqpfaHS3jX6CqLPoPfwvE-eFU_1ly4eY,59
|
2
|
+
dynamic_tables/dynamic_tables.py,sha256=y34xTOCxfYN0YLe82aq1zUqpdNoAcZ06kdE21EvxB_o,8298
|
3
|
+
dynamic_tables-0.1.0.dist-info/LICENSE,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
4
|
+
dynamic_tables-0.1.0.dist-info/METADATA,sha256=bDLWlQXC8f8ujUdOBUwRORJBqduQeS0mw_ruF4L6oKo,3871
|
5
|
+
dynamic_tables-0.1.0.dist-info/WHEEL,sha256=g4nMs7d-Xl9-xC9XovUrsDHGXt-FT0E17Yqo92DEfvY,92
|
6
|
+
dynamic_tables-0.1.0.dist-info/top_level.txt,sha256=IUnttnbuTiMp4f8hXorriWorV8twRB535pDm0yI6c3w,15
|
7
|
+
dynamic_tables-0.1.0.dist-info/RECORD,,
|
@@ -0,0 +1 @@
|
|
1
|
+
dynamic_tables
|