Skip to content

Commit

Permalink
moved and extended implementation of tags #92
Browse files Browse the repository at this point in the history
  • Loading branch information
bensteUEM committed Jan 8, 2025
1 parent a7b6ab7 commit 00cf6c8
Show file tree
Hide file tree
Showing 4 changed files with 188 additions and 78 deletions.
46 changes: 3 additions & 43 deletions churchtools_api/churchtools_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from churchtools_api.posts import ChurchToolsApiPosts
from churchtools_api.resources import ChurchToolsApiResources
from churchtools_api.songs import ChurchToolsApiSongs
from churchtools_api.tags import ChurchToolsApiTags

logger = logging.getLogger(__name__)

Expand All @@ -26,6 +27,7 @@ class ChurchToolsApi(
ChurchToolsApiPosts,
ChurchToolsApiCalendar,
ChurchToolsApiResources,
ChurchToolsApiTags,
):
"""Main class used to combine all api functions.
Expand All @@ -37,6 +39,7 @@ class ChurchToolsApi(
ChurchToolsApiFiles: all functions used for files
ChurchToolsApiCalendars: all functions used for calendars
ChurchToolsApiResources: all functions used for resources
ChurchToolsApiTags: all functions used for tags
"""

def __init__(
Expand Down Expand Up @@ -256,49 +259,6 @@ def get_services(self, **kwargs: dict) -> list[dict]:
logger.info("Services requested failed: %s", response.status_code)
return None

def get_tags(self, type: str, *, rtype: str = "original") -> list[dict] | None: # noqa: A002
"""Retrieve a list of all available tags.
of a specific ct_domain type from ChurchTools
Purpose: be able to find out tag-ids of all available tags for filtering by tag.
Arguments:
type: 'songs' or 'persons'
rtype: original, id_dict or name_dict.
Defaults to original only available if combined with type
Returns:
list of dicts or individual dict
if type is specified or None if not available
"""
url = self.domain + "/api/tags"
headers = {"accept": "application/json"}
params = {
"type": type,
}
response = self.session.get(url=url, params=params, headers=headers)

response_content = json.loads(response.content)

if response.status_code != requests.codes.ok:
logger.warning(response.content)
return None

response_data = response_content["data"]

if type:
match rtype:
case "id_dict":
return {item["id"]: item["name"] for item in response_data}
case "name_dict":
return {item["name"]: item["id"] for item in response_data}
case _:
return response_data

logger.debug("SongTags load successful %s", response_content)

return response_data

def get_options(self) -> dict:
"""Helper function which returns all configurable option fields from CT.
Expand Down
127 changes: 127 additions & 0 deletions churchtools_api/tags.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
"""module containing parts used for tag handling."""

import json
import logging

import requests

from churchtools_api.churchtools_api_abstract import ChurchToolsApiAbstract

logger = logging.getLogger(__name__)


class ChurchToolsApiTags(ChurchToolsApiAbstract):
"""Part definition of ChurchToolsApi which focuses on tags.
Args:
ChurchToolsApiAbstract: template with minimum references
"""

def __init__(self) -> None:
"""Inherited initialization."""
super()

def create_tag(self, name: str, tag_type: str) -> bool:
"""Create a new tag.
Args:
name: name of the tag
tag_type: category of the tag - at present "persons" and "songs" supported
Returns:
if successful
"""
# TODO @benste: consider implementing groups too
# https://github.com/bensteUEM/ChurchToolsAPI/issues/92
url = self.domain + "/api/tags"
headers = {"accept": "application/json"}
data = {"name": name, "type": tag_type}
response = self.session.post(url=url, headers=headers, json=data)

response_content = json.loads(response.content)

if response.status_code != requests.codes.ok:
logger.warning(response_content["errors"])
return False

response_content = json.loads(response.content)
return True

def delete_tag(self, name: str, tag_type: str) -> bool:
"""Placeholder for tag delete as soon as CT does support it.
Args:
name: name of the tag
tag_type: category of the tag - at present "persons" and "songs" supported
Returns:
if successful
"""
# TODO @benste: consider implementing groups too - CT Support
# https://github.com/bensteUEM/ChurchToolsAPI/issues/92
logger.exception("Function does not exist in CT API yet")
# TODO @benste: not tested because of CT Support issue 136613
# https://github.com/bensteUEM/ChurchToolsAPI/issues/92
url = self.domain + "/api/tags"
headers = {"accept": "application/json"}
data = {"name": name, "type": tag_type}
response = self.session.delete(url=url, headers=headers, json=data)

response_content = json.loads(response.content)

if response.status_code != requests.codes.no_content:
logger.warning(response_content["errors"])
return False

return True

def get_tags(self, type: str = "songs", rtype: str = "original") -> list[dict]: # noqa: A002
# TODO @benste: rename type to tag_type
# https://github.com/bensteUEM/ChurchToolsAPI/issues/127
# TODO @benste: consider implementing groups too
# https://github.com/bensteUEM/ChurchToolsAPI/issues/92
"""Retrieve a list of all available tags of a specific ct_domain type from CT.
Purpose: be able to find out tag-ids of all available tags for filtering by tag.
Arguments:
type: 'songs' (default) or 'persons'
rtype: original, id_dict or name_dict.
Defaults to original only available if combined with type
Returns:
list of dicts describing each tag. Each contains keys 'id' and 'name'
"""
url = self.domain + "/api/tags"
headers = {"accept": "application/json"}
params = {
"type": type,
}
response = self.session.get(url=url, params=params, headers=headers)

if response.status_code != requests.codes.ok:
logger.warning(
"%s Something went wrong fetching Song-tags: %s",
response.status_code,
response.content,
)
return None

response_content = json.loads(response.content)
response_content["data"].copy()
logger.debug("SongTags load successful %s", response_content)

response_data = response_content["data"]

if type:
match rtype:
case "id_dict":
return {item["id"]: item["name"] for item in response_data}
case "name_dict":
return {item["name"]: item["id"] for item in response_data}
case _:
return response_data

logger.debug("SongTags load successful %s", response_content)
return response_data
35 changes: 0 additions & 35 deletions tests/test_churchtools_api_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -339,41 +339,6 @@ def test_get_services(self) -> None:
result4 = self.api.get_services(returnAsDict=False)
assert isinstance(result4, list)

def test_get_tags(self) -> None:
"""Test function for get_tags() with default type song.
On ELKW1610.KRZ.TOOLS tag ID 49 has the name To Do
:return:
"""
result = self.api.get_tags(type="songs")

EXPECTED_MIN_RESULT = {"Test": 53}
assert any(item["name"] in EXPECTED_MIN_RESULT for item in result)
assert any(item["id"] in EXPECTED_MIN_RESULT.values() for item in result)

def test_get_tags_id_dict(self) -> None:
"""Test function for get_tags() with default type song.
On ELKW1610.KRZ.TOOLS tag ID 49 has the name To Do
:return:
"""
result = self.api.get_tags(type="songs", rtype="id_dict")
assert len(result) > 0

EXPECTED_MIN_RESULT = {53: "Test"}
assert all(item in result.items() for item in EXPECTED_MIN_RESULT.items())

def test_get_tags_name_dict(self) -> None:
"""Test function for get_tags() with default type song.
On ELKW1610.KRZ.TOOLS tag ID 49 has the name To Do
:return:
"""
result = self.api.get_tags(type="songs", rtype="name_dict")

EXPECTED_MIN_RESULT = {"Test": 53}
assert all(item in result.items() for item in EXPECTED_MIN_RESULT.items())

def test_has_event_schedule(self) -> None:
"""Tries to get boolean if event agenda exists for a CT Event.
Expand Down
58 changes: 58 additions & 0 deletions tests/test_churchtools_api_tags.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
"""module test tags."""

import json
import logging
import logging.config
from pathlib import Path

import pytest

from tests.test_churchtools_api_abstract import TestsChurchToolsApiAbstract

logger = logging.getLogger(__name__)

config_file = Path("logging_config.json")
with config_file.open(encoding="utf-8") as f_in:
logging_config = json.load(f_in)
log_directory = Path(logging_config["handlers"]["file"]["filename"]).parent
if not log_directory.exists():
log_directory.mkdir(parents=True)
logging.config.dictConfig(config=logging_config)


class TestChurchtoolsApiResources(TestsChurchToolsApiAbstract):
"""Test for Tags."""

@pytest.mark.skip(
"See Github issue 92 - and CT Support Case 136613 "
"- at present no delete available"
)
def test_create_get_delete_tags(self) -> None:
"""Checks creation and deletition of events using get events."""
SAMPLE_TAG_NAME = "testing_new_tag"
SAMPLE_TYPE = "persons"

existing_tags = self.api.get_tags(tag_type=SAMPLE_TYPE)
assert SAMPLE_TAG_NAME not in [tag["name"] for tag in existing_tags]

self.api.create_tag(name=SAMPLE_TAG_NAME, tag_type=SAMPLE_TYPE)

existing_tags = self.api.get_tags(tag_type=SAMPLE_TYPE)
assert SAMPLE_TAG_NAME not in [tag["name"] for tag in existing_tags]

self.api.delete_tag(tag_type=SAMPLE_TYPE, name=SAMPLE_TAG_NAME)
existing_tags = self.api.get_tags(tag_type=SAMPLE_TYPE)
assert SAMPLE_TAG_NAME not in [tag["name"] for tag in existing_tags]

def test_get_tags(self) -> None:
"""Test function for get_tags() with default type song.
On ELKW1610.KRZ.TOOLS tag ID 49 has the name To Do
"""
SAMPLE_TAG = {49: "ToDo"}

result = self.api.get_tags(type="songs")
assert len(result) > 0
test_tag = next(item for item in result if item["id"] == next(iter(SAMPLE_TAG)))
assert test_tag["id"] == next(iter(SAMPLE_TAG))
assert test_tag["name"] == next(iter(SAMPLE_TAG.values()))

0 comments on commit 00cf6c8

Please sign in to comment.