Skip to content

Commit 4284dbf

Browse files
authored
Merge branch 'dev' into 0.10.11
2 parents 74cad61 + 2c15041 commit 4284dbf

File tree

13 files changed

+407
-2
lines changed

13 files changed

+407
-2
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ celerybeat.pid
101101
*.sage.py
102102

103103
# Environments
104-
.env
104+
docker/.env
105105
.venv
106106
env/
107107
venv/

Dockerfile

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
FROM python:3.11-slim
2+
3+
RUN apt-get update && \
4+
apt-get install -y --no-install-recommends \
5+
libjpeg-dev \
6+
libz-dev \
7+
libfreetype6-dev \
8+
liblcms2-dev \
9+
libopenjp2-7-dev \
10+
libtiff5-dev \
11+
build-essential \
12+
libmagic1 \
13+
libwebp-dev && \
14+
rm -rf /var/lib/apt/lists/*
15+
16+
ENV PYTHONUNBUFFERED 1
17+
ENV PYTHONDONTWRITEBYTECODE 1
18+
19+
WORKDIR /osis-document
20+
21+
RUN pip install --upgrade pip && \
22+
pip install python-magic
23+
24+
COPY ./docker/server/requirements.txt ./requirements.txt
25+
RUN pip install -r ./requirements.txt
26+
27+
COPY ./docker/server/django-server-entrypoint.sh ./django-server-entrypoint.sh
28+
COPY ./docker/server/manage.py ./manage.py
29+
COPY ./docker/server/document ./document
30+
COPY ./osis_document /osis-document/osis_document
31+
32+
RUN chmod +x ./django-server-entrypoint.sh && \
33+
rm -rf ~/.cache/pip
34+
35+
EXPOSE 9503
36+
ENTRYPOINT ["./django-server-entrypoint.sh"]

docker/.env.example

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# General settings
2+
#ALLOWED_HOSTS=*
3+
4+
## Database settings
5+
#DATABASE_NAME=osis_document_local
6+
#POSTGRES_USER=osis
7+
#POSTGRES_PASSWORD=osis
8+
#POSTGRES_HOST=127.0.0.1
9+
#POSTGRES_PORT=5432
10+
#DATABASE_ATOMIC_REQUEST=False
11+
12+
## Queues settings
13+
#RABBITMQ_HOST='broker'
14+
#RABBITMQ_USER='guest'
15+
#RABBITMQ_PASSWORD='guest'
16+
#RABBITMQ_PORT=5672
17+
#RABBITMQ_CONTEXT_ROOT='/'
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#!/bin/bash
2+
3+
while true; do
4+
echo "[OSIS-Document] Re-starting Django runserver"
5+
python manage.py migrate
6+
python manage.py runserver 0.0.0.0:9503
7+
sleep 2
8+
done

docker/server/document/__init__.py

Whitespace-only changes.

docker/server/document/asgi.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
"""
2+
ASGI config for document project.
3+
4+
It exposes the ASGI callable as a module-level variable named ``application``.
5+
6+
For more information on this file, see
7+
https://docs.djangoproject.com/en/3.2/howto/deployment/asgi/
8+
"""
9+
10+
import os
11+
12+
from django.core.asgi import get_asgi_application
13+
14+
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'document.settings')
15+
16+
application = get_asgi_application()
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
##############################################################################
2+
#
3+
# OSIS stands for Open Student Information System. It's an application
4+
# designed to manage the core business of higher education institutions,
5+
# such as universities, faculties, institutes and professional schools.
6+
# The core business involves the administration of students, teachers,
7+
# courses, programs and so on.
8+
#
9+
# Copyright (C) 2015-2025 Université catholique de Louvain (http://www.uclouvain.be)
10+
#
11+
# This program is free software: you can redistribute it and/or modify
12+
# it under the terms of the GNU General Public License as published by
13+
# the Free Software Foundation, either version 3 of the License, or
14+
# (at your option) any later version.
15+
#
16+
# This program is distributed in the hope that it will be useful,
17+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
18+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19+
# GNU General Public License for more details.
20+
#
21+
# A copy of this license - GNU General Public License - is available
22+
# at the root of the source code of this program. If not,
23+
# see http://www.gnu.org/licenses/.
24+
#
25+
##############################################################################
26+
from django.conf import settings
27+
from opentelemetry import trace
28+
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
29+
from opentelemetry.instrumentation.celery import CeleryInstrumentor
30+
from opentelemetry.instrumentation.django import DjangoInstrumentor
31+
from opentelemetry.instrumentation.psycopg2 import Psycopg2Instrumentor
32+
from opentelemetry.instrumentation.requests import RequestsInstrumentor
33+
from opentelemetry.instrumentation.urllib3 import URLLib3Instrumentor
34+
from opentelemetry.sdk.resources import Resource
35+
from opentelemetry.sdk.trace import TracerProvider
36+
from opentelemetry.sdk.trace.export import BatchSpanProcessor
37+
from opentelemetry.trace import Tracer
38+
39+
40+
def initialize():
41+
resource = Resource(attributes={
42+
"service.name": getattr(settings, 'OTEL_SERVICE_NAME', 'OSIS-DOCUMENT')
43+
})
44+
provider = TracerProvider(resource=resource)
45+
otlp_exporter = OTLPSpanExporter(
46+
endpoint=getattr(settings, 'OTEL_EXPORTER_OTLP_ENDPOINT', 'http://localhost:4317'),
47+
insecure=bool(getattr(settings, 'OTEL_EXPORTER_OTLP_INSECURE', False)),
48+
)
49+
processor = BatchSpanProcessor(otlp_exporter) # ConsoleSpanExporter()
50+
provider.add_span_processor(processor)
51+
trace.set_tracer_provider(provider)
52+
53+
54+
def initialize_instrumentation():
55+
DjangoInstrumentor().instrument(tracer_provider=trace.get_tracer_provider(), is_sql_commentor_enabled=True)
56+
Psycopg2Instrumentor().instrument(
57+
tracer_provider=trace.get_tracer_provider(),
58+
skip_dep_check=True, # We use psycopg2-binary so we must skip dep check
59+
enable_commenter=True,
60+
commenter_options={},
61+
)
62+
URLLib3Instrumentor().instrument(tracer_provider=trace.get_tracer_provider(), skip_dep_check=True)
63+
RequestsInstrumentor().instrument(tracer_provider=trace.get_tracer_provider())
64+
CeleryInstrumentor().instrument(trace_provider=trace.get_tracer_provider())
65+
66+
67+
def get_tracer() -> Tracer:
68+
return trace.get_tracer(settings.OTEL_TRACER_MODULE_NAME, settings.OTEL_TRACER_LIBRARY_VERSION)

docker/server/document/settings.py

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
import os
2+
from django.utils.translation import gettext_lazy as _
3+
4+
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
5+
6+
SECRET_KEY = 'osis-document-secret'
7+
8+
DEBUG = True
9+
10+
ALLOWED_HOSTS = os.environ['ALLOWED_HOSTS'].split()
11+
12+
# Application definition
13+
INSTALLED_APPS = [
14+
'django.contrib.admin',
15+
'django.contrib.auth',
16+
'django.contrib.contenttypes',
17+
'django.contrib.sessions',
18+
'django.contrib.messages',
19+
'django.contrib.staticfiles',
20+
'django_celery_beat',
21+
'django_celery_results',
22+
'osis_document',
23+
]
24+
25+
MIDDLEWARE = [
26+
'django.middleware.security.SecurityMiddleware',
27+
'django.contrib.sessions.middleware.SessionMiddleware',
28+
'django.middleware.common.CommonMiddleware',
29+
'django.middleware.csrf.CsrfViewMiddleware',
30+
'django.contrib.auth.middleware.AuthenticationMiddleware',
31+
'django.contrib.messages.middleware.MessageMiddleware',
32+
'django.middleware.clickjacking.XFrameOptionsMiddleware',
33+
]
34+
35+
ROOT_URLCONF = 'document.urls'
36+
37+
TEMPLATES = [
38+
{
39+
'BACKEND': 'django.template.backends.django.DjangoTemplates',
40+
'DIRS': [],
41+
'APP_DIRS': True,
42+
'OPTIONS': {
43+
'context_processors': [
44+
'django.template.context_processors.debug',
45+
'django.template.context_processors.request',
46+
'django.contrib.auth.context_processors.auth',
47+
'django.contrib.messages.context_processors.messages',
48+
],
49+
},
50+
},
51+
]
52+
53+
WSGI_APPLICATION = 'document.wsgi.application'
54+
55+
LOGGING = {
56+
'version': 1,
57+
'disable_existing_loggers': True,
58+
'formatters': {
59+
'verbose': {
60+
'format': '%(asctime)s %(levelname)s %(module)s %(process)d %(thread)d %(message)s',
61+
'datefmt': '%d-%m-%Y %H:%M:%S'
62+
},
63+
'simple': {
64+
'format': '%(asctime)s %(levelname)s %(message)s',
65+
'datefmt': '%d-%m-%Y %H:%M:%S'
66+
},
67+
},
68+
'handlers': {
69+
'console': {
70+
'class': 'logging.StreamHandler',
71+
'formatter': 'simple',
72+
'level': 'DEBUG',
73+
},
74+
},
75+
'loggers': {
76+
'default': {
77+
'handlers': ['console'],
78+
'level': 'DEBUG',
79+
'propagate': False,
80+
},
81+
'queue_exception': {
82+
'handlers': ['console'],
83+
'level': 'DEBUG',
84+
'propagate': False,
85+
},
86+
'send_mail': {
87+
'handlers': ['console'],
88+
'level': 'DEBUG',
89+
'propagate': False,
90+
},
91+
'django': {
92+
'handlers': ['console'],
93+
'level': 'INFO',
94+
'propagate': True,
95+
},
96+
'celery': {
97+
'handlers': ['console'],
98+
'level': 'INFO',
99+
'propagate': True,
100+
},
101+
}
102+
}
103+
104+
# Database
105+
DATABASES = {
106+
'default': {
107+
'ENGINE': 'django.db.backends.postgresql_psycopg2',
108+
'NAME': os.environ.get("DATABASE_NAME", 'osis_document_local'),
109+
'USER': os.environ.get("POSTGRES_USER", 'osis'),
110+
'PASSWORD': os.environ.get("POSTGRES_PASSWORD", 'osis'),
111+
'HOST': os.environ.get("POSTGRES_HOST", '127.0.0.1'),
112+
'PORT': os.environ.get("POSTGRES_PORT", '5432'),
113+
'ATOMIC_REQUESTS': os.environ.get('DATABASE_ATOMIC_REQUEST', 'True').lower() == 'true',
114+
},
115+
}
116+
117+
# I18N
118+
LANGUAGE_CODE = 'fr-be'
119+
LANGUAGES = [
120+
('fr-be', _('French')),
121+
('en', _('English')),
122+
]
123+
124+
# Time
125+
TIME_ZONE = 'Europe/Brussels'
126+
USE_I18N = True
127+
USE_L10N = True
128+
USE_TZ = False
129+
130+
131+
# Static files (CSS, JavaScript, Images)
132+
STATIC_URL = '/static/'
133+
134+
# Media
135+
MEDIA_ROOT = os.path.join(BASE_DIR, "uploads")
136+
137+
# Osis document
138+
# OSIS_DOCUMENT_BASE_URL = 'http://osis-document-server:9503/api/osis-document/'
139+
OSIS_DOCUMENT_API_SHARED_SECRET = 'osis-document-api-shared-secret'
140+
OSIS_DOCUMENT_DOMAIN_LIST = [
141+
'localhost',
142+
'127.0.0.1',
143+
]
144+
OSIS_DOCUMENT_UPLOAD_LIMIT = '10000/minute'
145+
OSIS_DOCUMENT_TOKEN_MAX_AGE = 60 * 60
146+
OSIS_DOCUMENT_TEMP_UPLOAD_MAX_AGE = 60 * 15
147+
OSIS_DOCUMENT_ALLOWED_EXTENSIONS = ['pdf', 'txt', 'docx', 'doc', 'odt', 'png', 'jpg']
148+
149+
# Celery configuration
150+
CELERY_BROKER_URL = "amqp://{user}:{password}@{host}:{port}".format(
151+
user=os.environ.get('RABBITMQ_USER', 'guest'),
152+
password=os.environ.get('RABBITMQ_PASSWORD', 'guest'),
153+
host=os.environ.get('RABBITMQ_HOST', 'localhost'),
154+
port=os.environ.get('RABBITMQ_PORT', '5672'),
155+
)
156+
CELERY_BEAT_SCHEDULER = "django_celery_beat.schedulers:DatabaseScheduler"
157+
CELERY_RESULT_BACKEND = os.environ.get('CELERY_RESULT_BACKEND', 'django-db')
158+
CELERY_TIMEZONE = os.environ.get('TIME_ZONE', 'Europe/Brussels')
159+
DJANGO_CELERY_BEAT_TZ_AWARE = os.environ.get('USE_TZ', 'False').lower() == 'true'
160+
CELERY_ENABLE_UTC = False
161+
162+
163+
# OpenTelemetry
164+
# OTEL_ENABLED = True
165+
# OTEL_PYTHON_DJANGO_INSTRUMENT = True
166+
# OTEL_PYTHON_DJANGO_EXCLUDED_URLS="/admin/*"
167+
# OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST=".*"
168+
# OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE=".*"
169+
# OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SANITIZE_FIELDS=".*session.*,set-cookie,cookie,Authorization,X_USER_GLOBALID,X_USER_EMAIL,X_USER_FIRSTNAME,X_USER_LASTNAME"
170+
# OTEL_EXPORTER_OTLP_ENDPOINT="http://jaeger:4317"
171+
# OTEL_EXPORTER_OTLP_INSECURE = True
172+
# OTEL_SERVICE_NAME = "OSIS-DOCUMENT"
173+
# OTEL_TRACER_MODULE_NAME = "OSIS"
174+
# OTEL_TRACER_LIBRARY_VERSION = "1.0.0"

docker/server/document/urls.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
"""document URL Configuration
2+
3+
The `urlpatterns` list routes URLs to views. For more information please see:
4+
https://docs.djangoproject.com/en/3.2/topics/http/urls/
5+
Examples:
6+
Function views
7+
1. Add an import: from my_app import views
8+
2. Add a URL to urlpatterns: path('', views.home, name='home')
9+
Class-based views
10+
1. Add an import: from other_app.views import Home
11+
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
12+
Including another URLconf
13+
1. Import the include() function: from django.urls import include, path
14+
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
15+
"""
16+
from django.contrib import admin
17+
from django.urls import path, include
18+
19+
urlpatterns = [
20+
path('admin/', admin.site.urls),
21+
path('api/osis-document/', include('osis_document.urls')),
22+
]

docker/server/document/wsgi.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
"""
2+
WSGI config for document project.
3+
4+
It exposes the WSGI callable as a module-level variable named ``application``.
5+
6+
For more information on this file, see
7+
https://docs.djangoproject.com/en/3.2/howto/deployment/wsgi/
8+
"""
9+
10+
import os
11+
12+
from django.core.wsgi import get_wsgi_application
13+
14+
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'document.settings')
15+
16+
application = get_wsgi_application()

0 commit comments

Comments
 (0)