[Go] Session管理

manager.go

package sessions

import (
	"crypto/rand"
	"encoding/base64"
	"errors"
	"io"
	"net/http"
)

type Manager struct {
	database map[string]interface{}
}

var mg Manager

func NewManager() *Manager {
	return &mg
}

func (m *Manager) NewSessionID() string {
	b := make([]byte, 64)
	if _, err := io.ReadFull(rand.Reader, b); err != nil {
		return ""
	}
	return base64.URLEncoding.EncodeToString(b)
}

fun (m *Manager) New(r *http.Request, cookieName string)(*Session, error){
	cookie, err := r.Cookie(cookie.Name)
	if err == nil && m.Exists(cookie.Value){
		return nil, errors.New("sessionIDは既に発行されています")
	}

	session := NewSession(m, cookieName)
	session.ID = m.NewSessionID()
	session.request = r

	return session, nil
}

func(m *Manager) Save(r *http.Request, w http.ResponseWriter, session *Session) error {
	m.database[session.ID] = session

	c := &http.Cookie {
		Name: session.Name(),
		Value: session.ID,
		Path: "/",
	}

	http.SetCookie(session.writer, c)
	return nil
}


func(m *Manager) Exists(sessionID string) bool {
	_, r = m.database[sessionID]
	return r
}

func(m *Manager) Get(r *http.Request, cookieName string)(*Session, error){
	cookie, err := r.Cookie(cookieName)
	if err != nil {
		return nil, err
	}

	sessionID := cookie.Value
	buffer, exists := m.database[sessionID]
	if !exists {
		return nil, errors.New("無効なセッションIDです")
	}

	session := buffer.(*Session)
	session.request = r
	return session, nil
}

func(m *Manager) Destroy(sessionID string){
	delete(m.database, sessionID)
}

session.go

package sessions

import(
	"net/http"

	"github.com/gin-gonic/gin"
	"github.com/gorilla/context"
)

const (
	DefaultSessionName = "default-session"
	DefaultCookieName = "default-cookie"
)

type Session struct {
	cookieName string
	ID	string
	manager *Manager
	request *http.request
	writer http.ResponseWriter
	Values map[string]interface{}
}

func NewSession(manager *Manager, cookieName string) *Session {
	return &Session {
		cookieName: cookieName,
		manager: manager,
		Values: map[string]interface{}{},
	}
}

func StartSession(sessionName, cookieName string, manager *Manager) gin.HandlerFunc {
	return func(ctx *gin.Context){
		var session *Session
		var err error
		session, err = manager.Get(ctx.Request, cookieName)
		if err != nil {
			session, err = manager.New(ctx.Request, cookieName)
			if err != nil {
				println(err.Error())
				ctx.Abort()
			}
		}
		session.writer = ctx.Writer
		ctx.Set(sessionName, session)
		defer context.Clear(ctx.Request)
		ctx.Next()
	}
}

func StartDefaultSession(manager *Manager) gin.HandlerFunc {
	return StartSession(DefaultSessionName, DefaultCookieName, manager)
}

func GetSession(c *gin.Context, sessionName string) *Session {
	return c.MustGet(sessionName).(*Session)
}

func GetDefaultSession(c *gin.Context) *Session {
	return GetSession(c, DefaultSessionName)
}

func (s *Session) Save() error {
	return s.manager.Save(s.request, s.writer, s)
}

func (s *Session) Name() string {
	return s.cookieName
}

func (s *Session) Get(key string)(interface{}, bool){
	ret, exists := s.Value[key]
	return ret, exists
}

func(s *Session) Set(key string, val interface{}){
	s.Values[key] = val
}

func(s *Session) Delete(key string){
	delete(s.Values, key)
}

func(s *Session) Terminate(){
	s.manager.Destroy(s.ID)
}

h_Login.go

package req_handler

import (
	"fmt"
	"html/template"
	"net/http"
)

func HandlerLogin(w http.ResponseWrite, req *http.Request){

	tpl := template.Must(template.ParseFiles("templates/login.gtpl"))

	values := map[string]string{}

	if err := tpl.ExecuteTemplate(w, "login.gtpl", values); err != nil {
		fmt.Println(err)
	}
}

fun HandlerExecLogin(w http.ResponseWriter, req *http.Request){

	manager := sessions.NewManager()
	sessions.StartDefaultSession(manager)

	session := manager.Get(req, sessions.DefaultCookieName)
	session.Set("account", req.FormValue("account"))
	session.Save()

	http.Redirect(w, req, "http://192.168.34.10:10443/login-top", 301)
}

h_LoginTop.go

package req_handler

import (
	"fmt"
	"html/template"
	"net/http"
)

func HandlerLoginTop(w http.ResponseWriter, r *http.Request){
	tpl := template.Must(template.ParseFiles("templates/login-top.gtpl"))

	isLoggedIn := ""
	account := ""

	session := manager.Get(req, sessions.DefaultCookieName)
	if session.Values["account"]{
		isLoggedIn = "isLoggedIn"
		account = session.Values["account"]
	}

	values := map[string] string {
		"isLoggedIn": isLoggedIn,
		"account": account,
	}

	if err := tpl.ExecuteTemplate(w, "login-top.gtpl", values); err != nil {
		fmt.Println(err)
	}
}

func HandlerExecLogout(w http.ResponseWriter, req *http.Request){

	session := manager.Get(req, sessions.DefaultCookieName)
	session.Terminate()

	http.Redirect(w, req, "https://192.168.34.10:10443/login", 301)
}

なるほど、ginね
1. usersテーブルでユーザ名とパスワードが一致するかチェックして、一致した場合進めるようにする
2. sessionで管理する

セッションの使い方は覚えていけば良さそう

[Go] RevelでCRUD

route

GET     /                     App.Index
GET     /team/:id             App.Show 
GET     /edit/:id             App.Edit 
POST	/edit/complete		  App.Update
GET		/delete/:id			  App.Delete
GET		/create				  App.Create
POST	/store				  App.Store

controller

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

	result := []models.Baseballs{}
	DB.Find(&result)

	return c.Render(result)
}

func (c App) Show(id int) revel.Result {

	result := []models.Baseballs{}
	DB.Where("id = ?", id).First(&result)
	team := result[0]
	fmt.Println(team.Name)

	return c.Render(team)
}

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

	return c.Render()
}

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

	name := c.Params.Get("name")
	manager := c.Params.Get("manager")
	home := c.Params.Get("home")

	c.Validation.Required(name).Message("name is required")
	c.Validation.Required(manager).Message("manager is required")
	c.Validation.Required(home).Message("home is required")

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

	DB.Create(&models.Baseballs{
		Name: name,
		Manager: manager,
		Home: home,
	})

	return c.Render(name)
}

func (c App) Edit(id int) revel.Result {

	result := []models.Baseballs{}
	DB.Where("id = ?", id).First(&result)
	team := result[0]

	return c.Render(team)
}

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

	id := c.Params.Get("id")
	name := c.Params.Get("name")
	manager := c.Params.Get("manager")
	home := c.Params.Get("home")

	DB.Model(models.Baseballs{}).Where("id = ?", id).Update(&models.Baseballs{
		Name: name,
		Manager: manager,
		Home: home,
	})

	return c.Render(name)
}

func (c App) Delete(id int) revel.Result {

	DB.Where("id = ?", id).Delete(&models.Baseballs{})

	return c.Redirect(App.Index)
}

割と簡単にできますね
migrationとかあるのかわからんが、取り敢えずログイン機能までは作りたい

[Go] RevelでMySQLを操作

conf/app.conf

db.user = fuga
db.password = hoge
db.host = localhost
db.port = 3306
db.name = test
db.protocol = tcp

app/controllers/gorm.go

package controllers

import (
	"github.com/revel/revel"
	"github.com/jinzhu/gorm"
	"strings"
	"fmt"
	_"github.com/go-sql-driver/mysql"
)

var DB *gorm.DB


func InitDB(){
	db, err := gorm.Open("mysql", getConnectionString())

	if err != nil {
		panic(err.Error())
	}

	db.DB()
	DB = db
}


func getParamString(param string, defaultValue string) string {
	p, found := revel.Config.String(param)
	if !found {
		if defaultValue == "" {
			fmt.Sprintf("Could not find parameter: " + param)
		} else {
			return defaultValue
		}
	}
	return p
}


func getConnectionString() string {
	host := getParamString("db.host", "localhost")
	port := getParamString("db.port", "3306")
	user := getParamString("db.user", "root")
	pass := getParamString("db.password", "password")
	dbname := getParamString("db.name", "test")
	protocol := getParamString("db.protocol", "tcp")
	dbargs := getParamString("dbargs", "	")
	timezone := getParamString("db.timezone", "parseTime=True")

	if strings.Trim(dbargs, " ") != ""{
		dbargs = "?" + dbargs
	} else {
		dbargs = ""
	}

	return fmt.Sprintf("%s:%s@%s([%s]:%s)/%s?%s", user, pass, protocol, host, port, dbname, timezone)
}

app/models/idols.go

package models

type Idols struct {
	ID int
	Name string `json:"name"`
	Label string `json:"label"`
	Producer string `json:"producer"`
	birth string `json:"birth"`
	Member string `json:"member"`
	Song string `json:"song"`
}

app/controllers/app.go

package controllers

import (
	"github.com/revel/revel"
	"myapp/app/models"
	_"github.com/go-sql-driver/mysql"
)

type App struct {
	*revel.Controller
}

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

	result := []models.Idols{}
	DB.Where("id = ?", 1).First(&result)
	greeting := result[0].Name
	
	return c.Render(greeting)
}

### 更新

	result := models.Idols{}
	DB.First(&result, 5)
	result.Song = "flash"
	DB.Save(&result)

大体わかった
後はデモを作るか topicは野球あたりにしましょう

[Go] Revelでmysqlを操作する

package導入
$ go get github.com/jinzhu/gorm
$ go get github.com/go-sql-driver/mysql

conf/app.conf

db.user = hoge
db.password = fuga
db.host = localhost
db.port = 3306
db.name = test
db.protocol = tcp

app/models/gorm.go

package models

import (
	"github.com/revel/revel"
	"github.com/jinzhu/gorm"
	"strings"
	"time"
	"fmt"
	_"github.com/go-sql-driver/mysql"
)

var DB **gorm.DB

func InitDB(){
	db, err := gorm.Open("mysql", getConnectionString())

	if err != nil {
		revel.ERROR.Println("FATAL", err)
		panic(err)
	}

	db.DB()
	DB = &db
}

type Model struct {
	gorm.Model
	ID unit `gorm:"primary_key"`
	CreatedAt time.Time
	UpdatedAt time.Time 
	DeletedAt *time.Time
}

type Validator interface {
	IsStatisfied(interface{}) bool
	DefaultMessage() String
}

func getParamString(param string, defaultValue string) string {
	p, found := revel.Config.String(param)
	if !found {
		if defaultValue == "" {
			revel.ERROR.Fatal("Could not find parameter: " + param)
		} else {
			return defaultValue
		}
	}
	return p
}


func getConnectionString() string {
	host := getParamString("db.host", "localhost")
	port := getParamString("db.port", "3306")
	user := getParamString("db.user", "hoge")
	pass := getParamString("db.password", "fuga")
	dbname := getParamString("db.name", "test")
	protocol := getParamString("db.protocol", "tcp")
	dbargs := getParamString("dbargs", "	")
	timezone := getParamString("db.timezone", "parseTime=true&loc=Asia%2FTokyo")

	if strings.Trim(dbargs, " ") != ""{
		dbargs = "?" + dbargs
	} else {
		dbargs = ""
	}

	return fmt.Sprintf("%s:%s@%s([%s]:%s)/%s%s?%s", user, pass, protocol, host, port, dbname, dbargs, timezone)
}

app/init.go

func init() {
	revel.OnAppStart(models.InitDB)
}

なんかエラーになりますね。。

[Go] Revelでcreate application

app/views/App/index.html

    <div class="span6">
      {{template "flash.html" .}}
      <form action="/App/Hello" method="GET">
        <input type="text" name="myName"/><br><br>
        <input type="submit" value="Say hello!"/>
      </form>
    </div>

app/controllers/app.go

func (c App) Hello(myName string) revel.Result {
	return c.Render(myName)
}

conf/route

GET	/App/Hello	App.Hello

$ /home/vagrant/go/bin/revel run -a myapp
コンパイルし直す
http://192.168.34.10:9000/App/Hello?myName=test

### validation

func (c App) Hello(myName string) revel.Result {
	c.Validation.Required(myName).Message("Your name is required")
	c.Validation.MinSize(myName, 3).Message("Your name is not long enough!")

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

	return c.Render(myName)
}
<div class="span6">
      {{template "flash.html" .}}
      <form action="/App/Hello" method="GET">
        {{with $field := field "myName" .}}
        <input type="text" name="{{$field.Name}}" value="{{$field.Flash}}"/>
        {{end}}
        <br><br>
        <input type="submit" value="Say hello!"/>
      </form>
    </div>

OK, 後はMySQL連携

[Go] Revelでroutingとテンプレート

### Request Flow
conf/route

GET     /                                       App.Index

App.IndexとはApp ControllerのIndexメソッドに飛ぶということ

### Controller
app/controllers/app.go

package controllers

import (
	"github.com/revel/revel"
)

type App struct {
	*revel.Controller
}

func (c App) Index() revel.Result {
	greeting := "Alopha World"
	return c.Render(greeting)
}

### Template
app/views/App/index.html

{{set . "title" "Home"}}
{{template "header.html" .}}

<header class="jumbotron" style="background-color:#A9F16C">
  <div class="container">
    <div class="row">
      <h1>{{.greeting}}</h1>
      <p></p>
    </div>
  </div>
</header>

<div class="container">
  <div class="row">
    <div class="span6">
      {{template "flash.html" .}}
    </div>
  </div>
</div>

{{template "footer.html" .}}

なるほど、だいたい掴めた。直感的に分かりやすい。
よし、Revelで書こう

[Go] Revelのinstallとrun -a myapp

Revelの特徴: MVCモデル, 自動コンパイル

ドキュメントを読んでも埒が明かないので、公式のTutorialを始めます。

$ go version
go version go1.15.14 linux/amd64

To get the Revel framework
$ go get github.com/revel/revel
$ go get github.com/revel/cmd/revel

$ export PATH=”/home/vagrant/go/bin”
$ revel
Usage:
revel [OPTIONS]
// 省略

$ /home/vagrant/go/bin/revel new -a myapp
Revel executing: create a skeleton Revel application
Your application has been created in:
/home/vagrant/go/src/github.com/me/revel/myapp

You can run it with:
revel run -a myapp

$ cd myapp
$ /home/vagrant/go/bin/revel run -a myapp
http://192.168.34.10:9000/

OK, keep going

[音声認識] Juliusで独自辞書を作成

辞書ファイルを作成するには、「語彙」「音素」「構文」が必要

語彙: Juliusに認識させたい単語を定義
音素: 語彙の読みを定義
構文: 認識する文章の構成を定義

### 読みファイルの作成
/julius/dict/greeting.yomi
L 平仮名で作成

おはよう	おはよう
ございます ございます
こんにちは こんにちわ
こんばんは こんばんわ

### 音素ファイルの作成
$ iconv -f utf8 -t eucjp dict/greeting.yomi | gramtools/yomi2voca/yomi2voca.pl | iconv -f eucjp -t utf8 > dict/greeting.phone

perl: warning: Setting locale failed.
perl: warning: Please check that your locale settings:
LANGUAGE = (unset),
LC_ALL = (unset),
LANG = “ja_JP.UTF-8”
are supported and installed on your system.
perl: warning: Falling back to the standard locale (“C”).

どうやらjuliusはeucではなくutf8になってるらしい

$ iconv dict/greeting.yomi | gramtools/yomi2voca/yomi2voca.pl > dict/greeting.phone

/julius/dict/greeting.phone

おはよう	o h a y o u
ございます	g o z a i m a s u
こんにちは	k o N n i ch i w a
こんばんは	k o N b a N w a

### 構文ファイルの作成
greeting.grammar
L NS_Bが文書の開始、NS_Eが文章の終了 2行目以降がGREETで認識される読みの文字列

S : NS_B GREET NS_E
GREET : OHAYOU
GREET : OHAYOU GOZAIMASU
GREET : KONNICHIWA
GREET : KONBANWA

### 語彙ファイルの作成
各音素に対して、構文で割り振った読みと、開始終了を表すNS_B、NS_Eを設定

% OHAYOU
おはよう	o h a y o u
% GOZAIMASU
ございます	g o z a i m a s u
% KONNICHIWA
こんにちは	k o N n i ch i w a
% KONBANWA
こんばんは	k o N b a N w a
% NS_B
s		silB
% NS_E
/s	silE

辞書ファイルを作成する
$ cp -b gramtools/mkdfa/mkfa-1.44-flex/mkfa gramtools/mkdfa/mkfa
$ cp -b gramtools/dfa_minimize/dfa_minimize gramtools/mkdfa/dfa_minimize
$ cp -b gramtools/dfa_determinize/dfa_determinize gramtools/mkdfa/dfa_determinize

$ gramtools/mkdfa/mkdfa.pl dict/greeting
// 省略
now reversing dict/greeting.dfa into NFA “dict/greeting.dfa.forward_nfa”
executing [gramtools/mkdfa/dfa_determinize dict/greeting.dfa.forward_nfa -o dict/greeting.dfa.forward_beforeminimize]
6 categories, 6 nodes, 7 arcs
-> determinized: 5 nodes, 7 arcs
executing [gramtools/mkdfa/dfa_minimize dict/greeting.dfa.forward_beforeminimize -o dict/greeting.dfa.forward]
6 categories, 5 nodes, 7 arcs
-> minimized: 5 nodes, 7 arcs

generated: dict/greeting.dfa dict/greeting.term dict/greeting.dict dict/greeting.dfa.forward

### 辞書を指定
$ julius/julius -C ../ja_model/am-gmm.jconf -nostrip -gram dict/greeting -input mic

なるほど、辞書の作り方はOK
次はGoやな

[Python] リストで重複を調べる

& 演算子で取得

l1 = ['今日', '形態素', '解析', '研究']
l2 = ['明日', '研究', '麦芽', '形態素']

l1l2 = set(l1) & set(l2)
print(l1l2)

$ python3 app.py
{‘研究’, ‘形態素’}

lenを使って重複率を調べて分岐処理を行う

l1 = ['今日','の','形態素', '解析', '研究']
l2 = ['明日', '研究', '麦芽', '形態素', '研究']

l1l2 = set(l1) & set(l2)

if(len(l1l2) / len(l1) > 0.2):
	print('重複した文章です')
else:
	print('異なる文章です')

$ python3 app.py
重複した文章です

OK、意外と直ぐに行けた

Pythonでメモ帳にテキストデータを追加して保存

f = open('myfile.txt', 'w')
f = open('myfile.txt', 'a')
f = open('myfile.txt', 'x')

‘w’を指定した場合は新規に作成、上書き
‘a’を指定した場合は最後に追加で書き込み
‘x’はファイルが存在していた場合は、FileExistsError

– timedelta(hours=+9)として、日本時間の日時でテキストファイルを作ります。
– f.write(‘\n’)でコメントごとに改行を入れるようにします。

#! /usr/bin/python3
# -*- coding: utf-8 -*-
from datetime import datetime, timedelta, timezone
JST = timezone(timedelta(hours=+9), 'JST')

today = str(datetime.now(JST).date())

f = open(today +'.txt', 'a')
f.write('そしてアメリカのナイジャ・ヒューストンです')
f.write('\n')
f.close()

f = open(today +'.txt', 'a')
f.write('おっ お〜、ヤバ いややっぱ勝負強いっすよね〜')
f.write('\n')
f.close()

f = open(today +'.txt', 'a')
f.write('今日イチの雄叫びとガッツポーズが此処まで聞こえてきます')
f.write('\n')
f.close()

$ python3 app.py
そしてアメリカのナイジャ・ヒューストンです
おっ お〜、ヤバ いややっぱ勝負強いっすよね〜
今日イチの雄叫びとガッツポーズが此処まで聞こえてきます

OK