mirror of
https://github.com/Rushilwiz/launchx.git
synced 2025-04-04 12:10:17 -04:00
feat: added judges section
This commit is contained in:
parent
3a97b74639
commit
55fc02219f
|
@ -160,7 +160,7 @@ CRISPY_TEMPLATE_PACK = 'bootstrap4'
|
|||
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
|
||||
MEDIA_URL = '/media/'
|
||||
|
||||
LOGIN_REDIRECT_URL = 'blog-home'
|
||||
LOGIN_REDIRECT_URL = 'judges-portal'
|
||||
LOGIN_URL = 'login'
|
||||
|
||||
# Email
|
||||
|
|
|
@ -19,12 +19,13 @@ 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
|
||||
from django.contrib.auth import views as auth_views
|
||||
|
||||
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('login/', auth_views.LoginView.as_view(template_name='users/login.html'), name='login'),
|
||||
path('logout/', user_views.logout, name='logout'),
|
||||
path('admin/', innovate_admin.urls),
|
||||
]
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
from django.contrib import admin
|
||||
|
||||
from .models import Team, Competitor
|
||||
from .models import Team, Competitor, Judge, Score
|
||||
from django.contrib.auth.models import User, Group
|
||||
|
||||
# Register your models here.
|
||||
|
@ -14,6 +14,9 @@ admin_site = LaunchXAdminSite(name='launchx-admin')
|
|||
admin_site.register(User)
|
||||
admin_site.register(Group)
|
||||
|
||||
admin_site.register(Judge)
|
||||
admin_site.register(Score)
|
||||
|
||||
admin_site.register(Competitor)
|
||||
|
||||
class CompetitorInline(admin.TabularInline):
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
from django import forms
|
||||
from django.forms import modelformset_factory
|
||||
|
||||
from .models import Competitor, Team
|
||||
from .models import Competitor, Team, Score
|
||||
|
||||
class CompetitorForm(forms.ModelForm):
|
||||
name = forms.CharField(label='Full Name', widget=forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'John Doe'}))
|
||||
|
@ -24,7 +24,7 @@ CompetitorFormset = modelformset_factory(
|
|||
min_num=2, max_num=4)
|
||||
|
||||
class TeamForm(forms.ModelForm):
|
||||
name = forms.CharField(required=False, label="Team Name", widget=forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'BusinessX'}))
|
||||
name = forms.CharField(required=True, label="Team Name", widget=forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'BusinessX'}))
|
||||
reciept = forms.FileField(required=False)
|
||||
|
||||
class Meta:
|
||||
|
@ -40,4 +40,21 @@ class TeamForm(forms.ModelForm):
|
|||
m.number = Team.objects.all().count() + 1
|
||||
if commit:
|
||||
m.save()
|
||||
return m
|
||||
return m
|
||||
|
||||
class ScoreForm(forms.ModelForm):
|
||||
innovation = forms.IntegerField(min_value=0, max_value=30, widget=forms.NumberInput(attrs={'placeholder': '30'}))
|
||||
need = forms.IntegerField(min_value=0, max_value=35, widget=forms.NumberInput(attrs={'placeholder': '35'}))
|
||||
finances = forms.IntegerField(min_value=0, max_value=25, widget=forms.NumberInput(attrs={'placeholder': '25'}))
|
||||
creativity = forms.IntegerField(min_value=0, max_value=10, widget=forms.NumberInput(attrs={'placeholder': '10'}))
|
||||
|
||||
qna = forms.IntegerField(min_value=0, max_value=25, widget=forms.NumberInput(attrs={'placeholder': '25'}))
|
||||
speaking = forms.IntegerField(min_value=0, max_value=10, widget=forms.NumberInput(attrs={'placeholder': '10'}))
|
||||
persuasiveness = forms.IntegerField(min_value=0, max_value=10, widget=forms.NumberInput(attrs={'placeholder': '10'}))
|
||||
professionalism = forms.IntegerField(min_value=0, max_value=5, widget=forms.NumberInput(attrs={'placeholder': '5'}))
|
||||
|
||||
feedback = forms.CharField(required=False, widget=forms.Textarea(attrs={'placeholder': 'Enter feedback here (optional)', 'class': 'form-control'}))
|
||||
|
||||
class Meta:
|
||||
model = Score
|
||||
fields = ['innovation', 'need', 'finances', 'creativity', 'qna', 'speaking', 'persuasiveness', 'professionalism']
|
27
innovate/migrations/0004_judge.py
Normal file
27
innovate/migrations/0004_judge.py
Normal file
|
@ -0,0 +1,27 @@
|
|||
# Generated by Django 3.1.6 on 2021-02-19 05:04
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('innovate', '0003_auto_20210211_0122'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Judge',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Judge',
|
||||
'verbose_name_plural': 'Judges',
|
||||
},
|
||||
),
|
||||
]
|
19
innovate/migrations/0005_judge_iseven.py
Normal file
19
innovate/migrations/0005_judge_iseven.py
Normal file
|
@ -0,0 +1,19 @@
|
|||
# Generated by Django 3.1.6 on 2021-02-19 05:10
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('innovate', '0004_judge'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='judge',
|
||||
name='isEven',
|
||||
field=models.BooleanField(default=False),
|
||||
preserve_default=False,
|
||||
),
|
||||
]
|
36
innovate/migrations/0006_score.py
Normal file
36
innovate/migrations/0006_score.py
Normal file
|
@ -0,0 +1,36 @@
|
|||
# Generated by Django 3.1.6 on 2021-02-19 17:20
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import innovate.models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('innovate', '0005_judge_iseven'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Score',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('innovation', innovate.models.IntegerRangeField()),
|
||||
('need', innovate.models.IntegerRangeField()),
|
||||
('finances', innovate.models.IntegerRangeField()),
|
||||
('creativity', innovate.models.IntegerRangeField()),
|
||||
('qna', innovate.models.IntegerRangeField()),
|
||||
('speaking', innovate.models.IntegerRangeField()),
|
||||
('persuasiveness', innovate.models.IntegerRangeField()),
|
||||
('professionalism', innovate.models.IntegerRangeField()),
|
||||
('feedback', models.TextField(blank=True, null=True)),
|
||||
('judge', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='innovate.judge')),
|
||||
('team', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='innovate.team')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Feedback',
|
||||
'verbose_name_plural': 'Feedback',
|
||||
},
|
||||
),
|
||||
]
|
|
@ -1,7 +1,15 @@
|
|||
from django.db import models
|
||||
from django.contrib.auth.models import User
|
||||
from django.core.exceptions import ValidationError
|
||||
|
||||
# Create your models here.
|
||||
class IntegerRangeField(models.IntegerField):
|
||||
def __init__(self, verbose_name=None, name=None, min_value=None, max_value=None, **kwargs):
|
||||
self.min_value, self.max_value = min_value, max_value
|
||||
models.IntegerField.__init__(self, verbose_name, name, **kwargs)
|
||||
def formfield(self, **kwargs):
|
||||
defaults = {'min_value': self.min_value, 'max_value':self.max_value}
|
||||
defaults.update(kwargs)
|
||||
return super(IntegerRangeField, self).formfield(**defaults)
|
||||
|
||||
class Team(models.Model):
|
||||
number = models.IntegerField()
|
||||
|
@ -43,3 +51,44 @@ class Competitor(models.Model):
|
|||
|
||||
# if Competitor.objects.filter(email=self.email).count() > 0:
|
||||
# raise ValidationError({'email': 'Somebody with that email is already registered!'})
|
||||
|
||||
class Judge(models.Model):
|
||||
user = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||
isEven = models.BooleanField()
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Judge"
|
||||
verbose_name_plural = "Judges"
|
||||
|
||||
def __str__(self):
|
||||
return f'Judge {self.user.last_name}'
|
||||
|
||||
class Score(models.Model):
|
||||
judge = models.ForeignKey(Judge, on_delete=models.CASCADE)
|
||||
team = models.ForeignKey(Team, on_delete=models.CASCADE)
|
||||
|
||||
# product scores
|
||||
innovation = IntegerRangeField(min_value=0, max_value=30)
|
||||
need = IntegerRangeField(min_value=0, max_value=35)
|
||||
finances = IntegerRangeField(min_value=0, max_value=25)
|
||||
creativity = IntegerRangeField(min_value=0, max_value=10)
|
||||
|
||||
# delivery scores
|
||||
qna = IntegerRangeField(min_value=0, max_value=25)
|
||||
speaking = IntegerRangeField(min_value=0, max_value=10)
|
||||
persuasiveness = IntegerRangeField(min_value=0, max_value=10)
|
||||
professionalism = IntegerRangeField(min_value=0, max_value=5)
|
||||
fields = [innovation, need, finances, creativity, qna, speaking, persuasiveness, professionalism]
|
||||
|
||||
feedback = models.TextField(blank=True, null=True)
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Feedback"
|
||||
verbose_name_plural = "Feedback"
|
||||
|
||||
def __str__(self):
|
||||
return f'Team {self.team.number}\'s Feedback from {self.judge.user.get_full_name()}'
|
||||
|
||||
def get_total_score(self):
|
||||
fields = [self.innovation, self.need, self.finances, self.creativity, self.qna, self.speaking, self.persuasiveness, self.professionalism]
|
||||
return sum(fields)
|
6
innovate/static/innovate/portal.css
Normal file
6
innovate/static/innovate/portal.css
Normal file
|
@ -0,0 +1,6 @@
|
|||
h3 small {
|
||||
font-size: .66em;
|
||||
}
|
||||
.text-success {
|
||||
color: rgb(25, 207, 34) !important
|
||||
}
|
121
innovate/templates/innovate/feedback.html
Normal file
121
innovate/templates/innovate/feedback.html
Normal file
|
@ -0,0 +1,121 @@
|
|||
{% extends 'launchx/base.html' %}
|
||||
{% load launchx_extras %}
|
||||
{% block content %}
|
||||
<h1>Feedback for Team {{ team.number }}: {{ team.name }}</h1>
|
||||
<div class="d-flex">
|
||||
<form method="POST">
|
||||
{% csrf_token %}
|
||||
{{ form.non_field_errors }}
|
||||
{{ form.errors }}
|
||||
{% for hidden in form.hidden_fields %}
|
||||
{{ hidden }}
|
||||
{% endfor %}
|
||||
<input type="hidden" id="team_number" name="team_number" value="{{ team.number }}">
|
||||
<div class="container">
|
||||
<h2><span class="border-bottom border-3">Product</span></h2>
|
||||
<div class="col">
|
||||
<div class="row">
|
||||
<h3>Innovation</h3>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<p>Does the product have a unique value proposition (15 pts) ? i.e. does it use novel, innovative technologies, or combine existing technologies in a unique way (10 pts) ? How does their solution fit under the theme of innovation (5 pts) ? </p>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
{{ form.innovation }}/30
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="row">
|
||||
<h3>Marketability / Need</h3>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<p>Is there a need for this product (10 pts) ? Does the team know the intended market (5 pts) ? Do they complete a market analysis or examine their servicable avaible market (15 pts) ? Is the innovation practical (5 pts) ?</p>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
{{ form.need }}/35
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="row">
|
||||
<h3>Finances</h3>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<p>Does the team have a business model (10 pts) ? Do they know who their intended customer is (5 pts) ? Does the team perform a competitive analysis of their market (explain the differentiators between their innovation and current products/services in the market) (10 pts)?</p>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
{{ form.finances }}/25
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="row">
|
||||
<h3>Creativity / Professionalism </h3>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<p>Is their presentation creative AND professional (5 pts each) ?</p>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
{{ form.creativity }}/10
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container mt-md-3">
|
||||
<h2><span class="border-bottom border-3">Delivery</span></h2>
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<h3>Q&A Session</h3>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
{{ form.qna }}/25
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<h3>Speaking Quality </h3>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
{{ form.speaking }}/10
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<h3>Persuasiveness</h3>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
{{ form.persuasiveness }}/10
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<h3>Professionalism</h3>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
{{ form.professionalism }}/5
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container mt-md-3">
|
||||
<div class="col">
|
||||
<div class="row">
|
||||
<h2>Feedback</h2>
|
||||
</div>
|
||||
<div class="row">
|
||||
{{ form.feedback }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group mt-md-3">
|
||||
<button class="btn btn-light btn-block" type="submit">Submit Score</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
31
innovate/templates/innovate/portal.html
Normal file
31
innovate/templates/innovate/portal.html
Normal file
|
@ -0,0 +1,31 @@
|
|||
{% extends 'launchx/base.html' %}
|
||||
{% load launchx_extras %}
|
||||
|
||||
{% block styles %}
|
||||
<link rel="stylesheet" href="{% static 'innovate/portal.css' %}">
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container mt-md-3 w-75">
|
||||
<h1>Welcome Judge {{ request.user.last_name }},</h1>
|
||||
<h5>Thanks for judging at InnovateTJ, now time to score!</h5>
|
||||
<hr>
|
||||
</div>
|
||||
<div class="containter text-center">
|
||||
<h4>{{ completed_teams_count }} out of {{ teams_count }} teams scored</h4>
|
||||
</div>
|
||||
<div class="container w-50">
|
||||
{% for team in teams %}
|
||||
<div class="border border-2 border-secondary rounded-3 mt-md-4 p-md-4">
|
||||
<h3>Team {{ team.number }}: {{ team.name }} <small class="text-secondary">Unscored</small></h3>
|
||||
<a href="{% url '/innovate/judges/feedback' %}?team={{ team.number | urlencode }}"><button class="btn btn-light w-100">Score Team {{ team.number }}</button></a>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
{% for team, score in completed_teams %}
|
||||
<div class="border border-2 border-secondary rounded-3 mt-md-4 p-md-2">
|
||||
<h3>Team {{ team.number }}: {{ team.name }} <small><span class="text-success">Scored</span> {{ score.get_total_score }}/150</small></h3>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -5,4 +5,6 @@ urlpatterns = [
|
|||
path('', views.home, name='innovate-home'),
|
||||
path('signup/', views.signup, name='competitor-signup'),
|
||||
path('signup/confirm/', views.confirm, name='competitor-signup-confirm'),
|
||||
path('judges/', views.portal, name='judges-portal'),
|
||||
path('judges/feedback/', views.feedback, name='judges-feedback')
|
||||
]
|
|
@ -1,13 +1,16 @@
|
|||
from django.shortcuts import render, redirect
|
||||
|
||||
from .forms import CompetitorFormset, CompetitorForm, TeamForm
|
||||
from .models import Competitor
|
||||
from .forms import CompetitorFormset, CompetitorForm, TeamForm, ScoreForm
|
||||
from .models import Competitor, Team, Judge, Score
|
||||
|
||||
from django.contrib.auth.decorators import login_required
|
||||
|
||||
from django.template.loader import render_to_string
|
||||
from django.utils.html import strip_tags
|
||||
from config.settings import EMAIL_HOST_USER
|
||||
from django.core.mail import EmailMultiAlternatives, send_mail
|
||||
|
||||
from config.settings import EMAIL_HOST_USER
|
||||
|
||||
# Create your views here.
|
||||
def home(request):
|
||||
return render(request, 'innovate/index.html')
|
||||
|
@ -66,3 +69,62 @@ def send_confirmation(request, team, members):
|
|||
|
||||
def confirm(request):
|
||||
return render(request, 'innovate/confirm.html')
|
||||
|
||||
@login_required
|
||||
def portal(request):
|
||||
judge = Judge.objects.get(user=request.user)
|
||||
team_numbers = [team.number for team in Team.objects.all() if ((team.number % 2 == 0 and judge.isEven) or (team.number % 2 != 0 and not judge.isEven))]
|
||||
teams, completed_teams = [], []
|
||||
for number in team_numbers:
|
||||
if Score.objects.filter(team=Team.objects.get(number=number), judge=judge).count() == 0:
|
||||
teams.append(Team.objects.get(number=number))
|
||||
else:
|
||||
completed_teams.append((Team.objects.get(number=number), Score.objects.get(team=Team.objects.get(number=number), judge=judge)))
|
||||
|
||||
context = {
|
||||
'teams': teams,
|
||||
'completed_teams': completed_teams,
|
||||
'completed_teams_count': len(completed_teams),
|
||||
'teams_count': len(team_numbers)
|
||||
}
|
||||
|
||||
return render(request, 'innovate/portal.html', context=context)
|
||||
|
||||
@login_required
|
||||
def feedback(request):
|
||||
form = ScoreForm()
|
||||
judge = Judge.objects.get(user=request.user)
|
||||
if type(judge) != Judge:
|
||||
print('no team or judge')
|
||||
return redirect('judges-portal')
|
||||
|
||||
if request.method == 'POST':
|
||||
form = ScoreForm(request.POST)
|
||||
if form.is_valid():
|
||||
team = Team.objects.get(number=request.POST['team_number'])
|
||||
if ((team.number % 2 != 0 and judge.isEven) or (team.number % 2 == 0 and not judge.isEven)):
|
||||
print('judge not approved')
|
||||
return redirect('judges-portal')
|
||||
score = form.save(commit=False)
|
||||
score.judge = judge
|
||||
score.team = team
|
||||
score.feedback = form.cleaned_data['feedback']
|
||||
score.save()
|
||||
return redirect('judges-portal')
|
||||
elif request.GET.get('team'):
|
||||
team = Team.objects.get(number=request.GET['team'])
|
||||
if team == None:
|
||||
print('no team or judge')
|
||||
elif ((team.number % 2 != 0 and judge.isEven) or (team.number % 2 == 0 and not judge.isEven)):
|
||||
print('judge not approved')
|
||||
elif Score.objects.filter(team=team, judge=judge).count() != 0:
|
||||
print('feedback already exists')
|
||||
else:
|
||||
context = {
|
||||
'team': team,
|
||||
'judge': judge,
|
||||
'form': form,
|
||||
}
|
||||
return render(request, 'innovate/feedback.html', context=context)
|
||||
return redirect('judges-portal')
|
||||
|
||||
|
|
|
@ -27,11 +27,14 @@
|
|||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarToggle">
|
||||
<div class="navbar-nav mr-auto">
|
||||
<div class="navbar-nav me-auto mr-auto">
|
||||
<a class="nav-item nav-link {% if request.resolver_match.url_name == 'landing' %}active{% endif %}" href="{% url '/' %}">Home</a>
|
||||
<a class="nav-item nav-link {% if request.resolver_match.url_name == 'innovate-home' %}active{% endif %}" href="{% url '/innovate' %}">InnovateTJ</a>
|
||||
<a class="nav-item nav-link {% if request.resolver_match.url_name == 'calendar' %}active{% endif %}" href="{% url '/calendar' %}">Calendar</a>
|
||||
<a class="nav-item nav-link {% if request.resolver_match.url_name == 'officers' %}active{% endif %}" href="{% url '/officers' %}">Officers</a>
|
||||
{% if user.is_authenticated %}
|
||||
<a class="nav-item nav-link {% if request.resolver_match.url_name == 'judges-portal' %}active{% endif %}" href="{% url '/innovate/judges' %}">Judging Portal</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
<!-- Navbar Right Side -->
|
||||
<div class="navbar-nav">
|
||||
|
@ -57,3 +60,5 @@
|
|||
{% block scripts %}{% endblock %}
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
|
|
3
users/static/users/login.css
Normal file
3
users/static/users/login.css
Normal file
|
@ -0,0 +1,3 @@
|
|||
form {
|
||||
color: white
|
||||
}
|
|
@ -1,4 +1,18 @@
|
|||
{% extends 'users/base.html' %}
|
||||
{% load crispy_forms_tags %}
|
||||
{% load launchx_extras %}
|
||||
{% block styles %}
|
||||
<link rel="stylesheet" type="text/css" href="{% static 'users/login.css' %}">
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<h1 style="color: white">Login page also in development</h1>
|
||||
{% endblock %}
|
||||
<div class="d-flex flex-column min-vh-100 justify-content-center align-items-center">
|
||||
<form method="POST">
|
||||
{% csrf_token %}
|
||||
<legend class="">Judges Log In</legend>
|
||||
{{ form|crispy }}
|
||||
<div class="form-group mt-3">
|
||||
<button class="btn btn-light btn-block" type="submit">Login</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock content %}
|
6
users/templates/users/logout.html
Normal file
6
users/templates/users/logout.html
Normal file
|
@ -0,0 +1,6 @@
|
|||
{% extends 'launchx/base.html' %}
|
||||
{% load launchx_extras %}
|
||||
{% block content %}
|
||||
<h1 style="color: white">You've logged out!</h1>
|
||||
<a href="{% url '/' %}"><button class="btn btn-light">Back</button></a>
|
||||
{% endblock %}
|
|
@ -1,5 +1,15 @@
|
|||
from django.shortcuts import render
|
||||
|
||||
from django.contrib.auth import login as auth_login
|
||||
from django.contrib.auth import logout as auth_logout
|
||||
|
||||
from django.contrib.auth.decorators import login_required
|
||||
|
||||
# Create your views here.
|
||||
def login(request):
|
||||
return render(request, 'users/login.html')
|
||||
return render(request, 'users/login.html')
|
||||
|
||||
@login_required
|
||||
def logout(request):
|
||||
auth_logout(request)
|
||||
return render(request, 'users/logout.html')
|
Loading…
Reference in New Issue
Block a user