add card layouts, convert postgresql querries to mysql

This commit is contained in:
flynn 2019-01-22 03:00:52 +01:00
parent b5a66e6645
commit eb5ed906fd
15 changed files with 171 additions and 222 deletions

View file

@ -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

View file

@ -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" />

View file

@ -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"}

View file

@ -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"]

View file

@ -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>

View file

@ -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));

View file

@ -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));

View file

@ -1 +0,0 @@
DROP TYPE STATUS;

View file

@ -1,2 +0,0 @@
CREATE TYPE STATUS as ENUM
('open', 'accepted', 'declined');

View file

@ -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));

View file

@ -4,10 +4,6 @@ body {
height: 100%;
}
table {
margin-top: 2em;
}
.loader-container {
width: 700px;
height: 425px;

View file

@ -274,7 +274,7 @@
"value": "#ccc"
},
"strokeWidth": {
"value": 0.5
"value": 1
}
}
},

View file

@ -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)))

View file

@ -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")))))

View file

@ -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]