From 1532c908bfb0e768460ee3156a99208c23c584e2 Mon Sep 17 00:00:00 2001 From: Jacob Filik Date: Wed, 10 Jul 2024 08:42:52 +0100 Subject: [PATCH 1/4] Stop hosting client in fastapi --- Dockerfile | 50 ---------- xas-standards-api/Dockerfile | 23 ++--- .../src/xas_standards_api/app.py | 28 +----- xas-standards-client/Dockerfile | 25 +++++ xas-standards-client/nginx/default.conf | 46 +++++++++ xas-standards-client/src/App.tsx | 4 +- .../src/components/DiamondIcon.tsx | 73 +++++++------- .../src/components/ElementSelector.tsx | 4 + .../src/components/Header.tsx | 55 +++++++++-- .../src/components/ReviewCard.tsx | 84 ---------------- .../src/components/ReviewTable.tsx | 70 -------------- .../src/components/ReviewTextView.tsx | 29 ------ .../src/components/StandardsChart.tsx | 1 - .../src/components/StandardsTable.tsx | 22 ++--- xas-standards-client/src/components/Terms.tsx | 56 +++++++++++ .../src/components/UserIcon.tsx | 28 +++--- .../src/components/review/ReviewCard.tsx | 95 +++++++++++++++++++ .../components/{ => review}/ReviewPage.tsx | 4 +- .../src/components/{ => review}/ReviewTab.tsx | 4 +- .../src/components/review/ReviewTable.tsx | 66 +++++++++++++ .../src/components/review/ReviewTextView.tsx | 29 ++++++ .../submission/AdditionalInfoForm.tsx | 20 +--- .../submission/StandardSubmission.tsx | 67 ++++++------- .../submission/StandardSubmissionStepper.tsx | 3 +- xas-standards-client/src/hooks/useUser.ts | 1 - xas-standards-client/src/mocks/handlers.ts | 1 - 26 files changed, 479 insertions(+), 409 deletions(-) delete mode 100644 Dockerfile create mode 100644 xas-standards-client/Dockerfile create mode 100644 xas-standards-client/nginx/default.conf delete mode 100644 xas-standards-client/src/components/ReviewCard.tsx delete mode 100644 xas-standards-client/src/components/ReviewTable.tsx delete mode 100644 xas-standards-client/src/components/ReviewTextView.tsx create mode 100644 xas-standards-client/src/components/Terms.tsx create mode 100644 xas-standards-client/src/components/review/ReviewCard.tsx rename xas-standards-client/src/components/{ => review}/ReviewPage.tsx (94%) rename xas-standards-client/src/components/{ => review}/ReviewTab.tsx (94%) create mode 100644 xas-standards-client/src/components/review/ReviewTable.tsx create mode 100644 xas-standards-client/src/components/review/ReviewTextView.tsx diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 9016635..0000000 --- a/Dockerfile +++ /dev/null @@ -1,50 +0,0 @@ -#build client -#build api -#copy to runtime - -FROM node:18-bullseye-slim as build-web - -WORKDIR /client - -RUN yes | npm install -g pnpm - -RUN apt update - -COPY xas-standards-client . - -RUN yes | pnpm install - -RUN pnpm vite build - - - -FROM python:3.11 as build - -ARG PIP_OPTIONS=. - -# set up a virtual environment and put it in PATH -RUN python -m venv /venv -ENV PATH=/venv/bin:$PATH - -WORKDIR /api -COPY xas-standards-api . - -# install python package into /venv -RUN pip install ${PIP_OPTIONS} - -FROM python:3.11-slim as runtime - - -COPY --from=build-web /client/dist /client/dist -# Add apt-get system dependecies for runtime here if needed - -# copy the virtual environment from the build stage and put it in PATH -COPY --from=build /venv/ /venv/ -ENV PATH=/venv/bin:$PATH - -RUN apt-get update && apt-get install libpq5 -y - -EXPOSE 5000 - -# change this entrypoint if it is not the same as the repo -ENTRYPOINT ["xas-standards-api"] diff --git a/xas-standards-api/Dockerfile b/xas-standards-api/Dockerfile index ebd516f..577f363 100644 --- a/xas-standards-api/Dockerfile +++ b/xas-standards-api/Dockerfile @@ -1,25 +1,15 @@ -# This file is for use as a devcontainer and a runtime container -# -# The devcontainer should use the build target and run as root with podman -# or docker with user namespaces. -# +#build api +#copy to runtime FROM python:3.11 as build ARG PIP_OPTIONS=. -# Add any system dependencies for the developer/build environment here e.g. -# RUN apt-get update && apt-get upgrade -y && \ -# apt-get install -y --no-install-recommends \ -# desired-packages \ -# && rm -rf /var/lib/apt/lists/* - # set up a virtual environment and put it in PATH RUN python -m venv /venv ENV PATH=/venv/bin:$PATH -# Copy any required context for the pip install over -COPY . /context -WORKDIR /context +WORKDIR /api +COPY . . # install python package into /venv RUN pip install ${PIP_OPTIONS} @@ -32,6 +22,9 @@ FROM python:3.11-slim as runtime COPY --from=build /venv/ /venv/ ENV PATH=/venv/bin:$PATH +RUN apt-get update && apt-get install libpq5 -y + +EXPOSE 5000 + # change this entrypoint if it is not the same as the repo ENTRYPOINT ["xas-standards-api"] -CMD ["--version"] diff --git a/xas-standards-api/src/xas_standards_api/app.py b/xas-standards-api/src/xas_standards_api/app.py index 47f6a18..ce24e53 100644 --- a/xas-standards-api/src/xas_standards_api/app.py +++ b/xas-standards-api/src/xas_standards_api/app.py @@ -1,25 +1,9 @@ -import os -from fastapi import ( - FastAPI, -) -from fastapi.staticfiles import StaticFiles +from fastapi import FastAPI from fastapi_pagination import add_pagination -from starlette.responses import RedirectResponse from .routers import admin, open, protected -dev = False - -env_value = os.environ.get("FASTAPI_APP_ENV") - -if env_value and env_value == "development": - print("RUNNING IN DEV MODE") - dev = True - - -build_dir = os.environ.get("FRONTEND_BUILD_DIR") - app = FastAPI() app.include_router(open.router) @@ -27,13 +11,3 @@ app.include_router(admin.router) add_pagination(app) - - -@app.get("/login", response_class=RedirectResponse) -async def redirect_home(): - # proxy handles log in so if you reach here go home - return "/" - - -if build_dir: - app.mount("/", StaticFiles(directory="/client/dist", html=True), name="site") diff --git a/xas-standards-client/Dockerfile b/xas-standards-client/Dockerfile new file mode 100644 index 0000000..9f8cdbc --- /dev/null +++ b/xas-standards-client/Dockerfile @@ -0,0 +1,25 @@ +#build client +#build api +#copy to runtime + +FROM node:18-bullseye-slim as build-web + +WORKDIR /client + +RUN yes | npm install -g pnpm + +RUN apt update + +COPY . . + +RUN yes | pnpm install + +RUN pnpm vite build + +From nginx as host + +COPY --from=build-web /client/dist/ /usr/share/nginx/html +COPY ./nginx/default.conf /etc/nginx/conf.d/default.conf + +# change this entrypoint if it is not the same as the repo +ENTRYPOINT ["nginx","-g", "daemon off;"] diff --git a/xas-standards-client/nginx/default.conf b/xas-standards-client/nginx/default.conf new file mode 100644 index 0000000..b992137 --- /dev/null +++ b/xas-standards-client/nginx/default.conf @@ -0,0 +1,46 @@ +server { + listen 80; + server_name localhost; + + #access_log /var/log/nginx/host.access.log main; + + + location / { + root /usr/share/nginx/html; + index index.html index.htm; + try_files $uri /index.html =404; + } + + #error_page 404 /404.html; + + # redirect server error pages to the static page /50x.html + # + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } + + # proxy the PHP scripts to Apache listening on 127.0.0.1:80 + # + #location ~ \.php$ { + # proxy_pass http://127.0.0.1; + #} + + # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 + # + #location ~ \.php$ { + # root html; + # fastcgi_pass 127.0.0.1:9000; + # fastcgi_index index.php; + # fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name; + # include fastcgi_params; + #} + + # deny access to .htaccess files, if Apache's document root + # concurs with nginx's one + # + #location ~ /\.ht { + # deny all; + #} +} + diff --git a/xas-standards-client/src/App.tsx b/xas-standards-client/src/App.tsx index 8fcf423..a12d784 100644 --- a/xas-standards-client/src/App.tsx +++ b/xas-standards-client/src/App.tsx @@ -17,7 +17,8 @@ import { useState, useMemo } from "react"; import { Stack } from "@mui/material"; import { createTheme, ThemeProvider } from "@mui/material/styles"; -import ReviewPage from "./components/ReviewPage.tsx"; +import ReviewPage from "./components/review/ReviewPage.tsx"; +import Terms from "./components/Terms.tsx"; function App() { const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)"); @@ -56,6 +57,7 @@ function App() { } /> } /> + } /> - - - ) -} \ No newline at end of file + return ( + + + + ); +} diff --git a/xas-standards-client/src/components/ElementSelector.tsx b/xas-standards-client/src/components/ElementSelector.tsx index aa5c4ec..439524b 100644 --- a/xas-standards-client/src/components/ElementSelector.tsx +++ b/xas-standards-client/src/components/ElementSelector.tsx @@ -67,6 +67,10 @@ function ElementSelector(props: { > { + if (el >= elements.length) { + el = 0; + } + props.setSelectedElement(el); setAnchorEl(null); }} diff --git a/xas-standards-client/src/components/Header.tsx b/xas-standards-client/src/components/Header.tsx index 13d2f32..2ab36cf 100644 --- a/xas-standards-client/src/components/Header.tsx +++ b/xas-standards-client/src/components/Header.tsx @@ -2,7 +2,8 @@ import AppBar from "@mui/material/AppBar"; import Toolbar from "@mui/material/Toolbar"; import Typography from "@mui/material/Typography"; -import { useContext } from "react"; +import { useContext, useState } from "react"; +import { useNavigate } from "react-router-dom"; import { UserContext } from "../contexts/UserContext"; import ListItemButton from "@mui/material/ListItemButton"; @@ -10,7 +11,7 @@ import ListItem from "@mui/material/ListItem"; import List from "@mui/material/List"; import ListItemText from "@mui/material/ListItemText"; import Stack from "@mui/material/Stack"; -import { Checkbox } from "@mui/material"; +import { Checkbox, IconButton, Menu, MenuItem } from "@mui/material"; import { NavLink } from "react-router-dom"; @@ -46,17 +47,32 @@ export default function Header(props: { toggleColorMode: () => void; }) { const user = useContext(UserContext); - console.log(user); const loggedIn = user != null; const admin = user != null && user.admin; - console.log(loggedIn); + + const [anchorEl, setAnchorEl] = useState(null); + const navigate = useNavigate(); const navitems = { Home: "/", View: "/view", + Terms: "/terms", + }; + + const handleMenu = (event: React.MouseEvent) => { + setAnchorEl(event.currentTarget); }; - Object.keys(navitems).forEach((k) => console.log(k)); + const handleClose = () => { + setAnchorEl(null); + }; + + const signOut = () => { + handleClose(); + console.log("Sign out"); + navigate("/oauth2/sign_out"); + window.location.reload(); + }; return ( @@ -97,7 +113,34 @@ export default function Header(props: { ) : ( - + + + + + Sign out + + {user.identifier} )} diff --git a/xas-standards-client/src/components/ReviewCard.tsx b/xas-standards-client/src/components/ReviewCard.tsx deleted file mode 100644 index 7a2f011..0000000 --- a/xas-standards-client/src/components/ReviewCard.tsx +++ /dev/null @@ -1,84 +0,0 @@ -import { TextField,Button, Card, CardContent, Typography, Box, FormControl, InputLabel, Stack, Select, MenuItem} from "@mui/material"; - -import { AdminXASStandard } from "../models"; - -import { useState } from "react"; -import axios from "axios"; -import { AxiosResponse, AxiosError } from "axios"; - -const review_statuses = ["pending", "approved", "rejected"] -const standards_url = "/api/standards"; - - - -export default function ReviewCard(props: {standard : AdminXASStandard}) { - - const [reviewStatus, setReviewStatus] = useState("pending"); - const [reviewerComments, setReviewerComments] = useState(""); - - const handleSubmit = (event: React.FormEvent) => { - event.preventDefault(); - console.log("Reviewed") - - axios - .patch(standards_url, {reviewer_comments : reviewerComments, - review_status : reviewStatus, - standard_id : props.standard.id}) - .then((response: AxiosResponse) => { - console.log(response); - window.alert("Thank you for your review"); - }) - .catch((reason: AxiosError) => { - window.alert(reason.message); - }); - } - return ( - - - - - Submitted by: {props.standard.submitter.identifier} - - - - - Review Status - - - setReviewerComments(e.target.value)} - /> - - - - - - - ) -} \ No newline at end of file diff --git a/xas-standards-client/src/components/ReviewTable.tsx b/xas-standards-client/src/components/ReviewTable.tsx deleted file mode 100644 index 2c000fb..0000000 --- a/xas-standards-client/src/components/ReviewTable.tsx +++ /dev/null @@ -1,70 +0,0 @@ -import StandardsTableView from "./StandardsTableView" - -import { Stack } from "@mui/material"; - -import {AdminXASStandard, XASStandard } from "../models"; - -import { useState, useEffect } from "react"; - -import axios from "axios"; -import ReviewTab from "./ReviewTab"; - - -const standards_url = "/api/admin/standards"; - -const nResults = 7; - -export default function ReviewTable(props : { - standards: AdminXASStandard[]; - setStandards: (standards : XASStandard[]) => void; - updatePlot: (id : number) => void; -}) { - - const [selectedStandard, setSelectedStandard] = useState(); - const [current, setCurrent] = useState(null); - const [prevNext, setPrevNext] = useState(null); - - const setStandards = props.setStandards; - useEffect(() => { - const get_req = (cursor: string | null) => { - let url = standards_url; - - - - url = url + "?size=" + String(nResults); - - - if (cursor) { - url = url + "&cursor=" + cursor; - } - - axios.get(url).then((response) => { - const output: AdminXASStandard[] = response.data.items as AdminXASStandard[]; - setPrevNext([response.data.previous_page, response.data.next_page]); - setStandards(output); - }); - }; - get_req(current); - }, [current, setStandards]); - - const stds: (AdminXASStandard | null)[] = [...props.standards]; - - if (props.standards.length < nResults) { - while (stds.length < nResults) { - stds.push(null); - } - } - return ( - - - {selectedStandard && - - } - - ) -} \ No newline at end of file diff --git a/xas-standards-client/src/components/ReviewTextView.tsx b/xas-standards-client/src/components/ReviewTextView.tsx deleted file mode 100644 index 7e6c682..0000000 --- a/xas-standards-client/src/components/ReviewTextView.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import { Typography, Box } from "@mui/material"; -import { useEffect, useState } from "react"; -import axios from "axios"; -import { AdminXASStandard } from "../models"; - -const data_url = "/api/admin/data" - -export default function ReviewTextView(props: {standard : AdminXASStandard}) { - - const [fileString, setFileString] = useState("") - - useEffect(() => { - const get_req = (id : number) => { - - - axios.get(data_url + "/" + id).then((response) => { - setFileString(response.data) - }); - }; - get_req(props.standard.id); - }, [props.standard, setFileString]); - - return ( - - - {fileString} - - ) -} \ No newline at end of file diff --git a/xas-standards-client/src/components/StandardsChart.tsx b/xas-standards-client/src/components/StandardsChart.tsx index 8008f34..cfc7036 100644 --- a/xas-standards-client/src/components/StandardsChart.tsx +++ b/xas-standards-client/src/components/StandardsChart.tsx @@ -140,7 +140,6 @@ function XASChart(props: { label="Transmission" value={props.showTrans} onToggle={() => { - console.log(!props.showTrans); props.setShowTrans(!props.showTrans); }} disabled={!props.contains[0]} diff --git a/xas-standards-client/src/components/StandardsTable.tsx b/xas-standards-client/src/components/StandardsTable.tsx index 4fdcc09..9a34324 100644 --- a/xas-standards-client/src/components/StandardsTable.tsx +++ b/xas-standards-client/src/components/StandardsTable.tsx @@ -17,7 +17,7 @@ const nResults = 7; function StandardsTable(props: { standards: XASStandard[]; elements: Element[]; - setStandards: (standards : XASStandard[]) => void; + setStandards: (standards: XASStandard[]) => void; updatePlot: (id: number) => void; }): JSX.Element { const [selectedStandard, setSelectedStandard] = useState(); @@ -33,7 +33,7 @@ function StandardsTable(props: { let symbol = null; - if (z > 0) { + if (z > 0 && z <= elements.length) { symbol = elements[z - 1].symbol; } @@ -72,15 +72,15 @@ function StandardsTable(props: { selectedElement={selectedElement} setSelectedElement={setSelectedElement} /> - - {selectedStandard && - - } + + {selectedStandard && } ); } diff --git a/xas-standards-client/src/components/Terms.tsx b/xas-standards-client/src/components/Terms.tsx new file mode 100644 index 0000000..5ff25f8 --- /dev/null +++ b/xas-standards-client/src/components/Terms.tsx @@ -0,0 +1,56 @@ +import { Typography } from "@mui/material"; + +export default function Terms() { + return ( + +

+ Use of XAS Standards Database, both the uploading and downloading of + data, denotes agreement with the following terms: +

+
    +
  1. + The XAS Standards database is an open data repository for the + preservation and making available of research, educational and + informational content. Access to XAS Standards Database content is + open to all. +
  2. +
  3. + Content may be uploaded free of charge by those with a Diamond Light + Source Fed ID +
  4. +
  5. + The uploader is exclusively responsible for the content that they + upload to the XAS standards database and shall indemnify and hold + Diamond Light Source Ltd free and harmless in connection with their + use of the service. The uploader shall ensure that their content is + suitable for open dissemination, and that it complies with these terms + and applicable laws, including, but not limited to, privacy, data + protection and intellectual property rights. In addition, where data + that was originally sensitive personal data is being uploaded for open + dissemination through the XAS Standards database, the uploader shall + ensure that such data is either anonymised to an appropriate degree or + fully consent cleared. +
  6. +
  7. + Access to the XAS Standards Database, and all content, is provided on + an “as-is” basis. Users of content (“Users”) shall respect applicable + license conditions. Download and use of content from the XAS Standards + Database does not transfer any intellectual property rights in the + content to the User. +
  8. +
  9. + Users are exclusively responsible for their use of content, and shall + indemnify and hold Diamond Light Source Ltd free and harmless in + connection with their download and/or use. Hosting and making content + available through the XAS Standards Database does not represent any + approval or endorsement of such content by Diamond Light Source Ltd. +
  10. +
  11. + These Terms of Use are subject to change by Diamond Light Source at + any time and without notice, other than through posting the updated + Terms of Use on the XAS Standards website. +
  12. +
+
+ ); +} diff --git a/xas-standards-client/src/components/UserIcon.tsx b/xas-standards-client/src/components/UserIcon.tsx index 6a3b336..94139c4 100644 --- a/xas-standards-client/src/components/UserIcon.tsx +++ b/xas-standards-client/src/components/UserIcon.tsx @@ -1,15 +1,17 @@ -import { SvgIcon } from "@mui/material" +import { SvgIcon } from "@mui/material"; export default function UserIcon() { - return ( - - - - - - ) -} \ No newline at end of file + return ( + + + + + + ); +} diff --git a/xas-standards-client/src/components/review/ReviewCard.tsx b/xas-standards-client/src/components/review/ReviewCard.tsx new file mode 100644 index 0000000..0b2e8bf --- /dev/null +++ b/xas-standards-client/src/components/review/ReviewCard.tsx @@ -0,0 +1,95 @@ +import { + TextField, + Button, + Card, + CardContent, + Typography, + Box, + FormControl, + InputLabel, + Stack, + Select, + MenuItem, +} from "@mui/material"; + +import { AdminXASStandard } from "../../models"; + +import { useState } from "react"; +import axios from "axios"; +import { AxiosResponse, AxiosError } from "axios"; + +const review_statuses = ["pending", "approved", "rejected"]; +const standards_url = "/api/standards"; + +export default function ReviewCard(props: { standard: AdminXASStandard }) { + const [reviewStatus, setReviewStatus] = useState("pending"); + const [reviewerComments, setReviewerComments] = useState(""); + + const handleSubmit = (event: React.FormEvent) => { + event.preventDefault(); + + axios + .patch(standards_url, { + reviewer_comments: reviewerComments, + review_status: reviewStatus, + standard_id: props.standard.id, + }) + .then(() => { + window.alert("Thank you for your review"); + }) + .catch((reason: AxiosError) => { + window.alert(reason.message); + }); + }; + return ( + + + + + Submitted by: {props.standard.submitter.identifier} + + + + + + Review Status + + + + setReviewerComments(e.target.value)} + /> + + + + + + + ); +} diff --git a/xas-standards-client/src/components/ReviewPage.tsx b/xas-standards-client/src/components/review/ReviewPage.tsx similarity index 94% rename from xas-standards-client/src/components/ReviewPage.tsx rename to xas-standards-client/src/components/review/ReviewPage.tsx index ea0a607..6cf09df 100644 --- a/xas-standards-client/src/components/ReviewPage.tsx +++ b/xas-standards-client/src/components/review/ReviewPage.tsx @@ -1,13 +1,13 @@ import ReviewTable from "./ReviewTable"; import { Grid } from "@mui/material"; -import XASChart from "./StandardsChart"; +import XASChart from "../StandardsChart"; import axios from "axios"; import { useState } from "react"; -import { XASData, AdminXASStandard } from "../models"; +import { XASData, AdminXASStandard } from "../../models"; const data_url = "/api/admin/data"; diff --git a/xas-standards-client/src/components/ReviewTab.tsx b/xas-standards-client/src/components/review/ReviewTab.tsx similarity index 94% rename from xas-standards-client/src/components/ReviewTab.tsx rename to xas-standards-client/src/components/review/ReviewTab.tsx index 459ce40..f4d9f46 100644 --- a/xas-standards-client/src/components/ReviewTab.tsx +++ b/xas-standards-client/src/components/review/ReviewTab.tsx @@ -1,12 +1,12 @@ import { Tab, Tabs, Box } from "@mui/material"; -import StandardMetadataCard from "./StandardMetadataCard"; +import StandardMetadataCard from "../StandardMetadataCard"; import ReviewTextView from "./ReviewTextView"; import { useState } from "react"; import ReviewCard from "./ReviewCard"; -import { AdminXASStandard } from "../models"; +import { AdminXASStandard } from "../../models"; interface TabPanelProps { children?: React.ReactNode; diff --git a/xas-standards-client/src/components/review/ReviewTable.tsx b/xas-standards-client/src/components/review/ReviewTable.tsx new file mode 100644 index 0000000..9d3ae0c --- /dev/null +++ b/xas-standards-client/src/components/review/ReviewTable.tsx @@ -0,0 +1,66 @@ +import StandardsTableView from "../StandardsTableView"; + +import { Stack } from "@mui/material"; + +import { AdminXASStandard, XASStandard } from "../../models"; + +import { useState, useEffect } from "react"; + +import axios from "axios"; +import ReviewTab from "./ReviewTab"; + +const standards_url = "/api/admin/standards"; + +const nResults = 7; + +export default function ReviewTable(props: { + standards: AdminXASStandard[]; + setStandards: (standards: XASStandard[]) => void; + updatePlot: (id: number) => void; +}) { + const [selectedStandard, setSelectedStandard] = useState(); + const [current, setCurrent] = useState(null); + const [prevNext, setPrevNext] = useState(null); + + const setStandards = props.setStandards; + useEffect(() => { + const get_req = (cursor: string | null) => { + let url = standards_url; + + url = url + "?size=" + String(nResults); + + if (cursor) { + url = url + "&cursor=" + cursor; + } + + axios.get(url).then((response) => { + const output: AdminXASStandard[] = response.data + .items as AdminXASStandard[]; + setPrevNext([response.data.previous_page, response.data.next_page]); + setStandards(output); + }); + }; + get_req(current); + }, [current, setStandards]); + + const stds: (AdminXASStandard | null)[] = [...props.standards]; + + if (props.standards.length < nResults) { + while (stds.length < nResults) { + stds.push(null); + } + } + return ( + + + {selectedStandard && } + + ); +} diff --git a/xas-standards-client/src/components/review/ReviewTextView.tsx b/xas-standards-client/src/components/review/ReviewTextView.tsx new file mode 100644 index 0000000..4a3af1c --- /dev/null +++ b/xas-standards-client/src/components/review/ReviewTextView.tsx @@ -0,0 +1,29 @@ +import { Typography, Box } from "@mui/material"; +import { useEffect, useState } from "react"; +import axios from "axios"; +import { AdminXASStandard } from "../../models"; + +const data_url = "/api/admin/data"; + +export default function ReviewTextView(props: { standard: AdminXASStandard }) { + const [fileString, setFileString] = useState(""); + + useEffect(() => { + const get_req = (id: number) => { + axios.get(data_url + "/" + id).then((response) => { + setFileString(response.data); + }); + }; + get_req(props.standard.id); + }, [props.standard, setFileString]); + + return ( + + + {fileString} + + + ); +} diff --git a/xas-standards-client/src/components/submission/AdditionalInfoForm.tsx b/xas-standards-client/src/components/submission/AdditionalInfoForm.tsx index 505da5c..315bb72 100644 --- a/xas-standards-client/src/components/submission/AdditionalInfoForm.tsx +++ b/xas-standards-client/src/components/submission/AdditionalInfoForm.tsx @@ -5,16 +5,14 @@ import VisuallyHiddenInput from "./VisuallyHiddenInput"; function AdditionalInformationForm(props: { comments: string; setComments: (comments: string) => void; - handleFile2: React.ChangeEventHandler; }) { const comments = props.comments; const setComments = props.setComments; - const handleFile2 = props.handleFile2; return ( Additional Information - + setComments(e.target.value)} /> - - - ); } diff --git a/xas-standards-client/src/components/submission/StandardSubmission.tsx b/xas-standards-client/src/components/submission/StandardSubmission.tsx index a9010ba..51800e8 100644 --- a/xas-standards-client/src/components/submission/StandardSubmission.tsx +++ b/xas-standards-client/src/components/submission/StandardSubmission.tsx @@ -35,7 +35,7 @@ function StandardSubmission() { const { elements, edges, beamlines, licences } = useContext(MetadataContext); const [file, setFile] = useState(); - const [file2, setFile2] = useState(); + // const [file2, setFile2] = useState(); const [elementId, setElementId] = useState(1); const [edgeId, setEdgeId] = useState(1); @@ -79,16 +79,9 @@ function StandardSubmission() { //LICENCE form.append("licence", licence); - if (file2 != null) { - for (let i = 0; i < file2.length; i++) { - form.append("additional_files", file2[i]); - } - } - axios .post(standards_url, form) - .then((response: AxiosResponse) => { - console.log(response); + .then(() => { window.alert("Thank you for your submission"); navigate("/view"); }) @@ -142,12 +135,6 @@ function StandardSubmission() { } }; - const handleFile2 = (event: React.ChangeEvent) => { - if (event.target.files != null) { - setFile2(event.target.files); - } - }; - return ( XDI File - - - Submitted file must be in xdi format and contain an energy column, - and either "mu" datasets or "i" datasets with corresponding i0. - Inclusion of Reference datasets (murefer or irefer with i0) is - mandatory. - + + + + + + + Submitted file must be in xdi format and contain an energy + column, and either "mu" datasets or "i" datasets with + corresponding i0. Inclusion of Reference datasets (murefer or + irefer with i0) is mandatory. + + + @@ -234,7 +227,6 @@ function StandardSubmission() { @@ -259,8 +251,7 @@ function StandardSubmission() { } - label="By ticking I confirm info is correct and I grant permission for - diamond to publish data under selected licence" + label="By ticking I confirm info is correct and I grant permissionfor diamond to publish data under selected licence and that I agree to the terms of use" /> diff --git a/xas-standards-client/src/components/submission/StandardSubmissionStepper.tsx b/xas-standards-client/src/components/submission/StandardSubmissionStepper.tsx index ee023d8..6c9d458 100644 --- a/xas-standards-client/src/components/submission/StandardSubmissionStepper.tsx +++ b/xas-standards-client/src/components/submission/StandardSubmissionStepper.tsx @@ -112,8 +112,7 @@ function StandardSubmissionStepper() { axios .post(standards_url, form) - .then((response: AxiosResponse) => { - console.log(response); + .then(() => { window.alert("Thank you for your submission"); navigate("/view"); }) diff --git a/xas-standards-client/src/hooks/useUser.ts b/xas-standards-client/src/hooks/useUser.ts index 8d4b2c9..a7c1f5b 100644 --- a/xas-standards-client/src/hooks/useUser.ts +++ b/xas-standards-client/src/hooks/useUser.ts @@ -11,7 +11,6 @@ function useUser(): User | null { axios .get(user_url) .then((res: AxiosResponse) => { - // console.log(res.status); setCurrentUser({ identifier: res.data.user, admin: res.data.admin}); }) .catch((error) => { diff --git a/xas-standards-client/src/mocks/handlers.ts b/xas-standards-client/src/mocks/handlers.ts index 2c00132..3ce5485 100644 --- a/xas-standards-client/src/mocks/handlers.ts +++ b/xas-standards-client/src/mocks/handlers.ts @@ -17,7 +17,6 @@ import { data_response } from "./data_response"; export const handlers = [ http.post('/api/standards', () => { - console.log('Captured a "POST /api/standards" request') const standard1: XASStandard = { beamline: { facility: { name: "SSRL" }, name: "4-1", id:1 }, citation: From 5cca780b38a6ac811d36038e2e01b3f1c25f7cab Mon Sep 17 00:00:00 2001 From: Jacob Filik Date: Wed, 10 Jul 2024 07:58:16 +0000 Subject: [PATCH 2/4] fix lint --- xas-standards-api/src/xas_standards_api/app.py | 1 - 1 file changed, 1 deletion(-) diff --git a/xas-standards-api/src/xas_standards_api/app.py b/xas-standards-api/src/xas_standards_api/app.py index ce24e53..c2a8348 100644 --- a/xas-standards-api/src/xas_standards_api/app.py +++ b/xas-standards-api/src/xas_standards_api/app.py @@ -1,4 +1,3 @@ - from fastapi import FastAPI from fastapi_pagination import add_pagination From f2c263344eb228a711f3fb9d468fa620b0ab6562 Mon Sep 17 00:00:00 2001 From: Jacob Filik Date: Thu, 11 Jul 2024 08:22:32 +0000 Subject: [PATCH 3/4] add code for sample form enum --- database/tables.sql | 4 ++- .../src/xas_standards_api/crud.py | 12 +++++++ .../src/xas_standards_api/models/models.py | 8 ++++- .../models/response_models.py | 1 + .../src/xas_standards_api/routers/open.py | 14 +++++++- .../xas_standards_api/routers/protected.py | 2 ++ .../src/components/submission/SampleForm.tsx | 36 +++++++++++++++---- .../submission/StandardSubmission.tsx | 8 ++++- xas-standards-client/src/hooks/useMetadata.ts | 1 + xas-standards-client/src/models.ts | 1 + 10 files changed, 76 insertions(+), 11 deletions(-) diff --git a/database/tables.sql b/database/tables.sql index a65ace6..aff6a3f 100644 --- a/database/tables.sql +++ b/database/tables.sql @@ -224,7 +224,8 @@ CREATE TABLE xas_standard_data ( COMMENT ON TABLE xas_standard_data IS 'Data file storing the standard data'; CREATE TYPE review_status_enum AS ENUM('pending', 'approved', 'rejected'); -CREATE TYPE licence_enum AS ENUM('cc_by', 'cc_0', 'logged_in_only'); +CREATE TYPE licence_enum AS ENUM('cc_by', 'cc_0'); +CREATE TYPE sample_form_enum AS ENUM('other', 'foil', 'pellet'); CREATE Table xas_standard ( id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY, @@ -241,6 +242,7 @@ CREATE Table xas_standard ( sample_name TEXT, sample_prep TEXT, sample_comp TEXT, + sample_form sample_form_enum, beamline_id INTEGER, mono_name TEXT, mono_dspacing TEXT, diff --git a/xas-standards-api/src/xas_standards_api/crud.py b/xas-standards-api/src/xas_standards_api/crud.py index faf774d..9d1da4d 100644 --- a/xas-standards-api/src/xas_standards_api/crud.py +++ b/xas-standards-api/src/xas_standards_api/crud.py @@ -17,6 +17,7 @@ Person, PersonInput, ReviewStatus, + SampleForm, XASStandard, XASStandardAdminReviewInput, XASStandardData, @@ -82,6 +83,7 @@ def get_metadata(session): output["edges"] = select_all(session, Edge) output["beamlines"] = select_all(session, Beamline) output["licences"] = list(LicenceType) + output["sample_forms"] = list(SampleForm) return output @@ -260,3 +262,13 @@ def is_admin_user(session: Session, user_id: str): raise HTTPException(status_code=401, detail=f"User {user_id} not admin") return True + + +def get_registered_elements(session: Session): + statement = ( + select(Element) + .where(XASStandard.element_z == Element.z) + .where(XASStandard.review_status == ReviewStatus.approved) + ) + results = session.exec(statement) + return results.unique().all() diff --git a/xas-standards-api/src/xas_standards_api/models/models.py b/xas-standards-api/src/xas_standards_api/models/models.py index 2af0f80..3e286b5 100644 --- a/xas-standards-api/src/xas_standards_api/models/models.py +++ b/xas-standards-api/src/xas_standards_api/models/models.py @@ -14,7 +14,12 @@ class ReviewStatus(enum.Enum): class LicenceType(enum.Enum): cc_by = "cc_by" cc_0 = "cc_0" - logged_in_only = "logged_in_only" + + +class SampleForm(enum.Enum): + other = "other" + foil = "foil" + pellet = "pellet" class PersonInput(SQLModel): @@ -102,6 +107,7 @@ class XASStandardInput(SQLModel): sample_name: str sample_prep: Optional[str] sample_comp: Optional[str] + sample_form: SampleForm = Field(sa_column=Column(Enum(SampleForm))) beamline_id: int = Field(foreign_key="beamline.id") licence: LicenceType = Field(sa_column=Column(Enum(LicenceType))) diff --git a/xas-standards-api/src/xas_standards_api/models/response_models.py b/xas-standards-api/src/xas_standards_api/models/response_models.py index a7c8ab8..28c2bc4 100644 --- a/xas-standards-api/src/xas_standards_api/models/response_models.py +++ b/xas-standards-api/src/xas_standards_api/models/response_models.py @@ -36,3 +36,4 @@ class MetadataResponse(SQLModel): elements: List[Element] edges: List[Edge] licences: List[str] + sample_forms: List[str] diff --git a/xas-standards-api/src/xas_standards_api/routers/open.py b/xas-standards-api/src/xas_standards_api/routers/open.py index 529e445..5314002 100644 --- a/xas-standards-api/src/xas_standards_api/routers/open.py +++ b/xas-standards-api/src/xas_standards_api/routers/open.py @@ -4,7 +4,14 @@ from fastapi_pagination.cursor import CursorPage from sqlmodel import Session -from ..crud import get_data, get_file, get_metadata, get_standard, read_standards_page +from ..crud import ( + get_data, + get_file, + get_metadata, + get_registered_elements, + get_standard, + read_standards_page, +) from ..database import get_session from ..models.models import ReviewStatus from ..models.response_models import ( @@ -48,3 +55,8 @@ async def read_data( return get_file(session, id) return get_data(session, id) + + +@router.get("/api/elements/metrics") +async def read_elements(session: Session = Depends(get_session)): + return get_registered_elements(session) diff --git a/xas-standards-api/src/xas_standards_api/routers/protected.py b/xas-standards-api/src/xas_standards_api/routers/protected.py index 15deb0f..7c4fa40 100644 --- a/xas-standards-api/src/xas_standards_api/routers/protected.py +++ b/xas-standards-api/src/xas_standards_api/routers/protected.py @@ -38,6 +38,7 @@ def add_standard_file( beamline_id: Annotated[int, Form()], sample_name: Annotated[str, Form()], sample_prep: Annotated[str, Form()], + sample_form: Annotated[str, Form()], doi: Annotated[str, Form()], citation: Annotated[str, Form()], comments: Annotated[str, Form()], @@ -62,6 +63,7 @@ def add_standard_file( edge_id=edge_id, sample_name=sample_name, sample_prep=sample_prep, + sample_form=sample_form, submitter_comments=comments, citation=citation, licence=licence, diff --git a/xas-standards-client/src/components/submission/SampleForm.tsx b/xas-standards-client/src/components/submission/SampleForm.tsx index 43c332f..5326010 100644 --- a/xas-standards-client/src/components/submission/SampleForm.tsx +++ b/xas-standards-client/src/components/submission/SampleForm.tsx @@ -1,4 +1,11 @@ -import { Grid, TextField } from "@mui/material"; +import { + Grid, + TextField, + FormControl, + InputLabel, + Select, + MenuItem, +} from "@mui/material"; function SampleForm(props: { sampleName: string; @@ -7,6 +14,9 @@ function SampleForm(props: { setSampleComp: (composition: string) => void; samplePrep: string; setSamplePrep: (preparation: string) => void; + sampleForm: string; + setSampleForm: (preparation: string) => void; + sampleFormOptions: string[]; }) { const sampleName = props.sampleName; const setSampleName = props.setSampleName; @@ -49,12 +59,24 @@ function SampleForm(props: { /> - + + + Sample Form + + + ); diff --git a/xas-standards-client/src/components/submission/StandardSubmission.tsx b/xas-standards-client/src/components/submission/StandardSubmission.tsx index 51800e8..8328455 100644 --- a/xas-standards-client/src/components/submission/StandardSubmission.tsx +++ b/xas-standards-client/src/components/submission/StandardSubmission.tsx @@ -32,7 +32,8 @@ import { const standards_url = "/api/standards"; function StandardSubmission() { - const { elements, edges, beamlines, licences } = useContext(MetadataContext); + const { elements, edges, beamlines, licences, sample_forms } = + useContext(MetadataContext); const [file, setFile] = useState(); // const [file2, setFile2] = useState(); @@ -42,6 +43,7 @@ function StandardSubmission() { const [sampleName, setSampleName] = useState(""); const [sampleComp, setSampleComp] = useState(""); const [samplePrep, setSamplePrep] = useState(""); + const [sampleForm, setSampleForm] = useState(sample_forms[0]); const [beamlineId, setBeamlineId] = useState(1); const [beamlineHeader, setBeamlineHeader] = useState(""); const [doi, setDOI] = useState(""); @@ -72,6 +74,7 @@ function StandardSubmission() { form.append("sample_name", sampleName); form.append("sample_comp", sampleComp); form.append("sample_prep", samplePrep); + form.append("sample_form", sampleForm); form.append("doi", doi); form.append("citation", citation); form.append("comments", comments); @@ -203,6 +206,9 @@ function StandardSubmission() { setSampleComp={setSampleComp} samplePrep={samplePrep} setSamplePrep={setSamplePrep} + sampleForm={sampleForm} + setSampleForm={setSampleForm} + sampleFormOptions={sample_forms} /> diff --git a/xas-standards-client/src/hooks/useMetadata.ts b/xas-standards-client/src/hooks/useMetadata.ts index 0d51aaf..69240a8 100644 --- a/xas-standards-client/src/hooks/useMetadata.ts +++ b/xas-standards-client/src/hooks/useMetadata.ts @@ -11,6 +11,7 @@ function useMetadata(): AppMetadata { elements: [], edges: [], licences: [], + sample_forms: [] }); useEffect(() => { diff --git a/xas-standards-client/src/models.ts b/xas-standards-client/src/models.ts index 42827e9..cc90454 100644 --- a/xas-standards-client/src/models.ts +++ b/xas-standards-client/src/models.ts @@ -57,6 +57,7 @@ export interface AppMetadata { elements: Element[]; edges: Edge[]; licences: string[]; + sample_forms: string[]; } export interface User { From 3caf5636e4549a9463c84e02ca1f76ddb29091f4 Mon Sep 17 00:00:00 2001 From: Jacob Filik Date: Thu, 11 Jul 2024 12:03:44 +0000 Subject: [PATCH 4/4] fix tests --- xas-standards-api/tests/test_protected_router.py | 1 + xas-standards-api/tests/utils.py | 2 ++ 2 files changed, 3 insertions(+) diff --git a/xas-standards-api/tests/test_protected_router.py b/xas-standards-api/tests/test_protected_router.py index a5ecf2b..1fbfefb 100644 --- a/xas-standards-api/tests/test_protected_router.py +++ b/xas-standards-api/tests/test_protected_router.py @@ -69,6 +69,7 @@ def get_admin_user(): "beamline_id": 1, "sample_name": unique_sample_name, "sample_prep": "test", + "sample_form": "foil", "doi": "doi", "citation": "citation", "comments": "comments", diff --git a/xas-standards-api/tests/utils.py b/xas-standards-api/tests/utils.py index 09e35af..f749a7a 100644 --- a/xas-standards-api/tests/utils.py +++ b/xas-standards-api/tests/utils.py @@ -86,6 +86,7 @@ def build_test_database(session: Session): sample_name="sample", sample_prep="pellet", sample_comp="H", + sample_form="foil", beamline_id=1, licence=LicenceType.cc_0, id=1, @@ -108,6 +109,7 @@ def build_test_database(session: Session): sample_name="sample", sample_prep="pellet", sample_comp="He", + sample_form="pellet", beamline_id=1, licence=LicenceType.cc_0, id=2,