databased.dbmanager
1import argshell 2from pathier import Pathier 3 4from databased import DataBased, dbparsers 5 6 7class DBManager(argshell.ArgShell): 8 intro = "Starting dbmanager (enter help or ? for arg info)..." 9 prompt = "based>" 10 dbpath: Pathier = None # type: ignore 11 12 def do_use_db(self, arg: str): 13 """Set which database file to use.""" 14 dbpath = Pathier(arg) 15 if not dbpath.exists(): 16 print(f"{dbpath} does not exist.") 17 print(f"Still using {self.dbpath}") 18 elif not dbpath.is_file(): 19 print(f"{dbpath} is not a file.") 20 print(f"Still using {self.dbpath}") 21 else: 22 self.dbpath = dbpath 23 24 def do_dbpath(self, arg: str): 25 """Print the .db file in use.""" 26 print(self.dbpath) 27 28 def do_backup(self, arg: str): 29 """Create a backup of the current db file.""" 30 print(f"Creating a back up for {self.dbpath}...") 31 backup_path = self.dbpath.with_stem(f"{self.dbpath.stem}_bckup") 32 self.dbpath.copy(backup_path, True) 33 print("Creating backup is complete.") 34 print(f"Backup path: {backup_path}") 35 36 def do_size(self, arg: str): 37 """Display the size of the the current db file.""" 38 print(f"{self.dbpath.name} is {self.dbpath.size(True)}.") 39 40 @argshell.with_parser(dbparsers.get_create_table_parser) 41 def do_create_table(self, args: argshell.Namespace): 42 """Create a table.""" 43 with DataBased(self.dbpath) as db: 44 db.create_table(args.table_name, args.columns) 45 46 def do_drop_table(self, arg: str): 47 """Drop the specified table.""" 48 with DataBased(self.dbpath) as db: 49 db.drop_table(arg) 50 51 @argshell.with_parser( 52 dbparsers.get_add_row_parser, [dbparsers.verify_matching_length] 53 ) 54 def do_add_row(self, args: argshell.Namespace): 55 """Add a row to a table.""" 56 with DataBased(self.dbpath) as db: 57 db.add_row(args.table_name, args.values, args.columns or None) 58 59 def do_info(self, arg: str): 60 """Print out the names of the database tables, their columns, and the number of rows. 61 Pass a space-separated list of table names to only print info for those specific tables, 62 otherwise all tables will be printed.""" 63 print("Getting database info...") 64 with DataBased(self.dbpath) as db: 65 tables = arg.split() or db.get_table_names() 66 info = [ 67 { 68 "Table Name": table, 69 "Columns": ", ".join(db.get_column_names(table)), 70 "Number of Rows": db.count(table), 71 } 72 for table in tables 73 ] 74 print(DataBased.data_to_string(info)) 75 76 @argshell.with_parser(dbparsers.get_lookup_parser, [dbparsers.convert_match_pairs]) 77 def do_find(self, args: argshell.Namespace): 78 """Find and print rows from the database. 79 Use the -t/--tables, -m/--match_pairs, and -l/--limit flags to limit the search. 80 Use the -c/--columns flag to limit what columns are printed. 81 Use the -o/--order_by flag to order the results. 82 Use the -p/--partial_matching flag to enable substring matching on -m/--match_pairs 83 Pass -h/--help flag for parser help.""" 84 print("Finding records... ") 85 if len(args.columns) == 0: 86 args.columns = None 87 with DataBased(self.dbpath) as db: 88 tables = args.tables or db.get_table_names() 89 for table in tables: 90 results = db.get_rows( 91 table, 92 args.match_pairs, 93 columns_to_return=args.columns, 94 order_by=args.order_by, 95 limit=args.limit, 96 exact_match=not args.partial_matching, 97 ) 98 db.close() 99 print(f"{len(results)} matching rows in {table} table:") 100 try: 101 print(DataBased.data_to_string(results)) # type: ignore 102 except Exception as e: 103 print("Couldn't fit data into a grid.") 104 print(*results, sep="\n") 105 print() 106 107 @argshell.with_parser(dbparsers.get_search_parser) 108 def do_search(self, args: argshell.Namespace): 109 """Search and return any rows containg the searched substring in any of its columns. 110 Use the -t/--tables flag to limit the search to a specific table(s). 111 Use the -c/--columns flag to limit the search to a specific column(s).""" 112 print(f"Searching for {args.search_string}...") 113 with DataBased(self.dbpath) as db: 114 tables = args.tables or db.get_table_names() 115 for table in tables: 116 columns = args.columns or db.get_column_names(table) 117 matcher = " OR ".join( 118 f'{column} LIKE "%{args.search_string}%"' for column in columns 119 ) 120 query = f"SELECT * FROM {table} WHERE {matcher};" 121 results = db.query(query) 122 results = [db._get_dict(table, result) for result in results] 123 print(f"Found {len(results)} results in {table} table.") 124 print(DataBased.data_to_string(results)) 125 126 @argshell.with_parser(dbparsers.get_lookup_parser, [dbparsers.convert_match_pairs]) 127 def do_count(self, args: argshell.Namespace): 128 """Print the number of rows in the database. 129 Use the -t/--tables flag to limit results to a specific table(s). 130 Use the -m/--match_pairs flag to limit the results to rows matching these criteria. 131 Use the -p/--partial_matching flag to enable substring matching on -m/--match_pairs. 132 Pass -h/--help flag for parser help.""" 133 print("Counting rows...") 134 with DataBased(self.dbpath) as db: 135 tables = args.tables or db.get_table_names() 136 for table in tables: 137 num_rows = db.count(table, args.match_pairs, not args.partial_matching) 138 print(f"{num_rows} matching rows in {table} table.") 139 140 def do_query(self, arg: str): 141 """Execute a query against the current database.""" 142 print(f"Executing {arg}") 143 with DataBased(self.dbpath) as db: 144 results = db.query(arg) 145 try: 146 for result in results: 147 print(*result, sep="|-|") 148 except Exception as e: 149 print(f"{type(e).__name__}: {e}") 150 151 @argshell.with_parser(dbparsers.get_update_parser, [dbparsers.convert_match_pairs]) 152 def do_update(self, args: argshell.Namespace): 153 """Update a column to a new value. 154 Two required args: the column (-c/--column) to update and the value (-v/--value) to update to. 155 Use the -t/--tables flag to limit what tables are updated. 156 Use the -m/--match_pairs flag to specify which rows are updated. 157 >>> based>update -c username -v big_chungus -t users -m username lil_chungus 158 159 ^will update the username in the users 'table' to 'big_chungus' where the username is currently 'lil_chungus'^""" 160 print("Updating rows...") 161 with DataBased(self.dbpath) as db: 162 tables = args.tables or db.get_table_names() 163 for table in tables: 164 if db.update(table, args.column, args.new_value, args.match_pairs): 165 print(f"Updating rows in {table} table successful.") 166 else: 167 print(f"Failed to update rows in {table} table.") 168 169 @argshell.with_parser(dbparsers.get_lookup_parser, [dbparsers.convert_match_pairs]) 170 def do_delete(self, args: argshell.Namespace): 171 """Delete rows from the database. 172 Use the -t/--tables flag to limit what tables rows are deleted from. 173 Use the -m/--match_pairs flag to specify which rows are deleted. 174 Use the -p/--partial_matching flag to enable substring matching on -m/--match_pairs. 175 >>> based>delete -t users -m username chungus -p 176 177 ^will delete all rows in the 'users' table whose username contains 'chungus'^""" 178 print("Deleting records...") 179 with DataBased(self.dbpath) as db: 180 tables = args.tables or db.get_table_names() 181 for table in tables: 182 num_rows = db.delete(table, args.match_pairs, not args.partial_matching) 183 print(f"Deleted {num_rows} rows from {table} table.") 184 185 def do_flush_log(self, arg: str): 186 """Clear the log file for this database.""" 187 log_path = self.dbpath.with_name(self.dbpath.stem + "db.log") 188 if not log_path.exists(): 189 print(f"No log file at path {log_path}") 190 else: 191 print(f"Flushing log...") 192 log_path.write_text("") 193 194 def do_customize(self, arg: str): 195 """Generate a template file in the current working directory for creating a custom DBManager class. 196 Expects one argument: the name of the custom dbmanager. 197 This will be used to name the generated file as well as several components in the file content.""" 198 custom_file = (Pathier.cwd() / arg.replace(" ", "_")).with_suffix(".py") 199 if custom_file.exists(): 200 print(f"Error: {custom_file.name} already exists in this location.") 201 else: 202 variable_name = "_".join(word for word in arg.lower().split()) 203 class_name = "".join(word.capitalize() for word in arg.split()) 204 content = (Pathier(__file__).parent / "custom_manager.py").read_text() 205 content = content.replace("CustomManager", class_name) 206 content = content.replace("custommanager", variable_name) 207 custom_file.write_text(content) 208 209 def _choose_db(self, options: list[Pathier]) -> Pathier: 210 """Prompt the user to select from a list of files.""" 211 cwd = Pathier.cwd() 212 paths = [path.separate(cwd.stem) for path in options] 213 while True: 214 print( 215 f"DB options:\n{' '.join([f'({i}) {path}' for i,path in enumerate(paths,1)])}" 216 ) 217 choice = input("Enter the number of the option to use: ") 218 try: 219 index = int(choice) 220 if not 1 <= index <= len(options): 221 print("Choice out of range.") 222 continue 223 return options[index - 1] 224 except Exception as e: 225 print(f"{choice} is not a valid option.") 226 227 def preloop(self): 228 """Scan the current directory for a .db file to use. 229 If not found, prompt the user for one or to try again recursively.""" 230 if self.dbpath: 231 self.dbpath = Pathier(self.dbpath) 232 print(f"Defaulting to database {self.dbpath}") 233 else: 234 print("Searching for database...") 235 cwd = Pathier.cwd() 236 dbs = list(cwd.glob("*.db")) 237 if len(dbs) == 1: 238 self.dbpath = dbs[0] 239 print(f"Using database {self.dbpath}.") 240 elif dbs: 241 self.dbpath = self._choose_db(dbs) 242 else: 243 print(f"Could not find a .db file in {cwd}.") 244 path = input( 245 "Enter path to .db file to use or press enter to search again recursively: " 246 ) 247 if path: 248 self.dbpath = Pathier(path) 249 elif not path: 250 print("Searching recursively...") 251 dbs = list(cwd.rglob("*.db")) 252 if len(dbs) == 1: 253 self.dbpath = dbs[0] 254 print(f"Using database {self.dbpath}.") 255 elif dbs: 256 self.dbpath = self._choose_db(dbs) 257 else: 258 print("Could not find a .db file.") 259 self.dbpath = Pathier(input("Enter path to a .db file: ")) 260 if not self.dbpath.exists(): 261 raise FileNotFoundError(f"{self.dbpath} does not exist.") 262 if not self.dbpath.is_file(): 263 raise ValueError(f"{self.dbpath} is not a file.") 264 265 266def main(): 267 DBManager().cmdloop()
8class DBManager(argshell.ArgShell): 9 intro = "Starting dbmanager (enter help or ? for arg info)..." 10 prompt = "based>" 11 dbpath: Pathier = None # type: ignore 12 13 def do_use_db(self, arg: str): 14 """Set which database file to use.""" 15 dbpath = Pathier(arg) 16 if not dbpath.exists(): 17 print(f"{dbpath} does not exist.") 18 print(f"Still using {self.dbpath}") 19 elif not dbpath.is_file(): 20 print(f"{dbpath} is not a file.") 21 print(f"Still using {self.dbpath}") 22 else: 23 self.dbpath = dbpath 24 25 def do_dbpath(self, arg: str): 26 """Print the .db file in use.""" 27 print(self.dbpath) 28 29 def do_backup(self, arg: str): 30 """Create a backup of the current db file.""" 31 print(f"Creating a back up for {self.dbpath}...") 32 backup_path = self.dbpath.with_stem(f"{self.dbpath.stem}_bckup") 33 self.dbpath.copy(backup_path, True) 34 print("Creating backup is complete.") 35 print(f"Backup path: {backup_path}") 36 37 def do_size(self, arg: str): 38 """Display the size of the the current db file.""" 39 print(f"{self.dbpath.name} is {self.dbpath.size(True)}.") 40 41 @argshell.with_parser(dbparsers.get_create_table_parser) 42 def do_create_table(self, args: argshell.Namespace): 43 """Create a table.""" 44 with DataBased(self.dbpath) as db: 45 db.create_table(args.table_name, args.columns) 46 47 def do_drop_table(self, arg: str): 48 """Drop the specified table.""" 49 with DataBased(self.dbpath) as db: 50 db.drop_table(arg) 51 52 @argshell.with_parser( 53 dbparsers.get_add_row_parser, [dbparsers.verify_matching_length] 54 ) 55 def do_add_row(self, args: argshell.Namespace): 56 """Add a row to a table.""" 57 with DataBased(self.dbpath) as db: 58 db.add_row(args.table_name, args.values, args.columns or None) 59 60 def do_info(self, arg: str): 61 """Print out the names of the database tables, their columns, and the number of rows. 62 Pass a space-separated list of table names to only print info for those specific tables, 63 otherwise all tables will be printed.""" 64 print("Getting database info...") 65 with DataBased(self.dbpath) as db: 66 tables = arg.split() or db.get_table_names() 67 info = [ 68 { 69 "Table Name": table, 70 "Columns": ", ".join(db.get_column_names(table)), 71 "Number of Rows": db.count(table), 72 } 73 for table in tables 74 ] 75 print(DataBased.data_to_string(info)) 76 77 @argshell.with_parser(dbparsers.get_lookup_parser, [dbparsers.convert_match_pairs]) 78 def do_find(self, args: argshell.Namespace): 79 """Find and print rows from the database. 80 Use the -t/--tables, -m/--match_pairs, and -l/--limit flags to limit the search. 81 Use the -c/--columns flag to limit what columns are printed. 82 Use the -o/--order_by flag to order the results. 83 Use the -p/--partial_matching flag to enable substring matching on -m/--match_pairs 84 Pass -h/--help flag for parser help.""" 85 print("Finding records... ") 86 if len(args.columns) == 0: 87 args.columns = None 88 with DataBased(self.dbpath) as db: 89 tables = args.tables or db.get_table_names() 90 for table in tables: 91 results = db.get_rows( 92 table, 93 args.match_pairs, 94 columns_to_return=args.columns, 95 order_by=args.order_by, 96 limit=args.limit, 97 exact_match=not args.partial_matching, 98 ) 99 db.close() 100 print(f"{len(results)} matching rows in {table} table:") 101 try: 102 print(DataBased.data_to_string(results)) # type: ignore 103 except Exception as e: 104 print("Couldn't fit data into a grid.") 105 print(*results, sep="\n") 106 print() 107 108 @argshell.with_parser(dbparsers.get_search_parser) 109 def do_search(self, args: argshell.Namespace): 110 """Search and return any rows containg the searched substring in any of its columns. 111 Use the -t/--tables flag to limit the search to a specific table(s). 112 Use the -c/--columns flag to limit the search to a specific column(s).""" 113 print(f"Searching for {args.search_string}...") 114 with DataBased(self.dbpath) as db: 115 tables = args.tables or db.get_table_names() 116 for table in tables: 117 columns = args.columns or db.get_column_names(table) 118 matcher = " OR ".join( 119 f'{column} LIKE "%{args.search_string}%"' for column in columns 120 ) 121 query = f"SELECT * FROM {table} WHERE {matcher};" 122 results = db.query(query) 123 results = [db._get_dict(table, result) for result in results] 124 print(f"Found {len(results)} results in {table} table.") 125 print(DataBased.data_to_string(results)) 126 127 @argshell.with_parser(dbparsers.get_lookup_parser, [dbparsers.convert_match_pairs]) 128 def do_count(self, args: argshell.Namespace): 129 """Print the number of rows in the database. 130 Use the -t/--tables flag to limit results to a specific table(s). 131 Use the -m/--match_pairs flag to limit the results to rows matching these criteria. 132 Use the -p/--partial_matching flag to enable substring matching on -m/--match_pairs. 133 Pass -h/--help flag for parser help.""" 134 print("Counting rows...") 135 with DataBased(self.dbpath) as db: 136 tables = args.tables or db.get_table_names() 137 for table in tables: 138 num_rows = db.count(table, args.match_pairs, not args.partial_matching) 139 print(f"{num_rows} matching rows in {table} table.") 140 141 def do_query(self, arg: str): 142 """Execute a query against the current database.""" 143 print(f"Executing {arg}") 144 with DataBased(self.dbpath) as db: 145 results = db.query(arg) 146 try: 147 for result in results: 148 print(*result, sep="|-|") 149 except Exception as e: 150 print(f"{type(e).__name__}: {e}") 151 152 @argshell.with_parser(dbparsers.get_update_parser, [dbparsers.convert_match_pairs]) 153 def do_update(self, args: argshell.Namespace): 154 """Update a column to a new value. 155 Two required args: the column (-c/--column) to update and the value (-v/--value) to update to. 156 Use the -t/--tables flag to limit what tables are updated. 157 Use the -m/--match_pairs flag to specify which rows are updated. 158 >>> based>update -c username -v big_chungus -t users -m username lil_chungus 159 160 ^will update the username in the users 'table' to 'big_chungus' where the username is currently 'lil_chungus'^""" 161 print("Updating rows...") 162 with DataBased(self.dbpath) as db: 163 tables = args.tables or db.get_table_names() 164 for table in tables: 165 if db.update(table, args.column, args.new_value, args.match_pairs): 166 print(f"Updating rows in {table} table successful.") 167 else: 168 print(f"Failed to update rows in {table} table.") 169 170 @argshell.with_parser(dbparsers.get_lookup_parser, [dbparsers.convert_match_pairs]) 171 def do_delete(self, args: argshell.Namespace): 172 """Delete rows from the database. 173 Use the -t/--tables flag to limit what tables rows are deleted from. 174 Use the -m/--match_pairs flag to specify which rows are deleted. 175 Use the -p/--partial_matching flag to enable substring matching on -m/--match_pairs. 176 >>> based>delete -t users -m username chungus -p 177 178 ^will delete all rows in the 'users' table whose username contains 'chungus'^""" 179 print("Deleting records...") 180 with DataBased(self.dbpath) as db: 181 tables = args.tables or db.get_table_names() 182 for table in tables: 183 num_rows = db.delete(table, args.match_pairs, not args.partial_matching) 184 print(f"Deleted {num_rows} rows from {table} table.") 185 186 def do_flush_log(self, arg: str): 187 """Clear the log file for this database.""" 188 log_path = self.dbpath.with_name(self.dbpath.stem + "db.log") 189 if not log_path.exists(): 190 print(f"No log file at path {log_path}") 191 else: 192 print(f"Flushing log...") 193 log_path.write_text("") 194 195 def do_customize(self, arg: str): 196 """Generate a template file in the current working directory for creating a custom DBManager class. 197 Expects one argument: the name of the custom dbmanager. 198 This will be used to name the generated file as well as several components in the file content.""" 199 custom_file = (Pathier.cwd() / arg.replace(" ", "_")).with_suffix(".py") 200 if custom_file.exists(): 201 print(f"Error: {custom_file.name} already exists in this location.") 202 else: 203 variable_name = "_".join(word for word in arg.lower().split()) 204 class_name = "".join(word.capitalize() for word in arg.split()) 205 content = (Pathier(__file__).parent / "custom_manager.py").read_text() 206 content = content.replace("CustomManager", class_name) 207 content = content.replace("custommanager", variable_name) 208 custom_file.write_text(content) 209 210 def _choose_db(self, options: list[Pathier]) -> Pathier: 211 """Prompt the user to select from a list of files.""" 212 cwd = Pathier.cwd() 213 paths = [path.separate(cwd.stem) for path in options] 214 while True: 215 print( 216 f"DB options:\n{' '.join([f'({i}) {path}' for i,path in enumerate(paths,1)])}" 217 ) 218 choice = input("Enter the number of the option to use: ") 219 try: 220 index = int(choice) 221 if not 1 <= index <= len(options): 222 print("Choice out of range.") 223 continue 224 return options[index - 1] 225 except Exception as e: 226 print(f"{choice} is not a valid option.") 227 228 def preloop(self): 229 """Scan the current directory for a .db file to use. 230 If not found, prompt the user for one or to try again recursively.""" 231 if self.dbpath: 232 self.dbpath = Pathier(self.dbpath) 233 print(f"Defaulting to database {self.dbpath}") 234 else: 235 print("Searching for database...") 236 cwd = Pathier.cwd() 237 dbs = list(cwd.glob("*.db")) 238 if len(dbs) == 1: 239 self.dbpath = dbs[0] 240 print(f"Using database {self.dbpath}.") 241 elif dbs: 242 self.dbpath = self._choose_db(dbs) 243 else: 244 print(f"Could not find a .db file in {cwd}.") 245 path = input( 246 "Enter path to .db file to use or press enter to search again recursively: " 247 ) 248 if path: 249 self.dbpath = Pathier(path) 250 elif not path: 251 print("Searching recursively...") 252 dbs = list(cwd.rglob("*.db")) 253 if len(dbs) == 1: 254 self.dbpath = dbs[0] 255 print(f"Using database {self.dbpath}.") 256 elif dbs: 257 self.dbpath = self._choose_db(dbs) 258 else: 259 print("Could not find a .db file.") 260 self.dbpath = Pathier(input("Enter path to a .db file: ")) 261 if not self.dbpath.exists(): 262 raise FileNotFoundError(f"{self.dbpath} does not exist.") 263 if not self.dbpath.is_file(): 264 raise ValueError(f"{self.dbpath} is not a file.")
Subclass this to create custom ArgShells.
13 def do_use_db(self, arg: str): 14 """Set which database file to use.""" 15 dbpath = Pathier(arg) 16 if not dbpath.exists(): 17 print(f"{dbpath} does not exist.") 18 print(f"Still using {self.dbpath}") 19 elif not dbpath.is_file(): 20 print(f"{dbpath} is not a file.") 21 print(f"Still using {self.dbpath}") 22 else: 23 self.dbpath = dbpath
Set which database file to use.
29 def do_backup(self, arg: str): 30 """Create a backup of the current db file.""" 31 print(f"Creating a back up for {self.dbpath}...") 32 backup_path = self.dbpath.with_stem(f"{self.dbpath.stem}_bckup") 33 self.dbpath.copy(backup_path, True) 34 print("Creating backup is complete.") 35 print(f"Backup path: {backup_path}")
Create a backup of the current db file.
37 def do_size(self, arg: str): 38 """Display the size of the the current db file.""" 39 print(f"{self.dbpath.name} is {self.dbpath.size(True)}.")
Display the size of the the current db file.
41 @argshell.with_parser(dbparsers.get_create_table_parser) 42 def do_create_table(self, args: argshell.Namespace): 43 """Create a table.""" 44 with DataBased(self.dbpath) as db: 45 db.create_table(args.table_name, args.columns)
Create a table.
47 def do_drop_table(self, arg: str): 48 """Drop the specified table.""" 49 with DataBased(self.dbpath) as db: 50 db.drop_table(arg)
Drop the specified table.
52 @argshell.with_parser( 53 dbparsers.get_add_row_parser, [dbparsers.verify_matching_length] 54 ) 55 def do_add_row(self, args: argshell.Namespace): 56 """Add a row to a table.""" 57 with DataBased(self.dbpath) as db: 58 db.add_row(args.table_name, args.values, args.columns or None)
Add a row to a table.
60 def do_info(self, arg: str): 61 """Print out the names of the database tables, their columns, and the number of rows. 62 Pass a space-separated list of table names to only print info for those specific tables, 63 otherwise all tables will be printed.""" 64 print("Getting database info...") 65 with DataBased(self.dbpath) as db: 66 tables = arg.split() or db.get_table_names() 67 info = [ 68 { 69 "Table Name": table, 70 "Columns": ", ".join(db.get_column_names(table)), 71 "Number of Rows": db.count(table), 72 } 73 for table in tables 74 ] 75 print(DataBased.data_to_string(info))
Print out the names of the database tables, their columns, and the number of rows. Pass a space-separated list of table names to only print info for those specific tables, otherwise all tables will be printed.
77 @argshell.with_parser(dbparsers.get_lookup_parser, [dbparsers.convert_match_pairs]) 78 def do_find(self, args: argshell.Namespace): 79 """Find and print rows from the database. 80 Use the -t/--tables, -m/--match_pairs, and -l/--limit flags to limit the search. 81 Use the -c/--columns flag to limit what columns are printed. 82 Use the -o/--order_by flag to order the results. 83 Use the -p/--partial_matching flag to enable substring matching on -m/--match_pairs 84 Pass -h/--help flag for parser help.""" 85 print("Finding records... ") 86 if len(args.columns) == 0: 87 args.columns = None 88 with DataBased(self.dbpath) as db: 89 tables = args.tables or db.get_table_names() 90 for table in tables: 91 results = db.get_rows( 92 table, 93 args.match_pairs, 94 columns_to_return=args.columns, 95 order_by=args.order_by, 96 limit=args.limit, 97 exact_match=not args.partial_matching, 98 ) 99 db.close() 100 print(f"{len(results)} matching rows in {table} table:") 101 try: 102 print(DataBased.data_to_string(results)) # type: ignore 103 except Exception as e: 104 print("Couldn't fit data into a grid.") 105 print(*results, sep="\n") 106 print()
Find and print rows from the database. Use the -t/--tables, -m/--match_pairs, and -l/--limit flags to limit the search. Use the -c/--columns flag to limit what columns are printed. Use the -o/--order_by flag to order the results. Use the -p/--partial_matching flag to enable substring matching on -m/--match_pairs Pass -h/--help flag for parser help.
108 @argshell.with_parser(dbparsers.get_search_parser) 109 def do_search(self, args: argshell.Namespace): 110 """Search and return any rows containg the searched substring in any of its columns. 111 Use the -t/--tables flag to limit the search to a specific table(s). 112 Use the -c/--columns flag to limit the search to a specific column(s).""" 113 print(f"Searching for {args.search_string}...") 114 with DataBased(self.dbpath) as db: 115 tables = args.tables or db.get_table_names() 116 for table in tables: 117 columns = args.columns or db.get_column_names(table) 118 matcher = " OR ".join( 119 f'{column} LIKE "%{args.search_string}%"' for column in columns 120 ) 121 query = f"SELECT * FROM {table} WHERE {matcher};" 122 results = db.query(query) 123 results = [db._get_dict(table, result) for result in results] 124 print(f"Found {len(results)} results in {table} table.") 125 print(DataBased.data_to_string(results))
Search and return any rows containg the searched substring in any of its columns. Use the -t/--tables flag to limit the search to a specific table(s). Use the -c/--columns flag to limit the search to a specific column(s).
127 @argshell.with_parser(dbparsers.get_lookup_parser, [dbparsers.convert_match_pairs]) 128 def do_count(self, args: argshell.Namespace): 129 """Print the number of rows in the database. 130 Use the -t/--tables flag to limit results to a specific table(s). 131 Use the -m/--match_pairs flag to limit the results to rows matching these criteria. 132 Use the -p/--partial_matching flag to enable substring matching on -m/--match_pairs. 133 Pass -h/--help flag for parser help.""" 134 print("Counting rows...") 135 with DataBased(self.dbpath) as db: 136 tables = args.tables or db.get_table_names() 137 for table in tables: 138 num_rows = db.count(table, args.match_pairs, not args.partial_matching) 139 print(f"{num_rows} matching rows in {table} table.")
Print the number of rows in the database. Use the -t/--tables flag to limit results to a specific table(s). Use the -m/--match_pairs flag to limit the results to rows matching these criteria. Use the -p/--partial_matching flag to enable substring matching on -m/--match_pairs. Pass -h/--help flag for parser help.
141 def do_query(self, arg: str): 142 """Execute a query against the current database.""" 143 print(f"Executing {arg}") 144 with DataBased(self.dbpath) as db: 145 results = db.query(arg) 146 try: 147 for result in results: 148 print(*result, sep="|-|") 149 except Exception as e: 150 print(f"{type(e).__name__}: {e}")
Execute a query against the current database.
152 @argshell.with_parser(dbparsers.get_update_parser, [dbparsers.convert_match_pairs]) 153 def do_update(self, args: argshell.Namespace): 154 """Update a column to a new value. 155 Two required args: the column (-c/--column) to update and the value (-v/--value) to update to. 156 Use the -t/--tables flag to limit what tables are updated. 157 Use the -m/--match_pairs flag to specify which rows are updated. 158 >>> based>update -c username -v big_chungus -t users -m username lil_chungus 159 160 ^will update the username in the users 'table' to 'big_chungus' where the username is currently 'lil_chungus'^""" 161 print("Updating rows...") 162 with DataBased(self.dbpath) as db: 163 tables = args.tables or db.get_table_names() 164 for table in tables: 165 if db.update(table, args.column, args.new_value, args.match_pairs): 166 print(f"Updating rows in {table} table successful.") 167 else: 168 print(f"Failed to update rows in {table} table.")
Update a column to a new value. Two required args: the column (-c/--column) to update and the value (-v/--value) to update to. Use the -t/--tables flag to limit what tables are updated. Use the -m/--match_pairs flag to specify which rows are updated.
>>> based>update -c username -v big_chungus -t users -m username lil_chungus
^will update the username in the users 'table' to 'big_chungus' where the username is currently 'lil_chungus'^
170 @argshell.with_parser(dbparsers.get_lookup_parser, [dbparsers.convert_match_pairs]) 171 def do_delete(self, args: argshell.Namespace): 172 """Delete rows from the database. 173 Use the -t/--tables flag to limit what tables rows are deleted from. 174 Use the -m/--match_pairs flag to specify which rows are deleted. 175 Use the -p/--partial_matching flag to enable substring matching on -m/--match_pairs. 176 >>> based>delete -t users -m username chungus -p 177 178 ^will delete all rows in the 'users' table whose username contains 'chungus'^""" 179 print("Deleting records...") 180 with DataBased(self.dbpath) as db: 181 tables = args.tables or db.get_table_names() 182 for table in tables: 183 num_rows = db.delete(table, args.match_pairs, not args.partial_matching) 184 print(f"Deleted {num_rows} rows from {table} table.")
Delete rows from the database. Use the -t/--tables flag to limit what tables rows are deleted from. Use the -m/--match_pairs flag to specify which rows are deleted. Use the -p/--partial_matching flag to enable substring matching on -m/--match_pairs.
>>> based>delete -t users -m username chungus -p
^will delete all rows in the 'users' table whose username contains 'chungus'^
186 def do_flush_log(self, arg: str): 187 """Clear the log file for this database.""" 188 log_path = self.dbpath.with_name(self.dbpath.stem + "db.log") 189 if not log_path.exists(): 190 print(f"No log file at path {log_path}") 191 else: 192 print(f"Flushing log...") 193 log_path.write_text("")
Clear the log file for this database.
195 def do_customize(self, arg: str): 196 """Generate a template file in the current working directory for creating a custom DBManager class. 197 Expects one argument: the name of the custom dbmanager. 198 This will be used to name the generated file as well as several components in the file content.""" 199 custom_file = (Pathier.cwd() / arg.replace(" ", "_")).with_suffix(".py") 200 if custom_file.exists(): 201 print(f"Error: {custom_file.name} already exists in this location.") 202 else: 203 variable_name = "_".join(word for word in arg.lower().split()) 204 class_name = "".join(word.capitalize() for word in arg.split()) 205 content = (Pathier(__file__).parent / "custom_manager.py").read_text() 206 content = content.replace("CustomManager", class_name) 207 content = content.replace("custommanager", variable_name) 208 custom_file.write_text(content)
Generate a template file in the current working directory for creating a custom DBManager class. Expects one argument: the name of the custom dbmanager. This will be used to name the generated file as well as several components in the file content.
228 def preloop(self): 229 """Scan the current directory for a .db file to use. 230 If not found, prompt the user for one or to try again recursively.""" 231 if self.dbpath: 232 self.dbpath = Pathier(self.dbpath) 233 print(f"Defaulting to database {self.dbpath}") 234 else: 235 print("Searching for database...") 236 cwd = Pathier.cwd() 237 dbs = list(cwd.glob("*.db")) 238 if len(dbs) == 1: 239 self.dbpath = dbs[0] 240 print(f"Using database {self.dbpath}.") 241 elif dbs: 242 self.dbpath = self._choose_db(dbs) 243 else: 244 print(f"Could not find a .db file in {cwd}.") 245 path = input( 246 "Enter path to .db file to use or press enter to search again recursively: " 247 ) 248 if path: 249 self.dbpath = Pathier(path) 250 elif not path: 251 print("Searching recursively...") 252 dbs = list(cwd.rglob("*.db")) 253 if len(dbs) == 1: 254 self.dbpath = dbs[0] 255 print(f"Using database {self.dbpath}.") 256 elif dbs: 257 self.dbpath = self._choose_db(dbs) 258 else: 259 print("Could not find a .db file.") 260 self.dbpath = Pathier(input("Enter path to a .db file: ")) 261 if not self.dbpath.exists(): 262 raise FileNotFoundError(f"{self.dbpath} does not exist.") 263 if not self.dbpath.is_file(): 264 raise ValueError(f"{self.dbpath} is not a file.")
Scan the current directory for a .db file to use. If not found, prompt the user for one or to try again recursively.
Inherited Members
- cmd.Cmd
- Cmd
- precmd
- postcmd
- postloop
- parseline
- onecmd
- emptyline
- default
- completedefault
- completenames
- complete
- get_names
- complete_help
- print_topics
- columnize
- argshell.argshell.ArgShell
- do_quit
- do_help
- cmdloop