From d752da99273e726facc67971245cb95eb10d76e6 Mon Sep 17 00:00:00 2001
From: MeisterSeSe <105605891+MeisterSeSe@users.noreply.github.com>
Date: Thu, 19 Oct 2023 14:44:28 +0200
Subject: [PATCH 01/14] Refactor: Adapted Multiple Upload Card
---
.../upload_cards/file_create/Multiple.vue | 39 ++++++++-----------
1 file changed, 17 insertions(+), 22 deletions(-)
diff --git a/frontendVue3/src/components/upload_cards/file_create/Multiple.vue b/frontendVue3/src/components/upload_cards/file_create/Multiple.vue
index 15cdd40c..8ba2f7fd 100644
--- a/frontendVue3/src/components/upload_cards/file_create/Multiple.vue
+++ b/frontendVue3/src/components/upload_cards/file_create/Multiple.vue
@@ -25,21 +25,21 @@
show-size
>
-
-
-
-
+
+
+
+ mdi-information
+
+
+
@@ -161,7 +161,7 @@ let formData = reactive({
label: '',
description: '',
files: null,
- license: null,
+ license: "CC BY - SA 4.0 DEED",
family: null,
version: '',
tags: [],
@@ -174,11 +174,6 @@ const showDetails = ref(false);
let fileRules = [(v) => !!v || 'File is required'];
-let { licenses } = storeToRefs(fileStore);
-let licenseRules = [(v) => !!v || 'License is required'];
-
-let familyRules = [(v) => !!v || 'Family is required'];
-
let checkboxRules = [(v) => !!v || 'Checkbox must be checked'];
From 5899183ca9081e6a827bd1fd6690f23db52775d2 Mon Sep 17 00:00:00 2001
From: MeisterSeSe <105605891+MeisterSeSe@users.noreply.github.com>
Date: Thu, 19 Oct 2023 14:44:35 +0200
Subject: [PATCH 02/14] Refactor: Adapted Single Upload Card
---
.../upload_cards/file_create/Single.vue | 82 ++++++++++---------
1 file changed, 43 insertions(+), 39 deletions(-)
diff --git a/frontendVue3/src/components/upload_cards/file_create/Single.vue b/frontendVue3/src/components/upload_cards/file_create/Single.vue
index ae716572..dd000493 100644
--- a/frontendVue3/src/components/upload_cards/file_create/Single.vue
+++ b/frontendVue3/src/components/upload_cards/file_create/Single.vue
@@ -2,6 +2,20 @@
+
+
+
-
-
-
-
-
-
-
+
+
+
+ mdi-information
+
+
+
@@ -67,7 +68,6 @@
item-title="label"
item-value="id"
:required="true"
- :rules="familyRules"
variant="outlined"
density="comfortable"
hint="Add to or create new family"
@@ -176,7 +176,7 @@
+
+
From 0b913b4f430d6de603052f005b80cfc381092faf Mon Sep 17 00:00:00 2001
From: MeisterSeSe <105605891+MeisterSeSe@users.noreply.github.com>
Date: Thu, 19 Oct 2023 14:44:44 +0200
Subject: [PATCH 03/14] Refactor: Adapted Zip Upload Card
---
.../upload_cards/file_create/Zip.vue | 71 +++++++++----------
1 file changed, 35 insertions(+), 36 deletions(-)
diff --git a/frontendVue3/src/components/upload_cards/file_create/Zip.vue b/frontendVue3/src/components/upload_cards/file_create/Zip.vue
index 22c56dab..9cce6ed5 100644
--- a/frontendVue3/src/components/upload_cards/file_create/Zip.vue
+++ b/frontendVue3/src/components/upload_cards/file_create/Zip.vue
@@ -2,6 +2,20 @@
+
+
+
+
-
-
-
-
-
-
+
+
+ mdi-information
+
+
+
@@ -126,7 +127,7 @@
!!v || 'Label is required'];
let descriptionRules = [
- (v) => !!v || 'Description is required',
(v) => v.length <= 250 || 'Max 250 characters please',
];
let fileRules = [(v) => !!v || 'File is required'];
-let { licenses, tags, myOwnTags } = storeToRefs(fileStore);
-let licenseRules = [(v) => !!v || 'License is required'];
+let { tags, myOwnTags } = storeToRefs(fileStore);
-let familyRules = [(v) => !!v || 'Family is required'];
let checkboxRules = [(v) => !!v || 'Checkbox must be checked'];
-//let versionRules = [(v) => !!v || 'Version is required'];
-
let addTagMenu = ref(false);
let addFamilyMenu = ref(false);
+const redirectToLicensePage = () => {
+ window.open('https://creativecommons.org/licenses/by-sa/4.0/deed.de', '_blank');
+};
From e21e4d2af76e1290dd4a1b940d99d5e975bac7e9 Mon Sep 17 00:00:00 2001
From: MeisterSeSe <105605891+MeisterSeSe@users.noreply.github.com>
Date: Thu, 19 Oct 2023 14:45:09 +0200
Subject: [PATCH 04/14] Feat: Implemented DefaultLicense
---
.../upload_cards/file_create/ActionButtons.vue | 11 ++++++-----
frontendVue3/src/store/file.js | 2 ++
2 files changed, 8 insertions(+), 5 deletions(-)
diff --git a/frontendVue3/src/components/upload_cards/file_create/ActionButtons.vue b/frontendVue3/src/components/upload_cards/file_create/ActionButtons.vue
index b2e8ee82..0d35ca8c 100644
--- a/frontendVue3/src/components/upload_cards/file_create/ActionButtons.vue
+++ b/frontendVue3/src/components/upload_cards/file_create/ActionButtons.vue
@@ -24,11 +24,12 @@
From 282265a8964a33375955114b8cd3ec669e64c176 Mon Sep 17 00:00:00 2001
From: MeisterSeSe <105605891+MeisterSeSe@users.noreply.github.com>
Date: Fri, 20 Oct 2023 10:45:09 +0200
Subject: [PATCH 09/14] Fix: Added TagsFIlter for all Views
---
.../src/components/FeatureModelTable.vue | 16 +++++++++++++---
frontendVue3/src/views/HistoryDetail.vue | 12 ++++++------
frontendVue3/src/views/Home.vue | 5 +++--
3 files changed, 22 insertions(+), 11 deletions(-)
diff --git a/frontendVue3/src/components/FeatureModelTable.vue b/frontendVue3/src/components/FeatureModelTable.vue
index a6c281e7..3e5f3faf 100644
--- a/frontendVue3/src/components/FeatureModelTable.vue
+++ b/frontendVue3/src/components/FeatureModelTable.vue
@@ -21,20 +21,21 @@
vertical
>
-
+ >
{{ new Date(item.raw.uploaded_at).toLocaleString('en-US') }}
- {{ noDataText }}
+ {{ noDataMessage }}
@@ -289,6 +290,15 @@ const createDialog = ref(false);
const checkLocalStorage = computed(() => {
return !!localStorage.featureModelData;
});
+const noDataMessage = computed(() => {
+ // Wenn entweder die Suche oder die Tags aktiviert sind und keine passenden Elemente gefunden wurden
+ if ((search.value || selectedTags.value.length > 0) && filteredItems.value.length === 0) {
+ return "No Feature Model matches the search criteria.";
+ } else {
+ // Wenn weder Suche noch Tags aktiviert sind oder passende Elemente gefunden wurden
+ return props.noDataText;
+ }
+});
const filteredItems = computed(() => {
// Wenn die Suche leer ist, zeige alle Elemente
if (!search.value && selectedTags.value.length === 0) {
diff --git a/frontendVue3/src/views/HistoryDetail.vue b/frontendVue3/src/views/HistoryDetail.vue
index 2ee73f46..1805e2b4 100644
--- a/frontendVue3/src/views/HistoryDetail.vue
+++ b/frontendVue3/src/views/HistoryDetail.vue
@@ -221,7 +221,7 @@
:no-data-text="`No feature models in ${family.label} yet`"
:addable="false"
headline="Feature Models of Family"
- />
+ :available-tags="tags"/>
@@ -235,16 +235,15 @@ import {busyBoxConfigs} from "@/assets/busyBoxAnalyzeExample";
import {useDisplay, useTheme} from "vuetify";
import {VSkeletonLoader} from 'vuetify/labs/VSkeletonLoader'
import LineChart from '@/components/Charts/LineChart.vue';
-
-
-
-
-
+import {useFileStore} from "@/store/file";
+import {storeToRefs} from "pinia";
const breakpoints = useDisplay();
const theme = useTheme();
const route = useRoute();
const API_URL = import.meta.env.VITE_APP_DOMAIN;
+const fileStore = useFileStore();
+const { tags } = storeToRefs(useFileStore());
const family = ref({});
const files = ref([]);
@@ -364,5 +363,6 @@ function onElementHover(elem) {
onMounted(async () => {
await getFamily();
await fetchFeatureModelOfFamily();
+ fileStore.fetchTags();
});
diff --git a/frontendVue3/src/views/Home.vue b/frontendVue3/src/views/Home.vue
index 470e38dc..ddf09111 100644
--- a/frontendVue3/src/views/Home.vue
+++ b/frontendVue3/src/views/Home.vue
@@ -8,6 +8,7 @@
@@ -37,7 +38,7 @@ import { onMounted, ref } from 'vue';
import { storeToRefs } from 'pinia';
import { useFileStore } from '@/store/file';
-const { confirmedFeatureModels } = storeToRefs(useFileStore());
+const { confirmedFeatureModels, tags } = storeToRefs(useFileStore());
const fileStore = useFileStore();
const search = '';
@@ -79,7 +80,6 @@ const defaultItem = {
};
const licenses = [];
const families = [];
-const tags = [];
const check1 = false;
const check2 = false;
const check3 = false;
@@ -127,5 +127,6 @@ const tutorialSteps = [
onMounted(() => {
fileStore.fetchConfirmedFeatureModels();
+ fileStore.fetchTags();
});
From 90a1395b9753ff6c720109c62bd8e2eb6279bf9a Mon Sep 17 00:00:00 2001
From: MeisterSeSe <105605891+MeisterSeSe@users.noreply.github.com>
Date: Fri, 20 Oct 2023 14:19:09 +0200
Subject: [PATCH 10/14] Fix: Removed Register Button
---
frontendVue3/src/components/Navbar.vue | 7 -------
1 file changed, 7 deletions(-)
diff --git a/frontendVue3/src/components/Navbar.vue b/frontendVue3/src/components/Navbar.vue
index 45e4bcea..f6fab250 100644
--- a/frontendVue3/src/components/Navbar.vue
+++ b/frontendVue3/src/components/Navbar.vue
@@ -85,13 +85,6 @@
>
Admin
-
- Register
-
Date: Fri, 27 Oct 2023 12:57:03 +0200
Subject: [PATCH 11/14] Feat: Added Private Field for File Model
---
.../fileupload/migrations/0014_file_private.py | 18 ++++++++++++++++++
backend/core/fileupload/models.py | 6 +++++-
2 files changed, 23 insertions(+), 1 deletion(-)
create mode 100644 backend/core/fileupload/migrations/0014_file_private.py
diff --git a/backend/core/fileupload/migrations/0014_file_private.py b/backend/core/fileupload/migrations/0014_file_private.py
new file mode 100644
index 00000000..bb4070f8
--- /dev/null
+++ b/backend/core/fileupload/migrations/0014_file_private.py
@@ -0,0 +1,18 @@
+# Generated by Django 4.2.2 on 2023-10-26 07:29
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('core_fileupload', '0013_file_private_alter_file_family_alter_file_version'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='file',
+ name='private',
+ field=models.BooleanField(default=False),
+ ),
+ ]
diff --git a/backend/core/fileupload/models.py b/backend/core/fileupload/models.py
index 1b1eec76..0bf48814 100644
--- a/backend/core/fileupload/models.py
+++ b/backend/core/fileupload/models.py
@@ -119,8 +119,9 @@ def save_file(self, local_file, **kwargs):
raise TypeError("Tags is not set")
family = kwargs.get("family", None)
version = kwargs.get("version", None)
+ private = kwargs.get("private", None)
# get license from id
- if kwargs.get("license", None) is None:
+ if kwargs.get("license", None) is None and kwargs.get("private") is None:
raise TypeError("License not set!")
file = self.model(**kwargs)
@@ -176,6 +177,9 @@ class File(models.Model):
) # indicates if the user confirmed the upload
slug = models.SlugField(null=True)
confirmation_token = models.CharField(default="", max_length=255)
+ private = models.BooleanField(
+ default=False
+ )
def __str__(self):
# do not change that
From 482c754783a3a9c86418f436ffd24df4b4f433e5 Mon Sep 17 00:00:00 2001
From: MeisterSeSe <105605891+MeisterSeSe@users.noreply.github.com>
Date: Fri, 27 Oct 2023 12:57:44 +0200
Subject: [PATCH 12/14] Feat: Added new ViewSets for PrivateFileUpload
---
backend/core/fileupload/viewsets.py | 105 +++++++++++++++++++++++-----
backend/core/routers.py | 4 +-
2 files changed, 92 insertions(+), 17 deletions(-)
diff --git a/backend/core/fileupload/viewsets.py b/backend/core/fileupload/viewsets.py
index 037611b8..4e7cf4a1 100644
--- a/backend/core/fileupload/viewsets.py
+++ b/backend/core/fileupload/viewsets.py
@@ -180,7 +180,6 @@ def check_family(self, request, family_id):
return family and family.owner == request.user
return True
-
def check_tags(self, request, tag_ids):
for tag_id in tag_ids:
tag = Tag.objects.get(pk=tag_id)
@@ -204,21 +203,72 @@ def schedule_analysis(self, fs):
analysis_result.save()
-class BulkUploadApiView(UploadApiView):
+class PrivateFileUploadApiView(APIView):
+ permission_classes = [permissions.IsAuthenticated]
+
def post(self, request, format=None):
if not request.data["files"]:
return Response(
- {"files": "This field may not be blank."},
- status.HTTP_400_BAD_REQUEST,
- )
+ {"files": "This field may not be blank."},
+ status.HTTP_400_BAD_REQUEST,
+ )
try:
files = json.loads(request.data["files"])
except:
return Response(
- {"files": "This field must contain JSON."},
- status.HTTP_400_BAD_REQUEST,
+ {"files": "This field must contain JSON."},
+ status.HTTP_400_BAD_REQUEST,
+ )
+ for uploaded_file in files:
+ label = uploaded_file["label"]
+ description = uploaded_file["description"]
+ file = request.FILES[uploaded_file["file"]]
+ license = uploaded_file["license"]
+ tags = uploaded_file["tags"]
+
+ if not label or not file:
+ return Response(
+ {"message": "Missing required fields"},
+ status=status.HTTP_400_BAD_REQUEST,
)
+ validated_data = QueryDict("", mutable=True)
+ validated_data["label"] = label
+ validated_data["description"] = description
+ validated_data.setlist("tags", tags)
+ validated_data["local_file"] = file
+ validated_data["private"] = True
+ validated_data["license"] = license
+
+ fs = FilesSerializer(data=validated_data)
+ if fs.is_valid():
+ fs.save(owner=request.user)
+ return Response(
+ {"message": "File uploaded successfully"},
+ status=status.HTTP_201_CREATED,
+ )
+ else:
+ return Response(
+ {"message": "Invalid file format"},
+ status=status.HTTP_400_BAD_REQUEST,
+ )
+
+
+class BulkUploadApiView(UploadApiView):
+ def post(self, request, format=None):
+ if not request.data["files"]:
+ return Response(
+ {"files": "This field may not be blank."},
+ status.HTTP_400_BAD_REQUEST,
+ )
+ try:
+ files = json.loads(request.data["files"])
+ except:
+ return Response(
+ {"files": "This field must contain JSON."},
+ status.HTTP_400_BAD_REQUEST,
+ )
+
serializers = []
confirmation_token = generate_random_string(30)
@@ -270,16 +320,16 @@ class ZipUploadApiView(UploadApiView):
def post(self, request, format=None):
if not request.data["files"]:
return Response(
- {"files": "This field may not be blank."},
- status.HTTP_400_BAD_REQUEST,
- )
+ {"files": "This field may not be blank."},
+ status.HTTP_400_BAD_REQUEST,
+ )
try:
file_data = json.loads(request.data["files"])
except:
return Response(
- {"files": "This field must contain JSON."},
- status.HTTP_400_BAD_REQUEST,
- )
+ {"files": "This field must contain JSON."},
+ status.HTTP_400_BAD_REQUEST,
+ )
label = file_data["label"]
description = file_data["description"]
@@ -305,7 +355,7 @@ def post(self, request, format=None):
validated_data["label"] = label
validated_data["description"] = description
validated_data["license"] = license
- validated_data["version"] = f"{i+1}.0.0"
+ validated_data["version"] = f"{i + 1}.0.0"
validated_data["family"] = family
validated_data.setlist("tags", tags)
validated_data["local_file"] = local_file
@@ -411,6 +461,29 @@ def retrieve(self, request, *args, **kwargs):
return Response(anonymized_file)
+class PrivateFileViewSet(viewsets.ModelViewSet):
+ queryset = File.objects.filter(private=True)
+ serializer_class = FilesSerializer
+ permission_classes = [IsAuthenticatedOrReadOnly, IsOwnerOrIsAdminOrReadOnly]
+
+ def list(self, request, *args, **kwargs):
+ queryset = self.queryset.filter(owner=request.user)
+ serializer = self.serializer_class(queryset, many=True)
+ files = serializer.data
+ anonymized_files = []
+ for file in files:
+ anonymized_file = anonymize_file(file, request)
+ anonymized_files.append(anonymized_file)
+ return Response(anonymized_files)
+
+ def retrieve(self, request, *args, **kwargs):
+ instance = self.get_object()
+ if instance.owner != request.user:
+ return Response({"error": "Access denied"}, status=status.HTTP_403_FORBIDDEN)
+ serializer = self.serializer_class(instance)
+ return Response(serializer.data)
+
+
class FamiliesViewSet(
viewsets.GenericViewSet,
mixins.ListModelMixin,
@@ -527,6 +600,7 @@ def retrieve(self, request, *args, **kwargs):
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
+
class AnalysesViewSet(
viewsets.GenericViewSet,
mixins.CreateModelMixin,
@@ -534,7 +608,6 @@ class AnalysesViewSet(
mixins.DestroyModelMixin,
mixins.ListModelMixin,
):
-
queryset = Analysis.objects.all()
serializer_class = AnalysesSerializer
permission_classes = [
@@ -546,12 +619,12 @@ def list(self, request, **kwargs):
analyses = AnalysesSerializer(queryset, many=True).data
return Response(analyses)
+
class AnalysisResultsViewSet(
viewsets.GenericViewSet,
mixins.RetrieveModelMixin,
mixins.ListModelMixin,
):
-
queryset = AnalysisResult.objects.all()
serializer_class = AnalysisResultsSerializer
diff --git a/backend/core/routers.py b/backend/core/routers.py
index 617ce2fc..fb014092 100644
--- a/backend/core/routers.py
+++ b/backend/core/routers.py
@@ -13,7 +13,7 @@
ConfirmedFileViewSet,
UnconfirmedFileViewSet,
ConfirmFileUploadApiView,
- DeleteFileUploadApiView,
+ DeleteFileUploadApiView, PrivateFileUploadApiView, PrivateFileViewSet,
)
from core.user.viewsets import ActivateUserViewSet, UserInfoApiView
from core.auth.viewsets import LoginViewSet, RegistrationViewSet, RefreshViewSet
@@ -41,6 +41,7 @@
router.register(
r"files/uploaded/confirmed", ConfirmedFileViewSet, basename="confirmed-files"
)
+router.register(r'files/uploaded/private', PrivateFileViewSet, basename='private-files')
router.register(
r"files/uploaded/unconfirmed", UnconfirmedFileViewSet, basename="unconfirmed-files"
)
@@ -55,6 +56,7 @@
*router.urls,
path("bulk-upload/", BulkUploadApiView.as_view()),
path("zip-upload/", ZipUploadApiView.as_view()),
+ path("private-upload/", PrivateFileUploadApiView.as_view()),
re_path(r"files/uploaded/unconfirmed/confirm/(?P[\w\d]+)", ConfirmFileUploadApiView.as_view()),
re_path(r"files/uploaded/unconfirmed/delete/(?P[\w\d]+)", DeleteFileUploadApiView.as_view()),
path("api-auth/", include("rest_framework.urls")),
From 1fc3561abc0d65e459197dc44bb13f86d32d9c91 Mon Sep 17 00:00:00 2001
From: MeisterSeSe <105605891+MeisterSeSe@users.noreply.github.com>
Date: Fri, 27 Oct 2023 12:59:24 +0200
Subject: [PATCH 13/14] Feat: Implemented Possibility to upload private Feature
from LocalStore
---
.../src/components/FeatureModelTable.vue | 40 +++++++-
.../file_create/ActionButtons.vue | 26 ++++-
.../file_create/PrivateUpload.vue | 96 +++++++++++++++++++
frontendVue3/src/store/file.js | 31 ++++++
frontendVue3/src/views/Models.vue | 4 +-
5 files changed, 194 insertions(+), 3 deletions(-)
create mode 100644 frontendVue3/src/components/upload_cards/file_create/PrivateUpload.vue
diff --git a/frontendVue3/src/components/FeatureModelTable.vue b/frontendVue3/src/components/FeatureModelTable.vue
index 3e5f3faf..afe787db 100644
--- a/frontendVue3/src/components/FeatureModelTable.vue
+++ b/frontendVue3/src/components/FeatureModelTable.vue
@@ -54,6 +54,12 @@
density="comfortable"
>
+
+
+ See local storage
+
+
+
+
+
+
Upload from local storage