このあいだの、MediaWiki のデータベースとして MariaDB を採用したわけだけど、バックアップのために dump するには MySQL と同じように mysqldump が使える。
こんな感じ:
$ mysqldump -P 3306 -u root -p testwiki > testwiki.sql
takatoh's blog – Learning programming languages.
このあいだの、MediaWiki のデータベースとして MariaDB を採用したわけだけど、バックアップのために dump するには MySQL と同じように mysqldump が使える。
こんな感じ:
$ mysqldump -P 3306 -u root -p testwiki > testwiki.sql
ローカルネットワークで wiki を運用してるんだけど、Docker 上に移行すべく、今日はそのテスト。
MediaWiki も MariaDB も Docker Hub に公式イメージが有るのでそれを使わせてもらう。データベースは、はじめは MySQL を試したんだけどうまく動かなかった(原因不明)ので MariaDB に変えた。
用意した構成はこんなの:
takatoh@apostrophe:testwiki$ tree . . ├── docker-compose.yml ├── mysql │ └── db └── wiki └── images
docker-compose.yml はこう:
version: '3' services: testwiki: container_name: testwiki image: mediawiki:1.35.0 restart: always ports: - 8888:80 volumes: - ./wiki/images:/var/www/html/images # - ./wiki/LocalSettings.php:/var/www/html/LocalSettings.php mysql: container_name: db image: mariadb:10.5.6-focal restart: always environment: MYSQL_ROOT_PASSWORD: rootpasswd MYSQL_DATABASE: testwiki MYSQL_USER: mysqluser MYSQL_PASSWORD: mypassword ports: - 3306:3306 volumes: - ./mysql/db:/var/lib/mysql
コメントアウトしてある行は MediaWiki の設定ファイル。これはインストールが済んでから使う。
docker-compose
コマンドでコンテナを起動する。
takatoh@apostrophe:testwiki$ docker-compose up -d
ブラウザで http://localhost:8888/ にアクセスして MediaWiki のインストール(というかセットアップというか)をする。その際、データベース関係は docker-compose.yml の記述に合わせる。
その他はそれなりに設定すればいい。最後に LocalSettings.php ファイル(設定ファイル)をダウンロードしてインストールは終わり。
いったんコンテナを止める。
takatoh@apostrophe:testwiki$ docker-compose down
ダウンロードした設定ファイルを配置。
takatoh@apostrophe:testwiki$ cp ~/Downloads/LocalSettings.php wiki
docker-compose.yml のコメントをはずす(該当行だけ示す)。
- ./wiki/LocalSettings.php:/var/www/html/LocalSettings.php
コンテナを再起動。
takatoh@apostrophe:testwiki$ docker-compose up -d
これで無事起動した。
旧い wiki からデータを移行する手順。
wiki.sql ファイルを Docker コンテナと共有しているディレクトリにコピーする。
takatoh@apostrophe:testwiki$ cp wiki.sql ./mysql/db
./mysql/db ディレクトリは、データベースの Docker コンテナ(コンテナ名は db)からは /var/lib/mysql として認識されている(前述の docker-compose.yml ファイルを参照)。なのでデータベースのコンテナに接続して、データを流し込む。
takatoh@apostrophe:testwiki$ docker exec -it db bash root@007d71dfcb37:/# cd /var/lib/mysql root@007d71dfcb37:/var/lib/mysql# ls *.sql wiki.sql root@007d71dfcb37:/var/lib/mysql# mysql -u mysqluser -p testwiki < wiki.sql Enter password: root@007d71dfcb37:/var/lib/mysql# exit exit
これでデータベース側での作業は終了。ただ、このままだと MediaWiki でエラーになる。バージョンが上がっているので MediaWiki の使用するデータベーススキーマとかも変わっているからだ。
そこで、MediaWiki のコンテナに接続して更新スクリプトを実行する。更新スクリプトは /var/www/html/maintenance/update.php だ。
takatoh@apostrophe:testwiki$ docker exec -it testwiki bash root@f92dd50471e5:/var/www/html# cd maintenance root@f92dd50471e5:/var/www/html/maintenance# php update.php
これで完了。
11/7、本番環境も無事 Docker 上に移行した。
このあいだ作った、画像ファイルから EPUB ファイルをつくるスクリプトを、wheel でパッケージングして PyPI に公開した。バージョンは v0.3.0。ソースは GitHub に上げてある。
ファイル構成は変わったし、実行コマンドに click を導入して img2epub build
コマンドで EPUB を生成するようにしたけど、機能的にはこのあいだのスクリプトと大して変わってないので改めては説明しない。必要なら上のリンク先を見てほしい。
代わりに、Python でパッケージを開発して PyPI に公開するまでの作業で、これまで知らなかったりあやふやだったところを調べたのでその辺についてメモしておく。以前とはやり方が変わったりしてて、知識はアップデートしなきゃならんと思った。
開発用の仮想環境。以前は virtualenv というサードパーティのパッケージを使ってたけど、Python 3.3 から venv というのが標準でついてくる。プロジェクトのディレクトリで次のようにすると仮想環境が作られる。
takatoh@montana: myproject > python -m venv .venv
.venv とういのが仮想環境の名前。なんでもいいようだけど .venv とすることが多いみたい。仮想環境をアクティベートするには:
takatoh@montana: myproject > .venv/Scripts/Activate.ps1 (.venv) takatoh@montana: myproject >
アクティベートされると2行目のようにプロンプトに仮想環境名が表示される。仮想環境から抜けるにはこう:
(.venv) takatoh@montana: myproject > deactivate
作ったばかりの仮想環境には pip と setuptools しか入ってない。
(.venv) takatoh@montana: myproject > pip list Package Version ---------- ------- pip 20.2.1 setuptools 49.2.1 WARNING: You are using pip version 20.2.1; however, version 20.2.4 is available. You should consider upgrading via the 'c:\users\takatoh\documents\w\myproject.venv\scripts\python.exe -m pip install --upgrade pip' command.
pip の新しいバージョンがあるのでバージョンアップしておく。
(.venv) takatoh@montana: myproject > python -m pip install --upgrade pip
setup.py はパッケージングに使うスクリプトだけど、もろもろの設定はこのファイルには書かないのが最近の作法らしい。だから中身はこれだけ:
import setuptools setuptools.setup()
じゃあ、設定はどこに書くのかというと setup.cfg というファイルに書く。書式も ini ファイル風。
[metadata] name = myproject version = attr:myproj.__version__ description = My Python project example. author = takatoh author_email = [email protected] license = MIT License classifier = License :: OSI Approved :: MIT License Programming Language :: Python Programming Language :: Python :: 3 [options] zip_safe = False packages = find: include_package_data = True entry_points = file:entry_points.cfg install_requires = click
パッケージが実行コマンドを含むので [options] セクションに entry_points = file:entry_points.cfg を指定している。entry_points.cfg ファイルにコマンド名と実際に実行される関数を書くようになっている。その entry_points.cfg はこう:
[console_scripts] myproj = myproj.command:main
もうひとつ、パッケージに含むファイルについて。packages = find: という記述で Python のスクリプトファイルを含めることができる。だけど場合によってはスクリプトでないファイルを含めたいこともある。img2epub ではテンプレートファイルが必要だった。そういう時には include_package_data = True としておいて、MANIFEST.in にファイルを列挙する。こんな感じ:
include myproj/data/greeting.txt
ファイルは setup.py(あるいは setup.cfg というべき?)からの相対パスで書く。
というわけで、これでパッケージングの準備は完了。設定内容自体は以前の setup.py に書くスタイルと変わらないんだけど、なんかファイルが分散してわかりにくくなったんじゃないかなぁ。
pip install -e .
コマンドは、開発中のパッケージを「開発モード」でインストールしてくれる。
(.venv) takatoh@montana: myproject > pip install -e . Obtaining file:///C:/Users/takatoh/Documents/w/myproject Collecting click Using cached click-7.1.2-py2.py3-none-any.whl (82 kB) Installing collected packages: click, myproject Running setup.py develop for myproject Successfully installed click-7.1.2 myproject
依存パッケージがあれば一緒にインストールしてくれるのはもちろんだけど、開発中のパッケージのファイルを更新すると、自動的に反映してくれる。
(.venv) takatoh@montana: myproject > pip list Package Version Location ---------- ------- -------------------------------------- click 7.1.2 myproject 0.0.1 c:\users\takatoh\documents\w\myproject pip 20.2.4 setuptools 49.2.1
myproject だけパスが書いてある。これが開発中のパッケージだ。
※まだ書きかけ。
Qiita の↓の記事を読んで、EPUB って結構簡単(もちろん単純なものなら)なんだな、と思ったので Python でスクリプトを作ってみた。
フォルダに入った画像ファイル一式から EPUB ファイルを生成する。
ファイル構成は次の通り:
takatoh@montana: img2epub > tree /f . フォルダー パスの一覧 ボリューム シリアル番号は 681C-8AA1 です C:\USERS\TAKATOH\DOCUMENTS\W\IMG2EPUB │ .gitignore │ img2epub.py │ ├─data │ book.opf.template │ chap1.xhtml.template │ container.xml │ nav.xhtml │ └─sample sample-000.png sample-001.png sample-002.png sample-003.png sample-004.png sample-005.png sample-006.png sample-007.png
img2epub.py が Python で書いたスクリプト本体。data フォルダ以下のファイルは EPUB を構成するファイルあるいはそのテンプレート。スクリプトは次の通り:
#!/usr/bin/env python # encoding: utf-8 import sys import os import shutil import subprocess from datetime import datetime, timezone import uuid import glob from jinja2 import Template, Environment, FileSystemLoader def main(): src_dir = sys.argv[1] now = datetime.now(timezone.utc) tmp_dir_name = "tmp.epub.{time}".format(time=now.strftime("%Y%m%d%H%M%S")) make_dirs(tmp_dir_name) images = copy_images(src_dir, tmp_dir_name) images = [s.replace("\\","/") for s in sorted(images)] gen_mimetype(tmp_dir_name) copy_container(tmp_dir_name) book_opf_context = { "title": src_dir, "time": now.isoformat(), "images": images } gen_book_opf(tmp_dir_name, book_opf_context) copy_nav(tmp_dir_name) gen_chap1_xhtml(tmp_dir_name, book_opf_context) zip_epub(tmp_dir_name, src_dir) def make_dirs(tmp_dir_name): os.makedirs(os.path.join(tmp_dir_name, "META-INF")) os.makedirs(os.path.join(tmp_dir_name, "EPUB")) def copy_images(src_dir, tmp_dir_name): images_dir = os.path.join(tmp_dir_name, "EPUB/images") shutil.copytree(src_dir, images_dir) return glob.glob("{dir}/*".format(dir=images_dir)) def gen_mimetype(tmp_dir_name): with open(os.path.join(tmp_dir_name, "mimetype"), "w") as f: f.write("application/epub+zip") def copy_container(tmp_dir_name): shutil.copyfile("data/container.xml", os.path.join(tmp_dir_name, "META-INF/container.xml")) def gen_book_opf(tmp_dir_name, context): env = Environment(loader=FileSystemLoader("data")) template = env.get_template("book.opf.template") context["images"] = [s.replace("{tmp}/EPUB".format(tmp=tmp_dir_name), ".") for s in context["images"]] context["cover"] = context["images"][0] context["uuid"] = str(uuid.uuid4()) with open(os.path.join(tmp_dir_name, "EPUB/book.opf"), "w") as f: f.write(template.render(context)) def copy_nav(tmp_dir_name): shutil.copyfile("data/nav.xhtml", os.path.join(tmp_dir_name, "EPUB/nav.xhtml")) def gen_chap1_xhtml(tmp_dir_name, context): env = Environment(loader=FileSystemLoader("data")) template = env.get_template("chap1.xhtml.template") images = [s.replace("{tmp}/EPUB".format(tmp=tmp_dir_name), ".") for s in context["images"]] with open(os.path.join(tmp_dir_name, "EPUB/chap1.xhtml"), "w") as f: f.write(template.render(images=images)) def zip_epub(tmp_dir_name, title): epub_file_name = "../{title}.epub".format(title=title) os.chdir(tmp_dir_name) subprocess.run(["zip", "-X0", epub_file_name, "mimetype"], stdout=subprocess.DEVNULL) subprocess.run(["zip", "-r9", epub_file_name, "*", "-x", "mimetype"], stdout=subprocess.DEVNULL) os.chdir("..") main()
で、sample フォルダ以下が元になる画像ファイル一式。
スクリプトの引数に画像一式が入っているフォルダを指定するだけ。
takatoh@montana: img2epub > python img2epub.py sample
そうすると、EPUB ファイル(今回は sample.epub)と、EPUB にまとめる前のファイル一式の入ったフォルダ(同じく tmp.epub.20201027130853)ができる。このフォルダはテンポラリなものなので消しちゃってもいいんだけど、今の段階ではまだ残している。
takatoh@montana: img2epub > ls
Directory: C:\Users\takatoh\Documents\w\img2epub
Mode LastWriteTime Length Name
---- ------------- ------ ----
d---- 2020/10/27 20:07 data
d---- 2020/10/27 20:09 sample
d---- 2020/10/27 22:08 tmp.epub.20201027130853
-a--- 2020/10/27 20:07 9 .gitignore
-a--- 2020/10/27 20:07 2832 img2epub.py
-a--- 2020/10/27 22:08 8011218 sample.epub
あとは出来上がった sample.epub ファイルを好きな EPUB ビューワで見ればいい。
EPUB のファイルっていうのは、基本的には使用で決められたファイルとコンテンツを zip で一つにまとめて、拡張子を .epub
にしただけのファイルだ。単純に zip にしただけではないんだけど、そのへんはこの記事では触れない。冒頭の Qiita の記事か、EPUB 3.2 の仕様を参照のこと。
今回作ったスクリプト img2epub.py では、tmp.epub.* フォルダにその一式が入っている(つまりこれを zip 圧縮して .epub
ファイルを作る)。フォルダの中身は次の通り:
takatoh@montana: img2epub > tree /f tmp.epub.20201027130853 フォルダー パスの一覧 ボリューム シリアル番号は 681C-8AA1 です C:\USERS\TAKATOH\DOCUMENTS\W\IMG2EPUB\TMP.EPUB.20201027130853 │ mimetype │ ├─EPUB │ │ book.opf │ │ chap1.xhtml │ │ nav.xhtml │ │ │ └─images │ sample-000.png │ sample-001.png │ sample-002.png │ sample-003.png │ sample-004.png │ sample-005.png │ sample-006.png │ sample-007.png │ └─META-INF container.xml
EPUB/images 以下の画像ファイルは、元のデータをコピーしたもの。そのほかのファイルはスクリプトが生成したファイルだ。詳しくは略。
EPUB を構成するファイルの一部(book.opf や chap1.xhtml)を生成するためにテンプレートエンジンを使ってるんだけど、Python には string.Template
というテンプレートエンジンが標準でついている。これ、今回調べてて初めて知った。
ところがこの string.Template
、単純な値の挿入はできるけど繰り返しや条件分岐の機能がない。今回、条件分岐は使ってないけど繰り返すは必要だったので、結局 Jinja2 を使った。標準添付されてるのはいいけど、変数を値に置き換えるだけしかできないんじゃ、用途は限られるよなぁ。
PDF をページごとの画像ファイルに変換したくて、はじめは Python でできないか調べてた。そしたら↓のページで pdf2image という(Pythonの)ライブラリを紹介しているのを見つけた。
ところが記事を読んでみるとこう書いてある:
pdf2imageは「Poppler」というフリーのPDFコマンドラインツールを背後で用います。そのため、Popplerをダウンロードしておく必要があります。
それなら Poppler をそのまま使えばいいじゃん。
というわけで、Poppler for Windows をダウンロードした。
バージョンは 0.68.0 (poppler-0.68.0_x86.7z)。7zip なので 7z コマンドをインストールしてから展開した。
展開したファイルを眺めてみると、bin フォルダの中に pdfimages.exe という実行ファイルがある。これが使えそうだ。PATH を通してとりあえずヘルプを見てみた。
takatoh@montana: tmp > pdfimages -h pdfimages version 0.68.0 Copyright 2005-2018 The Poppler Developers - http://poppler.freedesktop.org Copyright 1996-2011 Glyph & Cog, LLC Usage: pdfimages [options] <PDF-file> <image-root> -f <int> : first page to convert -l <int> : last page to convert -png : change the default output format to PNG -tiff : change the default output format to TIFF -j : write JPEG images as JPEG files -jp2 : write JPEG2000 images as JP2 files -jbig2 : write JBIG2 images as JBIG2 files -ccitt : write CCITT images as CCITT files -all : equivalent to -png -tiff -j -jp2 -jbig2 -ccitt -list : print list of images instead of saving -opw <string> : owner password (for encrypted files) -upw <string> : user password (for encrypted files) -p : include page numbers in output file names -q : don't print any messages or errors -v : print copyright and version info -h : print usage information -help : print usage information --help : print usage information -? : print usage information
出力は JPEG が欲しかったので -j
オプションを指定。<image-root>
が何を指すのかよくわからないけどテキトーに。そしたらこうなった。
takatoh@montana: tmp > pdfimages -j sample.pdf foo
takatoh@montana: tmp > ls
Directory: C:\Users\takatoh\Documents\tmp
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 2020/10/24 8:53 5364971 foo-000.ppm
-a--- 2020/10/24 8:53 5364971 foo-001.ppm
-a--- 2020/10/24 8:53 5364971 foo-002.ppm
-a--- 2020/10/24 8:53 5364971 foo-003.ppm
-a--- 2020/10/24 8:53 5364971 foo-004.ppm
-a--- 2020/10/24 8:53 5364971 foo-005.ppm
-a--- 2020/10/24 8:53 5364971 foo-006.ppm
-a--- 2020/10/24 8:53 5364971 foo-007.ppm
-a--- 2020/03/28 9:12 4082441 sample.pdf
.ppm
って!いまどき .ppm
ファイルなんて何で見ればいいんだ。なら PNG でいいや。あと、<image-root>
は出力ファイルのプレフィックスみたいだな。なので .ppm
ファイルは削除してやり直した。
takatoh@montana: tmp > pdfimages -png sample.pdf sample
takatoh@montana: tmp > ls
Directory: C:\Users\takatoh\Documents\tmp
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 2020/03/28 9:12 4082441 sample.pdf
-a--- 2020/10/24 9:01 1269290 sample-000.png
-a--- 2020/10/24 9:01 446405 sample-001.png
-a--- 2020/10/24 9:01 893712 sample-002.png
-a--- 2020/10/24 9:01 1258104 sample-003.png
-a--- 2020/10/24 9:01 1301072 sample-004.png
-a--- 2020/10/24 9:01 1344592 sample-005.png
-a--- 2020/10/24 9:01 1157016 sample-006.png
-a--- 2020/10/24 9:01 755768 sample-007.png
これで OK。だけど、欲を言えば出力されるファイルをひとつのフォルダに入れたい。それらしいオプションは見当たらないけど、プレフィックスにパスを含めてやればできた。フォルダは先に作っておくこと。
takatoh@montana: tmp > mkdir outDirectory: C:\Users\takatoh\Documents\tmp
Mode LastWriteTime Length Name ---- ------------- ------ ---- d---- 2020/10/24 11:29 out takatoh@montana: tmp > pdfimages -png sample.pdf out/sample takatoh@montana: tmp > ls outDirectory: C:\Users\takatoh\Documents\tmp\out
Mode LastWriteTime Length Name ---- ------------- ------ ---- -a--- 2020/10/24 11:29 1269290 sample-000.png -a--- 2020/10/24 11:29 446405 sample-001.png -a--- 2020/10/24 11:29 893712 sample-002.png -a--- 2020/10/24 11:29 1258104 sample-003.png -a--- 2020/10/24 11:29 1301072 sample-004.png -a--- 2020/10/24 11:29 1344592 sample-005.png -a--- 2020/10/24 11:29 1157016 sample-006.png -a--- 2020/10/24 11:29 755768 sample-007.png
できた。
補足。
-j
オプションの説明は write JPEG images as JPEG files となっている。PDFの中身が JPEG なら JPEG で出力するってことのようだ。
他人の書いた VBA のコードを調べていて初めて知った。
今までずっと VBA の配列のインデックスは 1 始まりだと思ってたんだ。だからたとえば3個の要素を持つ配列についての繰り返しはこんなふうに書いてた。
Dim arr(3) ... For i = 1 To 3 MsgBox arr(i) Next i
これで期待通り動いてたんだから何の疑いも持たずにいた。数あるプログラミング言語の中で 1始まりは少数派かもしれないけど、n 個の要素を持つ配列のインデックスが 1 ~ n というのはわかりやすいともいえる。
だけど、実はインデックス 0 も使えるという。
え?
ということは、配列を Dim arr(3)
って宣言するとインデックス 0 から 3 までの4要素の配列ができるってこと?
実際にやってみよう。
Sub TEST() Dim arr(3) As String arr(0) = "Andy" arr(1) = "Bill" arr(2) = "Charlie" arr(3) = "Dave" For i = 0 To 3 MsgBox arr(i) Next i End Sub
この TEST マクロを実行すると、たしかに Andy、Bill、Charlie、Dave の4つが順にメッセージボックスに表示される。
うへぇ、なにこの変な仕様。
といっても公式サイトからダウンロードして展開して PATH を通すだけ。バージョンは 1.15.2 だった。
インストール手順。まずはダウンロードした tarball を /usr/local 以下に展開。
takatoh@apostrophe:Downloads$ sudo tar -C /usr/local -xzf go1.15.2.linux-amd64.tar.gz
/usr/loca/go/bin に PATH を通す。
export PATH=$PATH:/usr/local/go/bin
ついでに GOPATH を設定しておく。
export GOPATH=$HOME/go/thirdparty:$HOME/go/projects
go version
コマンドでバージョンが表示されれば OK。
takatoh@apostrophe:~$ go version go version go1.15.2 linux/amd64
すんごい楽だな。
あとで書く。
出来ない、というか少なくともウチの環境では出来なかった。
Windows10 の iTunes にある音楽を新しい iMac のミュージックアプリにどうやって移行するのか、ググってみたところで見つかったのが「移行アシスタント」だ。これは同じネットワーク内にある Windows 側と iMac 側の両方で移行アシスタントを実行すると簡単にデータ(今回の目的は音楽だけだけど、これに限らない)を移行できるというもの。少なくとも Apple のページにはそう書いてある。
ところが、実際にページに書いてある手順でやろうとしたところ、そもそも Windows 版の移行アシスタントがインストールできない。ファイルをダウンロードし直したり、PC を再起動したりと色々やってみたけど、どうにもならない。移行アシスタントアプリのバージョンのせいなのかそれとも Windows の環境のせいなのか、何度やってもダメだった。
仕方がないので、Windows 側の iTunes のデータフォルダを丸ごと USB メモリにコピーして iMac に挿した。で、ミュージックアプリにインポート。時間はかかったけど、とりあえず曲の移行はできた。
が、だ。
このやり方はつまり、ミュージックアプリに新しい曲をファイルから追加しただけなので、再生回数みたいなメタデータやプレイリストは移行されてない。
これらをどうやったら Windows から持ってくれるか、調べてみたけどいい方法が見つからなかった。もしかしたらやり方があるのかもしれないけど。
で、いつまでもこれだけに構っていられないので、もう諦めることにした。プレイリストもいくつか程度しか作ってないし、作り直せばいいだろ。
というわけで、何の役にも立たない記事だな。
あ、あとこれだけは書いておく。USB メモリを Windows と Mac の両方で使うには exFAT でフォーマットしておく必要がある。
とうとうやってしまった。何をって、タイトルの通り iMac を買ってしまったんだ。21.5 インチ Retina 4K ディスプレイモデル。今月初めに注文して、今日届いた。
今、とりあえずのセットアップが終わったところ。この記事も iMac で書いてる。スペックを書いておくと、次の通り:
これから使うツールやら何やらをインストールしていく。まずは Homebrew かな。作業が進んだら追記する。
公式サイトにインストール用のコマンドが載っているので、それをターミナルにコピペする。
takatoh@MISHIMAnoiMac ~ % /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"
インストールされてた。
takatoh@MISHIMAnoiMac ~ % ruby -v ruby 2.6.3p62 (2019-04-16 revision 67580) [universal.x86_64-darwin19]
takatoh@MISHIMAnoiMac ~ % git --version git version 2.24.3 (Apple Git-128)
セットアップ時にテキトーにつけられたホスト名が気に入らないので変更。
「bigswifty」にした。
これも入ってた。
takatoh@bigswifty ~ % python -V Python 2.7.16
こんなところか。
あとは、Windows の iTunes にある音楽を iMac に移動する。これは記事を分けよう。