"""Django management command: ``python manage.py import_tlsdb`` Reads ``TlsDB.csv`` from the project root and populates the ``tls_ref_data`` SQLite table, replacing the CSV as the authoritative source of TLS field-code reference metadata. Usage: python manage.py import_tlsdb python manage.py import_tlsdb --csv /path/to/TlsDB.csv python manage.py import_tlsdb --clear # re-import from scratch """ import csv import os from pathlib import Path from django.conf import settings from django.core.management.base import BaseCommand, CommandError from django.db import connection, transaction class Command(BaseCommand): """Import ``TlsDB.csv`` field definitions into ``tls_ref_data``.""" help = 'Import TlsDB.csv reference data into the tls_ref_data SQLite table' def add_arguments(self, parser): parser.add_argument( '--csv', type=str, default=None, help='Path to TlsDB.csv (default: project-root/TlsDB.csv)', ) parser.add_argument( '--clear', action='store_true', default=False, help='Clear existing data before importing (default: upsert)', ) def _resolve_csv_path(self, provided: str | None) -> Path: """Locate the TlsDB.csv file. Priority: 1. Explicit ``--csv`` path. 2. Project root (where ``manage.py`` lives). """ if provided: path = Path(provided).resolve() else: # manage.py lives in the project root — use that as anchor manage_dir = Path(__file__).resolve().parent.parent.parent.parent path = manage_dir / 'TlsDB.csv' if not path.exists(): raise CommandError(f'TlsDB.csv not found at: {path}') return path def handle(self, *args, **options): csv_path = self._resolve_csv_path(options['csv']) clear = options['clear'] # ── Parse CSV ──────────────────────────────────────────────────────── rows: list[dict[str, str]] = [] with open(csv_path, 'r', encoding='utf-8-sig') as f: reader = csv.DictReader(f) # Expected columns: 标题,含义,数据类型 for line_no, row in enumerate(reader, start=2): field_code = (row.get('标题') or '').strip() meaning = (row.get('含义') or '').strip() data_type = (row.get('数据类型') or '').strip() if not field_code: self.stderr.write(self.style.WARNING( f' [SKIP] Line {line_no}: empty field_code, skipping' )) continue rows.append({ 'field_code': field_code, 'meaning': meaning, 'data_type': data_type, }) if not rows: self.stderr.write(self.style.ERROR('No valid rows found in TlsDB.csv')) return # ── Insert into tls_ref_data ───────────────────────────────────────── with connection.cursor() as cursor: with transaction.atomic(): if clear: cursor.execute('DELETE FROM tls_ref_data') self.stdout.write(self.style.WARNING(f' Cleared existing data ({len(rows)} rows to insert)')) inserted = 0 updated = 0 for r in rows: cursor.execute( """ INSERT INTO tls_ref_data (field_code, meaning, data_type) VALUES (%s, %s, %s) ON CONFLICT(field_code) DO UPDATE SET meaning = EXCLUDED.meaning, data_type = EXCLUDED.data_type """, [r['field_code'], r['meaning'], r['data_type']], ) if cursor.rowcount == 1: inserted += 1 else: updated += 1 self.stdout.write(self.style.SUCCESS( f'\n[OK] Imported {len(rows)} TLS field definitions from: {csv_path}' )) self.stdout.write(self.style.SUCCESS( f' New: {inserted} | Updated: {updated} | Total: {len(rows)}' ))