diff --git a/README.md b/README.md index 07558ba3b3..c5073652cc 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,6 @@ ![last commit push](https://img.shields.io/github/last-commit/EsupPortail/Esup-Pod) [![Author](https://img.shields.io/badge/author-Ptitloup-blue)](https://www.linkedin.com/in/nicolas-can-a6bb7869/) - ## [FR] ### Plateforme de gestion de fichier vidéo diff --git a/pod/ai_enhancement/static/ai_enhancement/js/enrich-form.js b/pod/ai_enhancement/static/ai_enhancement/js/enrich-form.js index c43a253a55..234989ef42 100644 --- a/pod/ai_enhancement/static/ai_enhancement/js/enrich-form.js +++ b/pod/ai_enhancement/static/ai_enhancement/js/enrich-form.js @@ -4,6 +4,8 @@ * @since 3.7.0 */ +// Read-only globals defined in main.js +/* global decodeString remove_quotes removeAccentsAndLowerCase */ const BORDER_CLASS = 'border-d'; @@ -92,7 +94,7 @@ function addTogglePairInput(aiVersionElement, initialVersionElement, input, elem aiVersionElement.addEventListener('click', () => { let input = document.getElementById('id_' + element); togglePairInput(aiVersionElement, initialVersionElement, input, element); - }) + }); input.addEventListener('input', () => event__inputChange(initialVersionElement, aiVersionElement)); } @@ -182,7 +184,7 @@ function addEventListeners(videoSlug, videoTitle, videoDescription, videoDiscipl 'description', 'tags', 'disciplines', - ] + ]; const options = { method: 'GET', headers: { @@ -227,7 +229,7 @@ function addEventListeners(videoSlug, videoTitle, videoDescription, videoDiscipl }); aiVersionElement.addEventListener('click', () => { toggleMultiplePairInput(aiVersionElement, initialVersionElement, input); - }) + }); input.addEventListener('input', () => event__inputChange(initialVersionElement, aiVersionElement)); break; } diff --git a/pod/ai_enhancement/views.py b/pod/ai_enhancement/views.py index 26da7b90d2..e2d8de01d2 100644 --- a/pod/ai_enhancement/views.py +++ b/pod/ai_enhancement/views.py @@ -25,7 +25,6 @@ from pod.main.lang_settings import ALL_LANG_CHOICES, PREF_LANG_CHOICES from pod.main.utils import json_to_web_vtt from pod.main.views import in_maintenance -from pod.podfile.models import UserFolder from pod.quiz.utils import import_quiz from pod.video.models import Video, Discipline from pod.video_encode_transcript.transcript import saveVTT @@ -349,10 +348,7 @@ def enhance_subtitles(request: WSGIRequest, video_slug: str) -> HttpResponse: + str(True) ) - video_folder, created = UserFolder.objects.get_or_create( - name=video.slug, - owner=request.user, - ) + video_folder = video.get_or_create_video_folder() if enhancement_is_already_asked(video): enhancement = AIEnhancement.objects.filter(video=video).first() if enhancement.is_ready: diff --git a/pod/completion/tests/test_models.py b/pod/completion/tests/test_models.py index 97b1cdcaaf..e3d345d4f7 100644 --- a/pod/completion/tests/test_models.py +++ b/pod/completion/tests/test_models.py @@ -64,7 +64,7 @@ def test_attributs_full(self) -> None: print(" ---> test_attributs_full: OK! --- ContributorModel") - def test_attributs(self): + def test_attributs(self) -> None: contributor = Contributor.objects.get(id=2) video = Video.objects.get(id=1) self.assertEqual(contributor.video, video) @@ -76,7 +76,7 @@ def test_attributs(self): print(" [ BEGIN COMPLETION_TEST_MODELS ] ") print(" ---> test_attributs: OK! --- ContributorModel") - def test_bad_attributs(self): + def test_bad_attributs(self) -> None: video = Video.objects.get(id=1) contributor = Contributor() contributor.video = video @@ -90,7 +90,7 @@ def test_bad_attributs(self): print(" ---> test_bad_attributs: OK! --- ContributorModel") - def test_same(self): + def test_same(self) -> None: video = Video.objects.get(id=1) contributor = Contributor() contributor.video = video @@ -100,20 +100,20 @@ def test_same(self): print(" ---> test_same: OK! --- ContributorModel") - def test_delete(self): + def test_delete(self) -> None: Contributor.objects.get(id=1).delete() Contributor.objects.get(id=2).delete() self.assertTrue(Contributor.objects.all().count() == 0) print(" ---> test_delete: OK! --- ContributorModel") - def test_sites_property(self): + def test_sites_property(self) -> None: """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): + def test_str(self) -> None: """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) @@ -143,7 +143,7 @@ class DocumentModelTestCase(TestCase): "initial_data.json", ] - def setUp(self): + def setUp(self) -> None: owner = User.objects.create(username="test") videotype = Type.objects.create(title="others") video = Video.objects.create( @@ -168,7 +168,7 @@ def setUp(self): Document.objects.create(video=video, document=file) Document.objects.create(video=video) - def test_attributs_full(self): + def test_attributs_full(self) -> None: document = Document.objects.get(id=1) video = Video.objects.get(id=1) self.assertEqual(document.video, video) @@ -179,7 +179,7 @@ def test_attributs_full(self): print(" ---> test_attributs_full: OK! --- DocumentModel") - def test_attributs(self): + def test_attributs(self) -> None: document = Document.objects.get(id=2) video = Video.objects.get(id=1) self.assertEqual(document.video, video) @@ -187,20 +187,20 @@ def test_attributs(self): print(" ---> test_attributs: OK! --- DocumentModel") - def test_delete(self): + def test_delete(self) -> None: Document.objects.get(id=1).delete() Document.objects.get(id=2).delete() self.assertTrue(Document.objects.all().count() == 0) print(" ---> test_delete: OK! --- DocumentModel") - def test_sites_property(self): + def test_sites_property(self) -> None: """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): + def test_str(self) -> None: """Test the __str__ method of the Document model.""" document = Document.objects.get(id=1) video = Video.objects.get(id=1) @@ -209,7 +209,7 @@ def test_str(self): ) print(" ---> test_str: OK! --- DocumentModel") - def test_verify_document(self): + def test_verify_document(self) -> None: """Test the verify_document method of the Document model.""" document = Document.objects.get(id=1) document.document = None @@ -223,7 +223,7 @@ class OverlayModelTestCase(TestCase): "initial_data.json", ] - def setUp(self): + def setUp(self) -> None: owner = User.objects.create(username="test") videotype = Type.objects.create(title="others") video = Video.objects.create( @@ -243,7 +243,7 @@ def setUp(self): ) Overlay.objects.create(video=video, title="overlay2", content="test") - def test_attributs_full(self): + def test_attributs_full(self) -> None: overlay = Overlay.objects.get(id=1) video = Video.objects.get(id=1) self.assertEqual(overlay.video, video) @@ -255,7 +255,7 @@ def test_attributs_full(self): print(" ---> test_attributs_full: OK! --- OverlayModel") - def test_attributs(self): + def test_attributs(self) -> None: overlay = Overlay.objects.get(id=2) video = Video.objects.get(id=1) self.assertEqual(overlay.video, video) @@ -268,7 +268,7 @@ def test_attributs(self): print(" ---> test_attributs: OK! --- OverlayModel") - def test_title(self): + def test_title(self) -> None: video = Video.objects.get(id=1) overlay = Overlay() overlay.video = video @@ -279,7 +279,7 @@ def test_title(self): print(" ---> test_title: OK! --- OverlayModel") - def test_times(self): + def test_times(self) -> None: video = Video.objects.get(id=1) overlay = Overlay() overlay.video = video @@ -295,7 +295,7 @@ def test_times(self): print(" ---> test_times: OK! --- OverlayModel") - def test_overlap(self): + def test_overlap(self) -> None: video = Video.objects.get(id=1) overlay = Overlay() overlay.video = video @@ -307,7 +307,7 @@ def test_overlap(self): print(" ---> test_overlap: OK! --- OverlayModel") - def test_delete(self): + def test_delete(self) -> None: Overlay.objects.get(id=1).delete() Overlay.objects.get(id=2).delete() self.assertTrue(Overlay.objects.all().count() == 0) @@ -320,7 +320,7 @@ class TrackModelTestCase(TestCase): "initial_data.json", ] - def setUp(self): + def setUp(self) -> None: owner = User.objects.create(username="test") videotype = Type.objects.create(title="others") video = Video.objects.create( @@ -345,7 +345,7 @@ def setUp(self): Track.objects.create(video=video, lang="fr", kind="captions", src=file) Track.objects.create(video=video, lang="en") - def test_attributs_full(self): + def test_attributs_full(self) -> None: track = Track.objects.get(id=1) video = Video.objects.get(id=1) self.assertEqual(track.video, video) @@ -356,7 +356,7 @@ def test_attributs_full(self): print(" ---> test_attributs_full: OK! --- TrackModel") - def test_attributs(self): + def test_attributs(self) -> None: track = Track.objects.get(id=2) video = Video.objects.get(id=1) self.assertEqual(track.video, video) @@ -366,7 +366,7 @@ def test_attributs(self): print(" ---> test_attributs: OK! --- TrackModel") - def test_bad_attributs(self): + def test_bad_attributs(self) -> None: track = Track.objects.get(id=1) track.kind = None self.assertRaises(ValidationError, track.clean) @@ -381,7 +381,7 @@ def test_bad_attributs(self): print(" ---> test_bad_attributs: OK! --- TrackModel") - def test_same(self): + def test_same(self) -> None: video = Video.objects.get(id=1) track = Track() track.video = video diff --git a/pod/completion/views.py b/pod/completion/views.py index 7cdd9ac61d..00cbebd907 100644 --- a/pod/completion/views.py +++ b/pod/completion/views.py @@ -22,7 +22,6 @@ from .models import Overlay from .forms import OverlayForm from .models import CustomFileModel -from pod.podfile.models import UserFolder from pod.podfile.views import get_current_session_folder, file_edit_save from pod.main.lang_settings import ALL_LANG_CHOICES, PREF_LANG_CHOICES from pod.main.settings import LANGUAGE_CODE @@ -58,9 +57,7 @@ def get_completion_home_page_title(video: Video) -> str: def video_caption_maker(request, slug): """Caption maker app.""" video = get_object_or_404(Video, slug=slug, sites=get_current_site(request)) - video_folder, created = UserFolder.objects.get_or_create( - name=video.slug, owner=request.user - ) + request.session["current_session_folder"] = video.slug action = None if ( @@ -74,6 +71,7 @@ def video_caption_maker(request, slug): request, messages.ERROR, _("You cannot complement this video.") ) raise PermissionDenied + video_folder = video.get_or_create_video_folder() if request.method == "POST" and request.POST.get("action"): action = request.POST.get("action") if action in __CAPTION_MAKER_ACTION__: @@ -111,9 +109,7 @@ def video_caption_maker(request, slug): @staff_member_required(redirect_field_name="referrer") def video_caption_maker_save(request, video): """Caption maker save view.""" - video_folder, created = UserFolder.objects.get_or_create( - name=video.slug, owner=request.user - ) + video_folder = video.get_or_create_video_folder() if request.method == "POST": error = False diff --git a/pod/cut/tests/test_views.py b/pod/cut/tests/test_views.py index cec982dc02..189d68a389 100644 --- a/pod/cut/tests/test_views.py +++ b/pod/cut/tests/test_views.py @@ -12,15 +12,20 @@ from .. import views from importlib import reload +# ggignore-start +# gitguardian:ignore +PWD = "azerty1234" # nosec +# ggignore-end + class CutVideoViewsTestCase(TestCase): fixtures = [ "initial_data.json", ] - def setUp(self): - self.user = User.objects.create(username="test", password="azerty", is_staff=True) - self.user2 = User.objects.create(username="test2", password="azerty") + def setUp(self) -> None: + self.user = User.objects.create(username="test", password=PWD, is_staff=True) + self.user2 = User.objects.create(username="test2", password=PWD) self.video = Video.objects.create( title="videotest", owner=self.user, @@ -30,7 +35,7 @@ def setUp(self): ) self.video.additional_owners.add(self.user2) - def test_maintenance(self): + def test_maintenance(self) -> None: """Test Pod maintenance mode in CutVideoViewsTestCase.""" self.client.force_login(self.user) url = reverse("cut:video_cut", kwargs={"slug": self.video.slug}) @@ -46,7 +51,7 @@ def test_maintenance(self): self.assertRedirects(response, "/maintenance/") print(" ---> test_maintenance ok") - def test_get_full_duration(self): + def test_get_full_duration(self) -> None: """Test test_get_full_duration.""" CutVideo.objects.create( video=self.video, start=time(0, 0, 0), end=time(0, 0, 10), duration="00:00:10" @@ -61,7 +66,7 @@ def test_get_full_duration(self): print(" ---> test_get_full_duration ok") @override_settings(RESTRICT_EDIT_VIDEO_ACCESS_TO_STAFF_ONLY=True, USE_CUT=True) - def test_restrict_edit_video_access_staff_only(self): + def test_restrict_edit_video_access_staff_only(self) -> None: """Test test_restrict_edit_video_access_staff_only.""" reload(views) self.client.force_login(self.user2) @@ -80,7 +85,7 @@ def test_restrict_edit_video_access_staff_only(self): print(" ---> test_restrict_edit_video_access_staff_only ok") - def test_post_cut_valid_form(self): + def test_post_cut_valid_form(self) -> None: """Test test_post_cut_valid_form.""" self.client.force_login(self.user) post_data = { @@ -101,7 +106,7 @@ def test_post_cut_valid_form(self): print(" ---> test_post_cut_valid_form ok") - def test_post_cut_invalid_form(self): + def test_post_cut_invalid_form(self) -> None: """Test test_post_cut_invalid_form.""" self.client.force_login(self.user) post_data = { diff --git a/pod/enrichment/models.py b/pod/enrichment/models.py index 6511db8ba9..268cbb5395 100755 --- a/pod/enrichment/models.py +++ b/pod/enrichment/models.py @@ -22,11 +22,9 @@ import datetime if getattr(settings, "USE_PODFILE", False): + __FILEPICKER__ = True from pod.podfile.models import CustomImageModel from pod.podfile.models import CustomFileModel - from pod.podfile.models import UserFolder - - __FILEPICKER__ = True else: __FILEPICKER__ = False from pod.main.models import CustomImageModel @@ -65,18 +63,16 @@ def enrichment_to_vtt(list_enrichment, video) -> str: with open(temp_vtt_file.name, "w") as f: webvtt.write(f) if __FILEPICKER__: - videodir, created = UserFolder.objects.get_or_create( - name="%s" % video.slug, owner=video.owner - ) + video_folder = video.get_or_create_video_folder() previous_enrichment_file = CustomFileModel.objects.filter( name__startswith="enrichment", - folder=videodir, + folder=video_folder, created_by=video.owner, ) for enr in previous_enrichment_file: enr.delete() # do it like this to delete file enrichment_file, created = CustomFileModel.objects.get_or_create( - name="enrichment", folder=videodir, created_by=video.owner + name="enrichment", folder=video_folder, created_by=video.owner ) if enrichment_file.file and os.path.isfile(enrichment_file.file.path): diff --git a/pod/main/configuration.json b/pod/main/configuration.json index 19a5ad51ef..e7b1d988d9 100644 --- a/pod/main/configuration.json +++ b/pod/main/configuration.json @@ -49,13 +49,13 @@ "description": { "en": [ "URL for General Terms and Conditions for API uses for the AI video enhancement.", - "Example: 'https://aristote.univ.fr/cgu'", - "Project Link: https://www.demainestingenieurs.centralesupelec.fr/aristote/" + "Example: ''", + "Project Link: " ], "fr": [ "L’URL des conditions générales d’utilisation de l’API pour l’IA d’amélioration des vidéos.", - "Exemple : 'https://aristote.univ.fr/cgu'", - "Lien du projet : https://www.demainestingenieurs.centralesupelec.fr/aristote/" + "Exemple : ''", + "Lien du projet : " ] }, "pod_version_end": "", diff --git a/pod/main/static/js/main.js b/pod/main/static/js/main.js index 4922ac56f6..9828bd75f0 100644 --- a/pod/main/static/js/main.js +++ b/pod/main/static/js/main.js @@ -2,7 +2,8 @@ * @file Esup-Pod Main JavaScripts */ -/* exported getParents slideToggle fadeOut linkTo_UnCryptMailto showLoader videocheck send_form_data_vanilla */ +/* exported getParents slideToggle fadeOut linkTo_UnCryptMailto showLoader videocheck */ +/* exported send_form_data_vanilla decodeString */ // Read-only globals defined in video-script.html /* global player */ diff --git a/pod/main/templatetags/flat_page_edito_filter.py b/pod/main/templatetags/flat_page_edito_filter.py index db2e0c7108..536bd399f8 100644 --- a/pod/main/templatetags/flat_page_edito_filter.py +++ b/pod/main/templatetags/flat_page_edito_filter.py @@ -65,10 +65,10 @@ def display_content_by_block(content, request): # noqa: C901 if content.no_cache is True: params["cache"] = False - debug_elts.append("Cache is disable for this part") + debug_elts.append("Cache is disabled for this part") else: params["cache"] = True - debug_elts.append("Cache is enable for this part") + debug_elts.append("Cache is enabled for this part") if content.nb_element is not None or content.nb_element != "": params["nb-element"] = int(content.nb_element) diff --git a/pod/podfile/forms.py b/pod/podfile/forms.py index 619f185496..d14f0db972 100644 --- a/pod/podfile/forms.py +++ b/pod/podfile/forms.py @@ -49,10 +49,10 @@ class FileSizeValidator(object): ) code = "invalid_max_size" - def __init__(self, *args, **kwargs): + def __init__(self, *args, **kwargs) -> None: self.max_size = FILE_MAX_UPLOAD_SIZE * 1024 * 1024 # MO - def __call__(self, value): + def __call__(self, value) -> None: # Check the file size filesize = len(value) if self.max_size and filesize > self.max_size: @@ -103,7 +103,7 @@ class CustomImageModelForm(forms.ModelForm): "data-maxsize": FILE_MAX_UPLOAD_SIZE * 1024 * 1024, } - def __init__(self, *args, **kwargs): + def __init__(self, *args, **kwargs) -> None: super(CustomImageModelForm, self).__init__(*args, **kwargs) self.fields["folder"].widget = forms.HiddenInput() valid_ext = FileExtensionValidator(IMAGE_ALLOWED_EXTENSIONS) diff --git a/pod/podfile/models.py b/pod/podfile/models.py index b58424c838..f850d6926c 100644 --- a/pod/podfile/models.py +++ b/pod/podfile/models.py @@ -23,6 +23,8 @@ class UserFolder(models.Model): + """Folder that will contain custom files.""" + name = models.CharField(_("Name"), max_length=255) # parent = models.ForeignKey( # 'self', blank=True, null=True, related_name='children') @@ -65,12 +67,14 @@ def __str__(self) -> str: return "{0}".format(self.name) def get_all_files(self) -> list: + """Get all files in a UserFolder.""" file_list = self.customfilemodel_set.all() image_list = self.customimagemodel_set.all() result_list = sorted(chain(image_list, file_list), key=attrgetter("uploaded_at")) return result_list def delete(self) -> None: + """Delete a UserForlder and it's content.""" for file in self.customfilemodel_set.all(): file.delete() for img in self.customimagemodel_set.all(): @@ -115,6 +119,8 @@ def get_upload_path_files(instance, filename) -> str: class BaseFileModel(models.Model): + """Esup-Pod Base File Model.""" + name = models.CharField(_("Name"), max_length=255) description = models.CharField(max_length=255, blank=True) folder = models.ForeignKey(UserFolder, on_delete=models.CASCADE) @@ -126,18 +132,23 @@ class BaseFileModel(models.Model): ) def save(self, **kwargs) -> None: + """Save a BaseFile in DB.""" path, ext = os.path.splitext(self.file.name) # if not self.name or self.name == "": self.name = os.path.basename(path) return super(BaseFileModel, self).save(**kwargs) def class_name(self) -> str: + """Get the BaseFileModel class name.""" return self.__class__.__name__ def file_exist(self) -> bool: + """Check if a BaseFileModel exist.""" return self.file and os.path.isfile(self.file.path) class Meta: + """BaseFileModel Metadata.""" + abstract = True ordering = ["name"] diff --git a/pod/podfile/rest_views.py b/pod/podfile/rest_views.py index 2090f3b86f..4cb038759f 100644 --- a/pod/podfile/rest_views.py +++ b/pod/podfile/rest_views.py @@ -1,3 +1,5 @@ +"""Esup-Pod REST views.""" + from .models import UserFolder from .models import CustomImageModel, CustomFileModel from rest_framework import serializers, viewsets diff --git a/pod/podfile/static/podfile/js/filewidget.js b/pod/podfile/static/podfile/js/filewidget.js index 213a25a31f..adee09cd85 100644 --- a/pod/podfile/static/podfile/js/filewidget.js +++ b/pod/podfile/static/podfile/js/filewidget.js @@ -3,16 +3,35 @@ * @since 2.5.0 */ -if (typeof loaded == "undefined") { - var loaded = true; +// Read-only globals defined in customfilewidget.html +/* +global id_input static_url deletefolder_url deletefile_url +*/ + +// Read-only globals defined in main.js +/* +global isJson fadeOut +*/ - const loader = ` +// Read-only globals defined in base.html +/* +global HIDE_USERNAME +*/ + + +var list_folders_sub; + +const loader = `
${gettext('Loading…')}
`; + +if (typeof loaded == "undefined") { + var loaded = true; + document.addEventListener("click", (e) => { if (!e.target.parentNode) return; if ( @@ -88,29 +107,6 @@ if (typeof loaded == "undefined") { bsdirs.hide(); }); - /*document.querySelectorAll("#open-folder-icon > *").forEach((el) => { - el.style = "pointer-events: none; cursor: pointer;"; - }); - if (document.getElementById("open-folder-icon")) { - document.getElementById("open-folder-icon").style.cursor = "pointer"; - }*/ - - /*document.addEventListener("click", (e) => { - if ( - e.target.id != "open-folder-icon" && - !e.target.matches("open-folder-icon i") - ) - return; - - //unable click on span or i - document.querySelectorAll(".folder_name").forEach((e) => { - e.style = "pointer-events: none; "; - }); - - e.preventDefault(); - document.getElementById("dirs").classList.add("open"); - });*/ - document.addEventListener("change", (e) => { if (e.target.id != "ufile") return; document.getElementById("formuploadfile").querySelector("button").click(); @@ -140,7 +136,7 @@ if (typeof loaded == "undefined") { body: data_form, processData: false, contentType: false, - headers:{ + headers: { 'X-Requested-With': 'XMLHttpRequest', //Necessary to work with is_ajax }, }) @@ -164,10 +160,10 @@ if (typeof loaded == "undefined") { showalert( gettext("Error during exchange") + - "(" + - data + - ")
" + - gettext("No data could be stored."), + "(" + + data + + ")
" + + gettext("No data could be stored."), "alert-danger" ); }); @@ -455,7 +451,7 @@ if (typeof loaded == "undefined") { document.addEventListener("click", (e) => { var contain_target = false; - if (document.getElementById("currentfolderdelete")){ + if (document.getElementById("currentfolderdelete")) { contain_target = document.getElementById("currentfolderdelete").contains(e.target); } if (e.target.id == "currentfolderdelete" || contain_target) { @@ -511,7 +507,7 @@ if (typeof loaded == "undefined") { document.addEventListener("input", (e) => { if (e.target.id != "folder-search") return; var text = e.target.value.toLowerCase(); - if (folder_searching === true ) { + if (folder_searching === true) { return; } else { if (text.length > 2 || text.length === 0) { @@ -810,7 +806,7 @@ if (typeof loaded == "undefined") { document.addEventListener("DOMContentLoaded", () => { if (typeof myFilesView !== "undefined") { getFolders(""); - folder_observer = add_folder_observer(); + var folder_observer = add_folder_observer(); folder_observer.observe(list_folders_sub, { childList: true, subtree: true }); } }); diff --git a/pod/podfile/templates/podfile/list_folder_files.html b/pod/podfile/templates/podfile/list_folder_files.html index 676bcab9ff..40892a31fa 100644 --- a/pod/podfile/templates/podfile/list_folder_files.html +++ b/pod/podfile/templates/podfile/list_folder_files.html @@ -20,7 +20,7 @@

{% endif %} {% if enr_is_already_asked %} {% endif %} {% if video.get_encoding_step == "" %} {% endif %} {% if video.encoding_in_progress %} {% endif %} {% if video.get_encoding_step == "5 : transcripting audio" %}

+ {% trans 'The video is currently being transcripted.' %}

{% endif %} diff --git a/pod/video/tests/test_models.py b/pod/video/tests/test_models.py index 6925444c04..634aa8ce38 100644 --- a/pod/video/tests/test_models.py +++ b/pod/video/tests/test_models.py @@ -3,49 +3,46 @@ * run with 'python manage.py test pod.video.tests.test_models' """ -from django.test import TestCase +from django.test import TestCase, Client +from django.db import transaction from django.db.models import Count, Q -from django.template.defaultfilters import slugify from django.db.models.fields.files import ImageFieldFile +from django.db.utils import IntegrityError +from django.template.defaultfilters import slugify from django.contrib.auth.models import User from django.utils.translation import ugettext_lazy as _ +from django.urls import reverse from django.conf import settings from django.core.exceptions import ValidationError -from django.db.utils import IntegrityError -from django.db import transaction -from ..models import Channel -from ..models import Theme -from ..models import Type -from ..models import Discipline -from ..models import Video -from ..models import ViewCount +from ..models import Channel, Theme, Type +from ..models import Discipline, Video, ViewCount from ..models import get_storage_path_video from ..models import VIDEOS_DIR from ..models import Notes, AdvancedNotes from ..models import UserMarkerTime, VideoAccessToken -from pod.video_encode_transcript.models import VideoRendition -from pod.video_encode_transcript.models import EncodingVideo -from pod.video_encode_transcript.models import EncodingAudio -from pod.video_encode_transcript.models import PlaylistVideo -from pod.video_encode_transcript.models import EncodingLog -from pod.video_encode_transcript.models import EncodingStep +from pod.video_encode_transcript.models import VideoRendition, PlaylistVideo +from pod.video_encode_transcript.models import EncodingVideo, EncodingAudio +from pod.video_encode_transcript.models import EncodingLog, EncodingStep -from datetime import datetime -from datetime import timedelta +from datetime import datetime, timedelta import os import uuid if getattr(settings, "USE_PODFILE", False): __FILEPICKER__ = True - from pod.podfile.models import CustomImageModel - from pod.podfile.models import UserFolder + from pod.podfile.models import CustomImageModel, UserFolder else: __FILEPICKER__ = False from pod.main.models import CustomImageModel +# ggignore-start +# gitguardian:ignore +PWD = "azerty1234" # nosec +# ggignore-end + class ChannelTestCase(TestCase): """Test the channels.""" @@ -283,7 +280,7 @@ class VideoTestCase(TestCase): def setUp(self) -> None: """Create videos to be tested.""" - user = User.objects.create(username="pod", password="pod1234pod") + user = User.objects.create(username="pod", password=PWD) # Video 1 with minimum attributes Video.objects.create( @@ -421,6 +418,78 @@ def test_get_dublin_core(self) -> None: print(" ---> test_get_dublin_core of Video: OK!") + def test_video_additional_owners_rights(self) -> None: + """Check that additional owners have the correct rights.""" + # Create 2nd and 3rd staff users + user2 = User.objects.create(username="user2", password=PWD) + user2.is_staff = True + user2.save() + user3 = User.objects.create(username="user3", password=PWD) + user3.is_staff = True + user3.save() + + # Get the test video and associated Userfolder + video = Video.objects.get(id=1) + video_folder = video.get_or_create_video_folder() + + # Add an additional owner to the video + video.additional_owners.set([user2]) + video.save() + + # Check user2 can access to video folder + client = Client() + client.force_login(user2) + response = client.post( + reverse("podfile:editfolder"), + { + "folderid": video_folder.id, + }, + follow=True, + ) + + print("response: %s" % response) + self.assertEqual(response.status_code, 200) # OK + # Replace aditional owner by another one + video.additional_owners.set([user3]) + video.save() + + # Check user2 no more access video folder + response = client.post( + reverse("podfile:editfolder"), + { + "folderid": video_folder.id, + }, + follow=True, + ) + self.assertEqual(response.status_code, 403) # forbidden + + print("---> test_video_additional_owners_rights of VideoTestCase: OK") + + def test_synced_user_folder(self) -> None: + """Check that UserFolder is synced with video params.""" + # Create 2nd staff user + user2 = User.objects.create(username="user2", password=PWD) + user2.is_staff = True + user2.save() + + # Get the test video and associated Userfolder + video = Video.objects.get(id=1) + video_folder = video.get_or_create_video_folder() + + # Then, change owner and rename the video + video.owner = user2 + video.title = "Video renamed", + video.save() + + video_folder2 = video.get_or_create_video_folder() + + # Check there is no duplicated folder + self.assertEqual(video_folder2.id, video_folder.id) + self.assertEqual(video_folder2.name, video.slug) + self.assertEqual(video_folder2.owner, video.owner) + + print("---> test_synced_user_folder of VideoTestCase: OK") + class VideoRenditionTestCase(TestCase): """Test the Video Rendition.""" @@ -429,14 +498,14 @@ class VideoRenditionTestCase(TestCase): def create_video_rendition( self, - resolution="640x360", - minrate="500k", - video_bitrate="1000k", - maxrate="2000k", - audio_bitrate="300k", - encode_mp4=False, + resolution: str = "640x360", + minrate: str = "500k", + video_bitrate: str = "1000k", + maxrate: str = "2000k", + audio_bitrate: str = "300k", + encode_mp4: bool = False, ) -> VideoRendition: - # print("create_video_rendition: %s" % resolution) + """Create a video rendition.""" return VideoRendition.objects.create( resolution=resolution, minrate=minrate, @@ -524,7 +593,7 @@ class EncodingVideoTestCase(TestCase): # fixtures = ['initial_data.json', ] def setUp(self) -> None: - user = User.objects.create(username="pod", password="pod1234pod") + user = User.objects.create(username="pod", password=PWD) Type.objects.create(title="test") Video.objects.create( title="Video1", @@ -607,7 +676,7 @@ class EncodingAudioTestCase(TestCase): # fixtures = ['initial_data.json', ] def setUp(self) -> None: - user = User.objects.create(username="pod", password="pod1234pod") + user = User.objects.create(username="pod", password=PWD) Type.objects.create(title="test") Video.objects.create( title="Video1", @@ -676,7 +745,7 @@ class PlaylistVideoTestCase(TestCase): ] def setUp(self) -> None: - user = User.objects.create(username="pod", password="pod1234pod") + user = User.objects.create(username="pod", password=PWD) Video.objects.create( title="Video1", owner=user, @@ -744,7 +813,7 @@ class EncodingLogTestCase(TestCase): ] def setUp(self) -> None: - user = User.objects.create(username="pod", password="pod1234pod") + user = User.objects.create(username="pod", password=PWD) Video.objects.create( title="Video1", owner=user, @@ -785,7 +854,7 @@ class EncodingStepTestCase(TestCase): ] def setUp(self) -> None: - user = User.objects.create(username="pod", password="pod1234pod") + user = User.objects.create(username="pod", password=PWD) Video.objects.create( title="Video1", owner=user, @@ -832,7 +901,7 @@ class NotesTestCase(TestCase): ] def setUp(self) -> None: - user = User.objects.create(username="pod", password="pod1234pod") + user = User.objects.create(username="pod", password=PWD) Video.objects.create( title="Video1", owner=user, @@ -910,7 +979,7 @@ class UserMarkerTimeTestCase(TestCase): ] def setUp(self) -> None: - user = User.objects.create(username="pod", password="pod1234pod") + user = User.objects.create(username="pod", password=PWD) Video.objects.create( title="Video1", owner=user, @@ -985,7 +1054,7 @@ class VideoAccessTokenTestCase(TestCase): ] def setUp(self) -> None: - user = User.objects.create(username="pod", password="pod1234pod") + user = User.objects.create(username="pod", password=PWD) print("VIDEO: %s" % Video.objects.all().count()) self.video = Video.objects.create( title="Video1", diff --git a/pod/video_encode_transcript/Encoding_video_model.py b/pod/video_encode_transcript/Encoding_video_model.py index 7781c4995a..deaa5712a8 100644 --- a/pod/video_encode_transcript/Encoding_video_model.py +++ b/pod/video_encode_transcript/Encoding_video_model.py @@ -48,7 +48,6 @@ if getattr(settings, "USE_PODFILE", False): __FILEPICKER__ = True from pod.podfile.models import CustomImageModel - from pod.podfile.models import UserFolder from pod.podfile.models import CustomFileModel else: __FILEPICKER__ = False @@ -59,7 +58,7 @@ class Encoding_video_model(Encoding_video): """Encoding video model.""" - def remove_old_data(self): + def remove_old_data(self) -> None: """Remove data from previous encoding.""" video_to_encode = Video.objects.get(id=self.id) video_to_encode.thumbnail = None @@ -91,7 +90,7 @@ def remove_previous_encoding_log(self, video_to_encode): msg += "Audio: Nothing to delete" return msg - def remove_previous_encoding_objects(self, model_class, video_to_encode): + def remove_previous_encoding_objects(self, model_class, video_to_encode) -> str: """Remove previously encoded objects of the given model.""" msg = "\n" object_type = model_class.__name__ @@ -106,15 +105,15 @@ def remove_previous_encoding_objects(self, model_class, video_to_encode): msg += "Video: Nothing to delete" return msg - def remove_previous_encoding_video(self, video_to_encode): + def remove_previous_encoding_video(self, video_to_encode) -> str: """Remove previously encoded video.""" return self.remove_previous_encoding_objects(EncodingVideo, video_to_encode) - def remove_previous_encoding_audio(self, video_to_encode): + def remove_previous_encoding_audio(self, video_to_encode) -> str: """Remove previously encoded audio.""" return self.remove_previous_encoding_objects(EncodingAudio, video_to_encode) - def remove_previous_encoding_playlist(self, video_to_encode): + def remove_previous_encoding_playlist(self, video_to_encode) -> str: """Remove previously encoded playlist.""" return self.remove_previous_encoding_objects(PlaylistVideo, video_to_encode) @@ -122,7 +121,7 @@ def get_true_path(self, original): """Get the true path by replacing the MEDIA_ROOT from the original path.""" return original.replace(os.path.join(settings.MEDIA_ROOT, ""), "") - def store_json_list_mp3_m4a_files(self, info_video, video_to_encode): + def store_json_list_mp3_m4a_files(self, info_video, video_to_encode) -> None: """Store JSON list of MP3 and M4A files for encoding.""" encoding_list = ["list_m4a_files", "list_mp3_files"] for encode_item in encoding_list: @@ -140,7 +139,7 @@ def store_json_list_mp3_m4a_files(self, info_video, video_to_encode): source_file=self.get_true_path(mp3_files[audio_file]), ) - def store_json_list_mp4_hls_files(self, info_video, video_to_encode): + def store_json_list_mp4_hls_files(self, info_video, video_to_encode) -> None: mp4_files = info_video["list_mp4_files"] for video_file in mp4_files: if not check_file(mp4_files[video_file]): @@ -188,7 +187,7 @@ def store_json_list_mp4_hls_files(self, info_video, video_to_encode): source_file=playlist_file, ) - def store_json_encoding_log(self, info_video, video_to_encode): + def store_json_encoding_log(self, info_video, video_to_encode) -> None: # Need to modify start and stop log_to_text = "" # logs = info_video["encoding_log"] @@ -219,13 +218,10 @@ def store_json_encoding_log(self, info_video, video_to_encode): ) encoding_log.save() - def store_json_list_subtitle_files(self, info_video, video_to_encode): + def store_json_list_subtitle_files(self, info_video, video_to_encode) -> None: list_subtitle_files = info_video["list_subtitle_files"] if __FILEPICKER__: - videodir, created = UserFolder.objects.get_or_create( - name="%s" % video_to_encode.slug, - owner=video_to_encode.owner, - ) + videodir = video_to_encode.get_or_create_video_folder() for sub in list_subtitle_files: if not check_file(list_subtitle_files[sub][1]): @@ -259,16 +255,13 @@ def store_json_list_subtitle_files(self, info_video, video_to_encode): enrich_ready=True, ) - def store_json_list_thumbnail_files(self, info_video): + def store_json_list_thumbnail_files(self, info_video) -> Video: """store_json_list_thumbnail_files.""" video = Video.objects.get(id=self.id) list_thumbnail_files = info_video["list_thumbnail_files"] thumbnail = CustomImageModel() if __FILEPICKER__: - videodir, created = UserFolder.objects.get_or_create( - name="%s" % video.slug, - owner=video.owner, - ) + videodir = video.get_or_create_video_folder() thumbnail = CustomImageModel(folder=videodir, created_by=video.owner) for index, thumbnail_path in enumerate(list_thumbnail_files): if check_file(list_thumbnail_files[thumbnail_path]): @@ -298,7 +291,7 @@ def store_json_list_overview_files(self, info_video) -> Video: video.save() return video - def wait_for_file(self, filepath): + def wait_for_file(self, filepath) -> None: time_to_wait = 40 time_counter = 0 while not os.path.exists(filepath): @@ -367,7 +360,7 @@ def get_create_thumbnail_command_from_video(self, video_to_encode): encoding_log.save() return thumbnail_command - def recreate_thumbnail(self): + def recreate_thumbnail(self) -> None: self.create_output_dir() self.get_video_data() info_video = {} diff --git a/pod/video_encode_transcript/transcript.py b/pod/video_encode_transcript/transcript.py index 1cda78785e..f55bbb1ed0 100644 --- a/pod/video_encode_transcript/transcript.py +++ b/pod/video_encode_transcript/transcript.py @@ -38,7 +38,6 @@ if getattr(settings, "USE_PODFILE", False): __FILEPICKER__ = True from pod.podfile.models import CustomFileModel - from pod.podfile.models import UserFolder else: __FILEPICKER__ = False from pod.main.models import CustomFileModel @@ -160,9 +159,7 @@ def saveVTT(video: Video, webvtt: WebVTT, lang_code: str = None): improveCaptionsAccessibility(webvtt) msg += "\nstore vtt file in bdd with CustomFileModel model file field" if __FILEPICKER__: - videodir, created = UserFolder.objects.get_or_create( - name="%s" % video.slug, owner=video.owner - ) + videodir = video.get_or_create_video_folder() """ previousSubtitleFile = CustomFileModel.objects.filter( name__startswith="subtitle_%s" % lang,