diff --git a/backend/init.sql b/backend/init.sql
index edc4816d..37a79610 100644
--- a/backend/init.sql
+++ b/backend/init.sql
@@ -18,7 +18,8 @@ CREATE TABLE proteins (
name text NOT NULL UNIQUE PRIMARY KEY, -- user specified name of the protein (TODO: consider having a string limit)
length integer, -- length of amino acid sequence
mass numeric, -- mass in amu/daltons
- content bytea -- stored markdown for the protein article (TODO: consider having a limit to how big this can be)
+ content bytea, -- stored markdown for the protein article (TODO: consider having a limit to how big this can be)
+ refs bytea -- bibtex references mentioned in the content/article
);
/*
@@ -45,24 +46,27 @@ CREATE TABLE species (
/*
* Inserts example proteins into proteins table
*/
-INSERT INTO proteins (name, length, mass, content) VALUES (
+INSERT INTO proteins (name, length, mass, content, refs) VALUES (
'Gh_comp271_c0_seq1',
0,
0.0,
+ null,
null
);
-INSERT INTO proteins (name, length, mass, content) VALUES (
+INSERT INTO proteins (name, length, mass, content, refs) VALUES (
'Lb17_comp535_c2_seq1',
0,
0.0,
+ null,
null
);
-INSERT INTO proteins (name, length, mass, content) VALUES (
+INSERT INTO proteins (name, length, mass, content, refs) VALUES (
'Lh14_comp2336_c0_seq1',
0,
0.0,
+ null,
null
);
diff --git a/backend/src/api_types.py b/backend/src/api_types.py
index 5bd469ed..5b25eeba 100644
--- a/backend/src/api_types.py
+++ b/backend/src/api_types.py
@@ -28,6 +28,7 @@ class ProteinEntry(CamelModel):
length: int
mass: float
content: str | None = None
+ refs: str | None = None
class AllEntries(CamelModel):
@@ -37,7 +38,8 @@ class AllEntries(CamelModel):
class UploadBody(CamelModel):
name: str
content: str # markdown content from user
- pdb_file_base64: str
+ refs: str # references used in content (bibtex form)
+ pdb_file_str: str
class UploadError(str, enum.Enum):
@@ -55,3 +57,4 @@ class EditBody(CamelModel):
old_name: str # so we can identify the exact row we need to change
new_name: str
new_content: str | None = None
+ new_refs: str | None = None
diff --git a/backend/src/server.py b/backend/src/server.py
index fc74b0b2..02c29f6a 100644
--- a/backend/src/server.py
+++ b/backend/src/server.py
@@ -35,6 +35,30 @@ def get_all_entries():
log.error(e)
+@app.get("/search-entries/{query:str}", response_model=list[ProteinEntry] | None)
+def search_entries(query: str):
+ """Gets a list of protein entries by a search string
+ Returns: list[ProteinEntry] if found | None if not found
+ """
+ with Database() as db:
+ try:
+ entries_sql = db.execute_return(
+ """SELECT name, length, mass FROM proteins
+ WHERE name ILIKE \'%{}%\'""".format(query)
+ )
+ log.warn("log test")
+ log.warn(entries_sql)
+
+ # if we got a result back
+ if entries_sql is not None:
+ return [
+ ProteinEntry(name=name, length=length, mass=mass)
+ for name, length, mass in entries_sql
+ ]
+ except Exception as e:
+ log.error(e)
+
+
@app.get("/protein-entry/{protein_name:str}", response_model=ProteinEntry | None)
def get_protein_entry(protein_name: str):
"""Get a single protein entry by its id
@@ -43,7 +67,7 @@ def get_protein_entry(protein_name: str):
with Database() as db:
try:
entry_sql = db.execute_return(
- """SELECT name, length, mass, content FROM proteins
+ """SELECT name, length, mass, content, refs FROM proteins
WHERE name = %s""",
[protein_name],
)
@@ -53,13 +77,16 @@ def get_protein_entry(protein_name: str):
if entry_sql is not None and len(entry_sql) != 0:
# return the only entry
only_returned_entry = entry_sql[0]
- name, length, mass, content = only_returned_entry
- # if bytes are present, decode them into a string
+ name, length, mass, content, refs = only_returned_entry
+
+ # if byte arrays are present, decode them into a string
if content is not None:
content = bytea_to_str(content)
+ if refs is not None:
+ refs = bytea_to_str(refs)
return ProteinEntry(
- name=name, length=length, mass=mass, content=content
+ name=name, length=length, mass=mass, content=content, refs=refs
)
except Exception as e:
@@ -95,7 +122,7 @@ def upload_protein_entry(body: UploadBody):
# if name is unique, save the pdb file and add the entry to the database
try:
# TODO: consider somehow sending the file as a stream instead of a b64 string or send as regular string
- pdb = parse_protein_pdb(body.name, body.pdb_file_base64, encoding="b64")
+ pdb = parse_protein_pdb(body.name, body.pdb_file_str)
except Exception:
return UploadError.PARSE_ERROR
@@ -107,12 +134,13 @@ def upload_protein_entry(body: UploadBody):
# save to db
with Database() as db:
db.execute(
- """INSERT INTO proteins (name, length, mass, content) VALUES (%s, %s, %s, %s);""",
+ """INSERT INTO proteins (name, length, mass, content, refs) VALUES (%s, %s, %s, %s, %s);""",
[
pdb.name,
pdb.num_amino_acids,
pdb.mass_daltons,
str_to_bytea(body.content),
+ str_to_bytea(body.refs),
],
)
except Exception:
@@ -133,21 +161,33 @@ def edit_protein_entry(body: EditBody):
os.rename(pdb_file_name(body.old_name), pdb_file_name(body.new_name))
with Database() as db:
- # if we have content/markdown, then update it, otherwise just update the name
- if body.new_content is not None:
+ if body.new_name != body.old_name:
db.execute(
- """UPDATE proteins SET name = %s, content = %s WHERE name = %s""",
+ """UPDATE proteins SET name = %s WHERE name = %s""",
[
body.new_name,
+ body.old_name,
+ ],
+ )
+
+ if body.new_content is not None:
+ db.execute(
+ """UPDATE proteins SET content = %s WHERE name = %s""",
+ [
str_to_bytea(body.new_content),
body.old_name,
],
)
- else:
+
+ if body.new_refs is not None:
db.execute(
- """UPDATE proteins SET name = %s WHERE name = %s""",
- [body.new_name, body.old_name],
+ """UPDATE proteins SET refs = %s WHERE name = %s""",
+ [
+ str_to_bytea(body.new_refs),
+ body.old_name,
+ ],
)
+
except Exception:
return UploadError.WRITE_ERROR
diff --git a/frontend/package.json b/frontend/package.json
index 6ef311e6..72df7fa1 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -37,6 +37,7 @@
},
"type": "module",
"dependencies": {
+ "bibtex": "^0.9.0",
"marked": "^10.0.0"
}
}
diff --git a/frontend/src/lib/ArticleEditor.svelte b/frontend/src/lib/ArticleEditor.svelte
new file mode 100644
index 00000000..6b67cc05
--- /dev/null
+++ b/frontend/src/lib/ArticleEditor.svelte
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {#if content.length > 0 || refs.length > 0}
+
+ Article
+
+
+
+
+ References
+
+
+ {:else}
+ No content to render/preview
+ {/if}
+
+
+
diff --git a/frontend/src/lib/Markdown.svelte b/frontend/src/lib/Markdown.svelte
index d2bdeae0..92929eda 100644
--- a/frontend/src/lib/Markdown.svelte
+++ b/frontend/src/lib/Markdown.svelte
@@ -4,7 +4,21 @@
export let text = ``;
// they recommend also sanitizing input text https://github.com/cure53/DOMPurify
- $: mdToHTML = marked(text);
+
+ $: mdToHTML = marked(replaceCite(text));
+
+ /**
+ * @todo this is a hacky way to do this, but it works for now
+ * Instead use the builtin extensions https://marked.js.org/using_pro#extensions
+ */
+ function replaceCite(str: string) {
+ // replace \cite{} with [1]
+ const newStr = str.replaceAll(/\\cite{(.+?)}/g, (match, p1) => {
+ console.log(match, p1);
+ return `[${p1}]`;
+ });
+ return newStr;
+ }
{@html mdToHTML}
diff --git a/frontend/src/lib/References.svelte b/frontend/src/lib/References.svelte
new file mode 100644
index 00000000..fb713b08
--- /dev/null
+++ b/frontend/src/lib/References.svelte
@@ -0,0 +1,68 @@
+
+
+{#if bib}
+ {#each bib.entries_raw as entry, i}
+