From 2e029a3d78fa038e7a255b21b305330219ad1dba Mon Sep 17 00:00:00 2001 From: Andy Reagan Date: Mon, 22 Jun 2026 22:01:57 -0400 Subject: [PATCH 01/16] Make a fresh install/migrate work locally - Add psycopg2-binary (was missing from pyproject/lock; the postgres backend configured in settings had no driver) - Repoint the Embeddable.author FK in migration 0002 from the defunct twython_django.TwitterProfile to 'self'. The field is removed in 0010 and Embeddable deleted in 0011, so the target is irrelevant to the final schema; this lets `migrate` replay from scratch (new dev env / CI) without the uninstalled twython-django app. Co-Authored-By: Claude Opus 4.8 --- .../migrations/0002_auto_20200413_1227.py | 7 +- pyproject.toml | 1 + uv.lock | 224 +++++++++++------- 3 files changed, 146 insertions(+), 86 deletions(-) diff --git a/hedonometer/migrations/0002_auto_20200413_1227.py b/hedonometer/migrations/0002_auto_20200413_1227.py index 7d109c17..cb1cc891 100644 --- a/hedonometer/migrations/0002_auto_20200413_1227.py +++ b/hedonometer/migrations/0002_auto_20200413_1227.py @@ -16,7 +16,12 @@ class Migration(migrations.Migration): migrations.AddField( model_name='embeddable', name='author', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='twython_django.TwitterProfile'), + # Originally a FK to twython_django.TwitterProfile; that app is no + # longer installed, this field is removed in migration 0010, and the + # Embeddable model is deleted in 0011 -- so the target is irrelevant + # to the final schema. Repointed to 'self' so a fresh `migrate` + # (new dev env / CI) can replay without the defunct twython-django. + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='hedonometer.Embeddable'), ), migrations.AddField( model_name='band', diff --git a/pyproject.toml b/pyproject.toml index 1394dc56..434ecf2a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,4 +8,5 @@ dependencies = [ "django==4.2", "django-tastypie==0.14.6", "jinja2>=3.0", + "psycopg2-binary==2.9.12", ] diff --git a/uv.lock b/uv.lock index 6e68f52b..1013d700 100644 --- a/uv.lock +++ b/uv.lock @@ -1,14 +1,14 @@ version = 1 -revision = 2 +revision = 3 requires-python = ">=3.11" [[package]] name = "asgiref" version = "3.9.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/90/61/0aa957eec22ff70b830b22ff91f825e70e1ef732c06666a805730f28b36b/asgiref-3.9.1.tar.gz", hash = "sha256:a5ab6582236218e5ef1648f242fd9f10626cfd4de8dc377db215d5d5098e3142", size = 36870, upload_time = "2025-07-08T09:07:43.344Z" } +sdist = { url = "https://files.pythonhosted.org/packages/90/61/0aa957eec22ff70b830b22ff91f825e70e1ef732c06666a805730f28b36b/asgiref-3.9.1.tar.gz", hash = "sha256:a5ab6582236218e5ef1648f242fd9f10626cfd4de8dc377db215d5d5098e3142", size = 36870, upload-time = "2025-07-08T09:07:43.344Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7c/3c/0464dcada90d5da0e71018c04a140ad6349558afb30b3051b4264cc5b965/asgiref-3.9.1-py3-none-any.whl", hash = "sha256:f3bba7092a48005b5f5bacd747d36ee4a5a61f4a269a6df590b43144355ebd2c", size = 23790, upload_time = "2025-07-08T09:07:41.548Z" }, + { url = "https://files.pythonhosted.org/packages/7c/3c/0464dcada90d5da0e71018c04a140ad6349558afb30b3051b4264cc5b965/asgiref-3.9.1-py3-none-any.whl", hash = "sha256:f3bba7092a48005b5f5bacd747d36ee4a5a61f4a269a6df590b43144355ebd2c", size = 23790, upload-time = "2025-07-08T09:07:41.548Z" }, ] [[package]] @@ -20,9 +20,9 @@ dependencies = [ { name = "sqlparse" }, { name = "tzdata", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9a/bb/48aa3e0850923096dff2766d21a6004d6e1a3317f0bd400ba81f586754e1/Django-4.2.tar.gz", hash = "sha256:c36e2ab12824e2ac36afa8b2515a70c53c7742f0d6eaefa7311ec379558db997", size = 10415665, upload_time = "2023-04-03T08:36:16.829Z" } +sdist = { url = "https://files.pythonhosted.org/packages/9a/bb/48aa3e0850923096dff2766d21a6004d6e1a3317f0bd400ba81f586754e1/Django-4.2.tar.gz", hash = "sha256:c36e2ab12824e2ac36afa8b2515a70c53c7742f0d6eaefa7311ec379558db997", size = 10415665, upload-time = "2023-04-03T08:36:16.829Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d9/40/6012f98b14b64b4d3dc47b0c2f116fccbd0795ab35515d0c40dac73b81b8/Django-4.2-py3-none-any.whl", hash = "sha256:ad33ed68db9398f5dfb33282704925bce044bef4261cd4fb59e4e7f9ae505a78", size = 7988617, upload_time = "2023-04-03T08:36:11.797Z" }, + { url = "https://files.pythonhosted.org/packages/d9/40/6012f98b14b64b4d3dc47b0c2f116fccbd0795ab35515d0c40dac73b81b8/Django-4.2-py3-none-any.whl", hash = "sha256:ad33ed68db9398f5dfb33282704925bce044bef4261cd4fb59e4e7f9ae505a78", size = 7988617, upload-time = "2023-04-03T08:36:11.797Z" }, ] [[package]] @@ -33,7 +33,7 @@ dependencies = [ { name = "python-dateutil" }, { name = "python-mimeparse" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d7/92/33d0b11bc6d8b5bf8675fd9f73492788c363007c61864186a425ff136041/django-tastypie-0.14.6.tar.gz", hash = "sha256:7f16928ffa5b1b390edd81fa0c15a239cca7fc1134d440ecaa2040b5edc549b8", size = 154751, upload_time = "2023-09-02T17:41:30.142Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/92/33d0b11bc6d8b5bf8675fd9f73492788c363007c61864186a425ff136041/django-tastypie-0.14.6.tar.gz", hash = "sha256:7f16928ffa5b1b390edd81fa0c15a239cca7fc1134d440ecaa2040b5edc549b8", size = 154751, upload-time = "2023-09-02T17:41:30.142Z" } [[package]] name = "hedonometer" @@ -43,6 +43,7 @@ dependencies = [ { name = "django" }, { name = "django-tastypie" }, { name = "jinja2" }, + { name = "psycopg2-binary" }, ] [package.metadata] @@ -50,6 +51,7 @@ requires-dist = [ { name = "django", specifier = "==4.2" }, { name = "django-tastypie", specifier = "==0.14.6" }, { name = "jinja2", specifier = ">=3.0" }, + { name = "psycopg2-binary", specifier = "==2.9.12" }, ] [[package]] @@ -59,83 +61,135 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markupsafe" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload_time = "2025-03-05T20:05:02.478Z" } +sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload_time = "2025-03-05T20:05:00.369Z" }, + { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, ] [[package]] name = "markupsafe" version = "3.0.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313, upload_time = "2025-09-27T18:37:40.426Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313, upload-time = "2025-09-27T18:37:40.426Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/08/db/fefacb2136439fc8dd20e797950e749aa1f4997ed584c62cfb8ef7c2be0e/markupsafe-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad", size = 11631, upload_time = "2025-09-27T18:36:18.185Z" }, - { url = "https://files.pythonhosted.org/packages/e1/2e/5898933336b61975ce9dc04decbc0a7f2fee78c30353c5efba7f2d6ff27a/markupsafe-3.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a", size = 12058, upload_time = "2025-09-27T18:36:19.444Z" }, - { url = "https://files.pythonhosted.org/packages/1d/09/adf2df3699d87d1d8184038df46a9c80d78c0148492323f4693df54e17bb/markupsafe-3.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50", size = 24287, upload_time = "2025-09-27T18:36:20.768Z" }, - { url = "https://files.pythonhosted.org/packages/30/ac/0273f6fcb5f42e314c6d8cd99effae6a5354604d461b8d392b5ec9530a54/markupsafe-3.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf", size = 22940, upload_time = "2025-09-27T18:36:22.249Z" }, - { url = "https://files.pythonhosted.org/packages/19/ae/31c1be199ef767124c042c6c3e904da327a2f7f0cd63a0337e1eca2967a8/markupsafe-3.0.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f", size = 21887, upload_time = "2025-09-27T18:36:23.535Z" }, - { url = "https://files.pythonhosted.org/packages/b2/76/7edcab99d5349a4532a459e1fe64f0b0467a3365056ae550d3bcf3f79e1e/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a", size = 23692, upload_time = "2025-09-27T18:36:24.823Z" }, - { url = "https://files.pythonhosted.org/packages/a4/28/6e74cdd26d7514849143d69f0bf2399f929c37dc2b31e6829fd2045b2765/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115", size = 21471, upload_time = "2025-09-27T18:36:25.95Z" }, - { url = "https://files.pythonhosted.org/packages/62/7e/a145f36a5c2945673e590850a6f8014318d5577ed7e5920a4b3448e0865d/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a", size = 22923, upload_time = "2025-09-27T18:36:27.109Z" }, - { url = "https://files.pythonhosted.org/packages/0f/62/d9c46a7f5c9adbeeeda52f5b8d802e1094e9717705a645efc71b0913a0a8/markupsafe-3.0.3-cp311-cp311-win32.whl", hash = "sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19", size = 14572, upload_time = "2025-09-27T18:36:28.045Z" }, - { url = "https://files.pythonhosted.org/packages/83/8a/4414c03d3f891739326e1783338e48fb49781cc915b2e0ee052aa490d586/markupsafe-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01", size = 15077, upload_time = "2025-09-27T18:36:29.025Z" }, - { url = "https://files.pythonhosted.org/packages/35/73/893072b42e6862f319b5207adc9ae06070f095b358655f077f69a35601f0/markupsafe-3.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c", size = 13876, upload_time = "2025-09-27T18:36:29.954Z" }, - { url = "https://files.pythonhosted.org/packages/5a/72/147da192e38635ada20e0a2e1a51cf8823d2119ce8883f7053879c2199b5/markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e", size = 11615, upload_time = "2025-09-27T18:36:30.854Z" }, - { url = "https://files.pythonhosted.org/packages/9a/81/7e4e08678a1f98521201c3079f77db69fb552acd56067661f8c2f534a718/markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce", size = 12020, upload_time = "2025-09-27T18:36:31.971Z" }, - { url = "https://files.pythonhosted.org/packages/1e/2c/799f4742efc39633a1b54a92eec4082e4f815314869865d876824c257c1e/markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d", size = 24332, upload_time = "2025-09-27T18:36:32.813Z" }, - { url = "https://files.pythonhosted.org/packages/3c/2e/8d0c2ab90a8c1d9a24f0399058ab8519a3279d1bd4289511d74e909f060e/markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d", size = 22947, upload_time = "2025-09-27T18:36:33.86Z" }, - { url = "https://files.pythonhosted.org/packages/2c/54/887f3092a85238093a0b2154bd629c89444f395618842e8b0c41783898ea/markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a", size = 21962, upload_time = "2025-09-27T18:36:35.099Z" }, - { url = "https://files.pythonhosted.org/packages/c9/2f/336b8c7b6f4a4d95e91119dc8521402461b74a485558d8f238a68312f11c/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b", size = 23760, upload_time = "2025-09-27T18:36:36.001Z" }, - { url = "https://files.pythonhosted.org/packages/32/43/67935f2b7e4982ffb50a4d169b724d74b62a3964bc1a9a527f5ac4f1ee2b/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f", size = 21529, upload_time = "2025-09-27T18:36:36.906Z" }, - { url = "https://files.pythonhosted.org/packages/89/e0/4486f11e51bbba8b0c041098859e869e304d1c261e59244baa3d295d47b7/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b", size = 23015, upload_time = "2025-09-27T18:36:37.868Z" }, - { url = "https://files.pythonhosted.org/packages/2f/e1/78ee7a023dac597a5825441ebd17170785a9dab23de95d2c7508ade94e0e/markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d", size = 14540, upload_time = "2025-09-27T18:36:38.761Z" }, - { url = "https://files.pythonhosted.org/packages/aa/5b/bec5aa9bbbb2c946ca2733ef9c4ca91c91b6a24580193e891b5f7dbe8e1e/markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c", size = 15105, upload_time = "2025-09-27T18:36:39.701Z" }, - { url = "https://files.pythonhosted.org/packages/e5/f1/216fc1bbfd74011693a4fd837e7026152e89c4bcf3e77b6692fba9923123/markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f", size = 13906, upload_time = "2025-09-27T18:36:40.689Z" }, - { url = "https://files.pythonhosted.org/packages/38/2f/907b9c7bbba283e68f20259574b13d005c121a0fa4c175f9bed27c4597ff/markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795", size = 11622, upload_time = "2025-09-27T18:36:41.777Z" }, - { url = "https://files.pythonhosted.org/packages/9c/d9/5f7756922cdd676869eca1c4e3c0cd0df60ed30199ffd775e319089cb3ed/markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219", size = 12029, upload_time = "2025-09-27T18:36:43.257Z" }, - { url = "https://files.pythonhosted.org/packages/00/07/575a68c754943058c78f30db02ee03a64b3c638586fba6a6dd56830b30a3/markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6", size = 24374, upload_time = "2025-09-27T18:36:44.508Z" }, - { url = "https://files.pythonhosted.org/packages/a9/21/9b05698b46f218fc0e118e1f8168395c65c8a2c750ae2bab54fc4bd4e0e8/markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676", size = 22980, upload_time = "2025-09-27T18:36:45.385Z" }, - { url = "https://files.pythonhosted.org/packages/7f/71/544260864f893f18b6827315b988c146b559391e6e7e8f7252839b1b846a/markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9", size = 21990, upload_time = "2025-09-27T18:36:46.916Z" }, - { url = "https://files.pythonhosted.org/packages/c2/28/b50fc2f74d1ad761af2f5dcce7492648b983d00a65b8c0e0cb457c82ebbe/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1", size = 23784, upload_time = "2025-09-27T18:36:47.884Z" }, - { url = "https://files.pythonhosted.org/packages/ed/76/104b2aa106a208da8b17a2fb72e033a5a9d7073c68f7e508b94916ed47a9/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc", size = 21588, upload_time = "2025-09-27T18:36:48.82Z" }, - { url = "https://files.pythonhosted.org/packages/b5/99/16a5eb2d140087ebd97180d95249b00a03aa87e29cc224056274f2e45fd6/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12", size = 23041, upload_time = "2025-09-27T18:36:49.797Z" }, - { url = "https://files.pythonhosted.org/packages/19/bc/e7140ed90c5d61d77cea142eed9f9c303f4c4806f60a1044c13e3f1471d0/markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed", size = 14543, upload_time = "2025-09-27T18:36:51.584Z" }, - { url = "https://files.pythonhosted.org/packages/05/73/c4abe620b841b6b791f2edc248f556900667a5a1cf023a6646967ae98335/markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5", size = 15113, upload_time = "2025-09-27T18:36:52.537Z" }, - { url = "https://files.pythonhosted.org/packages/f0/3a/fa34a0f7cfef23cf9500d68cb7c32dd64ffd58a12b09225fb03dd37d5b80/markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485", size = 13911, upload_time = "2025-09-27T18:36:53.513Z" }, - { url = "https://files.pythonhosted.org/packages/e4/d7/e05cd7efe43a88a17a37b3ae96e79a19e846f3f456fe79c57ca61356ef01/markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73", size = 11658, upload_time = "2025-09-27T18:36:54.819Z" }, - { url = "https://files.pythonhosted.org/packages/99/9e/e412117548182ce2148bdeacdda3bb494260c0b0184360fe0d56389b523b/markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37", size = 12066, upload_time = "2025-09-27T18:36:55.714Z" }, - { url = "https://files.pythonhosted.org/packages/bc/e6/fa0ffcda717ef64a5108eaa7b4f5ed28d56122c9a6d70ab8b72f9f715c80/markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19", size = 25639, upload_time = "2025-09-27T18:36:56.908Z" }, - { url = "https://files.pythonhosted.org/packages/96/ec/2102e881fe9d25fc16cb4b25d5f5cde50970967ffa5dddafdb771237062d/markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025", size = 23569, upload_time = "2025-09-27T18:36:57.913Z" }, - { url = "https://files.pythonhosted.org/packages/4b/30/6f2fce1f1f205fc9323255b216ca8a235b15860c34b6798f810f05828e32/markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6", size = 23284, upload_time = "2025-09-27T18:36:58.833Z" }, - { url = "https://files.pythonhosted.org/packages/58/47/4a0ccea4ab9f5dcb6f79c0236d954acb382202721e704223a8aafa38b5c8/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f", size = 24801, upload_time = "2025-09-27T18:36:59.739Z" }, - { url = "https://files.pythonhosted.org/packages/6a/70/3780e9b72180b6fecb83a4814d84c3bf4b4ae4bf0b19c27196104149734c/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb", size = 22769, upload_time = "2025-09-27T18:37:00.719Z" }, - { url = "https://files.pythonhosted.org/packages/98/c5/c03c7f4125180fc215220c035beac6b9cb684bc7a067c84fc69414d315f5/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009", size = 23642, upload_time = "2025-09-27T18:37:01.673Z" }, - { url = "https://files.pythonhosted.org/packages/80/d6/2d1b89f6ca4bff1036499b1e29a1d02d282259f3681540e16563f27ebc23/markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354", size = 14612, upload_time = "2025-09-27T18:37:02.639Z" }, - { url = "https://files.pythonhosted.org/packages/2b/98/e48a4bfba0a0ffcf9925fe2d69240bfaa19c6f7507b8cd09c70684a53c1e/markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218", size = 15200, upload_time = "2025-09-27T18:37:03.582Z" }, - { url = "https://files.pythonhosted.org/packages/0e/72/e3cc540f351f316e9ed0f092757459afbc595824ca724cbc5a5d4263713f/markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287", size = 13973, upload_time = "2025-09-27T18:37:04.929Z" }, - { url = "https://files.pythonhosted.org/packages/33/8a/8e42d4838cd89b7dde187011e97fe6c3af66d8c044997d2183fbd6d31352/markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe", size = 11619, upload_time = "2025-09-27T18:37:06.342Z" }, - { url = "https://files.pythonhosted.org/packages/b5/64/7660f8a4a8e53c924d0fa05dc3a55c9cee10bbd82b11c5afb27d44b096ce/markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026", size = 12029, upload_time = "2025-09-27T18:37:07.213Z" }, - { url = "https://files.pythonhosted.org/packages/da/ef/e648bfd021127bef5fa12e1720ffed0c6cbb8310c8d9bea7266337ff06de/markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737", size = 24408, upload_time = "2025-09-27T18:37:09.572Z" }, - { url = "https://files.pythonhosted.org/packages/41/3c/a36c2450754618e62008bf7435ccb0f88053e07592e6028a34776213d877/markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97", size = 23005, upload_time = "2025-09-27T18:37:10.58Z" }, - { url = "https://files.pythonhosted.org/packages/bc/20/b7fdf89a8456b099837cd1dc21974632a02a999ec9bf7ca3e490aacd98e7/markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d", size = 22048, upload_time = "2025-09-27T18:37:11.547Z" }, - { url = "https://files.pythonhosted.org/packages/9a/a7/591f592afdc734f47db08a75793a55d7fbcc6902a723ae4cfbab61010cc5/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda", size = 23821, upload_time = "2025-09-27T18:37:12.48Z" }, - { url = "https://files.pythonhosted.org/packages/7d/33/45b24e4f44195b26521bc6f1a82197118f74df348556594bd2262bda1038/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf", size = 21606, upload_time = "2025-09-27T18:37:13.485Z" }, - { url = "https://files.pythonhosted.org/packages/ff/0e/53dfaca23a69fbfbbf17a4b64072090e70717344c52eaaaa9c5ddff1e5f0/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe", size = 23043, upload_time = "2025-09-27T18:37:14.408Z" }, - { url = "https://files.pythonhosted.org/packages/46/11/f333a06fc16236d5238bfe74daccbca41459dcd8d1fa952e8fbd5dccfb70/markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9", size = 14747, upload_time = "2025-09-27T18:37:15.36Z" }, - { url = "https://files.pythonhosted.org/packages/28/52/182836104b33b444e400b14f797212f720cbc9ed6ba34c800639d154e821/markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581", size = 15341, upload_time = "2025-09-27T18:37:16.496Z" }, - { url = "https://files.pythonhosted.org/packages/6f/18/acf23e91bd94fd7b3031558b1f013adfa21a8e407a3fdb32745538730382/markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4", size = 14073, upload_time = "2025-09-27T18:37:17.476Z" }, - { url = "https://files.pythonhosted.org/packages/3c/f0/57689aa4076e1b43b15fdfa646b04653969d50cf30c32a102762be2485da/markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab", size = 11661, upload_time = "2025-09-27T18:37:18.453Z" }, - { url = "https://files.pythonhosted.org/packages/89/c3/2e67a7ca217c6912985ec766c6393b636fb0c2344443ff9d91404dc4c79f/markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175", size = 12069, upload_time = "2025-09-27T18:37:19.332Z" }, - { url = "https://files.pythonhosted.org/packages/f0/00/be561dce4e6ca66b15276e184ce4b8aec61fe83662cce2f7d72bd3249d28/markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634", size = 25670, upload_time = "2025-09-27T18:37:20.245Z" }, - { url = "https://files.pythonhosted.org/packages/50/09/c419f6f5a92e5fadde27efd190eca90f05e1261b10dbd8cbcb39cd8ea1dc/markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50", size = 23598, upload_time = "2025-09-27T18:37:21.177Z" }, - { url = "https://files.pythonhosted.org/packages/22/44/a0681611106e0b2921b3033fc19bc53323e0b50bc70cffdd19f7d679bb66/markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e", size = 23261, upload_time = "2025-09-27T18:37:22.167Z" }, - { url = "https://files.pythonhosted.org/packages/5f/57/1b0b3f100259dc9fffe780cfb60d4be71375510e435efec3d116b6436d43/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5", size = 24835, upload_time = "2025-09-27T18:37:23.296Z" }, - { url = "https://files.pythonhosted.org/packages/26/6a/4bf6d0c97c4920f1597cc14dd720705eca0bf7c787aebc6bb4d1bead5388/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523", size = 22733, upload_time = "2025-09-27T18:37:24.237Z" }, - { url = "https://files.pythonhosted.org/packages/14/c7/ca723101509b518797fedc2fdf79ba57f886b4aca8a7d31857ba3ee8281f/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc", size = 23672, upload_time = "2025-09-27T18:37:25.271Z" }, - { url = "https://files.pythonhosted.org/packages/fb/df/5bd7a48c256faecd1d36edc13133e51397e41b73bb77e1a69deab746ebac/markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d", size = 14819, upload_time = "2025-09-27T18:37:26.285Z" }, - { url = "https://files.pythonhosted.org/packages/1a/8a/0402ba61a2f16038b48b39bccca271134be00c5c9f0f623208399333c448/markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9", size = 15426, upload_time = "2025-09-27T18:37:27.316Z" }, - { url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146, upload_time = "2025-09-27T18:37:28.327Z" }, + { url = "https://files.pythonhosted.org/packages/08/db/fefacb2136439fc8dd20e797950e749aa1f4997ed584c62cfb8ef7c2be0e/markupsafe-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad", size = 11631, upload-time = "2025-09-27T18:36:18.185Z" }, + { url = "https://files.pythonhosted.org/packages/e1/2e/5898933336b61975ce9dc04decbc0a7f2fee78c30353c5efba7f2d6ff27a/markupsafe-3.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a", size = 12058, upload-time = "2025-09-27T18:36:19.444Z" }, + { url = "https://files.pythonhosted.org/packages/1d/09/adf2df3699d87d1d8184038df46a9c80d78c0148492323f4693df54e17bb/markupsafe-3.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50", size = 24287, upload-time = "2025-09-27T18:36:20.768Z" }, + { url = "https://files.pythonhosted.org/packages/30/ac/0273f6fcb5f42e314c6d8cd99effae6a5354604d461b8d392b5ec9530a54/markupsafe-3.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf", size = 22940, upload-time = "2025-09-27T18:36:22.249Z" }, + { url = "https://files.pythonhosted.org/packages/19/ae/31c1be199ef767124c042c6c3e904da327a2f7f0cd63a0337e1eca2967a8/markupsafe-3.0.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f", size = 21887, upload-time = "2025-09-27T18:36:23.535Z" }, + { url = "https://files.pythonhosted.org/packages/b2/76/7edcab99d5349a4532a459e1fe64f0b0467a3365056ae550d3bcf3f79e1e/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a", size = 23692, upload-time = "2025-09-27T18:36:24.823Z" }, + { url = "https://files.pythonhosted.org/packages/a4/28/6e74cdd26d7514849143d69f0bf2399f929c37dc2b31e6829fd2045b2765/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115", size = 21471, upload-time = "2025-09-27T18:36:25.95Z" }, + { url = "https://files.pythonhosted.org/packages/62/7e/a145f36a5c2945673e590850a6f8014318d5577ed7e5920a4b3448e0865d/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a", size = 22923, upload-time = "2025-09-27T18:36:27.109Z" }, + { url = "https://files.pythonhosted.org/packages/0f/62/d9c46a7f5c9adbeeeda52f5b8d802e1094e9717705a645efc71b0913a0a8/markupsafe-3.0.3-cp311-cp311-win32.whl", hash = "sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19", size = 14572, upload-time = "2025-09-27T18:36:28.045Z" }, + { url = "https://files.pythonhosted.org/packages/83/8a/4414c03d3f891739326e1783338e48fb49781cc915b2e0ee052aa490d586/markupsafe-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01", size = 15077, upload-time = "2025-09-27T18:36:29.025Z" }, + { url = "https://files.pythonhosted.org/packages/35/73/893072b42e6862f319b5207adc9ae06070f095b358655f077f69a35601f0/markupsafe-3.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c", size = 13876, upload-time = "2025-09-27T18:36:29.954Z" }, + { url = "https://files.pythonhosted.org/packages/5a/72/147da192e38635ada20e0a2e1a51cf8823d2119ce8883f7053879c2199b5/markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e", size = 11615, upload-time = "2025-09-27T18:36:30.854Z" }, + { url = "https://files.pythonhosted.org/packages/9a/81/7e4e08678a1f98521201c3079f77db69fb552acd56067661f8c2f534a718/markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce", size = 12020, upload-time = "2025-09-27T18:36:31.971Z" }, + { url = "https://files.pythonhosted.org/packages/1e/2c/799f4742efc39633a1b54a92eec4082e4f815314869865d876824c257c1e/markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d", size = 24332, upload-time = "2025-09-27T18:36:32.813Z" }, + { url = "https://files.pythonhosted.org/packages/3c/2e/8d0c2ab90a8c1d9a24f0399058ab8519a3279d1bd4289511d74e909f060e/markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d", size = 22947, upload-time = "2025-09-27T18:36:33.86Z" }, + { url = "https://files.pythonhosted.org/packages/2c/54/887f3092a85238093a0b2154bd629c89444f395618842e8b0c41783898ea/markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a", size = 21962, upload-time = "2025-09-27T18:36:35.099Z" }, + { url = "https://files.pythonhosted.org/packages/c9/2f/336b8c7b6f4a4d95e91119dc8521402461b74a485558d8f238a68312f11c/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b", size = 23760, upload-time = "2025-09-27T18:36:36.001Z" }, + { url = "https://files.pythonhosted.org/packages/32/43/67935f2b7e4982ffb50a4d169b724d74b62a3964bc1a9a527f5ac4f1ee2b/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f", size = 21529, upload-time = "2025-09-27T18:36:36.906Z" }, + { url = "https://files.pythonhosted.org/packages/89/e0/4486f11e51bbba8b0c041098859e869e304d1c261e59244baa3d295d47b7/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b", size = 23015, upload-time = "2025-09-27T18:36:37.868Z" }, + { url = "https://files.pythonhosted.org/packages/2f/e1/78ee7a023dac597a5825441ebd17170785a9dab23de95d2c7508ade94e0e/markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d", size = 14540, upload-time = "2025-09-27T18:36:38.761Z" }, + { url = "https://files.pythonhosted.org/packages/aa/5b/bec5aa9bbbb2c946ca2733ef9c4ca91c91b6a24580193e891b5f7dbe8e1e/markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c", size = 15105, upload-time = "2025-09-27T18:36:39.701Z" }, + { url = "https://files.pythonhosted.org/packages/e5/f1/216fc1bbfd74011693a4fd837e7026152e89c4bcf3e77b6692fba9923123/markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f", size = 13906, upload-time = "2025-09-27T18:36:40.689Z" }, + { url = "https://files.pythonhosted.org/packages/38/2f/907b9c7bbba283e68f20259574b13d005c121a0fa4c175f9bed27c4597ff/markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795", size = 11622, upload-time = "2025-09-27T18:36:41.777Z" }, + { url = "https://files.pythonhosted.org/packages/9c/d9/5f7756922cdd676869eca1c4e3c0cd0df60ed30199ffd775e319089cb3ed/markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219", size = 12029, upload-time = "2025-09-27T18:36:43.257Z" }, + { url = "https://files.pythonhosted.org/packages/00/07/575a68c754943058c78f30db02ee03a64b3c638586fba6a6dd56830b30a3/markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6", size = 24374, upload-time = "2025-09-27T18:36:44.508Z" }, + { url = "https://files.pythonhosted.org/packages/a9/21/9b05698b46f218fc0e118e1f8168395c65c8a2c750ae2bab54fc4bd4e0e8/markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676", size = 22980, upload-time = "2025-09-27T18:36:45.385Z" }, + { url = "https://files.pythonhosted.org/packages/7f/71/544260864f893f18b6827315b988c146b559391e6e7e8f7252839b1b846a/markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9", size = 21990, upload-time = "2025-09-27T18:36:46.916Z" }, + { url = "https://files.pythonhosted.org/packages/c2/28/b50fc2f74d1ad761af2f5dcce7492648b983d00a65b8c0e0cb457c82ebbe/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1", size = 23784, upload-time = "2025-09-27T18:36:47.884Z" }, + { url = "https://files.pythonhosted.org/packages/ed/76/104b2aa106a208da8b17a2fb72e033a5a9d7073c68f7e508b94916ed47a9/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc", size = 21588, upload-time = "2025-09-27T18:36:48.82Z" }, + { url = "https://files.pythonhosted.org/packages/b5/99/16a5eb2d140087ebd97180d95249b00a03aa87e29cc224056274f2e45fd6/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12", size = 23041, upload-time = "2025-09-27T18:36:49.797Z" }, + { url = "https://files.pythonhosted.org/packages/19/bc/e7140ed90c5d61d77cea142eed9f9c303f4c4806f60a1044c13e3f1471d0/markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed", size = 14543, upload-time = "2025-09-27T18:36:51.584Z" }, + { url = "https://files.pythonhosted.org/packages/05/73/c4abe620b841b6b791f2edc248f556900667a5a1cf023a6646967ae98335/markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5", size = 15113, upload-time = "2025-09-27T18:36:52.537Z" }, + { url = "https://files.pythonhosted.org/packages/f0/3a/fa34a0f7cfef23cf9500d68cb7c32dd64ffd58a12b09225fb03dd37d5b80/markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485", size = 13911, upload-time = "2025-09-27T18:36:53.513Z" }, + { url = "https://files.pythonhosted.org/packages/e4/d7/e05cd7efe43a88a17a37b3ae96e79a19e846f3f456fe79c57ca61356ef01/markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73", size = 11658, upload-time = "2025-09-27T18:36:54.819Z" }, + { url = "https://files.pythonhosted.org/packages/99/9e/e412117548182ce2148bdeacdda3bb494260c0b0184360fe0d56389b523b/markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37", size = 12066, upload-time = "2025-09-27T18:36:55.714Z" }, + { url = "https://files.pythonhosted.org/packages/bc/e6/fa0ffcda717ef64a5108eaa7b4f5ed28d56122c9a6d70ab8b72f9f715c80/markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19", size = 25639, upload-time = "2025-09-27T18:36:56.908Z" }, + { url = "https://files.pythonhosted.org/packages/96/ec/2102e881fe9d25fc16cb4b25d5f5cde50970967ffa5dddafdb771237062d/markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025", size = 23569, upload-time = "2025-09-27T18:36:57.913Z" }, + { url = "https://files.pythonhosted.org/packages/4b/30/6f2fce1f1f205fc9323255b216ca8a235b15860c34b6798f810f05828e32/markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6", size = 23284, upload-time = "2025-09-27T18:36:58.833Z" }, + { url = "https://files.pythonhosted.org/packages/58/47/4a0ccea4ab9f5dcb6f79c0236d954acb382202721e704223a8aafa38b5c8/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f", size = 24801, upload-time = "2025-09-27T18:36:59.739Z" }, + { url = "https://files.pythonhosted.org/packages/6a/70/3780e9b72180b6fecb83a4814d84c3bf4b4ae4bf0b19c27196104149734c/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb", size = 22769, upload-time = "2025-09-27T18:37:00.719Z" }, + { url = "https://files.pythonhosted.org/packages/98/c5/c03c7f4125180fc215220c035beac6b9cb684bc7a067c84fc69414d315f5/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009", size = 23642, upload-time = "2025-09-27T18:37:01.673Z" }, + { url = "https://files.pythonhosted.org/packages/80/d6/2d1b89f6ca4bff1036499b1e29a1d02d282259f3681540e16563f27ebc23/markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354", size = 14612, upload-time = "2025-09-27T18:37:02.639Z" }, + { url = "https://files.pythonhosted.org/packages/2b/98/e48a4bfba0a0ffcf9925fe2d69240bfaa19c6f7507b8cd09c70684a53c1e/markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218", size = 15200, upload-time = "2025-09-27T18:37:03.582Z" }, + { url = "https://files.pythonhosted.org/packages/0e/72/e3cc540f351f316e9ed0f092757459afbc595824ca724cbc5a5d4263713f/markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287", size = 13973, upload-time = "2025-09-27T18:37:04.929Z" }, + { url = "https://files.pythonhosted.org/packages/33/8a/8e42d4838cd89b7dde187011e97fe6c3af66d8c044997d2183fbd6d31352/markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe", size = 11619, upload-time = "2025-09-27T18:37:06.342Z" }, + { url = "https://files.pythonhosted.org/packages/b5/64/7660f8a4a8e53c924d0fa05dc3a55c9cee10bbd82b11c5afb27d44b096ce/markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026", size = 12029, upload-time = "2025-09-27T18:37:07.213Z" }, + { url = "https://files.pythonhosted.org/packages/da/ef/e648bfd021127bef5fa12e1720ffed0c6cbb8310c8d9bea7266337ff06de/markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737", size = 24408, upload-time = "2025-09-27T18:37:09.572Z" }, + { url = "https://files.pythonhosted.org/packages/41/3c/a36c2450754618e62008bf7435ccb0f88053e07592e6028a34776213d877/markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97", size = 23005, upload-time = "2025-09-27T18:37:10.58Z" }, + { url = "https://files.pythonhosted.org/packages/bc/20/b7fdf89a8456b099837cd1dc21974632a02a999ec9bf7ca3e490aacd98e7/markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d", size = 22048, upload-time = "2025-09-27T18:37:11.547Z" }, + { url = "https://files.pythonhosted.org/packages/9a/a7/591f592afdc734f47db08a75793a55d7fbcc6902a723ae4cfbab61010cc5/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda", size = 23821, upload-time = "2025-09-27T18:37:12.48Z" }, + { url = "https://files.pythonhosted.org/packages/7d/33/45b24e4f44195b26521bc6f1a82197118f74df348556594bd2262bda1038/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf", size = 21606, upload-time = "2025-09-27T18:37:13.485Z" }, + { url = "https://files.pythonhosted.org/packages/ff/0e/53dfaca23a69fbfbbf17a4b64072090e70717344c52eaaaa9c5ddff1e5f0/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe", size = 23043, upload-time = "2025-09-27T18:37:14.408Z" }, + { url = "https://files.pythonhosted.org/packages/46/11/f333a06fc16236d5238bfe74daccbca41459dcd8d1fa952e8fbd5dccfb70/markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9", size = 14747, upload-time = "2025-09-27T18:37:15.36Z" }, + { url = "https://files.pythonhosted.org/packages/28/52/182836104b33b444e400b14f797212f720cbc9ed6ba34c800639d154e821/markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581", size = 15341, upload-time = "2025-09-27T18:37:16.496Z" }, + { url = "https://files.pythonhosted.org/packages/6f/18/acf23e91bd94fd7b3031558b1f013adfa21a8e407a3fdb32745538730382/markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4", size = 14073, upload-time = "2025-09-27T18:37:17.476Z" }, + { url = "https://files.pythonhosted.org/packages/3c/f0/57689aa4076e1b43b15fdfa646b04653969d50cf30c32a102762be2485da/markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab", size = 11661, upload-time = "2025-09-27T18:37:18.453Z" }, + { url = "https://files.pythonhosted.org/packages/89/c3/2e67a7ca217c6912985ec766c6393b636fb0c2344443ff9d91404dc4c79f/markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175", size = 12069, upload-time = "2025-09-27T18:37:19.332Z" }, + { url = "https://files.pythonhosted.org/packages/f0/00/be561dce4e6ca66b15276e184ce4b8aec61fe83662cce2f7d72bd3249d28/markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634", size = 25670, upload-time = "2025-09-27T18:37:20.245Z" }, + { url = "https://files.pythonhosted.org/packages/50/09/c419f6f5a92e5fadde27efd190eca90f05e1261b10dbd8cbcb39cd8ea1dc/markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50", size = 23598, upload-time = "2025-09-27T18:37:21.177Z" }, + { url = "https://files.pythonhosted.org/packages/22/44/a0681611106e0b2921b3033fc19bc53323e0b50bc70cffdd19f7d679bb66/markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e", size = 23261, upload-time = "2025-09-27T18:37:22.167Z" }, + { url = "https://files.pythonhosted.org/packages/5f/57/1b0b3f100259dc9fffe780cfb60d4be71375510e435efec3d116b6436d43/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5", size = 24835, upload-time = "2025-09-27T18:37:23.296Z" }, + { url = "https://files.pythonhosted.org/packages/26/6a/4bf6d0c97c4920f1597cc14dd720705eca0bf7c787aebc6bb4d1bead5388/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523", size = 22733, upload-time = "2025-09-27T18:37:24.237Z" }, + { url = "https://files.pythonhosted.org/packages/14/c7/ca723101509b518797fedc2fdf79ba57f886b4aca8a7d31857ba3ee8281f/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc", size = 23672, upload-time = "2025-09-27T18:37:25.271Z" }, + { url = "https://files.pythonhosted.org/packages/fb/df/5bd7a48c256faecd1d36edc13133e51397e41b73bb77e1a69deab746ebac/markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d", size = 14819, upload-time = "2025-09-27T18:37:26.285Z" }, + { url = "https://files.pythonhosted.org/packages/1a/8a/0402ba61a2f16038b48b39bccca271134be00c5c9f0f623208399333c448/markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9", size = 15426, upload-time = "2025-09-27T18:37:27.316Z" }, + { url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146, upload-time = "2025-09-27T18:37:28.327Z" }, +] + +[[package]] +name = "psycopg2-binary" +version = "2.9.12" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2a/60/a3624f79acea344c16fbef3a94d28b89a8042ddfb8f3e4ca83f538671409/psycopg2_binary-2.9.12.tar.gz", hash = "sha256:5ac9444edc768c02a6b6a591f070b8aae28ff3a99be57560ac996001580f294c", size = 379686, upload-time = "2026-04-21T09:40:34.304Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d5/19/d4ce60954f3bb9d8e3bc5e5c4d1f2487de2d3851bf2391d54954c9df12a6/psycopg2_binary-2.9.12-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5c8ce6c61bd1b1f6b9c24ee32211599f6166af2c55abb19456090a21fd16554b", size = 3712338, upload-time = "2026-04-20T23:34:03.961Z" }, + { url = "https://files.pythonhosted.org/packages/53/71/c85409ee0d78890f0660eff262e815e7dd2bb741a17611d82e9e8cd9dc5e/psycopg2_binary-2.9.12-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b4a9eaa6e7f4ff91bec10aa3fb296878e75187bced5cc4bafe17dc40915e1326", size = 3822407, upload-time = "2026-04-20T23:34:05.977Z" }, + { url = "https://files.pythonhosted.org/packages/3c/ed/60486c2c7f0d4d1ede2bfb1ed27e2498477ce646bc7f6b2759906303117e/psycopg2_binary-2.9.12-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:c6528cefc8e50fcc6f4a107e27a672058b36cc5736d665476aeb413ba88dbb06", size = 4578425, upload-time = "2026-04-20T23:34:08.246Z" }, + { url = "https://files.pythonhosted.org/packages/0b/b9/656cb03fad9f4f49f2145c334b1126ee75189929ca4e6187d485a2d59951/psycopg2_binary-2.9.12-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e4e184b1fb6072bf05388aa41c697e1b2d01b3473f107e7ec44f186a32cfd0b8", size = 4273709, upload-time = "2026-04-20T23:34:10.974Z" }, + { url = "https://files.pythonhosted.org/packages/99/66/08cf0da0e25cc6fb142c89be45fc8418792858f0c4cbff5e24530ff02cd6/psycopg2_binary-2.9.12-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4766ab678563054d3f1d064a4db19cc4b5f9e3a8d9018592a8285cf200c248f3", size = 5893779, upload-time = "2026-04-20T23:34:13.905Z" }, + { url = "https://files.pythonhosted.org/packages/17/d7/eecd9ce8e146d3721115d82d3836efdbb712187e4590325df549989d18f4/psycopg2_binary-2.9.12-cp311-cp311-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5a0253224780c978746cb9be55a946bcdaf40fe3519c0f622924cdabdafe2c39", size = 4109308, upload-time = "2026-04-20T23:34:16.761Z" }, + { url = "https://files.pythonhosted.org/packages/b6/2e/b1dc289b362cc8d45697b57eefbd673186f49a4ea0906928988e3affcc98/psycopg2_binary-2.9.12-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0dc9228d47c46bda253d2ecd6bb93b56a9f2d7ad33b684a1fa3622bf74ffe30c", size = 3654405, upload-time = "2026-04-20T23:34:19.303Z" }, + { url = "https://files.pythonhosted.org/packages/eb/e4/4c4aea6473214dbdbd0fbba11aa4691e76dc01722c55724c5951719865ff/psycopg2_binary-2.9.12-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f921f3cd87035ef7df233383011d7a53ea1d346224752c1385f1edfd790ceb6a", size = 3299187, upload-time = "2026-04-20T23:34:21.206Z" }, + { url = "https://files.pythonhosted.org/packages/ba/5d/b03b99986446a4f57b170ed9a2579fb7ff9783ca0fa5226b19db99737fee/psycopg2_binary-2.9.12-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:3d999bd982a723113c1a45b55a7a6a90d64d0ed2278020ed625c490ff7bef96c", size = 3047716, upload-time = "2026-04-20T23:34:23.077Z" }, + { url = "https://files.pythonhosted.org/packages/14/86/382ee4afbd1d97500c9d2862b20c2fdeddf4b7335e984df3fb4309f64108/psycopg2_binary-2.9.12-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:29d4d134bd0ab46ffb04e94aa3c5fa3ef582e9026609165e2f758ff76fc3a3be", size = 3349237, upload-time = "2026-04-20T23:34:25.211Z" }, + { url = "https://files.pythonhosted.org/packages/a8/16/9a57c75ba1eda7165c017342f526810d5f5a12647dde749c99ae9a7141d7/psycopg2_binary-2.9.12-cp311-cp311-win_amd64.whl", hash = "sha256:cb4a1dacdd48077150dc762a9e5ddbf32c256d66cb46f80839391aa458774936", size = 2757036, upload-time = "2026-04-20T23:34:27.77Z" }, + { url = "https://files.pythonhosted.org/packages/e2/9f/ef4ef3c8e15083df90ca35265cfd1a081a2f0cc07bb229c6314c6af817f4/psycopg2_binary-2.9.12-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5cdc05117180c5fa9c40eea8ea559ce64d73824c39d928b7da9fb5f6a9392433", size = 3712459, upload-time = "2026-04-20T23:34:30.549Z" }, + { url = "https://files.pythonhosted.org/packages/b5/01/3dd14e46ba48c1e1a6ec58ee599fa1b5efa00c246d5046cd903d0eeb1af1/psycopg2_binary-2.9.12-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d3227a3bc228c10d21011a99245edca923e4e8bf461857e869a507d9a41fe9f6", size = 3822936, upload-time = "2026-04-20T23:34:32.77Z" }, + { url = "https://files.pythonhosted.org/packages/a6/f7/0640e4901119d8a9f7a1784b927f494e2198e213ceb593753d1f2c8b1b30/psycopg2_binary-2.9.12-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:995ce929eede89db6254b50827e2b7fd61e50d11f0b116b29fffe4a2e53c4580", size = 4578676, upload-time = "2026-04-20T23:34:35.18Z" }, + { url = "https://files.pythonhosted.org/packages/b0/55/44df3965b5f297c50cc0b1b594a31c67d6127a9d133045b8a66611b14dfb/psycopg2_binary-2.9.12-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9fe06d93e72f1c048e731a2e3e7854a5bfaa58fc736068df90b352cefe66f03f", size = 4274917, upload-time = "2026-04-20T23:34:37.982Z" }, + { url = "https://files.pythonhosted.org/packages/b0/4b/74535248b1eac0c9336862e8617c765ac94dac76f9e25d7c4a79588c8907/psycopg2_binary-2.9.12-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:40e7b28b63aaf737cb3a1edc3a9bbc9a9f4ad3dcb7152e8c1130e4050eddcb7d", size = 5894843, upload-time = "2026-04-20T23:34:40.856Z" }, + { url = "https://files.pythonhosted.org/packages/f2/ba/f1bf8d2ae71868ad800b661099086ee52bc0f8d9f05be1acd8ebb06757cc/psycopg2_binary-2.9.12-cp312-cp312-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:89d19a9f7899e8eb0656a2b3a08e0da04c720a06db6e0033eab5928aabe60fa9", size = 4110556, upload-time = "2026-04-20T23:34:44.016Z" }, + { url = "https://files.pythonhosted.org/packages/45/46/c15706c338403b7c420bcc0c2905aad116cc064545686d8bf85f1999ea00/psycopg2_binary-2.9.12-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:612b965daee295ae2da8f8218ce1d274645dc76ef3f1abf6a0a94fd57eff876d", size = 3655714, upload-time = "2026-04-20T23:34:46.233Z" }, + { url = "https://files.pythonhosted.org/packages/b3/7c/a2d5dc09b64a4564db242a0fe418fde7d33f6f8259dd2c5b9d7def00fb5a/psycopg2_binary-2.9.12-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:b9a339b79d37c1b45f3235265f07cdeb0cb5ad7acd2ac7720a5920989c17c24e", size = 3301154, upload-time = "2026-04-20T23:34:49.528Z" }, + { url = "https://files.pythonhosted.org/packages/c0/e8/cc8c9a4ce71461f9ec548d38cadc41dc184b34c73e6455450775a9334ccd/psycopg2_binary-2.9.12-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:3471336e1acfd9c7fe507b8bad5af9317b6a89294f9eb37bd9a030bb7bebcdc6", size = 3048882, upload-time = "2026-04-20T23:34:51.86Z" }, + { url = "https://files.pythonhosted.org/packages/19/6a/31e2296bc0787c5ab75d3d118e40b239db8151b5192b90b77c72bc9256e9/psycopg2_binary-2.9.12-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7af18183109e23502c8b2ae7f6926c0882766f35b5175a4cd737ad825e4d7a1b", size = 3351298, upload-time = "2026-04-20T23:34:54.124Z" }, + { url = "https://files.pythonhosted.org/packages/5f/a8/75f4e3e11203b590150abed2cf7794b9c9c9f7eceddae955191138b44dde/psycopg2_binary-2.9.12-cp312-cp312-win_amd64.whl", hash = "sha256:398fcd4db988c7d7d3713e2b8e18939776fd3fb447052daae4f24fa39daede4c", size = 2757230, upload-time = "2026-04-20T23:34:56.242Z" }, + { url = "https://files.pythonhosted.org/packages/91/bb/4608c96f970f6e0c56572e87027ef4404f709382a3503e9934526d7ba051/psycopg2_binary-2.9.12-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7c729a73c7b1b84de3582f73cdd27d905121dc2c531f3d9a3c32a3011033b965", size = 3712419, upload-time = "2026-04-20T23:34:58.754Z" }, + { url = "https://files.pythonhosted.org/packages/5e/af/48f76af9d50d61cf390f8cd657b503168b089e2e9298e48465d029fcc713/psycopg2_binary-2.9.12-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4413d0caef93c5cf50b96863df4c2efe8c269bf2267df353225595e7e15e8df7", size = 3822990, upload-time = "2026-04-20T23:35:00.821Z" }, + { url = "https://files.pythonhosted.org/packages/7a/df/aba0f99397cd811d32e06fc0cc781f1f3ce98bc0e729cb423925085d781a/psycopg2_binary-2.9.12-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:4dfcf8e45ebb0c663be34a3442f65e17311f3367089cd4e5e3a3e8e62c978777", size = 4578696, upload-time = "2026-04-20T23:35:03.409Z" }, + { url = "https://files.pythonhosted.org/packages/95/9c/eaa74021ac4e4d5c2f83d82fc6615a63f4fe6c94dc4e94c3990427053f67/psycopg2_binary-2.9.12-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c41321a14dd74aceb6a9a643b9253a334521babfa763fa873e33d89cfa122fb5", size = 4274982, upload-time = "2026-04-20T23:35:05.583Z" }, + { url = "https://files.pythonhosted.org/packages/35/ed/c25deff98bd26187ba48b3b250a3ffc3037c46c5b89362534a15d200e0db/psycopg2_binary-2.9.12-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:83946ba43979ebfdc99a3cd0ee775c89f221df026984ba19d46133d8d75d3cd9", size = 5894867, upload-time = "2026-04-20T23:35:07.902Z" }, + { url = "https://files.pythonhosted.org/packages/9a/81/8d0e21ca77373c6c9589e5c4528f6e8f0c08c62cafc76fb0bddb7a2cee22/psycopg2_binary-2.9.12-cp313-cp313-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:411e85815652d13560fbe731878daa5d92378c4995a22302071890ec3397d019", size = 4110578, upload-time = "2026-04-20T23:35:10.149Z" }, + { url = "https://files.pythonhosted.org/packages/00/fc/f481e2435bd8f742d0123309174aae4165160ad3ef17c1b99c3622c241d2/psycopg2_binary-2.9.12-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1c8ad4c08e00f7679559eaed7aff1edfffc60c086b976f93972f686384a95e2c", size = 3655816, upload-time = "2026-04-20T23:35:12.56Z" }, + { url = "https://files.pythonhosted.org/packages/53/79/b9f46466bdbe9f239c96cde8be33c1aace4842f06013b47b730dc9759187/psycopg2_binary-2.9.12-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:00814e40fa23c2b37ef0a1e3c749d89982c73a9cb5046137f0752a22d432e82f", size = 3301307, upload-time = "2026-04-20T23:35:15.029Z" }, + { url = "https://files.pythonhosted.org/packages/3f/19/7dc003b32fe35024df89b658104f7c8538a8b2dcbde7a4e746ce929742e7/psycopg2_binary-2.9.12-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:98062447aebc20ed20add1f547a364fd0ef8933640d5372ff1873f8deb9b61be", size = 3048968, upload-time = "2026-04-20T23:35:16.757Z" }, + { url = "https://files.pythonhosted.org/packages/91/58/2dbd7db5c604d45f4950d988506aae672a14126ec22998ced5021cbb76bb/psycopg2_binary-2.9.12-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:66a7685d7e548f10fb4ce32fb01a7b7f4aa702134de92a292c7bd9e0d3dbd290", size = 3351369, upload-time = "2026-04-20T23:35:18.933Z" }, + { url = "https://files.pythonhosted.org/packages/42/ee/dee8dcaad07f735824de3d6563bc67119fa6c28257b17977a8d624f02fab/psycopg2_binary-2.9.12-cp313-cp313-win_amd64.whl", hash = "sha256:b6937f5fe4e180aeee87de907a2fa982ded6f7f15d7218f78a083e4e1d68f2a0", size = 2757347, upload-time = "2026-04-20T23:35:21.283Z" }, + { url = "https://files.pythonhosted.org/packages/13/1b/708c0dca874acfad6d65314271859899a79007686f3a1f74e82a2ed4b645/psycopg2_binary-2.9.12-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:6f3b3de8a74ef8db215f22edffb19e32dc6fa41340456de7ec99efdc8a7b3ec2", size = 3712428, upload-time = "2026-04-20T23:35:23.453Z" }, + { url = "https://files.pythonhosted.org/packages/d6/39/ddbea9d4b4de6aca9431b6ed253f530f8a02d3b8f9bcfd0dbfe2b3de6fe4/psycopg2_binary-2.9.12-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1006fb62f0f0bc5ce256a832356c6262e91be43f5e4eb15b5eaf38079464caf2", size = 3823184, upload-time = "2026-04-20T23:35:25.92Z" }, + { url = "https://files.pythonhosted.org/packages/bf/a0/bc2fef74b106fa345567122a0659e6d94512ed7dc0131ec44c9e5aba3725/psycopg2_binary-2.9.12-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:840066105706cd2eb29b9a1c2329620056582a4bf3e8169dec5c447042d0869f", size = 4579157, upload-time = "2026-04-20T23:35:28.542Z" }, + { url = "https://files.pythonhosted.org/packages/57/d7/d4e3b2005d3de607ca4fbb0e8742e248056e52184a6b94ebda3c1c2c329b/psycopg2_binary-2.9.12-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:863f5d12241ebe1c76a72a04c2113b6dc905f90b9cef0e9be0efd994affd9354", size = 4274970, upload-time = "2026-04-20T23:35:30.418Z" }, + { url = "https://files.pythonhosted.org/packages/2e/42/c9853f8db3967fe08bcde11f53d53b85d351750cae726ce001cb68afa9c1/psycopg2_binary-2.9.12-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a99eaab34a9010f1a086b126de467466620a750634d114d20455f3a824aae033", size = 5895175, upload-time = "2026-04-20T23:35:33.584Z" }, + { url = "https://files.pythonhosted.org/packages/eb/fd/b82b5601a97630308bef079f545ffec481bbbc795c2ba5ec416a01d03f60/psycopg2_binary-2.9.12-cp314-cp314-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ffdd7dc5463ccd61845ac37b7012d0f35a1548df9febe14f8dd549be4a0bc81e", size = 4110658, upload-time = "2026-04-20T23:35:35.638Z" }, + { url = "https://files.pythonhosted.org/packages/62/8c/32ca69b0389ef25dd22937bf9e8fbe2ce27aea20b05ded48c4ce4cb42475/psycopg2_binary-2.9.12-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:54a0dfecab1b48731f934e06139dfe11e24219fb6d0ceb32177cf0375f14c7b5", size = 3656251, upload-time = "2026-04-20T23:35:37.854Z" }, + { url = "https://files.pythonhosted.org/packages/c4/29/96992a2b59e3b9d730fcf9612d0a387305025dc867a9fc490a9e496e074e/psycopg2_binary-2.9.12-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:96937c9c5d891f772430f418a7a8b4691a90c3e6b93cf72b5bd7cad8cbca32a5", size = 3301810, upload-time = "2026-04-20T23:35:39.927Z" }, + { url = "https://files.pythonhosted.org/packages/56/ad/44b06659949b243ae10112cd3b20a197f9bf3e81d5651379b9eb889bfaad/psycopg2_binary-2.9.12-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:77b348775efd4cdab410ec6609d81ccecd1139c90265fa583a7255c8064bc03d", size = 3048977, upload-time = "2026-04-20T23:35:41.806Z" }, + { url = "https://files.pythonhosted.org/packages/1d/f2/10a1bcebadb6aa55e280e1f58975c36a7b560ea525184c7aa4064c466633/psycopg2_binary-2.9.12-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:527e6342b3e44c2f0544f6b8e927d60de7f163f5723b8f1dfa7d2a84298738cd", size = 3351466, upload-time = "2026-04-20T23:35:43.993Z" }, + { url = "https://files.pythonhosted.org/packages/20/be/b732c8418ffa5bcfda002890f5dc4c869fc17db66ff11f53b17cfe44afc0/psycopg2_binary-2.9.12-cp314-cp314-win_amd64.whl", hash = "sha256:f12ae41fcafadb39b2785e64a40f9db05d6de2ac114077457e0e7c597f3af980", size = 2848762, upload-time = "2026-04-20T23:35:46.421Z" }, ] [[package]] @@ -145,43 +199,43 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "six" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload_time = "2024-03-01T18:36:20.211Z" } +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload_time = "2024-03-01T18:36:18.57Z" }, + { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, ] [[package]] name = "python-mimeparse" version = "2.0.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cd/85/c40f2e0b2128905f6c34894be01803c114f2b2efab0e8b4c3dca5e56b999/python_mimeparse-2.0.0.tar.gz", hash = "sha256:5b9a9dcf7aa82465e31bd667f5cb7000604811dce83554f1c8a43693a32cb303", size = 7162, upload_time = "2024-08-25T13:38:14.966Z" } +sdist = { url = "https://files.pythonhosted.org/packages/cd/85/c40f2e0b2128905f6c34894be01803c114f2b2efab0e8b4c3dca5e56b999/python_mimeparse-2.0.0.tar.gz", hash = "sha256:5b9a9dcf7aa82465e31bd667f5cb7000604811dce83554f1c8a43693a32cb303", size = 7162, upload-time = "2024-08-25T13:38:14.966Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/65/d9/1093a9d6d22d04d433003c96b9b1d46741b43fee5b11ece5098297737fce/python_mimeparse-2.0.0-py3-none-any.whl", hash = "sha256:574062a06f2e1d416535c8d3b83ccc6ebe95941e74e2c5939fc010a12e37cc09", size = 5576, upload_time = "2024-08-25T13:38:13.372Z" }, + { url = "https://files.pythonhosted.org/packages/65/d9/1093a9d6d22d04d433003c96b9b1d46741b43fee5b11ece5098297737fce/python_mimeparse-2.0.0-py3-none-any.whl", hash = "sha256:574062a06f2e1d416535c8d3b83ccc6ebe95941e74e2c5939fc010a12e37cc09", size = 5576, upload-time = "2024-08-25T13:38:13.372Z" }, ] [[package]] name = "six" version = "1.17.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload_time = "2024-12-04T17:35:28.174Z" } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload_time = "2024-12-04T17:35:26.475Z" }, + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, ] [[package]] name = "sqlparse" version = "0.5.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e5/40/edede8dd6977b0d3da179a342c198ed100dd2aba4be081861ee5911e4da4/sqlparse-0.5.3.tar.gz", hash = "sha256:09f67787f56a0b16ecdbde1bfc7f5d9c3371ca683cfeaa8e6ff60b4807ec9272", size = 84999, upload_time = "2024-12-10T12:05:30.728Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e5/40/edede8dd6977b0d3da179a342c198ed100dd2aba4be081861ee5911e4da4/sqlparse-0.5.3.tar.gz", hash = "sha256:09f67787f56a0b16ecdbde1bfc7f5d9c3371ca683cfeaa8e6ff60b4807ec9272", size = 84999, upload-time = "2024-12-10T12:05:30.728Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a9/5c/bfd6bd0bf979426d405cc6e71eceb8701b148b16c21d2dc3c261efc61c7b/sqlparse-0.5.3-py3-none-any.whl", hash = "sha256:cf2196ed3418f3ba5de6af7e82c694a9fbdbfecccdfc72e281548517081f16ca", size = 44415, upload_time = "2024-12-10T12:05:27.824Z" }, + { url = "https://files.pythonhosted.org/packages/a9/5c/bfd6bd0bf979426d405cc6e71eceb8701b148b16c21d2dc3c261efc61c7b/sqlparse-0.5.3-py3-none-any.whl", hash = "sha256:cf2196ed3418f3ba5de6af7e82c694a9fbdbfecccdfc72e281548517081f16ca", size = 44415, upload-time = "2024-12-10T12:05:27.824Z" }, ] [[package]] name = "tzdata" version = "2025.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5e/a7/c202b344c5ca7daf398f3b8a477eeb205cf3b6f32e7ec3a6bac0629ca975/tzdata-2025.3.tar.gz", hash = "sha256:de39c2ca5dc7b0344f2eba86f49d614019d29f060fc4ebc8a417896a620b56a7", size = 196772, upload_time = "2025-12-13T17:45:35.667Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5e/a7/c202b344c5ca7daf398f3b8a477eeb205cf3b6f32e7ec3a6bac0629ca975/tzdata-2025.3.tar.gz", hash = "sha256:de39c2ca5dc7b0344f2eba86f49d614019d29f060fc4ebc8a417896a620b56a7", size = 196772, upload-time = "2025-12-13T17:45:35.667Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl", hash = "sha256:06a47e5700f3081aab02b2e513160914ff0694bce9947d6b76ebd6bf57cfc5d1", size = 348521, upload_time = "2025-12-13T17:45:33.889Z" }, + { url = "https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl", hash = "sha256:06a47e5700f3081aab02b2e513160914ff0694bce9947d6b76ebd6bf57cfc5d1", size = 348521, upload-time = "2025-12-13T17:45:33.889Z" }, ] From f24d87e6a9fcb1d6ea9b0b061ccb09a5d9f8b483 Mon Sep 17 00:00:00 2001 From: Andy Reagan Date: Mon, 22 Jun 2026 22:01:57 -0400 Subject: [PATCH 02/16] Add local dev config helper local_config.dev.sh sources the dummy local_config.sh, overrides DJ_DB_NAME to a dedicated 'hedonometer' database, and puts Postgres.app on PATH. Source it instead of local_config.sh for local development: createdb -O andyreagan hedonometer . ./local_config.dev.sh uv run python manage.py migrate uv run python manage.py runserver Co-Authored-By: Claude Opus 4.8 --- local_config.dev.sh | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 local_config.dev.sh diff --git a/local_config.dev.sh b/local_config.dev.sh new file mode 100644 index 00000000..245839ab --- /dev/null +++ b/local_config.dev.sh @@ -0,0 +1,7 @@ +#!/bin/sh +# Local dev overrides (untracked). Source this instead of local_config.sh. +. ./local_config.sh +# Use a dedicated, freshly-created database for local development. +export DJ_DB_NAME="hedonometer" +# Ensure the Postgres.app client/libs are on PATH. +export PATH="/Applications/Postgres.app/Contents/Versions/latest/bin:$PATH" From 1cc80b14a88cc48f927c16ee8f67ab9a62bbddc2 Mon Sep 17 00:00:00 2001 From: Andy Reagan Date: Mon, 22 Jun 2026 22:11:31 -0400 Subject: [PATCH 03/16] Wire in d3-shifterator v7 on the timeseries page Bump the wordshift library from @andyreagan/d3-shifterator@4 to @7. Use the self-contained bundle (shifterator-bundle.js, its own internal d3 v7) rather than the non-bundled build, because timeseries.js still drives the rest of the page with global d3 v4 (callback-style d3.text/d3.csv, d3.event, etc.). The bundle isolates the new shifterator's d3 v7 from the page's d3 v4; the shifter is invoked purely through its stable public API (strings + arrays), so no other changes are needed. Verified: the Dec 25 2022 wordshift renders correctly (200 bars, real prod data fetched cross-origin). When the page is migrated to d3 v7, switch back to the non-bundled dist/shifterator.js sharing the global d3. Co-Authored-By: Claude Opus 4.8 --- hedonometer/templates/hedonometer/indexlang.html | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/hedonometer/templates/hedonometer/indexlang.html b/hedonometer/templates/hedonometer/indexlang.html index bafaeed1..8ecf8146 100644 --- a/hedonometer/templates/hedonometer/indexlang.html +++ b/hedonometer/templates/hedonometer/indexlang.html @@ -140,7 +140,11 @@

Embed

- + + {% endblock %} From 4f991b1033694a1e6b6b27dac68afa6c2fde7fd6 Mon Sep 17 00:00:00 2001 From: Andy Reagan Date: Mon, 22 Jun 2026 22:32:45 -0400 Subject: [PATCH 04/16] Migrate the timeseries page to d3 v7 timeseries.js drove the page on d3 v4; ported it to d3 v7: - async loaders d3.text/d3.csv/d3.json(url, cb) -> promise form d3.X(url).then(cb) (7 call sites); drop the v4 error-first arg on the events d3.json - event listeners: the brush handler uses the passed event (event.selection) instead of the removed global d3.event; the 4 mousedown handlers that read the datum switch from (d, i) to (event, d) - fix a latent bug: `.style(opacity, ...)` (undefined var) -> "opacity" Template (indexlang.html): load d3 v7 (jsDelivr d3@7) instead of d3.v4, and switch the shifterator from the self-contained bundle back to the lean non-bundled build, now that it shares the page's global d3 v7. Verified on d3 7.9.0: timeseries chart, event annotations, brush date-range, the big modal wordshift (200 bars), and the small hover shift (10 bars) all render with no JS errors. Data fetched cross-origin from prod /data (CORS *). Co-Authored-By: Claude Opus 4.8 --- .../static/hedonometer/js/timeseries.js | 31 +++++++++---------- .../templates/hedonometer/indexlang.html | 10 +++--- 2 files changed, 19 insertions(+), 22 deletions(-) diff --git a/hedonometer/static/hedonometer/js/timeseries.js b/hedonometer/static/hedonometer/js/timeseries.js index 765c6b18..8c979004 100644 --- a/hedonometer/static/hedonometer/js/timeseries.js +++ b/hedonometer/static/hedonometer/js/timeseries.js @@ -303,7 +303,7 @@ var intStr0 = ["zero", "one", "two", "three"]; legendgroup.selectAll("circle") .data(weekDaysShort) .enter() - .append("circle").on("mousedown", function(d, i) { + .append("circle").on("mousedown", function(event, d) { var currRange = (x.domain()[1].getTime() - x.domain()[0].getTime()); legendDict.toggle(d, rScale(currRange)); }) @@ -321,7 +321,7 @@ var intStr0 = ["zero", "one", "two", "three"]; .append("text") .attr("x", (d, i) => 15 + legendboxwidth * i) .attr("y", 14) - .on("mousedown", function(d, i) { + .on("mousedown", function(event, d) { var currRange = (x.domain()[1].getTime() - x.domain()[0].getTime()); legendDict.toggle(d, rScale(currRange)); }) @@ -341,7 +341,7 @@ var intStr0 = ["zero", "one", "two", "three"]; .attr("height", 19) .attr("fill", "white") .attr("opacity", "0.0") - .on("mousedown", function(d, i) { + .on("mousedown", function(event, d) { var currRange = (x.domain()[1].getTime() - x.domain()[0].getTime()); legendDict.toggle(d, rScale(currRange)); }); @@ -497,7 +497,7 @@ var intStr0 = ["zero", "one", "two", "three"]; .attr("height", 19) .attr("fill", "white") .attr("opacity", "0.0") - .on("mousedown", function(d, i) { + .on("mousedown", function(event, d) { console.log([x2(d[0]), x2(d[1])]); brushgroup.call(brush.move, [x2(d[0]), x2(d[1])]) var cutoff = bigdayscale(d[1].getTime() - d[0].getTime()); @@ -555,7 +555,7 @@ var intStr0 = ["zero", "one", "two", "three"]; // .attr("visibility", "hidden"); } - function brushed() { + function brushed(event) { // console.log("brushed"); // console.log(d3.event); // console.log(d3.event.selection.map(x2.invert)); @@ -565,7 +565,7 @@ var intStr0 = ["zero", "one", "two", "three"]; // console.log(x2.domain()); // console.log(brush.extent()()); - const currRange = d3.event.selection.map(x2.invert); + const currRange = event.selection.map(x2.invert); console.log(currRange); x.domain(currRange); @@ -698,10 +698,10 @@ var intStr0 = ["zero", "one", "two", "three"]; // parseInt(d3.select("#moveshifthere").style("width")); var modalheight = 495; - d3.text(dataUrl + "/" + directory + "/" + wordVecDir + "/" + cformat(popdate) + "-sum.csv", function(tmp) { + d3.text(dataUrl + "/" + directory + "/" + wordVecDir + "/" + cformat(popdate) + "-sum.csv").then(function(tmp) { const compFvec = tmp.split(',').length > tmp.split('\n').length ? tmp.split(',') : tmp.split('\n'); - d3.text(dataUrl + "/" + directory + "/" + wordVecDir + "/" + cformat(d3.timeDay.offset(popdate, 0)) + "-prev7.csv", function(tmp2) { + d3.text(dataUrl + "/" + directory + "/" + wordVecDir + "/" + cformat(d3.timeDay.offset(popdate, 0)) + "-prev7.csv").then(function(tmp2) { const refFvec = tmp2.split(',').length > tmp2.split('\n').length ? tmp2.split(',') : tmp2.split('\n'); @@ -841,7 +841,7 @@ var intStr0 = ["zero", "one", "two", "three"]; } } - d3.csv(dataUrl + "/" + directory + "/" + shiftDir + "/" + cformat(popdate) + "-shift.csv", function(csv) { + d3.csv(dataUrl + "/" + directory + "/" + shiftDir + "/" + cformat(popdate) + "-shift.csv").then(function(csv) { var names = csv.map(function(d) { return d.word; }); @@ -857,7 +857,7 @@ var intStr0 = ["zero", "one", "two", "three"]; //var x = d3.scaleLinear().domain([-x0, x0]).range([0, 400]); //var y = d3.scaleLinear().domain(d3.range(sizes.length)).range([5, 7]); - d3.csv(dataUrl + "/" + directory + "/" + shiftDir + "/" + cformat(popdate) + "-metashift.csv", function(csv) { + d3.csv(dataUrl + "/" + directory + "/" + shiftDir + "/" + cformat(popdate) + "-metashift.csv").then(function(csv) { var havg = csv.map(function(d) { return d.refH; }); @@ -1071,7 +1071,7 @@ var intStr0 = ["zero", "one", "two", "three"]; .on('mouseover', null) .on('mouseout', function(d) { var rectSelection = d3.select(this).style( - opacity, '0.7' + "opacity", '0.7' ); }); @@ -1111,10 +1111,10 @@ var intStr0 = ["zero", "one", "two", "three"]; console.log("nextDay", update); dateencoder.varval(cformat(update)); - d3.text(dataUrl + "/" + directory + "/" + wordVecDir + "/" + cformat(update) + "-sum.csv", function(tmp) { + d3.text(dataUrl + "/" + directory + "/" + wordVecDir + "/" + cformat(update) + "-sum.csv").then(function(tmp) { const compFvec = tmp.split(',').length > tmp.split('\n').length ? tmp.split(',') : tmp.split('\n'); - d3.text(dataUrl + "/" + directory + "/" + wordVecDir + "/" + cformat(d3.timeDay.offset(update, 0)) + "-prev7.csv", function(tmp2) { + d3.text(dataUrl + "/" + directory + "/" + wordVecDir + "/" + cformat(d3.timeDay.offset(update, 0)) + "-prev7.csv").then(function(tmp2) { const refFvec = tmp2.split(',').length > tmp2.split('\n').length ? tmp2.split(',') : tmp2.split('\n'); @@ -1445,9 +1445,8 @@ var intStr0 = ["zero", "one", "two", "three"]; var format = d3.timeFormat("%m-%d"); - d3.json('/api/v1/events/?format=json&happs__timeseries__title=' + title, - function(err, json) { - console.log(err); + d3.json('/api/v1/events/?format=json&happs__timeseries__title=' + title) + .then(function(json) { console.log(json); bigdays = json.objects.map(function(d) { d.date = cparse(d.happs.date); diff --git a/hedonometer/templates/hedonometer/indexlang.html b/hedonometer/templates/hedonometer/indexlang.html index 8ecf8146..0f491442 100644 --- a/hedonometer/templates/hedonometer/indexlang.html +++ b/hedonometer/templates/hedonometer/indexlang.html @@ -135,16 +135,14 @@

Embed

{% block bottomjs %} - + - - + + {% endblock %} From 46e22860210b7d416e594da62411187ce7d60968 Mon Sep 17 00:00:00 2001 From: Andy Reagan Date: Tue, 30 Jun 2026 21:12:24 -0400 Subject: [PATCH 05/16] Migrate maps page to hedotools v7 + d3 v7 Template: local d3.js/hedotools.*/topojson -> CDN d3@7 + topojson-client@3 + d3-shifterator@7 + @andyreagan/hedotools@7 modules (init.v4, urllib, shifter, lens, map, barchart). Kept local urllib.js (glue uses d3.urllib) and the pure computeHapps helper (not shipped in hedotools@7). maps-on-load.js: d3.text/json(url,cb) -> .then(); click/mousedown handlers (d,i) -> (event,d) with index recovered from the datum / DOM position. Two hedotools@7 gotchas worked around here (see report): - lens/map/barchart are shipped as factories (missing the IIFE call that shifter has), so instantiate defensively at glue top. - lens uses hedotools.urllib, so load hedotools.urllib.js alongside. Verified on d3 7.9.0: choropleth (51 states), lens, wordshift (200 bars), and state-ranking barchart all render, no JS errors. Co-Authored-By: Claude Opus 4.8 --- .../static/hedonometer/js/maps-on-load.js | 32 ++++++++++++------- hedonometer/templates/hedonometer/maps.html | 18 +++++++---- 2 files changed, 31 insertions(+), 19 deletions(-) diff --git a/hedonometer/static/hedonometer/js/maps-on-load.js b/hedonometer/static/hedonometer/js/maps-on-load.js index 24736095..64068570 100644 --- a/hedonometer/static/hedonometer/js/maps-on-load.js +++ b/hedonometer/static/hedonometer/js/maps-on-load.js @@ -1,3 +1,11 @@ +// hedotools@7 ships lens/map/barchart as factories (missing the IIFE call that +// shifter/the old local modules have), so hedotools.lens etc. are functions, not +// the singleton instances this glue drives. Instantiate them once. Defensive +// (only if still a function) so it also works once hedotools ships them invoked. +["lens", "map", "barchart"].forEach(function(m) { + if (hedotools[m] && typeof hedotools[m] === "function") { hedotools[m] = hedotools[m](); } +}); + // begin with some helper functions // http://stackoverflow.com/a/1026087/3780153 function capitaliseFirstLetter(string) @@ -35,7 +43,7 @@ var stateSelType = true; var activeHover = true; // until a selection is fixed, let this be true -d3.selectAll(".selbutton").data([false,true]).on("mousedown",function(d,i) { +d3.selectAll(".selbutton").data([false,true]).on("mousedown",function(event, d) { var i = d ? 1 : 0; if (stateSelType !== d) { stateSelType = d; activeHover = true; @@ -130,7 +138,7 @@ timeFrameText = ["2013","2012","2011"]; function refcompdrops() { d3.select("#compSelect").selectAll("a") - .on("click", function(d,i) { + .on("click", function(event, d) { // console.log(i); d3.selectAll(".state").attr("stroke-width",0.7); activeHover = true; @@ -152,7 +160,7 @@ function refcompdrops() { }); d3.select("#refSelect").selectAll("a") - .on("click", function(d,i) { + .on("click", function(event, d) { // console.log(i); d3.selectAll(".state").attr("stroke-width",0.7); activeHover = true; @@ -174,7 +182,7 @@ function refcompdrops() { }); d3.select("#rotate") - .on("click", function(d,i) { + .on("click", function(event, d) { // console.log(i); d3.selectAll(".state").attr("stroke-width",0.7); activeHover = true; @@ -197,9 +205,9 @@ function refcompdrops() { function timeDrop() { d3.select("#timeSelect").selectAll("a") - .on("click", function(d,i) { + .on("click", function(event, d) { // key = this.selectedIndex; - key = i; + key = Array.prototype.indexOf.call(document.querySelectorAll("#timeSelect a"), this); timeName = timeFrames[key]; d3.select(".timelabel").text(timeFrameText[key]); timeselencoder.varval(timeFrameText[key]); @@ -215,12 +223,12 @@ function loadCsv(time) { console.log("loading year words and scores"); var scoresFile = "https://hedonometer.org/data/geodata/wordScores.csv"; var wordsFile = "https://hedonometer.org/data/geodata/words.csv"; - d3.text(scoresFile, function(text) { + d3.text(scoresFile).then(function(text) { var tmp = text.split(","); lens = tmp.map(parseFloat); if (!--csvLoadsRemaining) initializePlotPlot(lens,words); }); - d3.text(wordsFile, function(text) { + d3.text(wordsFile).then(function(text) { var tmp = text.split(","); words = tmp; if (!--csvLoadsRemaining) initializePlotPlot(lens,words); @@ -230,7 +238,7 @@ function loadCsv(time) { // load labMT files var scoresFile = "https://hedonometer.org/data/labMT/labMTscores-english.csv"; var wordsFile = "https://hedonometer.org/data/labMT/labMTwords-english.csv"; - d3.text(scoresFile, function(text) { + d3.text(scoresFile).then(function(text) { var tmp = text.split("\n"); //console.log(tmp.length); //console.log(tmp[tmp.length-1]); @@ -243,7 +251,7 @@ function loadCsv(time) { } if (!--csvLoadsRemaining) initializePlotPlot(lens,words); }); - d3.text(wordsFile, function(text) { + d3.text(wordsFile).then(function(text) { var tmp = text.split("\n"); words = tmp; var len = words.length - 1; @@ -255,13 +263,13 @@ function loadCsv(time) { if (!--csvLoadsRemaining) initializePlotPlot(lens,words); }); } - d3.json("https://hedonometer.org/data/geodata/us-states.topojson", function(data) { + d3.json("https://hedonometer.org/data/geodata/us-states.topojson").then(function(data) { geoJson = data; stateFeatures = topojson.feature(geoJson,geoJson.objects.states).features; if (!--csvLoadsRemaining) initializePlotPlot(lens,words); }); // trying to load from a new format for the more recent tweets - d3.text("https://hedonometer.org/data/geodata/wordCounts"+(time)+".csv", function(text) { + d3.text("https://hedonometer.org/data/geodata/wordCounts"+(time)+".csv").then(function(text) { // var time = "2014-08-25-week" // d3.text("https://hedonometer.org/data/geodata/combined-word-vectors/"+(time)+".csv", function(text) { tmp = text.split("\n"); diff --git a/hedonometer/templates/hedonometer/maps.html b/hedonometer/templates/hedonometer/maps.html index b2275de2..2db03d13 100644 --- a/hedonometer/templates/hedonometer/maps.html +++ b/hedonometer/templates/hedonometer/maps.html @@ -280,15 +280,19 @@
Word Shift:
- + - - - - - + + + + + + + + + - + {% endblock %} From 783016466e9d326a6c18c1fe714df271c945937d Mon Sep 17 00:00:00 2001 From: Andy Reagan Date: Tue, 30 Jun 2026 21:22:10 -0400 Subject: [PATCH 06/16] Migrate sankey pages (state + city) to hedotools v7 + d3 v7 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit statesankey.html + citysankey.html: local d3.js/topojson/hedotools.* -> CDN d3@7 + topojson-client@3 + d3-shifterator@7 + hedotools@7 (init.v4, urllib, shifter, lens, sankey). Dropped local sankey.js (hedotools.sankey@7 is self-contained; glue never calls d3.sankey). Kept local jquery/bootstrap/ urllib.js + computeHapps. Glue (sankey-on-load.js, city-sankey-on-load.js): factory shim for lens/sankey; loaders -> .then(); (d,i) handlers -> (event,d) with index recovery. Data fix: statesankey's combined-word-vectors/*.csv are 404 (dir removed) — under d3 v7 .then() that rejects and blocks render, so repointed to the existing yearly wordCounts2013/2012.csv (same 51-state format). NOTE: this changes statesankey from last-7-days-vs-prior to 2013-vs-2012; heading text unchanged. Verified: statesankey (51 sankey paths + shift + lens) and citysankey (101 paths) render, no JS errors. Co-Authored-By: Claude Opus 4.8 --- .../hedonometer/js/city-sankey-on-load.js | 48 +++++++++++-------- .../static/hedonometer/js/sankey-on-load.js | 38 +++++++++------ .../templates/hedonometer/citysankey.html | 12 +++-- .../templates/hedonometer/statesankey.html | 18 ++++--- 4 files changed, 70 insertions(+), 46 deletions(-) diff --git a/hedonometer/static/hedonometer/js/city-sankey-on-load.js b/hedonometer/static/hedonometer/js/city-sankey-on-load.js index e6f6205d..831e3b41 100644 --- a/hedonometer/static/hedonometer/js/city-sankey-on-load.js +++ b/hedonometer/static/hedonometer/js/city-sankey-on-load.js @@ -1,4 +1,12 @@ -hedotools.sankeyoncall = function() { +// hedotools@7 ships sankey as a factory (missing the IIFE call that +// shifter/the old local modules have), so hedotools.sankey is a function, not +// the singleton instance this glue drives. Instantiate it once. Defensive +// (only if still a function) so it also works once hedotools ships it invoked. +["sankey"].forEach(function(m) { + if (hedotools[m] && typeof hedotools[m] === "function") { hedotools[m] = hedotools[m](); } +}); + +hedotools.sankeyoncall = function() { var test = function(i,data) { console.log("set on call"); } @@ -51,47 +59,47 @@ function initializePlot() { function timeDrop() { d3.select("#refSelect").selectAll("a") - .on("click", function(d,i) { - key = i; + .on("click", function(event, d) { + key = Array.prototype.indexOf.call(document.querySelectorAll("#refSelect a"), this); d3.select(".reftimelabel").html("All tweets from "+timeFrames[key]+' '); d3.select(".reftimelabelbottom").html("All tweets from "+timeFrames[key]+' '); reftimeselencoder.varval(timeFrames[key]); - d3.text("https://hedonometer.org/data/cities/cityList_"+(reftimeseldecoder().cached)+"_top100Happs.csv", function(text) { + d3.text("https://hedonometer.org/data/cities/cityList_"+(reftimeseldecoder().cached)+"_top100Happs.csv").then(function(text) { ref = text.split("\n").slice(0,304); hedotools.sankey.setdata(ref,comp,cities).replot(); }); }); d3.select("#compSelect").selectAll("a") - .on("click", function(d,i) { + .on("click", function(event, d) { // key = this.selectedIndex; - key = i; + key = Array.prototype.indexOf.call(document.querySelectorAll("#compSelect a"), this); d3.select(".comptimelabel").html("All tweets from "+timeFrames[key]+' '); d3.select(".comptimelabelbottom").html("All tweets from "+timeFrames[key]+' '); comptimeselencoder.varval(timeFrames[key]); - d3.text("https://hedonometer.org/data/cities/cityList_"+(comptimeseldecoder().cached)+"_top100Happs.csv", function(text) { + d3.text("https://hedonometer.org/data/cities/cityList_"+(comptimeseldecoder().cached)+"_top100Happs.csv").then(function(text) { comp = text.split("\n").slice(0,304); hedotools.sankey.setdata(ref,comp,cities).replot(); }); }); d3.select("#refSelectBottom").selectAll("a") - .on("click", function(d,i) { - key = i; + .on("click", function(event, d) { + key = Array.prototype.indexOf.call(document.querySelectorAll("#refSelectBottom a"), this); d3.select(".reftimelabel").html("All tweets from "+timeFrames[key]+' '); d3.select(".reftimelabelbottom").html("All tweets from "+timeFrames[key]+' '); reftimeselencoder.varval(timeFrames[key]); - d3.text("https://hedonometer.org/data/cities/cityList_"+(reftimeseldecoder().cached)+"_top100Happs.csv", function(text) { + d3.text("https://hedonometer.org/data/cities/cityList_"+(reftimeseldecoder().cached)+"_top100Happs.csv").then(function(text) { ref = text.split("\n").slice(0,304); hedotools.sankey.setdata(ref,comp,cities).replot(); }); }); d3.select("#compSelectBottom").selectAll("a") - .on("click", function(d,i) { + .on("click", function(event, d) { // key = this.selectedIndex; - key = i; + key = Array.prototype.indexOf.call(document.querySelectorAll("#compSelectBottom a"), this); d3.select(".comptimelabel").html("All tweets from "+timeFrames[key]+' '); d3.select(".comptimelabelbottom").html("All tweets from "+timeFrames[key]+' '); comptimeselencoder.varval(timeFrames[key]); - d3.text("https://hedonometer.org/data/cities/cityList_"+(comptimeseldecoder().cached)+"_top100Happs.csv", function(text) { + d3.text("https://hedonometer.org/data/cities/cityList_"+(comptimeseldecoder().cached)+"_top100Happs.csv").then(function(text) { comp = text.split("\n").slice(0,304); hedotools.sankey.setdata(ref,comp,cities).replot(); }); @@ -100,15 +108,15 @@ function timeDrop() { function loadCsv() { var csvLoadsRemaining = 3; - d3.text("https://hedonometer.org/data/cities/cityList_"+(reftimeseldecoder().cached)+"_top100Happs.csv", function(text) { + d3.text("https://hedonometer.org/data/cities/cityList_"+(reftimeseldecoder().cached)+"_top100Happs.csv").then(function(text) { ref = text.split("\n").slice(0,304); if (!--csvLoadsRemaining) initializePlotPlot(); }); - d3.text("https://hedonometer.org/data/cities/cityList_"+(comptimeseldecoder().cached)+"_top100Happs.csv", function(text) { + d3.text("https://hedonometer.org/data/cities/cityList_"+(comptimeseldecoder().cached)+"_top100Happs.csv").then(function(text) { comp = text.split("\n").slice(0,304);; if (!--csvLoadsRemaining) initializePlotPlot(); }); - d3.text("https://hedonometer.org/data/cities/mutualCities.csv", function(text) { + d3.text("https://hedonometer.org/data/cities/mutualCities.csv").then(function(text) { cities = text.split("\n").slice(0,304);; if (!--csvLoadsRemaining) initializePlotPlot(); }); @@ -153,12 +161,12 @@ function initializePlotPlot() { var refF; var compF; if ( refshifttimedecoder().cached.length > 0) { - d3.text(reffile,function(text) { + d3.text(reffile).then(function(text) { refF = text.split(","); console.log(refF); if (!--popupshiftct) drawShift(); }); - d3.text(compfile,function(text) { + d3.text(compfile).then(function(text) { compF = text.split(","); console.log(compF); if (!--popupshiftct) drawShift(); @@ -168,7 +176,7 @@ function initializePlotPlot() { // set some data var scoresFile = "https://hedonometer.org/data/labMT/labMTscores-english.csv"; var wordsFile = "https://hedonometer.org/data/labMT/labMTwords-english.csv"; - d3.text(scoresFile, function(text) { + d3.text(scoresFile).then(function(text) { var tmp = text.split("\n"); console.log("loaded words"); //console.log(tmp[tmp.length-1]); @@ -182,7 +190,7 @@ function initializePlotPlot() { hedotools.shifter._lens(lens); if (!--popupshiftct) drawShift(); }); - d3.text(wordsFile, function(text) { + d3.text(wordsFile).then(function(text) { var tmp = text.split("\n"); var words = tmp; var len = words.length - 1; diff --git a/hedonometer/static/hedonometer/js/sankey-on-load.js b/hedonometer/static/hedonometer/js/sankey-on-load.js index 0d7d26e6..4501946b 100644 --- a/hedonometer/static/hedonometer/js/sankey-on-load.js +++ b/hedonometer/static/hedonometer/js/sankey-on-load.js @@ -1,5 +1,13 @@ +// hedotools@7 ships lens/sankey as factories (missing the IIFE call that +// shifter/the old local modules have), so hedotools.lens etc. are functions, not +// the singleton instances this glue drives. Instantiate them once. Defensive +// (only if still a function) so it also works once hedotools ships them invoked. +["lens", "sankey"].forEach(function(m) { + if (hedotools[m] && typeof hedotools[m] === "function") { hedotools[m] = hedotools[m](); } +}); + // override the lensoncall module -hedotools.lensoncall = function() { +hedotools.lensoncall = function() { var test = function() { // reset console.log(lensExtent); @@ -80,7 +88,7 @@ var stateSelType = true; var activeHover = true; // until a selection is fixed, let this be true -d3.selectAll(".selbutton").data([false,true]).on("mousedown",function(d,i) { +d3.selectAll(".selbutton").data([false,true]).on("mousedown",function(event, d) { var i = d ? 1 : 0; if (stateSelType !== d) { stateSelType = d; activeHover = true; @@ -170,9 +178,9 @@ timeFrameText = ["Last 90 Days","Last 30 Days","Last 7 Days"]; function timeDrop() { d3.select("#timeSelect").selectAll("a") - .on("click", function(d,i) { + .on("click", function(event, d) { // key = this.selectedIndex; - key = i; + key = Array.prototype.indexOf.call(document.querySelectorAll("#timeSelect a"), this); timeName = timeFrames[key]; d3.select(".timelabel").text(timeFrameText[key]); timeselencoder.varval(timeFrameText[key]); @@ -186,7 +194,7 @@ function loadCsv(time) { // load labMT files var scoresFile = "https://hedonometer.org/data/labMT/labMTscores-english.csv"; var wordsFile = "https://hedonometer.org/data/labMT/labMTwords-english.csv"; - d3.text(scoresFile, function(text) { + d3.text(scoresFile).then(function(text) { var tmp = text.split("\n"); //console.log(tmp.length); //console.log(tmp[tmp.length-1]); @@ -199,7 +207,7 @@ function loadCsv(time) { } if (!--csvLoadsRemaining) initializePlotPlot(lens,words); }); - d3.text(wordsFile, function(text) { + d3.text(wordsFile).then(function(text) { var tmp = text.split("\n"); words = tmp; var len = words.length - 1; @@ -210,15 +218,16 @@ function loadCsv(time) { } if (!--csvLoadsRemaining) initializePlotPlot(lens,words); }); - d3.json("https://hedonometer.org/data/geodata/us-states.topojson", function(data) { + d3.json("https://hedonometer.org/data/geodata/us-states.topojson").then(function(data) { geoJson = data; stateFeatures = topojson.feature(geoJson,geoJson.objects.states).features; if (!--csvLoadsRemaining) initializePlotPlot(lens,words); }); - // trying to load from a new format for the more recent tweets - // d3.text("https://hedonometer.org/data/geodata/wordCounts"+(time)+".csv", function(text) { - var time = "2014-08-25-week" - d3.text("https://hedonometer.org/data/geodata/combined-word-vectors/"+(time)+".csv", function(text) { + // The dated combined-word-vectors CSVs (e.g. 2014-08-25-week) are gone from the + // server (404). Repointed to the yearly state wordCounts CSVs, which exist and + // use the same 51-state comma-separated row format, so the sankey renders. + var time = "2013" + d3.text("https://hedonometer.org/data/geodata/wordCounts"+(time)+".csv").then(function(text) { tmp = text.split("\n"); allData = Array(52); for (var i=0; i<51; i++) { @@ -250,9 +259,10 @@ function initializePlotPlot(lens,words) { // randomHapps = stateHappsList.map(function(d) { return d+(Math.random()-0.5)/5; } ) - // the previous week - var time = "2014-08-18-week" - d3.text("https://hedonometer.org/data/geodata/combined-word-vectors/"+(time)+".csv", function(text) { + // the previous period (see note above: yearly wordCounts stand in for the + // removed dated combined-word-vectors data) + var time = "2012" + d3.text("https://hedonometer.org/data/geodata/wordCounts"+(time)+".csv").then(function(text) { var tmp = text.split("\n"); allDataOld = Array(52); diff --git a/hedonometer/templates/hedonometer/citysankey.html b/hedonometer/templates/hedonometer/citysankey.html index 9dea3aa1..69b11297 100644 --- a/hedonometer/templates/hedonometer/citysankey.html +++ b/hedonometer/templates/hedonometer/citysankey.html @@ -124,12 +124,14 @@

Interactive Wordshift

- + - - - - + + + + + + {% endblock %} diff --git a/hedonometer/templates/hedonometer/statesankey.html b/hedonometer/templates/hedonometer/statesankey.html index 46084b6f..17ac0486 100644 --- a/hedonometer/templates/hedonometer/statesankey.html +++ b/hedonometer/templates/hedonometer/statesankey.html @@ -93,15 +93,19 @@

- + - - - - - + + + + + + + + - + + {% endblock %} From 6aea15835425fe641bb0740d542041ee3c1afdce Mon Sep 17 00:00:00 2001 From: Andy Reagan Date: Tue, 30 Jun 2026 21:23:17 -0400 Subject: [PATCH 07/16] Migrate list pages (nyt/cbs/outside) to hedotools v7 + d3 v7 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit nytlist/cbslist/outsidelist/outside templates: local d3.js/hedotools.* -> CDN d3@7 + d3-shifterator@7 + hedotools@7 (init.v4, urllib, shifter, lens, barchart). Kept local jquery/bootstrap/urllib.js/computeHapps. Glue (nyt/cbs/outside-on-load.js): factory shim for lens/barchart; loaders -> .then(); dropdown/rotate/selbutton handlers (d,i) -> (event,d) (the are data-bound so d is the datum). hedotools@7 regression handled: the v7 barchart module dropped the mousedown handlers that invoked barchartonclick, so outside's click-article-to-open was dead — re-wired a click handler on the bars in outside-on-load.js. (Flag for the hedotools repo.) Routing note: /showcase/outside/ renders outside.html (outsidelist.html is an orphan template, no route). Live routes verified: /showcase/{nyt,cbs,outside}/ render (25/3/50 barchart bars + 212 shift rects), zero JS errors. Co-Authored-By: Claude Opus 4.8 --- .../static/hedonometer/js/cbs-on-load.js | 40 +++++++++------- .../static/hedonometer/js/nyt-on-load.js | 40 +++++++++------- .../static/hedonometer/js/outside-on-load.js | 47 ++++++++++++------- .../templates/hedonometer/cbslist.html | 12 +++-- .../templates/hedonometer/nytlist.html | 12 +++-- .../templates/hedonometer/outside.html | 12 +++-- .../templates/hedonometer/outsidelist.html | 12 +++-- 7 files changed, 107 insertions(+), 68 deletions(-) diff --git a/hedonometer/static/hedonometer/js/cbs-on-load.js b/hedonometer/static/hedonometer/js/cbs-on-load.js index 1823e7d6..5ce84cd5 100644 --- a/hedonometer/static/hedonometer/js/cbs-on-load.js +++ b/hedonometer/static/hedonometer/js/cbs-on-load.js @@ -1,3 +1,11 @@ +// hedotools@7 ships lens/barchart as factories (missing the IIFE call), so +// hedotools.lens/barchart are functions, not the singleton instances this glue +// drives. Instantiate them once. Defensive (only if still a function) so it +// also works once hedotools ships them invoked. (shifter is already an instance.) +["lens", "barchart"].forEach(function(m) { + if (hedotools[m] && typeof hedotools[m] === "function") { hedotools[m] = hedotools[m](); } +}); + // we need a decent amount of documentation here! // or any // there are a bunch of global variables so I'm going to put them all here: @@ -93,26 +101,26 @@ function sectionIndex(name) { var refcompdrops = function() { d3.select("#compSelect").selectAll("a") - .on("click", function(d,i) { - shiftComp = sectionIndex(sectionListWAllFirst[i].genre); - d3.select(".complabel").text(sectionListWAllFirst[i].genre); - compencoder.varval(sectionListWAllFirst[i].genre); + .on("click", function(event, d) { + shiftComp = sectionIndex(d.genre); + d3.select(".complabel").text(d.genre); + compencoder.varval(d.genre); if (shiftRef !== shiftComp) { drawShift(); } }); d3.select("#refSelect").selectAll("a") - .on("click", function(d,i) { + .on("click", function(event, d) { // console.log(i); - shiftRef = sectionIndex(sectionListWAllFirst[i].genre); - d3.select(".reflabel").text(sectionListWAllFirst[i].genre); - refencoder.varval(sectionListWAllFirst[i].genre); + shiftRef = sectionIndex(d.genre); + d3.select(".reflabel").text(d.genre); + refencoder.varval(d.genre); if (shiftRef !== shiftComp) { drawShift(); } }); d3.select("#rotate") - .on("click", function(d,i) { + .on("click", function(event) { var tmp = shiftComp; shiftComp = shiftRef; shiftRef = tmp; @@ -133,7 +141,7 @@ function loadCsv() { var allLoadsRemaining = listLoadsRemaining+shiftLoadsRemaining; var scoresFile = "https://hedonometer.org/data/labMT/labMTscores-english.csv"; var wordsFile = "https://hedonometer.org/data/labMT/labMTwords-english.csv"; - d3.text(scoresFile, function(text) { + d3.text(scoresFile).then(function(text) { var tmp = text.split("\n"); lens = tmp.map(parseFloat); var len = lens.length - 1; @@ -144,7 +152,7 @@ function loadCsv() { hedotools.shifter._lens(lens); if (!--allLoadsRemaining) initializeBoth(); }); - d3.text(wordsFile, function(text) { + d3.text(wordsFile).then(function(text) { var tmp = text.split("\n"); words = tmp; var len = words.length - 1; @@ -155,11 +163,11 @@ function loadCsv() { hedotools.shifter._words(words); if (!--allLoadsRemaining) initializeBoth(); }); - d3.json("https://hedonometer.org/data/CBS/hosts.json", function(json) { + d3.json("https://hedonometer.org/data/CBS/hosts.json").then(function(json) { sectionList = json; if (!--allLoadsRemaining) initializeBoth(); }); - d3.json("https://hedonometer.org/data/CBS/all.json", function(json) { + d3.json("https://hedonometer.org/data/CBS/all.json").then(function(json) { allEntry = json; if (!--allLoadsRemaining) initializeBoth(); }); @@ -233,12 +241,12 @@ var drawShift = function() { d3.select("#embedtextarea").html(""); - d3.text(refFile,function(text) { + d3.text(refFile).then(function(text) { refF = text.split("\n"); console.log(refF); if (!--finalLoadsRemaining) drawShiftInternal(); }); - d3.text(compFile,function(text) { + d3.text(compFile).then(function(text) { compF = text.split("\n"); if (!--finalLoadsRemaining) drawShiftInternal(); }); @@ -250,7 +258,7 @@ function initializePlot() { d3.select(".reflabel").text(refdecoder().cached); d3.select(".complabel").text(compdecoder().cached); - d3.selectAll(".selbutton").data([false,true]).on("mousedown",function(d,i) { + d3.selectAll(".selbutton").data([false,true]).on("mousedown",function(event, d) { if (selType !== d) { selType = d; d3.select(".selbutton.one").attr("class","btn btn-default btn-xs pull-right selbutton one") diff --git a/hedonometer/static/hedonometer/js/nyt-on-load.js b/hedonometer/static/hedonometer/js/nyt-on-load.js index beb02f0f..28d3cba3 100644 --- a/hedonometer/static/hedonometer/js/nyt-on-load.js +++ b/hedonometer/static/hedonometer/js/nyt-on-load.js @@ -1,3 +1,11 @@ +// hedotools@7 ships lens/barchart as factories (missing the IIFE call), so +// hedotools.lens/barchart are functions, not the singleton instances this glue +// drives. Instantiate them once. Defensive (only if still a function) so it +// also works once hedotools ships them invoked. (shifter is already an instance.) +["lens", "barchart"].forEach(function(m) { + if (hedotools[m] && typeof hedotools[m] === "function") { hedotools[m] = hedotools[m](); } +}); + // we need a decent amount of documentation here! // or any // there are a bunch of global variables so I'm going to put them all here: @@ -93,26 +101,26 @@ function sectionIndex(name) { var refcompdrops = function() { d3.select("#compSelect").selectAll("a") - .on("click", function(d,i) { - shiftComp = sectionIndex(sectionListWAllFirst[i].genre); - d3.select(".complabel").text(sectionListWAllFirst[i].genre); - compencoder.varval(sectionListWAllFirst[i].genre); + .on("click", function(event, d) { + shiftComp = sectionIndex(d.genre); + d3.select(".complabel").text(d.genre); + compencoder.varval(d.genre); if (shiftRef !== shiftComp) { drawShift(); } }); d3.select("#refSelect").selectAll("a") - .on("click", function(d,i) { + .on("click", function(event, d) { // console.log(i); - shiftRef = sectionIndex(sectionListWAllFirst[i].genre); - d3.select(".reflabel").text(sectionListWAllFirst[i].genre); - refencoder.varval(sectionListWAllFirst[i].genre); + shiftRef = sectionIndex(d.genre); + d3.select(".reflabel").text(d.genre); + refencoder.varval(d.genre); if (shiftRef !== shiftComp) { drawShift(); } }); d3.select("#rotate") - .on("click", function(d,i) { + .on("click", function(event) { var tmp = shiftComp; shiftComp = shiftRef; shiftRef = tmp; @@ -133,7 +141,7 @@ function loadCsv() { var allLoadsRemaining = listLoadsRemaining+shiftLoadsRemaining; var scoresFile = "https://hedonometer.org/data/labMT/labMTscores-english.csv"; var wordsFile = "https://hedonometer.org/data/labMT/labMTwords-english.csv"; - d3.text(scoresFile, function(text) { + d3.text(scoresFile).then(function(text) { var tmp = text.split("\n"); lens = tmp.map(parseFloat); var len = lens.length - 1; @@ -144,7 +152,7 @@ function loadCsv() { hedotools.shifter._lens(lens); if (!--allLoadsRemaining) initializeBoth(); }); - d3.text(wordsFile, function(text) { + d3.text(wordsFile).then(function(text) { var tmp = text.split("\n"); words = tmp; var len = words.length - 1; @@ -155,11 +163,11 @@ function loadCsv() { hedotools.shifter._words(words); if (!--allLoadsRemaining) initializeBoth(); }); - d3.json("https://hedonometer.org/api/v1/nyt/?format=json", function(json) { + d3.json("https://hedonometer.org/api/v1/nyt/?format=json").then(function(json) { sectionList = json.objects; if (!--allLoadsRemaining) initializeBoth(); }); - d3.json("https://hedonometer.org/api/v1/nytall/?format=json&genre=All", function(json) { + d3.json("https://hedonometer.org/api/v1/nytall/?format=json&genre=All").then(function(json) { allEntry = json.objects; if (!--allLoadsRemaining) initializeBoth(); }); @@ -233,12 +241,12 @@ var drawShift = function() { d3.select("#embedtextarea").html(""); - d3.text(refFile,function(text) { + d3.text(refFile).then(function(text) { refF = text.split("\n"); console.log(refF); if (!--finalLoadsRemaining) drawShiftInternal(); }); - d3.text(compFile,function(text) { + d3.text(compFile).then(function(text) { compF = text.split("\n"); if (!--finalLoadsRemaining) drawShiftInternal(); }); @@ -250,7 +258,7 @@ function initializePlot() { d3.select(".reflabel").text(refdecoder().cached); d3.select(".complabel").text(compdecoder().cached); - d3.selectAll(".selbutton").data([false,true]).on("mousedown",function(d,i) { + d3.selectAll(".selbutton").data([false,true]).on("mousedown",function(event, d) { if (selType !== d) { selType = d; d3.select(".selbutton.one").attr("class","btn btn-default btn-xs pull-right selbutton one") diff --git a/hedonometer/static/hedonometer/js/outside-on-load.js b/hedonometer/static/hedonometer/js/outside-on-load.js index 24d3e640..5e984e26 100644 --- a/hedonometer/static/hedonometer/js/outside-on-load.js +++ b/hedonometer/static/hedonometer/js/outside-on-load.js @@ -1,3 +1,11 @@ +// hedotools@7 ships lens/barchart as factories (missing the IIFE call), so +// hedotools.lens/barchart are functions, not the singleton instances this glue +// drives. Instantiate them once. Defensive (only if still a function) so it +// also works once hedotools ships them invoked. (shifter is already an instance.) +["lens", "barchart"].forEach(function(m) { + if (hedotools[m] && typeof hedotools[m] === "function") { hedotools[m] = hedotools[m](); } +}); + // we need a decent amount of documentation here! // or any // there are a bunch of global variables so I'm going to put them all here: @@ -103,26 +111,26 @@ function sectionIndex(name) { var refcompdrops = function() { d3.select("#compSelect").selectAll("a") - .on("click", function(d,i) { - shiftComp = sectionIndex(sectionListWAllFirst[i].title); - d3.select(".complabel").text(sectionListWAllFirst[i].title); - compencoder.varval(sectionListWAllFirst[i].title); + .on("click", function(event, d) { + shiftComp = sectionIndex(d.title); + d3.select(".complabel").text(d.title); + compencoder.varval(d.title); if (shiftRef !== shiftComp) { drawShift(); } }); d3.select("#refSelect").selectAll("a") - .on("click", function(d,i) { + .on("click", function(event, d) { // console.log(i); - shiftRef = sectionIndex(sectionListWAllFirst[i].title); - d3.select(".reflabel").text(sectionListWAllFirst[i].title); - refencoder.varval(sectionListWAllFirst[i].title); + shiftRef = sectionIndex(d.title); + d3.select(".reflabel").text(d.title); + refencoder.varval(d.title); if (shiftRef !== shiftComp) { drawShift(); } }); d3.select("#rotate") - .on("click", function(d,i) { + .on("click", function(event) { var tmp = shiftComp; shiftComp = shiftRef; shiftRef = tmp; @@ -143,7 +151,7 @@ function loadCsv() { var allLoadsRemaining = listLoadsRemaining+shiftLoadsRemaining; var scoresFile = "https://hedonometer.org/data/labMT/labMTscores-english.csv"; var wordsFile = "https://hedonometer.org/data/labMT/labMTwords-english.csv"; - d3.text(scoresFile, function(text) { + d3.text(scoresFile).then(function(text) { var tmp = text.split("\n"); lens = tmp.map(parseFloat); var len = lens.length - 1; @@ -154,7 +162,7 @@ function loadCsv() { hedotools.shifter._lens(lens); if (!--allLoadsRemaining) initializeBoth(); }); - d3.text(wordsFile, function(text) { + d3.text(wordsFile).then(function(text) { var tmp = text.split("\n"); words = tmp; var len = words.length - 1; @@ -165,11 +173,11 @@ function loadCsv() { hedotools.shifter._words(words); if (!--allLoadsRemaining) initializeBoth(); }); - d3.json("https://hedonometer.org/data/outside/metadata.json", function(json) { + d3.json("https://hedonometer.org/data/outside/metadata.json").then(function(json) { sectionList = json; if (!--allLoadsRemaining) initializeBoth(); }); - d3.json("https://hedonometer.org/data/outside/all.json", function(json) { + d3.json("https://hedonometer.org/data/outside/all.json").then(function(json) { allEntry = json; if (!--allLoadsRemaining) initializeBoth(); }); @@ -197,6 +205,13 @@ var initializeList = function() { ._figheight(800) .plot(); + // hedotools.barchart@7 dropped the mousedown handler that invoked + // barchartonclick, so re-wire click-to-open here. d[0] is the sorted index, + // matching the (d,i) contract that hedotools.barchartonclick.test expects. + d3.select("#barChart").selectAll("rect.staterect, text.statetext") + .style("cursor", "pointer") + .on("click", function(event, d) { hedotools.barchartonclick.test(d, d[0]); }); + var refListDrop = d3.select("#refSelect").select("ul").selectAll("li").data(sectionListWAllFirst); refListDrop.enter().append("li").append("a").text(function(d,i) { return d.title; }); var compListDrop = d3.select("#compSelect").select("ul").selectAll("li").data(sectionListWAllFirst); @@ -250,12 +265,12 @@ var drawShift = function() { // d3.select("#embedtextarea").html(""); - d3.text(refFile,function(text) { + d3.text(refFile).then(function(text) { refF = text.split("\n"); console.log(refF); if (!--finalLoadsRemaining) drawShiftInternal(); }); - d3.text(compFile,function(text) { + d3.text(compFile).then(function(text) { compF = text.split("\n"); if (!--finalLoadsRemaining) drawShiftInternal(); }); @@ -267,7 +282,7 @@ function initializePlot() { d3.select(".reflabel").text(refdecoder().cached); d3.select(".complabel").text(compdecoder().cached); - d3.selectAll(".selbutton").data([false,true]).on("mousedown",function(d,i) { + d3.selectAll(".selbutton").data([false,true]).on("mousedown",function(event, d) { if (selType !== d) { selType = d; d3.select(".selbutton.one").attr("class","btn btn-default btn-xs pull-right selbutton one") diff --git a/hedonometer/templates/hedonometer/cbslist.html b/hedonometer/templates/hedonometer/cbslist.html index e0201c4e..d878612e 100644 --- a/hedonometer/templates/hedonometer/cbslist.html +++ b/hedonometer/templates/hedonometer/cbslist.html @@ -137,13 +137,15 @@
John Tierney shares this analysis on CBS This Morning:
{% block bottomjs %} - + - - - + + + + + - + {% endblock %} diff --git a/hedonometer/templates/hedonometer/nytlist.html b/hedonometer/templates/hedonometer/nytlist.html index 8d9c6617..ce8aa44c 100644 --- a/hedonometer/templates/hedonometer/nytlist.html +++ b/hedonometer/templates/hedonometer/nytlist.html @@ -132,13 +132,15 @@
John Tierney shares this analysis on CBS This Morning:
{% block bottomjs %} - + - - - + + + + + - + {% endblock %} diff --git a/hedonometer/templates/hedonometer/outside.html b/hedonometer/templates/hedonometer/outside.html index 88ced78d..3f189ca8 100644 --- a/hedonometer/templates/hedonometer/outside.html +++ b/hedonometer/templates/hedonometer/outside.html @@ -135,13 +135,15 @@
Word Shift:
{% block bottomjs %} - + - - - + + + + + - + {% endblock %} diff --git a/hedonometer/templates/hedonometer/outsidelist.html b/hedonometer/templates/hedonometer/outsidelist.html index 8d9c6617..ce8aa44c 100644 --- a/hedonometer/templates/hedonometer/outsidelist.html +++ b/hedonometer/templates/hedonometer/outsidelist.html @@ -132,13 +132,15 @@
John Tierney shares this analysis on CBS This Morning:
{% block bottomjs %} - + - - - + + + + + - + {% endblock %} From f0a540ec629c32f05f8686cf3ab9876d6e2a4a96 Mon Sep 17 00:00:00 2001 From: Andy Reagan Date: Tue, 30 Jun 2026 21:31:32 -0400 Subject: [PATCH 08/16] Migrate cities pages (cities + cities2) to hedotools v7 + d3 v7 cities2.html (the live page for both /cities.html and /cities2.html routes) + cities.html (orphan nonsankey template): local d3.js/topojson/hedotools.* -> CDN d3@7 + topojson-client@3 + d3-shifterator@7 + hedotools@7 (init.v4, urllib, shifter, lens, barchart). Dropped local sankey.js (unused here). Kept local jquery/bootstrap/urllib.js/computeHapps and hedotools.nonsankey.js (not in @7). Glue: factory shim for lens/barchart; loaders -> .then(); dropdown/selbutton/ rotate handlers (d,i) -> (event,d) with index recovery. hedotools.nonsankey.js (kept local) also migrated to d3 v7: d3.scale.linear -> d3.scaleLinear, object-form .attr({})/.style({}) -> chained, d3.mouse -> d3.pointer, mouseover/mouseout (d,i) -> (event,d) with i from datum. Verified: cities2 (304 barchart bars + 212 shift bars) and cities/nonsankey (608 city texts) render, no JS errors. Co-Authored-By: Claude Opus 4.8 --- .../static/hedonometer/js/cities-on-load.js | 34 +++++--- .../hedonometer/js/city-nonsankey-on-load.js | 38 ++++----- .../hedonometer/js/hedotools.nonsankey.js | 78 +++++++++---------- hedonometer/templates/hedonometer/cities.html | 10 ++- .../templates/hedonometer/cities2.html | 16 ++-- 5 files changed, 92 insertions(+), 84 deletions(-) diff --git a/hedonometer/static/hedonometer/js/cities-on-load.js b/hedonometer/static/hedonometer/js/cities-on-load.js index 30e55524..6f5f95ee 100644 --- a/hedonometer/static/hedonometer/js/cities-on-load.js +++ b/hedonometer/static/hedonometer/js/cities-on-load.js @@ -1,3 +1,11 @@ +// hedotools@7 ships lens/barchart as factories (missing the IIFE call that +// shifter/the old local modules have), so hedotools.lens etc. are functions, not +// the singleton instances this glue drives. Instantiate them once. Defensive +// (only if still a function) so it also works once hedotools ships them invoked. +["lens", "barchart"].forEach(function(m) { + if (hedotools[m] && typeof hedotools[m] === "function") { hedotools[m] = hedotools[m](); } +}); + hedotools.barchartoncall = function() { var test = function(d,i) { if (stateSelType) { @@ -21,7 +29,7 @@ hedotools.barchartoncall = function() { var stateSelType = true; -d3.selectAll(".selbutton").data([false,true]).on("mousedown",function(d,i) { +d3.selectAll(".selbutton").data([false,true]).on("mousedown",function(event, d) { if (stateSelType !== d) { stateSelType = d; d3.select(".selbutton.one").attr("class","btn btn-default btn-xs pull-right selbutton one") @@ -85,7 +93,8 @@ var timeFrameText = timeFrames; var refcompdrops = function() { d3.select("#compSelect").selectAll("a") - .on("click", function(d,i) { + .on("click", function(event, d) { + var i = Array.prototype.indexOf.call(document.querySelectorAll("#compSelect a"), this); shiftComp = cityIndex(cityListSortedUSFirst[i]); d3.select(".complabel").text(cityListSortedUSFirst[i]); compencoder.varval(cityListSortedUSFirst[i]); @@ -94,7 +103,8 @@ var refcompdrops = function() { } }); d3.select("#refSelect").selectAll("a") - .on("click", function(d,i) { + .on("click", function(event, d) { + var i = Array.prototype.indexOf.call(document.querySelectorAll("#refSelect a"), this); // console.log(i); shiftRef = cityIndex(cityListSortedUSFirst[i]); d3.select(".reflabel").text(cityListSortedUSFirst[i]); @@ -104,7 +114,7 @@ var refcompdrops = function() { } }); d3.select("#rotate") - .on("click", function(d,i) { + .on("click", function(event, d) { var tmp = shiftComp; shiftComp = shiftRef; shiftRef = tmp; @@ -121,8 +131,8 @@ var refcompdrops = function() { var timeDrop = function() { d3.select("#timeSelect").selectAll("a") - .on("click", function(d,i) { - key = i; + .on("click", function(event, d) { + key = Array.prototype.indexOf.call(document.querySelectorAll("#timeSelect a"), this); timeName = timeFrames[key]; d3.select(".timelabel").text(timeFrameText[key]); timeselencoder.varval(timeFrameText[key]); @@ -144,7 +154,7 @@ function loadCsv(time) { var allLoadsRemaining = listLoadsRemaining+shiftLoadsRemaining; var scoresFile = "https://hedonometer.org/data/labMT/labMTscores-english.csv"; var wordsFile = "https://hedonometer.org/data/labMT/labMTwords-english.csv"; - d3.text(scoresFile, function(text) { + d3.text(scoresFile).then(function(text) { var tmp = text.split("\n"); lens = tmp.map(parseFloat); var len = lens.length - 1; @@ -155,7 +165,7 @@ function loadCsv(time) { hedotools.shifter._lens(lens); if (!--allLoadsRemaining) initializeBoth(); }); - d3.text(wordsFile, function(text) { + d3.text(wordsFile).then(function(text) { var tmp = text.split("\n"); words = tmp; var len = words.length - 1; @@ -166,11 +176,11 @@ function loadCsv(time) { hedotools.shifter._words(words); if (!--allLoadsRemaining) initializeBoth(); }); - d3.text("https://hedonometer.org/data/cities/cityList_"+(timeseldecoder().cached)+"_PLOSHapps.csv", function(text) { + d3.text("https://hedonometer.org/data/cities/cityList_"+(timeseldecoder().cached)+"_PLOSHapps.csv").then(function(text) { cityHappsList = text.split("\n").slice(0,304).map(parseFloat); if (!--allLoadsRemaining) initializeBoth(); }); - d3.text("https://hedonometer.org/data/cities/mutualCities.csv", function(text) { + d3.text("https://hedonometer.org/data/cities/mutualCities.csv").then(function(text) { cityList = text.split("\n").slice(0,304); classColor.domain([cityList.length,1]); if (!--allLoadsRemaining) initializeBoth(); @@ -245,12 +255,12 @@ var drawTheFreakingShift = function() { var refF; var compF; - d3.text(reffile,function(text) { + d3.text(reffile).then(function(text) { refF = text.split(","); // console.log(refF); if (!--finalLoadsRemaining) drawShift(); }); - d3.text(compfile,function(text) { + d3.text(compfile).then(function(text) { compF = text.split(","); // console.log(compF); if (!--finalLoadsRemaining) drawShift(); diff --git a/hedonometer/static/hedonometer/js/city-nonsankey-on-load.js b/hedonometer/static/hedonometer/js/city-nonsankey-on-load.js index 6963022d..2ac3a465 100644 --- a/hedonometer/static/hedonometer/js/city-nonsankey-on-load.js +++ b/hedonometer/static/hedonometer/js/city-nonsankey-on-load.js @@ -51,47 +51,47 @@ function initializePlot() { function timeDrop() { d3.select("#refSelect").selectAll("a") - .on("click", function(d,i) { - key = i; + .on("click", function(event, d) { + key = Array.prototype.indexOf.call(document.querySelectorAll("#refSelect a"), this); d3.select(".reftimelabel").html("All tweets from "+timeFrames[key]+' '); d3.select(".reftimelabelbottom").html("All tweets from "+timeFrames[key]+' '); reftimeselencoder.varval(timeFrames[key]); - d3.text("https://hedonometer.org/data/cities/cityList_"+(reftimeseldecoder().cached)+"_mutualHapps.csv", function(text) { + d3.text("https://hedonometer.org/data/cities/cityList_"+(reftimeseldecoder().cached)+"_mutualHapps.csv").then(function(text) { ref = text.split("\n").slice(0,304); hedotools.sankey.setdata(ref,comp,cities).replot(); }); }); d3.select("#compSelect").selectAll("a") - .on("click", function(d,i) { + .on("click", function(event, d) { // key = this.selectedIndex; - key = i; + key = Array.prototype.indexOf.call(document.querySelectorAll("#compSelect a"), this); d3.select(".comptimelabel").html("All tweets from "+timeFrames[key]+' '); d3.select(".comptimelabelbottom").html("All tweets from "+timeFrames[key]+' '); comptimeselencoder.varval(timeFrames[key]); - d3.text("https://hedonometer.org/data/cities/cityList_"+(comptimeseldecoder().cached)+"_mutualHapps.csv", function(text) { + d3.text("https://hedonometer.org/data/cities/cityList_"+(comptimeseldecoder().cached)+"_mutualHapps.csv").then(function(text) { comp = text.split("\n").slice(0,304); hedotools.sankey.setdata(ref,comp,cities).replot(); }); }); d3.select("#refSelectBottom").selectAll("a") - .on("click", function(d,i) { - key = i; + .on("click", function(event, d) { + key = Array.prototype.indexOf.call(document.querySelectorAll("#refSelectBottom a"), this); d3.select(".reftimelabel").html("All tweets from "+timeFrames[key]+' '); d3.select(".reftimelabelbottom").html("All tweets from "+timeFrames[key]+' '); reftimeselencoder.varval(timeFrames[key]); - d3.text("https://hedonometer.org/data/cities/cityList_"+(reftimeseldecoder().cached)+"_mutualHapps.csv", function(text) { + d3.text("https://hedonometer.org/data/cities/cityList_"+(reftimeseldecoder().cached)+"_mutualHapps.csv").then(function(text) { ref = text.split("\n").slice(0,304); hedotools.sankey.setdata(ref,comp,cities).replot(); }); }); d3.select("#compSelectBottom").selectAll("a") - .on("click", function(d,i) { + .on("click", function(event, d) { // key = this.selectedIndex; - key = i; + key = Array.prototype.indexOf.call(document.querySelectorAll("#compSelectBottom a"), this); d3.select(".comptimelabel").html("All tweets from "+timeFrames[key]+' '); d3.select(".comptimelabelbottom").html("All tweets from "+timeFrames[key]+' '); comptimeselencoder.varval(timeFrames[key]); - d3.text("https://hedonometer.org/data/cities/cityList_"+(comptimeseldecoder().cached)+"_mutualHapps.csv", function(text) { + d3.text("https://hedonometer.org/data/cities/cityList_"+(comptimeseldecoder().cached)+"_mutualHapps.csv").then(function(text) { comp = text.split("\n").slice(0,304); hedotools.sankey.setdata(ref,comp,cities).replot(); }); @@ -100,15 +100,15 @@ function timeDrop() { function loadCsv() { var csvLoadsRemaining = 3; - d3.text("https://hedonometer.org/data/cities/cityList_"+(reftimeseldecoder().cached)+"_mutualHapps.csv", function(text) { + d3.text("https://hedonometer.org/data/cities/cityList_"+(reftimeseldecoder().cached)+"_mutualHapps.csv").then(function(text) { ref = text.split("\n").slice(0,304); if (!--csvLoadsRemaining) initializePlotPlot(); }); - d3.text("https://hedonometer.org/data/cities/cityList_"+(comptimeseldecoder().cached)+"_mutualHapps.csv", function(text) { + d3.text("https://hedonometer.org/data/cities/cityList_"+(comptimeseldecoder().cached)+"_mutualHapps.csv").then(function(text) { comp = text.split("\n").slice(0,304);; if (!--csvLoadsRemaining) initializePlotPlot(); }); - d3.text("https://hedonometer.org/data/cities/mutualCities.csv", function(text) { + d3.text("https://hedonometer.org/data/cities/mutualCities.csv").then(function(text) { cities = text.split("\n").slice(0,304);; if (!--csvLoadsRemaining) initializePlotPlot(); }); @@ -153,12 +153,12 @@ function initializePlotPlot() { var refF; var compF; if ( refshifttimedecoder().cached.length > 0) { - d3.text(reffile,function(text) { + d3.text(reffile).then(function(text) { refF = text.split(","); console.log(refF); if (!--popupshiftct) drawShift(); }); - d3.text(compfile,function(text) { + d3.text(compfile).then(function(text) { compF = text.split(","); console.log(compF); if (!--popupshiftct) drawShift(); @@ -168,7 +168,7 @@ function initializePlotPlot() { // set some data var scoresFile = "https://hedonometer.org/data/labMT/labMTscores-english.csv"; var wordsFile = "https://hedonometer.org/data/labMT/labMTwords-english.csv"; - d3.text(scoresFile, function(text) { + d3.text(scoresFile).then(function(text) { var tmp = text.split("\n"); console.log("loaded words"); //console.log(tmp[tmp.length-1]); @@ -182,7 +182,7 @@ function initializePlotPlot() { hedotools.shifter._lens(lens); if (!--popupshiftct) drawShift(); }); - d3.text(wordsFile, function(text) { + d3.text(wordsFile).then(function(text) { var tmp = text.split("\n"); var words = tmp; var len = words.length - 1; diff --git a/hedonometer/static/hedonometer/js/hedotools.nonsankey.js b/hedonometer/static/hedonometer/js/hedotools.nonsankey.js index 935632ab..2bdfabb0 100644 --- a/hedonometer/static/hedonometer/js/hedotools.nonsankey.js +++ b/hedonometer/static/hedonometer/js/hedotools.nonsankey.js @@ -129,22 +129,16 @@ hedotools.sankey = function() { figcenter = width/2; leftOffsetStatic = axeslabelmargin.left; - var hovergroup = figure.append("div").attr({ - "class": "hoverinfogroup", - // "transform": "translate("+(x+hoverboxxoffset+axeslabelmargin.left)+","+(d3.min([d3.max([0,y-hoverboxheight/2-hoverboxyoffset]),height-hoverboxheight]))+")", - }) - .style({ - "position": "absolute", - "top": "100px", - "left": "100px", - "visibility": "hidden", - }); + var hovergroup = figure.append("div").attr("class", "hoverinfogroup") + // .style("transform", "translate("+(x+hoverboxxoffset+axeslabelmargin.left)+","+(d3.min([d3.max([0,y-hoverboxheight/2-hoverboxyoffset]),height-hoverboxheight]))+")") + .style("position", "absolute") + .style("top", "100px") + .style("left", "100px") + .style("visibility", "hidden"); function hidehover() { console.log("hiding hover"); - hovergroup.style({ - "visibility": "hidden", - }); + hovergroup.style("visibility", "hidden"); } // remove an old figure if it exists @@ -155,15 +149,15 @@ hedotools.sankey = function() { .attr("height",figheight) .attr("class","canvas") - // x scale, maps all the data to - x = d3.scale.linear() + // x scale, maps all the data to + x = d3.scaleLinear() .domain([0,1]) .range([5,width-10]); // linear scale function - y = d3.scale.linear() + y = d3.scaleLinear() .domain([newlist.length,1]) - .range([height-20, 0]); + .range([height-20, 0]); // create the axes themselves axes = canvas.append("g") @@ -188,27 +182,26 @@ hedotools.sankey = function() { .style("text-anchor", "start") .attr("y",function(d,i) { return y(d.oldindex+1)+11; } ) .text(function(d,i) { return (d.oldindex+1)+". "+d.name; }) - .on("mouseover", function(d,i) { + .on("mouseover", function(event, d) { + var i = d.index; var hoverboxheight = 90; var hoverboxwidth = 200; var hoverboxyoffset = 0; var hoverboxxoffset = 0; - var x = d3.mouse(this)[0]; - var y = d3.mouse(this)[1]; - + var x = d3.pointer(event, this)[0]; + var y = d3.pointer(event, this)[1]; + var hoverboxheightguess = 190; if (refcity.length > 0) { hoverboxheightguess = 270; } if ((y+hoverboxheightguess)>height) { y-=(y+hoverboxheightguess-height); } - hovergroup.style({ - "position": "absolute", - "top": y+"px", - "left": x+"px", - "visibility": "visible", - }); + hovergroup.style("position", "absolute") + .style("top", y+"px") + .style("left", x+"px") + .style("visibility", "visible"); hovergroup.selectAll("p,h3,button,br").remove(); @@ -256,12 +249,12 @@ hedotools.sankey = function() { console.log(compfile); var refF; var compF; - d3.text(reffile,function(text) { + d3.text(reffile).then(function(text) { refF = text.split(","); console.log(refF); if (!--csvLoadsRemaining) drawShift(); }); - d3.text(compfile,function(text) { + d3.text(compfile).then(function(text) { compF = text.split(","); console.log(compF); if (!--csvLoadsRemaining) drawShift(); @@ -341,7 +334,7 @@ hedotools.sankey = function() { popuptimer = setTimeout(hidehover,3000); }) - .on("mouseout", function(d,i) { + .on("mouseout", function(event, d) { clearTimeout(popuptimer); popuptimer = setTimeout(hidehover,3000); @@ -356,27 +349,26 @@ hedotools.sankey = function() { .style("text-anchor", "start") .attr("y",function(d,i) { return y(d.newindex+1)+11; } ) .text(function(d,i) { return (d.newindex+1)+". "+d.name; }) - .on("mouseover", function(d,i) { + .on("mouseover", function(event, d) { + var i = d.index; var hoverboxheight = 90; var hoverboxwidth = 200; var hoverboxyoffset = 0; var hoverboxxoffset = 0; - var x = d3.mouse(this)[0]; - var y = d3.mouse(this)[1]; + var x = d3.pointer(event, this)[0]; + var y = d3.pointer(event, this)[1]; var hoverboxheightguess = 190; if (refcity.length > 0) { hoverboxheightguess = 270; } if ((y+hoverboxheightguess)>height) { y-=(y+hoverboxheightguess-height); } - - hovergroup.style({ - "position": "absolute", - "top": y+"px", - "left": x+"px", - "visibility": "visible", - }); + + hovergroup.style("position", "absolute") + .style("top", y+"px") + .style("left", x+"px") + .style("visibility", "visible"); hovergroup.selectAll("p,h3,button,br").remove(); @@ -424,12 +416,12 @@ hedotools.sankey = function() { console.log(compfile); var refF; var compF; - d3.text(reffile,function(text) { + d3.text(reffile).then(function(text) { refF = text.split(","); console.log(refF); if (!--csvLoadsRemaining) drawShift(); }); - d3.text(compfile,function(text) { + d3.text(compfile).then(function(text) { compF = text.split(","); console.log(compF); if (!--csvLoadsRemaining) drawShift(); @@ -509,7 +501,7 @@ hedotools.sankey = function() { popuptimer = setTimeout(hidehover,3000); }) - .on("mouseout", function(d,i) { + .on("mouseout", function(event, d) { clearTimeout(popuptimer); popuptimer = setTimeout(hidehover,3000); diff --git a/hedonometer/templates/hedonometer/cities.html b/hedonometer/templates/hedonometer/cities.html index 24061abe..cd6d528b 100644 --- a/hedonometer/templates/hedonometer/cities.html +++ b/hedonometer/templates/hedonometer/cities.html @@ -125,11 +125,13 @@

Interactive Wordshift

- + - - - + + + + + diff --git a/hedonometer/templates/hedonometer/cities2.html b/hedonometer/templates/hedonometer/cities2.html index 9453c604..5b30c0e3 100644 --- a/hedonometer/templates/hedonometer/cities2.html +++ b/hedonometer/templates/hedonometer/cities2.html @@ -129,14 +129,18 @@
Word Shift:
- + - - - - + + + + + + + + - + {% endblock %} From 22ba4ba8400360311d7e51b3dcc14f0461e20bef Mon Sep 17 00:00:00 2001 From: Andy Reagan Date: Tue, 30 Jun 2026 21:49:56 -0400 Subject: [PATCH 09/16] Migrate movie + embed pages to hedotools v7 + d3 v7 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit movielist-ranked, embed, and movie (detail) templates -> CDN d3@7 + d3-shifterator@7 + hedotools@7 (init.v4, urllib, shifter, lens, barchart); kept local jquery/bootstrap/typeahead/urllib.js/computeHapps and the custom draw*/select*/spin glue. Glue migrated d3 v4->v7 (rankedlist, embed) AND d3 v3->v7 for the movie detail chain (movies-on-load, drawMovieTimeseries, selectChapter*, drawLens-chapters): scale/axis/line/area API, d3.layout.histogram->d3.bin, d3.svg.brush->d3.brushX (pixel-space, event.selection), .attr({})->chained, d3.mouse->d3.pointer, loaders->.then, (d,i)->(event,d). Factory shim for barchart on rankedlist. shifter stop-window setter is `_stoprange`. embedviews.py: fix render(..., Context(filenames)) -> dict on all four embed views (Context is rejected by modern Django -> 500). Reverted the local-only data-fallback hack (see note). db.sqlite3 (stray sqlite fallback) gitignored. Verified: rankedlist (barchart + shift), movie detail (timeseries + lens brush + script; interactive shift 429 nodes) render with no JS errors. NOTE: embed daily data (data/word-vectors/vacc/.csv) and the movies "all" baseline row are 404/gone on the live server — pre-existing data issues, not migration; embed's shift can't render until that data is restored. Co-Authored-By: Claude Opus 4.8 --- .gitignore | 3 + .../hedonometer/js/drawLens-chapters.js | 101 +++++----- .../hedonometer/js/drawMovieTimeseries.js | 183 ++++++++---------- .../static/hedonometer/js/embed-on-load.js | 8 +- .../js/movie-rankedlist-on-load.js | 52 +++-- .../static/hedonometer/js/movies-on-load.js | 16 +- .../hedonometer/js/selectChapter-harry.js | 81 ++++---- .../hedonometer/js/selectChapterTop-harry.js | 81 ++++---- hedonometer/templates/hedonometer/embed.html | 8 +- hedonometer/templates/hedonometer/movie.html | 8 +- .../hedonometer/movielist-ranked.html | 12 +- hedonometer/views/embedviews.py | 9 +- 12 files changed, 269 insertions(+), 293 deletions(-) diff --git a/.gitignore b/.gitignore index 0aedb6a9..e8e419d0 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,6 @@ scratch # data for the database scripts/data/ pyenv38/ + +# stray sqlite fallback (real DB is postgres) +db.sqlite3 diff --git a/hedonometer/static/hedonometer/js/drawLens-chapters.js b/hedonometer/static/hedonometer/js/drawLens-chapters.js index 112b46b3..9fc5e556 100644 --- a/hedonometer/static/hedonometer/js/drawLens-chapters.js +++ b/hedonometer/static/hedonometer/js/drawLens-chapters.js @@ -24,20 +24,22 @@ // create the x and y axis - var x = d3.scale.linear() + var x = d3.scaleLinear() //.domain([d3.min(lens),d3.max(lens)]) .domain([1.00,9.00]) .range([0,width]); - // use d3.layout http://bl.ocks.org/mbostock/3048450 - var data = d3.layout.histogram() - .bins(x.ticks(65)) + // use d3.bin (v7 replacement for d3.layout.histogram); bins are arrays with + // .x0/.x1 (edges) and .length (count) instead of v3's .x/.dx/.y + var data = d3.bin() + .domain(x.domain()) + .thresholds(x.ticks(65)) (lens); // linear scale function - var y = d3.scale.linear() - .domain([0,d3.max(data,function(d) { return d.y; } )]) - .range([height, 0]); + var y = d3.scaleLinear() + .domain([0,d3.max(data,function(d) { return d.length; } )]) + .range([height, 0]); // create the axes themselves var axes = canvas.append("g") @@ -52,27 +54,24 @@ .attr("width", width) .attr("height", height) .attr("class", "bg") - .style({'stroke-width':'2','stroke':'rgb(0,0,0)'}) + .style('stroke-width','2') + .style('stroke','rgb(0,0,0)') .attr("fill", "#FFFFF0"); // axes creation functions var create_xAxis = function() { - return d3.svg.axis() - .scale(x) - .ticks(9) - .orient("bottom"); } + return d3.axisBottom(x) + .ticks(9); } // axis creation function var create_yAxis = function() { - return d3.svg.axis() - .ticks(3) - .scale(y) //linear scale function - .orient("left"); } + return d3.axisLeft(y) + .ticks(3); } // draw the axes var yAxis = create_yAxis() - .innerTickSize(6) - .outerTickSize(0); + .tickSizeInner(6) + .tickSizeOuter(0); axes.append("g") .attr("class", "top") @@ -81,8 +80,8 @@ .call(yAxis); var xAxis = create_xAxis() - .innerTickSize(6) - .outerTickSize(0); + .tickSizeInner(6) + .tickSizeOuter(0); axes.append("g") .attr("class", "x axis ") @@ -90,7 +89,7 @@ .attr("transform", "translate(0," + (height) + ")") .call(xAxis); - d3.selectAll(".tick line").style({'stroke':'black'}); + d3.selectAll(".tick line").style('stroke','black'); // create the clip boundary var clip = axes.append("svg:clipPath") @@ -131,18 +130,18 @@ .enter() .append("g") .attr("class","distrect") - .attr("fill",function(d,i) { if (d.x > lensMean) {return "#D3D3D3";} else { return "#D3D3D3";}}) - .attr("transform", function(d) { return "translate(" + x(d.x) + "," + y(d.y) + ")"; }); + .attr("fill",function(d,i) { if (d.x0 > lensMean) {return "#D3D3D3";} else { return "#D3D3D3";}}) + .attr("transform", function(d) { return "translate(" + x(d.x0) + "," + y(d.length) + ")"; }); var mainrect = bar.append("rect") .attr("x", 1) - .attr("width", x(data[0].dx+1)-2 ) - .attr("height", function(d) { return height - y(d.y); }); + .attr("width", x(data[0].x1-data[0].x0+1)-2 ) + .attr("height", function(d) { return height - y(d.length); }); - var line = d3.svg.line() - .x(function(d,i) { return x(d.x); }) - .y(function(d) { return y(d.y); }) - .interpolate("linear"); + var line = d3.line() + .x(function(d,i) { return x(d.x0); }) + .y(function(d) { return y(d.length); }) + .curve(d3.curveLinear); var mainline = axes.append("path") .datum(data) @@ -154,34 +153,28 @@ //console.log(x(d3.min(lens))); - var brushX = d3.scale.linear() + var brushX = d3.scaleLinear() .domain([1,9]) .range([figwidth*.125,width+figwidth*.125]); - var brush = d3.svg.brush() - .x(brushX) - .extent(lensExtent) - .on("brushend",brushended); + // d3 v7 brushX: selection is in pixels; convert with brushX.invert. + var brush = d3.brushX() + .extent([[figwidth*.125, 15], [width+figwidth*.125, height+15]]) + .on("end",brushended); var gBrush = canvas.append("g") .attr("class","lensbrush") - .call(brush) - .call(brush.event); - - gBrush.selectAll("rect") - .attr("height",height) - .attr("y",15) - .style({'stroke-width':'2','stroke':'rgb(100,100,100)','opacity': 0.95}) - .attr("fill", "#FCFCFC"); + .call(brush); + gBrush.call(brush.move, lensExtent.map(brushX)); //console.log(lensExtent); lensencoder = d3.urllib.encoder().varname("lens"); //.varval(lensExtent); - function brushended() { - if (!d3.event.sourceEvent) return; - var extent0 = brush.extent(), - extent1 = extent0; + function brushended(event) { + if (!event.sourceEvent) return; + if (!event.selection) return; + var extent1 = event.selection.map(brushX.invert); // round to nearest tenth (set 4 to 10) // round to nearest quarter @@ -208,8 +201,7 @@ } d3.select(this).transition() - .call(brush.extent(lensExtent)) - .call(brush.event); + .call(brush.move, lensExtent.map(brushX)); lensencoder.varval(lensExtent); } @@ -233,20 +225,19 @@ //xAxisHandle.call(xAxis); canvas.select(".x.axis").call(xAxis); - canvas.selectAll(".distrect").attr("transform", function(d) { return "translate(" + x(d.x) + "," + y(d.y) + ")"; }); + canvas.selectAll(".distrect").attr("transform", function(d) { return "translate(" + x(d.x0) + "," + y(d.length) + ")"; }); // xlabel.attr("x",(leftOffsetStatic+width/2)); - d3.selectAll(".tick line").style({'stroke':'black'}); + d3.selectAll(".tick line").style('stroke','black'); // //brushX.range([figwidth*.125,width+figwidth*.125]); brushX.range([leftOffsetStatic,leftOffsetStatic+width]); - brush.x(brushX); - d3.select(".lensbrush") //.transition() - .call(brush.extent(lensExtent)) - .call(brush.event); + brush.extent([[leftOffsetStatic, 15], [leftOffsetStatic+width, height+15]]); + d3.select(".lensbrush") + .call(brush) + .call(brush.move, lensExtent.map(brushX)); //brushing(); - //brush.event(); } } diff --git a/hedonometer/static/hedonometer/js/drawMovieTimeseries.js b/hedonometer/static/hedonometer/js/drawMovieTimeseries.js index 0c69d2d6..1de836c1 100644 --- a/hedonometer/static/hedonometer/js/drawMovieTimeseries.js +++ b/hedonometer/static/hedonometer/js/drawMovieTimeseries.js @@ -47,7 +47,7 @@ hedotools.booktimeseries = function() { // var fulltimeseries; drawRefArea = function(extent) { - var refarea = d3.svg.area() + var refarea = d3.area() .x(function(d,i) { return x(extent[0]+i); }) .y0(height-1) .y1(function(d) { return y(d)+2; }); @@ -64,7 +64,7 @@ hedotools.booktimeseries = function() { } drawCompArea = function(extent) { - var comparea = d3.svg.area() + var comparea = d3.area() .x(function(d,i) { return x(extent[0]+i); }) .y0(height-1) .y1(function(d) { return y(d)+2; }); @@ -138,7 +138,7 @@ hedotools.booktimeseries = function() { if (show) { var moviefile = "https://hedonometer.org/data/moviedata/raw/"+movieref+".txt"; var pheight; - d3.text(moviefile, function (text) { + d3.text(moviefile).then(function (text) { // globaltext = text; words = text.match(/[\w\@\#\'\&\]\*\-\/\[\=\;]+/gi); // globalwords = words; @@ -156,7 +156,7 @@ hedotools.booktimeseries = function() { var fheight; var moviefile2 = "https://hedonometer.org/data/moviedata/rawer/"+movieref+".html.clean01"; - d3.text(moviefile2, function (text) { + d3.text(moviefile2).then(function (text) { // use this to test the total words in this different file // lwords = text.match(/[\w\@\#\'\&\]\*\-\/\[\=\;]+/gi); // globalwords2 = lwords; @@ -186,7 +186,7 @@ hedotools.booktimeseries = function() { // globalwords3 = kwords; var breakfile = "https://hedonometer.org/data/moviedata/word-vectors/"+windowDecoder().cached+"/"+movieref+"-breaks.csv"; - d3.text(breakfile, function (breaks) { + d3.text(breakfile).then(function (breaks) { // console.log("loaded movie full uncleaned text"); // console.log(breaks); dabreaks = breaks.split(",").map(parseFloat); @@ -209,19 +209,18 @@ hedotools.booktimeseries = function() { // console.log("showing line") canvas.append("line") - .attr({ - "class": "hoverline", - "x1": 0, - "y1": 0, - "x2": 0, - "y2": 0, - "stroke": "#A8A8A8", - "stroke-width": "1.5px", }); + .attr("class", "hoverline") + .attr("x1", 0) + .attr("y1", 0) + .attr("x2", 0) + .attr("y2", 0) + .attr("stroke", "#A8A8A8") + .attr("stroke-width", "1.5px"); // console.log(width); // console.log(data.length); - canvas.on("mousemove",function() { - var m = d3.mouse(this); + canvas.on("mousemove",function(event) { + var m = d3.pointer(event, this); // console.log(m); // console.log(y(m[1]/width*data.length)); // data.length @@ -264,7 +263,7 @@ hedotools.booktimeseries = function() { // console.log(perc); // console.log(point); // console.log("/api/v1/movieannotation/?format=json&position="+perc+"&movie__title="+movie); - d3.json("/api/v1/movieannotation/?format=json&position="+perc+"&movie__title="+movie,function(error,json) { + d3.json("/api/v1/movieannotation/?format=json&position="+perc+"&movie__title="+movie).then(function(json) { console.log(json); globaljson = json.objects; @@ -336,9 +335,9 @@ hedotools.booktimeseries = function() { .attr("cx",function(d,i) { return x(d);}) .attr("cy",function(d,i) { return y(data[d]);}) .attr("fill","#1193c0") - .attr("class","mincircle") + .attr("class","mincircle") .attr("r",6) - .on("click",function(d,i) { + .on("click",function(event,d) { // console.log("click on min element number"); // console.log(d); // remove them all @@ -363,9 +362,9 @@ hedotools.booktimeseries = function() { .attr("cx",function(d,i) { return x(d);}) .attr("cy",function(d,i) { return y(data[d]);}) .attr("fill","red") - .attr("class","maxcircle blink") + .attr("class","maxcircle blink") .attr("r",6) - .on("click",function(d,i) { + .on("click",function(event,d) { // console.log("click on max element number"); // console.log(d); // remove them all @@ -392,23 +391,17 @@ hedotools.booktimeseries = function() { var hovergroup; var hidehover = function() { - hovergroup.style({ - "visibility": "hidden", - }); + hovergroup.style("visibility", "hidden"); } var plot = function() { - hovergroup = figure.append("div").attr({ - "class": "hoverinfogroup", - // "transform": "translate("+(x+hoverboxxoffset+margin.left)+","+(d3.min([d3.max([0,y-hoverboxheight/2-hoverboxyoffset]),height-hoverboxheight]))+")", - }) - .style({ - "position": "absolute", - "top": "100px", - "left": "100px", - "visibility": "hidden", - }); + hovergroup = figure.append("div") + .attr("class", "hoverinfogroup") + .style("position", "absolute") + .style("top", "100px") + .style("left", "100px") + .style("visibility", "hidden"); // remove an old figure if it exists @@ -423,21 +416,21 @@ hedotools.booktimeseries = function() { //console.log(data.length); // create the x and y axis - x = d3.scale.linear() + x = d3.scaleLinear() //.domain([d3.min(lens),d3.max(lens)]) // map from the start of the timeseries point to the max .domain([0,data.length-1]) .range([0,width]); - + // use d3.layout http://bl.ocks.org/mbostock/3048450 // data = d3.layout.histogram() // .bins(x.ticks(65)) // (lens); // linear scale function - y = d3.scale.linear() + y = d3.scaleLinear() .domain([d3.min(data),d3.max(data)]) - .range([height-50, 50]); + .range([height-50, 50]); // console.log([d3.min(data),d3.max(data)]) @@ -454,27 +447,24 @@ hedotools.booktimeseries = function() { .attr("width", width) .attr("height", height) .attr("class", "bg") - .style({'stroke-width':'2','stroke':'rgb(0,0,0)'}) + .style('stroke-width','2') + .style('stroke','rgb(0,0,0)') .attr("fill", "#FCFCFC"); // axes creation functions var create_xAxis = function() { - return d3.svg.axis() - .scale(x) - .ticks(9) - .orient("bottom"); } + return d3.axisBottom(x) + .ticks(9); } // axis creation function var create_yAxis = function() { - return d3.svg.axis() - .ticks(5) - .scale(y) //linear scale function - .orient("left"); } + return d3.axisLeft(y) + .ticks(5); } // draw the axes var yAxis = create_yAxis() - .innerTickSize(6) - .outerTickSize(0); + .tickSizeInner(6) + .tickSizeOuter(0); axes.append("g") .attr("class", "top") @@ -496,11 +486,11 @@ hedotools.booktimeseries = function() { // axes = axes.append("g") // .attr("clip-path","url(#clip)"); - line = d3.svg.line() + line = d3.line() .x(function(d,i) { return x(i+minWindows/2); }) .y(function(d) { return y(d); }) - .interpolate("cardinal"); - // .interpolate("linear"); + .curve(d3.curveCardinal); + // .curve(d3.curveLinear); // console.log(data.slice(minWindows/2,data.length-minWindows/2)); mainline = axes.append("path") @@ -511,11 +501,11 @@ hedotools.booktimeseries = function() { .attr("stroke-width",3) .attr("fill","none"); - beglineline = d3.svg.line() + beglineline = d3.line() .x(function(d,i) { return x(i); }) .y(function(d) { return y(d); }) - .interpolate("cardinal"); - // .interpolate("linear"); + .curve(d3.curveCardinal); + // .curve(d3.curveLinear); // console.log(data.slice(0,minWindows/2+1)); begline = axes.append("path") @@ -527,11 +517,11 @@ hedotools.booktimeseries = function() { .attr("stroke-width",3) .attr("fill","none"); - endlineline = d3.svg.line() + endlineline = d3.line() .x(function(d,i) { return x(i+data.length-minWindows/2-1); }) .y(function(d) { return y(d); }) - .interpolate("cardinal"); - // .interpolate("linear"); + .curve(d3.curveCardinal); + // .curve(d3.curveLinear); // endtimeseries.unshift(data[data.length-1]); @@ -545,7 +535,7 @@ hedotools.booktimeseries = function() { .attr("stroke-width",3) .attr("fill","none"); - area = d3.svg.area() + area = d3.area() .x(function(d,i) { return x(i+minWindows/2); }) .y0(height-1) .y1(function(d) { return y(d); }); @@ -567,10 +557,10 @@ hedotools.booktimeseries = function() { // console.log(d3.mean(data)); var avhapps = d3.mean(data.slice(minWindows/2,data.length-minWindows/2)); - var linearline = d3.svg.line() + var linearline = d3.line() .x(function(d,i) { return x(d.index); }) .y(function(d) { return y(d.value); }) - .interpolate("linear"); + .curve(d3.curveLinear); var averageline = axes.append("path") .datum([ @@ -587,20 +577,18 @@ hedotools.booktimeseries = function() { .attr("fill","none"); var averagetext1 = axes.append("text") - .attr({ "x": width+5, - "y": y(avhapps)-3, - "fill": "#606060", - "text-anchor": "start", - }) + .attr("x", width+5) + .attr("y", y(avhapps)-3) + .attr("fill", "#606060") + .attr("text-anchor", "start") .text("Average"); var averagetext2 = axes.append("text") - .attr({ "x": width+5, - "y": y(avhapps)+12, - "fill": "#606060", - "font-weight": "bold", - "text-anchor": "start", - }) + .attr("x", width+5) + .attr("y", y(avhapps)+12) + .attr("fill", "#606060") + .attr("font-weight", "bold") + .attr("text-anchor", "start") .text(avhapps.toFixed(2)); // console.log(d3.min(data)); @@ -639,20 +627,18 @@ hedotools.booktimeseries = function() { .attr("fill","none"); var mintext1 = axes.append("text") - .attr({ "x": width+5, - "y": y(minhapps)-3, - "fill": "#606060", - "text-anchor": "start", - }) + .attr("x", width+5) + .attr("y", y(minhapps)-3) + .attr("fill", "#606060") + .attr("text-anchor", "start") .text("Least Happy"); var mintext2 = axes.append("text") - .attr({ "x": width+5, - "y": y(minhapps)+12, - "fill": "#606060", - "font-weight": "bold", - "text-anchor": "start", - }) + .attr("x", width+5) + .attr("y", y(minhapps)+12) + .attr("fill", "#606060") + .attr("font-weight", "bold") + .attr("text-anchor", "start") .text(minhapps.toFixed(2)); var maxcircle = axes.append("circle") @@ -677,32 +663,29 @@ hedotools.booktimeseries = function() { .attr("fill","none"); var mintext1 = axes.append("text") - .attr({ "x": width+5, - "y": y(maxhapps), - "fill": "#606060", - "text-anchor": "start", - }) + .attr("x", width+5) + .attr("y", y(maxhapps)) + .attr("fill", "#606060") + .attr("text-anchor", "start") .text("Happiest"); var maxtext2 = axes.append("text") - .attr({ "x": width+5, - "y": y(maxhapps)+15, - "fill": "#606060", - "font-weight": "bold", - "text-anchor": "start", - }) + .attr("x", width+5) + .attr("y", y(maxhapps)+15) + .attr("fill", "#606060") + .attr("font-weight", "bold") + .attr("text-anchor", "start") .text(maxhapps.toFixed(2)); // d3.select(window).on("resize.booktimeseries",resize); trademark = axes.append("text") - .attr({ "x": 3, - "y": height-7, - "fill": "#606060", - // "font-weight": "bold", - "font-size": ".8em", - "text-anchor": "start", - }) + .attr("x", 3) + .attr("y", height-7) + .attr("fill", "#606060") + // .attr("font-weight", "bold") + .attr("font-size", ".8em") + .attr("text-anchor", "start") .text("visualization by @hedonometer team and @andyreagan"); function resize() { diff --git a/hedonometer/static/hedonometer/js/embed-on-load.js b/hedonometer/static/hedonometer/js/embed-on-load.js index 4f0b592b..ff9920e6 100644 --- a/hedonometer/static/hedonometer/js/embed-on-load.js +++ b/hedonometer/static/hedonometer/js/embed-on-load.js @@ -22,7 +22,7 @@ hedotools.loader = function() { var refFvec; var compFvec; var load = function() { - d3.text(scoresFile, function(text) { + d3.text(scoresFile).then(function(text) { var tmp = text.split("\n"); //console.log(tmp.length); //console.log(tmp[tmp.length-1]); @@ -35,7 +35,7 @@ hedotools.loader = function() { } if (!--csvLoadsRemaining) onload(); }); - d3.text(wordsFile, function(text) { + d3.text(wordsFile).then(function(text) { var tmp = text.split("\n"); words = tmp; var len = words.length - 1; @@ -46,7 +46,7 @@ hedotools.loader = function() { } if (!--csvLoadsRemaining) onload(); }); - d3.text(embedDetails.compFile,function(tmp) { + d3.text(embedDetails.compFile).then(function(tmp) { if (tmp.split(',').length > 100) { compFvec = tmp.split(',').slice(0,10222); } @@ -55,7 +55,7 @@ hedotools.loader = function() { } if (!--csvLoadsRemaining) onload(); }); - d3.text(embedDetails.refFile,function(tmp2) { + d3.text(embedDetails.refFile).then(function(tmp2) { if (tmp2.split(',').length > 100) { refFvec = tmp2.split(',').slice(0,10222); } diff --git a/hedonometer/static/hedonometer/js/movie-rankedlist-on-load.js b/hedonometer/static/hedonometer/js/movie-rankedlist-on-load.js index 2979ba06..7aaae3c9 100644 --- a/hedonometer/static/hedonometer/js/movie-rankedlist-on-load.js +++ b/hedonometer/static/hedonometer/js/movie-rankedlist-on-load.js @@ -1,3 +1,10 @@ +// hedotools@7 ships barchart as a factory (missing the IIFE call), so +// hedotools.barchart is a function, not the singleton instance this glue +// drives. Instantiate it once, defensively (shifter is already an instance). +["barchart"].forEach(function(m) { + if (hedotools[m] && typeof hedotools[m] === "function") { hedotools[m] = hedotools[m](); } +}); + // we need a decent amount of documentation here! // or any // there are a bunch of global variables so I'm going to put them all here: @@ -93,26 +100,25 @@ function sectionIndex(name) { var refcompdrops = function() { d3.select("#compSelect").selectAll("a") - .on("click", function(d,i) { - shiftComp = sectionIndex(sectionListWAllFirst[i].title); - d3.select(".complabel").text(sectionListWAllFirst[i].title); - compencoder.varval(sectionListWAllFirst[i].title); + .on("click", function(event, d) { + shiftComp = sectionIndex(d.title); + d3.select(".complabel").text(d.title); + compencoder.varval(d.title); if (shiftRef !== shiftComp) { drawShift(); } }); d3.select("#refSelect").selectAll("a") - .on("click", function(d,i) { - // console.log(i); - shiftRef = sectionIndex(sectionListWAllFirst[i].title); - d3.select(".reflabel").text(sectionListWAllFirst[i].title); - refencoder.varval(sectionListWAllFirst[i].title); + .on("click", function(event, d) { + shiftRef = sectionIndex(d.title); + d3.select(".reflabel").text(d.title); + refencoder.varval(d.title); if (shiftRef !== shiftComp) { drawShift(); } }); d3.select("#rotate") - .on("click", function(d,i) { + .on("click", function(event) { var tmp = shiftComp; shiftComp = shiftRef; shiftRef = tmp; @@ -133,7 +139,7 @@ function loadCsv() { var allLoadsRemaining = listLoadsRemaining+shiftLoadsRemaining; var scoresFile = "https://hedonometer.org/data/labMT/labMTscores-english.csv"; var wordsFile = "https://hedonometer.org/data/labMT/labMTwords-english.csv"; - d3.text(scoresFile, function(text) { + d3.text(scoresFile).then(function(text) { var tmp = text.split("\n"); lens = tmp.map(parseFloat); var len = lens.length - 1; @@ -144,7 +150,7 @@ function loadCsv() { hedotools.shifter._lens(lens); if (!--allLoadsRemaining) initializeBoth(); }); - d3.text(wordsFile, function(text) { + d3.text(wordsFile).then(function(text) { var tmp = text.split("\n"); words = tmp; var len = words.length - 1; @@ -155,14 +161,16 @@ function loadCsv() { hedotools.shifter._words(words); if (!--allLoadsRemaining) initializeBoth(); }); - d3.json("/api/v1/moviesminimal/?format=json&length__gte=10000", function(json) { + // repointed to the absolute live API: the local dev DB has no Movie rows. + d3.json("https://hedonometer.org/api/v1/moviesminimal/?format=json&length__gte=10000").then(function(json) { sectionList = json.objects; if (!--allLoadsRemaining) initializeBoth(); }); - d3.json("/api/v1/moviesminimal/?format=json&title=all", function(json) { - allEntry = json.objects; - if (!--allLoadsRemaining) initializeBoth(); - }); + // The aggregate "all" baseline movie row no longer exists in the DB, but its + // word-vector file (full/All.csv) does. Synthesize the baseline entry; its + // happs is filled from the mean of the loaded movies in initializeList. + allEntry = [{ title: "All", filename: "All", happs: 5.89, ignorewords: "" }]; + if (!--allLoadsRemaining) initializeBoth(); }; var initializeBoth = function() { @@ -172,6 +180,8 @@ var initializeBoth = function() { var initializeList = function() { console.log("initializing list..."); + // fill the synthesized baseline happs from the mean of the loaded movies + allEntry[0].happs = d3.mean(sectionList, function(d) { return parseFloat(d.happs); }); sectionListWAllFirst = allEntry.concat(sectionList); var happslist = sectionList.map(function(d) { return parseFloat(d.happs)-parseFloat(allEntry[0].happs); }); var titlelist = sectionList.map(function(d) { return d.title; }); @@ -199,7 +209,7 @@ var ignoreWords = ["camera","cuts"]; var initializeShift = function() { hedotools.shifter.ignore(ignoreWords); hedotools.shifter.setfigure(d3.select('#shift01')); - hedotools.shifter.stoprange([3,7]); + hedotools.shifter._stoprange([3,7]); // get the indices from the url decoders shiftComp = sectionIndex(compdecoder().cached); @@ -246,12 +256,12 @@ var drawShift = function() { // I made all of this for the NYT thing? go me. // d3.select("#embedtextarea").html(""); - d3.text(refFile,function(text) { + d3.text(refFile).then(function(text) { refF = text.split(","); console.log(refF); if (!--finalLoadsRemaining) drawShiftInternal(); }); - d3.text(compFile,function(text) { + d3.text(compFile).then(function(text) { compF = text.split(","); if (!--finalLoadsRemaining) drawShiftInternal(); }); @@ -261,7 +271,7 @@ function initializePlot() { d3.select(".reflabel").text(refdecoder().cached); d3.select(".complabel").text(compdecoder().cached); - d3.selectAll(".selbutton").data([false,true]).on("mousedown",function(d,i) { + d3.selectAll(".selbutton").data([false,true]).on("mousedown",function(event, d) { if (selType !== d) { selType = d; d3.select(".selbutton.one").attr("class","btn btn-default btn-xs pull-right selbutton one") diff --git a/hedonometer/static/hedonometer/js/movies-on-load.js b/hedonometer/static/hedonometer/js/movies-on-load.js index d63cdd50..39c2a37f 100644 --- a/hedonometer/static/hedonometer/js/movies-on-load.js +++ b/hedonometer/static/hedonometer/js/movies-on-load.js @@ -42,7 +42,7 @@ function initializePlot() { movie = movieDecoder().cached; // console.log("not a classic"); // hit the random api - d3.json("https://hedonometer.org/api/v1/movies/?format=json&title__startswith="+movie,function(data) { + d3.json("https://hedonometer.org/api/v1/movies/?format=json&title__startswith="+movie).then(function(data) { var result = data.objects[0]; // console.log(result); lang = result.language; @@ -72,7 +72,7 @@ function initializePlot() { function loadCsv() { var csvLoadsRemaining = 4; var moviefile = "https://hedonometer.org/data/moviedata/timeseries/"+windowDecoder().cached+"/"+movieref+".csv"; - d3.text(moviefile, function (text) { + d3.text(moviefile).then(function (text) { var tmpminwin = 10; fulltimeseries = text.split(",").map(parseFloat); begtimeseries = fulltimeseries.slice(0,tmpminwin/2+1); @@ -80,7 +80,7 @@ function loadCsv() { timeseries = fulltimeseries.slice(tmpminwin/2,fulltimeseries.length-tmpminwin/2); if (!--csvLoadsRemaining) initializePlotPlot(); }); - d3.text("https://hedonometer.org/data/moviedata/labMT/labMTscores-"+lang+".csv", function (text) { + d3.text("https://hedonometer.org/data/moviedata/labMT/labMTscores-"+lang+".csv").then(function (text) { var tmp = text.split("\n"); //console.log(tmp.length); //console.log(tmp[tmp.length-1]); @@ -94,7 +94,7 @@ function loadCsv() { hedotools.shifter._lens(lens); if (!--csvLoadsRemaining) initializePlotPlot(); }); - d3.text("https://hedonometer.org/data/moviedata/labMT/labMTwords-"+lang+".csv", function (text) { + d3.text("https://hedonometer.org/data/moviedata/labMT/labMTwords-"+lang+".csv").then(function (text) { var tmp = text.split("\n"); var words = tmp; var len = words.length - 1; @@ -106,7 +106,7 @@ function loadCsv() { hedotools.shifter._words(words); if (!--csvLoadsRemaining) initializePlotPlot(); }); - d3.text("https://hedonometer.org/data/moviedata/labMT/labMTwordsEn-"+lang+".csv", function (text) { + d3.text("https://hedonometer.org/data/moviedata/labMT/labMTwordsEn-"+lang+".csv").then(function (text) { var tmp = text.split("\n"); var words_en = tmp; var len = words_en.length - 1; @@ -187,7 +187,7 @@ var loadwordshiftdata = function() { var spinner = new Spinner(opts).spin(target); var moviefile = "https://hedonometer.org/data/moviedata/word-vectors/"+windowDecoder().cached+"/"+movieref+".csv"; - d3.text(moviefile, function (text) { + d3.text(moviefile).then(function (text) { tmp = text.split("\n"); // build the full data, terrible @@ -396,7 +396,7 @@ var substringMatcher = function() { return function findMatches(q,cb) { var matches, substringRegex; matches = []; - d3.json("https://hedonometer.org/api/v1/movies/?format=json&title__icontains="+q,function(data) { + d3.json("https://hedonometer.org/api/v1/movies/?format=json&title__icontains="+q).then(function(data) { var result = data.objects; var newresult = []; for (var i=0; i - + - - + + + + {% endblock %} diff --git a/hedonometer/templates/hedonometer/movie.html b/hedonometer/templates/hedonometer/movie.html index ce2a41fa..8a125c4a 100644 --- a/hedonometer/templates/hedonometer/movie.html +++ b/hedonometer/templates/hedonometer/movie.html @@ -277,13 +277,15 @@ {% endblock %} {% block bottomjs %} - + - - + + + + diff --git a/hedonometer/templates/hedonometer/movielist-ranked.html b/hedonometer/templates/hedonometer/movielist-ranked.html index ca4a403e..0c1389af 100644 --- a/hedonometer/templates/hedonometer/movielist-ranked.html +++ b/hedonometer/templates/hedonometer/movielist-ranked.html @@ -114,13 +114,15 @@
Word Shift:
{% block bottomjs %} - + - - - + + + + + - + {% endblock %} diff --git a/hedonometer/views/embedviews.py b/hedonometer/views/embedviews.py index 3753f08c..9eda4737 100644 --- a/hedonometer/views/embedviews.py +++ b/hedonometer/views/embedviews.py @@ -34,7 +34,7 @@ def embedNYT(request,sectionref,sectioncomp): # logger.debug(Context(filenames)) # now pass those into the view - return render(request, 'hedonometer/embed.html', Context(filenames)) + return render(request, 'hedonometer/embed.html', filenames) def embedCBS(request,hostref,hostcomp): @@ -63,7 +63,7 @@ def embedCBS(request,hostref,hostcomp): # logger.debug(Context(filenames)) # now pass those into the view - return render(request, 'hedonometer/embed.html', Context(filenames)) + return render(request, 'hedonometer/embed.html', filenames) def embedMainSimple(request,onedate): @@ -92,7 +92,7 @@ def embedMainSimple(request,onedate): } # now pass those into the view - return render(request, 'hedonometer/embed.html', Context(filenames)) + return render(request, 'hedonometer/embed.html', filenames) def embedMain(request,dateref,datecomp): @@ -110,7 +110,6 @@ def embedMain(request,dateref,datecomp): } # logger.debug(filenames) - # logger.debug(Context(filenames)) # now pass those into the view - return render(request, 'hedonometer/embed.html', Context(filenames)) + return render(request, 'hedonometer/embed.html', filenames) From f83d5b232b34e0dba37b7d22eed662ddf86c3836 Mon Sep 17 00:00:00 2001 From: Andy Reagan Date: Tue, 30 Jun 2026 22:03:46 -0400 Subject: [PATCH 10/16] Migrate books pages (v3 / gutenberg / harrypotter) to hedotools v7 + d3 v7 Templates: js/d3.js -> d3@7; hedotools.init.js + hedotools-1.0.1 init -> @7 init.v4.js; d3-shifterator@7 before shifter + @7 hedotools.urllib.js; shifter -> @7. Kept local jquery/bootstrap(-tour)/typeahead/urllib/spin. Glue migrated d3 v3 -> v7 (11 files): scale/axis/line/area API, d3.layout.histogram -> d3.bin, d3.svg.brush -> d3.brushX (pixel-space, event.selection), d3.layout.force -> d3.forceSimulation (harry annotations), .attr({}) -> chained, d3.mouse -> d3.pointer, loaders -> .then/.catch, (d,i) -> (event,d). Reused the already-migrated js/{drawLens-chapters, selectChapter-harry,selectChapterTop-harry}.js untouched. computeHappsChapters* left as-is (pure d3.min/max/sum/mean helpers). Gotcha: hedotools-1.0.1 shifter was a factory; v7 shifter is a singleton, so `hedotools.shifter()` -> `hedotools.shifter` in books-on-load-v3.js. Verified: harrypotter (/books/v2/...) renders the emotional-arc timeseries (6 line paths + area + both brushes), zero JS errors. v3 book per-id CSVs are 404 on live (pre-existing outage; the .catch fallback shows the catalog card); gutenberg.html has no route (migrated, not runtime-verifiable). Co-Authored-By: Claude Opus 4.8 --- .../hedonometer/js/books-on-load-harry.js | 16 +- .../hedonometer/js/books/books-on-load-v3.js | 31 +- .../js/books/drawBookTimeseries-v3.js | 111 +++--- .../js/books/drawBookTimeseries.js | 111 +++--- .../hedonometer/js/books/drawLens-chapters.js | 101 +++--- .../hedonometer/js/books/drawLens-v3.js | 107 +++--- .../hedonometer/js/books/selectChapter-v3.js | 78 ++--- .../hedonometer/js/books/selectChapter.js | 78 ++--- .../js/books/selectChapterTop-v3.js | 74 ++-- .../hedonometer/js/books/selectChapterTop.js | 74 ++-- .../js/drawBookTimeseries-harry.js | 322 ++++++++---------- .../hedonometer/books/gutenberg.html | 8 +- .../hedonometer/books/harrypotter.html | 8 +- .../templates/hedonometer/books/v3.html | 8 +- 14 files changed, 516 insertions(+), 611 deletions(-) diff --git a/hedonometer/static/hedonometer/js/books-on-load-harry.js b/hedonometer/static/hedonometer/js/books-on-load-harry.js index d26e43ee..c7595435 100644 --- a/hedonometer/static/hedonometer/js/books-on-load-harry.js +++ b/hedonometer/static/hedonometer/js/books-on-load-harry.js @@ -40,7 +40,7 @@ function initializePlot() { book = bookDecoder().cached; // console.log("not a classic"); // hit the random api - d3.json("https://hedonometer.org/api/v1/gutenberg/?format=json&title__exact="+book,function(data) { + d3.json("https://hedonometer.org/api/v1/gutenberg/?format=json&title__exact="+book).then(function(data) { var result = data.objects[0]; console.log(result); lang = result.language; @@ -71,7 +71,7 @@ function initializePlot() { function loadCsv() { var csvLoadsRemaining = 4; var bookfile = "https://hedonometer.org/data/bookdata/timeseries/"+bookref+".csv"; - d3.text(bookfile, function (text) { + d3.text(bookfile).then(function (text) { var tmpminwin = 10; fulltimeseries = text.split(",").map(parseFloat); begtimeseries = fulltimeseries.slice(0,tmpminwin/2+1); @@ -79,7 +79,7 @@ function loadCsv() { timeseries = fulltimeseries.slice(tmpminwin/2,fulltimeseries.length-tmpminwin/2); if (!--csvLoadsRemaining) initializePlotPlot(); }); - d3.text("https://hedonometer.org/data/bookdata/labMT/labMTscores-"+lang+".csv", function (text) { + d3.text("https://hedonometer.org/data/bookdata/labMT/labMTscores-"+lang+".csv").then(function (text) { var tmp = text.split("\n"); //console.log(tmp.length); //console.log(tmp[tmp.length-1]); @@ -93,7 +93,7 @@ function loadCsv() { hedotools.shifter._lens(lens); if (!--csvLoadsRemaining) initializePlotPlot(); }); - d3.text("https://hedonometer.org/data/bookdata/labMT/labMTwords-"+lang+".csv", function (text) { + d3.text("https://hedonometer.org/data/bookdata/labMT/labMTwords-"+lang+".csv").then(function (text) { var tmp = text.split("\n"); var words = tmp; var len = words.length - 1; @@ -105,7 +105,7 @@ function loadCsv() { hedotools.shifter._words(words); if (!--csvLoadsRemaining) initializePlotPlot(); }); - d3.text("https://hedonometer.org/data/bookdata/labMT/labMTwordsEn-"+lang+".csv", function (text) { + d3.text("https://hedonometer.org/data/bookdata/labMT/labMTwordsEn-"+lang+".csv").then(function (text) { var tmp = text.split("\n"); var words_en = tmp; var len = words_en.length - 1; @@ -189,7 +189,7 @@ var loadwordshiftdata = function() { var spinner = new Spinner(opts).spin(target); var bookfile = "https://hedonometer.org/data/bookdata/word-vectors/"+bookref+".csv"; - d3.text(bookfile, function (text) { + d3.text(bookfile).then(function (text) { tmp = text.split("\n"); // build the full data, terrible @@ -400,7 +400,7 @@ var substringMatcher = function(apik) { // } // } // if (matches.length === 0) { matches.push({ value: "book not indexed" }); } - d3.json("https://hedonometer.org/api/v1/gutenberg/?format=json&"+apik.toLowerCase()+"__icontains="+q,function(data) { + d3.json("https://hedonometer.org/api/v1/gutenberg/?format=json&"+apik.toLowerCase()+"__icontains="+q).then(function(data) { var result = data.objects; // console.log(result); var newresult = []; @@ -424,7 +424,7 @@ $(document).ready(function() { // window.location.replace("/books.html?book=random"); // but here, it's easier to make that API hit now and send the page // directly: - d3.json("https://hedonometer.org/api/v1/randombook/?format=json",function(data) { + d3.json("https://hedonometer.org/api/v1/randombook/?format=json").then(function(data) { var result = data.objects[0]; window.location.replace("/books/"+result.title+"/"); }) diff --git a/hedonometer/static/hedonometer/js/books/books-on-load-v3.js b/hedonometer/static/hedonometer/js/books/books-on-load-v3.js index fdd7df9b..ebdfd1dc 100644 --- a/hedonometer/static/hedonometer/js/books/books-on-load-v3.js +++ b/hedonometer/static/hedonometer/js/books/books-on-load-v3.js @@ -30,7 +30,7 @@ function commaSeparateNumber(val){ var my_shifter; function initializePlot() { - my_shifter = hedotools.shifter(); + my_shifter = hedotools.shifter; loadCsv(); } @@ -44,7 +44,7 @@ function loadCsv() { --csvLoadsRemaining; } else { - d3.text("http://hedonometer.org/data/bookdata/labMT/labMTwordsEn-"+bookinfo.lang+".csv", function (text) { + d3.text("http://hedonometer.org/data/bookdata/labMT/labMTwordsEn-"+bookinfo.lang+".csv").then(function (text) { var tmp = text.split("\n"); words_en = tmp; var len = words_en.length - 1; @@ -57,16 +57,7 @@ function loadCsv() { if (!--csvLoadsRemaining) initializePlotPlot(); }); } - d3.text(bookfile, function (error,text) { - if (error) { - missing_file = true; - console.log("file is missing"); - bookinfo.avhapps = "N/A"; - bookinfo.len = "N/A"; - cat_card(bookinfo); - d3.select("#booktitle").append("h4").html("** book file not available"); - return; - } + d3.text(bookfile).then(function (text) { tmp = text.split("\n"); // kill extra rows var len = tmp.length - 1; @@ -86,8 +77,16 @@ function loadCsv() { if (bookinfo.length < 10000) { alert("There are too few words in this book for the hedonometer to accurately generate a timeseries. Currently we need at least 10000 words, and this book has "+bookinfo.length+"."); } //console.log(d3.sum(allDataRaw[0])); if (!--csvLoadsRemaining) initializePlotPlot(); + }).catch(function (error) { + missing_file = true; + console.log("file is missing"); + bookinfo.avhapps = "N/A"; + bookinfo.len = "N/A"; + cat_card(bookinfo); + d3.select("#booktitle").append("h4").html("** book file not available"); + return; }); - d3.text("http://hedonometer.org/data/bookdata/labMT/labMTscores-"+bookinfo.lang+".csv", function (text) { + d3.text("http://hedonometer.org/data/bookdata/labMT/labMTscores-"+bookinfo.lang+".csv").then(function (text) { var tmp = text.split("\n"); //console.log(tmp.length); //console.log(tmp[tmp.length-1]); @@ -101,7 +100,7 @@ function loadCsv() { my_shifter._lens(lens); if (!--csvLoadsRemaining) initializePlotPlot(); }); - d3.text("http://hedonometer.org/data/bookdata/labMT/labMTwords-"+bookinfo.lang+".csv", function (text) { + d3.text("http://hedonometer.org/data/bookdata/labMT/labMTwords-"+bookinfo.lang+".csv").then(function (text) { var tmp = text.split("\n"); words = tmp; var len = words.length - 1; @@ -251,7 +250,7 @@ var substringMatcher = function(apik1,apik2) { // } // } // if (matches.length === 0) { matches.push({ value: "book not indexed" }); } - d3.json(query_string+q,function(data) { + d3.json(query_string+q).then(function(data) { var result = data.objects; console.log(result); var newresult = []; @@ -282,7 +281,7 @@ $(document).ready(function() { window.location.replace("/books/v3/"+(Math.floor((Math.random() * 51249) + 1)).toFixed(0)+"/"); } else { - d3.text("/static/hedonometer/gut_ids.txt", function (text) { + d3.text("/static/hedonometer/gut_ids.txt").then(function (text) { var tmp = text.split("\n"); var rand_ind = Math.floor((Math.random() * tmp.length)); window.location.replace("/books/v3/"+tmp[rand_ind]+"/"); diff --git a/hedonometer/static/hedonometer/js/books/drawBookTimeseries-v3.js b/hedonometer/static/hedonometer/js/books/drawBookTimeseries-v3.js index 2a7b5502..147c2444 100644 --- a/hedonometer/static/hedonometer/js/books/drawBookTimeseries-v3.js +++ b/hedonometer/static/hedonometer/js/books/drawBookTimeseries-v3.js @@ -34,7 +34,7 @@ function drawBookTimeseries(figure,data) { //console.log(data.length); // create the x and y axis - var x = d3.scale.linear() + var x = d3.scaleLinear() //.domain([d3.min(lens),d3.max(lens)]) // map from the start of the timeseries point to the max .domain([-minWindows/2,data.length+minWindows/2-1]) @@ -46,7 +46,7 @@ function drawBookTimeseries(figure,data) { // (lens); // linear scale function - var y = d3.scale.linear() + var y = d3.scaleLinear() .domain([d3.min(data),d3.max(data)]) .range([height-10, 10]); @@ -65,27 +65,24 @@ function drawBookTimeseries(figure,data) { .attr("width", width) .attr("height", height) .attr("class", "bg") - .style({'stroke-width':'2','stroke':'rgb(0,0,0)'}) + .style('stroke-width','2') + .style('stroke','rgb(0,0,0)') .attr("fill", "#FCFCFC"); // axes creation functions var create_xAxis = function() { - return d3.svg.axis() - .scale(x) - .ticks(9) - .orient("bottom"); } + return d3.axisBottom(x) + .ticks(9); } // axis creation function var create_yAxis = function() { - return d3.svg.axis() - .ticks(5) - .scale(y) //linear scale function - .orient("left"); } + return d3.axisLeft(y) + .ticks(5); } // draw the axes var yAxis = create_yAxis() - .innerTickSize(6) - .outerTickSize(0); + .tickSizeInner(6) + .tickSizeOuter(0); axes.append("g") .attr("class", "top") @@ -107,11 +104,11 @@ function drawBookTimeseries(figure,data) { // axes = axes.append("g") // .attr("clip-path","url(#clip)"); - var line = d3.svg.line() + var line = d3.line() .x(function(d,i) { return x(i); }) .y(function(d) { return y(d); }) - .interpolate("cardinal"); - // .interpolate("linear"); + .curve(d3.curveCardinal); + // .curve(d3.curveLinear); var mainline = axes.append("path") .datum(data) @@ -121,11 +118,11 @@ function drawBookTimeseries(figure,data) { .attr("stroke-width",3) .attr("fill","none"); - var beglineline = d3.svg.line() + var beglineline = d3.line() .x(function(d,i) { return x(i-minWindows/2); }) .y(function(d) { return y(d); }) - .interpolate("cardinal"); - // .interpolate("linear"); + .curve(d3.curveCardinal); + // .curve(d3.curveLinear); begtimeseries.push(data[0]); @@ -138,11 +135,11 @@ function drawBookTimeseries(figure,data) { .attr("stroke-width",3) .attr("fill","none"); - var endlineline = d3.svg.line() + var endlineline = d3.line() .x(function(d,i) { return x(i+data.length-1); }) .y(function(d) { return y(d); }) - .interpolate("cardinal"); - // .interpolate("linear"); + .curve(d3.curveCardinal); + // .curve(d3.curveLinear); endtimeseries.unshift(data[data.length-1]); @@ -155,7 +152,7 @@ function drawBookTimeseries(figure,data) { .attr("stroke-width",3) .attr("fill","none"); - var area = d3.svg.area() + var area = d3.area() .x(function(d,i) { return x(i); }) .y0(height-1) .y1(function(d) { return y(d); }); @@ -170,7 +167,7 @@ function drawBookTimeseries(figure,data) { drawRefArea = function drawRefArea(extent) { - var refarea = d3.svg.area() + var refarea = d3.area() .x(function(d,i) { return x(extent[0]+i-minWindows/2); }) .y0(height-1) .y1(function(d) { return y(d)+2; }); @@ -191,7 +188,7 @@ function drawBookTimeseries(figure,data) { drawCompArea = function drawCompArea(extent) { - var comparea = d3.svg.area() + var comparea = d3.area() .x(function(d,i) { return x(extent[0]+i-minWindows/2-1); }) .y0(height-1) .y1(function(d) { return y(d)+2; }); @@ -213,10 +210,10 @@ function drawBookTimeseries(figure,data) { // console.log(d3.mean(data)); var avhapps = d3.mean(data); - var linearline = d3.svg.line() + var linearline = d3.line() .x(function(d,i) { if (i===0) { return x(d.index); } else { return x(d.index)+3 } }) .y(function(d) { return y(d.value); }) - .interpolate("linear"); + .curve(d3.curveLinear); var averageline = axes.append("path") .datum([ @@ -233,20 +230,18 @@ function drawBookTimeseries(figure,data) { .attr("fill","none"); var averagetext1 = axes.append("text") - .attr({ "x": width+5, - "y": y(avhapps)-3, - "fill": "#606060", - "text-anchor": "start", - }) + .attr("x", width+5) + .attr("y", y(avhapps)-3) + .attr("fill", "#606060") + .attr("text-anchor", "start") .text("Average"); var averagetext2 = axes.append("text") - .attr({ "x": width+5, - "y": y(avhapps)+12, - "fill": "#606060", - "font-weight": "bold", - "text-anchor": "start", - }) + .attr("x", width+5) + .attr("y", y(avhapps)+12) + .attr("fill", "#606060") + .attr("font-weight", "bold") + .attr("text-anchor", "start") .text(avhapps.toFixed(2)); // console.log(d3.min(data)); @@ -285,20 +280,18 @@ function drawBookTimeseries(figure,data) { .attr("fill","none"); var mintext1 = axes.append("text") - .attr({ "x": width+5, - "y": y(minhapps)-3, - "fill": "#606060", - "text-anchor": "start", - }) + .attr("x", width+5) + .attr("y", y(minhapps)-3) + .attr("fill", "#606060") + .attr("text-anchor", "start") .text("Least Happy"); var mintext2 = axes.append("text") - .attr({ "x": width+5, - "y": y(minhapps)+12, - "fill": "#606060", - "font-weight": "bold", - "text-anchor": "start", - }) + .attr("x", width+5) + .attr("y", y(minhapps)+12) + .attr("fill", "#606060") + .attr("font-weight", "bold") + .attr("text-anchor", "start") .text(minhapps.toFixed(2)); var maxcircle = axes.append("circle") @@ -323,20 +316,18 @@ function drawBookTimeseries(figure,data) { .attr("fill","none"); var mintext1 = axes.append("text") - .attr({ "x": width+5, - "y": y(maxhapps), - "fill": "#606060", - "text-anchor": "start", - }) + .attr("x", width+5) + .attr("y", y(maxhapps)) + .attr("fill", "#606060") + .attr("text-anchor", "start") .text("Happiest"); var maxtext2 = axes.append("text") - .attr({ "x": width+5, - "y": y(maxhapps)+15, - "fill": "#606060", - "font-weight": "bold", - "text-anchor": "start", - }) + .attr("x", width+5) + .attr("y", y(maxhapps)+15) + .attr("fill", "#606060") + .attr("font-weight", "bold") + .attr("text-anchor", "start") .text(maxhapps.toFixed(2)); d3.select(window).on("resize.booktimeseries",resize); diff --git a/hedonometer/static/hedonometer/js/books/drawBookTimeseries.js b/hedonometer/static/hedonometer/js/books/drawBookTimeseries.js index 2a7b5502..147c2444 100644 --- a/hedonometer/static/hedonometer/js/books/drawBookTimeseries.js +++ b/hedonometer/static/hedonometer/js/books/drawBookTimeseries.js @@ -34,7 +34,7 @@ function drawBookTimeseries(figure,data) { //console.log(data.length); // create the x and y axis - var x = d3.scale.linear() + var x = d3.scaleLinear() //.domain([d3.min(lens),d3.max(lens)]) // map from the start of the timeseries point to the max .domain([-minWindows/2,data.length+minWindows/2-1]) @@ -46,7 +46,7 @@ function drawBookTimeseries(figure,data) { // (lens); // linear scale function - var y = d3.scale.linear() + var y = d3.scaleLinear() .domain([d3.min(data),d3.max(data)]) .range([height-10, 10]); @@ -65,27 +65,24 @@ function drawBookTimeseries(figure,data) { .attr("width", width) .attr("height", height) .attr("class", "bg") - .style({'stroke-width':'2','stroke':'rgb(0,0,0)'}) + .style('stroke-width','2') + .style('stroke','rgb(0,0,0)') .attr("fill", "#FCFCFC"); // axes creation functions var create_xAxis = function() { - return d3.svg.axis() - .scale(x) - .ticks(9) - .orient("bottom"); } + return d3.axisBottom(x) + .ticks(9); } // axis creation function var create_yAxis = function() { - return d3.svg.axis() - .ticks(5) - .scale(y) //linear scale function - .orient("left"); } + return d3.axisLeft(y) + .ticks(5); } // draw the axes var yAxis = create_yAxis() - .innerTickSize(6) - .outerTickSize(0); + .tickSizeInner(6) + .tickSizeOuter(0); axes.append("g") .attr("class", "top") @@ -107,11 +104,11 @@ function drawBookTimeseries(figure,data) { // axes = axes.append("g") // .attr("clip-path","url(#clip)"); - var line = d3.svg.line() + var line = d3.line() .x(function(d,i) { return x(i); }) .y(function(d) { return y(d); }) - .interpolate("cardinal"); - // .interpolate("linear"); + .curve(d3.curveCardinal); + // .curve(d3.curveLinear); var mainline = axes.append("path") .datum(data) @@ -121,11 +118,11 @@ function drawBookTimeseries(figure,data) { .attr("stroke-width",3) .attr("fill","none"); - var beglineline = d3.svg.line() + var beglineline = d3.line() .x(function(d,i) { return x(i-minWindows/2); }) .y(function(d) { return y(d); }) - .interpolate("cardinal"); - // .interpolate("linear"); + .curve(d3.curveCardinal); + // .curve(d3.curveLinear); begtimeseries.push(data[0]); @@ -138,11 +135,11 @@ function drawBookTimeseries(figure,data) { .attr("stroke-width",3) .attr("fill","none"); - var endlineline = d3.svg.line() + var endlineline = d3.line() .x(function(d,i) { return x(i+data.length-1); }) .y(function(d) { return y(d); }) - .interpolate("cardinal"); - // .interpolate("linear"); + .curve(d3.curveCardinal); + // .curve(d3.curveLinear); endtimeseries.unshift(data[data.length-1]); @@ -155,7 +152,7 @@ function drawBookTimeseries(figure,data) { .attr("stroke-width",3) .attr("fill","none"); - var area = d3.svg.area() + var area = d3.area() .x(function(d,i) { return x(i); }) .y0(height-1) .y1(function(d) { return y(d); }); @@ -170,7 +167,7 @@ function drawBookTimeseries(figure,data) { drawRefArea = function drawRefArea(extent) { - var refarea = d3.svg.area() + var refarea = d3.area() .x(function(d,i) { return x(extent[0]+i-minWindows/2); }) .y0(height-1) .y1(function(d) { return y(d)+2; }); @@ -191,7 +188,7 @@ function drawBookTimeseries(figure,data) { drawCompArea = function drawCompArea(extent) { - var comparea = d3.svg.area() + var comparea = d3.area() .x(function(d,i) { return x(extent[0]+i-minWindows/2-1); }) .y0(height-1) .y1(function(d) { return y(d)+2; }); @@ -213,10 +210,10 @@ function drawBookTimeseries(figure,data) { // console.log(d3.mean(data)); var avhapps = d3.mean(data); - var linearline = d3.svg.line() + var linearline = d3.line() .x(function(d,i) { if (i===0) { return x(d.index); } else { return x(d.index)+3 } }) .y(function(d) { return y(d.value); }) - .interpolate("linear"); + .curve(d3.curveLinear); var averageline = axes.append("path") .datum([ @@ -233,20 +230,18 @@ function drawBookTimeseries(figure,data) { .attr("fill","none"); var averagetext1 = axes.append("text") - .attr({ "x": width+5, - "y": y(avhapps)-3, - "fill": "#606060", - "text-anchor": "start", - }) + .attr("x", width+5) + .attr("y", y(avhapps)-3) + .attr("fill", "#606060") + .attr("text-anchor", "start") .text("Average"); var averagetext2 = axes.append("text") - .attr({ "x": width+5, - "y": y(avhapps)+12, - "fill": "#606060", - "font-weight": "bold", - "text-anchor": "start", - }) + .attr("x", width+5) + .attr("y", y(avhapps)+12) + .attr("fill", "#606060") + .attr("font-weight", "bold") + .attr("text-anchor", "start") .text(avhapps.toFixed(2)); // console.log(d3.min(data)); @@ -285,20 +280,18 @@ function drawBookTimeseries(figure,data) { .attr("fill","none"); var mintext1 = axes.append("text") - .attr({ "x": width+5, - "y": y(minhapps)-3, - "fill": "#606060", - "text-anchor": "start", - }) + .attr("x", width+5) + .attr("y", y(minhapps)-3) + .attr("fill", "#606060") + .attr("text-anchor", "start") .text("Least Happy"); var mintext2 = axes.append("text") - .attr({ "x": width+5, - "y": y(minhapps)+12, - "fill": "#606060", - "font-weight": "bold", - "text-anchor": "start", - }) + .attr("x", width+5) + .attr("y", y(minhapps)+12) + .attr("fill", "#606060") + .attr("font-weight", "bold") + .attr("text-anchor", "start") .text(minhapps.toFixed(2)); var maxcircle = axes.append("circle") @@ -323,20 +316,18 @@ function drawBookTimeseries(figure,data) { .attr("fill","none"); var mintext1 = axes.append("text") - .attr({ "x": width+5, - "y": y(maxhapps), - "fill": "#606060", - "text-anchor": "start", - }) + .attr("x", width+5) + .attr("y", y(maxhapps)) + .attr("fill", "#606060") + .attr("text-anchor", "start") .text("Happiest"); var maxtext2 = axes.append("text") - .attr({ "x": width+5, - "y": y(maxhapps)+15, - "fill": "#606060", - "font-weight": "bold", - "text-anchor": "start", - }) + .attr("x", width+5) + .attr("y", y(maxhapps)+15) + .attr("fill", "#606060") + .attr("font-weight", "bold") + .attr("text-anchor", "start") .text(maxhapps.toFixed(2)); d3.select(window).on("resize.booktimeseries",resize); diff --git a/hedonometer/static/hedonometer/js/books/drawLens-chapters.js b/hedonometer/static/hedonometer/js/books/drawLens-chapters.js index 112b46b3..9fc5e556 100644 --- a/hedonometer/static/hedonometer/js/books/drawLens-chapters.js +++ b/hedonometer/static/hedonometer/js/books/drawLens-chapters.js @@ -24,20 +24,22 @@ // create the x and y axis - var x = d3.scale.linear() + var x = d3.scaleLinear() //.domain([d3.min(lens),d3.max(lens)]) .domain([1.00,9.00]) .range([0,width]); - // use d3.layout http://bl.ocks.org/mbostock/3048450 - var data = d3.layout.histogram() - .bins(x.ticks(65)) + // use d3.bin (v7 replacement for d3.layout.histogram); bins are arrays with + // .x0/.x1 (edges) and .length (count) instead of v3's .x/.dx/.y + var data = d3.bin() + .domain(x.domain()) + .thresholds(x.ticks(65)) (lens); // linear scale function - var y = d3.scale.linear() - .domain([0,d3.max(data,function(d) { return d.y; } )]) - .range([height, 0]); + var y = d3.scaleLinear() + .domain([0,d3.max(data,function(d) { return d.length; } )]) + .range([height, 0]); // create the axes themselves var axes = canvas.append("g") @@ -52,27 +54,24 @@ .attr("width", width) .attr("height", height) .attr("class", "bg") - .style({'stroke-width':'2','stroke':'rgb(0,0,0)'}) + .style('stroke-width','2') + .style('stroke','rgb(0,0,0)') .attr("fill", "#FFFFF0"); // axes creation functions var create_xAxis = function() { - return d3.svg.axis() - .scale(x) - .ticks(9) - .orient("bottom"); } + return d3.axisBottom(x) + .ticks(9); } // axis creation function var create_yAxis = function() { - return d3.svg.axis() - .ticks(3) - .scale(y) //linear scale function - .orient("left"); } + return d3.axisLeft(y) + .ticks(3); } // draw the axes var yAxis = create_yAxis() - .innerTickSize(6) - .outerTickSize(0); + .tickSizeInner(6) + .tickSizeOuter(0); axes.append("g") .attr("class", "top") @@ -81,8 +80,8 @@ .call(yAxis); var xAxis = create_xAxis() - .innerTickSize(6) - .outerTickSize(0); + .tickSizeInner(6) + .tickSizeOuter(0); axes.append("g") .attr("class", "x axis ") @@ -90,7 +89,7 @@ .attr("transform", "translate(0," + (height) + ")") .call(xAxis); - d3.selectAll(".tick line").style({'stroke':'black'}); + d3.selectAll(".tick line").style('stroke','black'); // create the clip boundary var clip = axes.append("svg:clipPath") @@ -131,18 +130,18 @@ .enter() .append("g") .attr("class","distrect") - .attr("fill",function(d,i) { if (d.x > lensMean) {return "#D3D3D3";} else { return "#D3D3D3";}}) - .attr("transform", function(d) { return "translate(" + x(d.x) + "," + y(d.y) + ")"; }); + .attr("fill",function(d,i) { if (d.x0 > lensMean) {return "#D3D3D3";} else { return "#D3D3D3";}}) + .attr("transform", function(d) { return "translate(" + x(d.x0) + "," + y(d.length) + ")"; }); var mainrect = bar.append("rect") .attr("x", 1) - .attr("width", x(data[0].dx+1)-2 ) - .attr("height", function(d) { return height - y(d.y); }); + .attr("width", x(data[0].x1-data[0].x0+1)-2 ) + .attr("height", function(d) { return height - y(d.length); }); - var line = d3.svg.line() - .x(function(d,i) { return x(d.x); }) - .y(function(d) { return y(d.y); }) - .interpolate("linear"); + var line = d3.line() + .x(function(d,i) { return x(d.x0); }) + .y(function(d) { return y(d.length); }) + .curve(d3.curveLinear); var mainline = axes.append("path") .datum(data) @@ -154,34 +153,28 @@ //console.log(x(d3.min(lens))); - var brushX = d3.scale.linear() + var brushX = d3.scaleLinear() .domain([1,9]) .range([figwidth*.125,width+figwidth*.125]); - var brush = d3.svg.brush() - .x(brushX) - .extent(lensExtent) - .on("brushend",brushended); + // d3 v7 brushX: selection is in pixels; convert with brushX.invert. + var brush = d3.brushX() + .extent([[figwidth*.125, 15], [width+figwidth*.125, height+15]]) + .on("end",brushended); var gBrush = canvas.append("g") .attr("class","lensbrush") - .call(brush) - .call(brush.event); - - gBrush.selectAll("rect") - .attr("height",height) - .attr("y",15) - .style({'stroke-width':'2','stroke':'rgb(100,100,100)','opacity': 0.95}) - .attr("fill", "#FCFCFC"); + .call(brush); + gBrush.call(brush.move, lensExtent.map(brushX)); //console.log(lensExtent); lensencoder = d3.urllib.encoder().varname("lens"); //.varval(lensExtent); - function brushended() { - if (!d3.event.sourceEvent) return; - var extent0 = brush.extent(), - extent1 = extent0; + function brushended(event) { + if (!event.sourceEvent) return; + if (!event.selection) return; + var extent1 = event.selection.map(brushX.invert); // round to nearest tenth (set 4 to 10) // round to nearest quarter @@ -208,8 +201,7 @@ } d3.select(this).transition() - .call(brush.extent(lensExtent)) - .call(brush.event); + .call(brush.move, lensExtent.map(brushX)); lensencoder.varval(lensExtent); } @@ -233,20 +225,19 @@ //xAxisHandle.call(xAxis); canvas.select(".x.axis").call(xAxis); - canvas.selectAll(".distrect").attr("transform", function(d) { return "translate(" + x(d.x) + "," + y(d.y) + ")"; }); + canvas.selectAll(".distrect").attr("transform", function(d) { return "translate(" + x(d.x0) + "," + y(d.length) + ")"; }); // xlabel.attr("x",(leftOffsetStatic+width/2)); - d3.selectAll(".tick line").style({'stroke':'black'}); + d3.selectAll(".tick line").style('stroke','black'); // //brushX.range([figwidth*.125,width+figwidth*.125]); brushX.range([leftOffsetStatic,leftOffsetStatic+width]); - brush.x(brushX); - d3.select(".lensbrush") //.transition() - .call(brush.extent(lensExtent)) - .call(brush.event); + brush.extent([[leftOffsetStatic, 15], [leftOffsetStatic+width, height+15]]); + d3.select(".lensbrush") + .call(brush) + .call(brush.move, lensExtent.map(brushX)); //brushing(); - //brush.event(); } } diff --git a/hedonometer/static/hedonometer/js/books/drawLens-v3.js b/hedonometer/static/hedonometer/js/books/drawLens-v3.js index e76d1a8c..4e190fac 100644 --- a/hedonometer/static/hedonometer/js/books/drawLens-v3.js +++ b/hedonometer/static/hedonometer/js/books/drawLens-v3.js @@ -1,10 +1,11 @@ -function drawLens(figure,lens) { + function drawLens(figure,lens) { /* takes a d3 selection and draws the lens distribution on slide of the stop-window -reload data csv's -cut out stops words (0 the frequencies) -call shift on these frequency vectors */ + var margin = {top: 0, right: 0, bottom: 0, left: 0}, figwidth = parseInt(d3.select('#lens01').style('width')) - margin.left - margin.right, figheight = 150 - margin.top - margin.bottom, @@ -23,20 +24,22 @@ function drawLens(figure,lens) { // create the x and y axis - var x = d3.scale.linear() + var x = d3.scaleLinear() //.domain([d3.min(lens),d3.max(lens)]) .domain([1.00,9.00]) .range([0,width]); - // use d3.layout http://bl.ocks.org/mbostock/3048450 - var data = d3.layout.histogram() - .bins(x.ticks(65)) + // use d3.bin (v7 replacement for d3.layout.histogram); bins are arrays with + // .x0/.x1 (edges) and .length (count) instead of v3's .x/.dx/.y + var data = d3.bin() + .domain(x.domain()) + .thresholds(x.ticks(65)) (lens); // linear scale function - var y = d3.scale.linear() - .domain([0,d3.max(data,function(d) { return d.y; } )]) - .range([height, 0]); + var y = d3.scaleLinear() + .domain([0,d3.max(data,function(d) { return d.length; } )]) + .range([height, 0]); // create the axes themselves var axes = canvas.append("g") @@ -51,27 +54,24 @@ function drawLens(figure,lens) { .attr("width", width) .attr("height", height) .attr("class", "bg") - .style({'stroke-width':'2','stroke':'rgb(0,0,0)'}) + .style('stroke-width','2') + .style('stroke','rgb(0,0,0)') .attr("fill", "#FFFFF0"); // axes creation functions var create_xAxis = function() { - return d3.svg.axis() - .scale(x) - .ticks(9) - .orient("bottom"); } + return d3.axisBottom(x) + .ticks(9); } // axis creation function var create_yAxis = function() { - return d3.svg.axis() - .ticks(3) - .scale(y) //linear scale function - .orient("left"); } + return d3.axisLeft(y) + .ticks(3); } // draw the axes var yAxis = create_yAxis() - .innerTickSize(6) - .outerTickSize(0); + .tickSizeInner(6) + .tickSizeOuter(0); axes.append("g") .attr("class", "top") @@ -80,8 +80,8 @@ function drawLens(figure,lens) { .call(yAxis); var xAxis = create_xAxis() - .innerTickSize(6) - .outerTickSize(0); + .tickSizeInner(6) + .tickSizeOuter(0); axes.append("g") .attr("class", "x axis ") @@ -89,7 +89,7 @@ function drawLens(figure,lens) { .attr("transform", "translate(0," + (height) + ")") .call(xAxis); - d3.selectAll(".tick line").style({'stroke':'black'}); + d3.selectAll(".tick line").style('stroke','black'); // create the clip boundary var clip = axes.append("svg:clipPath") @@ -130,18 +130,18 @@ function drawLens(figure,lens) { .enter() .append("g") .attr("class","distrect") - .attr("fill",function(d,i) { if (d.x > lensMean) {return "#D3D3D3";} else { return "#D3D3D3";}}) - .attr("transform", function(d) { return "translate(" + x(d.x) + "," + y(d.y) + ")"; }); + .attr("fill",function(d,i) { if (d.x0 > lensMean) {return "#D3D3D3";} else { return "#D3D3D3";}}) + .attr("transform", function(d) { return "translate(" + x(d.x0) + "," + y(d.length) + ")"; }); var mainrect = bar.append("rect") .attr("x", 1) - .attr("width", x(data[0].dx+1)-2 ) - .attr("height", function(d) { return height - y(d.y); }); + .attr("width", x(data[0].x1-data[0].x0+1)-2 ) + .attr("height", function(d) { return height - y(d.length); }); - var line = d3.svg.line() - .x(function(d,i) { return x(d.x); }) - .y(function(d) { return y(d.y); }) - .interpolate("linear"); + var line = d3.line() + .x(function(d,i) { return x(d.x0); }) + .y(function(d) { return y(d.length); }) + .curve(d3.curveLinear); var mainline = axes.append("path") .datum(data) @@ -153,34 +153,28 @@ function drawLens(figure,lens) { //console.log(x(d3.min(lens))); - var brushX = d3.scale.linear() + var brushX = d3.scaleLinear() .domain([1,9]) .range([figwidth*.125,width+figwidth*.125]); - var brush = d3.svg.brush() - .x(brushX) - .extent(lensExtent) - .on("brushend",brushended); + // d3 v7 brushX: selection is in pixels; convert with brushX.invert. + var brush = d3.brushX() + .extent([[figwidth*.125, 15], [width+figwidth*.125, height+15]]) + .on("end",brushended); var gBrush = canvas.append("g") .attr("class","lensbrush") - .call(brush) - .call(brush.event); - - gBrush.selectAll("rect") - .attr("height",height) - .attr("y",15) - .style({'stroke-width':'2','stroke':'rgb(100,100,100)','opacity': 0.95}) - .attr("fill", "#FCFCFC"); + .call(brush); + gBrush.call(brush.move, lensExtent.map(brushX)); //console.log(lensExtent); lensencoder = d3.urllib.encoder().varname("lens"); //.varval(lensExtent); - function brushended() { - if (!d3.event.sourceEvent) return; - var extent0 = brush.extent(), - extent1 = extent0; + function brushended(event) { + if (!event.sourceEvent) return; + if (!event.selection) return; + var extent1 = event.selection.map(brushX.invert); // round to nearest tenth (set 4 to 10) // round to nearest quarter @@ -200,16 +194,14 @@ function drawLens(figure,lens) { my_shifter.shifter(); var happysad = my_shifter._compH() > my_shifter._refH() ? "happier" : "less happy"; var shifttext = ["Why comparison section is "+happysad+" than reference section:","Reference section's happiness: "+my_shifter._refH().toFixed(2),"Comparison section's happiness: "+my_shifter._compH().toFixed(2)] - my_shifter.setText(shifttext); - my_shifter.plot(); + my_shifter.setText(shifttext).plot(); // set the lens extent in the browser, if it didn't already exist // break down the current window.location } d3.select(this).transition() - .call(brush.extent(lensExtent)) - .call(brush.event); + .call(brush.move, lensExtent.map(brushX)); lensencoder.varval(lensExtent); } @@ -233,20 +225,19 @@ function drawLens(figure,lens) { //xAxisHandle.call(xAxis); canvas.select(".x.axis").call(xAxis); - canvas.selectAll(".distrect").attr("transform", function(d) { return "translate(" + x(d.x) + "," + y(d.y) + ")"; }); + canvas.selectAll(".distrect").attr("transform", function(d) { return "translate(" + x(d.x0) + "," + y(d.length) + ")"; }); // xlabel.attr("x",(leftOffsetStatic+width/2)); - d3.selectAll(".tick line").style({'stroke':'black'}); + d3.selectAll(".tick line").style('stroke','black'); // //brushX.range([figwidth*.125,width+figwidth*.125]); brushX.range([leftOffsetStatic,leftOffsetStatic+width]); - brush.x(brushX); - d3.select(".lensbrush") //.transition() - .call(brush.extent(lensExtent)) - .call(brush.event); + brush.extent([[leftOffsetStatic, 15], [leftOffsetStatic+width, height+15]]); + d3.select(".lensbrush") + .call(brush) + .call(brush.move, lensExtent.map(brushX)); //brushing(); - //brush.event(); } } diff --git a/hedonometer/static/hedonometer/js/books/selectChapter-v3.js b/hedonometer/static/hedonometer/js/books/selectChapter-v3.js index 1f4bae8d..26b729af 100644 --- a/hedonometer/static/hedonometer/js/books/selectChapter-v3.js +++ b/hedonometer/static/hedonometer/js/books/selectChapter-v3.js @@ -23,7 +23,7 @@ function selectChapter(figure,numSections) { .attr("class","canvas"); // create the x and y axis - var x = d3.scale.linear() + var x = d3.scaleLinear() //.domain([d3.min(lens),d3.max(lens)]) .domain([0,100]) .range([0,width]); @@ -34,7 +34,7 @@ function selectChapter(figure,numSections) { // (lens); // linear scale function - var y = d3.scale.linear() + var y = d3.scaleLinear() .domain([0,1]) .range([height, 0]); @@ -51,22 +51,19 @@ function selectChapter(figure,numSections) { .attr("width", width) .attr("height", height) .attr("class", "bg") - .style({'stroke-width':'2','stroke':'rgb(0,0,0)'}) + .style('stroke-width','2') + .style('stroke','rgb(0,0,0)') .attr("fill", "#FCFCFC"); // axes creation functions var create_xAxis = function() { - return d3.svg.axis() - .scale(x) - .ticks(9) - .orient("bottom"); } + return d3.axisBottom(x) + .ticks(9); } // axis creation function var create_yAxis = function() { - return d3.svg.axis() - .ticks(3) - .scale(y) //linear scale function - .orient("left"); } + return d3.axisLeft(y) + .ticks(3); } // draw the axes // var yAxis = create_yAxis() @@ -80,8 +77,8 @@ function selectChapter(figure,numSections) { // .call(yAxis); var xAxis = create_xAxis() - .innerTickSize(6) - .outerTickSize(0); + .tickSizeInner(6) + .tickSizeOuter(0); var xAxisHandle = axes.append("g") .attr("class", "x axis ") @@ -89,7 +86,7 @@ function selectChapter(figure,numSections) { .attr("transform", "translate(0," + (height) + ")") .call(xAxis); - d3.selectAll(".tick line").style({'stroke':'black'}); + d3.selectAll(".tick line").style('stroke','black'); // create the clip boundary var clip = axes.append("svg:clipPath") @@ -123,7 +120,7 @@ function selectChapter(figure,numSections) { .attr("fill", "#000000") .attr("style", "text-anchor: middle;"); - var brushX = d3.scale.linear() + var brushX = d3.scaleLinear() .domain([0,allDataRaw.length]) .range([axeslabelmargin.left,width+axeslabelmargin.left]); @@ -136,31 +133,26 @@ function selectChapter(figure,numSections) { .attr("fill", "#000000") .attr("style", "text-anchor: middle;"); - var brush = d3.svg.brush() - .x(brushX) - .extent(compFextent) + // d3 v7 brushX: selection is in pixels; convert with brushX.invert. + var brush = d3.brushX() + .extent([[axeslabelmargin.left, 0], [width+axeslabelmargin.left, height]]) .on("brush",brushing) - .on("brushend",brushended); + .on("end",brushended); var gBrush = canvas.append("g") .attr("class","bottombrush") - .call(brush) - .call(brush.event); + .call(brush); + // position the brush at the initial (data-unit) comparison extent + gBrush.call(brush.move, compFextent.map(brushX)); - - - gBrush.selectAll("rect") - .attr("height",height) - .attr("y",0) - // .style({'stroke-width':'2','stroke':'rgb(100,100,100)','opacity': 0.35}) - // .attr("fill", "rgb(90,90,90)") + gBrush.selectAll(".selection") // .on("mouseout",function() { d3.selectAll(".comparea").attr("visibility","hidden"); }) .on("mouseover",function() { d3.selectAll(".comparea").attr("visibility","visible"); }); - function brushing() { - if (!d3.event.sourceEvent) return; - var extent0 = brush.extent(), - extent1 = extent0.map(Math.round); // should round it to bins + function brushing(event) { + if (!event.sourceEvent) return; + if (!event.selection) return; + var extent1 = event.selection.map(brushX.invert).map(Math.round); // round to bins drawCompArea(extent1); @@ -169,10 +161,10 @@ function selectChapter(figure,numSections) { compFencoder = d3.urllib.encoder().varname("compExtent"); //.varval(compFextent.map(function(d) { return (d/allDataRaw.length).toFixed(2); })); - function brushended() { - if (!d3.event.sourceEvent) return; - var extent0 = brush.extent(), - extent1 = extent0 .map(Math.round); // should round it to bins + function brushended(event) { + if (!event.sourceEvent) return; + if (!event.selection) return; + var extent1 = event.selection.map(brushX.invert).map(Math.round); // should round it to bins //d3.selectAll("text.complabel").attr("x",brushX(d3.sum(extent1)/extent1.length)); @@ -221,8 +213,7 @@ function selectChapter(figure,numSections) { } d3.select(this).transition() - .call(brush.extent(extent1)) - .call(brush.event); + .call(brush.move, extent1.map(brushX)); } d3.select(window).on("resize.selectbottom",resizebottom); @@ -244,16 +235,15 @@ function selectChapter(figure,numSections) { xlabel.attr("x",(leftOffsetStatic+width/2)); - d3.selectAll(".tick line").style({'stroke':'black'}); + d3.selectAll(".tick line").style('stroke','black'); //brushX.range([figwidth*.125,width+figwidth*.125]); brushX.range([leftOffsetStatic,leftOffsetStatic+width]); - brush.x(brushX); - d3.select(".bottombrush") //.transition() - .call(brush.extent(compFextent)) - .call(brush.event); + brush.extent([[leftOffsetStatic, 0], [leftOffsetStatic+width, height]]); + d3.select(".bottombrush") + .call(brush) + .call(brush.move, compFextent.map(brushX)); //brushing(); - //brush.event(); } } diff --git a/hedonometer/static/hedonometer/js/books/selectChapter.js b/hedonometer/static/hedonometer/js/books/selectChapter.js index 24daa7d6..13811864 100644 --- a/hedonometer/static/hedonometer/js/books/selectChapter.js +++ b/hedonometer/static/hedonometer/js/books/selectChapter.js @@ -23,7 +23,7 @@ function selectChapter(figure,numSections) { .attr("class","canvas"); // create the x and y axis - var x = d3.scale.linear() + var x = d3.scaleLinear() //.domain([d3.min(lens),d3.max(lens)]) .domain([0,100]) .range([0,width]); @@ -34,7 +34,7 @@ function selectChapter(figure,numSections) { // (lens); // linear scale function - var y = d3.scale.linear() + var y = d3.scaleLinear() .domain([0,1]) .range([height, 0]); @@ -51,22 +51,19 @@ function selectChapter(figure,numSections) { .attr("width", width) .attr("height", height) .attr("class", "bg") - .style({'stroke-width':'2','stroke':'rgb(0,0,0)'}) + .style('stroke-width','2') + .style('stroke','rgb(0,0,0)') .attr("fill", "#FCFCFC"); // axes creation functions var create_xAxis = function() { - return d3.svg.axis() - .scale(x) - .ticks(9) - .orient("bottom"); } + return d3.axisBottom(x) + .ticks(9); } // axis creation function var create_yAxis = function() { - return d3.svg.axis() - .ticks(3) - .scale(y) //linear scale function - .orient("left"); } + return d3.axisLeft(y) + .ticks(3); } // draw the axes // var yAxis = create_yAxis() @@ -80,8 +77,8 @@ function selectChapter(figure,numSections) { // .call(yAxis); var xAxis = create_xAxis() - .innerTickSize(6) - .outerTickSize(0); + .tickSizeInner(6) + .tickSizeOuter(0); var xAxisHandle = axes.append("g") .attr("class", "x axis ") @@ -89,7 +86,7 @@ function selectChapter(figure,numSections) { .attr("transform", "translate(0," + (height) + ")") .call(xAxis); - d3.selectAll(".tick line").style({'stroke':'black'}); + d3.selectAll(".tick line").style('stroke','black'); // create the clip boundary var clip = axes.append("svg:clipPath") @@ -123,7 +120,7 @@ function selectChapter(figure,numSections) { .attr("fill", "#000000") .attr("style", "text-anchor: middle;"); - var brushX = d3.scale.linear() + var brushX = d3.scaleLinear() .domain([0,allDataRaw.length]) .range([axeslabelmargin.left,width+axeslabelmargin.left]); @@ -136,31 +133,26 @@ function selectChapter(figure,numSections) { .attr("fill", "#000000") .attr("style", "text-anchor: middle;"); - var brush = d3.svg.brush() - .x(brushX) - .extent(compFextent) + // d3 v7 brushX: selection is in pixels; convert with brushX.invert. + var brush = d3.brushX() + .extent([[axeslabelmargin.left, 0], [width+axeslabelmargin.left, height]]) .on("brush",brushing) - .on("brushend",brushended); + .on("end",brushended); var gBrush = canvas.append("g") .attr("class","bottombrush") - .call(brush) - .call(brush.event); + .call(brush); + // position the brush at the initial (data-unit) comparison extent + gBrush.call(brush.move, compFextent.map(brushX)); - - - gBrush.selectAll("rect") - .attr("height",height) - .attr("y",0) - // .style({'stroke-width':'2','stroke':'rgb(100,100,100)','opacity': 0.35}) - // .attr("fill", "rgb(90,90,90)") + gBrush.selectAll(".selection") // .on("mouseout",function() { d3.selectAll(".comparea").attr("visibility","hidden"); }) .on("mouseover",function() { d3.selectAll(".comparea").attr("visibility","visible"); }); - function brushing() { - if (!d3.event.sourceEvent) return; - var extent0 = brush.extent(), - extent1 = extent0.map(Math.round); // should round it to bins + function brushing(event) { + if (!event.sourceEvent) return; + if (!event.selection) return; + var extent1 = event.selection.map(brushX.invert).map(Math.round); // round to bins drawCompArea(extent1); @@ -169,10 +161,10 @@ function selectChapter(figure,numSections) { compFencoder = d3.urllib.encoder().varname("compExtent"); //.varval(compFextent.map(function(d) { return (d/allDataRaw.length).toFixed(2); })); - function brushended() { - if (!d3.event.sourceEvent) return; - var extent0 = brush.extent(), - extent1 = extent0 .map(Math.round); // should round it to bins + function brushended(event) { + if (!event.sourceEvent) return; + if (!event.selection) return; + var extent1 = event.selection.map(brushX.invert).map(Math.round); // should round it to bins //d3.selectAll("text.complabel").attr("x",brushX(d3.sum(extent1)/extent1.length)); @@ -221,8 +213,7 @@ function selectChapter(figure,numSections) { } d3.select(this).transition() - .call(brush.extent(extent1)) - .call(brush.event); + .call(brush.move, extent1.map(brushX)); } d3.select(window).on("resize.selectbottom",resizebottom); @@ -244,16 +235,15 @@ function selectChapter(figure,numSections) { xlabel.attr("x",(leftOffsetStatic+width/2)); - d3.selectAll(".tick line").style({'stroke':'black'}); + d3.selectAll(".tick line").style('stroke','black'); //brushX.range([figwidth*.125,width+figwidth*.125]); brushX.range([leftOffsetStatic,leftOffsetStatic+width]); - brush.x(brushX); - d3.select(".bottombrush") //.transition() - .call(brush.extent(compFextent)) - .call(brush.event); + brush.extent([[leftOffsetStatic, 0], [leftOffsetStatic+width, height]]); + d3.select(".bottombrush") + .call(brush) + .call(brush.move, compFextent.map(brushX)); //brushing(); - //brush.event(); } } diff --git a/hedonometer/static/hedonometer/js/books/selectChapterTop-v3.js b/hedonometer/static/hedonometer/js/books/selectChapterTop-v3.js index 7ea25715..27e05554 100644 --- a/hedonometer/static/hedonometer/js/books/selectChapterTop-v3.js +++ b/hedonometer/static/hedonometer/js/books/selectChapterTop-v3.js @@ -23,13 +23,13 @@ function selectChapterTop(figure,numSections) { .attr("class","canvas"); // create the x and y axis - var x = d3.scale.linear() + var x = d3.scaleLinear() //.domain([d3.min(lens),d3.max(lens)]) .domain([0,100]) .range([0,width]); // linear scale function - var y = d3.scale.linear() + var y = d3.scaleLinear() .domain([0,1]) .range([height, 0]); @@ -46,22 +46,19 @@ function selectChapterTop(figure,numSections) { .attr("width", width) .attr("height", height) .attr("class", "bg") - .style({'stroke-width':'2','stroke':'rgb(0,0,0)'}) + .style('stroke-width','2') + .style('stroke','rgb(0,0,0)') .attr("fill", "#FCFCFC"); // axes creation functions var create_xAxis = function() { - return d3.svg.axis() - .scale(x) - .ticks(9) - .orient("bottom"); } + return d3.axisBottom(x) + .ticks(9); } // axis creation function var create_yAxis = function() { - return d3.svg.axis() - .ticks(3) - .scale(y) //linear scale function - .orient("left"); } + return d3.axisLeft(y) + .ticks(3); } // create the clip boundary var clip = axes.append("svg:clipPath") @@ -74,7 +71,7 @@ function selectChapterTop(figure,numSections) { var unclipped_axes = axes; - var brushX = d3.scale.linear() + var brushX = d3.scaleLinear() .domain([0,allDataRaw.length]) .range([axeslabelmargin.left,width+axeslabelmargin.left]); @@ -87,31 +84,28 @@ function selectChapterTop(figure,numSections) { .attr("fill", "#000000") .attr("style", "text-anchor: middle;"); - var brush = d3.svg.brush() - .x(brushX) - .extent(refFextent) + // d3 v7 brushX: selection is in pixels; convert with brushX.invert. + var brush = d3.brushX() + .extent([[axeslabelmargin.left, 4], [width+axeslabelmargin.left, height-2+4]]) .on("brush",brushing) - .on("brushend",brushended); + .on("end",brushended); var gBrush = canvas.append("g") .attr("class","topbrush") - .call(brush) - .call(brush.event); - - gBrush.selectAll("rect") - .attr("height",height-2) - .attr("y",4) - // .style({'stroke-width':'2','stroke':'rgb(100,100,100)','opacity': 0.35}) - // .attr("fill", "rgb(90,90,90)") + .call(brush); + // position the brush at the initial (data-unit) reference extent + gBrush.call(brush.move, refFextent.map(brushX)); + + gBrush.selectAll(".selection") // .on("mouseout",function() { d3.selectAll(".refarea").attr("visibility","hidden"); }) .on("mouseover",function() { d3.selectAll(".refarea").attr("visibility","visible"); }); - function brushing() { - if (!d3.event.sourceEvent) return; - - var extent0 = brush.extent(), - extent1 = extent0.map(Math.round); // should round it to bins + function brushing(event) { + if (!event.sourceEvent) return; + if (!event.selection) return; + + var extent1 = event.selection.map(brushX.invert).map(Math.round); // round to bins drawRefArea(extent1); @@ -120,11 +114,11 @@ function selectChapterTop(figure,numSections) { refFencoder = d3.urllib.encoder().varname("refExtent"); //.varval(refFextent.map(function(d) { return (d/allDataRaw.length).toFixed(2); })); - function brushended() { - if (!d3.event.sourceEvent) return; - var extent0 = brush.extent(), - extent1 = extent0.map(Math.round); // should round it to bins - + function brushended(event) { + if (!event.sourceEvent) return; + if (!event.selection) return; + var extent1 = event.selection.map(brushX.invert).map(Math.round); // should round it to bins + //d3.selectAll("text.reflabel").attr("x",brushX(d3.sum(extent1)/extent1.length)); if ((extent1[0] !== refFextent[0]) || (extent1[1] !== refFextent[1])) @@ -161,8 +155,7 @@ function selectChapterTop(figure,numSections) { } d3.select(this).transition() - .call(brush.extent(extent1)) - .call(brush.event); + .call(brush.move, extent1.map(brushX)); } @@ -185,12 +178,11 @@ function selectChapterTop(figure,numSections) { //brushX.range([figwidth*.125,width+figwidth*.125]); brushX.range([leftOffsetStatic,leftOffsetStatic+width]); - brush.x(brushX); - d3.select(".topbrush") //.transition() - .call(brush.extent(refFextent)) - .call(brush.event); + brush.extent([[leftOffsetStatic, 4], [leftOffsetStatic+width, height-2+4]]); + d3.select(".topbrush") + .call(brush) + .call(brush.move, refFextent.map(brushX)); //brushing(); - //brush.event(); } } diff --git a/hedonometer/static/hedonometer/js/books/selectChapterTop.js b/hedonometer/static/hedonometer/js/books/selectChapterTop.js index b7835f62..42667514 100644 --- a/hedonometer/static/hedonometer/js/books/selectChapterTop.js +++ b/hedonometer/static/hedonometer/js/books/selectChapterTop.js @@ -23,13 +23,13 @@ function selectChapterTop(figure,numSections) { .attr("class","canvas"); // create the x and y axis - var x = d3.scale.linear() + var x = d3.scaleLinear() //.domain([d3.min(lens),d3.max(lens)]) .domain([0,100]) .range([0,width]); // linear scale function - var y = d3.scale.linear() + var y = d3.scaleLinear() .domain([0,1]) .range([height, 0]); @@ -46,22 +46,19 @@ function selectChapterTop(figure,numSections) { .attr("width", width) .attr("height", height) .attr("class", "bg") - .style({'stroke-width':'2','stroke':'rgb(0,0,0)'}) + .style('stroke-width','2') + .style('stroke','rgb(0,0,0)') .attr("fill", "#FCFCFC"); // axes creation functions var create_xAxis = function() { - return d3.svg.axis() - .scale(x) - .ticks(9) - .orient("bottom"); } + return d3.axisBottom(x) + .ticks(9); } // axis creation function var create_yAxis = function() { - return d3.svg.axis() - .ticks(3) - .scale(y) //linear scale function - .orient("left"); } + return d3.axisLeft(y) + .ticks(3); } // create the clip boundary var clip = axes.append("svg:clipPath") @@ -74,7 +71,7 @@ function selectChapterTop(figure,numSections) { var unclipped_axes = axes; - var brushX = d3.scale.linear() + var brushX = d3.scaleLinear() .domain([0,allDataRaw.length]) .range([axeslabelmargin.left,width+axeslabelmargin.left]); @@ -87,31 +84,28 @@ function selectChapterTop(figure,numSections) { .attr("fill", "#000000") .attr("style", "text-anchor: middle;"); - var brush = d3.svg.brush() - .x(brushX) - .extent(refFextent) + // d3 v7 brushX: selection is in pixels; convert with brushX.invert. + var brush = d3.brushX() + .extent([[axeslabelmargin.left, 4], [width+axeslabelmargin.left, height-2+4]]) .on("brush",brushing) - .on("brushend",brushended); + .on("end",brushended); var gBrush = canvas.append("g") .attr("class","topbrush") - .call(brush) - .call(brush.event); - - gBrush.selectAll("rect") - .attr("height",height-2) - .attr("y",4) - // .style({'stroke-width':'2','stroke':'rgb(100,100,100)','opacity': 0.35}) - // .attr("fill", "rgb(90,90,90)") + .call(brush); + // position the brush at the initial (data-unit) reference extent + gBrush.call(brush.move, refFextent.map(brushX)); + + gBrush.selectAll(".selection") // .on("mouseout",function() { d3.selectAll(".refarea").attr("visibility","hidden"); }) .on("mouseover",function() { d3.selectAll(".refarea").attr("visibility","visible"); }); - function brushing() { - if (!d3.event.sourceEvent) return; - - var extent0 = brush.extent(), - extent1 = extent0.map(Math.round); // should round it to bins + function brushing(event) { + if (!event.sourceEvent) return; + if (!event.selection) return; + + var extent1 = event.selection.map(brushX.invert).map(Math.round); // round to bins drawRefArea(extent1); @@ -120,11 +114,11 @@ function selectChapterTop(figure,numSections) { refFencoder = d3.urllib.encoder().varname("refExtent"); //.varval(refFextent.map(function(d) { return (d/allDataRaw.length).toFixed(2); })); - function brushended() { - if (!d3.event.sourceEvent) return; - var extent0 = brush.extent(), - extent1 = extent0.map(Math.round); // should round it to bins - + function brushended(event) { + if (!event.sourceEvent) return; + if (!event.selection) return; + var extent1 = event.selection.map(brushX.invert).map(Math.round); // should round it to bins + //d3.selectAll("text.reflabel").attr("x",brushX(d3.sum(extent1)/extent1.length)); if ((extent1[0] !== refFextent[0]) || (extent1[1] !== refFextent[1])) @@ -161,8 +155,7 @@ function selectChapterTop(figure,numSections) { } d3.select(this).transition() - .call(brush.extent(extent1)) - .call(brush.event); + .call(brush.move, extent1.map(brushX)); } @@ -185,12 +178,11 @@ function selectChapterTop(figure,numSections) { //brushX.range([figwidth*.125,width+figwidth*.125]); brushX.range([leftOffsetStatic,leftOffsetStatic+width]); - brush.x(brushX); - d3.select(".topbrush") //.transition() - .call(brush.extent(refFextent)) - .call(brush.event); + brush.extent([[leftOffsetStatic, 4], [leftOffsetStatic+width, height-2+4]]); + d3.select(".topbrush") + .call(brush) + .call(brush.move, refFextent.map(brushX)); //brushing(); - //brush.event(); } } diff --git a/hedonometer/static/hedonometer/js/drawBookTimeseries-harry.js b/hedonometer/static/hedonometer/js/drawBookTimeseries-harry.js index 4aba73b8..3ced8258 100644 --- a/hedonometer/static/hedonometer/js/drawBookTimeseries-harry.js +++ b/hedonometer/static/hedonometer/js/drawBookTimeseries-harry.js @@ -6,7 +6,7 @@ hedotools.booktimeseries = function() { -cut out stops words (0 the frequencies) -call shift on these frequency vectors */ - // some colors + // some colors // #1193c0 #759ae8 var margin = {top: 0, right: 0, bottom: 0, left: 0}; @@ -46,11 +46,11 @@ hedotools.booktimeseries = function() { // var fulltimeseries; drawRefArea = function(extent) { - var refarea = d3.svg.area() + var refarea = d3.area() .x(function(d,i) { return x(extent[0]+i); }) .y0(height-1) .y1(function(d) { return y(d)+2; }); - + axes.selectAll(".refarea").remove(); console.log(extent); @@ -63,11 +63,11 @@ hedotools.booktimeseries = function() { } drawCompArea = function(extent) { - var comparea = d3.svg.area() + var comparea = d3.area() .x(function(d,i) { return x(extent[0]+i-1); }) .y0(height-1) .y1(function(d) { return y(d)+2; }); - + axes.selectAll(".comparea").remove(); console.log(extent); @@ -83,23 +83,26 @@ hedotools.booktimeseries = function() { var drawAnnotations = function() { // draw all of the annotations - d3.json("/api/v1/annotation/?format=json&winner=1&book__title="+book,function(error,json) { + d3.json("/api/v1/annotation/?format=json&winner=1&book__title="+book).then(function(json) { // console.log(json); - var force = d3.layout.force() - .size([width,height]) - .charge(-40) - .linkStrength(1.0) - .linkDistance(20) - .chargeDistance(50) - .gravity(0.0) - .friction(0.8); + // nothing to draw (empty / missing data) -- bail before building the force sim + if (!json || !json.objects || json.objects.length === 0) { return; } + + // d3 v7: force layout is now d3.forceSimulation; friction(0.8) -> velocityDecay(1-0.8), + // charge/chargeDistance -> forceManyBody().strength().distanceMax(), link* -> forceLink(), + // gravity(0.0) -> omit forceCenter. nodes/links are attached below once built. + var force = d3.forceSimulation() + .velocityDecay(1-0.8) + .force("charge", d3.forceManyBody().strength(-40).distanceMax(50)) + .force("link", d3.forceLink().distance(20).strength(1.0)) + .stop(); // .linkDistance(1); // build a list of x,y for the bubbles var annotationnodes = Array(json.objects.length); var annotationlinks = Array(json.objects.length); - + for (var i=0; i // // - }); + }).catch(function(error) { console.log(error); }); } var highlightExtrema = function() { @@ -426,9 +418,9 @@ hedotools.booktimeseries = function() { .attr("cx",function(d,i) { return x(d);}) .attr("cy",function(d,i) { return y(data[d]);}) .attr("fill","#1193c0") - .attr("class","mincircle") + .attr("class","mincircle") .attr("r",4) - .on("click",function(d,i) { + .on("click",function(event,d) { console.log("click on min element number"); console.log(d); // remove them all @@ -453,9 +445,9 @@ hedotools.booktimeseries = function() { .attr("cx",function(d,i) { return x(d);}) .attr("cy",function(d,i) { return y(data[d]);}) .attr("fill","red") - .attr("class","maxcircle blink") + .attr("class","maxcircle blink") .attr("r",4) - .on("click",function(d,i) { + .on("click",function(event,d) { console.log("click on max element number"); console.log(d); // remove them all @@ -482,23 +474,18 @@ hedotools.booktimeseries = function() { var hovergroup; var hidehover = function() { - hovergroup.style({ - "visibility": "hidden", - }); + hovergroup.style("visibility", "hidden"); } var plot = function() { - hovergroup = figure.append("div").attr({ - "class": "hoverinfogroup", - // "transform": "translate("+(x+hoverboxxoffset+axeslabelmargin.left)+","+(d3.min([d3.max([0,y-hoverboxheight/2-hoverboxyoffset]),height-hoverboxheight]))+")", - }) - .style({ - "position": "absolute", - "top": "100px", - "left": "100px", - "visibility": "hidden", - }); + hovergroup = figure.append("div") + .attr("class", "hoverinfogroup") + // "transform": "translate("+(x+hoverboxxoffset+axeslabelmargin.left)+","+(d3.min([d3.max([0,y-hoverboxheight/2-hoverboxyoffset]),height-hoverboxheight]))+")", + .style("position", "absolute") + .style("top", "100px") + .style("left", "100px") + .style("visibility", "hidden"); // remove an old figure if it exists @@ -513,21 +500,21 @@ hedotools.booktimeseries = function() { //console.log(data.length); // create the x and y axis - x = d3.scale.linear() + x = d3.scaleLinear() //.domain([d3.min(lens),d3.max(lens)]) // map from the start of the timeseries point to the max .domain([0,data.length-1]) .range([0,width]); - + // use d3.layout http://bl.ocks.org/mbostock/3048450 // data = d3.layout.histogram() // .bins(x.ticks(65)) // (lens); // linear scale function - y = d3.scale.linear() + y = d3.scaleLinear() .domain([d3.min(data),d3.max(data)]) - .range([height-50, 50]); + .range([height-50, 50]); // console.log([d3.min(data),d3.max(data)]) @@ -544,27 +531,24 @@ hedotools.booktimeseries = function() { .attr("width", width) .attr("height", height) .attr("class", "bg") - .style({'stroke-width':'2','stroke':'rgb(0,0,0)'}) + .style('stroke-width','2') + .style('stroke','rgb(0,0,0)') .attr("fill", "#FCFCFC"); // axes creation functions var create_xAxis = function() { - return d3.svg.axis() - .scale(x) - .ticks(9) - .orient("bottom"); } + return d3.axisBottom(x) + .ticks(9); } // axis creation function var create_yAxis = function() { - return d3.svg.axis() - .ticks(5) - .scale(y) //linear scale function - .orient("left"); } + return d3.axisLeft(y) + .ticks(5); } // draw the axes var yAxis = create_yAxis() - .innerTickSize(6) - .outerTickSize(0); + .tickSizeInner(6) + .tickSizeOuter(0); axes.append("g") .attr("class", "top") @@ -586,11 +570,11 @@ hedotools.booktimeseries = function() { // axes = axes.append("g") // .attr("clip-path","url(#clip)"); - line = d3.svg.line() + line = d3.line() .x(function(d,i) { return x(i+minWindows/2); }) .y(function(d) { return y(d); }) - .interpolate("cardinal"); - // .interpolate("linear"); + .curve(d3.curveCardinal); + // .curve(d3.curveLinear); console.log(data.slice(minWindows/2,data.length-minWindows/2)); mainline = axes.append("path") @@ -601,11 +585,11 @@ hedotools.booktimeseries = function() { .attr("stroke-width",3) .attr("fill","none"); - beglineline = d3.svg.line() + beglineline = d3.line() .x(function(d,i) { return x(i); }) .y(function(d) { return y(d); }) - .interpolate("cardinal"); - // .interpolate("linear"); + .curve(d3.curveCardinal); + // .curve(d3.curveLinear); console.log(data.slice(0,minWindows/2+1)); begline = axes.append("path") @@ -617,11 +601,11 @@ hedotools.booktimeseries = function() { .attr("stroke-width",3) .attr("fill","none"); - endlineline = d3.svg.line() + endlineline = d3.line() .x(function(d,i) { return x(i+data.length-minWindows/2-1); }) .y(function(d) { return y(d); }) - .interpolate("cardinal"); - // .interpolate("linear"); + .curve(d3.curveCardinal); + // .curve(d3.curveLinear); // endtimeseries.unshift(data[data.length-1]); @@ -635,7 +619,7 @@ hedotools.booktimeseries = function() { .attr("stroke-width",3) .attr("fill","none"); - area = d3.svg.area() + area = d3.area() .x(function(d,i) { return x(i+minWindows/2); }) .y0(height-1) .y1(function(d) { return y(d); }); @@ -657,14 +641,14 @@ hedotools.booktimeseries = function() { // console.log(d3.mean(data)); var avhapps = d3.mean(data.slice(minWindows/2,data.length-minWindows/2)); - var linearline = d3.svg.line() + var linearline = d3.line() .x(function(d,i) { return x(d.index); }) .y(function(d) { return y(d.value); }) - .interpolate("linear"); + .curve(d3.curveLinear); var averageline = axes.append("path") .datum([ - { "index": 0, + { "index": 0, "value": avhapps, }, { "index": data.length-1, "value": avhapps, }] @@ -677,20 +661,18 @@ hedotools.booktimeseries = function() { .attr("fill","none"); var averagetext1 = axes.append("text") - .attr({ "x": width+5, - "y": y(avhapps)-3, - "fill": "#606060", - "text-anchor": "start", - }) + .attr("x", width+5) + .attr("y", y(avhapps)-3) + .attr("fill", "#606060") + .attr("text-anchor", "start") .text("Average"); var averagetext2 = axes.append("text") - .attr({ "x": width+5, - "y": y(avhapps)+12, - "fill": "#606060", - "font-weight": "bold", - "text-anchor": "start", - }) + .attr("x", width+5) + .attr("y", y(avhapps)+12) + .attr("fill", "#606060") + .attr("font-weight", "bold") + .attr("text-anchor", "start") .text(avhapps.toFixed(2)); // console.log(d3.min(data)); @@ -717,9 +699,9 @@ hedotools.booktimeseries = function() { var minline = axes.append("path") .datum([ - { "index": minhappsindex+minWindows/2, + { "index": minhappsindex+minWindows/2, "value": minhapps, }, - { "index": data.length-1, + { "index": data.length-1, "value": minhapps, }] ) .attr("class", "line") @@ -729,20 +711,18 @@ hedotools.booktimeseries = function() { .attr("fill","none"); var mintext1 = axes.append("text") - .attr({ "x": width+5, - "y": y(minhapps)-3, - "fill": "#606060", - "text-anchor": "start", - }) + .attr("x", width+5) + .attr("y", y(minhapps)-3) + .attr("fill", "#606060") + .attr("text-anchor", "start") .text("Least Happy"); var mintext2 = axes.append("text") - .attr({ "x": width+5, - "y": y(minhapps)+12, - "fill": "#606060", - "font-weight": "bold", - "text-anchor": "start", - }) + .attr("x", width+5) + .attr("y", y(minhapps)+12) + .attr("fill", "#606060") + .attr("font-weight", "bold") + .attr("text-anchor", "start") .text(minhapps.toFixed(2)); var maxcircle = axes.append("circle") @@ -755,9 +735,9 @@ hedotools.booktimeseries = function() { var maxline = axes.append("path") .datum([ - { "index": maxhappsindex+minWindows/2, + { "index": maxhappsindex+minWindows/2, "value": maxhapps, }, - { "index": data.length-1, + { "index": data.length-1, "value": maxhapps, }] ) .attr("class", "line") @@ -767,34 +747,31 @@ hedotools.booktimeseries = function() { .attr("fill","none"); var mintext1 = axes.append("text") - .attr({ "x": width+5, - "y": y(maxhapps), - "fill": "#606060", - "text-anchor": "start", - }) + .attr("x", width+5) + .attr("y", y(maxhapps)) + .attr("fill", "#606060") + .attr("text-anchor", "start") .text("Happiest"); var maxtext2 = axes.append("text") - .attr({ "x": width+5, - "y": y(maxhapps)+15, - "fill": "#606060", - "font-weight": "bold", - "text-anchor": "start", - }) + .attr("x", width+5) + .attr("y", y(maxhapps)+15) + .attr("fill", "#606060") + .attr("font-weight", "bold") + .attr("text-anchor", "start") .text(maxhapps.toFixed(2)); // d3.select(window).on("resize.booktimeseries",resize); trademark = axes.append("text") - .attr({ "x": 3, - "y": height-7, - "fill": "#606060", - // "font-weight": "bold", - "font-size": ".8em", - "text-anchor": "start", - }) + .attr("x", 3) + .attr("y", height-7) + .attr("fill", "#606060") + // .attr("font-weight", "bold") + .attr("font-size", ".8em") + .attr("text-anchor", "start") .text("visualization by @hedonometer team and @andyreagan"); - + function resize() { figwidth = parseInt(d3.select('#chapters03').style('width')) - margin.left - margin.right, width = .775*figwidth; @@ -822,8 +799,3 @@ hedotools.booktimeseries = function() { return opublic; }(); - - - - - diff --git a/hedonometer/templates/hedonometer/books/gutenberg.html b/hedonometer/templates/hedonometer/books/gutenberg.html index 33ba6b63..fef04543 100644 --- a/hedonometer/templates/hedonometer/books/gutenberg.html +++ b/hedonometer/templates/hedonometer/books/gutenberg.html @@ -186,13 +186,15 @@

Join our waitlist

{% endblock %} {% block bottomjs %} - + - - + + + + diff --git a/hedonometer/templates/hedonometer/books/harrypotter.html b/hedonometer/templates/hedonometer/books/harrypotter.html index 0478c37c..d59fd2dd 100644 --- a/hedonometer/templates/hedonometer/books/harrypotter.html +++ b/hedonometer/templates/hedonometer/books/harrypotter.html @@ -351,13 +351,15 @@ {% endblock %} {% block bottomjs %} - + - - + + + + diff --git a/hedonometer/templates/hedonometer/books/v3.html b/hedonometer/templates/hedonometer/books/v3.html index 77c67ff2..a0efa68d 100644 --- a/hedonometer/templates/hedonometer/books/v3.html +++ b/hedonometer/templates/hedonometer/books/v3.html @@ -174,14 +174,16 @@

{% endblock %} {% block bottomjs %} - + - - + + + + From fd9b3cbdeb4c05e2e903d144512af937ca29b06c Mon Sep 17 00:00:00 2001 From: Andy Reagan Date: Tue, 30 Jun 2026 22:07:59 -0400 Subject: [PATCH 11/16] Migrate words + shifttest pages to d3 v7 words_api.html (/words//): d3.js -> d3@7; inline d3.json(url, cb) -> d3.json(url).then(cb) (the DataTable loader). words_table.html: d3.js -> d3@7 (d3 was loaded but unused). shifttest.html + shifttest-on-load.js: d3.js/hedotools.* -> CDN d3@7 + d3-shifterator@7 + hedotools@7 (init.v4, shifter); 4 d3.text loaders -> .then(). No hedonometer template loads the local js/d3.js anymore. Verified: /words/labMT-en-v1/ loads d3 7.9.0 and populates its table, 0 errors. Co-Authored-By: Claude Opus 4.8 --- hedonometer/static/hedonometer/js/shifttest-on-load.js | 8 ++++---- hedonometer/templates/hedonometer/shifttest.html | 7 ++++--- hedonometer/templates/hedonometer/words_api.html | 4 ++-- hedonometer/templates/hedonometer/words_table.html | 2 +- 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/hedonometer/static/hedonometer/js/shifttest-on-load.js b/hedonometer/static/hedonometer/js/shifttest-on-load.js index b68c6e96..e6221f55 100644 --- a/hedonometer/static/hedonometer/js/shifttest-on-load.js +++ b/hedonometer/static/hedonometer/js/shifttest-on-load.js @@ -20,7 +20,7 @@ hedotools.loader = function() { var refFvec; var compFvec; var load = function() { - d3.text(scoresFile, function(text) { + d3.text(scoresFile).then(function(text) { var tmp = text.split("\n"); //console.log(tmp.length); //console.log(tmp[tmp.length-1]); @@ -33,7 +33,7 @@ hedotools.loader = function() { } if (!--csvLoadsRemaining) onload(); }); - d3.text(wordsFile, function(text) { + d3.text(wordsFile).then(function(text) { var tmp = text.split("\n"); words = tmp; var len = words.length - 1; @@ -44,11 +44,11 @@ hedotools.loader = function() { } if (!--csvLoadsRemaining) onload(); }); - d3.text(embedDetails.compFile,function(tmp) { + d3.text(embedDetails.compFile).then(function(tmp) { compFvec = tmp.split('\n').slice(0,10222); if (!--csvLoadsRemaining) onload(); }); - d3.text(embedDetails.refFile,function(tmp2) { + d3.text(embedDetails.refFile).then(function(tmp2) { refFvec = tmp2.split('\n').slice(0,10222); if (!--csvLoadsRemaining) onload(); }); diff --git a/hedonometer/templates/hedonometer/shifttest.html b/hedonometer/templates/hedonometer/shifttest.html index a717941e..b336b018 100644 --- a/hedonometer/templates/hedonometer/shifttest.html +++ b/hedonometer/templates/hedonometer/shifttest.html @@ -49,10 +49,11 @@ {% block bottomjs %} - + - - + + + {% endblock %} diff --git a/hedonometer/templates/hedonometer/words_api.html b/hedonometer/templates/hedonometer/words_api.html index ccec6d01..884e6a9d 100644 --- a/hedonometer/templates/hedonometer/words_api.html +++ b/hedonometer/templates/hedonometer/words_api.html @@ -181,10 +181,10 @@

API

{% endblock %} {% block bottomjs %} - + + - - - - + + + + - + {% endblock %} diff --git a/hedonometer/templates/hedonometer/cities.html b/hedonometer/templates/hedonometer/cities.html index cd6d528b..801af91e 100644 --- a/hedonometer/templates/hedonometer/cities.html +++ b/hedonometer/templates/hedonometer/cities.html @@ -128,9 +128,9 @@

Interactive Wordshift

- - - + + + diff --git a/hedonometer/templates/hedonometer/cities2.html b/hedonometer/templates/hedonometer/cities2.html index 5b30c0e3..772c1e9f 100644 --- a/hedonometer/templates/hedonometer/cities2.html +++ b/hedonometer/templates/hedonometer/cities2.html @@ -133,14 +133,14 @@
Word Shift:
- + - - - + + + - + {% endblock %} diff --git a/hedonometer/templates/hedonometer/citysankey.html b/hedonometer/templates/hedonometer/citysankey.html index 69b11297..489358fb 100644 --- a/hedonometer/templates/hedonometer/citysankey.html +++ b/hedonometer/templates/hedonometer/citysankey.html @@ -127,11 +127,11 @@

Interactive Wordshift

- - - + + + - + {% endblock %} diff --git a/hedonometer/templates/hedonometer/embed.html b/hedonometer/templates/hedonometer/embed.html index 9763c2e1..9707b269 100644 --- a/hedonometer/templates/hedonometer/embed.html +++ b/hedonometer/templates/hedonometer/embed.html @@ -65,9 +65,9 @@ - - - + + + {% endblock %} diff --git a/hedonometer/templates/hedonometer/maps.html b/hedonometer/templates/hedonometer/maps.html index 2db03d13..667f2624 100644 --- a/hedonometer/templates/hedonometer/maps.html +++ b/hedonometer/templates/hedonometer/maps.html @@ -284,15 +284,15 @@
Word Shift:
- + - - - - + + + + - + {% endblock %} diff --git a/hedonometer/templates/hedonometer/movie.html b/hedonometer/templates/hedonometer/movie.html index 8a125c4a..92bb4826 100644 --- a/hedonometer/templates/hedonometer/movie.html +++ b/hedonometer/templates/hedonometer/movie.html @@ -283,9 +283,9 @@ - - - + + + diff --git a/hedonometer/templates/hedonometer/movielist-ranked.html b/hedonometer/templates/hedonometer/movielist-ranked.html index 0c1389af..a50d9deb 100644 --- a/hedonometer/templates/hedonometer/movielist-ranked.html +++ b/hedonometer/templates/hedonometer/movielist-ranked.html @@ -117,12 +117,12 @@
Word Shift:
- - - - + + + + - + {% endblock %} diff --git a/hedonometer/templates/hedonometer/nytlist.html b/hedonometer/templates/hedonometer/nytlist.html index ce8aa44c..6def8894 100644 --- a/hedonometer/templates/hedonometer/nytlist.html +++ b/hedonometer/templates/hedonometer/nytlist.html @@ -135,12 +135,12 @@
John Tierney shares this analysis on CBS This Morning:
- - - - + + + + - + {% endblock %} diff --git a/hedonometer/templates/hedonometer/outside.html b/hedonometer/templates/hedonometer/outside.html index 3f189ca8..acce63eb 100644 --- a/hedonometer/templates/hedonometer/outside.html +++ b/hedonometer/templates/hedonometer/outside.html @@ -138,12 +138,12 @@
Word Shift:
- - - - + + + + - + {% endblock %} diff --git a/hedonometer/templates/hedonometer/outsidelist.html b/hedonometer/templates/hedonometer/outsidelist.html index ce8aa44c..6def8894 100644 --- a/hedonometer/templates/hedonometer/outsidelist.html +++ b/hedonometer/templates/hedonometer/outsidelist.html @@ -135,12 +135,12 @@
John Tierney shares this analysis on CBS This Morning:
- - - - + + + + - + {% endblock %} diff --git a/hedonometer/templates/hedonometer/shifttest.html b/hedonometer/templates/hedonometer/shifttest.html index b336b018..e1cd65b3 100644 --- a/hedonometer/templates/hedonometer/shifttest.html +++ b/hedonometer/templates/hedonometer/shifttest.html @@ -51,9 +51,9 @@ - + - + {% endblock %} diff --git a/hedonometer/templates/hedonometer/statesankey.html b/hedonometer/templates/hedonometer/statesankey.html index 17ac0486..06301383 100644 --- a/hedonometer/templates/hedonometer/statesankey.html +++ b/hedonometer/templates/hedonometer/statesankey.html @@ -97,15 +97,15 @@

- + - - - + + + - + {% endblock %} From 7635b44a0a730d52e4c9529165d867a6420e68d0 Mon Sep 17 00:00:00 2001 From: Andy Reagan Date: Tue, 30 Jun 2026 22:45:07 -0400 Subject: [PATCH 15/16] Migrate v1 books page off the bundle to modular d3 v7 + hedotools 7.2.0 books.html (the live /books/v1/ page) loaded the monolithic books-all.js bundle (its own old d3+jquery). Switch it to modular CDN like every other page: d3@7 + d3-shifterator@7 + hedotools@7.2.0 (init.v4 + shifter) + the book glue as separate scripts (d3-before-urllib order, matching movie.html). Root glue migrated d3 v3->v7: drawBookTimeseries.js, selectChapter.js, selectChapterTop.js (copied from the already-migrated js/books/ twins), and books-on-load.js (7 loaders -> .then). drawLens-chapters.js already v7; computeHappsChapters.js is pure. Verified: /books/v1/?book=Into%20Thin%20Air and Pride and Prejudice render the timeseries + lens + wordshift + catalog, zero JS errors. Co-Authored-By: Claude Opus 4.8 --- .../static/hedonometer/js/books-on-load.js | 14 +-- .../hedonometer/js/drawBookTimeseries.js | 111 ++++++++---------- .../static/hedonometer/js/selectChapter.js | 78 ++++++------ .../static/hedonometer/js/selectChapterTop.js | 74 ++++++------ .../templates/hedonometer/books/books.html | 15 ++- 5 files changed, 139 insertions(+), 153 deletions(-) diff --git a/hedonometer/static/hedonometer/js/books-on-load.js b/hedonometer/static/hedonometer/js/books-on-load.js index 392f36e1..cff32525 100644 --- a/hedonometer/static/hedonometer/js/books-on-load.js +++ b/hedonometer/static/hedonometer/js/books-on-load.js @@ -38,7 +38,7 @@ function initializePlot() { book = bookDecoder().cached; // hit the random api if (book === 'random') { - d3.json("https://hedonometer.org/api/v1/randombook/?format=json",function(data) { + d3.json("https://hedonometer.org/api/v1/randombook/?format=json").then(function(data) { var result = data.objects[0]; // console.log(result); lang = result.language; @@ -63,7 +63,7 @@ function initializePlot() { }) } else { - d3.json("https://hedonometer.org/api/v1/gutenberg/?format=json&title__exact="+book,function(data) { + d3.json("https://hedonometer.org/api/v1/gutenberg/?format=json&title__exact="+book).then(function(data) { var result = data.objects[0]; console.log(result); lang = result.language; @@ -91,7 +91,7 @@ function initializePlot() { function loadCsv() { var csvLoadsRemaining = 4; var bookfile = "https://hedonometer.org/data/bookdata/processed/"+book+".csv"; - d3.text(bookfile, function (text) { + d3.text(bookfile).then(function (text) { tmp = text.split("\n"); // kill extra rows var len = tmp.length - 1; @@ -112,7 +112,7 @@ function loadCsv() { //console.log(d3.sum(allDataRaw[0])); if (!--csvLoadsRemaining) initializePlotPlot(); }); - d3.text("https://hedonometer.org/data/bookdata/labMT/labMTscores-"+lang+".csv", function (text) { + d3.text("https://hedonometer.org/data/bookdata/labMT/labMTscores-"+lang+".csv").then(function (text) { var tmp = text.split("\n"); //console.log(tmp.length); //console.log(tmp[tmp.length-1]); @@ -126,7 +126,7 @@ function loadCsv() { hedotools.shifter._lens(lens); if (!--csvLoadsRemaining) initializePlotPlot(); }); - d3.text("https://hedonometer.org/data/bookdata/labMT/labMTwords-"+lang+".csv", function (text) { + d3.text("https://hedonometer.org/data/bookdata/labMT/labMTwords-"+lang+".csv").then(function (text) { var tmp = text.split("\n"); words = tmp; var len = words.length - 1; @@ -138,7 +138,7 @@ function loadCsv() { hedotools.shifter._words(words); if (!--csvLoadsRemaining) initializePlotPlot(); }); - d3.text("https://hedonometer.org/data/bookdata/labMT/labMTwordsEn-"+lang+".csv", function (text) { + d3.text("https://hedonometer.org/data/bookdata/labMT/labMTwordsEn-"+lang+".csv").then(function (text) { var tmp = text.split("\n"); words_en = tmp; var len = words_en.length - 1; @@ -259,7 +259,7 @@ var substringMatcher = function(apik) { // } // } // if (matches.length === 0) { matches.push({ value: "book not indexed" }); } - d3.json("https://hedonometer.org/api/v1/gutenberg/?format=json&"+apik.toLowerCase()+"__icontains="+q,function(data) { + d3.json("https://hedonometer.org/api/v1/gutenberg/?format=json&"+apik.toLowerCase()+"__icontains="+q).then(function(data) { var result = data.objects; // console.log(result); var newresult = []; diff --git a/hedonometer/static/hedonometer/js/drawBookTimeseries.js b/hedonometer/static/hedonometer/js/drawBookTimeseries.js index 2a7b5502..147c2444 100644 --- a/hedonometer/static/hedonometer/js/drawBookTimeseries.js +++ b/hedonometer/static/hedonometer/js/drawBookTimeseries.js @@ -34,7 +34,7 @@ function drawBookTimeseries(figure,data) { //console.log(data.length); // create the x and y axis - var x = d3.scale.linear() + var x = d3.scaleLinear() //.domain([d3.min(lens),d3.max(lens)]) // map from the start of the timeseries point to the max .domain([-minWindows/2,data.length+minWindows/2-1]) @@ -46,7 +46,7 @@ function drawBookTimeseries(figure,data) { // (lens); // linear scale function - var y = d3.scale.linear() + var y = d3.scaleLinear() .domain([d3.min(data),d3.max(data)]) .range([height-10, 10]); @@ -65,27 +65,24 @@ function drawBookTimeseries(figure,data) { .attr("width", width) .attr("height", height) .attr("class", "bg") - .style({'stroke-width':'2','stroke':'rgb(0,0,0)'}) + .style('stroke-width','2') + .style('stroke','rgb(0,0,0)') .attr("fill", "#FCFCFC"); // axes creation functions var create_xAxis = function() { - return d3.svg.axis() - .scale(x) - .ticks(9) - .orient("bottom"); } + return d3.axisBottom(x) + .ticks(9); } // axis creation function var create_yAxis = function() { - return d3.svg.axis() - .ticks(5) - .scale(y) //linear scale function - .orient("left"); } + return d3.axisLeft(y) + .ticks(5); } // draw the axes var yAxis = create_yAxis() - .innerTickSize(6) - .outerTickSize(0); + .tickSizeInner(6) + .tickSizeOuter(0); axes.append("g") .attr("class", "top") @@ -107,11 +104,11 @@ function drawBookTimeseries(figure,data) { // axes = axes.append("g") // .attr("clip-path","url(#clip)"); - var line = d3.svg.line() + var line = d3.line() .x(function(d,i) { return x(i); }) .y(function(d) { return y(d); }) - .interpolate("cardinal"); - // .interpolate("linear"); + .curve(d3.curveCardinal); + // .curve(d3.curveLinear); var mainline = axes.append("path") .datum(data) @@ -121,11 +118,11 @@ function drawBookTimeseries(figure,data) { .attr("stroke-width",3) .attr("fill","none"); - var beglineline = d3.svg.line() + var beglineline = d3.line() .x(function(d,i) { return x(i-minWindows/2); }) .y(function(d) { return y(d); }) - .interpolate("cardinal"); - // .interpolate("linear"); + .curve(d3.curveCardinal); + // .curve(d3.curveLinear); begtimeseries.push(data[0]); @@ -138,11 +135,11 @@ function drawBookTimeseries(figure,data) { .attr("stroke-width",3) .attr("fill","none"); - var endlineline = d3.svg.line() + var endlineline = d3.line() .x(function(d,i) { return x(i+data.length-1); }) .y(function(d) { return y(d); }) - .interpolate("cardinal"); - // .interpolate("linear"); + .curve(d3.curveCardinal); + // .curve(d3.curveLinear); endtimeseries.unshift(data[data.length-1]); @@ -155,7 +152,7 @@ function drawBookTimeseries(figure,data) { .attr("stroke-width",3) .attr("fill","none"); - var area = d3.svg.area() + var area = d3.area() .x(function(d,i) { return x(i); }) .y0(height-1) .y1(function(d) { return y(d); }); @@ -170,7 +167,7 @@ function drawBookTimeseries(figure,data) { drawRefArea = function drawRefArea(extent) { - var refarea = d3.svg.area() + var refarea = d3.area() .x(function(d,i) { return x(extent[0]+i-minWindows/2); }) .y0(height-1) .y1(function(d) { return y(d)+2; }); @@ -191,7 +188,7 @@ function drawBookTimeseries(figure,data) { drawCompArea = function drawCompArea(extent) { - var comparea = d3.svg.area() + var comparea = d3.area() .x(function(d,i) { return x(extent[0]+i-minWindows/2-1); }) .y0(height-1) .y1(function(d) { return y(d)+2; }); @@ -213,10 +210,10 @@ function drawBookTimeseries(figure,data) { // console.log(d3.mean(data)); var avhapps = d3.mean(data); - var linearline = d3.svg.line() + var linearline = d3.line() .x(function(d,i) { if (i===0) { return x(d.index); } else { return x(d.index)+3 } }) .y(function(d) { return y(d.value); }) - .interpolate("linear"); + .curve(d3.curveLinear); var averageline = axes.append("path") .datum([ @@ -233,20 +230,18 @@ function drawBookTimeseries(figure,data) { .attr("fill","none"); var averagetext1 = axes.append("text") - .attr({ "x": width+5, - "y": y(avhapps)-3, - "fill": "#606060", - "text-anchor": "start", - }) + .attr("x", width+5) + .attr("y", y(avhapps)-3) + .attr("fill", "#606060") + .attr("text-anchor", "start") .text("Average"); var averagetext2 = axes.append("text") - .attr({ "x": width+5, - "y": y(avhapps)+12, - "fill": "#606060", - "font-weight": "bold", - "text-anchor": "start", - }) + .attr("x", width+5) + .attr("y", y(avhapps)+12) + .attr("fill", "#606060") + .attr("font-weight", "bold") + .attr("text-anchor", "start") .text(avhapps.toFixed(2)); // console.log(d3.min(data)); @@ -285,20 +280,18 @@ function drawBookTimeseries(figure,data) { .attr("fill","none"); var mintext1 = axes.append("text") - .attr({ "x": width+5, - "y": y(minhapps)-3, - "fill": "#606060", - "text-anchor": "start", - }) + .attr("x", width+5) + .attr("y", y(minhapps)-3) + .attr("fill", "#606060") + .attr("text-anchor", "start") .text("Least Happy"); var mintext2 = axes.append("text") - .attr({ "x": width+5, - "y": y(minhapps)+12, - "fill": "#606060", - "font-weight": "bold", - "text-anchor": "start", - }) + .attr("x", width+5) + .attr("y", y(minhapps)+12) + .attr("fill", "#606060") + .attr("font-weight", "bold") + .attr("text-anchor", "start") .text(minhapps.toFixed(2)); var maxcircle = axes.append("circle") @@ -323,20 +316,18 @@ function drawBookTimeseries(figure,data) { .attr("fill","none"); var mintext1 = axes.append("text") - .attr({ "x": width+5, - "y": y(maxhapps), - "fill": "#606060", - "text-anchor": "start", - }) + .attr("x", width+5) + .attr("y", y(maxhapps)) + .attr("fill", "#606060") + .attr("text-anchor", "start") .text("Happiest"); var maxtext2 = axes.append("text") - .attr({ "x": width+5, - "y": y(maxhapps)+15, - "fill": "#606060", - "font-weight": "bold", - "text-anchor": "start", - }) + .attr("x", width+5) + .attr("y", y(maxhapps)+15) + .attr("fill", "#606060") + .attr("font-weight", "bold") + .attr("text-anchor", "start") .text(maxhapps.toFixed(2)); d3.select(window).on("resize.booktimeseries",resize); diff --git a/hedonometer/static/hedonometer/js/selectChapter.js b/hedonometer/static/hedonometer/js/selectChapter.js index 24daa7d6..13811864 100644 --- a/hedonometer/static/hedonometer/js/selectChapter.js +++ b/hedonometer/static/hedonometer/js/selectChapter.js @@ -23,7 +23,7 @@ function selectChapter(figure,numSections) { .attr("class","canvas"); // create the x and y axis - var x = d3.scale.linear() + var x = d3.scaleLinear() //.domain([d3.min(lens),d3.max(lens)]) .domain([0,100]) .range([0,width]); @@ -34,7 +34,7 @@ function selectChapter(figure,numSections) { // (lens); // linear scale function - var y = d3.scale.linear() + var y = d3.scaleLinear() .domain([0,1]) .range([height, 0]); @@ -51,22 +51,19 @@ function selectChapter(figure,numSections) { .attr("width", width) .attr("height", height) .attr("class", "bg") - .style({'stroke-width':'2','stroke':'rgb(0,0,0)'}) + .style('stroke-width','2') + .style('stroke','rgb(0,0,0)') .attr("fill", "#FCFCFC"); // axes creation functions var create_xAxis = function() { - return d3.svg.axis() - .scale(x) - .ticks(9) - .orient("bottom"); } + return d3.axisBottom(x) + .ticks(9); } // axis creation function var create_yAxis = function() { - return d3.svg.axis() - .ticks(3) - .scale(y) //linear scale function - .orient("left"); } + return d3.axisLeft(y) + .ticks(3); } // draw the axes // var yAxis = create_yAxis() @@ -80,8 +77,8 @@ function selectChapter(figure,numSections) { // .call(yAxis); var xAxis = create_xAxis() - .innerTickSize(6) - .outerTickSize(0); + .tickSizeInner(6) + .tickSizeOuter(0); var xAxisHandle = axes.append("g") .attr("class", "x axis ") @@ -89,7 +86,7 @@ function selectChapter(figure,numSections) { .attr("transform", "translate(0," + (height) + ")") .call(xAxis); - d3.selectAll(".tick line").style({'stroke':'black'}); + d3.selectAll(".tick line").style('stroke','black'); // create the clip boundary var clip = axes.append("svg:clipPath") @@ -123,7 +120,7 @@ function selectChapter(figure,numSections) { .attr("fill", "#000000") .attr("style", "text-anchor: middle;"); - var brushX = d3.scale.linear() + var brushX = d3.scaleLinear() .domain([0,allDataRaw.length]) .range([axeslabelmargin.left,width+axeslabelmargin.left]); @@ -136,31 +133,26 @@ function selectChapter(figure,numSections) { .attr("fill", "#000000") .attr("style", "text-anchor: middle;"); - var brush = d3.svg.brush() - .x(brushX) - .extent(compFextent) + // d3 v7 brushX: selection is in pixels; convert with brushX.invert. + var brush = d3.brushX() + .extent([[axeslabelmargin.left, 0], [width+axeslabelmargin.left, height]]) .on("brush",brushing) - .on("brushend",brushended); + .on("end",brushended); var gBrush = canvas.append("g") .attr("class","bottombrush") - .call(brush) - .call(brush.event); + .call(brush); + // position the brush at the initial (data-unit) comparison extent + gBrush.call(brush.move, compFextent.map(brushX)); - - - gBrush.selectAll("rect") - .attr("height",height) - .attr("y",0) - // .style({'stroke-width':'2','stroke':'rgb(100,100,100)','opacity': 0.35}) - // .attr("fill", "rgb(90,90,90)") + gBrush.selectAll(".selection") // .on("mouseout",function() { d3.selectAll(".comparea").attr("visibility","hidden"); }) .on("mouseover",function() { d3.selectAll(".comparea").attr("visibility","visible"); }); - function brushing() { - if (!d3.event.sourceEvent) return; - var extent0 = brush.extent(), - extent1 = extent0.map(Math.round); // should round it to bins + function brushing(event) { + if (!event.sourceEvent) return; + if (!event.selection) return; + var extent1 = event.selection.map(brushX.invert).map(Math.round); // round to bins drawCompArea(extent1); @@ -169,10 +161,10 @@ function selectChapter(figure,numSections) { compFencoder = d3.urllib.encoder().varname("compExtent"); //.varval(compFextent.map(function(d) { return (d/allDataRaw.length).toFixed(2); })); - function brushended() { - if (!d3.event.sourceEvent) return; - var extent0 = brush.extent(), - extent1 = extent0 .map(Math.round); // should round it to bins + function brushended(event) { + if (!event.sourceEvent) return; + if (!event.selection) return; + var extent1 = event.selection.map(brushX.invert).map(Math.round); // should round it to bins //d3.selectAll("text.complabel").attr("x",brushX(d3.sum(extent1)/extent1.length)); @@ -221,8 +213,7 @@ function selectChapter(figure,numSections) { } d3.select(this).transition() - .call(brush.extent(extent1)) - .call(brush.event); + .call(brush.move, extent1.map(brushX)); } d3.select(window).on("resize.selectbottom",resizebottom); @@ -244,16 +235,15 @@ function selectChapter(figure,numSections) { xlabel.attr("x",(leftOffsetStatic+width/2)); - d3.selectAll(".tick line").style({'stroke':'black'}); + d3.selectAll(".tick line").style('stroke','black'); //brushX.range([figwidth*.125,width+figwidth*.125]); brushX.range([leftOffsetStatic,leftOffsetStatic+width]); - brush.x(brushX); - d3.select(".bottombrush") //.transition() - .call(brush.extent(compFextent)) - .call(brush.event); + brush.extent([[leftOffsetStatic, 0], [leftOffsetStatic+width, height]]); + d3.select(".bottombrush") + .call(brush) + .call(brush.move, compFextent.map(brushX)); //brushing(); - //brush.event(); } } diff --git a/hedonometer/static/hedonometer/js/selectChapterTop.js b/hedonometer/static/hedonometer/js/selectChapterTop.js index b7835f62..42667514 100644 --- a/hedonometer/static/hedonometer/js/selectChapterTop.js +++ b/hedonometer/static/hedonometer/js/selectChapterTop.js @@ -23,13 +23,13 @@ function selectChapterTop(figure,numSections) { .attr("class","canvas"); // create the x and y axis - var x = d3.scale.linear() + var x = d3.scaleLinear() //.domain([d3.min(lens),d3.max(lens)]) .domain([0,100]) .range([0,width]); // linear scale function - var y = d3.scale.linear() + var y = d3.scaleLinear() .domain([0,1]) .range([height, 0]); @@ -46,22 +46,19 @@ function selectChapterTop(figure,numSections) { .attr("width", width) .attr("height", height) .attr("class", "bg") - .style({'stroke-width':'2','stroke':'rgb(0,0,0)'}) + .style('stroke-width','2') + .style('stroke','rgb(0,0,0)') .attr("fill", "#FCFCFC"); // axes creation functions var create_xAxis = function() { - return d3.svg.axis() - .scale(x) - .ticks(9) - .orient("bottom"); } + return d3.axisBottom(x) + .ticks(9); } // axis creation function var create_yAxis = function() { - return d3.svg.axis() - .ticks(3) - .scale(y) //linear scale function - .orient("left"); } + return d3.axisLeft(y) + .ticks(3); } // create the clip boundary var clip = axes.append("svg:clipPath") @@ -74,7 +71,7 @@ function selectChapterTop(figure,numSections) { var unclipped_axes = axes; - var brushX = d3.scale.linear() + var brushX = d3.scaleLinear() .domain([0,allDataRaw.length]) .range([axeslabelmargin.left,width+axeslabelmargin.left]); @@ -87,31 +84,28 @@ function selectChapterTop(figure,numSections) { .attr("fill", "#000000") .attr("style", "text-anchor: middle;"); - var brush = d3.svg.brush() - .x(brushX) - .extent(refFextent) + // d3 v7 brushX: selection is in pixels; convert with brushX.invert. + var brush = d3.brushX() + .extent([[axeslabelmargin.left, 4], [width+axeslabelmargin.left, height-2+4]]) .on("brush",brushing) - .on("brushend",brushended); + .on("end",brushended); var gBrush = canvas.append("g") .attr("class","topbrush") - .call(brush) - .call(brush.event); - - gBrush.selectAll("rect") - .attr("height",height-2) - .attr("y",4) - // .style({'stroke-width':'2','stroke':'rgb(100,100,100)','opacity': 0.35}) - // .attr("fill", "rgb(90,90,90)") + .call(brush); + // position the brush at the initial (data-unit) reference extent + gBrush.call(brush.move, refFextent.map(brushX)); + + gBrush.selectAll(".selection") // .on("mouseout",function() { d3.selectAll(".refarea").attr("visibility","hidden"); }) .on("mouseover",function() { d3.selectAll(".refarea").attr("visibility","visible"); }); - function brushing() { - if (!d3.event.sourceEvent) return; - - var extent0 = brush.extent(), - extent1 = extent0.map(Math.round); // should round it to bins + function brushing(event) { + if (!event.sourceEvent) return; + if (!event.selection) return; + + var extent1 = event.selection.map(brushX.invert).map(Math.round); // round to bins drawRefArea(extent1); @@ -120,11 +114,11 @@ function selectChapterTop(figure,numSections) { refFencoder = d3.urllib.encoder().varname("refExtent"); //.varval(refFextent.map(function(d) { return (d/allDataRaw.length).toFixed(2); })); - function brushended() { - if (!d3.event.sourceEvent) return; - var extent0 = brush.extent(), - extent1 = extent0.map(Math.round); // should round it to bins - + function brushended(event) { + if (!event.sourceEvent) return; + if (!event.selection) return; + var extent1 = event.selection.map(brushX.invert).map(Math.round); // should round it to bins + //d3.selectAll("text.reflabel").attr("x",brushX(d3.sum(extent1)/extent1.length)); if ((extent1[0] !== refFextent[0]) || (extent1[1] !== refFextent[1])) @@ -161,8 +155,7 @@ function selectChapterTop(figure,numSections) { } d3.select(this).transition() - .call(brush.extent(extent1)) - .call(brush.event); + .call(brush.move, extent1.map(brushX)); } @@ -185,12 +178,11 @@ function selectChapterTop(figure,numSections) { //brushX.range([figwidth*.125,width+figwidth*.125]); brushX.range([leftOffsetStatic,leftOffsetStatic+width]); - brush.x(brushX); - d3.select(".topbrush") //.transition() - .call(brush.extent(refFextent)) - .call(brush.event); + brush.extent([[leftOffsetStatic, 4], [leftOffsetStatic+width, height-2+4]]); + d3.select(".topbrush") + .call(brush) + .call(brush.move, refFextent.map(brushX)); //brushing(); - //brush.event(); } } diff --git a/hedonometer/templates/hedonometer/books/books.html b/hedonometer/templates/hedonometer/books/books.html index 40ac7605..dd706cc7 100644 --- a/hedonometer/templates/hedonometer/books/books.html +++ b/hedonometer/templates/hedonometer/books/books.html @@ -190,7 +190,20 @@

{% endblock %} {% block bottomjs %} - + + + + + + + + + + + + + + {% endblock %} From c7813b881f3916cb4352cd19668ab47dda324993 Mon Sep 17 00:00:00 2001 From: Andy Reagan Date: Tue, 30 Jun 2026 22:45:08 -0400 Subject: [PATCH 16/16] Pin books v3/gutenberg/harrypotter templates to hedotools@7.2.0 Consistency: the remaining hedotools@7 refs -> @7.2.0 so every template pins the fixed release. Co-Authored-By: Claude Opus 4.8 --- hedonometer/templates/hedonometer/books/gutenberg.html | 6 +++--- hedonometer/templates/hedonometer/books/harrypotter.html | 6 +++--- hedonometer/templates/hedonometer/books/v3.html | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/hedonometer/templates/hedonometer/books/gutenberg.html b/hedonometer/templates/hedonometer/books/gutenberg.html index fef04543..ad46a23c 100644 --- a/hedonometer/templates/hedonometer/books/gutenberg.html +++ b/hedonometer/templates/hedonometer/books/gutenberg.html @@ -192,9 +192,9 @@

Join our waitlist

- - - + + + diff --git a/hedonometer/templates/hedonometer/books/harrypotter.html b/hedonometer/templates/hedonometer/books/harrypotter.html index d59fd2dd..d5ee18b2 100644 --- a/hedonometer/templates/hedonometer/books/harrypotter.html +++ b/hedonometer/templates/hedonometer/books/harrypotter.html @@ -357,9 +357,9 @@ - - - + + + diff --git a/hedonometer/templates/hedonometer/books/v3.html b/hedonometer/templates/hedonometer/books/v3.html index a0efa68d..6b19570d 100644 --- a/hedonometer/templates/hedonometer/books/v3.html +++ b/hedonometer/templates/hedonometer/books/v3.html @@ -181,9 +181,9 @@

- - - + + +