v0.1.0-prealpha1
Binaries: - juniority, an ncurses-based TUI application for viewing your idol database. - juniority-importer, an stdio data importer for modifying your idol database. - idoldb-transform, a data converter script to bootstrap off of ikki123-aidoru/idol-database. Data: - idol-database.sqlite3, an idol database bootstrapped with data from ikki123-aidoru/idol-database. - idol-database.jun, the juniority import syntax data used to produce the above database. An example of juniority import syntax for the curious. Features: - TUI application provides many views of idol database: - Dedicated views for idols, volumes, and studios. - Search views for searching over tags for volumes and idols. - Data importer is complete. It imports data, and can be used to deassociate tags and idols from volumes. All database modification is done through the data importer. - idoldb-transform script converts idol-database data into juniority import syntax that can be piped into the data importer.
このコミットが含まれているのは:
コミット
12b9f9cfe7
|
@ -0,0 +1,65 @@
|
|||
# Juniority
|
||||
|
||||
Juniority is an idol/movie organizer application. Juniority uses ncurses for a
|
||||
terminal user interface (TUI) and sqlite3 for data storage. Juniority is written
|
||||
in GNU Guile.
|
||||
|
||||
## Install
|
||||
|
||||
### Dependencies
|
||||
|
||||
* GNU Guile >=3.0.9
|
||||
* guile-ncurses >= 3.0
|
||||
* guile-sqlite3 >= 0.1.3
|
||||
|
||||
### GUILE_LOAD_PATH
|
||||
|
||||
If you already have a private Guile directory set up, use that. If not, set one up:
|
||||
|
||||
``` sh
|
||||
export PRIVATE_GUILE_LOAD_PATH="$HOME/.local/share/guile/site/3.0"
|
||||
mkdir -p $PRIVATE_GUILE_LOAD_PATH
|
||||
ln -sr $JUNIORITY_DIR $PRIVATE_GUILE_LOAD_PATH/juniority
|
||||
```
|
||||
|
||||
Edit your `.bashrc` to include this directory in your `GUILE_LOAD_PATH`:
|
||||
|
||||
``` sh
|
||||
PRIVATE_GUILE_LOAD_PATH="$HOME/.local/share/guile:$HOME/.local/share/guile/site/3.0"
|
||||
GUILE_LOAD_PATH="$PRIVATE_GUILE_LOAD_PATH:$GUILE_LOAD_PATH"
|
||||
```
|
||||
|
||||
Confirm that Guile is recognizing this change:
|
||||
|
||||
``` scheme
|
||||
scheme@(guile-user)> %load-path
|
||||
$1 = ( ... )
|
||||
```
|
||||
|
||||
### Binaries
|
||||
|
||||
You can symlink the binaries from your personal binary directory:
|
||||
|
||||
``` sh
|
||||
BINDIR="$HOME/.local/bin"
|
||||
pushd $BINDIR
|
||||
ln -sr $JUNIORITY_DIR/bin/juniority.scm ./juniority
|
||||
ln -sr $JUNIORITY_DIR/bin/juniority-import.scm ./juniority-import
|
||||
```
|
||||
|
||||
### Database
|
||||
|
||||
Juniority ships with a pre-loaded database thanks to [ikki123-aidoru/idol_database](https://github.com/ikki123-aidoru/idol_database). It is located at `data/idol-database.sqlite3`.
|
||||
|
||||
``` sh
|
||||
PRIVATE_DIR="$HOME/Private" # ecryptfs etc.
|
||||
mv $JUNIORITY_DIR/data/idol-database.sqlite3 $PRIVATE_DIR/juniority.sqlite3
|
||||
juniority !!:2
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
The binaries display help when invoked without arguments:
|
||||
|
||||
- `juniority`
|
||||
- `juniority-import`
|
|
@ -0,0 +1,127 @@
|
|||
#!/usr/bin/env -S guile -e main -s
|
||||
coding: utf-8
|
||||
!#
|
||||
|
||||
(use-modules
|
||||
(ice-9 match)
|
||||
(ice-9 peg)
|
||||
(ice-9 rdelim)
|
||||
(juniority grammar)
|
||||
)
|
||||
|
||||
;; This program transforms an idol_database
|
||||
;; idol/movie line into a statement in Juniority
|
||||
;; format.
|
||||
;; Consequently, it can be piped into
|
||||
;; `juniority-import'.
|
||||
;;
|
||||
;; https://github.com/ikki123-aidoru/idol_database
|
||||
|
||||
(define-peg-pattern DASH none "-")
|
||||
(define-peg-pattern SLASH none "/")
|
||||
(define-peg-pattern digit body (range #\0 #\9))
|
||||
(define-peg-pattern year all (+ digit))
|
||||
(define-peg-pattern month all (+ digit))
|
||||
(define-peg-pattern day all (+ digit))
|
||||
(define-peg-pattern dash-date all
|
||||
(and year DASH month DASH day))
|
||||
(define-peg-pattern slash-date all
|
||||
(and day SLASH month SLASH year))
|
||||
(define-peg-pattern date all
|
||||
(or dash-date slash-date "N/A"))
|
||||
|
||||
|
||||
(define (main args)
|
||||
(match args
|
||||
((bin idols-file movies-file)
|
||||
(let ((idols-hash (main-idols idols-file)))
|
||||
(main-movies idols-hash movies-file)))
|
||||
((bin . other)
|
||||
(format #t "Usage: ~a path/to/idols.tsv path/to/movies.tsv
|
||||
|
||||
This utility is meant to be used in concert with `juniority-import`. E.g.:
|
||||
~a path/to/idols.tsv path/to/movies.tsv | juniority-import path/to/juniority.db
|
||||
" bin bin))))
|
||||
|
||||
(define (main-idols idols-file)
|
||||
(let ((idols-hash (make-hash-table))
|
||||
(idols-port (open-file idols-file "r"))
|
||||
)
|
||||
(do ((line (read-line idols-port) (read-line idols-port)))
|
||||
((eof-object? line))
|
||||
|
||||
(match (string-split line #\tab)
|
||||
;; Ignore header
|
||||
(("Name" "Japanese Name" "Alias" "DOB" "Doubtful DOB") (noop))
|
||||
|
||||
;; Parse data
|
||||
((en-name "" alias dob _doubtful-dob) (noop)) ;; no
|
||||
((en-name jp-name alias dob _doubtful-dob)
|
||||
(let ((date-of-birth
|
||||
(if (or (string-null? dob)
|
||||
(string-any
|
||||
(lambda (ch) (not (or (char-numeric? ch) (eqv? #\- ch))))
|
||||
dob))
|
||||
""
|
||||
(normalize-date dob))))
|
||||
(format #t "@'~a' '~a'~a\n"
|
||||
jp-name
|
||||
en-name
|
||||
date-of-birth)
|
||||
(when (not (string-null? alias))
|
||||
(format #t "@'~a' '~a'~a\n"
|
||||
alias
|
||||
en-name
|
||||
date-of-birth))
|
||||
(hash-set! idols-hash en-name jp-name)
|
||||
))
|
||||
|
||||
;; Ignore empty lines
|
||||
(() (noop))
|
||||
))
|
||||
idols-hash))
|
||||
|
||||
(define (main-movies idols-hash movies-file)
|
||||
(let ((movies-port (open-file movies-file "r")))
|
||||
(do ((line (read-line movies-port) (read-line movies-port)))
|
||||
((eof-object? line))
|
||||
|
||||
(match (string-split line #\tab)
|
||||
;; Ignore headers
|
||||
(("Title" "Series" "Maker" "Release Date" "Actress") (noop))
|
||||
|
||||
;; Parse data
|
||||
((ident series _maker published idol-en-name)
|
||||
(let ((idol-jp-name (hash-ref idols-hash idol-en-name))
|
||||
(date-of-publication
|
||||
(if (or (string-null? published)
|
||||
(string-any
|
||||
(lambda (ch) (not (or (char-numeric? ch) (eqv? #\- ch))))
|
||||
published))
|
||||
""
|
||||
(normalize-date published))))
|
||||
(if idol-jp-name
|
||||
(format #t "~a @'~a' ~a\n"
|
||||
ident
|
||||
idol-jp-name
|
||||
date-of-publication)
|
||||
(noop))))
|
||||
|
||||
;; Ignore empty lines
|
||||
(() (noop))
|
||||
))))
|
||||
|
||||
(define (normalize-date the-date)
|
||||
(match (peg:tree (match-pattern date the-date))
|
||||
(('date "N/A") "")
|
||||
(('date ('dash-date _ _ _))
|
||||
(format #f " ~a" the-date))
|
||||
(('date ('slash-date ('day day) ('month month) ('year year)))
|
||||
(format #f " ~a-~a-~a"
|
||||
(if (eqv? 2 (string-length year))
|
||||
(if (< (string->number year) 35)
|
||||
(string-append "20" year)
|
||||
(string-append "19" year))
|
||||
year)
|
||||
month
|
||||
day))))
|
|
@ -0,0 +1,280 @@
|
|||
#!/usr/bin/env -S guile -e main -s
|
||||
coding: utf-8
|
||||
!#
|
||||
|
||||
(use-modules
|
||||
(ice-9 exceptions)
|
||||
(ice-9 match)
|
||||
(ice-9 format)
|
||||
(ice-9 peg)
|
||||
(ice-9 rdelim)
|
||||
(ice-9 regex)
|
||||
(juniority database)
|
||||
(juniority grammar)
|
||||
(juniority util)
|
||||
(srfi srfi-1)
|
||||
(srfi srfi-43)
|
||||
)
|
||||
|
||||
(define (main args)
|
||||
(match args
|
||||
((bin db-path) (main-import db-path))
|
||||
((bin . _)
|
||||
(format #t "Import:
|
||||
In order to import your existing records, it must be in the following format:
|
||||
stmt = volume-stmt | idol-stmt | tag-stmt
|
||||
volume-stmt = studio-[0-9]+ idol-assoc* tag-assoc* pub?
|
||||
idol-stmt = idol-tag tag-assoc* dob?
|
||||
|
||||
idol-assoc = idol-tag | idol-untag
|
||||
idol-tag = @'name'
|
||||
idol-untag = !@'name'
|
||||
|
||||
tag-assoc = tag | untag
|
||||
tag = 'name'
|
||||
untag = !'name'
|
||||
|
||||
dob = date
|
||||
pub = date
|
||||
date = nnnn-nn-nn
|
||||
n = [0-9]
|
||||
studio = [A-Z]+
|
||||
|
||||
Example:
|
||||
@'高橋ひかる' 'Takahashi' 'Hikaru' 2001-09-22
|
||||
BSTAR-001 @'高橋ひかる'
|
||||
BSTAR-019 @'高橋ひかる'
|
||||
@'蒼井玲奈' 'Aoi' 'Reina' 1996-03-19
|
||||
ICDV-30223 @'蒼井玲奈' 'thong' 'butt' 'catgirl' 'lollipop' 'popsicle'
|
||||
...
|
||||
|
||||
Notes:
|
||||
- Idol statements must precede volume statements that reference them.
|
||||
- Duplicate idol and volume statements are treated as updates.
|
||||
- (Idol-)Untag expressions remove a previously-associated tag (or idol).
|
||||
|
||||
Feed your records to juniority to bootstrap your database:
|
||||
|
||||
~a import path/to/db.sqlite3 < path/to/import.jun
|
||||
|
||||
Use import mode in the future to add records to your database. For manual entry, combine it with a heredoc for convenience:
|
||||
|
||||
~a import path/to/db.sqlite3 << EOF
|
||||
...
|
||||
EOF
|
||||
" bin bin))
|
||||
))
|
||||
|
||||
(define (main-import db-path)
|
||||
(let ((db (open-db db-path))
|
||||
)
|
||||
(migrate-db db)
|
||||
(do ((line (read-line) (read-line)))
|
||||
((eof-object? line))
|
||||
(process-import-line db (peg:tree (match-pattern import-line line)))
|
||||
)))
|
||||
|
||||
(define (process-import-line db line)
|
||||
(let ()
|
||||
(match line
|
||||
(('volume-entry ('volume identifier) 'idols 'tags ('date pub-date))
|
||||
(import-volume db identifier pub-date (list) (list)))
|
||||
(('volume-entry ('volume identifier) idols 'tags ('date pub-date))
|
||||
(import-volume db identifier pub-date (cdr idols) (list)))
|
||||
(('volume-entry ('volume identifier) 'idols tags ('date pub-date))
|
||||
(import-volume db identifier pub-date (list) (cdr tags)))
|
||||
(('volume-entry ('volume identifier) idols tags ('date pub-date))
|
||||
(import-volume db identifier pub-date (cdr idols) (cdr tags)))
|
||||
(('volume-entry ('volume identifier) 'idols 'tags)
|
||||
(import-volume db identifier #nil (list) (list)))
|
||||
(('volume-entry ('volume identifier) idols 'tags)
|
||||
(import-volume db identifier #nil (cdr idols) (list)))
|
||||
(('volume-entry ('volume identifier) 'idols tags)
|
||||
(import-volume db identifier #nil (list) (cdr tags)))
|
||||
(('volume-entry ('volume identifier) idols tags)
|
||||
(import-volume db identifier #nil (cdr idols) (cdr tags)))
|
||||
|
||||
(('idol-entry ('idol-tag jp-name) 'tags ('date birth-date))
|
||||
(import-idol db jp-name birth-date (list)))
|
||||
(('idol-entry ('idol-tag jp-name) tags ('date birth-date))
|
||||
(import-idol db jp-name birth-date (cdr tags)))
|
||||
(('idol-entry ('idol-tag jp-name) 'tags)
|
||||
(import-idol db jp-name #nil (list)))
|
||||
(('idol-entry ('idol-tag jp-name) tags)
|
||||
(import-idol db jp-name #nil (cdr tags)))
|
||||
(#f (noop))
|
||||
)))
|
||||
|
||||
(define (import-volume db identifier pub-date idols tags)
|
||||
(let* ((studio-callsign
|
||||
(first (string-split identifier (lambda (ch) (eq? #\- ch)))))
|
||||
|
||||
(volume-id (param-query-one db select-volume-stmt identifier))
|
||||
(studio-id (param-query-one db select-studio-stmt studio-callsign))
|
||||
(idol-ids #nil)
|
||||
(tag-ids #nil)
|
||||
|
||||
(existing-volume? #f)
|
||||
)
|
||||
(if (vector? volume-id)
|
||||
(begin
|
||||
(set! volume-id (vector-ref volume-id 0))
|
||||
(set! existing-volume? #t)
|
||||
(log "import-volume: found volume ~a" volume-id))
|
||||
(begin
|
||||
(set!
|
||||
volume-id
|
||||
(vector-ref
|
||||
(param-query-one db insert-volume-stmt identifier pub-date)
|
||||
0))
|
||||
(log "import-volume: inserted volume ~a" volume-id)))
|
||||
(if (vector? studio-id)
|
||||
(begin
|
||||
(set! studio-id (vector-ref studio-id 0))
|
||||
(log "import-volume: found studio ~a" studio-id))
|
||||
(begin
|
||||
(set!
|
||||
studio-id
|
||||
(vector-ref
|
||||
(param-query-one db insert-studio-stmt studio-callsign)
|
||||
0))
|
||||
(log "import-volume: inserted studio ~a" studio-id)))
|
||||
(set!
|
||||
idol-ids
|
||||
(map
|
||||
(lambda (idol)
|
||||
(let* ((idol-name (second idol))
|
||||
(idol-result (param-query-one db select-idol-stmt idol-name))
|
||||
(idol-id
|
||||
(if (vector? idol-result)
|
||||
(begin
|
||||
(log "import-volume: found idol ~a" idol-name)
|
||||
(vector-ref idol-result 0))
|
||||
(raise-exception
|
||||
(make-exception-with-message
|
||||
(format #f "import-volume: idol not found ~a" idol-name))))))
|
||||
(list (first idol) idol-id)))
|
||||
idols))
|
||||
(set!
|
||||
tag-ids
|
||||
(map
|
||||
(lambda (tag)
|
||||
(let* ((tag-name (second tag))
|
||||
(tag-result (param-query-one db select-tag-stmt tag-name))
|
||||
(tag-id (if (vector? tag-result)
|
||||
(begin
|
||||
(log "import-volume: found tag ~a" tag-name)
|
||||
(vector-ref tag-result 0))
|
||||
(begin
|
||||
(log "import-volume: inserted tag ~a" tag-name)
|
||||
(vector-ref
|
||||
(param-query-one db insert-tag-stmt tag-name)
|
||||
0)))))
|
||||
(list (first tag) tag-id)))
|
||||
tags))
|
||||
|
||||
(if existing-volume?
|
||||
(when pub-date
|
||||
(param-query-one db update-volume-pub-stmt pub-date volume-id)
|
||||
(log "import-volume: updated volume published date"))
|
||||
(begin
|
||||
(param-query-one db assoc-volume-studio-stmt volume-id studio-id)
|
||||
(log "import-volume: associated studio ~a" studio-id)))
|
||||
|
||||
(let ((existing-tags
|
||||
(param-query-map db
|
||||
(lambda (ct row) (vector-ref row 0))
|
||||
select-volume-tag-ids-stmt volume-id))
|
||||
(existing-idols
|
||||
(param-query-map db
|
||||
(lambda (ct row) (vector-ref row 0))
|
||||
select-volume-idol-ids-stmt volume-id)))
|
||||
|
||||
(for-each
|
||||
(lambda (idol)
|
||||
(match idol
|
||||
(('idol-tag idol-id)
|
||||
(unless (find (lambda (id) (eqv? idol-id id)) existing-idols)
|
||||
(param-query-one db assoc-volume-idol-stmt volume-id idol-id)
|
||||
(log "import-volume: associated idol ~a" idol-id)))
|
||||
(('idol-untag idol-id)
|
||||
(when (find (lambda (id) (eqv? idol-id id)) existing-idols)
|
||||
(param-query-one db deassoc-volume-idol-stmt volume-id idol-id)
|
||||
(log "import-volume: deassociated idol ~a" idol-id)))
|
||||
(other (log "import-volume: unmatched idol pattern: ~a" other))
|
||||
)
|
||||
)
|
||||
idol-ids)
|
||||
(for-each
|
||||
(lambda (tag)
|
||||
(match tag
|
||||
(('tag tag-id)
|
||||
(unless (find (lambda (id) (eqv? tag-id id)) existing-tags)
|
||||
(param-query-one db assoc-volume-tag-stmt volume-id tag-id)
|
||||
(log "import-volume: associated tag ~a" tag-id)))
|
||||
(('untag tag-id)
|
||||
(when (find (lambda (id) (eqv? tag-id id)) existing-tags)
|
||||
(param-query-one db deassoc-volume-tag-stmt volume-id tag-id)
|
||||
(log "import-volume: deassociated tag ~a" tag-id)))
|
||||
(other (log "import-volume: unmatched tag pattern: ~a" other))))
|
||||
tag-ids)
|
||||
)))
|
||||
|
||||
(define (import-idol db jp-name birth-date tags)
|
||||
(let* ((idol-id (param-query-one db select-idol-stmt jp-name))
|
||||
(tag-ids (list))
|
||||
|
||||
(existing-idol? #f)
|
||||
)
|
||||
(if (vector? idol-id)
|
||||
(begin
|
||||
(set! idol-id (vector-ref idol-id 0))
|
||||
(set! existing-idol? #t)
|
||||
(log "import-idol: found idol ~a" idol-id))
|
||||
(begin
|
||||
(set!
|
||||
idol-id
|
||||
(vector-ref (param-query-one db insert-idol-stmt jp-name birth-date) 0))
|
||||
(log "import-idol: inserted idol ~a" idol-id)))
|
||||
(set!
|
||||
tag-ids
|
||||
(map
|
||||
(lambda (tag)
|
||||
(let* ((tag-name (second tag))
|
||||
(tag-result (param-query-one db select-tag-stmt tag-name))
|
||||
(tag-id
|
||||
(if (vector? tag-result)
|
||||
(begin
|
||||
(log "import-idol: found tag ~a" tag-name)
|
||||
(vector-ref tag-result 0))
|
||||
(begin
|
||||
(log "import-idol: inserted tag ~a" tag-name)
|
||||
(vector-ref
|
||||
(param-query-one db insert-tag-stmt tag-name)
|
||||
0)))))
|
||||
(list (first tag) tag-id)))
|
||||
tags))
|
||||
|
||||
(when (and existing-idol? birth-date)
|
||||
(param-query-one db update-idol-dob-stmt birth-date idol-id)
|
||||
(log "import-idol: updated idol birth date"))
|
||||
|
||||
(let ((existing-tags
|
||||
(param-query-map db
|
||||
(lambda (ct row) (vector-ref row 0))
|
||||
select-idol-tag-ids-stmt idol-id)))
|
||||
(for-each
|
||||
(lambda (tag)
|
||||
(match tag
|
||||
(('tag tag-id)
|
||||
(unless (find (lambda (id) (eqv? tag-id id)) existing-tags)
|
||||
(param-query-one db assoc-idol-tag-stmt idol-id tag-id)
|
||||
(log "import-idol: associated tag ~a" tag-id)))
|
||||
(('untag tag-id)
|
||||
(when (find (lambda (id) (eqv? tag-id id)) existing-tags)
|
||||
(param-query-one db deassoc-idol-tag-stmt idol-id tag-id)
|
||||
(log "import-idol: deassociated tag ~a" tag-id)))
|
||||
(other (log "import-idol: unmatched tag pattern: ~a" other))))
|
||||
tag-ids)
|
||||
)
|
||||
))
|
1505
bin/juniority.scm
1505
bin/juniority.scm
ファイル差分が大きすぎるため省略します
差分を読み込み
ファイル差分が大きすぎるため省略します
差分を読み込み
バイナリファイルは表示されません。
|
@ -0,0 +1,475 @@
|
|||
(define-module (juniority database)
|
||||
#:use-module (ice-9 exceptions)
|
||||
#:use-module (ice-9 format)
|
||||
#:use-module (sqlite3)
|
||||
#:export (
|
||||
assoc-idol-tag-stmt
|
||||
assoc-volume-idol-stmt
|
||||
assoc-volume-studio-stmt
|
||||
assoc-volume-tag-stmt
|
||||
count-idols-stmt
|
||||
count-notes-stmt
|
||||
count-studios-stmt
|
||||
count-tags-stmt
|
||||
count-volumes-stmt
|
||||
create-tables-sql
|
||||
deassoc-idol-tag-stmt
|
||||
deassoc-volume-idol-stmt
|
||||
deassoc-volume-tag-stmt
|
||||
insert-idol-stmt
|
||||
insert-studio-stmt
|
||||
insert-tag-stmt
|
||||
insert-volume-stmt
|
||||
migrate-db
|
||||
open-db
|
||||
param-query
|
||||
param-query-all
|
||||
param-query-map
|
||||
param-query-one
|
||||
search-idols-stmt
|
||||
search-volumes-stmt
|
||||
select-all-idols-stmt
|
||||
select-all-studios-stmt
|
||||
select-all-tags-stmt
|
||||
select-all-volumes-stmt
|
||||
select-idol-notes-stmt
|
||||
select-idol-stmt
|
||||
select-idol-tag-ids-stmt
|
||||
select-idol-tags-stmt
|
||||
select-idol-volumes-stmt
|
||||
select-studio-notes-stmt
|
||||
select-studio-stmt
|
||||
select-studio-tags-stmt
|
||||
select-studio-volumes-stmt
|
||||
select-tag-stmt
|
||||
select-volume-idol-ids-stmt
|
||||
select-volume-idols-stmt
|
||||
select-volume-notes-stmt
|
||||
select-volume-stmt
|
||||
select-volume-tag-ids-stmt
|
||||
select-volume-tags-stmt
|
||||
update-idol-dob-stmt
|
||||
update-volume-pub-stmt
|
||||
))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; DATABAS ABASEDA
|
||||
;; ATABASEDA BASEDATAB
|
||||
;; TAB ATA ASE ABA
|
||||
;; ABA AB SED AS
|
||||
;; BAS BA EDA ASE
|
||||
;; ASE AS DATABASE
|
||||
;; SED SE ATABASEDA
|
||||
;; EDA ED TAB ATA
|
||||
;; DAT EDA ABA TAB
|
||||
;; ATABASEDA BASEDATAB
|
||||
;; TABASED ASEDATAB
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
(define (param-query db stmt . args)
|
||||
;; https://www.sqlite.org/c3ref/stmt.html
|
||||
;; use `sqlite-prepare' to prepare a statement
|
||||
(let ((p-stmt (sqlite-prepare db stmt)))
|
||||
;; use `sqlite-bind-arguments' to bind args to a param query
|
||||
(apply sqlite-bind-arguments (cons p-stmt args))
|
||||
;; use `sqlite-step' to run a prepared statement
|
||||
;; use `sqlite-reset' to commit the implicit transaction
|
||||
p-stmt))
|
||||
|
||||
(define (param-query-all db stmt . rest)
|
||||
(let ((results (list))
|
||||
(query (apply param-query (cons* db stmt rest)))
|
||||
)
|
||||
|
||||
(do ((result (sqlite-step query) (sqlite-step query)))
|
||||
((eq? result #f))
|
||||
(set! results (cons result results)))
|
||||
(sqlite-reset query)
|
||||
(reverse results)))
|
||||
|
||||
(define (param-query-map db fn stmt . rest)
|
||||
(let ((results (list))
|
||||
(query (apply param-query (cons* db stmt rest)))
|
||||
)
|
||||
|
||||
(do ((result (sqlite-step query) (sqlite-step query))
|
||||
(count 0 (+ 1 count)))
|
||||
((eq? result #f))
|
||||
(set! results (cons (fn count result) results)))
|
||||
(sqlite-reset query)
|
||||
(reverse results)))
|
||||
|
||||
(define (param-query-one db stmt . args)
|
||||
;; https://www.sqlite.org/c3ref/stmt.html
|
||||
;; use `sqlite-prepare' to prepare a statement
|
||||
(let ((p-stmt (sqlite-prepare db stmt))
|
||||
(result #nil))
|
||||
;; use `sqlite-bind-arguments' to bind args to a param query
|
||||
(apply sqlite-bind-arguments (cons p-stmt args))
|
||||
;; use `sqlite-step' to run a prepared statement
|
||||
(set! result (sqlite-step p-stmt))
|
||||
;; use `sqlite-reset' to commit the implicit transaction
|
||||
(sqlite-reset p-stmt)
|
||||
result))
|
||||
|
||||
(define (open-db db-path)
|
||||
(let ((db (sqlite-open db-path
|
||||
(logior
|
||||
SQLITE_OPEN_CREATE
|
||||
SQLITE_OPEN_READWRITE
|
||||
))))
|
||||
(unless (sqlite-db? db)
|
||||
(raise-exception
|
||||
(make-exception-with-message
|
||||
(format #t "sqlite: not a database: ~a"
|
||||
db-path))))
|
||||
db))
|
||||
(define (migrate-db db)
|
||||
(sqlite-exec db create-tables-sql))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; RIES QUER UER
|
||||
;; ERIESQUE SQUERIES ERI
|
||||
;; ERI RI SQU SQU RIE
|
||||
;; RI QU UE IES
|
||||
;; IESQ UE ER ESQ
|
||||
;; SQUERIES ER RI SQU
|
||||
;; RIESQU RI IE QUE
|
||||
;; UE IE ER ES UER
|
||||
;; UE UER ESQ IESQ ERI
|
||||
;; RIESQUER QUERIESQ RIESQUERIE
|
||||
;; SQUE RIES UE IESQUERIES
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
(define assoc-idol-tag-stmt
|
||||
"INSERT INTO idol_tag (idol_id, tag_id) VALUES (?, ?)")
|
||||
(define assoc-volume-idol-stmt
|
||||
"INSERT INTO volume_idol (volume_id, idol_id) VALUES (?, ?)")
|
||||
(define assoc-volume-studio-stmt
|
||||
"INSERT INTO volume_studio (volume_id, studio_id) VALUES (?, ?)")
|
||||
(define assoc-volume-tag-stmt
|
||||
"INSERT INTO volume_tag (volume_id, tag_id) VALUES (?, ?)")
|
||||
|
||||
(define count-idols-stmt "SELECT COUNT(*) FROM idol")
|
||||
(define count-notes-stmt "SELECT COUNT(*) FROM note")
|
||||
(define count-studios-stmt "SELECT COUNT(*) FROM studio")
|
||||
(define count-tags-stmt "SELECT COUNT(*) FROM tag")
|
||||
(define count-volumes-stmt "SELECT COUNT(*) FROM volume")
|
||||
|
||||
(define deassoc-idol-tag-stmt
|
||||
"DELETE FROM idol_tag WHERE idol_id = ? AND tag_id = ?")
|
||||
(define deassoc-volume-idol-stmt
|
||||
"DELETE FROM volume_idol WHERE volume_id = ? AND idol_id = ?")
|
||||
(define deassoc-volume-tag-stmt
|
||||
"DELETE FROM volume_tag WHERE volume_id = ? AND tag_id = ?")
|
||||
|
||||
(define insert-idol-stmt
|
||||
"INSERT INTO idol (jp_name, birth_date) VALUES (?, ?) RETURNING id")
|
||||
(define insert-studio-stmt
|
||||
"INSERT INTO studio (call_sign) VALUES (?) RETURNING id")
|
||||
(define insert-tag-stmt
|
||||
"INSERT INTO tag (name) VALUES (?) RETURNING id")
|
||||
(define insert-volume-stmt
|
||||
"INSERT INTO volume (identifier, published) VALUES (?, ?) RETURNING id")
|
||||
|
||||
(define search-idols-stmt
|
||||
"SELECT idol.jp_name, idol.birth_date, idol.rating
|
||||
FROM idol_tag
|
||||
JOIN idol ON idol_tag.idol_id = idol.id
|
||||
JOIN tag ON idol_tag.tag_id = tag.id
|
||||
WHERE tag.name LIKE '%' || ? || '%'
|
||||
ORDER BY idol.rating DESC NULLS LAST, idol_tag.id DESC")
|
||||
|
||||
(define search-volumes-stmt
|
||||
"SELECT volume.identifier, volume.published, volume.rating
|
||||
FROM volume_tag
|
||||
JOIN volume ON volume_tag.volume_id = volume.id
|
||||
JOIN tag ON volume_tag.tag_id = tag.id
|
||||
WHERE tag.name LIKE '%' || ? || '%'
|
||||
ORDER BY volume.rating DESC NULLS LAST, volume_tag.id DESC")
|
||||
|
||||
(define select-all-idols-stmt
|
||||
"SELECT jp_name, birth_date, rating FROM idol
|
||||
ORDER BY rating DESC NULLS LAST, birth_date DESC NULLS LAST")
|
||||
(define select-all-studios-stmt
|
||||
"SELECT call_sign, full_name, rating FROM studio
|
||||
ORDER BY call_sign ASC")
|
||||
(define select-all-tags-stmt
|
||||
"SELECT name FROM tag
|
||||
ORDER BY name ASC")
|
||||
(define select-all-volumes-stmt
|
||||
"SELECT identifier, published, rating FROM volume
|
||||
ORDER BY rating DESC NULLS LAST, published DESC NULLS LAST, identifier ASC")
|
||||
|
||||
(define select-idol-stmt
|
||||
"SELECT id, birth_date, rating, jp_name FROM idol WHERE jp_name = ?")
|
||||
(define select-idol-notes-stmt
|
||||
"SELECT note.text, note.updated
|
||||
FROM idol_note JOIN note ON idol_note.note_id = note.id
|
||||
WHERE idol_note.idol_id = ?
|
||||
ORDER BY note.created ASC")
|
||||
(define select-idol-tags-stmt
|
||||
"SELECT tag.name
|
||||
FROM idol_tag INNER JOIN tag ON idol_tag.tag_id = tag.id
|
||||
WHERE idol_tag.idol_id = ?
|
||||
ORDER BY tag.name ASC")
|
||||
(define select-idol-tag-ids-stmt
|
||||
"SELECT tag.id
|
||||
FROM idol_tag INNER JOIN tag ON idol_tag.tag_id = tag.id
|
||||
WHERE idol_tag.idol_id = ?
|
||||
ORDER BY tag.id ASC")
|
||||
(define select-idol-volumes-stmt
|
||||
"SELECT volume.identifier, volume.published, volume.rating, volume.title
|
||||
FROM volume_idol JOIN volume ON volume_idol.volume_id = volume.id
|
||||
WHERE volume_idol.idol_id = ?
|
||||
ORDER BY volume.identifier ASC")
|
||||
|
||||
(define select-studio-stmt
|
||||
"SELECT id, call_sign, full_name, rating FROM studio WHERE call_sign = ?")
|
||||
(define select-studio-notes-stmt
|
||||
"SELECT note.text, note.updated
|
||||
FROM studio_note JOIN note ON studio_note.note_id = note.id
|
||||
WHERE studio_note.studio_id = ?
|
||||
ORDER BY note.created ASC")
|
||||
(define select-studio-tags-stmt
|
||||
"SELECT tag.name
|
||||
FROM studio_tag JOIN tag ON studio_tag.tag_id = tag.id
|
||||
WHERE studio_tag.studio_id = ?
|
||||
ORDER BY tag.name ASC")
|
||||
(define select-studio-volumes-stmt
|
||||
"SELECT volume.identifier, volume.published, volume.rating, volume.title
|
||||
FROM volume_studio JOIN volume ON volume_studio.volume_id = volume.id
|
||||
WHERE volume_studio.studio_id = ?
|
||||
ORDER BY volume.identifier ASC")
|
||||
|
||||
(define select-tag-stmt
|
||||
"SELECT id FROM tag WHERE name = ?")
|
||||
|
||||
(define select-volume-stmt
|
||||
"SELECT id, identifier, title, rating, published FROM volume
|
||||
WHERE identifier = ?")
|
||||
(define select-volume-notes-stmt
|
||||
"SELECT note.text, note.updated
|
||||
FROM volume_note JOIN note ON volume_note.note_id = note.id
|
||||
WHERE volume_note.volume_id = ?
|
||||
ORDER BY note.created ASC")
|
||||
(define select-volume-tags-stmt
|
||||
"SELECT tag.name
|
||||
FROM volume_tag JOIN tag ON volume_tag.tag_id = tag.id
|
||||
WHERE volume_tag.volume_id = ?
|
||||
ORDER BY tag.name ASC")
|
||||
(define select-volume-tag-ids-stmt
|
||||
"SELECT tag.id
|
||||
FROM volume_tag JOIN tag ON volume_tag.tag_id = tag.id
|
||||
WHERE volume_tag.volume_id = ?
|
||||
ORDER BY tag.id ASC")
|
||||
(define select-volume-idols-stmt
|
||||
"SELECT idol.jp_name, idol.birth_date, idol.rating
|
||||
FROM volume_idol JOIN idol ON volume_idol.idol_id = idol.id
|
||||
WHERE volume_idol.volume_id = ?
|
||||
ORDER BY idol.birth_date ASC NULLS LAST, rating DESC NULLS LAST")
|
||||
(define select-volume-idol-ids-stmt
|
||||
"SELECT idol.id
|
||||
FROM volume_idol JOIN idol ON volume_idol.idol_id = idol.id
|
||||
WHERE volume_idol.volume_id = ?
|
||||
ORDER BY idol.id ASC")
|
||||
|
||||
(define update-idol-dob-stmt
|
||||
"UPDATE idol SET birth_date = ? WHERE id = ?")
|
||||
(define update-volume-pub-stmt
|
||||
"UPDATE volume SET published = ? WHERE id = ?")
|
||||
|
||||
(define create-tables-sql
|
||||
"
|
||||
CREATE TABLE IF NOT EXISTS tag (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name VARCHAR(255) NOT NULL
|
||||
);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS tag_name_unique ON tag (name);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS note (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
text TEXT NOT NULL,
|
||||
created INTEGER NOT NULL, -- epoch
|
||||
updated INTEGER NOT NULL -- epoch
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS studio (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
call_sign VARCHAR(255) NOT NULL,
|
||||
full_name VARCHAR(255),
|
||||
rating SMALLINT
|
||||
);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS studio_call_sign_unique ON studio (call_sign);
|
||||
CREATE INDEX IF NOT EXISTS studio_full_name ON studio (full_name);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS idol (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
jp_name VARCHAR(255),
|
||||
birth_date VARCHAR(10),
|
||||
rating SMALLINT
|
||||
);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idol_jp_name_unique ON idol (jp_name);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS volume (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
identifier VARCHAR(255) NOT NULL,
|
||||
title VARCHAR(255),
|
||||
rating SMALLINT,
|
||||
published VARCHAR(10) -- YYYY-MM-DD
|
||||
);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS volume_identifier_unique ON volume (identifier);
|
||||
CREATE INDEX IF NOT EXISTS volume_title ON volume (title);
|
||||
CREATE INDEX IF NOT EXISTS volume_published ON volume (published);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS volume_idol (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
volume_id INTEGER,
|
||||
idol_id INTEGER,
|
||||
CONSTRAINT fk_volume_idol_volume
|
||||
FOREIGN KEY (volume_id) REFERENCES volume (id)
|
||||
ON DELETE RESTRICT
|
||||
ON UPDATE RESTRICT,
|
||||
CONSTRAINT fk_volume_idol_idol
|
||||
FOREIGN KEY (idol_id) REFERENCES idol (id)
|
||||
ON DELETE RESTRICT
|
||||
ON UPDATE RESTRICT
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS volume_idol_volume ON volume_idol (volume_id);
|
||||
CREATE INDEX IF NOT EXISTS volume_idol_idol ON volume_idol (idol_id);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS volume_idol_unique ON volume_idol (volume_id, idol_id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS volume_studio (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
volume_id INTEGER,
|
||||
studio_id INTEGER,
|
||||
CONSTRAINT fk_volume_studio_volume
|
||||
FOREIGN KEY (volume_id) REFERENCES volume (id)
|
||||
ON DELETE RESTRICT
|
||||
ON UPDATE RESTRICT,
|
||||
CONSTRAINT fk_volume_studio_studio
|
||||
FOREIGN KEY (studio_id) REFERENCES studio (id)
|
||||
ON DELETE RESTRICT
|
||||
ON UPDATE RESTRICT
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS volume_studio_volume ON volume_studio (volume_id);
|
||||
CREATE INDEX IF NOT EXISTS volume_studio_studio ON volume_studio (studio_id);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS volume_studio_unique ON volume_studio (volume_id, studio_id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS volume_tag (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
volume_id INTEGER,
|
||||
tag_id INTEGER,
|
||||
CONSTRAINT fk_volume_tag_volume
|
||||
FOREIGN KEY (volume_id) REFERENCES volume (id)
|
||||
ON DELETE RESTRICT
|
||||
ON UPDATE RESTRICT,
|
||||
CONSTRAINT fk_volume_tag_tag
|
||||
FOREIGN KEY (tag_id) REFERENCES tag (id)
|
||||
ON DELETE RESTRICT
|
||||
ON UPDATE RESTRICT
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS volume_tag_volume ON volume_tag (volume_id);
|
||||
CREATE INDEX IF NOT EXISTS volume_tag_tag ON volume_tag (tag_id);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS volume_tag_unique ON volume_tag (volume_id, tag_id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS idol_tag (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
idol_id INTEGER,
|
||||
tag_id INTEGER,
|
||||
CONSTRAINT fk_idol_tag_idol
|
||||
FOREIGN KEY (idol_id) REFERENCES idol (id)
|
||||
ON DELETE RESTRICT
|
||||
ON UPDATE RESTRICT,
|
||||
CONSTRAINT fk_idol_tag_tag
|
||||
FOREIGN KEY (tag_id) REFERENCES tag (id)
|
||||
ON DELETE RESTRICT
|
||||
ON UPDATE RESTRICT
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idol_tag_tag ON idol_tag (tag_id);
|
||||
CREATE INDEX IF NOT EXISTS idol_tag_idol ON idol_tag (idol_id);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idol_tag_unique ON idol_tag (idol_id, tag_id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS studio_tag (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
studio_id INTEGER,
|
||||
tag_id INTEGER,
|
||||
CONSTRAINT fk_studio_tag_studio
|
||||
FOREIGN KEY (studio_id) REFERENCES studio (id)
|
||||
ON DELETE RESTRICT
|
||||
ON UPDATE RESTRICT,
|
||||
CONSTRAINT fk_studio_tag_tag
|
||||
FOREIGN KEY (tag_id) REFERENCES tag (id)
|
||||
ON DELETE RESTRICT
|
||||
ON UPDATE RESTRICT
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS studio_tag_tag ON studio_tag (tag_id);
|
||||
CREATE INDEX IF NOT EXISTS studio_tag_studio ON studio_tag (studio_id);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS studio_tag_unique ON studio_tag (studio_id, tag_id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS volume_note (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
volume_id INTEGER,
|
||||
note_id INTEGER,
|
||||
CONSTRAINT fk_volume_note_volume
|
||||
FOREIGN KEY (volume_id) REFERENCES volume (id)
|
||||
ON DELETE RESTRICT
|
||||
ON UPDATE RESTRICT,
|
||||
CONSTRAINT fk_volume_note_note
|
||||
FOREIGN KEY (note_id) REFERENCES note (id)
|
||||
ON DELETE RESTRICT
|
||||
ON UPDATE RESTRICT
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS volume_note_note ON volume_note (note_id);
|
||||
CREATE INDEX IF NOT EXISTS volume_note_volume ON volume_note (volume_id);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS volume_note_unique ON volume_note (volume_id, note_id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS idol_note (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
idol_id INTEGER,
|
||||
note_id INTEGER,
|
||||
CONSTRAINT fk_idol_note_idol
|
||||
FOREIGN KEY (idol_id) REFERENCES idol (id)
|
||||
ON DELETE RESTRICT
|
||||
ON UPDATE RESTRICT,
|
||||
CONSTRAINT fk_idol_note_note
|
||||
FOREIGN KEY (note_id) REFERENCES note (id)
|
||||
ON DELETE RESTRICT
|
||||
ON UPDATE RESTRICT
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idol_note_note ON idol_note (note_id);
|
||||
CREATE INDEX IF NOT EXISTS idol_note_idol ON idol_note (idol_id);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idol_note_unique ON idol_note (idol_id, note_id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS studio_note (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
studio_id INTEGER,
|
||||
note_id INTEGER,
|
||||
CONSTRAINT fk_studio_note_studio
|
||||
FOREIGN KEY (studio_id) REFERENCES studio (id)
|
||||
ON DELETE RESTRICT
|
||||
ON UPDATE RESTRICT,
|
||||
CONSTRAINT fk_studio_note_note
|
||||
FOREIGN KEY (note_id) REFERENCES note (id)
|
||||
ON DELETE RESTRICT
|
||||
ON UPDATE RESTRICT
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS studio_note_note ON studio_note (note_id);
|
||||
CREATE INDEX IF NOT EXISTS studio_note_studio ON studio_note (studio_id);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS studio_note_unique ON studio_note (studio_id, note_id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS tag_note (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
tag_id INTEGER,
|
||||
note_id INTEGER,
|
||||
CONSTRAINT fk_tag_note_tag
|
||||
FOREIGN KEY (tag_id) REFERENCES tag (id)
|
||||
ON DELETE RESTRICT
|
||||
ON UPDATE RESTRICT,
|
||||
CONSTRAINT fk_tag_note_note
|
||||
FOREIGN KEY (note_id) REFERENCES note (id)
|
||||
ON DELETE RESTRICT
|
||||
ON UPDATE RESTRICT
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS tag_note_note ON tag_note (note_id);
|
||||
CREATE INDEX IF NOT EXISTS tag_note_tag ON tag_note (tag_id);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS tag_note_unique ON tag_note (tag_id, note_id);
|
||||
")
|
|
@ -0,0 +1,91 @@
|
|||
(define-module (juniority grammar)
|
||||
#:use-module (ice-9 peg)
|
||||
#:export (import-line
|
||||
export-idol-stmt
|
||||
export-volume-stmt
|
||||
))
|
||||
|
||||
;; Whitespace
|
||||
(define-peg-pattern NL none "\n")
|
||||
(define-peg-pattern WS none (+ " "))
|
||||
|
||||
;; Date
|
||||
(define-peg-pattern digit body (range #\0 #\9))
|
||||
(define-peg-pattern date all
|
||||
(and (+ digit)
|
||||
"-"
|
||||
(+ digit)
|
||||
"-"
|
||||
(+ digit)))
|
||||
|
||||
;; Tags
|
||||
(define-peg-pattern tag-chars body
|
||||
(and (* (and peg-any (not-followed-by "'")))
|
||||
(and peg-any (followed-by "'"))))
|
||||
(define-peg-pattern tag-body body
|
||||
(and (ignore "'")
|
||||
tag-chars
|
||||
(ignore "'")))
|
||||
(define-peg-pattern tag all
|
||||
tag-body)
|
||||
(define-peg-pattern untag all
|
||||
(and (ignore "!")
|
||||
tag-body))
|
||||
(define-peg-pattern tags all (* (and WS (or tag untag))))
|
||||
|
||||
;; Idols
|
||||
(define-peg-pattern idol-tag all
|
||||
(and (ignore "@")
|
||||
tag-body))
|
||||
(define-peg-pattern idol-untag all
|
||||
(and (ignore "!")
|
||||
(ignore "@")
|
||||
tag-body))
|
||||
(define-peg-pattern idols all (* (and WS (or idol-tag idol-untag))))
|
||||
(define-peg-pattern idol-entry all
|
||||
(and idol-tag
|
||||
tags
|
||||
(? (and WS date))))
|
||||
|
||||
;; Volumes
|
||||
(define-peg-pattern caps-alpha body
|
||||
(range #\A #\Z))
|
||||
(define-peg-pattern volume all
|
||||
(and (+ caps-alpha)
|
||||
"-"
|
||||
(+ digit)
|
||||
))
|
||||
(define-peg-pattern volume-entry all
|
||||
(and volume
|
||||
idols
|
||||
tags
|
||||
(? (and WS date))
|
||||
))
|
||||
|
||||
;; Entry point
|
||||
(define-peg-pattern import-line body
|
||||
(or idol-entry
|
||||
volume-entry
|
||||
NL))
|
||||
|
||||
;; Export procedures
|
||||
(define (export-date-expr date)
|
||||
(if date
|
||||
(format #f " ~a" date)
|
||||
""))
|
||||
(define (export-idol-expr name)
|
||||
(format #f " @'~a'" name))
|
||||
(define (export-tag-expr name)
|
||||
(format #f " '~a'" name))
|
||||
|
||||
(define* (export-idol-stmt name tags #:optional born)
|
||||
(format #f "@'~a'~a~a"
|
||||
name
|
||||
(string-concatenate (map export-tag-expr tags))
|
||||
(export-date-expr born)))
|
||||
(define* (export-volume-stmt identifier idols tags #:optional published)
|
||||
(format #f "~a~a~a~a"
|
||||
identifier
|
||||
(string-concatenate (map export-idol-expr idols))
|
||||
(string-concatenate (map export-tag-expr tags))
|
||||
(export-date-expr published)))
|
|
@ -6,9 +6,9 @@ CREATE UNIQUE INDEX IF NOT EXISTS tag_name_unique ON tag (name);
|
|||
|
||||
CREATE TABLE IF NOT EXISTS note (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL,
|
||||
created INTEGER, -- epoch
|
||||
updated INTEGER -- epoch
|
||||
text TEXT NOT NULL,
|
||||
created INTEGER NOT NULL, -- epoch
|
||||
updated INTEGER NOT NULL -- epoch
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS studio (
|
||||
|
|
読み込み中…
新しいイシューから参照