Schema Analysis and Context Generation
Setup
Mix.install([
{:kino, "~> 0.11.0"},
{:explorer, "~> 0.7.0"},
{:jason, "~> 1.4"},
{:ecto, "~> 3.10"},
{:inflex, "~> 2.0"} # For string manipulation
])
File.cd!("../")
Code.require_file("config/config.exs")
alias MyApp.Repo
import Ecto.Query
Schema Inspector
defmodule SchemaAnalyzer do
def analyze_schema(schema_module) do
schema_module.__schema__(:fields)
|> Enum.map(fn field ->
type = schema_module.__schema__(:type, field)
associations = schema_module.__schema__(:associations)
%{
field: field,
type: type,
is_primary_key: field in schema_module.__schema__(:primary_key),
associations: associations
}
end)
end
def generate_qt_model(schema_info) do
"""
from PyQt6.QtCore import Qt, QAbstractTableModel
class #{schema_info.name}Model(QAbstractTableModel):
def __init__(self, data=None):
super().__init__()
self._data = data or []
self._headers = #{inspect(schema_info.fields)}
def rowCount(self, parent):
return len(self._data)
def columnCount(self, parent):
return len(self._headers)
def data(self, index, role):
if role == Qt.ItemDataRole.DisplayRole:
return str(self._data[index.row()][self._headers[index.column()]])
return None
"""
end
end
Interactive Schema Explorer
schema_input = Kino.Input.text("Enter schema module name (e.g., MyApp.User)")
form = Kino.Control.form([schema: schema_input], submit: "Analyze")
Kino.listen(form, fn %{data: %{schema: schema_name}} ->
module = String.split(schema_name, ".")
|> Enum.map(&String.to_atom/1)
|> Enum.reduce(nil, fn
atom, nil -> atom
atom, acc -> Module.concat(acc, atom)
end)
case module do
nil ->
Kino.Text.new("Invalid module name")
mod ->
schema_info = SchemaAnalyzer.analyze_schema(mod)
Kino.DataTable.new(schema_info)
end
end)
Context Generator
defmodule ContextGenerator do
def generate_context(schema_info) do
"""
# Elixir Context
defmodule #{schema_info.context_name} do
import Ecto.Query
def list_#{schema_info.plural_name} do
Repo.all(#{schema_info.schema_module})
end
def get_#{schema_info.singular_name}!(id) do
Repo.get!(#{schema_info.schema_module}, id)
end
def create_#{schema_info.singular_name}(attrs \\\ %{}) do
%#{schema_info.schema_module}{}
|> #{schema_info.schema_module}.changeset(attrs)
|> Repo.insert()
end
end
# Python Data Class
from dataclasses import dataclass
from datetime import datetime
from typing import Optional
@dataclass
class #{schema_info.name}:
#{schema_info.python_fields}
@classmethod
def from_json(cls, data: dict):
return cls(**data)
def to_json(self) -> dict:
return {
#{schema_info.json_fields}
}
"""
end
end
JSON-PyQt Data Transformer
defmodule DataTransformer do
def generate_transformers(schema_info) do
"""
# Elixir JSON encoder
defimpl Jason.Encoder, for: #{schema_info.schema_module} do
def encode(struct, opts) do
struct
|> Map.from_struct()
|> Map.drop([:__meta__, :__struct__])
|> Jason.Encode.map(opts)
end
end
# Python PyQt transformer
def transform_to_qt_model(json_data: list) -> '#{schema_info.name}Model':
model = #{schema_info.name}Model()
for item in json_data:
data_obj = #{schema_info.name}.from_json(item)
model.append_row(data_obj)
return model
"""
end
end
Export Configurations
widget = Kino.Layout.tabs([
"PyQt Model": Kino.Markdown.new("```python\n#{SchemaAnalyzer.generate_qt_model(schema_info)}\n```"),
"Context": Kino.Markdown.new("```elixir\n#{ContextGenerator.generate_context(schema_info)}\n```"),
"Transformers": Kino.Markdown.new("```python\n#{DataTransformer.generate_transformers(schema_info)}\n```")
])