Browse Source

Save summary data and obscure submission URL

pull/1/head
Angelo DiNardi 3 years ago
parent
commit
3064848ecb
  1. 9
      404.html
  2. 2
      README.md
  3. 63
      app.py
  4. 9
      config/nginx.conf.erb
  5. 101
      datasheet.jsx
  6. 4
      index.html
  7. 348
      load.sql
  8. 6
      package.json
  9. 13
      submit.html

9
404.html

@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
<!DOCTYPE html>
<html>
<head>
<title>SFBBO Colonial Waterbird Datasheet | 404</title>
</head>
<body>
<h1>file not found.</h1>
</body>
</html>

2
README.md

@ -0,0 +1,2 @@ @@ -0,0 +1,2 @@
# DB Setup
Load `load.sql`.

63
app.py

@ -37,6 +37,24 @@ app.teardown_appcontext(close_db) @@ -37,6 +37,24 @@ app.teardown_appcontext(close_db)
def index():
return render_template('index.html')
@app.route('/submit/<key>')
def submit(key):
with get_db() as db:
with db.cursor() as cur:
try:
cur.execute("""
SELECT id FROM create_keys WHERE id = %(id)s;
""", {
'id': key,
})
if cur.rowcount == 1:
return render_template('submit.html')
except Exception as e:
print(e)
return render_template('404.html')
@app.route('/data/colonies')
def get_colonies():
with get_db() as db:
@ -67,6 +85,7 @@ def get_species(): @@ -67,6 +85,7 @@ def get_species():
def save_sheet():
data = request.get_json()
submitted_by_name = str(data['submitterName'])
colony_id = int(data['colonyId'])
date = str(data['date'])
start_time = str(data['startTime'])
@ -94,7 +113,8 @@ def save_sheet(): @@ -94,7 +113,8 @@ def save_sheet():
significant_change_notes,
human_disturbance,
human_disturbance_notes,
additional_observations
additional_observations,
submitted_by_name
) VALUES (
%(colony_id)s,
%(date)s,
@ -106,9 +126,12 @@ def save_sheet(): @@ -106,9 +126,12 @@ def save_sheet():
%(significant_change_notes)s,
%(human_disturbance)s,
%(human_disturbance_notes)s,
%(additional_observations)s
%(additional_observations)s,
%(submitted_by_name)s
)
RETURNING id
""", {
'submitted_by_name': submitted_by_name,
'colony_id': colony_id,
'date': date,
'start_time': start_time,
@ -122,4 +145,40 @@ def save_sheet(): @@ -122,4 +145,40 @@ def save_sheet():
'additional_observations': additional_observations,
})
row = cur.fetchone()
data_sheet_id = row.id
# Save the survey summary data: these are the overall nest counts.
for species_code, summary in survey_summary.items():
total_nests = int(summary['totalNests'])
total_adults = int(summary['totalAdults'])
total_young = int(summary['totalYoung'])
total_possible_nests = int(summary['totalPossibleNests'])
cur.execute("""
INSERT INTO survey_summary (
data_sheet_id,
species_code,
total_nests,
total_adults,
total_young,
total_possible_nests
) VALUES (
%(data_sheet_id)s,
%(species_code)s,
%(total_nests)s,
%(total_adults)s,
%(total_young)s,
%(total_possible_nests)s
)
""", {
'data_sheet_id': data_sheet_id,
'species_code': species_code,
'total_nests': total_nests,
'total_adults': total_adults,
'total_young': total_young,
'total_possible_nests': total_possible_nests,
})
return jsonify(data)

9
config/nginx.conf.erb

@ -40,6 +40,13 @@ http { @@ -40,6 +40,13 @@ http {
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://app_server;
}
location /submit {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://app_server;
}
location = / {
@ -48,7 +55,7 @@ http { @@ -48,7 +55,7 @@ http {
proxy_redirect off;
proxy_pass http://app_server;
}
location / {
root /app/dist;
}

101
datasheet.jsx

@ -108,34 +108,6 @@ type SurveySummaryData = { @@ -108,34 +108,6 @@ type SurveySummaryData = {
};
export const Datasheet = (props: Props) => {
// constructor(props: Props) {
// super(props);
// this.state = {
// colonies: [],
// colonyId: null,
// date: "",
// observers: [],
// startTime: null,
// endTime: null,
// nestVisibility: null,
// visibilityComments: "",
// surveySummary: {},
// significantChanges: null,
// significantChangesDescription: "",
// humanDisturbance: null,
// humanDisturbanceDescription: "",
// additionalObservations: "",
// hepNestData: [],
// guteNestData: [],
// };
// }
let speciesCounter = 3;
const [savingData, setSavingData] = useState(false);
@ -143,6 +115,7 @@ export const Datasheet = (props: Props) => { @@ -143,6 +115,7 @@ export const Datasheet = (props: Props) => {
id: number,
name: string
}>>([]);
const [submitterName, setSubmitterName] = useState("");
const [colonyId, setColonyId] = useState("");
const [date, setDate] = useState("");
// const [observers, setObservers] = useState<Array<string>>([]);
@ -176,32 +149,38 @@ export const Datasheet = (props: Props) => { @@ -176,32 +149,38 @@ export const Datasheet = (props: Props) => {
}
const saveToServer = async() => {
const response = await fetch(`${API_HOST}/data/sheet/save`, {
method: "POST",
cache: "no-cache",
mode: "cors",
credentials: "same-origin",
headers: {
"Content-Type": "application/json",
"X-CSRFToken": window.CSRF_TOKEN,
},
body: JSON.stringify({
colonyId,
date,
startTime,
endTime,
nestVisibility,
visibilityComments,
surveySummary,
colonySignificantChanges,
colonySignificantChangesNotes,
colonyHumanDisturbance,
colonyHumanDisturbanceNotes,
colonyAdditionalObservations,
}),
});
console.log(response);
setSavingData(false);
try {
const response = await fetch(`${API_HOST}/data/sheet/save`, {
method: "POST",
cache: "no-cache",
mode: "cors",
credentials: "same-origin",
headers: {
"Content-Type": "application/json",
"X-CSRFToken": window.CSRF_TOKEN,
},
body: JSON.stringify({
submitterName,
colonyId,
date,
startTime,
endTime,
nestVisibility,
visibilityComments,
surveySummary,
colonySignificantChanges,
colonySignificantChangesNotes,
colonyHumanDisturbance,
colonyHumanDisturbanceNotes,
colonyAdditionalObservations,
}),
});
console.log(response);
} catch (e) {
console.error("error", e);
} finally {
setSavingData(false);
}
};
saveToServer();
@ -243,6 +222,18 @@ export const Datasheet = (props: Props) => { @@ -243,6 +222,18 @@ export const Datasheet = (props: Props) => {
</div>
<hr />
<Form>
<Form.Group as={Form.Row} controlId="submitter-name">
<Form.Label column sm="2">
Submitter's Name:
</Form.Label>
<Col>
<Form.Control
type="text"
onChange={e => setSubmitterName(e.target.value)}
value={submitterName}
/>
</Col>
</Form.Group>
<Form.Group as={Form.Row}>
<Form.Label column sm="2" htmlFor="colony-id">
Colony ID:
@ -612,7 +603,7 @@ const HepNestRow = (props: { @@ -612,7 +603,7 @@ const HepNestRow = (props: {
<React.Fragment>
<Cell>{props.number}</Cell>
<Cell>
<Form.Check />
<Form.Check checked={props.isFocal} />
</Cell>
<Cell>
<Form.Control as="select">

4
index.html

@ -7,7 +7,7 @@ @@ -7,7 +7,7 @@
<title>SFBBO Colonial Waterbird Datasheet</title>
</head>
<body>
<div id="app"></div>
<script src="app.jsx"></script>
<div id="app">Hello.</div>
<!-- <script src="app.jsx"></script> -->
</body>
</html>

348
load.sql

@ -0,0 +1,348 @@ @@ -0,0 +1,348 @@
--
-- PostgreSQL database dump
--
-- Dumped from database version 11.6 (Ubuntu 11.6-1.pgdg16.04+1)
-- Dumped by pg_dump version 12.1
-- SET statement_timeout = 0;
-- SET lock_timeout = 0;
-- SET idle_in_transaction_session_timeout = 0;
-- SET client_encoding = 'UTF8';
-- SET standard_conforming_strings = on;
-- SELECT pg_catalog.set_config('search_path', '', false);
-- SET check_function_bodies = false;
-- SET xmloption = content;
-- SET client_min_messages = warning;
-- SET row_security = off;
--
-- Name: pgcrypto; Type: EXTENSION; Schema: -; Owner: -
--
CREATE EXTENSION IF NOT EXISTS pgcrypto WITH SCHEMA public;
-- SET default_tablespace = 'public';
--
-- Name: colony; Type: TABLE; Schema: public; Owner: gyggowszznwhkm
--
CREATE TABLE public.colony (
id integer NOT NULL,
name text
);
ALTER TABLE public.colony OWNER TO gyggowszznwhkm;
--
-- Name: colony_id_seq; Type: SEQUENCE; Schema: public; Owner: gyggowszznwhkm
--
CREATE SEQUENCE public.colony_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER TABLE public.colony_id_seq OWNER TO gyggowszznwhkm;
--
-- Name: colony_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: gyggowszznwhkm
--
ALTER SEQUENCE public.colony_id_seq OWNED BY public.colony.id;
--
-- Name: data_sheet; Type: TABLE; Schema: public; Owner: gyggowszznwhkm
--
CREATE TABLE public.data_sheet (
id uuid NOT NULL DEFAULT gen_random_uuid(),
colony_id integer NOT NULL,
date date NOT NULL,
start_time time without time zone NOT NULL,
end_time time without time zone NOT NULL,
nest_visibility text NOT NULL,
visibility_comments text,
significant_changes boolean NOT NULL,
human_disturbance boolean NOT NULL,
significant_change_notes text,
human_disturbance_notes text,
additional_observations text,
submitted_by_name text NOT NULL,
reviewed_by text,
reviewed_timestamp timestamp with time zone,
created_timestamp timestamp with time zone DEFAULT now() NOT NULL,
CONSTRAINT data_sheet_nest_visibility_check CHECK ((nest_visibility = ANY (ARRAY['good'::text, 'moderate'::text, 'poor'::text])))
);
ALTER TABLE public.data_sheet OWNER TO gyggowszznwhkm;
--
-- Name: survey_summary; Type: TABLE; Schema: public; Owner: gyggowszznwhkm
--
CREATE TABLE public.survey_summary (
id uuid NOT NULL DEFAULT gen_random_uuid(),
data_sheet_id uuid NOT NULL,
species_code text NOT NULL,
total_nests integer NOT NULL,
total_adults integer NOT NULL,
total_young integer NOT NULL,
total_possible_nests integer NOT NULL
);
ALTER TABLE public.survey_summary OWNER TO gyggowszznwhkm;
--
-- Name: colony id; Type: DEFAULT; Schema: public; Owner: gyggowszznwhkm
--
ALTER TABLE ONLY public.colony ALTER COLUMN id SET DEFAULT nextval('public.colony_id_seq'::regclass);
--
-- Data for Name: colony; Type: TABLE DATA; Schema: public; Owner: gyggowszznwhkm
--
COPY public.colony (id, name) FROM stdin;
1 Agua Vista
2 Alameda NWR
71 Alameda NWR Pier and Breakwater Island
70 Albany Mudflats
3 Alcatraz
4 Almaden Lake
9 Alviso A12
10 Alviso A16
75 Alviso A17
74 Alviso A18
72 Alviso A4
73 Alviso A5
145 Alviso A5/A6/A7/A8
6 Alviso A6
5 Alviso A7
7 Alviso A8
8 Alviso A9/10
146 Alviso A9/A10/A11/A14
76 Anderson Reservoir
131 Anderson Resevoir
101 Artesian Slough
144 Bacon Island
127 Bair Island
132 Bair Island - Subcolony 10
124 Bair Island - Subcolony 11
125 Bair Island - Subcolony 12
133 Bair Island - Subcolony 13
126 Bair Island - Subcolony 2
123 Bair Island - Subcolony 3
11 Bair Island Towers
12 Bair Island Upland
77 Bay Bridge
13 Bay Farm Island, Alameda
14 Belmont Slough
15 Brooks Island
158 Bunting Pond
78 Burlingame
79 Calabazas Marsh
16 Calaveras Reservoir at Marsh Rd
135 Calaveras Reservoir, southern end
81 CCFS Coyote Creek Tree
136 Charleston Slough
17 Corte Madera
80 Coyote Creek Lagoon
84 Coyote Hills
18 Coyote Hills 2A/3A/4A levees
82 Coyote Hills N2A
85 Coyote Hills N4
83 Coyote Hills N5/N7 levee
147 Coyote Hills N6/N7 Levee
19 Coyote Parkway Lakes
130 Coyote Ranch Rd Colony
20 Crocker Lake
21 Don Castro
157 Downtown Oakland
87 Drawbridge A19, A21
89 Dumbarton
22 Dumbarton N1
23 Dumbarton N2
24 Dumbarton N3
88 Dumbarton PG&E Towers
28 Eden Landing
92 Eden Landing E10
94 Eden Landing E11
96 Eden Landing E12
134 Eden Landing E13
97 Eden Landing E14
27 Eden Landing E4/7
25 Eden Landing E6B
26 Eden Landing E8A
95 Eden Landing E9
91 Eden Landing E9/E14/E6B
93 Eden Landing Turk (South)
98 Elkhorn Slough
29 Elmwood Correctional
30 Grant Lake
31 Greco Island NW Tower
99 Guadalupe Slough
33 Hayward Shoreline
161 Kings Academy
34 Lake Chabot
35 Lake Cunningham
36 Lake Del Valle Park
37 Lake Elizabeth
143 Lake Merced Boat Docks
100 Lake Merced Impound Lake
39 Lake Merced Mesa
38 Lake Merced North
40 Lake Merced South
41 Lake Merritt
153 Lakeshore Park, Newark (Channel Island)
42 Lakeshore Park, Newark (no subcolony identified)
107 Lakeshore Park, Newark (Ramsgate Island)
108 Lakeshore Park, Newark (Salisbury Island)
43 Lexington Reservoir, Black Rd
44 Livermore VA Park & Hospital
45 Llagas Creek, Morgan Hill
102 Los Gatos Creek Park
104 Moffett A2E
46 Moffett A2W
103 Moffett A3N
49 Moffett A3W
47 Moffett AB1
48 Moffett AB2
50 Mountain View A1
52 Mountain View Charleston Slough Island
51 Mountain View Palo Alto FCC
105 Mowry
54 Mowry M1/M2 levee
151 Mowry M3 Islands
53 Mowry M4/M5 levee
55 Mundy Marsh
106 Napa Marsh
56 New Chicago Marsh
109 Newark
57 Ovation Court
58 Oyster Cove Pier
111 Palace of Fine Arts
59 Palo Alto Baylands
112 Palo Alto Flood Control Channel
113 Pescadero Marsh
60 Portola Valley
141 Purissima Canyon
128 Quarry Lakes Park
114 Ravenswood
162 Ravenswood Pond RSF2
61 Ravenswood R1
115 Reach 1A Pond
117 Red Rock Island
116 Redwood City Cargill Plant
156 Redwood City Harbor (E subcolony)
140 Redwood City Harbor (no subcolony identified)
154 Redwood City Harbor (NW subcolony)
155 Redwood City Harbor (SW subcolony)
62 Redwood Shores
142 Redwood Shores Parkway, Nob Hill Market
152 Redwood Shores Water Treatment Plant
63 Ruus Park
118 Salinas National Wildlife Refuge
119 San Antonio Valley
120 San Felipe Lake
121 San Mateo Bridge
90 San Pablo Dam Rd
160 Sandy Wool Lake
64 Shadow Cliffs
65 Shorebird Way
159 St. Francis Yacht Club
66 Steinberger Slough
67 Stow Lake
138 Sunol
129 Sunol Water Temple
139 Vasona County Park North
68 Vasona County Park SW
69 Vasona Reservoir Island
\.
--
-- Name: colony_id_seq; Type: SEQUENCE SET; Schema: public; Owner: gyggowszznwhkm
--
SELECT pg_catalog.setval('public.colony_id_seq', 163, true);
--
-- Name: colony colony_pkey; Type: CONSTRAINT; Schema: public; Owner: gyggowszznwhkm
--
ALTER TABLE ONLY public.colony
ADD CONSTRAINT colony_pkey PRIMARY KEY (id);
--
-- Name: data_sheet data_sheet_pkey; Type: CONSTRAINT; Schema: public; Owner: gyggowszznwhkm
--
ALTER TABLE ONLY public.data_sheet
ADD CONSTRAINT data_sheet_pkey PRIMARY KEY (id);
--
-- Name: survey_summary survey_summary_pkey; Type: CONSTRAINT; Schema: public; Owner: gyggowszznwhkm
--
ALTER TABLE ONLY public.survey_summary
ADD CONSTRAINT survey_summary_pkey PRIMARY KEY (id);
--
-- Name: data_sheet data_sheet_colony_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: gyggowszznwhkm
--
ALTER TABLE ONLY public.data_sheet
ADD CONSTRAINT data_sheet_colony_id_fkey FOREIGN KEY (colony_id) REFERENCES public.colony(id);
--
-- Name: survey_summary survey_summary_data_sheet_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: gyggowszznwhkm
--
ALTER TABLE ONLY public.survey_summary
ADD CONSTRAINT survey_summary_data_sheet_id_fkey FOREIGN KEY (data_sheet_id) REFERENCES public.data_sheet(id);
create table create_keys (
id uuid NOT NULL DEFAULT gen_random_uuid() PRIMARY KEY,
creation_date timestamp without time zone NOT NULL DEFAULT now()
);
--
-- Name: SCHEMA public; Type: ACL; Schema: -; Owner: gyggowszznwhkm
--
REVOKE ALL ON SCHEMA public FROM postgres;
REVOKE ALL ON SCHEMA public FROM PUBLIC;
GRANT ALL ON SCHEMA public TO gyggowszznwhkm;
GRANT ALL ON SCHEMA public TO PUBLIC;
--
-- Name: LANGUAGE plpgsql; Type: ACL; Schema: -; Owner: postgres
--
GRANT ALL ON LANGUAGE plpgsql TO gyggowszznwhkm;
--
-- PostgreSQL database dump complete
--

6
package.json

@ -26,9 +26,9 @@ @@ -26,9 +26,9 @@
"parcel-bundler": "^1.12.3"
},
"scripts": {
"dev": "parcel index.html",
"build": "parcel build index.html",
"watch": "parcel watch index.html",
"dev": "parcel submit.html 404.html index.html",
"build": "parcel build submit.html 404.html index.html",
"watch": "parcel watch submit.html 404.html index.html",
"flask": "SECRET_KEY=$(uuidgen) FLASK_ENV=development DATABASE_URL=$(heroku config:get DATABASE_URL) flask run"
},
"dependencies": {

13
submit.html

@ -0,0 +1,13 @@ @@ -0,0 +1,13 @@
<!DOCTYPE html>
<html>
<script>
CSRF_TOKEN = "{{ csrf_token() }}";
</script>
<head>
<title>SFBBO Colonial Waterbird Datasheet | Submit Sheet</title>
</head>
<body>
<div id="app"></div>
<script src="app.jsx"></script>
</body>
</html>
Loading…
Cancel
Save