22 | );
23 | }
24 |
--------------------------------------------------------------------------------
/client/censusGPT/src/components/dataPlot.js:
--------------------------------------------------------------------------------
1 | import Plot from 'react-plotly.js'
2 | import { getPlotConfig } from '../utils/plotly-ui-config'
3 |
4 |
5 | const DataPlot = (props) => {
6 | let config = getPlotConfig(props.rows, props.cols)
7 |
8 | return (
9 |
15 | );
16 | }
17 |
18 | export default DataPlot
--------------------------------------------------------------------------------
/client/censusGPT/src/components/disclaimer.js:
--------------------------------------------------------------------------------
1 | const Disclaimer = (props) => {
2 | const SF_disclaimer = (
3 | <>
4 | Note: SanFranciscoGPT currently only has data for crime, 311 cases, demographics, income, population, food, parks, and housing in SF. But we are working to add more data!
5 |
6 | 311 data and crime data are sourced from the city's website for public datasets and include data from 1/1/21 to 4/7/23.
7 |
8 | This app uses SF Analysis Neighborhoods which have boundaries formed specifically to fit census tracts.
9 | >
10 | );
11 |
12 | const Census_disclaimer = (
13 | <>
14 | Note: CensusGPT currently only has data for crime, demographics, income, education levels, and population in the USA. But we are working to add more data!
15 |
16 | Census data is sourced from the 2021 ACS (latest). Crime data is sourced from the FBI's 2019 UCR (latest).
17 | >
18 | );
19 |
20 | return (
21 |
42 | )
43 | }
44 |
--------------------------------------------------------------------------------
/client/censusGPT/src/components/examples.js:
--------------------------------------------------------------------------------
1 | // Examples
2 | import { capturePosthog } from "../utils/loggers/posthog"
3 | /**
4 | * Examples component
5 | * @param {*} props – The props for the example component used to pass in callback functions
6 | * @param {*} props.posthogInstance - The posthog instance
7 | * @param {*} props.setQuery - Sets the query in the search bar
8 | * @param {*} props.handleClick - Handles the search button click
9 | * @returns {JSX.Element} – The examples component
10 | */
11 | const Examples = (props) => {
12 | let basic_example_queries = [
13 | 'Five cities in Florida with the highest crime',
14 | 'Richest neighborhood in Houston, TX',
15 | ]
16 | let advanced_example_queries = [
17 | '3 neighborhoods in San Francisco that have the highest female to male ratio',
18 | 'Which area in San Francisco has the highest racial diversity and what is the percentage population of each race in that area?',
19 | // "Which 5 areas have the median income closest to the national median income?"
20 | ]
21 |
22 | if (props.version === 'San Francisco') {
23 | basic_example_queries = [
24 | 'Show me the locations of the 10 highest rated coffee shops with at least 100 ratings.',
25 | 'Which neighborhood has the most parks?',
26 | 'Show me all the needles in SF',
27 | 'Show me all the muggings',
28 | 'Which two neighborhoods have the most homeless activity?',
29 | 'Which five neighborhoods have the most poop on the street?',
30 | ]
31 | advanced_example_queries = [
32 | 'Which four neighborhoods had the most crime incidents involving guns or knives in 2021?',
33 | '3 neighborhoods with the highest female to male ratio',
34 | 'What are the top 5 neighborhoods with the most encampments per capita?',
35 | 'What hour of the day do most burglaries occur?',
36 | ]
37 | }
38 |
39 | return (
40 |
32 | )
33 |
34 | }
35 | const Disclaimer = (props) => {
36 | const SF_disclaimer = (
37 | <>
38 | Note: SanFranciscoGPT currently only has data for crime, 311 cases, demographics, income, population, food, parks, housing in SF. But we are working to add more data!
39 |
40 | 311 data and crime data are sourced from the city's website for public datasets and include data from 1/1/21 to 4/7/23.
41 |
42 | This app uses SF Analysis Neighborhoods which have boundaries formed specifically to fit census tracts.
43 | >
44 | );
45 |
46 | const Census_disclaimer = (
47 | <>
48 | Note: CensusGPT currently only has data for crime, demographics, income, education levels and population in the USA. But we are working to add more data!
49 |
50 | Census data is sourced from the 2021 ACS (latest). Crime data is sourced from the FBI's 2019 UCR (latest).
51 | >
52 | );
53 |
54 | return (
55 |
CaesarHQ operates the censusGPT.com website, which provides a service for generating SQL queries on census data.
6 |
Information Collection and Use
7 |
We collect information that you provide when using our website, including the SQL queries that you generate and any feedback that you provide. We use this information to improve our website and provide you with a better user experience.
8 |
Log Data
9 |
Like many website operators, we collect information that your browser sends whenever you visit our website. This may include information such as your computer's Internet Protocol (IP) address, browser type, browser version, the pages of our website that you visit, the time and date of your visit, the time spent on those pages and other statistics.
10 |
Cookies
11 |
We use cookies to collect information about your preferences and to personalize your experience on our website. You can instruct your browser to refuse all cookies or to indicate when a cookie is being sent. However, if you do not accept cookies, you may not be able to use some portions of our website.
12 |
Third Party Services
13 |
We may use third party services, such as Google Analytics, to collect, monitor and analyze usage of our website.
14 |
Security
15 |
The security of your personal information is important to us, but please remember that no method of transmission over the Internet, or method of electronic storage, is 100% secure. While we strive to use commercially acceptable means to protect your personal information, we cannot guarantee its absolute security.
16 |
Changes to Privacy Policy
17 |
CaesarHQ reserves the right to modify this privacy policy at any time without notice. Your continued use of our website following any such modification constitutes your agreement to be bound by the modified privacy policy.
18 |
Contact Us
19 |
If you have any questions about this privacy policy, please contact us at team@caesarhq.com.
20 |
This privacy policy is effective as of April 10, 2023.
Welcome to censusGPT.com, a website owned and operated by CaesarHQ. By accessing or using our website, you agree to be bound by the following terms and conditions:
7 |
Use of Service
8 |
Our website is designed to help you generate SQL queries on census data for informational and research purposes only. You agree to use our website in accordance with all applicable laws and regulations.
9 |
Intellectual Property
10 |
Our website and its entire contents, features, and functionality (including but not limited to all information, software, text, displays, images, video, and audio, and the design, selection, and arrangement thereof), are owned by CaesarHQ or its licensors and are protected by United States and international copyright, trademark, patent, trade secret, and other intellectual property or proprietary rights laws.
11 |
Disclaimer of Warranties
12 |
Our website and its contents are provided on an "as is" and "as available" basis without any warranties of any kind, either express or implied, including but not limited to warranties of merchantability, fitness for a particular purpose, or non-infringement. CaesarHQ does not warrant that our website will be uninterrupted or error-free, that defects will be corrected, or that our website or the server that makes it available are free of viruses or other harmful components.
13 |
Limitation of Liability
14 |
In no event shall CaesarHQ, its affiliates, licensors, or service providers be liable for any direct, indirect, incidental, special, consequential, or punitive damages arising from or related to your use of our website, including but not limited to any errors or omissions in any content, or any loss or damage of any kind incurred as a result of the use of any content (or product) posted, transmitted, or otherwise made available via the website.
15 |
Indemnification
16 |
You agree to indemnify, defend, and hold harmless CaesarHQ, its affiliates, licensors, and service providers, and its and their respective officers, directors, employees, contractors, agents, licensors, suppliers, successors, and assigns from and against any claims, liabilities, damages, judgments, awards, losses, costs, expenses, or fees (including reasonable attorneys' fees) arising out of or relating to your violation of these terms of service or your use of our website.
17 |
Changes to Terms of Service
18 |
CaesarHQ reserves the right to modify these terms of service at any time without notice. Your continued use of our website following any such modification constitutes your agreement to be bound by the modified terms of service.
19 |
Governing Law
20 |
These terms of service and any dispute or claim arising out of or related to your use of our website shall be governed by and construed in accordance with the laws of the State of California, without giving effect to any principles of conflicts of law.
21 |
Contact Us
22 |
If you have any questions about these terms of service, please contact us at team@caesarhq.com.
23 |
24 | );
25 | };
26 |
27 | export default TermsOfService;
28 |
29 |
30 |
--------------------------------------------------------------------------------
/client/censusGPT/src/reportWebVitals.js:
--------------------------------------------------------------------------------
1 | const reportWebVitals = (onPerfEntry) => {
2 | if (onPerfEntry && onPerfEntry instanceof Function) {
3 | import('web-vitals').then(
4 | ({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
5 | getCLS(onPerfEntry)
6 | getFID(onPerfEntry)
7 | getFCP(onPerfEntry)
8 | getLCP(onPerfEntry)
9 | getTTFB(onPerfEntry)
10 | }
11 | )
12 | }
13 | }
14 |
15 | export default reportWebVitals
16 |
--------------------------------------------------------------------------------
/client/censusGPT/src/setupTests.js:
--------------------------------------------------------------------------------
1 | // jest-dom adds custom jest matchers for asserting on DOM nodes.
2 | // allows you to do things like:
3 | // expect(element).toHaveTextContent(/react/i)
4 | // learn more: https://github.com/testing-library/jest-dom
5 | import '@testing-library/jest-dom'
6 |
--------------------------------------------------------------------------------
/client/censusGPT/src/utils/loggers/posthog.js:
--------------------------------------------------------------------------------
1 | import posthog from 'posthog-js'
2 |
3 | const POSTHOG_KEY = process.env.REACT_APP_POSTHOG_KEY
4 |
5 | if (POSTHOG_KEY) {
6 | posthog.init(POSTHOG_KEY, {
7 | api_host: 'https://app.posthog.com',
8 | })
9 | }
10 |
11 | export const capturePosthog = (eventName, properties) => {
12 | if (POSTHOG_KEY) {
13 | posthog.capture(eventName, properties)
14 | }
15 | }
--------------------------------------------------------------------------------
/client/censusGPT/src/utils/loggers/sentry.js:
--------------------------------------------------------------------------------
1 | import * as Sentry from '@sentry/react'
2 | import { BrowserTracing } from '@sentry/tracing'
3 |
4 | const SENTRY_ROUTE = process.env.REACT_APP_SENTRY_ROUTE
5 |
6 | if (SENTRY_ROUTE) {
7 | Sentry.init({
8 | dsn: SENTRY_ROUTE,
9 | integrations: [new BrowserTracing()],
10 | tracesSampleRate: 1.0,
11 | })
12 | }
13 |
14 | export const logSentryError = (queryContext, err) => {
15 | console.log('LOGGING TO SENTRY')
16 | if (SENTRY_ROUTE) {
17 | Sentry.setContext('queryContext', queryContext)
18 | Sentry.captureException(err)
19 | }
20 | }
--------------------------------------------------------------------------------
/client/censusGPT/src/utils/mapbox-ui-config.js:
--------------------------------------------------------------------------------
1 | /**
2 | * This file contains the UI configuration for the mapbox UI.
3 | */
4 |
5 | export const zipcodeFeatures = (zipcodes) => {
6 | return zipcodes.map((z) => {
7 | return {
8 | type: 'Feature',
9 | geometry: {
10 | type: 'Point',
11 | coordinates: [z.long, z.lat],
12 | },
13 | }
14 | })
15 | }
16 |
17 | export const citiesFeatures = (cities) => {
18 | return cities.map((c) => {
19 | return {
20 | type: 'Feature',
21 | geometry: {
22 | type: 'Point',
23 | coordinates: [c.long, c.lat],
24 | },
25 | }
26 | })
27 | }
28 |
29 | export const zipcodeLayerLow = (zipcodesFormatted) => {
30 | return {
31 | id: 'zips-kml',
32 | type: 'fill',
33 | source: 'zips-kml',
34 | minzoom: 5,
35 | layout: {
36 | visibility: 'visible',
37 | },
38 | paint: {
39 | 'fill-outline-color': 'black',
40 | 'fill-opacity': 0.9,
41 | 'fill-color': '#006AF9',
42 | },
43 | 'source-layer': 'Layer_0',
44 | filter: [
45 | 'in',
46 | ['get', 'Name'],
47 | ['literal', zipcodesFormatted], // Zip code in the feature is formatted like this: 94105
48 | ],
49 | }
50 | }
51 |
52 | export const zipcodeLayerHigh = {
53 | id: 'Zip',
54 | type: 'circle',
55 | layout: {
56 | visibility: 'visible',
57 | },
58 | maxzoom: 8,
59 | paint: {
60 | 'circle-radius': 10,
61 | 'circle-color': '#006AF9',
62 | 'circle-opacity': 1,
63 | },
64 | }
65 |
66 | export const citiesLayer = {
67 | id: 'cities',
68 | type: 'circle',
69 | layout: {
70 | visibility: 'visible',
71 | },
72 | paint: {
73 | 'circle-radius': 18,
74 | 'circle-color': '#006AF9',
75 | 'circle-opacity': 0.8,
76 | },
77 | }
78 |
79 | export const polygonsLayer = {
80 | id: 'polygons',
81 | type: 'fill',
82 | source: "polygons",
83 | layout: {
84 | visibility: 'visible',
85 | },
86 | paint: {
87 | 'fill-outline-color': 'black',
88 | 'fill-color': '#006AF9',
89 | 'fill-opacity': 0.8,
90 | },
91 | }
92 |
93 | export const pointsFeatures = (points) => {
94 | return points.map((p) => {
95 | return {
96 | type: 'Feature',
97 | geometry: {
98 | type: 'Point',
99 | coordinates: [p.long, p.lat],
100 | },
101 | }
102 | })
103 | }
104 |
105 | export const pointsLayer = {
106 | id: 'points',
107 | type: 'circle',
108 | layout: {
109 | visibility: 'visible',
110 | },
111 | paint: {
112 | 'circle-radius': 5,
113 | 'circle-color': '#006AF9',
114 | 'circle-opacity': 0.8,
115 | },
116 | }
--------------------------------------------------------------------------------
/client/censusGPT/src/utils/plotly-ui-config.js:
--------------------------------------------------------------------------------
1 | /**
2 | * This file contains the UI configuration for the Plotly UI.
3 | */
4 |
5 | const isGeoColumn = (columnName) => {
6 | if (columnName == 'zip_code' || columnName == 'city' || columnName == 'state') {
7 | return true
8 | }
9 | return false
10 | }
11 |
12 | export const getPlotConfig = (rows, cols) => {
13 | let data = []
14 | let layout = {}
15 |
16 | if (rows.length == 0 || cols.length == 0) {
17 | return {}
18 | } else if (rows.length >= 0 && cols.length == 2) {
19 | // 2 cols, N rows ==> Bar chart
20 | // Col 0 is X axis, Col 1 is Y axis
21 | // Example query: "Top 5 cities in CA with the highest crime and what is the total crime in each of those cities"
22 |
23 | data = [
24 | {
25 | x: rows.map(x => '\b' + x[0]), // convert to string. otherwise plotly treats 941002 as 94.1k
26 | y: rows.map(x => x[1]),
27 | type: 'bar',
28 | marker: { color: '#006AF9' }
29 | }
30 | ];
31 |
32 | layout = {
33 | xaxis: {title: cols[0]},
34 | yaxis: {title: cols[1]},
35 | }
36 |
37 | } else if (rows.length == 1 && cols.length >= 1) {
38 | // N cols, 1 row ==> Bar chart
39 | // columns is X axis, row 1 is Y axis
40 | // Example query: "What is the distribution of different categories of crimes in Dallas, TX"
41 |
42 | data = [
43 | {
44 | x: isGeoColumn(cols[0]) ? cols.slice(1) : cols,
45 | y: isGeoColumn(cols[0]) ? rows[0].slice(1) : rows[0],
46 | type: 'bar',
47 | marker: { color: '#006AF9' }
48 | }
49 | ];
50 |
51 | } else {
52 | // N cols, N rows ==> Stacked chart.
53 | // column 0 is X axis, column 1 to N is Y axis
54 | // Example query: "What is the percentage population of asian, black and hispanic people in all zipcodes in san francisco"
55 |
56 | for (let i = 1; i < cols.length; i++) {
57 |
58 | // if the column is not a number, don't plot it
59 | if (typeof rows[0][i] !== 'number') {
60 | continue
61 | }
62 |
63 | data.push({
64 | x: rows.map(x => '\b' + x[0]), // convert to string. otherwise plotly treats 941002 as 94.1k
65 | y: rows.map(x => x[i]),
66 | name: cols[i],
67 | type: 'bar'
68 | })
69 | }
70 |
71 | layout = {
72 | barmode: 'stack',
73 | xaxis: {title: cols[0]},
74 | }
75 | }
76 |
77 | layout = document.documentElement.classList.contains('dark') ? {
78 | ...layout,
79 | font: { color: '#fff' },
80 | yaxis: { gridcolor: '#444' }
81 | } : {
82 | ...layout,
83 | font: { color: '#000' }
84 | }
85 |
86 | return {data, layout}
87 | }
--------------------------------------------------------------------------------
/client/censusGPT/src/utils/user.js:
--------------------------------------------------------------------------------
1 | import { v4 as uuidv4 } from 'uuid';
2 |
3 | export const getUserId = () => {
4 | const localStorageKey = 'census_user_id';
5 | let userId = localStorage.getItem(localStorageKey);
6 |
7 | if (!userId) {
8 | // Generate a unique ID for the user
9 | userId = `${uuidv4()}_${new Date().getTime()}`;
10 |
11 | // Save the user ID in local storage
12 | localStorage.setItem(localStorageKey, userId);
13 | }
14 |
15 | return userId;
16 | }
17 |
--------------------------------------------------------------------------------
/client/censusGPT/src/utils/utils.js:
--------------------------------------------------------------------------------
1 | // Utils for the client side
2 |
3 | /**
4 | * Formatted Zipcodes for Mapbox
5 | * @param {*} zips
6 | * @returns {string[]} – The formatted zipcodes
7 | */
8 | export const getZipcodesMapboxFormatted = (zips) => {
9 | return zips.map((x) => '' + x['zipcode'] + '')
10 | }
11 |
12 | /**
13 | * Gets the zipcode's latitude longitude from the query search results
14 | * @param {*} result - The search results
15 | * @returns {object[]} – The formatted zipcodes
16 | */
17 | export const getZipcodes = (result) => {
18 | let zipcode_index = result.column_names.indexOf('zip_code')
19 | if (zipcode_index == -1 || !result.results) return []
20 |
21 | return result.results.map((x) => {
22 | return { zipcode: x['zip_code'], lat: x['lat'], long: x['long'] }
23 | })
24 | }
25 |
26 | /**
27 | * Gets the cities latitude longitude from the query search results
28 | * @param {*} result – The search results
29 | * @returns {object[]} – The zipcodes
30 | */
31 | export const getCities = (result) => {
32 | let city_index = result.column_names.indexOf('city')
33 | if (city_index == -1 || !result.results) return []
34 |
35 | return result.results.map((x) => {
36 | return { city: x['city'], lat: x['lat'], long: x['long'] }
37 | })
38 | }
39 |
--------------------------------------------------------------------------------
/client/censusGPT/src/vitals.js:
--------------------------------------------------------------------------------
1 | const vitalsUrl = 'https://vitals.vercel-analytics.com/v1/vitals'
2 |
3 | function getConnectionSpeed() {
4 | return 'connection' in navigator &&
5 | navigator['connection'] &&
6 | 'effectiveType' in navigator['connection']
7 | ? navigator['connection']['effectiveType']
8 | : ''
9 | }
10 |
11 | export function sendToVercelAnalytics(metric) {
12 | const analyticsId = process.env.REACT_APP_VERCEL_ANALYTICS_ID
13 | if (!analyticsId) {
14 | return
15 | }
16 |
17 | const body = {
18 | dsn: analyticsId,
19 | id: metric.id,
20 | page: window.location.pathname,
21 | href: window.location.href,
22 | event_name: metric.name,
23 | value: metric.value.toString(),
24 | speed: getConnectionSpeed(),
25 | }
26 |
27 | const blob = new Blob([new URLSearchParams(body).toString()], {
28 | // This content type is necessary for `sendBeacon`
29 | type: 'application/x-www-form-urlencoded',
30 | })
31 | if (navigator.sendBeacon) {
32 | navigator.sendBeacon(vitalsUrl, blob)
33 | } else
34 | fetch(vitalsUrl, {
35 | body: blob,
36 | method: 'POST',
37 | credentials: 'omit',
38 | keepalive: true,
39 | })
40 | }
41 |
--------------------------------------------------------------------------------
/client/censusGPT/tailwind.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | module.exports = {
3 | content: ["./src/**/*.{html,js}", "./src/*.{html,js}"],
4 | darkMode: 'class',
5 | theme: {
6 | extend: {
7 | colors: {
8 | 'dark': {
9 | 900: '#1F1F1F',
10 | 800: '#292929',
11 | 300: '#777777'
12 | }
13 | },
14 | },
15 | },
16 | plugins: [require('@tailwindcss/forms')],
17 | }
18 |
19 |
20 |
--------------------------------------------------------------------------------
/data/README.md:
--------------------------------------------------------------------------------
1 | ## Data
2 |
3 |
4 | The directory holds all the .CSV, .XLSX and other data files that are (or will be) included in [CensusGPT](https://censusgpt.com)'s dataset.
5 |
6 | If you would like to contribute data, please create a PR with your file and include a short description of the data you're contributing.
7 |
--------------------------------------------------------------------------------
/license.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------