【Next.js】基本的な使い方と特徴

– Next.jsはjsのみ。HTMLファイルは使わずに開発を行う
– Netx.jsでは、Reactとは異なり、複数ページ開発できる

{
    "scripts": {
        "dev": "next",
        "build": "next build",
        "start": "next start",
        "export": "next export"
    }
}

$ npm install –save next react react-dom

pages/index.js

export default () => <div>
    <h1>Next.js</h1>
    <div>Welcome to next.js!</div>
</div>

$ npm run dev

next.config.js

module.exports = {
    exportPathMap: function() {
        return {
            '/': {page: '/'}
        }
    }
}

$ npm run build

const h1 = {
    fontSize:'72pt',
    fontWeight:'bold',
    textAlign:'right',
    letterSpacing:'-8px',
    color:'#f0f0f0',
    margin:'-40px 0px'
}

const p = {
    margin:'0px',
    color:'#666',
    fontSize:'16pt'
}


export default () => <div>
    <h1 style={h1}>Next.js</h1>
    <p style={p}>Welcome to next.js!</p>
</div>

### 複数ページの開発
index.js

import Link from 'next/link';

const h1 = {
    fontSize:'72pt',
    fontWeight:'bold',
    textAlign:'right',
    letterSpacing:'-8px',
    color:'#f0f0f0',
    margin:'-40px 0px'
}

const p = {
    margin:'0px',
    color:'#666',
    fontSize:'16pt'
}


export default () => <div>
    <h1 style={h1}>Next.js</h1>
    <p style={p}>Welcome to next.js!</p>
    <hr />
    <div>
        <Link href="/other">
            Go to Other page &gt;&gt;
        </Link>
    </div>
</div>

other.js

import Link from 'next/link';

const h1 = {
    fontSize:'72pt',
    fontWeight:'bold',
    textAlign:'right',
    letterSpacing:'-8px',
    color:'#f0f0f0',
    margin:'-40px 0px'
}

const p = {
    margin:'0px',
    color:'#666',
    fontSize:'16pt'
}


export default () => <div>
    <h1 style={h1}>Next.js</h1>
    <p style={p}>This is Other page.</p>
    <hr />
    <div>
        <Link href="/">
            &lt;&lt;Back to Index page
        </Link>
    </div>
</div>

### Component
Counter.js

import React, { Component } from 'react';

export default class Counter extends Component {
    msgStyle = {
        fontSize:"16pt",
        backgroundColor:"#eef",
        padding:"5px"
    }

    constructor(props) {
        super(props);
        this.state = {
            counter:0,
            msg: 'counter: 0',
        };
        this.doAction = this.doAction.bind(this);
    }

    doAction() {
        this.setState((state) => {
            const num = state.counter + 1;
            return ({
                counter: num,
                msg: "counter: " + num
            });
        });
    }

    render() {
        return <p onClick={this.doAction}
            style={this.msgStyle}>
            {this.state.msg}
        </p>;
    }
}
import Counter from '../components/Counter';
import style from '../static/Style';

export default () => <div>
    {style}
    <h1>Next.js</h1>
    <p>Welcome to next.js!</p>
    <hr />
    <Counter />
</div>

Reactをより使いやすくした って感じやね

【React.js】コンポーネントの様々な機能

import React, {Component} from 'react';
import Rect from "./Rect";
import './App.css';

class App extends Component {

  input = '';

  msgStyle = {
    fontSize:"20pt",
    color:"#900",
    margin:"20px 0px",
    padding: "5px",
  }
  inputStyle = {
    fontSize: "12pt",
    padding: "5px"
  }

  constructor(props) {
    super(props);
    this.state = {
      message:'type your name:'
    };
    this.doChange = this.doChange.bind(this);
    this.doSubmit = this.doSubmit.bind(this);
  }

  doChange(event) {
    this.input = event.target.value;
  }

  doSubmit(event) {
    this.setState({
      message: 'Hello, ' + this.input + '!!'
    });
    event.preventDefault();
  }
  render() {
    return <div className="App">
    <h1>React</h1>
    <h2>{this.state.message}</h2>
    <form onSubmit={this.doSubmit}>
      <label>
        <span style={this.inputStyle}></span>Message:
        <input type="text" style={this.inputStyle} onChange={this.doChange} required pattern="[A-Za-z _,.]+"/>
      </label>
      <input type="submit" style={this.inputStyle} value="Click" />
    </form>
  </div>;
  }
}

import React, {Component} from 'react';
import Rect from "./Rect";
import './App.css';

class App extends Component {

  input = '';

  msgStyle = {
    fontSize:"20pt",
    color:"#900",
    margin:"20px 0px",
    padding: "5px",
  }
  constructor(props) {
    super(props);
    this.state = {
      message:'type your name:'
    };
    this.doCheck = this.doCheck.bind(this);
  }

  doCheck(event) {
    alert(event.target.value +
      "は長すぎます。(最大10文字)"
    );
  }

  render() {
    return <div className="App">
    <h1>React</h1>
    <h2>{this.state.message}</h2>
    <Message maxlength="10" onCheck={this.doCheck} />
  </div>;
  }
}

class Message extends Component {
  inputStyle = {
    fontSize:"12pt",
    padding:"5px"
  }

  constructor(props) {
    super(props);
    this.doChange = this.doChange.bind(this);
  }

  doChange(e) {
    if(e.target.value.length > this.props.maxlength) {
      this.props.onCheck(e);
      e.target.value =
        e.target.value.substr(0,this.props.maxlength);
    }
  }

  render() {
    return <input type="text" style={this.inputStyle} onChange={this.doChange} />
  }
}

export default App;

### コンテキスト

import React, {Component} from 'react';
import Rect from "./Rect";
import './App.css';

let data = {title: 'Title',
  message: 'this is sample message.'
};

const SampleContext = React.createContext(data);

class App extends Component {

  render() {
    return <div className="App">
    <h1>Context</h1>
    <Title />
    <Message />
  </div>;
  }
}

class Title extends Component {
  static contextType = SampleContext;

  render() {
    return (
      <div>
        <h2>{this.context.title}</h2>
      </div>
    );
  }
}

class Message extends Component {
  static contextType = SampleContext;

  render() {
    return (
      <div>
        <p>{this.context.message}</p>
      </div>
    )
  }
}

export default App;
import React, {Component} from 'react';
import Rect from "./Rect";
import './App.css';

let theme = {
  light: {
    backgroundColor: "#eef",
    color: "#006",
    padding:"10px",
  },
  dark: {
    backgroundColor:"#006",
    color:"#eef",
    padding:"10px",
  }
};

const ThemeContext = React.createContext(theme.dark);

class App extends Component {

  static contextType = ThemeContext;

  render() {
    return <div className="App" style={this.context}>
    <h1>Context</h1>
    <Title value="Content page" />
    <Message value="This is Content sample."/>
    <Message value="これはテーマのサンプルです。"/>
  </div>;
  }
}

class Title extends Component {
  static contextType = ThemeContext;

  render() {
    return (
      <div>
        <h2 style={this.context}>{this.props.value}</h2>
      </div>
    );
  }
}

class Message extends Component {
  static contextType = ThemeContext;

  render() {
    return (
      <div>
        <p style={this.context}>{this.props.value}</p>
      </div>
    )
  }
}

export default App;

【React.js】プロパティとステート

import React, {Component} from 'react';
import Rect from "./Rect";
import './App.css';

class App extends Component {

  data = [];

  msgStyle = {
    fontSize:"20pt",
    color:"#900",
    margin:"20px 0px",
    padding: "5px",
  }
  area = {
    width: "500px",
    height: "500px",
    border: "1px solid blue"
  }

  constructor(props) {
    super(props);
    this.state = {
      list: this.data
    };
    this.doAction = this.doAction.bind(this);
  }

  doAction(e) {
    let x = e.pageX;
    let y = e.pageY;
    this.data.push({x:x, y:y});
    this.setState({
      list:this.data
    });
  }

  draw(d) {
    let s = {
      position:"absolute",
        left: (d.x - 25) + "px",
        top:(d.y - 25) + "px",
        width: "50px",
        height: "50px",
        backgroundColor: "#66f3",
    };
    return <div style={s}></div>
  }

  render() {
    return <div className="App">
    <h1>React</h1>
    <h2 style={this.msgStyle}>show rect. </h2>
    <div style={this.area} onClick={this.doAction}>
      {this.data.map((value) => this.draw(value))}
    </div>
  </div>;
  }
}

export default App;

import React, {Component} from 'react';
import Rect from "./Rect";
import './App.css';

class App extends Component {

  data = [
    "This is list sample",
    "これはリストのサンプルです。",
    "配列をリストに変換します。"
  ];

  msgStyle = {
    fontSize:"20pt",
    color:"#900",
    margin:"20px 0px",
    padding: "5px",
  }

  constructor(props) {
    super(props);
    this.state = {
      list: this.data
    };
  }

  render() {
    return <div className="App">
    <h1>React</h1>
    <h2 style={this.msgStyle}>show list.</h2>
    <List title="サンプル・リスト" data={this.data} />
  </div>;
  }
}

class List extends Component {
  number = 1;

  title = {
    fontSize:"20pt",
    fontWeight:"bold",
    color:"blue",
  };

  render() {
    let data = this.props.data;
    return (
      <div>
        <p style={this.title}>{this.props.title}</p>
        <ul>
          {data.map((item) =>
            <Item number={this.number++} value={item} key={this.number} />
            )}
        </ul>
      </div>
    );
  }
}

class Item extends Component {
  li = {
    listStyleType:"square",
    fontSize:"16pt",
    color:"#06",
    margin:"0px",
    padding: "0px",
  }
  num = {
    fontWeight:"bold",
    color:"red"
  }

  render() {
    return (
      <li style={this.li}>
        <span style={this.num}>[{this.props.number}]</span>
         {this.props.value}
      </li>
    )
  }
}
export default App;

import React, {Component} from 'react';
import Rect from "./Rect";
import './App.css';

class App extends Component {

  input = '';

  msgStyle = {
    fontSize:"20pt",
    color:"#900",
    margin:"20px 0px",
    padding: "5px",
  }

  constructor(props) {
    super(props);
    this.state = {
      message:'type your name:'
    };
    this.doChange = this.doChange.bind(this);
    this.doSubmit = this.doSubmit.bind(this);
  }

  doChange(event) {
    this.input = event.target.value;
  }

  doSubmit(event) {
    this.setState({
      message: 'Hello, ' + this.input + '!!'
    });
    event.preventDefault();
  }
  render() {
    return <div className="App">
    <h1>React</h1>
    <Message title="Children">
      これはコンポーネント内のコンテンツです。
      マルでテキストを分割し、リストにして表示します。
      改行は必要ありません。
    </Message>
  </div>;
  }
}

class Message extends Component {
  li = {
    fontSize:"16pt",
    color:"#06",
    margin:"0px",
    padding: "0px",
  }

  render() {
    let content = this.props.children;
    let arr = content.split('。');
    let arr2 = [];
    for(let i = 0; i < arr.length;i++){
      if(arr[i].trim() != '') {
        arr2.push(arr[i]);
      }
    }
    let list = arr2.map((value,key) => (
      <li style={this.li} key={key}>{value}.</li>
    ));
    return <div>
      <h2>{this.props.title}</h2>
      <ol>{list}</ol>
    </div>
  }
}
export default App;

【React.js】プロジェクトでコンポーネントの開発

App.js

import React, {Component} from 'react';
import './App.css';

class App extends Component {

  render() {
    return <div className="App">
    <h1>React</h1>
    <Rect x="50" y="50" w="150" h="150" c="cyan" />
    <Rect x="150" y="100" w="150" h="150" c="magenta" />
    <Rect x="100" y="150" w="150" h="150" c="black" />
  </div>;
  }
}

class Rect extends Component {
  x = 0;
  y = 0;
  width = 0;
  height = 0;
  color = "white";
  style = {};

  constructor(props) {
    super(props);
    this.x = props.x;
    this.y = props.y;
    this.width = props.w;
    this.height = props.h;
    this.color = props.c;
    this.style = {
      backgroundColor:this.color,
      position:"absolute",
      left:this.x + "px",
      top:this.y + "px",
      width:this.width + "px",
      height:this.height + "px"
    }
  }
  render(){
    return <div style={this.style}></div>
  }
}

export default App;

### ファイルの分割
Rect.js

import React, {Component} from 'react';

class Rect extends Component {
    x = 0;
    y = 0;
    width = 0;
    height = 0;
    color = "white";
    style = {};
  
    constructor(props) {
      super(props);
      this.x = props.x;
      this.y = props.y;
      this.width = props.w;
      this.height = props.h;
      this.color = props.c;
      this.radius = props.r;
      this.style = {
        backgroundColor:this.color,
        position:"absolute",
        left:this.x + "px",
        top:this.y + "px",
        width:this.width + "px",
        height:this.height + "px",
        borderRadius:this.radius + "px"
      }
    }
    render(){
      return <div style={this.style}></div>
    }
  }

export default Rect;

App.js

import React, {Component} from 'react';
import Rect from "./Rect";
import './App.css';

class App extends Component {

  render() {
    return <div className="App">
    <h1>React</h1>
    <Rect x="50" y="50" w="150" h="150" c="cyan" r="50" />
    <Rect x="150" y="100" w="150" h="150" c="magenta" r="75" />
    <Rect x="100" y="150" w="150" h="150" c="black" r="25" />
  </div>;
  }
}

export default App;

### State

import React, {Component} from 'react';
import Rect from "./Rect";
import './App.css';

class App extends Component {

  msgStyle = {
    fontSize:"24pt",
    color:"#900",
    margin:"20px 0px",
    padding: "5px",
    borderBottom: "2px solid #900"
  }

  constructor(props) {
    super(props);
    this.state = {
      msg: 'Hello Component.',
    };
  }

  render() {
    return <div className="App">
    <h1>React</h1>
    <p style={this.msgStyle}>{this.state.msg}</p>
    <p style={this.msgStyle}>{this.props.msg}</p>
  </div>;
  }
}
  constructor(props) {
    super(props);
    this.state = {
      msg: 'Hello',
    };
    let timer = setInterval(() => {
      this.setState((state) => ({
        msg: state.msg + "!"
      }));
    }, 10000);
  }
  constructor(props) {
    super(props);
    this.state = {
      counter:0,
      msg: 'count start!',
    };
    this.doAction = this.doAction.bind(this);
  }

  doAction(e) {
    this.setState((state) => ({
      counter: state.counter + 1,
      msg: 'count: ' + state.counter
    }));
  }

  render() {
    return <div className="App">
    <h1>React</h1>
    <p style={this.msgStyle}>{this.state.msg}</p>
    <button style={this.btnStyle} onClick={this.doAction}>Click</button>
  </div>;
  }

【React.js】コンポーネントの理解

let dom = document.querySelector('#root');

const msg = {
    fontSize: "20pt",
    fontWeight: "bold",
    padding: "10px",
    color: "white",
    backgroundColor: "darkblue"
};

function Welcome(props) {
    return <p style={msg}>Hello React!!</p>;
}

let el = (
    <div>
        <Welcome />
    </div>
);
ReactDOM.render(el, dom);

### 属性の使用

let dom = document.querySelector('#root');

const msg1 = {
    fontSize: "20pt",
    padding: "10px",
    border: "double 5px magenta"
};

const msg2 = {
    fontSize: "16pt",
    fontWeight: "bold",
    padding: "10px",
    backgroundColor: "cyan"
};

function Welcome(props) {
    return <p style={props.style}>Hello, {props.name}!!</p>;
}

let el = (
    <div>
        <Welcome name="Taro" style={msg1} />
        <Welcome name="Hanako" style={msg2} />
    </div>
);
ReactDOM.render(el, dom);

### 計算するコンポーネント

let dom = document.querySelector('#root');

const msg = {
    fontSize: "16pt",
    fontWeight: "bold",
    padding: "10px",
    color: "white",
    backgroundColor: "darkblue"
};

function Calc(props) {
    let total = 0;
    for (let i = 1; i <= props.number; i++) {
        total += i;
    }
    return <p style={msg}>1から{props.number}までの合計は、「{total}」です。</p>;
}

let el = (
    <div>
        <Calc number="100" />
        <Calc number="200" />
        <Calc number="300" />
    </div>
);
ReactDOM.render(el, dom);

let dom = document.querySelector('#root');

class Rect extends React.Component {
    x = 0;
    y = 0;
    width = 0;
    height = 0;
    color = "white";
    style = {};

    constructor(props) {
        super(props);
        this.x = props.x;
        this.y = props.y;
        this.width = props.w;
        this.height = props.h;
        this.color = props.c;
        this.style = {
            backgroundColor:this.color,
            position:"absolute",
            left:this.x + "px",
            top:this.y + "px",
            width:this.width + "px",
            height: this.height + "px"
        }
    }

    render() {
        return <div style={this.style}></div>;
    }
}

let el = (
    <div>
        <Rect x="100" y="100" w="100" h="100" c="cyan" />
        <Rect x="150" y="150" w="100" h="100" c="magenta" />
    </div>
);
ReactDOM.render(el, dom);

ubuntuのcronでpythonを実行する

test.py

import os
import datetime

dt_now = datetime.datetime.now()
str = dt_now.strftime('%Y%m%d%H%M%S')
path = '/home/ubuntu/test/' + str + '.txt'
print(path)

f = open(path, 'w')
f.write('')
f.close()

$ which python3
/usr/bin/python3

$ sudo crontab -e
*/1 * * * * /usr/bin/python3 /home/ubuntu/test/test.py
ctr + x

$ ls
20250328142601.txt 20250328142701.txt test.py

うまく起動できているようです。
バッチの設定はいつもドキドキしますね。。

【Python】current pathを取得する

abspathがファイルの絶対パス。

import os

current_path = os.path.abspath(__file__)
exec_path = current_path.replace('/python/post.py', '')
path = "{}/config/hoge.txt".format(exec_path)
print(path)

なるほど、パスの取得は色々あって便利やね。

【Python】jsonデータを1行ずつ読み取ってjsonの配列にする

json.dumps した後に、配列(json_list=[])に入れると、配列自体はjsonではないので、うまくいかない。
配列に入れてからjson.dumpsすると、jsonデータとして扱われる。

import json
import requests


json_list = []
with open('./data/names.txt') as f:
    for line in f:
        json_open = json.loads(line)
        json_list.append(json_open)
        
json_data = json.dumps(json_list)
print(json_data)

response = requests.post(
    "http://httpbin.org/post", 
    data=json_data,
    headers={'Content-type': 'application/json'}
)
print(response.status_code)
print(response.text)

これ解決するのに半日以上かかりましたorz…

【Blockchain】トレーサビリティの仕組みを考える

### フロントエンド
プロセスの選択肢があり、写真を撮って、Postする。緯度、経度情報が必要な場合は付与する。

<head>
    <meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<title>Document</title>
	<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.3/css/bulma.min.css">
    <script src="https://cdn.jsdelivr.net/npm/vue@2.7.16/dist/vue.js"></script>
    <style>body {margin: 50px;}</style>
</head>
<body>
<div id="app">
    <form action="/complete" method="post" enctype="multipart/form-data">
    <h2 class="subtitle is-5">1.製品番号</h2>
    <span>1111001</span>
    <input type="hidden" name="serial_num" value="1111001"/>
    <br><br>
    <h2 class="subtitle is-5">2.製造工程を選択してください<h2>
    <select name="process" id="pet-select" class="select">
        <option value="">--Please choose an option--</option>
        <option value="1">企画デザイン</option>
        <option value="2">皮の検品</option>
        <option value="3">型抜き</option>
        <option value="4">パーツ作り</option>
        <option value="5">糊付け</option>
        <option value="6">表断面加工</option>
        <option value="7">縫製</option>
        <option value="8">仕上げ</option>
    </select>
    <br><br><br>
    <h2 class="subtitle is-5">3.背面カメラ</h2>
    <input type="file" name="file" onchange="previewFile(this);" capture="environment" accept="image/*"></label><br><br>
    <img id="preview"><br><br>
    <h2 class="subtitle is-5">4.現在地</h2>
    <span v-if="err">
        <!-- 緯度経度の情報を取得できませんでした。 -->
    </span>
    <lat></lat><br>
    <long></long><br>
    <input type="hidden" name="lat" v-model="lat"/>
    <input type="hidden" name="long" v-model="long"/>
    <br>
    <button class="button" type="submit">登録する</button>
    </form>
</div>

<script>
let lat = "35.6895014";
let long = "139.6917337";
function success(pos) {
  const crd = pos.coords;

  lat = crd.latitude;
  long = crd.longitude;
 
  let url = "/index.html?lat=" +lat + "&long=" + long;
  var app = new Vue({
    el: '#app',
    data: {
        lat: lat,
        long: long,
        err: null
    }
  })
}

function error(err) {
    Vue.component('lat',{
        template : '<span>緯度:' + lat + '</span>'
    })
    Vue.component('long',{
        template : '<span>経度:' + long + '</span>'
    })
    console.warn(`ERROR(${err.code}): ${err.message}`);
    let url = "/index.html?lat=" +lat + "&long=" + long;
    var app = new Vue({
    el: '#app',
    data: {
        lat: lat,
        long: long,
        err: err
    }
  })
  
}
navigator.geolocation.getCurrentPosition(success, error);
</script>
<script>
function previewFile(file) {
    if (file.files[0].size < 3000000) {
    var fileData = new FileReader();
    fileData.onload = (function() {
        document.getElementById('preview').setAttribute("style","width:150px;height:150px");
        document.getElementById('preview').src = fileData.result;
        
    });
    fileData.readAsDataURL(file.files[0]);
    }
}
</script>
</body>

### サーバサイド
追跡情報をチェーン上に入れるは、トランザクションとして署名して、walletから誰かのwallet宛に送らなければならない。
=> transaction detaの中に、商品識別子(serial number等)は必ず含める必要がある。
=> 商品識別子、製造プロセス、緯度、経度などの情報を全てトランザクションデータの中に入れる場合は、データ容量がある程度大きくなくてはいけないが、 商品識別子だけ入れて、後の項目はアプリケーション側(smart contract)のDBに保存すれば、データ容量は少なくて済む。その場合、アプリケーション側の開発が必要。
=> トレーサビリティに特化したブロックチェーンを作ることもできなくはないが、開発効率を考えるとナンセンス。
=> transaction dataにmessageの項目を作るのが一番良さそうには見える。

【JavaScript】ブラウザで緯度経度の情報を取得する

apiを使わずに、navigator.geolocation.getCurrentPositionだけでJSで取得でくる。
ただし、chromeではSSL環境では使用できない。そのため、テストをする際もローカルではデフォルトではテストできないので、SSL環境を構築する必要がある。※動作確認するために、ドメインを取得してLet’s Encryptの環境を作りました。

<head>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.7.16/dist/vue.js"></script>
</head>

<div id="app">
    <h1>現在地</h1>
    <span v-if="err">
        緯度経度の情報を取得できませんでした。
    </span>
    <ul>
        <li>緯度:{{lat}}</li>
        <li>経度:{{long}}</li>
    </ul>
    <a v-bind:href="`${url}`">登録する</a>
</div>

<script>
let lat = "";
let long = "";
function success(pos) {
  const crd = pos.coords;

  lat = crd.latitude;
  long = crd.longitude;
 
  let url = "/index.html?lat=" +lat + "&long=" + long;
  var app = new Vue({
    el: '#app',
    data: {
        lat: lat,
        long: long,
        url: url,
        err: null
    }
  })
}

function error(err) {
    console.warn(`ERROR(${err.code}): ${err.message}`);
    let url = "/index.html?lat=" +lat + "&long=" + long;
    var app = new Vue({
    el: '#app',
    data: {
        lat: lat,
        long: long,
        url: url,
        err: err
    }
  })
}
navigator.geolocation.getCurrentPosition(success, error);
</script>