「医療用フリーソフト紹介」に OpenDolphin の項を追加しようかな?と思ってたら、LSC での販売終了のお知らせ。
私がこの数奇?な運命を遂げたオープンソースの電子カルテに触ったのは比較的最近のことだ。具体的には、
猪股先生版 OpenDolphin https://github.com/Hiroaki-Inomata/OpenDolphin-2.7m
を最近の Mac 環境に導入したときからだ。
参考:『OpenDolphin-2.7(m) を Mac OSX にインストールする』
だから、きっちりとした記載をしようとするとえらく手間がかかる。
「日本初の Open Source の電子カルテ」というのがウリだったようなのだが、この点に関して「おや」と気になったことがあったので、とりあえずはこの点を中心に気になったことを軽い感じでまとめていく。
なお、特別に断らない限り、ここでいう OpenDolphin とは Ver 2.7 系列のことです。
コンテンツ
歴史
経産省の助成を受けて・・・云々の話はネットで探せば出てくると思うので、省く。
いわゆる「本家」と称されていた dolphin-dev リポジトリの管理会社は
digital globe → Life Science Computing(LSC) → Medley
と変遷している。
ただ、調べていくと、LSC 時代のある時(2018頃?)から、開発方針や対外的な協力関係は大きく変わったようだ。
前半は、俗っぽい言い方をするなら「喧嘩っぱやい」会社。権利意識が強く、実際、訴訟などもおこしていた。
残念ながら、その結果は意図していたようなものでなく、その影響なのか、これ以降、開発体制なども刷新されたようだ。
後半は、うってかわってマイルドな路線に切り替わる。
電子カルテとしての特徴
よく
・カルテ2号用紙をメタファーとしたリッチな GUI
・使用頻度の多い項目を簡易にドラッグ&ドロップで入力できる「スタンプ」機能
が特徴としてあげられている。
これは見てもらった方が早いでしょうか。
ただし、現在(2021年)では、こういった特徴はブラウザ型の電子カルテなどにも取りいれられている。
開発時期を考えると当時としては医師目線で使いやすいシステムであったとは言えるでしょう。
技術的特徴
技術的な話も少々。
開発言語は Java で、maven で管理されている。client, common, server という三つのプロジェクトからなる。この名称から予想されるようにクライアント・サーバーシステムである。
基本となる設計は、典型的な MCV(Model-Controller-Viewer) の三層アーキテクチャである。
カルテ記載内容などを記録・保存しておく必要があるため、アプリケーションサーバー(WildFly)を介してデータベース(PostgreSQL)と連携して動作する。
保存形式を決めるモデルは、具体的には上記 common プロジェクト内の infomodel パッケージで定義されている。オブジェクトをデータベースに格納させる際には、ORM である hibernate を用いている。
なお、よく「Java で作成されているためマルチプラットフォームであり、クライアントが Windows/Mac/Linux で動作する」と紹介されているが、これはやや誤解を生む表現である。サーバープログラムも pure Java で書かれているため、適切に環境を整えればサーバープログラムを Windows/Mac/Linux に設置(デプロイ)し動作させることは可能である。
ライセンス
ところで、オープンソースにつきものといえばライセンスだが、当初は GPL というかなり制約の厳しいライセンスを主張していた。が、これも体制の刷新を契機に取り扱いを変えてきている。
もちろん GitHub のリポジトリで謳われているライセンスは現在(2021年1月)でも GPL だが、メドレー本社はこれが妥当なものであるかは明言していない。
他社の商用利用(soso の GlassDolphin と MIA の OpenDolphin)は原則許可、名称の利用に関しても条件つきながら許可している。
これは以下のような事情があったためのようだ。
GitHub リポジトリでのプルリク・コードレビューがほぼない
この記事で
開発者を限定する、というのはあってもいいのだが(ニッチな分野では不特定多数の開発者をアテにはできないことがしばしばある)、そうだとしてもプルリクやマージは普通に行われる。ところが、「イルカ」ではこのプロセスがつい最近まで一切なかった。ちょうど下請けや孫請けにつくらせたコードをそのままボンッとリポジトリに送りこむかのように突如としてバージョンアップ版が出現していた。
とあって、「んな、バカな」と思っていたのだが、調べたら本当だった。
こういう調べものをするとき、GitHub などの Git ホスティングサービスは便利で誰がいつどのような変更を行ったか容易に確かめることができる。
で、調べてみると・・・
確かにわずか3件。
また、この3件のうち GlassDolphin の linuxmania さんが送ったプルリクエスト(PR)は 2.6.3 時代のもので、2.7.0 アップデートにより現在ではほぼその痕跡は残していない。
MasudaNaika(増田茂医師) が送った診療区分に関する bug fix はマージ(merge プルリクエストを取り込むこと)されて、現在でも生きているが、merge されたのは 2018/6/4 のことだ。
猪股版 OpenDolphin-2.7m のソースコードがこの bug を取り除いた形で GitHub 上に公開されたのは 2016/7/12 のことだから、この修正がどれほどの意味を持つのかという気はする。
Ver 2.2.1 時代のソースコードも
GitHub: https://github.com/nekop/opendolphin
に残されているが、このバージョンに至っては PR の痕跡は一切ない。
オープンソースのプロジェクトとしては運営形態がかなり特殊だったという指摘はもっともに思える。

これは、猪股先生の OpenDolphin-2.7m の開発形態と比較するとわかりやすいかもしれない。
猪股先生のリポジトリにある
https://github.com/Hiroaki-Inomata/OpenDolphin-2.7m/
が Fork 元になって、いくつかのリポジトリに分岐(Fork 先)しているのがわかると思う。
Fork 先で新たな機能を実装したり bug fix などを行って Fork 元(上流ブランチ)にそのコードを取り込んで欲しい場合、行われるのが Pull Request (PR などと略される)というものだ。
私も Fork 後、PRを送ったことがある。
https://github.com/ANN2b-MD/OpenDolphin-2.7m
が Fork したリポジトリ。
Mac ビルド用に軽微な変更が必要があったため、ローカルでコードを修正した後、リモートのリポジトリに上げた。
以下のように PR → merge が行われた。
( https://github.com/Hiroaki-Inomata/OpenDolphin-2.7m/pull/6)
このようなプロセスは GitHub 上の操作で比較的容易に実現できる。
この仕組みのおかげで、誰がいつどのような PR を行ったのか、また、それを merge する側はどのように評価(コードレビュー)したのかわかるようになっている。
上の場合は、コードレビューと言っても thanks だけですが(笑)。
通常のオープンソースのプロジェクトは、上記のように「本家」は大抵の場合、慣例的に一つであり、そこから各 Fork 先に枝分かれ状に分岐していく。逆に言えば、どれほど分岐しようとも、Fork 元をたどっていけば必ず一つの「源流」に行き着くことができる。
このように運営することのメリットは、分岐したブランチの開発者は誰でも本家に修正したコードを送ることができる、プロジェクトの最終産物(プロダクト)のバージョンの統一性が保たれる… などがある。
デメリットとしては、PR を送ったとしても必ずしも merge されるとは限らず、提案された機能などが最終プロダクトに取り込まれないといったことが挙げられる。
なお、dolphin-dev は、当初、このポリシーを取らず、ソースコード提供者を特定の開発者に限定していた。この痕跡は dolphin-dev の README の以下ような記載からも窺い知れる。
OpenDolphinにはコミッターが存在しません。フォークされた場合はそれぞれ独立した開発者になっていただき、 GitHub 等でソースの共有をお願いしています。
このような開発形態にしたため、機能の異なるいくつかのバージョンが存在するようになった。
現在でもソースが一般公開されている猪股バージョンのほか、
・入院対応が(部分的に)可能なバージョン
・音声入力機能を具備したバージョン
・クライアントでの処方箋出力機能を持ったバージョン
などがあったようである。
誰がソースコード提供者なのかわかりにくい
oracle のサンプルコードや公式には開発者とはされていない人のコードが散見される。
Junzo SATO 氏のコード
これは以前から指摘されていたのだが、もうちょっと網羅的に調べてみた。
Panel2.java (client project)
私のリポジトリであれば、
https://github.com/ANN2b-MD/OpenDolphin-2.7m/blob/master/client/src/main/java/open/dolphin/client/Panel2.java
/**
*
* @author Junzo SATO
*/
public class Panel2 extends JPanel implements Printable {
private String patientName;
private boolean printName;
private int height;
/** Creates a new instance of Panel2 */
public Panel2() {
}
しっかり読んでいるわけではないが、クライアントのエディタ画面を実装しているパネルはこの Panel2 のクラスが基本となっていたはずだ。
その他は以下のファイルに同氏の名前が残っていた。
SchemaHolder.java (client project)
public final class SchemaHolder extends AbstractComponentHolder implements ComponentHolder {
private SchemaModel schema;
//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
// Junzo SATO
// to restrict the size of the component,
// setBounds and setSize are overridden.
private final int fixedSize = 192;//160;/////////////////////////////////////////
private final int fixedWidth = fixedSize;
private final int fixedHeight = fixedSize;
//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
private boolean selected;
private Position start;
private Position end;
private final KartePane kartePane;
public SchemaHolder(KartePane kartePane, SchemaModel schema) {
this.kartePane = kartePane;
//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
// Junzo SATO
// for simplicity, the acpect ratio of the fixed rect is set to 1.
this.setDoubleBuffered(false);
this.setOpaque(true);
this.setBackground(Color.white);
//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
this.schema = schema;
//this.setImageIcon(schema.getIcon());
setIcon(adjustImageSize(schema.getIcon(), new Dimension(fixedWidth, fixedHeight)));
}
KarteViewer.java (client project)
// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
// Junzo SATO
public void printPanel2(final PageFormat format) {
String name = getContext().getPatient().getFullName();
boolean printName = true;
if (pPane==null) {
printName = printName && Project.getBoolean("plain.print.patinet.name");
}
panel2.printPanel(format, 1, false, name, getActualHeight()+60, printName);
}
KarteEditor.java (client project)
/**
* Courtesy of Junzo SATO
*/
private byte[] getJPEGByte(Image image) {
byte[] ret = null;
try {
JPanel myPanel = getUI();
Dimension d = new Dimension(image.getWidth(myPanel), image.getHeight(myPanel));
BufferedImage bf = new BufferedImage(d.width, d.height, BufferedImage.TYPE_INT_RGB);
Graphics g = bf.getGraphics();
g.setColor(Color.white);
g.drawImage(image, 0, 0, d.width, d.height, myPanel);
ByteArrayOutputStream bo = new ByteArrayOutputStream();
ImageIO.write(bf, "jpeg", bo);
ret = bo.toByteArray();
} catch (IOException e) {
e.printStackTrace(System.err);
}
return ret;
}
いずれも、クライアントのエディター上でカルテを表示・記載する際、かなり基本的となる部分で、特に Panel2.java では @author Junzo SATO と authoship を放棄していないことから、この方の著作権表記は retain していないとまずいと思われる。
ORACLE のコード
これはわかりやすかった。
以下の open.dolphin.helper パッケージの SpringUtilities.java は、oracle が公開しているサンプルコード(https://docs.oracle.com/javase/tutorial/uiswing/examples/layout/SpringGridProject/src/layout/SpringUtilities.java)とファイル名も含めてまったく同一だったからだ。
/*
* Copyright (c) 1995, 2008, Oracle and/or its affiliates. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of Oracle or the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package open.dolphin.helper;
import javax.swing.*;
import javax.swing.SpringLayout;
import java.awt.*;
/**
* A 1.4 file that provides utility methods for
* creating form- or grid-style layouts with SpringLayout.
* These utilities are used by several programs, such as
* SpringBox and SpringCompactGrid.
*/
public class SpringUtilities {
/**
* A debugging utility that prints to stdout the component's
* minimum, preferred, and maximum sizes.
*/
public static void printSizes(Component c) {
System.out.println("minimumSize = " + c.getMinimumSize());
System.out.println("preferredSize = " + c.getPreferredSize());
System.out.println("maximumSize = " + c.getMaximumSize());
}
けっこうあるもんですね。
今後
まず、これまでの「本家」を引き継いだメドレーだが、今後、開発・販売を再開する予定はないとしている。
一方、商用利用している組織は、今後も新機能を盛り込みながら、開発・サービスの提供を継続していくとのことである。
ライセンスに関しては、今後、なんらかの発表があるようである。
(適宜更新予定)
協力:猪股弘明
“OpenDolphin -wikipedia 風解説-” への7件の返信