herokuからShotgunへ定期的になにかする

  • 9
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

これはなに?

こちらの記事の続きです

【Python+heroku】Python入れてない状態からherokuで何か表示するまで
(前編)http://qiita.com/it_ks/items/afd1bdb792d41d0e1145
(後編)http://qiita.com/it_ks/items/ca6c7f6e8fc89e49e46d

Shotgun APIを云々したくてherokuの準備をしていたのでした。

補足1:使用経緯

株式会社GUNCY'Sさんの Road to Stingray というプロジェクトに
Shotgunセットアップ要員として参加しました。
ここではShotugnについて得られた知識等を(今後も進行系で)書いていけたらいいんじゃないかなと思っています。
あまり深いことはできていないので、範囲としては公式等で公開済みのもの(の日本語版)程度にとどまる気もしますが……

比較的小規模~中規模程度の案件かつ多拠点(遠隔作業)での事例ということになるかと思います。
(Shotgunは百人〜千人とかのプロダクションでも使われていますが、そうしたケースとはまた違う話になってくるのかなと)

補足2:タグ

qiitaでは「Shotgun」というタグは
同名のrubyのライブラリに関する話題ですでに使われていました。
ので、こちらは「ShotgunSoftrware」というタグにして棲み分けた方が、各方面しあわせなのかなと思います。
もし当記事を読まれた方でShotgunに関する記事を投稿されようという方は、
意図に賛同いただけましたら同様のタグ付けをしていただけると幸いです。

Shotgun

sg_01.png

https://shotgunsoftware.com/
(国内代理店)https://www.borndigital.co.jp/software/4070.html

さまざまな状況に対応できるようになっている反面、
セットアップなしの素のままではかなりジャジャ馬な一面もある逸品です(※個人の感想です)

API

githubで公開されています。

https://github.com/shotgunsoftware/python-api

wiki

https://github.com/shotgunsoftware/python-api/wiki

Pipeline Toolkit

上記APIをラップしつつ多様な外部ツールとの連携、Shotgun(web)の拡張を実現するために用意されたキット群の総称です。
https://support.shotgunsoftware.com/entries/94042238-Toolkit-Home-Page
今回は扱いません。

API同様githubで公開されていて、主に頭に「tk-(※ツールキットの略)」がついているものがそれです。
https://github.com/shotgunsoftware

こちらの記事(↓)で取り上げたftrackでいうところの
http://qiita.com/it_ks/items/374a320bc8282c7f65a8
「ftrack connect」に相当するものと言えます。

herokuスケジュール処理

公式ドキュメント:

Scheduled Jobs with Custom Clock Processes in Python with APScheduler
https://devcenter.heroku.com/articles/clock-processes-python

ちょうどqiitaにherokuとPythonでの記事が

Herokuで定期的に為替レートを取得し、ログをAmazon S3にアップロード
http://qiita.com/gumob@github/items/ca5d76186f94e592a5f0

定期処理自体は「APScheduler」モジュールを使います。
APSchedulerで検索すると、上位に出てくるのはコチラ。

APSchedulerで少し進んだジョブスケジューリング
http://qiita.com/yushin/items/a026626dbb291dd43dd8


定期処理の準備

まずは、herokuでの定期処理について設定していきます。
こちらに準じる形で進めます。

https://devcenter.heroku.com/articles/clock-processes-python

APScheduler導入

appのところまで降りて行ってvertual env をアクティベートしてから(←これは前回までの内容)、

pip install apscheduler

(sgenv) >pip install apscheduler
Collecting apscheduler
  Downloading APScheduler-3.0.5-py2.py3-none-any.whl (49kB)
    100% |################################| 53kB 2.4MB/s
Requirement already satisfied (use --upgrade to upgrade): six in c:\users\path\to\app\sgenv\lib\site-packages (from apscheduler)
Collecting futures (from apscheduler)
  Downloading futures-3.0.4-py2-none-any.whl
Collecting pytz (from apscheduler)
  Downloading pytz-2015.7-py2.py3-none-any.whl (476kB)
    100% |################################| 479kB 853kB/s
Collecting tzlocal (from apscheduler)
  Downloading tzlocal-1.2.tar.gz
Building wheels for collected packages: tzlocal
  Running setup.py bdist_wheel for tzlocal ... done
  Stored in directory: C:\Users\{user}\AppData\Local\pip\Cache\wheels\39\8d\3b\21db6b23bc7483a2e1cf391865427e8fbd09022
3d9db2cfab3
Successfully built tzlocal
Installing collected packages: futures, pytz, tzlocal, apscheduler
Successfully installed apscheduler-3.0.5 futures-3.0.4 pytz-2015.7 tzlocal-1.2

また、
requirements.txtに
APScheduler==3.0.0
と追記します。

clock.py

定期処理したいPythonスクリプトを用意します。
先ほどのドキュメントに準じて「clock.py」という名前にしています。

clock.py
from apscheduler.schedulers.blocking import BlockingScheduler

sched = BlockingScheduler()

@sched.scheduled_job('interval', minutes=3)
def timed_job():
    print('This job is run every three minutes.')

@sched.scheduled_job('cron', day_of_week='mon-fri', hour=17)
def scheduled_job():
    print('This job is run every weekday at 5pm.')

sched.start()

スクリプトの内容も、まずはドキュメントそのままで。

  • 3分に一回ログを出力
  • 毎日17時にログを出力 の二つのジョブがスケジュールされています。

Procfileへ追記

Procfileに下記の内容を追記します。

clock: python clock.py

clock:が設定名で、python〜〜が実行して欲しいコマンドです。
Procfileに記述しただけweb上のherokuダッシュボードに現れ、それぞれON/OFFできます。
無料のherokuでは一つしか稼動状態にできませんので、ひとつOFFにしたらひとつONにできるという感じです。
(ON/OFFするあたりは後述)

デプロイ

git add .してgit commit -m "hogehoge"してgit push heroku masterします。
addとcommitでは、さきほど追加したAPSchedulerの分いろいろずらずら流れます。

スケール

ドキュメントだと
heroku ps:scale clock=1
と書いてありますが、これで、さきほどProcfileに追記した「clock」を「1(=有効。ON)」にできます。
ただしすでに「web」というプロセスが動いているので、この状態で実行しても無効です。

>heroku ps:scale clock=1
Scaling dynos... failed
 !    Please verify your account in order to change resources (please enter a credit card) For more information, see htt
ps://devcenter.heroku.com/categories/billing Verify now at https://heroku.com/verify

ブラウザでherokuにログインして、すでにONになってるのをOFFにしてから、「clock」をONにします。
freedynos.png
ブラウザじゃなくてコマンドからやってもいいんですけど。ダッシュボード使いたかったんです。

確認

これで「clock」という処理が動き出してclock.pyの中身が実行されるようになりました。
ログを見てみたいと思います。herokuのログを見るときは↓

heroku logs

>heroku logs
2016-02-02T13:36:08.456761+00:00 app[web.1]:     res = instance.__dict__[self.name] = self.func(instance)
2016-02-02T13:36:08.456761+00:00 app[web.1]:   File "/app/.heroku/python/lib/python2.7/site-packages/django/utils/functional.py", line 33, in __get__
2016-02-02T13:36:08.456762+00:00 app[web.1]:   File "/app/.heroku/python/lib/python2.7/site-packages/django/core/urlresolvers.py", line 417, in url_patterns
2016-02-02T13:36:08.456763+00:00 app[web.1]:     patterns = getattr(self.urlconf_module, "urlpatterns", self.urlconf_module)
2016-02-02T13:36:08.456765+00:00 app[web.1]:   File "/app/.heroku/python/lib/python2.7/site-packages/django/core/urlresolvers.py", line 410, in urlconf_module
(中略)
2016-02-08T12:23:23.304081+00:00 heroku[api]: Scale to web=0 by 〜〜@gmail.com
2016-02-08T12:23:24.758985+00:00 heroku[api]: Scale to clock=1 by 〜〜@gmail.com
2016-02-08T12:23:29.007235+00:00 heroku[clock.1]: Starting process with command `python clock.py`
2016-02-08T12:23:29.571304+00:00 heroku[clock.1]: State changed from starting to up
2016-02-08T12:26:30.559161+00:00 app[clock.1]: This job is run every three minutes.

無事動き出しました

Shotgun APIを使う

無事動き出したら、clock.pyの中にShotgun APIを使った内容を書き足していきます。

APIの準備

githubのInstallingに従ってpipします。
https://github.com/shotgunsoftware/python-api#installing

pip install git+git://github.com/shotgunsoftware/python-api.git

バージョンを固定したい場合はこちらとのことなので、
pip install git+git://github.com/shotgunsoftware/python-api.git@v3.0.9
requirements.txtにはこちらを書きました。ちなみに当時v3.0.25

Shotgunにてスクリプトユーザを登録

Shotgunでの「ユーザ」は「HumanUser」と「ScriptUser」に分けられます。
前者がいわゆるユーザ、後者はPipeline Toolkitなどのスクリプトです。
herokuからアクセスしてもらう今回の内容もScriptUserとしてShotgunへアクセスするため
まずはこれを登録してあげます。

Shotgunにログインしたら、右上のメニューから「Scripts」を開き、「+スクリプト」。
sg_scriptuser.png

ここで登録する「スクリプト名」と、
登録後に得られる「アプリケーションキー」、
そしてライセンスされているShotgunサーバのURL(「https://{studio-name}.shotgunstudio.com」)
が次に必要になるので控えておきます。

あと、気分が盛り上がるのでScriptUserに適当なアイコンをつけてあげましょう。

herokuに環境変数登録

コマンドからでも(ryですがダッシュボードから。
一番右の「Settings」の「Config Variables」で環境変数を設定できます。

config_var_01.png
「Reveal Config Vars」を押して、隠れてるのを表示します。
すると↓こんなふうになります。
config_var_02.png
さきほど控えておいた「スクリプト名」「アプリキー」「URL」をそれぞれ登録しました。
スクリプト名まで覆っているあたりに無計画さが伺えます。

環境変数を読む

Pythonスクリプト中では、
os.environ.get('{変数名}')
のように書くことで、さきほど登録した環境変数を読めます。
clock.pyへはこういう感じで追記しました↓

import os

SG_KEY = os.environ.get('SG_KEY')
SG_SCRIPT = os.environ.get('SG_SCRIPT')
SG_SERVER = os.environ.get('SG_SERVER')

慣れてくると逆に import os を書き忘れたりするから注意です ←

shotgunオブジェクト取得

Shotgun() - Usage Example @Reference: Methods
https://github.com/shotgunsoftware/python-api/wiki/Reference%3A-Methods#usage-example

↑こちらの要領で、インポートしてインスタンスを受け取ります

import os
from shotgun_api3 import Shotgun

### get sg object ###
SG_KEY = os.environ.get('SG_KEY')
SG_SCRIPT = os.environ.get('SG_SCRIPT')
SG_SERVER = os.environ.get('SG_SERVER')

sg = Shotgun(SG_SERVER, SG_SCRIPT, SG_KEY)

Shotgunを検索して、結果を3分おきにプリント

今回は、「TEST」という名前のプロジェクトを検索して、それをログに出力することにします。

今回は返ってくる結果はひとつしかいらないので、
使うメソッドは find_one です。

sg.find_one("Project",[['name','is','TEST']],[])

ヒットしたら↓このようにShotgun上でのデータの種類(Entity Type)とIDとの辞書型で返ってきます
{'type': 'Project', 'id': 69}
※今回「TEST」のIDは69のようでした。

これを組み込んで出来上がったスクリプトがこんな感じ↓

clock.py
import os
from shotgun_api3 import Shotgun
from apscheduler.schedulers.blocking import BlockingScheduler

### get sg object ###
SG_SERVER = os.environ.get('SG_SERVER')
SG_SCRIPT = os.environ.get('SG_SCRIPT')
SG_KEY = os.environ.get('SG_KEY')
sg = Shotgun(SG_SERVER, SG_SCRIPT, SG_KEY)

sched = BlockingScheduler()

@sched.scheduled_job('interval', minutes=3)
def timed_job():
    testPrj = sg.find_one("Project",[['name','is','TEST']],[])
    print('every 3min search...',testPrj)

sched.start()

確認

pushしてheroku logsします。

2016-02-08T12:42:19.165168+00:00 heroku[clock.1]: State changed from crashed to starting
2016-02-08T12:42:23.381828+00:00 heroku[clock.1]: Starting process with command `python clock.py`
2016-02-08T12:42:24.077203+00:00 heroku[clock.1]: State changed from starting to up
2016-02-08T12:45:27.542367+00:00 app[clock.1]: ('every 3min search...', {'type': 'Project', 'id': 69})
2016-02-08T12:48:27.586542+00:00 app[clock.1]: ('every 3min search...', {'type': 'Project', 'id': 69})
2016-02-08T12:51:27.559255+00:00 app[clock.1]: ('every 3min search...', {'type': 'Project', 'id': 69})
(後略)

無事、検索してヒットした返り値が3分おきにプリントされてます。
定期的になにかするという目標を達しました。


まとめ

やったこと

  • Procfileにプロセス追加、ダッシュボード上で有効化
  • ShotgunAPIを使ったスクリプトを用意

ただし、heroku無料版は1日のうち6時間以上は必ずスリープする必要があるので、ずっと回すことはできません。

今後の展開

  • Shotgun APIの、find以外の処理(create、updateなど)も使う
  • 定期処理じゃないShotgun API利用
  • Shotgun APIと、また別のサービスのAPIとの連携

等が考えられます。

備考

find_one
https://github.com/shotgunsoftware/python-api/wiki/Reference%3A-Methods#find_one

検索に使うフィルタの書き方
Reference: Filter Syntax
https://github.com/shotgunsoftware/python-api/wiki/Reference%3A-Filter-Syntax