[Go Revel] FileのUploadと画像の保存

test.html

<h1>テストデータのアップロードページ</h1>
    <form name="upload" method="POST" action="/test/upload" enctype="multipart/form-data">
      <input type="file"   name="imgFile"  accept="image/jpeg, image/png">
      <input type="submit" name="submit"   value="commit">
    </form>

App.go

import (
	"github.com/revel/revel"
	"app/app/models"
	"fmt"
	"os"
)

func (c App) Test() revel.Result {

	return c.Render()
}

func (c App) Upload(imgFile *os.File) revel.Result {
  fmt.Printf("imgFile => %v\n", imgFile)
  return c.Redirect(App.Test)
}

imgFile => &{0xc0000a85a0}

### 画像の保存
– ioutil.WriteFile(path, data, permission)で画像を保存する

import (
	"github.com/revel/revel"
	"app/app/models"
	"fmt"
	"io/ioutil"
	"os"
)

func (c App) Upload(imgFile *os.File) revel.Result {
  fmt.Println(c.Params.Files["imgFile"][0].Filename)
  name := c.Params.Files["imgFile"][0].Filename
  data, e := ioutil.ReadAll(imgFile)
  if e != nil {
	fmt.Println("error")
  }
  ioutil.WriteFile("public/tmp/" + name, data, 755)

  return c.Redirect(App.Test)

}

おおお、なんか久しぶりに感動した。

[Go Revel] パスワード変更のバリデーション実装方法

html
L 新しいパスワードと、パスワード確認のフォームを用意する
L radioボタンでchangeなら表示、not changeなら非表示

<tr>
            <td>Password</td>
            <td><input type="radio" name="password_change" value="n" checked>Not Change <input type="radio" name="password_change" value="y">Change
            <div class="changeterm displaynone">
              <input type="password" name="password" class="input" placeholder="new password"><br>
              <input type="password" name="password_confirm" class="input" placeholder="new password confirm">
                    </div>
            </td>
          </tr>

app.go
  L passwordが4文字以上の場合は、Min()を使う
   L if文でpasswordとpassword_confirmが一致かではなく、requiredの中で一致するか確認する

func (c App) MypageConfirm(email string, message string, password string, password_confirm string) revel.Result {

	// 省略
	if len(password) > 0 {
		c.Validation.Min(len(password), 4).Message("Password must be more than 4 character")
		c.Validation.Required(password == password_confirm).Message("password and password confirmation must be same.")
	}

	// 省略
	return c.Render(name, email, message)
}

if文で書きたいところだが、変わってるなー

[Go Revel] CSRF対策を行う

### revel-csrfをインストール
$ go get github.com/cbonello/revel-csrf

app/init.go

import (
	"github.com/revel/revel"
	"prd/app/controllers"
	"github.com/cbonello/revel-csrf" // 追加
	_ "github.com/revel/modules"

)
	revel.Filters = []revel.Filter{
		// 省略
		csrf.CSRFFilter, 			   // 追加
	}

*.html

      <form action="/mypage-confirm" method="post" enctype="multipart/form-data">
      <input type="hidden" name="csrf_token" value="{{ .csrf_token }}" />
      // 省略

CSRFがないと何か落ち着きませんが、あって良かったです。

[Go Revel] Emailのバリデーション

c.Validation.Email(email)と書く

app.go

func (c App) MypageConfirm(email string, message string) revel.Result {

	name := c.Session["userName"]
	c.Validation.Required(email).Message("Email is required")
	c.Validation.Email(email).Message("Please enter in email format")

	if c.Validation.HasErrors(){
		c.Validation.Keep()
		c.FlashParams()
		return c.Redirect(App.Mypage)
	}

	return c.Render(name, email, message)
}

うん、良さそうではある

[Go Revel] テンプレートでナビテーションを一元管理する

Go Revelでnavigationとside menuをテンプレート化したい
-> classでページによってis-activeと付与する箇所は、テンプレート側でセットできるようにする。

/app/views/menu.html

  <section class="main-content columns is-fullheight">
  <aside class="column is-3 is-narrow-mobile is-fullheight section is-hidden-mobile">
    <p class="menu-label is-hidden-touch">MENU</p>
    <ul class="menu-list">
      <li>
        <a href="/home" class="{{.home}}">
          <span class="icon"><i class="fa fa-home"></i></span> Home
        </a>
      </li>
      <li>
        <a href="#" class="">
          <span class="icon"><i class="fa fa-table"></i></span> Channel
        </a>

        <ul>
          <li>
            <a href="#" class="{{.ch1}}">
              <span class="icon is-small"><i class="fa fa-link"></i></span> Ch1
            </a>
          </li>
          <li>
            <a href="#" class="{{.ch2}}">
              <span class="icon is-small"><i class="fa fa-link"></i></span> Ch2
            </a>
          </li>
          <li>
            <a href="#" class="{{.ch3}}">
              <span class="icon is-small"><i class="fa fa-link"></i></span> Ch3
            </a>
          </li>
        </ul>
      </li>
      <li>
        <a href="#" class="{{.mypage}}">
          <span class="icon"><i class="fa fa-id-card"></i></span> MyPage
        </a>
      </li>
      <li>
        <a href="#" class="{{.doc}}">
          <span class="icon"><i class="fa fa-file-alt"></i></span> Document
        </a>
      </li>
      <li>
        <a href="#" class="{{.chat}}">
          <span class="icon"><i class="fa fa-comments"></i></span> Chat room
        </a>
      </li>
    </ul>
  </aside>

/app/views/App/Index.html

{{template "nav.html" .}}
{{set . "home" "is-active"}}
{{template "menu.html" .}}

/app/views/App/mypage.html

{{template "nav.html" .}}
{{set . "mapage" "is-active"}}
{{template "menu.html" .}}

昔1000ページぐらいのサイトを運営してた時に、12/31に1000ページのall right reserved 20XXを1000ページ修正してたの思い出した🤮🤮🤮
絶対テンプレートを使う

Pythonでlistから最も近い数字を抽出し、辞書の値をprint

numpyを使う

# -*- coding: utf-8 -*-
import numpy as np

def getNearestValue(list, num):
	idx = np.abs(np.asarray(list) - num).argmin()
	return list[idx]

# 辞書
d = {}
d[0.743722677230835] = 'value1'
d[0.9442239999771118] = 'value2'
d[0.8997899889945984] = 'value3'
print(d)

# リスト
list = []
list.append(0.743722677230835)
list.append(0.9442239999771118)
list.append(0.8997899889945984)

# 最も近い数字
nearest = getNearestValue(list, 0.9602155685424805)

# 最も近い数字のvalue
print(nearest)
print(d[nearest])

$ python3 test.py
{0.743722677230835: ‘value1’, 0.9442239999771118: ‘value2’, 0.8997899889945984: ‘value3’}
0.9442239999771118
value2

やりたいことはできた。
これをNLPでやる。

[自然言語処理] python x transformerで感情分析

$ pip3 install fugashi
$ pip3 install ipadic

sentiment.py

# -*- coding: utf-8 -*-
from transformers import AutoTokenizer, AutoModelForSequenceClassification
from transformers import pipeline

tokenizer = AutoTokenizer.from_pretrained("daigo/bert-base-japanese-sentiment")
model = AutoModelForSequenceClassification.from_pretrained("daigo/bert-base-japanese-sentiment")

print(pipeline("sentiment-analysis", model="daigo/bert-base-japanese-sentiment", tokenizer="daigo/bert-base-japanese-sentiment")("私は幸福である。"))

$ python3 sentiment.py
[{‘label’: ‘ポジティブ’, ‘score’: 0.9843042492866516}]

単語やニュートラルな文章はポジティブに判定されやすい

“もうダメだ” にして再実行
$ python3 sentiment.py
[{‘label’: ‘ネガティブ’, ‘score’: 0.9892264604568481}]

なるほど
処理時間がかかるのがスネに傷やな

[自然言語処理] word2vecによる類似単語を検索

$ pip3 install gensim==3.8.1

### word2vecのファイルをDL
$ wget http://public.shiroyagi.s3.amazonaws.com/latest-ja-word2vec-gensim-model.zip

$ unzip latest-ja-word2vec-gensim-model.zip

app.py

# -*- coding: utf-8 -*-
from gensim.models import word2vec

model = word2vec.Word2Vec.load('word2vec.gensim.model')
results = model.wv.most_similar(positive=['日本'])
for result in results:
	print(result)

$ python3 app.py
(‘韓国’, 0.7088127732276917)
(‘台湾’, 0.6461570262908936)
(‘日本国内’, 0.6403890252113342)
(‘欧米’, 0.6350583434104919)
(‘日本国外’, 0.6200590133666992)
(‘台湾出身’, 0.6174061894416809)
(‘中華圏’, 0.612815260887146)
(‘日本の経済’, 0.6088820099830627)
(‘日本の歴史’, 0.6070738434791565)
(‘韓国国内’, 0.6054152250289917)

gensimはバージョンを指定しないとエラーになるので注意が必要

OK
これを繋げていく

[自然言語処理] pythonによる文章自動生成

transformersを使います
$ pip3 install transformers==4.3.3 torch==1.8.0 sentencepiece==0.1.91

# -*- coding: utf-8 -*-
import transformers

tokenizer = transformers.AutoTokenizer.from_pretrained("colorfulscoop/gpt2-small-ja")
model = transformers.AutoModelForCausalLM.from_pretrained("colorfulscoop/gpt2-small-ja")

input = tokenizer.encode("昔々あるところに", return_tensors="pt")
output = model.generate(input, do_sample=True, top_p=0.95, top_k=50, num_return_sequences=3)

print(tokenizer.batch_decode(output))

[‘昔々あるところには、お祭りの女神さんが現れ、そこでお姫様の姫様’, ‘昔々あるところに、ある。ある夏の日、彼は旅人と出会い、その目的がどう’, ‘昔々あるところに、一億年も前には人間たちが住んでいた。いまや、それはこの’]

おおお、なんか色々出来そうではある…
途中の処理を考える必要はあるが

[自然言語処理] pythonとasariで感情分析を行う

$ pip3 install asari

from asari.api import Sonar

sonar = Sonar()
sonar.ping(text="広告が多すぎる♡")

$ python3 asari.py
Traceback (most recent call last):
File “asari.py”, line 1, in
from asari.api import Sonar
File “/home/vagrant/dev/nlp/asari.py”, line 1, in
from asari.api import Sonar
ModuleNotFoundError: No module named ‘asari.api’; ‘asari’ is not a package

—-
Requirement already satisfied: joblib>=0.11 in /home/vagrant/.local/lib/python3.8/site-packages (from scikit-learn>=0.19.1->asari) (1.0.1)
Requirement already satisfied: threadpoolctl>=2.0.0 in /home/vagrant/.local/lib/python3.8/site-packages (from scikit-learn>=0.19.1->asari) (2.2.0)
Requirement already satisfied: six>=1.5 in /usr/lib/python3/dist-packages (from python-dateutil>=2.7.3->pandas>=0.22.0->asari) (1.14.0)
Installing collected packages: pytz, pandas, Janome, asari
Successfully installed Janome-0.4.1 asari-0.0.4 pandas-1.3.4 pytz-2021.3

python2系じゃないと動かないのか?

python2系の環境で再度やり直します。
$ pip install scikit-learn==0.20.4
$ pip install Janome==0.3.7

[{‘text’: ‘広’, ‘top_class’: ‘positive’, ‘classes’: [{‘class_name’: ‘negative’, ‘confidence’: 0.28345109397418017}, {‘class_name’: ‘positive’, ‘confidence’: 0.7165489060258198}]}, {‘text’: ‘告’, ‘top_class’: ‘positive’, ‘classes’: [{‘class_name’: ‘negative’, ‘confidence’: 0.3450436370027103}, {‘class_name’: ‘positive’, ‘confidence’: 0.6549563629972897}]}, {‘text’: ‘が’, ‘top_class’: ‘negative’, ‘classes’: [{‘class_name’: ‘negative’, ‘confidence’: 0.9006654377630458}, {‘class_name’: ‘positive’, ‘confidence’: 0.09933456223695437}]}, {‘text’: ‘多’, ‘top_class’: ‘negative’, ‘classes’: [{‘class_name’: ‘negative’, ‘confidence’: 0.9364137330979464}, {‘class_name’: ‘positive’, ‘confidence’: 0.06358626690205357}]}, {‘text’: ‘す’, ‘top_class’: ‘positive’, ‘classes’: [{‘class_name’: ‘negative’, ‘confidence’: 0.2124141523260128}, {‘class_name’: ‘positive’, ‘confidence’: 0.7875858476739873}]}, {‘text’: ‘ぎ’, ‘top_class’: ‘negative’, ‘classes’: [{‘class_name’: ‘negative’, ‘confidence’: 0.5383816766180572}, {‘class_name’: ‘positive’, ‘confidence’: 0.4616183233819428}]}, {‘text’: ‘る’, ‘top_class’: ‘negative’, ‘classes’: [{‘class_name’: ‘negative’, ‘confidence’: 0.6881484923868434}, {‘class_name’: ‘positive’, ‘confidence’: 0.3118515076131566}]}]

# -*- coding: utf-8 -*-
from asari.api import Sonar

sonar = Sonar()
text="広告が多すぎる"
res = sonar.ping(text="広告多すぎる♡")
print(res)

$ python3 app.py
{‘text’: ‘広告多すぎる♡’, ‘top_class’: ‘negative’, ‘classes’: [{‘class_name’: ‘negative’, ‘confidence’: 0.9086981552962491}, {‘class_name’: ‘positive’, ‘confidence’: 0.0913018447037509}]}

なるほど、janomeとscikit-learnはversionを指定すると動く