diff --git a/backend/apimanager/views.py b/backend/apimanager/views.py index eae401d..66de62c 100644 --- a/backend/apimanager/views.py +++ b/backend/apimanager/views.py @@ -1,10 +1,13 @@ #######################Django related imports#################### +import os +import random +from rest_framework import generics, status from rest_framework.views import APIView from rest_framework.response import Response from rest_framework.parsers import MultiPartParser, FormParser from django.core.files.storage import default_storage -from rest_framework import generics, status -import random +from django.conf import settings +from django.http import JsonResponse ################################################################# #API related imports from .models import ( @@ -113,77 +116,50 @@ class MediaUpload(APIView): files = request.FILES.getlist('media') resource_type = file_serializer.validated_data['resource_type'] resource_id = file_serializer.validated_data['resource_id'] - file_path_base = f'static/rangolio_data' + file_path_base = f'rangolio_data' for f in files: - file_unique_slug = ''.join(random.choices('ABCDEabcde1234', k=5)) - file_path = f"{file_path_base}/{resource_type}/{resource_id}/media/{file_unique_slug+resource_id+f.name}" + file_unique_slug = ''.join(random.choices('ABCDEabcde12345', k=6)) + if resource_id != resource_type: + file_path = f"{file_path_base}/{resource_type}/{resource_id}/media/{file_unique_slug+resource_id+f.name}" + else: + file_path = f"{file_path_base}/{resource_type}/media/{file_unique_slug+resource_id+f.name}" default_storage.save(file_path, f) return Response(file_serializer.data, status=status.HTTP_201_CREATED) else: return Response(file_serializer.errors, status=status.HTTP_400_BAD_REQUEST) +class ListMedia(APIView): + def get(self, request, resource_type, resource_id, format=None): + if resource_id != resource_type: + media_folder = os.path.join(settings.MEDIA_ROOT, 'rangolio_data', resource_type, resource_id, 'media') + else: + media_folder = os.path.join(settings.MEDIA_ROOT, 'rangolio_data', resource_type, 'media') + if not os.path.exists(media_folder): + return Response({'error': 'Media directory not found'}, status=status.HTTP_404_NOT_FOUND) -''' -class ETLFunctions(GenericAPIView): + media_files = [f for f in os.listdir(media_folder) if f.endswith(('.png', '.jpg', '.jpeg'))] + if resource_id != resource_type: + media_urls = [request.build_absolute_uri(f'{settings.MEDIA_URL}rangolio_data/{resource_type}/{resource_id}/media/' + f) for f in media_files] + else: + media_urls = [request.build_absolute_uri(f'{settings.MEDIA_URL}rangolio_data/{resource_type}/media/' + f) for f in media_files] - serializer_class = ETLData + return Response({'media': media_urls}, status=status.HTTP_200_OK) - def post(self, request, *args, **kwargs): - serializer = self.get_serializer(data=request.data) - serializer.is_valid(raise_exception=True) - data = serializer.data - if data['operation'] == "create-folder": - os.mkdir('../Analysis/'+data['postData']) - with open('../Analysis/'+data['postData']+'/'+data['postData']+'-run.log', 'w') as fp: - pass - fp.close() + def delete(self, request, resource_type, resource_id, format=None): + if resource_id != resource_type: + media_folder = os.path.join(settings.MEDIA_ROOT, 'rangolio_data', resource_type, resource_id, 'media') + else: + media_folder = os.path.join(settings.MEDIA_ROOT, 'rangolio_data', resource_type, 'media') + file_name = request.query_params.get('file') + if not file_name or not file_name.endswith(('.png', '.jpg', '.jpeg')): + return Response({'error': 'Invalid or no file name provided'}, status=status.HTTP_400_BAD_REQUEST) - if data['operation'] == "rename-folder": - os.rename('../Analysis/'+data['oldTitle'], '../Analysis/'+data['postData']) - - if data['operation'] == "create-partition-file": - print(os.getcwd()) - print(os.listdir(os.getcwd())) - partInfo = (data['postData'].split('|')) - with open(f'"{partInfo[0]}.part"', "w") as fpp: - pass - fpp.close() - partitionFile = open(f'"{partInfo[0]}.part"', "w+") - partitionFile.write(partInfo[1]) - partitionFile.close() - - if data['operation'] == "move-file": - pass - - return Response("Success", status=status.HTTP_200_OK) - -class BioTools(APIView): - def get(self, request): - params = request.GET.get('function', '') - params = params.split(";") - output = subprocess.check_output(f'seqmagick extract-ids ../Analysis/"{params[1]}"/"{(params[2])[:-1]}"', shell=True) - outgroups = (output.decode("utf-8")).split('\n') - outgroups = outgroups[:len(outgroups)-1] - return Response({'outgroups': outgroups}) - -class CommandRunner(GenericAPIView): - - serializer_class = InterimData - - def post(self, request, *args, **kwargs): - serializer = self.get_serializer(data=request.data) - serializer.is_valid(raise_exception=True) - data = serializer.data - runLogFile = f'../Analysis/{data["nodeName"]}/{data["nodeName"]}-run.log' - - try: - commands = ast.literal_eval(data['finalParameter']) - for key, value in commands.items(): - process = subprocess.Popen(value+f" > {runLogFile}", shell=True) - except: - process = subprocess.Popen(data['finalParameter']+f" > {runLogFile}", shell=True) - return Response("Command successfully sent for execution", status=status.HTTP_200_OK) -''' \ No newline at end of file + file_path = os.path.join(media_folder, file_name) + if os.path.exists(file_path): + os.remove(file_path) + return Response({'message': 'File deleted successfully'}, status=status.HTTP_204_NO_CONTENT) + else: + return Response({'error': 'File not found'}, status=status.HTTP_404_NOT_FOUND) \ No newline at end of file diff --git a/backend/backend/settings.py b/backend/backend/settings.py index aea85db..fff0a97 100644 --- a/backend/backend/settings.py +++ b/backend/backend/settings.py @@ -159,6 +159,8 @@ STATIC_URL = 'static/' DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' +MEDIA_URL = '/media/' +MEDIA_ROOT = os.path.join(BASE_DIR, 'media') CSRF_TRUSTED_ORIGINS = [ 'http://localhost:3000', diff --git a/backend/backend/urls.py b/backend/backend/urls.py index 8a81dab..f3c9694 100644 --- a/backend/backend/urls.py +++ b/backend/backend/urls.py @@ -16,8 +16,8 @@ Including another URLconf """ from django.contrib import admin from django.urls import path, re_path -from django.conf.urls import include -from .views import my_form +from django.conf import settings +from django.conf.urls.static import static from apimanager.views import ( UserDataUpdateAPIView, UserDataListAPIView, @@ -32,7 +32,8 @@ from apimanager.views import ( BlogRetrieveAPIView, BlogDeleteAPIView, BlogsByCategoryAPIView, - MediaUpload + MediaUpload, + ListMedia ) urlpatterns = [ @@ -51,4 +52,6 @@ urlpatterns = [ path('data/blog/update//', BlogUpdateAPIView.as_view(), name='blog-update-view'), path('data/blog/delete//', BlogDeleteAPIView.as_view(), name='blog-delete-view'), path('data/upload/', MediaUpload.as_view(), name='media-upload'), -] \ No newline at end of file + path('data/media///', ListMedia.as_view(), name='list-media'), +] +urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) \ No newline at end of file diff --git a/backend/resourceType/resourceId/Screenshot_20240324_111611_Firefox.jpg b/backend/resourceType/resourceId/Screenshot_20240324_111611_Firefox.jpg deleted file mode 100644 index 1e79f22..0000000 Binary files a/backend/resourceType/resourceId/Screenshot_20240324_111611_Firefox.jpg and /dev/null differ diff --git a/frontend/src/components/editable/home.jsx b/frontend/src/components/editable/home.jsx index fb7fe0a..3ac8cbe 100755 --- a/frontend/src/components/editable/home.jsx +++ b/frontend/src/components/editable/home.jsx @@ -55,7 +55,7 @@ function HomePage(props) { This field cannot be empty :''} - + diff --git a/frontend/src/components/editable/shared/media-lister.jsx b/frontend/src/components/editable/shared/media-lister.jsx new file mode 100644 index 0000000..451d5ec --- /dev/null +++ b/frontend/src/components/editable/shared/media-lister.jsx @@ -0,0 +1,52 @@ +import React, { useEffect, useState } from 'react'; +import { Button } from 'reactstrap'; +import EditableDataService from '../../../services/editable-data-service'; + +function MediaLister(props) { + const [media, setMedia] = useState([]); + + const fetchMedia = async () => { + try { + const response = await EditableDataService.getData(`/data/media/${props.resourceType}/${props.resourceId}/`); + setMedia(response.data.media); + } catch (error) { + props.notificationToggler('Error fetching media', 'danger') + } + }; + + useEffect(() => { + fetchMedia(); + }, []); + + const deleteMedia = (mediaUrl) => { + // Extract the file name from the mediaUrl + const fileName = mediaUrl.substring(mediaUrl.lastIndexOf('/') + 1); + + EditableDataService.deleteData(`/data/media/${props.resourceType}/${props.resourceId}/?file=${fileName}`) + .then(() => { + props.notificationToggler('Media deleted') + fetchMedia() + }) + .catch(error => { + props.notificationToggler('Error deleting media', 'danger') + }); + }; + + + return ( +
+

+ Choose media to insert +

+ {media.map(image => ( +
+ + + +
+ ))} +
+ ); +} + +export default MediaLister; \ No newline at end of file diff --git a/frontend/src/components/editable/shared/media-upload.jsx b/frontend/src/components/editable/shared/media-upload.jsx index 171dfc4..deb527b 100644 --- a/frontend/src/components/editable/shared/media-upload.jsx +++ b/frontend/src/components/editable/shared/media-upload.jsx @@ -1,5 +1,6 @@ import React, { useState } from 'react'; import FileComponent from './file-component.jsx'; +import MediaLister from './media-lister.jsx'; import { Button, ButtonGroup, Modal, ModalHeader, ModalBody, ModalFooter} from 'reactstrap'; function MediaUpload(props) { @@ -36,9 +37,7 @@ function MediaUpload(props) {
{ action === 'insert' ?
-

- Choose media to insert -

+
:
diff --git a/frontend/src/components/editable/shared/tiptap-custom-extensions/custom-image-extension.jsx b/frontend/src/components/editable/shared/tiptap-custom-extensions/custom-image-extension.jsx index 706635a..d44d94b 100644 --- a/frontend/src/components/editable/shared/tiptap-custom-extensions/custom-image-extension.jsx +++ b/frontend/src/components/editable/shared/tiptap-custom-extensions/custom-image-extension.jsx @@ -24,7 +24,7 @@ function ImageNode(props) { return (
- onEditAlt()} className='mx-auto d-block' src={src} alt={alt} /> + {alt}
{ alt ? diff --git a/frontend/src/components/editable/shared/tiptap.jsx b/frontend/src/components/editable/shared/tiptap.jsx index 849a7e5..1433e7b 100755 --- a/frontend/src/components/editable/shared/tiptap.jsx +++ b/frontend/src/components/editable/shared/tiptap.jsx @@ -29,6 +29,16 @@ import MediaUpload from './media-upload.jsx' const MenuBar = (props) => { const { editor } = useCurrentEditor() + const addMedia = (url) => { + editor.commands.setImage({ + src: url, + }); + }; + + if (!editor) { + return null + } + useEffect(() => { if (editor){ const handleChange = () => { @@ -321,6 +331,7 @@ const MenuBar = (props) => { > + ) } @@ -361,8 +372,7 @@ export default (props) => { if (props.content && GlobalTheme && ThemeConfig) return ( <> - - } extensions={extensions} content={props.content}> + } extensions={extensions} content={props.content}> ) } \ No newline at end of file