Skip to content

Commit

Permalink
feat: [AXM-1227, AXM-1235, AXM-1242] accredible models, client and sy…
Browse files Browse the repository at this point in the history
…ncing groups
  • Loading branch information
kyrylo-kh committed Dec 18, 2024
1 parent da2b598 commit c0f5cea
Show file tree
Hide file tree
Showing 9 changed files with 668 additions and 1 deletion.
108 changes: 108 additions & 0 deletions credentials/apps/badges/accredible/api_client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import logging
from django.conf import settings
from django.contrib.sites.models import Site
from attrs import asdict

from credentials.apps.badges.models import AccredibleAPIConfig, AccredibleGroup
from credentials.apps.badges.base_api_client import BaseBadgeProviderClient
from credentials.apps.badges.accredible.data import AccredibleBadgeData, AccredibleExpireBadgeData
from credentials.apps.badges.accredible.utils import get_accredible_api_base_url


logger = logging.getLogger(__name__)


class AccredibleAPIClient(BaseBadgeProviderClient):
"""
A client for interacting with the Accredible API.
This class provides methods for performing various operations on the Accredible API.
"""
PROVIDER_NAME = "Accredible"

def __init__(self, api_config: AccredibleAPIConfig):
"""
Initializes a AccredibleAPIClient object.
Args:
api_config (AccredibleAPIConfig): Configuration object for the Accredible API.
"""
self.api_config = api_config

def _get_base_api_url(self) -> str:
return get_accredible_api_base_url(settings)

def _get_headers(self) -> dict:
"""
Returns the headers for making API requests to Credly.
"""
return {
"Accept": "application/json",
"Content-Type": "application/json",
"Authorization": f"Bearer {self.api_config.api_key}",
}

def fetch_all_groups(self) -> dict:
"""
Fetch all groups.
"""
return self.perform_request("get", "issuer/all_groups")

def fetch_design_image(self, design_id: int) -> str:
"""
Fetches the design and return the URL of image.
"""
design_raw = self.perform_request("get", f"designs/{design_id}")
return design_raw.get("design", {}).get("rasterized_content_url")

def issue_badge(self, issue_badge_data: AccredibleBadgeData) -> dict:
"""
Issues a badge using the Accredible REST API.
Args:
issue_badge_data (IssueBadgeData): Data required to issue the badge.
"""
return self.perform_request("post", "credentials", asdict(issue_badge_data))

def revoke_badge(self, badge_id, data: AccredibleExpireBadgeData) -> dict:
"""
Revoke a badge with the given badge ID.
Args:
badge_id (str): ID of the badge to revoke.
data (dict): Additional data for the revocation.
"""
return self.perform_request("patch", f"credentials/{badge_id}", asdict(data))

def sync_groups(self, site_id: int) -> int:
"""
Pull all groups for a given Accredible API config.
Args:
site_id (int): ID of the site.
Returns:
int | None: processed items.
"""
try:
site = Site.objects.get(id=site_id)
except Site.DoesNotExist:
logger.error(f"Site with the id {site_id} does not exist!")
raise

groups_data = self.fetch_all_groups()
raw_groups = groups_data.get("groups", [])

for raw_group in raw_groups:
AccredibleGroup.objects.update_or_create(
id=raw_group.get("id"),
api_config=self.api_config,
defaults={
"site": site,
"name": raw_group.get("course_name"),
"description": raw_group.get("course_description"),
"icon": self.fetch_design_image(raw_group.get("primary_design_id")),
},
)

return len(raw_groups)
57 changes: 57 additions & 0 deletions credentials/apps/badges/accredible/data.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import attr
from datetime import datetime


@attr.s(auto_attribs=True, frozen=True)
class AccredibleRecipient:
"""
Represents the recipient data in the credential.
Attributes:
name (str): The recipient's name.
email (str): The recipient's email address.
"""
name: str
email: str

@attr.s(auto_attribs=True, frozen=True)
class AccredibleCredential:
"""
Represents the credential data.
Attributes:
recipient (RecipientData): Information about the recipient.
group_id (int): ID of the credential group.
name (str): Title of the credential.
description (str): Description of the credential.
issued_on (datetime): Date when the credential was issued.
complete (bool): Whether the credential process is complete.
"""

recipient: AccredibleRecipient
group_id: int
name: str
description: str
issued_on: datetime
complete: bool

@attr.s(auto_attribs=True, frozen=True)
class AccredibleExpiredCredential:
"""
Represents the data required to expire a credential.
"""
expired_on: datetime

@attr.s(auto_attribs=True, frozen=True)
class AccredibleBadgeData:
"""
Represents the data required to issue a badge.
"""
credential: AccredibleCredential

@attr.s(auto_attribs=True, frozen=True)
class AccredibleExpireBadgeData:
"""
Represents the data required to expire a badge.
"""
credential: AccredibleExpiredCredential
42 changes: 42 additions & 0 deletions credentials/apps/badges/accredible/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
def get_accredible_api_base_url(settings) -> str:
"""
Determines the base URL for the Accredible service based on application settings.
Parameters:
- settings: A configuration object containing the application's settings.
Returns:
- str: The base URL for the Accredible service (web site).
This will be the URL for the sandbox environment if `USE_SANDBOX` is
set to a truthy value in the configuration;
otherwise, it will be the production environment's URL.
"""

accredible_config = settings.BADGES_CONFIG["accredible"]

if accredible_config.get("USE_SANDBOX"):
return accredible_config["ACCREDIBLE_SANDBOX_API_BASE_URL"]

return accredible_config["ACCREDIBLE_API_BASE_URL"]


def get_accredible_base_url(settings) -> str:
"""
Determines the base URL for the Accredible service based on application settings.
Parameters:
- settings: A configuration object containing the application's settings.
Returns:
- str: The base URL for the Accredible service (web site).
This will be the URL for the sandbox environment if `USE_SANDBOX` is
set to a truthy value in the configuration;
otherwise, it will be the production environment's URL.
"""

credly_config = settings.BADGES_CONFIG["accredible"]

if credly_config.get("USE_SANDBOX"):
return credly_config["ACCREDIBLE_SANDBOX_BASE_URL"]

return credly_config["ACCREDIBLE_BASE_URL"]
Loading

0 comments on commit c0f5cea

Please sign in to comment.