Created
January 31, 2026 14:46
-
-
Save symbioquine/7a67edccd32ea634cc5bf0597f9c7f31 to your computer and use it in GitHub Desktop.
Finds orphaned quantities in farmOS - not referenced by any current log revision
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| { | |
| "metadata": { | |
| "kernelspec": { | |
| "name": "python", | |
| "display_name": "Python (Pyodide)", | |
| "language": "python" | |
| }, | |
| "language_info": { | |
| "codemirror_mode": { | |
| "name": "python", | |
| "version": 3 | |
| }, | |
| "file_extension": ".py", | |
| "mimetype": "text/x-python", | |
| "name": "python", | |
| "nbconvert_exporter": "python", | |
| "pygments_lexer": "ipython3", | |
| "version": "3.8" | |
| } | |
| }, | |
| "nbformat_minor": 5, | |
| "nbformat": 4, | |
| "cells": [ | |
| { | |
| "id": "952a6f9d-30db-4a1c-898e-f098c2b58d54", | |
| "cell_type": "code", | |
| "source": "from pyodide.http import pyfetch\nfrom js import location\nimport json\n\nasync def get_all_entities(base_url):\n next_page = base_url\n while next_page is not None:\n resp = await pyfetch(next_page, method='GET')\n\n data = await resp.json()\n\n for entity in data.get('data', []):\n yield entity\n\n next_page = data.get('links', {}).get('next', {}).get('href', None)\n", | |
| "metadata": { | |
| "trusted": true | |
| }, | |
| "outputs": [], | |
| "execution_count": 1 | |
| }, | |
| { | |
| "id": "3132a1de-2720-4d00-ba73-7903dc8b0666", | |
| "cell_type": "code", | |
| "source": "all_quantity_types = [q['attributes']['drupal_internal__id'] async for q in get_all_entities(location.origin + '/api/quantity_type/quantity_type')]\n\nprint(all_quantity_types)", | |
| "metadata": { | |
| "trusted": true | |
| }, | |
| "outputs": [ | |
| { | |
| "name": "stdout", | |
| "output_type": "stream", | |
| "text": "['material', 'standard']\n" | |
| } | |
| ], | |
| "execution_count": 2 | |
| }, | |
| { | |
| "id": "32a3b012-2503-4962-a977-f5e9ae9eaaae", | |
| "cell_type": "code", | |
| "source": "all_quantity_ids = {q['id'] for quantity_type in all_quantity_types async for q in get_all_entities(f'{location.origin}/api/quantity/{quantity_type}?fields[quantity--{quantity_type}]=id&sort=drupal_internal__id')}\n\nlen(all_quantity_ids)", | |
| "metadata": { | |
| "trusted": true | |
| }, | |
| "outputs": [ | |
| { | |
| "execution_count": 14, | |
| "output_type": "execute_result", | |
| "data": { | |
| "text/plain": "5538" | |
| }, | |
| "metadata": {} | |
| } | |
| ], | |
| "execution_count": 14 | |
| }, | |
| { | |
| "id": "4034aa2c-0952-4b61-b17c-f88b43e4fe57", | |
| "cell_type": "code", | |
| "source": "all_log_types = [q['attributes']['drupal_internal__id'] async for q in get_all_entities(location.origin + '/api/log_type/log_type')]\n\nprint(all_log_types)", | |
| "metadata": { | |
| "trusted": true | |
| }, | |
| "outputs": [ | |
| { | |
| "name": "stdout", | |
| "output_type": "stream", | |
| "text": "['activity', 'birth', 'harvest', 'input', 'maintenance', 'observation', 'seeding']\n" | |
| } | |
| ], | |
| "execution_count": 5 | |
| }, | |
| { | |
| "id": "62b7d49e-a1d7-412c-ab56-54c47510c0a2", | |
| "cell_type": "code", | |
| "source": "# Kind of a weird function: iterate through the pages, but yield the included entities\nasync def get_all_entities_includes(base_url):\n next_page = base_url\n while next_page is not None:\n resp = await pyfetch(next_page, method='GET')\n\n data = await resp.json()\n\n for entity in data.get('included', []):\n yield entity\n\n next_page = data.get('links', {}).get('next', {}).get('href', None)\n\nquantity_fields_query_part = '&'.join([f'fields[quantity--{quantity_type}]=id' for quantity_type in all_quantity_types])\n\nlogs_with_quantities_filter = 'filter[a][condition][path]=quantity.id&filter[a][condition][operator]=IS NOT NULL'\n\nbase_query = lambda log_type: f'{location.origin}/api/log/{log_type}?{logs_with_quantities_filter}&sort=drupal_internal__id&fields[log--{log_type}]=&include=quantity&{quantity_fields_query_part}'\n\nall_referenced_quantity_ids = {q['id'] for log_type in all_log_types async for q in get_all_entities_includes(base_query(log_type))}\n\nlen(all_referenced_quantity_ids)", | |
| "metadata": { | |
| "trusted": true | |
| }, | |
| "outputs": [ | |
| { | |
| "execution_count": 10, | |
| "output_type": "execute_result", | |
| "data": { | |
| "text/plain": "5514" | |
| }, | |
| "metadata": {} | |
| } | |
| ], | |
| "execution_count": 10 | |
| }, | |
| { | |
| "id": "91763140-6784-49c5-991e-d4e8911488d7", | |
| "cell_type": "code", | |
| "source": "len(all_quantity_ids - all_referenced_quantity_ids)", | |
| "metadata": { | |
| "trusted": true | |
| }, | |
| "outputs": [ | |
| { | |
| "execution_count": 15, | |
| "output_type": "execute_result", | |
| "data": { | |
| "text/plain": "24" | |
| }, | |
| "metadata": {} | |
| } | |
| ], | |
| "execution_count": 15 | |
| }, | |
| { | |
| "id": "89c882f9-313d-4e87-885e-35241bbef6c9", | |
| "cell_type": "code", | |
| "source": "len(all_referenced_quantity_ids - all_quantity_ids)", | |
| "metadata": { | |
| "trusted": true | |
| }, | |
| "outputs": [ | |
| { | |
| "execution_count": 16, | |
| "output_type": "execute_result", | |
| "data": { | |
| "text/plain": "0" | |
| }, | |
| "metadata": {} | |
| } | |
| ], | |
| "execution_count": 16 | |
| } | |
| ] | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment