Skip to content

Commit

Permalink
Generate layout with SQL query or AI prompt
Browse files Browse the repository at this point in the history
  • Loading branch information
loicknuchel committed Jan 14, 2025
1 parent 9117665 commit 57c0a6c
Show file tree
Hide file tree
Showing 29 changed files with 471 additions and 146 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
<%= render "docs/_h2.html", title: "Data statistics" %>
<%= render "docs/_h2.html", title: "SQL query" %>
<%= render "docs/_h2.html", title: "Rows navigation" %>
<%= render "docs/_h2.html", title: "AI queries" %>
<% end %>

<%= render "docs/_footer.html", conn: @conn, page: @page, prev: @prev, next: @next %>
2 changes: 1 addition & 1 deletion backend/lib/azimutt_web/templates/website/index.html.heex
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
<div class="hidden sm:flex justify-center my-3 space-x-6">
<a href={Azimutt.config(:azimutt_github)} target="_blank" rel="noopener noreferrer" class="px-3 py-1 text-sm font-semibold leading-6 text-gray-600 transition duration-300 ease-in-out rounded-full bg-gray-50 ring-1 ring-inset ring-gray-200 hover:bg-gray-100 hover:shadow-lg hover:scale-105">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" class="inline -mt-1"><path d="M11.86 1.6c1.9 0 3.67.44 5.3 1.31a9.8 9.8 0 015.24 8.75c0 2.27-.65 4.32-1.96 6.12a9.95 9.95 0 01-5.09 3.73c-.23.05-.4.03-.53-.07a.52.52 0 01-.2-.42v-3.46c0-.89-.22-1.5-.7-1.9 1.03-.1 1.8-.25 2.3-.42.79-.27 1.39-.7 1.81-1.24.48-.71.7-1.7.7-2.94 0-.52-.1-.99-.3-1.36-.14-.27-.4-.61-.8-1.03.1-.28.18-.62.2-1a4.67 4.67 0 00-.3-1.82c-.22-.05-.62.02-1.13.22-.32.15-.72.32-1.18.57l-.55.37a9.73 9.73 0 00-5.27 0l-.55-.37a8.54 8.54 0 00-1.2-.57c-.5-.2-.88-.24-1.13-.17a3.9 3.9 0 00-.3 1.83c0 .4.05.69.17.94-.32.42-.57.79-.73 1.13-.15.35-.22.77-.22 1.31 0 1.24.22 2.2.68 2.9.4.56.97 1 1.75 1.28.5.17 1.26.3 2.26.42-.35.32-.58.79-.68 1.4-.47.23-.92.3-1.38.25a2.02 2.02 0 01-1.68-1.1c-.17-.3-.4-.55-.68-.75l-.62-.3-.3-.05c-.3 0-.48.05-.48.15-.03.1.05.2.2.32l.2.18c.2.1.4.3.58.54.17.2.3.42.42.67l.18.32c.15.42.4.74.8.94a3 3 0 001.2.37c.33.02.69.02 1.06-.05l.45-.05.05 2.52c0 .17-.07.3-.2.42-.12.1-.3.15-.52.07a10.13 10.13 0 01-5.17-3.73 10.34 10.34 0 01-1.96-6.15c0-1.88.45-3.58 1.33-5.11a9.93 9.93 0 013.69-3.64 10.53 10.53 0 015.24-1.33zM5.21 15.51c.05-.05.13-.07.2-.02.08.05.1.1.08.17-.03.07-.1.07-.2.02s-.1-.12-.08-.17zm.45.35c.05-.05.13-.05.2.05.08.07.1.14.06.2-.05.04-.13.04-.2-.06-.1-.07-.1-.14-.06-.2zm.43.52c.05-.03.1-.03.15-.03a.2.2 0 01.1.1c.08.1.08.2 0 .25a.08.08 0 01-.1 0c-.02-.03-.1-.05-.15-.08-.05-.12-.05-.22 0-.24zm.5.59c.03-.03.08-.03.13-.03l.15.08c.05.05.07.1.07.15.03.05 0 .1-.02.12-.08.07-.18.07-.3-.05a.22.22 0 01-.08-.17c0-.08.03-.08.05-.1zm.68.52c.03-.1.1-.15.25-.1.15.05.2.1.2.17-.02.08-.05.13-.1.15-.05.02-.1.02-.17 0-.08-.02-.1-.07-.15-.1-.05-.05-.05-.07-.03-.12zm1.28.17c0-.02-.02-.05-.08-.07a.4.4 0 00-.2-.05c-.15 0-.2.05-.2.17 0 .1.08.15.23.15.17-.03.25-.08.25-.2zm.55-.25c.15 0 .23.05.23.13.02.07-.05.15-.2.2-.05 0-.1 0-.15-.03-.05-.02-.08-.07-.08-.15 0-.07.08-.12.2-.15z" fill="currentColor"></path></svg>
1412 stars
1522 stars
</a>
<a href={Azimutt.config(:azimutt_slack)} target="_blank" rel="noopener noreferrer" class="px-3 py-1 text-sm font-semibold leading-6 text-gray-600 transition duration-300 ease-in-out rounded-full bg-gray-50 ring-1 ring-inset ring-gray-200 hover:bg-gray-100 hover:shadow-lg hover:scale-105">
<svg xmlns="http://www.w3.org/2000/svg" width="17" height="16" fill="none" class="inline -mt-1"><path fill="#E01E5A" d="M4.441 9.847c0 .809-.661 1.47-1.47 1.47-.81 0-1.471-.661-1.471-1.47 0-.81.661-1.471 1.47-1.471h1.471v1.47zm.739 0c0-.81.66-1.471 1.47-1.471.81 0 1.47.661 1.47 1.47v3.683c0 .81-.66 1.47-1.47 1.47-.81 0-1.47-.66-1.47-1.47V9.847z"></path><path fill="#36C5F0" d="M6.65 3.942c-.81 0-1.47-.662-1.47-1.47C5.18 1.661 5.84 1 6.65 1c.81 0 1.47.662 1.47 1.471v1.47H6.65zm.003.741c.81 0 1.47.66 1.47 1.47 0 .81-.66 1.47-1.47 1.47H2.97c-.81 0-1.471-.66-1.471-1.47 0-.81.661-1.47 1.47-1.47h3.683z"></path><path fill="#2EB67D" d="M12.555 6.153c0-.81.66-1.47 1.47-1.47.81 0 1.47.66 1.47 1.47 0 .81-.66 1.47-1.47 1.47h-1.47v-1.47zm-.739 0c0 .81-.661 1.47-1.47 1.47-.81 0-1.471-.66-1.471-1.47V2.471c0-.81.661-1.47 1.47-1.47.81 0 1.471.66 1.471 1.47v3.682z"></path><path fill="#ECB22E" d="M10.346 12.058c.809 0 1.47.661 1.47 1.47 0 .81-.661 1.471-1.47 1.471-.81 0-1.471-.66-1.471-1.47v-1.47h1.47zm0-.741c-.81 0-1.471-.661-1.471-1.47 0-.81.661-1.471 1.47-1.471h3.683c.81 0 1.47.661 1.47 1.47 0 .81-.66 1.471-1.47 1.471h-3.682z"></path></svg>
Expand Down
37 changes: 29 additions & 8 deletions frontend/src/Components/Molecules/Modal.elm
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ import Components.Atoms.Icon as Icon exposing (Icon(..))
import ElmBook exposing (Msg)
import ElmBook.Actions as Actions
import ElmBook.Chapter as Chapter exposing (Chapter)
import Html exposing (Html, div, h3, input, p, span, text)
import Html.Attributes exposing (autofocus, class, id, name, placeholder, type_, value)
import Html exposing (Html, div, h3, input, p, span, text, textarea)
import Html.Attributes exposing (autofocus, class, cols, id, name, placeholder, rows, type_, value)
import Html.Events exposing (onClick, onInput)
import Libs.Bool as B
import Libs.Html.Attributes exposing (ariaHidden, ariaLabelledby, ariaModal, css, role)
import Libs.Maybe as Maybe
import Libs.Models.HtmlId exposing (HtmlId)
import Libs.Tailwind as Tw exposing (Color, TwClass, batch, bg_100, focus, sm, text_600)

Expand Down Expand Up @@ -62,11 +63,12 @@ confirm model isOpen =
type alias PromptModel msg =
{ id : HtmlId
, color : Color
, icon : Icon
, icon : Maybe Icon
, title : String
, message : Html msg
, placeholder : String
, value : String
, multiline : Bool
, onUpdate : String -> msg
, confirm : String
, cancel : String
Expand All @@ -93,17 +95,35 @@ prompt model isOpen =
, onBackgroundClick = model.onCancel
}
[ div [ css [ "px-6 pt-6", sm [ "flex items-start" ] ] ]
[ div [ css [ "mx-auto flex-shrink-0 flex items-center justify-center h-12 w-12 rounded-full", bg_100 model.color, sm [ "mx-0 h-10 w-10" ] ] ]
[ Icon.outline model.icon (text_600 model.color)
]
[ model.icon
|> Maybe.mapOrElse
(\icon ->
div [ css [ "mx-auto flex-shrink-0 flex items-center justify-center h-12 w-12 rounded-full", bg_100 model.color, sm [ "mx-0 h-10 w-10" ] ] ]
[ Icon.outline icon (text_600 model.color)
]
)
(text "")
, div [ css [ "mt-3 text-center", sm [ "mt-0 ml-4 text-left" ] ] ]
[ h3 [ class "text-lg leading-6 font-medium text-gray-900", id titleId ]
[ text model.title ]
, div [ class "mt-2" ]
[ p [ class "text-sm text-gray-500" ] [ model.message ]
]
, div [ class "mt-1" ]
[ input [ type_ "text", name fieldId, id fieldId, value model.value, onInput model.onUpdate, placeholder model.placeholder, autofocus True, css [ "shadow-sm block w-full border-gray-300 rounded-md", focus [ "ring-indigo-500 border-indigo-500" ], sm [ "text-sm" ] ] ] []
[ if model.multiline then
let
lines : List String
lines =
model.value |> String.split "\n"

maxCol : Int
maxCol =
lines |> List.map String.length |> List.maximum |> Maybe.withDefault 0
in
textarea [ name fieldId, id fieldId, rows (lines |> List.length |> max 5 |> min 30), cols (maxCol |> max 40 |> min 120), value model.value, onInput model.onUpdate, placeholder model.placeholder, autofocus True, css [ "shadow-sm block w-full border-gray-300 rounded-md", focus [ "ring-indigo-500 border-indigo-500" ], sm [ "text-sm" ] ] ] []

else
input [ type_ "text", name fieldId, id fieldId, value model.value, onInput model.onUpdate, placeholder model.placeholder, autofocus True, css [ "shadow-sm block w-full border-gray-300 rounded-md", focus [ "ring-indigo-500 border-indigo-500" ], sm [ "text-sm" ] ] ] []
]
]
]
Expand Down Expand Up @@ -216,11 +236,12 @@ doc =
, prompt
{ id = "modal-title"
, color = Tw.blue
, icon = QuestionMarkCircle
, icon = Just QuestionMarkCircle
, title = "Please enter your name"
, message = text "This will be useful later ;)"
, placeholder = ""
, value = state.input
, multiline = False
, onUpdate = setInput
, confirm = "Ok"
, cancel = "Cancel"
Expand Down
14 changes: 7 additions & 7 deletions frontend/src/Components/Organisms/Details.elm
Original file line number Diff line number Diff line change
Expand Up @@ -208,11 +208,11 @@ viewTable goToList goToSchema goToTable goToColumn showTable loadLayout openData
, table.item.comment |> Maybe.mapOrElse viewComment (div [] [])
, notes |> viewNotes
, tags |> viewTags
, inLayouts |> List.nonEmptyMap (\l -> viewProp [ text "In layouts" ] (l |> List.sort |> List.map (viewLayout loadLayout))) (div [] [])
, table.item.origins |> List.nonEmptyMap (\origin -> viewProp [ text "From sources" ] (origin |> List.sortBy .name |> List.map (\o -> viewSource openDataExplorer table.item.id Nothing (stats |> Dict.get (SourceId.toString o.id) |> Maybe.andThen Result.toMaybe |> Maybe.map .rows) o))) (div [] [])
, viewTableConstraints table.item
, outRelations |> List.nonEmptyMap (\r -> viewProp [ text "References" ] (r |> List.sortBy .table |> List.map (viewTableRelation goToTable defaultSchema))) (div [] [])
, inRelations |> List.nonEmptyMap (\r -> viewProp [ text "Referenced by" ] (r |> List.sortBy .table |> List.map (viewTableRelation goToTable defaultSchema))) (div [] [])
, viewTableConstraints table.item
, inLayouts |> List.nonEmptyMap (\l -> viewProp [ text "In layouts" ] (l |> List.sort |> List.map (viewLayout loadLayout))) (div [] [])
, table.item.origins |> List.nonEmptyMap (\origin -> viewProp [ text "From sources" ] (origin |> List.sortBy .name |> List.map (\o -> viewSource openDataExplorer table.item.id Nothing (stats |> Dict.get (SourceId.toString o.id) |> Maybe.andThen Result.toMaybe |> Maybe.map .rows) o))) (div [] [])
, viewTableStats table.item.origins table.item.stats stats
, viewProp [ text (table.item.columns |> String.pluralizeD "column") ]
[ ul [ role "list", class "-mx-3 relative z-0 divide-y divide-gray-200" ]
Expand Down Expand Up @@ -291,12 +291,12 @@ viewColumn goToList goToSchema goToTable goToColumn showTable loadLayout openDat
, column.item.comment |> Maybe.mapOrElse viewComment (div [] [])
, notes |> viewNotes
, tags |> viewTags
, viewColumnStats column.item.origins column.item.stats stats
, inLayouts |> List.nonEmptyMap (\l -> viewProp [ text "In layouts" ] (l |> List.sort |> List.map (viewLayout loadLayout))) (div [] [])
, column.item.origins |> List.nonEmptyMap (\origin -> viewProp [ text "From sources" ] (origin |> List.sortBy .name |> List.map (viewSource openDataExplorer table.item.id (Just column.item.path) Nothing))) (div [] [])
, viewColumnConstraints table.item column.item
, column.item.outRelations |> List.nonEmptyMap (\r -> viewProp [ text "References" ] (r |> List.sortBy ErdColumnRef.toId |> List.map (viewColumnRelation goToColumn defaultSchema))) (div [] [])
, column.item.inRelations |> List.nonEmptyMap (\r -> viewProp [ text "Referenced by" ] (r |> List.sortBy ErdColumnRef.toId |> List.map (viewColumnRelation goToColumn defaultSchema))) (div [] [])
, viewColumnConstraints table.item column.item
, inLayouts |> List.nonEmptyMap (\l -> viewProp [ text "In layouts" ] (l |> List.sort |> List.map (viewLayout loadLayout))) (div [] [])
, column.item.origins |> List.nonEmptyMap (\origin -> viewProp [ text "From sources" ] (origin |> List.sortBy .name |> List.map (viewSource openDataExplorer table.item.id (Just column.item.path) Nothing))) (div [] [])
, viewColumnStats column.item.origins column.item.stats stats
]
]

Expand Down
25 changes: 2 additions & 23 deletions frontend/src/Components/Slices/LlmGenerateSqlBody.elm
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import Components.Atoms.Badge as Badge
import Components.Atoms.Button as Button
import Components.Atoms.Icon as Icon
import Components.Molecules.Tooltip as Tooltip
import Components.Slices.LlmKey exposing (promptLlmKey)
import Components.Slices.PlanDialog as PlanDialog
import Conf
import Dict
Expand All @@ -15,7 +16,6 @@ import Html exposing (Html, br, div, h3, label, option, p, select, text, textare
import Html.Attributes exposing (autofocus, class, disabled, for, id, name, placeholder, rows, selected, value)
import Html.Events exposing (onClick, onInput)
import Libs.Dict as Dict
import Libs.Html exposing (extLink)
import Libs.Html.Attributes exposing (css)
import Libs.List as List
import Libs.Maybe as Maybe
Expand Down Expand Up @@ -107,7 +107,7 @@ update openPrompt updateLlmKey erd msg model =
|> Maybe.map
(\( ( _, _, kind ), source ) ->
( { model | loading = True }
, Cmd.batch [ Ports.llmGenerateSql llm.key llm.model model.prompt kind source, Track.generateSqlQueried erd.project source llm.model model.prompt ]
, Cmd.batch [ Ports.llmGenerateSql llm.key llm.model kind source model.prompt, Track.generateSqlQueried erd.project source llm.model model.prompt ]
)
)
|> Maybe.withDefault ( { model | generatedSql = Err "No selected database source" |> Just }, Cmd.none )
Expand All @@ -120,27 +120,6 @@ update openPrompt updateLlmKey erd msg model =
)


promptLlmKey : (Prompt msg -> String -> msg) -> (String -> msg) -> msg
promptLlmKey openPrompt updateLlmKey =
openPrompt
{ color = Tw.blue
, icon = Icon.Key
, title = "OpenAI API Key"
, message =
p []
[ text "Please enter your OpenAI API Key to use it within Azimutt."
, br [] []
, text "You can get it on "
, extLink "https://platform.openai.com/api-keys" [ class "link" ] [ text "platform.openai.com/api-keys" ]
, text "."
]
, confirm = "Save"
, cancel = "Cancel"
, onConfirm = updateLlmKey >> T.send
}
""


view : (Msg -> msg) -> (Cmd msg -> msg) -> (List msg -> msg) -> (String -> msg) -> (SourceId -> SqlQueryOrigin -> msg) -> msg -> HtmlId -> ProjectRef -> Erd -> Model -> Html msg
view wrap send batch toastSuccess openDataExplorer onClose titleId projectRef erd model =
let
Expand Down
32 changes: 32 additions & 0 deletions frontend/src/Components/Slices/LlmKey.elm
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
module Components.Slices.LlmKey exposing (promptLlmKey)

import Components.Atoms.Icon as Icon
import Html exposing (br, p, text)
import Html.Attributes exposing (class)
import Libs.Html exposing (extLink)
import Libs.Tailwind as Tw
import Libs.Task as T
import Shared exposing (Prompt)


promptLlmKey : (Prompt msg -> String -> msg) -> (String -> msg) -> msg
promptLlmKey openPrompt updateLlmKey =
openPrompt
{ color = Tw.blue
, icon = Just Icon.Key
, title = "OpenAI API Key"
, message =
p []
[ text "Please enter your OpenAI API Key to use it within Azimutt."
, br [] []
, text "You can get it on "
, extLink "https://platform.openai.com/api-keys" [ class "link" ] [ text "platform.openai.com/api-keys" ]
, text "."
]
, placeholder = ""
, multiline = False
, confirm = "Save"
, cancel = "Cancel"
, onConfirm = updateLlmKey >> T.send
}
""
2 changes: 1 addition & 1 deletion frontend/src/Models/Project/Comment.elm
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ short content =
|> String.split "\n"
|> List.head
|> Maybe.withDefault ""
|> String.left 50
|> String.left 120
|> (\show -> Bool.cond (show == trimmed) show (show ++ "… double click to see all"))


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ updateSource now source schema errors model =
items |> List.foldl (\a ( curModel, curExtra ) -> curModel |> f a |> Tuple.mapSecond (Extra.combine curExtra >> Extra.dropHistory)) m
in
( model |> mapAmlSidebarM (setErrors errors) |> mapErdM (Erd.mapSource source.id (Source.updateWith parsed)), Extra.none )
|> apply toShow (\( id, hint ) -> mapErdMT (showTable now id hint "aml") >> setDirtyM)
|> apply toShow (\( id, hint ) -> mapErdMT (showTable now id [] hint "aml") >> setDirtyM)
|> apply toHide (\id -> mapErdMT (hideTable now id) >> setDirtyM)
|> apply updated (\t -> mapErdMT (showColumns now t.id (ShowColumns.List (amlColumns |> Dict.getOrElse t.id []))) >> setDirtyM)

Expand Down
11 changes: 8 additions & 3 deletions frontend/src/PagesComponents/Organization_/Project_/Models.elm
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import Models.ParserError exposing (ParserError)
import Models.Position as Position
import Models.Project.CanvasProps exposing (CanvasProps)
import Models.Project.ColumnId exposing (ColumnId)
import Models.Project.ColumnName exposing (ColumnName)
import Models.Project.ColumnPath exposing (ColumnPath)
import Models.Project.ColumnRef exposing (ColumnRef)
import Models.Project.ColumnStats exposing (ColumnStats)
Expand Down Expand Up @@ -243,7 +244,7 @@ type Msg
| DeleteProject ProjectInfo
| GoToTable TableId
| ShowTable TableId (Maybe PositionHint) String
| ShowTables (List TableId) (Maybe PositionHint) String
| ShowTables (List { id : TableId, columns : List ColumnName }) (Maybe PositionHint) String
| ShowAllTables String
| HideTable TableId
| UnHideTable_ Int ErdTableLayout
Expand Down Expand Up @@ -464,9 +465,11 @@ prompt : String -> Html Msg -> String -> (String -> Msg) -> Msg
prompt title content input message =
PromptOpen
{ color = Tw.blue
, icon = QuestionMarkCircle
, icon = Just QuestionMarkCircle
, title = title
, message = content
, placeholder = ""
, multiline = False
, confirm = "Ok"
, cancel = "Cancel"
, onConfirm = message >> T.send
Expand All @@ -478,9 +481,11 @@ simplePrompt : String -> (String -> Msg) -> Msg
simplePrompt label message =
PromptOpen
{ color = Tw.blue
, icon = QuestionMarkCircle
, icon = Just QuestionMarkCircle
, title = label
, message = text ""
, placeholder = ""
, multiline = False
, confirm = "Ok"
, cancel = "Cancel"
, onConfirm = message >> T.send
Expand Down
Loading

0 comments on commit 57c0a6c

Please sign in to comment.