diff --git a/.github/workflows/pod.yml b/.github/workflows/pod.yml
deleted file mode 100644
index ed7671d3dd..0000000000
--- a/.github/workflows/pod.yml
+++ /dev/null
@@ -1,135 +0,0 @@
----
-name: Django Pod CI
-
-on:
- push:
- branches: [master, develop]
- pull_request:
- branches: ["*"]
-
-jobs:
- build:
- runs-on: ubuntu-latest
-
- strategy:
- max-parallel: 4
- matrix:
- python-version: ['3.8', '3.9', '3.10']
-
- steps:
- - uses: actions/checkout@v4
-
- - name: Configure sysctl limits (for ES)
- run: |
- sudo swapoff -a
- sudo sysctl -w vm.swappiness=1
- sudo sysctl -w fs.file-max=262144
- sudo sysctl -w vm.max_map_count=262144
-
- - name: Set up Python ${{ matrix.python-version }}
- uses: actions/setup-python@v5
- with:
- python-version: ${{ matrix.python-version }}
-
- # Remove apt repos that are known to break from time to time
- # See https://github.com/actions/virtual-environments/issues/323
- - name: Remove broken apt repos [Ubuntu]
- run: |
- for apt_file in `grep -lr microsoft /etc/apt/sources.list.d/`; do sudo rm $apt_file; done
-
- - name: Install Dependencies
- run: |
- sudo apt-get clean
- sudo apt-get update
- sudo apt-get install ffmpeg
- sudo apt-get install -y ffmpegthumbnailer
- sudo apt-get install -y npm
- python -m pip install --upgrade pip
- pip install -r requirements.txt
- pip install -r requirements-dev.txt
- sudo npm install -g yarn
-
- - name: Flake8 compliance
- run: |
- flake8 --max-complexity=7 --ignore=E501,W503,E203 --exclude .git,pod/*/migrations/*.py,*_settings.py
-
- - name: Runs Elasticsearch
- uses: elastic/elastic-github-actions/elasticsearch@refactor_with_plugins
- with:
- # stack-version: 7.6.0
- stack-version: 6.8.23
- plugins: analysis-icu
-
- - name: Setup Pod
- run: |
- coverage run --source='.' manage.py create_pod_index --settings=pod.main.test_settings
- python manage.py makemigrations --settings=pod.main.test_settings
- python manage.py migrate --settings=pod.main.test_settings
- cd pod
- yarn
-
- - name: Run Tests with coverage
- env:
- PYTHONUNBUFFERED: 1
- run: |
- coverage run --append --source='.' manage.py test -v 3 --settings=pod.main.test_settings
-
- ## Start Accessibility tests with pa11y ##
-
- - name: Install pa11y-ci dependencies.
- if: matrix.python-version == '3.9'
- run: |
- npm install -g Badatos/pa11y-ci
-
- - name: Run Django test server
- if: matrix.python-version == '3.9'
- env:
- PYTHONUNBUFFERED: 1
- run: |
- python manage.py loaddata pod/video/fixtures/initial_data.json --settings=pod.main.test_settings
- python manage.py loaddata pod/main/fixtures/initial_data.json --settings=pod.main.test_settings
- python manage.py loaddata pod/video/fixtures/sample_videos.json --settings=pod.main.test_settings
- python manage.py collectstatic --clear --settings=pod.main.test_settings
- coverage run --append --source='.' manage.py runserver localhost:9090 --insecure --settings=pod.main.test_settings &
- sleep 5
-
- - name: Run pa11y-ci.
- if: matrix.python-version == '3.9'
- # tee reads `stdin` and writes it to both `stdout` and a file
- run: |
- pa11y-ci 2>&1 | tee pa11y_output.txt
-
- - name: Read pa11y_output file.
- id: pa11y_output
- if: matrix.python-version == '3.9'
- uses: juliangruber/read-file-action@v1
- with:
- path: ./pa11y_output.txt
-
- - name: Comment on pull request.
- if: contains(steps.pa11y_output.outputs.content, 'Errors in http://')
- uses: thollander/actions-comment-pull-request@v2
- with:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- message: 'Pa11y testing results
-
-
-```
-
-${{ steps.pa11y_output.outputs.content }}```
-
-
-
>> __ref: [https://docs.djangoproject.com/fr/3.2/ref/settings/#debug]()__
+ - `USE_DEBUG_TOOLBAR`
+
+ > valeur par défaut : `True`
+
+ >> Une valeur booléenne qui active ou désactive l'outil de débogage.
+ >> Ne déployez jamais de site en production avec le réglage USE_DEBUG_TOOLBAR activé.
+ >> __ref: [https://django-debug-toolbar.readthedocs.io/en/latest/]()__
+
- `LOGIN_URL`
> valeur par défaut : `/authentication_login/`
@@ -1425,6 +1433,26 @@ Vous pouvez tout Ă fait rajouter des langues comme vous le souhaitez. Il faudra
>> voir `ACTIVE_MODEL_ENRICH`
+### Configuration de l'application Cut
+Application Cut permettant de découper des vidéos.
+Mettre `USE_CUT` Ă True pour activer cette application.
+
+ - `USE_CUT`
+
+ > valeur par défaut : `True`
+
+ >> Activation de l'application Cut
+
+### Configuration de l'application dressing
+Application Dressing pour customiser une vidéo avec un filigrane et des crédits.
+Mettre `USE_DRESSING` Ă True pour activer cette application.
+
+ - `USE_DRESSING`
+
+ > valeur par défaut : `True`
+
+ >> Activation des habillages. Permet aux utilisateurs de customiser une vidéo avec un filigrane et des crédits.
+
### Configuration application enrichment
### Configuration application d'import vidéo
diff --git a/Makefile b/Makefile
index f3e38371b2..af8e910193 100755
--- a/Makefile
+++ b/Makefile
@@ -91,6 +91,7 @@ createconfigs:
export
COMPOSE = docker-compose -f ./docker-compose-dev-with-volumes.yml -p esup-pod
COMPOSE_FULL = docker-compose -f ./docker-compose-full-dev-with-volumes.yml -p esup-pod
+COMPOSE_FULL_TEST = docker-compose -f ./docker-compose-full-dev-with-volumes-test.yml -p esup-pod
DOCKER_LOGS = docker logs -f
#docker-start-build:
@@ -123,6 +124,9 @@ docker-build:
ifeq ($(DOCKER_ENV), full)
@$(COMPOSE_FULL) build --build-arg ELASTICSEARCH_VERSION=$(ELASTICSEARCH_TAG) --build-arg NODE_VERSION=$(NODE_TAG) --build-arg PYTHON_VERSION=$(PYTHON_TAG) --no-cache
@$(COMPOSE_FULL) up
+else ifeq ($(DOCKER_ENV), full-test)
+ @$(COMPOSE_FULL_TEST) build --build-arg ELASTICSEARCH_VERSION=$(ELASTICSEARCH_TAG) --build-arg NODE_VERSION=$(NODE_TAG) --build-arg PYTHON_VERSION=$(PYTHON_TAG) --no-cache
+ @$(COMPOSE_FULL_TEST) up
else
@$(COMPOSE) build --build-arg ELASTICSEARCH_VERSION=$(ELASTICSEARCH_TAG) --build-arg NODE_VERSION=$(NODE_TAG) --build-arg PYTHON_VERSION=$(PYTHON_TAG) --no-cache
@$(COMPOSE) up
@@ -135,6 +139,8 @@ docker-start:
# (Attention, il a été constaté que sur un mac, le premier lancement peut prendre plus de 5 minutes.)
ifeq ($(DOCKER_ENV), full)
@$(COMPOSE_FULL) up
+else ifeq ($(DOCKER_ENV), full-test)
+ @$(COMPOSE_FULL_TEST) up
else
@$(COMPOSE) up
endif
@@ -145,6 +151,8 @@ endif
docker-stop:
ifeq ($(DOCKER_ENV), full)
@$(COMPOSE_FULL) down -v
+else ifeq ($(DOCKER_ENV), full-test)
+ @$(COMPOSE_FULL_TEST) down -v
else
@$(COMPOSE) down -v
endif
@@ -152,6 +160,8 @@ endif
docker-reset:
ifeq ($(DOCKER_ENV), full)
@$(COMPOSE_FULL) down -v
+else ifeq ($(DOCKER_ENV), full-test)
+ @$(COMPOSE_FULL_TEST) down -v
else
@$(COMPOSE) down -v
endif
@@ -163,7 +173,8 @@ endif
sudo rm -rf ./pod/node_modules
# sudo rm -rf ./pod/db_migrations
sudo rm -rf ./pod/db_migrations
- # sudo rm -rf ./pod/db.sqlite3"
+ # sudo rm -rf ./pod/db.sqlite3
sudo rm -rf ./pod/db.sqlite3
- # sudo rm -rf ./pod/media"
+ sudo rm -rf ./pod/db_remote.sqlite3
+ # sudo rm -rf ./pod/media
sudo rm -rf ./pod/media
diff --git a/docker-compose-full-dev-with-volumes-test.yml b/docker-compose-full-dev-with-volumes-test.yml
new file mode 100755
index 0000000000..8913c02ebf
--- /dev/null
+++ b/docker-compose-full-dev-with-volumes-test.yml
@@ -0,0 +1,95 @@
+x-pod-volumes: &pod-volumes
+ - .:/usr/src/app
+
+x-elasticsearch-volumes: &elasticsearch-volumes
+ - ./dockerfile-dev-with-volumes/config/elasticsearch/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml
+
+version: '3.7'
+
+services:
+ pod-back:
+ container_name: pod-back-with-volumes
+ build:
+ context: .
+ dockerfile: dockerfile-dev-with-volumes/pod-back/Dockerfile
+ depends_on:
+ - elasticsearch
+ - redis
+ env_file:
+ - ./.env.dev
+ ports:
+ - 9090:8080
+ volumes: *pod-volumes
+
+ pod-encode:
+ container_name: pod-encode-with-volumes
+ build:
+ context: .
+ dockerfile: dockerfile-dev-with-volumes/pod-encode/Dockerfile
+ depends_on:
+ - pod-back
+ env_file:
+ - ./.env.dev
+ volumes: *pod-volumes
+
+ pod-transcript:
+ container_name: pod-transcript-with-volumes
+ build:
+ context: .
+ dockerfile: dockerfile-dev-with-volumes/pod-transcript/Dockerfile
+ depends_on:
+ - pod-back
+ env_file:
+ - ./.env.dev
+ volumes: *pod-volumes
+
+ pod-xapi:
+ container_name: pod-xapi-with-volumes
+ build:
+ context: .
+ dockerfile: dockerfile-dev-with-volumes/pod-xapi/Dockerfile
+ depends_on:
+ - pod-back
+ env_file:
+ - ./.env.dev
+ volumes: *pod-volumes
+
+ elasticsearch:
+ container_name: elasticsearch-with-volumes
+ build:
+ context: .
+ dockerfile: dockerfile-dev-with-volumes/elasticsearch/dockerfile-elasticsearch-dev
+ ports:
+ - 9200:9200
+ environment:
+ - discovery.type=single-node
+ - bootstrap.memory_lock=true
+ - "ES_JAVA_OPTS=-Xms2g -Xmx2g"
+ volumes: *elasticsearch-volumes
+
+ redis:
+ container_name: redis-with-volumes
+ image: ${REDIS_TAG}
+ env_file:
+ - ./.env.dev
+ ports:
+ - 6379:6379
+
+ pa11y-ci:
+ container_name: pa11y-ci
+ build:
+ context: .
+ dockerfile: dockerfile-dev-with-volumes/pa11y-ci/dockerfile-pa11y-ci
+ depends_on:
+ - pod-back
+ volumes: *pod-volumes
+
+# redis-commander:
+# container_name: redis-commander
+# hostname: redis-commander
+# image: rediscommander/redis-commander:latest
+# restart: always
+# environment:
+# - REDIS_HOSTS=local:redis:6379
+# ports:
+# - "8081:8081"
diff --git a/dockerfile-dev-with-volumes/pa11y-ci/config.json b/dockerfile-dev-with-volumes/pa11y-ci/config.json
new file mode 100644
index 0000000000..b0772eb470
--- /dev/null
+++ b/dockerfile-dev-with-volumes/pa11y-ci/config.json
@@ -0,0 +1,27 @@
+{
+ "defaults": {
+ "timeout": 10000,
+ "viewport": {
+ "width": 1280,
+ "height": 1024
+ },
+ "concurrency": 1,
+ "debug": true,
+ "chromeLaunchConfig": {
+ "args": [
+ "--no-sandbox"
+ ]
+ },
+ "reporters": [
+ "cli",
+ ["json", { "fileName": "/usr/src/app/pa11y-results.json" }]
+ ]
+ },
+ "urls": [
+ "http://pod-back:8080/",
+ "http://pod-back:8080/accounts/login",
+ "http://pod-back:8080/videos",
+ "http://pod-back:8080/video/0001-video-test/",
+ "http://pod-back:8080/live/events/"
+ ]
+}
diff --git a/dockerfile-dev-with-volumes/pa11y-ci/config_mobile.json b/dockerfile-dev-with-volumes/pa11y-ci/config_mobile.json
new file mode 100644
index 0000000000..3a39f4e972
--- /dev/null
+++ b/dockerfile-dev-with-volumes/pa11y-ci/config_mobile.json
@@ -0,0 +1,28 @@
+{
+ "defaults": {
+ "timeout": 10000,
+ "viewport": {
+ "width": 320,
+ "height": 480,
+ "isMobile": true
+ },
+ "concurrency": 1,
+ "debug": true,
+ "chromeLaunchConfig": {
+ "args": [
+ "--no-sandbox"
+ ]
+ },
+ "reporters": [
+ "cli",
+ ["json", { "fileName": "/usr/src/app/pa11y-results-mobile.json" }]
+ ]
+ },
+ "urls": [
+ "http://pod-back:8080/",
+ "http://pod-back:8080/accounts/login",
+ "http://pod-back:8080/videos",
+ "http://pod-back:8080/video/0001-video-test/",
+ "http://pod-back:8080/live/events/"
+ ]
+}
diff --git a/dockerfile-dev-with-volumes/pa11y-ci/dockerfile-pa11y-ci b/dockerfile-dev-with-volumes/pa11y-ci/dockerfile-pa11y-ci
new file mode 100644
index 0000000000..1c994b9f21
--- /dev/null
+++ b/dockerfile-dev-with-volumes/pa11y-ci/dockerfile-pa11y-ci
@@ -0,0 +1,6 @@
+FROM buildkite/puppeteer:v1.15.0
+RUN npm install --global --unsafe-perm pa11y-ci
+COPY ./dockerfile-dev-with-volumes/pa11y-ci/my-entrypoint-pa11y.sh /tmp/my-entrypoint-pa11y.sh
+RUN chmod 755 /tmp/my-entrypoint-pa11y.sh
+# ENTRYPOINT ["pa11y-ci", "-c", "/usr/config.json"]
+ENTRYPOINT ["bash", "/tmp/my-entrypoint-pa11y.sh"]
diff --git a/dockerfile-dev-with-volumes/pa11y-ci/my-entrypoint-pa11y.sh b/dockerfile-dev-with-volumes/pa11y-ci/my-entrypoint-pa11y.sh
new file mode 100644
index 0000000000..35af73093d
--- /dev/null
+++ b/dockerfile-dev-with-volumes/pa11y-ci/my-entrypoint-pa11y.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+echo "Launching commands into pa11y: pa11y-ci -c /usr/config.json"
+sleep infinity
diff --git a/pod/completion/models.py b/pod/completion/models.py
index e1be046df0..089bdb56bd 100644
--- a/pod/completion/models.py
+++ b/pod/completion/models.py
@@ -137,11 +137,13 @@ def verify_not_same_contributor(self):
def __str__(self):
return "Video:{0} - Name:{1} - Role:{2}".format(self.video, self.name, self.role)
- def get_base_mail(self):
+ def get_base_mail(self) -> str:
+ """Encode an email as base64 to prevent robots crawling."""
data = base64.b64encode(self.email_address.encode())
- return data
+ return data.decode("utf-8")
- def get_noscript_mail(self):
+ def get_noscript_mail(self) -> str:
+ """Replace @ by __AT__ in email adress to prevent robots crawling."""
return self.email_address.replace("@", "__AT__")
@@ -178,10 +180,7 @@ def verify_document(self):
msg = list()
if not self.document:
msg.append(_("Please enter a document."))
- if len(msg) > 0:
- return msg
- else:
- return list()
+ return msg
def verify_not_same_document(self):
msg = list()
diff --git a/pod/completion/tests/test_models.py b/pod/completion/tests/test_models.py
index a4c46b53cf..43574ea062 100644
--- a/pod/completion/tests/test_models.py
+++ b/pod/completion/tests/test_models.py
@@ -1,10 +1,14 @@
"""Unit tests for completion models."""
+import base64
+
from django.conf import settings
+from django.contrib.sites.models import Site
from django.test import TestCase
from django.contrib.auth.models import User
from django.core.files.uploadedfile import SimpleUploadedFile
from django.core.exceptions import ValidationError
+from django.utils.translation import ugettext_lazy as _
from pod.video.models import Video
from pod.video.models import Type
from pod.completion.models import Contributor
@@ -30,11 +34,16 @@ class ContributorModelTestCase(TestCase):
]
def setUp(self):
+ self.site = Site.objects.get(id=1)
owner = User.objects.create(username="test")
- videotype = Type.objects.create(title="others")
+ video_type = Type.objects.create(title="others")
video = Video.objects.create(
- title="video", type=videotype, owner=owner, video="test.mp4"
+ title="video",
+ type=video_type,
+ owner=owner,
+ video="test.mp4",
)
+ video.sites.add(self.site)
Contributor.objects.create(
video=video,
name="contributor",
@@ -98,6 +107,36 @@ def test_delete(self):
print(" ---> test_delete: OK! --- ContributorModel")
+ def test_sites_property(self):
+ """Test the sites property of the Contributor model."""
+ contributor = Contributor.objects.get(id=1)
+ self.assertEqual(contributor.sites, Video.objects.get(id=1).sites)
+ print(" ---> test_sites_property: OK! --- ContributorModel")
+
+ def test_str(self):
+ """Test the sites property of the Contributor model when the video is deleted."""
+ contributor = Contributor.objects.get(id=1)
+ video = Video.objects.get(id=1)
+ self.assertEqual(
+ str(contributor),
+ f"Video:{video} - Name:{contributor.name} - Role:{contributor.role}",
+ )
+ print(" ---> test_str: OK! --- ContributorModel")
+
+ def test_get_base_mail(self):
+ """Test the get_base_mail method of the Contributor model."""
+ contributor = Contributor.objects.get(id=1)
+ self.assertEqual(
+ contributor.get_base_mail(),
+ base64.b64encode(contributor.email_address.encode()).decode("utf-8"),
+ )
+ print(" ---> test_get_base_mail: OK! --- ContributorModel")
+
+ def test_get_noscript_mail(self):
+ """Test the get_noscript_mail method of the Contributor model."""
+ contributor = Contributor.objects.get(id=1)
+ self.assertEqual(contributor.get_noscript_mail(), "contributor__AT__pod.com")
+
class DocumentModelTestCase(TestCase):
fixtures = [
@@ -155,6 +194,29 @@ def test_delete(self):
print(" ---> test_delete: OK! --- DocumentModel")
+ def test_sites_property(self):
+ """Test the sites property of the Contributor model."""
+ document = Document.objects.get(id=1)
+ self.assertEqual(document.sites, Video.objects.get(id=1).sites)
+ print(" ---> test_sites_property: OK! --- DocumentModel")
+
+ def test_str(self):
+ """Test the __str__ method of the Document model."""
+ document = Document.objects.get(id=1)
+ video = Video.objects.get(id=1)
+ self.assertEqual(
+ str(document), f"Document: {document.document.name} - Video: {video}"
+ )
+ print(" ---> test_str: OK! --- DocumentModel")
+
+ def test_verify_document(self):
+ """Test the verify_document method of the Document model."""
+ document = Document.objects.get(id=1)
+ document.document = None
+ document.save()
+ self.assertIn(_("Please enter a document."), document.verify_document())
+ print(" ---> test_verify_document: OK! --- DocumentModel")
+
class OverlayModelTestCase(TestCase):
fixtures = [
diff --git a/pod/custom/settings_local_docker_full_test.py b/pod/custom/settings_local_docker_full_test.py
index 8e71aa0e1c..f381222919 100644
--- a/pod/custom/settings_local_docker_full_test.py
+++ b/pod/custom/settings_local_docker_full_test.py
@@ -9,6 +9,16 @@
('Nicolas', 'nicolas@univ.fr'),
)
+DATABASES = {
+ "default": {
+ "ENGINE": "django.db.backends.sqlite3",
+ 'NAME': '/usr/src/app/pod/db_remote.sqlite3',
+ 'TEST': {
+ 'NAME': '/usr/src/app/pod/db_remote.sqlite3',
+ },
+ }
+}
+
USE_PODFILE = True
USE_NOTIFICATIONS = False
EMAIL_ON_ENCODING_COMPLETION = False
diff --git a/pod/cut/context_processors.py b/pod/cut/context_processors.py
new file mode 100644
index 0000000000..f85873609e
--- /dev/null
+++ b/pod/cut/context_processors.py
@@ -0,0 +1,12 @@
+"""Esup-Pod context processors for cut app."""
+
+from django.conf import settings as django_settings
+
+USE_CUT = getattr(django_settings, "USE_CUT", True)
+
+
+def context_settings(request):
+ """Return all context settings for cut app"""
+ new_settings = {}
+ new_settings["USE_CUT"] = USE_CUT
+ return new_settings
diff --git a/pod/cut/forms.py b/pod/cut/forms.py
index f778bd0c5c..cd91bb5788 100644
--- a/pod/cut/forms.py
+++ b/pod/cut/forms.py
@@ -14,6 +14,20 @@ class Meta:
model = CutVideo
fields = ["video", "start", "end", "duration"]
widgets = {
- "start": forms.TimeInput(attrs={"type": "time", "step": "1"}),
- "end": forms.TimeInput(attrs={"type": "time", "step": "1"}),
+ "start": forms.TimeInput(
+ attrs={
+ "step": "1",
+ "required": "required",
+ "size": "8",
+ "class": "text-center",
+ }
+ ),
+ "end": forms.TimeInput(
+ attrs={
+ "step": "1",
+ "required": "required",
+ "size": "8",
+ "class": "text-center",
+ }
+ ),
}
diff --git a/pod/cut/models.py b/pod/cut/models.py
index 646c22aef3..074dd64c56 100644
--- a/pod/cut/models.py
+++ b/pod/cut/models.py
@@ -23,16 +23,19 @@ def clean(self):
)
def verify_time(self):
- """VĂ©rifier si ce sont les mĂȘmes valeurs (pour Ă©viter un rĂ©encodage inutile)"""
+ """Check if they are the same values (to avoid unnecessary re-encoding)."""
if CutVideo.objects.filter(video=self.video.id).exists():
previous_cut = CutVideo.objects.get(video=self.video.id)
if previous_cut.start == self.start and previous_cut.end == self.end:
return False
# Convert start and end to seconds
- start = time_to_seconds(self.start)
- end = time_to_seconds(self.end)
- duration = time_to_seconds(self.duration)
+ try:
+ start = time_to_seconds(self.start)
+ end = time_to_seconds(self.end)
+ duration = time_to_seconds(self.duration)
+ except ValueError:
+ return False
if start < 0 or start >= end or end is None or start is None or end > duration:
return False
else:
diff --git a/pod/cut/templates/video_cut.html b/pod/cut/templates/video_cut.html
index 5b6654a99e..8a1f9246be 100644
--- a/pod/cut/templates/video_cut.html
+++ b/pod/cut/templates/video_cut.html
@@ -41,7 +41,7 @@