File size: 7,132 Bytes
f84a02d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
#!/usr/bin/env python3
"""
Database Session Migration Script

Identifies and helps migrate manual database session management to the
context manager pattern.

Usage:
    python scripts/migrate_db_sessions.py          # Identify files to migrate
    python scripts/migrate_db_sessions.py --fix    # Auto-fix simple cases
"""
import ast
import os
from pathlib import Path
import re
from typing import List, Tuple


def find_manual_session_patterns(file_path: str) -> List[Tuple[int, str, str]]:
    """
    Find manual session management patterns in a Python file.

    Returns list of (line_number, pattern_type, matched_line)
    """
    patterns = [
        (r'SessionLocal\(\)', 'Direct SessionLocal() call'),
        (r'with\s+SessionLocal\(\)', 'Manual with SessionLocal()'),
        (r'db\s*=\s*SessionLocal\(\)', 'Variable assignment'),
        (r'\.close\(\)', 'Manual close() call'),
        (r'\.commit\(\)', 'Manual commit() call'),
    ]

    findings = []

    try:
        with open(file_path, 'r', encoding='utf-8') as f:
            lines = f.readlines()

        for line_num, line in enumerate(lines, 1):
            for pattern, description in patterns:
                if re.search(pattern, line):
                    findings.append((line_num, description, line.strip()))
                    break  # Only report first match per line

    except Exception as e:
        print(f"Error reading {file_path}: {e}")

    return findings


def scan_directory(directory: str, exclude_dirs: List[str] = None) -> dict:
    """
    Scan directory for Python files with manual session management.
    """
    if exclude_dirs is None:
        exclude_dirs = ['venv', '__pycache__', '.pytest_cache',
                       'node_modules', '.git', 'migrations', 'alembic']

    results = {
        'files_with_manual_sessions': [],
        'total_files_scanned': 0,
        'files_with_issues': {}
    }

    for root, dirs, files in os.walk(directory):
        # Remove excluded directories
        dirs[:] = [d for d in dirs if d not in exclude_dirs]

        for file in files:
            if file.endswith('.py'):
                file_path = os.path.join(root, file)
                results['total_files_scanned'] += 1

                findings = find_manual_session_patterns(file_path)
                if findings:
                    results['files_with_manual_sessions'].append(file_path)
                    results['files_with_issues'][file_path] = findings

    return results


def categorize_by_priority(results: dict) -> dict:
    """
    Categorize files by migration priority.
    """
    high_priority = []
    medium_priority = []
    low_priority = []

    for file_path, issues in results['files_with_issues'].items():
        line_count = len(issues)

        # High priority: Service layer files with multiple issues
        if any(path in file_path for path in ['service', 'services', 'core']):
            if line_count >= 3:
                high_priority.append((file_path, line_count))
            else:
                medium_priority.append((file_path, line_count))

        # Medium priority: API routes, integrations
        elif any(path in file_path for path in ['api', 'integrations']):
            if line_count >= 3:
                medium_priority.append((file_path, line_count))
            else:
                low_priority.append((file_path, line_count))

        # Low priority: Scripts, tests, tools
        else:
            low_priority.append((file_path, line_count))

    # Sort by issue count (descending)
    high_priority.sort(key=lambda x: x[1], reverse=True)
    medium_priority.sort(key=lambda x: x[1], reverse=True)
    low_priority.sort(key=lambda x: x[1], reverse=True)

    return {
        'high': high_priority,
        'medium': medium_priority,
        'low': low_priority
    }


def main():
    """Main entry point."""
    import argparse

    parser = argparse.ArgumentParser(description='Migrate database session management')
    parser.add_argument('--directory', default='.', help='Directory to scan')
    parser.add_argument('--fix', action='store_true', help='Auto-fix simple cases')
    parser.add_argument('--output', help='Output file for results')
    args = parser.parse_args()

    print("=" * 80)
    print("Database Session Migration Scanner")
    print("=" * 80)
    print()

    print(f"Scanning directory: {args.directory}")
    print()

    results = scan_directory(args.directory)

    print(f"Files scanned: {results['total_files_scanned']}")
    print(f"Files with manual sessions: {len(results['files_with_manual_sessions'])}")
    print()

    if not results['files_with_manual_sessions']:
        print("✅ No files with manual session management found!")
        return

    # Categorize by priority
    categorized = categorize_by_priority(results)

    # Print results
    print("Priority Classification:")
    print("-" * 80)

    if categorized['high']:
        print(f"\n🔴 HIGH PRIORITY ({len(categorized['high'])} files):")
        print("   Service layer files with multiple manual session patterns")
        for file_path, count in categorized['high'][:10]:
            rel_path = os.path.relpath(file_path, args.directory)
            print(f"   - {rel_path} ({count} issues)")

    if categorized['medium']:
        print(f"\n🟡 MEDIUM PRIORITY ({len(categorized['medium'])} files):")
        print("   API routes and integrations")
        for file_path, count in categorized['medium'][:10]:
            rel_path = os.path.relpath(file_path, args.directory)
            print(f"   - {rel_path} ({count} issues)")

    if categorized['low']:
        print(f"\n🟢 LOW PRIORITY ({len(categorized['low'])} files):")
        print("   Scripts, tests, and tools")
        for file_path, count in categorized['low'][:10]:
            rel_path = os.path.relpath(file_path, args.directory)
            print(f"   - {rel_path} ({count} issues)")

    print()
    print("=" * 80)
    print(f"Total: {len(results['files_with_manual_sessions'])} files need migration")
    print("=" * 80)

    # Write output file if requested
    if args.output:
        with open(args.output, 'w') as f:
            f.write("# Database Session Migration Report\n\n")
            f.write(f"Total files: {len(results['files_with_manual_sessions'])}\n\n")

            f.write("## High Priority\n\n")
            for file_path, count in categorized['high']:
                rel_path = os.path.relpath(file_path, args.directory)
                f.write(f"- {rel_path} ({count} issues)\n")

            f.write("\n## Medium Priority\n\n")
            for file_path, count in categorized['medium']:
                rel_path = os.path.relpath(file_path, args.directory)
                f.write(f"- {rel_path} ({count} issues)\n")

            f.write("\n## Low Priority\n\n")
            for file_path, count in categorized['low']:
                rel_path = os.path.relpath(file_path, args.directory)
                f.write(f"- {rel_path} ({count} issues)\n")

        print(f"\nReport written to: {args.output}")


if __name__ == '__main__':
    main()