kaggle-prep 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.
@@ -0,0 +1,18 @@
1
+ __version__="0.1.0"
2
+ __author__="Sumit Gavali"
3
+ __email__="Sumitrg0007@gmail.com"
4
+
5
+ from .cli import main
6
+ from .profiler import DataProfiler
7
+ from .report import generate_standalone_report
8
+ from .visualizer import generate_eda_plots
9
+ from .notebook import generate_notebook
10
+
11
+ __version__ = "0.1.0"
12
+ __all__ = [
13
+ 'main',
14
+ 'DataProfiler',
15
+ 'generate_standalone_report',
16
+ 'generate_eda_plots',
17
+ 'generate_notebook'
18
+ ]
kaggle_prep/cli.py ADDED
@@ -0,0 +1,451 @@
1
+ import argparse
2
+ from pathlib import Path
3
+ import os
4
+ import profile
5
+ import time
6
+ import pandas as pd
7
+ from kaggle.api.kaggle_api_extended import KaggleApi
8
+ from .notebook import generate_notebook
9
+ from .visualizer import generate_eda_plots, generate_preprocessing_code
10
+
11
+ # Import our custom profiler and report generator
12
+ from .profiler import DataProfiler, save_profile_json, print_profile_summary
13
+ from .report import generate_standalone_report
14
+
15
+
16
+ # ============================================================
17
+ # FUNCTION 1: Parse Arguments
18
+ # ============================================================
19
+ def parse_arguments():
20
+ """Parse and return command line arguments"""
21
+ parser = argparse.ArgumentParser(
22
+ description="Prepare Kaggle datasets for data analysis."
23
+ )
24
+
25
+ parser.add_argument(
26
+ "dataset",
27
+ help="Kaggle dataset identifier, e.g. adyen/dabstep-benchmark"
28
+ )
29
+
30
+ parser.add_argument(
31
+ "--output-dir",
32
+ default="data",
33
+ help="Directory to save downloaded data (default: data)"
34
+ )
35
+ parser.add_argument(
36
+ "--notebook",
37
+ action="store_true",
38
+ help="Generate a Jupyter notebook with EDA and preprocessing code"
39
+ )
40
+ parser.add_argument(
41
+ "--verbose",
42
+ action="store_true",
43
+ help="Enable verbose output for debugging"
44
+ )
45
+ parser.add_argument(
46
+ "--local",
47
+ action="store_true",
48
+ help="Use existing data - works with --profile, --report, etc"
49
+ )
50
+
51
+
52
+ parser.add_argument(
53
+ "--competition",
54
+ action="store_true",
55
+ help="Download from competition instead of dataset"
56
+ )
57
+
58
+ parser.add_argument(
59
+ "--profile",
60
+ action="store_true",
61
+ help="Generate data profile after download"
62
+ )
63
+
64
+ parser.add_argument(
65
+ "--report",
66
+ action="store_true",
67
+ help="Generate a standalone HTML report after download"
68
+ )
69
+ parser.add_argument(
70
+ "--visualize",
71
+ action="store_true",
72
+ help="generate EDA visualization (plots)"
73
+ )
74
+ parser.add_argument(
75
+ "--preprocess",
76
+ action="store_true",
77
+ help="generate preprocessing code"
78
+ )
79
+
80
+ return parser.parse_args()
81
+
82
+
83
+ # ============================================================
84
+ # FUNCTION 2: Display Initial Info
85
+ # ============================================================
86
+ def display_startup_info(args):
87
+ """Display startup information"""
88
+ if args.verbose:
89
+ print(f"šŸ“¦ Dataset: {args.dataset}")
90
+ print(f"šŸ“ Output directory: {args.output_dir}")
91
+ if args.competition:
92
+ print(f"šŸ† Competition mode enabled")
93
+
94
+
95
+ # ============================================================
96
+ # FUNCTION 3: Check Credentials
97
+ # ============================================================
98
+ def check_credentials(verbose=False):
99
+ """Check if Kaggle credentials exist"""
100
+ kaggle_dir = Path.home() / ".kaggle"
101
+ kaggle_json = kaggle_dir / "kaggle.json"
102
+
103
+ if not kaggle_json.exists():
104
+ print(f"āŒ Kaggle credentials not found at: {kaggle_json}")
105
+ print("\nšŸ“ To get credentials:")
106
+ print(" 1. Go to https://www.kaggle.com/settings/api")
107
+ print(" 2. Click 'Create New Token'")
108
+ print(f" 3. Save kaggle.json to: {kaggle_dir}")
109
+ return None
110
+
111
+ if verbose:
112
+ print(f"āœ… Found Kaggle credentials at: {kaggle_json}")
113
+
114
+ return kaggle_dir
115
+
116
+
117
+ # ============================================================
118
+ # FUNCTION 4: Authenticate
119
+ # ============================================================
120
+ def authenticate_kaggle(kaggle_dir, verbose=False):
121
+ """Authenticate with Kaggle API"""
122
+ print(f"šŸ”„ Authenticating with Kaggle API...")
123
+
124
+ try:
125
+ os.environ['KAGGLE_CONFIG_DIR'] = str(kaggle_dir)
126
+ api = KaggleApi()
127
+ api.authenticate()
128
+
129
+ if verbose:
130
+ print(f"āœ… Authentication successful!")
131
+
132
+ return api
133
+
134
+ except Exception as e:
135
+ print(f"āŒ Authentication error: {e}")
136
+ print("\nšŸ” Troubleshooting:")
137
+ print(" 1. Make sure kaggle.json is not empty")
138
+ print(" 2. Try re-downloading from Kaggle settings")
139
+ print(" 3. Check your internet connection")
140
+ return None
141
+
142
+
143
+ # ============================================================
144
+ # FUNCTION 5: Create Output Directory
145
+ # ============================================================
146
+ def create_output_directory(output_dir):
147
+ """Create output directory if it doesn't exist"""
148
+ output_path = Path(output_dir)
149
+ output_path.mkdir(parents=True, exist_ok=True)
150
+ return output_path
151
+
152
+
153
+ # ============================================================
154
+ # FUNCTION 6: Format File Size
155
+ # ============================================================
156
+ def format_file_size(size_bytes):
157
+ """Convert bytes to human-readable format"""
158
+ if size_bytes > 1024 * 1024 * 1024: # GB
159
+ return f"{size_bytes/(1024*1024*1024):.2f} GB"
160
+ elif size_bytes > 1024 * 1024: # MB
161
+ return f"{size_bytes/(1024*1024):.1f} MB"
162
+ elif size_bytes > 1024: # KB
163
+ return f"{size_bytes/1024:.1f} KB"
164
+ else:
165
+ return f"{size_bytes} bytes"
166
+
167
+
168
+ # ============================================================
169
+ # FUNCTION 7: List Files
170
+ # ============================================================
171
+ def list_downloaded_files(output_path):
172
+ """List all downloaded files with sizes"""
173
+ print("\nšŸ“„ Downloaded files:")
174
+
175
+ files = list(output_path.rglob("*"))
176
+ if not files:
177
+ print(" No files found!")
178
+ return
179
+
180
+ for file in files:
181
+ if file.is_file():
182
+ size = file.stat().st_size
183
+ size_str = format_file_size(size)
184
+ print(f" - {file.name} ({size_str})")
185
+
186
+
187
+ # ============================================================
188
+ # FUNCTION 8: Load First CSV
189
+ # ============================================================
190
+ def load_first_csv(data_path):
191
+ """Load the first CSV file found in the directory"""
192
+ csv_files = list(Path(data_path).rglob("*.csv"))
193
+
194
+ if not csv_files:
195
+ print("āŒ No CSV files found to profile!")
196
+ return None
197
+
198
+ try:
199
+ df = pd.read_csv(csv_files[0])
200
+ print(f"šŸ“„ Loaded: {csv_files[0].name} ({len(df):,} rows, {len(df.columns)} columns)")
201
+ return df
202
+ except Exception as e:
203
+ print(f"āŒ Error loading CSV: {e}")
204
+ return None
205
+
206
+
207
+ # ============================================================
208
+ # FUNCTION 9: Download Dataset
209
+ # ============================================================
210
+ def download_dataset(api, dataset_name, output_path, verbose=False):
211
+ """Download a dataset from Kaggle"""
212
+ print(f"ā¬‡ļø Downloading {dataset_name}...")
213
+
214
+ try:
215
+ api.dataset_download_files(
216
+ dataset_name,
217
+ path=str(output_path),
218
+ unzip=True
219
+ )
220
+
221
+ print(f"āœ… Download complete! Files saved to: {output_path}")
222
+
223
+ if verbose:
224
+ list_downloaded_files(output_path)
225
+
226
+ return True
227
+
228
+ except Exception as e:
229
+ handle_download_error(e, dataset_name)
230
+ return False
231
+
232
+
233
+ # ============================================================
234
+ # FUNCTION 10: Download Competition
235
+ # ============================================================
236
+ def download_competition(api, competition_name, output_path, verbose=False):
237
+ """Download competition files from Kaggle"""
238
+ print(f"ā¬‡ļø Downloading competition: {competition_name}...")
239
+
240
+ try:
241
+ api.competition_download_files(
242
+ competition_name,
243
+ path=str(output_path)
244
+ )
245
+
246
+ print(f"āœ… Download complete! Files saved to: {output_path}")
247
+
248
+ if verbose:
249
+ list_downloaded_files(output_path)
250
+
251
+ return True
252
+
253
+ except Exception as e:
254
+ handle_download_error(e, competition_name)
255
+ return False
256
+
257
+
258
+ # ============================================================
259
+ # FUNCTION 11: Handle Download Errors
260
+ # ============================================================
261
+ def handle_download_error(error, dataset_name):
262
+ """Handle and explain download errors"""
263
+ print(f"āŒ Error downloading: {error}")
264
+ error_msg = str(error)
265
+
266
+ if "403" in error_msg:
267
+ print("\nšŸ” Troubleshooting 403 Forbidden error:")
268
+ print("This usually means the dataset requires accepting terms or is restricted.")
269
+ print(f"1. Visit: https://www.kaggle.com/datasets/{dataset_name}")
270
+ print("2. Click 'Download' and accept any terms")
271
+ print("3. Wait a moment, then try again")
272
+ print("\nšŸ“ Or try --competition flag if it's a competition:")
273
+ print(f" python kaggle_prep/cli.py {dataset_name} --competition --verbose")
274
+
275
+ elif "404" in error_msg:
276
+ print(f"\nšŸ” Dataset '{dataset_name}' not found!")
277
+ print("Check the spelling or try searching on Kaggle")
278
+ print("\nšŸ“ Examples:")
279
+ print(" python kaggle_prep/cli.py uciml/iris --verbose")
280
+ print(" python kaggle_prep/cli.py debayank2024/netflix-movies-and-series --verbose")
281
+
282
+ elif "429" in error_msg:
283
+ print("\nāš ļø Rate limit reached!")
284
+ print("Kaggle limits how many requests you can make.")
285
+ print("Wait 1 hour and try again.")
286
+
287
+ else:
288
+ print(f"\nšŸ” Unexpected error: {error}")
289
+
290
+
291
+ # ============================================================
292
+ # FUNCTION 12: Profile Downloaded Data
293
+ # ============================================================
294
+ def profile_downloaded_data(output_path, dataset_name, generate_report=False):
295
+ """Run profiling on downloaded data"""
296
+
297
+ print("\nšŸ“Š Generating data profile...")
298
+
299
+ # Load the data
300
+ df = load_first_csv(Path(output_path))
301
+ if df is None:
302
+ return
303
+
304
+ # Create profile using our custom profiler
305
+ profiler = DataProfiler(df, dataset_name)
306
+ profile = profiler.profile()
307
+
308
+ # Save JSON profile
309
+ json_path = save_profile_json(profile)
310
+
311
+ # Print summary to console
312
+ print_profile_summary(profile)
313
+
314
+ # Generate HTML report if requested
315
+ if generate_report:
316
+ generate_standalone_report(profile)
317
+
318
+ def generate_starter_notebook(output_path, dataset_name, df=None, profile=None):
319
+ print("Generating starter notebook...")
320
+ if df is None:
321
+ df=load_first_csv(output_path)
322
+
323
+ notebook_path = generate_notebook(
324
+ dataset_name=dataset_name,
325
+ df=df,
326
+ profile=profile,
327
+ output_dir="notebooks"
328
+ )
329
+ print(f" Starter notebook saved to: {notebook_path}")
330
+
331
+ #===============================================================
332
+ def generate_visualizations(output_path, dataset_name,df):
333
+ print("Generating EDA visualizations..")
334
+
335
+ vis_dir =f"eda_plots_{dataset_name.replace('/','_')}"
336
+
337
+ plot_path = generate_eda_plots(
338
+ df=df,
339
+ output_dir=vis_dir,
340
+ max_cols=10,
341
+ fig_dpi=150
342
+ )
343
+
344
+ print(f" Visualizations saved to: {plot_path}")
345
+ return plot_path
346
+ ##==============================================
347
+ def save_preprocessing_code(output_path, dataset_name, df):
348
+ """Generate and save preprocessing code"""
349
+
350
+ print("\n Generating preprocessing code...")
351
+
352
+ # Generate code
353
+ code = generate_preprocessing_code(df)
354
+
355
+ # Save to file
356
+ safe_name = dataset_name.replace('/', '_')
357
+ code_path = Path(output_path) / f"{safe_name}_preprocess.py"
358
+
359
+ with open(code_path, 'w', encoding='utf-8') as f:
360
+ f.write(code)
361
+
362
+ print(f" Preprocessing code saved to: {code_path}")
363
+ return code_path
364
+
365
+ # ============================================================
366
+ # MAIN FUNCTION
367
+ # ============================================================
368
+ def main():
369
+ """Main entry point for the CLI tool"""
370
+
371
+ # Step 1: Get user input
372
+ args = parse_arguments()
373
+
374
+ # Step 2: Show startup info
375
+ display_startup_info(args)
376
+ output_path = Path(args.output_dir)
377
+ data_exists = any(output_path.glob("*.csv")) or any(output_path.glob("*.xlsx")) or any(output_path.glob("*.json"))
378
+
379
+ if args.local and data_exists:
380
+ print("šŸ“ Using existing data (--local flag detected)")
381
+ print(f" Data found in: {output_path}")
382
+ else:
383
+ # Only download if:
384
+ # 1. --local is NOT used, OR
385
+ # 2. Data doesn't exist
386
+ if not data_exists:
387
+ print("šŸ“‚ No existing data found. Downloading...")
388
+ else:
389
+ print("šŸ“„ Downloading data (use --local to skip download next time)")
390
+
391
+
392
+ # Step 3: Check credentials
393
+ kaggle_dir = check_credentials(args.verbose)
394
+ if not kaggle_dir:
395
+ return
396
+
397
+ # Step 4: Authenticate
398
+ api = authenticate_kaggle(kaggle_dir, args.verbose)
399
+ if not api:
400
+ return
401
+
402
+ # Step 5: Create output directory
403
+ output_path = create_output_directory(args.output_dir)
404
+
405
+ # Step 6: Download
406
+ if args.competition:
407
+ download_competition(api, args.dataset, output_path, args.verbose)
408
+ else:
409
+ download_dataset(api, args.dataset, output_path, args.verbose)
410
+
411
+ df=None
412
+ profile=None
413
+ if args.profile or args.report or args.visualize or args.preprocess or args.notebook:
414
+ df = load_first_csv(Path(args.output_dir))
415
+
416
+ # If data loaded successfully
417
+ if df is not None:
418
+ # Generate profile if requested
419
+ if args.profile or args.report:
420
+ profiler = DataProfiler(df, args.dataset)
421
+ profile = profiler.profile()
422
+
423
+ if args.profile:
424
+ json_path = save_profile_json(profile)
425
+
426
+ if args.report:
427
+ generate_standalone_report(profile)
428
+
429
+ # Step 7: Profile if requested
430
+ if args.visualize:
431
+ generate_visualizations(output_path, args.dataset, df)
432
+
433
+ # Generate preprocessing code if requested
434
+ if args.preprocess:
435
+ save_preprocessing_code(output_path, args.dataset, df)
436
+
437
+ # Generate notebook if requested
438
+ if args.notebook:
439
+ generate_starter_notebook(args.dataset, df, profile)
440
+
441
+ # Step 8: Done!
442
+ print("\n All done! Happy data science! ")
443
+
444
+
445
+
446
+
447
+ # ============================================================
448
+ # ENTRY POINT
449
+ # ============================================================
450
+ if __name__ == "__main__":
451
+ main()