351 lines
6.5 KiB
Markdown
351 lines
6.5 KiB
Markdown
![](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
|