Powered by AppSignal & Oban Pro
Would you like to see your link here? Contact us

Schema Analysis and Context Generation

notebooks/schema_analyzer.livemd

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```")
])