Skip to content

Commit aadb687

Browse files
committedJun 18, 2022
👕 Lints with pre-commit for django-upgrade mostly
1 parent dd6c9f4 commit aadb687

33 files changed

+749
-404
lines changed
 

‎grants/admin.py

+13-8
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,30 @@
1+
from __future__ import annotations
2+
13
from django.contrib import admin
2-
from .models import Program, Applicant, Score, Answer
4+
5+
from .models import Answer, Applicant, Program, Score
6+
37

48
class AnswerInline(admin.StackedInline):
59
model = Answer
610
extra = 1
711

12+
813
admin.site.register(
914
Program,
10-
list_display = ["id", "name", "completed"],
11-
list_display_links = ["id", "name"],
12-
prepopulated_fields = {"slug": ("name", )}
15+
list_display=["id", "name", "completed"],
16+
list_display_links=["id", "name"],
17+
prepopulated_fields={"slug": ("name",)},
1318
)
1419

1520
admin.site.register(
1621
Applicant,
17-
list_display = ["id", "name", "email", "program", "applied"],
18-
list_display_links = ["id", "name"],
19-
inlines = [AnswerInline],
22+
list_display=["id", "name", "email", "program", "applied"],
23+
list_display_links=["id", "name"],
24+
inlines=[AnswerInline],
2025
)
2126

2227
admin.site.register(
2328
Score,
24-
list_display = ["id", "applicant", "user"],
29+
list_display=["id", "applicant", "user"],
2530
)

‎grants/forms.py

+18-11
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,22 @@
1+
from __future__ import annotations
2+
13
from django import forms
2-
from .models import Question, Score, Resource, Allocation
34

5+
from .models import Allocation, Question, Resource, Score
46

5-
class QuestionForm(forms.ModelForm):
67

8+
class QuestionForm(forms.ModelForm):
79
class Meta:
810
fields = ["question", "type", "required"]
911
model = Question
1012

1113
def clean_type(self):
1214
type = self.cleaned_data["type"]
13-
if self.instance and type != self.instance.type and self.instance.answers.exists():
15+
if (
16+
self.instance
17+
and type != self.instance.type
18+
and self.instance.answers.exists()
19+
):
1420
raise forms.ValidationError("Cannot change once this question has answers")
1521
return type
1622

@@ -22,12 +28,14 @@ class BaseApplyForm(forms.Form):
2228

2329
def __init__(self, program, *args, **kwargs):
2430
self.program = program
25-
super(BaseApplyForm, self).__init__(*args, **kwargs)
31+
super().__init__(*args, **kwargs)
2632

2733
def clean_email(self):
2834
email = self.cleaned_data["email"]
2935
if self.program.applicants.filter(email=email).exists():
30-
raise forms.ValidationError("An application with that email address has already been submitted.")
36+
raise forms.ValidationError(
37+
"An application with that email address has already been submitted."
38+
)
3139
return email
3240

3341

@@ -48,7 +56,7 @@ def clean(self):
4856
continue
4957
if name != name2 and value and value == value2:
5058
raise forms.ValidationError(
51-
"You cannot choose the same source (%s) for more than one question (%s and %s)." % (
59+
"You cannot choose the same source ({}) for more than one question ({} and {}).".format(
5260
value,
5361
name,
5462
name2,
@@ -57,32 +65,31 @@ def clean(self):
5765

5866

5967
class ScoreForm(forms.ModelForm):
60-
6168
class Meta:
6269
fields = ["score", "comment"]
6370
model = Score
6471

6572

6673
class ResourceForm(forms.ModelForm):
67-
6874
class Meta:
6975
fields = ["name", "type", "amount"]
7076
model = Resource
7177

7278

7379
class AllocationForm(forms.ModelForm):
74-
7580
class Meta:
7681
fields = ["resource", "amount"]
7782
model = Allocation
7883

7984
def __init__(self, applicant, *args, **kwargs):
8085
self.applicant = applicant
81-
super(AllocationForm, self).__init__(*args, **kwargs)
86+
super().__init__(*args, **kwargs)
8287
self.fields["resource"].queryset = self.applicant.program.resources
8388

8489
def clean_resource(self):
8590
resource = self.cleaned_data["resource"]
8691
if self.applicant.allocations.filter(resource=resource).exists():
87-
raise forms.ValidationError("That resource is already allocated. Delete it if you wish to change it.")
92+
raise forms.ValidationError(
93+
"That resource is already allocated. Delete it if you wish to change it."
94+
)
8895
return resource

‎grants/migrations/0001_initial.py

+141-59
Original file line numberDiff line numberDiff line change
@@ -1,106 +1,188 @@
1-
# -*- coding: utf-8 -*-
2-
from __future__ import unicode_literals
1+
from __future__ import annotations
32

4-
from django.db import models, migrations
3+
from django.db import migrations, models
54

65

76
class Migration(migrations.Migration):
87

9-
dependencies = [
10-
]
8+
dependencies = []
119

1210
operations = [
1311
migrations.CreateModel(
14-
name='Answer',
12+
name="Answer",
1513
fields=[
16-
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
17-
('answer', models.TextField()),
14+
(
15+
"id",
16+
models.AutoField(
17+
verbose_name="ID",
18+
serialize=False,
19+
auto_created=True,
20+
primary_key=True,
21+
),
22+
),
23+
("answer", models.TextField()),
1824
],
19-
options={
20-
},
25+
options={},
2126
bases=(models.Model,),
2227
),
2328
migrations.CreateModel(
24-
name='Applicant',
29+
name="Applicant",
2530
fields=[
26-
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
27-
('name', models.TextField()),
28-
('email', models.TextField()),
29-
('applied', models.DateTimeField(null=True, blank=True)),
31+
(
32+
"id",
33+
models.AutoField(
34+
verbose_name="ID",
35+
serialize=False,
36+
auto_created=True,
37+
primary_key=True,
38+
),
39+
),
40+
("name", models.TextField()),
41+
("email", models.TextField()),
42+
("applied", models.DateTimeField(null=True, blank=True)),
3043
],
31-
options={
32-
},
44+
options={},
3345
bases=(models.Model,),
3446
),
3547
migrations.AddField(
36-
model_name='answer',
37-
name='applicant',
38-
field=models.ForeignKey(to='grants.Applicant', to_field='id', on_delete=models.CASCADE),
48+
model_name="answer",
49+
name="applicant",
50+
field=models.ForeignKey(
51+
to="grants.Applicant", to_field="id", on_delete=models.CASCADE
52+
),
3953
preserve_default=True,
4054
),
4155
migrations.CreateModel(
42-
name='Program',
56+
name="Program",
4357
fields=[
44-
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
45-
('name', models.CharField(max_length=100)),
46-
('slug', models.SlugField(unique=True)),
47-
('applications_open', models.DateTimeField(null=True, blank=True)),
48-
('applications_close', models.DateTimeField(null=True, blank=True)),
49-
('grants_announced', models.DateTimeField(null=True, blank=True)),
50-
('program_starts', models.DateTimeField(null=True, blank=True)),
51-
('completed', models.BooleanField(default=False)),
58+
(
59+
"id",
60+
models.AutoField(
61+
verbose_name="ID",
62+
serialize=False,
63+
auto_created=True,
64+
primary_key=True,
65+
),
66+
),
67+
("name", models.CharField(max_length=100)),
68+
("slug", models.SlugField(unique=True)),
69+
("applications_open", models.DateTimeField(null=True, blank=True)),
70+
("applications_close", models.DateTimeField(null=True, blank=True)),
71+
("grants_announced", models.DateTimeField(null=True, blank=True)),
72+
("program_starts", models.DateTimeField(null=True, blank=True)),
73+
("completed", models.BooleanField(default=False)),
5274
],
53-
options={
54-
},
75+
options={},
5576
bases=(models.Model,),
5677
),
5778
migrations.AddField(
58-
model_name='applicant',
59-
name='program',
60-
field=models.ForeignKey(to='grants.Program', to_field='id', on_delete=models.CASCADE),
79+
model_name="applicant",
80+
name="program",
81+
field=models.ForeignKey(
82+
to="grants.Program", to_field="id", on_delete=models.CASCADE
83+
),
6184
preserve_default=True,
6285
),
6386
migrations.CreateModel(
64-
name='Question',
87+
name="Question",
6588
fields=[
66-
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
67-
('type', models.CharField(max_length=50, choices=[(b'boolean', b'Yes/No'), (b'text', b'Text'), (b'integer', b'Integer value')])),
68-
('question', models.TextField()),
69-
('order', models.IntegerField(default=0)),
70-
('program', models.ForeignKey(to='grants.Program', to_field='id', on_delete=models.CASCADE)),
89+
(
90+
"id",
91+
models.AutoField(
92+
verbose_name="ID",
93+
serialize=False,
94+
auto_created=True,
95+
primary_key=True,
96+
),
97+
),
98+
(
99+
"type",
100+
models.CharField(
101+
max_length=50,
102+
choices=[
103+
("boolean", "Yes/No"),
104+
("text", "Text"),
105+
("integer", "Integer value"),
106+
],
107+
),
108+
),
109+
("question", models.TextField()),
110+
("order", models.IntegerField(default=0)),
111+
(
112+
"program",
113+
models.ForeignKey(
114+
to="grants.Program", to_field="id", on_delete=models.CASCADE
115+
),
116+
),
71117
],
72-
options={
73-
},
118+
options={},
74119
bases=(models.Model,),
75120
),
76121
migrations.AddField(
77-
model_name='answer',
78-
name='question',
79-
field=models.ForeignKey(to='grants.Question', to_field='id', on_delete=models.CASCADE),
122+
model_name="answer",
123+
name="question",
124+
field=models.ForeignKey(
125+
to="grants.Question", to_field="id", on_delete=models.CASCADE
126+
),
80127
preserve_default=True,
81128
),
82129
migrations.CreateModel(
83-
name='Resource',
130+
name="Resource",
84131
fields=[
85-
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
86-
('name', models.CharField(max_length=100)),
87-
('type', models.CharField(max_length=50, choices=[(b'money', b'Money'), (b'ticket', b'Ticket'), (b'place', b'Place'), (b'accomodation', b'Accomodation')])),
88-
('program', models.ForeignKey(to='grants.Program', to_field='id', on_delete=models.CASCADE)),
132+
(
133+
"id",
134+
models.AutoField(
135+
verbose_name="ID",
136+
serialize=False,
137+
auto_created=True,
138+
primary_key=True,
139+
),
140+
),
141+
("name", models.CharField(max_length=100)),
142+
(
143+
"type",
144+
models.CharField(
145+
max_length=50,
146+
choices=[
147+
("money", "Money"),
148+
("ticket", "Ticket"),
149+
("place", "Place"),
150+
("accomodation", "Accomodation"),
151+
],
152+
),
153+
),
154+
(
155+
"program",
156+
models.ForeignKey(
157+
to="grants.Program", to_field="id", on_delete=models.CASCADE
158+
),
159+
),
89160
],
90-
options={
91-
},
161+
options={},
92162
bases=(models.Model,),
93163
),
94164
migrations.CreateModel(
95-
name='ResourceDonation',
165+
name="ResourceDonation",
96166
fields=[
97-
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
98-
('amount', models.IntegerField()),
99-
('source', models.TextField(null=True, blank=True)),
100-
('resource', models.ForeignKey(to='grants.Resource', to_field='id', on_delete=models.CASCADE)),
167+
(
168+
"id",
169+
models.AutoField(
170+
verbose_name="ID",
171+
serialize=False,
172+
auto_created=True,
173+
primary_key=True,
174+
),
175+
),
176+
("amount", models.IntegerField()),
177+
("source", models.TextField(null=True, blank=True)),
178+
(
179+
"resource",
180+
models.ForeignKey(
181+
to="grants.Resource", to_field="id", on_delete=models.CASCADE
182+
),
183+
),
101184
],
102-
options={
103-
},
185+
options={},
104186
bases=(models.Model,),
105187
),
106188
]

‎grants/migrations/0002_question_required.py

+5-6
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,18 @@
1-
# -*- coding: utf-8 -*-
2-
from __future__ import unicode_literals
1+
from __future__ import annotations
32

4-
from django.db import models, migrations
3+
from django.db import migrations, models
54

65

76
class Migration(migrations.Migration):
87

98
dependencies = [
10-
('grants', '0001_initial'),
9+
("grants", "0001_initial"),
1110
]
1211

1312
operations = [
1413
migrations.AddField(
15-
model_name='question',
16-
name='required',
14+
model_name="question",
15+
name="required",
1716
field=models.BooleanField(default=False),
1817
preserve_default=True,
1918
),
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,18 @@
1-
# -*- coding: utf-8 -*-
2-
from __future__ import unicode_literals
1+
from __future__ import annotations
32

4-
from django.db import models, migrations
3+
from django.db import migrations, models
54

65

76
class Migration(migrations.Migration):
87

98
dependencies = [
10-
('grants', '0002_question_required'),
9+
("grants", "0002_question_required"),
1110
]
1211

1312
operations = [
1413
migrations.AlterField(
15-
model_name='applicant',
16-
name='email',
14+
model_name="applicant",
15+
name="email",
1716
field=models.EmailField(max_length=75),
1817
),
1918
]
+16-9
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,30 @@
1-
# -*- coding: utf-8 -*-
2-
from __future__ import unicode_literals
1+
from __future__ import annotations
32

4-
from django.db import models, migrations
3+
from django.db import migrations, models
54

65

76
class Migration(migrations.Migration):
87

98
dependencies = [
10-
('grants', '0003_auto_20140623_0719'),
9+
("grants", "0003_auto_20140623_0719"),
1110
]
1211

1312
operations = [
1413
migrations.AlterField(
15-
model_name='question',
16-
name='type',
17-
field=models.CharField(max_length=50, choices=[(b'boolean', b'Yes/No'), (b'text', b'Short text'), (b'textarea', b'Long text'), (b'integer', b'Integer value')]),
14+
model_name="question",
15+
name="type",
16+
field=models.CharField(
17+
max_length=50,
18+
choices=[
19+
("boolean", "Yes/No"),
20+
("text", "Short text"),
21+
("textarea", "Long text"),
22+
("integer", "Integer value"),
23+
],
24+
),
1825
),
1926
migrations.AlterUniqueTogether(
20-
name='applicant',
21-
unique_together=set([(b'program', b'email')]),
27+
name="applicant",
28+
unique_together={("program", "email")},
2229
),
2330
]
+31-17
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,52 @@
1-
# -*- coding: utf-8 -*-
2-
from __future__ import unicode_literals
1+
from __future__ import annotations
32

4-
from django.db import models, migrations
53
from django.conf import settings
4+
from django.db import migrations, models
65

76

87
class Migration(migrations.Migration):
98

109
dependencies = [
1110
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
12-
('grants', '0004_auto_20140624_0435'),
11+
("grants", "0004_auto_20140624_0435"),
1312
]
1413

1514
operations = [
1615
migrations.CreateModel(
17-
name='Score',
16+
name="Score",
1817
fields=[
19-
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
20-
('score', models.FloatField(null=True, blank=True)),
21-
('comment', models.TextField(null=True, blank=True)),
22-
('score_history', models.TextField(null=True, blank=True)),
23-
('applicant', models.ForeignKey(to='grants.Applicant', on_delete=models.CASCADE)),
24-
('user', models.ForeignKey(to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)),
18+
(
19+
"id",
20+
models.AutoField(
21+
verbose_name="ID",
22+
serialize=False,
23+
auto_created=True,
24+
primary_key=True,
25+
),
26+
),
27+
("score", models.FloatField(null=True, blank=True)),
28+
("comment", models.TextField(null=True, blank=True)),
29+
("score_history", models.TextField(null=True, blank=True)),
30+
(
31+
"applicant",
32+
models.ForeignKey(to="grants.Applicant", on_delete=models.CASCADE),
33+
),
34+
(
35+
"user",
36+
models.ForeignKey(
37+
to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE
38+
),
39+
),
2540
],
26-
options={
27-
},
41+
options={},
2842
bases=(models.Model,),
2943
),
3044
migrations.AlterUniqueTogether(
31-
name='score',
32-
unique_together=set([(b'applicant', b'user')]),
45+
name="score",
46+
unique_together={("applicant", "user")},
3347
),
3448
migrations.AlterUniqueTogether(
35-
name='answer',
36-
unique_together=set([(b'applicant', b'question')]),
49+
name="answer",
50+
unique_together={("applicant", "question")},
3751
),
3852
]
+20-15
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,42 @@
1-
# -*- coding: utf-8 -*-
2-
from __future__ import unicode_literals
1+
from __future__ import annotations
32

4-
from django.db import models, migrations
3+
from django.db import migrations, models
54

65

76
class Migration(migrations.Migration):
87

98
dependencies = [
10-
('grants', '0005_auto_20140624_1719'),
9+
("grants", "0005_auto_20140624_1719"),
1110
]
1211

1312
operations = [
1413
migrations.RemoveField(
15-
model_name='resourcedonation',
16-
name='resource',
14+
model_name="resourcedonation",
15+
name="resource",
1716
),
1817
migrations.DeleteModel(
19-
name='ResourceDonation',
18+
name="ResourceDonation",
2019
),
2120
migrations.AddField(
22-
model_name='resource',
23-
name='amount',
21+
model_name="resource",
22+
name="amount",
2423
field=models.PositiveIntegerField(default=0),
2524
preserve_default=False,
2625
),
2726
migrations.AlterField(
28-
model_name='score',
29-
name='comment',
30-
field=models.TextField(help_text=b'Seen only by other voters, not by the applicant', null=True, blank=True),
27+
model_name="score",
28+
name="comment",
29+
field=models.TextField(
30+
help_text="Seen only by other voters, not by the applicant",
31+
null=True,
32+
blank=True,
33+
),
3134
),
3235
migrations.AlterField(
33-
model_name='score',
34-
name='score',
35-
field=models.FloatField(help_text=b'From 0 (terrible) to 5 (excellent)', null=True, blank=True),
36+
model_name="score",
37+
name="score",
38+
field=models.FloatField(
39+
help_text="From 0 (terrible) to 5 (excellent)", null=True, blank=True
40+
),
3641
),
3742
]
+25-13
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,42 @@
1-
# -*- coding: utf-8 -*-
2-
from __future__ import unicode_literals
1+
from __future__ import annotations
32

4-
from django.db import models, migrations
3+
from django.db import migrations, models
54

65

76
class Migration(migrations.Migration):
87

98
dependencies = [
10-
('grants', '0006_auto_20140625_0139'),
9+
("grants", "0006_auto_20140625_0139"),
1110
]
1211

1312
operations = [
1413
migrations.CreateModel(
15-
name='Allocation',
14+
name="Allocation",
1615
fields=[
17-
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
18-
('amount', models.PositiveIntegerField()),
19-
('applicant', models.ForeignKey(to='grants.Applicant', on_delete=models.CASCADE)),
20-
('resource', models.ForeignKey(to='grants.Resource', on_delete=models.CASCADE)),
16+
(
17+
"id",
18+
models.AutoField(
19+
verbose_name="ID",
20+
serialize=False,
21+
auto_created=True,
22+
primary_key=True,
23+
),
24+
),
25+
("amount", models.PositiveIntegerField()),
26+
(
27+
"applicant",
28+
models.ForeignKey(to="grants.Applicant", on_delete=models.CASCADE),
29+
),
30+
(
31+
"resource",
32+
models.ForeignKey(to="grants.Resource", on_delete=models.CASCADE),
33+
),
2134
],
22-
options={
23-
},
35+
options={},
2436
bases=(models.Model,),
2537
),
2638
migrations.AlterUniqueTogether(
27-
name='allocation',
28-
unique_together=set([(b'applicant', b'resource')]),
39+
name="allocation",
40+
unique_together={("applicant", "resource")},
2941
),
3042
]

‎grants/migrations/0008_program_users.py

+5-6
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,19 @@
1-
# -*- coding: utf-8 -*-
2-
from __future__ import unicode_literals
1+
from __future__ import annotations
32

4-
from django.db import models, migrations
53
from django.conf import settings
4+
from django.db import migrations, models
65

76

87
class Migration(migrations.Migration):
98

109
dependencies = [
11-
('grants', '0007_auto_20140625_0314'),
10+
("grants", "0007_auto_20140625_0314"),
1211
]
1312

1413
operations = [
1514
migrations.AddField(
16-
model_name='program',
17-
name='users',
15+
model_name="program",
16+
name="users",
1817
field=models.ManyToManyField(to=settings.AUTH_USER_MODEL, blank=True),
1918
preserve_default=True,
2019
),

‎grants/migrations/0009_uploadedcsv.py

+16-10
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,31 @@
1-
# -*- coding: utf-8 -*-
2-
from __future__ import unicode_literals
1+
from __future__ import annotations
32

4-
from django.db import models, migrations
3+
from django.db import migrations, models
54

65

76
class Migration(migrations.Migration):
87

98
dependencies = [
10-
('grants', '0008_program_users'),
9+
("grants", "0008_program_users"),
1110
]
1211

1312
operations = [
1413
migrations.CreateModel(
15-
name='UploadedCSV',
14+
name="UploadedCSV",
1615
fields=[
17-
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
18-
('csv', models.BinaryField(editable=False)),
19-
('uploaded', models.DateTimeField(auto_now_add=True)),
16+
(
17+
"id",
18+
models.AutoField(
19+
verbose_name="ID",
20+
serialize=False,
21+
auto_created=True,
22+
primary_key=True,
23+
),
24+
),
25+
("csv", models.BinaryField(editable=False)),
26+
("uploaded", models.DateTimeField(auto_now_add=True)),
2027
],
21-
options={
22-
},
28+
options={},
2329
bases=(models.Model,),
2430
),
2531
]
+61-36
Original file line numberDiff line numberDiff line change
@@ -1,81 +1,106 @@
1-
# -*- coding: utf-8 -*-
2-
from __future__ import unicode_literals
1+
from __future__ import annotations
32

4-
from django.db import models, migrations
53
from django.conf import settings
4+
from django.db import migrations, models
65

76

87
class Migration(migrations.Migration):
98

109
dependencies = [
11-
('grants', '0009_uploadedcsv'),
10+
("grants", "0009_uploadedcsv"),
1211
]
1312

1413
operations = [
1514
migrations.AddField(
16-
model_name='program',
17-
name='join_code',
15+
model_name="program",
16+
name="join_code",
1817
field=models.CharField(max_length=100, null=True, blank=True),
1918
preserve_default=True,
2019
),
2120
migrations.AlterField(
22-
model_name='allocation',
23-
name='applicant',
24-
field=models.ForeignKey(related_name='allocations', to='grants.Applicant', on_delete=models.CASCADE),
21+
model_name="allocation",
22+
name="applicant",
23+
field=models.ForeignKey(
24+
related_name="allocations",
25+
to="grants.Applicant",
26+
on_delete=models.CASCADE,
27+
),
2528
preserve_default=True,
2629
),
2730
migrations.AlterField(
28-
model_name='allocation',
29-
name='resource',
30-
field=models.ForeignKey(related_name='allocations', to='grants.Resource', on_delete=models.CASCADE),
31+
model_name="allocation",
32+
name="resource",
33+
field=models.ForeignKey(
34+
related_name="allocations",
35+
to="grants.Resource",
36+
on_delete=models.CASCADE,
37+
),
3138
preserve_default=True,
3239
),
3340
migrations.AlterField(
34-
model_name='answer',
35-
name='applicant',
36-
field=models.ForeignKey(related_name='answers', to='grants.Applicant', on_delete=models.CASCADE),
41+
model_name="answer",
42+
name="applicant",
43+
field=models.ForeignKey(
44+
related_name="answers", to="grants.Applicant", on_delete=models.CASCADE
45+
),
3746
preserve_default=True,
3847
),
3948
migrations.AlterField(
40-
model_name='answer',
41-
name='question',
42-
field=models.ForeignKey(related_name='answers', to='grants.Question', on_delete=models.CASCADE),
49+
model_name="answer",
50+
name="question",
51+
field=models.ForeignKey(
52+
related_name="answers", to="grants.Question", on_delete=models.CASCADE
53+
),
4354
preserve_default=True,
4455
),
4556
migrations.AlterField(
46-
model_name='applicant',
47-
name='program',
48-
field=models.ForeignKey(related_name='applicants', to='grants.Program', on_delete=models.CASCADE),
57+
model_name="applicant",
58+
name="program",
59+
field=models.ForeignKey(
60+
related_name="applicants", to="grants.Program", on_delete=models.CASCADE
61+
),
4962
preserve_default=True,
5063
),
5164
migrations.AlterField(
52-
model_name='question',
53-
name='program',
54-
field=models.ForeignKey(related_name='questions', to='grants.Program', on_delete=models.CASCADE),
65+
model_name="question",
66+
name="program",
67+
field=models.ForeignKey(
68+
related_name="questions", to="grants.Program", on_delete=models.CASCADE
69+
),
5570
preserve_default=True,
5671
),
5772
migrations.AlterField(
58-
model_name='resource',
59-
name='program',
60-
field=models.ForeignKey(related_name='resources', to='grants.Program', on_delete=models.CASCADE),
73+
model_name="resource",
74+
name="program",
75+
field=models.ForeignKey(
76+
related_name="resources", to="grants.Program", on_delete=models.CASCADE
77+
),
6178
preserve_default=True,
6279
),
6380
migrations.AlterField(
64-
model_name='score',
65-
name='applicant',
66-
field=models.ForeignKey(related_name='scores', to='grants.Applicant', on_delete=models.CASCADE),
81+
model_name="score",
82+
name="applicant",
83+
field=models.ForeignKey(
84+
related_name="scores", to="grants.Applicant", on_delete=models.CASCADE
85+
),
6786
preserve_default=True,
6887
),
6988
migrations.AlterField(
70-
model_name='score',
71-
name='score',
72-
field=models.FloatField(help_text=b'From 1 (terrible) to 5 (excellent)', null=True, blank=True),
89+
model_name="score",
90+
name="score",
91+
field=models.FloatField(
92+
help_text="From 1 (terrible) to 5 (excellent)", null=True, blank=True
93+
),
7394
preserve_default=True,
7495
),
7596
migrations.AlterField(
76-
model_name='score',
77-
name='user',
78-
field=models.ForeignKey(related_name='scores', to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE),
97+
model_name="score",
98+
name="user",
99+
field=models.ForeignKey(
100+
related_name="scores",
101+
to=settings.AUTH_USER_MODEL,
102+
on_delete=models.CASCADE,
103+
),
79104
preserve_default=True,
80105
),
81106
]
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,23 @@
1-
# -*- coding: utf-8 -*-
2-
from __future__ import unicode_literals
1+
from __future__ import annotations
32

43
from django.db import migrations, models
54

65

76
class Migration(migrations.Migration):
87

98
dependencies = [
10-
('grants', '0010_auto_20150320_1734'),
9+
("grants", "0010_auto_20150320_1734"),
1110
]
1211

1312
operations = [
1413
migrations.AddField(
15-
model_name='program',
16-
name='duplicate_emails',
14+
model_name="program",
15+
name="duplicate_emails",
1716
field=models.BooleanField(default=False),
1817
),
1918
migrations.AlterField(
20-
model_name='applicant',
21-
name='email',
19+
model_name="applicant",
20+
name="email",
2221
field=models.EmailField(max_length=254),
2322
),
2423
]
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,17 @@
1-
# -*- coding: utf-8 -*-
2-
from __future__ import unicode_literals
1+
from __future__ import annotations
32

4-
from django.db import migrations, models
3+
from django.db import migrations
54

65

76
class Migration(migrations.Migration):
87

98
dependencies = [
10-
('grants', '0011_auto_20160113_0505'),
9+
("grants", "0011_auto_20160113_0505"),
1110
]
1211

1312
operations = [
1413
migrations.AlterUniqueTogether(
15-
name='applicant',
16-
unique_together=set([]),
14+
name="applicant",
15+
unique_together=set(),
1716
),
1817
]

‎grants/models.py

+41-15
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from __future__ import annotations
2+
13
from django.db import models
24
from urlman import Urls
35

@@ -56,7 +58,9 @@ class Resource(models.Model):
5658
("accomodation", "Accomodation"),
5759
]
5860

59-
program = models.ForeignKey(Program, related_name="resources", on_delete=models.CASCADE)
61+
program = models.ForeignKey(
62+
Program, related_name="resources", on_delete=models.CASCADE
63+
)
6064
name = models.CharField(max_length=100)
6165
type = models.CharField(max_length=50, choices=TYPE_CHOICES)
6266
amount = models.PositiveIntegerField()
@@ -94,7 +98,9 @@ class Question(models.Model):
9498
("integer", "Integer value"),
9599
]
96100

97-
program = models.ForeignKey(Program, related_name="questions", on_delete=models.CASCADE)
101+
program = models.ForeignKey(
102+
Program, related_name="questions", on_delete=models.CASCADE
103+
)
98104
type = models.CharField(max_length=50, choices=TYPE_CHOICES)
99105
question = models.TextField()
100106
required = models.BooleanField(default=False)
@@ -115,7 +121,9 @@ class Applicant(models.Model):
115121
Someone applying for a grant.
116122
"""
117123

118-
program = models.ForeignKey(Program, related_name="applicants", on_delete=models.CASCADE)
124+
program = models.ForeignKey(
125+
Program, related_name="applicants", on_delete=models.CASCADE
126+
)
119127
name = models.TextField()
120128
email = models.EmailField()
121129

@@ -143,10 +151,10 @@ def variance(self):
143151
c = sum(data) / float(len(data))
144152
if n < 2:
145153
return 0
146-
ss = sum((x-c)**2 for x in data)
147-
ss -= sum((x-c) for x in data)**2/len(data)
148-
assert not ss < 0, 'negative sum of square deviations: %f' % ss
149-
return ss / (n-1)
154+
ss = sum((x - c) ** 2 for x in data)
155+
ss -= sum((x - c) for x in data) ** 2 / len(data)
156+
assert not ss < 0, "negative sum of square deviations: %f" % ss
157+
return ss / (n - 1)
150158

151159
def stdev(self):
152160
return self.variance() ** 0.5
@@ -157,8 +165,12 @@ class Allocation(models.Model):
157165
An allocation of some Resources to an Applicant.
158166
"""
159167

160-
applicant = models.ForeignKey(Applicant, related_name="allocations", on_delete=models.CASCADE)
161-
resource = models.ForeignKey(Resource, related_name="allocations", on_delete=models.CASCADE)
168+
applicant = models.ForeignKey(
169+
Applicant, related_name="allocations", on_delete=models.CASCADE
170+
)
171+
resource = models.ForeignKey(
172+
Resource, related_name="allocations", on_delete=models.CASCADE
173+
)
162174
amount = models.PositiveIntegerField()
163175

164176
class Meta:
@@ -172,8 +184,12 @@ class Answer(models.Model):
172184
An applicant's answer to a question.
173185
"""
174186

175-
applicant = models.ForeignKey(Applicant, related_name="answers", on_delete=models.CASCADE)
176-
question = models.ForeignKey(Question, related_name="answers", on_delete=models.CASCADE)
187+
applicant = models.ForeignKey(
188+
Applicant, related_name="answers", on_delete=models.CASCADE
189+
)
190+
question = models.ForeignKey(
191+
Question, related_name="answers", on_delete=models.CASCADE
192+
)
177193
answer = models.TextField()
178194

179195
class Meta:
@@ -187,10 +203,20 @@ class Score(models.Model):
187203
A score and optional comment on an applicant by a user.
188204
"""
189205

190-
applicant = models.ForeignKey(Applicant, related_name="scores", on_delete=models.CASCADE)
191-
user = models.ForeignKey("users.User", related_name="scores", on_delete=models.CASCADE)
192-
score = models.FloatField(blank=True, null=True, help_text="From 1 (terrible) to 5 (excellent)")
193-
comment = models.TextField(blank=True, null=True, help_text="Seen only by other voters, not by the applicant")
206+
applicant = models.ForeignKey(
207+
Applicant, related_name="scores", on_delete=models.CASCADE
208+
)
209+
user = models.ForeignKey(
210+
"users.User", related_name="scores", on_delete=models.CASCADE
211+
)
212+
score = models.FloatField(
213+
blank=True, null=True, help_text="From 1 (terrible) to 5 (excellent)"
214+
)
215+
comment = models.TextField(
216+
blank=True,
217+
null=True,
218+
help_text="Seen only by other voters, not by the applicant",
219+
)
194220
score_history = models.TextField(blank=True, null=True)
195221

196222
class Meta:

‎grants/tests.py

-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1 @@
1-
from django.test import TestCase
2-
31
# Create your tests here.

‎grants/views/bulk_load.py

+11-7
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
1+
from __future__ import annotations
2+
3+
import collections
14
import csv
25
import datetime
3-
import collections
6+
47
from django import forms
58
from django.db.transaction import atomic
69
from django.views.generic import TemplateView
10+
11+
from ..forms import BulkLoadMapBaseForm, BulkLoadUploadForm
12+
from ..models import Answer, Applicant, Score, UploadedCSV
713
from .program import ProgramMixin
8-
from ..forms import BulkLoadUploadForm, BulkLoadMapBaseForm
9-
from ..models import Applicant, Answer, UploadedCSV, Score
1014

1115

1216
class BulkLoader(ProgramMixin):
@@ -49,11 +53,11 @@ def post(self, request):
4953
# Save and import!
5054
errors = []
5155
successful = 0
52-
target_map = dict(
53-
(name, int(value))
56+
target_map = {
57+
name: int(value)
5458
for name, value in form.cleaned_data.items()
5559
if name != "csv_id" and value
56-
)
60+
}
5761
for i, row in enumerate(rows[1:]):
5862
try:
5963
with atomic():
@@ -136,7 +140,7 @@ def process_row(self, row, target_map):
136140
pass
137141
applicant.save()
138142
# Save answers
139-
for key, offset in target_map.items():
143+
for key, _offset in target_map.items():
140144
if key not in ["name", "email", "timestamp"]:
141145
raw_answer = row[target_map[key]]
142146
question = self.program.questions.get(pk=key.lstrip("q"))

‎grants/views/program.py

+82-53
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,30 @@
1+
from __future__ import annotations
2+
13
import collections
4+
25
from django import forms
3-
from django.utils import timezone
46
from django.http import Http404
5-
from django.shortcuts import render, redirect
6-
from django.views.generic import View, TemplateView, ListView, FormView, UpdateView
7-
from ..models import Program, Question, Answer, Applicant, Score, Resource
8-
from ..forms import QuestionForm, BaseApplyForm, ScoreForm, ResourceForm, AllocationForm
9-
7+
from django.shortcuts import redirect, render
8+
from django.utils import timezone
9+
from django.views.generic import FormView, ListView, TemplateView, UpdateView, View
1010

11-
def index(request):
12-
return render(request, "index.html", {
13-
"accessible_programs": Program.objects.filter(users__pk=request.user.pk).order_by("name"),
14-
})
11+
from ..forms import AllocationForm, BaseApplyForm, QuestionForm, ResourceForm, ScoreForm
12+
from ..models import Answer, Applicant, Program, Question, Resource, Score
1513

1614

17-
class ProgramMixin(object):
15+
def index(request):
16+
return render(
17+
request,
18+
"index.html",
19+
{
20+
"accessible_programs": Program.objects.filter(
21+
users__pk=request.user.pk
22+
).order_by("name"),
23+
},
24+
)
25+
26+
27+
class ProgramMixin:
1828
"""
1929
Generic view base class which does things in context of a Program.
2030
"""
@@ -28,12 +38,12 @@ def dispatch(self, *args, **kwargs):
2838
return redirect("login")
2939
if self.login_required and not self.program.user_allowed(self.request.user):
3040
raise Http404("Not logged in")
31-
return super(ProgramMixin, self).dispatch(*args, **kwargs)
41+
return super().dispatch(*args, **kwargs)
3242

3343
def render_to_response(self, context, **kwargs):
34-
context['program'] = self.program
35-
context['user_allowed_program'] = self.program.user_allowed(self.request.user)
36-
return super(ProgramMixin, self).render_to_response(context, **kwargs)
44+
context["program"] = self.program
45+
context["user_allowed_program"] = self.program.user_allowed(self.request.user)
46+
return super().render_to_response(context, **kwargs)
3747

3848

3949
class ProgramHome(ProgramMixin, TemplateView):
@@ -49,7 +59,9 @@ def get_context_data(self):
4959
user.num_votes = user.scores.filter(applicant__program=self.program).count()
5060
return {
5161
"num_applicants": self.program.applicants.count(),
52-
"num_scored": self.request.user.scores.filter(applicant__program=self.program).count(),
62+
"num_scored": self.request.user.scores.filter(
63+
applicant__program=self.program
64+
).count(),
5365
"users": users,
5466
}
5567

@@ -63,8 +75,8 @@ class ProgramQuestions(ProgramMixin, FormView):
6375
form_class = QuestionForm
6476

6577
def get_context_data(self, **kwargs):
66-
context = super(ProgramQuestions, self).get_context_data(**kwargs)
67-
context['questions'] = self.program.questions.order_by("order")
78+
context = super().get_context_data(**kwargs)
79+
context["questions"] = self.program.questions.order_by("order")
6880
return context
6981

7082
def form_valid(self, form):
@@ -104,8 +116,8 @@ class ProgramApply(ProgramMixin, FormView):
104116
template_name = "program-apply.html"
105117

106118
def get_form_kwargs(self):
107-
kwargs = super(ProgramApply, self).get_form_kwargs()
108-
kwargs['program'] = self.program
119+
kwargs = super().get_form_kwargs()
120+
kwargs["program"] = self.program
109121
return kwargs
110122

111123
def get_form_class(self):
@@ -117,23 +129,25 @@ def get_form_class(self):
117129
"text": forms.CharField,
118130
"textarea": forms.CharField,
119131
"integer": forms.IntegerField,
120-
}[question.type](required=question.required, widget=widget, label=question.question)
121-
return type("ApplicationForm", (BaseApplyForm, ), fields)
132+
}[question.type](
133+
required=question.required, widget=widget, label=question.question
134+
)
135+
return type("ApplicationForm", (BaseApplyForm,), fields)
122136

123137
def form_valid(self, form):
124138
applicant = Applicant.objects.create(
125-
program = self.program,
126-
name = form.cleaned_data["name"],
127-
email = form.cleaned_data["email"],
128-
applied = timezone.now(),
139+
program=self.program,
140+
name=form.cleaned_data["name"],
141+
email=form.cleaned_data["email"],
142+
applied=timezone.now(),
129143
)
130144
for question in self.program.questions.order_by("order"):
131145
value = form.cleaned_data.get("question-%s" % question.id, None) or None
132146
if value:
133147
Answer.objects.create(
134-
applicant = applicant,
135-
question = question,
136-
answer = value,
148+
applicant=applicant,
149+
question=question,
150+
answer=value,
137151
)
138152
return redirect(self.program.urls.apply_success)
139153

@@ -162,20 +176,24 @@ def get_queryset(self):
162176
else:
163177
self.sort = "applied"
164178
# Fetch applicants
165-
applicants = list(self.program.applicants.prefetch_related("scores").order_by("-applied"))
179+
applicants = list(
180+
self.program.applicants.prefetch_related("scores").order_by("-applied")
181+
)
166182
for applicant in applicants:
167-
applicant.has_scored = applicant.scores.filter(user=self.request.user).exists()
183+
applicant.has_scored = applicant.scores.filter(
184+
user=self.request.user
185+
).exists()
168186
if applicant.has_scored:
169187
applicant.average_score = applicant.average_score()
170188
else:
171-
applicant.average_score = -1
189+
applicant.average_score = -1
172190
if self.sort == "score":
173191
applicants.sort(key=lambda a: a.average_score, reverse=True)
174192
return applicants
175193

176194
def get_context_data(self):
177-
context = super(ProgramApplicants, self).get_context_data()
178-
context['sort'] = self.sort
195+
context = super().get_context_data()
196+
context["sort"] = self.sort
179197
return context
180198

181199

@@ -192,7 +210,9 @@ def get(self, request, applicant_id):
192210
for question in questions:
193211
question.answer = question.answers.filter(applicant=applicant).first()
194212
# See if we already scored this one
195-
score = Score.objects.filter(applicant=applicant, user=self.request.user).first()
213+
score = Score.objects.filter(
214+
applicant=applicant, user=self.request.user
215+
).first()
196216
old_score = score.score if score else None
197217
if score:
198218
all_scores = Score.objects.filter(applicant=applicant)
@@ -207,7 +227,11 @@ def get(self, request, applicant_id):
207227
new_score.user = self.request.user
208228
if old_score and new_score.score != old_score:
209229
new_score.score_history = ",".join(
210-
[x.strip() for x in (new_score.score_history or "").split(",") if x.strip()]
230+
[
231+
x.strip()
232+
for x in (new_score.score_history or "").split(",")
233+
if x.strip()
234+
]
211235
+ ["%.1f" % old_score]
212236
)
213237
new_score.save()
@@ -217,12 +241,14 @@ def get(self, request, applicant_id):
217241
return redirect(".")
218242
else:
219243
form = ScoreForm(instance=score)
220-
return self.render_to_response({
221-
"applicant": applicant,
222-
"questions": questions,
223-
"all_scores": all_scores,
224-
"form": form,
225-
})
244+
return self.render_to_response(
245+
{
246+
"applicant": applicant,
247+
"questions": questions,
248+
"all_scores": all_scores,
249+
"form": form,
250+
}
251+
)
226252

227253
post = get
228254

@@ -234,7 +260,11 @@ class RandomUnscoredApplicant(ProgramMixin, View):
234260
"""
235261

236262
def get(self, request):
237-
applicant = self.program.applicants.exclude(scores__user=self.request.user).order_by("?").first()
263+
applicant = (
264+
self.program.applicants.exclude(scores__user=self.request.user)
265+
.order_by("?")
266+
.first()
267+
)
238268
if applicant:
239269
return redirect(applicant.urls.view)
240270
else:
@@ -251,17 +281,17 @@ class ApplicantAllocations(ProgramMixin, FormView):
251281

252282
def dispatch(self, *args, **kwargs):
253283
self.applicant = Applicant.objects.get(pk=kwargs.pop("applicant_id"))
254-
return super(ApplicantAllocations, self).dispatch(*args, **kwargs)
284+
return super().dispatch(*args, **kwargs)
255285

256286
def get_form_kwargs(self):
257-
kwargs = super(ApplicantAllocations, self).get_form_kwargs()
258-
kwargs['applicant'] = self.applicant
287+
kwargs = super().get_form_kwargs()
288+
kwargs["applicant"] = self.applicant
259289
return kwargs
260290

261291
def get_context_data(self, **kwargs):
262-
context = super(ApplicantAllocations, self).get_context_data(**kwargs)
263-
context['applicant'] = self.applicant
264-
context['allocations'] = self.applicant.allocations.order_by("resource__name")
292+
context = super().get_context_data(**kwargs)
293+
context["applicant"] = self.applicant
294+
context["allocations"] = self.applicant.allocations.order_by("resource__name")
265295
return context
266296

267297
def form_valid(self, form):
@@ -275,8 +305,7 @@ def post(self, request, *args, **kwargs):
275305
if "delete" in request.POST:
276306
self.applicant.allocations.filter(pk=request.POST["delete_id"]).delete()
277307
return redirect(self.applicant.urls.allocations)
278-
return super(ApplicantAllocations, self).post(request, *args, **kwargs)
279-
308+
return super().post(request, *args, **kwargs)
280309

281310

282311
class ProgramResources(ProgramMixin, FormView):
@@ -288,8 +317,8 @@ class ProgramResources(ProgramMixin, FormView):
288317
template_name = "program-resources.html"
289318

290319
def get_context_data(self, **kwargs):
291-
context = super(ProgramResources, self).get_context_data(**kwargs)
292-
context['resources'] = self.program.resources.order_by("name")
320+
context = super().get_context_data(**kwargs)
321+
context["resources"] = self.program.resources.order_by("name")
293322
return context
294323

295324
def form_valid(self, form):

‎grorg/settings.py

+24-23
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,11 @@
88
https://docs.djangoproject.com/en/dev/ref/settings/
99
"""
1010

11+
from __future__ import annotations
12+
1113
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
1214
import os
15+
1316
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
1417

1518
SECRET_KEY = "secret"
@@ -42,14 +45,14 @@
4245
# Application definition
4346

4447
INSTALLED_APPS = (
45-
'django.contrib.admin',
46-
'django.contrib.auth',
47-
'django.contrib.contenttypes',
48-
'django.contrib.sessions',
49-
'django.contrib.messages',
50-
'django.contrib.staticfiles',
51-
'users',
52-
'grants',
48+
"django.contrib.admin",
49+
"django.contrib.auth",
50+
"django.contrib.contenttypes",
51+
"django.contrib.sessions",
52+
"django.contrib.messages",
53+
"django.contrib.staticfiles",
54+
"users",
55+
"grants",
5356
)
5457

5558
MIDDLEWARE = (
@@ -63,29 +66,29 @@
6366
"django.middleware.clickjacking.XFrameOptionsMiddleware",
6467
)
6568

66-
ROOT_URLCONF = 'grorg.urls'
69+
ROOT_URLCONF = "grorg.urls"
6770

68-
WSGI_APPLICATION = 'grorg.wsgi.application'
71+
WSGI_APPLICATION = "grorg.wsgi.application"
6972

70-
AUTH_USER_MODEL = 'users.User'
71-
LOGOUT_REDIRECT_URL = '/'
73+
AUTH_USER_MODEL = "users.User"
74+
LOGOUT_REDIRECT_URL = "/"
7275

7376
# Database
7477
# https://docs.djangoproject.com/en/dev/ref/settings/#databases
7578

7679
DATABASES = {
77-
'default': {
78-
'ENGINE': 'django.db.backends.sqlite3',
79-
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
80+
"default": {
81+
"ENGINE": "django.db.backends.sqlite3",
82+
"NAME": os.path.join(BASE_DIR, "db.sqlite3"),
8083
}
8184
}
8285

8386
# Internationalization
8487
# https://docs.djangoproject.com/en/dev/topics/i18n/
8588

86-
LANGUAGE_CODE = 'en-us'
89+
LANGUAGE_CODE = "en-us"
8790

88-
TIME_ZONE = 'UTC'
91+
TIME_ZONE = "UTC"
8992

9093
USE_I18N = True
9194

@@ -97,15 +100,13 @@
97100
# Static files (CSS, JavaScript, Images)
98101
# https://docs.djangoproject.com/en/dev/howto/static-files/
99102

100-
STATIC_URL = '/static/'
101-
STATIC_ROOT = 'staticfiles'
103+
STATIC_URL = "/static/"
104+
STATIC_ROOT = "staticfiles"
102105

103-
STATICFILES_DIRS = (
104-
os.path.join(BASE_DIR, "static"),
105-
)
106+
STATICFILES_DIRS = (os.path.join(BASE_DIR, "static"),)
106107

107108
# Import local settings if present
108109
try:
109-
from local_settings import *
110+
from local_settings import * # noqa
110111
except ImportError:
111112
pass

‎grorg/urls.py

+43-19
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,51 @@
1+
from __future__ import annotations
2+
13
from django.contrib import admin, auth
24
from django.urls import path, re_path
3-
from grants.views import program, bulk_load
5+
6+
from grants.views import bulk_load, program
47
from users import views as users
58

69
urlpatterns = [
7-
path('', program.index),
10+
path("", program.index),
811
path("login/", auth.views.LoginView.as_view(template_name="login.html")),
912
path("logout/", auth.views.LogoutView.as_view()),
10-
path('register/', users.register),
11-
path('join/', users.join),
12-
path('admin/', admin.site.urls),
13-
re_path(r'^(?P<program>[^/]+)/$', program.ProgramHome.as_view()),
14-
re_path(r'^(?P<program>[^/]+)/questions/$', program.ProgramQuestions.as_view()),
15-
re_path(r'^(?P<program>[^/]+)/questions/(?P<question_id>[^/]+)/$', program.ProgramQuestionEdit.as_view()),
16-
re_path(r'^(?P<program>[^/]+)/apply/$', program.ProgramApply.as_view()),
17-
re_path(r'^(?P<program>[^/]+)/apply/success/$', program.ProgramApplySuccess.as_view()),
18-
re_path(r'^(?P<program>[^/]+)/applicants/$', program.ProgramApplicants.as_view()),
19-
re_path(r'^(?P<program>[^/]+)/applicants/bulk/$', bulk_load.BulkLoadApplicants.as_view()),
20-
re_path(r'^(?P<program>[^/]+)/applicants/bulk_scores/$', bulk_load.BulkLoadScores.as_view()),
21-
re_path(r'^(?P<program>[^/]+)/applicants/random-unscored/$', program.RandomUnscoredApplicant.as_view()),
22-
re_path(r'^(?P<program>[^/]+)/applicants/(?P<applicant_id>[^/]+)/$', program.ProgramApplicantView.as_view()),
23-
re_path(r'^(?P<program>[^/]+)/applicants/(?P<applicant_id>[^/]+)/allocations/$', program.ApplicantAllocations.as_view()),
24-
re_path(r'^(?P<program>[^/]+)/resources/$', program.ProgramResources.as_view()),
25-
re_path(r'^(?P<program>[^/]+)/resources/(?P<resource_id>[^/]+)/$', program.ProgramResourceEdit.as_view()),
26-
13+
path("register/", users.register),
14+
path("join/", users.join),
15+
path("admin/", admin.site.urls),
16+
re_path(r"^(?P<program>[^/]+)/$", program.ProgramHome.as_view()),
17+
re_path(r"^(?P<program>[^/]+)/questions/$", program.ProgramQuestions.as_view()),
18+
re_path(
19+
r"^(?P<program>[^/]+)/questions/(?P<question_id>[^/]+)/$",
20+
program.ProgramQuestionEdit.as_view(),
21+
),
22+
re_path(r"^(?P<program>[^/]+)/apply/$", program.ProgramApply.as_view()),
23+
re_path(
24+
r"^(?P<program>[^/]+)/apply/success/$", program.ProgramApplySuccess.as_view()
25+
),
26+
re_path(r"^(?P<program>[^/]+)/applicants/$", program.ProgramApplicants.as_view()),
27+
re_path(
28+
r"^(?P<program>[^/]+)/applicants/bulk/$", bulk_load.BulkLoadApplicants.as_view()
29+
),
30+
re_path(
31+
r"^(?P<program>[^/]+)/applicants/bulk_scores/$",
32+
bulk_load.BulkLoadScores.as_view(),
33+
),
34+
re_path(
35+
r"^(?P<program>[^/]+)/applicants/random-unscored/$",
36+
program.RandomUnscoredApplicant.as_view(),
37+
),
38+
re_path(
39+
r"^(?P<program>[^/]+)/applicants/(?P<applicant_id>[^/]+)/$",
40+
program.ProgramApplicantView.as_view(),
41+
),
42+
re_path(
43+
r"^(?P<program>[^/]+)/applicants/(?P<applicant_id>[^/]+)/allocations/$",
44+
program.ApplicantAllocations.as_view(),
45+
),
46+
re_path(r"^(?P<program>[^/]+)/resources/$", program.ProgramResources.as_view()),
47+
re_path(
48+
r"^(?P<program>[^/]+)/resources/(?P<resource_id>[^/]+)/$",
49+
program.ProgramResourceEdit.as_view(),
50+
),
2751
]

‎grorg/wsgi.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@
77
https://docs.djangoproject.com/en/dev/howto/deployment/wsgi/
88
"""
99

10+
from __future__ import annotations
11+
1012
import os
13+
1114
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "grorg.settings")
1215
try:
1316
from whitenoise.django import DjangoWhiteNoise
@@ -16,7 +19,8 @@
1619
else:
1720
USE_WHITENOISE = True
1821

19-
from django.core.wsgi import get_wsgi_application
22+
from django.core.wsgi import get_wsgi_application # noqa
23+
2024
application = get_wsgi_application()
2125

2226
if USE_WHITENOISE:

‎manage.py

+2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
#!/usr/bin/env python
2+
from __future__ import annotations
3+
24
import os
35
import sys
46

‎static/css/reset.css

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* http://meyerweb.com/eric/tools/css/reset/
1+
/* http://meyerweb.com/eric/tools/css/reset/
22
v2.0 | 20110126
33
License: none (public domain)
44
*/
@@ -12,8 +12,8 @@ b, u, i, center,
1212
dl, dt, dd, ol, ul, li,
1313
fieldset, form, label, legend,
1414
table, caption, tbody, tfoot, thead, tr, th, td,
15-
article, aside, canvas, details, embed,
16-
figure, figcaption, footer, header, hgroup,
15+
article, aside, canvas, details, embed,
16+
figure, figcaption, footer, header, hgroup,
1717
menu, nav, output, ruby, section, summary,
1818
time, mark, audio, video {
1919
margin: 0;
@@ -24,7 +24,7 @@ time, mark, audio, video {
2424
vertical-align: baseline;
2525
}
2626
/* HTML5 display-role reset for older browsers */
27-
article, aside, details, figcaption, figure,
27+
article, aside, details, figcaption, figure,
2828
footer, header, hgroup, menu, nav, section {
2929
display: block;
3030
}

‎static/fonts/alte_din/din1451alt_g-webfont.svg

+1-1
Loading

‎static/js/jquery-1.9.1.min.js

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎users/admin.py

+5-3
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1+
from __future__ import annotations
2+
13
from django.contrib import admin
2-
from .models import User
34

5+
from .models import User
46

57
admin.site.register(
68
User,
7-
list_display = ["id", "name", "email", "is_staff", "is_active"],
8-
list_display_links = ["id", "email"],
9+
list_display=["id", "name", "email", "is_staff", "is_active"],
10+
list_display_links=["id", "email"],
911
)

‎users/forms.py

+12-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
1+
from __future__ import annotations
2+
13
from django import forms
4+
25
from grants.models import Program
6+
37
from .models import User
48

59

@@ -11,12 +15,14 @@ class RegisterForm(forms.Form):
1115
program_code = forms.CharField(required=True)
1216

1317
def clean_email(self):
14-
if User.objects.filter(email=self.cleaned_data['email']).exists():
18+
if User.objects.filter(email=self.cleaned_data["email"]).exists():
1519
raise forms.ValidationError("User with this email already exists")
16-
return self.cleaned_data['email']
20+
return self.cleaned_data["email"]
1721

1822
def clean_program_code(self):
19-
program = Program.objects.filter(join_code=self.cleaned_data['program_code']).first()
23+
program = Program.objects.filter(
24+
join_code=self.cleaned_data["program_code"]
25+
).first()
2026
if not program:
2127
raise forms.ValidationError("Invalid code")
2228
return program
@@ -27,7 +33,9 @@ class JoinForm(forms.Form):
2733
program_code = forms.CharField(required=True)
2834

2935
def clean_program_code(self):
30-
program = Program.objects.filter(join_code=self.cleaned_data['program_code']).first()
36+
program = Program.objects.filter(
37+
join_code=self.cleaned_data["program_code"]
38+
).first()
3139
if not program:
3240
raise forms.ValidationError("Invalid code")
3341
return program

‎users/migrations/0001_initial.py

+67-17
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,84 @@
1-
# -*- coding: utf-8 -*-
2-
from __future__ import unicode_literals
1+
from __future__ import annotations
32

4-
from django.db import models, migrations
53
import django.utils.timezone
4+
from django.db import migrations, models
65

76

87
class Migration(migrations.Migration):
98

109
dependencies = [
11-
('auth', '__first__'),
10+
("auth", "__first__"),
1211
]
1312

1413
operations = [
1514
migrations.CreateModel(
16-
name='User',
15+
name="User",
1716
fields=[
18-
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
19-
('password', models.CharField(max_length=128, verbose_name='password')),
20-
('last_login', models.DateTimeField(default=django.utils.timezone.now, verbose_name='last login')),
21-
('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
22-
('name', models.CharField(max_length=255, verbose_name=b'Name')),
23-
('email', models.EmailField(unique=True, max_length=75, verbose_name=b'Email Address', blank=True)),
24-
('is_staff', models.BooleanField(default=False, verbose_name=b'Staff status')),
25-
('is_active', models.BooleanField(default=True, verbose_name=b'Active')),
26-
('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name=b'Date joined')),
27-
('groups', models.ManyToManyField(to='auth.Group', verbose_name='groups', blank=True)),
28-
('user_permissions', models.ManyToManyField(to='auth.Permission', verbose_name='user permissions', blank=True)),
17+
(
18+
"id",
19+
models.AutoField(
20+
verbose_name="ID",
21+
serialize=False,
22+
auto_created=True,
23+
primary_key=True,
24+
),
25+
),
26+
("password", models.CharField(max_length=128, verbose_name="password")),
27+
(
28+
"last_login",
29+
models.DateTimeField(
30+
default=django.utils.timezone.now, verbose_name="last login"
31+
),
32+
),
33+
(
34+
"is_superuser",
35+
models.BooleanField(
36+
default=False,
37+
help_text="Designates that this user has all permissions without explicitly assigning them.",
38+
verbose_name="superuser status",
39+
),
40+
),
41+
("name", models.CharField(max_length=255, verbose_name="Name")),
42+
(
43+
"email",
44+
models.EmailField(
45+
unique=True,
46+
max_length=75,
47+
verbose_name="Email Address",
48+
blank=True,
49+
),
50+
),
51+
(
52+
"is_staff",
53+
models.BooleanField(default=False, verbose_name="Staff status"),
54+
),
55+
(
56+
"is_active",
57+
models.BooleanField(default=True, verbose_name="Active"),
58+
),
59+
(
60+
"date_joined",
61+
models.DateTimeField(
62+
default=django.utils.timezone.now, verbose_name="Date joined"
63+
),
64+
),
65+
(
66+
"groups",
67+
models.ManyToManyField(
68+
to="auth.Group", verbose_name="groups", blank=True
69+
),
70+
),
71+
(
72+
"user_permissions",
73+
models.ManyToManyField(
74+
to="auth.Permission",
75+
verbose_name="user permissions",
76+
blank=True,
77+
),
78+
),
2979
],
3080
options={
31-
'abstract': False,
81+
"abstract": False,
3282
},
3383
bases=(models.Model,),
3484
),
+23-10
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,39 @@
1-
# -*- coding: utf-8 -*-
2-
from __future__ import unicode_literals
1+
from __future__ import annotations
32

4-
from django.db import models, migrations
3+
from django.db import migrations, models
54

65

76
class Migration(migrations.Migration):
87

98
dependencies = [
10-
('users', '0001_initial'),
9+
("users", "0001_initial"),
1110
]
1211

1312
operations = [
1413
migrations.AlterField(
15-
model_name='user',
16-
name='groups',
17-
field=models.ManyToManyField(related_query_name='user', related_name='user_set', to='auth.Group', blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of his/her group.', verbose_name='groups'),
14+
model_name="user",
15+
name="groups",
16+
field=models.ManyToManyField(
17+
related_query_name="user",
18+
related_name="user_set",
19+
to="auth.Group",
20+
blank=True,
21+
help_text="The groups this user belongs to. A user will get all permissions granted to each of his/her group.",
22+
verbose_name="groups",
23+
),
1824
preserve_default=True,
1925
),
2026
migrations.AlterField(
21-
model_name='user',
22-
name='user_permissions',
23-
field=models.ManyToManyField(related_query_name='user', related_name='user_set', to='auth.Permission', blank=True, help_text='Specific permissions for this user.', verbose_name='user permissions'),
27+
model_name="user",
28+
name="user_permissions",
29+
field=models.ManyToManyField(
30+
related_query_name="user",
31+
related_name="user_set",
32+
to="auth.Permission",
33+
blank=True,
34+
help_text="Specific permissions for this user.",
35+
verbose_name="user permissions",
36+
),
2437
preserve_default=True,
2538
),
2639
]
+22-12
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,39 @@
1-
# -*- coding: utf-8 -*-
2-
from __future__ import unicode_literals
1+
from __future__ import annotations
32

43
from django.db import migrations, models
54

65

76
class Migration(migrations.Migration):
87

98
dependencies = [
10-
('users', '0002_auto_20150320_1734'),
9+
("users", "0002_auto_20150320_1734"),
1110
]
1211

1312
operations = [
1413
migrations.AlterField(
15-
model_name='user',
16-
name='email',
17-
field=models.EmailField(unique=True, max_length=254, verbose_name=b'Email Address', blank=True),
14+
model_name="user",
15+
name="email",
16+
field=models.EmailField(
17+
unique=True, max_length=254, verbose_name="Email Address", blank=True
18+
),
1819
),
1920
migrations.AlterField(
20-
model_name='user',
21-
name='groups',
22-
field=models.ManyToManyField(related_query_name='user', related_name='user_set', to='auth.Group', blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', verbose_name='groups'),
21+
model_name="user",
22+
name="groups",
23+
field=models.ManyToManyField(
24+
related_query_name="user",
25+
related_name="user_set",
26+
to="auth.Group",
27+
blank=True,
28+
help_text="The groups this user belongs to. A user will get all permissions granted to each of their groups.",
29+
verbose_name="groups",
30+
),
2331
),
2432
migrations.AlterField(
25-
model_name='user',
26-
name='last_login',
27-
field=models.DateTimeField(null=True, verbose_name='last login', blank=True),
33+
model_name="user",
34+
name="last_login",
35+
field=models.DateTimeField(
36+
null=True, verbose_name="last login", blank=True
37+
),
2838
),
2939
]

‎users/models.py

+18-5
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,31 @@
1-
from django.db import models
2-
from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin, BaseUserManager
1+
from __future__ import annotations
2+
3+
from django.contrib.auth.models import (
4+
AbstractBaseUser,
5+
BaseUserManager,
6+
PermissionsMixin,
7+
)
38
from django.core.mail import send_mail
9+
from django.db import models
410
from django.utils import timezone
511

612

713
class UserManager(BaseUserManager):
8-
914
def _create_user(self, email, password, is_staff, is_superuser, **extra_fields):
1015
"""
1116
Creates and saves a User with the given username, email and password.
1217
"""
1318
now = timezone.now()
1419
email = self.normalize_email(email)
15-
user = self.model(email=email, is_staff=is_staff, is_active=True, is_superuser=is_superuser, last_login=now, date_joined=now, **extra_fields)
20+
user = self.model(
21+
email=email,
22+
is_staff=is_staff,
23+
is_active=True,
24+
is_superuser=is_superuser,
25+
last_login=now,
26+
date_joined=now,
27+
**extra_fields,
28+
)
1629
user.set_password(password)
1730
user.save(using=self._db)
1831
return user
@@ -33,7 +46,7 @@ class User(AbstractBaseUser, PermissionsMixin):
3346

3447
objects = UserManager()
3548

36-
USERNAME_FIELD = 'email'
49+
USERNAME_FIELD = "email"
3750
REQUIRED_FIELDS = []
3851

3952
def get_full_name(self):

‎users/tests.py

-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1 @@
1-
from django.test import TestCase
2-
31
# Create your tests here.

‎users/views.py

+21-16
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,62 @@
1-
from django.contrib.auth import views as auth_views, authenticate, login as auth_login, re
1+
from __future__ import annotations
2+
3+
from django.contrib.auth import authenticate
4+
from django.contrib.auth import login as auth_login
5+
from django.contrib.auth import views as auth_views
26
from django.contrib.auth.decorators import login_required
3-
from django.shortcuts import render, redirect
4-
from .forms import RegisterForm, JoinForm
7+
from django.shortcuts import redirect, render
8+
9+
from .forms import JoinForm, RegisterForm
510
from .models import User
611

12+
713
def login(request):
814
return auth_views.login(request, template_name="login.html")
915

16+
1017
def logout(request):
1118
return auth_views.logout(request, next_page="/")
1219

20+
1321
def register(request):
1422
if request.method == "POST":
1523
form = RegisterForm(request.POST)
1624
if form.is_valid():
1725
# Make user
1826
user = User(
19-
email = form.cleaned_data['email'],
20-
name = form.cleaned_data['name'],
27+
email=form.cleaned_data["email"],
28+
name=form.cleaned_data["name"],
2129
)
22-
user.set_password(form.cleaned_data['password'])
30+
user.set_password(form.cleaned_data["password"])
2331
user.save()
2432
# Assign them to a program
25-
program = form.cleaned_data['program_code']
33+
program = form.cleaned_data["program_code"]
2634
program.users.add(user)
2735
program.save()
2836
# Log them in
2937
auth_login(
3038
request,
3139
authenticate(
32-
email = form.cleaned_data['email'],
33-
password = form.cleaned_data['password'],
40+
email=form.cleaned_data["email"],
41+
password=form.cleaned_data["password"],
3442
),
3543
)
3644
return redirect("/")
3745
else:
3846
form = RegisterForm()
39-
return render(request, "register.html", {
40-
"form": form
41-
})
47+
return render(request, "register.html", {"form": form})
48+
4249

4350
@login_required
4451
def join(request):
4552
if request.method == "POST":
4653
form = JoinForm(request.POST)
4754
if form.is_valid():
4855
# Assign them to the program
49-
program = form.cleaned_data['program_code']
56+
program = form.cleaned_data["program_code"]
5057
program.users.add(request.user)
5158
program.save()
5259
return redirect("/")
5360
else:
5461
form = JoinForm()
55-
return render(request, "join.html", {
56-
"form": form
57-
})
62+
return render(request, "join.html", {"form": form})

0 commit comments

Comments
 (0)
Please sign in to comment.