Life is Like a Boat

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

大統領選以降のセクター別騰落率

大統領選以降のセクター別騰落率でトップ10を調べてみました。

トップは総合電機。バフェット氏が今年、日本の商社株に初めて投資したと発表した総合商社セクターもランクインしてます。 ちなみに総合電機セクターにはソニー、日立、パナソニックなどが含まれています。 f:id:nerimplo:20201128221504p:plain

比較のために3月から大統領選までの騰落率トップ10はこちらになります。メンツがガラッと変わっているのがわかります。 f:id:nerimplo:20201128222004p:plain

GASからGoogleFormを使ってクイズを作る

仕事でGoogle Formsを使う機会が出てきました。 自分はGoogleのプラットフォームを使って仕事する機会が今までなかったのですが、Google Formsはかなり生産性高くアンケート作成やクイズ作成ができるので便利です。

ただ、大量に質問がある場合、ドラッグアンドドロップで質問を移動したり、選択肢の作成、クイズの場合は答えキーのセットなど手間がかかり面倒臭さが増してきます。マウスでぽちぽちやると気が遠くなりそうです。 そこでGoogleAppsScriptを使い自動生成する方法を調べてみました。

以下、Google Formのクイズを作成するGASスニペットです。ここではsetPoints(10)で1問10点のクイズを作っています。

const QUIZ_FOLDER_ID = 'YOUR_FOLDER_ID'

function myFunction() {
  addFormItems()
}

function addFormItems() {
  
  const formTitle = 'TestQuiz';
  const form = FormApp.create(formTitle).setIsQuiz(true)

  const item = form.addMultipleChoiceItem()
  
  item
  .setTitle('1+4')
  .setChoices([
    item.createChoice('1', false),
    item.createChoice('3', false),
    item.createChoice('5', true),
    item.createChoice('2', false),
  ]).setPoints(10).setRequired(true)
    
   
   const formFile = DriveApp.getFileById(form.getId())
   DriveApp.getFolderById(QUIZ_FOLDER_ID).addFile(formFile)
  
}

スクリプトのエディタから実行し、YOUR_FOLDER_IDのフォルダ下に生成されたクイズを確認すると、きちんと正答にチェックがついています!

f:id:nerimplo:20200909160413p:plain

FormApp.create(formTitle).setIsQuiz(true)をしないと点数つけられるクイズ形式のフォームにできないというのが一番の肝だと思います。

これをセットしてあげないと、Exception: 無効なフォーム データです。テストの設定を有効にしてから、もう一度お試しください。 というググラビリティの低い(苦笑 エラーが出てきます。

f:id:nerimplo:20200909160347p:plain

ローコーディングのAzure Logic Appsを使って画像付きのツイートを投稿する

MicrosoftのAzure Logic Appsを使って画像付きのツイートを投稿する方法のまとめです。

Azure Logic Appsは

タスク、ビジネスプロセス、ワークフローをスケジュール、自動化、オーケストレーションするのに役立つAzureのサービスの一つ。

です。これをローコードでできるという点が肝です。

www.youtube.com

視覚的にロジックを作成でき、特に条件や繰り返しなどは、子供向けプログラミングアプリのスクラッチのような感覚があります。

作ってみよう

Azure Portalから

f:id:nerimplo:20200722100021p:plain

Logic Appsを選びAddをクリック

f:id:nerimplo:20200722100102p:plain

Logic Appの作成フォームが現れます。

f:id:nerimplo:20200722100106p:plain

App名をこのようにします。

f:id:nerimplo:20200722100235p:plain

入力後、CreateボタンをクリックするとLogic Appの作成が始まります。1-2分もかからず完了するはずです。

Logic Apps Designer画面に切り替わるとテンプレートが表示されます。

f:id:nerimplo:20200722103402p:plain

Step1 - Triggerを選ぶ

Start with a common triggerとあります。Triggerは何をきっかけに一連のロジックを走らせたいか、です。

ここではPOSTリクエストを受け取った時にTwitterに投稿としたいので、

When a HTTP request is receivedをTriggerとして選びます。

f:id:nerimplo:20200722104714p:plain

編集画面が表示されるので、まずRequest BodyのJSONスキーマを決めてあげます。 加えるプロパティを画像とテキストとURLにしましょう。

{
    "properties": {
        "text": {
            "type": "string"
        },
        "media_data": {
            "type": "string"
        },
        "url": {
            "type": "string"
        }
    },
    "type": "object"
}

Add new parameterの入力フィールドからMethodを選び POSTを選びます。

これで第一のステップは完了です。

f:id:nerimplo:20200722104717p:plain

Step2 - Post a tweet

New Stepを加えるために、Twitterと検索フィールドに入力し、Post a tweetを選びます。

f:id:nerimplo:20200722104912p:plain

TwitterへのSign inを求められ、連携アプリを認証します。

認証後、このようなパラメターを加られるようになっているはずです。

f:id:nerimplo:20200722105327p:plain

Tweet textとMediaとあります。Tweet textは文字列部分なので、本文とURLにしましょう。

入力フィールドをクリックすると、dynamic contentを選べる画面が出てきます。

bodyとurlが先のステップ(When a HTTP request is received)のJSONスキーマで指定されたものです。POSTする際、JSONに含める値となります。

f:id:nerimplo:20200722134837p:plain

media_dataは今回の場合、画像になります。 画像はPOSTする際、base64にエンコードした文字列とする必要があります。 また、expressionを使って少々加工する必要があります。

base64ToBinary(split(triggerBody()?['media_data'],',')[1])

f:id:nerimplo:20200722110307p:plain

スクリーンショットを参考に、上記のexpressionを丸ごとコピーペーストしてみてください。 expressionの数式を説明すると、JSONのmedia_dataプロパティが

"media_data": "data:image/png;base64,asfdasfasdffasfd...............",

となっているので、最初のカンマ以降の文字列splitで取得、それをbase64ToBinary関数を使ってbase64エンコードの文字列をバイナリにします。

POSTしてみる

早速テストデータをエンドポイントにPOSTしてみます。

エンドポイントのURLは新規で作成したLogic Appを保存した際に発行されます。

f:id:nerimplo:20200722130624p:plain

PostmanやPawを使ってやってみます。curlでもOKです。

f:id:nerimplo:20200722132125p:plain

media_dataは画像をbase64エンコードした文字列です。この辺のサイトを使うかPythonのライブラリを使ってエンコードしてあげればいいです。

www.base64-image.de

ちなみにcurlだと

curl -X "POST" "YOUR_ENDPOINT" \
-H 'Content-Type: application/json; charset=utf-8' \
-d $'{
   "media_data": "data:image/png;base64,asfdasfasdffasfd...............",
   "url": "https://www.google.co.jp/",
   "body": "テスト"
}'

こうなります。

うまくいけば、このように画像と文字付きのツイートが投稿されます。

f:id:nerimplo:20200722135714p:plain

Logic Appsは元となるテンプレートだけでもかなりの種類があります。組み合わせはアイデア次第です。

ちなみに、Office365のPower AutomateはLogic Apps上に作られているので操作性は一緒です。 バージョン管理しているのでチームでのメンテにも向いていると思います。

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
  • メディア: ペーパーバック