検索結果: pro

キバンインターナショナルでは、UstreamやLiveStreamを利用した生中継や、eラーニング教材としての動画撮影を行っています。その中でたまったノウハウのうち、生中継や動画撮影の場合にあると便利な小物を紹介していきます。本日ご紹介するのは、スケッチブックです。

何かと役に立つスケッチブック

このように、スタッフ側から出演者にTL(ツイッターのタイムライン)を拾って閲覧者ともコミュニケーションをとって、といった指示を出したり、(出演者は、番組の進行でいっぱいいっぱいなので、このような指示は、よくあります。)

スタッフから出演者へ指示をしたり

 

逆にスタッフ側から出演者にお願いをしたり。下は、放送が安定しなかったり、機材再接続などの理由で一旦再起動をしたいので番組中でのアナウンスをお願いしています。

スタッフサイドからのお願いを告知したり

 

このように、番組中では、様々な理由により、スタッフと番組出演者とのコミュニケーションが必要なのですが、身振り手振りでは絶対に正確に伝わりませんので、カンペとしてのスケッチブックが役に立ちます。特に生中継の場合に重宝しますが、録画コンテンツの撮影の場合でも、出演者に声がけして収録を止めてしまうと後々編集が面倒なので、収録を止めずにコミュニケーションできるので、便利です。
ウェブ動画力 ROIを最大化する動画マーケティングの実践的ノウハウ96という書籍でも、カンペの準備が役に立つと書かれています。単なるカンペであればもう少し小さいものでも良いのですが、今回、スケッチブックを紹介したいのは、その広大な面積でカンペ以外にもいろいろと重宝するのです。

 

例えば、以下のように番組中にフリップとして利用して、閲覧者への告知に利用したり、

さらには、このように番組内の告知に利用したり・・・

 

番組キャラクターを描いてみたり、と。

番組キャラクターを描いたり

他にもゲスト出演者にいろいろ描いてもらったり、サインもらったり、なかなか重宝します。そうそう、わざわざ写真をとってませんが、サインペンももちろん重要です。しかも、肝心な時に掠れたり、書けなくなったりするので予備サインペンは重要です。(こんな時にマーフィーの法則を思い出します。)

今回は、スケッチブックが超便利、というお話でしたが、その他にも折を見て役立ち小物を紹介していきたいと思いますので、ご期待ください。

なお、今回はスケッチブックのサンプルとして、どろひよチャンネル(http://www.ustream.tv/channel/drohiyo-ch?lang=ja_JP)さんが利用されていた物をお借りしました。どうもありがとうございます。(なお、どろひよチャンネルの次回放送は、2月9日21:00~ の予定です。こちらもよろしくお願い致します。)

今回の参考書籍

1/7に発売された,「USTREAMで会社をPRする本」.函館にも売っていないかなぁと色々な店を回ってみたのですが,文教堂書店函館昭和店で販売されていました!

「広告」コーナーに置いてありました

「広告」コーナーに置いてありました

ちなみに,書店の場所は,ここ.昭和タウンプラザ内にあります.

大きな地図で見る

書店の中も広く,函館市でも一番大きな書店です.

書店はかなり広いです

誰でもライブ(生中継)の動画を配信・視聴できる無料のサービスであるUSTREAMで商品・サービス・企業をPRして、ビジネスを加速させる方法が書かれており,USTREAMでビジネスに活用したい方に非常におすすめです.

ちなみに,僕はUSTREAMする際はiEEE1394カメラと三脚・ノートPC,インターネット回線にWiMAXを使用して生中継をしています.カメラはと三脚は,こんな感じです.

カメラと三脚

カメラと三脚

三脚の下に,ノートPCを設置し,WiMAXのモバイルルーターを起動させて生中継を行っています.

WiMAX回線は帯域制限が無く,更に回線速度も下り40Mbps,上り10Mbpsという高速で,外でUSTREAMをするには非常に最適です.以前はイーモバイルを使用して生中継をしていたのですが,帯域制限が厳しくなり,下手をするとすぐに帯域制限にひっかかってしまい,回線速度が落ちてしまうため現在はWiMAXを使用しています.函館でもWiMAXのエリアが徐々に広がってきていて,中継できる範囲も広がってきています.

ビデオカメラとノートPCとインターネット回線さえあれば,どこでも,かつ簡単に生中継できるのがUSTREAMです.

是非,「USTREAMで会社をPRする本」をご一読になっていただければと思います.

USTREAMで会社をPRする本

FAQ 「ユーザを自動ログインさせたい」を追加いたしました。ユーザごとの個別のURLを作成することで、ユーザを自動ログインさせることができます。この方法で作成したURLを他人に知られてしまうと、不正利用の原因になるのでご注意ください。

自動ログイン

自動ログイン

株式会社キバンインターナショナルhttp://kiban.jp)が運営しているパンダスタジオhttp://pandastudio.tv/)では、『パンダスタジオ100改善プロジェクト』と名付けて、日々、お客様の声を聞き、改善をおこなっています。

今回は、ご利用者さまアンケートを始めましたのでお知らせいたします。

▲アンケート原稿を画面に埋め込んでみました。また、こちらをクリックして頂ければ全画面で表示されます。

アンケートでお尋ねするポイントは3つ。

  1. スタッフについて
  2. スタジオについて
  3. お客様の求めるサービス・機材について

スタジオご利用者さまにご意見をお聞かせいただき、スタジオ運営に反映させることで改善をどんどん行っていきます。アンケート自体も、紙で行うのがよいか、それともeラーニングの仕組みを活用して行うのがよいか、改良を重ねてお客様のニーズに確実にお応えできるようにしたいと思います。

こうした細かいことを積み重ねて、パンダスタジオのご利用者さまに最高の体験をして頂けるよう、努力して参ります。

パンダスタジオ(http://pandastudio.tv/)では、ハイアングルでの映像をいれられるように、トラスの上に、USBカメラを追加しました。番組のオープニングやエンディングに映像をきりかえて利用することができます。

3.5メートルのハイアングルカメラをパンダスタジオに設置

▲3.5メートルのハイアングルカメラをパンダスタジオに設置

今回導入したLOGICOOL キューカム オービット 200万画素 カールツァイス社製レンズ採用 QCAM-200RXは、管理画面の矢印キーでカメラアングルも変更できます。

パンダスタジオのハイアングルカメラは、カメラアングルをリモートで操作

▲パンダスタジオのハイアングルカメラは、カメラアングルをリモートで操作

ズームも自由にコントロールすることができます。そのため、トラスの上(350cm)の位置にあったとしても、カメラを触ることなく、自由に画角、範囲を調整することができます。

ハイアングルカメラは、USB接続で、PCから全部操作できて便利

▲ハイアングルカメラは、USB接続で、PCから全部操作できて便利

Pythonを使う理由と作った物(ソース付)

CATEGORIES 北海道ラボby.a.takeuchi98 Comments2011.01.21

みなさん、Pythonってご存知でしょうか?
ニシキヘビ。。ではないです。
プログラミング言語のPythonです。

↓Pythonのロゴ。蛇が2匹です。

このPython。日本国内では、それほど知名度は高くないのですが、2010年で最も成長したプログラミング言語にも選ばれるなど、急速に人気の高まりつつある言語です。2011年にはPHPとC++を抜かして3位になれそうな勢いです!では、このPython、どうして急に人気がでてきたのでしょうか?理由は大きくわけて3つあると思います。

Pythonが急に普及し始めた理由

  1. GAEで動作する

    これが最大の理由だと思います。Googleのサーバを使って簡単にサービスを提供できます。1日あたり1GBまでの転送なら、料金は一切かかりません ※1。無料利用分を超えて使った場合も、課金設定(1日当たりの支払い額の上限)を設定するだけで、簡単にサーバを増強できます。ロードバランス、データベースのレプリケーション、そんなことは一切考える必要がありません。全部自動でやってくれます。固定費が不要なので、AmazonEC2なんかより、よっぽど敷居は低いです。

  2. 理解しやすさ

    これには異論が多いと思います。C、Java、PHPなどとは似ても似つかないソースコード。でも、Python、慣れてしまえば読みやすいですよ。Pythonはインデントに縛られた言語です。プログラムの構造と見た目(インデント)が必ず一致しているので、誰が書いても似たようなソースコードになります。慣れないうちは思うように書き進められないかもしれませんが、しばらく使い込めば「読みやすいコード」が自然にかけるようになっているはずです。

  3. 遅さは問題ではなくなった

    Pythonはスクリプト言語です。コンパイル言語と比べると桁違いに遅いです。でも、Webアプリケーションのボトルネックって、言語ではないですよね?多くの場合、データベースアクセスや、ネットワーク通信、マルチメディアファイルのダウンロードがボトルネックです。スクリプト言語の遅さが問題になっていたのは10年前の話です。今は、PHPだって、RoRだって、Pythonだって実用上問題ない速度で動作します。1番にもつながりますが、言語の速度ではなく、スケールアウトするかどうか、つまり、サーバの台数に応じてきちんと性能が伸びるかが問題なのです。

※1 CPUの使用時間(無料分: 6.5時間/日)なども課金対象ですが、通信量制限(無料分: 1GB/日)が最も課金対象になりやすいです。

で、使ってみた。

Pythonを使う○○個の理由とか並べてても、実際に使ってみないと説得力がないので、実際につかってみた。ソースコードも全文貼り付けていますが、初心者が書いたコードなのであまり信用しないで頂けると幸いです。

1. サーバ監視

サーバ監視

サーバ監視

GAEからサーバの監視をしてみた。HTTPリクエストの応答時間を計るだけのシンプルなサービスだが、安定して稼動している。社内のサーバは、Zabbixなどを活用してデータセンタ内からきっちり監視しているが、データセンタと外部との回線の障害などに関しては、外部からの監視が有用だ。(スクリーンショットに掲載したデータは私が個人的に借りているサーバものです)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
#!/usr/bin/env python

import cgi
import datetime
import wsgiref.handlers
import urllib
import appengine_utilities.sessions
import os

from datetime import datetime
from datetime import timedelta
from google.appengine.api.urlfetch import fetch
from google.appengine.ext import db
from google.appengine.api import users
from google.appengine.ext import webapp
from google.appengine.api import images
from google.appengine.api.labs import taskqueue

class Site(db.Model):
  date = db.DateTimeProperty(auto_now_add=True)
  name = db.StringProperty()
  url  = db.URLProperty()

class Log(db.Model):
  date = db.DateTimeProperty(auto_now_add=True)
  response = db.FloatProperty()
  url = db.URLProperty()

class MainPage(webapp.RequestHandler):
  def get(self):
    session = appengine_utilities.sessions.Session()
    if "admin" in session:
      self.response.out.write('<html><head><title>Response Monitor!!</title></head><body>')
      self.response.out.write('<form method="post" action="/"><input type="submit" value="logout"/></form>')
      self.response.out.write('[ <a href="http://blog.elearning.co.jp?s=add&amp;search_404=1">+</a> ]<br />')
      sites = db.GqlQuery("SELECT * FROM Site ORDER BY date DESC LIMIT 10")
      for site in sites:
        self.response.out.write('[ <a href="#" onclick="if(confirm(\'Are you sure to delete it?\'))location.href=\'/delete?id=%s\'">delete</a> ] ' % (site.key()))
        self.response.out.write('<a href="/detail/%s">%s</a> [ %s ]<br />' % (site.url, site.name, site.url))
      self.response.out.write("</body></html>")
    else:
      self.response.out.write('<html><head><title>Response Monitor!!</title></head><body>')
      self.response.out.write('<form method="post" action="/"><input type="password" name="password"/><input type="submit" value="login"/></form>')
      sites = db.GqlQuery("SELECT * FROM Site ORDER BY date DESC LIMIT 10")
      for site in sites:
        self.response.out.write('<a href="/detail/%s">%s</a> [ %s ]<br />' % (site.url, site.name, site.url))
      self.response.out.write("</body></html>")
     
  def post(self):
    session = appengine_utilities.sessions.Session()
    if self.request.get("password") == "hakodate2010":
      session["admin"] = 1
    else:
      if "admin" in session:
        del session["admin"]
    self.redirect('/')

class Detail(webapp.RequestHandler):
  def get(self, key):
    self.response.out.write('<html><head><title>Log for %s</title></head><body>' % urllib.unquote(key))
    self.response.out.write("Log for '%s'" % urllib.unquote(key))
    self.response.out.write("<br />")
    responseMap = {};
    for i in range(0,4):
      query = db.GqlQuery("SELECT * FROM Log WHERE url = :1 ORDER BY date DESC LIMIT %s, 1000" % (1000 * i) , urllib.unquote(key) );
      for log in query:
        log.date+=timedelta(hours=9)
        day = str(log.date)[0:10]
        hour = str(log.date)[11:13]
        if day not in responseMap:
          responseMap[day] = {}
        if hour not in responseMap[day]:
          responseMap[day][hour] = {}
        responseMap[day][hour][log.date] = log.response

    self.response.out.write('<table border=1 style="border-style:solid ">')
    self.response.out.write("<tr><td></td>")
    for day in sorted(list(responseMap)):
      self.response.out.write("<td>")
      self.response.out.write(day)
    self.response.out.write("</td></tr>")

    for hour in range(0,24):
      hour = "%02d" % hour
      self.response.out.write("<tr>")
      self.response.out.write("<td>%s</td>" % hour)
      for day in sorted(list(responseMap)):
        self.response.out.write('<td valign="top">')
        if hour in responseMap[day]:
          limitter = 12
          for log in sorted(list(responseMap[day][hour])):
            limitter = limitter - 1
            if limitter < 0: break
            if responseMap[day][hour][log] == 0:
              self.response.out.write("<font color=red>")
              self.response.out.write(str(log)[14:19] + " ERR ")
            elif responseMap[day][hour][log] < 1.5 and responseMap[day][hour][log] != 0:
              self.response.out.write("<font>")
              self.response.out.write(str(log)[14:19] + " %.3f" % responseMap[day][hour][log])
            elif responseMap[day][hour][log] < 3:
              self.response.out.write('<font color="#FF8135">')
              self.response.out.write(str(log)[14:19] + " %.3f" % responseMap[day][hour][log])
            else:
              self.response.out.write('<font color="#FFBF00">')
              self.response.out.write(str(log)[14:19] + " %.3f" % responseMap[day][hour][log])
            self.response.out.write("</font><br />")
        self.response.out.write("</td>")
      self.response.out.write("</tr>")
    self.response.out.write("</table>")
    self.response.out.write('<div><a href="/">back</a></div>')
    self.response.out.write('</body></html>')

class Cron(webapp.RequestHandler):
  def get(self):
    size = 5
    sites = db.GqlQuery("SELECT * FROM Site ORDER BY date DESC")
    for i in range(0, sites.count(), size):
      params = {}
      for j in range(0, size):
        if i + j >= sites.count():
          break
        params["url"+str(j)] = sites[i+j].url
      taskqueue.add(url='/worker', params = params)

class Worker(webapp.RequestHandler):
  def post(self):
    if(int(self.request.headers.environ['HTTP_X_APPENGINE_TASKRETRYCOUNT']) > 0):
      return
    for key in self.request.arguments():
      try:
        url = self.request.get(key)
        fetchAndLog(url)
      except:
        pass
 
class Add(webapp.RequestHandler):
  def get(self):
    self.response.out.write("""
      <form action="/add" method="post" enctype='multipart/form-data'>
        <div>Name: <input type="text" name="name" /></div>
        <div>URL: <input type="text" name="url" /></div>
        <div><input type="submit" value="submit"/></div>
      </form>"""
)
  def post(self):
    if self.request.get('name') != '' or self.request.get('url') != '':
      site = Site()
      site.url = self.request.get('url')
      site.name = self.request.get('name')
      site.put();
    self.redirect('/')

class Delete(webapp.RequestHandler):
  def get(self):
    Site.get(self.request.get('id')).delete()
    self.redirect('/')

def fetchAndLog(url):
  log = Log()
  log.url = url
  log.response = 0.0
  log.put()
  start = datetime.now()
  ret = fetch(url = url, deadline = 30)
  end = datetime.now()
  diff = end - start
  log.response = diff.seconds + diff.microseconds / 1000000.0
  log.put()

application = webapp.WSGIApplication([
  ('/', MainPage),
  ('/add', Add),
  ('/delete', Delete),
  ('/cron', Cron),
  ('/worker', Worker),
  ('/detail/(.*)', Detail),
], debug=True)

def main():
  wsgiref.handlers.CGIHandler().run(application)

if __name__ == '__main__':
  main()

2. ZIPのアップローダー

GAEって静的ファイルおけないんでしょ?ってよく言われるので、アップロードしたZIPファイルを展開して公開するサービスを作ってみた。ファイルはデータストアに保存しているので厳密な意味では静的ファイルではない。(静的ファイルをデプロイすることもできますが、その場合、GAEの管理ツールを使わないとファイルを更新できません。)

zipアップローダー

zipアップローダー

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
#!/usr/bin/env python

import cgi
import datetime
import wsgiref.handlers

from google.appengine.ext import db
from google.appengine.api import users
from google.appengine.ext import webapp
from google.appengine.api import images

class Entry(db.Model):
    content = db.StringProperty(multiline=True)
    date = db.DateTimeProperty(auto_now_add=True)
    data = db.BlobProperty()
    contentType = db.StringProperty()

class MainPage(webapp.RequestHandler):
    def get(self):
        self.response.out.write('<html><body>[ <a href="http://blog.elearning.co.jp?s=add&amp;search_404=1">+</a> ]<br />')
        entries = db.GqlQuery("SELECT * FROM Entry ORDER BY date DESC LIMIT 10")
        for entry in entries:
            self.response.out.write('[<a href="#" onclick="if(confirm(\'Are you sure to delete it?\'))location.href=\'/delete?id=%s\'">d</a>][<a href="http://blog.elearning.co.jp?s=edit?id=%s&amp;search_404=1">e</a>] %s<br />' % (entry.key(), entry.key(), cgi.escape(entry.content)))
            if entry.data:
                self.response.out.write('<a href="/image/%s/data"><img src="/image/%s/thumnail"/></a><br />' % (entry.key(),entry.key()))
        self.response.out.write("</body></html>")

class Get(webapp.RequestHandler):
    def get(self, key):
        image = db.get(key)
        if type == 'thumnail':
            self.response.headers['Content-Type'] = 'image/jpeg'
            self.response.out.write(image.thumnail)
        else:
            if image.contentType:
                self.response.headers['Content-Type'] = image.contentType.encode('utf-8')
            else:
                self.response.headers['Content-Type'] = 'image/jpeg'
            self.response.out.write(image.data)

class Add(webapp.RequestHandler):
    def get(self):
        self.response.out.write("""
            <form action="/add" method="post" enctype='multipart/form-data'>
                <div><textarea name="content" rows="3" cols="60"></textarea></div>
                <div><input type="file" name="file"/></div>
                <div><input type="submit" value="submit"/></div>
            </form>"""
)
    def post(self):
        if self.request.get('content') != '' or self.request.get('file') != '':
            entry = Entry()
            entry.content = self.request.get('content')
            if self.request.get('file'):
                entry.data = self.request.POST.get('file').file.read()
                img = images.Image(entry.data)
                img.resize(60, 100)
                entry.contentType = self.request.body_file.vars['file'].headers['content-type']
            entry.put();
        self.redirect('/')

class Edit(webapp.RequestHandler):
    def get(self):
        post = Entry.get(self.request.get('id'))
        self.response.out.write("""
            <form action="/edit?id=%s" method="post">
                <div><textarea name="content" rows="3" cols="60">%s</textarea></div>
                <div><input type="submit" value="submit"/></div>
            </form>"""
% (self.request.get('id'), post.content))
    def post(self):
        entry = Entry.get(self.request.get('id'))
        entry.content = self.request.get('content')
        entry.put()
        self.redirect('/')

class Delete(webapp.RequestHandler):
    def get(self):
        Entry.get(self.request.get('id')).delete()
        self.redirect('/')

application = webapp.WSGIApplication([
    ('/', MainPage),
    ('/add', Add),
    ('/get/([-\w]+)', Get),
], debug=True)

def main():
    wsgiref.handlers.CGIHandler().run(application)

if __name__ == '__main__':
    main()

3. ブログシステム

ブログシステムを作ってみた。一覧表示ページ、個別記事ページ、タグページぐらいしかないが、一般的な用途ならこれぐらいでいい気がする。RSSを配信しているので、FeedTweetなどを使ってTwttterに更新履歴を流したりもできる。Wordpressとなどと比べると機能も少ないし、デザインもテンプレート化できていないが、レスポンスはよいし、満足して使っている。

python-blog

python-blog

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from google.appengine.ext import webapp
from google.appengine.ext.webapp import util
from google.appengine.ext import db
from google.appengine.api import users
import urllib, datetime, re
step = 10
title = "Python blog system"

class AuthHandler(webapp.RequestHandler):
  def get(self, key = ""):
    if users.get_current_user() == None:
      self.write("<a href="%s">Sign in or register</a>." % users.create_login_url("/admin"))
    elif users.is_current_user_admin() != True:
      self.write('Your account %s is not admin. <a href="%s">Log out</a> and log in with an admin account.' % (users.get_current_user(), users.create_logout_url("/admin")))
    else:
      if key:
        self.get2(key)
      else:
        self.get2()
  def post(self, key = ""):
    if users.is_current_user_admin():
      if key:
        self.post2(key)
      else:
        self.post2()
  def write(self, str):
    self.response.out.write(str)

class MainHandler(AuthHandler):
  def get(self, pageStr):
    try:
      page = int(pageStr)
    except ValueError:
      page = 0
    printHeader(self, title)
    self.write('<h1><a href="/">%s</a></h1>' % title)
    if users.is_current_user_admin():
      self.write('<h2>[<a href="http://blog.elearning.co.jp?s=&amp;search_404=1">New</a>] [<a href="%s">Log out</a>]</h2>'  % users.create_logout_url("/"))
    entries = Entry.all().order("-datetime").fetch(step + 1, page * step)
    for entry in entries[:step]:
      printEntry(self, entry)
    if len(entries) > step:
      self.write('[ <a href="/%d"> Next %d</a> ]' % (page + 1, step))
    if page > 0:
      self.write('[ <a href="/%d"> Prev %d</a> ]' % (page - 1, step))
    printFooter(self)

class RSSHandler(AuthHandler):
  def get(self, pageStr):
    self.write(
u"""< ?xml version="1.0" encoding="utf-8"?>
<rdf:rdf xmlns:dc="http://purl.org/dc/elements/1.1/"
  xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
  xmlns="http://purl.org/rss/1.0/">
  <channel rdf:about="http://python-blog-system.appspot.com/">
    <title>%(title)s</title>
    <link>http://python-blog-system.appspot.com</link>
    <description>%(title)s</description>
    <dc:language>ja</dc:language>
    <dc:creator>N/A</dc:creator>
    <dc:date>%(now)s</dc:date>
  </channel>"""
% {'now' : '2010-12-31T16:57:12+09:00', 'title' : title})
    for entry in Entry.all().order("-datetime").fetch(30):
      self.write("""  <item rdf:about="http://python-blog-system.appspot.com/entry/%(key)s">
    <title>%(title)s</title>
    <link>http://python-blog-system.appspot.com/entry/%(key)s</link>
    <description>%(body)s</description>
    <dc:date>%(datetime)s</dc:date>
  </item>
"""
% {"key" : entry.key(), "title" : h(entry.title), "body" : h(entry.body), "datetime" : entry.formattedDatetimeInJST})
    self.write("</rdf:rdf>")

class AdminHandler(AuthHandler):
  def get2(self, key):
    self.redirect("/")

class PostHandler(AuthHandler):
  def get2(self,key = ""):
    entry = Entry.get(key) if key != '' else Entry()
    title = "Edit Entry" if key!= '' else "New Entry"
    deleteButton = """<input type="button" value="delete" onclick="if(confirm('Are you sure to delete it?'))location.href='/delete/%s'"/>""" % key if key!= '' else ''
    printHeader(self, title)
    self.write("<h1>%s</h1>" % title)
    self.write(u"""<form method="post" action="/post/%(key)s">
<h2>タイトル</h2><input type="text" name="title" value="%(title)s" style="width:400px"/>
<h2>本文</h2><textarea name="body" style="width:400px;height:300px;">%(body)s</textarea>
<h2>タグ</h2><input type="text" name="tags" value="%(tags)s" style="width:400px"/>
<h2>画像 [<a href="http://blog.elearning.co.jp?s=uploader&amp;search_404=1" target="_blank">uploader</a>]</h2>
<div>%(deleteButton)s<input type="submit" value="submit"/></div></form>"""
% {"key" : key, "title" : entry.title, "body" : entry.body, "tags" : entry.tagStr(), "deleteButton":deleteButton})
    printFooter(self)
  def post2(self, key = ""):
    if self.request.get("title") != '' and self.request.get("body") != '':
      entry = Entry.get(key) if key != '' else Entry()
      entry.title = self.request.get("title")
      entry.body = self.request.get("body")
      entry.tags = []
      for tagStr in self.request.get('tags').replace(u' ',' ').replace('  ',' ').replace(',',' ').split(' '):
        tag = Tag.all().filter("tag =", tagStr).get()
        if tag == None:
          tag = Tag(tag = tagStr)
          tag.put()
        entry.tags.append(tag.key())
      entry.put()
    self.redirect('/')

class PostCommentHandler(AuthHandler):
  def post(self, key):
    if self.request.get("comment") != '':
      Comment(
        entry = Entry.get(key),
        comment = self.request.get("comment"),
        delpass = self.request.get("delpass"),
        nickname = self.request.get("nickname")
      ).put()
    self.redirect("/entry/%s" % key)

class DeleteHandler(AuthHandler):
  def get2(self, key):
    db.delete(Entry.get(key))
    self.redirect('/')

class DeleteCommentHandler(AuthHandler):
  def post(self, key):
    comment = Comment.get(key)
    entry_key = comment.entry.key()
    self.write(self.request.get("delpass"))
    self.write(comment.delpass)
    if self.request.get("delpass") == comment.delpass:
      db.delete(comment)
    self.redirect('/entry/%s' % entry_key)

class TagHandler(AuthHandler):
  def get(self, key):
    tagStr = urllib.unquote(key).decode('utf-8')
    title = "Python blog system / %s" % tagStr
    printHeader(self, title);
    tag = Tag.all().filter("tag =", tagStr).get()
    self.write('<h1><a href="/">Python blog system</a> / %s</h1>' % h(tagStr))
    if tag:
      for entry in tag.entries:
        printEntry(self, entry)
    else:
      self.write("<h2>Tag %s does not exist</h2>" % h(tagStr))
    printFooter(self)

class EntryHandler(AuthHandler):
  def get(self, key):
    entry = Entry.get(key)
    printHeader(self, "%s / %s" % (title, entry.title));
    self.write('<h1><a href="/">%s</a></h1>' % title)
    printEntry(self, entry, commentDetail = True)
    printFooter(self)

class UploaderHandler(AuthHandler):
  def get2(self, key):
    printHeader(self, "Uploader")
    self.write("<h1>Uploader</h1>")
    for image in Image.all():
      self.write('<h2><img src="/image/%(key)s"/><br /><input type="text" value="[img:%(key)s]" style="width:300px;font-size:x-small"/><input type="button" value="delete" onclick="location.href=\'/deleteImage/%(key)s\'"/></h2>' % {"key":image.key()})
    self.write(u'<h2><form action="/uploader" enctype="multipart/form-data" method="post"><input type="file" name="file"/><input type="submit" value="Upload"/></form></h2>')
    printFooter(self)
  def post2(self, key):
    if self.request.get('file'):
      self.write("hoge")
      image = Image()
      image.image = self.request.POST.get('file').file.read()
      image.contentType = self.request.body_file.vars['file'].headers['content-type']
      image.put()
    self.redirect('/uploader')

class DeleteImageHandler(AuthHandler):
  def get2(self, key):
    Image.get(key).delete()
    self.redirect('/uploader')

class ImageHandler(AuthHandler):
  def get(self, key):
    image = Image.get(key)
    self.response.headers['Content-Type'] = image.contentType.encode('utf-8')
    self.response.out.write(image.image)

def printEntry(self, entry, commentDetail = False):
  user = users.get_current_user()
  if user:
    editLink = '[<a href="/post/%s">edit</a>]' % entry.key()
  else:
    editLink = ''
  self.write('<div class="entry">\n<div class="entryHeader">\n')
  self.write('<h2 class="title"><a href="/entry/%(key)s">%(title)s</a> %(editLink)s</h2> <div class="entryDate">%(datetime)s</div>'
    % {"key" : entry.key(), "title" : h(entry.title), "editLink" : editLink, "datetime" : entry.formattedDatetimeInJST})
  self.write('</div>\n')#header
  self.write(replaceImages(linkURLs(nl2br(h(entry.body)))));
  self.write('\n<div class="entryFooter">tag:\n')
  for tag in entry.tags:
    tagObj = Tag.get(tag)
    self.write('<a href="/tag/%s"><span class="tag">%s</span></a>\n' % (urllib.quote_plus(tagObj.tag.encode('utf-8')) ,h(tagObj.tag)))
  if commentDetail:
    self.write(u'<h2>コメント</h2><div class="comments"><a name="comments">\n')
    for comment in entry.comments.order('datetime'):
      if comment.nickname == None or comment.nickname == "":
        comment.nickname = "Anonymous"
      delbutton = u"""
        <div style="float:right">
          <form method="post" name="form" action="/deleteComment/%(key)s">
          <input type="text" name="delpass" class="delcommentpassword"/>
          <input type="button" onclick="if(confirm('本当に削除しますか?'))form.submit()" value="削除" class="delcommentbutton"/>
          </form>
        </div><br clear="all"/>"""
% {"key":comment.key()}
      self.write('<h3 class="comment">%s: %s %s</h3>' % (comment.nickname, comment.comment, delbutton))
    self.write(u'<div style="width:84px;float:left;font-size:xx-small;position:relative;top:6px;">名前</div>')
    self.write(u'<div style="width:184px;float:left;font-size:xx-small;position:relative;top:6px;">コメント</div>')
    self.write(u'<div style="width:100px;float:left;font-size:xx-small;position:relative;top:6px;">削除パス</div>')
    self.write(u'<br clear="all"/>')
    self.write(u'<form action="/postComment/%s" method="post" style="padding:0">' % entry.key())
    self.write(u'<input type="text" name="nickname" style="width:80px;"/>')
    self.write(u'<input type="text" name="comment" style="width:180px;"/>')
    self.write(u'<input type="text" name="delpass" style="width:50px;"/>')
    self.write(u'<input type="submit" value="投稿" style="width:50px"/>')
    self.write("</form></a></div>\n")
  else:
    self.write(u'<a href="/entry/%s#comments">コメント(%s)</a>\n' % (entry.key(), entry.comments.count()))
  self.write("</div>\n")#footer
  self.write("</div>\n")#entry

def urlReplacer(match, limit = 45):
  return '<a href="%s" target="_blank">%s</a>' % (match.group(), match.group()[:limit] + ('...' if len(match.group()) > limit else ''))

def linkURLs(str):
  return re.sub(r'([^"]|^)(https?|ftp)(://[\w:;/.?%#&=+-]+)', urlReplacer, str)

def replaceImages(str):
  return re.sub(r'\[img:(.*)\]', r'<img src="http://blog.elearning.co.jp?s=\1&amp;search_404=1" style="max-width:400px"/>', str)

def printHeader(self, title):
  self.write("""< ?xml version="1.0" encoding="UTF-8"?>
< !DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja">
<head>
  <link rel="alternate" type="application/rss+xml" title="RSS" href="rss"/>
  <meta http-equiv="content-script-type" content="text/javascript"/>
  <meta http-equiv="content-type" content="text/html; charset=utf-8"/>
  <title>%s</title>
  <link rel="stylesheet" type="text/css" href="/css/style.css"/>
  <meta name = "viewport" content = "width=420"/>
  <script type="text/javascript">
  var _gaq = _gaq || [];
  _gaq.push(['_setAccount', 'UA-20245912-2']);
  _gaq.push(['_trackPageview']);
  (function() {
    var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
    ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
    var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
  })();
  </script>"""
% (h(title)))
  self.write("</head>\n<body>\n");

def printFooter(self):
  self.write(u'<div>developed by <a href="http://php6.jp/python/">python練習帳</a></div>\n</body>\n</html>')

def nl2br(str):
  return str.replace('\r\n','\n').replace('\n','<br />\n')

class Entry(db.Model):
  title = db.StringProperty(default = "")
  body = db.TextProperty(default = "")
  tags = db.ListProperty(db.Key)
  datetime = db.DateTimeProperty(auto_now_add = True)
  @property
  def formattedDatetimeInJST(self):
    return (self.datetime + datetime.timedelta(hours=9)).strftime("%Y-%m-%d %H:%M:%S")
  def tagStr(self):
    return " ".join([Tag.get(x).tag for x in self.tags])

class Tag(db.Model):
  tag = db.StringProperty()
  @property
  def entries(self):
    return Entry.all().filter('tags', self.key()).order('-datetime')

class Comment(db.Model):
  comment = db.TextProperty(default = "")
  entry = db.ReferenceProperty(Entry, collection_name = 'comments')
  user = db.UserProperty()
  datetime = db.DateTimeProperty(auto_now_add = True)
  delpass = db.TextProperty()
  nickname = db.TextProperty()

class Image(db.Model):
  image = db.BlobProperty()
  contentType = db.StringProperty()

def main():
  application = webapp.WSGIApplication([
    ('/tag/(.*)', TagHandler),
    ('/entry/(.*)', EntryHandler),
    ('/admin/?(.*)', AdminHandler),
    ('/postComment/?(.*)', PostCommentHandler),
    ('/post/?(.*)', PostHandler),
    ('/rss/?(.*)', RSSHandler),
    ('/deleteComment/?(.*)', DeleteCommentHandler),
    ('/deleteImage/(.*)', DeleteImageHandler),
    ('/delete/?(.*)', DeleteHandler),
    ('/uploader/?(.*)', UploaderHandler),
    ('/image/(.*)', ImageHandler),
    ('/(.*)', MainHandler)
  ], debug=True)
  util.run_wsgi_app(application)

def h(html):
  return html.replace('&','&amp;').replace('< ','&lt;').replace('>','&gt;').replace('"','&quot;')

if __name__ == '__main__':
  main()

おわりに

Pythonの良さをざっと紹介しましたが、どのように感じられましたでしょうか?C、Java、PHPなどに慣れた人にとっては異質なソースコードだと思います。でも、一度慣れてしまうと、すごく合理的で分かりやすい言語だと思います。このブログにPython関連の記事を書き続けるのは気が引けますので、興味を持たれた方はPython練習帳をご覧いただければと思います。

いつでもどこでもeラーニングが利用できるようになるには、完全にシステムがオフライン対応版になるか、出先でも自宅やオフィスのようにブロードバンドで接続される必要があります。

出先で高速接続ができるように、常に投資を志津つけてきました。最近、私自身は、Wimax(日本電気 モバイルWiMAXルータ AtermWM3500R プラチナブラック PA-WM3500R(AT)Bと、DoCoMoもモバイルルータ(BUFFALO ポータブルWiFiルーター Portable Wi-Fi DWR-PG
)を持ち歩いています。以前は、eモバイルのルーターを持ち歩いていたので、同時に3つのルーターを持ち歩いていた時期があります。

基本的には都内であれば、Wimaxで利用して、地下鉄やWiMaxが入らない地方であれば、電波のエリアが広いDoCoMoのルータを持ち歩いています。eモバイルは速度の遅さと、すぐに帯域制限がかかって使い物にならないため、最近は放置して持ち歩かず、最低料金だけを支払っていました。

ところが最近、eモバイルが、DC-HSDPA規格(下り最大:42Mbps*2、上り最大:5.8Mbps*2)に対応というD41HWを発売しており、もしかして最強のモバイル環境では、、、と気になっていました。

eモバイルの42MB対応のモバイルルーター D41HW

eモバイルの42MB対応のモバイルルーター D41HW

同じことを気にしているユーザーも居るもので、山手線の全駅で、WiMAXと、eモバイルのどちらが早いかを検証したサイトがありました。

WiMax VS. eモバイルの対決は、WiMaxの圧勝!

WiMax VS. eモバイルの対決は、WiMaxの圧勝!

快適にアプリケーションを使ってもらうためには、最適なメモリー管理と動作のボトルネックを解消することです。

最適な学習環境を提供するために、SmartBrainはメモリー管理こそ無いものの、パフォーマンスチューニングを日々行っています。
実は、使いやすい・気持ち良い ・楽しいは学習にかかわらず仕事でも重要ですよね。

今回は、iPhoneの開発で利用するLeaksについてちょっとした使い方をまとめます。
memory warning Level=**みたいなものが出てるそこのあなた!!
Leaksを使ってメモリーリークを解消しましょう。

(次回はTime Profilerについての予定)
それでは、まずXCodeを起動してLeaksを実行しましょう。
(当然何かしらのプロジェクトを開いてないと実行できません)



実行をひらきましょう



パフォーマンスツールを使って実行からLeaksをクリック

すると以下のような画面が立ち上がります。
(通常立ち上がりと同時にシミュレータとサンプリングが開始されます。)



Leaks起動数秒後

起動しただけなのに、もう漏れてますね。これはいけません。メモリーリークがあると、”落ちる”減少が頻発します。またNSUserDefaultsに正しく保存できないなど、 百害あって一利なしです!

それでは、メモリーリークを根絶しましょう。画面左側の「水道管が破裂」したようなアイコンのあるエリアをクリックしましょう。すると以下の画面に変わるはずです。



左上のLeaksボタンを押すとこのような画面になります (右ペインは画面上部のViewの一番右を押しましょう)

完全にリークしてますね。右ペインには下から main -> TopMenu…. -> ViewCon….とメモリーリークを起こしている部分までのつながりが表示されています。 (人形の黒いアイコンが付いているものがユーザー定義の関数です)それでは、右ペインの ViewController4 viewDidLoadを見てみましょう。



メモリーリーク箇所を特定

NSMutableArrayが66.7%も漏れていますね。 きっとreleaseのし忘れでしょう。といった具合に リークしている可能性がある箇所を分かりやすく表示してくれるのがLeaksです。

ちなみに、メモリーリークチェックはデフォルトで10秒置きに起動します。すぐにチェックしたい!という人はしたの画像に有るように「Check for Leaks Now」を押してあげるとすぐにチェックしてくれます。



すぐにメモリーリークをチェックしたい場合

地道にメモリーリークは潰していくしかありませんね。

Ustreamがソフトバンクによって日本語化されてから、Ustreamの使い道は大きく分けて二つになってきました。
個人が自分の配信したい内容を自由に配信したり、ポッドキャスト的な自主番組を作るという流れと、企業がUstreamを利用して自社PRなどに使うというものです。

個人と企業がUstreamを使ってより大規模で、本格的な配信を行うためには、いくつかの課題があります。 その1つが、Ustreamを使った場合の商業利用の規定です。これまでUstreamは、「非商業的な利用」に限るという利用規約があり、企業による明確な商業利用目的であったり、Ustream番組で生計を たてたいと考える個人やグループの配信が難しいという問題がありました。

それに対して、Ustreamが打ち出したのが「オープン・ペイパービュー」です。認可を受けたグループであれば、誰でも課金配信をすることができるよになるというものです。この発表にあわせて、Ustreamは「非商業的な利用」に加えて、「書面による事前認可があれば商業利用ができる」(Any commercial use of the Site, Services, Content or User Submission requires Ustream prior written approval (e.g., pursuant to additional or separate terms and conditions provided by Ustream).という内容に変更となりました。

公開課金システム「オープン・ペイ・パー・ビュー」

日本では、Ustreamとは別の動きとしてサードパーティより「リンクトシアター」「IZONN」などがあります。
こうした中で1つシステム面で飛びぬけているのがリンクトシアターではないでしょうか。

リンクトシアターでは、Ustreamなどのライブ配信サービスに対して投げ銭であったり、アイテム課金をすることができます。

リンクトシアター

リンクトシアターは、ログインして、あらかじめPayPalなどを使って課金しておくと、配信中の番組について投げ銭や花束などを使って、配信者にお金を渡すことができるサービスです。
これまでUstreamを行ってきたアーティストなどが、街頭での投げ銭だけでなくUstreamでの投げ銭を受け取ることができるようになったということになります。

リンクトシアターは、運営者にとってはデジタル上の「セミナー会場」の大きさを選ぶことができ、Twitterとの連携を行ってメッセージを送ったりすることができるようになっています。

大規模での利用は、年末の浜崎あゆみコンサートのように、Ustream自体の課金配信になることが予想されますが、リンクトシアターはそれよりもやや規模の小さい、ライブハウス規模の人たちへの選択肢の一つになるのではないかと考えています。

「eラーニングをすべての人に!」を実現させる手段として、製品コンセプトや製品体系のわかりやすはもちろん必須です。そして忘れてはいけない重要ポイントが、製品価格のわかりやすさだと思います。

私たち株式会社キバンインターナショナルは、eラーニングの専門企業ととして、下記の教材作成ツールを販売しています。

PPT2Flash Professional 29,400円
QuizCreator 16,800円
DemoCreator 16,800円
WebVideoAuthor 16,800円
PPT2Mobile 16,800円

がありますが、なんと!2種類しかない分かりやすい価格体系にしております。さらに驚きの低価格も実現しています。

eラーニングシステム SmartBrain(スマートブレイン)も、1ユーザー月額200円か、同時にログインできるユーザー数でカウントする同時接続数による課金(同時接続数1に対して2,100円)。価格のわかりやすさは、製品のコンセプトのわかりやすさを同じぐらい重要だと思うのですが、いかがでしょうか。

  • 製品・サービス
    PC、iPhone対応のeラーニングシステム。20名まで無料でASPサービスを利用できます
    PC、iPhone対応のeラーニング学習管理システム(LMS)【SmartBrain】
    http://smartbrain.info/
    PC、iPhone対応のeラーニングシステム。ユーザ数無制限のASPコースをご用意。


    eラーニングポータルサイト【elearning.co.jp】
    http://elearning.co.jp/
    eラーニング専門企業(株)キバンインターナショナルの製品を紹介しています。


    コンテンツビジネス支援パック
    http://contentsbank.jp/

    Ustream配信、動画コンテンツ制作、セミナーにご利用いただけるレンタルスタジオ
    Ustreamレンタルスタジオ「パンダスタジオ」
    http://pandastudio.tv/

    eラーニング専門企業(株)キバンインターナショナルのスタッフが、eラーニングに関する情報・最新事情をBlogでご紹介。月50本程度の情報発信を行っています。
    ブログ「blog.eラーニング.co.jp」
    http://blog.elearning.co.jp/

  • アーカイブ
  • カテゴリー
  • Amazon
  • タグ