diff --git a/config/settings.py b/config/settings.py index 1598205..3ba6717 100644 --- a/config/settings.py +++ b/config/settings.py @@ -47,7 +47,8 @@ INSTALLED_APPS = [ 'django.contrib.messages', 'whitenoise.runserver_nostatic', 'django.contrib.staticfiles', - + + 'crispy_forms', 'django_secrets' ] @@ -75,6 +76,7 @@ TEMPLATES = [ 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', + 'django.template.context_processors.media', ], }, }, @@ -151,4 +153,4 @@ MEDIA_ROOT = os.path.join(BASE_DIR, 'media') MEDIA_URL = '/media/' LOGIN_REDIRECT_URL = 'blog-home' -LOGIN_URL = 'login' \ No newline at end of file +LOGIN_URL = 'login' diff --git a/config/urls.py b/config/urls.py index 20bd978..27be4df 100644 --- a/config/urls.py +++ b/config/urls.py @@ -13,14 +13,20 @@ Including another URLconf 1. Import the include() function: from django.urls import include, path 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) """ +from django.conf import settings +from django.conf.urls.static import static from django.contrib import admin from django.urls import path, include from users import views as user_views +from innovate.admin import admin_site as innovate_admin urlpatterns = [ path('', include('launchx.urls')), path('innovate/', include('innovate.urls'), name='innovate'), path('login/', user_views.login, name='login'), path('logout/', user_views.login, name='logout'), - path('admin/', admin.site.urls), + path('admin/', innovate_admin.urls), ] + +if settings.DEBUG: + urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) \ No newline at end of file diff --git a/innovate/admin.py b/innovate/admin.py index 8c38f3f..d4452aa 100644 --- a/innovate/admin.py +++ b/innovate/admin.py @@ -1,3 +1,18 @@ from django.contrib import admin +from .models import Team, Competitor +from django.contrib.auth.models import User, Group + # Register your models here. +class LaunchXAdminSite(admin.AdminSite): + site_header = "LaunchX Admin" + site_title = "LaunchX Admin Portal" + index_title = "Welcome to LaunchX Admin Page" + +admin_site = LaunchXAdminSite(name='launchx-admin') + +admin_site.register(User) +admin_site.register(Group) + +admin_site.register(Competitor) +admin_site.register(Team) \ No newline at end of file diff --git a/innovate/forms.py b/innovate/forms.py new file mode 100644 index 0000000..45c3d1f --- /dev/null +++ b/innovate/forms.py @@ -0,0 +1,41 @@ +from django import forms +from django.forms import modelformset_factory + +from .models import Competitor, Team + +class CompetitorForm(forms.ModelForm): + name = forms.CharField(label='Full Name', widget=forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'John Doe'})) + email = forms.EmailField(label='Email', widget=forms.EmailInput(attrs={'class': 'form-control', 'placeholder': 'example@email.com'})) + + class Meta: + model = Competitor + fields = ['name', 'email'] + + def __init__(self, *args, **kwargs): + super(CompetitorForm, self).__init__(*args, **kwargs) + self.label_suffix = '' + + +CompetitorFormset = modelformset_factory( + Competitor, + form=CompetitorForm, + min_num=2, max_num=4, extra=0) + +class TeamForm(forms.ModelForm): + name = forms.CharField(required=False, label="Team Name (Optional)", widget=forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'BusinessX'})) + reciept = forms.FileField(required=False) + + class Meta: + model = Team + fields = ['name', 'reciept'] + + def __init__(self, *args, **kwargs): + super(TeamForm, self).__init__(*args, **kwargs) + self.label_suffix = '' + + def save(self, commit=True): + m = super(TeamForm, self).save(commit=False) + m.number = Team.objects.all().count() + 1 + if commit: + m.save() + return m \ No newline at end of file diff --git a/innovate/migrations/0001_initial.py b/innovate/migrations/0001_initial.py new file mode 100644 index 0000000..7f79711 --- /dev/null +++ b/innovate/migrations/0001_initial.py @@ -0,0 +1,42 @@ +# Generated by Django 3.1.6 on 2021-02-03 17:45 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Team', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('number', models.IntegerField()), + ('name', models.CharField(blank=True, max_length=20, null=True)), + ('reciept', models.FileField(blank=True, null=True, upload_to='reciepts/')), + ], + options={ + 'verbose_name': 'Team', + 'verbose_name_plural': 'Teams', + }, + ), + migrations.CreateModel( + name='Competitor', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(blank=True, default='', max_length=20)), + ('email', models.EmailField(max_length=254)), + ('is_leader', models.BooleanField(default=False)), + ('team', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='innovate.team')), + ], + options={ + 'verbose_name': 'Competitor', + 'verbose_name_plural': 'Competitors', + }, + ), + ] diff --git a/innovate/models.py b/innovate/models.py index 71a8362..50f150f 100644 --- a/innovate/models.py +++ b/innovate/models.py @@ -1,3 +1,43 @@ from django.db import models +from django.core.exceptions import ValidationError # Create your models here. + +class Team(models.Model): + number = models.IntegerField() + name = models.CharField(max_length=20, null=True, blank=True) + reciept = models.FileField(upload_to='reciepts/', null=True, blank=True) + + class Meta: + verbose_name = "Team" + verbose_name_plural = "Teams" + + def __str__(self): + return self.name + + def clean(self): + # Don't allow teams to have the same name. + if Team.objects.filter(name=self.name).count() > 0: + raise ValidationError({'name': 'That name is already taken! Sorry.'}) + +class Competitor(models.Model): + name = models.CharField(max_length=20, blank=True, default='') + email = models.EmailField(max_length = 254) + is_leader = models.BooleanField(default=False) + + team = models.ForeignKey(Team, on_delete=models.CASCADE) + + class Meta: + verbose_name = "Competitor" + verbose_name_plural = "Competitors" + + def __str__(self): + return self.name + + def clean(self): + # Don't allow teams to have the same name. + if Competitor.objects.filter(name=self.name).count() > 0: + raise ValidationError({'name': 'Somebody with that name is already registered!'}) + + if Competitor.objects.filter(email=self.email).count() > 0: + raise ValidationError({'email': 'Somebody with that email is already registered!'}) diff --git a/innovate/static/innovate/formsets.js b/innovate/static/innovate/formsets.js new file mode 100644 index 0000000..a339cb7 --- /dev/null +++ b/innovate/static/innovate/formsets.js @@ -0,0 +1,250 @@ +/** + * jQuery Formset 1.5-pre + * @author Stanislaus Madueke (stan DOT madueke AT gmail DOT com) + * @requires jQuery 1.2.6 or later + * + * Copyright (c) 2009, Stanislaus Madueke + * All rights reserved. + * + * Licensed under the New BSD License + * See: http://www.opensource.org/licenses/bsd-license.php + */ +;(function($) { + $.fn.formset = function(opts) + { + var options = $.extend({}, $.fn.formset.defaults, opts), + flatExtraClasses = options.extraClasses.join(' '), + totalForms = $('#id_' + options.prefix + '-TOTAL_FORMS'), + maxForms = $('#id_' + options.prefix + '-MAX_NUM_FORMS'), + minForms = $('#id_' + options.prefix + '-MIN_NUM_FORMS'), + childElementSelector = 'input,select,textarea,label,div', + $$ = $(this), + + applyExtraClasses = function(row, ndx) { + if (options.extraClasses) { + row.removeClass(flatExtraClasses); + row.addClass(options.extraClasses[ndx % options.extraClasses.length]); + } + }, + + updateElementIndex = function(elem, prefix, ndx) { + var idRegex = new RegExp(prefix + '-(\\d+|__prefix__)-'), + replacement = prefix + '-' + ndx + '-'; + if (elem.attr("for")) elem.attr("for", elem.attr("for").replace(idRegex, replacement)); + if (elem.attr('id')) elem.attr('id', elem.attr('id').replace(idRegex, replacement)); + if (elem.attr('name')) elem.attr('name', elem.attr('name').replace(idRegex, replacement)); + }, + + hasChildElements = function(row) { + return row.find(childElementSelector).length > 0; + }, + + showAddButton = function() { + return maxForms.length == 0 || // For Django versions pre 1.2 + (maxForms.val() == '' || (maxForms.val() - totalForms.val() > 0)); + }, + + /** + * Indicates whether delete link(s) can be displayed - when total forms > min forms + */ + showDeleteLinks = function() { + return minForms.length == 0 || // For Django versions pre 1.7 + (minForms.val() == '' || (totalForms.val() - minForms.val() > 0)); + }, + + insertDeleteLink = function(row) { + var delCssSelector = $.trim(options.deleteCssClass).replace(/\s+/g, '.'), + addCssSelector = $.trim(options.addCssClass).replace(/\s+/g, '.'); + + var delButtonHTML = '' + options.deleteText +''; + if (options.deleteContainerClass) { + // If we have a specific container for the remove button, + // place it as the last child of that container: + row.find('[class*="' + options.deleteContainerClass + '"]').append(delButtonHTML); + } else if (row.is('TR')) { + // If the forms are laid out in table rows, insert + // the remove button into the last table cell: + row.children(':last').append(delButtonHTML); + } else if (row.is('UL') || row.is('OL')) { + // If they're laid out as an ordered/unordered list, + // insert an