Skip to content

Commit 3dfed5c

Browse files
Allow participants to self-assess and share their competencies (#271)
* Adding competency app for participants to self-assess * Finishing assessment view * Adding team view and link from team page * Only retrieve assessement rating if visible * change label * Fixing some linting * Changing header * Fixing linting * Removing bootstrap icons, adding permissions to new views and fixing bug with no competencies
1 parent 156d8f4 commit 3dfed5c

29 files changed

Lines changed: 1361 additions & 16 deletions

competencies/__init__.py

Whitespace-only changes.

competencies/admin.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
from django.contrib import admin
2+
3+
from competencies.models import (
4+
Competency, CompetencyDifficulty,
5+
CompetencyAssessment, CompetencyAssessmentRating)
6+
7+
admin.site.register(CompetencyDifficulty)
8+
admin.site.register(Competency)
9+
admin.site.register(CompetencyAssessment)
10+
admin.site.register(CompetencyAssessmentRating)

competencies/apps.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
from django.apps import AppConfig
2+
3+
4+
class CompetenciesConfig(AppConfig):
5+
name = 'competencies'
Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
[
2+
{
3+
"model": "competencies.competencydifficulty",
4+
"pk": 1,
5+
"fields": {
6+
"created": "2022-08-05T11:37:54.601Z",
7+
"updated": "2022-08-05T11:37:54.601Z",
8+
"created_by": 1,
9+
"display_name": "Easy",
10+
"numeric_difficulty": 1.0
11+
}
12+
},
13+
{
14+
"model": "competencies.competencydifficulty",
15+
"pk": 2,
16+
"fields": {
17+
"created": "2022-08-05T11:40:28.237Z",
18+
"updated": "2022-08-05T11:40:28.237Z",
19+
"created_by": 1,
20+
"display_name": "Intermediate",
21+
"numeric_difficulty": 2.0
22+
}
23+
},
24+
{
25+
"model": "competencies.competencydifficulty",
26+
"pk": 3,
27+
"fields": {
28+
"created": "2022-08-05T11:43:34.855Z",
29+
"updated": "2022-08-05T11:43:34.855Z",
30+
"created_by": 1,
31+
"display_name": "Hard",
32+
"numeric_difficulty": 4.0
33+
}
34+
},
35+
{
36+
"model": "competencies.competencydifficulty",
37+
"pk": 4,
38+
"fields": {
39+
"created": "2022-08-05T13:57:47.318Z",
40+
"updated": "2022-08-05T13:57:47.318Z",
41+
"created_by": 1,
42+
"display_name": "Very Hard",
43+
"numeric_difficulty": 5.0
44+
}
45+
},
46+
{
47+
"model": "competencies.competencydifficulty",
48+
"pk": 5,
49+
"fields": {
50+
"created": "2022-08-05T13:59:26.450Z",
51+
"updated": "2022-08-05T13:59:26.451Z",
52+
"created_by": 1,
53+
"display_name": "Technical",
54+
"numeric_difficulty": 1.2
55+
}
56+
},
57+
{
58+
"model": "competencies.competencydifficulty",
59+
"pk": 6,
60+
"fields": {
61+
"created": "2022-08-05T13:59:45.617Z",
62+
"updated": "2022-08-05T13:59:45.617Z",
63+
"created_by": 1,
64+
"display_name": "Normal",
65+
"numeric_difficulty": 2.5
66+
}
67+
},
68+
{
69+
"model": "competencies.competencydifficulty",
70+
"pk": 7,
71+
"fields": {
72+
"created": "2022-08-05T14:02:05.685Z",
73+
"updated": "2022-08-05T14:02:05.685Z",
74+
"created_by": 1,
75+
"display_name": "Noob",
76+
"numeric_difficulty": 0.5
77+
}
78+
},
79+
{
80+
"model": "competencies.competencydifficulty",
81+
"pk": 8,
82+
"fields": {
83+
"created": "2022-08-05T14:02:32.024Z",
84+
"updated": "2022-08-05T14:02:32.024Z",
85+
"created_by": 1,
86+
"display_name": "Noobs",
87+
"numeric_difficulty": 0.6
88+
}
89+
},
90+
{
91+
"model": "competencies.competencydifficulty",
92+
"pk": 9,
93+
"fields": {
94+
"created": "2022-08-05T14:02:56.550Z",
95+
"updated": "2022-08-05T14:02:56.550Z",
96+
"created_by": 1,
97+
"display_name": "Nooobs!",
98+
"numeric_difficulty": 0.7
99+
}
100+
},
101+
{
102+
"model": "competencies.competency",
103+
"pk": 1,
104+
"fields": {
105+
"created": "2022-08-05T14:17:38.220Z",
106+
"updated": "2022-08-05T14:17:38.220Z",
107+
"created_by": 1,
108+
"display_name": "HTML",
109+
"perceived_difficulty": 1,
110+
"is_visible": true
111+
}
112+
},
113+
{
114+
"model": "competencies.competency",
115+
"pk": 2,
116+
"fields": {
117+
"created": "2022-08-05T14:23:41.673Z",
118+
"updated": "2022-08-05T14:56:13.462Z",
119+
"created_by": 1,
120+
"display_name": "CSS",
121+
"perceived_difficulty": 1,
122+
"is_visible": true
123+
}
124+
},
125+
{
126+
"model": "competencies.competency",
127+
"pk": 3,
128+
"fields": {
129+
"created": "2022-08-05T14:23:49.767Z",
130+
"updated": "2022-08-05T14:23:49.767Z",
131+
"created_by": 1,
132+
"display_name": "JavaScript",
133+
"perceived_difficulty": 2,
134+
"is_visible": true
135+
}
136+
},
137+
{
138+
"model": "competencies.competency",
139+
"pk": 4,
140+
"fields": {
141+
"created": "2022-08-05T14:24:11.521Z",
142+
"updated": "2022-08-05T14:24:11.521Z",
143+
"created_by": 1,
144+
"display_name": "Python",
145+
"perceived_difficulty": 3,
146+
"is_visible": true
147+
}
148+
},
149+
{
150+
"model": "competencies.competency",
151+
"pk": 5,
152+
"fields": {
153+
"created": "2022-08-05T14:24:17.102Z",
154+
"updated": "2022-08-05T14:24:17.102Z",
155+
"created_by": 1,
156+
"display_name": "Django",
157+
"perceived_difficulty": 4,
158+
"is_visible": true
159+
}
160+
},
161+
{
162+
"model": "competencies.competency",
163+
"pk": 6,
164+
"fields": {
165+
"created": "2022-08-05T14:24:23.195Z",
166+
"updated": "2022-08-05T14:24:23.195Z",
167+
"created_by": 1,
168+
"display_name": "Kanban",
169+
"perceived_difficulty": 1,
170+
"is_visible": true
171+
}
172+
},
173+
{
174+
"model": "competencies.competency",
175+
"pk": 7,
176+
"fields": {
177+
"created": "2022-08-05T14:24:29.380Z",
178+
"updated": "2022-08-05T14:24:29.380Z",
179+
"created_by": 1,
180+
"display_name": "Readme",
181+
"perceived_difficulty": 1,
182+
"is_visible": true
183+
}
184+
},
185+
{
186+
"model": "competencies.competency",
187+
"pk": 8,
188+
"fields": {
189+
"created": "2022-08-05T14:24:35.502Z",
190+
"updated": "2022-08-05T14:24:35.502Z",
191+
"created_by": 1,
192+
"display_name": "Wireframes",
193+
"perceived_difficulty": 1,
194+
"is_visible": true
195+
}
196+
},
197+
{
198+
"model": "competencies.competency",
199+
"pk": 9,
200+
"fields": {
201+
"created": "2022-08-05T14:24:42.333Z",
202+
"updated": "2022-08-05T14:24:42.333Z",
203+
"created_by": 1,
204+
"display_name": "SCRUM",
205+
"perceived_difficulty": 2,
206+
"is_visible": true
207+
}
208+
},
209+
{
210+
"model": "competencies.competency",
211+
"pk": 10,
212+
"fields": {
213+
"created": "2022-08-05T14:24:48.822Z",
214+
"updated": "2022-08-05T14:24:48.822Z",
215+
"created_by": 1,
216+
"display_name": "Presenting",
217+
"perceived_difficulty": 1,
218+
"is_visible": true
219+
}
220+
}
221+
]

competencies/forms.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
from django import forms
2+
from django.forms import BaseModelFormSet
3+
4+
from competencies.models import (
5+
Competency, CompetencyDifficulty,
6+
CompetencyAssessment, CompetencyAssessmentRating)
7+
8+
9+
class RequiredModelFormSet(BaseModelFormSet):
10+
def __init__(self, *args, **kwargs):
11+
super(RequiredModelFormSet, self).__init__(*args, **kwargs)
12+
for form in self.forms:
13+
form.empty_permitted = False
14+
form.use_required_attribute = True
15+
16+
17+
class CompetencyForm(forms.ModelForm):
18+
perceived_difficulty = forms.ModelChoiceField(
19+
queryset=CompetencyDifficulty.objects.all(),
20+
widget=forms.Select(attrs={
21+
'class': 'form-control'
22+
}))
23+
24+
class Meta:
25+
model = Competency
26+
fields = ['display_name', 'perceived_difficulty']
27+
28+
29+
class CompetencyDifficultyForm(forms.ModelForm):
30+
class Meta:
31+
model = CompetencyDifficulty
32+
fields = ['display_name', 'numeric_difficulty']
33+
34+
35+
class CompetencyAssessmentForm(forms.ModelForm):
36+
is_visible = forms.BooleanField(
37+
required=False,
38+
label="Make my assessment visible to my teams and facilitators")
39+
40+
class Meta:
41+
model = CompetencyAssessment
42+
fields = ['user', 'is_visible']
43+
44+
45+
class CompetencyAssessmentRatingForm(forms.ModelForm):
46+
competency = forms.ModelChoiceField(
47+
queryset=Competency.objects.all(),
48+
widget=forms.Select(attrs={
49+
'class': 'form-control disabled-select',
50+
}))
51+
52+
class Meta:
53+
model = CompetencyAssessmentRating
54+
fields = ['id', 'user_assessment', 'competency', 'rating']

competencies/helpers.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
from django.core.exceptions import ObjectDoesNotExist
2+
3+
from competencies.forms import CompetencyAssessmentForm
4+
from competencies.models import CompetencyAssessment
5+
6+
7+
def get_or_create_competency_assessment(data):
8+
try:
9+
existing_assessment = CompetencyAssessment.objects.get(
10+
user=data.get('user'))
11+
existing_assessment.is_visible = data.get('is_visible') == 'on'
12+
existing_assessment.save()
13+
return existing_assessment
14+
except ObjectDoesNotExist:
15+
# If no assessment exists, just continue
16+
pass
17+
18+
form = CompetencyAssessmentForm(data)
19+
if form.is_valid():
20+
return form.save()
21+
return
22+
23+
24+
def populate_competency_assessment_for_formset(competency_assessment, data):
25+
keys = [key for key in data.keys()
26+
if key.endswith('-competency')]
27+
28+
for key in keys:
29+
assessment_key = key.replace('-competency', '-user_assessment')
30+
data[assessment_key] = competency_assessment.id

0 commit comments

Comments
 (0)