Functional

This commit is contained in:
Antoine Martin 2022-05-13 05:11:35 +02:00
parent 7a053983a7
commit c924918387
15 changed files with 281 additions and 19 deletions

View file

@ -104,9 +104,9 @@ AUTH_PASSWORD_VALIDATORS = [
# Internationalization # Internationalization
# https://docs.djangoproject.com/en/4.0/topics/i18n/ # https://docs.djangoproject.com/en/4.0/topics/i18n/
LANGUAGE_CODE = 'en-us' LANGUAGE_CODE = 'fr-fr'
TIME_ZONE = 'UTC' TIME_ZONE = 'Europe/Paris'
USE_I18N = True USE_I18N = True

View file

@ -1,8 +1,25 @@
import re
from django import forms from django import forms
from . import models from . import models
SCRATCH_ID_REGEX = re.compile(r'https://scratch\.mit\.edu/projects/([0-9]+)')
def get_scratch_id_from_url(url):
match = SCRATCH_ID_REGEX.search(url)
return int(match.group(1))
class ScratchURLField(forms.URLField):
def clean(self, value):
url = super().clean(value)
scratch_id = get_scratch_id_from_url(url)
return scratch_id
class ScratchProjectAddForm(forms.ModelForm): class ScratchProjectAddForm(forms.ModelForm):
url = forms.URLField(label='Lien Scratch du projet (URL)') url = ScratchURLField(label='Lien Scratch du projet (URL)')
class Meta: class Meta:
fields = ('name', 'author_name', 'url') fields = ('name', 'author_name', 'url')

View file

@ -1,6 +1,7 @@
# Generated by Django 4.0.4 on 2022-05-13 00:55 # Generated by Django 4.0.4 on 2022-05-13 05:27
import datetime import datetime
import django.core.validators
from django.db import migrations, models from django.db import migrations, models
import django.db.models.deletion import django.db.models.deletion
@ -28,7 +29,7 @@ class Migration(migrations.Migration):
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=128, verbose_name='Nom du projet')), ('name', models.CharField(max_length=128, verbose_name='Nom du projet')),
('author_name', models.CharField(max_length=128, verbose_name='Auteur')), ('author_name', models.CharField(max_length=128, verbose_name='Auteur')),
('project_id', models.PositiveBigIntegerField(unique=True, verbose_name='Scratch project id')), ('project_url', models.URLField(validators=[django.core.validators.RegexValidator(regex='https?://scratch\\.mit\\.edu/projects/([0-9]+)/?')], verbose_name='Scratch project url')),
('event', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='scratch_show.event')), ('event', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='scratch_show.event')),
], ],
), ),

View file

@ -2,6 +2,7 @@ from datetime import date
from django.db import models from django.db import models
from django.urls import reverse from django.urls import reverse
from django.core.validators import RegexValidator
class Event(models.Model): class Event(models.Model):
name = models.CharField("Nom de l'évènement", max_length=128) name = models.CharField("Nom de l'évènement", max_length=128)
@ -12,7 +13,7 @@ class Event(models.Model):
class ScratchProject(models.Model): class ScratchProject(models.Model):
name = models.CharField('Nom du projet', max_length=128) name = models.CharField('Nom du projet', max_length=128)
author_name = models.CharField('Auteur', max_length=128) author_name = models.CharField('Auteur', max_length=128)
project_id = models.PositiveBigIntegerField('Scratch project id', unique=True) project_url = models.URLField('Scratch project url', validators=[RegexValidator(regex=r'https?://scratch\.mit\.edu/projects/([0-9]+)/?')])
event = models.ForeignKey(Event, on_delete=models.CASCADE) event = models.ForeignKey(Event, on_delete=models.CASCADE)
def get_absolute_url(self): def get_absolute_url(self):

View file

@ -0,0 +1,155 @@
@font-face {
font-family: "Helvetica Neue";
src: url("fonts/helveticaneue.woff2") format("woff2");
}
@font-face {
font-family: "Helvetica";
src: url("fonts/helvetica.woff2") format("woff2");
}
h1, h2, h3, h4, h5, h6 {
font-family: "Helvetica Neue", sans-serif;
}
h1 {
font-size: 2em;
margin-bottom: 1em;
}
:root {
--text-color: #282828;
--bg-color: #f9f9f9;
}
body {
font-family: "Helvetica", sans-serif;
font-size: 1.3em;
margin: 0;
padding: 0;
color: var(--text-color);
background-color: var(--bg-color);
}
header.site-header {
background-color: #35479A;
text-align: center;
color: var(--bg-color);
padding: 0.4em 1em;
display: flex;
align-items: center;
height: 3.4em;
}
header.site-header img {
width: 56px;
margin-right: 1em;
}
.container {
margin-left: auto;
margin-right: auto;
width: 868px;
}
h1.site-title {
margin: 0;
font-size: 1.4em;
}
h1.article-title {
margin: 0;
font-size: 3em;
}
.article-subtitle {
color: #575757;
font-weight: 500;
}
.article-title-box {
height: 15em;
text-align: center;
display: flex;
align-items: center;
flex-direction: column;
justify-content: center;
background-color: #ededed;
margin-bottom: 4em;
}
.article-container {
display: flex;
align-items: center;
align-content: flex-start;
justify-content: flex-start;
}
a.code-stub-button {
text-decoration: none;
border-radius: 6px;
background-color: #35479a;
color: white;
padding: 0.5em 0.8em;
font-weight: 600;
display: inline-block;
text-align: center;
line-height: 28px;
}
.home-title-box {
background-color: #222;
color: whitesmoke;
text-align: center;
padding: 2em 0;
}
.site-header-title {
margin: 0;
font-size: 5em;
}
.site-header-subtitle {
color: #ccc;
}
.article-list {
list-style-type: none;
max-width: 690px;
margin-left: auto;
margin-right: auto;
padding: 0 1em;
}
.article-item {
display: block;
background: lightgray;
margin: 1em 0;
padding: 1em;
}
.article-item h2 {
font-size: 1.2em;
margin: 0;
}
.article-item a {
text-decoration: none;
color: inherit;
display: block;
padding: 0;
}
img {
max-width: 100%;
}
.header-home-link {
display: flex;
text-decoration: none;
align-items: center;
}
.header-home-link:visited {
color: inherit;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

View file

@ -0,0 +1,27 @@
{% load static %}
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="{% static 'scratch_show/base.css' %}" />
<link rel="icon" type="image/png" href="{% static 'scratch_show/favicon.png' %}" />
<title>Ateliers Scratch by Prologin</title>
</head>
<body>
<header class="site-header">
<a href="/" class="header-home-link">
<img src="https://prologin.org/static/img/logo_cube.png" alt="Prologin Cube Logo">
<h1 class="site-title">Ateliers Scratch by Prologin</h1>
</a>
</header>
<main>
<div class="container">
{% block content %}
{% endblock %}
</div>
</main>
</body>
</html>

View file

@ -0,0 +1,15 @@
{% extends 'scratch_show/base.html' %}
{% block content %}
<h1>{{ event.name }}, le {{ event.date|date }}</h1>
<ul>
{% for project in projects %}
<li><a href="{% url 'scratch_show:project-detail' project.pk %}">{{ project.name }}</a> par {{ project.author_name }}</li>
{% empty %}
<li>Aucun projet pour le moment !</li>
{% endfor %}
</ul>
{% endblock %}

View file

@ -0,0 +1,21 @@
{% extends 'scratch_show/base.html' %}
{% block content %}
{% if current_event %}
<h2>Atelier en cours: <a href="{% url 'scratch_show:event-detail' current_event.pk %}">{{ current_event.name }}</a></h2>
<b><a href="{% url 'scratch_show:project-add' %}">Clique ici pour ajouter ton projet !</a></b>
{% endif %}
<h2>Ateliers passés</h2>
<ul>
{% for event in past_events %}
<li><a href="{% url 'scratch_show:event-detail' event.pk %}">{{ event.name }}</a> le {{ event.date|date}}</li>
{% empty %}
<li>Pas d'ancien atelier pour le moment.</li>
{% endfor %}
</ul>
{% endblock %}

View file

@ -1,4 +1,10 @@
{% extends 'scratch_show/base.html' %}
{% block content %}
<h1>{{ object.name }}</h1> <h1>{{ object.name }}</h1>
<p>Créateur : {{ object.author_name }}</p> <p>Créateur : {{ object.author_name }}</p>
<iframe src="https://scratch.mit.edu/projects/{{ object.project_id }}/embed" allowtransparency="true" width="485" height="402" frameborder="0" scrolling="no" allowfullscreen></iframe> <iframe src="{{ object.project_url }}/embed" allowtransparency="true" width="485" height="402" frameborder="0" scrolling="no" allowfullscreen></iframe>
{% endblock %}

View file

@ -1,4 +1,11 @@
{% extends 'scratch_show/base.html' %}
{% block content %}
<form method="post">{% csrf_token %} <form method="post">{% csrf_token %}
{{ form.as_p }} {{ form.as_p }}
<input type="submit" value="Save"> <input type="submit" value="Save">
</form> </form>
{% endblock %}

View file

@ -4,6 +4,8 @@ from . import views
app_name = 'scratch_show' app_name = 'scratch_show'
urlpatterns = [ urlpatterns = [
path('', views.IndexView.as_view(), name='index'),
path('add', views.ScratchProjectAddView.as_view(), name='project-add'), path('add', views.ScratchProjectAddView.as_view(), name='project-add'),
path('project/<int:pk>', views.ScratchProjectDetailView.as_view(), name='project-detail'), path('project/<int:pk>/', views.ScratchProjectDetailView.as_view(), name='project-detail'),
path('event/<int:pk>/', views.EventDetailView.as_view(), name='event-detail'),
] ]

View file

@ -1,34 +1,44 @@
import re
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
from django.utils import timezone from django.utils import timezone
from django.views.generic.edit import CreateView from django.views.generic.base import TemplateView
from django.views.generic.detail import DetailView from django.views.generic.detail import DetailView
from django.views.generic.edit import CreateView
from django.core.exceptions import ValidationError
from . import forms, models from . import forms, models
SCRATCH_ID_REGEX = re.compile(r'https://scratch\.mit\.edu/projects/([0-9]+)') class IndexView(TemplateView):
template_name = "scratch_show/index.html"
def get_scratch_id_from_url(url): def get_context_data(self, **kwargs):
match = SCRATCH_ID_REGEX.search(url) context = super().get_context_data(**kwargs)
return int(match.group(1)) today = timezone.now().date()
context['current_event'] = models.Event.objects.filter(date=today).first()
context['past_events'] = models.Event.objects.exclude(date=today).order_by('-date')
return context
class ScratchProjectAddView(CreateView): class ScratchProjectAddView(CreateView):
form_class = forms.ScratchProjectAddForm model = models.ScratchProject
template_name = 'scratch_show/scratchproject_form.html' fields = ['name', 'author_name', 'project_url']
def dispatch(self, request, *args, **kwargs): def dispatch(self, request, *args, **kwargs):
self.event = get_object_or_404(models.Event, date=timezone.now().date(), accept_projects=True) self.event = get_object_or_404(models.Event, date=timezone.now().date(), accept_projects=True)
return super().dispatch(request, *args, **kwargs) return super().dispatch(request, *args, **kwargs)
def form_valid(self, form): def form_valid(self, form):
url = form.cleaned_data['url']
form.instance.project_id = get_scratch_id_from_url(url)
form.instance.event = self.event form.instance.event = self.event
return super().form_valid(form) return super().form_valid(form)
class ScratchProjectDetailView(DetailView): class ScratchProjectDetailView(DetailView):
model = models.ScratchProject model = models.ScratchProject
class EventDetailView(DetailView):
model = models.Event
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['projects'] = models.ScratchProject.objects.filter(event=context['object'].pk)
return context