Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/GustavoNightmare/InformacionMuseo/llms.txt

Use this file to discover all available pages before exploring further.

Every time an admin creates, edits, or deletes a species record — or views a species detail page while authenticated as an admin — BioScan Museo writes an entry to the species_audit_log table. Each entry captures who made the change, what action was taken, the before and after values for updated fields, and a snapshot of the species’ name and QR identifier at the time of the action. Because snapshot columns are written at the moment of the event, audit records remain readable and queryable even after a species has been permanently deleted from the catalog.

Data Model

The SpeciesAuditLog model (table: species_audit_log) stores one row per auditable event. Update events produce one row per changed field, so a single form submission that modifies five fields creates five audit rows sharing the same timestamp and user_id.
ColumnTypeDescription
idInteger (PK)Auto-increment primary key
species_idString(64)ID of the affected species (matches Species.id); retained after deletion
user_idInteger (FK → user.id)Admin who performed the action; NULL for system-initiated events
actionString(20)One of created, updated, deleted, or viewed_admin
field_nameString(64)Name of the changed field; only populated for updated actions
old_valueTextValue of the field before the change; only for updated actions
new_valueTextValue of the field after the change; only for updated actions
created_atDateTimeUTC timestamp of the event
notesTextHuman-readable note attached to created and deleted entries
species_name_snapshotString(200)Common name of the species at the time of the event
species_scientific_name_snapshotString(200)Scientific name of the species at the time of the event
qr_id_snapshotString(64)QR identifier of the species at the time of the event

Snapshot Columns

The three snapshot columns (species_name_snapshot, species_scientific_name_snapshot, qr_id_snapshot) are populated on every audit write, regardless of action type. When a species is later deleted, these columns allow the audit UI and API to display meaningful information about deleted species without needing to join against a now-missing row. The admin audit view automatically falls back to snapshot values whenever db.session.get(Species, log.species_id) returns None.

Action Types

ActionWhen it firesfield_name populated
createdPOST /admin/especies/nueva — new species saved successfullyNo; a summary note is stored instead
updatedPOST /admin/especies/<id>/editar — one row per changed fieldYes — the name of the field that changed
deletedPOST /admin/especies/<id>/eliminar — before the record is removedNo; a deletion note is stored instead
viewed_adminGET /especie/<qr_id>?origin=manual when the requester is an adminNo

Fields Tracked for Updates

The log_species_updated() helper compares a snapshot of the species taken before the edit form is submitted against the updated object after processing. A separate audit row is written for each field where str(old_value) != str(new_value). The following fields are tracked: qr_id · nombre_comun · nombre_cientifico · familia · orden · descripcion · habitat · dieta · zonas · museo_info · imagen · audio · curiosidades_json · thumb_pos_x · thumb_pos_y · thumb_zoom

viewed_admin Deduplication

To prevent the audit log from filling up with repeated view events during normal browsing, log_species_viewed_admin() checks for an existing viewed_admin entry for the same user_id and species_id within the past 5 minutes. If a recent entry is found, no new row is written. This window is hardcoded in app.py and applies per user-species pair.

Admin UI

The audit log is accessible to admins at /admin/especies/auditoria. The view is paginated at 10 entries per page (most recent first) and supports the following filter controls:
FilterQuery paramAccepted values
Speciesspecies_idInternal species ID (e.g. condor-001)
Actionactioncreated, updated, deleted, viewed_admin
Admin useruser_idInteger user ID; only admin users appear in the dropdown
Start datestartYYYY-MM-DD; defaults to 30 days ago
End dateendYYYY-MM-DD; defaults to today
PagepageInteger; defaults to 1
All filter parameters are validated server-side. An invalid action value is silently reset to show all actions. A species_id that has no existing species record but does have historical audit rows is still accepted, allowing admins to review the audit history of deleted species.

JSON API

The same data is available as a JSON response at:
GET /api/admin/especies/auditoria
This endpoint accepts the identical query parameters as the HTML view and requires an active admin session. It returns a JSON object built by build_audit_context().

Response Structure

KeyTypeDescription
filters.species_idstringActive species filter, or ""
filters.actionstringActive action filter, or ""
filters.user_idstringActive user filter, or ""
filters.startstringStart date in YYYY-MM-DD
filters.endstringEnd date in YYYY-MM-DD
filters.species_optionsarrayAll species for filter dropdown: [{id, nombre_comun}]
filters.user_optionsarrayAll admin users for filter dropdown: [{id, nombre}]
logsarrayFormatted audit entries (see below)
totalintegerTotal matching rows across all pages
pagination.pageintegerCurrent page number
pagination.pagesintegerTotal number of pages
pagination.has_nextbooleanWhether a next page exists
pagination.has_prevbooleanWhether a previous page exists
pagination.next_numinteger | nullNext page number
pagination.prev_numinteger | nullPrevious page number

Log Entry Object

Each element in the logs array has the following shape:
KeyTypeDescription
idintegerAudit log row ID
species_idstringInternal species ID
species_namestringCommon name (live or from snapshot if species is deleted)
species_scientific_namestringScientific name (live or snapshot)
qr_idstringQR code identifier (live or snapshot)
actionstringcreated, updated, deleted, or viewed_admin
field_namestring | nullField that changed (only for updated)
old_valuestring | nullValue before the change (only for updated)
new_valuestring | nullValue after the change (only for updated)
created_atstringUTC timestamp formatted as YYYY-MM-DD HH:MM:SS
user_idinteger | nullID of the admin user; null for system events
user_namestringDisplay name of the admin user, or "Sistema" if none
notesstring | nullNote attached to created or deleted entries
Example request:
curl -b cookies.txt \
  "http://localhost:5000/api/admin/especies/auditoria?action=updated&start=2025-01-01&page=1"
Example response (truncated):
{
  "filters": {
    "species_id": "",
    "action": "updated",
    "user_id": "",
    "start": "2025-01-01",
    "end": "2025-07-10",
    "species_options": [
      { "id": "condor-001", "nombre_comun": "Cóndor Andino" }
    ],
    "user_options": [
      { "id": 1, "nombre": "Administrador" }
    ]
  },
  "logs": [
    {
      "id": 42,
      "species_id": "condor-001",
      "species_name": "Cóndor Andino",
      "species_scientific_name": "Vultur gryphus",
      "qr_id": "condor-001",
      "action": "updated",
      "field_name": "descripcion",
      "old_value": "Ave carroñera de los Andes.",
      "new_value": "Ave carroñera emblemática de los Andes, con una envergadura de hasta 3,2 metros.",
      "created_at": "2025-06-15 14:32:07",
      "user_id": 1,
      "user_name": "Administrador",
      "notes": null
    }
  ],
  "total": 134,
  "pagination": {
    "page": 1,
    "pages": 14,
    "has_next": true,
    "has_prev": false,
    "next_num": 2,
    "prev_num": null
  }
}
Use the JSON API to build external reports or export audit data to a spreadsheet. Combine the species_id and date range filters to export the full change history for a single species before archiving or deleting it.

Audit Write Timing

All audit helpers (log_species_created, log_species_updated, log_species_deleted) call db.session.add() but do not commit. The commit is always performed by the calling route handler after all processing is complete. This means a failed upload or validation error before the commit will also roll back the audit entries, keeping the log consistent with actual database state. The viewed_admin helper follows the same pattern — db.session.add() without commit — and the route handler commits after the deduplication check passes.
Audit rows for deleted species are written before db.session.delete(item) is called in the delete route. This guarantees the snapshot columns capture the species’ final state before any cascading deletes are processed.

Database Indexes

The species_audit_log table is created with four indexes to support efficient filtering across the large volumes of data generated by seed-demo-data or busy production deployments:
IndexColumnPurpose
idx_audit_speciesspecies_idFilter by species
idx_audit_useruser_idFilter by admin user
idx_audit_actionactionFilter by action type
idx_audit_timecreated_atOrder by and date-range filter
These indexes are created by ensure_schema_updates() and are safe to apply to an existing database that already has data in the table.

Build docs developers (and LLMs) love