Skip to content

Commit

Permalink
chore: [ACI-249] clean up not used imports (#143)
Browse files Browse the repository at this point in the history
* chore: [ACI-249] clean up not used imports

* chore: add TODO items for utils

* refactor: add related name for fulfillments

* refactor: make progress.complete a property

* refactor: document signal handlers

* refactor: add external UUID for Credly badge issuing tracking

* refactor: avoid confusion between Credential and User Credential

* chore: disable penalties until released

* refactor: remove obsolete relation

* fix: badge data serialization issue with UUID
  • Loading branch information
wowkalucky authored Apr 16, 2024
1 parent 310d341 commit 770e9bb
Show file tree
Hide file tree
Showing 16 changed files with 271 additions and 86 deletions.
56 changes: 48 additions & 8 deletions credentials/apps/badges/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ class BadgeRequirementInline(admin.TabularInline):
show_change_link = True
extra = 0

# FIXME: disable until "Release VI"
exclude = [
"group",
]


class BadgePenaltyInline(admin.TabularInline):
model = BadgePenalty
Expand All @@ -50,6 +55,9 @@ def formfield_for_manytomany(self, db_field, request, **kwargs):
class FulfillmentInline(admin.TabularInline):
model = Fulfillment
extra = 0
readonly_fields = [
"requirement",
]


class DataRuleInline(admin.TabularInline):
Expand Down Expand Up @@ -156,7 +164,8 @@ class CredlyBadgeTemplateAdmin(admin.ModelAdmin):
)
inlines = [
BadgeRequirementInline,
BadgePenaltyInline,
# FIXME: disable until "Release V"
# BadgePenaltyInline,
]

def has_add_permission(self, request):
Expand Down Expand Up @@ -235,6 +244,10 @@ class BadgeRequirementAdmin(admin.ModelAdmin):
"template",
"event_type",
]
# FIXME: disable until "Release VI"
exclude = [
"group",
]

def has_add_permission(self, request):
return False
Expand Down Expand Up @@ -284,21 +297,36 @@ class BadgeProgressAdmin(admin.ModelAdmin):
]
list_display = [
"id",
"template",
"username",
"template",
"complete",
]
list_display_links = (
"id",
"username",
"template",
)
readonly_fields = (
"username",
"template",
"complete",
"ratio",
)

@admin.display(boolean=True)
def complete(self, obj):
"""
TODO: switch dedicated `is_complete` bool field
Identifies if all requirements are already fulfilled.
NOTE: (performance) dynamic evaluation.
"""
return bool(getattr(obj, "credential", False))
return obj.completed

def ratio(self, obj):
"""
Displays progress value.
"""
return obj.ratio


class CredlyBadgeAdmin(admin.ModelAdmin):
Expand All @@ -307,18 +335,29 @@ class CredlyBadgeAdmin(admin.ModelAdmin):
"""

list_display = (
"uuid",
"username",
"credential",
"status",
"state",
"external_uuid",
)
list_filter = (
"status",
"state",
"uuid",
)
list_filter = ("state",)
search_fields = (
"username",
"uuid",
"external_uuid",
)
readonly_fields = (
"credential_id",
"credential_content_type",
"username",
"download_url",
"state",
"uuid",
"external_uuid",
)


Expand All @@ -328,5 +367,6 @@ class CredlyBadgeAdmin(admin.ModelAdmin):
admin.site.register(CredlyBadgeTemplate, CredlyBadgeTemplateAdmin)
admin.site.register(CredlyBadge, CredlyBadgeAdmin)
admin.site.register(BadgeRequirement, BadgeRequirementAdmin)
admin.site.register(BadgePenalty, BadgePenaltyAdmin)
# FIXME: disable until "Release V"
# admin.site.register(BadgePenalty, BadgePenaltyAdmin)
admin.site.register(BadgeProgress, BadgeProgressAdmin)
2 changes: 1 addition & 1 deletion credentials/apps/badges/credly/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@


@attr.s(auto_attribs=True, frozen=True)
class IssueBadgeData:
class CredlyBadgeData:
"""
Represents the data required to issue a badge.
Expand Down
59 changes: 43 additions & 16 deletions credentials/apps/badges/issuers.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from django.utils.translation import gettext as _

from credentials.apps.badges.credly.api_client import CredlyAPIClient
from credentials.apps.badges.credly.data import IssueBadgeData
from credentials.apps.badges.credly.data import CredlyBadgeData
from credentials.apps.badges.credly.exceptions import CredlyAPIError
from credentials.apps.badges.models import BadgeTemplate, CredlyBadge, CredlyBadgeTemplate, UserCredential
from credentials.apps.badges.signals.signals import notify_badge_awarded, notify_badge_revoked
Expand Down Expand Up @@ -69,7 +69,16 @@ def issue_credential(

return user_credential

def award(self, credential_id, username):
def award(self, *, username, credential_id):
"""
Awards a badge.
Creates user credential record for the given badge template, for a given user.
Notifies about the awarded badge (public signal).
Returns: UserCredential
"""

credential = self.get_credential(credential_id)
user_credential = self.issue_credential(credential, username)

Expand All @@ -92,26 +101,31 @@ class CredlyBadgeTemplateIssuer(BadgeTemplateIssuer):
issued_credential_type = CredlyBadgeTemplate
issued_user_credential_type = CredlyBadge

def issue_credly_badge(self, credential_id, user_credential):
def issue_credly_badge(self, *, user_credential):
"""
Requests Credly service for external badge issuing based on internal user credential (CredlyBadge).
"""

user = get_user_by_username(user_credential.username)
badge_template = user_credential.credential

credential = self.get_credential(credential_id)
credly_api = CredlyAPIClient(credential.organization.uuid)
issue_badge_data = IssueBadgeData(
credly_badge_data = CredlyBadgeData(
recipient_email=user.email,
issued_to_first_name=(user.first_name or user.username),
issued_to_last_name=(user.last_name or user.username),
badge_template_id=str(credential.uuid),
issued_at=credential.created.strftime("%Y-%m-%d %H:%M:%S %z"),
badge_template_id=str(badge_template.uuid),
issued_at=badge_template.created.strftime("%Y-%m-%d %H:%M:%S %z"),
)

try:
response = credly_api.issue_badge(issue_badge_data)
credly_api = CredlyAPIClient(badge_template.organization.uuid)
response = credly_api.issue_badge(credly_badge_data)
except CredlyAPIError:
user_credential.state = "error"
user_credential.save()
raise

user_credential.uuid = response.get("data").get("id")
user_credential.external_uuid = response.get("data").get("id")
user_credential.state = response.get("data").get("state")
user_credential.save()

Expand All @@ -131,14 +145,27 @@ def revoke_credly_badge(self, credential_id, user_credential):
user_credential.state = response.get("data").get("state")
user_credential.save()

def award(self, credential_id, username):
user_credential = super().award(credential_id, username)
if not user_credential.is_issued:
self.issue_credly_badge(credential_id, user_credential)
return user_credential
def award(self, *, username, credential_id):
"""
Awards a Credly badge.
- Creates user credential record for the given badge template, for a given user;
- Notifies about the awarded badge (public signal);
- Issues external Credly badge (Credly API);
Returns: (CredlyBadge) user credential
"""

credly_badge = super().award(username=username, credential_id=credential_id)

# do not issue new badges if the badge was issued already
if not credly_badge.propagated:
self.issue_credly_badge(user_credential=credly_badge)

return credly_badge

def revoke(self, credential_id, username):
user_credential = super().revoke(credential_id, username)
if user_credential.is_issued:
if user_credential.propagated:
self.revoke_credly_badge(credential_id, user_credential)
return user_credential
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Generated by Django 3.2.20 on 2024-04-16 11:56

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


class Migration(migrations.Migration):

dependencies = [
('badges', '0013_alter_datarule_requirement'),
]

operations = [
migrations.AlterField(
model_name='fulfillment',
name='requirement',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='fulfillments', to='badges.badgerequirement'),
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 3.2.20 on 2024-04-16 15:46

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('badges', '0014_alter_fulfillment_requirement'),
]

operations = [
migrations.AddField(
model_name='credlybadge',
name='external_uuid',
field=models.UUIDField(blank=True, help_text='Credly service badge identifier', null=True, unique=True),
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 3.2.20 on 2024-04-16 16:01

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
('badges', '0015_credlybadge_external_uuid'),
]

operations = [
migrations.RenameField(
model_name='badgeprogress',
old_name='credential',
new_name='user_credential',
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Generated by Django 3.2.20 on 2024-04-16 17:34

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
('badges', '0016_rename_credential_badgeprogress_user_credential'),
]

operations = [
migrations.RemoveField(
model_name='badgeprogress',
name='user_credential',
),
]
Loading

0 comments on commit 770e9bb

Please sign in to comment.