Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • hansegucker/AlekSIS-Core
  • pinguin/AlekSIS-Core
  • AlekSIS/official/AlekSIS-Core
  • sunweaver/AlekSIS-Core
  • sggua/AlekSIS-Core
  • edward/AlekSIS-Core
  • magicfelix/AlekSIS-Core
7 results
Show changes
Commits on Source (77)
Showing
with 247 additions and 83 deletions
stages:
- test
- build
- test
- deploy
variables:
GIT_SUBMODULE_STRATEGY: recursive
PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
POSTGRESQL_USER: biscuit
POSTGRESQL_DB: biscuit
BISCUIT_http__allowed_hosts: "['*']"
BISCUIT_caching__memcached__address: memcached:11211
BISCUIT_database__host: db
test:
stage: test
cache:
key:
files:
- poetry.lock
paths:
- .cache/pip
build_wheel:
stage: build
image:
name: python:3.8-buster
before_script:
- apt-get -y update && apt-get -y install postgresql libpq5 libpq-dev libssl-dev sudo
- pip install poetry
- adduser --disabled-password --gecos "Test User" testuser
name: registry.edugit.org/teckids/docker-images/python-pimped:master
script:
- sudo -u testuser poetry install
- sudo -u testuser poetry run tox
- poetry build
artifacts:
paths:
- dist/
build_docker:
stage: build
......@@ -39,6 +50,47 @@ build_docker:
--cleanup
only:
- master
- tags
test_wheel:
stage: test
image:
name: registry.edugit.org/teckids/docker-images/python-pimped:master
services:
- selenium/hub
- selenium/node-chrome
- selenium/node-firefox
before_script:
- adduser --disabled-password --gecos "Test User" testuser
- mkdir -p screenshots && chown testuser screenshots
script:
- poetry export --without-hashes --dev -f requirements.txt | pip install -r /dev/stdin
- eatmydata pip install dist/*.whl
- python ./manage.py compilemessages
- eatmydata python ./manage.py yarn install
- python ./manage.py collectstatic --no-input --clear
- sudo -u testuser eatmydata env TEST_SCREENSHOT_PATH=./screenshots tox
- pip freeze | safety check --stdin --full-report
artifacts:
paths:
- screenshots/
test_docker:
stage: test
image:
name: registry.edugit.org/teckids/docker-images/python-pimped:master
services:
- name: postgres:12
alias: db
- name: memcached
alias: memcached
- name: registry.edugit.org/biscuit/biscuit-ng:${CI_COMMIT_REF_NAME}
alias: app
script:
- echo true
only:
- master
- tags
deploy_demo-master:
stage: deploy
......@@ -65,6 +117,7 @@ deploy_demo-master:
- grep -v "build:" docker-compose.yml | ssh root@demo-master.biscuit-sis.org
env BISCUIT_IMAGE_TAG=${CI_COMMIT_REF_NAME}
NGINX_HTTP_PORT=80
BISCUIT_maintenance__debug=true
docker-compose
-p biscuit-${CI_ENVIRONMENT_SLUG}
-f /dev/stdin
......@@ -75,13 +128,10 @@ deploy_demo-master:
pages:
stage: deploy
image:
name: python:3.8-buster
before_script:
- apt-get -y update && apt-get -y install make
- pip install poetry
name: registry.edugit.org/teckids/docker-images/python-pimped:master
script:
- poetry install
- poetry run make -C docs html BUILDDIR=../public/docs
- poetry export --without-hashes --dev -f requirements.txt | eatmydata pip install -r /dev/stdin
- make -C docs html BUILDDIR=../public/docs
artifacts:
paths:
- public/
......
......@@ -82,6 +82,26 @@ giving the user control over these decisions is not possible. Developers
need to decide what should resonably be followed.
The case on supporting non-free services
----------------------------------------
Defined by the `Free Software Definition`_, it is an essential freedom to
be allowed to use free software for any purpose, without limitation. Thus,
interoperability with non-free services shall not be ruled out, and the
BiscuIT project explicitly welcomes implementing support for
interoperability with non-free services.
However, to purposefullt foster free software and services, if
interoperability for a certain kind of non-free service is implemented, this
must be done in a generalised manner (i.e. using open protocols and
interfaces). For example, if implementing interoperability with some
cloud-hosted calendar provider can be implemented either through a
proprietary API, or through a standard iCalendar/Webcal interfaces, the
latter is to be preferred. Lacking such support, if a proprietary service
is connected through a proprietary, single-purpose interface, measures shall
be taken to also support alternative free services.
Text documents
--------------
......@@ -105,4 +125,5 @@ licence possible.
.. _Sane software manifesto: https://sane-software.globalcode.info/
.. _Accessibility Manifesto: http://accessibilitymanifesto.com/
.. _User Data Manifesto: https://userdatamanifesto.org/
.. _Free Software Definition: https://www.gnu.org/philosophy/free-sw.en.html
.. _reStructuredText: http://docutils.sourceforge.net/rst.html
......@@ -16,9 +16,10 @@ ENV BISCUIT_backup__location /var/lib/biscuit/backups
ENV POETRY_VERSION 1.0.0b3
# Install necessary Debian packages for build and runtime
RUN apt-get update && \
apt-get upgrade -y && \
apt-get install -y --no-install-recommends \
RUN apt-get -y update && \
apt-get -y install eatmydata && \
eatmydata apt-get -y upgrade && \
eatmydata apt-get install -y --no-install-recommends \
build-essential \
gettext \
libpq5 \
......@@ -30,35 +31,32 @@ RUN apt-get update && \
# Install core dependnecies
WORKDIR /usr/src/app
COPY poetry.lock pyproject.toml ./
RUN pip install "poetry==$POETRY_VERSION"; \
poetry export -f requirements.txt | pip install -r /dev/stdin; \
pip install gunicorn
RUN eatmydata pip install "poetry==$POETRY_VERSION"; \
poetry export -f requirements.txt | eatmydata pip install -r /dev/stdin; \
eatmydata pip install gunicorn
# Install core
COPY biscuit ./biscuit/
COPY LICENCE README.rst manage.py ./
RUN mkdir -p /var/lib/biscuit/media /var/lib/biscuit/static /var/lib/biscuit/backups; \
poetry build && pip install dist/*.whl
poetry build && eatmydata pip install dist/*.whl
# Build messages and assets
RUN python manage.py compilemessages; \
python manage.py yarn install; \
python manage.py collectstatic --no-input --clear
eatmydata python manage.py yarn install
# Clean up build dependencies
RUN apt-get remove --purge -y \
RUN eatmydata apt-get remove --purge -y \
build-essential \
gettext \
libpq-dev \
libssl-dev \
yarnpkg; \
apt-get autoremove --purge -y; \
eatmydata apt-get autoremove --purge -y; \
apt-get clean -y; \
pip uninstall -y poetry; \
eatmydata pip uninstall -y poetry; \
rm -f /var/lib/apt/lists/*_*; \
rm -rf /root/.cache; \
rm -rf biscuit/node_modules; \
rm -rf /usr/local/lib/node_modules
rm -rf /root/.cache
# Declare a persistent volume for all data
VOLUME /var/lib/biscuit
......
Subproject commit 9014661e518c6d9457723571d7b0ef1ac9be6c6b
Subproject commit 874b76b40b9b7e05d8934559587009cd545fdd40
Subproject commit d4da734afa405a6af2adbe607f553e2b759737d6
Subproject commit 296d95e813b94c756e378265dfaac68fbafe34c1
Subproject commit b03369df8214f062a18a70824c6a257cf4ab427d
Subproject commit 15b1e50d32f39ed59ed73357c4e107cc94761bac
......@@ -96,16 +96,6 @@ MENUS = {
}
]
},
{
'name': _('Support'),
'url': '#',
'submenu': [
{
'name': _('Get support'),
'url': 'contact_form'
}
]
}
],
'DATA_MANAGEMENT_MENU': [
],
......
# Generated by Django 2.2.8 on 2019-12-09 21:04
from django.contrib.auth import get_user_model
from django.db import migrations
def create_superuser(apps, schema_editor):
User = get_user_model()
if not User.objects.filter(is_superuser=True).exists():
User.objects.create_superuser(
username='admin',
email='root@example.com',
password='admin'
).save()
class Migration(migrations.Migration):
dependencies = [
('core', '0005_unlink_school'),
]
operations = [
migrations.RunPython(create_superuser)
]
# Generated by Django 2.2.8 on 2019-12-11 23:27
from django.db import migrations, models
def mark_current_term(apps, schema_editor):
db_alias = schema_editor.connection.alias
SchoolTerm = apps.get_model('core', 'SchoolTerm') # noqa
if not SchoolTerm.objects.filter(current=True).exists():
SchoolTerm.objects.using(db_alias).latest('date_start').update(current=True)
class Migration(migrations.Migration):
dependencies = [
('core', '0006_create_superuser'),
]
operations = [
migrations.RemoveField(
model_name='school',
name='current_term',
),
migrations.AddField(
model_name='schoolterm',
name='current',
field=models.NullBooleanField(default=None, unique=True),
),
]
......@@ -38,7 +38,9 @@ class School(models.Model):
logo = ImageCropField(verbose_name=_('School logo'), blank=True, null=True)
logo_cropping = ImageRatioField('logo', '600x600', size_warning=True)
current_term = models.ForeignKey('SchoolTerm', models.CASCADE, related_name='+')
@property
def current_term(self):
return SchoolTerm.objects.get(current=True)
class Meta:
ordering = ['name', 'name_official']
......@@ -57,6 +59,13 @@ class SchoolTerm(models.Model):
date_end = models.DateField(verbose_name=_(
'Effective end date of term'), null=True)
current = models.NullBooleanField(default=None, unique=True)
def save(self, *args, **kwargs):
if self.current is False:
self.current = None
super().save(*args, **kwargs)
class Person(models.Model, ExtensibleModel):
""" A model describing any person related to a school, including, but not
......
......@@ -34,7 +34,9 @@ DEBUG = _settings.get('maintenance.debug', False)
INTERNAL_IPS = _settings.get('maintenance.internal_ips', [])
DEBUG_TOOLBAR_CONFIG = {
'RENDER_PANELS': True,
'SHOW_COLLAPSED': True
'SHOW_COLLAPSED': True,
'JQUERY_URL': '',
'SHOW_TOOLBAR_CALLBACK': 'biscuit.core.util.core_helpers.dt_show_toolbar'
}
ALLOWED_HOSTS = _settings.get('http.allowed_hosts', [])
......@@ -65,7 +67,6 @@ INSTALLED_APPS = [
'menu_generator',
'phonenumber_field',
'debug_toolbar',
'contact_form',
'django_select2',
'hattori',
'django_otp.plugins.otp_totp',
......@@ -89,7 +90,6 @@ STATICFILES_FINDERS = [
MIDDLEWARE = [
# 'django.middleware.cache.UpdateCacheMiddleware',
'debug_toolbar.middleware.DebugToolbarMiddleware',
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.locale.LocaleMiddleware',
......@@ -97,6 +97,7 @@ MIDDLEWARE = [
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'debug_toolbar.middleware.DebugToolbarMiddleware',
'django_otp.middleware.OTPMiddleware',
'impersonate.middleware.ImpersonateMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
......
{# -*- engine:django -*- #}
{% extends "core/base.html" %}
{% load bootstrap4 i18n %}
{% block page_title %}BiscuIT SIS support{% endblock %}
{% block content %}
<h1>{% blocktrans %}Get support{% endblocktrans %}</h1>
<form method="post">
{% csrf_token %}
{% bootstrap_form form %}
<button type="submit" class="btn btn-dark">
{% blocktrans %}Send{% endblocktrans %}
</button>
</form>
{% endblock %}
From: {{ name }} <{{ email }}>
{{ body }}
{# -*- engine:django -*- #}
{% extends "core/base.html" %}
{% load bootstrap4 i18n %}
{% block page_title %}BiscuIT School Information System (SIS){% endblock %}
{% block content %}
<div class="alert alert-success" role="alert">
{% blocktrans %}
The message was successfully submitted.
{% endblocktrans %}
</div>
{% endblock %}
[BiscuIT Support]
import os
import pytest
from django.conf import settings
from django.test.selenium import SeleniumTestCase, SeleniumTestCaseBase
from django.urls import reverse
SeleniumTestCaseBase.external_host = os.environ.get('TEST_HOST', '') or None
SeleniumTestCaseBase.browsers = list(filter(bool, os.environ.get('TEST_SELENIUM_BROWSERS', '').split(',')))
SeleniumTestCaseBase.selenium_hub = os.environ.get('TEST_SELENIUM_HUB', '') or None
class SeleniumTests(SeleniumTestCase):
serialized_rollback = True
@classmethod
def _screenshot(cls, filename):
screenshot_path = os.environ.get('TEST_SCREENSHOT_PATH', None)
if screenshot_path:
os.makedirs(os.path.join(screenshot_path, cls.browser), exist_ok=True)
return cls.selenium.save_screenshot(os.path.join(screenshot_path, cls.browser, filename))
else:
return False
@pytest.mark.django_db
def test_index(self):
self.selenium.get(self.live_server_url + '/')
assert 'BiscuIT' in self.selenium.title
self._screenshot('index.png')
@pytest.mark.django_db
def test_login_default_superuser(self):
username = 'admin'
password = 'admin'
# Navigate to configured login page
self.selenium.get(self.live_server_url + reverse(settings.LOGIN_URL))
self._screenshot('login_default_superuser_blank.png')
# Find login form input fields and enter defined credentials
self.selenium.find_element_by_xpath(
'//label[contains(text(), "Username")]/../input'
).send_keys(username)
self.selenium.find_element_by_xpath(
'//label[contains(text(), "Password")]/../input'
).send_keys(password)
self._screenshot('login_default_superuser_filled.png')
# Submit form by clicking django-two-factor-auth's Next button
self.selenium.find_element_by_xpath(
'//button[contains(text(), "Next")]'
).click()
self._screenshot('login_default_superuser_submitted.png')
# Should redirect away from login page and not put up an alert about wrong credentials
assert 'Please enter a correct username and password.' not in self.selenium.page_source
assert reverse(settings.LOGIN_URL) not in self.selenium.current_url
......@@ -34,7 +34,6 @@ urlpatterns = [
path('group/<int:id_>/edit', views.edit_group, name='edit_group_by_id'),
path('', views.index, name='index'),
path('maintenance-mode/', include('maintenance_mode.urls')),
path('contact/', include('contact_form.urls')),
path('impersonate/', include('impersonate.urls')),
path('__i18n__/', include('django.conf.urls.i18n')),
path('select2/', include('django_select2.urls')),
......
......@@ -2,9 +2,23 @@ from importlib import import_module
import pkgutil
from typing import Sequence
from django.conf import settings
from django.http import HttpRequest
def dt_show_toolbar(request: HttpRequest) -> bool:
from debug_toolbar.middleware import show_toolbar # noqa
if not settings.DEBUG:
return False
if show_toolbar(request):
return True
elif hasattr(request, 'user') and request.user.is_superuser:
return True
return False
def get_app_packages() -> Sequence[str]:
""" Find all packages within the biscuit.apps namespace. """
......
......@@ -19,6 +19,7 @@ services:
- BISCUIT_http__allowed_hosts="['*']"
- BISCUIT_caching__memcached__address=memcached:11211
- BISCUIT_database__host=db
- BISCUIT_maintenance__debug=${BISCUIT_maintenance__debug:-false}
depends_on:
- db
- memcached
......
......@@ -19,6 +19,7 @@ done
python manage.py flush --no-input
python manage.py migrate
python manage.py collectstatic --no-input --clear
if [[ -n "$@" ]]; then
exec "$@"
......