async def create_comment(res: Response, comment: dict) -> Dict[str, Any]: """Create new comment\n Args:\n comment (dict): Comment to create\n Returns:\n Dict[str, Any]: Comment created\n """ data = { "success": True, "comment": {}, "detail": "Comment successfully created", } comment_owner = API_functools.get_or_default( await Person.filter(pk=comment.get("user", 0)), 0, None) if comment_owner is None: res.status_code = status.HTTP_404_NOT_FOUND data["success"] = False data["detail"] = "Comment owner doesn't exist" return data comment["user"] = comment_owner data["comment"] = API_functools.tortoise_to_dict(await Comment.create(**comment)) return jsonable_encoder(data)
async def users( request: Request, res: Response, limit: Optional[int] = 20, offset: Optional[int] = 0, sort: Optional[str] = "id:asc", ) -> Optional[List[Dict[str, Any]]]: """Get all users or some of them using 'offset' and 'limit' Args: limit (int, optional): max number of returned users. Defaults to 100. offset (int, optional): first user to return (use with limit). Defaults to 1. sort (str, optional): the order of the result. attribute:(asc {ascending} or desc {descending}). Defaults to "id:asc". Returns: Optional[List[Dict[str, Any]]]: list of users found or Dict with error """ response = { "success": False, "users": [], } order_by = API_functools.valid_order(Person, sort) if order_by is None: res.status_code = status.HTTP_400_BAD_REQUEST return { **response, "detail": "Invalid sort parameters. it must match \ attribute:order. ex: id:asc or id:desc", } if offset < 0 or limit < 1: res.status_code = status.HTTP_400_BAD_REQUEST return { **response, "detail": "Invalid values: offset(>=0) or limit(>0)", } nb_users = await Person.all().count() users = await Person_Pydantic.from_queryset( Person.all().limit(limit).offset(offset).order_by(order_by)) if len(users) == 0: res.status_code = status.HTTP_404_NOT_FOUND return {**response, "detail": "Not Found"} return API_functools.manage_next_previous_page(request, users, nb_users, limit, offset)
async def test_instance_of(self): obj = await Person.create(**INIT_DATA.get("person", [])[0]) elements = { "Hello World": str, 1: int, obj: Person, (1, 2, 3, 4): tuple, } for el, instance in elements.items(): assert API_functools.instance_of(el, instance) is True assert API_functools.instance_of("Hello", int) is False
async def filter_comments( req: Request, res: Response, max_comments: int, data: Optional[list[dict]] = None, filters: Optional[dict] = None, offset: Optional[int] = 20, limit: Optional[int] = 0, sort: Optional[str] = "id:asc", ): response = { "success": False, "comments": [], } if data is None: order_by = API_functools.valid_order(Comment, sort) if order_by is None: res.status_code = status.HTTP_400_BAD_REQUEST return { **response, "detail": invalid_sort_detail, } if offset < 0 or limit < 1: res.status_code = status.HTTP_400_BAD_REQUEST return { **response, "detail": "Invalid values: offset(>=0) or limit(>0)", } if data is None: comments = await API_functools.add_owner_fullname( jsonable_encoder(await ( Comment.all() if filters is None else Comment.filter(**filters) ).prefetch_related("vote").prefetch_related("children").annotate( votes=Count("vote", distinct=True) ).annotate(nb_children=Count("children", distinct=True) ).limit(limit).offset(offset).order_by(order_by).values( *API_functools.get_attributes(Comment), "votes", "nb_children"))) else: comments = data if len(comments) == 0: res.status_code = status.HTTP_404_NOT_FOUND return {**response, "detail": "Not Found"} return API_functools.manage_next_previous_page(req, comments, max_comments, limit, offset, data_type="comments")
async def users_by_attribute(res: Response, user_attribute: Any, value: Any) -> List[Dict[str, Any]]: """Get user by attribute except Args: user_attribute (Any): user's attribute you can combine two or more attributes with keywords "Or", "And" ex: idOremail, genderAndemail Returns: List[Dict[str, Any]]: List of users found """ response = {"success": False, "users": []} lower_user_attribute = user_attribute.lower() if ("and" not in lower_user_attribute and "or" not in lower_user_attribute ) and not API_functools.is_attribute_of(user_attribute, Person): res.status_code = status.HTTP_400_BAD_REQUEST return { **response, "detail": f""" Invalid attribute filter. Try with: {API_functools.get_attributes(Person)} """, } query_builder = Database.query_filter_builder(user_attribute, value) persons = await Person_Pydantic.from_queryset( Person.filter(*query_builder).order_by("id")) if len(persons) == 0: res.status_code = status.HTTP_404_NOT_FOUND return {**response, "detail": "Not Found"} return {"success": True, "users": persons}
async def fix_comment(res: Response, comment_ID: int, comment_data: PartialComment) -> Dict[str, Any]: """Fix some comment attributes according to PartialComment class\n Args:\n comment_ID (int): user ID\n comment_data (PartialComment): new data\n Returns:\n Dict[str, Any]: contains updated Comment data or error\n """ response = {"success": True, "comment": {}} comment_found = await Comment.get_or_none(id=comment_ID) if comment_found is None: res.status_code = status.HTTP_404_NOT_FOUND response["success"] = False response["detail"] = f"Comment with ID {comment_ID} doesn't exist." return response comment_updated = comment_found.update_from_dict(comment_data.__dict__) await comment_updated.save() response["detail"] = "Comment successfully patched" response["comment"] = API_functools.tortoise_to_dict(comment_updated) return jsonable_encoder(response)
async def update_comment(res: Response, comment_ID: int, comment_data: CommentBaseModel) -> Dict[str, Any]: """Update comment attributes according to CommentBaseModel class\n Args:\n comment_ID (int): comment to update\n comment_data (CommentBaseModel): new comment data\n Returns:\n Dict[str, Any]: contains comment new data or error\n """ response = {"success": True, "comment": {}} new_owner = await Person.get_or_none(id=comment_data.user) if new_owner is None: res.status_code = status.HTTP_404_NOT_FOUND response["success"] = False response["detail"] = "Comment owner doesn't exist." return response comment_found = await Comment.get_or_none(id=comment_ID) if comment_found is None: res.status_code = status.HTTP_404_NOT_FOUND response["success"] = False response["detail"] = f"Comment with ID {comment_ID} doesn't exist." return response comment_data.user = new_owner comment_updated = comment_found.update_from_dict(comment_data.__dict__) await comment_updated.save() response["detail"] = "Comment successfully updated" response["comment"] = API_functools.tortoise_to_dict(comment_updated) return jsonable_encoder(response)
async def json_children( self, fields: list[str] = [], order_by: str = "id", deep: bool = False ) -> dict[str, Any]: """return all comments child of current comment (comments that reply to the current comment) Args: fields (list[str]): list of fields to return, default [] order_by (str, optional): ordering return. Defaults to "id". deep (bool: optional): get/not also deep children (response to child's comment) Returns: dict[str, Any]: data found """ from app.api.utils import API_functools fields = fields if len(fields) > 0 else API_functools.get_attributes(Comment) filter_key = {"top_parent_id" if deep else "parent_id": self.id} return await API_functools.add_owner_fullname( jsonable_encoder( await ( Comment.filter(**filter_key) .prefetch_related("vote") .annotate(votes=Count("vote", distinct=True)) .annotate(nb_children=Count("children", distinct=True)) .order_by(order_by) .values(*fields, "votes", "nb_children") ) ) )
def test_get_or_default(self): list_object = ( { "name": "John Doe" }, { "name": "Bob Doe" }, { "name": "Alice Doe" }, ) for index, obj in enumerate(list_object): assert API_functools.get_or_default(list_object, index, None) == obj assert API_functools.get_or_default(list_object, len(list_object), None) is None
def test_valid_order(self): # valid order must consist of an attribute of the Person class # and the word "asc" or "desc" orders = [ ("first_name", None), ("notattributte:asc", None), ("id:notvalidkeyword", None), ("first_name:asc", "first_name"), ("first_name:desc", "-first_name"), ] for order in orders: assert API_functools.valid_order(Person, order[0]) == order[1]
async def users_by_ID(res: Response, user_ID: int) -> Dict[str, Any]: """Get user by ID Args: user_ID (int): user ID Returns: Dict[str, Any]: user found or Error """ user = await Person_Pydantic.from_queryset(Person.filter(pk=user_ID)) data = { "success": True, "user": API_functools.get_or_default(user, 0, {}), } if not API_functools.instance_of(data["user"], Person): res.status_code = status.HTTP_404_NOT_FOUND data["success"] = False data["detail"] = "Not Found" return data
def test_comment_attributes(self): expected_attrs = ( "id", "added", "edited", "content", "parent_id", "user_id", "top_parent_id", ) actual_attrs = API_functools.get_attributes(Comment) for attr in expected_attrs: assert attr in actual_attrs assert len(expected_attrs) == len(actual_attrs)
async def test__insert_default_data(self): # Insert a Person user_to_create = INIT_DATA.get("person", [])[0] user_created = await API_functools._insert_default_data( "person", user_to_create) assert API_functools.instance_of(user_created, Person) is True # Insert a Comment comment_to_create = { **INIT_DATA.get("comment", [])[0], "user": user_created.id, } comment_created = await API_functools._insert_default_data( "comment", comment_to_create) assert API_functools.instance_of(comment_created, Comment) is True # Insert a Vote vote_to_create = { "user": user_created.id, "comment": comment_created.id, } vote_created = await API_functools._insert_default_data( "vote", vote_to_create) assert API_functools.instance_of(vote_created, Vote) is True
def test_manage_next_previous_page(self): scope = {"type": "http", "path": "/", "method": "GET"} request = Request(scope) scenes = [ { "data": (0, 5, 0), # nb_total_data, limit, offset "expected": { "next": None, "previous": None, "success": False, "users": [], }, }, { "data": (15, 5, 5), "expected": { "next": "/?limit=5&offset=10", "previous": "/?limit=5&offset=0", "success": False, "users": [], }, }, { "data": (10, 5, 0), "expected": { "next": "/?limit=5&offset=5", "previous": None, "success": False, "users": [], }, }, { "data": (10, 5, 5), "expected": { "next": None, "previous": "/?limit=5&offset=0", "success": False, "users": [], }, }, ] for scene in scenes: # scene 1 next=None, previous=None actual = API_functools.manage_next_previous_page(request, [], *scene["data"], data_type="users") assert actual == scene["expected"]
async def test_tortoise_to_dict(self): actual = API_functools.tortoise_to_dict( await Person(**INIT_DATA.get("person", [])[0])) actual["date_of_birth"] = jsonable_encoder(actual["date_of_birth"]) assert actual == { "avatar": avatar, "company": "Edgetag", "country_of_birth": "Egypt", "date_of_birth": "1978-04-15", "email": "*****@*****.**", "first_name": "Shalom", "gender": "Male", "id": None, "is_admin": True, "job": "Compensation Analyst", "last_name": "Handes", }
def test_user_attributes(self): expected_attrs = ( "id", "is_admin", "first_name", "last_name", "email", "gender", "avatar", "job", "company", "date_of_birth", "country_of_birth", ) actual_attrs = API_functools.get_attributes(Person) for attr in expected_attrs: assert attr in actual_attrs assert len(expected_attrs) == len(actual_attrs)
def test_get_attributes(self): # Test get_attribute with kwargs user_attributes = ( "id", "is_admin", "name", "email", "gender", "avatar", "job", "company", "date_of_birth", "country_of_birth", "full_name", ) assert (API_functools.get_attributes( Person, replace={"first_name": "name"}, add=("full_name", ), exclude=("last_name", ), ) == user_attributes)
async def delete_comment(res: Response, comment_ID: int) -> Dict[str, Any]: """Delete a comment\n Args:\n comment_ID (int): comment to delete\n Returns:\n Dict[str, Any]: contains deleted comment data or error\n """ response = {"success": False, "comment": {}} comment_found = await Comment.get_or_none(id=comment_ID) if comment_found is None: res.status_code = status.HTTP_404_NOT_FOUND response["detail"] = f"Comment with ID {comment_ID} doesn't exist" return response await comment_found.delete() response["success"] = True response["comment"] = API_functools.tortoise_to_dict(comment_found) response["detail"] = f"Comment {comment_ID} deleted successfully тнР" return jsonable_encoder(response)
def test_strip_spaces(self): s = API_functools.strip_spaces(" Hello World ") assert s == "Hello World"
async def comments_by_ID( req: Request, res: Response, comment_ID: int, children: bool = False, limit: Optional[int] = 20, offset: Optional[int] = 0, sort: Optional[str] = "id:asc", ) -> Dict[str, Any]: """Get comment by ID Args: comment_ID (int): comment ID children (bool): get current comment children limit (int, optional): max number of returned comments. Defaults to 100. offset (int, optional): first comment to return (use with limit). Defaults to 1. sort (str, optional): the order of the result. attribute:(asc {ascending} or desc {descending}). Defaults to "id:asc". Returns: Dict[str, Any]: contains comment found """ key, value = ("comment", {}) if not children else ("comments", []) response = {"success": True, key: value, "detail": "Successful operation"} if not await Comment.exists(pk=comment_ID): res.status_code = status.HTTP_404_NOT_FOUND response["success"] = False response["detail"] = "Not Found" return response if children: order_by = API_functools.valid_order(Comment, sort) if order_by is None: res.status_code = status.HTTP_400_BAD_REQUEST return { **response, "success": False, "detail": invalid_sort_detail, } comment = await Comment.filter(pk=comment_ID).first() comments = await comment.json_children(order_by=order_by) response["comments"] = comments return await filter_comments( req, res, len(response["comments"]), data=response["comments"], offset=offset, limit=limit, sort=sort, ) else: response["comment"] = API_functools.get_or_default( await API_functools.add_owner_fullname([ API_functools.get_or_default( jsonable_encoder(await Comment.filter( pk=comment_ID ).prefetch_related("vote").prefetch_related( "children").annotate( votes=Count("vote", distinct=True) ).annotate(nb_children=Count("children", distinct=True) ).values( *API_functools.get_attributes(Comment), "votes", "nb_children", )), index=0, default={}, ) ]), index=0, default={}, ) return response
def test_is_attribute_of(self): for attr in API_functools.get_attributes(Person): assert API_functools.is_attribute_of(attr, Person) is True assert API_functools.is_attribute_of("invalid", Person) is False