Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion reports/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ The following reports are currently available:

| Notebook | Report Type |
| --- | --- |
| annual_category_location_heatmap.ipynb | Heatmap of number of sightings of each species in a category at a location during a specified year |
| annual_category_location_heatmap.ipynb | Heatmap of number of sightings of each species in a category at a location during a specified year |
| category_life_list.ipynb | Life list for the species in a category, including total sightings and location count |
| location_richness_map.ipynb | Interactive map of species richness (number of unique species sighted) by location |
| year_on_year_species_location_trend.ipynb | Year-on-year sightings trend for a species, optionally limited to one location |

Expand Down
137 changes: 137 additions & 0 deletions reports/category_life_list.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "d1c0d0e6",
"metadata": {},
"source": [
"# Category Life List\n",
"\n",
"This notebook generates and exports a life list for all sightings of the species in a category. For each species in that category, the total number of sightings of the species and the number of locations where sightings were recorded are shown alongside the species name. To use it, update the category in the first code cell, below, before running the notebook."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e2d768fd",
"metadata": {},
"outputs": [],
"source": [
"# Species category to report on\n",
"category = \"\""
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "1b9351fe",
"metadata": {},
"outputs": [],
"source": [
"from pathlib import Path\n",
"import sqlparse\n",
"\n",
"# Read the query file\n",
"query_file_path = Path(\"sql\") / \"category_life_list.sql\"\n",
"with open(query_file_path.absolute(), \"r\") as f:\n",
" query = f.read().replace(\"\\n\", \" \")\n",
"\n",
"# Replace the placeholders\n",
"query = query.replace(\"$CATEGORY\", category)\n",
"\n",
"# Show a pretty-printed form of the query\n",
"print(sqlparse.format(query, reindent=True, keyword_case='upper'))"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "0a3f55c5",
"metadata": {},
"outputs": [],
"source": [
"import pandas as pd\n",
"import sqlite3\n",
"import os\n",
"\n",
"# Connect to the database, execute the query and read the results into a dataframe\n",
"database_path = os.environ[\"NATURE_RECORDER_DB\"]\n",
"connection = sqlite3.connect(database_path)\n",
"df = pd.read_sql_query(query, connection, parse_dates=[\"Date\"])\n",
"\n",
"# Check there is some data\n",
"if not df.shape[0]:\n",
" message = f\"No data found for category '{category}'\"\n",
" raise ValueError(message)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "a1b8b432",
"metadata": {},
"outputs": [],
"source": [
"# Aggregate the data to produce the life list\n",
"life_list = (\n",
" df\n",
" .groupby(\"Species\")\n",
" .agg(\n",
" Sightings=(\"Count\", \"sum\"),\n",
" Locations=(\"Location\", pd.Series.nunique)\n",
" )\n",
" .reset_index()\n",
" .sort_values(by=\"Species\", ascending=True)\n",
")\n",
"\n",
"\n",
"# Print the life list\n",
"with pd.option_context('display.max_rows', None,\n",
" 'display.max_columns', None,\n",
" 'display.precision', 3,\n",
" ):\n",
" display(life_list)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "7827f304",
"metadata": {},
"outputs": [],
"source": [
"import re\n",
"\n",
"# Create the folder to hold exported reports\n",
"export_folder_path = Path(\"exported\")\n",
"export_folder_path.mkdir(parents=True, exist_ok=True)\n",
"\n",
"# Export the life list\n",
"clean_category = re.sub(\"[^0-9a-zA-Z ]+\", \"\", category).replace(\" \", \"-\")\n",
"export_file_path = export_folder_path / f\"{clean_category}-Life-List.xlsx\"\n",
"life_list.to_excel(export_file_path.absolute(), sheet_name=\"Sightings\")"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "venv",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.4"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
6 changes: 6 additions & 0 deletions reports/sql/category_life_list.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
SELECT l.Name AS 'Location', sp.Name AS 'Species', c.Name AS 'Category', DATE( s.Date ) AS 'Date', IFNULL( s.Number, 1 ) AS 'Count'
FROM SIGHTINGS s
INNER JOIN SPECIES sp ON sp.Id = s.SpeciesId
INNER JOIN CATEGORIES c ON c.Id = sp.CategoryId
INNER JOIN LOCATIONS l ON l.Id = s.LocationId
WHERE c.Name = "$CATEGORY";
2 changes: 1 addition & 1 deletion reports/sql/sightings.sql
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
SELECT l.Name AS 'Location', sp.Name AS 'Species', sp.Name AS 'Category', DATE( s.Date ) AS 'Date', IFNULL( s.Number, 1 ) AS 'Count'
SELECT l.Name AS 'Location', sp.Name AS 'Species', c.Name AS 'Category', DATE( s.Date ) AS 'Date', IFNULL( s.Number, 1 ) AS 'Count'
FROM SIGHTINGS s
INNER JOIN SPECIES sp ON sp.Id = s.SpeciesId
INNER JOIN CATEGORIES c ON c.Id = sp.CategoryId
Expand Down
2 changes: 1 addition & 1 deletion reports/sql/species_year_on_year.sql
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
SELECT l.Name AS 'Location', sp.Name AS 'Species', sp.Name AS 'Category', DATE( s.Date ) AS 'Date', IFNULL( s.Number, 1 ) AS 'Count'
SELECT l.Name AS 'Location', sp.Name AS 'Species', c.Name AS 'Category', DATE( s.Date ) AS 'Date', IFNULL( s.Number, 1 ) AS 'Count'
FROM SIGHTINGS s
INNER JOIN SPECIES sp ON sp.Id = s.SpeciesId
INNER JOIN CATEGORIES c ON c.Id = sp.CategoryId
Expand Down