AGENTS.md
Not logged in

Guidance for agents working in this repository.

What this project is

IPVTechDVData: a domestic-violence (DV) data collection app. It scrapes police logs and court decisions, stores them, and serves a Flask web UI for keyword search, tagging, and time-series charts (count + percentage over time). Two entity types are tracked: PoliceLog (police blotter entries) and Decision (court opinions). Both share a common Tag system with two tag types: case and device.

Version control: Fossil, not Git

This is a Fossil repository (.fslckout), not Git. The repository file is ../IPVTech.fossil. Use fossil commands, e.g. fossil status, fossil diff, fossil commit. There is no .git.

Python 2 codebase

All Python code targets Python 2 and will not run on Python 3 without porting:

There is no requirements.txt, setup.py, or Makefile. Dependencies must be installed manually under a Python 2 interpreter:

Repository layout

frontend/          Flask web application (the main product)
  main.py            App entry point, all routes
  db.py              Peewee ORM models + MySQL connection
  consts.py          Hardcoded incident type/device lists for tag suggestions
  flask_login.py     VENDORED copy of flask-login 0.2.10 (see below)
  flask_pagination.py VENDORED copy of flask-paginate (see below)
  index2.fcgi        FastCGI entry point for production (flup)
  ipvtech.sql        MySQL schema + seed data dump (Navicat export, ~2014)
  static/            Bootstrap 3, jQuery, d3 v3, nv.d3, bootstrap-tagsinput
  task_scripts/      Scraping/ingest scripts (run separately from the web app)
crawler/           Standalone Cambridge police-log scraper (sqlite-based)
images/            Project logos

Vendored libraries — do NOT pip install these

frontend/flask_login.py and frontend/flask_pagination.py are vendored copies committed into the repo. main.py imports them as local modules (from flask_login import LoginManager, from flask_pagination import Pagination). If you pip install flask-login it will shadow/conflict with these. Treat them as part of the codebase, not external dependencies. Do not assume they match any particular upstream release behavior.

Missing templates directory

main.py calls render_template() for: index.html, result.html, decision_result.html, case.html, decision.html, report.html, login.html. No templates/ directory exists in the repo. The app cannot render any page as-is. Either the templates were never committed or were removed. This is the single biggest blocker to running the frontend locally.

Two parallel data pipelines

There are two separate, divergent implementations of the Cambridge scraper — be careful which one you're looking at:

  1. crawler/police_log_scraper.py — writes to a local SQLite file policelog.sqlite (schema in crawler/policelog_db.sql). Columns: report_date, report_time, occur_date, occur_time as separate TEXT fields.
  2. frontend/task_scripts/scrape_cambridge_police_log.py — near-identical logic but writes to MySQL ipvtech.policelog via MySQLdb, using string-interpolated SQL (SQL-injection-prone). Same separate date/time column layout.

The actual MySQL schema in ipvtech.sql (used by the Flask app) instead has combined report_time and occur_time DATETIME columns — no report_date/occur_date columns. So the scraper column layout does not match the schema the web app expects. This schema drift is pre-existing and unresolved.

The court-decisions pipeline is separate:

Database

Security-sensitive hardcoded values (pre-existing, insecure)

These are committed in source — do not propagate the pattern, and do not assume they are secret:

Running the app

Routes & data flow (main.py)

Route Method Purpose
/ GET Index page
/search GET Paginated PoliceLog search (keyword/source/tag filters)
/decisions GET Paginated Decision search (same filter pattern)
/search_data GET JSON time-series (count + %) for PoliceLog, consumed by nv.d3 charts
/decision_data GET JSON time-series for Decisions
/case/<id> GET Single PoliceLog detail + tags
/decision/<id> GET Single Decision detail + tags
/report GET Self-report incident form
/insert POST Insert a user-reported PoliceLog (source="reported")
/register POST Register user (md5 password), logs them in
/login POST Stub — returns "hello", not implemented
/save_tags, /save_decision_tags GET Replace all tags on a record (case + device), via query-string type_tags/device_tags

Conventions to know when editing main.py

Gotchas summary