Life is Like a Boat

忘備録や経済、投資、プログラミングに関するメモやtipsなど

PowerApps導入にあたって役立つリンク集

提案資料作成などで事例紹介をスライドに含めておくとウケがいいケースが多いと思います。

参考にしたサイトを忘備録代わりにまとめてみました。

こちらは、海外事例をもとにした紹介です。 www.cloudtimes.jp

アプリ開発者でなくてもアプリ開発できる。それがきっかけになってキャリアチェンジしたという成功事例の紹介です。

自分が好きな事例が、Excel管理の弊害を克服した事例です。 Hawkray製薬では医療関係者との面談をExcelシートに記入、メール添付していまいした。

メールがデータベースになってしまうと、「ある日のある業者との面談を検索したい」場合、Excelの中身も検索対象にしてくれる全文検索をすればまぁ不可能ではないと思いますが、そこから定量的な数値を出したり、確度高い見込み客を把握するなどしていると手が負えません。

同社が作成したアプリはCDSでデータ管理し、PowerAutomateを使って通知やリマインダーを出すものです。上長はBIレポートを参照し部下に適切な指示ができるようになりました。

開発者側の理解を得るのにはこうしたリンクはどうでしょうか。

シンプルな出退勤管理は一番取り組みやすい。社内アプリ第一号にもってこいのネタだと思います。

blog.funkit.jp

制限時間2時間かつローコーディングで開発できる生産性の高さが魅力です。

ascii.jp

自分はモバイル向けのAndroid/iOS/ハイブリッドアプリの開発経験があるのですが、

  • すでにMicrosoftのリソースが社内にある(Office 365を使っている、Active Directoryでユーザ管理など)
  • アプリのユーザ対象が社員

であれば、PowerAppsを使った開発の生産性の高さに驚きます。

マイクロソフトが提供する公式Youtubeチャンネルより、アプリ作成をステップごとに解説した動画です。開発側/業務側の人、両方にみてもらえると思います。

www.youtube.com

社内研修で笑いながらアプリ作れますね。

www.youtube.com

4月雑感

4月の振り返りです。

仕事面

仕事面ではMicrosoftのPowerAppsについて時間を割くことが多かったです。

PowerAppsとSharePointの連携についてQiita記事を書きました。

PowerAppsから画像をSharePointにアップロードする - Qiita

PowerAppsは業務をよく知っている非エンジニアでもローコーディングでアプリを作れるようにするというアプリ開発の民主化を大義としていて、実際触ってみると、なるほどよく設計しているなと感嘆する箇所がいくつもあります。なんでも初心者のうちはセットアップに時間を費やすより、やはりサクッと動くものが作りたいです。このエコシステムはそれを実現できる。

さらに言うと、実は中学や高校のプログラミング教育に最適なんじゃないかと思っています。例えば、スクラッチで時間割アプリは作ろうと思えば作れるかもしれませんが、それを自分のモバイル端末で動かそうとするとハードルは高い。

PowerAppsはモバイル端末への展開が驚くほど簡単なので、例えば高校の商業科の生徒が、会計知識とPowerAppsでアプリ開発できるスキルあったら、地元の中小企業で即戦力とはすぐに行かなくても大歓迎される人材になれると思うんです。この辺をプロボノで支援できないかなぁなどと考えています。

家庭面

休校措置やテレワークで家族皆で日中過ごすことが増えました。

朝食妻、昼は私、夜は半分半分で食事を作っています。もともと学生時代から自炊していたし、料理作る日もトータルで見たら妻より私の方が多く、作り始めてから頂きますするまでのスピードとおかずのレパートリーの幅で圧倒的に私が勝ってると思うので、料理は全く苦ではないです。

しかもエンジニアなら2つのコンロを使って、一方で野菜茹でながら、もう一方で肉を炒めるマルチタスクできて当然ですよね(ドヤッ

PowerAppsからAzure Blob Storageに画像をアップロードする

ここ数週間、MicrosoftのPower Appsを触っています。

Power Appsのメリットは、かなり少ないコード量で、多くの学習コストをかけず業務アプリが開発できる点です。

特にある業務をモバイルアプリ化する部分に生産性の高さを感じています。自分は前に大企業の業務向けiOS/Androidアプリの仕事に関わっていたのですが、 + iOS/Androidそれぞれの開発不要 + コンポーネントを組み合わせ(UIパーツ) + API連携 これらができることで、3ヶ月かけていた開発が3日以内にリリースに近い状態まで持っていける肌感覚があります。 多くのユーザに配布が必要なtoCアプリでなく、Microsoftライセンスがすでにある企業に最適なプロダクトだと感じます(Power AppsのPlan1/2が必要です Pricing - Power Apps )。

www.cloudtimes.jp

Power Appsの概要の説明は割愛するとして、画像の取り扱いについて知見が溜まってきたので共有したいと思います。 「PowerApps 画像」のような検索キーワードでたどり着いた人の助けになれば幸いです。

数週間触ってみて、画像の取り扱いはPower Appsでは注意した方がよい印象を持っています。 というのも、一般的なCommon Data Serviceを使う場合Planにもよりますが、容量の上限を考えるとたくさんの画像を扱う場合には適していないからです。2020年3月時点でファイルストレージは2GBです。 ケチってSharePointを使うケースもありますが、これは他システムとの連携を考えた場合に使い勝手悪いです。例えば、DjangoサイトからSharePoint上の画像ファイルにアクセスするケースです。

クラウドベースの画像管理のサービスのCloudinaryを使うのも案です。未検証なのですが、この場合、PowerAppsから画像バイナリを送信、AWS Lambda/Azure Functionなりを使ってCloudinaryにアップロードという経路になると思います。

いろいろ事例を調べて、もっとシンプルにいけそうなのが、Azure Blob Storageを使う方法です。

これはPowerAppsのボタンとカメラコンポーネント、Azure Storageにコンテナを作るだけでPowerAppsから画像アップロードが可能です。

f:id:nerimplo:20200323122025p:plain

画像アップロード用のボタンのOnSelectに

AzureBlobStorage.CreateFile("example-container",Concatenate(GUID(), Text(Today(), "yyyy-mm-dd"), ".jpg"), Camera2.Stream)

を加えてあげます。example-containerというAzure StorageコンテナにCamera2.StreamのImage型データをGUIDと日付、.jpgの拡張子付きでファイルを作る数式です。

注意点としてStreamRateを100にする必要があります。デフォルトでは(カメラコンポーネントを追加する時)には0になっているため、アップロードボタンを押してもコンテナ側には0バイトのファイルしか作られません。これはハマりポイントでした。

Azure Storageのコンテナ作成はこちらに解説があります。 powerapps.microsoft.com

Django + Zendesk APIで問い合わせフォームを作る

DjangoでWebサイト作ると間違いなく問い合わせフォームを作ると思います。

Djangoだけで完結するシステムだと、ユーザは問い合わせ内容をフォームに記述し、送信ボタンを押した後、Django側で問い合わせレコードをDBに作成する、という流れになると思います。

問い合わせ対応の効率化

その問い合わせ内容を例えば社内のサポート部隊で管理したい場合はどうでしょうか。

問い合わせチケット作成後、一次サポートのAさんにそのチケットをアサイン、Aさんがオーナーシップを持って担当部署に照会する必要があるかもしれません。そのやり取りを記した社内メモも残したいですよね。

ショッピングサイト(Django製)の例を使えば、楽天やAmazonにも出店しているかもしれません。その場合の問い合わせを一元管理したいところです。

購入後に何かしらのトラブルがあった場合、コールセンターに問い合わせできる電話番号が必要でしょう。メールでもチャットでも対応できるようにしたい。カスタマーサポートって思っている以上にやることが多いです。

これらを自前で実装するかどうか、結構判断がいるところです。 世の中には金を払えば手に入る良いサービスがあり、貴重な開発リソースをもう世の中に既にあるサービスの開発に使うべきでしょうか。

SaaSを使う

そこが特化型のSaaSの出番で、上記の課題を解決してくれるのがZendeskです。NasdaqにZENのティッカーで上場しています。

このSaaSを使うメリットとして

  • 問い合わせ対応を効率化
  • 顧客の疑問を自己解決できるオンラインコミュニティを作れる
  • リアルタイムのチャットサポート
  • 通話記録(Zendesk Talkという機能)
  • CRM機能

などがあります。

Djangoとの連携

DjangoとZendeskを連携させて使う場合、問い合わせフォームの送り先がZendeskになるイメージです。

以下、FormViewを継承したクラスでform_validの中でrequestsを使ってZendesk APIのエンドポイントにPOSTするサンプルコードです。

class Contact(FormView):
    template_name = 'web/contact.html'
    form_class = ContactForm

    def form_valid(self, form):
        subject = form.data.get('subject')
        description = form.data.get('description')
        email = form.data.get('email')
        user_name = form.data.get('user_name')

        # Package the data for the API
        data = {'request': {
            'subject': subject,
            'comment': {'body': description},
            'requester': {
                'name': user_name,
                'email': email
            },
        }}
        ticket = json.dumps(data)

        # Make the API request
        url = 'https://your_sub_domain.zendesk.com/api/v2/requests.json'
        headers = {'content-type': 'application/json'}
        r = requests.post(
            url,
            data=ticket,
            headers=headers
        )
        if r.status_code != 201:
            if r.status_code == 401 or 422:
                status = 'Could not authenticate you. Check your email address or register.'
            else:
                status = 'Problem with the request. Status ' + str(r.status_code)
            response = render_to_response('web/error.html')
            return response
        return redirect('web:thankyou')

Zendeskでは30日間のトライアルが可能です。

試しにcurlでエンドポイントを叩いてみたい場合のサンプルはこちらです。

curl -X "POST" "https://your_sub_domain.zendesk.com/api/v2/requests.json" \
     -H 'content-type: application/json' \
     -d $'{
  "request": {
    "tags": [
      "故障",
      "プリンタ"
    ],
    "subject": "緊急事態 - Help Needed",
    "comment": {
      "uploads": [
        "your token from upload api"
      ],
      "body": "プリンターが火を吹いています!!!!!! 大変!!!"
    },
    "requester": {
      "name": "nerimplo",
      "email": "hogehoge@example.com"
    },
    "is_public": "false"
  }
}'

(このTweetでは添付画像を加えています。フローとしてはUpload APIで先に添付ファイルをアップロード、戻り値のtokenをRequest APIに含めてあげます)

手っ取り早くテストしたければこのcurlでチケットがZendesk側に作成されます。Djangoで実装する際にはreCAPTCHAを追加してボットやスパム対策を施すといいと思います。

上記のケースはDjangoのFormビューとの連携ですが、ZendeskにはJavaScript Widgetがあり、Django Templateのscriptタグ内に埋め込んであげるだけでサイトの右または左下に問い合わせWidgetを埋め込むことができます。わざわざ問い合わせフォームがあるページに誘導するより、手軽さを考えるとこちらの方がいいかもしれません。

参考サイト

Zendesk Developer Portal

Zendeskって何?全7サービスを紹介。 - サポートタイムズ

Mastering Zendesk

Mastering Zendesk

  • 作者:Cedric F. Jacob
  • 出版社/メーカー: Packt Publishing
  • 発売日: 2017/01/13
  • メディア: ペーパーバック

1月雑感

あけましておめでとうございます。実はこの投稿が2020年初です。

ブログ書くペース落ちたなと昨年からの投稿数/月を見ているんですが、今年は月1は維持したいなと思ってます。

 

昨年末から振り返ると、年末年始は帰省先で過ごしていました。

 

その間やったアイテムをあげると

山登り、買い物、釣り、墓掃除、庭掃除、子供と庭で遊ぶ(バトミントン、野球)、BBQと焼き芋、餅付き

で、餅付きなど普段なかなか出来ないことをやっているうちにあっという間に1日過ぎていく感じでした。

 

子供と外遊びすると、やっぱり庭付きの戸建てがある環境はいいなと自分が育った家ですが改めて思います。

 

去年から住む場所の話を家族でしています。

親戚が多い九州に引っ越すか、それならスタートアップが熱いと聞く福岡、英語は問題なく使えるので海外住めるなどと言われますが、やはり日本国の社会保障の素晴らしさは外に出て分かるもので、その選択肢は現実的じゃないかなと思っています。いろんな要素が絡んでいるので結局現状維持になりそうな予感がしています。

 

だいたいにおいて選択肢が複数ある場合、一つを選ぶと他の道を選んだ場合の何々になれたかもしれない自分を消している事になると思います。その選択の連続で生きてきて、2020年になりました。そう考えると、次の10年どこで何している、何していたいと聞かれたところで、正直全然見通せないです。

 

 

 

 

投資面では、昨年は日本株よりも米株が良い成績でした。

 

その中でもTSLAの去年8月からのリバウンド、その後の新高値更新が1番のサプライズです。この会社の株式は同社のロケットが火星に行くくらい先の未来まで長期的に保有するつもりでいます。


今年も引き続き「堀」の性質を持つビジネスを展開する企業を保有し、新たに探していきたいと思います。その中でも、業界特化型SaaSは好みの分野です。例えば、昨年8月以降のパフォーマンスはパッとしなかったですが、VEEVの様なタイプです。

 

 

連日新型肺炎のニュースですが、自分はこういう時にどうも楽観視する癖があると思っています。

 

2009年の豚インフルの時を思いだすと最初は大騒ぎしていたが、後で収束していきました。豚インフルかかった友人と鎮静化して数ヶ月後がお茶した時、あぁそういえば最初のころ留学帰りの高校生が感染してその学校の校長が叩かれてたよねぇ、と話したのを覚えています。

 

過去の事例を参照値として見たがる癖なんだと思ってます。

 

重症化率や致死率を過去と比較してこれだったから今回こうなるだろうと推測するわけですが、中国のサプライチェーンに与える影響度合いはより高まっているはずです。

 

特に製造業の調達担当者がどう行動するか、もともと春節はスケジュールに組み込まれていたはずで、それが伸びたことで様子見るか、あるいは他国の調達先を探すか。

速報性があるじぶん銀行製造業PMI(https://www.markiteconomics.com/public?language=ja)や来月の景気ウォッチャー調査のコメントを見てみたいところです。

 

 

Django x Salesforce

この記事はDjango Advent Calendar 2019の記事です。

Djangoは2019年にちょっと時間割いて勉強しておこうと思ったPythonのWebフレームワークです。

きっかけはFinpyに参加されていたakiyokoさんのLTで、氏が著した下記3本やチュートリアルをみながら学習していった感じです。

現場で使える Django の教科書《基礎編》

現場で使える Django の教科書《基礎編》

現場で使える Django の教科書《実践編》

現場で使える Django の教科書《実践編》

Advent Calenderは初参加で何か書くぞと宣言したはいいもの、このネタを思いついたのが実は昨日ですw

Djangoと何かを掛け合わせて相乗効果がでるようなネタがいいなと、自分のSalesforce開発に関わった経験とDjangoを繋ぐためのライブラリを探したところ、見つけたのがdjango-salesforceです。

このライブラリのメリットを一言で表すと、「Django ORMと統合することでSalesforce上のレコードをPostgreSQLやMySQLをBackendに使う場合の様に操作できる点」だと思います。

github.com

セットアップ

基本公式のREADMEに全て書いてあります。

django-salesforce/README.rst at master · django-salesforce/django-salesforce · GitHub

セットアップの肝としては、Django側とSalesforce側のやり取りはConnected Appを通して行われます。Connected AppはOAuth認証を使用して外部アプリとSalesforce APIを統合するためのものです。この辺の解説が詳しいです。

Help | Training | Salesforce

Connected Appの具体的な使用事例としては、外部のBIツールからこれを介してSalesforce内のCRMデータにアクセスし、BIツール側でレポートを作るなどがあります。

Model

DjangoでのModelがSalesforceでのオプジェクトになります。ドキュメントによれば全てのModelはsalesforce.models.Modelを継承していなければなりません。 また、Salesforce上に既にオブジェクトが存在している必要があります。例えば、Djangoチュートリアルで使われるPollアプリの質問や選択肢がSalesforce上にカスタムオブジェクトとして(Questionc, Choicec)として存在することが期待されます。これはdjango-salesforceがメタデータの作成までサポートしていないからだと思います。githubのissuesをざっくり眺めたのですが、この辺り将来的なロードマップへの言及は見つけられませんでした。

Salesforce側にModelが既にあれば、Django既存のデータベースをスキャンしてModelを自動作成してくれるinspectdbコマンドを使うことでmodels.pyへの記述が楽になります。ドキュメントによると、このコマンドを使ってmodels.pyを記述していく方法が推奨されている様です。 例: > python manage.py inspectdb Opportunity --database=salesforce

ここではDjangoチュートリアルのPollアプリを基に考えます。

Pollアプリ - Question

DjangoチュートリアルのPollアプリはQuestionとChoiceから構成されます。

先にSalesforce側でQuestion__cというカスタムオブジェクトを作成します。

f:id:nerimplo:20191216105612p:plain

f:id:nerimplo:20191216105617p:plain

次にinspectdbコマンドでQuestion__cのModelを自動生成します。

python manage.py inspectdb Question__c --database=salesforce

class Question(models.Model):
    name = models.CharField(max_length=80, verbose_name='質問名', default=models.DEFAULTED_ON_CREATE, blank=True, null=True)
    question_text = models.CharField(custom=True, db_column='question_text__c', max_length=200, verbose_name='question_text', blank=True, null=True)
    pub_date = models.DateTimeField(custom=True, db_column='pub_date__c', verbose_name='pub_date', blank=True, null=True)
    class Meta(models.Model.Meta):
        db_table = 'Question__c'
        verbose_name = '質問'
        verbose_name_plural = '質問'
        # keyPrefix = 'a07'

コマンド実行後のアウトプットは標準項目も含んでいるのですが、好きな様に簡略化しろとドキュメントにはあるので、この様にしました。models.pyにはname, question_text, pub_dateのみの項目とします。なお、inspectdbコマンドでカスタムオブジェクトのモデルを作成するとCが必ず最後につく様です。これは削除するようにしました。

ここまで、確認を兼ねてCRUDができるかやってみました。

Pollアプリ - CRUD

q = Question(name=4, question_text='どこにいきたい?', pub_date=datetime.today())
q.save()

f:id:nerimplo:20191216100746p:plain

ちゃんと保存されています!所有者は接続アプリのログインユーザーになっています。

> q = Question.objects.filter(name=2)
> q
<SalesforceQuerySet [<Question: Question object (a072v00001AWH9XAAX)>]>
> q[0].name
'2'
> q[0].question_text
'夏休み旅行の予算はいくらですか'

UpdateやDeleteも通常のDjangoモデルのそれと同じに扱える様です。deleteメソッドの場合、Salesforce側では論理削除になっています(削除済みのフラグが付けられ、ごみ箱入り)

Pollアプリ - Choice

Djangoチュートリアルも参考に選択肢(Choice)をmodels.pyに加えてみます。

class Choice(models.SalesforceModel):
    name = models.CharField(max_length=80, verbose_name='選択肢名', default=models.DEFAULTED_ON_CREATE, blank=True, null=True)
    question = models.ForeignKey('Question', models.DO_NOTHING, custom=True, blank=True, null=True, db_column='Question__c')
    choice_text = models.CharField(custom=True, db_column='choice_text__c', max_length=20, verbose_name='choice_text', blank=True, null=True)
    votes = models.DecimalField(custom=True, db_column='votes__c', max_digits=18, decimal_places=0, verbose_name='votes', blank=True, null=True)
    class Meta(models.Model.Meta):
        db_table = 'Choice__c'
        verbose_name = '選択肢'
        verbose_name_plural = '選択肢'

質問レコードを選んで、選択肢を加えてみます。

> q = Question.objects.get(name=1)
> q.id
'a072v00001AWH9SAAX'
> c = Choice(question_id=q.id, name='A', choice_text='A', votes=10)
> c.save()
> c = Choice(question_id=q.id, name='B', choice_text='B', votes=1)
> c.save()
> c = Choice(question_id=q.id, name='C', choice_text='C', votes=1)
> c.save()

f:id:nerimplo:20191216130238p:plain

関連リストにちゃんと選択肢が表示されています。

選択肢nameにAを含む質問

> Question.objects.filter(choice__name='A')
<SalesforceQuerySet [<Question: Question object (a072v00001AWH9SAAX)>]>

選択肢のvotesが5以上の質問

> Question.objects.filter(choice__votes__gt=5)
<salesforce.backend.models_sql_query.SalesforceQuery object at 0x10b8a9d90>
>>> q = Question.objects.get(name=1)
>>> q
<Question: Question object (a072v00001AWH9SAAX)>
>>> q.choice_set.all()
<SalesforceQuerySet [<Choice: Choice object (a082v00002pwq0hAAA)>, <Choice: Choice object (a082v00002pwq2YAAQ)>, <Choice: Choice object (a082v00002pwq2OAAQ)>]>

_setを使ったクエリもDjangoライクにできます。ライブラリ側でORM->SOQLクエリにしている様です。

活用案

ざっくりdjango-salesforceを使ってみた感じですが、このライブラリの活用案を考えてみたところ、

  1. 既にDjangoベースのtoC向けのシステムがあって、CRM側のデータを使ってフロントで見せたい。
  2. Django側にCRM側のデータを引っ張り出してユーザー認証後の画面で編集
  3. ライセンスをケチる必要がある(爆

があると思います。 このライブラリを使わなくても解決はできそうですが、必要は発明の母というので私が認識していない活用事例があるはずだ思っています。思いついて語りたい方、Twitterまで連絡ください!

勝ち馬に乗る

よく勝ち馬に乗るというアナロジーでモメンタム投資が語られます。モメンタム戦略では中期(3-12ヶ月)の株価パフォーマンスに基づいてポートフォリオを構築します。

今回は定義を変えて昨年の騰落率がプラスでかつ今年の騰落率がそれを上回っている「勝ち馬であり続ける勝ち馬」をみてみました。

まず2018年をプラスで引けた銘柄は525銘柄しかありません。全上場企業がだいたい3700社として、14%程度です。

そのうち、年初来で18年の騰落率をも上回っている企業は182社あります。

そのうちのトップ10は下記のテーブルの通りです。

コード セクター 社名 2018年騰落率 年初来騰落率
7748 製造用機械・電気機械 ホロン 127.24 134.43
3849 システム・ソフトウエア 日本テクノ・ラボ 102.93 186.4
3983 広告 オロ 89.99 128.2
7564 衣料品・服飾品小売 ワークマン 86.48 152.11
6067 企業向け専門サービス メディアフラッグ 81.23 108.46
9698 システム・ソフトウエア クレオ 60.51 76.57
6544 建設資材・設備 JESHD 52.56 69.55
4734 システム・ソフトウエア ビーイング 50.98 56.74
2492 システム・ソフトウエア インフォマート 48.23 88.05
3038 食品卸 神戸物産 47.67 101.48

1位のホロンはで半導体電子ビーム測定・検査装置専業の研究開発型企業(四季報より)だそうです。調べるまで聞いた事ない会社でした。

上記182社からさらにセクター別に銘柄数をカウントしてみると、システム・ソフトウェア、バイオ・医薬品、飲食店という順になりました。よく日経新聞に載っている産業天気図で好調さが伺えるセクターから高いパフォーマンスを出す企業が出ている事がわかります。だいたいこのセクターはアメリカ株でもパフォーマンスがよく、先月のNBRでも出演者のFMが似た様な話をしていました。

セクター 銘柄数 年初からの騰落率
システム・ソフトウエア 37 67.4
バイオ・医薬品関連 10 46.7
飲食店 10 15.8
コンテンツ制作・配信 9 67.2
企業向け専門サービス 8 113.9
製造用機械・電気機械 7 65.3
不動産・住宅 7 48.9
建設・土木 7 21.2
陸運 7 17.4
広告 6 67.8