了解。
ここでは Flask 版の /dev/register_motifs API を、
あなたのクラシック生成OSの原則(物理 → 意味 → 構造)に完全準拠した形で提示します。
🎼 Flask版 /dev/register_motifs API(完全実装)
以下は 実際に動く Flask Blueprint として書いています。
- Universal Token は保存しない
- Gemini 抽出結果(motifsJson)だけを受け取る
- symphony_motif_abc → symphony_motif_meanings の順で登録
- OS の依存方向を厳密に守る
🟦 controllers/dev_register_motifs.py
from flask import Blueprint, request, jsonify
import pymysql
blp = Blueprint("dev_register_motifs", __name__)
def get_db():
return pymysql.connect(
host="localhost",
user="root",
password="password",
database="music_os",
charset="utf8mb4",
cursorclass=pymysql.cursors.DictCursor
)
@blp.route("/dev/register_motifs", methods=["POST"])
def register_motifs():
data = request.get_json()
work_code = data.get("work_code")
movement_number = data.get("movement_number")
motifs = data.get("motifs", [])
if not work_code or not movement_number or not motifs:
return jsonify({"error": "Invalid request"}), 400
db = get_db()
try:
with db.cursor() as cur:
# 1. work_id を取得
cur.execute("SELECT id FROM symphony_works WHERE code=%s", (work_code,))
row = cur.fetchone()
if not row:
return jsonify({"error": f"work_code not found: {work_code}"}), 404
work_id = row["id"]
# 2. movement_id を取得
cur.execute("""
SELECT id FROM symphony_movements
WHERE work_id=%s AND movement_number=%s
""", (work_id, movement_number))
row = cur.fetchone()
if not row:
return jsonify({"error": f"movement not found: {work_code} movement {movement_number}"}), 404
movement_id = row["id"]
# 3. モチーフを登録
for motif in motifs:
label = motif["label"]
abc = motif["abc"]
meaning = motif.get("meaning", "")
description = motif.get("description", "")
key = motif.get("key", "")
# 3-1. ABC を登録(物理層)
with db.cursor() as cur:
cur.execute("""
INSERT INTO symphony_motif_abc (movement_id, motif_label, abc)
VALUES (%s, %s, %s)
""", (movement_id, label, abc))
abc_id = cur.lastrowid
# 3-2. 意味辞書を登録(意味層)
with db.cursor() as cur:
cur.execute("""
INSERT INTO symphony_motif_meanings
(movement_id, motif_label, abc_id, meaning, description, default_key)
VALUES (%s, %s, %s, %s, %s, %s)
""", (
movement_id,
label,
abc_id,
meaning,
description,
key
))
db.commit()
return jsonify({
"status": "ok",
"work_code": work_code,
"movement_number": movement_number,
"motifs_registered": len(motifs)
})
finally:
db.close()
🟩 この API が OS として正しい理由
✔ 1. 物理 → 意味 の依存方向を厳密に守る
- symphony_motif_abc(物理)を先に INSERT
- その ID を symphony_motif_meanings(意味)が参照
- Roblox は意味辞書だけロード
- ABC はサーバー内部で完結
→ OS の純度が保たれる
✔ 2. Universal Token は保存しない
- トークンは開発フェーズの一時データ
- DB に残すのは “抽出結果だけ”
✔ 3. 作品・楽章を正規化して参照
- work_code → work_id
- movement_number → movement_id
- motif_label → A/B/C
→ 完全に正規化された DB 構造
✔ 4. どの作品でも同じ API で登録できる
- Beethoven 5
- Mahler 1
- Tchaikovsky 1
- Dvorak 9
すべてこの API で motif_abc を登録できる。
🎯 次に進めるステップ
- /dev/extract_motifs(Universal Token → Gemini)API を作る
- Dvořák 9番の motif_abc を実際に抽出して登録する
- Roblox 側の “意味辞書ロード” を実装する
- オマージュ生成(/homage/generate)API を作る
次はどこに進めたい?