mirror of
https://gitlab.com/wheres-the-tp/server.git
synced 2026-01-25 07:54:57 -06:00
separate images into its own table
This commit is contained in:
parent
1a0bfbe6a6
commit
8da28e8406
5 changed files with 108 additions and 65 deletions
|
|
@ -1,6 +1,8 @@
|
||||||
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 (
|
||||||
|
|
@ -12,95 +14,87 @@ CREATE TABLE food_items (
|
||||||
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
|
|
||||||
);
|
|
||||||
|
|
@ -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))
|
||||||
|
|
|
||||||
|
|
@ -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))
|
||||||
|
|
@ -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)))
|
||||||
|
|
@ -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 *
|
||||||
Loading…
Add table
Reference in a new issue