dynamic-tables 0.1.1__py3-none-any.whl → 0.2.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.
@@ -1,6 +1,6 @@
1
1
  # this is dynamic_tables.py
2
2
 
3
- import psycopg2, re
3
+ import psycopg2, re, logging
4
4
  from psycopg2 import sql
5
5
 
6
6
  class DynamicTables:
@@ -15,7 +15,70 @@ class DynamicTables:
15
15
  self.column_dynamic = ''
16
16
  self.table_prefix = 'dtbl_'
17
17
  self.table_name_dynamic = ''
18
+ self.version = '0.1.2'
19
+ """Initialize with default logging level (CRITICAL) and no file logging."""
20
+ self.log_file_handler = None
21
+ self.setup_logging(level=logging.CRITICAL, log_to_file=False)
22
+
23
+ def setup_logging(self, level=logging.CRITICAL, log_to_file=False):
24
+ """
25
+ Configures logging level and optionally enables file logging.
26
+
27
+ Args:
28
+ level (int): Logging level (e.g., logging.DEBUG, logging.INFO).
29
+ log_to_file (bool): Enable or disable file logging.
30
+ """
31
+ logging.basicConfig(
32
+ level=level,
33
+ format="%(asctime)s [%(levelname)s] %(message)s",
34
+ handlers=[logging.StreamHandler()] # Console only by default
35
+ )
36
+
37
+ # Enable file logging if requested
38
+ if log_to_file and not self.log_file_handler:
39
+ self.log_file_handler = logging.FileHandler("dynamic_tables.log")
40
+ self.log_file_handler.setFormatter(logging.Formatter("%(asctime)s [%(levelname)s] %(message)s"))
41
+ logging.getLogger().addHandler(self.log_file_handler)
42
+
43
+ # Disable file logging if requested
44
+ elif not log_to_file and self.log_file_handler:
45
+ logging.getLogger().removeHandler(self.log_file_handler)
46
+ self.log_file_handler.close()
47
+ self.log_file_handler = None
48
+
49
+ logging.info(f"Logging level set to {logging.getLevelName(level)}")
50
+ if log_to_file:
51
+ logging.info("File logging enabled (dynamic_tables.log)")
52
+
53
+ def change_log_level(self, level_name):
54
+ """
55
+ Change the logging level at runtime.
56
+ Args:
57
+ level_name (str): "DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"
58
+ """
59
+ level = getattr(logging, level_name.upper(), logging.CRITICAL)
60
+ logging.getLogger().setLevel(level)
61
+ logging.info(f"Log level changed to {level_name.upper()}")
62
+
63
+ def set_columns(self, input_columns):
64
+ """Sets the columns for table creation based on user input."""
65
+ self.column_list = []
66
+ for column in input_columns.split(','):
67
+ column = column.strip()
68
+ parts = column.split(maxsplit=1)
69
+ if len(parts) == 2:
70
+ self.column_list.append((parts[0], parts[1]))
71
+ else:
72
+ logging.warning(f"Invalid column format: '{column}'")
73
+
74
+ def set_table_prefix(self, input_prefix):
75
+ """Defines a custom table name prefix."""
76
+ self.table_prefix = input_prefix
18
77
 
78
+ def set_dynamic_column(self, input_column):
79
+ """Sets the column that determines dynamic table names."""
80
+ self.column_dynamic = input_column
81
+
19
82
  def connection_open(self, dbname, user, password, host):
20
83
  """Opens a connection to the PostgreSQL database and initializes the cursor."""
21
84
  self.conn = psycopg2.connect(
@@ -26,28 +89,29 @@ class DynamicTables:
26
89
  )
27
90
  self.cur = self.conn.cursor()
28
91
 
29
- def connectHC(self, dbname, user, password, host):
92
+ def connectHC(self, database, user, password, host):
30
93
  """Helper function to open a connection using hardcoded credentials."""
31
- self.connection_open(dbname, user, password, host)
94
+ self.connection_open(database, user, password, host)
32
95
 
33
- def connectJSON(self):
96
+ def connectJSON(self, config_path="config.json"):
34
97
  """Opens a database connection using credentials from a JSON config file."""
35
98
  import json
36
- with open("config.json") as config_file:
99
+ with open(config_path) as config_file:
37
100
  config = json.load(config_file)
38
- self.conn = psycopg2.connect(**config)
39
- self.cur = self.conn.cursor()
101
+ database=config["database"]
102
+ user=config["user"]
103
+ password=config["password"]
104
+ host=config["host"]
105
+ self.connection_open(database, user, password, host)
40
106
 
41
107
  def connectENVS(self):
42
108
  """Opens a database connection using environment variables."""
43
109
  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()
110
+ database=os.getenv("DTABLES_ENVS_PGSQL_DATABASE")
111
+ user=os.getenv("DTABLES_ENVS_PGSQL_USER")
112
+ password=os.getenv("DTABLES_ENVS_PGSQL_PASSWORD")
113
+ host=os.getenv("DTABLES_ENVS_PGSQL_HOST")
114
+ self.connection_open(database, user, password, host)
51
115
 
52
116
  def connection_close(self):
53
117
  """Closes the database cursor and connection."""
@@ -58,28 +122,120 @@ class DynamicTables:
58
122
  """Alias for connection_close() to improve readability."""
59
123
  self.connection_close()
60
124
 
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
125
+ def get_tables(self):
126
+ """Returns a list of all table names in the public schema."""
127
+ try:
128
+ self.cur.execute("SELECT table_name FROM information_schema.tables WHERE table_schema = 'public';")
129
+ tables = self.cur.fetchall()
130
+
131
+ if tables:
132
+ table_list = [table[0] for table in tables] # Convert to a simple list of table names
133
+ logging.info(f"Found {len(table_list)} tables.")
134
+ logging.debug(f"Tables: {table_list}")
135
+ return table_list
136
+ else:
137
+ logging.info("No tables found.")
138
+ return [] # Empty list instead of None for consistency
65
139
 
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 = ''
140
+ except Exception as e:
141
+ logging.error(f"Error retrieving tables: {e}", exc_info=True)
142
+ return None
70
143
 
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]
144
+ def get_columns(self, table_name):
145
+ """Returns a list of all columns and their data types for a given table."""
146
+ try:
147
+ query = sql.SQL("""
148
+ SELECT column_name, data_type
149
+ FROM information_schema.columns
150
+ WHERE table_name = %s
151
+ ORDER BY ordinal_position;
152
+ """)
153
+ self.cur.execute(query, (table_name,))
154
+ columns = self.cur.fetchall()
155
+
156
+ if columns:
157
+ logging.info(f"Retrieved {len(columns)} columns from '{table_name}'.")
158
+ return columns
75
159
  else:
76
- column_dict[column_name] = args[column_number]
160
+ logging.warning(f"No columns found for table '{table_name}' or the table does not exist.")
161
+ return []
77
162
 
78
- if self.table_name_dynamic:
79
- self.create_table(self.table_name_dynamic, self.column_list)
163
+ except Exception as e:
164
+ logging.error(f"Error retrieving columns for '{table_name}': {e}", exc_info=True)
165
+ return None
80
166
 
81
- self.insert_data(self.table_name_dynamic, column_dict)
167
+ def get_table_rows(self, table_name):
168
+ """Retrieves all rows from the specified table and returns them."""
169
+ try:
170
+ query = sql.SQL("SELECT * FROM {}").format(sql.Identifier(table_name))
171
+ self.cur.execute(query)
172
+ rows = self.cur.fetchall()
173
+
174
+ if rows:
175
+ logging.info(f"Retrieved {len(rows)} rows from '{table_name}'.")
176
+ logging.debug(f"Rows from '{table_name}': {rows}")
177
+ return rows # Returns data for other functions
178
+ else:
179
+ logging.info(f"Table '{table_name}' exists but has no data.")
180
+ return []
181
+
182
+ except Exception as e:
183
+ logging.error(f"Error retrieving rows from '{table_name}': {e}", exc_info=True)
184
+ return None
185
+
186
+ def show_db(self):
187
+ """Displays the name of the current database."""
188
+ self.cur.execute("SELECT current_database();")
189
+ db_name = self.cur.fetchone()[0]
190
+ logging.info(f"Connected to database: {db_name}")
191
+
192
+ def show_tables(self):
193
+ """Prints a list of all table names (uses get_tables)."""
194
+ tables = self.get_tables()
195
+
196
+ if tables:
197
+ print("Tables in the database:")
198
+ for table in tables:
199
+ print(f" - {table}")
200
+ else:
201
+ print("No tables found or an error occurred.")
82
202
 
203
+ def show_columns(self, table_name):
204
+ """Prints column details for human readability (uses get_columns)."""
205
+ columns = self.get_columns(table_name)
206
+
207
+ if columns:
208
+ print(f"Columns in '{table_name}':")
209
+ for name, dtype in columns:
210
+ print(f" - {name}: {dtype}")
211
+ else:
212
+ print(f"No columns found for '{table_name}' or the table does not exist.")
213
+
214
+ def show_columns_all(self):
215
+ """Lists column details for all tables."""
216
+ self.cur.execute("SELECT table_name FROM information_schema.tables WHERE table_schema = 'public';")
217
+ for table in self.cur.fetchall():
218
+ self.show_columns(table[0])
219
+
220
+ def show_table(self, table_name):
221
+ """Displays all rows from the specified table (uses get_table_rows)."""
222
+ rows = self.get_table_rows(table_name)
223
+
224
+ if rows:
225
+ print(f"Rows in '{table_name}':")
226
+ for row in rows:
227
+ print(f" - {row}")
228
+ else:
229
+ print(f"Table '{table_name}' exists but has no data or an error occurred.")
230
+
231
+ def show_version(self):
232
+ """Show the current software version"""
233
+ print(self.version)
234
+
235
+ def select_table(self, table_name):
236
+ """Retrieves and prints all rows from the specified table."""
237
+ self.show_table(table_name)
238
+
83
239
  def insert_data(self, table_name, data_dict):
84
240
  """Inserts data into a dynamically created table."""
85
241
  columns = data_dict.keys()
@@ -114,111 +270,67 @@ class DynamicTables:
114
270
  self.cur.execute(query)
115
271
  self.conn.commit()
116
272
 
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."""
273
+ def delete_tables(self):
274
+ """Deletes all tables that match the current table prefix."""
138
275
  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()
276
+ query = """
277
+ SELECT table_name FROM information_schema.tables
278
+ WHERE table_schema = 'public' AND table_name LIKE %s;
279
+ """
280
+ prefix_pattern = self.table_prefix + "%" # Dynamic prefix
281
+ logging.debug(f"Prefix pattern: {prefix_pattern}")
147
282
 
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}")
283
+ self.cur.execute(query, (prefix_pattern,))
284
+ tables = self.cur.fetchall()
156
285
 
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])
286
+ if tables:
287
+ logging.info(f"Found {len(tables)} tables to delete.")
288
+ logging.debug(f"Tables to delete: {tables}")
162
289
 
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()
290
+ for table in tables:
291
+ table_name = table[0]
292
+ logging.info(f"Dropping table: {table_name}")
168
293
 
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.")
294
+ drop_query = sql.SQL("DROP TABLE IF EXISTS {} CASCADE;").format(sql.Identifier(table_name))
295
+ self.cur.execute(drop_query)
174
296
 
175
- return rows
297
+ self.conn.commit()
298
+ logging.info("Successfully deleted matching tables.")
299
+ else:
300
+ logging.info("No tables found matching the prefix.")
176
301
 
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,))
302
+ except Exception as e:
303
+ logging.error(f"Error deleting tables: {e}", exc_info=True)
186
304
 
187
- tables = self.cur.fetchall()
188
- print("DEBUG - Found tables to delete:", tables) # Debugging output
305
+ def status(self):
306
+ """Logs the currently configured column settings."""
307
+ if self.column_list:
308
+ logging.info("Configured Columns:")
309
+ for column in self.column_list:
310
+ logging.info(f" - {column}")
311
+ else:
312
+ logging.info("No columns configured.")
189
313
 
190
- for table in tables:
191
- table_name = table[0]
192
- print(f"Dropping table: {table_name}")
314
+ logging.info(f"Dynamic Column: {self.column_dynamic if self.column_dynamic else 'None'}")
193
315
 
194
- drop_query = sql.SQL("DROP TABLE IF EXISTS {} CASCADE;").format(sql.Identifier(table_name))
195
- self.cur.execute(drop_query)
316
+ def format_table_name(self, input_column):
317
+ """Formats an input column name to create a valid table name."""
318
+ cleaned = re.sub(r'[^a-zA-Z0-9_]', '', input_column)
319
+ return self.table_prefix + cleaned
196
320
 
197
- self.conn.commit()
321
+ def input(self, *args):
322
+ """Handles data insertion by dynamically determining the table name and inserting values."""
323
+ column_dict = {}
324
+ self.table_name_dynamic = ''
198
325
 
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]))
326
+ for column_number, (column_name, _) in enumerate(self.column_list):
327
+ if column_name == self.column_dynamic:
328
+ self.table_name_dynamic = self.format_table_name(args[column_number])
329
+ column_dict[column_name] = args[column_number]
207
330
  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
331
+ column_dict[column_name] = args[column_number]
217
332
 
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')
333
+ if self.table_name_dynamic:
334
+ self.create_table(self.table_name_dynamic, self.column_list)
224
335
 
336
+ self.insert_data(self.table_name_dynamic, column_dict)
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025
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.
22
+
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dynamic-tables
3
- Version: 0.1.1
3
+ Version: 0.2.0
4
4
  Summary: A dynamic table creation and management library for PostgreSQL
5
5
  Home-page: https://github.com/scottrodeo/dynamic-tables-python
6
6
  Author: Scott Rodeo
@@ -15,8 +15,11 @@ Description-Content-Type: text/markdown
15
15
  Requires-Dist: psycopg2
16
16
 
17
17
  # Dynamic Tables (Python)
18
+
18
19
  A Python library for dynamically creating and managing PostgreSQL tables based on input data.
19
20
 
21
+ ---
22
+
20
23
  ## 🚀 Features
21
24
  - Automatically creates tables based on incoming data.
22
25
  - Supports dynamic naming conventions.
@@ -26,16 +29,19 @@ A Python library for dynamically creating and managing PostgreSQL tables based o
26
29
  ---
27
30
 
28
31
  ## 📥 Installation
29
- ### **1️⃣ Install via GitHub (Recommended for Development)**
32
+
33
+ ### 1️⃣ Install via GitHub (Recommended for Development)
30
34
  Clone the repository and install in editable mode:
35
+
31
36
  ```bash
32
37
  git clone https://github.com/scottrodeo/dynamic-tables-python.git
33
38
  cd dynamic-tables-python
34
39
  pip install -e .
35
40
  ```
36
41
 
37
- ### **2️⃣ Install Directly via `pip`**
38
- The package is available on PyPI, you can install it with:
42
+ ### 2️⃣ Install Directly via `pip`
43
+ The package is available on PyPI, install it with:
44
+
39
45
  ```bash
40
46
  pip install dynamic-tables
41
47
  ```
@@ -43,22 +49,21 @@ pip install dynamic-tables
43
49
  ---
44
50
 
45
51
  ## 🏃‍♂️ Running the Example
46
- ### **1️⃣ Quick Run (Without Installation)**
47
- If you don't want to install the package, you can directly run the example script:
52
+
53
+ ### 1️⃣ Quick Run (Without Installation)
54
+ Run the example script directly:
55
+
48
56
  ```bash
49
57
  python3 examples/example.py
50
58
  ```
59
+
51
60
  💡 *This works because the script dynamically adjusts `sys.path`.*
52
61
 
53
- ### **2️⃣ Recommended (After Installation)**
54
- If you've installed the package (`pip install -e .`), simply run:
55
- ```bash
56
- python3 examples/example.py
57
- ```
58
62
 
59
63
  ---
60
64
 
61
65
  ## 📌 Example Usage
66
+
62
67
  Once installed, you can use `dynamic_tables` in your Python scripts:
63
68
 
64
69
  ```python
@@ -67,11 +72,12 @@ from dynamic_tables import DynamicTables
67
72
  # Initialize the dynamic table manager
68
73
  tables = DynamicTables()
69
74
 
70
- # Example: Creating and inserting data dynamically
75
+ # Configure dynamic table properties
71
76
  tables.set_table_prefix("dtbl_")
72
77
  tables.set_columns("domain TEXT, category TEXT, lang TEXT")
73
78
  tables.set_dynamic_column("domain")
74
79
 
80
+ # Insert dynamic data
75
81
  tables.input("wikipedia.org", "cats", "en")
76
82
  tables.input("wikipedia.org", "dogs", "en")
77
83
 
@@ -82,6 +88,7 @@ tables.show_tables()
82
88
  ---
83
89
 
84
90
  ## 🛠️ Available Functions
91
+
85
92
  | Function | Description |
86
93
  |----------|-------------|
87
94
  | `set_columns("name TYPE, age TYPE")` | Define table schema |
@@ -90,20 +97,39 @@ tables.show_tables()
90
97
  | `input(value1, value2, ...)` | Insert a new row dynamically |
91
98
  | `show_tables()` | List all dynamically created tables |
92
99
  | `show_columns("table_name")` | Show column details for a specific table |
100
+ | `show_columns_all()` | Show column details for all tables |
93
101
  | `select_table("table_name")` | Retrieve all rows from a table |
94
102
  | `delete_tables()` | Drop all tables matching the prefix |
103
+ | `create_table("table_name", [("column1", "TYPE"), ("column2", "TYPE")])` | Create a table dynamically |
104
+ | `insert_data("table_name", {"column1": value1, "column2": value2})` | Insert data into a specific table |
105
+ | `get_tables()` | Retrieve a list of all tables in the database |
106
+ | `get_columns("table_name")` | Retrieve all columns for a given table |
107
+ | `get_table_rows("table_name")` | Retrieve all rows from a table |
108
+ | `connectHC("dbname", "user", "password", "host")` | Connect using hardcoded credentials |
109
+ | `connectJSON("config.json")` | Connect using credentials from a JSON config file |
110
+ | `connectENVS()` | Connect using environment variables |
111
+ | `connection_open("dbname", "user", "password", "host")` | Open a PostgreSQL database connection |
112
+ | `connection_close()` | Close the database connection |
113
+ | `status()` | Show the current configuration status |
114
+ | `show_db()` | Display the connected database name |
115
+ | `setup_logging(level, log_to_file)` | Configure logging settings |
116
+ | `change_log_level("LEVEL")` | Change the logging level at runtime |
117
+ | `show_version()` | Display the current version of the library |
95
118
 
96
119
  ---
97
120
 
98
121
  ## ⚡ Development
99
- ### **Running Tests**
100
- To run the test suite:
122
+
123
+ ### Running Tests
124
+ Run the test suite:
125
+
101
126
  ```bash
102
127
  pytest tests/
103
128
  ```
104
129
 
105
- ### **Linting**
130
+ ### Linting
106
131
  Ensure your code follows best practices:
132
+
107
133
  ```bash
108
134
  flake8 dynamic_tables/
109
135
  ```
@@ -111,7 +137,8 @@ flake8 dynamic_tables/
111
137
  ---
112
138
 
113
139
  ## 🤝 Contributing
114
- Contributions are welcome! If you'd like to improve `dynamic_tables`, follow these steps:
140
+
141
+ Contributions are welcome! To contribute:
115
142
  1. Fork the repository.
116
143
  2. Create a new branch (`git checkout -b feature-branch`).
117
144
  3. Commit your changes (`git commit -m "Added new feature"`).
@@ -121,18 +148,19 @@ Contributions are welcome! If you'd like to improve `dynamic_tables`, follow the
121
148
  ---
122
149
 
123
150
  ## 📄 License
151
+
124
152
  This project is licensed under the **MIT License**. See the [LICENSE](LICENSE) file for details.
125
153
 
126
154
  ---
127
155
 
128
156
  ## 🌎 Links
157
+
129
158
  - **GitHub Repository:** [Dynamic Tables (Python)](https://github.com/scottrodeo/dynamic-tables-python)
130
- - **Documentation:** *(To be added)*
131
159
  - **Issue Tracker:** [Report Issues](https://github.com/scottrodeo/dynamic-tables-python/issues)
132
160
 
133
161
  ---
134
162
 
135
- ### **🚀 Happy Coding!**
163
+ ### 🚀 Happy Coding!
136
164
 
137
165
 
138
166
 
@@ -0,0 +1,7 @@
1
+ dynamic_tables/__init__.py,sha256=ttyVGUaIlALPqpfaHS3jX6CqLPoPfwvE-eFU_1ly4eY,59
2
+ dynamic_tables/dynamic_tables.py,sha256=ciBbvj6EVimTsBX-_BePHo-XjItxpH8KIB_OAAMMvCc,13018
3
+ dynamic_tables-0.2.0.dist-info/LICENSE,sha256=Vi3cItkblr6fZwGbNlp_HnBaMFwXSWYPkrVQLXX3LCs,1057
4
+ dynamic_tables-0.2.0.dist-info/METADATA,sha256=BNqat1kOqOcEjaW58r5A_Um6VC8l44s3-u3z6gVkIDk,4711
5
+ dynamic_tables-0.2.0.dist-info/WHEEL,sha256=g4nMs7d-Xl9-xC9XovUrsDHGXt-FT0E17Yqo92DEfvY,92
6
+ dynamic_tables-0.2.0.dist-info/top_level.txt,sha256=IUnttnbuTiMp4f8hXorriWorV8twRB535pDm0yI6c3w,15
7
+ dynamic_tables-0.2.0.dist-info/RECORD,,
File without changes
@@ -1,7 +0,0 @@
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.1.dist-info/LICENSE,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
- dynamic_tables-0.1.1.dist-info/METADATA,sha256=dIkcf9wNnr4A4xT9EpNVCH_NcUtriRsVnk-POy8hJIw,3818
5
- dynamic_tables-0.1.1.dist-info/WHEEL,sha256=g4nMs7d-Xl9-xC9XovUrsDHGXt-FT0E17Yqo92DEfvY,92
6
- dynamic_tables-0.1.1.dist-info/top_level.txt,sha256=IUnttnbuTiMp4f8hXorriWorV8twRB535pDm0yI6c3w,15
7
- dynamic_tables-0.1.1.dist-info/RECORD,,