Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Convert to deps.edn and update to use latest driver methods. #3

Merged
merged 4 commits into from
Aug 26, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions .dir-locals.el
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
((nil . ((indent-tabs-mode . nil) ; always use spaces for tabs
(require-final-newline . t))) ; add final newline on save
(clojure-mode . ((cider-clojure-cli-aliases . "dev:build")
(clojure-indent-style . always-align))))
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@
.\#*
/target
/.nrepl-port
.cpcache
.class
63 changes: 40 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,39 +2,56 @@

![screenshot](screenshots/sudoku-driver.png)

All you need you do is drop the driver in your `plugins/` directory. You can grab it [here](https://github.com/metabase/sudoku-driver/releases/download/1.0.0/sudoku.metabase-driver.jar) or build it yourself:
All you need you do is drop the driver in your `/path/to/metabase/plugins/` directory.

## Building the driver
## Building the driver

### Prereq: Install Metabase as a local maven dependency, compiled for building drivers
## Prereq: Install the Clojure CLI

Clone the [Metabase repo](https://github.com/metabase/metabase) first if you haven't already done so.
Make sure you have the `clojure` CLI version `1.10.3.933` or newer installed; you can check this with `clojure
--version`. Follow the instructions at https://clojure.org/guides/getting_started if you need to install a
newer version.

```bash
cd /path/to/metabase_source
lein install-for-building-drivers
## Build it

```sh
clojure -X:dev:build
```

### Build the Sudoku driver
will create `target/sudoku.metabase-driver.jar`. Copy this file to `/path/to/metabase/plugins/` and restart your
server, and the driver will show up.

## Hacking on the driver locally

It's easiest to create an alias in `~/.clojure/deps.edn` to include the source paths for your driver, e.g.

```bash
# (In the Sudoku driver directory)
lein clean
DEBUG=1 LEIN_SNAPSHOTS_IN_RELEASE=true lein uberjar
```clojure
;; ~/.clojure/deps.edn
{:aliases
{:user/sudoku-driver
{:extra-deps {metabase/sudoku-driver {:local/root "/home/cam/sudoku-driver"}}
:jvm-opts ["-Dmb.dev.additional.driver.manifest.paths=/home/cam/sudoku-driver/resources/metabase-plugin.yaml"]}}}
```

### Copy it to your plugins dir and restart Metabase
```bash
mkdir -p /path/to/metabase/plugins/
cp target/uberjar/sudoku.metabase-driver.jar /path/to/metabase/plugins/
jar -jar /path/to/metabase/metabase.jar
And then start a (n)REPL or run a dev server from the main Metabase project directory with something like:

```sh
# start a regular REPL
clojure -M:user/sudoku-driver

# start an nREPL
clojure -M:user/sudoku-driver:nrepl

# start a local dev server server
clojure -M:user/sudoku-driver:run
```

*or:*
You can also pass these options directly to `clojure` e.g.

```bash
mkdir -p /path/to/metabase_source/plugins
cp target/uberjar/sudoku.metabase-driver.jar /path/to/metabase_source/plugins/
cd /path/to/metabase_source
lein run
```sh
# start the dev server
clojure \
-Sdeps '{:deps {metabase/sudoku-driver {:local/root "/home/cam/sudoku-driver"}}}' \
-J-Dmb.dev.additional.driver.manifest.paths=/home/cam/sudoku-driver/resources/metabase-plugin.yaml \
-M:run
```
15 changes: 15 additions & 0 deletions deps.edn
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{:paths
["src" "resources"]

:deps
{org.clojure/core.logic {:mvn/version "1.0.0"}}

;; build the driver with clojure -X:build
:aliases
{:build
{:extra-deps {metabase/metabase-core {:local/root "../metabase"}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once metabase/metabase#17606 is merged in I'll update this to use a specific Git SHA instead of assuming you have it locally in a parallel directory

metabase/build-drivers {:local/root "../metabase/bin/build-drivers"}}
:exec-fn build-drivers.build-driver/build-driver!
:exec-args {:driver :sudoku
:project-dir "."
:target-dir "./target"}}}}
24 changes: 0 additions & 24 deletions project.clj

This file was deleted.

47 changes: 32 additions & 15 deletions src/metabase/driver/sudoku.clj
Original file line number Diff line number Diff line change
@@ -1,16 +1,26 @@
(ns metabase.driver.sudoku
(:require [metabase.driver :as driver]
[metabase.query-processor.store :as qp.store]
[metabase.driver.sudoku.query-processor :as sudoku.qp]))
(:require [clojure.pprint :as pprint]
[metabase.driver :as driver]
[metabase.driver.sudoku.query-processor :as sudoku.qp]
[metabase.query-processor.store :as qp.store]))

(driver/register! :sudoku)

(defmethod driver/supports? [:sudoku :basic-aggregations] [_ _] false)
(defmethod driver/database-supports? [:sudoku :basic-aggregations]
[_driver _feature _database]
false)

(defmethod driver/can-connect? :sudoku [_ _]
;; deprecated -- this will be removed in the near future.
(defmethod driver/supports? [:sudoku :basic-aggregations]
[_driver _feature]
false)

(defmethod driver/can-connect? :sudoku
[_driver _details]
true)

(defmethod driver/describe-database :sudoku [_ _]
(defmethod driver/describe-database :sudoku
[_driver _database]
{:tables
(set
(for [table-name ["easy"
Expand All @@ -19,18 +29,25 @@
{:name table-name
:schema nil}))})

(defmethod driver/describe-table :sudoku [_ _ {table-name :name}]
(defmethod driver/describe-table :sudoku
[_driver _database {table-name :name}]
{:name table-name
:schema nil
:fields (set (for [i (range 1 10)]
{:name (format "col_%d" i)
:database-type "org.metabase.enterprise_sudoku.NewColumnFactoryAbstractColumnProxyImpl"
:base-type :type/Integer}))})
{:name (format "col_%d" i)
:database-type "org.metabase.enterprise_sudoku.NewColumnFactoryAbstractColumnProxyImpl"
:base-type :type/Integer
:database-position (dec i)}))})

(defmethod driver/mbql->native :sudoku [_ {{source-table-id :source-table} :query, :as mbql-query}]
(println "mbql-query:" mbql-query) ; NOCOMMIT
(defmethod driver/mbql->native :sudoku
[_driver {{source-table-id :source-table} :query, :as mbql-query}]
(println "MBQL query:")
(pprint/pprint mbql-query)
(:name (qp.store/table source-table-id)))

(defmethod driver/execute-query :sudoku [_ {{difficulty :query} :native, :as native-query}]
(println "native-query:" native-query) ; NOCOMMIT
(sudoku.qp/rando-board-query-results difficulty))
(defmethod driver/execute-reducible-query :sudoku
[_driver {difficulty :native, :as query} _context respond]
(println "Native query:" (pr-str (select-keys query [:native])))
(let [metadata (sudoku.qp/column-metadata)
rows (sudoku.qp/random-board-rows (keyword difficulty))]
(respond metadata rows)))
65 changes: 36 additions & 29 deletions src/metabase/driver/sudoku/query_processor.clj
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
(everyg fd/distinct cols)
(everyg fd/distinct squares))))

(defn- rando-solved-board []
(defn- random-solved-board []
(or (first (solve-board
;; stick 10 rand digits in a grid & try to solve
(loop [[position & more] (take 10 (shuffle (range 0 81))), board (vec (repeat 81 0))]
Expand All @@ -32,34 +32,41 @@
;; if unsolvable try again
(recur)))

(defn- rando-board [difficulty]
(let [num-holes (- 81 ({:easy 48, :medium 36, :hard 24} (keyword difficulty)))
solved-board (vec (rando-solved-board))
holes-seq (shuffle (range 0 81))
unique-solution? #(= 1 (count (solve-board % :max-solutions 2)))]
(loop [[hole & more] holes-seq, remaining-holes num-holes, board solved-board]
(cond
(zero? remaining-holes)
board
(defn- random-board [difficulty]
{:pre [(#{:easy :medium :hard} difficulty)]}
(let [num-holes (- 81 ({:easy 48, :medium 36, :hard 24} (keyword difficulty)))
solved-board (vec (random-solved-board))
holes-seq (shuffle (range 0 81))
unique-solution? #(= 1 (count (solve-board % :max-solutions 2)))]
(loop [[hole & more] holes-seq, remaining-holes num-holes, board solved-board]
(cond
(zero? remaining-holes)
board

;; if we run out of possible holes to dig start over with shuffled sequence of hole positions
(not hole)
(recur (shuffle holes-seq) num-holes solved-board)
;; if we run out of possible holes to dig start over with shuffled sequence of hole positions
(not hole)
(recur (shuffle holes-seq) num-holes solved-board)

:else
(let [new-board (assoc board hole 0)]
;; try digging a hole
(if (unique-solution? new-board)
;; if board is still solvable, recurse with new board state
(recur more (dec remaining-holes) new-board)
;; otherwise throw out the bad hole position and recurse
(recur more remaining-holes board)))))))
:else
(let [new-board (assoc board hole 0)]
;; try digging a hole
(if (unique-solution? new-board)
;; if board is still solvable, recurse with new board state
(recur more (dec remaining-holes) new-board)
;; otherwise throw out the bad hole position and recurse
(recur more remaining-holes board)))))))

(defn rando-board-query-results [difficulty]
{:rows (for [row (partition 9 (rando-board difficulty))]
;; replace the zeroes in the board with nils
(for [cell row]
(when-not (zero? cell)
cell)))
:columns (for [i (range 1 10)]
(format "col_%d" i))})
(defn column-metadata []
{:cols (mapv
(fn [i]
{:name (format "col_%d" i)
:base_type :type/Integer
:effective_type :type/Integer})
(range 1 10))})

(defn random-board-rows [difficulty]
(for [row (partition 9 (random-board difficulty))]
;; replace the zeroes in the board with nils
(for [cell row]
(when-not (zero? cell)
cell))))