Skip to content

Commit

Permalink
Merge pull request #1840 from dandi/contact-owner
Browse files Browse the repository at this point in the history
Contact owner
  • Loading branch information
mvandenburgh committed Jul 26, 2024
2 parents 2d53a2f + a5741dd commit a3f2d9d
Show file tree
Hide file tree
Showing 5 changed files with 176 additions and 6 deletions.
13 changes: 12 additions & 1 deletion dandiapi/api/tests/test_dandiset.py
Original file line number Diff line number Diff line change
Expand Up @@ -825,6 +825,7 @@ def test_dandiset_rest_get_owners(api_client, dandiset, social_account):
{
'username': social_account.extra_data['login'],
'name': social_account.extra_data['name'],
'email': None,
}
]

Expand All @@ -836,7 +837,13 @@ def test_dandiset_rest_get_owners_no_social_account(api_client, dandiset, user):
resp = api_client.get(f'/api/dandisets/{dandiset.identifier}/users/')

assert resp.status_code == 200
assert resp.data == [{'username': user.username, 'name': f'{user.first_name} {user.last_name}'}]
assert resp.data == [
{
'username': user.username,
'name': f'{user.first_name} {user.last_name}',
'email': None,
}
]


@pytest.mark.parametrize(
Expand Down Expand Up @@ -871,6 +878,7 @@ def test_dandiset_rest_change_owner(
{
'username': social_account2.extra_data['login'],
'name': social_account2.extra_data['name'],
'email': social_account2.extra_data['email'],
}
]
assert list(dandiset.owners) == [user2]
Expand Down Expand Up @@ -943,10 +951,12 @@ def test_dandiset_rest_add_owner(
{
'username': social_account1.extra_data['login'],
'name': social_account1.extra_data['name'],
'email': social_account1.extra_data['email'],
},
{
'username': social_account2.extra_data['login'],
'name': social_account2.extra_data['name'],
'email': social_account2.extra_data['email'],
},
]
assert list(dandiset.owners) == [user1, user2]
Expand Down Expand Up @@ -983,6 +993,7 @@ def test_dandiset_rest_remove_owner(
{
'username': social_account1.extra_data['login'],
'name': social_account1.extra_data['name'],
'email': social_account1.extra_data['email'],
}
]
assert list(dandiset.owners) == [user1]
Expand Down
13 changes: 10 additions & 3 deletions dandiapi/api/views/dandiset.py
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,7 @@ def unembargo(self, request, dandiset__pk):
)
# TODO: move these into a viewset
@action(methods=['GET', 'PUT'], detail=True)
def users(self, request, dandiset__pk): # noqa: C901
def users(self, request, dandiset__pk):
dandiset: Dandiset = self.get_object()
if request.method == 'PUT':
if dandiset.unembargo_in_progress:
Expand Down Expand Up @@ -423,15 +423,22 @@ def users(self, request, dandiset__pk): # noqa: C901
try:
owner_account = SocialAccount.objects.get(user=owner_user)
owner_dict = {'username': owner_account.extra_data['login']}
if 'name' in owner_account.extra_data:
owner_dict['name'] = owner_account.extra_data['name']
owner_dict['name'] = owner_account.extra_data.get('name', None)
owner_dict['email'] = (
owner_account.extra_data['email']
# Only logged-in users can see owners' email addresses
if request.user.is_authenticated and 'email' in owner_account.extra_data
else None
)
owners.append(owner_dict)
except SocialAccount.DoesNotExist:
# Just in case some users aren't using social accounts, have a fallback
owners.append(
{
'username': owner_user.username,
'name': f'{owner_user.first_name} {owner_user.last_name}',
'email': owner_user.email if request.user.is_authenticated else None,
}
)

return Response(owners, status=status.HTTP_200_OK)
1 change: 1 addition & 0 deletions web/src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export interface User {
username: string,
name: string,
admin?: boolean,
email: string,
status: 'INCOMPLETE' | 'PENDING' | 'APPROVED' | 'REJECTED',
approved: boolean,
}
Expand Down
143 changes: 143 additions & 0 deletions web/src/views/DandisetLandingView/ContactDialog.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
<template>
<v-menu
offset-y
left
>
<template
#activator="{ on, attrs }"
>
<v-btn
id="contact"
outlined
block
v-bind="attrs"
v-on="on"
>
<v-icon
color="primary"
left
>
mdi-card-account-mail
</v-icon>
<span>Contact</span>
<v-spacer />
<v-icon right>
mdi-chevron-down
</v-icon>
</v-btn>
</template>
<v-card
>
<v-card-title class="pb-0" style="min-width: fit-content;">
Select an e-mail recipient:
</v-card-title>
<v-list>
<v-tooltip
:disabled="!disableDandisetOwnersButton"
open-on-hover
left
>
<template #activator="{ on }">
<div
v-on="on"
>
<v-list-item
:disabled="disableDandisetOwnersButton"
:href="makeTemplate(dandisetOwnerEmails)"
>
<v-icon
color="primary"
left
small
>
mdi-card-account-mail
</v-icon>
Dandiset Owners
</v-list-item>
</div>
</template>
<span v-if="!loggedIn()"> You must be logged in to contact the owner </span>
<span v-if="!dandisetOwnerEmails?.length"> No owner e-mail available </span>
</v-tooltip>
<v-divider />
<v-tooltip
:disabled="!disableContactPersonButton"
open-on-hover
left
>
<template #activator="{ on }">
<div v-on="on">
<v-list-item
:disabled="disableContactPersonButton"
:href="makeTemplate(dandisetContactPersonEmails)"
>
<v-icon
color="primary"
left
small
>
mdi-card-account-mail
</v-icon>
Dandiset Contact Person
</v-list-item>
</div>
</template>
<span> No contact e-mail available </span>
</v-tooltip>
</v-list>
</v-card>
</v-menu>
</template>
<script setup lang="ts">
import { computed } from 'vue';
import { useDandisetStore } from '@/stores/dandiset';
import { loggedIn } from '@/rest';
import type { User, Person, Organization, Email} from '@/types';
const store = useDandisetStore();
const currentDandiset = computed(() => store.dandiset);
const dandisetOwnerEmails = computed(() => store.owners?.map((owner: User) => owner.email) || []);
const dandisetContactPersonEmails = computed(() =>
currentDandiset.value?.metadata?.contributor?.filter(
(contact: Person | Organization) =>
contact.roleName?.includes("dcite:ContactPerson")
)
.map((contact: Person | Organization) =>
contact.email as Email
)
// Exclude users missing an email
.filter((email?: Email) => email !== undefined)
// Exclude users with an empty email
.filter((email: Email) => email !== '')
|| []
);

const makeTemplate = (contacts: string[]) => {
if (currentDandiset.value === undefined) {
throw new Error('Dandiset is undefined.');
}
if (currentDandiset.value){
const subject = encodeURIComponent(`Regarding Dandiset ${currentDandiset.value.dandiset.identifier} ("${currentDandiset.value.name}")`);
const contact = contacts.join(',');
return `mailto:${contact}?subject=${subject}`;
}
};

const disableContactPersonButton = computed(() => !dandisetContactPersonEmails.value?.length)
const disableDandisetOwnersButton = computed(
// Only logged in users can access owners' emails
() => !loggedIn() || !dandisetOwnerEmails.value?.length
);

</script>
<style scoped>
.v-btn--outlined {
border: thin solid #E0E0E0;
color: #424242;
font-weight: 400;
}
</style>
12 changes: 10 additions & 2 deletions web/src/views/DandisetLandingView/DandisetActions.vue
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,15 @@
</template>
</DownloadDialog>
</v-row>
<v-row no-gutters>
<v-row
no-gutters
>
<CiteAsDialog>
<template
#activator="{ on }"
>
<v-btn
id="download"
id="cite_as"
outlined
block
v-on="on"
Expand All @@ -63,6 +65,11 @@
</template>
</CiteAsDialog>
</v-row>
<v-row
no-gutters
>
<ContactDialog />
</v-row>
</div>

<!-- Files and Metadata buttons -->
Expand Down Expand Up @@ -151,6 +158,7 @@ import { open as openMeditor } from '@/components/Meditor/state';
import DownloadDialog from './DownloadDialog.vue';
import CiteAsDialog from './CiteAsDialog.vue';
import ShareDialog from './ShareDialog.vue';
import ContactDialog from './ContactDialog.vue';
const store = useDandisetStore();
Expand Down

0 comments on commit a3f2d9d

Please sign in to comment.