Skip to content

Commit

Permalink
Duplicate these columns to a new table - closes #29
Browse files Browse the repository at this point in the history
  • Loading branch information
simonw committed Jul 26, 2024
1 parent 81bc1b4 commit 4e7766e
Show file tree
Hide file tree
Showing 4 changed files with 129 additions and 7 deletions.
37 changes: 36 additions & 1 deletion datasette_extract/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import ijson
import json
import ulid
import urllib


@hookimpl
Expand Down Expand Up @@ -114,12 +115,25 @@ async def extract_create_table(datasette, request, scope, receive):
properties,
)

fields = []
if "_fields" in request.args:
try:
fields = [
field
for field in json.loads(request.args["_fields"])
if isinstance(field, dict) and isinstance(field.get("index"), int)
]
except (json.JSONDecodeError, TypeError):
fields = []
if not fields:
fields = [{"index": i} for i in range(10)]

return Response.html(
await datasette.render_template(
"extract_create_table.html",
{
"database": database,
"fields": range(10),
"fields": fields,
},
request=request,
)
Expand Down Expand Up @@ -220,6 +234,26 @@ async def extract_to_table(datasette, request, scope, receive):
) or ""
instructions = previous_runs[0]["instructions"] or ""

duplicate_url = (
datasette.urls.database(database)
+ "/-/extract?"
+ urllib.parse.urlencode(
{
"_fields": json.dumps(
[
{
"index": i,
"name": col["name"],
"type": col["type"].__name__,
"hint": col["hint"],
}
for i, col in enumerate(columns)
]
)
}
)
)

return Response.html(
await datasette.render_template(
"extract_to_table.html",
Expand All @@ -229,6 +263,7 @@ async def extract_to_table(datasette, request, scope, receive):
"schema": schema,
"columns": columns,
"instructions": instructions,
"duplicate_url": duplicate_url,
"previous_runs": previous_runs,
},
request=request,
Expand Down
12 changes: 6 additions & 6 deletions datasette_extract/templates/extract_create_table.html
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@ <h1>Extract data and create a new table in {{ database }}</h1>
<div id="fields-container">
{% for field in fields %}
<p>
<label>Name <input type="text" name="name_{{ field }}"></label>
<label>Type <select name="type_{{ field }}">
<option value="string">Text</option>
<option value="integer">Integer</option>
<option value="float">Float</option>
<label>Name <input type="text" name="name_{{ field.index }}" value="{{ field.name or '' }}"></label>
<label>Type <select name="type_{{ field.index }}">
<option value="string"{% if field.type == "str" %} selected{% endif %}>Text</option>
<option value="integer"{% if field.type == "int" %} selected{% endif %}>Integer</option>
<option value="float"{% if field.type == "float" %} selected{% endif %}>Float</option>
</select>
</label>
<label>Hint
<input size="40" type="text" name="hint_{{ field }}" value="" placeholder="Optional hint">
<input size="40" type="text" name="hint_{{ field.index }}" value="{{ field.hint or '' }}" placeholder="Optional hint">
</label>
</p>
{% endfor %}
Expand Down
2 changes: 2 additions & 0 deletions datasette_extract/templates/extract_to_table.html
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ <h1>Extract data into {{ database }} / {{ table }}</h1>
<p><input type="submit" value="Extract"></p>
</form>

<p><a href="{{ duplicate_url }}">Duplicate these columns to a new table</a></p>

{% include "_extract_drop_handler.html" %}

{% if previous_runs %}
Expand Down
85 changes: 85 additions & 0 deletions tests/test_web.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import asyncio
from datasette.app import Datasette
import json
import pytest
import urllib


@pytest.mark.vcr(ignore_localhost=True)
Expand Down Expand Up @@ -134,3 +136,86 @@ async def test_action_menus_require_api_key(monkeypatch, path, has_env_variable)
assert fragment in response.text
else:
assert fragment not in response.text


@pytest.mark.asyncio
async def test_create_table_copying_columns():
ds = Datasette()
data = ds.add_memory_database("data")
await data.execute_write(
"create table foo (name text, age integer, weight float, bio text)"
)
cookies = {"ds_actor": ds.client.actor_cookie({"id": "root"})}
response = await ds.client.get("/data/foo/-/extract", cookies=cookies)
assert response.status_code == 200
fields_raw = response.text.split('<p><a href="/data/-/extract?_fields=')[1].split(
'"'
)[0]
fields = json.loads(urllib.parse.unquote_plus(fields_raw))
assert fields == [
{"index": 0, "name": "name", "type": "str", "hint": ""},
{"index": 1, "name": "age", "type": "int", "hint": ""},
{"index": 2, "name": "weight", "type": "float", "hint": ""},
{"index": 3, "name": "bio", "type": "str", "hint": ""},
]
# Navigating to the /data/-/extract page with that link should prefill the form
response2 = await ds.client.get(
f"/data/-/extract?_fields={fields_raw}", cookies=cookies
)
expected = """
<p>
<label>Name <input type="text" name="name_0" value="name"></label>
<label>Type <select name="type_0">
<option value="string" selected>Text</option>
<option value="integer">Integer</option>
<option value="float">Float</option>
</select>
</label>
<label>Hint
<input size="40" type="text" name="hint_0" value="" placeholder="Optional hint">
</label>
</p>
<p>
<label>Name <input type="text" name="name_1" value="age"></label>
<label>Type <select name="type_1">
<option value="string">Text</option>
<option value="integer" selected>Integer</option>
<option value="float">Float</option>
</select>
</label>
<label>Hint
<input size="40" type="text" name="hint_1" value="" placeholder="Optional hint">
</label>
</p>
<p>
<label>Name <input type="text" name="name_2" value="weight"></label>
<label>Type <select name="type_2">
<option value="string">Text</option>
<option value="integer">Integer</option>
<option value="float" selected>Float</option>
</select>
</label>
<label>Hint
<input size="40" type="text" name="hint_2" value="" placeholder="Optional hint">
</label>
</p>
<p>
<label>Name <input type="text" name="name_3" value="bio"></label>
<label>Type <select name="type_3">
<option value="string" selected>Text</option>
<option value="integer">Integer</option>
<option value="float">Float</option>
</select>
</label>
<label>Hint
<input size="40" type="text" name="hint_3" value="" placeholder="Optional hint">
</label>
</p>
"""
expected_stripped = strip_whitespace(expected)
actual_stripped = strip_whitespace(response2.text)
assert expected_stripped in actual_stripped


def strip_whitespace(text):
return "".join(text.split())

0 comments on commit 4e7766e

Please sign in to comment.