Skip to content

Commit

Permalink
feat: [ACI-331, ACI-357, ACI-363] badge templates creation (#58)
Browse files Browse the repository at this point in the history
  • Loading branch information
kyrylo-kh authored and wowkalucky committed Feb 23, 2024
1 parent 466953f commit 5f6a392
Show file tree
Hide file tree
Showing 10 changed files with 202 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,38 @@

from credentials.apps.badges.toggles import is_badges_enabled

from .models import CredlyOrganization
from .models import CredlyOrganization, BadgeTemplate
from .utils import sync_badge_templates_for_organization
from .forms import CredlyOrganizationAdminForm


class CredlyOrganizationAdmin(admin.ModelAdmin):
"""
Credly organization admin setup.
"""
form = CredlyOrganizationAdminForm
list_display = ("name", "uuid", "api_key",)
actions = ("sync_organization_badge_templates",)

@admin.action(description="Sync organization badge templates")
def sync_organization_badge_templates(self, request, queryset):
"""
Sync badge templates for selected organizations.
"""
for organization in queryset:
sync_badge_templates_for_organization(organization.uuid)


class BadgeTemplateAdmin(admin.ModelAdmin):
"""
Badge template admin setup.
"""
list_display = ("name", "uuid", "organization", "state",)
list_filter = ("state", "organization",)
search_fields = ("name", "uuid",)


# register admin configurations with respect to the feature flag
if is_badges_enabled():
admin.site.register(CredlyOrganization, CredlyOrganizationAdmin)
admin.site.register(BadgeTemplate, BadgeTemplateAdmin)
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from credentials.apps.badges.apps import BadgesAppConfig
from credentials.apps.badges.toggles import is_badges_enabled
from credentials.apps.badges.toggles import is_badges_enabled, check_badges_enabled
from credentials.apps.plugins.constants import PROJECT_TYPE, PluginSettings, PluginURLs, SettingsType


Expand Down Expand Up @@ -35,10 +35,9 @@ class CredlyBadgesConfig(BadgesAppConfig):
}
} if is_badges_enabled() else {} # TODO: improve this

@ check_badges_enabled
def ready(self):
"""
Activate installed badges plugins if they are enabled.
Performs initial registrations for checks, signals, etc.
"""
super().ready()
32 changes: 32 additions & 0 deletions credentials/apps/badges/distribution/credly/credly_badges/forms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
"""
Django forms for the credly badges
"""

from django import forms
from django.utils.translation import gettext_lazy as _

from .models import CredlyOrganization
from .rest_api import CredlyAPIClient
from .exceptions import CredlyAPIError

class CredlyOrganizationAdminForm(forms.ModelForm):
class Meta:
model = CredlyOrganization
fields = "__all__"

def clean(self):
"""
Validate that organization is existing on Credly services.
"""
cleaned_data = super().clean()

uuid = cleaned_data.get("uuid")
api_key = cleaned_data.get("api_key")

try:
credly_api_client = CredlyAPIClient(uuid, api_key)
credly_api_client.fetch_organization()
except CredlyAPIError:
raise forms.ValidationError(_('Invalid organization ID or API key. Organization not found on Credly services.'))

return cleaned_data
Empty file.
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import logging
from django.core.management.base import BaseCommand
from credly_badges.utils import sync_badge_templates_for_organization
from credly_badges.models import CredlyOrganization


logger = logging.getLogger(__name__)


class Command(BaseCommand):
help = 'Sync badge templates for a specific organization or all organizations'

def add_arguments(self, parser):
parser.add_argument('--organization_id', type=str, help='UUID of the organization.')

def handle(self, *args, **options):
"""
Sync badge templates for a specific organization or all organizations.
Usage:
./manage.py sync_organization_badge_templates
./manage.py sync_organization_badge_templates --organization_id c117c179-81b1-4f7e-a3a1-e6ae30568c13
"""
organization_id = options.get('organization_id')

if organization_id:
logger.info(f'Syncing badge templates for single organization: {organization_id}')
sync_badge_templates_for_organization(organization_id)
else:
all_organization_ids = CredlyOrganization.get_all_organization_ids()
logger.info(f'Organization id was not provided. Syncing badge templates for all organizations: {all_organization_ids}')
for organization_id in all_organization_ids:
sync_badge_templates_for_organization(organization_id)

logger.info('Done.')
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Generated by Django 4.2.7 on 2024-01-26 11:47

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
('credly_badges', '0001_initial'),
]

operations = [
migrations.AlterField(
model_name='credlyorganization',
name='api_key',
field=models.CharField(help_text='Credly API shared secret for organization.', max_length=255),
),
migrations.AlterField(
model_name='credlyorganization',
name='name',
field=models.CharField(help_text='Organization display name.', max_length=255),
),
migrations.AlterField(
model_name='credlyorganization',
name='uuid',
field=models.UUIDField(help_text='Unique organization ID.', unique=True),
),
migrations.CreateModel(
name='BadgeTemplate',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('uuid', models.UUIDField(help_text='Unique badge template ID.', unique=True)),
('name', models.CharField(help_text='Badge template name.', max_length=255)),
('state', models.CharField(choices=[('active', 'Active'), ('archived', 'Archived'), ('draft', 'Draft'), ('inactive', 'Inactive')], default='inactive', help_text='State of the badge template.', max_length=255)),
('organization', models.ForeignKey(help_text='Organization of the badge template.', on_delete=django.db.models.deletion.CASCADE, to='credly_badges.credlyorganization')),
],
),
]
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,42 @@ class CredlyOrganization(TimeStampedModel):
uuid = models.UUIDField(unique=True, help_text=_('Unique organization ID.'))
name = models.CharField(max_length=255, help_text=_('Organization display name.'))
api_key = models.CharField(max_length=255, help_text=_('Credly API shared secret for organization.'))

def __str__(self):
return self.name

@classmethod
def get_all_organization_ids(cls):
"""
Get all organization IDs.
"""
return cls.objects.values_list('uuid', flat=True)


class BadgeTemplate(models.Model):
"""
Badge template model.
"""
STATE_CHOICES = (
('active', _('Active')),
('archived', _('Archived')),
('draft', _('Draft')),
('inactive', _('Inactive')),
)

uuid = models.UUIDField(unique=True, help_text=_('Unique badge template ID.'))
name = models.CharField(max_length=255, help_text=_('Badge template name.'))
organization = models.ForeignKey(
CredlyOrganization,
on_delete=models.CASCADE,
help_text=_('Organization of the badge template.')
)
state = models.CharField(
max_length=255,
choices=STATE_CHOICES,
default='inactive',
help_text=_('State of the badge template.')
)

def __str__(self):
return self.name
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@
import requests
from attrs import asdict
from django.conf import settings
from requests.packages.urllib3.exceptions import HTTPError

from requests.exceptions import HTTPError
from .exceptions import CredlyAPIError


Expand All @@ -21,6 +20,8 @@ class CredlyAPIClient:
This class provides methods for performing various operations on the Credly API,
such as fetching organization details, fetching badge templates, issuing badges,
and revoking badges.
TODO: improve client to return data in a more usable format
"""

def __init__(self, organization_id, api_key):
Expand Down
29 changes: 29 additions & 0 deletions credentials/apps/badges/distribution/credly/credly_badges/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
from django.shortcuts import get_object_or_404

from .rest_api import CredlyAPIClient
from .models import CredlyOrganization, BadgeTemplate


def sync_badge_templates_for_organization(organization_id):
"""
Sync badge templates for a specific organization and create records in the database.
Args:
organization_id (str): UUID of the organization.
Raises:
Http404: If organization is not found.
"""
organization = get_object_or_404(CredlyOrganization, uuid=organization_id)

credly_api_client = CredlyAPIClient(organization_id, organization.api_key)
badge_templates_data = credly_api_client.fetch_badge_templates()

for badge_template_data in badge_templates_data.get('data', []):
BadgeTemplate.objects.update_or_create(
uuid=badge_template_data.get('id'),
defaults={
'name': badge_template_data.get('name'),
'organization': organization,
}
)

0 comments on commit 5f6a392

Please sign in to comment.