Unverified Commit 31a65df0 authored by Julien Palard's avatar Julien Palard
Browse files

Make the hkis app usable alone.

parent 83d783c6
# Using HackInScience
# HackInScience
First step after creating a superuser (using `./manage.py
createsuperuser` is to login to `/admin`, from here you can change
everything about your installation: manage users, create pages, create
exercises, teams, review answers, and so on.
## Setup
First `firejail` and `redis` (from your package manager), like:
apt install firejail redis
Install the dependencies:
python -m pip install requirements.txt
Then run the classical Django things:
./manage.py migrate
./manage.py loaddata initial
./manage.py compilemessages --ignore '.tox' --ignore '.venv'
./manage.py createsuperuser
Then in two distinct terminals, or by detaching one of them:
./manage.py correction_bot
and:
./manage.py runserver
Now you can login to `/admin`, from here you can change everything
about your installation: manage users, create pages, create exercises,
teams, review answers, and so on.
A `/exercises` page, and a `/help` page has been created automatically
for you, you may want to start by adding some exercises to the
`exercises` page.
Alternatively, one can use the `hkis` app alone, in order to
personalize registration, urls, and so on, for this you can install
hkis as a django app in your own project:
pip install django-hkis
## The pages
......@@ -38,29 +69,11 @@ handy around 50~70 exercises.
# How to contribute
## Requirements
This project requires Python 3.6 at least.
## Updating the intial fixture
## Install
To save the initial.json file, use:
```
pip install -r requirements.txt
./manage.py migrate
./manage.py loaddata initial # To get some users and exercises
# (Initial data can be updated using:
./manage.py dumpdata -e contenttypes -e sessions -o hkis/fixtures/initial.json)
./manage.py createsuperuser
./manage.py runserver
```
To also run the correction bot you'll need to install `firejail` and
`redis` then run:
```bash
DJANGO_SETTINGS_MODULE=hackinscience_org.settings celery -A hkis.tasks worker
```
./manage.py dumpdata --indent 4 -e admin -e auth.Permission -e contenttypes -e sessions -o hkis/fixtures/initial.json
## Translations
......@@ -71,7 +84,7 @@ Templates are translated using django `makemessages` and `compilemessages` comma
$ ./manage.py makemessages --locale fr
$ ./manage.py makemessages --locale fr --domain djangojs
# Edit the .po files
$ ./manage.py compilemessages
$ ./manage.py compilemessages --ignore '.tox' --ignore '.venv'
```
Exercises (title and wording) are translated via the admin (or the
......
from django import forms
from django.utils.translation import gettext_lazy
from registration.forms import RegistrationForm
class HkisRegistrationForm(RegistrationForm):
email = forms.EmailField(label=gettext_lazy("E-mail (optional)"), required=False)
field_order = ["username", "password1", "password2", "email"]
from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
from django.core.asgi import get_asgi_application
import hkis.routing
application = ProtocolTypeRouter(
{
"websocket": AuthMiddlewareStack(URLRouter(hkis.routing.websocket_urlpatterns)),
"http": get_asgi_application(),
}
)
......@@ -10,14 +10,14 @@ For the full list of settings and their values, see
https://docs.djangoproject.com/en/2.0/ref/settings/
"""
import os
from pathlib import Path
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/2.0/howto/deployment/checklist/
# See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = "ndoh1n6%d%7i$hm^joz4%0j@ix!8to6p)n0lkse4e1jm$7(adh"
......@@ -105,7 +105,7 @@ ROOT_URLCONF = "hackinscience_org.urls"
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": [os.path.join(BASE_DIR, "templates")],
"DIRS": [BASE_DIR / "templates"],
"APP_DIRS": True,
"OPTIONS": {
"context_processors": [
......@@ -123,6 +123,7 @@ TEMPLATES = [
CRISPY_TEMPLATE_PACK = "bootstrap4"
WSGI_APPLICATION = "hackinscience_org.wsgi.application"
ASGI_APPLICATION = "hackinscience_org.routing.application"
# Database
......@@ -131,7 +132,7 @@ WSGI_APPLICATION = "hackinscience_org.wsgi.application"
DATABASES = {
"default": {
"ENGINE": "django.db.backends.sqlite3",
"NAME": os.path.join(BASE_DIR, "db.sqlite3"),
"NAME": BASE_DIR / "db.sqlite3",
}
}
......@@ -173,7 +174,7 @@ STATIC_URL = "/static/"
# Django registration
ACCOUNT_ACTIVATION_DAYS = 7
REGISTRATION_FORM = "hkis.forms.HkisRegistrationForm"
REGISTRATION_FORM = "hackinscience_org.forms.HkisRegistrationForm"
LOGIN_REDIRECT_URL = "/exercises/"
LOGOUT_REDIRECT_URL = "/"
......@@ -185,8 +186,6 @@ REST_FRAMEWORK = {
"PAGE_SIZE": 100,
}
ASGI_APPLICATION = "hackinscience_org.routing.application"
CHANNEL_LAYERS = {
"default": {
"BACKEND": "channels_redis.core.RedisChannelLayer",
......@@ -196,7 +195,7 @@ CHANNEL_LAYERS = {
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTOCOL", "https")
LOCALE_PATHS = [BASE_DIR + "/locale/"]
LOCALE_PATHS = [BASE_DIR / "locale"]
CELERY_BROKER_URL = "redis://localhost:6379/0"
CELERY_RESULT_BACKEND = CELERY_BROKER_URL
......
......@@ -5,6 +5,7 @@ from django.urls import path, include
urlpatterns = [
path("accounts/", include("registration.backends.simple.urls")),
path("admin/", admin.site.urls),
path("__debug__/", include(debug_toolbar.urls)),
path("", include("hkis.urls")),
......
This diff is collapsed.
from django import forms
from django.utils.translation import gettext_lazy as _
from django_ace import AceWidget
from registration.forms import RegistrationForm
from hkis.models import Answer
......@@ -22,8 +20,3 @@ class AnswerForm(forms.ModelForm):
),
"exercise": forms.HiddenInput(),
}
class HkisRegistrationForm(RegistrationForm):
email = forms.EmailField(label=_("E-mail (optional)"), required=False)
field_order = ["username", "password1", "password2", "email"]
from django.core.management.base import BaseCommand
import subprocess
class Command(BaseCommand):
help = "Run a correction bot process"
def handle(self, *args, **options):
subprocess.run(["celery", "-A", "hkis.tasks", "worker"])
{% extends "registration/registration_base.html" %}
{% load i18n static crispy_forms_tags %}
{% block content %}
{{ form.media }}
<form method="post" action="." class="form">{% csrf_token %}
{{ form|crispy }}
<button type="submit" class="btn btn-primary">{% trans "Submit" %}</button>
</form>
{% endblock %}
......@@ -12,11 +12,14 @@
{% block data %}
{% get_current_language as LANGUAGE_CODE %}
data-language-code="{{ LANGUAGE_CODE }}"
data-profile-url="{% if user.is_anonymous %}{% url "auth_login" %}{% else %}{% url "profile" user.id %}{% endif %}"
data-leaderboard-url="{% url "leaderboard" %}"
data-current-rank="{{ current_rank|default:999999 }}"
data-exercise-id="{{object.id|default:0}}"
data-auth-login-url="{% url "auth_login" %}"
{% url "auth_login" as auth_login %}
{% if auth_login %}
data-profile-url="{% if user.is_anonymous %}{{ auth_login }}{% else %}{% url "profile" user.id %}{% endif %}"
data-auth-login-url="{{ auth_login }}"
{% endif %}
data-is-impersonating="{% if is_impersonating %}true{% else %}false{% endif %}"
{% endblock %}
......
......@@ -31,8 +31,14 @@
Write code, get instant feedback from our correction bot <br/>
with detailed information about what went wrong so you'll never get stuck.{% endblocktrans %}
</br><br/>
<a class="btn btn-secondary" href="{% url 'auth_login' %}" role="button">{% trans "Sign in" %}</a>
<a class="btn btn-primary" href="{% url 'registration_register' %}" role="button">{% trans "Sign up" %}</a>
{% url "auth_login" as auth_login %}
{% if auth_login %}
<a class="btn btn-secondary" href="{{ auth_login }}" role="button">{% trans "Sign in" %}</a>
{% endif %}
{% url "registration_register" as registration_register %}
{% if registration_register %}
<a class="btn btn-primary" href="{{ registration_register }}" role="button">{% trans "Sign up" %}</a>
{% endif %}
<br/>
</p>
</div>
......
......@@ -41,7 +41,10 @@
{% if user.is_authenticated %}
<a class="nav-link" href="{% url 'auth_logout' %}">{% trans "Log out" %}</a>
{% else %}
<a class="nav-link" href="{% url 'auth_login' %}">{% trans "Sign in" %}</a>
{% url "auth_login" as auth_login %}
{% if auth_login %}
<a class="nav-link" href="{% url 'auth_login' %}">{% trans "Sign in" %}</a>
{% endif %}
{% endif %}
</li>
</ul>
......
from django.contrib.auth.models import Permission, User, Group
from django.contrib.contenttypes.models import ContentType
from django.test import TestCase
from rest_framework.test import APITestCase
from hkis.models import User, UserInfo, Exercise, Page, Category
from hkis.models import UserInfo, Exercise, Page, Category, Answer
class TestRankUserWithInfo(TestCase):
......@@ -101,11 +102,28 @@ class TestAdminSuperUser(TestCase):
# self.selenium.find_element_by_xpath('//input[@value="Save"]').click()
def set_teacher_permissions():
teacher = Group.objects.get(name="Teacher")
exercise_content_type = ContentType.objects.get_for_model(Exercise)
answer_content_type = ContentType.objects.get_for_model(Answer)
for content_type, perm in (
(answer_content_type, "view_answer"),
(answer_content_type, "change_answer"),
(exercise_content_type, "add_exercise"),
(exercise_content_type, "change_exercise"),
(exercise_content_type, "view_exercise"),
(exercise_content_type, "delete_exercise"),
):
permission = Permission.objects.get(codename=perm)
teacher.permissions.add(permission)
class TestAdminStaffWithTeacherGroup(TestCase):
fixtures = ["initial"]
def setUp(self):
self.user = User.objects.get(username="Lisa")
set_teacher_permissions()
self.client.force_login(self.user)
def test_get_admin_exercises(self):
......
......@@ -14,7 +14,6 @@ urlpatterns = [
path("teams/<slug:slug>/stats", views.team_stats, name="team_stats"),
path("api/", include(router.urls)),
path("api-auth/", include("rest_framework.urls")),
path("accounts/", include("registration.backends.simple.urls")),
path("profile/<int:pk>", views.ProfileView.as_view(), name="profile"),
path("leaderboard/", views.Leaderboard.as_view(), name="leaderboard"),
path("<slug:page>/", views.PageView.as_view(), name="page"),
......
[build-system]
requires = ["setuptools", "wheel"]
build-backend = "setuptools.build_meta"
[flake8]
ignore = E501
[metadata]
name = django-hkis
version = attr: hkis.__version__
description = Django HackInScience exercises app.
long_description = file: README.md
long_description_content_type = text/markdown; charset=UTF-8
author = Julien Palard
author_email = julien@palard.fr
url = https://framagit.org/hackinscience/hkis-website
license = MIT License
classifiers =
Development Status :: 5 - Production/Stable
License :: OSI Approved :: MIT License
Programming Language :: Python :: 3
[options]
packages = find:
python_requires = >= 3.7
install_requires =
bleach
celery[redis]
channels
channels-redis
django
django-ace
django-crispy-forms
django-extensions
django-filter
django-modeltranslation
django-rest-framework
markdown
pygments
[options.package_data]
hkis = templates/*/*.html, static/*, static/*/*, locale/*/*/*.po, fixtures/*.json
[options.packages.find]
exclude =
example
hackinscience_org
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment