Skip to content

Commit

Permalink
feat: add editing to tutorials (#229)
Browse files Browse the repository at this point in the history
* feat: edit

* fix: remove hit
  • Loading branch information
xnought authored Apr 9, 2024
1 parent 03e5548 commit 6100655
Show file tree
Hide file tree
Showing 8 changed files with 257 additions and 10 deletions.
44 changes: 44 additions & 0 deletions backend/src/api/tutorials.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,47 @@ def upload_tutorial(body: TutorialUpload, req: Request):
db.execute(query, [body.title, body.description, body.content, body.refs])
except Exception as e:
raise HTTPException(404, detail=str(e))


class TutorialEdit(CamelModel):
title: str # used to id the tutorial
# potential changes
new_title: str | None = None
new_description: str | None = None
new_content: str | None = None
new_refs: str | None = None


@router.put("/tutorial/edit", response_model=None)
def edit_tutorial(body: TutorialEdit, req: Request):
requires_authentication(req)
with Database() as db:
try:
if body.new_title is not None:
db.execute(
"""UPDATE tutorials SET title = %s WHERE title = %s""",
[body.new_title, body.title],
)
# then for the remaining queries, use the new title
body.title = body.new_title

if body.new_description is not None:
db.execute(
"""UPDATE tutorials SET description = %s WHERE title = %s""",
[body.new_description, body.title],
)

if body.new_content is not None:
db.execute(
"""UPDATE tutorials SET content = %s WHERE title = %s""",
[body.new_content, body.title],
)

if body.new_refs is not None:
db.execute(
"""UPDATE tutorials SET refs = %s WHERE title = %s""",
[body.new_refs, body.title],
)

except Exception as e:
raise HTTPException(404, detail=str(e))
8 changes: 7 additions & 1 deletion frontend/src/Router.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import Align from "./routes/Align.svelte";
import Tutorial from "./routes/Tutorial.svelte";
import UploadTutorial from "./routes/UploadTutorial.svelte";
import EditTutorial from "./routes/EditTutorial.svelte";
</script>

<Router>
Expand All @@ -32,7 +33,12 @@
<Route path="/protein/:id" let:params
><Protein urlId={params.id} /></Route
>
<Route path="/edit/:id" let:params><Edit urlId={params.id} /></Route>
<Route path="/protein/edit/:id" let:params
><Edit urlId={params.id} /></Route
>
<Route path="/tutorial/edit/:id" let:params
><EditTutorial tutorialTitle={params.id} /></Route
>
<Route path="/force-upload-thumbnails"><ForceUploadThumbnails /></Route>
<Route path="/align/:a/:b" let:params
><Align proteinA={params.a} proteinB={params.b} /></Route
Expand Down
1 change: 1 addition & 0 deletions frontend/src/lib/openapi/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export type { SearchProteinsBody } from './models/SearchProteinsBody';
export type { SearchProteinsResults } from './models/SearchProteinsResults';
export type { SimilarProtein } from './models/SimilarProtein';
export type { Tutorial } from './models/Tutorial';
export type { TutorialEdit } from './models/TutorialEdit';
export type { TutorialUpload } from './models/TutorialUpload';
export type { UploadBody } from './models/UploadBody';
export { UploadError } from './models/UploadError';
Expand Down
12 changes: 12 additions & 0 deletions frontend/src/lib/openapi/models/TutorialEdit.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/* generated using openapi-typescript-codegen -- do no edit */
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
export type TutorialEdit = {
title: string;
newTitle?: (string | null);
newDescription?: (string | null);
newContent?: (string | null);
newRefs?: (string | null);
};

20 changes: 20 additions & 0 deletions frontend/src/lib/openapi/services/DefaultService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import type { SearchProteinsBody } from '../models/SearchProteinsBody';
import type { SearchProteinsResults } from '../models/SearchProteinsResults';
import type { SimilarProtein } from '../models/SimilarProtein';
import type { Tutorial } from '../models/Tutorial';
import type { TutorialEdit } from '../models/TutorialEdit';
import type { TutorialUpload } from '../models/TutorialUpload';
import type { UploadBody } from '../models/UploadBody';
import type { UploadError } from '../models/UploadError';
Expand Down Expand Up @@ -326,4 +327,23 @@ export class DefaultService {
},
});
}
/**
* Edit Tutorial
* @param requestBody
* @returns any Successful Response
* @throws ApiError
*/
public static editTutorial(
requestBody: TutorialEdit,
): CancelablePromise<any> {
return __request(OpenAPI, {
method: 'PUT',
url: '/tutorial/edit',
body: requestBody,
mediaType: 'application/json',
errors: {
422: `Validation Error`,
},
});
}
}
149 changes: 149 additions & 0 deletions frontend/src/routes/EditTutorial.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
<script lang="ts">
import { Backend, UploadError, setToken } from "../lib/backend";
import { Button, Input, Label, Helper } from "flowbite-svelte";
import { navigate } from "svelte-routing";
import ArticleEditor from "../lib/ArticleEditor.svelte";
import { onMount } from "svelte";
import { user } from "../lib/stores/user";
export let tutorialTitle: string;
let titleOriginal: string = "";
let descriptionOriginal: string = "";
let refsOriginal: string = "";
let contentOriginal: string = "";
let titleEdited: string = "";
let descriptionEdited: string = "";
let refsEdited: string = "";
let contentEdited: string = "";
let uploadError: boolean = false;
onMount(async () => {
if (!$user.loggedIn) {
alert(
"You are not logged in. You are being redirected to home. TODO: Make this better."
);
navigate("/");
}
try {
const tutorial = await Backend.getTutorial(tutorialTitle);
titleOriginal = tutorial.title;
titleEdited = titleOriginal;
descriptionOriginal = tutorial.description ?? "";
descriptionEdited = descriptionOriginal;
refsOriginal = tutorial.refs ?? "";
refsEdited = refsOriginal;
contentOriginal = tutorial.content ?? "";
contentEdited = contentOriginal;
} catch (e) {
console.log("error!");
}
});
function returnIfEditedElseNone(oldString: string, newString: string) {
if (oldString === newString) {
return undefined;
} else {
return newString;
}
}
$: wasEdited =
titleEdited !== titleOriginal ||
descriptionEdited !== descriptionOriginal ||
refsEdited !== refsOriginal ||
contentEdited !== contentOriginal;
</script>

<svelte:head>
<title>Tutorial Upload</title>
</svelte:head>

<section class="p-5">
<div class="w-500 flex flex-col gap-5">
<div>
<Label
color={uploadError ? "red" : undefined}
for="tutorial-title"
class="block mb-2"
>
Title *</Label
>
<Input
bind:value={titleEdited}
color={uploadError ? "red" : "base"}
style="width: 300px"
id="tutorial-title"
placeholder="Enter a unique title..."
/>
{#if uploadError}
<Helper class="mt-2" color="red"
>This name already exists, please create a unique name and
resubmit</Helper
>
{/if}
</div>

<div>
<Label for="tutorial-desc" class="block mb-2">Description</Label>
<Input
bind:value={descriptionEdited}
style="width: 600px"
id="tutorial-desc"
placeholder="Enter a description (optional)..."
/>
</div>

<div>
<ArticleEditor
bind:content={contentEdited}
bind:refs={refsEdited}
/>
</div>

<div>
<Button
on:click={async () => {
try {
setToken();
await Backend.editTutorial({
title: titleOriginal, // how to id the tutorial
// changes (put as undefined if not changed)
newTitle: returnIfEditedElseNone(
titleOriginal,
titleEdited
),
newDescription: returnIfEditedElseNone(
descriptionOriginal,
descriptionEdited
),
newContent: returnIfEditedElseNone(
contentOriginal,
contentEdited
),
newRefs: returnIfEditedElseNone(
refsOriginal,
refsEdited
),
});
navigate(`/tutorial/${titleEdited}`);
} catch (e) {
uploadError = true;
}
}}
disabled={!wasEdited}>Edit Tutorial</Button
>
<Button
outline
on:click={() => navigate(`/tutorial/${tutorialTitle}`)}
>Cancel</Button
>
</div>
</div>
</section>
20 changes: 11 additions & 9 deletions frontend/src/routes/Protein.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,16 @@
<!-- TITLE AND DESCRIPTION -->
<h1 id="title">
{undoFormatProteinName(entry.name)}
{#if $user.loggedIn}
<Button
outline
size="xs"
on:click={async () => {
navigate(`/protein/edit/${entry?.name}`);
}}
><PenOutline class="mr-2" size="sm" />Edit Protein Entry</Button
>
{/if}
</h1>

<div id="description">
Expand Down Expand Up @@ -95,7 +105,7 @@
</div>
<div id="right-side" class="flex flex-col">
<div class="flex gap-2">
<Button
<Button outline
>Download <ChevronDownSolid
size="md"
class="ml-2"
Expand All @@ -109,14 +119,6 @@
>
{/each}
</Dropdown>
{#if $user.loggedIn}
<Button
on:click={async () => {
navigate(`/edit/${entry?.name}`);
}}
><PenOutline class="mr-2" size="lg" />Edit Entry</Button
>
{/if}
</div>

<div style="position: sticky; top: 55px; right: 0; z-index:999;">
Expand Down
13 changes: 13 additions & 0 deletions frontend/src/routes/Tutorial.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
import EntryCard from "../lib/EntryCard.svelte";
import References from "../lib/References.svelte";
import Markdown from "../lib/Markdown.svelte";
import { Button } from "flowbite-svelte";
import { navigate } from "svelte-routing";
import { PenOutline } from "flowbite-svelte-icons";
import { user } from "../lib/stores/user";
export let tutorialTitle: string;
Expand All @@ -17,6 +21,15 @@
{#if tutorial}
<h1 id="title">
{tutorial.title}

{#if $user.loggedIn}
<Button
outline
size="xs"
on:click={() => navigate(`/tutorial/edit/${tutorialTitle}`)}
><PenOutline class="mr-2" size="sm" />Edit Tutorial
</Button>
{/if}
</h1>
<div id="description">
{#if tutorial.description}
Expand Down

0 comments on commit 6100655

Please sign in to comment.