Hatena::ブログ(Diary)

kuroの覚え書き

[Vmax] [Macintosh/PC] [Network] [Make] [science] [Programing]

注:個人的覚え書きであり、いかなる内容も保証されるものではありません。
(ツッコミは歓迎しますが、全てに応えられるとも限りませんのであしからず。)

2017-09-22

[][][]group_byでsumしてquery

やりたいこと

テーブルTestで、カラムz_idの数値をカラムnameとgeneが同じサンプルで合計してtotal_pointsとして各サンプルに付与し、total_pointsが2以上になるサンプルをqueryしたい

作ったコード

            total_points = func.sum(Test.z_id).label('total_points')

            sample = session.query(Test.id, total_points).filter(Test.samp.like(form.name.data)).group_by(Test.samp, Test.gene)

これだとnameとgeneが同じサンプルは1サンプルに統一してtotal_pointsを付与してリストアップしてしまう。

合計は計算してもサンプルはまとめてしまわないでほしいんだけどな。

あと、total_pointsで

.filter(total_points >= 2)

とくっつけてもエラーになる。このままではtotal_pointsはTestにつながっていないからfilterがかからないのだよね。そこでやっぱりjoinがいるのだとは思う。

しかしjoinといえばリレーションを張ったテーブル間でつなぐ例しか見つからないんだが・・・



検索しまくった結果、さらにサブクエリーと併用する必要があることがわかってきた。

http://omake.accense.com/static/doc-ja/sqlalchemy/ormtutorial.html#select

http://symfoware.blog68.fc2.com/blog-entry-1379.html

  stmt = session.query(Test.samp, func.sum(Test.z_id).label('total_points')).group_by(Test.samp).subquery()
  sample = session.query(Test, stmt.c.total_points).outerjoin((stmt, Test.samp==stmt.c.samp)).filter(Test.samp.like(form.name.data)).filter(Test.gene.like(form.gene.data)).filter(Test.count<=form.count.data).filter(stmt.c.total_points >= 9)
return render_template('index.html', contents=sample, form=form)

このようにstmtに一旦z_idの合計とsampを対応付ける仮想テーブルを作り、outerjoinでTestの各sampに紐付けした上でクエリーをもう一度かけるらしい。

この結果を表示するにはindex.htmlの方に

        {% for samp, count in contents %}
        <tr>
          <td>{{ samp.id }}</td>
          <td>{{ samp.samp }}</td>
          <td>{{ samp.gene }}</td>
          <td>{{ samp.zygo }}</td>
          <td>{{ samp.count }}</td>
          <td>{{ samp.z_id }}</td>
          <td>{{ count }}</td>
        </tr>

こんな感じの受け皿を用意しておかないとならないようで、こっちも工夫がいる。

f:id:k-kuro:20170922170030p:image

これでようやく一歩近づいたが、今度はgroup_byをsampとgeneの2つでやる方法を考えないとね。あとcaseによる数値化も

まずはgroup_byを2つにする件。これは案外簡単であった。

from sqlalchemy.sql.expression import and_

stmt = session.query(Test.samp, Test.gene, func.sum(Test.z_id).label('total_points')).group_by(Test.samp, Test.gene).subquery()
sample = session.query(Test, stmt.c.total_points).outerjoin((stmt, and_(Test.samp==stmt.c.samp, Test.gene==stmt.c.gene))).filter(Test.samp.like(form.name.data)).filter(Test.gene.like(form.gene.data)).filter(Test.count<=form.count.data).filter(stmt.c.total_points >= 2)

これだけ

f:id:k-kuro:20170922183825p:image

次にcaseによる数値化。homなら2それ以外(het)なら1にしてsumすればいいので

stmt = session.query(Test.samp, Test.gene, func.sum(case([(Test.zygo=='hom', 2)], else_=1)).label('total_points')).filter(Test.count<=form.count.data).group_by(Test.samp, Test.gene).subquery()
sample = session.query(Test, stmt.c.total_points).outerjoin((stmt, and_(Test.samp==stmt.c.samp, Test.gene==stmt.c.gene))).filter(Test.count<=form.count.data).filter(Test.samp.like(form.name.data)).filter(Test.gene.like(form.gene.data)).filter(stmt.c.total_points >= 2)

これでちゃんと動作したよ。

最初homなら2、hetなら1それ以外0でやろうとしたらエラーが出た。homとhetしかないのだから2択に変えたらうまくいった。

[][][] SQL表現言語

結局ナマのSQL文に近いSQL表現言語を使わないと、ややこしい検索はできない気がしてきた。

from sqlalchemy import select
...
conn = engine.connect()

s = select([Test])
sample = conn.execute(s) 

これが

sample = session.query(Test)

と等価であることがわかった。

2017-09-21

[][][]検索式のコードを単純化

デフォルトにあいまい検索を取り入れることで複雑なif分岐をできるだけなくし、コードをすっきりさせる。コードの可読性は若干落ちるが、複雑な検索式の組み合わせをifを多用して作ると長大でredundantなコードになってしまい、混乱の元になる。

    if  form.name.data:
        sample = sample.filter(Test.samp.like(form.name.data))
    if form.gene.data:
        sample = sample.filter(Test.gene.like(form.gene.data))
    if form.count.data:
        sample = sample.filter(Test.count<=form.count.data)

このあたりも

           sample = session.query(Test).filter(Test.samp.like(form.name.data)).filter(Test.gene.like(form.gene.data)).filter(Test.count<=form.count.data)

と一行にしてしまって、代わりにデフォルト値を

class TestForm(FlaskForm):
    name = StringField('Sample name:', default='%%')
    gene = StringField('Gene name:', default='%%')
    count = IntegerField('count cut-off:', default=15)

と設定してしまう。

2017-09-20

[][][] SQLAlchemy関連参照リンク一覧

Flask-SQLAlchemy is a SQLAlchemy extension for Flask. http://flask-sqlalchemy.pocoo.org/2.1/

SQLAlchemyについて http://study-flask.readthedocs.io/ja/latest/flask_sqlalchemy.html

SQLAlchemy 1.2 Documentation http://docs.sqlalchemy.org/en/latest/orm/tutorial.html

SQLAlchemyのセッションを学ぶ(セッション作成編) http://lab.hde.co.jp/2008/11/sqlalchemy.html

sqlalchemyでgroup_byしてsum http://qiita.com/fukkyy/items/c3bee1ff5ea0a828cb3e

SQLAlchemy + MySQL を使ってみる(その1)http://qiita.com/fukkyy/items/c3bee1ff5ea0a828cb3e

SQL to SQLAlchemy conversions http://blog.vero4ka.info/blog/2016/05/04/sql-to-sqlalchemy-conversions/

[Python] SQLAlchemy との戦い http://d.hatena.ne.jp/heavenshell/20160220/1455987788

c-bata web Flask-SQLAlchemyメモ http://nwpct1.hatenablog.com/entry/flask-sqlalchemy

日常の記録 SQLAlchemyでMySQLに接続 http://yuizho.hatenablog.com/entry/2013/06/26/012044

podhmo's diary sqlalchemyで自己結合とか相関サブクエリとか試す。http://pod.hatenablog.com/entry/2012/12/22/204513

偏った言語信者の垂れ流し SQLAlchemyでサブクエリとJOINを使ってみる http://tokibito.hatenablog.com/entry/20140222/1393039413

chykaのブログ SQLAlchemyで"case" http://chyka.hatenablog.jp/entry/2016/03/14/002436

stackoverflow https://stackoverflow.com/search?q=sqlalchemy

[][][] SQLAlchemyを組み込む step by step

やることは

  • データベースへの接続
  • テーブルの定義
  • マッピング先のPythonクラスを定義する
  • セッションの作成

という感じ。

まずはどのサイトでも書かれているimport

しかしサイトごとに書き方が色々で、最もざっくりしたものだと

from sqlalchemy import *
from sqlalchemy.orm import *

とか。

step by stepで都度必要なものを確認したいので、これは避けたい。

importのあとにまず行うのはデータベースへの接続なようなので、(テーブルを先に定義していることもある)

from sqlalchemy import create_engine

これを__init__pyに記述することにする。

データベースへの接続はざっくり型だと

engine = create_engine('sqlite:///test.db', echo=True)

こんな感じで、丁寧型(この例はpostgresql)だと

url = 'postgresql+psycopg2://pgadmin:password@192.168.1.101:5432/sample'
engine = create_engine(url, echo=True)

こんな感じ。

私の場合、すでにconfg.pyに

SQLALCHEMY_DATABASE_URI = 'sqlite:///flask.data'

こう書いて、__init__pyで

app.config.from_object('app.config')

これで読み込んでいるので

engine = create_engine(app.config['SQLALCHEMY_DATABASE_URI'], echo=True)

としておくのでいいと思う。

次にテーブルの定義だが、多くの場合同時にクラスマッピングもやるスタイルになっているようだ。

まず declarative_base() 関数 を使って Base というクラスを生成する必要がある。

from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base(app)

これも__init__.pyに書いておこう。

さて、テーブルの定義の方はmodels.pyに書いてあるのでこちらを編集する。

__init__.pyに書いたBaseを読み込むため、

from app import Base

これまでのFlask-SQLAlchemyの書式では

class Role(db.Model):
    __tablename__ = 'roles'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), unique=True)
    users = db.relationship('User', backref='role', lazy='dynamic')

    def __init__(self, name, users):
        self.name = name
        self.users = users

    def __repr__(self):
        return '<Role %r>' % self.name

こうなっていた定義を

from sqlalchemy import Column, Integer, String
from sqlalchemy.orm import relation, backref

class Role(Base):
    __tablename__ = 'roles'
    id = Column(Integer, primary_key=True)
    name = Column(String(64), unique=True)
    users = relation('User', backref='role', lazy='dynamic')

    def __init__(self, name, users):
        self.name = name
        self.users = users

    def __repr__(self):
        return '<Role %r>' % self.name

こういうふうに書き換える。TableのカラムやTYPEの定義をimportしておくことも忘れずに。

テーブルの生成は

Base.metadata.create_all(engine)

を実行するのだが、すでにテーブルがあるときには何も起こらない。これも__init__.pyに記述しておくことにする。

from app.models import *
Base.metadata.create_all(engine)

これでいけると思うのだが。

ということで起動してみると、前のdb = SQLAlchemy(app)に絡むところがことごとくエラーになるのでそれらをコメントアウトなり修正してやったところ、無事に起動し、データベースも構築された。もともとあったデータベースをおいておいても破壊されているようすもないので、ここまでのところはちゃんと動作していると思われる。

次に本題の検索関係を構築していく。

まずセッションの作成を行う。

__init__.pyに

from sqlalchemy.orm import sessionmaker
Session = sessionmaker(bind=engine)

を追加。そして、検索式や結果を表示するプログラム****.pyに

from app import Session
    session = Session()

を追加。これで準備は整ったはず。

さあ、検索を始めよう。


sample = session.query(Samples).all()
return render_template('/samples/index.html', contents=sample)

こうやったらSamplesの全体がsampleに格納され、/samples/index.htmlにレンダリングされて表示されることを確認した。

イケてるイケてる。

ところが

    sample = session.query(Samples).all()
    if form.name.data:
        if form.aname.data:
             sample = sample.filter(Samples.samplename.like(form.name.data))
        else:
            sample = sample.filter_by(samplename=form.name.data)
    if form.pheno.data:
        if form.apheno.data:
             sample = sample.filter(Samples.phenotype.like(form.pheno.data))
        else:
            sample = sample.filter_by(phenotype=form.pheno.data)

こいうふうに絞込をかけようとすると

AttributeError: 'list' object has no attribute 'filter_by'

こんなエラーが出て止まってしまう。

sample = session.query(Samples).all()

のall()が原因で、こいつがついているとsampleはlistになってしまう。これをはずせばキチンと動いた。

これにて、Flask-SQLAlchemyから素のSQLAlchemyに移行完了した。

さて、そうなると今度は問題のcase、sum、group_byを使えるかどうかだ。


caseの書式は

case([(Exome.zygo == 'het', 1)], [(Exome.zygo == 'hom', 2)], else_=0)

これであっていると思う。

func.sum ... group_byの使い方がいまいちわからない。というかやっぱりJOINを使わないといけないのか。

(func.sum(case([(Exome.zygo == 'het', 1)], [(Exome.zygo == 'hom', 2)], else_=0)).label('i_count)).group_by(Exome.SampleName_phenotype, Exome.Gene_wgEncodeGencodeCompV19)

ここまではいいんじゃないかと思う。

ここで出来上がったi_countを元のExomeにくっつけた仮想テーブルでfilterをかけてi_count >=2となる行をselectして返せばいい。

書くと簡単だけど、これをプログラムにしなければならない。

[][] SQLite3にcsvからデータを取り込む

$ sqlite3 test.db
sqlite> .show
        echo: off
         eqp: off
  explain: off
     headers: off
        mode: list
   nullvalue: ""
      output: stdout
colseparator: "|"
rowseparator: "\n"
       stats: off
       width: 
sqlite> .separator ,
sqlite> .show
        echo: off
         eqp: off
  explain: off
     headers: off
        mode: list
   nullvalue: ""
      output: stdout
colseparator: ","
rowseparator: "\n"
       stats: off
       width: 
sqlite> .import /pass/to/test1.csv test1
sqlite> select * from test1;
1,N001,gene1,het,1,1
2,N001,gene1,het,1,1
3,N001,gene1,het,20,1
4,N001,gene1,het,2,1
5,N001,gene2,het,1,1
6,N001,gene2,het,1,1
7,N001,gene2,hom,1,2
8,N001,gene3,hom,20,2
9,N002,gene1,het,1,1
10,N002,gene1,hom,1,2
11,N002,gene4,het,20,1
12,N002,gene5,het,1,1
13,N002,gene6,het,20,1
14,N002,gene7,het,1,1
15,N002,gene8,het,1,1
16,N002,gene9,het,1,1
17,N003,gene10,hom,1,2
18,N003,gene11,het,1,1
19,N003,gene12,het,1,1
20,N003,gene13,het,1,1
sqlite> .header on
sqlite> .mode column
sqlite> select * from test1;
id          samp        gene        zygo        count       z_id      
----------  ----------  ----------  ----------  ----------  ----------
1           N001        gene1       het         1           1         
2           N001        gene1       het         1           1         
3           N001        gene1       het         20          1         
4           N001        gene1       het         2           1         
5           N001        gene2       het         1           1         
6           N001        gene2       het         1           1         
7           N001        gene2       hom         1           2         
8           N001        gene3       hom         20          2         
9           N002        gene1       het         1           1         
10          N002        gene1       hom         1           2         
11          N002        gene4       het         20          1         
12          N002        gene5       het         1           1         
13          N002        gene6       het         20          1         
14          N002        gene7       het         1           1         
15          N002        gene8       het         1           1         
16          N002        gene9       het         1           1         
17          N003        gene10      hom         1           2         
18          N003        gene11      het         1           1         
19          N003        gene12      het         1           1         
20          N003        gene13      het         1           1         
sqlite> .quit

[][][] SQLalchemyの検索についてテストする簡易なページを作成

detest.py

from flask import Flask, render_template
from sqlalchemy import create_engine, Column, Integer, String, func
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from sqlalchemy.sql.expression import case
from flask_wtf import FlaskForm
from wtforms import StringField, SelectField, IntegerField, SubmitField
from flask_bootstrap import Bootstrap

app = Flask(__name__)

app.config['SECRET_KEY'] = 'hard to guess string'
app.config['DEBUG']= True
bootstrap = Bootstrap(app)

engine = create_engine('sqlite:///test.db', echo=True)
Base = declarative_base()

Session = sessionmaker(bind=engine)
session = Session()

class Test(Base):
    __tablename__ = 'test1'
    id = Column(Integer, primary_key=True)
    samp = Column(String(64))
    gene = Column(String(64))
    zygo = Column(String(64))
    count = Column(Integer)
    z_id = Column(Integer)

    Base.metadata.create_all(engine)

class TestForm(FlaskForm):
    name = StringField('Sample name:')
    gene = StringField('Gene name:')
    count = IntegerField('count cut-off:')
    inhe = SelectField('inherit model', choices=[('', 'any'), ('recessive', 'recessive'), ('dominant', 'dominant'), ('pathogenic', 'pathogenic')])
    zygo = SelectField('zygote', choices=[('', 'both'), ('%het%', 'hetero'), ('%hom%', 'homo')])
    submit = SubmitField('Search')

@app.route("/", methods=['GET', 'POST'])
def index():
    form = TestForm()
    sample = session.query(Test)
    if  form.name.data:
        sample = sample.filter(Test.samp.like(form.name.data))
    if form.gene.data:
        sample = sample.filter(Test.gene.like(form.gene.data))
    if form.count.data:
        sample = sample.filter(Test.count<=form.count.data)
    if form.zygo.data:
        sample = sample.filter(Test.zygo.like(form.zygo.data)) 
   return render_template('index.html', contents=sample, form=form)


if __name__ == "__main__":
	app.run()

index.html

<!-- templates/index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    {% import "bootstrap/wtf.html" as wtf %}
</head>
<body>
  {{ wtf.quick_form(form) }}
  <div class="table-responsive" style="height:300px; overflow:scroll">
    <table class="table table-striped">
      <thread>
        <tr>
          <th>ID</th>
          <th>Sample</th>
          <th>Gene</th>
          <th>Zygote</th>
          <th>count</th>
          <th>Z_ID</th>
        </tr>
      </thread>
      <tbody>
        {% for samp in contents %}
        <tr>
          <td>{{ samp.id }}</td>
          <td>{{ samp.samp }}</td>
          <td>{{ samp.gene }}</td>
          <td>{{ samp.zygo }}</td>
          <td>{{ samp.count }}</td>
          <td>{{ samp.z_id }}</td>
        </tr>
        {% else %}
          <td>no entry exist!</td>
        {% endfor %}
      </tbody>
    </table>
  </div>
</body>
</html>

これに上のtest.dbを読み込んでテスト環境にする。

検索式を書き込んで実行テストし放題。

f:id:k-kuro:20170921001513p:image

2017-09-19

[][] Flask-SQLAlchemyと普通のSQLAlchemyはいろいろ違う

FlaskのextentionのFlask-SQLAlchemyと素のSQLAlchemyはどうやら色々と仕様が異なっている模様。

ネットの情報は素のSQLAlchemyの情報がほとんどなので、Flask-SQLAlchemyにこだわっていると、うまく動かない事が多い。

これはbootstrapのときもそうだった。お手軽にインストールできて、Flask本に書かれたままに組み込んできたせいでかえって身動きが取れなくなっている感じがする。

というわけで、一旦現状でサーバにあげたデータベースはフリーズして、素のSQLAlchemyを組み込むことからやり直してみよう。

2017-09-18

[][][] phpのSQLクエリをSQLalchemyに移植

$query1 =
"SELECT * FROM `$table_name1`
left join (
select sum(case when `V_count` <= $value1 and `A`='x' then 1 when `V_count` <= $value1 and `A`='y' then 2 else 0 end) as count,`N`,`M` from `$table_name1`
group by `N`, `M`) as tbls
using (`N`,`M`)
where `$table_name1`.`M` REGEXP '$gene_name1'
and `$table_name1`.`N` REGEXP '$sample_name1'
and count >=2";

何がしたいのかはわかったと思うが、はてさてこのクエリをどう書けばいいんでしょうか?

V_countが$value1以下でAがxのときはcount=1

V_countが$value1以下でAがyのときはcount=2

それ以外は0

これをN,Mに該当するものでそれぞれ集めて合計した結果が2以上になるものを選べ

ってことかな?

断片断片はなんとなくわかってきたようなそうでないような

caseの部分

case([and_(Exome.variant_count <= form.count.data, Exome.zygo == 'het'), 1], [and_(Exome.variant_count <= form.count.data, Exome.zygo == 'hom’), 2], else_=0)

なんだと思うけど、結局Exome.variant_count <= form.count.dataは必須なんだから、まずはこいつだけでフィルターしてやってもいいんじゃなかろうか。

    if form.count.data:
        exome = exome.filter(Exome.variant_count<=form.count.data)

2017-09-17

[] Raspberry pi/minibianによるNAS

USBメモリをマウントしてNASの記憶領域として使うメモ。

USBメモリは変則的にHFS+フォーマット。El Capitan (MacOSX 10.11)ではHFS+はジャーナリングつきでしかフォーマットできないが、ジャーナリングがついているとminibianでは書き込みができない。

なのでmacでジャーナリングを切っておく必要がある。

diskutil disableJournal /dev/sda2

これで読み書き自由にマウント出来るようになる。

ただしまだ問題があって、minibianでアンマウントをしくじると、リードオンリーに固定されてしまう。

そうなったときは一旦アンマウントし、

$ sudo fsck.hfsplus /dev/sda2
** /dev/sda2
** Checking HFS Plus volume.
** Checking Extents Overflow file.
** Checking Catalog file.
** Checking Catalog hierarchy.
** Checking Extended Attributes file.
** Checking volume bitmap.
** Checking volume information.
** The volume USB32G appears to be OK.

これで回復する。

まあ32GBだしFAT32でいいんだけど。

2017-09-16

[][][] IGV.jsのreferenceトラックにアミノ酸情報を表示できるか?

https://github.com/igvteam/igv.js/issues/182

結論だけ書くと、いまのところ対応できていないとのこと。(2017 3月現在)

2017-09-14

[][] apache + flask +wsgiで動かす

サーバに上げて運用できそうな感じになってきたのでwwwサーバをapacheに切り替えてみたい。

まずはローカルでテストっと。

なになに最近のOSXはweb共有コントロールパネルがなくなっちゃったから

sudo apachectl start

で手動起動すると。

$ ps aux | grep httpd

おや?起動してない・・・

http://hubfactory.jp/act/1127171

このサイトと同じ状況っぽいので

$ sudo apachectl configtest
Password:
AH00526: Syntax error on line 20 of /private/etc/apache2/extra/httpd-mpm.conf:
Invalid command 'LockFile', perhaps misspelled or defined by a module not included in the server configuration

同じだね。

$ sudo nano /private/etc/apache2/extra/httpd-mpm.conf

LockFile “/private/var/log/apache2/accept.lock”

をコメントアウトし、

$ sudo apachectl restart
$ ps aux | grep httpd
kuro             1286   0.1  0.0  2423376    228 s000  R+    9:53AM   0:00.00 grep httpd
_www              1272   0.0  0.0  2458072    872   ??  S     9:52AM   0:00.00 /usr/sbin/httpd -D FOREGROUND
root              1271   0.0  0.0  2450904   4960   ??  Ss    9:52AM   0:00.08 /usr/sbin/httpd -D FOREGROUND

できたね。

http://localhost/

を開くと、お約束の"It works!"が見れた。

このままだと/Library/WebServer/Documents/がドキュメントルートなので、ちょい面倒だから設定を変える。

諸々の設定は

http://tonop.cocolog-nifty.com/blog/2016/02/os-x-1011-el-ca.html

こちらを参考にした。

これで

http://locahost/~kuro/

のようにユーザのディレクトリにアクセスできるようになった。

続いてwsgiのインストール

$ pip3 install mod_wsgi
Collecting mod_wsgi
  Downloading mod_wsgi-4.5.18.tar.gz (2.5MB)
    100% |                 | 2.5MB 604kB/s 
Installing collected packages: mod-wsgi
  Running setup.py install for mod-wsgi ... done
Successfully installed mod-wsgi-4.5.18

簡単にpipで済ませてしまおう。

さて、今回まずはテスト用に

flasktest
    +-template
   |      +-index.html
   |        +-layout.html
    +-sample.py
    +-sample.wsgi

こういう感じの構成で用意した。

sample.wsgiの中身は

import sys, os
sys.path.append('~/kkuro/Sites/flasktest')

from sample import app as application

これだけで、sample.pyは特に手を加えない。

wsgiが入っていないと

http://localhost:5000/

で動作することを確認したら、おもむろに

http://[サーバのアドレス]:5000/でアクセス!

f:id:k-kuro:20170914113508p:image

できた。

このままではポート5000で占領してしまうのでポートのセッティングとディレクトリを調整してやればいけるはず


っと思ったがなんかおかしい。ポート5000で開いているってことはapacheを介していないってことだ。これはflaskで直接動いているわ。 ちょっとみなおす。

おかしい原因はpipでのインストールにありそうな気配がする(今回の環境はMacOSX)ので一旦

pip3 uninstall mod_wsgi

として、改めてbrewでインストールすることにする。

http://qiita.com/arc279/items/df28bd100cc2f72fad3c

なるほど

You must manually edit /etc/apache2/httpd.conf to include
  LoadModule wsgi_module /usr/local/opt/mod_wsgi/libexec/mod_wsgi.so

とな

/private/etc/apache2/httpd.conf

Include /private/etc/apache2/other/*.conf

のコメントを外しておいたので

/private/etc/apache2/other/python.conf

LoadModule wsgi_module /usr/local/opt/mod_wsgi/libexec/mod_wsgi.so
ScriptAlias /flasktest /Users/kuro/Sites/flasktest/sample.py

とかくと良さそうだ。

2017-09-13

[][][] IGV.jsのカスタマイズ

javascriptに関する知識が乏しいので、なかなか思うようにプログラムを作れないでいる。

https://github.com/igvteam/igv.js

ここにリポジトリは集約されているのだが、結構いっぱいの人がそれぞれにカスタマイズを加えながらプロジェクトが展開されており、追いきれない。公式にリリースされているバージョンでは若干機能が限定されているので、テストリリースをベースに、他のブランチで実装された機能を取り込んで、結局は自分なりのブランチを作ることになるんだろうか。ちょとハードルが高いかな。

現状

https://github.com/igvteam/igv.js/wiki

にリンクされた

http://igv.org/web/

からディレクトリを辿って

http://igv.org/web/test/multilocus/

ここにあるigv-multilocus.jsが一番機能的に良さげなので、こいつを利用しているが、もっと洗練されたものもありそうな気がする。

f:id:k-kuro:20170913113940p:image

https://github.com/igvteam/igv.js/issues/275

このブランチのbrowser.jsはかなり色々作り込んでいるっぽいのだが、どうだろうか。


というかスクリプトファイルのコードはかなり膨大で、そのうちのほんのすこししか実際には使っていないようで、スクリプト自体よりもその使い方の実例がほしいのだが、サンプルとして置かれているものはどれも似たようなものがばかりで、これっという決定打がない。

かといってjavascriptを一から勉強ってのもなあ。

[] javascriptでリンクをクリックしてイベントを起こすonclick

以前は

<a href="#" onclick="....">hoge</a>

みたいな書き方が普通だったのだが最近は違う書き方がいろいろあるらしい。

<span style="cursor: pointer" onclick="...">検索</span>

spanをつかった方法でもいいらしいがa hrefのほうが馴染みがあるので

<a href="javascript:void(0)" onclick="igv.browser.loadTrack({
                            url: 'https://data.broadinstitute.org/igvdata/BodyMap/hg19/IlluminaHiSeq2000_BodySites/brain_merged/accepted_hits.bam',
                            name: 'Brain (BodyMap)'})">Brain (BodyMap)</a>

こんな感じに書き換えてみる

[][] IGV.jsに読み込んだTrackをonclickでクリアする

<script type="text/javascript">
var removeTrackByName = function (trackName) {
    for (var i = 0, l = igv.browser.trackViews.length; i < l; i++) {
        var trackView = igv.browser.trackViews[i];
        if (trackView.track.name === trackName) {
            igv.browser.removeTrack(trackView.track);
            return;
        }
    }
};
</script>

こいつをhtml内に仕込む。

そのうえで

<a href="javascript:void(0)" onclick="removeTrackByName('DRR006760..bam')">Clear Tracks</a>

ここからクリックすればすでに読み込まれているDRR006760.bamのTrackが消える。

もともと読み込んでないときは何もおこらない。

ただし、複数の関数をonclickに仕込もうとすると、何もおこらないに引きずられて他の関数も沈黙するので要注意。

一番上にゲノムのポジション、次にDNA配列情報、その下にBamで一番下に遺伝子情報が表示されているわけだが、bamを読み込むときに標準ではその時表示されていたbamはそのままに、次に読み込んだbamが並べられてしまう。

実際のところ同じポジションの違う実験間の比較をしたいわけではないのでこれでは無駄に同じ内容のトラックが並んでしまい、意味がないわけで、クリックする度に書き換えていってもらうほうがいいのだね。

そのためにonclickでトラックを消す機能を盛り込みたいのだがこれが結構難しく、実装まで時間を要した。最終的に採用した方法はbase.htmlに

</script>
<script type="text/javascript">
var removeAllTracks = function () {
    for (var i = 2, l = igv.browser.trackViews.length; i < l; i++) {
        var trackView = igv.browser.trackViews[i].track;
            igv.browser.removeTrack(trackView);
            return;
        }
    };
</script>

こういうスクリプトを書いておき、読み込ませる側のリンクは

<a href="#"
              onclick='removeAllTracks();
                igv.browser.search("chr{{ samp.pos }}");
                igv.browser.loadTrack({ type: "alignment",
                sourceType: "file",
                url: "/static/exome/bam/{{ samp.Bam_file }}",
                indexURL: "/static/exome/bam/{{ samp.Bam_file }}.bai",
                name: "{{ samp.Bam_file }}",
                autoHeight: "true"})'>{{ samp.pos }}</a>

こんな感じ。これにより

f:id:k-kuro:20170913192231p:image

f:id:k-kuro:20170913192709p:image

こんな感じにクリックする度に内容が書き換えられる仕様となった。

スクリプトでi = 2にして上2つのトラックは消さず、その下のトラックを消す仕様としたところがミソ。


追記

<script type="text/javascript">
var removeBamTracks = function () {
        var trackView = igv.browser.trackViews[2].track;
            igv.browser.removeTrack(trackView);
            return;
};
</script>

こう書いたほうが無駄がない。

2017-09-12

[]VNCの起動、終了覚書

VNCで繋いだ環境の中で何やら暴走して固まってしまったので対処方法を

sshでサーバに直接接続し、

PS AX | grep vnc

で自分の使っているvncコネクションを探す。

26096 ?        Sl    12:57 /usr/bin/Xvnc :1 -desktop hogehoge.hagehage.jp:1 (kuro)-httpd /usr/share/vnc/classes -auth /home/kuro/.Xauthority -geometry 1024x768 -rfbwait 30000 -rfbauth /home/kuro/.vnc/passwd -rfbport 5901 -fp catalogue:/etc/X11/fontpath.d -pn

こんな感じ。

で、

$ vncserver -kill :1
Killing Xvnc process ID 26096
Xvnc seems to be deadlocked.  Kill the process manually and then re-run
    /usr/bin/vncserver -kill :1
to clean up the socket files.

こんな感じで接続を切る。

あたらしく接続を確保するには

$ vncserver :9 -geometry 1280x1024 -depth 24

こんな感じに

[][]サーバで起動

とりあえずここまでlocalhostでのテストであったが、いよいよサーバに上げてネットワーク経由で動作させてみる。

manage.pyの記述をちょっと変えるだけなんだけど。

from flask_script import Shell, Server
from flask_migrate import Migrate, MigrateCommand
from app import app, db, manager
from app.models import User, Role
import app.views.home

migrate = Migrate(app, db)

def make_shell_context():
    return dict(app=app, db=db, User=User, Role=Role)
manager.add_command("shell", Shell(make_context=make_shell_context))
manager.add_command("db", MigrateCommand)
manager.add_command("runserver", Server(host="192.168.1.1", port=5000))

if __name__ == '__main__':
    manager.run()

flask_scriptからServerをインポートし、

manager.add_command("runserver", Server(host="192.168.1.1", port=5000))

の1行を追加するだけ。

これで192.168.1.1:5000に外からアクセスできるようになる。(ポート5000は開けておく必要がある)

ユーザー認証とかかかってないので完全オープンなスペースに置くのは危険だが、ファイアウォール内でVPN経由のみのアクセスを許容しているようなサーバなら、問題はないのかもしれない。

[]SQLiteのつかいかた

$ sqlite3 [データベース]

で、[データベース]を開く

終了時は

sqlite> .quit
sqlite> .help
.backup ?DB? FILE      Backup DB (default "main") to FILE
.bail on|off           Stop after hitting an error.  Default OFF
.binary on|off         Turn binary output on or off.  Default OFF
.clone NEWDB           Clone data into NEWDB from the existing database
.databases             List names and files of attached databases
.dbinfo ?DB?           Show status information about the database
.dump ?TABLE? ...      Dump the database in an SQL text format
                         If TABLE specified, only dump tables matching
                         LIKE pattern TABLE.
.echo on|off           Turn command echo on or off
.eqp on|off            Enable or disable automatic EXPLAIN QUERY PLAN
.exit                  Exit this program
.explain ?on|off?      Turn output mode suitable for EXPLAIN on or off.
                         With no args, it turns EXPLAIN on.
.fullschema            Show schema and the content of sqlite_stat tables
.headers on|off        Turn display of headers on or off
.help                  Show this message
.import FILE TABLE     Import data from FILE into TABLE
.indexes ?TABLE?       Show names of all indexes
                         If TABLE specified, only show indexes for tables
                         matching LIKE pattern TABLE.
.limit ?LIMIT? ?VAL?   Display or change the value of an SQLITE_LIMIT
.log FILE|off          Turn logging on or off.  FILE can be stderr/stdout
.mode MODE ?TABLE?     Set output mode where MODE is one of:
                         ascii    Columns/rows delimited by 0x1F and 0x1E
                         csv      Comma-separated values
                         column   Left-aligned columns.  (See .width)
                         html     HTML <table> code
                         insert   SQL insert statements for TABLE
                         line     One value per line
                         list     Values delimited by .separator strings
                         tabs     Tab-separated values
                         tcl      TCL list elements
.nullvalue STRING      Use STRING in place of NULL values
.once FILENAME         Output for the next SQL command only to FILENAME
.open ?FILENAME?       Close existing database and reopen FILENAME
.output ?FILENAME?     Send output to FILENAME or stdout
.print STRING...       Print literal STRING
.prompt MAIN CONTINUE  Replace the standard prompts
.quit                  Exit this program
.read FILENAME         Execute SQL in FILENAME
.restore ?DB? FILE     Restore content of DB (default "main") from FILE
.save FILE             Write in-memory database into FILE
.scanstats on|off      Turn sqlite3_stmt_scanstatus() metrics on or off
.schema ?TABLE?        Show the CREATE statements
                         If TABLE specified, only show tables matching
                         LIKE pattern TABLE.
.separator COL ?ROW?   Change the column separator and optionally the row
                         separator for both the output mode and .import
.shell CMD ARGS...     Run CMD ARGS... in a system shell
.show                  Show the current values for various settings
.stats on|off          Turn stats on or off
.system CMD ARGS...    Run CMD ARGS... in a system shell
.tables ?TABLE?        List names of tables
                         If TABLE specified, only list tables matching
                         LIKE pattern TABLE.
.timeout MS            Try opening locked tables for MS milliseconds
.timer on|off          Turn SQL timer on or off
.trace FILE|off        Output each SQL statement as it is run
.vfsname ?AUX?         Print the name of the VFS stack
.width NUM1 NUM2 ...   Set column widths for "column" mode
                         Negative values right-justify

いろいろなステータスを見る

  • .show  設定がどうなっているか一覧
sqlite> .show
        echo: off
         eqp: off
  explain: off
     headers: off
        mode: list
   nullvalue: ""
      output: stdout
colseparator: "|"
rowseparator: "\n"
       stats: off
       width: 
  • .table  テーブル一覧
  • .schema  テーブル名と構造の一覧

検索結果表示の仕方を変える

  • .headers on  検索結果の一番上にカラムの名前を表示
  • . mode column  カラムの幅を揃えて表示する(excelで見るように縦に並ぶ)

いわゆるSQL文はMySQLとだいたい同じ。