Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@
!/3MF/
!/calibre/
!/rekordbox/
!/pdmaner/
# Step 5: Inside each software dir, ignore everything (including dotfiles)
/gimp/*
/gimp/.*
Expand Down Expand Up @@ -197,6 +198,8 @@
/calibre/*
/calibre/.*
/rekordbox/*
/pdmaner/*
/pdmaner/.*
/rekordbox/.*

# Step 6: ...except agent-harness/
Expand Down Expand Up @@ -258,7 +261,8 @@
!/sbox/agent-harness/
!/quietshrink/agent-harness/
!/mailchimp/agent-harness/
!/rekordbox/agent-harness/
!/rekordbox/
!/pdmaner/agent-harness/

# Exclude non-gedit demo macros from macrocli (local only)
/macrocli/agent-harness/cli_anything/macrocli/macro_definitions/demo/flameshot*
Expand Down
125 changes: 125 additions & 0 deletions pdmaner/agent-harness/PDMANER.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
# PDManer — Agent Harness Analysis

## Software Summary

PDManer (元数建模) is an open-source desktop database modeling tool — the Chinese
PowerDesigner alternative. Built with Electron + React + Redux + Java (jar backend).

- **Version**: 4.9.4
- **Repo**: https://gitee.com/robergroup/pdmaner
- **Status**: Archived, superseded by PDMaas
- **Native format**: JSON project files (`.chnr.json` extension)

## Phase 1: Codebase Analysis

### 1. Backend Engine

PDManer has **two backends**:

| Component | Tech | Role |
|-----------|------|------|
| Project data | JSON file (`.chnr.json`) | Full project state serialization |
| Java operations | `pdmaner-java.jar` | DB reverse engineering, code generation, connection testing |

No headless CLI exists. The CLI harness will:
- **Manipulate JSON directly** for project/entity/view/diagram/dict/domain CRUD
- **Call Java jar via subprocess** for DB operations (reverse parse, connect test, code-gen)

### 2. Data Model

The `.chnr.json` project file structure:

```json
{
"name": "project-name",
"describe": "",
"avatar": "",
"version": "4.9.4",
"createdTime": "2024-01-19 16:23:16",
"updatedTime": "2024-01-19 16:23:16",
"dbConns": [],
"profile": {
"default": { "db": "<uuid>", "dbConn": "", "entityInitFields": [...] },
"dataTypeSupports": [{ "defKey": "MySQL", "id": "<uuid>", ... }],
"codeTemplates": [...],
"uiHint": [],
"headers": [...],
"namingRules": { ... }
},
"entities": [{
"id": "<uuid>", "defKey": "", "defName": "", "comment": "",
"type": "P", "fields": [...], "indexes": [...], "correlations": [...],
"headers": [...], "properties": {}, "sysProps": { "nameTemplate": "{defKey}[{defName}]" },
"notes": {}, "env": { "base": { "nameSpace": "", "codeRoot": "" } }
}],
"views": [...],
"diagrams": [{ "id": "<uuid>", "defKey": "", "defName": "",
"relationType": "entity", "canvasData": { "cells": [...] } }],
"dicts": [{ "id": "<uuid>", "defKey": "", "defName": "", "items": [...] }],
"domains": [{ "id": "<uuid>", "defKey": "", "defName": "", "applyFor": "", "len": "", "scale": "" }],
"dataTypeMapping": { "mappings": [{ "id": "<uuid>", "defKey": "", "MySQL": "...", "PostgreSQL": "..." }] },
"viewGroups": [{ "id": "<uuid>", "defKey": "", "refEntities": [...], "refViews": [...], "refDiagrams": [...] }],
"standardFields": [...],
"logicEntities": [...],
"namingRules": { "entityDefKey": {...}, "fieldDefKey": {...} }
}
```

### 3. Key Domain Objects

#### Entity (Data Table)
- `defKey` — table code (snake_case)
- `defName` — table display name (中文)
- `fields[]` — column definitions with defKey, type, len, scale, primaryKey, notNull, etc.
- `indexes[]` — index definitions
- `correlations[]` — foreign key relationships
- `type` — 'P' (physical) or 'L' (logical)

#### Field
- `defKey` — column code
- `defName` — column name
- `type` — database type (set via domain mapping)
- `domain` — domain ref (references domains[].id)
- `len`, `scale` — type parameters
- `primaryKey`, `notNull`, `autoIncrement` — constraints
- `comment` — column comment
- `refDict` — data dictionary reference
- `defaultValue` — default value
- `hideInGraph` — hidden in ER diagram

#### Diagram (ER Canvas)
- `canvasData.cells[]` — nodes (shape=table/edit-node) and edges (shape=erdRelation)
- Table nodes reference entities via `originKey`
- ER relations define source/target ports

### 4. GUI Actions → Operations Mapping

| GUI Action | Redux Action | CLI Operation |
|------------|-------------|---------------|
| New project | CREATE_PROJECT_SUCCESS | `project new` |
| Open project | READ_PROJECT_SUCCESS | `project open` |
| Save project | SAVE_PROJECT_SUCCESS | `project save` |
| Add entity | (tab system) | `entity add` |
| Edit entity fields | (tab system) | `entity update-field` |
| Delete entity | (tab system) | `entity delete` |
| Add relation | (canvas) | `diagram add-relation` |
| Export SQL | (tool) | `export ddl` |
| Import PDM | (tool) | `import pdman` |
| Reverse DB | (tool) | `db reverse` |
| Code generate | (tool) | `code generate` |

### 5. Java Backend Capabilities

The `pdmaner-java.jar` supports:
- Database connection testing
- Database reverse engineering (schema → entities)
- Code generation (Java, MyBatis, etc.)
- SQL DDL generation

### 6. Approach for This Harness

Since PDManer has no headless CLI, we take a **hybrid approach**:
1. **JSON manipulation** (Python) — project, entity, view, diagram, dict, domain CRUD
2. **Java subprocess** — DB operations, code generation (call pdmaner-java.jar)
3. **SQL generation** (Python) — generate DDL from entity definitions using type mappings
4. **Export** (Python) — generate Word/Excel/markdown from project data
92 changes: 92 additions & 0 deletions pdmaner/agent-harness/cli_anything/pdmaner/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# cli-anything-pdmaner

CLI harness for **PDManer** (元数建模) — a database modeling tool.

Command-line interface to create, read, update, and manage database models
in PDManer project files (`.chnr.json`).

## Features

- **Project management** — create, open, save project files
- **Entity management** — add/update/delete database tables with fields and indexes
- **View management** — database views referencing entities
- **ER Diagram** — add tables and relations to diagrams
- **Data Dictionary** — manage code-value mappings
- **Domain management** — data type domains with database mappings
- **DDL Export** — generate CREATE TABLE statements for various databases
- **JSON output** — all commands support `--json` for agent consumption
- **REPL mode** — interactive session with undo/redo
- **Session** — state management with undo/redo history

## Installation

```bash
pip install -e .
```

Requires Python 3.9+ and Click.

## Usage

### One-shot commands

```bash
# Create a new project
cli-anything-pdmaner --json project new --name mydb --output mydb.chnr.json

# Open an existing project
cli-anything-pdmaner --project mydb.chnr.json entity list

# Add an entity
cli-anything-pdmaner --project mydb.chnr.json entity add --defkey user --defname "用户表"

# Add a field
cli-anything-pdmaner --project mydb.chnr.json entity add-field user \
--defkey id --type BIGINT --pk --notnull --autoinc --comment "主键ID"

cli-anything-pdmaner --project mydb.chnr.json entity add-field user \
--defkey name --type VARCHAR --len 64 --notnull --comment "姓名"

# Export DDL
cli-anything-pdmaner --project mydb.chnr.json export ddl --db MySQL

# Export to SQL file
cli-anything-pdmaner --project mydb.chnr.json export sql -o output.sql --db MySQL
```

### JSON mode (for agent consumption)

```bash
cli-anything-pdmaner --json --project mydb.chnr.json entity list
cli-anything-pdmaner --json --project mydb.chnr.json entity get user
cli-anything-pdmaner --json --project mydb.chnr.json export ddl
```

### Interactive REPL

```bash
cli-anything-pdmaner
cli-anything-pdmaner repl mydb.chnr.json
```

## Command Reference

| Group | Commands |
|-------|----------|
| `project` | new, open, save, info |
| `entity` | list, get, add, update, delete, add-field, update-field, delete-field, add-index, delete-index |
| `view` | list, add, delete |
| `diagram` | list, add, delete, add-table, add-relation |
| `dict` | list, add, delete, add-item, delete-item |
| `domain` | list, add, delete, mappings, supports |
| `export` | ddl, sql |
| `session` | status, undo, redo, save |

## Project File Format

PDManer projects are JSON files with `.chnr.json` extension. The CLI
manipulates these files directly — no PDManer GUI required.

## License

ISC
6 changes: 6 additions & 0 deletions pdmaner/agent-harness/cli_anything/pdmaner/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
"""cli-anything-pdmaner: CLI harness for PDManer database modeling tool.

Manipulate PDManer project files (.chnr.json) from the command line.
"""

__version__ = "0.1.0"
6 changes: 6 additions & 0 deletions pdmaner/agent-harness/cli_anything/pdmaner/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
"""Allow running as python -m cli_anything.pdmaner."""

from cli_anything.pdmaner.pdmaner_cli import main

if __name__ == "__main__":
main()
Empty file.
99 changes: 99 additions & 0 deletions pdmaner/agent-harness/cli_anything/pdmaner/core/diagram.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
"""ER Diagram management operations."""

from .project import _uid


def get_diagrams(data):
"""List all diagrams."""
return data.get("diagrams", [])


def get_diagram(data, id_or_defkey):
"""Get a single diagram."""
for d in data.get("diagrams", []):
if d.get("id") == id_or_defkey or d.get("defKey") == id_or_defkey:
return d
return None


def add_diagram(data, defKey, defName="", relationType="entity", comment=""):
"""Add a new ER diagram."""
dia = {
"defKey": defKey,
"defName": defName or defKey,
"id": _uid(),
"comment": comment,
"relationType": relationType,
"canvasData": {"cells": []},
}
data.setdefault("diagrams", []).append(dia)
return dia


def delete_diagram(data, id_or_defkey):
"""Delete a diagram."""
dia = get_diagram(data, id_or_defkey)
if not dia:
raise ValueError(f"Diagram not found: {id_or_defkey}")
did = dia["id"]
data["diagrams"] = [d for d in data.get("diagrams", []) if d["id"] != did]
for vg in data.get("viewGroups", []):
vg["refDiagrams"] = [r for r in vg.get("refDiagrams", []) if r != did]
return dia


def add_table_to_diagram(data, diagram_id, entity_id, x=100, y=100):
"""Add an entity table node to a diagram."""
from .entity import get_entity
dia = get_diagram(data, diagram_id)
if not dia:
raise ValueError(f"Diagram not found: {diagram_id}")
entity = get_entity(data, entity_id)
if not entity:
raise ValueError(f"Entity not found: {entity_id}")
node = {
"id": _uid(),
"shape": "table",
"position": {"x": x, "y": y},
"originKey": entity["id"],
"count": 0,
"size": {"width": 240, "height": 120},
"autoSize": True,
}
dia["canvasData"].setdefault("cells", []).append(node)
return node


def add_relation_to_diagram(data, diagram_id, source_entity_id, target_entity_id,
source_field, target_field, relation="1:n"):
"""Add an ER relation edge between two entities."""
dia = get_diagram(data, diagram_id)
if not dia:
raise ValueError(f"Diagram not found: {diagram_id}")

cells = dia["canvasData"].get("cells", [])
source_node = next((c for c in cells if c.get("originKey") == source_entity_id
and c.get("shape") == "table"), None)
target_node = next((c for c in cells if c.get("originKey") == target_entity_id
and c.get("shape") == "table"), None)

if not source_node:
raise ValueError(f"Source entity {source_entity_id} not on diagram")
if not target_node:
raise ValueError(f"Target entity {target_entity_id} not on diagram")

edge = {
"id": _uid(),
"relation": relation,
"shape": "erdRelation",
"source": {
"cell": source_node["id"],
"port": f"{source_field}|out",
},
"target": {
"cell": target_node["id"],
"port": f"{target_field}|in",
},
}
dia["canvasData"]["cells"].append(edge)
return edge
Loading
Loading