Compare commits
2 Commits
011067509a
...
8dcee2d0bf
Author | SHA1 | Date |
---|---|---|
Ryan Rix | 8dcee2d0bf | |
Ryan Rix | dd7b1c4656 |
22
arcology.org
22
arcology.org
|
@ -862,17 +862,21 @@ This will display the header arguments to =org-babel= source blocks: You're star
|
|||
span.babel-args {
|
||||
text-align: right;
|
||||
display: block;
|
||||
background: var(--light-gray);
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
span.babel-args::before {
|
||||
max-width: 800px;
|
||||
width: 50%;
|
||||
height: 1px;
|
||||
text-align: left;
|
||||
vertical-align: middle;
|
||||
background: var(--dark-gray);
|
||||
content: "";
|
||||
display: inline-block;
|
||||
pre.src {
|
||||
border-top: 1px solid var(--black);
|
||||
background-color: var(--light-gray);
|
||||
font-style: normal;
|
||||
overflow: scroll;
|
||||
margin-top: 0;
|
||||
|
||||
padding-top: 1em;
|
||||
padding-left: 0.5em;
|
||||
padding-bottom: 1em;
|
||||
padding-right: 0.5em;
|
||||
}
|
||||
#+end_src
|
||||
|
||||
|
|
|
@ -75,17 +75,21 @@ section.sidebar > div.backlinks {
|
|||
span.babel-args {
|
||||
text-align: right;
|
||||
display: block;
|
||||
background: var(--light-gray);
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
span.babel-args::before {
|
||||
max-width: 800px;
|
||||
width: 50%;
|
||||
height: 1px;
|
||||
text-align: left;
|
||||
vertical-align: middle;
|
||||
background: var(--dark-gray);
|
||||
content: "";
|
||||
display: inline-block;
|
||||
pre.src {
|
||||
border-top: 1px solid var(--black);
|
||||
background-color: var(--light-gray);
|
||||
font-style: normal;
|
||||
overflow: scroll;
|
||||
margin-top: 0;
|
||||
|
||||
padding-top: 1em;
|
||||
padding-left: 0.5em;
|
||||
padding-bottom: 1em;
|
||||
padding-right: 0.5em;
|
||||
}
|
||||
/* Org Page-specific CSS Stylings:6 ends here */
|
||||
|
||||
|
|
|
@ -213,38 +213,23 @@ roam/management/commands/__init__.py (this isn't needed)
|
|||
These will be the "public" interfaces for the generators, a set of Emacs functions that can be loaded in to the environment to call out to the management commands:
|
||||
|
||||
#+begin_src emacs-lisp :tangle ~/org/cce/arcology-django-helpers.el :results none
|
||||
(defun arroyo-collect-files (module &optional role)
|
||||
(setenv "ARCOLOGY_DB_PATH" "/home/rrix/org/arcology-django/db.sqlite3")
|
||||
(let* ((flake-path "path:/home/rrix/org/arcology-django")
|
||||
(cmd (s-join " " (list (format "nix run %s#arcology" flake-path)
|
||||
"--"
|
||||
"generate -m"
|
||||
module
|
||||
(when role
|
||||
(format "-r %s" role))
|
||||
"--dry-run"
|
||||
"2>/dev/null"
|
||||
" | sort")))
|
||||
(output (shell-command-to-string cmd)))
|
||||
output))
|
||||
;; (defun arroyo-collect-files (module &optional role)
|
||||
;; (setenv "ARCOLOGY_DB_PATH" "/home/rrix/org/arcology-django/db.sqlite3")
|
||||
;; (let* ((flake-path "path:/home/rrix/org/arcology-django")
|
||||
;; (cmd (s-join " " (list (format "nix run %s#arcology" flake-path)
|
||||
;; "--"
|
||||
;; "generate -m"
|
||||
;; module
|
||||
;; (when role
|
||||
;; (format "-r %s" role))
|
||||
;; "--dry-run"
|
||||
;; "2>/dev/null"
|
||||
;; " | sort")))
|
||||
;; (output (shell-command-to-string cmd)))
|
||||
;; output))
|
||||
|
||||
(defun arroyo-generate-imports (module &optional role destination do-sort)
|
||||
(setenv "ARCOLOGY_DB_PATH" "/home/rrix/org/arcology-django/db.sqlite3")
|
||||
(let* ((flake-path "path:/home/rrix/org/arcology-django")
|
||||
(do-sort (and do-sort t))
|
||||
(cmd (s-join " " (list (format "nix run %s#arcology" flake-path)
|
||||
"--"
|
||||
"generate -m"
|
||||
module
|
||||
(when role
|
||||
(format "-r %s" role))
|
||||
(when destination
|
||||
(format "-d %s" destination))
|
||||
"2>/dev/null"
|
||||
(when do-sort
|
||||
" | sort"))))
|
||||
(output (shell-command-to-string cmd)))
|
||||
output))
|
||||
(arcology-api-generator module role destination do-sort))
|
||||
|
||||
(defun arroyo-generate-imports-formatted (format-fn output)
|
||||
(thread-last (arroyo-generate-imports "home-manager" "server")
|
||||
|
|
164
localapi.org
164
localapi.org
|
@ -3,18 +3,47 @@
|
|||
:END:
|
||||
#+TITLE: A Localhost API for the Arcology
|
||||
#+FILETAGS: :Project:
|
||||
|
||||
#+ARCOLOGY_KEY: arcology/localapi
|
||||
#+ARROYO_EMACS_MODULE: arcology-localapi-commands
|
||||
|
||||
By moving the database out of EmacSQL and in to Django, I have hobbled some of the Arcology's [[id:knowledge_base][Knowledge Management]] [[id:e79d4cdc-082f-4b1d-ae08-d979802f09ee][Living Systems]] and meta-cognitive skills. =arroyo-db-query= no longer existing means that things like the [[id:20230526T105534.711282][Direnv arroyo-db integration]] stopped working, and the helpers in [[id:cce/my_nixos_configuration][My NixOS configuration]] and elsewhere rely on the stringly-typed [[id:20231217T154938.132553][Arcology generate Command]] and a =nix run= invocation to function.
|
||||
|
||||
I think it's worth building a small HTTP JSON API which can serve some of the common queries, to at least see if it's worth the trouble..
|
||||
|
||||
Here's how to use this from Emacs Lisp:
|
||||
|
||||
- =(arcology-localapi-call method path)= is an API helper which given an API path will fetch the data and return a deserialized JSON structure
|
||||
- these interactive commands will fetch a URL, putting it on your kill ring or clipboard if you call it with =M-x= or equivalent:
|
||||
- =(arcology-key-to-url page-key &optional heading-id)= will take an =ARCOLOGY_KEY= and return a URL
|
||||
- =(arcology-file-to-url file-path &optional heading-id)= will do the same with a file path from =(buffer-file-name)= or so.
|
||||
- =(arcology-url-at-point)= gets the org-id of the heading your cursor is in if it has one, and makes a URL that links directly to that.
|
||||
- =(arcology-read-url)= pops up a list of all the org-roam headings, and returns a URL to it.
|
||||
|
||||
* Server view and Arroyo Emacs lisp scaffolding
|
||||
|
||||
I probably should use Django REST Framework for this, but. I'm just gonna beat two stones together until a JSON API falls out. This will probably change the first time I add a =POST= call or want to do more complex auth stuff.
|
||||
|
||||
* Server view scaffolding
|
||||
The API will be simple, with a bearer token provided by a local state file:
|
||||
|
||||
These are URLs and basic imports... nothing to concern yourself with, probably!
|
||||
#+begin_src emacs-lisp :tangle ~/org/cce/arcology-localapi-commands.el
|
||||
(use-package plz)
|
||||
|
||||
(defcustom arcology-localapi-bearer-token "changeme!"
|
||||
"API Token, can be read by a file see [`other variable i haven't defined but it's a function dark laughter']")
|
||||
|
||||
(defcustom arcology-api-base "http://127.0.0.1:8000/api/v1"
|
||||
"localapi base url")
|
||||
|
||||
(defun arcology-localapi-call (method path &rest plz-args)
|
||||
(let ((plz-args (or plz-args '(:as json-read))))
|
||||
(apply 'plz method (format "%s%s" arcology-api-base path)
|
||||
:headers `(("Authorization" . ,(format "Bearer %s" arcology-localapi-bearer-token)))
|
||||
plz-args
|
||||
)))
|
||||
#+end_src
|
||||
|
||||
These are the API URLs and basic imports... nothing to concern yourself with, probably!
|
||||
|
||||
#+begin_src python :tangle localapi/urls.py :mkdirp yes
|
||||
from django.contrib import admin
|
||||
|
@ -24,6 +53,8 @@ from localapi import views
|
|||
|
||||
urlpatterns = [
|
||||
path("", views.index),
|
||||
path("generate/<slug:module>/<slug:role>", views.generate, name="keyword_by_key"),
|
||||
path("generate/<slug:module>", views.generate, name="keyword_by_key"),
|
||||
path("keywords/<slug:key>", views.keyword_by_key, name="keyword_by_key"),
|
||||
re_path("keywords/(?P<key>[0-9a-zA-Z_-]+)/(?P<value>[0-9a-zA-Z/_\-]+)", views.keyword_by_key_value, name="keyword_by_key"),
|
||||
re_path("page/(?P<route_key>[0-9a-zA-Z/_\-]+)", views.page_metadata, name="page_metadata"),
|
||||
|
@ -53,7 +84,7 @@ def index(request):
|
|||
return JsonResponse(dict(state="ok :)"))
|
||||
#+end_src
|
||||
|
||||
* DONE Local API Auth Middleware
|
||||
** DONE Local API Auth Middleware
|
||||
:PROPERTIES:
|
||||
:ID: 20240313T170425.687381
|
||||
:END:
|
||||
|
@ -135,6 +166,7 @@ def keyword_by_key_value(request, key, value):
|
|||
))
|
||||
#+end_src
|
||||
|
||||
** NEXT add elisp APIs
|
||||
* Page and File metadata
|
||||
|
||||
And some really simple APIs to get information about =Page= and =File= objects out of the database:
|
||||
|
@ -150,7 +182,23 @@ def _json_page(page):
|
|||
)
|
||||
#+end_src
|
||||
|
||||
=GET http://127.0.0.1:8000/api/v1/page/arcology/localapi=
|
||||
=GET http://127.0.0.1:8000/api/v1/page/arcology/localapi= or this Emacs Lisp command:
|
||||
A command which will take an =ARCOLOGY_KEY= and the ID of a heading on that page, and return a URL for it:
|
||||
|
||||
#+begin_src emacs-lisp :tangle ~/org/cce/arcology-localapi-commands.el
|
||||
(defun arcology-key-to-url (page-key &optional heading-id)
|
||||
(interactive "sKey: \nsHeading ID: ")
|
||||
(let ((url (concat
|
||||
(thread-last
|
||||
(arcology-localapi-call 'get (format "/page/%s" page-key))
|
||||
(alist-get 'page)
|
||||
(alist-get 'url))
|
||||
(when heading-id (format "#%s" heading-id)))))
|
||||
(when (called-interactively-p)
|
||||
(kill-new url)
|
||||
(message "%s" url))
|
||||
url))
|
||||
#+end_src
|
||||
|
||||
#+begin_src python :tangle localapi/views.py
|
||||
@authenticated
|
||||
|
@ -166,7 +214,25 @@ def page_metadata(request, route_key):
|
|||
))
|
||||
#+end_src
|
||||
|
||||
=GET http://127.0.0.1:8000/api/v1/file/arcology-django/localapi.org= (yes, there is a double-slash there. it's a full path *after* the
|
||||
=GET http://127.0.0.1:8000/api/v1/file/arcology-django/localapi.org= and the Emacs Lisp command which will do the same:
|
||||
|
||||
#+begin_src emacs-lisp :tangle ~/org/cce/arcology-localapi-commands.el
|
||||
(defun arcology-file-to-url (file-path &optional heading-id)
|
||||
(interactive "fKey: \nsHeading ID: ")
|
||||
(let ((url (concat
|
||||
(thread-last
|
||||
(file-relative-name file-path org-roam-directory)
|
||||
(format "http://127.0.0.1:8000/api/v1/file/%s")
|
||||
(arcology-localapi-call 'get)
|
||||
(alist-get 'file)
|
||||
(alist-get 'page)
|
||||
(alist-get 'url))
|
||||
(when heading-id (format "#%s" heading-id)))))
|
||||
(when (called-interactively-p)
|
||||
(kill-new url)
|
||||
(message "%s" url))
|
||||
url))
|
||||
#+end_src
|
||||
|
||||
#+begin_src python :tangle localapi/views.py
|
||||
from django.conf import settings
|
||||
|
@ -186,12 +252,96 @@ def file_metadata(request, file_path):
|
|||
))
|
||||
#+end_src
|
||||
|
||||
* NEXT [[id:20231217T154938.132553][Arcology generate Command]]
|
||||
* Some more ELisp helpers
|
||||
:PROPERTIES:
|
||||
:ID: 20240313T212950.461285
|
||||
:END:
|
||||
|
||||
#+begin_src emacs-lisp :tangle ~/org/cce/arcology-localapi-commands.el
|
||||
(defun arcology-url-at-point (pom)
|
||||
(interactive "m")
|
||||
(let ((url (arcology-key-to-url
|
||||
(thread-last
|
||||
(org-collect-keywords '("ARCOLOGY_KEY"))
|
||||
(first)
|
||||
(second))
|
||||
(and (> (org-outline-level) 0)
|
||||
(org-id-get)))))
|
||||
(when (called-interactively-p)
|
||||
(kill-new url)
|
||||
(message "%s" url))
|
||||
url))
|
||||
#+end_src
|
||||
|
||||
A command which will get the URL for a heading you can select using your =completing-read=:
|
||||
|
||||
#+begin_src emacs-lisp :tangle ~/org/cce/arcology-localapi-commands.el
|
||||
(defun arcology-read-url ()
|
||||
(interactive)
|
||||
(let* ((node (org-roam-node-read))
|
||||
(url (arcology-file-to-url (org-roam-node-file node)
|
||||
(and (> (org-outline-level) 0)
|
||||
(org-id-get)))))
|
||||
(when (called-interactively-p)
|
||||
(kill-new url)
|
||||
(message "%s" url))))
|
||||
#+end_src
|
||||
|
||||
* DONE [[id:20231217T154938.132553][Arcology generate Command]]
|
||||
:LOGBOOK:
|
||||
- State "DONE" from "NEXT" [2024-03-13 Wed 23:56]
|
||||
:END:
|
||||
|
||||
emacs-lisp code along with the HTTP endpoints to populate things from the Generators
|
||||
|
||||
#+begin_src emacs-lisp :tangle ~/org/cce/arcology-localapi-commands.el
|
||||
(defun arcology-api-generator (module &optional role destination do-sort)
|
||||
(interactive)
|
||||
(let ((url (format "/generate/%s%s" module
|
||||
(if role (format "/%s" role) ""))))
|
||||
(let ((gen-text (with-current-buffer
|
||||
(arcology-localapi-call 'get url :as 'buffer)
|
||||
(buffer-substring-no-properties (point-min) (point-max))))
|
||||
(buf (if destination
|
||||
(find-file destination)
|
||||
(get-buffer-create (format "*%s out*" module)))))
|
||||
(with-current-buffer buf
|
||||
(erase-buffer)
|
||||
(insert gen-text)
|
||||
(when do-sort
|
||||
(sort-lines nil (point-min) (point-max)))
|
||||
(when destination
|
||||
(save-buffer))
|
||||
(when (called-interactively-p)
|
||||
(display-buffer))
|
||||
(buffer-string)))))
|
||||
#+end_src
|
||||
|
||||
#+begin_src emacs-lisp :tangle ~/org/cce/arcology-localapi-commands.el
|
||||
(provide 'cce/arcology-localapi-commands)
|
||||
#+end_src
|
||||
|
||||
#+begin_src python :tangle localapi/views.py
|
||||
import tempfile
|
||||
from django.core.management import call_command
|
||||
|
||||
@authenticated
|
||||
def generate(request, module, role=None):
|
||||
f = tempfile.NamedTemporaryFile()
|
||||
fn = f.name
|
||||
# i hate this!!!
|
||||
if role:
|
||||
call_command('generate', destination=fn, module=module, role=role)
|
||||
else:
|
||||
call_command('generate', destination=fn, module=module)
|
||||
f.seek(0)
|
||||
|
||||
return HttpResponse(f)
|
||||
#+end_src
|
||||
|
||||
this is all full of jank. having to pass to a temporary file because the generate commands are locked away in a django management command is silly, i should refactor all of this. the branch is silly, too. But it's very easy now to get my literate programming helpers to quickly generate the code imports and whatnot as soon as the local server is running.
|
||||
|
||||
* NEXT NixOS deployment for endpoints in [[id:cce/my_nixos_configuration][My NixOS configuration]] or rather [[id:cce/home-manager][home-manager]]
|
||||
bearer token setup and all that
|
||||
|
||||
|
||||
this thing now needs to run as a systemd user unit!
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# [[file:../localapi.org::*Server view scaffolding][Server view scaffolding:1]]
|
||||
# [[file:../localapi.org::*Server view and Arroyo Emacs lisp scaffolding][Server view and Arroyo Emacs lisp scaffolding:2]]
|
||||
from django.contrib import admin
|
||||
from django.urls import path, re_path, include
|
||||
|
||||
|
@ -6,9 +6,11 @@ from localapi import views
|
|||
|
||||
urlpatterns = [
|
||||
path("", views.index),
|
||||
path("generate/<slug:module>/<slug:role>", views.generate, name="keyword_by_key"),
|
||||
path("generate/<slug:module>", views.generate, name="keyword_by_key"),
|
||||
path("keywords/<slug:key>", views.keyword_by_key, name="keyword_by_key"),
|
||||
re_path("keywords/(?P<key>[0-9a-zA-Z_-]+)/(?P<value>[0-9a-zA-Z/_\-]+)", views.keyword_by_key_value, name="keyword_by_key"),
|
||||
re_path("page/(?P<route_key>[0-9a-zA-Z/_\-]+)", views.page_metadata, name="page_metadata"),
|
||||
re_path("file/(?P<file_path>[0-9a-zA-Z/_\-\.]+)", views.file_metadata, name="file_metadata"),
|
||||
]
|
||||
# Server view scaffolding:1 ends here
|
||||
# Server view and Arroyo Emacs lisp scaffolding:2 ends here
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# [[file:../localapi.org::*Server view scaffolding][Server view scaffolding:2]]
|
||||
# [[file:../localapi.org::*Server view and Arroyo Emacs lisp scaffolding][Server view and Arroyo Emacs lisp scaffolding:3]]
|
||||
from django.http import HttpRequest, HttpResponse, HttpResponseNotFound, Http404, JsonResponse
|
||||
from django.shortcuts import render, get_object_or_404
|
||||
|
||||
|
@ -10,13 +10,13 @@ from prometheus_client import Counter, Histogram
|
|||
|
||||
import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
# Server view scaffolding:2 ends here
|
||||
# Server view and Arroyo Emacs lisp scaffolding:3 ends here
|
||||
|
||||
# [[file:../localapi.org::*Server view scaffolding][Server view scaffolding:3]]
|
||||
# [[file:../localapi.org::*Server view and Arroyo Emacs lisp scaffolding][Server view and Arroyo Emacs lisp scaffolding:4]]
|
||||
@authenticated
|
||||
def index(request):
|
||||
return JsonResponse(dict(state="ok :)"))
|
||||
# Server view scaffolding:3 ends here
|
||||
# Server view and Arroyo Emacs lisp scaffolding:4 ends here
|
||||
|
||||
# [[file:../localapi.org::*Keyword metadata][Keyword metadata:1]]
|
||||
import roam.models
|
||||
|
@ -62,7 +62,7 @@ def _json_page(page):
|
|||
)
|
||||
# Page and File metadata:1 ends here
|
||||
|
||||
# [[file:../localapi.org::*Page and File metadata][Page and File metadata:2]]
|
||||
# [[file:../localapi.org::*Page and File metadata][Page and File metadata:3]]
|
||||
@authenticated
|
||||
def page_metadata(request, route_key):
|
||||
page = arcology.models.Page.objects.get(route_key=route_key)
|
||||
|
@ -74,9 +74,9 @@ def page_metadata(request, route_key):
|
|||
page=_json_page(page),
|
||||
keywords=_json_keywords(keywords)
|
||||
))
|
||||
# Page and File metadata:2 ends here
|
||||
# Page and File metadata:3 ends here
|
||||
|
||||
# [[file:../localapi.org::*Page and File metadata][Page and File metadata:3]]
|
||||
# [[file:../localapi.org::*Page and File metadata][Page and File metadata:5]]
|
||||
from django.conf import settings
|
||||
|
||||
@authenticated
|
||||
|
@ -92,4 +92,22 @@ def file_metadata(request, file_path):
|
|||
page=_json_page(page),
|
||||
),
|
||||
))
|
||||
# Page and File metadata:3 ends here
|
||||
# Page and File metadata:5 ends here
|
||||
|
||||
# [[file:../localapi.org::*\[\[id:20231217T154938.132553\]\[Arcology generate Command\]\]][[[id:20231217T154938.132553][Arcology generate Command]]:3]]
|
||||
import tempfile
|
||||
from django.core.management import call_command
|
||||
|
||||
@authenticated
|
||||
def generate(request, module, role=None):
|
||||
f = tempfile.NamedTemporaryFile()
|
||||
fn = f.name
|
||||
# i hate this!!!
|
||||
if role:
|
||||
call_command('generate', destination=fn, module=module, role=role)
|
||||
else:
|
||||
call_command('generate', destination=fn, module=module)
|
||||
f.seek(0)
|
||||
|
||||
return HttpResponse(f)
|
||||
# [[id:20231217T154938.132553][Arcology generate Command]]:3 ends here
|
||||
|
|
Loading…
Reference in New Issue