Backup database 2025-12-25 and clean up project structure

This commit is contained in:
Codex Agent 2025-12-25 09:50:02 +08:00
parent a9dc7c490e
commit 06944cd251
21 changed files with 3515 additions and 5585 deletions

6
.gitignore vendored
View File

@ -31,6 +31,12 @@ analyze_*.py
final_importer.py
ultimate_importer.py
*_importer.py
repair_*.py
script_*.py
test_*.py
# Database Backups
backup_*.sql
# Temporary Data/Reports
excel_info.txt

69
current_permits.txt Normal file
View File

@ -0,0 +1,69 @@
《卫星地面接收设施安装服务许可证》(换发)审批,《卫星地面接收设施安装服务许可证》(注销)审批,《卫星地面接收设施安装服务许可证》(新证)审批
《市场准入负面清单》禁止准入类:禁止违规开展金融相关经营活动“非金融机构、不从事金融活动的企业,在注册名称和经营范围中原则上不得使用与金融相关的字样”(设立依据效力层级不足允许暂时保留的禁止或许可措施)
一次性内部资料准印证核发
互联网上网服务营业场所信息网络安全审核
人力资源服务(不含职业中介活动、劳务派遣服务)备案
仅销售预包装食品备案
从事出版物零售业务许可(含音像制品、电子出版物)
从事包装装潢印刷品和其他印刷品(不含商标、票据、保密印刷)印刷经营活动企业(不含外资企业)的设立、变更审批
从事县内道路旅客运输包车经营许可
公共场所卫生许可
养老机构备案
农药经营许可
出版物发行单位在批准的经营范围内通过互联网等信息网络从事出版物发行业务的备案
出版物批发、零售单位设立不具备法人资格的分支机构,或者出版单位设立发行本版出版物的不具备法人资格的发行分支机构的备案
出版物批发单位设立、变更审批
医疗废物经营许可证核发
医疗机构(三级医院、三级妇幼保健院、急救中心、急救站、临床检验中心、中外合资合作医疗机构、港澳台独资医疗机构)设置审批
医疗机构(不含诊所)执业许可(执业登记)
印章刻制业许可证核发
危险化学品建设项目安全条件审查、安全设施设计审查
危险化学品经营许可证核发
危险废物收集经营许可证核发(广东省厅事项名称) 【国家标准名:危险废物经营许可】
巡游出租汽车经营许可
巡游出租汽车车辆运营证核发
广播电视节目制作经营许可证(载明事项变更)审批,广播电视节目制作经营许可证(新证)审批
广播电视视频点播业务许可证(乙种)审批
废弃电器电子产品处理企业资格审批
建设项目环境影响评价文件审批(广东省厅事项名称) 【国家标准名:“建设项目环境影响评价审批(海洋工程、核与辐射类除外)”】
房地产经纪机构及其分支机构设立备案
托育机构备案
承印加工境外一般性出版物审批
承印加工境外包装装潢和其他印刷品备案核准
排污许可证核发
放射诊疗许可
旅馆业特种行业许可证核发
机动车维修经营备案
机动车驾驶培训机构备案
校车使用许可
歌舞娱乐场所从事娱乐场所经营活动审批
民办职业培训学校新设立、变更
水路运输辅助业登记备案
港口经营许可
游艺娱乐场所从事娱乐场所经营活动审批
游艺娱乐场所从事娱乐场所经营活动审批,内资娱乐场所变更、延续、补证、注销审批
演出经纪机构延续,演出经纪机构从事营业性演出经营活动审批,演出经纪机构变更,演出经纪机构补证,演出经纪机构注销
烟花爆竹(批发)许可证核发
烟草专卖零售许可
烟草专卖零售许可证核发(电子烟零售)
燃气燃烧器具安装、维修企业资质核准
燃气经营许可证核发
特种设备使用登记
生鲜乳准运证明核发
电视剧制作许可证(乙种)载明内容变更,电视剧制作许可证(乙种)延期,电视剧制作许可证(乙种)申请
社会力量举办非学历教育机构审批
种畜禽生产经营许可
第三类医疗器械经营许可
第二、三类非药品类易制毒化学品生产、经营备案
第二类精神药品零售业务审批
经营性人力资源服务许可
药品经营许可证(零售)
营业执照
营业执照 (必填项)
蜂种生产经营许可证核发
辐射安全许可
道路旅客运输站(场)经营许可
道路货物运输经营许可
金属冶炼建设项目安全设施设计审查
音像制作单位的变更审批,音像制作单位的设立审批
饮用水供水单位卫生许可

33
inspect_data.py Normal file
View File

@ -0,0 +1,33 @@
import sqlite3
import pandas as pd
import os
db_path = 'law_risk.db'
excel_path = '主题-事项绑定.xlsx'
print("--- Database Schema ---")
if os.path.exists(db_path):
conn = sqlite3.connect(db_path)
cursor = conn.cursor()
cursor.execute("SELECT name FROM sqlite_master WHERE type='table';")
tables = cursor.fetchall()
for table_name in tables:
print(f"Table: {table_name[0]}")
cursor.execute(f"PRAGMA table_info({table_name[0]})")
columns = cursor.fetchall()
for col in columns:
print(f" {col[1]} ({col[2]})")
conn.close()
else:
print(f"Database file not found: {db_path}")
print("\n--- Excel File Preview ---")
if os.path.exists(excel_path):
try:
df = pd.read_excel(excel_path)
print(df.head())
print("\nColumns:", df.columns.tolist())
except Exception as e:
print(f"Error reading Excel: {e}")
else:
print(f"Excel file not found: {excel_path}")

81
inspect_output.txt Normal file

File diff suppressed because one or more lines are too long

69
inspect_pg_schema.py Normal file
View File

@ -0,0 +1,69 @@
import os
import pg8000.dbapi as pg
import pandas as pd
import sys
# Redirect stdout to a file
sys.stdout = open('task_analysis.txt', 'w', encoding='utf-8')
def load_env():
env_vars = {}
try:
with open('.env', 'r', encoding='utf-8') as f:
for line in f:
line = line.strip()
if not line or line.startswith('#'):
continue
if '=' in line:
k, v = line.split('=', 1)
env_vars[k.strip()] = v.strip()
except Exception:
pass
return env_vars
env = load_env()
HOST = env.get("LIC_PG_HOST", "172.24.240.1")
PORT = int(env.get("LIC_PG_PORT", "5432"))
USER = env.get("LIC_PG_USER", "postgres")
PASSWORD = env.get("LIC_PG_PASSWORD", "")
DATABASE = env.get("LIC_PG_DATABASE", "licensing_risks")
print(f"Connecting to DB...")
try:
conn = pg.connect(host=HOST, port=PORT, user=USER, password=PASSWORD, database=DATABASE)
cursor = conn.cursor()
print("\n--- Regions ---")
cursor.execute("SELECT id, name FROM regions")
for r in cursor.fetchall():
print(f"{r[1]}: {r[0]}")
print("\n--- Current Counts ---")
cursor.execute("SELECT COUNT(*) FROM themes")
print(f"Themes: {cursor.fetchone()[0]}")
try:
cursor.execute("SELECT COUNT(*) FROM region_theme_permits")
print(f"Region Theme Permits: {cursor.fetchone()[0]}")
except: pass
try:
cursor.execute("SELECT COUNT(*) FROM permit_theme_rules")
print(f"Permit Theme Rules: {cursor.fetchone()[0]}")
except: pass
conn.close()
except Exception as e:
print(f"DB Error: {e}")
print("\n--- Excel Structure ---")
try:
# Read header at row 1
df = pd.read_excel('主题-事项绑定.xlsx', header=1)
print("Columns:", df.columns.tolist())
# Identify key columns
print("\nSample Data:")
print(df.iloc[:3].to_string())
except Exception as e:
print(f"Excel Error: {e}")

15
inspect_sheets.py Normal file
View File

@ -0,0 +1,15 @@
import pandas as pd
import sys
sys.stdout = open('excel_sheets.txt', 'w', encoding='utf-8')
try:
xl = pd.ExcelFile('主题-事项绑定.xlsx')
print("Sheet names:", xl.sheet_names)
for sheet in xl.sheet_names:
print(f"\n--- Sheet: {sheet} ---")
df = pd.read_excel(xl, sheet_name=sheet, header=None, nrows=5)
print(df.to_string())
except Exception as e:
print(f"Error: {e}")

0
law_risk.db Normal file
View File

View File

@ -5283,8 +5283,7 @@ def list_permit_risk_snapshot_summaries(
) -> List[Dict[str, Any]]:
"""
Return snapshot summaries for checkpoint history views.
The payload JSON stores the flattened view row, so we project key fields for UI display.
Groups results by snapshot_batch_id to return one entry per batch operation.
"""
filters: List[str] = []
params: List[Any] = []
@ -5301,29 +5300,34 @@ def list_permit_risk_snapshot_summaries(
where_clause = f"WHERE {' AND '.join(filters)}" if filters else ""
# Group by batch_id so that we show one history entry per operation (which may contain multiple risk items).
# If snapshot_batch_id is missing (legacy data), fall back to snapshot_id to treat each as unique.
sql = f"""
SELECT
snapshot_id,
region_id,
permit_id,
risk_id,
permit_risk_key,
version,
edited_by,
change_summary,
created_at,
payload ->> 'region_name' AS region_name,
payload ->> 'permit_name' AS permit_name,
payload ->> 'risk_content' AS risk_content,
payload ->> 'legal_basis' AS legal_basis,
payload ->> 'document_no' AS document_no,
payload ->> 'permit_status' AS permit_status,
payload ->> 'snapshot_batch_id' AS snapshot_batch_id,
payload ->> 'permit_source_name' AS permit_source_name,
payload ->> 'permit_source_type' AS permit_source_type
MAX(snapshot_id::text) as snapshot_id,
MAX(region_id::text) as region_id,
MAX(permit_id::text) as permit_id,
MAX(risk_id::text) as risk_id,
MAX(permit_risk_key) as permit_risk_key,
MAX(version) as version,
MAX(edited_by) as edited_by,
MAX(change_summary) as change_summary,
MAX(created_at) as created_at,
MAX(payload ->> 'region_name') AS region_name,
MAX(payload ->> 'permit_name') AS permit_name,
MAX(payload ->> 'risk_content') AS risk_content,
MAX(payload ->> 'legal_basis') AS legal_basis,
MAX(payload ->> 'document_no') AS document_no,
MAX(payload ->> 'permit_status') AS permit_status,
COALESCE(MAX(payload ->> 'snapshot_batch_id'), MAX(snapshot_id::text)) AS snapshot_batch_id,
MAX(payload ->> 'permit_source_name') AS permit_source_name,
MAX(payload ->> 'permit_source_type') AS permit_source_type,
COUNT(*) as item_count
FROM permit_risk_snapshots
{where_clause}
ORDER BY created_at DESC
GROUP BY COALESCE(payload ->> 'snapshot_batch_id', snapshot_id::text)
ORDER BY MAX(created_at) DESC
LIMIT %s OFFSET %s
"""
params.extend([limit, offset])
@ -5353,6 +5357,7 @@ def list_permit_risk_snapshot_summaries(
snapshot_batch_id,
permit_source_name,
permit_source_type,
item_count,
) in rows:
summaries.append(
{
@ -5361,7 +5366,7 @@ def list_permit_risk_snapshot_summaries(
"permit_id": str(permit_uuid),
"risk_id": str(risk_uuid),
"permit_risk_key": permit_risk_key,
"version": int(version),
"version": int(version) if version else 0,
"created_at": _convert_snapshot_value(created_at),
"edited_by": editor,
"change_summary": summary_text or "",
@ -5374,6 +5379,7 @@ def list_permit_risk_snapshot_summaries(
"snapshot_batch_id": snapshot_batch_id or "",
"permit_source_name": permit_source_name or "",
"permit_source_type": permit_source_type or "",
"item_count": int(item_count),
}
)
return summaries
@ -5385,7 +5391,7 @@ def count_permit_risk_snapshots(
permit_id: Optional[str] = None,
edited_by: Optional[str] = None,
) -> int:
"""Return total snapshots matching the optional filters."""
"""Return total snapshot batches matching the optional filters."""
filters: List[str] = []
params: List[Any] = []
@ -5400,7 +5406,8 @@ def count_permit_risk_snapshots(
params.append(edited_by)
where_clause = f"WHERE {' AND '.join(filters)}" if filters else ""
sql = f"SELECT COUNT(*) FROM permit_risk_snapshots {where_clause}"
# Count distinct batches (using same COALESCE logic as list query)
sql = f"SELECT COUNT(DISTINCT COALESCE(payload ->> 'snapshot_batch_id', snapshot_id::text)) FROM permit_risk_snapshots {where_clause}"
with _lic_pg_conn() as conn:
cur = conn.cursor()

172
rebuild_bindings.py Normal file
View File

@ -0,0 +1,172 @@
import os
import pg8000.dbapi as pg
import pandas as pd
import uuid
import datetime
import sys
# Output log
log_file = open('rebuild_log.txt', 'w', encoding='utf-8')
sys.stdout = log_file
def log(msg):
print(msg)
log_file.flush()
def load_env():
env_vars = {}
try:
with open('.env', 'r', encoding='utf-8') as f:
for line in f:
line = line.strip()
if not line or line.startswith('#'):
continue
if '=' in line:
k, v = line.split('=', 1)
env_vars[k.strip()] = v.strip()
except Exception:
pass
return env_vars
env = load_env()
HOST = env.get("LIC_PG_HOST", "172.24.240.1")
PORT = int(env.get("LIC_PG_PORT", "5432"))
USER = env.get("LIC_PG_USER", "postgres")
PASSWORD = env.get("LIC_PG_PASSWORD", "")
DATABASE = env.get("LIC_PG_DATABASE", "licensing_risks")
CONN = None
def get_conn():
global CONN
if CONN is None:
CONN = pg.connect(host=HOST, port=PORT, user=USER, password=PASSWORD, database=DATABASE)
CONN.autocommit = True
return CONN
def run():
try:
conn = get_conn()
cursor = conn.cursor()
# 1. Clear existing data
log("Clearing existing data...")
# Order matters for foreign keys if they exist
cursor.execute("DELETE FROM region_theme_permits")
cursor.execute("DELETE FROM permit_theme_rules")
cursor.execute("DELETE FROM themes")
# cursor.execute("DELETE FROM region_themes") # If it exists? Check if it errors?
try:
cursor.execute("DELETE FROM region_themes")
except:
pass
log("Data cleared.")
# 2. Get Region ID for "市级"
log("Fetching Region ID for '市级'...")
cursor.execute("SELECT id FROM regions WHERE name = '市级'")
res = cursor.fetchone()
if not res:
log("Error: '市级' region not found!")
return
region_id = res[0]
log(f"Region ID: {region_id}")
# 3. Read Excel
log("Reading Excel...")
df = pd.read_excel('主题-事项绑定.xlsx', sheet_name='新主题事项表-12个')
# Rename columns to standard
df.rename(columns={'主题名称': 'theme_name', '风险提示事项名称': 'permit_name'}, inplace=True)
# Fallback if old name is used
if 'permit_name' not in df.columns and '事项名称' in df.columns:
df.rename(columns={'事项名称': 'permit_name'}, inplace=True)
# Forward fill theme_name (handle merged cells which come as NaN)
df['theme_name'] = df['theme_name'].fillna(method='ffill')
# Clean data
df['theme_name'] = df['theme_name'].astype(str).str.strip()
df['permit_name'] = df['permit_name'].astype(str).str.strip()
# Drop duplicates to prevent unique constraint violations
original_count = len(df)
df.drop_duplicates(subset=['theme_name', 'permit_name'], inplace=True)
log(f"Dropped {original_count - len(df)} duplicate rows.")
log(f"Found {len(df)} unique rows.")
# 4. Prepare Lookups
log("Loading existing permits...")
cursor.execute("SELECT name, id FROM permits")
permit_map = {row[0].strip(): row[1] for row in cursor.fetchall()}
log(f"Loaded {len(permit_map)} existing permits.")
# 5. Process
theme_map = {} # name -> uuid
results = []
for idx, row in df.iterrows():
t_name = row['theme_name']
p_name = row['permit_name']
if pd.isna(t_name) or t_name == 'nan' or not t_name:
results.append({'Theme': t_name, 'Permit': p_name, 'Status': 'Skipped (No Theme)'})
continue
if pd.isna(p_name) or p_name == 'nan' or not p_name:
results.append({'Theme': t_name, 'Permit': p_name, 'Status': 'Skipped (No Permit Name)'})
continue
# Create Theme if needed
if t_name not in theme_map:
t_id = uuid.uuid4()
cursor.execute("INSERT INTO themes (id, name) VALUES (%s, %s)", (t_id, t_name))
theme_map[t_name] = t_id
t_id = theme_map[t_name]
# Bind Rule (name based)
rule_id = uuid.uuid4()
cursor.execute("""
INSERT INTO permit_theme_rules (id, theme_id, permit_name, region_id, created_at)
VALUES (%s, %s, %s, %s, NOW())
""", (rule_id, t_id, p_name, region_id))
# Bind Physical (id based)
status = "Bound (Rule Only)"
if p_name in permit_map:
p_id = permit_map[p_name]
# Ensure theme is registered in the region
cursor.execute("""
INSERT INTO region_themes (region_id, theme_id)
VALUES (%s, %s)
ON CONFLICT DO NOTHING
""", (region_id, t_id))
cursor.execute("""
INSERT INTO region_theme_permits (region_id, theme_id, permit_id)
VALUES (%s, %s, %s)
""", (region_id, t_id, p_id))
status = "Bound (Success)"
else:
status = "Bound Rule, but Permit Not Found in DB"
results.append({'Theme': t_name, 'Permit': p_name, 'Status': status})
# 6. Save Report
log("Saving report...")
report_df = pd.DataFrame(results)
try:
report_df.to_excel('主题-事项绑定结果.xlsx', index=False)
except PermissionError:
log("File locked, saving to '主题-事项绑定结果_new.xlsx' instead.")
report_df.to_excel('主题-事项绑定结果_new.xlsx', index=False)
log("Done.")
conn.close()
except Exception as e:
log(f"CRITICAL ERROR: {e}")
import traceback
traceback.print_exc(file=log_file)
if __name__ == '__main__':
run()

12
rebuild_log.txt Normal file
View File

@ -0,0 +1,12 @@
Clearing existing data...
Data cleared.
Fetching Region ID for '市级'...
Region ID: 2c29ca08-efc6-4e2c-abc2-d73685e0bdd1
Reading Excel...
Dropped 1 duplicate rows.
Found 61 unique rows.
Loading existing permits...
Loaded 72 existing permits.
Saving report...
File locked, saving to '主题-事项绑定结果_new.xlsx' instead.
Done.

68
schema_info.txt Normal file
View File

@ -0,0 +1,68 @@
Connecting to postgres@8.138.196.105:5432/licensing_risks
--- Listing Tables ---
Table Found: themes
Table Found: region_theme_permits
Table Found: region_themes
Table Found: risks
Table Found: operation_logs
Table Found: permit_approval_departments
Table Found: business_scopes
Table Found: permit_subitems
Table Found: regions
Table Found: permits
Table Found: region_permit_details
Table Found: permit_risk_snapshots
Table Found: region_permit_risk_vw
Table Found: region_permit_risks
Table Found: region_permit_scopes
Table Found: region_scopes
Table Found: permit_theme_rules
Table Found: region_permit_subitems
Table Found: region_permit_theme_overrides
Table Found: auth_users
Table Found: service_departments
Table Found: permit_files
Table Found: permit_sources
Table Found: permit_file_links
Table Found: service_department_permits
--- Structure of themes ---
id (uuid)
name (text)
--- Structure of theme_permits ---
(Table not found or empty or different schema)
--- Structure of region_theme_permits ---
region_id (uuid)
theme_id (uuid)
permit_id (uuid)
--- Structure of permits ---
id (uuid)
name (text)
--- Structure of permit_theme_rules ---
id (uuid)
theme_id (uuid)
created_at (timestamp with time zone)
region_id (uuid)
permit_name (text)
responsible_department (text)
--- Structure of permit_themes ---
(Table not found or empty or different schema)
--- Excel Analysis ---
0 ... 13
0 佛山市企业开办风险提示涉企许可(备案)\n事项清单 ... NaN
1 序号 ... 是否首次上线
2 1 ... 是
3 2 ... 是
4 3 ... 是
[5 rows x 14 columns]
Detected header row: 0
Columns: ['佛山市企业开办风险提示涉企许可(备案)\n事项清单', 'Unnamed: 1', 'Unnamed: 2', 'Unnamed: 3', 'Unnamed: 4', 'Unnamed: 5', 'Unnamed: 6', 'Unnamed: 7', 'Unnamed: 8', 'Unnamed: 9', 'Unnamed: 10', 'Unnamed: 11', 'Unnamed: 12', 'Unnamed: 13']

34
task_analysis.txt Normal file
View File

@ -0,0 +1,34 @@
Connecting to DB...
--- Regions ---
市级: 2c29ca08-efc6-4e2c-abc2-d73685e0bdd1
高明区(有意见): 4032e664-9548-4c7b-9e77-82cdb0d0ab85
市级(无意见): d01b8aa3-cb59-4bd1-9721-5319ea745708
禅城区(无意见): 854ec808-340a-4b9d-a3d5-8521225bddfd
南海区(无意见): ea4479ac-2ddf-4e64-8e28-96999defacd9
顺德区(无意见): ccfafc9a-0900-423b-8f6d-d87e3614152d
三水区(无意见): 54cd17c1-19b7-4d73-ae02-182bc6805a1b
Sheet1: 60068d61-992e-4ea5-beb1-cf29227ba135
高明区(样版): b6223a86-f053-40f1-b172-ef7ba28df103
高明区: 9ba7e257-bef7-4579-a124-9c97ec224e8a
营业执照: a2faa968-d8f7-4034-849f-1d20ae8243ce
高明区 (样版): 9183cf5f-cf4e-45e8-b7e4-5b31014f1a0f
高明区(样版): d1ba645d-2095-44b4-801d-bbab99542c50
禅城区: fa078753-974a-4fa7-8240-3b59689dc21d
南海区: e86a675c-2047-418a-a0d7-ee341f8e38fd
顺德区: 058d6257-25cf-420b-a6bc-52cfa32d562b
三水区: bd4dfda9-cbc2-41e3-9f55-5d2608782cab
--- Current Counts ---
Themes: 12
Region Theme Permits: 61
Permit Theme Rules: 61
--- Excel Structure ---
Columns: ['序号', '事项名称', '是否市级实施', '是否区级实施', '备注', '牵头部门', '部门系统简称\n审批服务部门', '市级', '禅城区', '南海区', '顺德区', '高明区', '三水区', '是否首次上线']
Sample Data:
序号 事项名称 是否市级实施 是否区级实施 备注 牵头部门 部门系统简称\n审批服务部门 市级 禅城区 南海区 顺德区 高明区 三水区 是否首次上线
0 1 旅馆业特种行业许可证核发 NaN NaN NaN 市公安局 公安机关 NaN NaN NaN NaN NaN NaN 是
1 2 公章刻制业特种行业许可证核发 NaN NaN NaN 市公安局 公安机关 NaN NaN NaN NaN NaN NaN 是
2 3 互联网上网服务营业场所信息网络安全审核 NaN NaN NaN 市公安局 公安机关 NaN NaN NaN NaN NaN NaN 是

View File

@ -0,0 +1,117 @@
import pandas as pd
import os
import sys
# Add project root to path
sys.path.append(os.getcwd())
from lawrisk.services import licensing_repo as lic_repo
from lawrisk.utils.env_loader import load_env
def bind_departments():
load_env()
file_path = "审批服务部门_修正.xlsx"
output_path = "审批部门绑定结果.xlsx"
if not os.path.exists(file_path):
print(f"File not found: {file_path}")
return
print("Loading Excel...")
df = pd.read_excel(file_path)
# Normalize headers
col_map = {c: c.strip() for c in df.columns}
df.rename(columns=col_map, inplace=True)
# Identify key columns
permit_col = None
dept_col = None
for c in df.columns:
if "事项名称" in c:
permit_col = c
if "部门系统简称" in c:
dept_col = c
if not permit_col or not dept_col:
print(f"Required columns not found. Found: {df.columns.tolist()}")
return
print(f"Using Permit Column: '{permit_col}'")
print(f"Using Department Column: '{dept_col}'")
results = []
updated_count = 0
with lic_repo._lic_pg_conn() as conn:
cur = conn.cursor()
for idx, row in df.iterrows():
p_name = str(row[permit_col]).strip()
d_name = str(row[dept_col]).strip()
if not p_name or p_name == 'nan':
continue
# Check if permit exists
cur.execute("SELECT id FROM permits WHERE name = %s", (p_name,))
res = cur.fetchone()
status = "未绑定"
note = "事项不存在"
if res:
pid = res[0]
# Update unit_name in region_permit_details
# We update for ALL regions for this permit, or just '市级'?
# Usually 'unit_name' is specific to a sheet/region, but here it seems global for the permit.
# We will update for all entries of this permit_id to be safe/consistent with "Service Department" concept.
try:
cur.execute("""
UPDATE region_permit_details
SET unit_name = %s
WHERE permit_id = %s
""", (d_name, pid))
if cur.rowcount > 0:
status = "已绑定"
note = f"更新了 {cur.rowcount} 条记录"
updated_count += 1
else:
status = "未更新"
note = "事项存在但无详情记录"
except Exception as e:
status = "错误"
note = str(e)
print(f"Processing: {p_name} -> {status} ({note})")
results.append({
"序号": row.get("序号", ""),
"事项名称": p_name,
"应绑部门": d_name,
"绑定状态": status,
"备注": note
})
conn.commit()
print(f"Binding complete. Updated {updated_count} permits.")
# Save report
res_df = pd.DataFrame(results)
res_df.to_excel(output_path, index=False)
# Check for unbound
unbound = res_df[res_df["绑定状态"] != "已绑定"]
if not unbound.empty:
print("\n=== 未成功绑定的事项 ===")
print(unbound[["事项名称", "备注"]])
else:
print("\n所有事项均已成功绑定!")
if __name__ == "__main__":
bind_departments()

View File

@ -0,0 +1,68 @@
import sys
import os
# Init path to allow importing lawrisk
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from lawrisk.services import licensing_repo as lic_repo
from lawrisk.utils.env_loader import load_env
def delete_permit_by_name(permit_name):
load_env()
conn = lic_repo._lic_pg_conn()
cur = conn.cursor()
try:
print(f"Searching for permit: {permit_name}")
cur.execute("SELECT id FROM permits WHERE name = %s", (permit_name,))
row = cur.fetchone()
if not row:
print(f"Permit '{permit_name}' not found.")
return
permit_id = row[0]
print(f"Found permit ID: {permit_id}")
# List of tables to clean up that reference permit_id
# Based on licensing_repo.py structures
tables = [
"region_permit_risks",
"region_permit_scopes",
"region_permit_subitems",
"region_permit_details",
"region_theme_permits",
"region_permit_theme_overrides", # Might not exist or might be empty, but good to try
"permit_sources"
]
for table in tables:
try:
# Check if table exists first to avoid crashing on optional tables
cur.execute(f"SELECT to_regclass('{table}')")
if cur.fetchone()[0]:
print(f"Cleaning table: {table}")
cur.execute(f"DELETE FROM {table} WHERE permit_id = %s", (permit_id,))
print(f" Deleted {cur.rowcount} rows.")
else:
print(f"Table {table} does not exist, skipping.")
except Exception as e:
print(f"Error cleaning {table}: {e}")
# Don't abort, try next table
# Finally delete from permits
print("Deleting from permits table...")
cur.execute("DELETE FROM permits WHERE id = %s", (permit_id,))
print(f"Deleted {cur.rowcount} rows from permits.")
conn.commit()
print(f"Successfully deleted permit '{permit_name}' and likely associated data.")
except Exception as e:
conn.rollback()
print(f"An error occurred: {e}")
finally:
conn.close()
if __name__ == "__main__":
delete_permit_by_name("食品小作坊登记证")

100
tools/find_permit.py Normal file
View File

@ -0,0 +1,100 @@
import sys
import os
# Init path to allow importing lawrisk
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from lawrisk.services import licensing_repo as lic_repo
from lawrisk.utils.env_loader import load_env
def find_permit():
load_env()
# Try to access _lic_pg_conn, handle if it's not directly exposed or named differently
if hasattr(lic_repo, "_lic_pg_conn"):
conn_func = getattr(lic_repo, "_lic_pg_conn")
elif hasattr(lic_repo, "get_connection"):
conn_func = getattr(lic_repo, "get_connection")
else:
# Fallback to looking for connection logic inside licensing_repo (it might be a context manager)
# But for now assume _lic_pg_conn exists as per audit_risks.py
conn_func = lic_repo._lic_pg_conn
# Check if it returns a connection or context manager
# _lic_pg_conn in licensing_repo seems to be a context manager based on 'with _lic_pg_conn() as conn'
# But audit_risks.py calls it as 'conn = lic_repo._lic_pg_conn()' ?
# Let's check audit_risks.py again.
# Line 17: conn = lic_repo._lic_pg_conn()
# Line 94: conn.close()
# This implies it returns a connection object, not a context manager.
# But licensing_repo.py line 778 says: 'with _lic_pg_conn() as conn:' ...
# This means _lic_pg_conn() can be used as a context manager OR returns something that has __enter__.
# A standard pg8000 connection can be used as a context manager.
try:
conn = conn_func()
except Exception as e:
print(f"Error connecting to DB: {e}")
return
cur = conn.cursor()
if len(sys.argv) > 1:
permit_name = sys.argv[1]
else:
permit_name = "仅销售预包装食品备案登记"
print(f"Searching for permit: {permit_name}")
# Check strict match
cur.execute("SELECT id, name FROM permits WHERE name = %s", (permit_name,))
strict_match = cur.fetchone()
if strict_match:
print(f"Found EXACT match: ID={strict_match[0]}, Name={strict_match[1]}")
permit_id = strict_match[0]
# Check regions
cur.execute("""
SELECT r.name
FROM region_theme_permits rtp
JOIN regions r ON r.id = rtp.region_id
WHERE rtp.permit_id = %s
GROUP BY r.name
""", (permit_id,))
regions = cur.fetchall()
if regions:
print("Bind to regions:")
for r_name in regions:
print(f" - {r_name[0]}")
else:
print("No region bindings found.")
# Check risks
cur.execute("""
SELECT count(*) FROM region_permit_risks WHERE permit_id = %s
""", (permit_id,))
risk_count_row = cur.fetchone()
risk_count = risk_count_row[0] if risk_count_row else 0
print(f"Total risk entries: {risk_count}")
else:
print("No exact match found.")
# Check fuzzy match
print("\nChecking fuzzy matches...")
cur.execute("SELECT id, name FROM permits WHERE name ILIKE %s", (f"%{permit_name}%",))
fuzzy_matches = cur.fetchall()
if fuzzy_matches:
for mid, mname in fuzzy_matches:
if strict_match and mid == strict_match[0]:
continue
print(f"Found partial match: ID={mid}, Name={mname}")
else:
if not strict_match:
print("No fuzzy matches found.")
conn.close()
if __name__ == "__main__":
find_permit()

View File

@ -0,0 +1,65 @@
import pandas as pd
import os
import sys
# Add project root to path
sys.path.append(os.getcwd())
from lawrisk.services import licensing_repo as lic_repo
from lawrisk.utils.env_loader import load_env
def generate_report():
load_env()
file_path = "审批服务部门.xlsx"
output_path = "审批服务部门_核对结果.xlsx"
if not os.path.exists(file_path):
print(f"File not found: {file_path}")
return
print("Loading Excel...")
df = pd.read_excel(file_path)
# Normalize headers to find the correct column
col_map = {c: c.strip() for c in df.columns}
df.rename(columns=col_map, inplace=True)
permit_col = None
for c in df.columns:
if "事项名称" in c:
permit_col = c
break
if not permit_col:
print("Could not find '事项名称' column.")
return
print(f"Using '{permit_col}' as permit name column.")
print("Fetching system permits...")
with lic_repo._lic_pg_conn() as conn:
cur = conn.cursor()
cur.execute("SELECT name FROM permits")
db_permits = set(row[0] for row in cur.fetchall())
print(f"Found {len(db_permits)} permits in database.")
# Check matches
results = []
for idx, row in df.iterrows():
p_name = str(row[permit_col]).strip()
if p_name in db_permits:
results.append("已匹配") # Matched
else:
results.append("未匹配") # Not Matched
df["匹配状态"] = results
print(f"Saving report to {output_path}...")
df.to_excel(output_path, index=False)
print("Done.")
if __name__ == "__main__":
generate_report()

13
tools/inspect_columns.py Normal file
View File

@ -0,0 +1,13 @@
import pandas as pd
import os
file_path = "审批服务部门.xlsx"
if not os.path.exists(file_path):
print(f"File not found: {file_path}")
else:
try:
df = pd.read_excel(file_path)
print("Columns:", df.columns.tolist())
except Exception as e:
print(f"Error reading excel: {e}")

View File

@ -0,0 +1,13 @@
import pandas as pd
import os
file_path = "审批服务部门_修正.xlsx"
if not os.path.exists(file_path):
print(f"File not found: {file_path}")
else:
try:
df = pd.read_excel(file_path)
print("Columns:", df.columns.tolist())
except Exception as e:
print(f"Error reading excel: {e}")

65
tools/rename_permit.py Normal file
View File

@ -0,0 +1,65 @@
import sys
import os
import psycopg2
# Init path to allow importing lawrisk
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from lawrisk.services import licensing_repo as lic_repo
from lawrisk.utils.env_loader import load_env
def rename_permit(permit_id, new_name):
load_env()
if hasattr(lic_repo, "_lic_pg_conn"):
conn_func = getattr(lic_repo, "_lic_pg_conn")
else:
print("Could not find connection function")
return
try:
conn = conn_func()
except Exception as e:
print(f"Error connecting to DB: {e}")
return
try:
cur = conn.cursor()
# Verify existence
cur.execute("SELECT name FROM permits WHERE id = %s", (permit_id,))
row = cur.fetchone()
if not row:
print(f"Permit with ID {permit_id} not found.")
return
old_name = row[0]
print(f"Renaming permit {permit_id}:")
print(f" Old Name: {old_name}")
print(f" New Name: {new_name}")
# Check if new name exists
cur.execute("SELECT id FROM permits WHERE name = %s", (new_name,))
if cur.fetchone():
print(f"Error: A permit with the name '{new_name}' already exists.")
return
# Update
cur.execute("UPDATE permits SET name = %s WHERE id = %s", (new_name, permit_id))
conn.commit()
print("Successfully renamed.")
except Exception as e:
conn.rollback()
print(f"Error updating permit: {e}")
finally:
conn.close()
if __name__ == "__main__":
if len(sys.argv) < 3:
print("Usage: python rename_permit.py <permit_id> <new_name>")
else:
pid = sys.argv[1]
nname = sys.argv[2]
rename_permit(pid, nname)