From 80786c4965964f7a0a8ee63cd1f14a4d98ff3088 Mon Sep 17 00:00:00 2001 From: simonwt Date: Fri, 3 Apr 2026 00:50:38 +0100 Subject: [PATCH] Vote casting --- development.md | 8 ++--- src/trustcafeapiwrapper/jobs/vote/__init__.py | 1 + src/trustcafeapiwrapper/jobs/vote/votecast.py | 12 +++++++ src/trustcafeapiwrapper/utils/__init__.py | 1 + .../utils/get_child_spksk_from_paths.py | 34 ++++++++++++++++++ .../wrappers/reaction/react.py | 35 ++----------------- .../wrappers/vote/__init__.py | 1 + .../wrappers/vote/votecast.py | 21 +++++++++++ testing.py | 12 +++++-- tests/utils/get_child_spksk_from_paths.py | 24 +++++++++++++ tests/wrappers/vote.py | 34 ++++++++++++++++++ unittests.py | 11 ++++-- 12 files changed, 152 insertions(+), 42 deletions(-) create mode 100644 src/trustcafeapiwrapper/jobs/vote/__init__.py create mode 100644 src/trustcafeapiwrapper/jobs/vote/votecast.py create mode 100644 src/trustcafeapiwrapper/utils/get_child_spksk_from_paths.py create mode 100644 src/trustcafeapiwrapper/wrappers/vote/__init__.py create mode 100644 src/trustcafeapiwrapper/wrappers/vote/votecast.py create mode 100644 tests/utils/get_child_spksk_from_paths.py create mode 100644 tests/wrappers/vote.py diff --git a/development.md b/development.md index 0e6dee4..f5126a1 100644 --- a/development.md +++ b/development.md @@ -2,10 +2,10 @@ ## Debates -1. Not sure about the name `jobs` and `run_job`. Requests is an +1. Hating the name `jobs` and `run_job`. Requests is an existing package dependency though. `tasks` seems to broard. I'm sure there's probably an already existing name that I can't articulate how -to find. +to find. `apirequests`, `rqsts`, `apicalls`? 2. Should these `job` be called with dot notation? Or with slashes? Or something else? Is it okay to have these as strings? 3. Should we do validation in the `jobs` or let the server do that all? @@ -14,5 +14,5 @@ something else? Is it okay to have these as strings? ## ToDo: 1. Make more jobs -2. Make wrappers to make it less cumbersome when posting etc -3. Publish to PyPi or whatever \ No newline at end of file +2. Make more wrappers +3. Write more documentation \ No newline at end of file diff --git a/src/trustcafeapiwrapper/jobs/vote/__init__.py b/src/trustcafeapiwrapper/jobs/vote/__init__.py new file mode 100644 index 0000000..c21f33f --- /dev/null +++ b/src/trustcafeapiwrapper/jobs/vote/__init__.py @@ -0,0 +1 @@ +from .votecast import votecast \ No newline at end of file diff --git a/src/trustcafeapiwrapper/jobs/vote/votecast.py b/src/trustcafeapiwrapper/jobs/vote/votecast.py new file mode 100644 index 0000000..7264c6e --- /dev/null +++ b/src/trustcafeapiwrapper/jobs/vote/votecast.py @@ -0,0 +1,12 @@ +def votecast(API, payload: dict) -> dict: + """ + Casts a vote in the API. + + Args: + payload (dict): The data for the vote. + + Returns: + dict: The vote data. + """ + vote_data = API.make_request("POST", "content", "votecast", data=payload, authenticate=True) + return vote_data \ No newline at end of file diff --git a/src/trustcafeapiwrapper/utils/__init__.py b/src/trustcafeapiwrapper/utils/__init__.py index bc12c4e..f0d7955 100644 --- a/src/trustcafeapiwrapper/utils/__init__.py +++ b/src/trustcafeapiwrapper/utils/__init__.py @@ -2,3 +2,4 @@ from .get_parent_pksk_from_path import get_parent_pksk_from_path from .get_post_pksk import get_post_pksk from .make_comment_sk import make_comment_sk from .make_post_sk import make_post_sk +from .get_child_spksk_from_paths import get_child_spksk_from_paths \ No newline at end of file diff --git a/src/trustcafeapiwrapper/utils/get_child_spksk_from_paths.py b/src/trustcafeapiwrapper/utils/get_child_spksk_from_paths.py new file mode 100644 index 0000000..839bbdb --- /dev/null +++ b/src/trustcafeapiwrapper/utils/get_child_spksk_from_paths.py @@ -0,0 +1,34 @@ +from trustcafeapiwrapper.utils import get_parent_pksk_from_path, get_post_pksk, make_comment_sk, make_post_sk + +def get_child_spksk_from_paths(parent_path: str, item_path: str): + + ''' + + If it's a post we want to create a pk/sk like: + '{entity}#{parent_slug}' / 'post#{post_slug}' + + If it's a comment we want to create a pk/sk like: + 'post#{post_slug}' / 'comment#{comment_slug}' + + + ''' + slug = item_path.split('/')[-1] + if item_path.startswith('/post'): + # It's a reaction on a post + top_level_parent_pk = get_parent_pksk_from_path(parent_path) + item_pksk = get_post_pksk(top_level_parent_pk, item_path) + entity = 'post' + else: + # It's a reaction on a comment + item_pksk = { + 'pk': make_post_sk(parent_path), + 'sk': make_comment_sk(item_path) + } + entity = 'comment' + + return { + "pk": item_pksk.get('pk', None), + "sk": item_pksk.get('sk', None), + "entity": entity, + "slug": slug + } \ No newline at end of file diff --git a/src/trustcafeapiwrapper/wrappers/reaction/react.py b/src/trustcafeapiwrapper/wrappers/reaction/react.py index 3c25408..26bc2e0 100644 --- a/src/trustcafeapiwrapper/wrappers/reaction/react.py +++ b/src/trustcafeapiwrapper/wrappers/reaction/react.py @@ -1,5 +1,5 @@ -from trustcafeapiwrapper.utils import get_parent_pksk_from_path, get_post_pksk, make_comment_sk, make_post_sk +from trustcafeapiwrapper.utils import get_child_spksk_from_paths def react(reaction_type: str, parent_path: str, item_path: str): """ React to something. ie a post or a comment. @@ -12,40 +12,11 @@ def react(reaction_type: str, parent_path: str, item_path: str): dict: A dictionary containing the job name and payload for creating the post that will be processed by the API client wrapper function. """ - - ''' - - If it's a post we want to create a pk/sk like: - '{entity}#{parent_slug}' / 'post#{post_slug}' - - If it's a comment we want to create a pk/sk like: - 'post#{post_slug}' / 'comment#{comment_slug}' - - - ''' - slug = item_path.split('/')[-1] - if item_path.startswith('/post'): - # It's a reaction on a post - top_level_parent_pk = get_parent_pksk_from_path(parent_path) - item_pksk = get_post_pksk(top_level_parent_pk, item_path) - entity = 'post' - else: - # It's a reaction on a comment - item_pksk = { - 'pk': make_post_sk(parent_path), - 'sk': make_comment_sk(item_path) - } - entity = 'comment' - + parent = get_child_spksk_from_paths(parent_path, item_path) return { "job_function": "reaction.reacttosomething", "payload": { "reaction": reaction_type, - "parent": { - "pk": item_pksk.get('pk', None), - "sk": item_pksk.get('sk', None), - "entity": entity, - "slug": slug - } + "parent": parent } } diff --git a/src/trustcafeapiwrapper/wrappers/vote/__init__.py b/src/trustcafeapiwrapper/wrappers/vote/__init__.py new file mode 100644 index 0000000..c21f33f --- /dev/null +++ b/src/trustcafeapiwrapper/wrappers/vote/__init__.py @@ -0,0 +1 @@ +from .votecast import votecast \ No newline at end of file diff --git a/src/trustcafeapiwrapper/wrappers/vote/votecast.py b/src/trustcafeapiwrapper/wrappers/vote/votecast.py new file mode 100644 index 0000000..742d24b --- /dev/null +++ b/src/trustcafeapiwrapper/wrappers/vote/votecast.py @@ -0,0 +1,21 @@ +from trustcafeapiwrapper.utils import get_child_spksk_from_paths + +def votecast(vote: str, parent_path: str, item_path: str): + """ + Creates a new vote in the API. + + Args: + + Returns: + dict: A dictionary containing the job name and payload for creating the post + that will be processed by the API client wrapper function. + """ + parent = get_child_spksk_from_paths(parent_path, item_path) + + return { + "job_function": "vote.votecast", + "payload": { + "vote": vote, + "parent": parent + } + } diff --git a/testing.py b/testing.py index dda5955..ec8ea9f 100644 --- a/testing.py +++ b/testing.py @@ -148,7 +148,7 @@ from trustcafeapiwrapper.wrappers.post.create_post import create_post # post_text="This is a test post created via the create_post wrapper function.", # parent_path="/branch/music", # )) -from trustcafeapiwrapper.wrappers.reaction import react +# from trustcafeapiwrapper.wrappers.reaction import react # save_response(create_post( # "This is a test post created via the create_post wrapper function.", # "/userprofile/simon-little", @@ -163,8 +163,14 @@ from trustcafeapiwrapper.wrappers.reaction import react # "This is a test post created via the create_post wrapper function.", # ))) -save_response(API.wrapped(react( - "thumbs_up", +# save_response(API.wrapped(react( +# "thumbs_up", +# "/", +# "/post/1775075313-63ffb852" +# ))) +from trustcafeapiwrapper.wrappers.vote import votecast +save_response(API.wrapped(votecast( + "up", "/", "/post/1775075313-63ffb852" ))) \ No newline at end of file diff --git a/tests/utils/get_child_spksk_from_paths.py b/tests/utils/get_child_spksk_from_paths.py new file mode 100644 index 0000000..b639c6d --- /dev/null +++ b/tests/utils/get_child_spksk_from_paths.py @@ -0,0 +1,24 @@ +import unittest +from trustcafeapiwrapper.utils.get_child_spksk_from_paths import get_child_spksk_from_paths +class TestGetChildSpkskFromPaths(unittest.TestCase): + def test_post_reaction(self): + parent_path = '/user/johndoe' + item_path = '/post/12345' + expected_output = { + "pk": "userprofile#johndoe", + "sk": "post#12345", + "entity": "post", + "slug": "12345" + } + self.assertEqual(get_child_spksk_from_paths(parent_path, item_path), expected_output) + + def test_comment_reaction(self): + parent_path = '/post/12345' + item_path = '/comment/67890' + expected_output = { + "pk": "post#12345", + "sk": "comment#67890", + "entity": "comment", + "slug": "67890" + } + self.assertEqual(get_child_spksk_from_paths(parent_path, item_path), expected_output) \ No newline at end of file diff --git a/tests/wrappers/vote.py b/tests/wrappers/vote.py new file mode 100644 index 0000000..5f672f5 --- /dev/null +++ b/tests/wrappers/vote.py @@ -0,0 +1,34 @@ +import unittest +from trustcafeapiwrapper.wrappers.vote.votecast import votecast +class TestVoteCast(unittest.TestCase): + def test_vote_cast_to_post(self): + vote = 'up' + parent_path = '/' + item_path = '/post/12345' + result = votecast(vote, parent_path, item_path) + + self.assertIsInstance(result, dict) + self.assertIn("job_function", result) + self.assertIn("payload", result) + self.assertEqual(result["job_function"], "vote.votecast") + self.assertEqual(result["payload"]["vote"], vote) + self.assertEqual(result["payload"]["parent"]["pk"], "maintrunk#maintrunk") + self.assertEqual(result["payload"]["parent"]["sk"], "post#12345") + self.assertEqual(result["payload"]["parent"]["entity"], "post") + self.assertEqual(result["payload"]["parent"]["slug"], "12345") + + def test_vote_cast_to_comment(self): + vote = 'down' + parent_path = '/post/12345' + item_path = '/comment/67890' + result = votecast(vote, parent_path, item_path) + + self.assertIsInstance(result, dict) + self.assertIn("job_function", result) + self.assertIn("payload", result) + self.assertEqual(result["job_function"], "vote.votecast") + self.assertEqual(result["payload"]["vote"], vote) + self.assertEqual(result["payload"]["parent"]["pk"], "post#12345") + self.assertEqual(result["payload"]["parent"]["sk"], "comment#67890") + self.assertEqual(result["payload"]["parent"]["entity"], "comment") + self.assertEqual(result["payload"]["parent"]["slug"], "67890") \ No newline at end of file diff --git a/unittests.py b/unittests.py index 6390de7..e98234f 100644 --- a/unittests.py +++ b/unittests.py @@ -2,11 +2,16 @@ import sys sys.path.insert(0, './src/') import unittest -from tests.wrappers.create_post import TestCreatePost -from tests.wrappers.create_comment import TestCreateComment -from tests.wrappers.react import TestReact from tests.utils.get_post_pksk import TestGetPostPksk from tests.utils.get_parent_pksk_from_path import TestGetParentPkskFromPath from tests.utils.make_comment_sk import TestMakeCommentSk from tests.utils.make_post_sk import TestMakePostSk +from tests.utils.get_child_spksk_from_paths import TestGetChildSpkskFromPaths + + +from tests.wrappers.create_post import TestCreatePost +from tests.wrappers.create_comment import TestCreateComment +from tests.wrappers.react import TestReact +from tests.wrappers.vote import TestVoteCast + unittest.main()