Import: Paris.py #16
|
@ -0,0 +1,350 @@
|
|||
![](static/hkis-home.png)
|
||||
|
||||
::: notes
|
||||
|
||||
Remercier ceux qui nous hébergent.
|
||||
|
||||
# HackInScience
|
||||
|
||||
<!-- .slide: data-background="static/background.jpg" -->
|
||||
|
||||
## Hackinscience
|
||||
|
||||
<br/>
|
||||
|
||||
<b>Julien Palard</b>
|
||||
|
||||
<tt>Paris.Py #16</tt>
|
||||
|
||||
|
||||
## Julien Palard
|
||||
|
||||
- Python core dev / freelance
|
||||
- CPython documentation translator (french only)
|
||||
- Python teacher/coach, at
|
||||
- Makina Corpus
|
||||
- Center for Research and Interdisciplinarity
|
||||
- Sup'Internet
|
||||
- …
|
||||
|
||||
julien@python.org, @sizeof, https://mdk.fr
|
||||
|
||||
|
||||
# HackInScience
|
||||
|
||||
Back in 2015, we started a project with the CRI of Paris Descartes: A
|
||||
one-week intensive Python class. ![](descartes-and-cri.jpg)
|
||||
|
||||
::: notes
|
||||
|
||||
"The Center for Research and Interdisciplinarity" en anglais.
|
||||
|
||||
- Ouvert à tous : élèves de l'université, chercheurs, public.
|
||||
|
||||
|
||||
## Pedagogy
|
||||
|
||||
We were aiming for 50 students per week, so we had to adapt,
|
||||
we needed:
|
||||
|
||||
- A short Python introduction (slides and a teacher).
|
||||
- Lots of exercises, sorted by difficulty.
|
||||
- A bot to correct exercises, live.
|
||||
- 2 to 3 highly available teachers in the room.
|
||||
- Short optional lessons about very specific topics.
|
||||
- Rules, teaching best practices.
|
||||
|
||||
::: notes
|
||||
|
||||
Rules like: never touch a student keyboard: they're here to learn.
|
||||
|
||||
|
||||
## 2015: Focus on content
|
||||
|
||||
We started by focusing on exercises (needed to give a course) and the
|
||||
correction bot (needed to scale).
|
||||
|
||||
Workflow from a student point of view:
|
||||
|
||||
- Clone the exercise repo
|
||||
- Do exercises
|
||||
- git push
|
||||
- Receive a mail with the correction
|
||||
|
||||
::: notes
|
||||
|
||||
It was crude, but it worked.
|
||||
|
||||
We automated exercises corrections (like exercises unit tests) using a
|
||||
Python script.
|
||||
|
||||
|
||||
## 2016: Focus on confort
|
||||
|
||||
Antoine Angot developped a website so students do no longer needed to
|
||||
use git. Emails were no longer needed, which is nice too.
|
||||
|
||||
::: notes
|
||||
|
||||
Thx bro!
|
||||
|
||||
Turns out that teaching git took a lot of time over teaching Python...
|
||||
|
||||
And emails were easily marked as spam even with SPF and DKIM configured properly.
|
||||
|
||||
|
||||
## 2017: Focus on content, again
|
||||
|
||||
The year to enhance existing parts, to rely on them, this is also the
|
||||
year of... many repositories:
|
||||
|
||||
- One for the website
|
||||
- One for the exercises (markdown files)
|
||||
- One for our internal documentation
|
||||
- One for the correction bot
|
||||
- One for the ansible playbook
|
||||
|
||||
::: notes
|
||||
|
||||
Exercises had a check.py and a solution.py
|
||||
|
||||
Internal documentation == teaching best practices.
|
||||
|
||||
|
||||
## 2018: Migrating to Django
|
||||
|
||||
Jérôme Schneider started to migrate the website to Django.<br/>
|
||||
(So we could modify it more easily.)
|
||||
|
||||
|
||||
::: notes
|
||||
|
||||
Thx bro :)
|
||||
A nice part was templates: the syntax was almost the same.
|
||||
|
||||
|
||||
# Show me the code!
|
||||
|
||||
So what is it now? 181 lines of Python (first fully working version), it
|
||||
should fit in a few slides ;)
|
||||
|
||||
::: notes
|
||||
|
||||
Today it's more like 700 lines of code, mainly little tweaks
|
||||
and personalisation.
|
||||
|
||||
|
||||
# Django models
|
||||
|
||||
```python
|
||||
class Exercise(models.Model):
|
||||
title = models.CharField(max_length=255)
|
||||
check = models.TextField()
|
||||
solution = models.TextField()
|
||||
wording = models.TextField()
|
||||
```
|
||||
|
||||
::: notes
|
||||
|
||||
Already know Django? Focus on the content.
|
||||
|
||||
|
||||
## Django models
|
||||
|
||||
Helps in generating:
|
||||
|
||||
- DB migrations;
|
||||
- Admin pages;
|
||||
- Forms;
|
||||
- Views (Using class-based views);
|
||||
- API (Using DRF);
|
||||
- Queries ("ORM" as humans say).
|
||||
|
||||
::: notes
|
||||
|
||||
A Django Model makes the code aware of the database structure.
|
||||
|
||||
- Autocompletion
|
||||
- ...
|
||||
|
||||
|
||||
# Django view
|
||||
|
||||
```python
|
||||
class ExerciseListView(LoginRequiredMixin, ListView):
|
||||
model = Exercise
|
||||
template_name = "hkis/exercises.html"
|
||||
|
||||
```
|
||||
|
||||
::: notes
|
||||
|
||||
How to read this Mixin thing?
|
||||
|
||||
Read: ExerciseListView inherite first from LoginRequiredMixin, then on ListView.
|
||||
So LoginRequiredMixin can place its method before ListView, like intercepting them.
|
||||
|
||||
|
||||
## Django view
|
||||
|
||||
|
||||
![](static/hkis-exercises.png)
|
||||
|
||||
|
||||
## Django view
|
||||
|
||||
```python
|
||||
class ExerciseView(LoginRequiredMixin, DetailView):
|
||||
model = Exercise
|
||||
template_name = "hkis/exercise.html"
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
"""Add student answers.
|
||||
"""
|
||||
context = super().get_context_data(**kwargs)
|
||||
context["answers"] = self.object.answers.filter(
|
||||
user=self.request.user
|
||||
).order_by("-id")
|
||||
return context
|
||||
```
|
||||
|
||||
## Django view
|
||||
|
||||
![](static/hkis-exercise.png)
|
||||
|
||||
|
||||
# Need an API now
|
||||
|
||||
```python
|
||||
class ExerciseSerializer(serializers.HyperlinkedModelSerializer):
|
||||
class Meta:
|
||||
model = Exercise
|
||||
fields = '__all__'
|
||||
|
||||
class ExerciseViewSet(viewsets.ModelViewSet):
|
||||
queryset = Exercise.objects.all()
|
||||
serializer_class = ExerciseSerializer
|
||||
|
||||
router = routers.DefaultRouter()
|
||||
router.register('exercises', ExerciseViewSet)
|
||||
```
|
||||
|
||||
::: notes
|
||||
|
||||
Why DRF use a router? Because there's many views in a ViewSet.
|
||||
|
||||
## Need an API now
|
||||
|
||||
|
||||
![](static/hkis-api.png)
|
||||
|
||||
::: notes
|
||||
|
||||
A nice feature: The API replies with HTML when asked for, so
|
||||
it's browsable using a web browser.
|
||||
|
||||
|
||||
# Need an admin interface now
|
||||
|
||||
```python
|
||||
from django.contrib import admin
|
||||
from website.models import Answer, Exercise
|
||||
|
||||
admin.site.register(Answer)
|
||||
admin.site.register(Exercise)
|
||||
```
|
||||
|
||||
## Admin interface
|
||||
|
||||
![](static/hkis-admin.png)
|
||||
|
||||
## Admin interface
|
||||
|
||||
![](static/hkis-admin-markdown.png)
|
||||
|
||||
::: notes
|
||||
|
||||
We're using markdown to redact exercises.
|
||||
|
||||
## Admin interface
|
||||
|
||||
![](static/hkis-admin-check.png)
|
||||
|
||||
::: notes
|
||||
|
||||
This is the Python script used to check an exercise.
|
||||
|
||||
|
||||
# We need lives updates, now!
|
||||
|
||||
So when a student submit an answer, he gets a correction as soon as possible.
|
||||
|
||||
We can use `Django Channels` for this.
|
||||
|
||||
|
||||
## Django Channels
|
||||
|
||||
Need first to setup some kind of high level router:
|
||||
|
||||
```python
|
||||
application = ProtocolTypeRouter(
|
||||
{"websocket": AuthMiddlewareStack(
|
||||
URLRouter(website.routing.websocket_urlpatterns))}
|
||||
)
|
||||
```
|
||||
|
||||
and
|
||||
|
||||
```python
|
||||
websocket_urlpatterns = [
|
||||
url(
|
||||
r"^ws/answers/(?P<user_id>[0-9]+)/(?P<exercise_id>[0-9]+)/$",
|
||||
consumers.AnswerConsumer,
|
||||
)
|
||||
]
|
||||
```
|
||||
|
||||
## Django Channels
|
||||
|
||||
Note the `ws/` prefix, it's usefull once in production:
|
||||
|
||||
- nginx → gunicorn, for HTTP
|
||||
- nginx → daphne, for websockets
|
||||
|
||||
|
||||
## Django Channels
|
||||
|
||||
|
||||
We're disambiguating it nginx side:
|
||||
|
||||
```
|
||||
location /ws {
|
||||
proxy_pass http://unix:/run/hkis-website/daphne.sock;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "Upgrade";
|
||||
}
|
||||
|
||||
location / {
|
||||
proxy_pass http://unix:/run/hkis-website/website.sock;
|
||||
proxy_set_header Host $host;
|
||||
}
|
||||
```
|
||||
|
||||
# The future
|
||||
|
||||
Display exercises as a tree, with subjects like datascience, web,
|
||||
low-level, and so on as group of leafs of the tree.
|
||||
|
||||
::: notes
|
||||
|
||||
So one could navigate to its preferred subject.
|
||||
|
||||
|
||||
# Questions?
|
||||
|
||||
- julien@python.org
|
||||
- Twitter: @sizeof
|
||||
- IRC: mdk on #python-fr (freenode)
|
||||
- https://mdk.fr
|
||||
- https://hackinscience.org
|
After Width: | Height: | Size: 73 KiB |
After Width: | Height: | Size: 49 KiB |
After Width: | Height: | Size: 36 KiB |
After Width: | Height: | Size: 30 KiB |
After Width: | Height: | Size: 65 KiB |
After Width: | Height: | Size: 74 KiB |
After Width: | Height: | Size: 24 KiB |
After Width: | Height: | Size: 769 KiB |
After Width: | Height: | Size: 56 KiB |