add card layouts, convert postgresql querries to mysql
This commit is contained in:
parent
b5a66e6645
commit
eb5ed906fd
15 changed files with 171 additions and 222 deletions
10
README.md
10
README.md
|
@ -69,16 +69,6 @@ In this case you need to shutdown and restart using run or repl.
|
|||
|
||||
You can find these function available in the [userspace definitions][2]
|
||||
|
||||
### Notes
|
||||
#### Database
|
||||
* ENUM TYPE
|
||||
|
||||
Because of the lack of typing in clojure and the forced typing of the jdbc driver
|
||||
we need to manually manage conversion of enum types to clojure keywords.
|
||||
|
||||
When adding an enum to the database, make sure to add it to the '+schema-enums+' set see [db][3]
|
||||
|
||||
|
||||
|
||||
## Production
|
||||
|
||||
|
|
4
cat.iml
4
cat.iml
|
@ -71,7 +71,7 @@
|
|||
<orderEntry type="library" name="Leiningen: com.google.javascript/closure-compiler-externs:v20180805" level="project" />
|
||||
<orderEntry type="library" name="Leiningen: com.google.javascript/closure-compiler-unshaded:v20180805" level="project" />
|
||||
<orderEntry type="library" name="Leiningen: com.google.jsinterop/jsinterop-annotations:1.0.0" level="project" />
|
||||
<orderEntry type="library" name="Leiningen: com.google.protobuf/protobuf-java:3.0.2" level="project" />
|
||||
<orderEntry type="library" name="Leiningen: com.google.protobuf/protobuf-java:3.6.1" level="project" />
|
||||
<orderEntry type="library" name="Leiningen: com.googlecode.json-simple/json-simple:1.1.1" level="project" />
|
||||
<orderEntry type="library" name="Leiningen: com.lambdaworks/scrypt:1.4.0" level="project" />
|
||||
<orderEntry type="library" name="Leiningen: com.layerware/hugsql-adapter-clojure-java-jdbc:0.4.9" level="project" />
|
||||
|
@ -127,6 +127,7 @@
|
|||
<orderEntry type="library" name="Leiningen: metosin/vega-tools:0.2.0" level="project" />
|
||||
<orderEntry type="library" name="Leiningen: migratus:1.2.0" level="project" />
|
||||
<orderEntry type="library" name="Leiningen: mount:0.1.15" level="project" />
|
||||
<orderEntry type="library" name="Leiningen: mysql/mysql-connector-java:8.0.12" level="project" />
|
||||
<orderEntry type="library" name="Leiningen: net.cgrand/parsley:0.9.3" level="project" />
|
||||
<orderEntry type="library" name="Leiningen: net.cgrand/regex:1.1.0" level="project" />
|
||||
<orderEntry type="library" name="Leiningen: net.cgrand/sjacket:0.1.1" level="project" />
|
||||
|
@ -185,7 +186,6 @@
|
|||
<orderEntry type="library" name="Leiningen: org.openid4java/openid4java-nodeps:0.9.6" level="project" />
|
||||
<orderEntry type="library" name="Leiningen: org.ow2.asm/asm-all:4.2" level="project" />
|
||||
<orderEntry type="library" name="Leiningen: org.ow2.asm/asm:5.1" level="project" />
|
||||
<orderEntry type="library" name="Leiningen: org.postgresql/postgresql:42.2.5" level="project" />
|
||||
<orderEntry type="library" name="Leiningen: org.projectodd.wunderboss/wunderboss-clojure:0.13.1" level="project" />
|
||||
<orderEntry type="library" name="Leiningen: org.projectodd.wunderboss/wunderboss-core:0.13.1" level="project" />
|
||||
<orderEntry type="library" name="Leiningen: org.projectodd.wunderboss/wunderboss-web-undertow:0.13.1" level="project" />
|
||||
|
|
|
@ -12,4 +12,4 @@
|
|||
:access-token-uri "https://api.twitter.com/oauth/access_token"
|
||||
:authorize-uri "https://api.twitter.com/oauth/authenticate"
|
||||
; set your dev database connection URL here
|
||||
:database-url "postgresql://localhost:5432/a_db_name?user=a_username&password=a_password"}
|
||||
:database-url "mysql://localhost:3306/a_db_name?user=a_username&password=a_password"}
|
|
@ -27,7 +27,9 @@
|
|||
[org.clojure/clojurescript "1.10.439" :scope "provided"]
|
||||
[org.clojure/tools.cli "0.4.1"]
|
||||
[org.clojure/tools.logging "0.4.1"]
|
||||
[org.postgresql/postgresql "42.2.5"]
|
||||
;[org.postgresql/postgresql "42.2.5"]
|
||||
[mysql/mysql-connector-java "8.0.12"]
|
||||
[com.google.protobuf/protobuf-java "3.6.1"]
|
||||
;https://www.webjars.org/
|
||||
[org.webjars.npm/bulma "0.7.2"]
|
||||
[org.webjars/font-awesome "5.6.1"]
|
||||
|
|
|
@ -101,101 +101,134 @@
|
|||
{% if user %}
|
||||
<section class="section">
|
||||
<div class="columns">
|
||||
<div class="column is-narrow">
|
||||
<h3 class="subtitle is-3">Your cuddles</h3>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Person 1</th>
|
||||
<th>Person 2</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for relation in user-relations %}
|
||||
<tr>
|
||||
<td>{{relation.name}}</td>
|
||||
<td>{{relation.name_2}}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="column">
|
||||
<div class="card">
|
||||
<header class="card-header">
|
||||
<p class="card-header-title">
|
||||
Your cuddles
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<div class="card-content">
|
||||
<div class="content">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Person 1</th>
|
||||
<th>Person 2</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for relation in user-relations %}
|
||||
<tr>
|
||||
<td>{{relation.name}}</td>
|
||||
<td>{{relation.name_2}}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column">
|
||||
<form action="/request_relation" method="post">
|
||||
{% csrf-field %}
|
||||
<div class="field has-addons">
|
||||
<div class="control">
|
||||
<div class="select">
|
||||
<select name="to_id" id="to_id">
|
||||
{% for user in other_users %}
|
||||
<option value="{{user.id}}">{{user.name}}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control">
|
||||
<input class="button is-link" type="submit" value="Request cuddle!">
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<div class="columns">
|
||||
<div class="column">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Outgoing requests</th>
|
||||
<th>Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
{% for rr in rel-requests-out %}
|
||||
<tr>
|
||||
<td>{{rr.to_name}}
|
||||
</td>
|
||||
<td>
|
||||
{% include "parts/colored-status.html" %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
<div class="column">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Incoming requests</th>
|
||||
<th colspan="2">Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
{% for rr in rel-requests-in %}
|
||||
<tr>
|
||||
<td>
|
||||
<span>{{rr.from_name}}</span>
|
||||
</td>
|
||||
<td {% ifunequal rr.status "open" %}colspan='2'{% endifunequal %}>
|
||||
{% include "parts/colored-status.html" %}
|
||||
</td>
|
||||
{% ifequal rr.status "open" %}
|
||||
<td>
|
||||
<form action="/relation_request/{{rr.rr_id}}/status" method="post">
|
||||
{% csrf-field %}
|
||||
<div class="field has-addons is-right">
|
||||
<div class="control">
|
||||
<button type="submit" name="accept"
|
||||
class="button is-success is-small is-rounded is-outlined">Accept
|
||||
</button>
|
||||
</div>
|
||||
<div class="control">
|
||||
<button type="submit" name="decline"
|
||||
class="button is-danger is-small is-rounded is-outlined">Decline
|
||||
</button>
|
||||
</div>
|
||||
<div class="card">
|
||||
<header class="card-header">
|
||||
<p class="card-header-title">
|
||||
Outgoing requests
|
||||
</p>
|
||||
</header>
|
||||
<div class="card-content">
|
||||
<div class="content">
|
||||
<form action="/request_relation" method="post">
|
||||
{% csrf-field %}
|
||||
<div class="field has-addons">
|
||||
<div class="control">
|
||||
<div class="select">
|
||||
<select name="to_id" id="to_id">
|
||||
{% for user in other_users %}
|
||||
<option value="{{user.id}}">{{user.name}}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</form>
|
||||
</td>
|
||||
{% endifequal %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
<div class="control">
|
||||
<input class="button is-link" type="submit" value="Request cuddle!">
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<br/>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Outgoing requests</th>
|
||||
<th>Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
{% for rr in rel-requests-out %}
|
||||
<tr>
|
||||
<td>{{rr.to_name}}
|
||||
</td>
|
||||
<td>
|
||||
{% include "parts/colored-status.html" %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column">
|
||||
<div class="card">
|
||||
<header class="card-header">
|
||||
<p class="card-header-title">
|
||||
Incoming requests
|
||||
</p>
|
||||
</header>
|
||||
<div class="card-content">
|
||||
<div class="content">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Incoming requests</th>
|
||||
<th colspan="2">Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
{% for rr in rel-requests-in %}
|
||||
<tr>
|
||||
<td>
|
||||
<span>{{rr.from_name}}</span>
|
||||
</td>
|
||||
<td {% ifunequal rr.status
|
||||
"open" %}colspan='2'{% endifunequal %}>
|
||||
{% include "parts/colored-status.html" %}
|
||||
</td>
|
||||
{% ifequal rr.status "open" %}
|
||||
<td>
|
||||
<form action="/relation_request/{{rr.rr_id}}/status" method="post">
|
||||
{% csrf-field %}
|
||||
<div class="field has-addons is-right">
|
||||
<div class="control">
|
||||
<button type="submit" name="accept"
|
||||
class="button is-success is-small is-rounded is-outlined">
|
||||
Accept
|
||||
</button>
|
||||
</div>
|
||||
<div class="control">
|
||||
<button type="submit" name="decline"
|
||||
class="button is-danger is-small is-rounded is-outlined">
|
||||
Decline
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</td>
|
||||
{% endifequal %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
CREATE TABLE users
|
||||
(id SERIAL PRIMARY KEY,
|
||||
(id INT NOT NULL AUTO_INCREMENT,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
gender VARCHAR(255));
|
||||
gender VARCHAR(255),
|
||||
|
||||
PRIMARY KEY(id));
|
|
@ -1,6 +1,8 @@
|
|||
CREATE TABLE relations
|
||||
(id SERIAL PRIMARY KEY,
|
||||
from_id INTEGER NOT NULL,
|
||||
to_id INTEGER NOT NULL,
|
||||
FOREIGN KEY (from_id) REFERENCES users (id),
|
||||
FOREIGN KEY (to_id) REFERENCES users (id));
|
||||
(id INT NOT NULL AUTO_INCREMENT,
|
||||
from_id INT NOT NULL,
|
||||
to_id INT NOT NULL,
|
||||
|
||||
PRIMARY KEY(id),
|
||||
FOREIGN KEY (from_id) REFERENCES users(id),
|
||||
FOREIGN KEY (to_id) REFERENCES users(id));
|
|
@ -1 +0,0 @@
|
|||
DROP TYPE STATUS;
|
|
@ -1,2 +0,0 @@
|
|||
CREATE TYPE STATUS as ENUM
|
||||
('open', 'accepted', 'declined');
|
|
@ -1,7 +1,9 @@
|
|||
CREATE TABLE relation_requests
|
||||
(id SERIAL PRIMARY KEY,
|
||||
from_id INTEGER NOT NULL,
|
||||
to_id INTEGER NOT NULL,
|
||||
status STATUS NOT NULL,
|
||||
(id INT NOT NULL AUTO_INCREMENT,
|
||||
from_id INT NOT NULL,
|
||||
to_id INT NOT NULL,
|
||||
status ENUM('open', 'accepted', 'declined') NOT NULL,
|
||||
|
||||
PRIMARY KEY (id),
|
||||
FOREIGN KEY (from_id) REFERENCES users (id),
|
||||
FOREIGN KEY (to_id) REFERENCES users (id));
|
|
@ -4,10 +4,6 @@ body {
|
|||
height: 100%;
|
||||
}
|
||||
|
||||
table {
|
||||
margin-top: 2em;
|
||||
}
|
||||
|
||||
.loader-container {
|
||||
width: 700px;
|
||||
height: 425px;
|
||||
|
|
|
@ -274,7 +274,7 @@
|
|||
"value": "#ccc"
|
||||
},
|
||||
"strokeWidth": {
|
||||
"value": 0.5
|
||||
"value": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -1,33 +1,30 @@
|
|||
(ns cat.db.core
|
||||
(:require
|
||||
[cheshire.core :refer [generate-string parse-string]]
|
||||
[clj-time.jdbc]
|
||||
[clojure.java.jdbc :as jdbc]
|
||||
[clojure.tools.logging :as log]
|
||||
[conman.core :as conman]
|
||||
[java-time :as jt]
|
||||
[java-time.pre-java8 :as jt]
|
||||
[cat.config :refer [env]]
|
||||
[mount.core :refer [defstate]]
|
||||
[clojure.string :as s])
|
||||
(:import org.postgresql.util.PGobject
|
||||
java.sql.Array
|
||||
clojure.lang.IPersistentMap
|
||||
clojure.lang.IPersistentVector
|
||||
[java.sql
|
||||
(:import [java.sql
|
||||
BatchUpdateException
|
||||
PreparedStatement]))
|
||||
|
||||
(defstate ^:dynamic *db*
|
||||
:start (if-let [jdbc-url (env :database-url)]
|
||||
(conman/connect! {:jdbc-url jdbc-url})
|
||||
(do
|
||||
(log/warn "database connection URL was not found, please set :database-url in your config, e.g: dev-config.edn")
|
||||
*db*))
|
||||
:stop (conman/disconnect! *db*))
|
||||
:start (if-let [jdbc-url (env :database-url)]
|
||||
(conman/connect! {:jdbc-url jdbc-url})
|
||||
(do
|
||||
(log/warn "database connection URL was not found, please set :database-url in your config, e.g: dev-config.edn")
|
||||
*db*))
|
||||
:stop (conman/disconnect! *db*))
|
||||
|
||||
(conman/bind-connection *db* "sql/queries.sql")
|
||||
|
||||
|
||||
(extend-protocol jdbc/IResultSetReadColumn
|
||||
java.sql.Timestamp
|
||||
java.sql.Timestamp
|
||||
(result-set-read-column [v _2 _3]
|
||||
(.toLocalDateTime v))
|
||||
java.sql.Date
|
||||
|
@ -35,36 +32,10 @@
|
|||
(.toLocalDate v))
|
||||
java.sql.Time
|
||||
(result-set-read-column [v _2 _3]
|
||||
(.toLocalTime v))
|
||||
Array
|
||||
(result-set-read-column [v _ _] (vec (.getArray v)))
|
||||
PGobject
|
||||
(result-set-read-column [pgobj _metadata _index]
|
||||
(let [type (.getType pgobj)
|
||||
value (.getValue pgobj)]
|
||||
(case type
|
||||
"json" (parse-string value true)
|
||||
"jsonb" (parse-string value true)
|
||||
"citext" (str value)
|
||||
value))))
|
||||
|
||||
(defn to-pg-json [value]
|
||||
(doto (PGobject.)
|
||||
(.setType "jsonb")
|
||||
(.setValue (generate-string value))))
|
||||
|
||||
(extend-type clojure.lang.IPersistentVector
|
||||
jdbc/ISQLParameter
|
||||
(set-parameter [v ^java.sql.PreparedStatement stmt ^long idx]
|
||||
(let [conn (.getConnection stmt)
|
||||
meta (.getParameterMetaData stmt)
|
||||
type-name (.getParameterTypeName meta idx)]
|
||||
(if-let [elem-type (when (= (first type-name) \_) (apply str (rest type-name)))]
|
||||
(.setObject stmt idx (.createArrayOf conn elem-type (to-array v)))
|
||||
(.setObject stmt idx (to-pg-json v))))))
|
||||
(.toLocalTime v)))
|
||||
|
||||
(extend-protocol jdbc/ISQLValue
|
||||
java.util.Date
|
||||
java.util.Date
|
||||
(sql-value [v]
|
||||
(java.sql.Timestamp. (.getTime v)))
|
||||
java.time.LocalTime
|
||||
|
@ -78,46 +49,4 @@
|
|||
(jt/sql-timestamp v))
|
||||
java.time.ZonedDateTime
|
||||
(sql-value [v]
|
||||
(jt/sql-timestamp v))
|
||||
IPersistentMap
|
||||
(sql-value [value] (to-pg-json value))
|
||||
IPersistentVector
|
||||
(sql-value [value] (to-pg-json value)))
|
||||
|
||||
|
||||
; postgres enum type <--> clojure namespaces keywords
|
||||
|
||||
; https://raw.githubusercontent.com/qutebrowser/qutebrowser/master/doc/img/cheatsheet-big.png
|
||||
; Handle Inserting of keywords as enums into the db
|
||||
; Usage: (insert! pg-db :files {:name "my-file.txt", :status :processing-status/pending})
|
||||
(defn kw->pgenum
|
||||
"Convert clj keyword to equivalent PGobject"
|
||||
[kw]
|
||||
(let [type (-> (namespace kw)
|
||||
(s/replace "-" "_"))
|
||||
value (name kw)]
|
||||
(doto (PGobject.)
|
||||
(.setType type)
|
||||
(.setValue value))))
|
||||
|
||||
(extend-type clojure.lang.Keyword
|
||||
jdbc/ISQLValue
|
||||
(sql-value [kw]
|
||||
(kw->pgenum kw)))
|
||||
|
||||
; Handle extracting keywords from the db enum type
|
||||
; Usage:
|
||||
; (query (:db user/system) ["SELECT * FROM files"])
|
||||
; => ({:status :processing-status/pending, :name "my-file.txt"})
|
||||
(def +schema-enums+
|
||||
"A set of all PostgreSQL enums in schema.sql. Used to convert
|
||||
enum-values back into Clojure keywords."
|
||||
#{"status"})
|
||||
|
||||
(extend-type String
|
||||
jdbc/IResultSetReadColumn
|
||||
(result-set-read-column [val rsmeta idx]
|
||||
(let [type (.getColumnTypeName rsmeta idx)]
|
||||
(if (contains? +schema-enums+ type)
|
||||
(keyword (s/replace type "_" "-") val)
|
||||
val))))
|
||||
(jt/sql-timestamp v)))
|
|
@ -35,10 +35,6 @@
|
|||
:title "Wrong request parameters"
|
||||
:message "Please contact your system administrator to fix this issue"}))
|
||||
|
||||
(defn map-status-to-value
|
||||
[relation-requests]
|
||||
(map (fn [rr] (cond-> rr (:status rr) (assoc :status (name (:status rr))))) relation-requests))
|
||||
|
||||
(defroutes home-routes
|
||||
(GET "/" req
|
||||
(let [users (get-users)
|
||||
|
@ -53,10 +49,10 @@
|
|||
other_users (when user
|
||||
(seq (filter (fn [usr] (not (= (:id usr) (:id user))))
|
||||
users)))
|
||||
rel-requests-out (seq (map-status-to-value (db/get-relation-requests-from-user {:from_id (:id user)})))
|
||||
rel-requests-in (seq (map-status-to-value (db/get-relation-requests-to-user {:to_id (:id user)})))]
|
||||
(log/info (str "Session: " (:session req)))
|
||||
(log/info (str "Relation requests: \n OUTGOING: " rel-requests-out "\n INCOMING: " rel-requests-in))
|
||||
rel-requests-out (seq (db/get-relation-requests-from-user {:from_id (:id user)}))
|
||||
rel-requests-in (seq (db/get-relation-requests-to-user {:to_id (:id user)}))]
|
||||
;(log/info (str "Session: " (:session req)))
|
||||
;(log/info (str "Relation requests: \n OUTGOING: " rel-requests-out "\n INCOMING: " rel-requests-in))
|
||||
;(log/info (str "User relations: " user-relations))
|
||||
;(log/info (str "Other Users: " other_users))
|
||||
(home-page {:relations relations
|
||||
|
@ -103,8 +99,8 @@
|
|||
(contains? body :accept) (do
|
||||
(let [rr (db/get-relation-request rr_id_map)]
|
||||
(db/create-relation! {:from_id (:from_id rr) :to_id (:to_id rr)}))
|
||||
(db/update-relation-request-status! (assoc rr_id_map :status :status/accepted)))
|
||||
(contains? body :decline) (db/update-relation-request-status! (assoc rr_id_map :status :status/declined))
|
||||
(db/update-relation-request-status! (assoc rr_id_map :status "accepted")))
|
||||
(contains? body :decline) (db/update-relation-request-status! (assoc rr_id_map :status "declined"))
|
||||
:else false)]
|
||||
(if success
|
||||
(response/found "/")
|
||||
|
@ -117,7 +113,7 @@
|
|||
(do
|
||||
(db/create-relation-request! {:from_id (get-in req [:session :user :id])
|
||||
:to_id (:to_id result)
|
||||
:status :status/open})
|
||||
:status "open"})
|
||||
(response/found "/"))
|
||||
(do
|
||||
(response/bad-request "Incorrect input")))))
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
[cat.moauth :as mo]
|
||||
[cat.db.core :refer [*db*] :as db]))
|
||||
|
||||
(def admins #{31 ;flynn
|
||||
(def admins #{1 ;flynn
|
||||
})
|
||||
|
||||
(defn set-user! [user session redirect-url]
|
||||
|
|
Loading…
Reference in a new issue