Merge pull request #1 from edwilding/contribute_games
Added tonnes of features, lets see what breaks.
This commit is contained in:
commit
eb4922ba0b
@ -7,8 +7,7 @@ from .models import User, Coord
|
||||
|
||||
@admin.register(Coord)
|
||||
class CoordAdmin(admin.ModelAdmin):
|
||||
list_display = 'id', 'country',
|
||||
search_fields = 'country',
|
||||
list_display = 'id',
|
||||
|
||||
|
||||
class CustomUserAdmin(UserAdmin):
|
||||
|
@ -5,26 +5,17 @@ from django.conf import settings
|
||||
from django.forms import widgets
|
||||
from django.forms.utils import to_current_timezone
|
||||
from django.contrib.auth.forms import UserCreationForm, UserChangeForm
|
||||
from django.forms import modelformset_factory
|
||||
|
||||
from geogame.main.models import (
|
||||
Coord, User, GameRound
|
||||
Coord, User, GameRound, Challenge
|
||||
)
|
||||
|
||||
from dal import autocomplete
|
||||
#from django_starfield import Stars
|
||||
|
||||
|
||||
class CoordForm(forms.ModelForm):
|
||||
|
||||
class ChallengeForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = Coord
|
||||
fields = ('lat', 'lng', 'country')
|
||||
widgets = {'country': autocomplete.ModelSelect2(url='game:country-autocomplete')}
|
||||
|
||||
def clean(self):
|
||||
cleaned_data = super(CoordForm, self).clean()
|
||||
lat = cleaned_data.get('lat')
|
||||
lng = cleaned_data.get('lng')
|
||||
model = Challenge
|
||||
fields = ('name',)
|
||||
|
||||
|
||||
class GuessForm(forms.ModelForm):
|
||||
@ -47,7 +38,7 @@ class APIForm(forms.ModelForm):
|
||||
|
||||
class Meta:
|
||||
model = User
|
||||
fields = ('api_key',)
|
||||
fields = ('api_key', 'display_name',)
|
||||
|
||||
|
||||
class CustomUserCreationForm(UserCreationForm):
|
||||
@ -62,3 +53,12 @@ class CustomUserChangeForm(UserChangeForm):
|
||||
class Meta:
|
||||
model = User
|
||||
fields = ('username', 'email')
|
||||
|
||||
|
||||
|
||||
|
||||
ChallengeCoordFormSet = modelformset_factory(
|
||||
Coord,
|
||||
fields=('lat', 'lng',),
|
||||
extra=1,
|
||||
)
|
@ -20,13 +20,18 @@ class Command(BaseCommand):
|
||||
|
||||
with transaction.atomic():
|
||||
|
||||
country = Country.objects.get(country='United Kingdom')
|
||||
country = Country.objects.get(country="United States of America")
|
||||
user = User.objects.first()
|
||||
with open('/home/ubuntu/lat_lng.csv', 'r') as csvfile:
|
||||
datareader = csv.reader(csvfile)
|
||||
for row in datareader:
|
||||
if row:
|
||||
Coord.objects.create(lat=row[0], lng=row[1], country=country, user=user)
|
||||
_,c = Coord.objects.get_or_create(
|
||||
lat=row[0],
|
||||
lng=row[1],
|
||||
country=country,
|
||||
user=user
|
||||
)
|
||||
|
||||
if options['dry_run']:
|
||||
transaction.set_rollback(True)
|
||||
|
64
geogame/main/migrations/0004_auto_20191125_2344.py
Normal file
64
geogame/main/migrations/0004_auto_20191125_2344.py
Normal file
@ -0,0 +1,64 @@
|
||||
# Generated by Django 2.2.4 on 2019-11-25 23:44
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('main', '0003_auto_20190822_2204'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Challenge',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(blank=True, db_index=True, max_length=255, verbose_name='Challenge Name')),
|
||||
('average', models.PositiveIntegerField()),
|
||||
],
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='coord',
|
||||
name='country',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='game',
|
||||
name='country',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='coord',
|
||||
name='reports',
|
||||
field=models.PositiveIntegerField(default=0),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='gameround',
|
||||
name='result',
|
||||
field=models.PositiveIntegerField(default=0),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='user',
|
||||
name='display_name',
|
||||
field=models.CharField(blank=True, db_index=True, max_length=25, verbose_name='display name'),
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='Country',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='challenge',
|
||||
name='user',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='challenge_user', to=settings.AUTH_USER_MODEL),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='coord',
|
||||
name='challenge',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='coord_challenge', to='main.Challenge'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='game',
|
||||
name='challenge',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='game_challenge', to='main.Challenge'),
|
||||
),
|
||||
]
|
@ -5,6 +5,7 @@ import uuid
|
||||
from django.contrib.postgres.search import SearchVectorField, SearchQuery, SearchVector
|
||||
from django.conf import settings
|
||||
from django.db import models, transaction, IntegrityError
|
||||
from django.db.models import Avg
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.conf import settings
|
||||
@ -13,8 +14,6 @@ from django.shortcuts import reverse
|
||||
from django.contrib.auth.models import AbstractUser
|
||||
from django.utils import timezone
|
||||
|
||||
|
||||
from django_countries.fields import CountryField
|
||||
from geopy import distance
|
||||
|
||||
|
||||
@ -23,17 +22,16 @@ class User(AbstractUser):
|
||||
last_name = models.CharField(_('last name'), max_length=50, blank=True, db_index=True)
|
||||
email = models.EmailField(_('email address'), blank=False, null=False, db_index=True)
|
||||
api_key = models.CharField(_('google maps api key'), max_length=255, blank=True, db_index=True)
|
||||
display_name = models.CharField(_('display name'), max_length=25, blank=True, db_index=True)
|
||||
|
||||
def generate_new_game(self, country=None):
|
||||
def generate_new_game(self):
|
||||
game = Game.objects.create(
|
||||
start=timezone.now(),
|
||||
user=self,
|
||||
score=0,
|
||||
active=True,
|
||||
)
|
||||
qs = Coord.objects.all()
|
||||
if country:
|
||||
qs = qs.filter(country=country)
|
||||
qs = Coord.objects.filter(reports=0)
|
||||
coords = qs.order_by('?')[:5]
|
||||
for i, coord in enumerate(coords):
|
||||
round = GameRound.objects.create(
|
||||
@ -52,34 +50,65 @@ class User(AbstractUser):
|
||||
return Game.objects.filter(user=self).update(active=False)
|
||||
|
||||
|
||||
class Country(models.Model):
|
||||
country = models.CharField(_('Country'), max_length=255, null=False, blank=False)
|
||||
class Challenge(models.Model):
|
||||
name = models.CharField(_('Challenge Name'), max_length=255, blank=True, db_index=True)
|
||||
average = models.PositiveIntegerField(default=0)
|
||||
user = models.ForeignKey(User, models.PROTECT,
|
||||
related_name='challenge_user',
|
||||
null=False, blank=False)
|
||||
|
||||
def __str__(self):
|
||||
return self.country
|
||||
def update_average_score(self):
|
||||
average = GameRound.objects.filter(game__challenge=self).aggregate(Avg('result')).get('result__avg', 0)
|
||||
self.average = average
|
||||
self.save()
|
||||
return average
|
||||
|
||||
def setup_challenge(self, user):
|
||||
rounds = Coord.objects.filter(challenge=self)
|
||||
game = Game.objects.create(
|
||||
challenge=self,
|
||||
start=timezone.now(),
|
||||
user=user,
|
||||
score=0,
|
||||
active=True,
|
||||
)
|
||||
for order, coord in enumerate(rounds):
|
||||
round = GameRound.objects.create(
|
||||
game=game,
|
||||
coord=coord,
|
||||
order=order
|
||||
)
|
||||
if order == 0:
|
||||
round_id = round.id
|
||||
return game.id, round_id
|
||||
|
||||
|
||||
class Coord(models.Model):
|
||||
lng = models.CharField(_('longitude'), max_length=50, null=False, blank=False)
|
||||
lat = models.CharField(_('latitude'), max_length=50, null=False, blank=False)
|
||||
country = models.ForeignKey(Country, models.PROTECT,
|
||||
related_name='coord_country',
|
||||
null=False, blank=False)
|
||||
user = models.ForeignKey(User, models.PROTECT,
|
||||
related_name='coord_user',
|
||||
null=False, blank=False)
|
||||
reports = models.PositiveIntegerField(default=0)
|
||||
challenge = models.ForeignKey(Challenge, models.PROTECT,
|
||||
related_name='coord_challenge',
|
||||
null=True, blank=True)
|
||||
|
||||
def report(self):
|
||||
self.reports = self.reports + 1
|
||||
self.save()
|
||||
|
||||
|
||||
class Game(models.Model):
|
||||
challenge = models.ForeignKey(Challenge, models.PROTECT,
|
||||
related_name='game_challenge',
|
||||
null=True, blank=True)
|
||||
start = models.DateTimeField()
|
||||
user = models.ForeignKey(User, models.PROTECT,
|
||||
related_name='game_user',
|
||||
null=False, blank=False)
|
||||
score = models.PositiveIntegerField()
|
||||
active = models.BooleanField()
|
||||
country = models.ForeignKey(Country, models.PROTECT,
|
||||
related_name='game_country',
|
||||
null=True, blank=True)
|
||||
|
||||
def get_rounds(self):
|
||||
return GameRound.objects.filter(game=self)
|
||||
@ -95,10 +124,18 @@ class GameRound(models.Model):
|
||||
order = models.IntegerField(_('round order'))
|
||||
guess_lat = models.CharField(_('latitude'), max_length=50, blank=True, null=True)
|
||||
guess_lng = models.CharField(_('longitude'), max_length=50, blank=True, null=True)
|
||||
result = models.PositiveIntegerField(default=0)
|
||||
|
||||
def get_distance(self):
|
||||
if not self.guess_lat or not self.guess_lng:
|
||||
self.distance = 0
|
||||
self.save()
|
||||
return 0
|
||||
|
||||
actual_coord = (self.coord.lat, self.coord.lng,)
|
||||
guess_coord = (self.guess_lat, self.guess_lng,)
|
||||
return distance.distance(actual_coord, guess_coord).km
|
||||
|
||||
result = distance.distance(actual_coord, guess_coord).km
|
||||
self.result = result
|
||||
self.save()
|
||||
return result
|
@ -2,8 +2,8 @@ from django.conf.urls import url
|
||||
from django.contrib.auth.decorators import permission_required
|
||||
|
||||
from geogame.main.views import (
|
||||
RoundView, RoundRecapView, GameRecapView, NewGameView,
|
||||
ContributeView, CountryAutocomplete, RemoveCoordView
|
||||
RoundView, RoundRecapView, GameRecapView, NewGameView, ChallengeListView,
|
||||
EditChallengeView, RemoveCoordView, CoordDeleteView, ChallengeCreateView
|
||||
)
|
||||
|
||||
|
||||
@ -13,7 +13,8 @@ urlpatterns = [
|
||||
url(r'^round-recap/(?P<game_pk>\d+)/(?P<round_pk>\d+)/$', RoundRecapView.as_view(), name="round-recap-view"),
|
||||
url(r'^remove-coord/(?P<game_pk>\d+)/(?P<round_pk>\d+)/$', RemoveCoordView.as_view(), name="remove-coord"),
|
||||
url(r'^end-recap/(?P<game_pk>\d+)/$', GameRecapView.as_view(), name="end-recap-view"),
|
||||
url(r'^contribute/$', ContributeView.as_view(), name="contribute"),
|
||||
url(r'^country-autocomplete/$', CountryAutocomplete.as_view(), name='country-autocomplete',
|
||||
),
|
||||
url(r'^create-challenge/$', ChallengeCreateView.as_view(), name="create-challenge"),
|
||||
url(r'^list-challenge/$', ChallengeListView.as_view(), name="list-challenge"),
|
||||
url(r'^edit-challenge/(?P<pk>\d+)/$', EditChallengeView.as_view(), name="edit-challenge"),
|
||||
url(r'^coord/(?P<pk>\d+)/delete/$', CoordDeleteView.as_view(), name="coord-delete"),
|
||||
]
|
||||
|
@ -1,6 +1,7 @@
|
||||
from django.conf import settings
|
||||
from django.shortcuts import get_object_or_404, redirect
|
||||
from django.views.generic import TemplateView, ListView
|
||||
from django.db.models import Avg
|
||||
from django.views.generic.edit import CreateView, DeleteView, UpdateView
|
||||
from django.views.generic.detail import DetailView
|
||||
from django.views import View
|
||||
@ -10,25 +11,12 @@ from django.urls import reverse_lazy
|
||||
from braces import views
|
||||
|
||||
from geogame.main.models import (
|
||||
Game, GameRound, Coord, User, Country
|
||||
Game, GameRound, Coord, User, Challenge
|
||||
)
|
||||
from dal import autocomplete
|
||||
from geogame.main.forms import GuessForm, CoordForm, APIForm
|
||||
from geogame.main.forms import GuessForm, ChallengeCoordFormSet, APIForm, ChallengeForm
|
||||
|
||||
|
||||
class CountryAutocomplete(autocomplete.Select2QuerySetView):
|
||||
def get_queryset(self):
|
||||
# Don't forget to filter out results depending on the visitor !
|
||||
if not self.request.user.is_authenticated:
|
||||
return Country.objects.none()
|
||||
|
||||
qs = Country.objects.all().order_by('country')
|
||||
|
||||
if self.q:
|
||||
qs = qs.filter(country__icontains=self.q)
|
||||
|
||||
return qs
|
||||
|
||||
|
||||
class HomePageView(TemplateView):
|
||||
template_name = 'main/homepage.html'
|
||||
@ -60,6 +48,12 @@ class HomePageView(TemplateView):
|
||||
class ProfilePageView(views.LoginRequiredMixin, TemplateView):
|
||||
template_name = 'main/profile.html'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
user = self.request.user
|
||||
context['user_challenges'] = Challenge.objects.filter(user=user)
|
||||
return context
|
||||
|
||||
|
||||
class UpdateAPIView(views.LoginRequiredMixin, UpdateView):
|
||||
model = User
|
||||
@ -70,20 +64,75 @@ class UpdateAPIView(views.LoginRequiredMixin, UpdateView):
|
||||
return reverse_lazy('profile')
|
||||
|
||||
|
||||
class ContributeView(views.LoginRequiredMixin, CreateView):
|
||||
model = Coord
|
||||
form_class = CoordForm
|
||||
template_name = 'main/contribute_form.html'
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse_lazy('game:contribute')
|
||||
class ChallengeCreateView(views.LoginRequiredMixin, CreateView):
|
||||
template_name = 'main/create_challenge_form.html'
|
||||
form_class = ChallengeForm
|
||||
|
||||
def form_valid(self, form):
|
||||
self.object = form.save(commit=False)
|
||||
self.object.user = self.request.user
|
||||
self.object.save()
|
||||
messages.success(self.request, "Your coordinates have been added.")
|
||||
return redirect(self.get_success_url())
|
||||
messages.success(self.request, 'Challenge Created Successfully, add some Co-ords')
|
||||
return redirect(reverse_lazy('game:edit-challenge', args=(self.object.id,)))
|
||||
|
||||
|
||||
class EditChallengeView(views.UserPassesTestMixin, TemplateView):
|
||||
template_name = 'main/edit_challenge_form.html'
|
||||
|
||||
def test_func(self, user):
|
||||
challenge = get_object_or_404(Challenge, pk=self.kwargs['pk'])
|
||||
return challenge.user == user
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
challenge = get_object_or_404(Challenge, pk=self.kwargs['pk'])
|
||||
context['challenge'] = challenge
|
||||
context['formset'] = ChallengeCoordFormSet(queryset=Coord.objects.none())
|
||||
context['coords'] = Coord.objects.filter(challenge=challenge)
|
||||
return context
|
||||
|
||||
def post(self, *args, **kwargs):
|
||||
challenge = get_object_or_404(Challenge, pk=self.kwargs['pk'])
|
||||
formset = ChallengeCoordFormSet(self.request.POST)
|
||||
|
||||
if formset.is_valid():
|
||||
for form in formset:
|
||||
obj = form.save(commit=False)
|
||||
obj.challenge = challenge
|
||||
obj.user = self.request.user
|
||||
obj.save()
|
||||
#XX prevent duplicates
|
||||
messages.success(self.request, 'Co-ords Updated Successfully')
|
||||
return redirect(reverse_lazy('game:edit-challenge', args=(challenge.id,)))
|
||||
messages.error(self.request, 'The form was invalid - something has gone wrong')
|
||||
return redirect(reverse_lazy('game:edit-challenge', args=(challenge.id,)))
|
||||
|
||||
|
||||
class CoordDeleteView(views.UserPassesTestMixin, DeleteView):
|
||||
model = Coord
|
||||
|
||||
def test_func(self, user):
|
||||
coord = self.get_object()
|
||||
return coord.user == user
|
||||
|
||||
def get_success_url(self):
|
||||
challenge = self.object.challenge
|
||||
messages.success(self.request, 'Coord Removed Successfully')
|
||||
return reverse_lazy('game:edit-challenge',args=(challenge.id,))
|
||||
|
||||
|
||||
class ChallengeListView(views.LoginRequiredMixin, ListView):
|
||||
template_name = 'main/challenge_list.html'
|
||||
context_object_name = 'challenges'
|
||||
paginate_by = 50
|
||||
|
||||
def get_queryset(self):
|
||||
ids = Coord.objects.filter(challenge__isnull=False).\
|
||||
order_by().\
|
||||
values('challenge').\
|
||||
distinct()
|
||||
|
||||
return Challenge.objects.filter(id__in=ids).order_by('average')
|
||||
|
||||
|
||||
class NewGameView(views.LoginRequiredMixin, View):
|
||||
@ -91,6 +140,10 @@ class NewGameView(views.LoginRequiredMixin, View):
|
||||
def get(self, request, *args, **kwargs):
|
||||
user = self.request.user
|
||||
user.deactive_games()
|
||||
if request.GET.get('challenge'):
|
||||
challenge = get_object_or_404(Challenge, pk=request.GET.get('challenge'))
|
||||
game_pk, round_pk = challenge.setup_challenge(user)
|
||||
else:
|
||||
game_pk, round_pk = user.generate_new_game()
|
||||
|
||||
return redirect(
|
||||
@ -151,9 +204,40 @@ class RemoveCoordView(View):
|
||||
def post(self, request, *args, **kwargs):
|
||||
round = get_object_or_404(GameRound, pk=self.kwargs.get('round_pk', 0))
|
||||
game = get_object_or_404(Game, pk=self.kwargs.get('game_pk', 0))
|
||||
qs = Coord.objects.all()
|
||||
if round.game.country:
|
||||
qs = qs.filter(country=round.game.country)
|
||||
dodgy_coord = round.coord
|
||||
dodgy_coord.report()
|
||||
|
||||
if game.challenge:
|
||||
round.score = 30000
|
||||
round.save()
|
||||
next = round.order + 1
|
||||
next_round = GameRound.objects.filter(
|
||||
game=game,
|
||||
order=next,
|
||||
).first()
|
||||
if next_round:
|
||||
return redirect(
|
||||
reverse_lazy(
|
||||
'game:round-view',
|
||||
kwargs={
|
||||
'game_pk': game.pk,
|
||||
'round_pk': next_round.pk,
|
||||
}
|
||||
)
|
||||
)
|
||||
else:
|
||||
return redirect(
|
||||
reverse_lazy(
|
||||
'game:end-recap-view',
|
||||
kwargs={
|
||||
'game_pk': game.pk,
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
else:
|
||||
|
||||
qs = Coord.objects.filter(reports=0)
|
||||
coords = qs.order_by('?')[:5]
|
||||
|
||||
current_coords = GameRound.objects.filter(game=game).exclude(id=round.id).values_list('coord__id', flat=True)
|
||||
@ -167,14 +251,13 @@ class RemoveCoordView(View):
|
||||
reverse_lazy(
|
||||
'game:round-view',
|
||||
kwargs={
|
||||
'game_pk': round.game.pk,
|
||||
'game_pk': game.pk,
|
||||
'round_pk': round.pk,
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
|
||||
class RoundRecapView(views.UserPassesTestMixin, TemplateView):
|
||||
template_name = 'main/round_recap.html'
|
||||
|
||||
@ -195,13 +278,19 @@ class RoundRecapView(views.UserPassesTestMixin, TemplateView):
|
||||
context['game_id'] = round.game.id
|
||||
context['distance'] = "{0:.3f}".format(round.get_distance())
|
||||
|
||||
if round.order == 4:
|
||||
context['last_round'] = True
|
||||
else:
|
||||
next_round = GameRound.objects.get(
|
||||
next_round = GameRound.objects.filter(
|
||||
game=round.game,
|
||||
order=round.order + 1
|
||||
)
|
||||
).first()
|
||||
|
||||
if not next_round:
|
||||
#not every game is part of a challenge, so try updated the challenge average
|
||||
try:
|
||||
round.game.challenge.update_average_score()
|
||||
except:
|
||||
pass
|
||||
context['last_round'] = True
|
||||
else:
|
||||
context['next_round_id'] = next_round.id
|
||||
return context
|
||||
|
||||
@ -214,7 +303,6 @@ class GameRecapView(views.UserPassesTestMixin, TemplateView):
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(GameRecapView, self).get_context_data(**kwargs)
|
||||
user = self.request.user
|
||||
game_id = self.kwargs.get('game_pk', 0)
|
||||
game = get_object_or_404(Game, pk=game_id)
|
||||
|
||||
@ -229,6 +317,17 @@ class GameRecapView(views.UserPassesTestMixin, TemplateView):
|
||||
)
|
||||
distance_total += round.get_distance()
|
||||
|
||||
context['average_distance'] = "{0:.3f}".format(distance_total / 5)
|
||||
context['results'] = coord_results
|
||||
context['average_distance'] = GameRound.objects.filter(game=game)\
|
||||
.aggregate(Avg('result'))\
|
||||
.get('result__avg', 0)
|
||||
# not every game is part of a challenge, so keep this in a try
|
||||
try:
|
||||
context['all_average'] = game.challenge.average
|
||||
except:
|
||||
pass
|
||||
return context
|
||||
|
||||
|
||||
|
||||
# handle reports differently if in a challenge (skip with score of 30000 instead of finding a random one)
|
@ -47,6 +47,7 @@ INSTALLED_APPS = [
|
||||
'django_extensions',
|
||||
'dal',
|
||||
'dal_select2',
|
||||
'dynamic_formsets',
|
||||
|
||||
'geogame.main',
|
||||
'django_countries',
|
||||
|
@ -11,7 +11,9 @@
|
||||
<link rel="shortcut icon" type="image/png" href="{% static 'favicon.ico' %}"/>
|
||||
<title>Geogame</title>
|
||||
</head>
|
||||
|
||||
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
|
||||
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
|
||||
<body>
|
||||
<nav class="navbar navbar-expand-lg navbar-light bg-light">
|
||||
<img id="logo" src="{% static 'brand.png' %}"></img>
|
||||
@ -29,7 +31,7 @@
|
||||
<a href="{% url 'profile' %}" class="nav-link">Profile</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="{% url 'game:contribute' %}" class="nav-link">Contribute</a>
|
||||
<a href="{% url 'game:create-challenge' %}" class="nav-link">Contribute</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="{% url 'account_logout' %}" class="nav-link">Logout</a>
|
||||
@ -58,7 +60,4 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
|
||||
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
|
||||
</body>
|
||||
|
@ -28,9 +28,6 @@
|
||||
<li class="nav-item">
|
||||
<a href="{% url 'profile' %}" class="nav-link">Profile</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="{% url 'game:contribute' %}" class="nav-link">Contribute</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="{% url 'account_logout' %}" class="nav-link">Logout</a>
|
||||
</li>
|
||||
|
@ -4,7 +4,7 @@
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-lg-10 offset-lg-1 content">
|
||||
<h1 class="section-header">API key for for {{user.email}}</h1>
|
||||
<h1 class="section-header">Details for {{user.email}}</h1>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -15,7 +15,7 @@
|
||||
{{ form.non_field_errors }}
|
||||
{% csrf_token %}
|
||||
{{form.as_p}}
|
||||
<button type="submit" class="btn btn-primary">Update API Key</button>
|
||||
<button type="submit" class="btn btn-primary">Update User Info</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
49
geogame/templates/main/challenge_list.html
Normal file
49
geogame/templates/main/challenge_list.html
Normal file
@ -0,0 +1,49 @@
|
||||
{% extends 'game_base.html' %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-lg-10 offset-lg-1 content">
|
||||
<h1 class="section-header">All Challenges</h1>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-lg-10 offset-lg-1 content">
|
||||
<table>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Average</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
{% for challenge in challenges %}
|
||||
<tr>
|
||||
<td>{{challenge.name}}</td>
|
||||
<td>{{challenge.average}}</td>
|
||||
<td><a class="btn btn-success btn-block" href="{% url 'game:new-game' %}?challenge={{challenge.id}}" role="button">Play</a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% if is_paginated %}
|
||||
<div class="pagination">
|
||||
<span class="page-links">
|
||||
{% if page_obj.has_previous %}
|
||||
<a href="{% url 'supplier:my-suppliers-list' %}?page={{ page_obj.previous_page_number }}">previous</a>
|
||||
{% endif %}
|
||||
<span class="page-current">
|
||||
Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}.
|
||||
</span>
|
||||
{% if page_obj.has_next %}
|
||||
<a href="{% url 'supplier:my-suppliers-list' %}?page={{ page_obj.next_page_number }}">next</a>
|
||||
{% endif %}
|
||||
</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
|
35
geogame/templates/main/create_challenge_form.html
Normal file
35
geogame/templates/main/create_challenge_form.html
Normal file
@ -0,0 +1,35 @@
|
||||
{% extends 'game_base.html' %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-lg-10 offset-lg-1 content">
|
||||
<h1 class="section-header">Create Challenge</h1>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-lg-10 offset-lg-1 content">
|
||||
<form action="" method="POST">
|
||||
{{ form.non_field_errors }}
|
||||
{% csrf_token %}
|
||||
<div class="form-group form-row">
|
||||
<label for="staticName" class="col-sm-4 col-form-label">Name</label>
|
||||
<div class="col-sm-8">
|
||||
{{form.name}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group form-row">
|
||||
<div class="col-sm-12">
|
||||
<a class="btn btn-danger pull-left" href="{% url 'profile' %}">Cancel</a>
|
||||
<button type="submit" class="btn btn-primary pull-right">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
92
geogame/templates/main/edit_challenge_form.html
Normal file
92
geogame/templates/main/edit_challenge_form.html
Normal file
@ -0,0 +1,92 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load staticfiles%}
|
||||
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-lg-10 offset-lg-1 content">
|
||||
<h1 class="section-header">Edit Challenge {{challenge.name}}</h1>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-lg-10 offset-lg-1 content">
|
||||
{% if form.errors %}
|
||||
{% for field in form %}
|
||||
{% for error in field.errors %}
|
||||
<div class="alert alert-danger">
|
||||
<strong>{{ error|escape }}</strong>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
{% for error in form.non_field_errors %}
|
||||
<div class="alert alert-danger">
|
||||
<strong>{{ error|escape }}</strong>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>Latitude</th>
|
||||
<th>Longitude</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
{% for coord in coords %}
|
||||
<tr>
|
||||
<td>
|
||||
{{ coord.lat }}
|
||||
</td>
|
||||
<td>
|
||||
{{ coord.lng }}
|
||||
</td>
|
||||
<td style="width:105px">
|
||||
<form action="{% url 'game:coord-delete' pk=coord.pk %}" method="POST">
|
||||
{% csrf_token %}
|
||||
<input type="submit" class="btn btn-danger btn-block" value="Remove" name="delete" style="margin-right:0">
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
|
||||
|
||||
<form id="coordForm" method="post" action="">
|
||||
{% csrf_token %}
|
||||
<table border="0" cellpadding="0" cellspacing="0">
|
||||
<tbody>
|
||||
{% for form in formset.forms %}
|
||||
<tr>
|
||||
<td>{{ form.lat }}</td>
|
||||
<td>{{ form.lng }}</td>
|
||||
<td style="width:105px"></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{{ formset.management_form }}
|
||||
</form>
|
||||
<input class="btn btn-primary pull-right" type="submit" value="Save" form="coordForm">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="{% static 'dynamic_formsets/jquery.formset.js' %}" type="text/javascript"> </script>
|
||||
<script type="text/javascript">
|
||||
$('#coordForm').formset();
|
||||
// function defer(method) {
|
||||
// if (window.jQuery) {
|
||||
// $('.individual-form').formset();
|
||||
// } else {
|
||||
// setTimeout(function() { defer(method) }, 50);
|
||||
// }
|
||||
// }
|
||||
</script>
|
||||
|
||||
{% endblock %}
|
@ -14,7 +14,7 @@
|
||||
{% endif %}
|
||||
|
||||
<div class="alert alert-info">
|
||||
Your average guess was {{average_distance}}km away.
|
||||
Your average guess was {{average_distance}}km away.{% if all_average %} The average for this challenge is {{all_average}}km{%endif%}
|
||||
</div>
|
||||
|
||||
<div id="map" class="map"></div>
|
||||
|
@ -15,17 +15,20 @@
|
||||
{% if has_api_key %}
|
||||
{% if existing_game %}
|
||||
<a class="btn btn-info btn-lg btn-block" href="{% url 'game:round-view' game_pk=existing_game.id round_pk=existing_round.id %}" role="button">Continue Last Game</a>
|
||||
<a class="btn btn-success btn-lg btn-block" href="{% url 'game:new-game' %}" role="button">New Game</a>
|
||||
<a class="btn btn-success btn-lg btn-block" href="{% url 'game:new-game' %}" role="button">New Random Game</a>
|
||||
<a class="btn btn-warning btn-lg btn-block" href="{% url 'game:list-challenge' %}" role="button">View Challenges</a>
|
||||
{% else %}
|
||||
<button type="button" class="btn btn-info btn-lg btn-block" disabled>Continue Last Game</button>
|
||||
<a class="btn btn-success btn-lg btn-block" href="{% url 'game:new-game' %}" role="button">New Game</a>
|
||||
<a class="btn btn-success btn-lg btn-block" href="{% url 'game:new-game' %}" role="button">New Random Game</a>
|
||||
<a class="btn btn-warning btn-lg btn-block" href="{% url 'game:list-challenge' %}" role="button">View Challenges</a>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<div class="alert alert-warning">
|
||||
You must set an api key on your profile page to play the game. Please read the FAQ for more info.
|
||||
</div>
|
||||
<button type="button" class="btn btn-info btn-lg btn-block" disabled>Continue Last Game</button>
|
||||
<button type="button" class="btn btn-success btn-lg btn-block" disabled>New Game</button>
|
||||
<button type="button" class="btn btn-success btn-lg btn-block" disabled>New Random Game</button>
|
||||
<button type="button" class="btn btn-warning btn-lg btn-block" disabled>View Challenges</button>
|
||||
{% endif %}
|
||||
|
||||
{% else %}
|
||||
@ -60,7 +63,7 @@
|
||||
<p>Sometimes a coordinate with no valid streetview is selected for you, you will just get a black screen.</p>
|
||||
<p>If this occurs please click the "Broken Streetview" button and you will be provided with a new scene and the faulty coordinate will be removed from the database.</p>
|
||||
<p><strong>There isn't a great variety of countries, why?</strong></p>
|
||||
<p>I am relying on crowd sourcing of playable scenes, please consider helping out by <a href="{% url 'game:contribute' %}">adding a few coordinates of your own</a>.</p>
|
||||
<p>I am relying on crowd sourcing of playable scenes, please consider helping out by <a href="{% url 'game:create-challenge' %}">adding a few coordinates of your own</a>.</p>
|
||||
<p>You get to keep track of the coordinates you add, and see how well (or badly) other people do on them!</p>
|
||||
<p>For now I have managed to find some data on uk coordinates that I can use, but its not so easy getting data for other countries.</p>
|
||||
<p><strong>I didn't receive my email reset password, what gives?</strong></p>
|
||||
|
@ -16,7 +16,7 @@
|
||||
<a class="btn btn-primary btn-lg" style="margin-top:16px" href="{% url 'account_change_password' %}">Change Password</a>
|
||||
</p>
|
||||
<p>
|
||||
<a class="btn btn-primary btn-lg" href="{% url 'api-key-view' pk=user.pk %}">API key</a>
|
||||
<a class="btn btn-primary btn-lg" href="{% url 'api-key-view' pk=user.pk %}">Edit Details</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@ -25,6 +25,7 @@
|
||||
<div class="row">
|
||||
<div class="col-lg-10 offset-lg-1 content">
|
||||
<h3 class="section-header">Played Games</h3>
|
||||
<hr>
|
||||
<p>Coming soon</p>
|
||||
</div>
|
||||
</div>
|
||||
@ -33,7 +34,23 @@
|
||||
<div class="row">
|
||||
<div class="col-lg-10 offset-lg-1 content">
|
||||
<h3 class="section-header">Your Contributions</h3>
|
||||
<p>Coming soon</p>
|
||||
<hr>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Challenge Name</th>
|
||||
<th>Average Score</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
{% for challenge in user_challenges %}
|
||||
<tr>
|
||||
<td>{{challenge.name}}</td>
|
||||
<td>{{challenge.average}}</td>
|
||||
<td><a class="btn btn-warning btn-block" href="{% url 'game:edit-challenge' pk=challenge.pk %}" role="button">Edit</a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
<hr>
|
||||
<a class="btn btn-primary btn-lg" href="{% url 'game:create-challenge' %}">Create a New Challenge</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
Loading…
Reference in New Issue
Block a user