From 9ddc6f3771c194ca7f990708cb6b07865017219d Mon Sep 17 00:00:00 2001 From: TussockyJoker Date: Sun, 5 Apr 2026 20:09:35 +0100 Subject: [PATCH 1/6] fixed 'register now' button going to wrong url on about page --- templates/about/about.jinja | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/about/about.jinja b/templates/about/about.jinja index 5f756b5..28ecf97 100644 --- a/templates/about/about.jinja +++ b/templates/about/about.jinja @@ -73,7 +73,7 @@ Create a free account to track species, contribute to our database, and connect with the conservation community.

- {{ button(url='/accounts/user_registration', button_text='Create an Account', extra_classes='homepage-btn') }} + {{ button(url='/register', button_text='Create an Account', extra_classes='homepage-btn') }}
From 5bd1ec7fa4802e6c008d46c0bb4ec4e8dd0837fb Mon Sep 17 00:00:00 2001 From: TussockyJoker Date: Sun, 5 Apr 2026 23:33:52 +0100 Subject: [PATCH 2/6] changed display_all_items macro to display_all_species and added display_all_articles macro --- templates/display_items.jinja | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/templates/display_items.jinja b/templates/display_items.jinja index 6f4e6da..ff9be00 100644 --- a/templates/display_items.jinja +++ b/templates/display_items.jinja @@ -1,6 +1,6 @@ -{% macro display_all_items(items)%} +{% macro display_all_species(items)%} {# Displays all species in the provided 'items' array #} -{# Used in the species database and species results pages #} +{# Used in the species database and search species results pages #}
{% for item in items %} @@ -15,4 +15,19 @@ {% endfor %}
+{% endmacro %} +{% macro display_all_articles(items)%} +{# Displays all articles in the provided 'items' array #} +{# Used in the article library and article search results pages #} +
+ {% for item in items %} + +

{{ item['article_title'] }}

+

+ Main category: {{ item['category'] }} +

+
+ + {% endfor %} +
{% endmacro %} \ No newline at end of file From 8dc5dacd09172ff6b78dfd8f0bda8015ffe876e1 Mon Sep 17 00:00:00 2001 From: TussockyJoker Date: Mon, 6 Apr 2026 04:54:22 +0100 Subject: [PATCH 3/6] added articles table to db --- database/db_init.py | 63 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 62 insertions(+), 1 deletion(-) diff --git a/database/db_init.py b/database/db_init.py index acda5d0..9aeee3d 100644 --- a/database/db_init.py +++ b/database/db_init.py @@ -90,7 +90,27 @@ SEED_SPECIES = [ "photoid": "celebes crested macaque" }, ] - +test_article = [ + { + "article_id":"a-starrier-night", + "title" :"A Starrier Night", + "subtitle" :("In the past decade, our planet has rapidly lost its night sky. In response," + "West Texas created the largest international dark-sky reserve on Earth."), + "main_text" :("Light pollution has been growing rapidly, with the sky brightening nearly 10% each year " + "since 2011. In West Texas, the McDonald Observatory—the darkest observatory in the " + "continental US—faced an operational threat as development crept into the region. " + "Superintendent Teznie Pugh and colleagues partnered with local landowners, parks, and " + "The Nature Conservancy to protect the night sky. In 2022, DarkSky International certified " + "the Greater Big Bend International Dark Sky Reserve, covering 9.6 million acres and " + "spanning the US-Mexico border—the largest International Dark Sky Reserve in the world. " + "The Davis Mountains Preserve (33,000 acres) forms the core of the reserve. The greatest " + "ongoing threat comes from oil and gas development in the Permian Basin. The reserve also " + "supports ecological research into the effects of light pollution on nocturnal wildlife, " + "plants, and ecosystems."), + "author" :"Jenny Rogers", + "publish_date":"2026-02-13" + } +] def init_database(): with get_db() as conn: @@ -109,7 +129,48 @@ def init_database(): print("Database initialised.") seed_species() init_programs_database() + init_articles() +def init_articles(): + with get_db() as conn: + # table for articles in library + conn.execute(""" + CREATE TABLE IF NOT EXISTS articles ( + article_id TEXT PRIMARY KEY UNIQUE, + title TEXT, + subtitle TEXT, + main_text TEXT, + author TEXT, + publish_date);""") + print("Successfully created/found articles table.") + # table to store links relevant to articles (e.g. sources or original article) + conn.execute(""" + CREATE TABLE IF NOT EXISTS article_links ( + link_id INTEGER PRIMARY KEY AUTOINCREMENT, + article_id TEXT NOT NULL, + url TEXT NOT NULL, + link_text TEXT, + FOREIGN KEY (article_id) REFERENCES articles(article_id) + ON DELETE CASCADE);""") + print("Succesfully found/created article_links table.") + for article in test_article: + table_exists = conn.execute( + "SELECT article_id FROM articles WHERE article_id = ?", + (article["article_id"],)).fetchone() + if table_exists: + print(f" SKIP {article['article_id']} (already exists)") + continue + conn.execute(""" + INSERT INTO articles + (article_id, title, subtitle, main_text, author, publish_date) + VALUES (?, ?, ?, ?, ?, ?)""", + (article["article_id"], + article["title"], + article["subtitle"], + article["main_text"], + article["author"], + article["publish_date"],)) + print(f" ADD {article['article_id']}") def seed_species(): with get_db() as conn: From 23884c2bad88c2bb4fe9845a950cd3ccac69fdee Mon Sep 17 00:00:00 2001 From: TussockyJoker Date: Mon, 6 Apr 2026 05:29:34 +0100 Subject: [PATCH 4/6] article library page and individual articles working --- database/db_commands.py | 10 ++++++++ routes/library/article.py | 32 ++++++++++++++++++++++++++ routes/library/article_library.py | 30 ++++++++++++++++++++++++ templates/display_items.jinja | 5 ++-- templates/library/article.jinja | 23 ++++++++++++++++++ templates/library/library.jinja | 16 +++++++++++++ templates/species/species.jinja | 4 ++-- templates/species/species_search.jinja | 4 ++-- 8 files changed, 118 insertions(+), 6 deletions(-) create mode 100644 routes/library/article.py create mode 100644 routes/library/article_library.py create mode 100644 templates/library/article.jinja create mode 100644 templates/library/library.jinja diff --git a/database/db_commands.py b/database/db_commands.py index d7871c1..57066e2 100644 --- a/database/db_commands.py +++ b/database/db_commands.py @@ -92,6 +92,16 @@ def get_all_species(): with get_db() as conn: rows = conn.execute("SELECT * FROM species").fetchall() return [dict(row) for row in rows] + +def get_all_articles(): + with get_db() as conn: + rows = conn.execute("SELECT * FROM articles").fetchall() + return [dict(row) for row in rows] + +def get_article_by_id(id): + with get_db() as conn: + row = conn.execute("SELECT * FROM articles WHERE article_id = ? LIMIT 1", (id,)).fetchone() + return dict(row) if row else None def get_species_by_name(species_english): diff --git a/routes/library/article.py b/routes/library/article.py new file mode 100644 index 0000000..f11d7bc --- /dev/null +++ b/routes/library/article.py @@ -0,0 +1,32 @@ +from flask import render_template, Blueprint, send_from_directory +from database.db_commands import get_article_by_id, get_species_by_id +import sqlite3 + +page = Blueprint("article", __name__) + +# this page works by taking the species name (in english) from the url and using that to read the database. +# Because of this we are able to create one page and simply generate links based on the contents of the database +# i.e. we can create a button with the hyperlink /species/iguana and this will +# allow the user to view the information about the species +# please be aware the page expects a .jpg at: +# /static/images/species_database/{{photoid}}.jpg but this will be generated automatically +# so long as you use the species_portal or species editor pages I created - CS + + +@page.route("/articles/") +def data(article_id): + database_entry = get_article_by_id(article_id) + if database_entry is None: + raise TypeError(f"No species named '{species_english}' found in the database.") + return render_template( + "library/article.jinja", + article_id =database_entry["article_id" ].capitalize(), + title =database_entry["title" ].capitalize(), + subtitle =database_entry["subtitle" ], + main_text =database_entry["main_text" ].capitalize(), + author =database_entry["author" ], + publish_date=database_entry["publish_date" ]) + +@page.route("/species/image/") +def image(photoid): + return send_from_directory("database/images", photoid) \ No newline at end of file diff --git a/routes/library/article_library.py b/routes/library/article_library.py new file mode 100644 index 0000000..a55b9bc --- /dev/null +++ b/routes/library/article_library.py @@ -0,0 +1,30 @@ +from flask import render_template, Blueprint, request +import configparser + +from database.db_commands import get_all_articles, search_species, get_species_by_name +from utilities.featured_species import featured_species_function + +config = configparser.ConfigParser() + +page = Blueprint("articles", __name__, url_prefix="/articles") + +# article library page +@page.route("") +def all_species(): + article_list = get_all_articles() + + article_list = sorted(article_list, key=lambda sp: sp["title"]) + + for sp in article_list: + print(sp['title']) + + return render_template("library/library.jinja", articles=article_list) + +# search page +@page.route("/search") +def search(): + query = request.args.get("search-query") + + results = search_species(query) + print(results) + return render_template("/species/species_search.jinja", results=results, query=query) \ No newline at end of file diff --git a/templates/display_items.jinja b/templates/display_items.jinja index ff9be00..df9be6f 100644 --- a/templates/display_items.jinja +++ b/templates/display_items.jinja @@ -22,9 +22,10 @@
{% for item in items %} -

{{ item['article_title'] }}

+

{{ item['title'] }}

- Main category: {{ item['category'] }} + Main category: {{ item['author'] }} + Date published: {{ item["publish_date"] }}

diff --git a/templates/library/article.jinja b/templates/library/article.jinja new file mode 100644 index 0000000..2b29863 --- /dev/null +++ b/templates/library/article.jinja @@ -0,0 +1,23 @@ +{% extends "base.jinja" %} +{% block title %}{{ english_name }} {% endblock %} +{% block head %} +{% endblock %} +{% block content %} +{% from 'button.jinja' import button %} +
+
+

{{ title }}

+

{{ subtitle }}

+

Category: {{author}}

+

Category: {{publish_date}}

+ +
+
+

{{ main_text }}

+
+
+{# this button should only be visible to certain account types #} +{#
+ {{ button(url='/species/editor/'+species_id, button_text='Edit Species') }} +
#} +{% endblock %} \ No newline at end of file diff --git a/templates/library/library.jinja b/templates/library/library.jinja new file mode 100644 index 0000000..dc20e3d --- /dev/null +++ b/templates/library/library.jinja @@ -0,0 +1,16 @@ +{% extends "base.jinja" %} +{% block title %}Species Database{% endblock %} +{% block head %} +{% endblock %} +{% block content %} +{% from 'button.jinja' import button %} +{% from 'display_items.jinja' import display_all_articles %} +{% from 'search_bar.jinja' import search_bar%} +
+Article Library +
+{# All Articles #} +{{search_bar("/articles/search", "")}} +

All Articles

+{{display_all_articles(articles)}} +{% endblock %} \ No newline at end of file diff --git a/templates/species/species.jinja b/templates/species/species.jinja index 046cb27..08f4245 100644 --- a/templates/species/species.jinja +++ b/templates/species/species.jinja @@ -5,7 +5,7 @@ {% block content %} {% from 'button.jinja' import button %} {% from 'featured_species.jinja' import featured_species_section %} -{% from 'display_items.jinja' import display_all_items %} +{% from 'display_items.jinja' import display_all_species %} {% from 'search_bar.jinja' import search_bar%}
Species Database @@ -16,5 +16,5 @@ Species Database {# All Species #} {{search_bar("/species/search", "")}}

All Species

-{{display_all_items(species)}} +{{display_all_species(species)}} {% endblock %} \ No newline at end of file diff --git a/templates/species/species_search.jinja b/templates/species/species_search.jinja index a7dd344..cbd06a5 100644 --- a/templates/species/species_search.jinja +++ b/templates/species/species_search.jinja @@ -4,12 +4,12 @@ {% endblock %} {% block content %} {% from 'button.jinja' import button %} -{% from 'display_items.jinja' import display_all_items %} +{% from 'display_items.jinja' import display_all_species %} {% from 'search_bar.jinja' import search_bar%}
Species Database
{# All Species #} {{search_bar("/species/search", query)}} -{{display_all_items(results)}} +{{display_all_species(results)}} {% endblock %} \ No newline at end of file From 1e8f3b67f3927bad57dfd5a1b79fd08a6ab9e7a6 Mon Sep 17 00:00:00 2001 From: TussockyJoker Date: Mon, 6 Apr 2026 15:13:58 +0100 Subject: [PATCH 5/6] article searching working --- database/db_commands.py | 12 ++++++++++++ routes/library/article_library.py | 6 +++--- templates/library/article_search.jinja | 15 +++++++++++++++ 3 files changed, 30 insertions(+), 3 deletions(-) create mode 100644 templates/library/article_search.jinja diff --git a/database/db_commands.py b/database/db_commands.py index 57066e2..ca43acd 100644 --- a/database/db_commands.py +++ b/database/db_commands.py @@ -137,6 +137,18 @@ WHERE (query.lower(),) ).fetchall() return [dict(row) for row in rows] + +def search_articles(query): + with get_db() as conn: + rows = conn.execute("""SELECT * FROM articles WHERE + instr(LOWER(COALESCE(CAST(article_id AS TEXT), '')), ?1) > 0 + OR instr(LOWER(COALESCE(CAST(title AS TEXT), '')), ?1) > 0 + OR instr(LOWER(COALESCE(CAST(subtitle AS TEXT), '')), ?1) > 0 + OR instr(LOWER(COALESCE(CAST(author AS TEXT), '')), ?1) > 0 + OR instr(LOWER(COALESCE(CAST(publish_date AS TEXT), '')), ?1) > 0;""", + (query.lower(),) + ).fetchall() + return [dict(row) for row in rows] def delete_species(species_id): with get_db() as conn: diff --git a/routes/library/article_library.py b/routes/library/article_library.py index a55b9bc..f4e3e3f 100644 --- a/routes/library/article_library.py +++ b/routes/library/article_library.py @@ -1,7 +1,7 @@ from flask import render_template, Blueprint, request import configparser -from database.db_commands import get_all_articles, search_species, get_species_by_name +from database.db_commands import get_all_articles, search_articles, get_species_by_name from utilities.featured_species import featured_species_function config = configparser.ConfigParser() @@ -25,6 +25,6 @@ def all_species(): def search(): query = request.args.get("search-query") - results = search_species(query) + results = search_articles(query) print(results) - return render_template("/species/species_search.jinja", results=results, query=query) \ No newline at end of file + return render_template("/library/article_search.jinja", results=results, query=query) \ No newline at end of file diff --git a/templates/library/article_search.jinja b/templates/library/article_search.jinja new file mode 100644 index 0000000..8830d1b --- /dev/null +++ b/templates/library/article_search.jinja @@ -0,0 +1,15 @@ +{% extends "base.jinja" %} +{% block title %}Species Database{% endblock %} +{% block head %} +{% endblock %} +{% block content %} +{% from 'button.jinja' import button %} +{% from 'display_items.jinja' import display_all_articles %} +{% from 'search_bar.jinja' import search_bar%} +
+Article Library +
+{# All Species #} +{{search_bar("/articles/search", query)}} +{{display_all_articles(results)}} +{% endblock %} \ No newline at end of file From f5e2349acab7a7d9c4ee96ab5149e4f516e96ee5 Mon Sep 17 00:00:00 2001 From: TussockyJoker Date: Mon, 6 Apr 2026 15:16:43 +0100 Subject: [PATCH 6/6] updated example db with new articles table --- database/example_db.db | Bin 57344 -> 69632 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/database/example_db.db b/database/example_db.db index 3b0ac705aa1cb91708c4be285b5bf546ba05ad6e..804ba99c72c0d947aea2fcb951d8c1143375f3e8 100644 GIT binary patch delta 2858 zcmc&$OKclO7fdpp+|7>hE3AE(`Sc;t8`TuXe$3N@6 zcDwi5M}6-;IeImh%Z=h+_jCAWSs%Zen|S+Zq40gKIFQff8^t@NH%GQgwbFR;`QpdJ zm7!wcx53X2-5tn(b@N95Yd4M;2ggpG>Ul$?)C+;eX~05Q34>@g*>`bxda*iLtCHH} z)P*Y9w?s}4lZSmI&`Q8@dHeQA`L9)7AS|g>U#@L`Gd%XxzN%IncCWhy?e2~2JKG_J z%PGka$`9vq_Tpl7_S`%+7niG# z(3}Qo2#+%+X?g#sZjA=fN(!%_*D{MoY)e~dL&T4(YPP&ENE$1grZ9e{FfcZKV*5<# z_>qp}WJT@%H@Z_Cy4%+~P#oC#xo~Xj*ie2fSK2K8T7<%nh4Uk~M%4T#`LUf*^80Z6 z`tVTSR-xbXd{t$JZ!lZq+^}uta@X*54XEksj4{hoZPTY7IErCVMWIXums+0d>b_zy z*EBstbD8F91~(jRcbV=3bv&Jdt}xBFJkzlZ1%PRw8H%CnIybzwq2gV~p}C$9zJBhz z>%)g68;U)kc+};-&UMH4HQY(nZ0b9@=i44r3fXXs&a$tmg$-Xb(o_$ma3W5Q7KncB(ExlYb%NZzU`Z~p(@y`Q%&(L z*LS&YKSQZud0;8J?&&JWKFcyK6I?Jo+fpnS81r}8?A-eBZ0{38`KjEk_K9tB{{UqJhNLN?1j;V~ zuGipl8+%v`_$c8Sc1Yv8TneHT;)te!h-gSSjaMs(A^Ra-2O=U;#ivYT;)3Kwnj#Ea zvNq%30Ys!8i;Wh8e|i%NfJa1h^DA& zM!d_HCgludj?GABoLuxIBbthM^WL@hr9G0Ylpr3gpq#9Wi6{*sh*H$FyoB|Bn=({R zBO;m*cjcxH=?FnNXVRMVRAu~kcI9Xp4Rm;K2>*#j1ynPYe^?*G*0-Pq_Uyig{o)D<#Oh9Lf79d^=aAc+^4(Tpxmk$jSL6TOpDPlZ(fZ>aJ zfH7wWhsHSEI4QgH1HO+bqAX+?9ymp4Z6p|mFAv{F5b`ka@vO^po_6K~v6Iatg|%ej z1&AU{O4MwBf6M|vwm I2Om3s0@g8Fr2qf` delta 135 zcmZozz|wGld4jZH2?GNI9}vR;*F+s-r4k0cE)ibd(+u34d<@(feAoHgd7Ah`_%-<) z_!e>La`N$R7A)xH*v!Xymt|rA$L7zxf=vQ!Jo^~T<5RijpNJVeaZ*ae1vE7^);Ht00vYe%>V!Z