24 changed files with 570 additions and 0 deletions
@ -0,0 +1,13 @@ |
|||||||
|
<!DOCTYPE html>{% load static %} |
||||||
|
<html> |
||||||
|
<head> |
||||||
|
<meta charset="utf-8"> |
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1"> |
||||||
|
{% block 'extra_css' %}{% endblock %} |
||||||
|
<link rel="icon" href="{% static 'includes/' %}{{favicon}}"> |
||||||
|
<title>EscapeGame</title> |
||||||
|
</head> |
||||||
|
<body class="bg-light" style="">{% if messages %}{% for message in messages %} |
||||||
|
<p>{{message}}</p>{% endfor %}{% endif %}{% block 'body' %} |
||||||
|
{% endblock %}</body> |
||||||
|
</html> |
||||||
@ -0,0 +1,52 @@ |
|||||||
|
<!DOCTYPE html>{% load static %} |
||||||
|
<html lang="fr"><head> |
||||||
|
<meta http-equiv="content-type" content="text/html; charset=UTF-8"> |
||||||
|
<meta charset="utf-8"> |
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> |
||||||
|
<meta name="description" content="Escape Game"> |
||||||
|
<meta name="author" content="Christophe SCAYA"> |
||||||
|
<link rel="icon" href="{% static 'escape-icon-2.jpg' %}"> |
||||||
|
|
||||||
|
<title>Cover Template for Bootstrap</title> |
||||||
|
|
||||||
|
<!-- Bootstrap core CSS --> |
||||||
|
<link href="{% static 'bootstrap.min.css' %}" rel="stylesheet"> |
||||||
|
|
||||||
|
<!-- Custom styles for this template --> |
||||||
|
<link href="{% static 'cover.css' %}" rel="stylesheet"> |
||||||
|
</head> |
||||||
|
|
||||||
|
<body class="text-center"> |
||||||
|
|
||||||
|
<div class="cover-container d-flex h-100 p-3 mx-auto flex-column"> |
||||||
|
<header class="masthead mb-auto"> |
||||||
|
<div class="inner"> |
||||||
|
<h4 class="masthead-brand">{{title}}</h4> |
||||||
|
</div> |
||||||
|
</header> |
||||||
|
|
||||||
|
{% for message in messages %} |
||||||
|
<div class="container-fluid p-0"> |
||||||
|
<div class="alert {{ message.tags }} alert-dismissible" role="alert" > |
||||||
|
<button type="button" class="close" data-dismiss="alert" aria-label="Close"> |
||||||
|
<span aria-hidden="True">×</span> |
||||||
|
</button> |
||||||
|
{{ message }} |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
{% endfor %}{% block 'contenu' %}{% endblock %} |
||||||
|
|
||||||
|
<footer class="mastfoot mt-auto"></footer> |
||||||
|
</div> |
||||||
|
|
||||||
|
|
||||||
|
<!-- Bootstrap core JavaScript |
||||||
|
================================================== --> |
||||||
|
<!-- Placed at the end of the document so the pages load faster --> |
||||||
|
<script src="{% static 'jquery-3.2.1.slim.min.js' %}" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script> |
||||||
|
<!--<script>window.jQuery || document.write('<script src="../../assets/js/vendor/jquery-slim.min.js"><\/script>')</script>--> |
||||||
|
<script src="{% static 'popper.min.js' %}"></script> |
||||||
|
<script src="{% static 'bootstrap.min.js' %}"></script> |
||||||
|
|
||||||
|
</body> |
||||||
|
</html> |
||||||
@ -0,0 +1,18 @@ |
|||||||
|
from django import forms |
||||||
|
from django.core.exceptions import ValidationError |
||||||
|
from django.utils.translation import gettext_lazy as _ |
||||||
|
from .models import Equipe |
||||||
|
|
||||||
|
def validateEquipe(value): |
||||||
|
if len(Equipe.objects.filter(code=value)) == 0: |
||||||
|
raise ValidationError( |
||||||
|
_("Cette équipe n'existe pas"), |
||||||
|
params={'value': value}, |
||||||
|
) |
||||||
|
|
||||||
|
class LoginForm(forms.Form): |
||||||
|
equipe = forms.DecimalField(label="Code équipe", min_value=1000, max_value=9999, decimal_places=0, validators=[validateEquipe]) |
||||||
|
|
||||||
|
class CreateGameForm(forms.Form): |
||||||
|
nb_equipes = forms.DecimalField(label="Nombre d'équipes", min_value=0, max_value=10, decimal_places=0) |
||||||
|
nb_challenges = forms.DecimalField(label="Nombre de challenges", min_value=0, max_value=10, decimal_places=0) |
||||||
@ -0,0 +1,68 @@ |
|||||||
|
from .models import * |
||||||
|
from django.db.models import Max; |
||||||
|
from django.apps import apps |
||||||
|
from datetime import datetime |
||||||
|
|
||||||
|
def getMaxChallenge(equipe): |
||||||
|
max_rank = Challenge.objects.filter(equipe=equipe).aggregate(max_rank=Max("rank"))['max_rank'] |
||||||
|
try: |
||||||
|
challenge = Challenge.objects.filter(equipe=equipe, rank=max_rank)[0] |
||||||
|
return challenge |
||||||
|
except Challenge.DoesNotExist: |
||||||
|
return None |
||||||
|
|
||||||
|
""" |
||||||
|
Fonction createChallenge |
||||||
|
Ajoute un challenge à l'équipe fournie et le retourne. Si le nombre max de challenges est déjà atteint, |
||||||
|
la fonction retourne None. |
||||||
|
Si prevZone est fournie, la zone du nouveau challenge sera la plus éloignée possible. |
||||||
|
""" |
||||||
|
def createChallenge(equipe,prevZone=None): |
||||||
|
max_rank = Challenge.objects.filter(equipe=equipe).aggregate(max_rank=Max("rank"))['max_rank'] |
||||||
|
if max_rank is None: |
||||||
|
max_rank = 0 |
||||||
|
if max_rank < apps.get_app_config('app').nb_challenges: |
||||||
|
challenge = Challenge(equipe=equipe,zone=attributeZone(prevZone)) |
||||||
|
challenge.save() |
||||||
|
return challenge |
||||||
|
else: |
||||||
|
return None |
||||||
|
|
||||||
|
""" |
||||||
|
Fonction attributeZone |
||||||
|
Cette fonction recherche une zone avec l'attribut libre et lui donne l'attribut A |
||||||
|
Si une zone est donnée en paramètre, le zone recherchée sera la zone libre la plus éloignée de celle fournie en paramètre. |
||||||
|
""" |
||||||
|
def attributeZone(zone=None): |
||||||
|
if zone is None: |
||||||
|
max_id = Zone.objects.filter(etat='L').aggregate(max_id=Max("id"))['max_id'] |
||||||
|
if max_id is None:#aucune zone libre disponible |
||||||
|
return None |
||||||
|
print("max_id "+str(max_id)) |
||||||
|
pk = random.randint(1, max_id) |
||||||
|
zone = Zone.objects.get(pk=pk) |
||||||
|
zone.etat = 'A' |
||||||
|
zone.save() |
||||||
|
return zone |
||||||
|
else: |
||||||
|
return None#todo |
||||||
|
|
||||||
|
def startEquipeTime(equipe): |
||||||
|
if equipe.start_time is None: |
||||||
|
equipe.start_time = datetime.now() |
||||||
|
equipe.save() |
||||||
|
|
||||||
|
def stopEquipeTime(equipe): |
||||||
|
if equipe.end_time is None: |
||||||
|
equipe.end_time = datetime.now() |
||||||
|
equipe.save() |
||||||
|
|
||||||
|
def startChallengeTime(challenge): |
||||||
|
if challenge.start_time is None: |
||||||
|
challenge.start_time = datetime.now() |
||||||
|
challenge.save() |
||||||
|
|
||||||
|
def stopChallengeTime(equipe): |
||||||
|
if challenge.end_time is None: |
||||||
|
challenge.end_time = datetime.now() |
||||||
|
challenge.save() |
||||||
@ -0,0 +1,40 @@ |
|||||||
|
# Generated by Django 4.1 on 2022-08-12 12:03 |
||||||
|
|
||||||
|
from django.db import migrations, models |
||||||
|
import django.db.models.deletion |
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration): |
||||||
|
|
||||||
|
dependencies = [ |
||||||
|
('app', '0003_alter_zone_code_alter_distance_unique_together'), |
||||||
|
] |
||||||
|
|
||||||
|
operations = [ |
||||||
|
migrations.CreateModel( |
||||||
|
name='Equipe', |
||||||
|
fields=[ |
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), |
||||||
|
('nom', models.CharField(max_length=50)), |
||||||
|
('code', models.IntegerField(default=3892)), |
||||||
|
('start_time', models.TimeField()), |
||||||
|
('end_time', models.TimeField()), |
||||||
|
], |
||||||
|
), |
||||||
|
migrations.AlterField( |
||||||
|
model_name='zone', |
||||||
|
name='code', |
||||||
|
field=models.IntegerField(default=4254), |
||||||
|
), |
||||||
|
migrations.CreateModel( |
||||||
|
name='Challenge', |
||||||
|
fields=[ |
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), |
||||||
|
('code', models.IntegerField(default=6920)), |
||||||
|
('start_time', models.TimeField()), |
||||||
|
('end_time', models.TimeField()), |
||||||
|
('equipe', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='app.equipe')), |
||||||
|
('zone', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='app.zone')), |
||||||
|
], |
||||||
|
), |
||||||
|
] |
||||||
@ -0,0 +1,29 @@ |
|||||||
|
# Generated by Django 4.1 on 2022-08-22 14:30 |
||||||
|
|
||||||
|
import app.models |
||||||
|
from django.db import migrations, models |
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration): |
||||||
|
|
||||||
|
dependencies = [ |
||||||
|
('app', '0004_equipe_alter_zone_code_challenge'), |
||||||
|
] |
||||||
|
|
||||||
|
operations = [ |
||||||
|
migrations.AlterField( |
||||||
|
model_name='challenge', |
||||||
|
name='code', |
||||||
|
field=models.IntegerField(default=app.models.random_string, unique=True), |
||||||
|
), |
||||||
|
migrations.AlterField( |
||||||
|
model_name='equipe', |
||||||
|
name='code', |
||||||
|
field=models.IntegerField(default=app.models.random_string, unique=True), |
||||||
|
), |
||||||
|
migrations.AlterField( |
||||||
|
model_name='zone', |
||||||
|
name='code', |
||||||
|
field=models.IntegerField(default=3734), |
||||||
|
), |
||||||
|
] |
||||||
@ -0,0 +1,19 @@ |
|||||||
|
# Generated by Django 4.1 on 2022-08-22 14:32 |
||||||
|
|
||||||
|
import app.models |
||||||
|
from django.db import migrations, models |
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration): |
||||||
|
|
||||||
|
dependencies = [ |
||||||
|
('app', '0005_alter_challenge_code_alter_equipe_code_and_more'), |
||||||
|
] |
||||||
|
|
||||||
|
operations = [ |
||||||
|
migrations.AlterField( |
||||||
|
model_name='zone', |
||||||
|
name='code', |
||||||
|
field=models.IntegerField(default=app.models.random_string, unique=True), |
||||||
|
), |
||||||
|
] |
||||||
@ -0,0 +1,33 @@ |
|||||||
|
# Generated by Django 4.1 on 2022-08-23 13:45 |
||||||
|
|
||||||
|
from django.db import migrations, models |
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration): |
||||||
|
|
||||||
|
dependencies = [ |
||||||
|
('app', '0006_alter_zone_code'), |
||||||
|
] |
||||||
|
|
||||||
|
operations = [ |
||||||
|
migrations.AlterField( |
||||||
|
model_name='challenge', |
||||||
|
name='end_time', |
||||||
|
field=models.TimeField(null=True), |
||||||
|
), |
||||||
|
migrations.AlterField( |
||||||
|
model_name='challenge', |
||||||
|
name='start_time', |
||||||
|
field=models.TimeField(null=True), |
||||||
|
), |
||||||
|
migrations.AlterField( |
||||||
|
model_name='equipe', |
||||||
|
name='end_time', |
||||||
|
field=models.TimeField(null=True), |
||||||
|
), |
||||||
|
migrations.AlterField( |
||||||
|
model_name='equipe', |
||||||
|
name='start_time', |
||||||
|
field=models.TimeField(null=True), |
||||||
|
), |
||||||
|
] |
||||||
@ -0,0 +1,33 @@ |
|||||||
|
# Generated by Django 4.1 on 2022-08-23 13:47 |
||||||
|
|
||||||
|
from django.db import migrations, models |
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration): |
||||||
|
|
||||||
|
dependencies = [ |
||||||
|
('app', '0007_alter_challenge_end_time_alter_challenge_start_time_and_more'), |
||||||
|
] |
||||||
|
|
||||||
|
operations = [ |
||||||
|
migrations.AlterField( |
||||||
|
model_name='challenge', |
||||||
|
name='end_time', |
||||||
|
field=models.TimeField(blank=True, null=True), |
||||||
|
), |
||||||
|
migrations.AlterField( |
||||||
|
model_name='challenge', |
||||||
|
name='start_time', |
||||||
|
field=models.TimeField(blank=True, null=True), |
||||||
|
), |
||||||
|
migrations.AlterField( |
||||||
|
model_name='equipe', |
||||||
|
name='end_time', |
||||||
|
field=models.TimeField(blank=True, null=True), |
||||||
|
), |
||||||
|
migrations.AlterField( |
||||||
|
model_name='equipe', |
||||||
|
name='start_time', |
||||||
|
field=models.TimeField(blank=True, null=True), |
||||||
|
), |
||||||
|
] |
||||||
@ -0,0 +1,18 @@ |
|||||||
|
# Generated by Django 4.1 on 2022-08-24 13:43 |
||||||
|
|
||||||
|
from django.db import migrations, models |
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration): |
||||||
|
|
||||||
|
dependencies = [ |
||||||
|
('app', '0008_alter_challenge_end_time_alter_challenge_start_time_and_more'), |
||||||
|
] |
||||||
|
|
||||||
|
operations = [ |
||||||
|
migrations.AddField( |
||||||
|
model_name='challenge', |
||||||
|
name='rank', |
||||||
|
field=models.IntegerField(default=1), |
||||||
|
), |
||||||
|
] |
||||||
@ -0,0 +1,19 @@ |
|||||||
|
# Generated by Django 4.1 on 2022-08-24 13:56 |
||||||
|
|
||||||
|
from django.db import migrations, models |
||||||
|
import django.db.models.deletion |
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration): |
||||||
|
|
||||||
|
dependencies = [ |
||||||
|
('app', '0009_challenge_rank'), |
||||||
|
] |
||||||
|
|
||||||
|
operations = [ |
||||||
|
migrations.AlterField( |
||||||
|
model_name='challenge', |
||||||
|
name='zone', |
||||||
|
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='app.zone'), |
||||||
|
), |
||||||
|
] |
||||||
@ -0,0 +1,19 @@ |
|||||||
|
# Generated by Django 4.1 on 2022-08-24 20:49 |
||||||
|
|
||||||
|
from django.db import migrations, models |
||||||
|
import django.db.models.deletion |
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration): |
||||||
|
|
||||||
|
dependencies = [ |
||||||
|
('app', '0010_alter_challenge_zone'), |
||||||
|
] |
||||||
|
|
||||||
|
operations = [ |
||||||
|
migrations.AlterField( |
||||||
|
model_name='challenge', |
||||||
|
name='zone', |
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='app.zone'), |
||||||
|
), |
||||||
|
] |
||||||
@ -0,0 +1,18 @@ |
|||||||
|
# Generated by Django 4.1 on 2022-09-01 20:56 |
||||||
|
|
||||||
|
from django.db import migrations, models |
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration): |
||||||
|
|
||||||
|
dependencies = [ |
||||||
|
('app', '0011_alter_challenge_zone'), |
||||||
|
] |
||||||
|
|
||||||
|
operations = [ |
||||||
|
migrations.AddField( |
||||||
|
model_name='zone', |
||||||
|
name='description', |
||||||
|
field=models.TextField(default=''), |
||||||
|
), |
||||||
|
] |
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1,110 @@ |
|||||||
|
/* |
||||||
|
* Globals |
||||||
|
*/ |
||||||
|
|
||||||
|
/* Links */ |
||||||
|
a, |
||||||
|
a:focus, |
||||||
|
a:hover { |
||||||
|
color: #fff; |
||||||
|
} |
||||||
|
|
||||||
|
/* Custom default button */ |
||||||
|
.btn-secondary, |
||||||
|
.btn-secondary:hover, |
||||||
|
.btn-secondary:focus { |
||||||
|
color: #333; |
||||||
|
text-shadow: none; /* Prevent inheritance from `body` */ |
||||||
|
background-color: #fff; |
||||||
|
border: .05rem solid #fff; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/* |
||||||
|
* Base structure |
||||||
|
*/ |
||||||
|
|
||||||
|
html, |
||||||
|
body { |
||||||
|
height: 100%; |
||||||
|
background-color: #666; |
||||||
|
} |
||||||
|
|
||||||
|
body { |
||||||
|
display: -ms-flexbox; |
||||||
|
display: -webkit-box; |
||||||
|
display: flex; |
||||||
|
-ms-flex-pack: center; |
||||||
|
-webkit-box-pack: center; |
||||||
|
justify-content: center; |
||||||
|
color: #fff; |
||||||
|
text-shadow: 0 .05rem .1rem rgba(0, 0, 0, .5); |
||||||
|
box-shadow: inset 0 0 5rem rgba(0, 0, 0, .5); |
||||||
|
} |
||||||
|
|
||||||
|
.cover-container { |
||||||
|
max-width: 42em; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/* |
||||||
|
* Header |
||||||
|
*/ |
||||||
|
.masthead { |
||||||
|
margin-bottom: 2rem; |
||||||
|
} |
||||||
|
|
||||||
|
.masthead-brand { |
||||||
|
margin-bottom: 0; |
||||||
|
} |
||||||
|
|
||||||
|
.nav-masthead .nav-link { |
||||||
|
padding: .25rem 0; |
||||||
|
font-weight: 700; |
||||||
|
color: rgba(255, 255, 255, .5); |
||||||
|
background-color: transparent; |
||||||
|
border-bottom: .25rem solid transparent; |
||||||
|
} |
||||||
|
|
||||||
|
.nav-masthead .nav-link:hover, |
||||||
|
.nav-masthead .nav-link:focus { |
||||||
|
border-bottom-color: rgba(255, 255, 255, .25); |
||||||
|
} |
||||||
|
|
||||||
|
.nav-masthead .nav-link + .nav-link { |
||||||
|
margin-left: 1rem; |
||||||
|
} |
||||||
|
|
||||||
|
.nav-masthead .active { |
||||||
|
color: #fff; |
||||||
|
border-bottom-color: #fff; |
||||||
|
} |
||||||
|
|
||||||
|
@media (min-width: 48em) { |
||||||
|
.masthead-brand { |
||||||
|
float: left; |
||||||
|
} |
||||||
|
.nav-masthead { |
||||||
|
float: right; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/* |
||||||
|
* Cover |
||||||
|
*/ |
||||||
|
.cover { |
||||||
|
padding: 0 1.5rem; |
||||||
|
} |
||||||
|
.cover .btn-lg { |
||||||
|
padding: .75rem 1.25rem; |
||||||
|
font-weight: 700; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/* |
||||||
|
* Footer |
||||||
|
*/ |
||||||
|
.mastfoot { |
||||||
|
color: rgba(255, 255, 255, .5); |
||||||
|
} |
||||||
|
After Width: | Height: | Size: 11 KiB |
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1,24 @@ |
|||||||
|
{% extends 'admin_template.html' %}{% block 'body' %} |
||||||
|
<div> |
||||||
|
<h3>Interface de gestion :</h3> |
||||||
|
<div> |
||||||
|
<a href={% url 'app:createNewGame' %}>Créer un nouveau jeu</a> |
||||||
|
<p>{{ info }}</p> |
||||||
|
</div> |
||||||
|
<div> |
||||||
|
<table width="100%" border=1 class="table table-striped w-auto table-responsive-lg"> |
||||||
|
<thead> |
||||||
|
<tr> |
||||||
|
<th scope="col"></th>{% for equipe in liste_equipe %} |
||||||
|
<th scope="col">{{equipe.nom}}</th>{% endfor %} |
||||||
|
</tr> |
||||||
|
</thead> |
||||||
|
<tbody>{% for equipe in liste_equipe %} |
||||||
|
<tr> |
||||||
|
<td scope="col">{{equipe.nom}}</td> |
||||||
|
</tr>{% endfor %} |
||||||
|
</tbody> |
||||||
|
</table> |
||||||
|
</div> |
||||||
|
</form> |
||||||
|
</div>{% endblock %} |
||||||
@ -0,0 +1,18 @@ |
|||||||
|
{% extends 'default_template.html' %}{% block 'contenu' %} |
||||||
|
<div>{% if form %} |
||||||
|
<h3>Bienvenue dans cet EscapeGame !</h3> |
||||||
|
<p>Pour commencer l'aventure,<br/>saisissez le code de votre équipe ! </p> |
||||||
|
<form action="{% url "app:setEquipe" %}" method="POST">{% csrf_token %} |
||||||
|
{{ form }} |
||||||
|
<input type="submit" value="OK"> |
||||||
|
<!--<table> |
||||||
|
{{ form.as_table }} |
||||||
|
<tr> |
||||||
|
<td colspan="2"><input type="submit" value="OK"></td> |
||||||
|
</tr> |
||||||
|
</table>--> |
||||||
|
</form>{% elif zone %} |
||||||
|
<h3>Bienvenue équipe {{equipe_nom}} ! Pour votre prochain challenge, rendez-vous dans la zone {{zone}} |
||||||
|
et flashez le QR code pour connaître votre prochaine mission ! </h3> |
||||||
|
{% endif %} |
||||||
|
</div>{% endblock %} |
||||||
@ -0,0 +1,3 @@ |
|||||||
|
{% extends 'default_template.html' %}{% block 'contenu' %} |
||||||
|
<main role="main" class="inner cover">{{zone.description |safe}} |
||||||
|
</main>{% endblock %} |
||||||
@ -0,0 +1,12 @@ |
|||||||
|
from django.urls import path |
||||||
|
|
||||||
|
from . import views |
||||||
|
|
||||||
|
app_name = 'app' |
||||||
|
urlpatterns = [ |
||||||
|
path('', views.displayZone, name='displayZone'), |
||||||
|
path('<int:zone_id>/', views.displayZone, name='displayZone'), |
||||||
|
path('setEquipe/', views.setEquipe, name='setEquipe'), |
||||||
|
path('dashboard/', views.dashboard, name='dashboard'), |
||||||
|
path('dashboard/createNewGame', views.createNewGame, name='createNewGame'), |
||||||
|
] |
||||||
Loading…
Reference in new issue