Skip to content

Commit

Permalink
Merge pull request #37 from davewalker5/sighting-notes
Browse files Browse the repository at this point in the history
Sighting notes
  • Loading branch information
davewalker5 authored Feb 26, 2022
2 parents 267681e + beb0213 commit a16330d
Show file tree
Hide file tree
Showing 21 changed files with 167 additions and 54 deletions.
10 changes: 5 additions & 5 deletions docker/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
FROM python:3.10-slim-bullseye AS runtime

COPY naturerecorderpy-1.0.23.0 /opt/naturerecorderpy-1.0.23.0
COPY naturerecorderpy-1.0.24.0 /opt/naturerecorderpy-1.0.24.0

WORKDIR /opt/naturerecorderpy-1.0.23.0
WORKDIR /opt/naturerecorderpy-1.0.24.0

RUN apt-get update -y
RUN pip install -r requirements.txt
RUN pip install nature_recorder-1.0.23-py3-none-any.whl
RUN pip install nature_recorder-1.0.24-py3-none-any.whl

ENV NATURE_RECORDER_DATA_FOLDER=/var/opt/naturerecorderpy-1.0.23.0
ENV NATURE_RECORDER_DB=/var/opt/naturerecorderpy-1.0.23.0/naturerecorder.db
ENV NATURE_RECORDER_DATA_FOLDER=/var/opt/naturerecorderpy-1.0.24.0
ENV NATURE_RECORDER_DB=/var/opt/naturerecorderpy-1.0.24.0/naturerecorder.db

ENTRYPOINT [ "python" ]
CMD [ "-m", "naturerec_web", "production" ]
2 changes: 1 addition & 1 deletion features/steps/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ def _(context):
species = create_test_species(row["Species"], category.id)
gender = [key for key, value in Gender.gender_map().items() if value == row["Gender"]][0]
with_young = 1 if row["WithYoung"] == "Yes" else 0
_ = create_sighting(location.id, species.id, sighting_date, int(row["Number"]), gender, with_young)
_ = create_sighting(location.id, species.id, sighting_date, int(row["Number"]), gender, with_young, None)


@given("A set of conservation status schemes")
Expand Down
2 changes: 1 addition & 1 deletion features/steps/jobs.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def _(context):
category = create_test_category("Birds")
species = create_test_species("Cormorant", category.id)
gender = [key for key, value in Gender.gender_map().items() if value == "Unknown"][0]
_ = create_sighting(location.id, species.id, sighting_date, None, gender, 0)
_ = create_sighting(location.id, species.id, sighting_date, None, gender, 0, None)

# Kick off the export
exporter = SightingsExportHelper("sightings.csv", None, None, None, None)
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ def find_package_files(directory, remove_root):

setuptools.setup(
name="nature_recorder",
version="1.0.23",
version="1.0.24",
description="Wildlife sightings database",
packages=setuptools.find_packages("src"),
include_package_data=True,
Expand Down
2 changes: 2 additions & 0 deletions sql/add_notes.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ALTER TABLE Sightings
ADD Notes TEXT NULL;
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
+-----------+-----------------------------------------------------------------------------+
| Longitude | Longitude for the location in decimal format |
+-----------+-----------------------------------------------------------------------------+
| Notes | Sighting notes |
+-----------+-----------------------------------------------------------------------------+
"""

from .data_exchange_helper_base import DataExchangeHelperBase
Expand All @@ -51,7 +53,8 @@ class SightingsDataExchangeHelperBase(DataExchangeHelperBase):
'Postcode',
'Country',
'Latitude',
'Longitude'
'Longitude',
'Notes'
]

def __init__(self, action):
Expand Down
5 changes: 3 additions & 2 deletions src/naturerec_model/data_exchange/sightings_import_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ def import_sightings(self):
number = int(row[2]) if row[2].strip() else None
gender = [key for key, value in Gender.gender_map().items() if value == row[3].strip().title()][0]
with_young = 1 if row[4].strip().title() == "Yes" else 0
_ = create_sighting(location_id, species_id, date, number, gender, with_young)
notes = row[14] if row[14] else None
_ = create_sighting(location_id, species_id, date, number, gender, with_young, notes)

def _read_csv_rows(self):
"""
Expand Down Expand Up @@ -74,7 +75,7 @@ def _validate_row(cls, row, row_number):
:param row: CSV row (collection of fields)
:param row_number: Row number for error reporting
"""
if len(row) != 14:
if len(row) != 15:
raise ValueError(f"Malformed data at row {row_number}")

cls._check_not_empty(row, 0, row_number) # Species
Expand Down
10 changes: 7 additions & 3 deletions src/naturerec_model/logic/sightings.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def _check_for_existing_records(session, location_id, species_id, date):
return [sighting.id for sighting in sightings]


def create_sighting(location_id, species_id, date, number, gender, with_young):
def create_sighting(location_id, species_id, date, number, gender, with_young, notes):
"""
Create a new sighting
Expand All @@ -37,6 +37,7 @@ def create_sighting(location_id, species_id, date, number, gender, with_young):
:param number: Number of individuals seen
:param gender: Gender of the individuals seen
:param with_young: Whether or not young were seen
:param notes: Sighting notes
:return: An instance of the Sighting class for the created record
"""
try:
Expand All @@ -51,15 +52,16 @@ def create_sighting(location_id, species_id, date, number, gender, with_young):
sighting_date=date,
number=number,
gender=gender,
withYoung=with_young)
withYoung=with_young,
notes=notes)
session.add(sighting)
except IntegrityError as e:
raise ValueError("Invalid sighting properties") from e

return sighting


def update_sighting(sighting_id, location_id, species_id, date, number, gender, with_young):
def update_sighting(sighting_id, location_id, species_id, date, number, gender, with_young, notes):
"""
Update an existing sighting
Expand All @@ -70,6 +72,7 @@ def update_sighting(sighting_id, location_id, species_id, date, number, gender,
:param number: Number of individuals seen
:param gender: Gender of the individuals seen
:param with_young: Whether or not young were seen
:param notes: Sighting notes
:return: An instance of the Sighting class for the updated record
"""
try:
Expand Down Expand Up @@ -97,6 +100,7 @@ def update_sighting(sighting_id, location_id, species_id, date, number, gender,
sighting.number = number
sighting.gender = gender
sighting.withYoung = with_young
sighting.notes = notes
session.add(sighting)
except IntegrityError as e:
raise ValueError("Invalid sighting properties") from e
Expand Down
8 changes: 6 additions & 2 deletions src/naturerec_model/model/sighting.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ class Sighting(Base):
withYoung = Column(Integer, default=0, nullable=False)
#: Number of individuals seen
gender = Column(Integer, default=Gender.UNKNOWN, nullable=False)
#: Sighting notes
notes = Column(String, default=None, nullable=True)

#: Related location instance
location = relationship("Location", lazy="joined")
Expand All @@ -50,7 +52,8 @@ def __repr__(self):
f"date={self.date!r}, " \
f"number={self.number!r}, " \
f"withYoung={self.withYoung!r}, " \
f"gender={self.gender!r})"
f"gender={self.gender!r}, " \
f"notes={self.notes!r})"

@property
def sighting_date(self):
Expand Down Expand Up @@ -88,5 +91,6 @@ def csv_columns(self):
self.location.postcode,
self.location.country,
self.location.latitude,
self.location.longitude
self.location.longitude,
self.notes
]
17 changes: 14 additions & 3 deletions src/naturerec_web/sightings/sightings_blueprint.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"""

import datetime
import html
from flask import Blueprint, render_template, request, session, redirect
from flask_login import login_required
from naturerec_model.logic import list_sightings, get_sighting, create_sighting, update_sighting
Expand Down Expand Up @@ -186,22 +187,27 @@ def edit(sighting_id):
category_id = get_posted_int("category")
session["category_id"] = category_id

# Get the notes and escape them
notes = html.escape(request.form["notes"])

if sighting_id:
_ = update_sighting(sighting_id,
location_id,
get_posted_int("species"),
sighting_date,
get_posted_int("number"),
get_posted_int("gender"),
get_posted_bool("with_young"))
get_posted_bool("with_young"),
notes)
sighting = get_sighting(sighting_id)
else:
created_id = create_sighting(location_id,
get_posted_int("species"),
sighting_date,
get_posted_int("number"),
get_posted_int("gender"),
get_posted_bool("with_young")).id
get_posted_bool("with_young"),
notes).id
sighting = get_sighting(created_id)

# Construct the confirmation message
Expand All @@ -210,7 +216,12 @@ def edit(sighting_id):
f"at {sighting.location.name} " \
f"on {sighting.display_date}"

return _render_sighting_editing_page(sighting_id, message, None)
# If we're editing an existing sighting, return to the sightings list page, so the
# change can be seen in the sightings list. Otherwise, return to the editing page
if sighting_id:
return redirect("/sightings/list")
else:
return _render_sighting_editing_page(sighting_id, message, None)
except ValueError as e:
return _render_sighting_editing_page(sighting_id, None, e)
else:
Expand Down
6 changes: 6 additions & 0 deletions src/naturerec_web/sightings/templates/sightings/edit.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@
{% set number = sighting.number if sighting.number else "" %}
{% set current_gender = sighting.gender %}
{% set current_with_young = sighting.withYoung %}
{% set current_notes = sighting.notes if sighting.notes else "" %}
{% else %}
{% set title = "Add Sighting" %}
{% set submit_title = "Add Sighting" %}
{% set species_id = 0 %}
{% set number = "" %}
{% set current_gender = "" %}
{% set current_with_young = "" %}
{% set current_notes = "" %}
{% endif %}
{% set location_required = "required" %}
{% set species_required = "required" %}
Expand Down Expand Up @@ -58,6 +60,10 @@ <h1>{{ title }}</h1>
{% endfor %}
</select>
</div>
<div class="form-group">
<label for="notes">Notes</label>
<textarea class="form-control" name="notes" rows="3">{{ current_notes }}</textarea>
</div>
<div class="button-bar">
{% if sighting %}
<button type="button" class="btn btn-light">
Expand Down
13 changes: 13 additions & 0 deletions src/naturerec_web/sightings/templates/sightings/list.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ <h1>Sightings</h1>
{% if sightings | length > 0 %}
<p>{{ sightings | length }} matching sighting{% if sightings | length > 1 %}s{% endif %} found</p>
{% include "sightings/sightings.html" with context %}
{% include "sightings/notes.html" with context %}
{% else %}
<span>There are no sightings in the database matching the specified criteria</span>
{% endif %}
Expand All @@ -31,6 +32,18 @@ <h1>Sightings</h1>
$("#category").val("");
document.forms[0].submit();
});

// Wire up the model "notes" dialog content update
var notesModal = document.getElementById('notes-popup');
notesModal.addEventListener('show.bs.modal', function (event) {
// Extract the content from the element that triggered the modal
var element = event.relatedTarget;
var notes = element.getAttribute('data-bs-sighting-notes');

// Apply the notes to the modal
var modalBody = notesModal.querySelector('.modal-body');
modalBody.innerHTML = notes;
});
})
</script>
{% endblock %}
15 changes: 15 additions & 0 deletions src/naturerec_web/sightings/templates/sightings/notes.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<div class="modal fade" id="notes-popup" tabindex="-1" aria-labelledby="notes-popup-title" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="notes-popup-title">Notes</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary" data-bs-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
14 changes: 14 additions & 0 deletions src/naturerec_web/sightings/templates/sightings/sightings.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
<th>Number</th>
<th>Gender</th>
<th>Young</th>
<th/>
{% if edit_enabled %}
<th/>
<th/>
Expand All @@ -18,6 +19,11 @@
</thead>
<tbody>
{% for sighting in sightings %}
{% if sighting.notes != None %}
{% set sighting_notes = sighting.notes %}
{% else %}
{% set sighting_notes = "" %}
{% endif %}
<tr>
<td>{{ sighting.display_date }}</td>
<td>{{ sighting.location.name }}</td>
Expand All @@ -26,6 +32,14 @@
<td>{{ sighting.number if sighting.number else "" }}</td>
<td>{{ sighting.gender_name }}</td>
<td>{{ sighting.with_young_name }}</td>
<td>
{% if sighting_notes | length > 0 %}
<i class="far fa-sticky-note icon-blue notes-icon" title="View Notes" data-bs-toggle="modal"
data-bs-target="#notes-popup" data-bs-sighting-notes="{{ sighting_notes }}"></i>
{% else %}
<i class="far fa-sticky-note icon-grey" title="There are no notes for this sighting"></i>
{% endif %}
</td>
{% if edit_enabled %}
<td>
<a href="{{ url_for( 'species_ratings.list_status_ratings', species_id = sighting.speciesId ) }}">
Expand Down
10 changes: 10 additions & 0 deletions src/naturerec_web/static/css/site.css
Original file line number Diff line number Diff line change
Expand Up @@ -128,4 +128,14 @@ div.message {
.geocode {
color: #286090;
cursor: pointer;
}

/* --- Icons --------------------------------------------------------------- */

.icon-blue {
color: #0c6efd;
}

.icon-grey {
color: grey;
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ def setUp(self) -> None:
self._category = create_category("Birds")
self._gull = create_species(self._category.id, "Black-Headed Gull")
self._location = create_location(name="Radley Lakes", county="Oxfordshire", country="United Kingdom")
_ = create_sighting(self._location.id, self._gull.id, datetime.date(2021, 12, 14), None, Gender.UNKNOWN, False)
_ = create_sighting(self._location.id, self._gull.id, datetime.date(2021, 12, 14), None, Gender.UNKNOWN, False,
None)

def test_can_export_life_list(self):
# Export the sightings
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ def setUp(self) -> None:
self._gull = create_species(self._category.id, "Black-Headed Gull")
self._cormorant = create_species(self._category.id, "Cormorant")
self._location = create_location(name="Radley Lakes", county="Oxfordshire", country="United Kingdom")
_ = create_sighting(self._location.id, self._gull.id, datetime.date(2021, 12, 14), None, Gender.UNKNOWN, False)
_ = create_sighting(self._location.id, self._gull.id, datetime.date(2021, 12, 14), None, Gender.UNKNOWN, False,
None)

def test_can_export_sightings(self):
# Export the sightings
Expand All @@ -34,7 +35,7 @@ def test_can_export_sightings(self):

self.assertEqual(2, len(rows))
self.assertEqual(SightingsExportHelper.COLUMN_NAMES, rows[0])
self.assertEqual(14, len(rows[1]))
self.assertEqual(15, len(rows[1]))
self.assertEqual("Black-Headed Gull", rows[1][0])
self.assertEqual("Birds", rows[1][1])
self.assertEqual("", rows[1][2])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ def _perform_valid_import(self):
filename = os.path.join(get_data_path(), "valid_status_import.csv")
TestSightingsImportHelper._create_test_file(filename, [
"Species,Category,Number,Gender,WithYoung,Date,Location,Address,City,County,Postcode,Country,"
"Latitude,Longitude\n",
"Latitude,Longitude,Notes\n",
"Robin,Birds,1,Unknown,No,01/02/2021,Abingdon,An Address,Abingdon,Oxfordshire,OX14,United Kingdom,"
"51.6708,-1.2880\n"
"51.6708,-1.2880,\n"
])

# Import the sightings
Expand Down
3 changes: 2 additions & 1 deletion tests/naturerec_model/logic/test_reports.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ def setUp(self) -> None:
self._category = create_category("Birds")
self._species = create_species(self._category.id, "Black-Headed Gull")
self._location = create_location(name="Radley Lakes", county="Oxfordshire", country="United Kingdom")
create_sighting(self._location.id, self._species.id, datetime.date(2021, 12, 14), 30, Gender.UNKNOWN, False)
create_sighting(self._location.id, self._species.id, datetime.date(2021, 12, 14), 30, Gender.UNKNOWN, False,
None)

def test_can_get_location_species_report(self):
report_df = location_species_report(datetime.date(2021, 12, 1), datetime.datetime.today(), self._location.id,
Expand Down
Loading

0 comments on commit a16330d

Please sign in to comment.