#JavaScript

0 フォロワー · 13 投稿

JavaScriptはしばしばJSと省略される、高水準のインタープリター型プログラミング言語です。 これは、動的で、型付けの弱い、プロトタイプベースのマルチパラダイムとしても特徴付けられる言語です。 詳細はこちら。

記事 Toshihiko Minamoto · 5月 20, 2025 7m read

Django フレームワークは長年学習したいと思ってきましたが、いつも他の差し迫ったプロジェクトが優先されてきました。 多くの開発者と同様に、機械学習においては Python を使用していますが、初めてウェブプログラミングについて学習したころは、PHP がまだまだ優勢でした。そのため、機械学習の作品を公開する目的でウェブアプリケーションを作成するための新しい複雑なフレームワークを選択する機会が訪れても、私は依然として PHP に目を向けていました。 ウェブサイトの構築には Laravel と呼ばれるフレームワークを使用してきましたが、この PHP フレームワークから最新の MVC(モデルビューコントローラー)というウェブプログラミングのパターンに出会いました。 さらに複雑なことに、私は最新の JavaScript フレームワークを使用してフロントエンドを構築するのを好んでいます。 React を使用するのがより一般的のようですが、私は Vue.js に一番慣れているため、このプロジェクトではそれを使用することにしました。

なぜ複雑なフレームワークを使用するのでしょうか? Django、Laravel、React、または Vue などのフレームワークを学習する際の最大の難関は何でしょうか?

答えは人それぞれですが、私は MVC フレームワークがアプリの構造化に関するガイドを非常に多く提供してくれるため、気に入っています。毎回、作り直す必要がありません。 初めはこういったフレームワークは制約が多くて難解に思えるかもしれませんが、構造に慣れてしまえば、新しい機能をより追加しやすいと思います。

問題は、物事があまりにも単純になりすぎる可能性があることです。 Django のようなフレームワークは、よく知られた概念に基づいているかもしれませんが、Django では特に馴染みのない名前と構造を持つ多くの省略形や仮定に依存しています。 私のアプリケーションでは、Django は API とすべてのウェブルーティングを処理しています。 新しい API エンドポイントを追加する場合は、views.py のファイルに関数を追加してから、urls.py ファイルに移動して、その関数をインポートするステートメントと、API エンドポイントが提供されている URL を定義する別のステートメントを追加する必要があります。 その後で、データを取得してユーザーに表示するか操作するために、そのエンドポイントをクエリする JavaScript を使って、フロントエンドの Vue コンポーネントを編集する必要があります。

プロジェクトのセットアップが完了したら、このような機能の追加は迅速に行えます。 約 4 行のコードを追加するだけで、HTTP リクエストを処理し、必要なデータを JSON 形式で返すように、views.py ファイルの新しい関数に必要なロジックに集中できます。 難しいのは、それらのファイルが何であるか、そしてそれらがどのように連携してアプリケーション全体を作り上げるかを学ぶことです。

Django のようなフレームワークを学習するには、実際に動作する例を探して、データのフローを感じ取れる小さな変更を適用して見るのが最適な方法だと思います。 概念が明確になり始めて理解できるようになってきたら、ドキュメントを参考にしましょう。 AI モデルにコードを説明してもらい、様々な標準ファイルがフレームワークでどのように動作するかを尋ねましょう。 これらのツールが、長期的には時間を節約し、アプリケーションの保守と更新を容易にする方法として登場したことにすぐに気づくでしょう。 Django と Vue フレームワークには標準の構造があるため、後で戻ってきても、なぜ特定の方法でコーディングしたのかをすぐに理解でき、作業についての理解を再び深めやすくなっているでしょう。 また、アプリケーションの基本構造に慣れているため、他の人のアプリケーションを理解し、主な機能を把握するのもより簡単です。

では、これから始めようとしている人の支援となる Django の基礎とは何でしょうか? 私にとっては、最初に理解すべきことは、Django プロジェクトは Django の新規プロジェクトを作成するコマンドの実行によって 生成され、これによって構築を開始するために使用できる「基本プロジェクト」を構成する一連の基本ファイルとフォルダが生成されるということです。 プロジェクトフォルダには、プロジェクト全体に適用される設定を含むいくつかの Python ファイルがあります。 頻繫にアクセスする重要なフォルダは、すべての設定が含まれる settings.py と、urls.py です。 「Django はどのようにして静的ファイルを配置する場所を決定しているのか」といった疑問がある場合、その答えは通常 settings.py のどこかにあります。 アプリケーションに新しい URL を追加する場合は、urls.py ファイルを更新する必要があります。

これらのプロジェクトレベルのファイルと共に、プロジェクト内のアプリごとにフォルダを作成します。 これらのアプリは登録されている必要があります。つまり、 settings.py ファイルで名前を付ける必要があります。 プロジェクト内のメインのアプリフォルダはドキュメントと呼ばれます。 どのフォルダ内には、models.py ファイル、serializer.py ファイル、views.py ファイルがあります。 ファイルは他にもありますが、これらが重要な 3 つのファイルです。

models.py 内には、Document オブジェクトとそのフィールドを指定します。 Document オブジェクトに保管する予定の情報を保存するために必要なスキーマを使って IRIS データベースに Documents テーブルを作成するのは、Django に任せられます。 私の models.py ファイルでは、Documents には 255 文字以内の名前、大量のテキストであるコンテンツフィールド、ベクトルが補完されるデータベース名(別のテキストフィールド)、埋め込みタイプ(別のテキストフィールド)、および数値で表現されるベクトル埋め込みの次元が含まれることを指定しています。 これらの定義を使用することで、Fjango は必要な列タイプで必要なデータベーステーブルを作成します。 すると、データベースへのオブジェクトの保存は、Document.save() だけで完了です。

serializer.py ファイル内には、オブジェクトと JSON の変換方法に関する定義が含まれます。 基本的なユースケースでは、これを定義する標準的な方法があり、このプロジェクトで確認できます。

では、Django の核心である views.py ファイルを確認しましょう。 ここに、HTTP リクエストを受け取って、HTTP レスポンス全体、または JSON API の場合は JSON API レスポンスなどのデータを返す関数を定義します。 つまり、Django ではウェブページ全体を制作して、アプリのフロントエンドとしても使用することも、JSON データのみを提供して、フロントエンドを全く別のプラットフォームで構築することもできます。

最初は、一見恣意的なファイルや規則をすべて使用するのは面倒に感じるかもしれませんが、そうすることでアプリケーションが動作し始め、HTTP リクエストを処理して、レスポンスとして正しいデータを提供するようになることが分かれば、新しい機能を構築し続けるのが非常に楽しくなります。 HTTP リクエストを処理するオブジェクト、ウェブルート、および関数を 1 つ定義すれば、2 つ目、そして 3 つ目を簡単に定義してアプリケーションに機能を追加できるようになります。

私のプロジェクトは、github: https://github.com/grongierisc/iris-django-template にある @Guillaume Rongier が作成した Iris Django Template をフォークしたものです。

このテンプレートには Django のみが含まれており、Django フレームワークの学習に非常に役立ちました。私が行った主な追加項目の 1 つは、Tailwind CSS を使用した Vue.js の追加です。最新の JavaScript フレームワークをこのパッケージに統合して、IRIS で実行する単一ページのアプリケーションを作成できます。 単一ページのアプリケーションは、xhr リクエストを送信して JSON データを取得し、完全に再読み込みすることなく動的にページを更新する JavaScript アプリケーションです。 これには長所と短所がありますが、最新のウェブ開発の特徴です。

RAG と IRIS 上のベクトルストアの例としてだけでなく、Vue.js と Tailwind を使って IRIS 上に最新の柔軟なウェブアプリケーションを簡単に素早く作成する目的で Django を使用するためのテンプレートとして、私のプロジェクトを確認することをお勧めします。 リポジトリはこちらの GitHub にあります: https://github.com/mindfulcoder49/iris-django-template

ご質問があれば、ぜひお答えします。このプロジェクトを独自のユースケースに適合しようとするする際に問題が発生した場合は、私の洞察を提供いたします。

0
0 63
記事 Toshihiko Minamoto · 12月 10, 2024 9m read

コミュニティメンバーから、Python 2024 コンテストでの出品に対する非常に素晴らしいフィードバックが届きました。 ここで紹介させていただきます。

純粋な IRIS の 5 倍以上のサイズでコンテナーをビルドしているため、時間がかかっています

コンテナーの始動も時間はかかりますが、完了します

バックエンドは説明通りにアクセス可能です

プロダクションは稼動しています

フロントエンドは反応します

何を説明したいのかがよくわかりません

私以外のエキスパート向けに書かれた説明のようです

出品はこちら: https://openexchange.intersystems.com/package/IRIS-RAG-App

このようなフィードバックをいただけて、本当に感謝しています。プロジェクトに関する記事を書く素晴らしいきっかけとなりました。 このプロジェクトにはかなり包括的なドキュメントが含まれてはいますが、ベクトル埋め込み、RAG パイプライン、LLM テキスト生成のほか、Python や LLamaIndex などの人気の Python ライブラリに精通していることが前提です。

この記事は、IRIS での RAG ワークフローを実証するに当たって、上記の前提事項や、それらが IRIS で RAG ワークフローをこのプロジェクトにどのように適合するかについてを説明する試みです。AI をまったく使用せずに書かれています。

コンテナーが大きいのは、ベクトル埋め込みの作成に関わる Python パッケージに必要なライブラリ依存関係が非常に大きいためです。 より選択的にインポートすることで、サイズを大幅に縮小することが可能です。

コンテナーの初回ビルドには確かに時間がかかりますが、一度ビルドすれば起動時間は短くなります。 とはいえ、起動時間は確かに改善できるかもしれません。 起動にこれほどの時間がかかる主な理由は、アプリケーションのある個所が最後の起動から変更されていることを想定して entrypoint.sh が更新されているためです。これには、データベースの移行、CSS 構成、JavaScript 構成、Python バックエンドコードも含まれており、起動のたびにプロジェクト全体がリコンパイルされます。 これは、このプロジェクトを開発し始めやすくするためで、そうでない場合は、変更が適用されるたびに、フロントエンドとバックエンドのビルドを適切に実行するのが困難になってしまいます。 こうすることで、プロジェクトのコードを変更した場合はコンテナーを再起動し、場合によってはバックエンドのプロダクションを復旧すれば、アプリケーションのインターフェースと操作に変更が反映されます。

バックエンドのプロダクションは、HTTP リクエストを Django アプリケーションに渡すものであり、このパッケージの相互運用性にとって非常に重要であると確信しています。 ただし、私自身は IRIS プラットフォームの初心者であるため、プロダクションについてもっと学ぶ必要があります。

次に、ベクトル埋め込み、LLM、および RAG について、包括的に説明したいと思います。 この内最初に作られたのはベクトル埋め込みです。 まず、ベクトルについて説明します。 ほとんどのコンテキストにおいて、ベクトルは方向です。 空間のどこかを指す矢印です。 より正式には、ベクトルは「大きさだけでなく方向も持つ量」です。 これは、特定の方向へ移動し、空間内の特定の地点で爆発する花火によって例えることができます。 すべての花火が同じ中心点、つまり原点である [0,0,0] から発射され、その原点の周囲に雲となって飛び散るとします。 数学的には、3 つの座標系 [x,y,z] を使用して各花火の爆発の位置を表現することができ、これが花火の爆発の「ベクトル埋め込み」となります。 花火のビデオをたくさん撮影し、花火の爆発をすべてデータセットとして記録すると、花火の一種のベクトル埋め込みデータベース、つまりベクトルストアが作成されることになります。

花火に関する情報を使って何ができるでしょうか? 特定の花火を指して、花火全体の中から同じ点に最も近い位置で爆発した花火について尋ねると、空間の近くの点で爆発した他の花火を検索できます。 最もちかいものを見つけるだけですが、これを行うために数式があります。

花火ごとに、x、y、z の 3 つの数値のみを記録したことに注意してください。3 次元空間において、地上の花火発射台を [0,0,0] としています。

他の特定の花火に対して、距離と時間の両方の観点で最も近く爆発した花火も知りたい場合はどうでしょうか? それを知るには、花火の映像を確認して、各爆発の時間も記録しなければなりません。 これで、4 つの数値を持つ 4 次元ベクトルが取得されました。花火の爆発の 3 次元の位置に爆発の時間を加えたベクトルです。 ベクトル埋め込みにもう 1 つの次元を追加することで、花火の埋め込みがより記述的になりました。

これを機械学習に変換するとどうなるでしょうか? 手短に言えば、大量のテキストデータを処理することで、コンピューター科学者は、フレーズ、文章、段落、またはページなどのテキストを変換し、理論的な高次元空間の点を表現する非常に長い一連の数値に変換できる埋め込みモデルを作成することができました。

4 つの数字ではなく、300、700、さらには 1500 もの数字があります。 これらは、1 つのテキストが互いに「近い」か「遠い」かを 1500 通りまたは1500 次元の意味で表します。 テキストの意味を何らかの方法で表す数字を作成する手段があるというわけですから、多くの人にとって魅力的な概念と言えるでしょう。

数学を使用すると、これらの高次元テキストベクトル埋め込みのうち 2 つを比較して、同じモデルによって作成された場合に、それらが互いにどの程度類似しているか、つまり「近い」かを調べることができます。

このアプリで最初の行われているのが正にこれです。ユーザーはドキュメントを追加して名前を付け、埋め込みのタイプを選択する必要があります。 サーバーはそのドキュメントを受け取り、テキストのチャンクに分割してから、それぞれのチャンクをベクトル埋め込みに変換します。そのチャンクはそのドキュメントの専用のテーブルの行として保存されます。 各ドキュメントは、さまざまなテキスト埋め込みモデルによって作成されるベクトル埋め込みの可変長に対応できるように、それぞれの専用テーブルに保存されます。

ドキュメントがベクトル埋め込みとしてデータベースに保存されると、ユーザーはドキュメントに「尋ねる」クエリを入力できるようになります。 このクエリは 2 つの方法で使用されます。 1 つは、ドキュメントを検索するためです。 従来のテキスト検索は実行せずに、「ベクトル検索」を実行します。 アプリはクエリを受け取り、それをベクトル埋め込みに変換してから、クエリベクトル埋め込みに最も築地する埋め込みのあるドキュメントのセクションを検索します。 各ドキュメントセクションには 0 と 1 の間の類似性スコアが生成され、top_k_similarity と similarity_threshold に基づいて、ベクトルデータベースから複数のセクションが検索されます。 基本的に、取得するドキュメントのセクション数と取得の対象となるためにクエリとどの程度類似している必要があるかを指定することができます。

これが、検索拡張生成における取得です。 次は生成に移りましょう。

コンピューター科学者がテキストを意味的に重要な数値ベクトル埋め込みに変換する方法を見つけると、次に、テキストを生成するモデルの作成に移りました。 これは大きな成功を生み出し、現在では GPT-4、LLama3、Claude 3.5 などの大規模言語モデルとなっています。 これらの LLM はプロンプトまたはクエリを受け取り、補完または回答を提供します。これは LLM が提示されたテキストであるプロンプトから最も続行できる可能性があると考えるテキストです。

LLM は大量のテキストデータに対してトレーニングする必要があり、その回答または補完はそのトレーニングデータに制限されます。 トレーニングセットにないデータを含む可能性のある補完を LLM に提供させる場合、または補完を特定のナレッジセットに基づかせる場合は、1 つの方法として、プロンプトに追加のコンテキストデータを含めることができます。 基本的に、トレーニングされていない内容について LLM から回答を得たい場合、プロンプトに情報を提供する必要があるということです。

多くの人は、ChatGPT やローカルの LLama インストールが自分の個人文書に基づいて回答を提供してくれることを望む状況に陥っていました。 ドキュメント内でその情報を検索し、プロンプトに貼り付けて、質問を入力するだけの単純な操作であり、手作業で行っていました。 それ自体が検索拡張生成です。 RAG は、より正確または利便的な応答を得られるように、ユーザークエリに関連する情報を検索し、LLM にクエリを提供する操作を自動化したに過ぎません。

このアプリでは、ベクトル検索で取得したドキュメントセクションは、インターフェースでモデルとしてラベル付けされている選択された LLM にクエリとともに送信され、回答のコンテキストが提供されます。

このプロジェクト用に制作した動画の例では、シェイクスピアの 2 つの戯曲の全文を含むドキュメント「ハムレット」と「リア王」を使って、「この戯曲の悪役は誰ですか?」と尋ねています。 IRIS データベースには、ハムレットとリア王の 2 つのテーブルがすでに存在します。 各テーブルには、各戯曲のテキストをセクションに分割して作成されたベクトル埋め込みの行が入力されています。 これらの埋め込みは、一連の長い数値によって各ドキュメントセクションの多次元を表現しています。

サーバーは、「この戯曲の悪役は誰ですか」という質問を、リア王のベクトル埋め込みを生成した Text-to-Vector モデルを使用して数値ベクトルに変換し、リア王テーブル内でそれに最も類似するセクションを見つけます。 これらはおそらく悪役という語が言及されたセクションかもしれませんが、悪役が明示的に言及されていない場合でも、裏切り、裏切り、欺瞞などの他の悪役についても言及されている可能性があります。 こういったドキュメントのセクションは、クエリに追加され、合わせてプロンプトとして LLM に送信されます。LLM は提供されたドキュメントのセクションに基づいて質問に回答します。

これはドキュメントごとに個別に実行されるため、クエリの回答はクエリされているドキュメントに応じて異なります。 これにより頭字語が補完されます。ベクトル検索の力を使用して関連するコンテキスト情報を取得することで、LLM からの応答の生成を強化しているためです。

この記事をお読みいただきありがとうございました。このトピックについては今後の記事でも発展させたいと思います。 フィードバックをお待ちしています。

0
0 82
記事 Toshihiko Minamoto · 11月 15, 2023 9m read

前の記事 - AI による臨床文書の保管、取得、検索の単純化

この記事では、AI を使用した文字起こしと要約によってヘルスケアに変革を起こす OpenAI の高度な言語モデルの可能性を探ります。 OpenAPI の最先端 API を活用して、録音データを文字起こしし、自然言語処理アルゴリズムを使って簡潔な要約を生成するための重要なインサイトを抽出するプロセスを掘り下げていきます。

似たような機能は Amazon Medical Transcibe や Medvoice などの既存のソリューションでも提供されていますが、この記事では、OpenAI テクノロジーを使用してこれらの強力な機能を InterSystems FHIR に実装することに焦点を当てています。

Vue.js の録音データ

Vue.js アプリのボイスレコーダーは、完全にネイティブであり、Mediarecorder インターフェースを使って JavaScript で記述されています。 これは、アプリケーションを軽量に維持しながら、録音オプションを完全に制御できるようにすることを目的としています。 以下は、録音入力の開始と停止を行うスニペットです。

// オーディオストリームをチャンクとして保存する録音開始メソッドasync startRecording() {
      try {
        const stream = await navigator.mediaDevices.getUserMedia({
          audio: true,
        });
        this.mediaRecorder = new MediaRecorder(stream);
        this.mediaRecorder.start();

        this.mediaRecorder.ondataavailable = (event) => {
          this.chunks.push(event.data);
        };
        this.isRecording = true;
      } catch (error) {
        console.error("Error starting recording:", error);
      }
}

// 停止後にブロブを作成する(そして転写メソッドを呼び出す)録画停止メソッド
stopRecording() {
      if (this.mediaRecorder) {
        this.isLoading = true;
        this.mediaRecorder.stop();
        this.mediaRecorder.onstop = async () => {
          const blob = new Blob(this.chunks, {
            type: "audio/webm;codecs=opus",
          });
          awaitthis.sendAudioToWhisper(
            new File([blob], `file${Date.now()}.m4a`)
          );

          this.getSummary(this.transcription);
        };
      }
}

文字起こしコンポーネント

OpenAI の Whisper モデルを使った音声データの文字起こしには、いくつかの基本コンポーネントが使用されます。 以下のコードスニペットは、文字起こしプロセスに関わるステップを示します。

const apiKey = process.env.OPENAI_API_KEY;

const formData = new FormData();
formData.append("file", blob);
formData.append("model", "whisper-1");
formData.append("response_format", "json");
formData.append("temperature", "0");
formData.append("language", "en");

try {
  const response = await fetch(
    "https://api.openai.com/v1/audio/transcriptions",
    {
      method: "POST",
      headers: {
        Accept: "application/json",
        Authorization: `Bearer ${apiKey}`,
      },
      body: formData,
      redirect: "follow",
    }
  );

  const data = await response.json();
  if (data.text) {
    this.transcription = data.text;
  }
} catch (error) {
  console.error("Error sending audio to Whisper API:", error);
}

returnthis.transcription;
  1. API キー - OPENAI_API_KEY は、OpenAI API にアクセスするために必要な認証トークンです。
  2. フォームデータ - 文字起こしされる音声ファイルは、FormData オブジェクトに追加されます。 選択されたモデル(whisper-1)、レスポンス形式(json)、体温(``)、および言語(en)などの追加パラメーターも含まれています。
  3. API リクエスト - OpenAI API エンドポイント https://api.openai.com/v1/audio/transcriptions への POST リクエストには、ヘッダーとフォームデータを含むボディを指定して、fetch メソッドで送信されています。
  4. レスポンス処理 - API からのレスポンスがキャプチャされ、文字起こしされたテキストが data オブジェクトから抽出されます。 文字起こしを変数 this.transcription に割り当てて、さらに処理するか使用することができます。

要約コンポーネント

以下のコードスニペットは、OpenAI の text-davinci-003 モデルを使用したテキスト要約プロセスに関わる基本コンポーネントを示しています。

response = openai.Completion.create(
    model="text-davinci-003",
    prompt="Summarize the following text and give title and summary in json format. \
            Sample output - {\"title\": \"some-title\", \"summary\": \"some-summary\"}.\
            Input - "
    + text,
    temperature=1,
    max_tokens=300,
    top_p=1,
    frequency_penalty=0,
    presence_penalty=1
)

return response["choices"][0]["text"].replace('\n', '')
  1. モデルの選択 - model パラメーターは text-davinci-003 に設定されており、OpenAI のテキスト補完モデルが要約に使用されていることを示します。
  2. プロンプト - モデルに提供されるプロンプトは、望ましい結果を指定しています。これは入力テキストを要約して JSON 形式でタイトルと要約を返します。 入力テキストは、プロンプトに連結して処理されます。 OpenAI を通じてレスポンス変換を処理できるところに興味深いポイントがあります。 受信側での検証のみで十分であり、将来的にはコンバーターがほとんど必要なくなる可能性があります。
  3. 生成パラメーター - 生成された要約の動作と品質を制御するために、temperaturemax_tokenstop_pfrequency_penaltypresence_penalty などのパラメーターが設定されます。
  4. API リクエストとレスポンスの処理 - API リクエストを行うために、openai.Completion.create() メソッドが呼び出されます。 レスポンスがキャプチャされ、生成された要約テキストがレスポンスオブジェクトから抽出されます。 要約テキストに含まれる改行文字(\n)は、最終結果を返す前に取り除かれます。

FHIR のドキュメント参照

OpenAI テクノロジーを使用して医師と患者の会話の文字起こしと要約を実装する文脈においては、FHIR 規格内での診療記録の保管を考慮することが重要です。 FHIR は、診療記録などの医療情報を様々な医療システムやアプリケーション間で交換するための構造化された標準アプローチです。 FHIR の DocumentReference リソースは、診療記録や関連文書を保管するための専用のセクションとして機能します。

文字起こしと要約機能を医師と患者の会話ワークフローに統合する場合、生成される文字起こしと要約は、FHIR Documents リソース内の診療記録として保管できます。 これにより、生成されたインサイトへのアクセス、取得、およびヘルスケアプロバイダーやその他の承認機関の間での共有を簡単に行えます。

{
    "resourceType": "Bundle",
    "id": "1a3a6eac-182e-11ee-9901-0242ac170002",
    "type": "searchset",
    "timestamp": "2023-07-01T16:34:36Z",
    "total": 1,
    "link": [
        {
            "relation": "self",
            "url": "http://localhost:52773/fhir/r4/Patient/1/DocumentReference"
        }
    ],
    "entry": [
        {
            "fullUrl": "http://localhost:52773/fhir/r4/DocumentReference/1921",
            "resource": {
                "resourceType": "DocumentReference",
                "author": [
                    {
                        "reference": "Practitioner/3"
                    }
                ],
                "subject": {
                    "reference": "Patient/1"
                },
                "status": "current",
                "content": [
                    {
                        "attachment": {
                            "contentType": "application/json",
                            "data": ""
                        }
                    }
                ],
                "id": "1921",
                "meta": {
                    "lastUpdated": "2023-07-01T16:34:33Z",
                    "versionId": "1"
                }
            },
            "search": {
                "mode": "match"
            }
        }
    ]
}

試してみましょう

  1. プロジェクトをクローン - 次の GitHub リンクからプロジェクトリポジトリをクローンします: https://github.com/ikram-shah/iris-fhir-transcribe-summarize-export
  2. ローカルでセットアップ - 提供された指示に従って、プロジェクトをローカルマシン上にセットアップします。 セットアップ中に問題が発生した場合は、お知らせください。
  3. 患者を選択 - プロジェクト内の提供されたサンプルリストから患者を選択します。 この患者は、文字起こしと要約に使用される医師と患者の会話と関連付けられます。
  4. 対話ページ - 患者が選択されたら、プロジェクト内の対話ページに移動します。 このページで、「Take Notes」オプションを見つけてクリックし、医師と患者の会話の文字起こしプロセスを開始します。
  5. 文字起こしの表示と編集 - 文字起こしプロセスが完了したら、生成された文字起こしを表示するオプションが表示されます。 さらに整理してわかりやすくするために、文字起こしに関連付けられたタイトルと要約を編集することもできます。
  6. FHIR DocumentReference に保存 - タイトルと要約の処理が完了し、変更を保存すると、FHIR DocumentReference 内に自動的に保管されます。 これにより、関連する診療記録がキャプチャされ、それぞれの患者の記録に確実に関連付けられます。 現時点では、このプロジェクトは文字起こしテキスト全体を保存しませんが、 完全な文字起こしの補完を含めるように変更することもできます。

デモ

アプリケーション全体のデモはこちらでご覧ください: https://youtu.be/3Th6bR4rw0w

今後の方向性

AI を活用した文字起こしと要約の応用を遠隔医療通信に拡大することには、計り知れない可能性が秘められています。 これらの機能を Zoom、Teams、Google Meet などの一般的な会議プラットフォームに統合することで、医師と患者のリモート対話を合理化できる可能性があります。 遠隔医療セッションの自動文字起こしと要約機能には、正確な文書作成や分析の強化といったメリットがあります。 ただし、データプライバシーが重大な課題として残されます。 これに対応するためには、外部サーバーにデータを送信する前に、個人を特定できる情報(PII)をフィルターまたは匿名化する対策を実装する必要があります。

今後の方向性としては、ローカルで処理するためのオンデバイス AI モデルの調査、多言語コミュニケーションのサポートの改善、プライバシー維持した手法の進歩が挙げられます。

有用な実装だと思われた方は、Grand Prix 2023 でこのアプリに投票してください。

0
1 337
記事 Toshihiko Minamoto · 4月 7, 2022 4m read

皆さん、こんにちは!

@Henrique.GonçalvesDias と私は、MessageViewer の最近のアップデートにおいて、IRIS Interoperability でメッセージを可視化する新しい方法を提案しました。 そのアップデートでは、UML シーケンス図に基づく可視化を提供しようとしました。 詳細は、の記事をご覧ください。

このようなダイアグラムを描画するために必要な困難な幾何学計算を行えるように、素晴らしい mermaid JS オープンソースライブラリを使用しました。 この記事で紹介したいのは、このライブラリの使用方法です。 ここでは、シーケンス図のみに焦点を当てますが、このライブラリでは非常にたくさんのことを行えることを忘れないでください。

mermaid では Markdown に着想を得た構文を使用して、ダイアグラムを定義します。 非常に直感的であるため、退屈な文章を長々と書くのではなく、例を紹介したいと思います。

sequenceDiagram
Alice->>John: Hello John, how are you?loopHealthcheck    John->>John: Fight against hypochondriaendNoterightofJohn: Rational thoughts!John-->>Alice: Great!John->>Bob: How about you?

mermaid エンジンはこの定義に基づいて、SVG を使って直接 Web ページに以下のダイアグラムをレンダリングします。

この例は mermaid のドキュメントから得たもので、このオンラインエディターで試すことができます。 色々試すことのできる構成がたくさんあります。

ご覧の通り、ダイアグラムの定義では、アクターと参加者、相互に送信しているイベントとメッセージを指定するだけです。

また、Web ページにダイアグラムを表示するには、div コンテナにダイアグラムを指定し、mermaid エンジンを初期化してダイアグラムをレンダリングする JS コードのみが必要です。 

<divclass="mermaid">
sequenceDiagramAlice->>John: Hello John, how are you?loopHealthcheck    John->>John: Fight against hypochondriaendNoterightofJohn: Rational thoughts!John-->>Alice: Great!John->>Bob: How about you?</div>
mermaid.initialize({
    startOnLoad:true,    theme:'forest'});

この例は、こちらのフィドルにあります。

これは、提案された作業のフロントエンドベースです。 バックエンドでは、IRIS 相互運用性セッションからメッセージを取得し、適切な JSON オブジェクトにフォーマットしてフロントエンドに送り返す REST エンドポイントを設定する必要があります。 この記事の焦点はフロントエンドのコードであるため、バックエンドの実装には触れませんが、dispatchservice クラスでこれを確認できます。

バックエンドは、以下のような JSON を送り返します。

{
    "participants":[        "diashenrique.messageviewer.Service.SendMessage",        "diashenrique.messageviewer.Operation.ConsumeMessageClass"    ],    "messages":[        {            "id":"1182",            "from":"diashenrique.messageviewer.Service.SendMessage",            "to":"diashenrique.messageviewer.Operation.ConsumeMessageClass",            "message":"2021-10-05 03:16:56.059 SimpleMessage"        },        {            "id":"1183",            "from":"diashenrique.messageviewer.Operation.ConsumeMessageClass",            "to":"diashenrique.messageviewer.Service.SendMessage",            "message":"2021-10-05 03:16:56.06 NULL"        }    ]}

最後に、簡単な JS 関数を使用して、この JSON を mermaid シーケンス図の仕様に合わせて以下のように変換します。

sequenceDiagram
autonumberparticipantP0asdiashenrique.messageviewer.Service.SendMessageparticipantP1asdiashenrique.messageviewer.Operation.ConsumeMessageClassP0->>P1: 2021-10-05 03:16:56.059 SimpleMessageP1->>P0: 2021-10-05 03:16:56.06 NULL

そして、これがレンダリングされたシーケンス図です。

完全な JS コードはこちらで確認できます。

以上です。 この記事があなたの素晴らしいプロジェクトにいくらかでも役立てられれば幸いです。

それではまた!

0
0 1094
記事 Toshihiko Minamoto · 2月 9, 2022 2m read

これは、前回の「DockerマイクロサーバーとしてのIRIS Native APIを使用するWebSocketクライアントJS」のフォローアップです。

すべてのピースが1つのDockerイメージにまとめられたため、インストールがはるかに簡単になりました。
作業が楽になります。 ただしもちろん、マイクロサービスの原則はわかりにくくなくなっています。
オールインワンのバンドルパッケージであるため、 コンパクトになっています。

動作には変更がないため、 下位互換性のあるマイナーリリースと解釈されてしまう可能性があります。

これには、intersystems/iris-community:2020.2.0.204.0が含まれています。

実行するのは、以下の通りです。

  • docker pull rcemper/rcc:iris-nodejs-compact(1回)
  • docker run --rm --init -d \ --name=iris1 -p 52773:52773 -p 51773:51773 \ rcemper/rcc:iris-nodejs-compact (コンテナーを起動)
  • docker exec -it iris1 bash wsgo.sh (マイクロサービスを起動)
  • docker exec -it iris1 bash wsdemo.sh (テストデータを作成する制御を開始し、echoServerに送信して受信)
  • docker exec -it iris1 bash wsstop.sh (制御から実行されない場合は、最終的にサービスを停止)
  • docker stop iris1 (コンテナーを終了)

それだけです。

2021年7月7日:
正規化されたV2がリリースされました。

  • git clone https://github.com/rcemper/IRIS-NativeAPI-Nodejs-compact.git
  • docker-compose up -d
  • docker-compose exec iris iris session iris %ZSocket
  • docker-compose exec iris node WsockIris.js
0
0 280
記事 Toshihiko Minamoto · 2月 1, 2022 5m read
これはIRIS 2020.2で動作するコーディングの例です 
最新バージョンとは同期していません。
また、InterSystemsのサポートによるサービスはありません

動作中のデモを確認できるデモビデオを以下で公開しています。https://youtu.be/dSV-0RJ5Olg

皆さんこんにちは
完全に新しいIRISイメージと**たった4行**のDockerコマンドを使って実行するイメージを使ってマイクロサービスのデモを行いましょう。 
2020年6月1日 - rcc

すべてのパーツを1つのコンテナイメージにまとめたコンパクトなオールインワンバージョンが公開されました。
詳細はこちら: IRIS-NativeAPI-Nodejs-compact
2020年5月24日 - rcc

Dockerを使った簡易インストールを追加しました。コンテキストを参照
2020年5月25日 - rcc

Linux & Windowsに最適な検証済みの強化スクリプトはこちら
https://github.com/rcemper/WSockClientMicroSV/blob/master/READMEwindows.MD
2020年5月26日 - rcc

このデモは、Caché用にすでに存在するNode.jsに基づくWebSocketクライアントを再設計したものです。 主に以下のような変更点があります。

  • 新しいIRIS Native API for Node.jsの使用。特にグローバル配列を操作する場合
  • 直接トリガーされたクライアントからサーバー設計への変更
  • マイクロサービス/マイクロサーバーの例として、結果を別のdockerイメージに配置
  • マイクロサービスの実行を制御するための単純なインターフェースをIRISに追加

ユーティリティをIRISホストで直接呼び出す代わりに、System Interoperability(別名 ENSEMBLE)で通常行うようにして、work-packagesをMicroServiceに送信します。
もちろん、2つ以上のWebSocketサーバーオプションがあります。
WebSocketクライアントサービスがジョブを完了したら、その結果を取得します。

組み込みのWebSocketクライアントに対するメリットは、すべてのネットワーク、セキュリティ、ファイアウォールの問題を主要なデータサーバーに寄せ付けないことです。 Node.js がこの分野で持っている経験と品質については言うまでもありません。

デモは wss: デフォルトのechoサーバーとして使用します。
次に、テキストを数行入力します。
独自のテキストの任意の場所に「Lorem Ipsum」を追加して、コンテンツを長くすると良いでしょう。
そして、それをサービスに送信して、エコーを待ちます。
また、制御プログラムをExitまたはサービスをStopする前に、
テキストを変更するオプションもあります。

この処理はすべて非同期で実行されます。
完了を待たずに、それまでにechoサーバーから受信した内容を、リスナーが
定期的に表示します。

インストールするには、以下が必要です。

  • IRISのdocker イメージ(intersystems/iris-community:2020.2.0.199.0)

  • WebSocketマイクロサーバーのdockerイメージ(docker pull rcemper/rcc:demoJS

  • 簡略化されたinit: initに次のdockerコマンドを実行してください。 docker run --name ini1 --init -it --rm \ -privileged -v $(pwd):/external \ rcemper/rcc:demoJS bash /rcc/init.sh ### 元のアプローチは有効ではありますが、必須ではありません。 ###

  • IRIS-Docker-micro-Durabilityを利用するためにOpen Exchangeまたはここから入手したWSockClientMicroSV.tar.gz

  • demoディレクトリの確認: Dockerイメージはnobodyであるため、保護をrwx (chmod 777) に設定します。###

これを実行するには、まずdemoディレクトリからIRISを起動します(-dまたは-itを実行して動作を観察します)。(!)

docker run --name iris1 --init --rm -d \
-p 52773:52773 -p 51773:51773 \
-v $(pwd):/external \
intersystems/iris-community:2020.2.0.199.0 \
-b /external/pre.copy

次にMicroServerを起動します。

docker run --name rcc1 --init -it --rm \
rcemper/rcc:demoJS /usr/bin/node /rcc/nodejs/WSockIris.js $(hostname -I)

. -itを使って起動したため、以下が表示されます。

platform = linux: ubuntu  

    *****************************  
    Connect to IRIS on: 192.168.0.23  
Successfully connected to InterSystems IRIS.  
    *** wait 3sec for request ***  
    ******* Startup done ********  

    *** wait 3sec for request ***  
    *** wait 3sec for request ***  

次に、新しいLinuxターミナルで制御アプリケーションを実行します。

docker exec -it iris1 iris session iris ZSocket  

以下が表示されます。

*** Welcome to WebSocket Micoservice demo ***  
Known Hosts (*=Exit) [1]:  
1  wss://echo.websocket.org/  
2  --- server 2 ----  
3  --- server 3 ----  
select (1):  ==> wss://echo.websocket.org/  
#
Enter text to get echoed from WebSocketClient Service
Terminate with * at first position
or get generated text by %
or append new text with @

1    hello socket microServer
2    now you got 2 lines
3    *

Select action for WebClient Service
New EchoServer (E), Send+Listen(S),New Text(N),Exit(X), Exit+Stop Client(Z) [S]s
%%%%%%%%%%%%%%%%%%%%%%%%%%

******* 0 Replies *******

******* 2 Replies *******
1    hello socket microServer
2    now you got 2 lines


Select action for WebClient Service

マイクロサービスでは以下が表示されます。

*** wait 3sec for request ***
echoserver:  wss://echo.websocket.org/
** Lines to process: 1 **
********* next turn *********
* WebSocket Client connected *
****** Client is ready ****** 

Line: 1 text> 'hello socket microServer '
Received: 1 > 'hello socket microServer '

Line: 2 text> 'now you got 2 lines '
Received: 2 > 'now you got 2 lines '

******* lines sent: 2 ******
*** replies received: 2 ****

*** wait 3sec for request ***

事後警告。 イメージのバージョンを必ず確認してください!
私はntersystems/iris-community:2020.2.0.
204にハマってしまいました。

注意: すべてのスクリプトはLinuxでのみテストされています!

0
0 313
記事 Toshihiko Minamoto · 6月 8, 2021 17m read

以前、WRCケースのエスカレーションを受けました。お客様は、Cachéに、rawDEFLATE圧縮/解凍機能が組み込まれているかを尋ねていました。

DEFLATEについて話すには、Zlibについても話す必要があります。Zlibは、90年代半ばに開発された無料の圧縮/解凍ライブラリで、、デファクトスタンダードとなっているからです。

Zlibは特定のDEFLATE圧縮/解凍アルゴリズムと、ラッパー(gzip、zlibなど)内でのカプセル化するという考えの下で動作します。
https://en.wikipedia.org/wiki/Zlib

Caché Object Script(COS)ではすでにGZIPがサポートされており、gzipファイルを使用するために、ファイルデバイスまたはtcpデバイス、またはStreamclassで/GZIP=1を使用できるようになっています。
http://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY=GOBJ_propstream_gzip

「CSP-Gateway/Web-Gateway」Webサーバーモジュールでも、Caché-Serverから受信するhttp-data送信をGZIP圧縮/解凍するために、Zlibライブラリが使用されています。 (CSP、Zen、SOAP、RESTなど)

しかし、GZIP形式には、DEFLATEで圧縮された生の本文にラップされた追加のヘッダーとトレーラーが含まれていました。

お客様望んでいるのはこれではありません。 お客様のユースケースでは、DEFLATEで圧縮された生のコンテンツの作成・解凍のみを行う必要がありました。

これはZlibライブラリではサポートされていますが、Caché API/関数内からは現在公開されていません。

追加するにはどうすればいいでしょうか?

「どうにかしてZlibライブラリにアクセスする必要があります。」

コールアウトを使って、Caché内からZlibを利用できるようにすることはできないだろうか?

「はい、可能です。」

Cachéのコールアウトを使うと、C/C++呼び出し規則をサポートするほかの言語で書かれたほかのライブラリ(WindowsのDLL、UnixのSO)から、実行可能ファイル、オペレーティングシステムのコマンド、または関数を呼び出すことができます。

Cachéコールアウトは、$ZF関数で提供されています。 http://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY=BGCLをご覧ください。

たとえば、オペレーティングシステムのコマンドを発行する場合、$ZF(-1) と$ZF(-2) 関数を使用できます。

$ZF(-1, command) はプログラムまたはオペレーティングシステムのコマンドを、生成された子プロセスとして実行し、その子プロセスがexitステータスを返すまで、現在のプロセスの実行を一時的に停止しますが、$ZF(-2, command) は、非同期的に動作するため、生成された子プロセスが完了するのを待ちません。そのためそのプロセスから直接ステータス情報を受信することができません。

もう1つの方法は、オペレーティングシステムレベルと同様に、コマンド・パイプを使用してプロセスと通信する方法です。 ここでは、パイプを介して出力を送信してプロセスを制御し、パイプを読み取って入力を受信し、プロセス出力を取得することができます。
http://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY=GIOD_ipc_pipes

注意: 今後、$ZFとパイプを使ったCachéコールアウトの仕組みのサポートを終了し、より安全なコールアウト方法に置き換える予定です。 最新情報をお楽しみに。

私はWeb専門のエンジニアなので、JavaScriptを好んで使用しています。 しかし、皆さんもよくご存知のとおり、WebページのコンテキストにおいてWebブラウザでクライアントがJavaScriptを実行するのではなく、サーバーで実行するための何かが必要です。

一般的に使用されている人気の高いJavaScriptサーバーのランタイム環境/エンジンは、Node.jsです。
これは、ChromeのV8 JavaScriptエンジンを基礎に構築された、コミュニティが主導するJavaScriptランタイム環境です。 Node.jsは、イベント駆動型のノンブロッキング非同期I/Oモデルを使用しており、軽量で非常に効率的です。
https://nodejs.org/en/

幸いなことに、Node.jsにはzlibモジュールが含まれており、私たちの計画に最適です。 https://nodejs.org/api/zlib.html

CachéもNode.jsをサポートしていますが、わずかに異なります。 強力なcache.nodeコネクタ/インターフェースが備わっているため、Caché内のデータとメソッドをNode.jsから簡単に利用することができます。 http://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY=BXJS

この特定のユースケースと要件の場合、私たちが求めているものはこのことではありません

Node.jsを介して純粋なJavaScriptを実行し、Cachéに結果を返す必要があるからです。
つまり、これは逆方向になります。

前述のコマンドパイプのコールアウトの仕組みが適しているようです。

Node.jsをダウンロードしてインストールしたら、このプランが機能するかどうかを試してみましょう。

USER>set cmd="node -v",oldIO=$IO open cmd:"QR" use cmd read result close prog use oldIO
USER>w result
v8.9.1

このテストでわかるように、期待どおりに動作しています。 「node-v」コマンドによって、現在インストールされているNode.jsのランタイム環境に関するバージョン情報が返されています。

「できました!」

ここで、指定されたコマンドラインの引数から、zlibモジュールと生のDEFLATE/INFLATEアルゴリズムを使って、ファイルのコンテンツを圧縮/解凍するノードスクリプトをコーディングしましょう。

これは簡単に行えます。 次のコードを使って、プロジェクトフォルダに_zlib.js_を作成します。

//zlib.js
Const
  func = process.argv[2],
  infile = process.argv[3],
  outfile = process.argv[4];

  const zlib = require('zlib');
  const fs = require('fs');

  if (func=='DEFLATERAW') {
    var wobj = zlib.createDeflateRaw();
  }
  else  {
    var wobj = zlib.createInflateRaw();
  }

  const instream = fs.createReadStream(infile);
  const outstream = fs.createWriteStream(outfile);

  instream.pipe(wobj).pipe(outstream);

  console.log(func + ' ' + infile + ' -> ' + outfile);

このスクリプトは、次のようなコマンドを使ってOSコンソールから実行し、生のDEFLATEを使用して、既存のinput.txtファイルをoutput.zzに圧縮できます。

C:\projects\zlib>node zlib.js DEFLATERAW input.txt output.zz
DEFLATERAW input.txt -> output.zz

注意: 便宜上、このコードは、ノードスクリプトが実行しているフォルダ(c:\projects\zlibなど)にあるファイルの圧縮/解凍のみをサポートしています。 そのため、少なくともinput.txtファイルをこの場所に作成するかコピーするようにしてください。
 

最初に、スクリプトコードは、「zlib」(Zlibライブラリノード)と「fs」(ファイルアクセス/操作用のファイルシステムノード)モジュールの機能を使用するために、これらを配置しています。

その後で、_process.argv_を使って、受信するコマンドライン引数にアクセスしています。
_argv_は「引数ベクトル(argument vector)」の略で、最初の2つの要素である「node」とスクリプトファイルへのフルパスを含む配列です。 3つ目の要素(インデックス2)は「関数/アルゴリズム名」、4つ目と5つ目の要素(インデックス3と4)は、入力ファイルの引数「infile」と出力ファイルの引数「outfile」です。

最後に、パイプ処理を使用して、入力ファイルストリームと出力ファイルストリームの両方に適切なzlibメソッドを使用しています。

関数の結果を戻すには、コンソールで結果メッセージを出力するだけです。

「以上です。」
 

これがCachéから動作するか試してみましょう。

USER>set cmd="node c:\projects\zlib\zlib.js DEFLATERAW input.txt output.zz",oldIO=$IO open cmd:"QR" use cmd read result close cmd use oldIO
USER>w result
DEFLATERAW input.txt -> output.zz

「期待どおりに動作しました。」
 

次のコマンドを使用して、圧縮済みのoutput.zzファイルをoutput.txtに解凍(inflate)することができます。

USER>Set cmd="node c:\projects\zlib\zlib.js INFLATERAW output.zz output.txt",...

こうすると、output.txtファイルのコンテンツとサイズは、input.txtファイルと全く同じになります。

「問題は解決しました。」

コマンドパイプを使用して、ノードスクリプトへのコールアウトにより、Cachéでファイルの生のDEFLATE圧縮/解凍を利用できるようになりました。

しかし、パフォーマンスの観点から、考慮すべきことがあります。コールアウトの仕組みには、コールアウトのたびに新しい子プロセスが起動されるというオーバーヘッドが伴います。

パフォーマンスが重要でない場合、または完了すべき処理作業が時間のかかるものである場合、ファイルサイズが大きくなっても圧縮と解凍は問題はないでしょうし、プロセスの開始にかかる時間のオーバーヘッドも無視できます。 しかし、多数の比較的小さなファイルを次々と集中して圧縮/解凍するのであれば、このオーバーヘッドは回避した方が良いと言えます。

では、どのようにして回避すればよいのでしょうか。

コールアウトが行われるたびに新しい子プロセスが作成されないようにする必要があります。

「どうすればよいですか?」

受信するリクエストをリスンし、要求どおりに目的の操作を行うサーバーとして実行するように、スクリプトを準備する必要があります。

もっともらしく聞き覚えのある方法でしょうか。そうです。これが現在、RESTful HTTP API/サービスで行えるとされている方法です。

Node.jsであれば、HTTPプロトコルに基づく単純なサーバーを非常に簡単に作成できます。

Node.jsには、組み込みの「http」モジュールを使用して、すぐに使用できるオーバーヘッドの低いHTTPサーバーがサポートされています。

「http」モジュールを含めるには、以下に示されるように、_simple_https.js_スクリプトファイルに、通常どおりノードのrequire() メソッドを使用します。

//simple_https.js
const
  http = require('http'),
  server = http.createServer(function (request, response) {
    response.writeHead(200, {'Content-Type' : 'text/plain'});
    response.end('Hello World!\n');
  });
server.listen(3000, function(){
    console.log('ready captain!');
});

この単純なhttpサーバーをOSコンソールから起動するには、次のコマンドを使用します。

C:\projects\zlib>node simple_http.js
ready captain!

ここでは、「curl」を使って、これをテストしています。curlは一般的に使用される便利なコマンドラインツールで、HTTPリクエストを特定のサーバーに発行します。
https://curl.haxx.se/

「-i」フラグを追加して、レスポンス本文のほかにHTTPヘッダーを出力する必要があるということを、curlに指示しています。

C:\curl>curl -i http://localhost:3000
HTTP/1.1 200 OK
Content-Type: text/plain
Date: Mon, 22 Jan 2018 13:07:06 GMT
Connection: keep-alive
Transfer-Encoding: chunked

Hello World!

これはうまく動作しますが、低レベルの「http」モジュールに対してhttpサービスを直接記述するのは面倒であり、時間のかかる作業です。

Node.jsには活発に活動しているオープンソースコミュニティがあり、Node.jsアプリケーションに機能を追加できる優れたモジュールがたくさん作成されているため、このケースのRESTful APIを開発するには「Express」を使用することにします。

Express.js」または単に「Express」とも呼ばれるモジュールは、WebアプリとAPIを構築するために設計された、Node.js用のWebアプリケーションフレームワークです。

無ければ結局は自分で書くことになるのですが、その手間を省くことのできる多数の配管コードが提供されています。 URLパスに基づいて受信リクエストをルーティングしたり、受信データを解析したり、不正な形式のリクエストを拒否したりなどすることができます。

Expressフレームワークを使えば、こういったタスクやその他無数のタスクを実現できるようになります。 Node.jsの標準的なサーバーフレームワークであるのも、当然でしょう。
http://expressjs.com/
 

すべてのNodeモジュールと同様に、「Express」を使用するには、まずnpm(ノードパッケージマネージャー)を使ってインストールしなければ、使うことはできません。

C:\projects\zlib>node install express
...

「express」とそのほかに必要なモジュールを含めるには、以下に示されるように、_zlibserver.js_スクリプトファイルに、通常どおりノードのrequire() メソッドを使用します。

//zslibserver.js
const express = require('express');
const zlib = require('zlib');
const fs = require('fs');
 
var app = express();
 
 app.get('/zlibapi/:func/:infile/:outfile', function(req, res) {
    res.type('application/json');
    
    var infile=req.params.infile;
    var outfile=req.params.outfile;
    
    try {
        
        var stats = fs.statSync(infile);
        var infileSize = stats.size;
        
        switch(req.params.func) {
          case "DEFLATERAW":
            var wobj = zlib.createDeflateRaw();
            break;
          case "INFLATERAW":
            var wobj = zlib.createInflateRaw();
            break;
          case "DEFLATE":
            var wobj = zlib.createDeflate();
            break;
          case "INFLATE":
            var wobj = zlib.createInflate();
            break;
          case "GZIP":
            var wobj=zlib.createGzip();
            break;
          case "GUNZIP":
            var wobj=zlib.createGunzip();
            break;
          default:
            res.status(500).json({ "error" : "bad function" });
            return;
        }
    
        const instream = fs.createReadStream(infile);
        const outstream = fs.createWriteStream(outfile);
      
        var d = new Date();
        console.log(d.toLocaleDateString() + ' ' + d.toLocaleTimeString() + ' : ' + req.params.func + ' ' + infile + ' -> ' + outfile + '...');
      
        instream.pipe(wobj).pipe(outstream).on('finish', function(){
            
            var d = new Date();
            console.log(d.toLocaleDateString() + ' ' + d.toLocaleTimeString() + ' : ' + 'finished!');
        
            var stats = fs.statSync(outfile);
            var outfileSize = stats.size
        
            res.status(200).json( { "result" : "OK" , "infileSize" : infileSize, "outfileSize" : outfileSize, "ratio" : (outfileSize / infileSize * 100).toFixed(2) + "%" } );
            return;
      });
      
    }
    catch(err) {
      res.status(500).json({ "error" : err.message});
      return;
    }
    
});
app.listen(3000, function(){
    console.log("zlibserver is ready captain.");
});

まず、「zlib」、「fs」、および「express」モジュールを取り込み、expressの「app」(アプリケーション)コンテキストを作成しています。

Expressの機能は、リクエストオブジェクトとレスポンスオブジェクトを操作して処理を行える非同期のミドルウェア関数を介して提供されます。

app.get() では、Expressに、ルートの /zlibapi/:func/:infile/:outfile_パスへのHTTP GETリクエストを処理するのかを指示しています。 app.get() を使用すると、ルート/パスに複数のハンドラを登録することができます。 パスの「:variable_」のチャンクは「名前付きルートパラメーター」と呼ばれます。
APIがヒットしたら、expressはURLのその部分を取得し、req.paramsで使用できるようにします。

コードには、RAWDEFLATE/RAWINFLATEのほかに、zlibがサポートする圧縮/解凍ラッパー形式のGZIP/GUZIPやDEFLATE/INFLATEのサポートが追加されています。

また、出発点として、基本的なTry/Catchエラー処理も追加しました。

結果とともにJSONオブジェクトを送り返すには、レスポンスオブジェクトのresと、res.sendStatus() に相当するres.status() を使用しています。
詳細については、Expressのドキュメントをご覧ください。

最後に、受信するHTTPリクエストのリスンをTCPポート3000で開始しています。

それでは、「zlibserver」アプリを実行して、実際に動作するかを見てみましょう。

C:\projects\zlib>node zlibserver.js
zlibserver is ready captain.

実行することがわかったので、サービスとして使用してみることにします。

ここではCachéから試してみますが、「curl」やその他の「Postman」などのサードパーティツールを使って、「zlibserver」RESTful APIをテストすることもできます。

%Net.HttpRequest_を使用して、Caché COSに、GETリクエストを実行する単純なRESTクライアントを実装する必要があります。これはそれほど手間のかからない作業ですが、コードを数行、記述する必要があります。 以下は、クラスの_utils.Http:getJSON() メソッドです。

Include %occErrors
Class utils.Http [ Abstract ]
{
  ClassMethod getJSON(server As %String = "localhost", port As %String = "3000", url As %String = "",  
    user As %String = "", pwd As %String = "", test As %Boolean = 0) As %DynamicAbstractObject
  {
     set prevSLang=##class(%Library.MessageDictionary).SetSessionLanguage("en")
              
     set httprequest=##class(%Net.HttpRequest).%New()
     set httprequest.Server=server
     set httprequest.Port=port

     if user'="" do httprequest.SetParam("CacheUserName",user)
     if pwd'="" do httprequest.SetParam("CachePassword",pwd)

     set sc=httprequest.SetHeader("Accept","application/json")
     if $$$ISERR(sc) $$$ThrowStatus(sc)
     set sc=httprequest.SetHeader("ContentType","application/json")
     if $$$ISERR(sc) $$$ThrowStatus(sc)

     try {
         set sc=httprequest.Get(url,test)
         if $$$ISERR(sc) $$$ThrowStatus(sc)
         if (httprequest.HttpResponse.StatusCode \ 100) = 2 {
             set response = ##class(%DynamicAbstractObject).%FromJSON(httprequest.HttpResponse.Data)
         }
         else {
             Throw ##class(%Exception.General).%New(httprequest.HttpResponse.ReasonPhrase, $$$GeneralError,,httprequest.HttpResponse.StatusLine)
         }            
     }
     catch exception  {
         set response = $$$NULLOREF
         throw exception             
     }            
     Quit response
  }
}

Cachéから、次のようにして使用することができます。
 

USER>try { set res="",res = ##class(utils.Http).getJSON(,,"/zlibapi/DEFLATERAW/input.txt/output.zz"),result=res.result } catch (exc) { Set result=$system.Status.GetOneErrorText(exc.AsStatus()) }
USER>w result
OK
USER>w res.%ToJSON()
{"result":"OK","infileSize":241243,"outfileSize":14651,"ratio":"6.07%"}

「うまく動作しました!」

以下は、curlを使ったAPIのテスト方法です(エクステントtest.logファイルを使用しています)。

C:\curl>curl -i http://localhost:3000/zlibapi/GZIP/test.log/test.gz

HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 76
ETag: W/"4c-iaOk5W3g6IlIEkzJaRbf3EmxrKs"
Date: Fri, 26 Jan 2018 07:43:17 GMT
Connection: keep-alive

{"result":"OK","infileSize":36771660,"outfileSize":8951176,"ratio":"24.34%"}

C:\curl>curl -i http://localhost:3000/zlibapi/GUNZIP/test.gz/test.txt

HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 77
ETag: W/"4d-tGgowYnW3G9ctHKcpvWmnMgnUHM"
Date: Fri, 26 Jan 2018 07:43:36 GMT
Connection: keep-alive

{"result":"OK","infileSize":8951176,"outfileSize":36771660,"ratio":"410.80%"}

C:\curl>curl -i http://localhost:3000/zlibapi/DEFLATERAW/test.log/test.zz

HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 76
ETag: W/"4c-4svUs7nFvjwm/JjYrPrSSwhDklU"
Date: Fri, 26 Jan 2018 07:44:26 GMT
Connection: keep-alive

{"result":"OK","infileSize":36771660,"outfileSize":8951158,"ratio":"24.34%"}

C:\curl>curl -i http://localhost:3000/zlibapi/INFLATERAW/test.zz/test.txt

HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 77
ETag: W/"4d-7s7jwh1nxCU+6Qi7nX2TB3Q1IzA"
Date: Fri, 26 Jan 2018 07:44:42 GMT
Connection: keep-alive

{"result":"OK","infileSize":8951158,"outfileSize":36771660,"ratio":"410.80%"}

ここで、受信ジョブを実行/受信/処理する間のzlibserverのコンソール出力を確認できます。

C:\projects\zlib>node zlibserver
zlibserver is ready captain.
2018-1-26 08:43:14 : GZIP test.log -> test.gz...
2018-1-26 08:43:17 : finished!
2018-1-26 08:43:36 : GUNZIP test.gz -> test.txt...
2018-1-26 08:43:36 : finished!
2018-1-26 08:44:23 : DEFLATERAW test.log -> test.zz...
2018-1-26 08:44:26 : finished!
2018-1-26 08:44:42 : INFLATERAW test.zz -> test.txt...
2018-1-26 08:44:42 : finished!

 

お客様の問題とそれを解決する上で達成したことをまとめましょう。

RESTを使ったNode.jsのコールアウトによって、Cachéを非常に簡単に拡張できることを学びました。

まず、この記事のきっかけとなった元々の特定のユースケースを「ブラインドアウト」し、優れたモードモジュールが豊富に用意され、APIによって広範な機能の可能性が提供されている、素晴らしいNode.jsエコシステムの観点から前向きに考えてみると、魅力的なソリューションで、Cachéから簡単にAPIにアクセスして制御することができるようになりました。

もっとも頻繁に使用されているNode.jsモジュール/APIのリストについては、次のリンクを参照してください。
http://www.creativebloq.com/features/20-nodejs-modules-you-need-to-know

「サポートの現場からは以上です!」 :)

お役に立てられれば幸いです。

Bernd
 

0
0 979
記事 Mihoko Iijima · 6月 1, 2021 6m read

これは InterSystems FAQ サイトの記事です。

InterSystems 製品を利用した REST/JSON の操作方法を、簡単なサンプルを利用して解説します。

サンプルでは、REST クライアント、HTML、ターミナルからデータ(JSON)を送信し、サーバ(InterSystems製品)で JSON 形式のデータを返す REST ディスパッチクラスを使った簡単な流れになっています。

サンプルは、https://github.com/Intersystems-jp/FAQ-REST-SimpleSample からダウンロードいただけます。

サンプルの利用手順

RESTディスパッチクラスとサンプルHTMLファイルが含まれています

(1) サンプルファイル(XML)のインポート

管理ポータルからインポートします(スタジオを利用されている場合は、スタジオでもインポートできます)。

管理ポータルは http://localhost:52773/csp/sys/UtilHome.csp でアクセスできます。

※ ポート番号はお使いの環境に合わせて変更してください。

管理ポータルにアクセスできたら以下メニューでインポートを行ってください。

0
0 7734
記事 Toshihiko Minamoto · 4月 5, 2021 11m read

はじめに

Webで行われるサーバーとクライアント間のほとんどの通信は、リクエストとレスポンスの構造に基づいており、 クライアントがサーバーにリクエストを送信すると、サーバーがそのリクエストに対するレスポンスを送信します。 WebSocketプロトコルは、サーバーとクライアント間の双方向通信チャンネルを提供するプロトコルで、サーバーがリクエストを受信しなくても、クライアントにメッセージを送信することができます。 WebSocketプロトコルと、InterSystems IRISでの実装についての詳細は、以下のリンクをご覧ください。

このチュートリアルは、「非同期WebSocket -- クイックチュートリアル」を、Caché 2016.2以上とInterSystems IRIS 2018.1以上向けに更新したものです。

非同期動作と同期動作

InterSystems IRISでは、WebSocket接続を同期的または非同期的に実装することができます。 クライアントとサーバー間のWebSocket接続がどのように動作するかは、%CSP.WebSocketクラスの「SharedConnection」プロパティによって決まります。

  • SharedConnection=1: 非同期動作

  • SharedConnection=0: 同期動作

クライアントとInterSystems IRISインスタンスがホスティングされているサーバーとの間のWebSocket接続には、IRISインスタンスとWebゲートウェイとの間のコネクションが含まれます。 WebSocketの同期動作では、そのコネクションはプライベートチャンネルが使用されますが、 非同期動作では、複数のWebSocketクライアントで、IRISインスタンスとWebゲートウェイとの間のコネクションが共有されます。 WebSocketの非同期動作は、各クライアントを処理するWebゲートウェイとIRISインスタンス間のコネクションを排他的に行う必要がないため、同一のサーバーに多数のクライアントが接続する場合に、そのメリットを発揮します。

このチュートリアルでは、WebSocketの非同期動作を行います。 したがって、開いているすべてのチャットウィンドウは、Webゲートウェイと、WebSocketサーバークラスをホストするIRISインスタンス間のコネクションを共有することになります。

チャットアプリケーションの概要

WebSocketの「hello world」は、ユーザーがそのアプリケーションにログインしているすべてのユーザーにブロードキャストされるメッセージを送信できるチャットアプリケーションです。 このチュートリアルでは、チャットアプリケーションには次のコンポーネントが含まれます。

  • サーバー: %CSP.WebSocketを継承したクラスに実装されています。

  • クライアント: CSPページで実装されています。

このチャットアプリケーションの実装では、次の内容を実現します。

  • ユーザーは、開いているすべてのチャットウィンドウにメッセージをブロードキャストできます。

  • オンラインユーザーは、開いているすべてのチャットウィンドウの「オンラインユーザー」リストに表示されます。

  • ユーザーは、「alias」キーワードで始まるメッセージを作成することで、ユーザー名を変更できます。このメッセージはブロードキャストされませんが、「オンラインユーザー」リストの内容を更新します。

  • ユーザーがチャットウィンドウを閉じると、「オンラインユーザー」リストから削除されます。

チャットアプリケーションのソースコードについては、こちらのGitHubリポジトリをご覧ください。

クライアント

チャットアプリケーションのクライアント側は、チャットウィンドウのスタイル定義、WebSocket接続の宣言、サーバー間との通信を処理するWebSocketのイベントとメソッド、およびサーバーに送信されるメッセージをパッケージ化し、受信メッセージを処理するヘルパー関数を含むCSPページによって実装されます。

まず、アプリケーションが、JavaScript WebSocketライブラリを使用して、WebSocket接続をどのように初期化するのか確認しましょう。

    ws = new WebSocket(((window.location.protocol === "https:")? "wss:":"ws:")
                    + "//"+ window.location.host + "/csp/user/Chat.Server.cls");

new は、WebSocketクラスの新しいインスタンスを作成します。 これにより、「wss」(WebSocket通信チャンネルにTLSを使用することを示します)または「ws」プロトコルを使って、サーバーにWebSocket接続が開きます。 サーバーは、Chat.Server クラスを定義するウェブサーバーのポート番号とインスタンスのホスト名で指定されてます(この情報は window.location.host 変数に格納されます)。 サーバークラスの名前(Chat.Server.cls)は、サーバー上のリソースのGETリクエストとして、WebSocketの開始URIに含まれます。

WebSocket接続の確立に成功すると ws.onopen イベントが発生し、**接続中から接続**に状態が移行します。

    ws.onopen = function(event){
        document.getElementById("headline").innerHTML = "CHAT - CONNECTED";
    };

このイベントにより、チャットウィンドウの見出しが変更され、クライアントとサーバーが接続されていることが示されるようになります。

メッセージの送信

ユーザーがメッセージを送信するアクションによって、send 関数がトリガーされます。 この関数は ws.send メソッドを囲むラッパーとして機能し、クライアントのメッセージをWebSocket接続を介してサーバーに送信する仕組みが含まれます。

function send() {
    var line=$("#inputline").val();
    if (line.substr(0,5)=="alias"){
        alias=line.split(" ")[1];
        if (alias==""){
            alias="default";
        }
        var data = {}
        data.User = alias
        ws.send(JSON.stringify(data));
        } else {
        var msg=btoa(line);
        var data={};
        data.Message=msg;
        data.Author=alias;
        if (ws && msg!="") {
            ws.send(JSON.stringify(data));
        }
    }
    $("#inputline").val("");
}

send は、サーバーに送信される情報(エイリアスの更新または一般的なメッセージ)をJSONオブジェクトにパッケージ化し、送信される情報の種類に応じたキー/値ペアを定義します。 btoa は、一般メッセージの内容を、base-64でエンコードされたASCII文字列に変換します。

メッセージの受信

クライアントがサーバーからメッセージを受信すると、ws.onmessage イベントがトリガーされます。

ws.onmessage = function(event) {
    var d=JSON.parse(event.data);
    if (d.Type=="Chat") {
        $("#chat").append(wrapmessage(d));
            $("#chatdiv").animate({ scrollTop: $('#chatdiv').prop("scrollHeight")}, 1000);
    } else if(d.Type=="userlist") {
        var ul = document.getElementById("userlist");
        while(ul.firstChild){ul.removeChild(ul.firstChild)};
        $("#userlist").append(wrapuser(d.Users));
    } else if(d.Type=="Status"){
        document.getElementById("headline").innerHTML = "CHAT - connected - "+d.WSID;
    }
};

クライアントが受信するメッセージの種類(「チャット」、「ユーザーリスト」、「ステータス」)に応じて、onmessage イベントによって、wrapmessage または wrapuser が呼び出され、チャットウィンドウの該当するセクションに受信データを取り込みます。 受信メッセージがステータス更新であれば、チャットウィンドウのステータスの見出しがWebSocket IDで更新されます。このIDは、チャットウィンドウに関連付けられた双方向WebSocket接続を識別するものです。

その他のクライアントコンポーネント

クライアントとサーバー間の通信でエラーが発生すると、WebSocketの onerror メソッドがトリガーされ、エラーの発生を通知するアラートの発行とページのステータス見出しの更新が行われます。

ws.onerror = function(event) {
    document.GetElementById("headline").innerHTML = "CHAT - error";
    alert("Received error"); 
};

クライアントとサーバー間のWebSocket接続が閉じると、onclose メソッドがトリガーされ、ステータスの見出しが更新されます。

ws.onclose = function(event) {
    ws = null;
    document.getElementById("headline").innerHTML = "CHAT - disconnected";
}

サーバー

チャットアプリケーションのサーバー側は、%CSP.WebSocket を拡張した Chat.Server クラスで実装されます。 サーバークラスは、%CSP.WebSocketのプロパティとメソッドを継承しています。これについては以下の方で説明します。 Chat.Server は、クライアントからのメッセージを処理するカスタムメソッドと、クライアントにメッセージをブロードキャストするカスタムメソッドも実装します。

サーバーの起動前処理

OnPreServer() は、WebSocketサーバーが作成されて %CSP.WebSocket クラスから継承される前に実行されます。

Method OnPreServer() As %Status
{
    set ..SharedConnection=1
    if (..WebSocketID '= ""){ 
        set ^Chat.WebSocketConnections(..WebSocketID)=""
    } else {
        set ^Chat.Errors($INCREMENT(^Chat.Errors),"no websocketid defined")=$HOROLOG 
    }
    Quit $$$OK
}

このメソッドは、SharedConnection クラスのパラメーターを1に設定し、WebSocket接続が非同期であり、InterSystems IRISインスタンスとWebゲートウェイ間の接続を定義する複数のプロセスでサポートされることを示します。 SharedConnection パラメーターは、OnPreServer() でしか変更できません。 また、OnPreServer() は、クライアントに関連付けられているWebSocket IDを ^Chat.WebSocketConnections グローバルに格納します。

Serverメソッド

サーバーが実行するロジックの本文は、Server() メソッドに含まれます。

Method Server() As %Status
{
    do ..StatusUpdate(..WebSocketID)
    for {       
        set data=..Read(.size,.sc,1) 
        if ($$$ISERR(sc)){
            if ($$$GETERRORCODE(sc)=$$$CSPWebSocketTimeout) {
                //$$$DEBUG("no data")
            }
            if ($$$GETERRORCODE(sc)=$$$CSPWebSocketClosed){
                kill ^Chat.WebSocketConnections(..WebSocketID)
                do ..RemoveUser($g(^Chat.Users(..WebSocketID))) 
                kill ^Chat.Users(..WebSocketID)
                quit  // Client closed WebSocket
            }
        } else{
            if data["User"{
                do ..AddUser(data,..WebSocketID)
            } else {
                set mid=$INCREMENT(^Chat.Messages)
                set ^Chat.Messages(mid)=data
                do ..ProcessMessage(mid)
            }
        }
    }
    Quit $$$OK
}

このメソッドは、クライアントからの受信メッセージを読み取り(%CSP.WebSockets クラスの Read メソッド)、取得したJSONオブジェクトを ^Chat.Messages グローバルに追加し、接続されているその他すべてのチャットクライアントにメッセージを転送するための ProcessMessage() を呼び出します。 ユーザーがチャットウィンドウを閉じると(つまり、サーバーへのWebSocket接続が終了すると)、Server() メソッドの Read の呼び出しによって、それが評価するエラーコードが $$$CSPWebSocketClosed マクロに返され、メソッドはそれに応じて閉鎖の処理に進みます。

メッセージの処理と配信

ProcessMessage() は、受信チャットメッセージにメタデータを追加して SendData() を呼び出し、メッセージをパラメーターとして渡します。

ClassMethod ProcessMessage(mid As %String)
{
    set msg = ##class(%DynamicObject).%FromJSON($GET(^Chat.Messages(mid)))
    set msg.Type="Chat"
    set msg.Sent=$ZDATETIME($HOROLOG,3)
    do ..SendData(msg)
}

ProcessMessage() は、^Chat.Messages グローバルからJSONの書式付きメッセージを取得し、%DynamicObject クラスの %FromJSON メソッドを使って、そのメッセージをInterSystems IRIS オブジェクトに変換します。 この変換により、メッセージが接続されているすべてのチャットクライアントに転送される前に、データを編集しやすくなります。 Type 属性を値「Chat」で追加し、クライアントはこれを使用して、受信メッセージの処理方法を決定しアンス。 SendData() は、接続されているその他すべてのチャットクライアントにメッセージを送信します。

ClassMethod SendData(data As %DynamicObject)
{
    set c = ""
    for {
        set c = $order(^Chat.WebSocketConnections(c))
        if c="" Quit
        set ws = ..%New()
        set sc = ws.OpenServer(c)
        if $$$ISERR(sc) { do ..HandleError(c,"open") } 
        set sc = ws.Write(data.%ToJSON())
        if $$$ISERR(sc) { do ..HandleError(c,"write") }
    }
}

SendData() は、InterSystems IRISオブジェクトをJSON文字列に変換し直し(data.%ToJSON())、すべてのチャットクライアントにメッセージをプッシュします。 SendData() は、クライアントとサーバー間のそれぞれの接続に関連付けられたWebSocket IDを ^Chat.WebSocketConnections グローバルから取得し、そのIDを使って、%CSP.WebSocket クラスの OpenServer メソッドで、WebSocket接続を開きます。 OpenServer メソッドを使ってこの処理を実行できるのは、WebSocket接続が非同期であるからです。IRIS-Webゲートウェイの既存のプールからプロセスをプルし、特定のチャットクライアントへのサーバー接続を識別するWebSocket IDを割り当てています。 最後に、 Write()%CSP.WebSocket メソッドによって、JSON 文字列に変換されたメッセージがクライアントにプッシュされます。

まとめ

このチャットアプリケーションでは、クライアントとInterSystems IRISをホストするサーバー間にWebScocket接続を確立する方法が示されています。 InterSystems IRISにおけるプロコルとその実装については、引き続き、「はじめに」セクションに記載されているリンクをご覧ください。

0
0 3627
質問 Mitsuru Amano · 3月 13, 2021

こんにちは、皆さん

私は、%CSP.WebSocketの非同期動作(SharedConnection=1)で、ソケットのクローズを検知する手段が見つからなくて困っています。

WebアプリケーションとIRIS間でソケットの接続、データの送受信は出来ましたが、Webアプリケーション側でソケットクローズしたとき、IRIS側でそれを検知する方法が分かりませんでした。

どなたか、IRIS(サーバ側)でソケットのクローズを検知する方法をご存じでしたら教えて下さい。

3
0 220
記事 Megumi Kakechi · 2月 18, 2021 3m read

jQuery($.getJSON と $.ajax)を使用した InterSystems IRIS データのJSON形式での取得方法をご紹介します。

以下にサンプルをご用意しました。

https://github.com/Intersystems-jp/REST_JSON_sample


サンプルには、次の内容が含まれます。

・REST + JSON
・REST + CORS

※それぞれ、$.getJSON と $.ajax で取得する方法を記載しています。

※サーバ側ではSelect文の実行結果をJSON_OBJECT関数を使用しJSON文字列で出力しています。
 関数については以下のドキュメントをご覧ください。
 JSON_OBJECT関数


使用手順は以下になります。

0
0 583
記事 Henrique Dias · 10月 8, 2020 2m read

npm-iris とは何ですか? 

N.P.Mは "No Project Mess "の略です。

N.P.M.は、InterSystems IRISとBootstrap 4を使用したプロジェクト&タスク管理アプリです。

No Project Messは、シンプルで直感的なプロジェクトとタスクの管理ソフトウェアで、開発者や中小企業が日々の複雑な問題を軽減できるように作成されています。 
スプレッドシート、カンバン、カレンダー、ガントチャートなど、タスクのためのさまざまなビューを提供しています。

2
0 175
記事 Shintaro Kaminaka · 8月 3, 2020 6m read

みなさん、こんにちは。

Open Exchangeで FHIRリポジトリに接続するFHIR Patient Browserが公開されました。

https://openexchange.intersystems.com/package/FHIR-Patient-Browser

Open-Source のJavaScriptライブラリであるfhir.jsを利用した、FHIRサーバに接続するためのFHIRクライアントアプリケーションです。

この記事では、このOpen Exchangeアプリケーション実行に必要なNode.jsのインストールや、コンパイルおよび実行方法について解説します。(筆者もNode.jsのインストールや実行は初めてです!)

1.FHIRPatientBrowserのソースのダウンロード

上記Open Exchangeリンク先の「Download」をクリックすると、以下のGithubサイトへ移動します。

https://github.com/antonum/FHIRPatientBrowser
 

以下の画像のようにCodeからZIP形式でダウンロードし、展開します。(もちろんGitなどのツールを利用して入手してもOKです。)

2. Node.jsのインストール

Node.jsのインストーラーはこちらのサイトからダウンロード可能です。

0
0 387