1
Fork 0
mirror of https://github.com/Steffo99/sophon.git synced 2024-12-22 06:44:21 +00:00

Add research projects listing

This commit is contained in:
Steffo 2021-09-21 18:01:46 +02:00
parent 8604609ea7
commit 721c9afaef
9 changed files with 212 additions and 7 deletions

View file

@ -4,10 +4,14 @@ import rest_framework.routers
from . import views from . import views
router = rest_framework.routers.DefaultRouter() group_router = rest_framework.routers.DefaultRouter()
router.register("", views.ResearchProjectViewSet, basename="research-project") group_router.register("", views.ResearchProjectsByGroupViewSet, basename="research-project-by-group")
slug_router = rest_framework.routers.DefaultRouter()
slug_router.register("", views.ResearchProjectsBySlugViewSet, basename="research-project-by-slug")
urlpatterns = [ urlpatterns = [
path("", include(router.urls)), path("by-group/<slug:group_slug>/", include(group_router.urls)),
path("by-slug/", include(slug_router.urls)),
] ]

View file

@ -1,3 +1,5 @@
import abc
from django.db.models import Q from django.db.models import Q
from sophon.core.models import ResearchGroup from sophon.core.models import ResearchGroup
@ -6,7 +8,29 @@ from sophon.core.views import SophonGroupViewSet
from . import models from . import models
class ResearchProjectViewSet(SophonGroupViewSet): class ResearchProjectViewSet(SophonGroupViewSet, metaclass=abc.ABCMeta):
def get_group_from_serializer(self, serializer) -> ResearchGroup:
return serializer.validated_data["group"]
class ResearchProjectsByGroupViewSet(ResearchProjectViewSet):
def get_queryset(self):
if self.request.user.is_anonymous:
return models.ResearchProject.objects.filter(
Q(group__slug=self.kwargs["group_slug"]) &
Q(visibility="PUBLIC")
)
else:
return models.ResearchProject.objects.filter(
Q(group__slug=self.kwargs["group_slug"]) & (
Q(visibility="PUBLIC") |
Q(visibility="INTERNAL") |
Q(visibility="PRIVATE", group__members__in=[self.request.user])
)
)
class ResearchProjectsBySlugViewSet(ResearchProjectViewSet):
def get_queryset(self): def get_queryset(self):
if self.request.user.is_anonymous: if self.request.user.is_anonymous:
return models.ResearchProject.objects.filter( return models.ResearchProject.objects.filter(
@ -18,6 +42,3 @@ class ResearchProjectViewSet(SophonGroupViewSet):
Q(visibility="INTERNAL") | Q(visibility="INTERNAL") |
Q(visibility="PRIVATE", group__members__in=[self.request.user]) Q(visibility="PRIVATE", group__members__in=[self.request.user])
) )
def get_group_from_serializer(self, serializer) -> ResearchGroup:
return serializer.validated_data["group"]

View file

@ -0,0 +1,36 @@
import * as React from "react"
import * as ReactDOM from "react-dom"
import {useDRFManagedDetail} from "../hooks/useDRF";
import {Box, Heading} from "@steffo/bluelib-react";
import {ResearchProject} from "../types";
import {Loading} from "./Loading";
interface ResearchGroupDescriptionBoxProps {
pk: string,
}
export function ResearchGroupDescriptionBox({pk}: ResearchGroupDescriptionBoxProps): JSX.Element {
const group = useDRFManagedDetail<ResearchProject>("/api/core/groups/", pk)
if(group.resource) {
return (
<Box>
<Heading level={3}>
{group.resource.name}
</Heading>
<p>
{group.resource.description}
</p>
</Box>
)
}
else {
return (
<Box>
<Loading/>
</Box>
)
}
}

View file

@ -0,0 +1,43 @@
import * as React from "react"
import * as ReactDOM from "react-dom"
import {ObjectPanel} from "./ObjectPanel";
import {ResearchProject} from "../types";
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
import {faEnvelope, faGlobe, faLock, faQuestion, faUniversity} from "@fortawesome/free-solid-svg-icons";
import {Link} from "./Link";
export function ResearchProjectPanel({visibility, slug, name, description, group}: ResearchProject): JSX.Element {
let accessIcon: JSX.Element
if(visibility === "PUBLIC") {
accessIcon = <FontAwesomeIcon icon={faGlobe} title={"Public"}/>
}
else if(visibility === "INTERNAL") {
accessIcon = <FontAwesomeIcon icon={faUniversity} title={"Internal"}/>
}
else if(visibility === "PRIVATE") {
accessIcon = <FontAwesomeIcon icon={faLock} title={"Private"}/>
}
else {
accessIcon = <FontAwesomeIcon icon={faQuestion} title={"Unknown"}/>
}
return (
<ObjectPanel>
<ObjectPanel.Icon>
{accessIcon}
</ObjectPanel.Icon>
<ObjectPanel.Name>
<Link href={`/g/${group}/p/${slug}`}>
{name}
</Link>
</ObjectPanel.Name>
<ObjectPanel.Text>
</ObjectPanel.Text>
<ObjectPanel.Buttons>
</ObjectPanel.Buttons>
</ObjectPanel>
)
}

View file

@ -0,0 +1,41 @@
import * as React from "react"
import * as ReactDOM from "react-dom"
import {useDRFManagedList} from "../hooks/useDRF";
import {ResearchGroup, ResearchProject} from "../types";
import {Loading} from "./Loading";
import {ResearchGroupPanel} from "./ResearchGroupPanel";
import {Box, Heading} from "@steffo/bluelib-react";
import {ResearchProjectPanel} from "./ResearchProjectPanel";
interface ProjectsListBoxProps {
group_pk: string
}
export function ResearchProjectsByGroupListBox({group_pk}: ProjectsListBoxProps): JSX.Element {
const {resources} = useDRFManagedList<ResearchProject>(`/api/projects/by-group/${group_pk}/`, "slug")
const groups = React.useMemo(
() => {
if(!resources) {
return <Loading/>
}
return resources.map(
(res, key) => <ResearchProjectPanel {...res} key={key}/>
)
},
[resources]
)
return (
<Box>
<Heading level={3}>
Research projects
</Heading>
<div>
{groups}
</div>
</Box>
)
}

View file

@ -0,0 +1,36 @@
import * as React from "react"
import * as ReactDOM from "react-dom"
import {useDRFManagedList} from "../hooks/useDRF";
import {ResearchGroup, ResearchProject} from "../types";
import {Loading} from "./Loading";
import {ResearchGroupPanel} from "./ResearchGroupPanel";
import {Box, Heading} from "@steffo/bluelib-react";
import {ResearchProjectPanel} from "./ResearchProjectPanel";
export function ResearchProjectsListBox(): JSX.Element {
const {resources} = useDRFManagedList<ResearchProject>(`/api/projects/by-slug/`, "slug")
const groups = React.useMemo(
() => {
if(!resources) {
return <Loading/>
}
return resources.map(
(res, key) => <ResearchProjectPanel {...res} key={key}/>
)
},
[resources]
)
return (
<Box>
<Heading level={3}>
Research projects
</Heading>
<div>
{groups}
</div>
</Box>
)
}

View file

@ -1,6 +1,7 @@
import * as React from "react" import * as React from "react"
import {ResearchGroupListBox} from "../components/ResearchGroupListBox"; import {ResearchGroupListBox} from "../components/ResearchGroupListBox";
import {InstanceDescriptionBox} from "../components/InstanceDescriptionBox"; import {InstanceDescriptionBox} from "../components/InstanceDescriptionBox";
import {ResearchProjectsListBox} from "../components/ResearchProjectsListBox";
export function InstancePage(): JSX.Element { export function InstancePage(): JSX.Element {
@ -8,6 +9,7 @@ export function InstancePage(): JSX.Element {
<div> <div>
<InstanceDescriptionBox/> <InstanceDescriptionBox/>
<ResearchGroupListBox/> <ResearchGroupListBox/>
<ResearchProjectsListBox/>
</div> </div>
) )
} }

View file

@ -0,0 +1,19 @@
import * as React from "react"
import * as ReactDOM from "react-dom"
import {ResearchGroupDescriptionBox} from "../components/ResearchGroupDescriptionBox";
import {ResearchProjectsByGroupListBox} from "../components/ResearchProjectsByGroupListBox";
interface ResearchGroupPageProps {
pk: string,
}
export function ResearchGroupPage({pk}: ResearchGroupPageProps): JSX.Element {
return (
<div>
<ResearchGroupDescriptionBox pk={pk}/>
<ResearchProjectsByGroupListBox group_pk={pk}/>
</div>
)
}

View file

@ -5,9 +5,11 @@ import { InstancePage } from "./InstancePage"
import { ErrorCatcherBox, NotFoundBox } from "../components/ErrorBox" import { ErrorCatcherBox, NotFoundBox } from "../components/ErrorBox"
import { InstanceTitle } from "../components/InstanceTitle" import { InstanceTitle } from "../components/InstanceTitle"
import { UserPage } from "./UserPage" import { UserPage } from "./UserPage"
import { ResearchGroupPage } from "./ResearchGroupPage"
export function Router() { export function Router() {
// noinspection RequiredAttributes
return <> return <>
<Reach.Router primary={false}> <Reach.Router primary={false}>
<InstanceTitle default/> <InstanceTitle default/>
@ -16,6 +18,7 @@ export function Router() {
<Reach.Router primary={true}> <Reach.Router primary={true}>
<LoginPage path={"/"}/> <LoginPage path={"/"}/>
<InstancePage path={"/g/"}/> <InstancePage path={"/g/"}/>
<ResearchGroupPage path={"/g/:pk"}/>
<UserPage path={"/u/:pk"}/> <UserPage path={"/u/:pk"}/>
<NotFoundBox default/> <NotFoundBox default/>
</Reach.Router> </Reach.Router>