diff --git a/backend/.gitignore b/backend/.gitignore new file mode 100644 index 0000000..6d3c17f --- /dev/null +++ b/backend/.gitignore @@ -0,0 +1,2 @@ +data/ +env/ \ No newline at end of file diff --git a/backend/main.py b/backend/main.py index 706013c..b171dc4 100644 --- a/backend/main.py +++ b/backend/main.py @@ -8,6 +8,7 @@ REQUEST_ARG_POST_ID = 'postId' REQUEST_ARG_IMAGE_URL = 'imageUrl' + def posts(request: Request) -> Response: """Return data related to posts. @@ -34,24 +35,24 @@ 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 occurred. 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 == '' + image_url = request.args.get(REQUEST_ARG_IMAGE_URL) + if image_url is None or image_url == '': add_post_metadata = AddPostMetadata() # TODO: Handle uploads except CreatePostException: diff --git a/backend/ml/models.py b/backend/ml/models.py new file mode 100644 index 0000000..d4a019d --- /dev/null +++ b/backend/ml/models.py @@ -0,0 +1,55 @@ +from typing import List + + +class HashtagPrediction: + """A single prediction for a hashtag; a mapping of a hashtag to its confidence. + + Attributes: + hashtag (str): A possibly relevant hashtag for a piece of content + confidence (float): The probability that the hashtag is relevant + """ + + def __init__(self, hashtag: str, confidence: float): + self.hashtag = hashtag + self.confidence = confidence + + +class ClassPrediction: + """A single prediction for an object's class; a mapping of a class to its confidence. + + Attributes: + object_class (str): The category of + confidence (float): The probability that the hashtag is relevant + """ + + def __init__(self, object_class: str, confidence: float): + self.object_class = object_class + self.confidence = confidence + + +class TextPostPredictionResult: + def __init__(self, hashtags: List[HashtagPrediction], entities: List[str]): + pass + + +class ImagePostPredictionResult: + """ + Attributes: + hashtags (List[HashtagPrediction]): A list of eight relevant hashtags + classes (List[str]) + """ + + def __init__(self, hashtags: List[HashtagPrediction], classes: List[str]): + pass + + +class PredictionSuggestion: + """A suggestion to re-tag a data point. + + Attributes: + prediction_id (str): + new_value (dict): A json payload containing new data + """ + + def __init__(self, prediction_id: str, new_value: dict): + pass diff --git a/backend/posts/intelligence.py b/backend/posts/intelligence.py index 50a28fb..73dafea 100644 --- a/backend/posts/intelligence.py +++ b/backend/posts/intelligence.py @@ -2,7 +2,6 @@ import os import base64 from typing import List -from models import PostMetadata API_BASE = 'https://api.imagga.com/v2' @@ -45,11 +44,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..2505e1e 100644 --- a/backend/posts/models.py +++ b/backend/posts/models.py @@ -1,19 +1,59 @@ +"""Domain-layer abstractions for data models that the client will accept.""" -class PostMetadata: +from typing import List, Dict, Tuple + + +class ImagePost: """A data container for post attributes. This is used to store post data after being generated using machine learning. + + Attributes: + uid (str): A unique identifier for this post + image_url (str): The downloadable location + hashtags (List[Dict[str, int]]): A list of hashtags and their confidence ratings + captions (List[Dict[str, int]]): A list of captions and their confidence ratings """ - def __init__(self, id: str, image_url: str, hashtags: list, caption: str): - self.id = id + def __init__(self, uid: str, image_url: str, hashtags: List[Dict[str, int]], + captions: List[Dict[str, int]], upload_timestamp: int, segments: List[Tuple[int, int]]): + self.uid = uid self.image_url = image_url self.hashtags = hashtags - self.caption = caption + self.captions = captions + self.upload_timestamp = upload_timestamp + + +class TextPost: + """A data container for text post attributes.""" + + def __init__(self, uid: str, content: str, hashtags: List[Dict[str, int]], + captions: List[Dict[str, int]], upload_timestamp: int): + self.uid = uid + self.content = content + self.hashtags = hashtags + self.captions = captions + self.upload_timestamp = upload_timestamp -class AddPostMetadata: +class AddTextPostMetadata: + """Metadata needed to upload a new text post to the backend. + + Attributes: + text (str): The contents of the post + """ + + def __init__(self, text: str): + self.text = text + + +class AddImagePostMetadata: + """Metadata needed to upload a new image post to the database. + + Attributes: + image_url (str): A URL to this image stored in Cloud Storage + """ def ___init__(self, image_url: str): self.image_url = image_url diff --git a/backend/posts/posts.py b/backend/posts/posts.py index e6bdeec..793305f 100644 --- a/backend/posts/posts.py +++ b/backend/posts/posts.py @@ -61,12 +61,13 @@ def get_post_by_id(post_id: str) -> PostMetadata: A PostMetadata object containing post data. """ try: - doc = db.collection(COLLECTION_POSTS) \ + posts = db.collection(COLLECTION_POSTS) \ .document(post_id) \ .get() + # TODO: Validate that this post is text if len(posts) < 1: raise UnknownPostIdException() - return + return posts[0] except NotFound: raise UnknownPostIdException() except: