From 168181734c78bf71e2abd02579e060919452ff0b Mon Sep 17 00:00:00 2001 From: Willie Chalmers III Date: Mon, 30 Sep 2019 06:48:06 -0500 Subject: [PATCH 01/22] chore: Delete .DS_Store This is a Mac-only file that should not be the code repository. --- .DS_Store | Bin 6148 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 .DS_Store diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index fed4b6aa2c4220413a8b56dbe3bff490a6f0219b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHKyKVwO473x9lhRP8++WBatPp$wKM)8~C=iE2NPS&?JL5+pPJ6T|&{(qP*6Z2T zO>sVph%PR>7m>M$bZ|rYw$PgGn>Y558AJ6mPSVR+K6mH0LsLDSFm5M%*?_l~EB?0c z{fuVwYyELN^vCzObox|gsQ?wA0#twsP=Q+tpl6$PPl1e7fC^B7KLzajP~e6&aR~HJ z2ZE0Pz!}nRc=lNWSS$goi9;YVFbyg&sG1{&1|9j5bv1Da47#Y_jC1m4%?U;QcI1nf zi`GC!DnJFs3OvSgZvB4+zcK%hNnB9@DsWc{=xn)OF7Qg(Tbq}&UR&U=@Lxl%mm_#9 j26`*T!dmh3Rb8=X?AOF0(CNrK9mpR6(}hL_{zHKu2Co~< From 7efadd5b3939b9138cfdbb5c82626b77c04c7802 Mon Sep 17 00:00:00 2001 From: Sanjeev Penupala Date: Wed, 23 Oct 2019 21:57:22 -0500 Subject: [PATCH 02/22] Changed Upload Function --- backend/posts/posts.py | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/backend/posts/posts.py b/backend/posts/posts.py index e6bdeec..546e558 100644 --- a/backend/posts/posts.py +++ b/backend/posts/posts.py @@ -1,7 +1,8 @@ """Functions related to post management and database communcation.""" from typing import List -from firebase_admin import firestore, initialize_app +import firebase_admin +from firebase_admin import firestore, initialize_app, credentials, storage from google.cloud.exceptions import NotFound from intelligence import generate_image_metadata from models import AddPostMetadata, PostMetadata @@ -11,13 +12,13 @@ # Initialize Firebase with Application Default Credentials initialize_app(name='hashpost') - db = firestore.client() +# Initialize Firebase Bucket +firebase_admin.initialize_app({'storageBucket': 'hashpost.appspot.com'}) -def add_post(data: AddPostMetadata) -> str: +def add_post(data: AddPostMetadata) -> PostMetadata: """Upload a post's metadata to our database. - Our database is currently Google Cloud Firestore. Args: @@ -29,15 +30,23 @@ def add_post(data: AddPostMetadata) -> str: Raises: CreatePostException if an internal server error occurred. """ + try: post_data = generate_image_metadata(data.image_url) (timestamp, doc) = db.collection(COLLECTION_POSTS) \ .add(post_data.to_json()) - # TODO: Add timestamp to document - return doc.id + return post_data except: raise CreatePostException() +def upload_blob(source_file_name, destination_blob_name): + """Uploads a file to the bucket.""" + bucket = storage.bucket() + blob = bucket.blob(destination_blob_name) + blob.upload_from_filename(source_file_name) + # Checks If File Is Successfully Uploaded + print('File {} uploaded to {}.'.format(source_file_name, destination_blob_name)) + def get_posts() -> List[PostMetadata]: """Queries all posts that match the given filters. From 0a61a9973e32582467a0911fde25c497a6e8f0e1 Mon Sep 17 00:00:00 2001 From: Sanjeev Penupala Date: Wed, 23 Oct 2019 21:57:59 -0500 Subject: [PATCH 03/22] Updated Main --- backend/main.py | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/backend/main.py b/backend/main.py index 706013c..b9f9fa1 100644 --- a/backend/main.py +++ b/backend/main.py @@ -1,8 +1,10 @@ import json from flask import Request, make_response -from posts.posts import get_posts +from flask_uploads import UploadSet, IMAGES +from posts.posts import get_posts, add_post, upload_blob from posts.models import AddPostMetadata from posts.exceptions import CreatePostException, GetPostException, UnknownPostIdException +from requests import Response REQUEST_ARG_USER_ID = 'userId' REQUEST_ARG_POST_ID = 'postId' @@ -34,26 +36,30 @@ def posts(request: Request) -> Response: 'Content-Length': len(posts_json) } response = make_response((posts_json, status, headers)) - except GetPostQueryException: - error_body = { - 'errorCode': 'unknown', - 'message': 'An unknown server error occured. Try again later.' - } - response = make_response((error_body, 500)) except UnknownPostIdException: error_body = { 'errorCode': 'unknownPostId', 'message': 'The provided post does not exist.' } response = make_response((error_body, 404)) + except GetPostException: + error_body = { + 'errorCode': 'unknown', + 'message': 'An unknown server error occured. Try again later.' + } + response = make_response((error_body, 500)) except: response = _generate_server_error() - elif request.method === 'POST': + elif request.method == 'POST': try: - imageUrl = request.args.get(REQUEST_ARG_IMAGE_URL) - if imageUrl is None or imageUrl == '' - add_post_metadata = AddPostMetadata() - # TODO: Handle uploads + # TODO: Upload photo from request to Cloud Storage + # TODO: Create new AddPostMetadata object using image URL from Cloud Storage + # TODO: Call add_posts with /\ + # TODO: Return the result of add_post as JSON + photos = UploadSet('photos', IMAGES) + fileName = photos.save(request.files['photo']) + upload_blob(fileName, "/posts/test") + except CreatePostException: response = _generate_server_error() else: @@ -80,7 +86,7 @@ def auth(request: Request) -> Response: """ # TODO: Handle request - return Response('OK', status=200) + return make_response(('OK', 200)) def _generate_server_error() -> Response: From b71cdbcb3efd4854b6460c3f9f649d80d9d1d99c Mon Sep 17 00:00:00 2001 From: Sanjeev Penupala Date: Wed, 23 Oct 2019 21:58:32 -0500 Subject: [PATCH 04/22] fix: Willie Python mistakes --- backend/posts/intelligence.py | 4 ++-- backend/posts/models.py | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/backend/posts/intelligence.py b/backend/posts/intelligence.py index 50a28fb..414f283 100644 --- a/backend/posts/intelligence.py +++ b/backend/posts/intelligence.py @@ -45,11 +45,11 @@ def generate_image_metadata(image_url: str) -> List[PostPredictionData]: Returns: A list of PostPredictionData containing results from hashtag generation. """ - result_json = _get_imagga_data(imageUrl) + result_json = _get_imagga_data(image_url) predictions = [] for json_object in result_json.result.tags: tag = json_object['tag']['en'] confidence = json_object['confidence'] prediction = PostPredictionData(confidence, tag) - predictions.add(prediction) + predictions.append(prediction) return predictions diff --git a/backend/posts/models.py b/backend/posts/models.py index 8fa8643..be36cd2 100644 --- a/backend/posts/models.py +++ b/backend/posts/models.py @@ -6,11 +6,10 @@ class PostMetadata: machine learning. """ - def __init__(self, id: str, image_url: str, hashtags: list, caption: str): + def __init__(self, id: str, image_url: str, hashtags: list): self.id = id self.image_url = image_url self.hashtags = hashtags - self.caption = caption class AddPostMetadata: From 943451cf51ea5a3acfba187cc86df38dfb36d3c4 Mon Sep 17 00:00:00 2001 From: Sanjeev Penupala Date: Wed, 23 Oct 2019 21:59:02 -0500 Subject: [PATCH 05/22] Added GitIgnore Commands --- .gitignore | 3 ++- backend/.gitignore | 0 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 backend/.gitignore diff --git a/.gitignore b/.gitignore index 4e04fc1..22753a2 100644 --- a/.gitignore +++ b/.gitignore @@ -74,4 +74,5 @@ # Backend-related # Don't check in virtual environment -/backend/env \ No newline at end of file +/backend/env +env/ \ No newline at end of file diff --git a/backend/.gitignore b/backend/.gitignore new file mode 100644 index 0000000..e69de29 From 0a86a8b71a949faeae44c88559f08b1d5279fb42 Mon Sep 17 00:00:00 2001 From: Sanjeev Penupala Date: Wed, 30 Oct 2019 19:00:32 -0500 Subject: [PATCH 06/22] Updated posts.py --- backend/posts/posts.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/posts/posts.py b/backend/posts/posts.py index 546e558..b1da680 100644 --- a/backend/posts/posts.py +++ b/backend/posts/posts.py @@ -44,7 +44,7 @@ def upload_blob(source_file_name, destination_blob_name): bucket = storage.bucket() blob = bucket.blob(destination_blob_name) blob.upload_from_filename(source_file_name) - # Checks If File Is Successfully Uploaded + # Prints If File Is Successfully Uploaded print('File {} uploaded to {}.'.format(source_file_name, destination_blob_name)) From 708301b59f189c02650fcdb31c985cdda9e2f88c Mon Sep 17 00:00:00 2001 From: Sanjeev Penupala Date: Wed, 30 Oct 2019 19:05:08 -0500 Subject: [PATCH 07/22] Updated Settings --- .vscode/settings.json | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..5703476 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "python.pythonPath": "env\\Scripts\\python.exe", + "python.linting.pylintEnabled": true, + "python.linting.enabled": true +} \ No newline at end of file From fa422f584dc886f3b448c79899a661a5444f0b35 Mon Sep 17 00:00:00 2001 From: Sanjeev Penupala Date: Sun, 3 Nov 2019 23:22:52 -0600 Subject: [PATCH 08/22] Updated upload function --- backend/posts/posts.py | 45 +++++++++++++++++++++++++++++++++--------- 1 file changed, 36 insertions(+), 9 deletions(-) diff --git a/backend/posts/posts.py b/backend/posts/posts.py index b1da680..4c2d659 100644 --- a/backend/posts/posts.py +++ b/backend/posts/posts.py @@ -1,8 +1,14 @@ """Functions related to post management and database communcation.""" from typing import List + import firebase_admin from firebase_admin import firestore, initialize_app, credentials, storage +import pyrebase + +from flask import Flask, render_template, request, flash, redirect, url_for +from werkzeug.utils import secure_filename + from google.cloud.exceptions import NotFound from intelligence import generate_image_metadata from models import AddPostMetadata, PostMetadata @@ -10,12 +16,24 @@ COLLECTION_POSTS = u'/posts' + +firebaseConfig = { + "apiKey": "AIzaSyCA3OrlBV1UIeSl2AXL0YnlqCE0xMwv_s4", + "authDomain": "hashpost.firebaseapp.com", + "databaseURL": "https://hashpost.firebaseio.com", + "projectId": "hashpost", + "storageBucket": "hashpost.appspot.com", + "messagingSenderId": "142555671587", + "appId": "1:142555671587:web:6807408877be551d391596" +} + # Initialize Firebase with Application Default Credentials initialize_app(name='hashpost') db = firestore.client() -# Initialize Firebase Bucket -firebase_admin.initialize_app({'storageBucket': 'hashpost.appspot.com'}) +firebase = pyrebase.initialize_app(firebaseConfig) # Intialize Firebase with configuration settings +storage = firebase.storage() # Intialize Storage on the Cloud +ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg'} def add_post(data: AddPostMetadata) -> PostMetadata: """Upload a post's metadata to our database. @@ -30,7 +48,7 @@ def add_post(data: AddPostMetadata) -> PostMetadata: Raises: CreatePostException if an internal server error occurred. """ - + try: post_data = generate_image_metadata(data.image_url) (timestamp, doc) = db.collection(COLLECTION_POSTS) \ @@ -39,14 +57,18 @@ def add_post(data: AddPostMetadata) -> PostMetadata: except: raise CreatePostException() -def upload_blob(source_file_name, destination_blob_name): +def upload(image): """Uploads a file to the bucket.""" - bucket = storage.bucket() - blob = bucket.blob(destination_blob_name) - blob.upload_from_filename(source_file_name) + if image.filename == '': # If File Name is Empty + flash('No selected file') + return redirect(request.url) + if image and allowed_file(image.filename): # If Image Exists And Is An Allowed Extension + fileName = secure_filename(image.filename) # Store File Name + path = "images/" + fileName + imgUrl = storage.child(path).put(image) # Places Image Into Cloud Storage And Stores The JSON Response Of This Command # Prints If File Is Successfully Uploaded - print('File {} uploaded to {}.'.format(source_file_name, destination_blob_name)) - + print("File {} uploaded to 'hashpost'.".format(fileName)) + print("Image URL : " + imgUrl) def get_posts() -> List[PostMetadata]: """Queries all posts that match the given filters. @@ -96,3 +118,8 @@ def delete_post(post_id: str): raise UnknownPostIdException() except: raise DeletePostException() + +# Helper Functions +def allowed_file(filename): + return '.' in filename and \ + filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS From 032960e8c08a48364db32792301aa0ee84772d45 Mon Sep 17 00:00:00 2001 From: Sanjeev Penupala Date: Sun, 3 Nov 2019 23:23:14 -0600 Subject: [PATCH 09/22] Updated main.py --- backend/main.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/backend/main.py b/backend/main.py index b9f9fa1..f82f55b 100644 --- a/backend/main.py +++ b/backend/main.py @@ -1,7 +1,7 @@ import json from flask import Request, make_response from flask_uploads import UploadSet, IMAGES -from posts.posts import get_posts, add_post, upload_blob +from posts.posts import get_posts, add_post, upload from posts.models import AddPostMetadata from posts.exceptions import CreatePostException, GetPostException, UnknownPostIdException from requests import Response @@ -50,22 +50,21 @@ def posts(request: Request) -> Response: response = make_response((error_body, 500)) except: response = _generate_server_error() - elif request.method == 'POST': + elif request.method == 'POST' and 'photo' in request.files: try: # TODO: Upload photo from request to Cloud Storage # TODO: Create new AddPostMetadata object using image URL from Cloud Storage # TODO: Call add_posts with /\ # TODO: Return the result of add_post as JSON - photos = UploadSet('photos', IMAGES) - fileName = photos.save(request.files['photo']) - upload_blob(fileName, "/posts/test") + image = request.files['photo'] + upload(image) except CreatePostException: response = _generate_server_error() else: error_body = { 'errorCode': 'methodNotSupported', - 'message': 'This HTTP method is support supported by this server.' + 'message': 'This HTTP method is not supported by this server.' } response = make_response((error_body, 405)) return response From 2bf81c662ddf2bbb87f8a3e9c2b4200f770e172e Mon Sep 17 00:00:00 2001 From: Sanjeev Penupala Date: Sun, 3 Nov 2019 23:25:41 -0600 Subject: [PATCH 10/22] Removed API Key --- backend/posts/posts.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/posts/posts.py b/backend/posts/posts.py index 4c2d659..c3a6b80 100644 --- a/backend/posts/posts.py +++ b/backend/posts/posts.py @@ -18,7 +18,7 @@ firebaseConfig = { - "apiKey": "AIzaSyCA3OrlBV1UIeSl2AXL0YnlqCE0xMwv_s4", + "apiKey": "", "authDomain": "hashpost.firebaseapp.com", "databaseURL": "https://hashpost.firebaseio.com", "projectId": "hashpost", From 1b97a1e3cee6cfdf41dac171f6367f73710d3147 Mon Sep 17 00:00:00 2001 From: Sanjeev Penupala Date: Sun, 3 Nov 2019 23:28:14 -0600 Subject: [PATCH 11/22] Updated main.py --- .vscode/settings.json | 2 +- backend/main.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 5703476..b76b3a6 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,5 @@ { - "python.pythonPath": "env\\Scripts\\python.exe", + "python.pythonPath": "C:\\Users\\Mr. Jeevs\\AppData\\Local\\Programs\\Python\\Python37-32\\python.exe", "python.linting.pylintEnabled": true, "python.linting.enabled": true } \ No newline at end of file diff --git a/backend/main.py b/backend/main.py index f82f55b..03dfed3 100644 --- a/backend/main.py +++ b/backend/main.py @@ -1,6 +1,5 @@ import json from flask import Request, make_response -from flask_uploads import UploadSet, IMAGES from posts.posts import get_posts, add_post, upload from posts.models import AddPostMetadata from posts.exceptions import CreatePostException, GetPostException, UnknownPostIdException From 0c9c5b4f7de9b1ed5ac4260100a5ff574f54b825 Mon Sep 17 00:00:00 2001 From: Sanjeev Penupala Date: Mon, 4 Nov 2019 21:32:11 -0600 Subject: [PATCH 12/22] Updated .gitingore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 22753a2..e5c4c99 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ .buildlog/ .history .svn/ +.vscode/settings.json # IntelliJ related *.iml From 3294642f8cd242af3f38be2b64a16bd4511b6e80 Mon Sep 17 00:00:00 2001 From: Sanjeev Penupala Date: Wed, 6 Nov 2019 18:04:23 -0600 Subject: [PATCH 13/22] Updated Upload Function --- backend/posts/posts.py | 29 ++++++++++------------------- 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/backend/posts/posts.py b/backend/posts/posts.py index c3a6b80..62293c5 100644 --- a/backend/posts/posts.py +++ b/backend/posts/posts.py @@ -16,23 +16,12 @@ COLLECTION_POSTS = u'/posts' - -firebaseConfig = { - "apiKey": "", - "authDomain": "hashpost.firebaseapp.com", - "databaseURL": "https://hashpost.firebaseio.com", - "projectId": "hashpost", - "storageBucket": "hashpost.appspot.com", - "messagingSenderId": "142555671587", - "appId": "1:142555671587:web:6807408877be551d391596" -} - # Initialize Firebase with Application Default Credentials -initialize_app(name='hashpost') +initialize_app({'storageBucket': 'hashpost.appspot.com'}) +bucket = storage.bucket() + db = firestore.client() -firebase = pyrebase.initialize_app(firebaseConfig) # Intialize Firebase with configuration settings -storage = firebase.storage() # Intialize Storage on the Cloud ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg'} def add_post(data: AddPostMetadata) -> PostMetadata: @@ -64,11 +53,13 @@ def upload(image): return redirect(request.url) if image and allowed_file(image.filename): # If Image Exists And Is An Allowed Extension fileName = secure_filename(image.filename) # Store File Name - path = "images/" + fileName - imgUrl = storage.child(path).put(image) # Places Image Into Cloud Storage And Stores The JSON Response Of This Command - # Prints If File Is Successfully Uploaded - print("File {} uploaded to 'hashpost'.".format(fileName)) - print("Image URL : " + imgUrl) + path = "posts/" + fileName + + blob = bucket.blob(path) # Set Destination + blob.upload_from_file(image) # Upload Image File + + imgUrl = blob.path # Get Image URL Of BLOB + return imgUrl def get_posts() -> List[PostMetadata]: """Queries all posts that match the given filters. From f14cf3467f7d964321f88eeca47e66983a007047 Mon Sep 17 00:00:00 2001 From: Sanjeev Penupala Date: Wed, 6 Nov 2019 18:04:41 -0600 Subject: [PATCH 14/22] Updated get_imagga_data function --- backend/posts/intelligence.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/backend/posts/intelligence.py b/backend/posts/intelligence.py index 414f283..96e6e4e 100644 --- a/backend/posts/intelligence.py +++ b/backend/posts/intelligence.py @@ -1,6 +1,7 @@ import requests import os import base64 +import urllib.parse from typing import List from models import PostMetadata @@ -23,18 +24,21 @@ def __init__(self, confidence: float, tag: str): def _get_imagga_data(image_url: str): - api_key = os.getenv('IMAGGA_API_KEY', None) + api_key = os.getenv('IMAGGA_API_KEY', None) # Get API Key From Environment Variable + url_query = urllib.parse.quote(image_url) # URL Encode Given Image URL + if api_key is None: raise Exception('API key not provided in environment variables!') hashed_key = str(base64.b64encode(api_key.encode('utf-8')), 'utf-8') - data = { - 'image': image_url, - 'limit': TAG_LIMIT, - } + ''' headers = { 'Authorization': f'Basic {hashed_key}' } - response = requests.post(API_ENDPOINT_TAGS, data, headers) + ''' + access_url = f'https://api.imagga.com/v2/tags?image_url={url_query}&limit={TAG_LIMIT}' + + # TODO: Does Not Accept "hashed_key" as a parameter. Figure what "api_secret" is + response = requests.get(access_url, auth=(api_key, api_secret)) response_json = response.json() return response_json From 5a85ffb592eb62a46b3a54bf20944446e4b25d2a Mon Sep 17 00:00:00 2001 From: Sanjeev Penupala Date: Wed, 6 Nov 2019 18:05:16 -0600 Subject: [PATCH 15/22] Updated main.py --- backend/main.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/backend/main.py b/backend/main.py index 03dfed3..19cc155 100644 --- a/backend/main.py +++ b/backend/main.py @@ -3,6 +3,7 @@ from posts.posts import get_posts, add_post, upload from posts.models import AddPostMetadata from posts.exceptions import CreatePostException, GetPostException, UnknownPostIdException +from posts.intelligence import _get_imagga_data from requests import Response REQUEST_ARG_USER_ID = 'userId' @@ -56,8 +57,9 @@ def posts(request: Request) -> Response: # TODO: Call add_posts with /\ # TODO: Return the result of add_post as JSON image = request.files['photo'] - upload(image) - + imgUrl = upload(image) + json_response = _get_imagga_data(imgUrl) + print(json_response) except CreatePostException: response = _generate_server_error() else: From 9c70cf4c3785d9f67b8fbd874080059a4e9f04f0 Mon Sep 17 00:00:00 2001 From: Sanjeev Penupala Date: Wed, 6 Nov 2019 21:45:28 -0600 Subject: [PATCH 16/22] Updated posts.py --- backend/posts/posts.py | 1 - 1 file changed, 1 deletion(-) diff --git a/backend/posts/posts.py b/backend/posts/posts.py index 62293c5..b603e65 100644 --- a/backend/posts/posts.py +++ b/backend/posts/posts.py @@ -4,7 +4,6 @@ import firebase_admin from firebase_admin import firestore, initialize_app, credentials, storage -import pyrebase from flask import Flask, render_template, request, flash, redirect, url_for from werkzeug.utils import secure_filename From 2bbfa5ca4c358040aef748af3180e40192bd3530 Mon Sep 17 00:00:00 2001 From: Sanjeev Penupala Date: Wed, 6 Nov 2019 23:47:29 -0600 Subject: [PATCH 17/22] Updated intelligence.py --- backend/posts/intelligence.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/posts/intelligence.py b/backend/posts/intelligence.py index 96e6e4e..4b56875 100644 --- a/backend/posts/intelligence.py +++ b/backend/posts/intelligence.py @@ -38,7 +38,7 @@ def _get_imagga_data(image_url: str): access_url = f'https://api.imagga.com/v2/tags?image_url={url_query}&limit={TAG_LIMIT}' # TODO: Does Not Accept "hashed_key" as a parameter. Figure what "api_secret" is - response = requests.get(access_url, auth=(api_key, api_secret)) + response = requests.get(access_url, auth=(api_key, hashed_key)) response_json = response.json() return response_json From 4fdfc7bdf3dcd869950e0937b129dfdedc7fe307 Mon Sep 17 00:00:00 2001 From: Sanjeev Penupala Date: Sat, 16 Nov 2019 16:13:09 -0600 Subject: [PATCH 18/22] remove settings.json --- .vscode/settings.json | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index b76b3a6..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "python.pythonPath": "C:\\Users\\Mr. Jeevs\\AppData\\Local\\Programs\\Python\\Python37-32\\python.exe", - "python.linting.pylintEnabled": true, - "python.linting.enabled": true -} \ No newline at end of file From 29990a80318f6522df7d15084ff0cb2f861db34c Mon Sep 17 00:00:00 2001 From: Sanjeev Penupala Date: Sat, 16 Nov 2019 16:14:30 -0600 Subject: [PATCH 19/22] Updated main.py --- backend/main.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/backend/main.py b/backend/main.py index 19cc155..d822b36 100644 --- a/backend/main.py +++ b/backend/main.py @@ -52,14 +52,10 @@ def posts(request: Request) -> Response: response = _generate_server_error() elif request.method == 'POST' and 'photo' in request.files: try: - # TODO: Upload photo from request to Cloud Storage - # TODO: Create new AddPostMetadata object using image URL from Cloud Storage - # TODO: Call add_posts with /\ - # TODO: Return the result of add_post as JSON image = request.files['photo'] imgUrl = upload(image) json_response = _get_imagga_data(imgUrl) - print(json_response) + return json_response except CreatePostException: response = _generate_server_error() else: From eb49e9df08a707ec7ae2c22cd9d80363b9f4ecec Mon Sep 17 00:00:00 2001 From: Sanjeev Penupala Date: Sat, 16 Nov 2019 16:14:52 -0600 Subject: [PATCH 20/22] Loaded dataset (trial) --- backend/posts/intelligence.py | 72 ++++++++++++++++++++++++++++++++--- 1 file changed, 67 insertions(+), 5 deletions(-) diff --git a/backend/posts/intelligence.py b/backend/posts/intelligence.py index 4b56875..451534b 100644 --- a/backend/posts/intelligence.py +++ b/backend/posts/intelligence.py @@ -1,3 +1,6 @@ +from __future__ import absolute_import, division, print_function, unicode_literals +import tensorflow as tf + import requests import os import base64 @@ -5,10 +8,34 @@ from typing import List from models import PostMetadata -API_BASE = 'https://api.imagga.com/v2' +import IPython.display as display +from PIL import Image +import numpy as np +import matplotlib.pyplot as plt +import pathlib -API_ENDPOINT_TAGS = f'{API_BASE}/tags' +AUTOTUNE = tf.data.experimental.AUTOTUNE + +# TF Model Variables +# data_dir = pathlib.Path("C:/Users/Mr. Jeevs/.keras/datasets/flower_photos") +data_dir = pathlib.Path("C:/Users/Mr. Jeevs/.keras/datasets/train2017") +print(data_dir) + +image_count = len(list(data_dir.glob('*.jpg'))) +print(image_count) + +''' +CLASS_NAMES = np.array([item.name for item in data_dir.glob('*') if item.name != "LICENSE.txt"]) +print(CLASS_NAMES) +roses = list(data_dir.glob('roses/*')) +for image_path in roses[:3]: + display.display(Image.open(str(image_path))) +''' + +# Imagga Variables +API_BASE = 'https://api.imagga.com/v2' +API_ENDPOINT_TAGS = f'{API_BASE}/tags' TAG_LIMIT = 10 class PostPredictionData: @@ -25,11 +52,10 @@ def __init__(self, confidence: float, tag: str): def _get_imagga_data(image_url: str): api_key = os.getenv('IMAGGA_API_KEY', None) # Get API Key From Environment Variable - url_query = urllib.parse.quote(image_url) # URL Encode Given Image URL - if api_key is None: raise Exception('API key not provided in environment variables!') hashed_key = str(base64.b64encode(api_key.encode('utf-8')), 'utf-8') + url_query = urllib.parse.quote(image_url) # URL Encode Given Image URL ''' headers = { 'Authorization': f'Basic {hashed_key}' @@ -42,8 +68,8 @@ def _get_imagga_data(image_url: str): response_json = response.json() return response_json - def generate_image_metadata(image_url: str) -> List[PostPredictionData]: + """Generate image metadata. Returns: @@ -57,3 +83,39 @@ def generate_image_metadata(image_url: str) -> List[PostPredictionData]: prediction = PostPredictionData(confidence, tag) predictions.append(prediction) return predictions + +def __get__tag__data(image_url: str): + list_ds = tf.data.Dataset.list_files(str(data_dir/'*')) + + for f in list_ds.take(5): + print(f.numpy()) + + # Set `num_parallel_calls` so multiple images are loaded/processed in parallel. + labeled_ds = list_ds.map(process_path, num_parallel_calls=AUTOTUNE) + + for image, label in labeled_ds.take(1): + print("Image shape: ", image.numpy().shape) + print("Label: ", label.numpy()) + +def get_label(file_path): + # convert the path to a list of path components + parts = tf.strings.split(file_path, os.path.sep) + # The second to last is the class-directory + return parts[-2] == CLASS_NAMES + +def decode_img(img): + # convert the compressed string to a 3D uint8 tensor + img = tf.image.decode_jpeg(img, channels=3) + # Use `convert_image_dtype` to convert to floats in the [0,1] range. + img = tf.image.convert_image_dtype(img, tf.float32) + # resize the image to the desired size. + return tf.image.resize(img, [IMG_WIDTH, IMG_HEIGHT]) + +def process_path(file_path): + label = get_label(file_path) + # load the raw data from the file as a string + img = tf.io.read_file(file_path) + img = decode_img(img) + return img, label + +__get__tag__data("masala") \ No newline at end of file From f084805ab6ad73afd6e639014b8773f878dd7ab2 Mon Sep 17 00:00:00 2001 From: Sanjeev Penupala Date: Mon, 18 Nov 2019 21:28:17 -0600 Subject: [PATCH 21/22] Updated .gitignore --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index e5c4c99..f54378d 100644 --- a/.gitignore +++ b/.gitignore @@ -8,7 +8,8 @@ .buildlog/ .history .svn/ -.vscode/settings.json +*.json + # IntelliJ related *.iml From f234535898f0056e04c77c40dab75992bbbb59d1 Mon Sep 17 00:00:00 2001 From: Sanjeev Penupala Date: Mon, 18 Nov 2019 21:28:28 -0600 Subject: [PATCH 22/22] Added pre-trained model --- backend/posts/intelligence.py | 67 +++++++++++++++++++++++++---------- 1 file changed, 48 insertions(+), 19 deletions(-) diff --git a/backend/posts/intelligence.py b/backend/posts/intelligence.py index 451534b..a80fb33 100644 --- a/backend/posts/intelligence.py +++ b/backend/posts/intelligence.py @@ -7,6 +7,7 @@ import urllib.parse from typing import List from models import PostMetadata +import json import IPython.display as display from PIL import Image @@ -15,6 +16,8 @@ import pathlib AUTOTUNE = tf.data.experimental.AUTOTUNE +IMG_WIDTH = 299 +IMG_HEIGHT = 299 # TF Model Variables # data_dir = pathlib.Path("C:/Users/Mr. Jeevs/.keras/datasets/flower_photos") @@ -84,24 +87,45 @@ def generate_image_metadata(image_url: str) -> List[PostPredictionData]: predictions.append(prediction) return predictions -def __get__tag__data(image_url: str): - list_ds = tf.data.Dataset.list_files(str(data_dir/'*')) +def __get__tag__data(): + with open("instances_train2017.json") as f: + train_data = json.load(f) + images = train_data["images"] + annotations = train_data["annotations"] + + fileNames = [] + catIDs = [] + + for i in images[:10]: + fileNames.append(i["file_name"]) + for j in annotations: + if(i["id"] == j["image_id"]): + catIDs.append(j["category_id"]) + break + + print(fileNames) + print(catIDs) + + list_ds = tf.data.Dataset.from_tensor_slices((fileNames, catIDs)) + + print("8=========================================================>") for f in list_ds.take(5): - print(f.numpy()) + print(f) + print("8=========================================================>") # Set `num_parallel_calls` so multiple images are loaded/processed in parallel. - labeled_ds = list_ds.map(process_path, num_parallel_calls=AUTOTUNE) + labeled_ds = list_ds.map(process_img_label, num_parallel_calls=AUTOTUNE) + print(labeled_ds) + print("8=========================================================>") + thing = labeled_ds.take(1) + print(thing) - for image, label in labeled_ds.take(1): - print("Image shape: ", image.numpy().shape) - print("Label: ", label.numpy()) + m = tf.keras.Sequential([ + hub.KerasLayer("https://tfhub.dev/google/imagenet/inception_resnet_v2/classification/4", trainable=True, arguments=dict(batch_norm_momentum=0.99)) + ]) -def get_label(file_path): - # convert the path to a list of path components - parts = tf.strings.split(file_path, os.path.sep) - # The second to last is the class-directory - return parts[-2] == CLASS_NAMES + m.build([None, 299, 299, 3]) # Batch input shape. def decode_img(img): # convert the compressed string to a 3D uint8 tensor @@ -111,11 +135,16 @@ def decode_img(img): # resize the image to the desired size. return tf.image.resize(img, [IMG_WIDTH, IMG_HEIGHT]) -def process_path(file_path): - label = get_label(file_path) - # load the raw data from the file as a string - img = tf.io.read_file(file_path) - img = decode_img(img) - return img, label +def process_img_label(fileName, label): + # load the raw data from the file as a string + img = tf.io.read_file(f'C:/Users/Mr. Jeevs/.keras/datasets/train2017/{fileName}') + img = decode_img(img) + print(img) + print(fileName) + print(label) + return img, label + +def get_file_name(data): + return data["file_name"] -__get__tag__data("masala") \ No newline at end of file +__get__tag__data() \ No newline at end of file