mirror of
https://github.com/Southampton-RSG/breccia-mapper.git
synced 2026-03-03 03:17:07 +00:00
19
activities/migrations/0004_activity_attendance_list.py
Normal file
19
activities/migrations/0004_activity_attendance_list.py
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
# Generated by Django 2.2.10 on 2020-02-19 15:20
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('people', '0005_user_one_person'),
|
||||||
|
('activities', '0003_rename_activity_series_fk'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='activity',
|
||||||
|
name='attendance_list',
|
||||||
|
field=models.ManyToManyField(related_name='activities', to='people.Person'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -1,6 +1,9 @@
|
|||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
|
|
||||||
|
from people import models as people_models
|
||||||
|
|
||||||
|
|
||||||
class ActivityType(models.Model):
|
class ActivityType(models.Model):
|
||||||
"""
|
"""
|
||||||
Representation of the type of activity being conducted.
|
Representation of the type of activity being conducted.
|
||||||
@@ -33,7 +36,7 @@ class ActivityMedium(models.Model):
|
|||||||
|
|
||||||
class ActivitySeries(models.Model):
|
class ActivitySeries(models.Model):
|
||||||
"""
|
"""
|
||||||
A series of related :class:`Activity`s
|
A series of related :class:`Activity`s.
|
||||||
"""
|
"""
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name_plural = 'activity series'
|
verbose_name_plural = 'activity series'
|
||||||
@@ -58,7 +61,7 @@ class ActivitySeries(models.Model):
|
|||||||
|
|
||||||
class Activity(models.Model):
|
class Activity(models.Model):
|
||||||
"""
|
"""
|
||||||
An instance of an activity - e.g. a workshop
|
An instance of an activity - e.g. a workshop.
|
||||||
"""
|
"""
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name_plural = 'activities'
|
verbose_name_plural = 'activities'
|
||||||
@@ -73,15 +76,19 @@ class Activity(models.Model):
|
|||||||
on_delete=models.PROTECT,
|
on_delete=models.PROTECT,
|
||||||
blank=True, null=True)
|
blank=True, null=True)
|
||||||
|
|
||||||
#: What type of activity does this series represent?
|
#: What type of activity is this?
|
||||||
type = models.ForeignKey(ActivityType,
|
type = models.ForeignKey(ActivityType,
|
||||||
on_delete=models.PROTECT,
|
on_delete=models.PROTECT,
|
||||||
blank=False, null=False)
|
blank=False, null=False)
|
||||||
|
|
||||||
#: How are activities in this series typically conducted?
|
#: How was this activity conducted?
|
||||||
medium = models.ForeignKey(ActivityMedium,
|
medium = models.ForeignKey(ActivityMedium,
|
||||||
on_delete=models.PROTECT,
|
on_delete=models.PROTECT,
|
||||||
blank=False, null=False)
|
blank=False, null=False)
|
||||||
|
|
||||||
|
#: Who attended this activity?
|
||||||
|
attendance_list = models.ManyToManyField(people_models.Person,
|
||||||
|
related_name='activities')
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return self.name
|
return self.name
|
||||||
|
|||||||
@@ -10,6 +10,20 @@
|
|||||||
</ol>
|
</ol>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
|
{% if user_is_attending %}
|
||||||
|
<button class="btn btn-danger"
|
||||||
|
onclick="clickCancelAttend();">
|
||||||
|
Cancel Attendance
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{% else %}
|
||||||
|
<button class="btn btn-success"
|
||||||
|
onclick="clickAttend();">
|
||||||
|
Attend
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
<dl>
|
<dl>
|
||||||
@@ -23,4 +37,91 @@
|
|||||||
<dd>{{ activity.medium }}</dd>
|
<dd>{{ activity.medium }}</dd>
|
||||||
</dl>
|
</dl>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<table class="table table-borderless">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<tbody>
|
||||||
|
{% for person in activity.attendance_list.all %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ person }}</td>
|
||||||
|
<td>
|
||||||
|
<a class="btn btn-sm btn-info"
|
||||||
|
href="{% url 'people:person.detail' pk=person.pk %}">Profile</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
{% empty %}
|
||||||
|
<tr>
|
||||||
|
<td>No records</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
|
||||||
|
{% block extra_script %}
|
||||||
|
<script type="application/javascript">
|
||||||
|
/**
|
||||||
|
* Get the value of a named cookie.
|
||||||
|
*/
|
||||||
|
function getCookie(name) {
|
||||||
|
for (const cookie of document.cookie.split(';')) {
|
||||||
|
const tokens = cookie.split('=');
|
||||||
|
if (tokens[0].trim() === name) {
|
||||||
|
return tokens[1].trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Submit that user is attending this activity.
|
||||||
|
*/
|
||||||
|
function clickAttend() {
|
||||||
|
$.ajax({
|
||||||
|
url: '{% url "activities:activity.attendance" pk=activity.pk %}',
|
||||||
|
type: 'POST',
|
||||||
|
contentType: 'application/json',
|
||||||
|
headers: {
|
||||||
|
'X-CSRFToken': getCookie('csrftoken')
|
||||||
|
},
|
||||||
|
data: JSON.stringify({
|
||||||
|
pk: {{ request.user.person.pk }}
|
||||||
|
}),
|
||||||
|
success: function() {
|
||||||
|
location.reload()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Submit that user is not attending this activity.
|
||||||
|
*/
|
||||||
|
function clickCancelAttend() {
|
||||||
|
$.ajax({
|
||||||
|
url: '{% url "activities:activity.attendance" pk=activity.pk %}',
|
||||||
|
type: 'DELETE',
|
||||||
|
contentType: 'application/json',
|
||||||
|
headers: {
|
||||||
|
'X-CSRFToken': getCookie('csrftoken')
|
||||||
|
},
|
||||||
|
data: JSON.stringify({
|
||||||
|
pk: {{ request.user.person.pk }}
|
||||||
|
}),
|
||||||
|
success: function() {
|
||||||
|
location.reload()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -21,4 +21,8 @@ urlpatterns = [
|
|||||||
path('activities/<int:pk>',
|
path('activities/<int:pk>',
|
||||||
views.ActivityDetailView.as_view(),
|
views.ActivityDetailView.as_view(),
|
||||||
name='activity.detail'),
|
name='activity.detail'),
|
||||||
|
|
||||||
|
path('activities/<int:pk>/attendance',
|
||||||
|
views.ActivityAttendanceView.as_view(),
|
||||||
|
name='activity.attendance'),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,9 +1,14 @@
|
|||||||
"""
|
"""
|
||||||
Views for displaying / manipulating models within the Activities app.
|
Views for displaying / manipulating models within the Activities app.
|
||||||
"""
|
"""
|
||||||
from django.views.generic import DetailView, ListView
|
import json
|
||||||
|
|
||||||
|
from django.http import HttpResponse
|
||||||
|
from django.views.generic import DetailView, ListView, View
|
||||||
|
from django.views.generic.detail import SingleObjectMixin
|
||||||
|
|
||||||
from . import models
|
from . import models
|
||||||
|
from people import models as people_models
|
||||||
|
|
||||||
|
|
||||||
class ActivitySeriesListView(ListView):
|
class ActivitySeriesListView(ListView):
|
||||||
@@ -38,3 +43,44 @@ class ActivityDetailView(DetailView):
|
|||||||
"""
|
"""
|
||||||
model = models.Activity
|
model = models.Activity
|
||||||
template_name = 'activities/activity/detail.html'
|
template_name = 'activities/activity/detail.html'
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super().get_context_data(**kwargs)
|
||||||
|
|
||||||
|
context['user_is_attending'] = self.object.attendance_list.filter(pk=self.request.user.person.pk).exists()
|
||||||
|
|
||||||
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
class ActivityAttendanceView(SingleObjectMixin, View):
|
||||||
|
"""
|
||||||
|
View to add or delete attendance of an activity.
|
||||||
|
"""
|
||||||
|
model = models.Activity
|
||||||
|
|
||||||
|
def post(self, request, *args, **kwargs):
|
||||||
|
self.object = self.get_object()
|
||||||
|
|
||||||
|
if request.is_ajax():
|
||||||
|
data = json.loads(request.body)
|
||||||
|
|
||||||
|
person = people_models.Person.objects.get(pk=data['pk'])
|
||||||
|
self.object.attendance_list.add(person)
|
||||||
|
|
||||||
|
return HttpResponse(status=204)
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
def delete(self, request, *args, **kwargs):
|
||||||
|
self.object = self.get_object()
|
||||||
|
|
||||||
|
if request.is_ajax():
|
||||||
|
data = json.loads(request.body)
|
||||||
|
|
||||||
|
person = people_models.Person.objects.get(pk=data['pk'])
|
||||||
|
self.object.attendance_list.remove(person)
|
||||||
|
|
||||||
|
return HttpResponse(status=204)
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|||||||
@@ -234,3 +234,11 @@ CONSTANCE_CONFIG_FIELDSETS = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
CONSTANCE_BACKEND = 'constance.backends.database.DatabaseBackend'
|
CONSTANCE_BACKEND = 'constance.backends.database.DatabaseBackend'
|
||||||
|
|
||||||
|
|
||||||
|
# Bootstrap settings
|
||||||
|
# See https://django-bootstrap4.readthedocs.io/en/latest/settings.html
|
||||||
|
|
||||||
|
BOOTSTRAP4 = {
|
||||||
|
'include_jquery': 'full',
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,7 +7,6 @@
|
|||||||
<html lang="{{ LANGUAGE_CODE|default:'en_us' }}">
|
<html lang="{{ LANGUAGE_CODE|default:'en_us' }}">
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
|
|
||||||
<!-- Required meta tags -->
|
<!-- Required meta tags -->
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
|
|||||||
Reference in New Issue
Block a user