Public
Edited
Jan 6, 2023
1 fork
Importers
Insert cell
Insert cell
congress
select "World!" as Hello;
Insert cell
Insert cell
Insert cell
Insert cell
congress = new DatasetteClient(
"https://ds.agarcia.dev/congress/congress-members"
)
Insert cell
Insert cell
Insert cell
congress
SELECT bioguide,
first_name,
last_name,
CAST(strftime('%Y', DATE(birthday)) AS INTEGER) AS birth_year
FROM members
WHERE birth_year between 1981 AND 1996
ORDER BY birth_year DESC
Insert cell
Insert cell
Insert cell
viewof party = Inputs.select(["Democrat", "Republican", "Independent"], {
label: "Political party"
})
Insert cell
congress
SELECT
terms.type,
terms.state,
terms.party,
full_name,
terms.start as start,
terms.end
FROM members
LEFT JOIN terms ON terms.member = members.bioguide
GROUP BY bioguide
HAVING
date(max(terms.end)) > date("now")
and terms.type = "sen"
and terms.party = ${party}
ORDER BY state ASC
Insert cell
Insert cell
partyMembers
Insert cell
Insert cell
covid = new DatasetteClient("https://covid-19.datasettes.com/covid")
Insert cell
covid
select *
from latimes_county_totals
where date = "2021-09-01"
Insert cell
Insert cell
congress
WITH dates(date) AS (
SELECT DISTINCT start FROM terms WHERE start >= "1975-01-01"
)
, members_gen as (
SELECT
members.*,
members.birthday < '1928-01-01' as other,
members.birthday between '1928-01-01' and '1945-12-31' as silent,
members.birthday between '1946-01-01' and '1964-12-31' as boomer,
members.birthday between '1965-01-01' and '1980-12-31' as x,
members.birthday between '1981-01-01' and '1996-12-31' as millenial
FROM members
)

SELECT
dates.date,
sum(other) as num_other,
sum(silent) as num_silent,
sum(boomer) as num_boomer,
sum(x) as "num_Gen X",
sum(millenial) as num_millenial
from terms
inner join dates on terms.start <= dates.date and terms.end > dates.date
left join members_gen on members_gen.bioguide = terms.member
where terms.type = 'rep'
group by dates.date
Insert cell
repsGenerations
Insert cell
Insert cell
chartData = d3.merge(
repsGenerations.map((d) =>
["num_other", "num_silent", "num_boomer", "num_Gen X", "num_millenial"].map(
(k) => ({
date: new Date(d.date),
count: d[k],
generation: k
})
)
)
)
Insert cell
Insert cell
import { StackedAreaChart } from "@d3/stacked-area-chart"
Insert cell
import { Swatches } from "@d3/color-legend"
Insert cell
Insert cell
Insert cell
chart = StackedAreaChart(chartData, {
x: (d) => d.date,
y: (d) => d.count,
z: (d) => d.generation,
colors: ["#ccc", "#2b6455", "#53887b", "#ddc172", "#c79a27"],
yLabel: "↑ # House Representatives"
})
Insert cell
Insert cell
Insert cell
Insert cell
class DatasetteClient {
constructor(baseUrl) {
this.baseUrl = baseUrl;
}

_getFetchURL(sql, params, type = "json") {
const searchParams = new URLSearchParams();
searchParams.append("sql", sql);
if (params)
for (const key in params) {
searchParams.append(key, params[key]);
}
return `${this.baseUrl}.${type}?${searchParams.toString()}`;
}

_parseTemplate(strings, ...values) {
let sql = "";
const params = {};
let paramsI = 0;
for (const [i, s] of strings.entries()) {
if (i < values.length) {
// If an array is given inside ${}, then create a new parameter
// for each element and join the param names together in SQL
// ex "select 1 in ${[1,2,3]}" ->
// {sql: "select 1 in (:p0,p1,:p2)", params:{p0:1, p1:2, p2:3} }
if (Array.isArray(values[i])) {
const sqlArrayParts = [];
for (const v of values[i]) {
const param = `p${paramsI++}`;
sqlArrayParts.push(`:${param}`);
params[param] = v;
}
sql += `${s}(${sqlArrayParts.join(",")})`;
}
// Otherwise just replace element with string
else {
const param = `p${paramsI++}`;
sql += `${s}:${param}`;
params[param] = values[i];
}
} else {
sql += s;
}
}

return { sql, params };
}

_element(name, props, children) {
if (arguments.length === 2) (children = props), (props = undefined);
const element = document.createElement(name);
if (props !== undefined) for (const p in props) element[p] = props[p];
if (children !== undefined)
for (const c of children) element.appendChild(c);
return element;
}

_text(value) {
return document.createTextNode(value);
}

async query(query, params) {
const fetchUrl = this._getFetchURL(query, params, "csv");
return d3.csv(fetchUrl, d3.autoType);
}

async geoquery(query, params) {
const fetchUrl = this._getFetchURL(query, params, "geojson");
const result = await fetch(fetchUrl).then((r) => r.json());
if (typeof result.ok !== "undefined" && !result.ok)
throw Error(result.error);
return result;
}

async _row_count(query, params) {
const count_query = `select count(*) as num_rows from (${query.replace(
/(\s*;?\s*)*$/,
""
)})`;
const count_data = await this._page(count_query, params);
return count_data[0].num_rows;
}

async _page(query, params) {
const fetchUrl = this._getFetchURL(query, params);
const result = await fetch(fetchUrl).then((r) => r.json());
if (typeof result.ok !== "undefined" && !result.ok)
throw Error(result.error);

const data = result.rows.map((row) =>
row.reduce((res, field, index) => {
res[result.columns[index]] = field;
return res;
}, {})
);

data.truncated = result.truncated;

return data;
}

// have to redefine bc Datasette doesnt use non-named parameters ("?")
async describe(object) {
const rows = await (object === undefined
? this.query(`SELECT name FROM sqlite_master WHERE type = 'table'`)
: this.query(`SELECT * FROM pragma_table_info(:p0)`, { p0: object }));
if (!rows.length) throw new Error("Not found");
const { columns } = rows;
return this._element("table", { value: rows }, [
this._element("thead", [
this._element(
"tr",
columns.map((c) => this._element("th", [this._text(c)]))
)
]),
this._element(
"tbody",
rows.map((r) =>
this._element(
"tr",
columns.map((c) => this._element("td", [this._text(r[c])]))
)
)
)
]);
}

async sql(strings, ...values) {
const { sql, params } = this._parseTemplate(strings, ...values);
return this.query(sql, params);
}
}
Insert cell
congress
-- testing error handling
select * from membersx;
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more