Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions .github/workflows/blackformatter.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: Check Python Code Formatting

on:
pull_request:
branches:
- main

jobs:
format-check:
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4
with:
persist-credentials: false

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12.x"

- name: Install Black
run: pip install black

- name: Run Black
run: black . --check
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# TeleBand
# MusicCPR

The music education learning management system

Expand Down
5 changes: 5 additions & 0 deletions config/api_router.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
)
from teleband.musics.api.views import PieceViewSet
from teleband.instruments.api.views import InstrumentViewSet
from teleband.users.api.views import UserInstrumentConfigViewSet

if settings.DEBUG:
router = DefaultRouter()
Expand All @@ -34,6 +35,7 @@
router.register("pieces", PieceViewSet)
router.register("piece-plans", PiecePlanViewSet)
router.register("instruments", InstrumentViewSet)
router.register("configs", UserInstrumentConfigViewSet)

courses_router = nested_cls(router, "courses", lookup="course_slug")
courses_router.register("assignments", AssignmentViewSet) # option basename omitted
Expand All @@ -51,6 +53,9 @@
attachments_router = nested_cls(assignments_router, "submissions", lookup="submission")
attachments_router.register("attachments", AttachmentViewSet)

# config_router = nested_cls(router, "users", lookup="user_username")
# config_router.register("configs", UserInstrumentConfigViewSet)

app_name = "api"
urlpatterns = router.urls
urlpatterns += [
Expand Down
4 changes: 2 additions & 2 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ Remove a user/role/course enrollment
curl -v \
--request GET \
--header 'Content-Type: application/json' \
-H 'Authorization: Token e9a82a7c334fbdfc52f502efebebec474708eef0' \
https://dev-api.tele.band/api/pieces/ && echo "\n"
-H 'Authorization: Token a2ffdae8df89b2909eb03d21cec559e95eba2e44' \
http://127.0.0.1:8000/api/configs/ && echo "\n"


```
Expand Down
30 changes: 30 additions & 0 deletions teleband/instruments/migrations/0005_instrumentconfig.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Generated by Django 5.0.6 on 2025-11-13 15:24

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("instruments", "0004_instrument_midi_program_number"),
]

operations = [
migrations.CreateModel(
name="InstrumentConfig",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("name", models.CharField(max_length=20)),
("description", models.CharField(max_length=100)),
("settings", models.JSONField(default=dict)),
],
),
]
16 changes: 16 additions & 0 deletions teleband/instruments/migrations/0006_delete_instrumentconfig.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Generated by Django 5.0.6 on 2025-11-13 17:39

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
("instruments", "0005_instrumentconfig"),
]

operations = [
migrations.DeleteModel(
name="InstrumentConfig",
),
]
4 changes: 3 additions & 1 deletion teleband/users/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from teleband.users.forms import UserChangeForm, UserCreationForm
from teleband.users.models import Role, GroupInvitation
from teleband.courses.models import Enrollment

from teleband.users.models import InstrumentConfig
User = get_user_model()


Expand Down Expand Up @@ -51,3 +51,5 @@ class UserAdmin(auth_admin.UserAdmin):
class RoleAdmin(VersionAdmin):
list_display = ("id", "name")
search_fields = ("name",)

admin.site.register(InstrumentConfig)
7 changes: 7 additions & 0 deletions teleband/users/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from teleband.instruments.api.serializers import InstrumentSerializer
from teleband.utils.serializers import GenericNameSerializer
from teleband.users.models import InstrumentConfig

User = get_user_model()

Expand Down Expand Up @@ -35,3 +36,9 @@ class UserInstrumentSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ["id", "name", "instrument", "external_id", "grade"]


class UserInstrumentConfigSerializer(serializers.ModelSerializer):
class Meta:
model = InstrumentConfig
fields = ["id", "name", "description", "settings", "file"]
23 changes: 21 additions & 2 deletions teleband/users/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from rest_framework.decorators import action
from rest_framework.mixins import ListModelMixin, RetrieveModelMixin, UpdateModelMixin
from rest_framework.response import Response
from rest_framework.viewsets import GenericViewSet
from rest_framework.viewsets import GenericViewSet, ModelViewSet

from rest_framework.authtoken.models import Token
from rest_framework.authtoken.views import ObtainAuthToken
Expand All @@ -22,9 +22,15 @@
from invitations.exceptions import AlreadyAccepted, AlreadyInvited, UserRegisteredEmail
from invitations.forms import CleanEmailMixin

from .serializers import UserSerializer, UserInstrumentSerializer
from .serializers import (
UserSerializer,
UserInstrumentSerializer,
UserInstrumentConfigSerializer,
)
from teleband.courses.models import Enrollment, Course
from teleband.users.models import InstrumentConfig

from django.db.models import Q

User = get_user_model()
Invitation = get_invitation_model()
Expand Down Expand Up @@ -140,4 +146,17 @@ def post(self, request, *args, **kwargs):
return response


class UserInstrumentConfigViewSet(ModelViewSet):
serializer_class = UserInstrumentConfigSerializer
queryset = InstrumentConfig.objects.all()

def get_queryset(self):
# this returns all configs for the user and the default confgis (those with user=None)
return InstrumentConfig.objects.filter(Q(user=self.request.user) | Q(user=None))

# this helped to map the configs to the user creating them
def perform_create(self, serializer):
return serializer.save(user=self.request.user)


obtain_delete_auth_token = ObtainDeleteAuthToken.as_view()
30 changes: 30 additions & 0 deletions teleband/users/migrations/0011_instrumentconfig.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Generated by Django 5.0.6 on 2025-11-13 17:39

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("users", "0010_alter_user_external_id"),
]

operations = [
migrations.CreateModel(
name="InstrumentConfig",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("name", models.CharField(max_length=20)),
("description", models.CharField(max_length=100)),
("settings", models.JSONField(default=dict)),
],
),
]
24 changes: 24 additions & 0 deletions teleband/users/migrations/0012_instrumentconfig_user.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Generated by Django 5.0.6 on 2025-11-20 14:21

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


class Migration(migrations.Migration):

dependencies = [
("users", "0011_instrumentconfig"),
]

operations = [
migrations.AddField(
model_name="instrumentconfig",
name="user",
field=models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.CASCADE,
to=settings.AUTH_USER_MODEL,
),
),
]
20 changes: 20 additions & 0 deletions teleband/users/migrations/0013_instrumentconfig_file.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Generated by Django 5.0.6 on 2026-03-30 11:09

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("users", "0012_instrumentconfig_user"),
]

operations = [
migrations.AddField(
model_name="instrumentconfig",
name="file",
field=models.FileField(
blank=True, null=True, upload_to="instrument_config_samples/"
),
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 5.0.6 on 2026-04-20 16:23

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("users", "0013_instrumentconfig_file"),
]

operations = [
migrations.AlterField(
model_name="instrumentconfig",
name="description",
field=models.CharField(blank=True, max_length=100, null=True),
),
]
24 changes: 24 additions & 0 deletions teleband/users/migrations/0015_auto_20260420_1659.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Generated by Django 5.0.6 on 2026-04-20 20:59

from django.db import migrations
defaultConfigs = [
{
"name": "Roland-GR-1-Trumpet",
"settings": {},
"file": "instrument_config_samples/Roland-GR-1-Trumpet-C5.wav",
"user": None
}
]

def update_site_forward(apps, schema_editor):
instrument_config_model = apps.get_model("users", "InstrumentConfig")
for config in defaultConfigs:
instrument_config_model.objects.create(**config)

class Migration(migrations.Migration):

dependencies = [
("users", "0014_alter_instrumentconfig_description"),
]

operations = [migrations.RunPython(update_site_forward)]
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Generated by Django 5.0.6 on 2026-04-21 01:21

from django.db import migrations

def update_site_forward(apps, schema_editor):
instrument_config_model = apps.get_model("users", "InstrumentConfig")
instrument_config_model.objects.filter(name="Roland-GR-1-Trumpet", user=None).update(description="Default Roland GR-1 Trumpet configuration.")
class Migration(migrations.Migration):

dependencies = [
("users", "0015_auto_20260420_1659"),
]

operations = [migrations.RunPython(update_site_forward)]
11 changes: 11 additions & 0 deletions teleband/users/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,14 @@ def __str__(self):

class GroupInvitation(Invitation):
group = models.ForeignKey(Group, on_delete=models.DO_NOTHING)


class InstrumentConfig(models.Model):
name = models.CharField(max_length=20)
description = models.CharField(max_length=100, null=True, blank=True)
settings = models.JSONField(default=dict)
file = models.FileField(upload_to="instrument_config_samples/", null=True, blank=True)
user = models.ForeignKey(User, null=True, on_delete=models.CASCADE)

def __str__(self):
return self.name
Loading