Skip to content

Commit

Permalink
Merge pull request #45 from DSSD-Madison/refactor
Browse files Browse the repository at this point in the history
Refactor
  • Loading branch information
JackBlake-zkq authored May 9, 2024
2 parents a134dbb + 074a26d commit fa47435
Show file tree
Hide file tree
Showing 11 changed files with 308 additions and 306 deletions.
1 change: 1 addition & 0 deletions backend/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/venv
51 changes: 21 additions & 30 deletions backend/cache_script.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
import json
import threading
from firebase_admin import credentials, firestore, storage, initialize_app
from google.cloud import storage as gcs
from datetime import datetime
from google.api_core.datetime_helpers import DatetimeWithNanoseconds

cred = credentials.Certificate('creds.json')
bucket = os.environ['STORAGE_BUCKET'] if 'STORAGE_BUCKET' in os.environ else 'red-coral-map.appspot.com'
Expand All @@ -13,8 +13,9 @@
db = firestore.client()
bucket = storage.bucket()

utc_time = datetime.utcnow()
timestamp = utc_time.timestamp()
timestamp = DatetimeWithNanoseconds.fromisoformat(datetime.now().isoformat())

to_remove = []

def minify_json(data):
return json.dumps(data, separators=(',', ':'))
Expand All @@ -24,20 +25,22 @@ def read_firestore():
types = {}
incidents = {}

for doc in db.collection('Categories').stream():
categories[doc.id] = doc.to_dict()

for doc in db.collection('Types').stream():
types[doc.id] = doc.to_dict()
out = {
"readAt": timestamp.isoformat()
}

for doc in db.collection('Incidents').stream():
incidents[doc.id] = doc.to_dict()
for d, collection_name in [(categories, 'Categories'), (types, 'Types'), (incidents, 'Incidents')]:
for doc in db.collection(collection_name).stream():
d[doc.id] = doc.to_dict()
if d[doc.id].get('deleted', False):
to_remove.append((collection_name, doc.id))
del d[doc.id]
continue
if 'updatedAt' in d[doc.id]:
del d[doc.id]['updatedAt'] # not serializable and it's not needed
out[collection_name] = d

return {
'Categories': categories,
'Types': types,
'Incidents': incidents
}
return out

def save_to_cloud_storage(data):
minified_data = minify_json(data)
Expand All @@ -48,24 +51,12 @@ def save_to_cloud_storage(data):
blob = bucket.blob('state.json')
blob.upload_from_filename('state.json')

def update_firestore_with_timestamp(collection_name, doc_id):
db.collection(collection_name).document(doc_id).update({'readAt': timestamp})

def update_documents_concurrently(collection_name, doc_ids):
threads = []
for doc_id in doc_ids:
thread = threading.Thread(target=update_firestore_with_timestamp, args=(collection_name, doc_id))
threads.append(thread)
thread.start()
for thread in threads:
thread.join()

def main():
data = read_firestore()
save_to_cloud_storage(data)

# Update documents in Firestore with readAt timestamp concurrently
for collection_name, doc_ids in data.items():
update_documents_concurrently(collection_name, doc_ids.keys())
# Remove deleted documents
for collection_name, doc_id in to_remove:
db.collection(collection_name).document(doc_id).delete()

main()
142 changes: 114 additions & 28 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import React, { useEffect, useState } from 'react'
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom'
import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom'
import { initializeApp } from 'firebase/app'
import { getAuth } from 'firebase/auth'
import { getAuth, signOut } from 'firebase/auth'
import 'leaflet/dist/leaflet.css'
import Home from 'components/Home'
import Map from 'components/Map'
import Login from 'components/Login'
import CRUDDash from 'components/CRUDDash'
import { getFirestore, collection, doc } from 'firebase/firestore'
import { getStorage } from 'firebase/storage'
import { addDocWithTimestamp, setDocWithTimestamp, deleteDocWithTimestamp, getData } from 'utils'
import { Incident, DB } from 'types'
import LoadingOverlay from './components/LoadingOverlay'

const App: React.FC = () => {
const app = initializeApp({
Expand All @@ -18,52 +23,133 @@ const App: React.FC = () => {
})

const auth = getAuth(app)
const firestore = getFirestore(app)
const storage = getStorage(app, import.meta.env.VITE_FIREBASE_STORAGE_BUCKET)

useEffect(() => {
auth.authStateReady().then(() => {
setIsLoggedIn(auth.currentUser != null)
})
}, [])
async function addIncident(incident: Incident): Promise<boolean> {
// setIsLoading(true)
try {
const ref = await addDocWithTimestamp(collection(firestore, 'Incidents'), JSON.parse(JSON.stringify(incident)))
data.Incidents[ref.id] = incident
// setIsLoading(false)
return true
} catch (e) {
console.error(e)
// setIsLoading(false)
return false
}
}

async function editIncident(incidentID: keyof DB['Incidents'], incident: Incident): Promise<boolean> {
// setIsLoading(true)
try {
await setDocWithTimestamp(doc(firestore, `Incidents/${incidentID}`), JSON.parse(JSON.stringify(incident)))
data.Incidents[incidentID] = incident
// setIsLoading(false)
return true
} catch (e) {
console.error(e)
// setIsLoading(false)
return false
}
}

async function deleteIncident(incidentID: keyof DB['Incidents']): Promise<boolean> {
try {
// setIsLoading(true)
await deleteDocWithTimestamp(doc(firestore, `Incidents/${incidentID}`))
delete data.Incidents[incidentID]
// setIsLoading(false)
return true
} catch (e) {
console.error(e)
// setIsLoading(false)
return false
}
}

// init firestore, storage
const [data, setData] = useState<DB>({
Categories: {},
Types: {},
Incidents: {},
filterBounds: {
maxYear: 0,
minYear: 0,
locations: {},
},
})

const [loadCount, setLoadCount] = useState(0)
const [isLoggedIn, setIsLoggedIn] = useState<boolean>(false) // State variable for sign-in status

const handleSignInSuccess = () => {
setIsLoggedIn(true) // Update the sign-in status to true
useEffect(() => {
return auth.onAuthStateChanged((user) => setIsLoggedIn(!!user))
}, [])

async function fetchData(isAdmin: boolean) {
setLoadCount((prev) => prev + 1)
getData(isAdmin, storage, firestore)
.then((db) => {
setData(db)
})
.catch((e) => {
console.error(e)
alert('No se pudo cargar la información')
})
.finally(() => {
setLoadCount((prev) => prev - 1)
})
}

// const Home = lazy(() => import('components/Home'))
// const Login = lazy(() => import('components/Login'))
// const CRUDDash = lazy(() => import('components/CRUDDash'))
useEffect(() => {
fetchData(isLoggedIn)
}, [isLoggedIn])

function Admin() {
function LoginPage() {
return (
<>
{!isLoggedIn && <Login auth={auth} onSignInSuccess={handleSignInSuccess} />}
{isLoggedIn && <Home app={app} isAdmin={true} />}
{!isLoggedIn && <Login auth={auth} />}
{isLoggedIn && <Navigate to="/" />}
</>
)
}

function AdminDash() {
return (
<>
{!isLoggedIn && <Login auth={auth} onSignInSuccess={handleSignInSuccess} />}
{isLoggedIn && <CRUDDash app={app} />}
{!isLoggedIn && <Login auth={auth} />}
{isLoggedIn && <CRUDDash firestore={firestore} data={data} />}
</>
)
}

return (
<Router>
{/* <Suspense fallback={<LoadingOverlay isVisible={true} color="#ab0000" />}> */}
<Routes>
<Route path="/" element={<Home app={app} isAdmin={false} />} />
<Route path="/admin" element={<Admin />} />
<Route path="/admin/dash" element={<AdminDash />} />
</Routes>
{/* </Suspense> */}
</Router>
<>
<Router>
<Routes>
<Route
path="/"
element={<Map data={data} isAdmin={isLoggedIn} addIncident={addIncident} editIncident={editIncident} deleteIncident={deleteIncident} />}
/>
<Route path="/login" element={<LoginPage />} />
<Route path="/admin" element={<Navigate to="/login" />} />
<Route path="/admin/dash" element={<AdminDash />} />
</Routes>
</Router>
<LoadingOverlay isVisible={loadCount > 0} color={'#888888'} />
<div className="letf-0 absolute bottom-0 z-[500] pb-5 pl-2">
{isLoggedIn && (
<button onClick={() => signOut(auth)} className=" cursor-pointer rounded-md bg-gray-200 p-1 hover:bg-gray-300">
Salir del Sistema
</button>
)}
{!isLoggedIn && (
<button onClick={() => (window.location.href = '/login')} className="cursor-pointer rounded-md bg-gray-300 p-1 hover:bg-gray-400">
Registrarse como Admin
</button>
)}
</div>
</>
)
}

Expand Down
Loading

0 comments on commit fa47435

Please sign in to comment.