-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathscript.py
229 lines (203 loc) · 6.16 KB
/
script.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
import sqlite3
import requests
import subprocess
import json
from datetime import datetime
from pathlib import Path
import time
URL = "https://arcgis.tampagov.net/arcgis/rest/services/OpenData/Planning/MapServer/31/query?outFields=*&where=1%3D1&f=geojson"
DB_PATH = "dev-locations/locations.db"
TIMEOUT = 20
def convert_timestamp(ts):
"""Convert Unix timestamp in milliseconds to ISO format"""
if ts:
return datetime.fromtimestamp(ts/1000.0).isoformat()
return None
def init_db():
conn = sqlite3.connect(DB_PATH, timeout=TIMEOUT)
cursor = conn.cursor()
cursor.execute("PRAGMA journal_mode=DELETE")
cursor.execute("PRAGMA busy_timeout=5000")
cursor.execute("PRAGMA synchronous=NORMAL")
cursor.execute("""CREATE TABLE IF NOT EXISTS current_full (
id INTEGER,
OBJECTID INTEGER,
RECORDID TEXT PRIMARY KEY,
ADDRESS TEXT,
UNIT TEXT,
APPSTATUS TEXT,
TENTATIVEHEARING TEXT,
TENTATIVETIME TEXT,
RECORDALIAS TEXT,
MAPDOT TEXT,
CRA TEXT,
NEIGHBORHOOD TEXT,
COUNCILDISTRICT TEXT,
CREATED TEXT,
CREATEDDATE INTEGER,
LASTUPDATE INTEGER,
LASTEDITOR TEXT,
GlobalID TEXT,
URL TEXT,
geometry TEXT
)""")
cursor.execute("""CREATE VIEW IF NOT EXISTS current AS
SELECT
RECORDID,
ADDRESS,
UNIT,
RECORDALIAS as Type,
CRA,
NEIGHBORHOOD,
COUNCILDISTRICT,
CREATEDDATE,
LASTUPDATE,
URL,
geometry
FROM current_full
""")
cursor.execute("""CREATE TABLE IF NOT EXISTS archived (
id INTEGER,
OBJECTID INTEGER,
RECORDID TEXT PRIMARY KEY,
ADDRESS TEXT,
UNIT TEXT,
APPSTATUS TEXT,
TENTATIVEHEARING TEXT,
TENTATIVETIME TEXT,
RECORDALIAS TEXT,
MAPDOT TEXT,
CRA TEXT,
NEIGHBORHOOD TEXT,
COUNCILDISTRICT TEXT,
CREATED TEXT,
CREATEDDATE INTEGER,
LASTUPDATE INTEGER,
LASTEDITOR TEXT,
GlobalID TEXT,
URL TEXT,
geometry TEXT,
archived_date TEXT
)""")
cursor.execute("""CREATE TABLE IF NOT EXISTS archived_full (
id INTEGER,
OBJECTID INTEGER,
RECORDID TEXT PRIMARY KEY,
ADDRESS TEXT,
UNIT TEXT,
APPSTATUS TEXT,
TENTATIVEHEARING TEXT,
TENTATIVETIME TEXT,
RECORDALIAS TEXT,
MAPDOT TEXT,
CRA TEXT,
NEIGHBORHOOD TEXT,
COUNCILDISTRICT TEXT,
CREATED TEXT,
CREATEDDATE INTEGER,
LASTUPDATE INTEGER,
LASTEDITOR TEXT,
GlobalID TEXT,
URL TEXT,
geometry TEXT,
archived_date TEXT
)""")
cursor.execute("""CREATE VIEW IF NOT EXISTS archived AS
SELECT
RECORDID,
ADDRESS,
UNIT,
RECORDALIAS as Type,
MAPDOT,
CRA,
NEIGHBORHOOD,
COUNCILDISTRICT,
CREATEDDATE,
LASTUPDATE,
URL,
geometry,
archived_date
FROM archived_full
""")
return conn, cursor
def fetch_geojson():
try:
response = requests.get(URL, timeout=10)
response.raise_for_status()
with open('temp.geojson', 'w') as f:
f.write(response.text)
return True
except (requests.RequestException, ValueError) as e:
print(f"Error fetching data: {e}")
return False
def archive_missing_records(cursor, conn, current_ids):
# Insert only records that don't already exist in archived_full
cursor.execute("""
INSERT INTO archived_full
SELECT current_full.*, datetime('now')
FROM current_full
WHERE current_full.RECORDID NOT IN ({})
AND NOT EXISTS (
SELECT 1 FROM archived_full
WHERE archived_full.RECORDID = current_full.RECORDID
)
""".format(','.join('?' * len(current_ids))), current_ids)
cursor.execute("""
DELETE FROM current_full
WHERE RECORDID NOT IN ({})
""".format(','.join('?' * len(current_ids))), current_ids)
conn.commit()
def import_geojson():
subprocess.run([
'geojson-to-sqlite',
DB_PATH,
'current_full', # Change target to table instead of view
'temp.geojson',
'--pk=RECORDID'
], check=True)
def convert_dates(cursor, conn):
cursor.execute("""
UPDATE current_full SET
CREATEDDATE = strftime('%Y-%m-%dT%H:%M:%SZ', datetime(CREATEDDATE/1000, 'unixepoch')),
LASTUPDATE = strftime('%Y-%m-%dT%H:%M:%SZ', datetime(LASTUPDATE/1000, 'unixepoch'))
WHERE CREATEDDATE IS NOT NULL
AND LASTUPDATE IS NOT NULL
AND CREATEDDATE > 0
AND LASTUPDATE > 0
""")
conn.commit()
def cleanup_db_files():
"""Remove SQLite journal files before serving with datasette"""
for ext in ['-wal', '-shm', '-journal']:
Path(f"{DB_PATH}{ext}").unlink(missing_ok=True)
def main():
try:
# Clean up any stale WAL files
cleanup_db_files()
conn, cursor = init_db()
if fetch_geojson():
response = requests.get(URL)
data = response.json()
if 'features' not in data:
print("Error: Missing 'features' in response")
return
current_records = data['features']
current_ids = [r['properties']['RECORDID'] for r in current_records]
archive_missing_records(cursor, conn, current_ids)
conn.close()
import_geojson()
# Reopen connection and convert dates
conn = sqlite3.connect(DB_PATH, timeout=TIMEOUT)
cursor = conn.cursor()
convert_dates(cursor, conn)
conn.close()
Path('temp.geojson').unlink(missing_ok=True)
except Exception as e:
print(f"Error: {e}")
raise
finally:
if 'conn' in locals():
conn.close()
cleanup_db_files()
if __name__ == "__main__":
main()