Elsaの技術日記(徒然なるままに)

主に自分で作ったアプリとかの報告・日記を記載

MENU

Go言語でログインサーバ構築

前回Go言語でhttpsサーバを構築してみました。
elsammit-beginnerblg.hatenablog.com

サイトによってはログイン権限が必要になることもありますよね??
そこで今回は、httpsでのログインサーバを構築してみたいと思います!!



■動作環境

前回のhttpsサーバの環境と同様に、
・OS:Ubuntu20.04
・言語:go言語(1.13.8)
・ツール:gin
を用いていきます。

■フロントエンド実装

まずはhtmlでフロントエンドの実装をしていきます。
コードはこちら。

<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta cahrset="UTF-8">
        <title>Gin Application</title>
    </head>
    <body>
        <form method="post" action="/login">
            <h1>Login</h1>
            <input type="text" name="userId"><br/>
            <input type="password" name="password">
            <input type="submit" value="ログイン">
            <script type="text/javascript">
            console.log({{.result}})
            if({{.result}} != 0){
                alert({{.text}})
            }
            </script>
        </form>
    <body>
</html>

こちらをhtmlコードは下記のような画面が構成されます。
f:id:Elsammit:20210320190036p:plain

やっていることは単純で、
ユーザ名、パスワードを入力する欄を用意し、
ログインボタンを押下すると"/login"パスで入力情報がサーバサイドに送信。
といった構成になっております。

responseはjson形式で得られ、resultキーにログイン結果が格納されるので結果を元にalertを出すコードにしました。

■サーバサイド DB作成

次にサーバサイド。
まずはユーザ名やパスワードを格納しておくためのDBを用意します。
今回はログインのみかつユーザも1名だけなのでファイルで管理してみても良かったのですが、、、
汎用性を考えてDBでの構築を行いました。

DBの作成やデータ登録はこちらにまとめましたのでこちらをご確認ください。
elsammit-beginnerblg.hatenablog.com

今回は、
テーブル名を、
・UserInfo
とし、カラム名を、
・ユーザ名
・パスワード
とし、登録内容は、
・elsammit
・test
としました。

■サーバサイド 入力データからログイン結果判定

では今回の本題である、Go言語でログインサーバを構築していきたいと思います。
百聞は一見に如かず!!
まずはコードを載せます。

package main

import (
	"database/sql"
	"fmt"
	"log"
	"net/http"

	"github.com/gin-gonic/gin"
	_ "github.com/mattn/go-sqlite3"
)

type state struct {
	username string
	password string
}

func DB_Read(Name string, Password string) int {
	ret := -1
	DbConnection, err1 := sql.Open("sqlite3", "./db.sql")
	if err1 != nil {
		log.Fatal(err1)
	}

	cmd := "SELECT * FROM UserInfo"
	rows, _ := DbConnection.Query(cmd)
	defer rows.Close()

	for rows.Next() {
		var s state
		err := rows.Scan(&s.username, &s.password)
		if err != nil {
			log.Println(err)
		}
		fmt.Println(s.username, s.password)
		if s.username == Name {
			if s.password == Password {
				ret = 0
			} else {
				ret = 1
			}
		} else {
			ret = 2
		}
	}
	return ret
}

func Index(ctx *gin.Context) {
	fmt.Println("Index")
	ctx.HTML(http.StatusOK, "signin.html", gin.H{
		"text":   "hello",
		"result": 0,
	})
}

func Login(ctx *gin.Context) {
	fmt.Println("Login")

	userid, _ := ctx.GetPostForm("userId")
	password, _ := ctx.GetPostForm("password")
	buf := DB_Read(userid, password)
	sendMsg := "None"
	html := "signin.html"
	result := -1
	if buf == 0 {
		sendMsg = "OK"
		html = "LoginOK.html"
		result = 0
	} else if buf == 1 {
		sendMsg = "Password not collect"
	} else if buf == 2 {
		sendMsg = "User Name not resistered"
	}
	fmt.Println(userid)
	fmt.Println(password)
	ctx.HTML(http.StatusOK, html, gin.H{
		"text":   sendMsg,
		"result": result,
	})
}

func main() {
	r := gin.Default()
	r.LoadHTMLGlob("html/*")
	r.Static("/image", "./image")
	r.Static("/Certificate", "./Certificate")
	r.GET("/", Index)       // ログイン画面(GET処理)
	r.POST("/login", Login) // ログイン画面(POST処理)
	r.RunTLS(":8080", "./Certificate/server.pem", "./Certificate/server.key")
}

ちょっとコードが長いですね😅
長くなっている理由はDBからのデータ読み出しの処理が長くなってしまったためです。
長くなっているDBについてまずは解説していきます。
DBの読み出し部分のコードはこちら。

func DB_Read(Name string, Password string) int {
	ret := -1
	DbConnection, err1 := sql.Open("sqlite3", "./db.sql")
	if err1 != nil {
		log.Fatal(err1)
	}

	cmd := "SELECT * FROM UserInfo"
	rows, _ := DbConnection.Query(cmd)
	defer rows.Close()

	for rows.Next() {
		var s state
		err := rows.Scan(&s.username, &s.password)
		if err != nil {
			log.Println(err)
		}
		fmt.Println(s.username, s.password)
		if s.username == Name {
			if s.password == Password {
				ret = 0
			} else {
				ret = 1
			}
		} else {
			ret = 2
		}
	}
	return ret
}

引数は入力されたユーザ名、パスワードです。

DBからのデータを

	cmd := "SELECT * FROM UserInfo"
	rows, _ := DbConnection.Query(cmd)
	defer rows.Close()

で読み出し、rowsに読み出しデータを格納。

	for rows.Next() {
		var s state
		err := rows.Scan(&s.username, &s.password)
		if err != nil {
			log.Println(err)
		}
		fmt.Println(s.username, s.password)
		if s.username == Name {
			if s.password == Password {
				return  0
			} else {
				ret = 1
			}
		} else {
			ret = 2
		}
	}

にてrows内のデータを読み出していきます。

var s state

ですが、あらかじめ構造体としてこちらを定義しております。

type state struct {
	username string
	password string
}

こちらの構造体にデータを格納し、入力されたデータとDBで読み出したデータの比較を行っております。

そして、、、
こちらの関数にてフロントエンドに送信するメッセージやログイン結果をresponseしています。

func Login(ctx *gin.Context) {
	fmt.Println("Login")

	userid, _ := ctx.GetPostForm("userId")
	password, _ := ctx.GetPostForm("password")
	buf := DB_Read(userid, password)
	sendMsg := "None"
	html := "signin.html"
	result := -1
	if buf == 0 {
		sendMsg = "OK"
		html = "LoginOK.html"
		result = 0
	} else if buf == 1 {
		sendMsg = "Password not collect"
	} else if buf == 2 {
		sendMsg = "User Name not resistered"
	}
	fmt.Println(userid)
	fmt.Println(password)
	ctx.HTML(http.StatusOK, html, gin.H{
		"text":   sendMsg,
		"result": result,
	})
}

■最後に

今回は登録人数が1名だったこともあり、判定式が1名用で作成してしまっております😥
登録ユーザが複数人いる場合には今回のコードだとダメな部分あるので変更しなきゃな🤔

後、
サーバーサイド側のパスワード管理やSQLインジェクション対応などのセキュリティに関しもしっかりしとかなきゃですね!!
まだまだセキュリティ関係は勉強が足りないのが悩みどころ😫
重要なところなのでもっと勉強していきます!!



Go言語 ginでhttpsサーバとして扱う

今回はgo言語でのhttpsサーバ立ち上げ方法についてまとめたいと思います!!
今までhttpアクセスができるようにサーバ立ち上げておりましたが、セキュリティに配慮するならhttpsも扱えた方がいいよな🤔
と思い、試しにginで実現させてみました。



■環境

今回httpsサーバ立ち上げで実施した環境はこちら。
・OS:Ubuntu20.04
・言語:go言語(1.13.8)
・ツール:gin

オレオレ証明書

httpsサーバを立ち上げるにあたり、SSL証明書が必要になります。
今回は自己署名を用いることにします。

自分の環境で試しに動かすなら自己署名でよいですが、
不特定多数が使用する場合や企業等ちゃんとした環境で動作させたい場合には正規の認証局が発行する証明書を利用ください。

では自己署名の作成していきます。

まずは秘密鍵を生成します。
秘密鍵を生成するにあたってOpenSSLを用います。
OpenSSLをインストールする場合はこちらのコマンドを実行すればOKです。

apt-get install openssl

ではOpenSSLで秘密鍵を生成します。
コマンドはこちら。

openssl genrsa -aes128 -out server.key 2048

こちらを実行すると下記メッセージが出力され、パスフレーズの入力が求められるため入力を行います。

Enter pass phrase for server.key:
Verifying - Enter pass phrase for server.key:

もしパスフレーズが不要の場合には下記を実行してパスフレーズの削除を行います。

openssl rsa -in server.key -out server.key

次に証明書署名要求(CSR / Certificate Signing Request)を作成します。
証明書署名要求とは、認証局にサーバの公開鍵に電子署名してもらうよう要求するメッセージのことを指します。
コマンドはこちら。

openssl req -new -days 3650 -key server.key -out server.csr

こちらの通り、location等の入力が求められるので各項目を入力していきます。

Country Name (2 letter code) [AU]:JP
State or Province Name (full name) [Some-State]:Tokyo
Locality Name (eg, city) []:
Organization Name (eg, company) [Internet Widgits Pty Ltd]:
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:
Email Address []:

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:

そして、最後にサーバ証明書の作成を行います。
コマンドはこちら。

openssl x509 -in server.csr -out server.pem -req -signkey server.key -days 3650

これで証明書の作成が完了です。
こちらを実行することにより、

server.crt  
server.csr  
server.key

というファイルが生成されているかと思います。

httpsサーバを立ち上げる

ではhttpsサーバを立ち上げていきます。

まずはサーバであるgo ginを用いたコードです。
こちら!!

package main

import (
	"fmt"
	"net/http"

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

func Index(ctx *gin.Context) {
	fmt.Println("Index")
	ctx.HTML(http.StatusOK, "test.html", gin.H{})
}

func main() {
	r := gin.Default()
	r.LoadHTMLGlob("html/*")
	r.GET("/", Index) // ログイン画面(GET処理)
	r.RunTLS(":8080", "./Certificate/server.pem", "./Certificate/server.key")
}

こちらのコードを実行すれば、
httpsでアクセスするとtest.htmlのページが表示されるようになるかと思います。

httpsサーバとして立ち上げるためには、

r := gin.Default()
r.RunTLS(":8080", "./Certificate/server.pem", "./Certificate/server.key")

と定義すればOKです。
RunTLSがhttpsサーバを立ち上げるためのコードで引数に先ほど生成した秘密鍵や証明書を指定します。

■ついでに

ginで表示させたページに対してjavascriptファイルやimageファイルなどの静的ファイルにアクセスするためには、

r.Static("/Certificate", "./Certificate")

というようにStaticで利用したいファイルが格納されたフォルダを指定すればOKです。

■最後に

今回はセキュリティも考慮してhttpsサーバを立ち上げる方法についてまとめてみました。
今度はログイン機能でも作ってみようかな?🤔



htmlでスライドショー作成 ~合わせて桜フォトフレームのご紹介~

少し時間が空いてしまいました😅
色々と所要があり、ブログの作成が行えず時間が空いてしまいました。

今回は先日のブログでご紹介した桜フォトフレームについての技術内容についてまとめておこうと思います。



■桜フォトフレーム

桜フォトフレームは別のブログにてご紹介しております。
elsammit-diarypractice.hatenablog.com

こちらのように桜を背景に写真をスライドショーさせるWebアプリになります。
f:id:Elsammit:20210313094910g:plain

すでに桜の表現についてはまとめていますのでこちらをご参照ください。
elsammit-beginnerblg.hatenablog.com

今回は写真をスライドショーさせる処理についてまとめておきます。

■桜フォトフレームのソースコード

ソースコードはこちらに格納しております。
よろしければどうぞ。
https://github.com/Elsammit/SakuraPhotoFrame.git

■写真のスライドショー

スライドショーはjavascriptとhtml、cssで実現させました。
まずはhtmlです。
コードはこちら。

<html>
    <head>
        <meta charset="utf-8">
    </head>
    <body>
        <link rel="stylesheet" href="sakura.css">
        <script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
        <script src="sakura.js"></script>
        <div id="photo">
            <ul class="slideshow">
                <li class="current"><img src="image/Photo1.JPG" alt="A" class="photoImg" id="photoNum1"></li>
                <li><img src="image/Photo2.JPG" alt="B" class="photoImg" id="photoNum2"></li>
                <li><img src="image/Photo3.JPG" alt="C" class="photoImg"  id="photoNum3"></li>
                <li><img src="image/Photo4.JPG" alt="D" class="photoImg" id="photoNum4"></li>
                <li><img src="image/Photo5.JPG" alt="E" class="photoImg"  id="photoNum5"></li>
            </ul>
        </div>
    </body>
</html>

このソースのようなcurrentクラスが定義されたimgタグが画像として表示します。

<li class="current"><img src="image/Photo1.JPG" alt="A" class="photoImg" id="photoNum1"></li>

javascriptでは一定時間毎にcurrentクラスを切り替えることで表示するimgタグを切り替えていきます。

さらに、jQueryを用いるので

<script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>

を定義しました。

ではjavascriptのコードです。こちら!!

var count = 0;
(function($){
    $(document).ready(function(){
      var slides = $(".slideshow > li");
      
      function toggle_slide(){
        count = (count + 1) % 5;
        slides.removeClass("current").eq(count).addClass("current");
      }
      setInterval(toggle_slide, 5000);
    });  
})(jQuery);

slideshowクラス内のliを変数slidesとして取得。
そして、一定期間(今回は5秒間)でcurrentクラスを切り替えています。
切り替えのコードはこちら。

slides.removeClass("current").eq(count).addClass("current");

こちらのコードは現在のcurrentクラスを削除し(count+1) %5したli番号にcurrentクラスを付与します。

最後にcssですが、

.slideshow > li {
    opacity: 0;
    transition: opacity 1s;
}
  
.slideshow > li.current {
    opacity: 1;
}

としています。
currentクラスはopacityで表示させ、他の画像は0で非表示とさせました。
また、切り替えはフェードアウトさせたかったので、
transitionを設定しました。

ついでですが、、、
写真(画像)下部の●ですが、コードはこちらのようになります。

function selectImgcircle(count){
    for(var i=1;i<=5;i++){
        var buf = "select" + i;
        document.getElementById(buf).style.border = "1px solid rgb(206, 202, 202)";
        document.getElementById(buf).style.backgroundColor = "rgb(206, 202, 202)";
    }
    
    var selectID = "select"+(count+1);
    document.getElementById(selectID).style.border = "1px solid black";
    document.getElementById(selectID).style.backgroundColor = "black";
}
<div id="selectedImg">
    <div id="select1"></div>
    <div id="select2"></div>
    <div id="select3"></div>
    <div id="select4"></div>
    <div id="select5"></div>
</div>

javascriptにて指定されたcount値に応じてselect id値を切り替えております。
選択された●はblackとし、未選択時のidにはrgb(206, 202, 202)
としております。

■最後に

今回は写真(画像)をスライドショーをさせる方法をまとめ、作成した桜フォトフレームをご紹介させていただきました!!
今度は何を作ろうかな??🤔

何か作成したらブログにまとめていきたいと思います。
今後も頑張っていきますのでよろしくお願いします!!



Web上で桜をヒラヒラ舞わせてみる

もうすぐ春ですね!!
春と言えば桜!!ということで、Web上で桜をヒラヒラ舞わせてみようと思い、コーディングしてみました。
まだまだ自粛でお花見も難しそうなので、お部屋で桜が舞い散る風景を見て楽しめればと思っております。

今回はフレームワークは用いずに、
 ・html
 ・javascript
 ・css
の基本技術を用いて桜を作成していきたいと思います。



■参考資料

今回桜をヒラヒラ舞わせるにあたり、こちらの記事を参考にさせて頂きました。
actyway.com

■結果

今回ご紹介するコードを実行するとこのようなWebページが作成できます。
f:id:Elsammit:20210313113737g:plain

■桜ヒラヒラ(html)

htmlですが、今回はjavascriptcssを読み出すだけしか実行していないです。
コードはこちら。

<html>
    <head>
        <title>桜フォトフレーム</title>
    </head>
    <body>
        <link rel="stylesheet" href="sakura.css">
        <script src="sakura.js"></script>
    </body>
</html>

■桜ヒラヒラ(css)

cssでは背景画像の作成と花びらの作成を行っております。
花びらはこちらの通り作成しており、

.hana{
    position:absolute;
    height:0;
    width:0;
    border: 10px solid pink;
    border-radius:15px;
    border-top-right-radius:0;
    border-bottom-left-radius:0;
}
.hana::after{
	content:"";
	display:block;
	position:absolute;
	top:-7px;
	left:-7px;
	height:0;
	width:0;
	border: 10px solid pink;
	border-radius: 15px;
	border-top-right-radius: 0;
	border-bottom-left-radius: 0;
	-webkit-transform: rotate(15deg);
	-ms-transform: rotate(15deg);
	transform: rotate(15deg);
}

ただし、これだけだと花びら全て同一角度になってしまうので、こちらのコードで複数の角度を用意。

.type1{
    -webkit-transform: rotate(0deg);
    -ms-transform: rotate(0deg);
    transform: rotate(0deg);
    border-color:#ffecf7;
    animation:v1 6s infinite;
}

.type2{
    -webkit-transform: rotate(60deg);
    -ms-transform: rotate(60deg);
    transform: rotate(60deg);
    border-color:#ffdbf0;
    animation:v2 6s infinite;
}

.type3{
    -webkit-transform: rotate(120deg);
    -ms-transform: rotate(120deg);
    transform: rotate(120deg);
    border-color:#febae2;
    animation:v3 5s infinite;
}

.type4{
    -webkit-transform: rotate(180deg);
    -ms-transform: rotate(180deg);
    transform: rotate(180deg);
    border-color:#fea9db;
    animation:v4 5s infinite;
}

.type5{
    -webkit-transform: rotate(240deg);
    -ms-transform: rotate(240deg);
    transform: rotate(240deg);
    border-color:#fd77c6;
    animation:v5 4s infinite;
}

.type6{
    -webkit-transform: rotate(360deg);
    -ms-transform: rotate(360deg);
    transform: rotate(360deg);
    border-color:#fd66bf;
    animation:v6 4s infinite;
}

.type1::after{
    border-color:#ffecf7;
}

.type2::after{
    border-color:#ffdbf0;
}

.type3::after{
    border-color:#febae2;
}

.type4::after{
    border-color:#fea9db;
}

.type5::after{
    border-color:#fd77c6;
}

.type6::after{
    border-color:#fd66bf;
}

最後に、落ちていく花びらが同じ角度で落ちていくのは不自然なので、

@keyframes v1{
	0%{transform: rotate(0deg);}
	50%{transform: rotate(270deg);}
	100%{transform: rotate(1deg);}
}
@keyframes v2{
	0%{transform: rotate(-90deg);}
	50%{transform: rotate(-360deg);}
	100%{transform: rotate(-89deg);}
}
@keyframes v3{
	0%{transform: rotate(0deg);}
	50%{transform: rotate(270deg);}
	100%{transform: rotate(1deg);}
}
@keyframes v4{
	0%{transform: rotate(-90deg);}
	50%{transform: rotate(-360deg);}
	100%{transform: rotate(-89deg);}
}
@keyframes v5{
	0%{transform: rotate(0deg);}
	50%{transform: rotate(270deg);}
	100%{transform: rotate(1deg);}
}
@keyframes v6{
	0%{transform: rotate(-90deg);}
	50%{transform: rotate(-360deg);}
	100%{transform: rotate(-89deg);}
}

を追加。

背景は、

body{
    background-image: url(image/背景画像);
    background-size: cover;
    background-repeat: no-repeat;
}

で定義。

■桜ヒラヒラ(javascript)

最後にjavascriptです。
まずは花びらを生成します。
コードはこちら。

    // 花びら生成.
    for(var i=0;i<maxNum;i++){
		var m = d.createElement('div');
		m.id = 'hanabira'+i;
		top[i] = Math.random()*-1000+u;
		left[i] = Math.random()*w.innerWidth - width_padding;

                var clss = 'hana type'+(Math.floor(Math.random()*6)+1);
		m.setAttribute('class',clss);
		m.setAttribute('style','z-index:'+(z+i)+';top:'+top[i]+'px;left:'+left[i]+'px;');
		q.appendChild(m); 
		yura[i] = Math.random()*40+5; 
		speed[i] = Math.random()*5+2; 
		group[i] = d.getElementById('hanabira'+i);
		count[i] = 0;
	}

こちらでdivタグを生成の上、絶対値指定でtop、leftの位置指定を行います。
そして、先ほどのtypeクラスや落ちるスピードを指定していきます。

実際に花びらが落ちていくコードはこちら。

// 花びらを繰り返し動かす部分
setInterval(function(){
	for(var i=0;i<maxNum;i++){
		if(top[i]<u+h-40){
			if(yura[i]>=count[i]){
				left[i] = left[i]+0.5+Math.random()*0.5;
			}else{
				left[i] = left[i]-0.5-Math.random()*0.5;
			}
			if((yura[i]*2)<=count[i]){
				count[i] = 0;
			}
		}else{
			top[i] = u-40;
			left[i] = Math.random()*w.innerWidth - width_padding;
		}
		top[i] = top[i]+speed[i];
		group[i].style.top = top[i]+'px';
		group[i].style.left = left[i]+'px';
		count[i]++;
	}
},45);

カウンタが指定しているyura値に達しているか否かで右左で動かす方向が切り替わり、
下方向にはspeed値で指定した値で移動する関数になっています。
本関数は45msでコールされるので、45ms毎に移動動作します。

最後に、表示する枚数はmaxNumにて指定するのですが、
今回は50を指定しました。

■おまけ

本コードで試しに500枚の花びらを舞わせてみました!!
結果はこちらw
f:id:Elsammit:20210313184340g:plain

花びらがとても多い😅
花びらの枚数はほどほどがいいですねw

ということで自分は見栄えがキレイな50枚にして使うことにしました!!
次いでに10000枚にするとカクカク動いて使い物になりませんでしたw

ソースコード

コードですが、こちらに格納しました。
https://github.com/Elsammit/sakura.git

■最後に

今回は春を先取りして桜をヒラヒラ舞わせてみました。
これを用いて、フォトフレーム的なものを作成してみました!!
アプリとしての紹介はこちらでさせて頂きました。
https://elsammit-diarypractice.hatenablog.com/entry/2021/03/13/135531

技術的な内容はまたこちらのブログにも投稿したいと思いますので、お待ちください。


golangでクラスを使うには??(別ファイルで定義したクラスの利用も載せてみた)

今回は今までと毛色を変えてGo言語でクラスを用いる方法についてまとめていきたいと思います!!
さらに、定義するクラスと実際に用いるファイルは分けたいので別ファイルのクラスを用いる方法も合わせて載せていきます!!



■go言語でクラスって使えるの?

go言語でクラス定義が出来るのか??🤔
調べた結果、、、
go言語はオブジェクト指向言語ではないため、クラスという概念は存在しないようです。
ただし、クラス・オブジェクトが全く使えない。というわけではなく、
構造体・インターフェースに置き換えることが出来、疑似的にクラスのように利用することが可能になるようです。

■go言語でクラスっぽく書いてみる

では実際にgo言語で構造体を用いてクラスっぽく書いてみたいと思います。

コードはこちら。

package main

import (
	"fmt"
	"strconv"
)

type calculator struct{}

func (t calculator) pluse(a int, b int) int {
	return a + b
}

func (t calculator) minus(a int, b int) int {
	return a - b
}

func main() {
	var calcType calculator
	ret1 := calcType.pluse(2, 1)
	ret2 := calcType.minus(2, 1)

	fmt.Println("ret1:" + strconv.Itoa(ret1) + ", ret2:" + strconv.Itoa(ret2))
}

こちらのコードは加算、減算を行うクラス(構造体)を定義し、メイン関数にて呼び出して返り値をログ出力しているコードになります。
こちらがクラスっぽく記載した構造体定義になります。

type calculator struct{}

func (t calculator) pluse(a int, b int) int {
	return a + b
}

func (t calculator) minus(a int, b int) int {
	return a - b
}

こちらのように定義することで、

var calcType calculator
ret1 := calcType.pluse(2, 1)
ret2 := calcType.minus(2, 1)

というように呼び出せば使えるようになります。
少しクセはありますが、クラスのように使えてますね!!

少しだけ脱線してしまいますが、、、
数値(int型)を文字列に変換する場合には、

import (
	"strconv"
)
 strconv.Itoa(数値)

というように strconv.Itoaを用いればOKです。

■別ファイルで定義したクラス(構造体)を呼び出してみる

では、別ファイルで定義した構造体を呼び出す方法についてまとめていきます。

まずフォルダ構成ですが、今回はこちらとします。

|
|-function
|         |-func1.go
|-main.go

ではfunc1.goのコードを作成します。

//func1.go

package func1

type Calculator struct{}

func (t Calculator) Pluse(a int, b int) int {
	return a + b
}

func (t Calculator) Minus(a int, b int) int {
	return a - b
}

コードは先ほどと同様に加算・減算を定義します。
先ほどのコードと大きく異なる点があります。
それは、、、
各構造体や関数の頭文字を大文字にしていることです。
別ファイルから読み出す際には頭文字を大文字にしないとエラーになってしまうようです😅
ハマりやすいところなので注意!!

そしてmain.goはこちら。

package main

import (
	"./function"
	"fmt"
	"strconv"
)

func main() {
	var calcType func1.Calculator
	ret1 := calcType.Pluse(2, 1)
	ret2 := calcType.Minus(2, 1)

	fmt.Println("ret1:" + strconv.Itoa(ret1) + ", ret2:" + strconv.Itoa(ret2))
}

まず、importにフォルダ名であるfunctionを定義します。
そして、

pakage名.構造体名

といったように定義を行い後は先ほどと同様に関数を呼び出せばOKです。

そこまで難しくはないのですが、やはり大文字にする点は注意が必要ですね。。
思わぬ落とし穴にはまってしまいそうなので注意したいと思います!!

■最後に

go言語はオブジェクト指向ではないのでクラスという概念がないことは知らなかったので勉強になりました!!
まだまだgo言語は知らない点が多いので勉強していきたいと思います!!
知らない点はまた記事に上げたいと思います。



html, cssでレスポンシブ対応について

実は今ちょっとしたWebアプリを作成しているところw。
作成中に
スマホへの対応もしとこうかな?🤔
と思ったのですが、、、

レスポンシブ対応したことなかった!!😅
と気づき、調べてみました。

HTML5&CSS3デザインブック

忘れないように今回もまとめておこうと思います!!



■viewportの指定

まずは画面サイズに応じて適切なレイアウトを行うためにviewportを指定していきます。

viewportとは、、、
viewportとはWebページの表示領域を指します。viewportを正しく指定すると、画面サイズに応じたレイアウトの最適化ができるようになります。レスポンシブデザインではこのviewportが重要で必須のテクニックになります。
※参考:https://www.site-convert.com/archives/1528

このviewportですが、htmlのmetaタグで指定すればOKです。
コードはこんな感じです。

<meta name="viewport" content="width=device-width" initial-scale=1, minimum-scale=1>

content内に各種プロパティを設定すればよく、
【widthプロパティ】
表示領域を指定するプロパティ。
device-widthを指定した場合、表示しているデバイスの画面サイズが表示領域になり、レスポンシブ対応の場合にはdevice-widthを利用します。

【initial-scale、minimum-scale】
初期表示のズーム倍率の指定になります。
initial-scaleは特別な理由がない限りは1、minimum-scaleは実装したアプリに応じて値を指定すればよさそうです。

■画面サイズに応じたレイアウト・デザインの指定

viewportにより画面サイズの取得が出来るようになりました。
今度はレイアウト・デザインの指定です。

レイアウト・デザインを指定するために、メディアクエリを利用していきます。

メディアクエリとは、、、
レスポンシブデザインを実現するための重要なテクニックです。
レスポンシブデザインでは、デバイスの画面幅に応じて異なるデザインを記述する必要があります。
メディアクエリを使用することで、デバイスの画面幅が〇〇px以上だったらこのCSS、〇〇px以下だったらこっちのCSSといった感じで、画面幅に応じてCSSを切り替えることができます。
※参考:https://www.site-convert.com/archives/1528

メディアクエリの書き方としては2種類あり、
 ①PCをメインにしつつ、タブレットスマホの場合にはレイアウトデザインを切り替える
 ②スマホをメインにしつつ、タブレットやPCの場合にはレイアウトデザインを切り替える
になります。

①の場合には、

/* PCの場合にはこちらように外に記述*/

@media screen and (max-width999px){
 /*タブレット用にデザイン・レイアウトするcssを記述*/
}

@media screen and (max-width:350px){
 /*スマホ用にデザイン・レイアウトするcssを記述*/
}

と記述します。
こちらのコードですが、
 ・1000px以上:cssでデフォルト記述された値を参照
 ・999px以下、351px以上:タブレット用として@mediaに囲われたcssを読み出す
 ・350px以下:スマホ用として@mediaに囲われたcssを読み出す
と実行されます。
こちらですが、上から順番に実行されるので、
タブレット用とスマホ用を逆に指定しまうと、スマホの場合でもタブレット用のメディアクエリが読み出されてしまうので注意。

②の場合は、

@media screen and (min-width: 351px) {
	/*タブレット用にデザイン・レイアウトするcssを記述*/
}
@media screen and (min-width: 1000px) {
	/*スマホ用にデザイン・レイアウトするcssを記述*/
}

と記述します。
こちらのコードも先ほどと同様に、
 ・1000px以上:cssでデフォルト記述された値を参照
 ・999px以下、351px以上:タブレット用として@mediaに囲われたcssを読み出す
 ・350px以下:スマホ用として@mediaに囲われたcssを読み出す
となります。
先ほど記載しました通り、①と②はメインとして設定するcssをPCなのかスマホなのか、の違いのみなので、
cssとしてPC用で作成して、タブレットスマホは差分でよい場合には①、
cssとしてスマホ用で作成して、タブレット・PCは差分でよい場合には②、
を用いればOKです。

■レスポンシブ対応したデザインをデバッグする

ここまでの方法を活用していけば、レスポンシブ対応したレイアウト・デザインを指定することが可能になるのですが、
肝心のデバッグ方法はどうすればよいのか?🤔
と疑問を持っていました。

こちらのデバッグですが、Google chromeデベロッパーツールを用いれば簡単にできます!!
手順はこちら。
 (1)Google Chromeで自分がデバッグしたいページを表示
 (2)F12キーを押下してデベロッパーツールを表示
 (3)下記デバイスアイコンをクリック
f:id:Elsammit:20210307185723p:plain
 (4)デバッグしたい画像サイズを指定

Google Chromeを用いれば簡単にレスポンシブ対応のデバッグが行えて便利😂

■実際にデバッグしてみる

では、レスポンシブ対応したコードでレスポンシブ対応によるデザインが正しく動いているか動かしてみます!!
結果はこちら。
f:id:Elsammit:20210307190544g:plain

上手く動いていますね😆
OKです!!

■最後に

初めてレスポンシブ対応してみましたが、複数種類の画面サイズに合わせてレイアウトを切り替えるのって結構難しいですね。。
設計する段階から共通部とレスポンシブ対応部を分けて考えていくと簡単になるのかな?🤔
これからレスポンシブ対応意識した設計も心がけていきたいです!!



Kaggle初心者がTitanic - Machine Learning from Disasterを解いてみた

約半年前に登録していたKaggle。
https://elsammit-beginnerblg.hatenablog.com/entry/2020/09/24/221054

データサイエンスの森 Kaggleの歩き方

たまに機械学習の環境として利用させていただいていたのですが、
何も挑戦していなかったんですよね😥
まだ早い、まだ早い、と思ってしまい一歩が踏み出せなかった。。

ふと、
流石に登録しただけじゃな、、
ちょっと挑戦してみよう!!
と思い立ち、
まずは登竜門である「Titanic - Machine Learning from Disaster」を解いて投稿まで実施してみることにしました!!



■Titanic - Machine Learning from Disasterとは?

Kaggle側が用意した機械学習初学者向けの課題です。
課題はタイタニック号の難破船を生き延びた乗客を予測するモデルの作成
になります。

用意されているデータは、
レーニング用データ:train.csv
テスト用データ:test.csv
テスト結果データ:gender_submission.csv
の3つです。
レーニングデータはこちらのようなデータが格納されております。
f:id:Elsammit:20210306120300p:plain

テストデータはこちら。
f:id:Elsammit:20210306120424p:plain

要するにトレーニングデータを元に学習を進めて、
テストデータよりSurvived(0/1)を予測してね。
という問題になります。

■まずは自力で

ではまずは自力で実施してみました。

まずは先ほど取得したcsvを読み出します。
そして、性別とEmbarked(出港地)を文字列⇒整数値に変換した結果を列に追加していきます。
コードはこちら。

import numpy as np # linear algebra
import pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)

train = pd.read_csv("/kaggle/input/titanic/train.csv")
test = pd.read_csv("/kaggle/input/titanic/test.csv")
sample = pd.read_csv("/kaggle/input/titanic/gender_submission.csv")

def type_to_num(p_type,test):
    if p_type == test:
        return 0
    else:
        return 1

def Embarked_Num(Embarked):
    if Embarked == "S":
        return 0
    elif Embarked == "C":
        return 1
    elif Embarked == "Q":
        return 2
    else:
        return 0
    
Sex = train["Sex"].apply(type_to_num,test="male")
train["sex_num"] = Sex
Embarked = train["Embarked"].apply(Embarked_Num)
train["Embarked_num"] = Embarked
train["Age"] = train["Age"].fillna(train["Age"].median())

train.head(5)

試しにこちらを実行すると、こちらのようにEmbarked_numとsex_num列が追加されているのが分かるかと思います。
f:id:Elsammit:20210306121602p:plain

では次にトレーニングデータを元に学習を進めていきます。
モデルは使い慣れているLogisticRegressionで実施してみました。

from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split

X = train.loc[:,["Pclass","Age","SibSp","Parch","Fare","sex_num","Embarked_num"]].values
y = train["Survived"].values

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.1, random_state=0)

lr = LogisticRegression(C=1.0)
lr.fit(X, y)

そして、、、トレーニングしたモデルで実際に予測を行ってみたいと思います!!
先ほどと同様に性別等を文字列⇒整数値に変換し列追加して、モデルに引数として与えて予測させてみます。

Sex_Test = train["Sex"].apply(type_to_num,test="male")
test["sex_num"] = Sex_Test
Embarked_test = train["Embarked"].apply(Embarked_Num)
test["Embarked_num"] = Embarked_test
test["Age"] = test["Age"].fillna(train["Age"].median())
test.Fare[152] = test.Fare.median()

Xtest = test.loc[:,["Pclass","Age","SibSp","Parch","Fare","sex_num","Embarked_num"]].values
ytest = sample["Survived"].values

print("-------------------------------------------------")
print("testデータに対するscore: %.3f" % lr.score(Xtest, ytest))
print("-------------------------------------------------")

こちらを実施した結果、、、(泣)

-------------------------------------------------
testデータに対するscore: 0.531
-------------------------------------------------

ダメダメでした。。

ただせっかく頑張ったので、とりあえずアップしてみることに。
アップは予測結果を書き込んだcsvファイルで行うようなので予測結果をcsvファイルに書き込みます。
コードはこちら。

Prediction = lr.predict(Xtest)

PassengerId = np.array(test["PassengerId"]).astype(int)
solution = pd.DataFrame(Prediction, PassengerId, columns = ["Survived"])
solution.to_csv("result.csv", index_label = ["PassengerId"])

こちらを実行するとresult.csvが生成されます。

では早速結果をアップしてみます。
アップは、
Titanic - Machine Learning from Disaster
コンペに行き、Submit Predictions選択。
f:id:Elsammit:20210306122514p:plain

そして、下記の通り先ほど作成したresult.csvをアップロードの上、
Make Submissionを押下すればOKです。
f:id:Elsammit:20210306122708p:plain

結果ですが、
20611位
ぐらいかな??あまりに結果が悪すぎて順位が見切れてしまっている。
あまりいい結果が得られず残念😥

■他の方のコードを流用してみる

では次に学習も兼ねて他の方のコードを参考にしてみます。
今回はこちらの方を参考にさせて頂きました。
www.codexa.net

詳しいコードはこちらのサイトにて説明されているのでこちらでは省きますが、
私のコードと異なる点は、
 ①モデルは決定木を用いていること
 ②性別とEmbarkedを置き換えている
 ③欠損されたデータに対して平均値を用いて補完をしっかり行っている
の3点でした。

こちらの結果を先ほどと同様にcsvファイルにしてアップロードを行ってみたところ、
スコアは0.73で順位は19000台になりました!!

■最後に

今回は初めてのKaggle投稿に調整してみました!!
他の方のコードを見て思ったのは、
データが上手く扱えていない、適切なモデルを選択出来ていない!!

機械学習を行う上でこれらは重要になるので、、勉強進めます。
後、ただ机上で勉強だけしても生のデータ扱わないと伸びないところもあるかと思うので、
もっと積極的にKaggleのコンペにも参加していきたいと思いました👍