Skip to content

Commit

Permalink
Merge pull request #355 from OpenImaging/project-creation
Browse files Browse the repository at this point in the history
Project creation
  • Loading branch information
dchiquito authored Mar 4, 2022
2 parents 93de11b + 5df730a commit 3e538a0
Show file tree
Hide file tree
Showing 13 changed files with 45 additions and 22 deletions.
2 changes: 1 addition & 1 deletion client/src/components/DataImportExport.vue
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ export default defineComponent({

<v-card-text
v-for="importError in importErrorList"
v-bind:key="importError"
:key="importError"
class="console-format"
>
{{ importError }}
Expand Down
1 change: 0 additions & 1 deletion client/src/components/KeyboardShortcutDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ export default defineComponent({
},
});
</script>

<template>
<v-dialog
:value="value"
Expand Down
22 changes: 14 additions & 8 deletions client/src/components/ProjectSettings.vue
Original file line number Diff line number Diff line change
@@ -1,23 +1,27 @@
<script lang="ts">
import {
computed, defineComponent, ref, watchEffect,
computed, defineComponent, ref, watchEffect, inject,
} from '@vue/composition-api';
import { mapMutations } from 'vuex';
import store from '@/store';
import djangoRest from '@/django';
import DataImportExport from '@/components/DataImportExport.vue';
import { User } from '@/types';
export default defineComponent({
name: 'ProjectSettings',
components: {
DataImportExport,
},
inject: ['user'],
setup() {
const user: User = inject('user');
const currentProject = computed(() => store.state.currentProject);
const globalSettings = computed(() => store.state.globalSettings);
const projects = computed(() => store.state.projects);
const isGlobal = computed(() => store.getters.isGlobal);
const userCanEditProject = computed(
() => user.is_superuser || user.username === currentProject.value.creator,
);
const importPath = ref('');
const exportPath = ref('');
Expand Down Expand Up @@ -70,6 +74,8 @@ export default defineComponent({
}
return {
user,
userCanEditProject,
currentProject,
isGlobal,
projects,
Expand Down Expand Up @@ -127,7 +133,7 @@ export default defineComponent({
v.endsWith('.csv') ||
'Needs to be a json or csv file'
]"
:disabled="!user.is_superuser"
:disabled="!userCanEditProject"
:error-messages="importPathError"
label="Import path"
placeholder="Specify a server path to read an import file"
Expand All @@ -149,7 +155,7 @@ export default defineComponent({
v.endsWith('.csv') ||
'Needs to be a json or csv file'
]"
:disabled="!user.is_superuser"
:disabled="!userCanEditProject"
:error-messages="exportPathError"
label="Export path"
placeholder="Specify a server path to write an export file"
Expand All @@ -164,17 +170,17 @@ export default defineComponent({
style="flex-direction: row"
>
<v-btn
v-if="user.is_superuser"
v-if="userCanEditProject"
:disabled="!changed"
type="submit"
color="primary"
>
Save
</v-btn>
<DataImportExport v-if="user.is_superuser" />
<DataImportExport v-if="(userCanEditProject)" />
<div style="flex-grow:2">
<v-btn
v-if="user.is_superuser && !isGlobal"
v-if="userCanEditProject && !isGlobal"
class="red white--text"
style="float: right;"
@click="showDeleteWarningOverlay = true"
Expand All @@ -184,7 +190,7 @@ export default defineComponent({
</div>
</v-flex>
<v-overlay
v-if="user.is_superuser && !isGlobal"
v-if="userCanEditProject && !isGlobal"
:value="showDeleteWarningOverlay"
:dark="false"
>
Expand Down
4 changes: 2 additions & 2 deletions client/src/components/ProjectUsers.vue
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ export default {
<v-col cols="12">
Members
<v-tooltip
v-if="user.is_superuser"
v-if="user.is_superuser || user.username == currentProject.creator"
bottom
style="display: inline; padding-left: 5px"
>
Expand Down Expand Up @@ -128,7 +128,7 @@ export default {
<v-col cols="12">
Collaborators <span class="gray-info">(Read only)</span>
<v-tooltip
v-if="user.is_superuser"
v-if="user.is_superuser || user.username == currentProject.creator"
bottom
style="display: inline; padding-left: 5px"
>
Expand Down
2 changes: 1 addition & 1 deletion client/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,10 @@ djangoRest.restoreLogin(store).then(async () => {
vuetify,
router,
store: store.original,
render: (h) => h(App),
provide: {
user, MIQAConfig,
},
render: (h) => h(App),
})
.$mount('#app')
// @ts-ignore
Expand Down
1 change: 1 addition & 0 deletions client/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ interface Project {
total_scans: number,
total_complete: number,
}
creator: string;
}

interface Email {
Expand Down
7 changes: 5 additions & 2 deletions client/src/views/Projects.vue
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export default defineComponent({
ProjectSettings,
ProjectUsers,
},
inject: ['user'],
inject: ['user', 'MIQAConfig'],
setup() {
const loadingProjects = ref(true);
store.dispatch.loadProjects().then(() => {
Expand Down Expand Up @@ -166,7 +166,10 @@ export default defineComponent({
</span>
</v-tooltip>
</v-list-item>
<v-list-item style="text-align: center">
<v-list-item
v-if="user.is_superuser || MIQAConfig.NORMAL_USERS_CAN_CREATE_PROJECTS"
style="text-align: center"
>
<v-text-field
v-if="creating"
v-model="newName"
Expand Down
4 changes: 2 additions & 2 deletions client/src/vue-utilities/prompt-service/Prompt.vue
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,15 @@
<v-spacer />
<v-btn
v-if="confirm"
@click="negative"
text
@click="negative"
>
{{ negativeButton }}
</v-btn>
<v-btn
@click="positive"
color="primary"
text
@click="positive"
>
{{ positiveButton }}
</v-btn>
Expand Down
2 changes: 1 addition & 1 deletion client/src/vue-utilities/snackbar-service/Snackbar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
{{ text }}
<v-btn
v-if="callback"
@click="buttonClicked"
color="primary"
text
@click="buttonClicked"
>
{{ button }}
</v-btn>
Expand Down
1 change: 1 addition & 0 deletions miqa/core/rest/other_endpoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,6 @@ def get(self, request):
'UNDEFINED': ArtifactState.UNDEFINED.value,
},
'S3_SUPPORT': settings.S3_SUPPORT,
'NORMAL_USERS_CAN_CREATE_PROJECTS': settings.NORMAL_USERS_CAN_CREATE_PROJECTS,
}
)
12 changes: 10 additions & 2 deletions miqa/core/rest/project.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from django.conf import settings
from drf_yasg.utils import no_body, swagger_auto_schema
from guardian.shortcuts import get_objects_for_user, get_users_with_perms
from rest_framework import mixins, serializers, status
Expand Down Expand Up @@ -82,19 +83,23 @@ def convert_state_string(last_reviewer_role):
class ProjectSerializer(serializers.ModelSerializer):
class Meta:
model = Project
fields = ['id', 'name', 'status', 'experiments', 'settings']
fields = ['id', 'name', 'status', 'experiments', 'settings', 'creator']
ref_name = 'projects'

status = serializers.SerializerMethodField('get_status')
experiments = ExperimentSerializer(many=True, required=False)
settings = serializers.SerializerMethodField('get_settings')
creator = serializers.SerializerMethodField('get_creator')

def get_status(self, obj):
return obj.get_status()

def get_settings(self, obj):
return ProjectSettingsSerializer(obj).data

def get_creator(self, obj):
return obj.creator.username


class ProjectViewSet(
ReadOnlyModelViewSet,
Expand All @@ -118,9 +123,12 @@ def get_queryset(self):
return projects.all()

def create(self, request, *args, **kwargs):
if not settings.NORMAL_USERS_CAN_CREATE_PROJECTS and not request.user.is_superuser:
return Response(status=status.HTTP_401_UNAUTHORIZED)
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
project = serializer.save(creator=request.user)
project.update_group('tier_2_reviewer', [request.user])
headers = self.get_success_headers(serializer.data)
return Response(
self.get_serializer(project).data, status=status.HTTP_201_CREATED, headers=headers
Expand All @@ -145,7 +153,7 @@ def create(self, request, *args, **kwargs):
def settings_(self, request, **kwargs):
project: Project = self.get_object()
if request.method == 'PUT':
if not request.user.is_superuser:
if not (request.user.is_superuser or request.user == project.creator):
return Response(status=status.HTTP_401_UNAUTHORIZED)

# TODO: need help changing the auto schema to expect permissions object
Expand Down
8 changes: 6 additions & 2 deletions miqa/core/tests/test_rest.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@ def test_project_settings_get(user_api_client, project, user):


@pytest.mark.django_db
def test_project_settings_put(user_api_client, project, user):
def test_project_settings_put(user_api_client, project_factory, user_factory, user):
creator = user_factory()
project = project_factory(creator=creator)
user_api_client = user_api_client()
my_perms = get_perms(user, project)
new_perms = {
Expand Down Expand Up @@ -84,7 +86,9 @@ def test_project_settings_put(user_api_client, project, user):


@pytest.mark.django_db
def test_settings_endpoint_requires_superuser(user_api_client, project, user):
def test_settings_endpoint_requires_superuser(user_api_client, project_factory, user_factory, user):
creator = user_factory()
project = project_factory(creator=creator)
resp = user_api_client().put(
f'/api/v1/projects/{project.id}/settings',
data={
Expand Down
1 change: 1 addition & 0 deletions miqa/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ class MiqaMixin(ConfigMixin):
CORS_ALLOW_CREDENTIALS = True
ZARR_SUPPORT = True
S3_SUPPORT = True
NORMAL_USERS_CAN_CREATE_PROJECTS = values.Value(environ=True, default=False)

@staticmethod
def before_binding(configuration: ComposedConfiguration) -> None:
Expand Down

0 comments on commit 3e538a0

Please sign in to comment.