separate images into its own table

This commit is contained in:
Bart Akeley 2018-03-10 11:56:40 -06:00
parent 1a0bfbe6a6
commit 8da28e8406
5 changed files with 108 additions and 65 deletions

View file

@ -1,106 +1,100 @@
CREATE EXTENSION IF NOT EXISTS "postgis"; CREATE EXTENSION IF NOT EXISTS "postgis";
CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
DROP TYPE IF EXISTS QUANTITY CASCADE;
CREATE TYPE QUANTITY AS ENUM ('lots', 'many', 'few', 'none'); CREATE TYPE QUANTITY AS ENUM ('lots', 'many', 'few', 'none');
DROP TYPE IF EXISTS CATEGORY CASCADE;
CREATE TYPE CATEGORY AS ENUM ('beverages', 'desserts', 'entrees', 'other'); CREATE TYPE CATEGORY AS ENUM ('beverages', 'desserts', 'entrees', 'other');
DROP TABLE IF EXISTS food_items CASCADE; DROP TABLE IF EXISTS food_items CASCADE;
CREATE TABLE food_items ( CREATE TABLE food_items (
id uuid PRIMARY KEY, id uuid PRIMARY KEY,
name VARCHAR(100), name VARCHAR(100),
place_id VARCHAR(50), place_id VARCHAR(50),
category CATEGORY, category CATEGORY,
images VARCHAR, images VARCHAR,
thumbImage VARCHAR, thumbImage VARCHAR,
loc geography(POINT,4326) loc geography(POINT,4326)
); );
DROP TABLE quantities CASCADE; DROP TABLE IF EXISTS images CASCADE;
CREATE TABLE images (
filename VARCHAR(200) PRIMARY KEY,
food_item_id uuid REFERENCES food_items (id),
username VARCHAR(100),
date timestamp (1) with time zone
);
DROP TABLE IF EXISTS quantities CASCADE;
CREATE TABLE quantities ( CREATE TABLE quantities (
food_item_id uuid, food_item_id uuid REFERENCES food_items (id),
date timestamp (1) with time zone, date timestamp (1) with time zone,
quantity QUANTITY, quantity QUANTITY,
PRIMARY KEY(food_item_id, date) PRIMARY KEY(food_item_id, date)
); );
CREATE INDEX IF NOT EXISTS food_loc_index ON food_items USING GIST ( loc ); CREATE INDEX IF NOT EXISTS food_loc_index ON food_items USING GIST ( loc );
INSERT INTO food_items (id, name, place_id, category, images, thumbImage, loc) DROP VIEW IF EXISTS latest_quantities;
CREATE VIEW latest_quantities AS SELECT food_item_id, quantity, date from quantities q1 where date = (
SELECT max(date) FROM quantities q2 WHERE q1.food_item_id=q2.food_item_id
);
INSERT INTO food_items (id, name, place_id, category, loc)
VALUES ( VALUES (
uuid_generate_v4(), uuid_generate_v4(),
'Big John Cookies', 'Big John Cookies',
'ChIJf5QJFBK1RIYRjjfxZz9Z0O0', 'ChIJf5QJFBK1RIYRjjfxZz9Z0O0',
'desserts', 'desserts',
'https://s-media-cache-ak0.pinimg.com/564x/e7/5f/08/e75f08b00c0bc7f2b01b3d1a636389f6.jpg,http://images.media-allrecipes.com/userphotos/560x315/1107530.jpg',
'http://images.media-allrecipes.com/userphotos/560x315/1107530.jpg',
ST_GeogFromText('SRID=4326;POINT(-97.7532464 30.270667599999996)') ST_GeogFromText('SRID=4326;POINT(-97.7532464 30.270667599999996)')
); );
INSERT INTO food_items (id, name, place_id, category, images, thumbImage, loc) INSERT INTO food_items (id, name, place_id, category, loc)
VALUES ( VALUES (
uuid_generate_v4(), uuid_generate_v4(),
'Jelly Filled Donuts', 'Jelly Filled Donuts',
'ChIJ72if-Qe1RIYRCzMucGEEdBA', 'ChIJ72if-Qe1RIYRCzMucGEEdBA',
'desserts', 'desserts',
'https://timenewsfeed.files.wordpress.com/2013/06/jellydonut.jpg',
'https://timenewsfeed.files.wordpress.com/2013/06/jellydonut.jpg',
ST_GeogFromText('SRID=4326;POINT(-97.742308 30.263963300000004)') ST_GeogFromText('SRID=4326;POINT(-97.742308 30.263963300000004)')
); );
INSERT INTO food_items (id, name, place_id, category, images, thumbImage, loc) INSERT INTO food_items (id, name, place_id, category, loc)
VALUES ( VALUES (
uuid_generate_v4(), uuid_generate_v4(),
'Spelt Brownies', 'Spelt Brownies',
'ChIJG44vBQi1RIYRvWGHdYUolZY', 'ChIJG44vBQi1RIYRvWGHdYUolZY',
'desserts', 'desserts',
'http://www.momshealthyeats.com/wp-content/uploads/2011/11/spelt-fudge-brownie.jpg',
'http://www.momshealthyeats.com/wp-content/uploads/2011/11/spelt-fudge-brownie.jpg',
ST_GeogFromText('SRID=4326;POINT(-97.7419085 30.265019100000004)') ST_GeogFromText('SRID=4326;POINT(-97.7419085 30.265019100000004)')
); );
INSERT INTO food_items (id, name, place_id, category, images, thumbImage, loc) INSERT INTO food_items (id, name, place_id, category, loc)
VALUES ( VALUES (
uuid_generate_v4(), uuid_generate_v4(),
'Oatmeal Raisin Cookies', 'Oatmeal Raisin Cookies',
'ChIJf5QJFBK1RIYRjjfxZz9Z0O0', 'ChIJf5QJFBK1RIYRjjfxZz9Z0O0',
'desserts', 'desserts',
'http://cookingontheside.com/wp-content/uploads/2009/04/oatmeal_raisin_cookies_stack-400.jpg',
'http://cookingontheside.com/wp-content/uploads/2009/04/oatmeal_raisin_cookies_stack-400.jpg',
ST_GeogFromText('SRID=4326;POINT(-97.7532464 30.270667599999996)') ST_GeogFromText('SRID=4326;POINT(-97.7532464 30.270667599999996)')
); );
INSERT INTO food_items (id, name, place_id, category, images, thumbImage, loc) INSERT INTO food_items (id, name, place_id, category, loc)
VALUES ( VALUES (
uuid_generate_v4(), uuid_generate_v4(),
'Donuts with Sprinkles', 'Donuts with Sprinkles',
'ChIJ72if-Qe1RIYRCzMucGEEdBA', 'ChIJ72if-Qe1RIYRCzMucGEEdBA',
'desserts', 'desserts',
'https://dannivee.files.wordpress.com/2013/10/img_1950.jpg',
'https://dannivee.files.wordpress.com/2013/10/img_1950.jpg',
ST_GeogFromText('SRID=4326;POINT(-97.742308 30.263963300000004)') ST_GeogFromText('SRID=4326;POINT(-97.742308 30.263963300000004)')
); );
INSERT INTO food_items (id, name, place_id, category, images, thumbImage, loc) INSERT INTO food_items (id, name, place_id, category, loc)
VALUES ( VALUES (
uuid_generate_v4(), uuid_generate_v4(),
'Powdered Donuts', 'Powdered Donuts',
'ChIJ72if-Qe1RIYRCzMucGEEdBA', 'ChIJ72if-Qe1RIYRCzMucGEEdBA',
'desserts', 'desserts',
'http://3.bp.blogspot.com/-NUKSXr1qLHs/UQmsaEFgbTI/AAAAAAAAA_Y/l4psfVl4a5A/s1600/white-powdered-sugar-doughnuts-tracie-kaska.jpg',
'http://3.bp.blogspot.com/-NUKSXr1qLHs/UQmsaEFgbTI/AAAAAAAAA_Y/l4psfVl4a5A/s1600/white-powdered-sugar-doughnuts-tracie-kaska.jpg',
ST_GeogFromText('SRID=4326;POINT(-97.742308 30.263963300000004)') ST_GeogFromText('SRID=4326;POINT(-97.742308 30.263963300000004)')
); );
INSERT INTO food_items (id, name, place_id, category, images, thumbImage, loc) INSERT INTO food_items (id, name, place_id, category, loc)
VALUES ( VALUES (
uuid_generate_v4(), uuid_generate_v4(),
'Snickerdoodles', 'Snickerdoodles',
'ChIJr3szW6a1RIYRkM7LRpnBIO0', 'ChIJr3szW6a1RIYRkM7LRpnBIO0',
'desserts', 'desserts',
'http://josephcphillips.com/wp-content/uploads/2015/02/snickerdoodles2.jpg',
'http://josephcphillips.com/wp-content/uploads/2015/02/snickerdoodles2.jpg',
ST_GeogFromText('SRID=4326;POINT(-97.73798459999999 30.266898599999998)') ST_GeogFromText('SRID=4326;POINT(-97.73798459999999 30.266898599999998)')
); );
INSERT INTO food_items (id, name, place_id, category, images, thumbImage, loc) INSERT INTO food_items (id, name, place_id, category, loc)
VALUES ( VALUES (
uuid_generate_v4(), uuid_generate_v4(),
'Pizza', 'Pizza',
'ChIJr3szW6a1RIYRkM7LRpnBIO0', 'ChIJr3szW6a1RIYRkM7LRpnBIO0',
'desserts', 'desserts',
'http://www.foodandhealth.co.uk/wp-content/uploads/2016/05/Hot-stone-Vegan-Pizza.jpg',
'http://www.foodandhealth.co.uk/wp-content/uploads/2016/05/Hot-stone-Vegan-Pizza.jpg',
ST_GeogFromText('SRID=4326;POINT(-97.73798459999999 30.266898599999998)') ST_GeogFromText('SRID=4326;POINT(-97.73798459999999 30.266898599999998)')
); );
INSERT INTO quantities SELECT id, current_timestamp, 'many' FROM food_items; INSERT INTO quantities SELECT id, current_timestamp, 'many' FROM food_items;
CREATE VIEW latest_quantities AS SELECT food_item_id, quantity, date from quantities q1 where date = (
SELECT max(date) FROM quantities q2 WHERE q1.food_item_id=q2.food_item_id
);

View file

@ -5,7 +5,13 @@
(defcredential (env :aws-access-key-id) (env :aws-secret-access-key) (env :aws-region)) (defcredential (env :aws-access-key-id) (env :aws-secret-access-key) (env :aws-region))
(defn put-s3 (defn put-s3
"upload given filename and file to s3" "upload given filename and file to s3"
[file-name file] [file-name file]
(put-object :bucket-name "aretherecookies" :key file-name :file file)) (put-object :bucket-name "aretherecookies" :key file-name :file file))
(defn build-s3-url
"given an image name return fully qualified URL"
[filename]
(str "https://s3-us-west-2.amazonaws.com/aretherecookies/" filename))

View file

@ -84,10 +84,9 @@
(defn get-images (defn get-images
"query database for a list of images for a food item id" "query database for a list of images for a food item id"
[foodItemId] [foodItemId]
(let [images (:images (select-images @pooled-db {:id foodItemId}))] (select-images @pooled-db {:id foodItemId}))
(filter #(not (str/blank? %)) (str/split (str images) #","))))
(defn set-images (defn add-image
"update the list of images for given food item id" "update the list of images for given food item id"
[foodItemId images] [image]
(update-images @pooled-db {:id foodItemId :images (str/join "," images)})) (insert-image @pooled-db image))

View file

@ -3,32 +3,61 @@
insert-quantity insert-quantity
create-food-item create-food-item
get-images get-images
set-images]] add-image]]
[aretherecookies.parsers :refer [parse-special-types parse-location]] [aretherecookies.parsers :refer [parse-special-types
[aretherecookies.aws :refer [put-s3]] parse-location]]
[aretherecookies.aws :refer [put-s3
build-s3-url]]
[clojure.data.json :as json] [clojure.data.json :as json]
[clojure.string :as str] [clojure.string :as str]
[buddy.auth :refer [authenticated? throw-unauthorized]])) [buddy.auth :refer [authenticated?
throw-unauthorized]])
(:import (java.util UUID)))
(defn safe-json (defn safe-json
"converts hashmaps into a JSON string suitable for a network response" "converts hashmaps into a JSON string suitable for a network response"
[data] [data]
(json/write-str data :value-fn parse-special-types)) (json/write-str data :value-fn parse-special-types))
(defn bad-request (defn bad-request
"returns a HTTP 404 error with the supplied body" "returns a HTTP 404 error with the supplied body"
[body] [body]
{:status 400 :body (safe-json body)}) {:status 400 :body (safe-json body)})
(defn food-items-handler [req]
(defn image-filename-to-url
"convert image filename key to url key"
[image]
(assoc image :url (build-s3-url (get image :filename))))
(defn images-to-s3-urls
"convert all the images of a food item to fully qualified s3 URLs"
[food-item]
(if (get :images food-item)
(update food-item :images (map build-s3-url))
food-item))
(defn food-items-handler
"return a list of food items for a given filter and location"
[req]
(println "/fooditems ---->" (:body req)) (println "/fooditems ---->" (:body req))
(let [{body :body} req] (let [{body :body} req]
(safe-json (safe-json
(hash-map (hash-map
:filter (:filter body) :filter (:filter body)
:fooditems (map parse-location (query-food-items body)))))) :fooditems (->>
(query-food-items body)
(map parse-location)
(map images-to-s3-urls))))))
(defn quantity-handler [req]
(defn quantity-handler
"get the latest quantity for a food item"
[req]
(let [{{foodItemId :foodItemId quantity :quantity} :body} req] (let [{{foodItemId :foodItemId quantity :quantity} :body} req]
(if-not (authenticated? req) (throw-unauthorized)) (if-not (authenticated? req) (throw-unauthorized))
(println "/quantity ---->" foodItemId quantity) (println "/quantity ---->" foodItemId quantity)
@ -36,10 +65,14 @@
(insert-quantity {:foodItemId foodItemId :quantity quantity}) (insert-quantity {:foodItemId foodItemId :quantity quantity})
:value-fn parse-special-types))) :value-fn parse-special-types)))
(def required-keys [:name :placeId :latitude :longitude :category :quantity])
(defn get-missing-keys (defn get-missing-keys
"returns a list of required keys which were not found on the given food item"
[foodItem] [foodItem]
(remove #(get foodItem %) required-keys)) (remove
#(get foodItem %)
[:name :placeId :latitude :longitude :category :quantity]))
(defn add-food-item-handler (defn add-food-item-handler
"validate food item fields from request and insert into database returning newly created item" "validate food item fields from request and insert into database returning newly created item"
@ -54,13 +87,18 @@
(parse-location %) (parse-location %)
(safe-json %))))) (safe-json %)))))
(defn add-image-handler (defn add-image-handler
"given foodItemId from route and photo from multipart form post uploads image into s3 and adds URL into food item record" "given foodItemId from route and photo from multipart form post uploads image into s3 and adds URL into food item record"
[{{foodItemId :foodItemId {image :tempfile} :photo} :params}] [req]
(let [existing-images (get-images foodItemId) (if-not (authenticated? req) (throw-unauthorized))
img-name (str foodItemId "/" (+ 1 (count existing-images)) ".png") (let [username (get-in req [:params :username] "Bart Akeley")
img-url (str "https://s3-us-west-2.amazonaws.com/aretherecookies/" img-name)] foodItemId (get-in req [:params :foodItemId])
(println "/addimage ---->" img-url) image (get-in req [:params :photo :tempfile])
img-name (str foodItemId "/" (UUID/randomUUID) ".png")]
(println "/addimage ---->" img-name)
(put-s3 img-name image) (put-s3 img-name image)
(set-images foodItemId (conj existing-images img-url)) (add-image {:food_item_id foodItemId
img-url)) :filename img-name
:username username})
(build-s3-url img-name)))

View file

@ -4,12 +4,12 @@ SELECT
f.name AS name, f.name AS name,
f.place_id AS place_id, f.place_id AS place_id,
f.category AS category, f.category AS category,
f.images AS images,
f.thumbImage AS thumbImage, f.thumbImage AS thumbImage,
ST_AsGeoJSON(f.loc) AS location, ST_AsGeoJSON(f.loc) AS location,
ST_Distance(f.loc, ST_SetSRID(ST_Point(:lng, :lat), 4326)::geography) / 1609 AS distance, ST_Distance(f.loc, ST_SetSRID(ST_Point(:lng, :lat), 4326)::geography) / 1609 AS distance,
q.quantity AS quantity, q.quantity AS quantity,
q.date AS lastUpdated q.date AS lastUpdated,
(select filename from images where food_item_id = f.id) as images
FROM food_items f FROM food_items f
LEFT OUTER JOIN latest_quantities q LEFT OUTER JOIN latest_quantities q
ON f.id = q.food_item_id ON f.id = q.food_item_id
@ -59,13 +59,19 @@ RETURNING
name AS name, name AS name,
place_id AS place_id, place_id AS place_id,
category AS category, category AS category,
images AS images,
thumbImage AS thumbImage, thumbImage AS thumbImage,
ST_AsGeoJSON(loc) AS location, ST_AsGeoJSON(loc) AS location,
ST_Distance(loc, ST_SetSRID(ST_Point(:longitude, :latitude), 4326)::geography) / 1609 AS distance ST_Distance(loc, ST_SetSRID(ST_Point(:longitude, :latitude), 4326)::geography) / 1609 AS distance
-- :name select-images -- :name select-images
SELECT images FROM food_items WHERE id=:v:id::uuid SELECT * FROM images WHERE food_item-id=:v:food_item_id:uuid
-- :name update-images -- :name insert-image
UPDATE food_items SET images=:v:images WHERE id=:v:id::uuid RETURNING images INSERT INTO images (filename, username, date, food_item_id)
VALUES (
:v:filename,
:v:username,
current_timestamp,
:v:food_item_id::uuid
)
RETURNING *