Skip to content

Commit

Permalink
feat: update models
Browse files Browse the repository at this point in the history
  • Loading branch information
wowkalucky committed Feb 11, 2024
1 parent 9e91ebd commit 08f46ba
Show file tree
Hide file tree
Showing 24 changed files with 307 additions and 117 deletions.
22 changes: 19 additions & 3 deletions credentials/apps/badges/admin.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""
Admin section configuration.
"""

from django.contrib import admin

from .toggles import is_badges_enabled
Expand All @@ -11,9 +12,24 @@ class BadgeTemplateAdmin(admin.ModelAdmin):
"""
Badge template admin setup.
"""
list_display = ("name", "uuid", "status", "type", "is_active",)
list_filter = ("status", "type", "is_active",)
search_fields = ("name", "uuid",)

list_display = (
"name",
"uuid",
"origin",
"is_active",
)
list_filter = (
"is_active",
"origin",
)
search_fields = (
"name",
"uuid",
)
readonly_fields = [
"origin",
]


# register admin configurations with respect to the feature flag
Expand Down
8 changes: 5 additions & 3 deletions credentials/apps/badges/apps.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
from django.apps import AppConfig
from django.conf import settings
from edx_django_utils.plugins import add_plugins, get_plugin_apps

from credentials.apps.plugins.constants import PROJECT_TYPE, PluginSettings, PluginURLs, SettingsType
from credentials.apps.plugins.constants import PluginSettings, PluginURLs

from .toggles import check_badges_enabled, is_badges_enabled
from .toggles import check_badges_enabled


class BadgesAppConfig(AppConfig):
Expand All @@ -20,6 +21,7 @@ class BadgesConfig(BadgesAppConfig):
"""
Core badges application configuration.
"""

default = True
name = "credentials.apps.badges"
verbose_name = "Badges"
Expand All @@ -31,7 +33,7 @@ def ready(self):
Performs initial registrations for checks, signals, etc.
"""
from . import signals # pylint: disable=unused-import,import-outside-toplevel
from .checks import badges_checks # pylint: disable=unused-import,import-outside-toplevel
from .signals import collecting # pylint: disable=unused-import,import-outside-toplevel

super().ready()
1 change: 1 addition & 0 deletions credentials/apps/badges/checks.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""
Badges app self-checks.
"""

from django.conf import settings
from django.core.checks import Error, Tags, register

Expand Down
13 changes: 0 additions & 13 deletions credentials/apps/badges/constants.py

This file was deleted.

32 changes: 27 additions & 5 deletions credentials/apps/badges/distribution/credly/credly_badges/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,13 @@ class CredlyOrganizationAdmin(admin.ModelAdmin):
"""
Credly organization admin setup.
"""

form = CredlyOrganizationAdminForm
list_display = ("name", "uuid", "api_key",)
list_display = (
"name",
"uuid",
"api_key",
)
actions = ("sync_organization_badge_templates",)

@admin.action(description="Sync organization badge templates")
Expand All @@ -32,12 +37,29 @@ class CredlyBadgeTemplateAdmin(admin.ModelAdmin):
"""
Badge template admin setup.
"""
list_display = ("name", "uuid", "status", "organization", "is_active",)
list_filter = ("status", "is_active", "organization")
search_fields = ("name", "uuid",)

list_display = (
"organization",
"state",
"name",
"uuid",
"is_active",
)
list_filter = (
"organization",
"is_active",
"state",
)
search_fields = (
"name",
"uuid",
)
readonly_fields = [
"organization",
"state",
]


# register admin configurations with respect to the feature flag
if is_badges_enabled():
admin.site.register(CredlyOrganization, CredlyOrganizationAdmin)
admin.site.register(CredlyBadgeTemplate, CredlyBadgeTemplateAdmin)
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ def perform_request(self, method, url_suffix, data=None):

def fetch_organization(self):
"""
Fetches the organization from the Credly API.
Fetches Credly Organization data.
"""
return self.perform_request("get", "")

Expand Down Expand Up @@ -108,8 +108,8 @@ def _raise_for_error(self, response):
try:
response.raise_for_status()
except HTTPError:
logger.error(f"Error while processing credly api request: {response.status_code} - {response.text}")
raise CredlyAPIError
logger.error(f"Error while processing Credly API request: {response.status_code} - {response.text}")
raise CredlyAPIError(response.text)

def _get_headers(self):
"""
Expand Down
30 changes: 19 additions & 11 deletions credentials/apps/badges/distribution/credly/credly_badges/apps.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
from credentials.apps.badges.apps import BadgesAppConfig
from credentials.apps.badges.toggles import check_badges_enabled, is_badges_enabled
from credentials.apps.plugins.constants import PROJECT_TYPE, PluginSettings, PluginURLs, SettingsType
from credentials.apps.badges.toggles import check_badges_enabled
from credentials.apps.plugins.constants import (
PROJECT_TYPE,
PluginSettings,
PluginURLs,
SettingsType,
)


class CredlyBadgesConfig(BadgesAppConfig):
Expand All @@ -15,27 +20,30 @@ class CredlyBadgesConfig(BadgesAppConfig):
- organization badge templates are used to setup Open edX badge templates;
- earned badges are distributed to the Credly service;
"""

name = "credly_badges"
plugin_label = "Credly (by Pearson)"
default = True

plugin_app = {
PluginURLs.CONFIG: {
PROJECT_TYPE: {
PluginURLs.NAMESPACE: 'credly_badges',
PluginURLs.REGEX: 'credly_badges/',
PluginURLs.RELATIVE_PATH: 'urls',
PluginURLs.NAMESPACE: "credly_badges",
PluginURLs.REGEX: "credly-badges/",
PluginURLs.RELATIVE_PATH: "urls",
}
},
PluginSettings.CONFIG: {
PROJECT_TYPE: {
SettingsType.BASE: {PluginSettings.RELATIVE_PATH: 'settings.base'},
SettingsType.PRODUCTION: {PluginSettings.RELATIVE_PATH: 'settings.production'},
SettingsType.TEST: {PluginSettings.RELATIVE_PATH: 'settings.test'},
SettingsType.BASE: {PluginSettings.RELATIVE_PATH: "settings.base"},
SettingsType.PRODUCTION: {PluginSettings.RELATIVE_PATH: "settings.production"},
SettingsType.TEST: {PluginSettings.RELATIVE_PATH: "settings.test"},
},
}
} if is_badges_enabled() else {} # TODO: improve this
},
# register signal handlers?
}

@ check_badges_enabled
@check_badges_enabled
def ready(self):
"""
Performs initial registrations for checks, signals, etc.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ class CredlyAPIError(Exception):
"""
Exception raised for errors that occur during interactions with the Credly API.
"""

pass
27 changes: 20 additions & 7 deletions credentials/apps/badges/distribution/credly/credly_badges/forms.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"""
Django forms for the credly badges
Credly Badges admin forms.
"""

from django import forms
Expand All @@ -11,23 +11,36 @@


class CredlyOrganizationAdminForm(forms.ModelForm):
"""
Additional actions for Credly Organization items.
"""

class Meta:
model = CredlyOrganization
fields = "__all__"

def clean(self):
"""
Validate that organization is existing on Credly services.
Perform Credly API check for given organization ID.
- Credly Organization exists;
- fetch additional data for such organization;
"""
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.'))
credly_api_client = CredlyAPIClient(uuid, api_key)
self._ensure_organization_exists(credly_api_client)

return cleaned_data

def _ensure_organization_exists(self, api_client):
"""
Try to fetch organization data by the configured Credly Organization ID.
"""
try:
return api_client.fetch_organization()
except CredlyAPIError as exc:
raise forms.ValidationError(exc)
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@


class Command(BaseCommand):
help = 'Sync badge templates for a specific organization or all organizations'
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.')
parser.add_argument("--organization_id", type=str, help="UUID of the organization.")

def handle(self, *args, **options):
"""
Expand All @@ -22,15 +22,17 @@ def handle(self, *args, **options):
./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')
organization_id = options.get("organization_id")

if organization_id:
logger.info(f'Syncing badge templates for single organization: {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}')
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.')
logger.info("Done.")
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# Generated by Django 4.2.10 on 2024-02-11 16:14

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


class Migration(migrations.Migration):

dependencies = [
("credly_badges", "0002_alter_credlyorganization_api_key_and_more"),
]

operations = [
migrations.AddField(
model_name="credlybadgetemplate",
name="state",
field=model_utils.fields.StatusField(
choices=[
("draft", "draft"),
("active", "active"),
("archived", "archived"),
],
default="draft",
help_text="Credly badge template state (auto-managed).",
max_length=100,
no_check_for_status=True,
),
),
migrations.AlterField(
model_name="credlybadgetemplate",
name="organization",
field=models.ForeignKey(
help_text="Credly Organization - template owner.",
on_delete=django.db.models.deletion.CASCADE,
to="credly_badges.credlyorganization",
),
),
migrations.AlterField(
model_name="credlyorganization",
name="api_key",
field=models.CharField(
help_text="Credly API shared secret for Credly Organization.",
max_length=255,
),
),
migrations.AlterField(
model_name="credlyorganization",
name="name",
field=models.CharField(
blank=True,
help_text="Verbose name for Credly Organization.",
max_length=255,
null=True,
),
),
migrations.AlterField(
model_name="credlyorganization",
name="uuid",
field=models.UUIDField(
help_text="Put your Credly Organization ID here.", unique=True
),
),
]
Loading

0 comments on commit 08f46ba

Please sign in to comment.