mirror of
https://github.com/SqrtMinusOne/biome.git
synced 2025-12-10 14:35:13 +03:00
biome-multi: basically works
This commit is contained in:
parent
2f96d6de86
commit
b351a52c8c
4 changed files with 296 additions and 5 deletions
37
biome-api.el
37
biome-api.el
|
|
@ -128,5 +128,42 @@ called with QUERY and the data returned by the API as arguments."
|
||||||
(cl-function (lambda (&key error-thrown response &allow-other-keys)
|
(cl-function (lambda (&key error-thrown response &allow-other-keys)
|
||||||
(biome-api--show-error error-thrown response))))))
|
(biome-api--show-error error-thrown response))))))
|
||||||
|
|
||||||
|
(defun biome-api-get-multiple (queries callback)
|
||||||
|
"Get data from Open Meteo API.
|
||||||
|
|
||||||
|
QUERIES is a list of forms as defined by `biome-query-current'. CALLBACK is
|
||||||
|
called with QUERIES and the data returned by the API as arguments."
|
||||||
|
(let (requests)
|
||||||
|
(seq-map-indexed
|
||||||
|
(lambda (query idx)
|
||||||
|
(push
|
||||||
|
(request (alist-get (alist-get :name query)
|
||||||
|
biome-api-urls nil nil #'equal)
|
||||||
|
:type "GET"
|
||||||
|
:params (biome-api--get-params query)
|
||||||
|
:parser #'json-read
|
||||||
|
:success (cl-function
|
||||||
|
(lambda (&key data &allow-other-keys)
|
||||||
|
;; I'm not sure why, but `request-response-done-p' for
|
||||||
|
;; the current request returns nil. I don't
|
||||||
|
;; know how stable this is, so...
|
||||||
|
(let ((completed-count
|
||||||
|
(cl-loop for i from 0
|
||||||
|
for request in requests
|
||||||
|
if (or (request-response-done-p request)
|
||||||
|
(= i (- (length requests) 1 idx)))
|
||||||
|
sum 1)))
|
||||||
|
(message "Completed %d/%d requests"
|
||||||
|
completed-count (length queries))
|
||||||
|
(when (eq (length queries) completed-count)
|
||||||
|
(funcall callback (copy-tree queries)
|
||||||
|
(mapcar #'request-response-data
|
||||||
|
(reverse requests)))))))
|
||||||
|
:error
|
||||||
|
(cl-function (lambda (&key error-thrown response &allow-other-keys)
|
||||||
|
(biome-api--show-error error-thrown response))))
|
||||||
|
requests))
|
||||||
|
queries)))
|
||||||
|
|
||||||
(provide 'biome-api)
|
(provide 'biome-api)
|
||||||
;;; biome-api.el ends here
|
;;; biome-api.el ends here
|
||||||
|
|
|
||||||
236
biome-multi.el
Normal file
236
biome-multi.el
Normal file
|
|
@ -0,0 +1,236 @@
|
||||||
|
;;; biome-multi.el --- Do multiple queries to Open Meteo -*- lexical-binding: t -*-
|
||||||
|
|
||||||
|
;; Copyright (C) 2024 Korytov Pavel
|
||||||
|
|
||||||
|
;; Author: Korytov Pavel <thexcloud@gmail.com>
|
||||||
|
;; Maintainer: Korytov Pavel <thexcloud@gmail.com>
|
||||||
|
|
||||||
|
;; This file is NOT part of GNU Emacs.
|
||||||
|
|
||||||
|
;; This program is free software: you can redistribute it and/or modify
|
||||||
|
;; it under the terms of the GNU General Public License as published by
|
||||||
|
;; the Free Software Foundation, either version 3 of the License, or
|
||||||
|
;; (at your option) any later version.
|
||||||
|
|
||||||
|
;; This program is distributed in the hope that it will be useful,
|
||||||
|
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
;; GNU General Public License for more details.
|
||||||
|
|
||||||
|
;; You should have received a copy of the GNU General Public License
|
||||||
|
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
;;; Commentary:
|
||||||
|
|
||||||
|
;; TODO
|
||||||
|
|
||||||
|
;;; Code:
|
||||||
|
(require 'biome-query)
|
||||||
|
(require 'font-lock)
|
||||||
|
(require 'transient)
|
||||||
|
|
||||||
|
(defvar biome-multi-query-current nil
|
||||||
|
"Current query.
|
||||||
|
|
||||||
|
This is a list of forms as defined by `biome-query-current'.")
|
||||||
|
|
||||||
|
(defvar biome-multi--callback nil
|
||||||
|
"Call this with the selected query.")
|
||||||
|
|
||||||
|
(defclass biome-multi--transient-report (transient-suffix)
|
||||||
|
((transient :initform t))
|
||||||
|
"A class to display the current report for `biome-multi'.")
|
||||||
|
|
||||||
|
(cl-defmethod transient-init-value ((_ biome-multi--transient-report))
|
||||||
|
"A dummy method for `biome-multi--transient-report'."
|
||||||
|
nil)
|
||||||
|
|
||||||
|
(cl-defmethod transient-format ((_ biome-multi--transient-report))
|
||||||
|
"TODO."
|
||||||
|
(if (seq-empty-p biome-multi-query-current)
|
||||||
|
(propertize "Add at least one query" 'face 'error)
|
||||||
|
(cl-loop for i from 0
|
||||||
|
for query in biome-multi-query-current
|
||||||
|
concat (propertize (format "Query #%d: %s\n" i
|
||||||
|
(alist-get :name query))
|
||||||
|
'face 'font-lock-keyword-face)
|
||||||
|
concat (biome-query--format query)
|
||||||
|
if (not (eq i (1- (length biome-multi-query-current))))
|
||||||
|
concat "\n")))
|
||||||
|
|
||||||
|
(transient-define-infix biome-multi--transient-report-infix ()
|
||||||
|
:class 'biome-multi--transient-report
|
||||||
|
:key "~~1")
|
||||||
|
|
||||||
|
(defun biome-multi-add-query ()
|
||||||
|
(interactive)
|
||||||
|
(biome-query
|
||||||
|
(lambda (query)
|
||||||
|
(if (seq-empty-p biome-multi-query-current)
|
||||||
|
(setq biome-multi-query-current (list (copy-tree query)))
|
||||||
|
(nconc biome-multi-query-current (list (copy-tree query))))
|
||||||
|
(biome-multi-query biome-multi--callback))))
|
||||||
|
|
||||||
|
(defun biome-multi-reset ()
|
||||||
|
(interactive)
|
||||||
|
(setq biome-multi-query-current nil))
|
||||||
|
|
||||||
|
(defun biome-multi-edit (idx)
|
||||||
|
(interactive "nQuery number: ")
|
||||||
|
(when (or (< idx 0)
|
||||||
|
(>= idx (length biome-multi-query-current)))
|
||||||
|
(user-error "Invalid query number"))
|
||||||
|
(setq biome-query-current (nth idx biome-multi-query-current))
|
||||||
|
(setq biome-query--callback
|
||||||
|
(lambda (query)
|
||||||
|
(setf (nth idx biome-multi-query-current) query)
|
||||||
|
(biome-multi-query biome-multi--callback)))
|
||||||
|
(biome-query--section-open (alist-get :name biome-query-current)))
|
||||||
|
|
||||||
|
(defun biome-multi-remove (idx)
|
||||||
|
(interactive "nQuery number: ")
|
||||||
|
(when (or (< idx 0)
|
||||||
|
(>= idx (length biome-multi-query-current)))
|
||||||
|
(user-error "Invalid query number"))
|
||||||
|
(setq biome-multi-query-current
|
||||||
|
(cl-loop for query in biome-multi-query-current
|
||||||
|
for i from 0
|
||||||
|
unless (eq i idx)
|
||||||
|
collect query)))
|
||||||
|
|
||||||
|
(defun biome-multi-exec ()
|
||||||
|
(interactive)
|
||||||
|
(when (seq-empty-p biome-multi-query-current)
|
||||||
|
(user-error "No queries to execute"))
|
||||||
|
(funcall biome-multi--callback biome-multi-query-current))
|
||||||
|
|
||||||
|
(transient-define-prefix biome-multi-query (callback)
|
||||||
|
["Open Meteo Multi Query"
|
||||||
|
(biome-multi--transient-report-infix)]
|
||||||
|
["Queries"
|
||||||
|
:class transient-row
|
||||||
|
("a" "Add query" biome-multi-add-query)
|
||||||
|
("e" "Edit query" biome-multi-edit :transient t)
|
||||||
|
("d" "Delete query" biome-multi-remove :transient t)]
|
||||||
|
["Actions"
|
||||||
|
:class transient-row
|
||||||
|
("RET" "Run" biome-multi-exec)
|
||||||
|
("R" "Reset" biome-multi-reset :transient t)
|
||||||
|
("q" "Quit" transient-quit-one)]
|
||||||
|
(interactive (list nil))
|
||||||
|
(unless callback
|
||||||
|
(error "Callback is not set. Run M-x `biome-multi' instead"))
|
||||||
|
(setq biome-multi--callback callback)
|
||||||
|
(transient-setup 'biome-multi-query))
|
||||||
|
|
||||||
|
(defun biome-multi--unique-names-grouped (names-by-group group-names)
|
||||||
|
(let ((name-occurences (make-hash-table :test #'equal))
|
||||||
|
(names-mapping (make-hash-table :test #'equal)))
|
||||||
|
(cl-loop for names in names-by-group
|
||||||
|
do (cl-loop for name in names
|
||||||
|
do (puthash name
|
||||||
|
(1+ (gethash name name-occurences 0))
|
||||||
|
name-occurences)))
|
||||||
|
(cl-loop for names in names-by-group
|
||||||
|
for group-name in group-names
|
||||||
|
do (cl-loop
|
||||||
|
for name in names
|
||||||
|
for occurences = (gethash name name-occurences)
|
||||||
|
do (puthash (format "%s--%s" group-name name)
|
||||||
|
(if (= occurences 1)
|
||||||
|
name
|
||||||
|
(format "%s_%s" name
|
||||||
|
(replace-regexp-in-string
|
||||||
|
(rx space) "_" (downcase group-name))))
|
||||||
|
names-mapping)))
|
||||||
|
names-mapping))
|
||||||
|
|
||||||
|
(defun biome-multi--unique-names (names)
|
||||||
|
(let ((name-occurences (make-hash-table :test #'equal))
|
||||||
|
(added-occurences (make-hash-table :test #'equal)))
|
||||||
|
(cl-loop for name in names
|
||||||
|
do (puthash name
|
||||||
|
(1+ (gethash name name-occurences 0))
|
||||||
|
name-occurences))
|
||||||
|
(cl-loop for name in names
|
||||||
|
for occurences = (gethash name name-occurences)
|
||||||
|
for added = (gethash name added-occurences)
|
||||||
|
collect (if (= occurences 1)
|
||||||
|
name
|
||||||
|
(format "%s_%d" name
|
||||||
|
(puthash
|
||||||
|
name
|
||||||
|
(1+ (or added 0))
|
||||||
|
added-occurences))))))
|
||||||
|
|
||||||
|
(defun biome-multi--join-results (queries query-names vars-mapping results)
|
||||||
|
(let ((times (make-hash-table :test #'equal))
|
||||||
|
(var-values-per-time (make-hash-table :test #'equal)))
|
||||||
|
(cl-loop for result in results
|
||||||
|
for query in queries
|
||||||
|
for query-name in query-names
|
||||||
|
for group-name = (alist-get :group query)
|
||||||
|
for vars-field = (intern group-name)
|
||||||
|
for times-vector = (thread-last
|
||||||
|
result (alist-get vars-field) (alist-get 'time))
|
||||||
|
do (cl-loop for time across times-vector
|
||||||
|
do (puthash time t times))
|
||||||
|
do (cl-loop for (var-name . values) in (seq-filter
|
||||||
|
(lambda (v) (not (eq 'time (car v))))
|
||||||
|
(alist-get vars-field result))
|
||||||
|
for mapped-var-name =
|
||||||
|
(gethash (format "%s--%s" query-name var-name) vars-mapping)
|
||||||
|
for var-values = (make-hash-table :test #'equal)
|
||||||
|
do (cl-loop for time across times-vector
|
||||||
|
for value across values
|
||||||
|
do (puthash time value var-values))
|
||||||
|
do (puthash mapped-var-name var-values var-values-per-time)))
|
||||||
|
(let ((times-sorted (seq-sort #'string-lessp (hash-table-keys times))))
|
||||||
|
`((time . ,(vconcat times-sorted))
|
||||||
|
,@(cl-loop for var-name being the hash-keys of var-values-per-time
|
||||||
|
using (hash-values var-values)
|
||||||
|
collect
|
||||||
|
(cons (intern var-name)
|
||||||
|
(vconcat
|
||||||
|
(cl-loop for time in times-sorted
|
||||||
|
collect (gethash time var-values)))))))))
|
||||||
|
|
||||||
|
(defun biome-multi--merge (queries results)
|
||||||
|
"Merge QUERIES into one query.
|
||||||
|
|
||||||
|
QUERIES is a list of forms as defined by `biome-query-current'."
|
||||||
|
(let* ((vars-by-group
|
||||||
|
(cl-loop for query in queries
|
||||||
|
for group = (alist-get :group query)
|
||||||
|
collect (alist-get group (alist-get :params query)
|
||||||
|
nil nil #'string-equal)))
|
||||||
|
(query-names
|
||||||
|
(biome-multi--unique-names
|
||||||
|
(cl-loop for query in queries
|
||||||
|
collect (alist-get :name query))))
|
||||||
|
(vars-mapping (biome-multi--unique-names-grouped vars-by-group query-names)))
|
||||||
|
`(((:name . "Multi Query")
|
||||||
|
(:group . "multi")
|
||||||
|
(:params . (("multi" .
|
||||||
|
,(cl-loop for var-name being the hash-values of vars-mapping
|
||||||
|
collect var-name)))))
|
||||||
|
((multi_units
|
||||||
|
. ,(cons
|
||||||
|
(cons 'time "iso8601")
|
||||||
|
(cl-loop
|
||||||
|
for result in results
|
||||||
|
for query in queries
|
||||||
|
for query-name in query-names
|
||||||
|
for group-name = (alist-get :group query)
|
||||||
|
for units-field = (intern (format "%s_units" group-name))
|
||||||
|
append (cl-loop
|
||||||
|
for (var-name . unit) in (alist-get units-field result)
|
||||||
|
unless (equal var-name 'time)
|
||||||
|
collect (cons (intern
|
||||||
|
(gethash (format "%s--%s" query-name var-name)
|
||||||
|
vars-mapping))
|
||||||
|
unit)))))
|
||||||
|
(multi . ,(biome-multi--join-results queries query-names vars-mapping results))))))
|
||||||
|
|
||||||
|
(provide 'biome-multi)
|
||||||
|
;;; biome-multi.el ends here
|
||||||
|
|
@ -175,12 +175,14 @@ KEY is the api key of the variable. VAR-NAMES is the output of
|
||||||
(capitalize (replace-regexp-in-string
|
(capitalize (replace-regexp-in-string
|
||||||
(regexp-quote "_") " " key))))
|
(regexp-quote "_") " " key))))
|
||||||
|
|
||||||
(cl-defmethod transient-format ((_obj biome-query--transient-report))
|
(defun biome-query--format (query)
|
||||||
"Format the `biome-query-current'."
|
"Format QUERY for display.
|
||||||
(let ((group (alist-get :group biome-query-current))
|
|
||||||
|
QUERY is a form as defined by `transient-define-prefix'."
|
||||||
|
(let ((group (alist-get :group query))
|
||||||
(var-names (biome-query--get-var-names-cache))
|
(var-names (biome-query--get-var-names-cache))
|
||||||
lat lon group-vars line-vars vars)
|
lat lon group-vars line-vars vars)
|
||||||
(dolist (item (alist-get :params biome-query-current))
|
(dolist (item (alist-get :params query))
|
||||||
(cond
|
(cond
|
||||||
((stringp item)
|
((stringp item)
|
||||||
(push (biome-query--get-header item var-names) vars))
|
(push (biome-query--get-header item var-names) vars))
|
||||||
|
|
@ -251,6 +253,10 @@ KEY is the api key of the variable. VAR-NAMES is the output of
|
||||||
(when line-vars
|
(when line-vars
|
||||||
(concat (mapconcat #'identity line-vars "\n") "\n")))))
|
(concat (mapconcat #'identity line-vars "\n") "\n")))))
|
||||||
|
|
||||||
|
(cl-defmethod transient-format ((_obj biome-query--transient-report))
|
||||||
|
"Format the `biome-query-current'."
|
||||||
|
(biome-query--format biome-query-current))
|
||||||
|
|
||||||
(transient-define-infix biome-query--transient-report-infix ()
|
(transient-define-infix biome-query--transient-report-infix ()
|
||||||
:class 'biome-query--transient-report
|
:class 'biome-query--transient-report
|
||||||
:key "~~1")
|
:key "~~1")
|
||||||
|
|
@ -953,7 +959,7 @@ SUFFIXES is a list of suffix definitions."
|
||||||
"Process the query made by `biome-query'."
|
"Process the query made by `biome-query'."
|
||||||
(interactive)
|
(interactive)
|
||||||
(unless biome-query--callback
|
(unless biome-query--callback
|
||||||
(user-error "Biome-query--callback is not set"))
|
(error "Biome-query--callback is not set"))
|
||||||
(funcall biome-query--callback biome-query-current))
|
(funcall biome-query--callback biome-query-current))
|
||||||
|
|
||||||
(defun biome-query--generate-preset ()
|
(defun biome-query--generate-preset ()
|
||||||
|
|
|
||||||
12
biome.el
12
biome.el
|
|
@ -40,6 +40,7 @@
|
||||||
|
|
||||||
;;; Code:
|
;;; Code:
|
||||||
(require 'biome-api)
|
(require 'biome-api)
|
||||||
|
(require 'biome-multi)
|
||||||
(require 'biome-query)
|
(require 'biome-query)
|
||||||
(require 'biome-grid)
|
(require 'biome-grid)
|
||||||
|
|
||||||
|
|
@ -73,6 +74,17 @@ API."
|
||||||
;; previous invocation of `biome'
|
;; previous invocation of `biome'
|
||||||
(biome-query--section-open (alist-get :name biome-query-current)))
|
(biome-query--section-open (alist-get :name biome-query-current)))
|
||||||
|
|
||||||
|
(defun biome-multi ()
|
||||||
|
"Run multiple queries to Open Meteo and join results."
|
||||||
|
(interactive)
|
||||||
|
(biome-multi-query
|
||||||
|
(lambda (query)
|
||||||
|
(biome-api-get-multiple
|
||||||
|
query
|
||||||
|
(lambda (queries results)
|
||||||
|
(let ((merged (biome-multi--merge queries results)))
|
||||||
|
(funcall biome-frontend (nth 0 merged) (nth 1 merged))))))))
|
||||||
|
|
||||||
(defmacro biome-def-preset (name params)
|
(defmacro biome-def-preset (name params)
|
||||||
"Declare a query preset.
|
"Declare a query preset.
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue