diff --git a/app/interface/admin.py b/app/interface/admin.py index f18f0cd..c529eac 100644 --- a/app/interface/admin.py +++ b/app/interface/admin.py @@ -2,28 +2,33 @@ from django.contrib import admin from . import models + class EnsembleAdmin(admin.ModelAdmin): - list_display = ['name', 'slug'] + list_display = ["name", "slug"] + class ModuleInline(admin.StackedInline): model = models.Module extra = 0 -class ProjectAdmin(admin.ModelAdmin): - list_display = ['name', 'ensemble', 'event_date', 'active'] - list_filter = ['ensemble', 'active'] +class ProjectAdmin(admin.ModelAdmin): + list_display = ["name", "ensemble", "event_date", "active"] + list_filter = ["ensemble", "active"] inlines = [ModuleInline] + class ResourceAdmin(admin.ModelAdmin): - list_display = ['name', 'media_type', 'project'] - list_filter = ['project'] + list_display = ["name", "media_type", "project"] + list_filter = ["project"] + class WikiPageAdmin(admin.ModelAdmin): - list_display = ['title', 'project'] - list_filter = ['project'] + list_display = ["title", "project"] + list_filter = ["project"] + admin.site.register(models.Ensemble, EnsembleAdmin) admin.site.register(models.Project, ProjectAdmin) admin.site.register(models.Resource, ResourceAdmin) -admin.site.register(models.WikiPage, WikiPageAdmin) \ No newline at end of file +admin.site.register(models.WikiPage, WikiPageAdmin) diff --git a/app/interface/apps.py b/app/interface/apps.py index 7810300..0c7d2c4 100644 --- a/app/interface/apps.py +++ b/app/interface/apps.py @@ -2,4 +2,4 @@ from django.apps import AppConfig class InterfaceConfig(AppConfig): - name = 'interface' + name = "interface" diff --git a/app/interface/fields.py b/app/interface/fields.py index 4e96092..4367a06 100644 --- a/app/interface/fields.py +++ b/app/interface/fields.py @@ -1,4 +1,5 @@ from crispy_forms.layout import Field + class BulmaFileUpload(Field): - template = 'bulma/file_upload.html' \ No newline at end of file + template = "bulma/file_upload.html" diff --git a/app/interface/forms.py b/app/interface/forms.py index 9ac708a..872a4de 100644 --- a/app/interface/forms.py +++ b/app/interface/forms.py @@ -5,58 +5,72 @@ from crispy_bulma.layout import FormGroup from . import models, fields + class BaseForm(forms.Form): - def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.helper = self.get_form_helper() def get_form_helper(self): helper = FormHelper(self) - #helper.add_input(Submit('submit', 'Submit', css_class='button is-link')) - #helper.layout.subm append(HTML('Cancel')) - #print(helper.layout) - helper.layout.append(FormGroup( - Submit('submit', 'Save', css_class="button is-primary"), - HTML('{% if view.cancel_url %}
{% endif %}') - )) + # helper.add_input(Submit('submit', 'Submit', css_class='button is-link')) + # helper.layout.subm append(HTML('Cancel')) + # print(helper.layout) + helper.layout.append( + FormGroup( + Submit("submit", "Save", css_class="button is-primary"), + HTML( + '{% if view.cancel_url %}{% endif %}' + ), + ) + ) return helper class ProjectForm(forms.ModelForm, BaseForm): - class Meta: model = models.Project - fields = ['name', 'description', 'modules', 'event_date'] - #widgets = { + fields = ["name", "description", "modules", "event_date"] + # widgets = { # 'event_date': forms.DateTimeInput(attrs={'type': 'date'}) - #} + # } + + modules = forms.MultipleChoiceField( + choices=[(x, x.title()) for x in models.settings.POLYPHONIC_MODULES], + widget=forms.CheckboxSelectMultiple, + required=False, + ) - modules = forms.MultipleChoiceField(choices=[ (x, x.title()) for x in models.settings.POLYPHONIC_MODULES ], - widget=forms.CheckboxSelectMultiple, required=False) class ResourceForm(forms.ModelForm, BaseForm): - class Meta: model = models.Resource - fields = ['name', 'media_type', 'description', 'file'] + fields = ["name", "media_type", "description", "file"] def get_form_helper(self): helper = super().get_form_helper() helper[3].wrap(fields.BulmaFileUpload) return helper -class WikiForm(forms.ModelForm, BaseForm): +class WikiForm(forms.ModelForm, BaseForm): class Meta: model = models.WikiPage - fields = ['title', 'markdown'] + fields = ["title", "markdown"] + class CodeForm(BaseForm): - code = forms.CharField(max_length=14, - widget=forms.TextInput(attrs={'placeholder': 'xxx-xxx-xxx', 'inputmode': 'numeric'})) + code = forms.CharField( + max_length=14, + widget=forms.TextInput( + attrs={"placeholder": "xxx-xxx-xxx", "inputmode": "numeric"} + ), + ) passphrase = forms.CharField(max_length=32) + class ResourceUploadForm(forms.Form): pass -# file = S3UploadField() \ No newline at end of file + + +# file = S3UploadField() diff --git a/app/interface/models.py b/app/interface/models.py index 34b0409..dbf3fe4 100644 --- a/app/interface/models.py +++ b/app/interface/models.py @@ -14,11 +14,12 @@ import os.path from .utils import sign_data MEDIA_TYPES = [ - ('audio', "Audio"), - ('video', "Video"), - ('general', "General"), + ("audio", "Audio"), + ("video", "Video"), + ("general", "General"), ] + def rough_date(d): if not d: return False, "sometime..." @@ -27,53 +28,62 @@ def rough_date(d): if in_past: days = abs(days) if days == 0: - m = int((d-timezone.now()).seconds/60) + m = int((d - timezone.now()).seconds / 60) if m > 60: return in_past, "{0:d} hours".format(int(m / 60)) return in_past, "{0:d} minutes!".format(int(m % 60)) if days >= 14: - return in_past, "{0:d} weeks".format(int(days/7)) + return in_past, "{0:d} weeks".format(int(days / 7)) if days >= 7: return in_past, "{0:d} weeks, {1:d} days".format(int(days / 7), int(days % 7)) return in_past, f"{days} days" def generate_code(length=9): - return "".join([ random.choice('0123456789') for _ in range(length) ]) + return "".join([random.choice("0123456789") for _ in range(length)]) + class EnsembleQuerySet(models.QuerySet): - def for_user(self, user, ensemble_keys=[], project_keys=[]): if user.is_superuser: - return self + return self f = models.Q(slug__in=ensemble_keys) | models.Q(projects__in=project_keys) - + if user.is_authenticated: f |= models.Q(admins=user.pk) return self.filter(f).distinct() + class Ensemble(models.Model): - ''' A group that plays together - - ''' - name = models.CharField(max_length=100, - help_text="Display name") - slug = models.SlugField(max_length=100, editable=False, unique=True, - help_text="Short name for the ensemble - used for folders") - admins = models.ManyToManyField('auth.User', related_name='ensembles') - details = models.TextField(blank=True, - help_text="Description of the ensemble (markdown)") - storage = models.ForeignKey('byostorage.UserStorage', null=True, on_delete=models.SET_NULL, - help_text="Default storage for this ensemble") - nonce = models.SmallIntegerField(default=1, - help_text="Increment this to reset the authentication links") + """A group that plays together""" + + name = models.CharField(max_length=100, help_text="Display name") + slug = models.SlugField( + max_length=100, + editable=False, + unique=True, + help_text="Short name for the ensemble - used for folders", + ) + admins = models.ManyToManyField("auth.User", related_name="ensembles") + details = models.TextField( + blank=True, help_text="Description of the ensemble (markdown)" + ) + storage = models.ForeignKey( + "byostorage.UserStorage", + null=True, + on_delete=models.SET_NULL, + help_text="Default storage for this ensemble", + ) + nonce = models.SmallIntegerField( + default=1, help_text="Increment this to reset the authentication links" + ) objects = EnsembleQuerySet.as_manager() class Meta: - ordering = ('slug', ) + ordering = ("slug",) def active_projects(self): return self.projects.active().current() @@ -83,7 +93,7 @@ class Ensemble(models.Model): return False if user.is_superuser: return True - return user.pk in self.admins.values_list('pk', flat=True) + return user.pk in self.admins.values_list("pk", flat=True) def save(self, **kwargs): if not self.slug: @@ -91,49 +101,56 @@ class Ensemble(models.Model): super(Ensemble, self).save(**kwargs) def get_absolute_url(self): - return resolve_url('ensemble_detail', ensemble=self.slug) + return resolve_url("ensemble_detail", ensemble=self.slug) def auth(self): - return sign_data(f'{self.pk}-{self.nonce}', 12) + return sign_data(f"{self.pk}-{self.nonce}", 12) def __str__(self): return self.name + class ProjectQuerySet(models.QuerySet): def current(self): - return self.filter(models.Q(event_date__gte=(timezone.now()-timezone.timedelta(7))) | models.Q(event_date=None)) - + return self.filter( + models.Q(event_date__gte=(timezone.now() - timezone.timedelta(7))) + | models.Q(event_date=None) + ) + def active(self): return self.filter(active=True) def for_user(self, user, project_keys=[], ensemble_keys=[]): if user.is_superuser: - return self + return self f = models.Q(pk__in=project_keys) | models.Q(ensemble__slug__in=ensemble_keys) - + if user.is_authenticated: f |= models.Q(ensemble__admins=user.pk) return self.filter(f) + class Project(models.Model): - ''' A Project linked to an ensemble - ''' + """A Project linked to an ensemble""" + name = models.CharField(max_length=100) - ensemble = models.ForeignKey(Ensemble, related_name='projects', on_delete=models.CASCADE, null=True) - description = models.TextField(blank=True, - help_text="Markdown format") + ensemble = models.ForeignKey( + Ensemble, related_name="projects", on_delete=models.CASCADE, null=True + ) + description = models.TextField(blank=True, help_text="Markdown format") active = models.BooleanField(default=True) - event_date =models.DateTimeField(null=True, blank=True) + event_date = models.DateTimeField(null=True, blank=True) owner = models.CharField(max_length=255, blank=True) - nonce = models.SmallIntegerField(default=1, - help_text="Increment this to reset the authentication links") + nonce = models.SmallIntegerField( + default=1, help_text="Increment this to reset the authentication links" + ) objects = ProjectQuerySet.as_manager() class Meta: - ordering = ['active', 'event_date'] + ordering = ["active", "event_date"] @property def days(self): @@ -162,62 +179,74 @@ class Project(models.Model): @property def active_modules(self): - return self.modules.values_list('name', flat=True) + return self.modules.values_list("name", flat=True) def get_absolute_url(self): - return resolve_url('project_detail', project=self.pk) - + return resolve_url("project_detail", project=self.pk) + def auth(self): - return sign_data(f'{self.pk}-{self.nonce}', 12) + return sign_data(f"{self.pk}-{self.nonce}", 12) def __str__(self): return self.name + class Module(models.Model): - ''' Enable modules on a oriject - ''' - name = models.SlugField(max_length=20, choices=[ (x, x.title()) for x in settings.POLYPHONIC_MODULES ]) - project = models.ForeignKey(Project, related_name="modules", on_delete=models.CASCADE) + """Enable modules on a oriject""" + + name = models.SlugField( + max_length=20, choices=[(x, x.title()) for x in settings.POLYPHONIC_MODULES] + ) + project = models.ForeignKey( + Project, related_name="modules", on_delete=models.CASCADE + ) def __str__(self): return self.name + def resource_key(resource, filename): - return f'{resource.project.folder}/resources/{filename}' + return f"{resource.project.folder}/resources/{filename}" + class Resource(models.Model): - ''' A viewable file resource attached to a project + """A viewable file resource attached to a project e.g PDF instructions, MP3 backing track - ''' - project = models.ForeignKey(Project, related_name='resources', on_delete=models.CASCADE) + """ + + project = models.ForeignKey( + Project, related_name="resources", on_delete=models.CASCADE + ) name = models.CharField(max_length=100) description = models.TextField(blank=True) file = models.FileField(storage=BYOStorage(), upload_to=resource_key) - media_type = models.CharField(max_length=10, choices=MEDIA_TYPES, default='*') + media_type = models.CharField(max_length=10, choices=MEDIA_TYPES, default="*") visible = models.BooleanField(default=True) class Meta: - ordering = ['-visible', '-pk'] + ordering = ["-visible", "-pk"] def accept(self): - if self.media_type == 'general': + if self.media_type == "general": return ".*" return f"{self.media_type}/*" def __str__(self): return self.name + class WikiPage(models.Model): - ''' An editable wiki page for the project in markdown format - - ''' - project = models.ForeignKey(Project, related_name='wiki_pages', on_delete=models.CASCADE) + """An editable wiki page for the project in markdown format""" + + project = models.ForeignKey( + Project, related_name="wiki_pages", on_delete=models.CASCADE + ) title = models.CharField(max_length=255) markdown = models.TextField() def get_absolute_url(self): - return resolve_url('wiki', project=self.project_id, pk=self.pk) + return resolve_url("wiki", project=self.project_id, pk=self.pk) def __str__(self): - return self.title \ No newline at end of file + return self.title diff --git a/app/interface/templatetags/path_filters.py b/app/interface/templatetags/path_filters.py index 804ac1f..4856509 100644 --- a/app/interface/templatetags/path_filters.py +++ b/app/interface/templatetags/path_filters.py @@ -3,7 +3,9 @@ import os.path register = template.Library() + def basename(value): return os.path.basename(value) -register.filter('basename', basename) \ No newline at end of file + +register.filter("basename", basename) diff --git a/app/interface/templatetags/polyphonic.py b/app/interface/templatetags/polyphonic.py index e531c37..08bcde3 100644 --- a/app/interface/templatetags/polyphonic.py +++ b/app/interface/templatetags/polyphonic.py @@ -3,12 +3,16 @@ from django.utils import timesince register = template.Library() + def roughtimesince(value): return timesince.timesince(value, depth=1) -register.filter('roughtimesince', roughtimesince) + +register.filter("roughtimesince", roughtimesince) + def roughtimeuntil(value): return timesince.timeuntil(value, depth=1) -register.filter('roughtimeuntil', roughtimeuntil) \ No newline at end of file + +register.filter("roughtimeuntil", roughtimeuntil) diff --git a/app/interface/templatetags/url_tools.py b/app/interface/templatetags/url_tools.py index 171e80a..9a19808 100644 --- a/app/interface/templatetags/url_tools.py +++ b/app/interface/templatetags/url_tools.py @@ -2,9 +2,10 @@ from django import template register = template.Library() + @register.simple_tag(takes_context=True) def url_update(context, **kwargs): params = context.request.GET.copy() for k in kwargs: params[k] = kwargs[k] - return "?" + params.urlencode() \ No newline at end of file + return "?" + params.urlencode() diff --git a/app/interface/tests/__init__.py b/app/interface/tests/__init__.py index e52c9e0..1f199a8 100644 --- a/app/interface/tests/__init__.py +++ b/app/interface/tests/__init__.py @@ -4,8 +4,8 @@ from django.contrib.auth.models import User from django.utils import timezone from datetime import timedelta -class AccessTestCase(TestCase): +class AccessTestCase(TestCase): USERS = () ENSEMBLES = () @@ -19,13 +19,13 @@ class AccessTestCase(TestCase): cls.users = {} for details in cls.USERS: - cls.users[details['username']] = User.objects.create_user(**details) + cls.users[details["username"]] = User.objects.create_user(**details) now = timezone.now() cls.ensembles = {} for details in cls.ENSEMBLES: - admins = details.pop('admins', []) + admins = details.pop("admins", []) obj = models.Ensemble.objects.create(**details) for admin in admins: obj.admins.add(cls.users[admin]) @@ -33,37 +33,41 @@ class AccessTestCase(TestCase): cls.projects = {} for details in cls.PROJECTS: - when = details.pop('when', 0) - ensemble = details.pop('ensemble') - details['event_date'] = now + timedelta(days=when) if when else None + when = details.pop("when", 0) + ensemble = details.pop("ensemble") + details["event_date"] = now + timedelta(days=when) if when else None obj = cls.ensembles[ensemble].projects.create(**details) - cls.projects[details['name']] = obj - + cls.projects[details["name"]] = obj + return def test_protected_views(self): - self.assertAccess({ x: False for x in self.PROTECTED_URLS }) + self.assertAccess({x: False for x in self.PROTECTED_URLS}) - if 'admin' in self.users: - self.client.force_login(self.users['admin']) - self.assertAccess({ x: True for x in self.PROTECTED_URLS }) + if "admin" in self.users: + self.client.force_login(self.users["admin"]) + self.assertAccess({x: True for x in self.PROTECTED_URLS}) def login(self, user, passwd): - response = self.client.post('/login', {'username': user, 'password': passwd}) + response = self.client.post("/login", {"username": user, "password": passwd}) self.assertEqual(response.status_code, 302, f"Failed to login as {user}") def authorize(self, model, **kwargs): object = model.objects.get(**kwargs) - response = self.client.get(f'{object.get_absolute_url()}?auth={object.auth()}') + response = self.client.get(f"{object.get_absolute_url()}?auth={object.auth()}") self.assertEqual(response.status_code, 302) def assertAccess(self, urls): for url, expected in urls.items(): response = self.client.get(url) - self.assertEqual(response.status_code == 200, expected, f"Expected {expected} for {url} (status: {response.status_code})") + self.assertEqual( + response.status_code == 200, + expected, + f"Expected {expected} for {url} (status: {response.status_code})", + ) - def assertObjectList(self, response, expected, element='name'): + def assertObjectList(self, response, expected, element="name"): self.assertEqual(response.status_code, 200, "No result returned") - objects = response.context['object_list'].values_list(element, flat=True) - self.assertEqual(list(objects), expected) \ No newline at end of file + objects = response.context["object_list"].values_list(element, flat=True) + self.assertEqual(list(objects), expected) diff --git a/app/interface/tests/test_access.py b/app/interface/tests/test_access.py index 5727219..05d1c9f 100644 --- a/app/interface/tests/test_access.py +++ b/app/interface/tests/test_access.py @@ -5,179 +5,213 @@ from django.contrib.auth.models import User from . import AccessTestCase -class InterfaceAccessTestCase(AccessTestCase): +class InterfaceAccessTestCase(AccessTestCase): USERS = ( - {'username': 'admin', 'password': 'secret', 'is_superuser': True, 'is_staff': True}, - {'username': 'homer', 'password': 'maggie'}, + { + "username": "admin", + "password": "secret", + "is_superuser": True, + "is_staff": True, + }, + {"username": "homer", "password": "maggie"}, ) ENSEMBLES = ( - {'name': 'The Be Sharps', 'slug': 'be-sharps', 'admins': ['homer']}, - {'name': 'Lisa & the Bleeding Gums', 'slug': 'bleeding-gums'}, - {'name': 'Party Posse'}, + {"name": "The Be Sharps", "slug": "be-sharps", "admins": ["homer"]}, + {"name": "Lisa & the Bleeding Gums", "slug": "bleeding-gums"}, + {"name": "Party Posse"}, ) PROJECTS = ( - {'name': 'Baker St', 'ensemble': 'bleeding-gums', 'when': -12}, - {'name': 'Navy Recruitment Day', 'ensemble': 'party-posse', 'when': 6}, - {'name': 'Barbershop Contest', 'ensemble': 'be-sharps', 'when': 28}, - {'name': 'Open Mic Night', 'ensemble': 'bleeding-gums', 'when': 1 }, - {'name': 'Current Repertoire', 'ensemble': 'be-sharps'}, + {"name": "Baker St", "ensemble": "bleeding-gums", "when": -12}, + {"name": "Navy Recruitment Day", "ensemble": "party-posse", "when": 6}, + {"name": "Barbershop Contest", "ensemble": "be-sharps", "when": 28}, + {"name": "Open Mic Night", "ensemble": "bleeding-gums", "when": 1}, + {"name": "Current Repertoire", "ensemble": "be-sharps"}, ) PROTECTED_URLS = ( - '/ensembles/be-sharps', - '/ensembles/be-sharps/new-project', - - '/projects/3', - '/projects/3/resources', - '/projects/3/resources/add', - - '/admin/interface/ensemble/', - '/admin/interface/project/', - '/admin/interface/resource/', - '/admin/interface/wikipage/', + "/ensembles/be-sharps", + "/ensembles/be-sharps/new-project", + "/projects/3", + "/projects/3/resources", + "/projects/3/resources/add", + "/admin/interface/ensemble/", + "/admin/interface/project/", + "/admin/interface/resource/", + "/admin/interface/wikipage/", ) def test_bad_login(self): - with self.assertRaisesMessage(self.failureException, 'Failed to login as admin'): - self.login('admin', 'admin') + with self.assertRaisesMessage( + self.failureException, "Failed to login as admin" + ): + self.login("admin", "admin") def test_superuser_ensembles(self): - self.login('admin', 'secret') - response = self.client.get('/ensembles') - self.assertObjectList(response, ['The Be Sharps', 'Lisa & the Bleeding Gums', 'Party Posse']) - self.assertContains(response, 'Django Admin') + self.login("admin", "secret") + response = self.client.get("/ensembles") + self.assertObjectList( + response, ["The Be Sharps", "Lisa & the Bleeding Gums", "Party Posse"] + ) + self.assertContains(response, "Django Admin") def test_superuser_ensemble_permissions(self): - self.login('admin', 'secret') - response = self.client.get('/ensembles/party-posse') - self.assertTrue(response.context['request'].is_admin) + self.login("admin", "secret") + response = self.client.get("/ensembles/party-posse") + self.assertTrue(response.context["request"].is_admin) self.assertContains(response, "Add project") - self.assertAccess({ - '/ensembles/be-sharps': True, - '/ensembles/bleeding-gums': True, - '/ensembles/party-posse': True, - '/ensembles/unknown': False, - '/ensembles/be-sharps/new-project': True, - }) + self.assertAccess( + { + "/ensembles/be-sharps": True, + "/ensembles/bleeding-gums": True, + "/ensembles/party-posse": True, + "/ensembles/unknown": False, + "/ensembles/be-sharps/new-project": True, + } + ) def test_superuser_projects(self): - self.login('admin', 'secret') - response = self.client.get('/projects') - self.assertObjectList(response, ['Current Repertoire', 'Open Mic Night', 'Navy Recruitment Day', 'Barbershop Contest']) - - self.assertObjectList(self.client.get('/ensembles/bleeding-gums'), ['Open Mic Night']) - self.assertObjectList(self.client.get('/ensembles/bleeding-gums?inactive'), ['Open Mic Night', 'Baker St']) + self.login("admin", "secret") + response = self.client.get("/projects") + self.assertObjectList( + response, + [ + "Current Repertoire", + "Open Mic Night", + "Navy Recruitment Day", + "Barbershop Contest", + ], + ) + self.assertObjectList( + self.client.get("/ensembles/bleeding-gums"), ["Open Mic Night"] + ) + self.assertObjectList( + self.client.get("/ensembles/bleeding-gums?inactive"), + ["Open Mic Night", "Baker St"], + ) def test_user_ensembles(self): - self.login('homer', 'maggie') - response = self.client.get('/ensembles') - self.assertObjectList(response, ['The Be Sharps']) + self.login("homer", "maggie") + response = self.client.get("/ensembles") + self.assertObjectList(response, ["The Be Sharps"]) - self.assertNotContains(response, 'Django Admin') + self.assertNotContains(response, "Django Admin") def test_user_ensemble_permissions(self): - self.login('homer', 'maggie') - response = self.client.get('/ensembles/be-sharps') - self.assertTrue(response.context['request'].is_admin) + self.login("homer", "maggie") + response = self.client.get("/ensembles/be-sharps") + self.assertTrue(response.context["request"].is_admin) self.assertContains(response, "Add project") - self.assertContains(response, 'Show all') - self.assertAccess({ - '/ensembles/be-sharps': True, - '/ensembles/bleeding-gums': False, - '/ensembles/party-posse': False, - '/ensembles/be-sharps/new-project': True, - '/ensembles/party-posse/new-project': False, - }) + self.assertContains(response, "Show all") + self.assertAccess( + { + "/ensembles/be-sharps": True, + "/ensembles/bleeding-gums": False, + "/ensembles/party-posse": False, + "/ensembles/be-sharps/new-project": True, + "/ensembles/party-posse/new-project": False, + } + ) - self.authorize(models.Ensemble, slug='bleeding-gums') - self.assertAccess({ - '/ensembles/be-sharps': True, - '/ensembles/bleeding-gums': True, - '/ensembles/party-posse': False, - '/ensembles/be-sharps/new-project': True, - '/ensembles/party-posse/new-project': False, - }) - response = self.client.get('/ensembles/bleeding-gums') - self.assertFalse(response.context['request'].is_admin) - self.assertNotContains(response, 'Add project') - self.assertNotContains(response, 'Show all') + self.authorize(models.Ensemble, slug="bleeding-gums") + self.assertAccess( + { + "/ensembles/be-sharps": True, + "/ensembles/bleeding-gums": True, + "/ensembles/party-posse": False, + "/ensembles/be-sharps/new-project": True, + "/ensembles/party-posse/new-project": False, + } + ) + response = self.client.get("/ensembles/bleeding-gums") + self.assertFalse(response.context["request"].is_admin) + self.assertNotContains(response, "Add project") + self.assertNotContains(response, "Show all") def test_user_projects(self): - self.login('homer', 'maggie') - response = self.client.get('/projects') - self.assertObjectList(response, ['Current Repertoire', 'Barbershop Contest']) - response = self.client.get('/projects/3') - self.assertTrue(response.context['request'].is_admin) + self.login("homer", "maggie") + response = self.client.get("/projects") + self.assertObjectList(response, ["Current Repertoire", "Barbershop Contest"]) + response = self.client.get("/projects/3") + self.assertTrue(response.context["request"].is_admin) - - self.assertAccess({ - '/projects/3': True, - '/projects/3/resources': True, - '/projects/3/resources/add': True, - '/projects/4': False, - '/projects/4/resources': False, - '/projects/4/resources/add': False, - }) + self.assertAccess( + { + "/projects/3": True, + "/projects/3/resources": True, + "/projects/3/resources/add": True, + "/projects/4": False, + "/projects/4/resources": False, + "/projects/4/resources/add": False, + } + ) self.authorize(models.Project, pk=4) - response = self.client.get('/projects') - self.assertObjectList(response, ['Current Repertoire', 'Open Mic Night', 'Barbershop Contest']) - response = self.client.get('/projects/4') - self.assertFalse(response.context['request'].is_admin) + response = self.client.get("/projects") + self.assertObjectList( + response, ["Current Repertoire", "Open Mic Night", "Barbershop Contest"] + ) + response = self.client.get("/projects/4") + self.assertFalse(response.context["request"].is_admin) def test_anon_ensembles(self): - response = self.client.get('/ensembles') + response = self.client.get("/ensembles") self.assertObjectList(response, []) - self.assertContains(response, 'You don\'t currently have access to any ensembles') + self.assertContains( + response, "You don't currently have access to any ensembles" + ) def test_anon_authorized_ensemble(self): - self.authorize(models.Ensemble, slug='party-posse') - response = self.client.get('/ensembles/party-posse') - self.assertContains(response, 'Party Posse') - - response = self.client.get('/ensembles') - self.assertObjectList(response, ['Party Posse']) + self.authorize(models.Ensemble, slug="party-posse") + response = self.client.get("/ensembles/party-posse") + self.assertContains(response, "Party Posse") + + response = self.client.get("/ensembles") + self.assertObjectList(response, ["Party Posse"]) + + self.assertAccess( + { + "/ensembles/be-sharps": False, + "/ensembles/party-posse": True, + "/ensembles/bleeding-gums": False, + "/ensembles/unknown": False, + } + ) + response = self.client.get("/projects") + self.assertObjectList(response, ["Navy Recruitment Day"]) - self.assertAccess({ - '/ensembles/be-sharps': False, - '/ensembles/party-posse': True, - '/ensembles/bleeding-gums': False, - '/ensembles/unknown': False, - }) - response = self.client.get('/projects') - self.assertObjectList(response, ['Navy Recruitment Day']) - def test_anon_authorized_project(self): self.authorize(models.Project, pk=4) - self.assertObjectList(self.client.get('/projects'), ['Open Mic Night']) - self.assertObjectList(self.client.get('/ensembles'), ['Lisa & the Bleeding Gums']) + self.assertObjectList(self.client.get("/projects"), ["Open Mic Night"]) + self.assertObjectList( + self.client.get("/ensembles"), ["Lisa & the Bleeding Gums"] + ) - self.assertAccess({ - '/projects/4': True, - '/projects/4/resources': True, - '/projects/1': False, - '/projects/1/resources': False, - }) + self.assertAccess( + { + "/projects/4": True, + "/projects/4/resources": True, + "/projects/1": False, + "/projects/1/resources": False, + } + ) def test_anon_permission_denied(self): - self.assertAccess({ - '/ensembles': True, - '/ensembles/be-sharps': False, - '/ensembles/party-posse': False, - '/ensembles/bleeding-gums': False, - '/ensembles/unknown': False, - }) + self.assertAccess( + { + "/ensembles": True, + "/ensembles/be-sharps": False, + "/ensembles/party-posse": False, + "/ensembles/bleeding-gums": False, + "/ensembles/unknown": False, + } + ) def test_anon_deauthorize_project(self): self.authorize(models.Project, pk=4) - self.assertAccess({ - '/projects/4': True - }) + self.assertAccess({"/projects/4": True}) models.Project.objects.filter(pk=4).update(nonce=2) - self.assertAccess({ - '/projects/4': False - }) \ No newline at end of file + self.assertAccess({"/projects/4": False}) diff --git a/app/interface/tests/test_integration.py b/app/interface/tests/test_integration.py index fb41d81..0e85ea6 100644 --- a/app/interface/tests/test_integration.py +++ b/app/interface/tests/test_integration.py @@ -1,6 +1,6 @@ from django.test import TestCase -class IntegrationTestCase(TestCase): +class IntegrationTestCase(TestCase): def test_runs(self): - self.assertTrue(True) \ No newline at end of file + self.assertTrue(True) diff --git a/app/interface/urls.py b/app/interface/urls.py index c7515a0..6badf5e 100644 --- a/app/interface/urls.py +++ b/app/interface/urls.py @@ -77,4 +77,3 @@ if settings.DEBUG: urlpatterns.append( path("local_storage/