Skip to content

Commit

Permalink
interactive demo via @wq/analyst
Browse files Browse the repository at this point in the history
  • Loading branch information
sheppard committed Apr 2, 2024
1 parent d00992c commit a4cbcdc
Show file tree
Hide file tree
Showing 18 changed files with 279 additions and 14 deletions.
8 changes: 8 additions & 0 deletions .github/workflows/pages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,16 @@ jobs:
run: |
curl -L -s https://unpkg.com/wq > docs/js/wq.js
curl -L -s https://unpkg.com/@wq/markdown@latest > docs/js/markdown.js
curl -L -s https://unpkg.com/@wq/analyst@next > docs/js/analyst.js
curl -L -s https://unpkg.com/@wq/chart@next > docs/js/chart.js
sed -i "s/^import\(.*\)https:\/\/unpkg.com\/wq/import\1.\/wq.js/" docs/js/*.js
sed -i "s/^import\(.*\)https:\/\/unpkg.com\/@wq\/markdown@next/import\1.\/markdown.js/" docs/js/*.js
sed -i "s/^import\(.*\)https:\/\/unpkg.com\/@wq\/analyst/import\1.\/analyst.js/" docs/js/*.js
sed -i "s/^import\(.*\)https:\/\/unpkg.com\/@wq\/chart/import\1.\/chart.js/" docs/js/*.js
- name: Export Django site
run: |
python -m pip install django djangorestframework pandas openpyxl matplotlib
python -m unittest tests.generate_docs
- name: Build with Jekyll
uses: actions/jekyll-build-pages@v1
with:
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,6 @@
build
dist
node_modules
docs/static
docs/timeseries.*
docs/weather.*
9 changes: 9 additions & 0 deletions docs/_layouts/default.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,15 @@
margin-right: auto;
max-width: 100%;
}
.MuiAppBar-colorPrimary img {
border-radius: 4px;
padding-left: 4px;
padding-right: 4px;
margin-left: -18px !important;
margin-top: 4px;
margin-bottom: 4px;
background-color: rgba(0, 0, 0, 0.6);
}
</style>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-LTN8HFGJT2"></script>
<script>
Expand Down
22 changes: 20 additions & 2 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,32 @@ wq_config:

#### [Django REST Framework] + [pandas] = A Model-driven Visualization API

**Django REST Pandas** (DRP) provides a simple way to generate and serve [pandas] DataFrames via the [Django REST Framework]. The resulting API can serve up CSV (and a number of [other formats][formats] for consumption by a client-side visualization tool like [d3.js].
**Django REST Pandas** (DRP) provides a simple way to generate and serve [pandas] DataFrames via the [Django REST Framework]. The resulting API can serve up CSV (and a number of [other formats][formats] for consumption by a client-side visualization tool like [@wq/analyst]:

```js
// @wq/analyst
{
"title": "Live Demo",
"url": "/weather.csv",
"initial_rows": 10,
"initial_order": {
"date": "desc"
},
"formats": {
"csv": "CSV",
"xlsx": "Excel",
"json": "JSON",
"html": "HTML"
}
}
```

[**Django REST Pandas on GitHub**](https://github.com/wq/django-rest-pandas)

[pandas]: https://pandas.pydata.org/
[Django REST Framework]: https://www.django-rest-framework.org/
[formats]: ./renderers/index.md
[d3.js]: ./@wq/chart.md
[@wq/analyst]: ./@wq/analyst.md

## News

Expand Down
11 changes: 9 additions & 2 deletions docs/js/$index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,21 @@ layout: null
---

import wq, { modules } from 'https://unpkg.com/wq';
import markdown, { renderers } from 'https://unpkg.com/@wq/markdown@next';
import markdown, { components } from 'https://unpkg.com/@wq/markdown@next';
import analyst from 'https://unpkg.com/@wq/analyst';

import Demo from './demo.js';

const React = modules['react'];
const { Typography, Link } = modules['@wq/material'];

wq.use(markdown);
components.code = Demo;

wq.use([markdown, analyst]);

const config = {
site_title: 'Django REST Pandas',
logo: '/images/icons/django-rest-pandas.svg',
store: {
service: '',
defaults: {
Expand Down Expand Up @@ -52,6 +58,7 @@ function pageConf(page) {
icon: page.wq_config.icon_data ? page.wq_config.name : null,
markdown: page.content,
list: true,
form: [],
cache: 'all',
can_change: false,
can_add: false,
Expand Down
31 changes: 31 additions & 0 deletions docs/js/demo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { modules } from "https://unpkg.com/wq";
import { components } from "https://unpkg.com/@wq/markdown";
import { Analyst } from "https://unpkg.com/@wq/analyst";

const React = modules.react;
const Code = components.code;

export default function CodeDetect(props) {
const { children: value } = props;
if (value.includes("// @wq/analyst")) {
const config = parseConfig(value);
if (config) {
return React.createElement(Analyst, config);
} else {
return React.createElement(Code, {
children: "// Error parsing @wq/analyst config\n\n" + value,
});
}
} else {
return React.createElement(Code, props);
}
}

function parseConfig(value) {
value = value.replace("// @wq/analyst", "").trim();
try {
return JSON.parse(value);
} catch {
return null;
}
}
1 change: 1 addition & 0 deletions tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@
setup_test_environment()
django.setup()
call_command("makemigrations", "testapp", interactive=False)
call_command("makemigrations", "weather", interactive=False)
call_command("migrate", interactive=False)
10 changes: 5 additions & 5 deletions tests/files/multitimeseries.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,20 @@
<html lang="en-us" dir="ltr">
<head>
<title>Multi Time Series</title>
<link rel="stylesheet" href="admin/css/base.css">
<link rel="stylesheet" href="/static/admin/css/base.css">

<link rel="stylesheet" href="admin/css/dark_mode.css">
<script src="admin/js/theme.js" defer></script>
<link rel="stylesheet" href="/static/admin/css/dark_mode.css">
<script src="/static/admin/js/theme.js" defer></script>



<link rel="stylesheet" type="text/css" href="admin/css/forms.css">
<link rel="stylesheet" type="text/css" href="/static/admin/css/forms.css">




<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="admin/css/responsive.css">
<link rel="stylesheet" href="/static/admin/css/responsive.css">


<meta name="robots" content="NONE,NOARCHIVE">
Expand Down
10 changes: 5 additions & 5 deletions tests/files/timeseries.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,20 @@
<html lang="en-us" dir="ltr">
<head>
<title>Time Series Custom</title>
<link rel="stylesheet" href="admin/css/base.css">
<link rel="stylesheet" href="/static/admin/css/base.css">

<link rel="stylesheet" href="admin/css/dark_mode.css">
<script src="admin/js/theme.js" defer></script>
<link rel="stylesheet" href="/static/admin/css/dark_mode.css">
<script src="/static/admin/js/theme.js" defer></script>



<link rel="stylesheet" type="text/css" href="admin/css/forms.css">
<link rel="stylesheet" type="text/css" href="/static/admin/css/forms.css">




<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="admin/css/responsive.css">
<link rel="stylesheet" href="/static/admin/css/responsive.css">


<meta name="robots" content="NONE,NOARCHIVE">
Expand Down
52 changes: 52 additions & 0 deletions tests/generate_docs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import unittest
from rest_framework.test import APITestCase
from tests.testapp.models import TimeSeries
from tests.weather.models import Station
from django.core.management import call_command
import pathlib


DOCS = pathlib.Path("docs")

STATIONS = {
"MSP": "USW00014922",
"ATL": "USW00013874",
"LAX": "USW00023174",
}

class DocsTestCase(APITestCase):
def setUp(self):
data = (
("2014-01-01", 0.5),
("2014-01-02", 0.4),
("2014-01-03", 0.6),
("2014-01-04", 0.2),
("2014-01-05", 0.1),
)
for date, value in data:
TimeSeries.objects.create(date=date, value=value)

for name, code in STATIONS.items():
station = Station.objects.create(name=name, code=code)
station.load_weather()

def test_docs(self):
call_command('collectstatic', interactive=False)
for url in (
"timeseries.html",
"timeseries.csv",
"timeseries.json",
"timeseries.xlsx",
"timeseries.png",
"timeseries.svg",
"weather.html",
"weather.csv",
"weather.json",
"weather.xlsx",
"weather.png",
"weather.svg",
):
response = self.client.get(f"/{url}")
path = DOCS / url
path.parent.mkdir(parents=True, exist_ok=True)
path.write_bytes(response.content)
4 changes: 4 additions & 0 deletions tests/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
"django.contrib.contenttypes",
"django.contrib.messages",
"django.contrib.sessions",
"django.contrib.staticfiles",
"tests.testapp",
"tests.weather",
"rest_pandas",
"rest_framework",
)
Expand All @@ -16,6 +18,8 @@
}
}
ROOT_URLCONF = "tests.urls"
STATIC_URL = "/static"
STATIC_ROOT = "docs/static"
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
Expand Down
1 change: 1 addition & 0 deletions tests/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@

urlpatterns = [
path("", include("tests.testapp.urls")),
path("", include("tests.weather.urls")),
path("admin", admin.site.urls),
]
61 changes: 61 additions & 0 deletions tests/weather/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# Generated by Django 5.0.3 on 2024-04-02 02:59

import django.db.models.deletion
from django.db import migrations, models


class Migration(migrations.Migration):

initial = True

dependencies = []

operations = [
migrations.CreateModel(
name="Station",
fields=[
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("name", models.CharField(max_length=50, unique=True)),
("code", models.CharField(max_length=20, unique=True)),
],
),
migrations.CreateModel(
name="Weather",
fields=[
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("date", models.DateField(verbose_name="Date")),
(
"tavg",
models.IntegerField(null=True, verbose_name="Average Temp (°F)"),
),
("tmax", models.IntegerField(verbose_name="Max Temp (°F)")),
("tmin", models.IntegerField(verbose_name="Min Temp (°F)")),
("prcp", models.FloatField(verbose_name="Precipitation (in)")),
("snow", models.FloatField(null=True, verbose_name="Snow (in)")),
("snwd", models.FloatField(null=True, verbose_name="Snow Depth (in)")),
(
"station",
models.ForeignKey(
on_delete=django.db.models.deletion.PROTECT,
to="weather.station",
),
),
],
),
]
Empty file.
41 changes: 41 additions & 0 deletions tests/weather/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
from django.db import models
import requests


DATA_URL = "https://www.ncei.noaa.gov/access/past-weather/{code}/data.csv"


class Station(models.Model):
name = models.CharField(max_length=50, unique=True)
code = models.CharField(max_length=20, unique=True)

def load_weather(self):
response = requests.get(DATA_URL.format(code=self.code))

for i, row in enumerate(response.iter_lines(decode_unicode=True)):
if i < 2:
continue
assert row.count(",") == 6
date, tavg, tmax, tmin, prcp, snow, snwd = row.split(",")
if date < '2020-01-01':
continue
self.weather_set.create(
date=date,
tavg=tavg or None,
tmax=tmax or tavg,
tmin=tmin,
prcp=prcp or None,
snow=snow or None,
snwd=snwd or None,
)


class Weather(models.Model):
station = models.ForeignKey(Station, on_delete=models.PROTECT)
date = models.DateField(verbose_name="Date")
tavg = models.IntegerField(verbose_name="Average Temp (°F)", null=True)
tmax = models.IntegerField(verbose_name="Max Temp (°F)")
tmin = models.IntegerField(verbose_name="Min Temp (°F)")
prcp = models.FloatField(verbose_name="Precipitation (in)")
snow = models.FloatField(verbose_name="Snow (in)", null=True)
snwd = models.FloatField(verbose_name="Snow Depth (in)", null=True)
12 changes: 12 additions & 0 deletions tests/weather/serializers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from rest_framework import serializers
from .models import Weather


class WeatherSerializer(serializers.ModelSerializer):
station = serializers.ReadOnlyField(source="station.name", label="Station")

class Meta:
model = Weather
exclude = ["id"]
pandas_index = ["date"] # Date
pandas_unstacked_header = ["Station"]
Loading

0 comments on commit a4cbcdc

Please sign in to comment.