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

Schema Bridge: PyQt ↔ Elixir

notebooks/schema_bridge.livemd

Schema Bridge: PyQt ↔ Elixir

Setup

Mix.install([
  {:jason, "~> 1.4"},
  {:ecto, "~> 3.10"},
  {:kino, "~> 0.11.0"},
  {:explorer, "~> 0.7.0"}
])

# Project setup
File.cd!("../")
Code.require_file("config/config.exs")

Schema Generator

defmodule SchemaBridge do
  def generate_ecto_schema(json_schema) do
    """
    defmodule #{json_schema["module_name"]} do
      use Ecto.Schema
      import Ecto.Changeset

      schema "#{json_schema["table_name"]}" do
        #{generate_fields(json_schema["fields"])}
        timestamps()
      end

      def changeset(struct, params \\\\ %{}) do
        struct
        |> cast(params, #{generate_field_list(json_schema["fields"])})
        |> validate_required(#{generate_required_fields(json_schema["fields"])})
      end
    end
    """
  end

  def generate_pyqt_model(json_schema) do
    """
    from PyQt6.QtCore import Qt, QAbstractTableModel
    from dataclasses import dataclass, field
    from typing import Optional, List, Dict
    from datetime import datetime

    @dataclass
    class #{json_schema["class_name"]}:
        #{generate_python_fields(json_schema["fields"])}

    class #{json_schema["class_name"]}Model(QAbstractTableModel):
        def __init__(self, data=None):
            super().__init__()
            self._data = data or []
            self._headers = #{generate_python_headers(json_schema["fields"])}
    """
  end
end

Schema Validator

defmodule SchemaValidator do
  def validate_schema(schema) do
    with {:ok, _} <- validate_structure(schema),
         {:ok, _} <- validate_types(schema),
         {:ok, _} <- validate_constraints(schema) do
      {:ok, schema}
    end
  end
end

Interactive Schema Editor

schema_input = Kino.Input.textarea("Enter JSON Schema")
form = Kino.Control.form([schema: schema_input], submit: "Generate")

Kino.listen(form, fn %{data: %{schema: schema_json}} ->
  case Jason.decode(schema_json) do
    {:ok, schema} ->
      Kino.Layout.tabs([
        "Ecto Schema": Kino.Markdown.new("```elixir\n#{SchemaBridge.generate_ecto_schema(schema)}\n```"),
        "PyQt Model": Kino.Markdown.new("```python\n#{SchemaBridge.generate_pyqt_model(schema)}\n```")
      ])
    {:error, error} ->
      Kino.Markdown.new("Error: #{inspect(error)}")
  end
end)