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>

Next.js 入門

$ npm install -g pnpm
$ pnpm -v
10.6.5
$ npx create-next-app@latest nextjs-blog –use-pnpm
$ cd nextjs-blog
$ pnpm run dev

app/page.tsx

import Image from "next/image";

export default function Home() {
  return (
    <main className="flex min-h-screen flex-col items-center justify-between p-24">
      <h1 className="text-4xl font-bold">Welcome!</h1>
      <p className="text-xl">This is where our journey begins!</p>
    </main>
  );
}
[/code

$ mkdir -p app/about
$ touch app/about/page.tsx

export default function About() {
    return (
      <div className="p-24">
        <h1 className="text-2xl font-bold mb-4">About My Blog</h1>
        <p>This blog app is designed to share insights and knowledge.</p>
      </div>
    );
  }

http://192.168.33.10:3000/about

appディレクトリで各ファイルが対応するURLに自動的にマッピングされる
publicは静的ファイル
next.config.jsはNext.jsの設定をカスタマイズ

export const posts = [
    {id: '1', title: '最初の投稿', content: 'これは最初の投稿です', author: 'Alice', createdAt: new Date('2025-01-01')},
    {id: '2', title: '2番目の投稿', content: 'これは2番目の投稿です', author: 'Bob', createdAt: new Date('2025-01-02')},
]
import Link from 'next/link';
import {posts} from '@/app/lib/placeholder-data';

export default function BlogList() {
    return (
        <div className="container mx-auto px-4 py-8">
            <h1 className="text-3xl font-bold mb-4">ブログ投稿一覧</h1>
            <ul className="space-y-4">
                {posts.map((post) => (
                    <li key={post.id} className="border p-4 rounded-lg">
                        <Link href={`/blog/${post.id}`} className="text-xl font-semibold text-blue-600 hover:underline">
                            {post.title}
                        </Link>
                        <p className="text-gray-600">{post.author} - {post.createdAt.toLocaleDateString()}</p>    
                    </li>
                ))}
            </ul>

        </div>
    );
}

なるほど、世界観は少しだけ理解しました^^

DEXとは何か? Uniswap/Raydiumを触ってみる

DEXはDecentralized Exchangesの略称で、仲介者となる企業が存在しない、ユーザ同士が直接仮想通貨を取引できるブロックチェーン上に構築される取引所

### 代表的なDEX
– Uniswap (ECR-20規格であれば全て取引できる)
– PancakeSwap
– dYdX (仮想通貨の先物取引)

ECR20は、イーサリアムのブロックチェーン上で動作するトークンの統一ルール、規格の一つ

### 日本の仮想通貨法
仮想通貨法には第一号仮想通貨、第二号仮想通貨の分類があり、前者は物品の購入などに際して不特定の者に使用できかつ不特定の者を相手として交換でき、後者は不特定の者を相手として第一号仮想通貨と交換できる
ECR20トークンはイーサリアムと交換可能なことから、第二号仮想通貨に該当する

### uniswap
metamaskなどのwalletがあれば簡単にswapができる
Uniswapでswapを実行すると、metamask上のwalletでも、swapした通貨を保有していることが確認できる。
https://app.uniswap.org/

イーサリアム: UniSwap
Binance Smart Chain: PancakeSwap
Solana : Raydium, Jupiter

Raydimでswapする場合は、PhantomなどSolのwalletを作成して、あとは接続するだけです。

RaydimもUIはUniSwapとかなり似ています。

なるほど、DEXはかなり凄いですね…

【Rust】ステートマシンによるメール判定【Algorithm】

fn isalphanum(c: char) -> bool {
    return c.is_alphabetic() || c.is_numeric();
}

fn isdot(c: char) -> bool {
    return c == '.';
}

fn isatmark(c: char) -> bool {
    return c == '@';
}

fn isend(c: char) -> bool {
    return c == '$';
}

fn isemail(mut s: String) -> bool {
    s += "$";

    let INIT = 0;
    let LOCAL_NOTDOT = 1;
    let LOCAL_DOT = 2;
    let ATMART = 3;
    let DOMAIN_NOTDOT = 4;
    let DOMAIN_DOT = 5;
    let OK = 6;
    let NG = 7;

    let mut state = INIT;

    for c in s.chars() {
        if state == INIT {
            if isalphanum(c) {
                state = LOCAL_NOTDOT;
            } else {
                state = NG;
                break;
            }
        } else if state == LOCAL_NOTDOT {
            if isalphanum(c) {
                state = LOCAL_NOTDOT;
            } else if isdot(c) {
                state = LOCAL_DOT;
            } else if isatmark(c) {
                state = ATMART;
            } else {
                state = NG;
                break;
            }
        } else if state == LOCAL_DOT {
            if isalphanum(c) {
                state = LOCAL_NOTDOT;
            } else {
                state = NG;
                break;
            }
        } else if state == ATMART {
            if isalphanum(c) {
                state = DOMAIN_NOTDOT;
            } else {
                state = NG;
                break;
            }
        } else if state == DOMAIN_NOTDOT {
            if isalphanum(c) {
                state = DOMAIN_NOTDOT;
            } else if isdot(c) {
                state = DOMAIN_DOT;
            } else if isend(c) {
                state = OK;
            } else {
                state = NG;
                break;
            }
        } else if state == DOMAIN_DOT {
            if isalphanum(c) {
                state = DOMAIN_NOTDOT;
            } else {
                state = NG;
                break;
            }
        } else if state == OK {
            break;
        } else if state == NG {
            break;
        } else {
            state = NG
        }
    }
    return state == OK;
}

fn main() {
    let email = "t@g.m".to_string();
    if isemail(email.clone()) {
        println!("メールアドレスです。{}", email);
    } else {
        println!("メールアドレスではありません。");
    }
}

Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.25s
Running `target/debug/basic`
メールアドレスです。t@g.m

【Rust】ボイアーとムーアで過半数の取得を計算する【Algorithm】

### 単純に計算していく方法

fn main() {
    
    let mut vote = [
        "山田太郎".to_string(), "鈴木花子".to_string(), "佐藤三郎".to_string(), "鈴木花子".to_string(), 
        "鈴木花子".to_string(), "佐藤三郎".to_string(), "鈴木花子".to_string(), "佐藤三郎".to_string(), 
        "鈴木花子".to_string(), "鈴木花子".to_string(), "山田太郎".to_string(), "鈴木花子".to_string(), 
    ];

    let n = vote.len();

    let half = n / 2;
    let mut winner = "".to_string();

    for i in 0..n {
        if vote[i] == "" {
            continue;
        }

        let mut count = 1;

        for j in i+1..n {
            if vote[j] == vote[i] {
                count += 1;
                vote[j] = "".to_string();
                if count > half {
                    winner = vote[i].clone();
                    break;
                }
            }
        }
        if winner != "".to_string() {
            break;
        }
    }

    if winner != "".to_string() {
        println!("{}が過半数を取りました", winner);
    } else {
        println!("過半数を取った人はいません");
    }
}

Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.24s
Running `target/debug/basic`
鈴木花子が過半数を取りました

### ボイアーとムーアのアルゴリズム
最初に最も多い組み合わせを調べ、それが過半数に達しているか計算する。
計算量が少なくて済む。

fn main() {
    
    let vote = [
        "山田太郎".to_string(), "鈴木花子".to_string(), "佐藤三郎".to_string(), "鈴木花子".to_string(), 
        "鈴木花子".to_string(), "佐藤三郎".to_string(), "鈴木花子".to_string(), "佐藤三郎".to_string(), 
        "鈴木花子".to_string(), "鈴木花子".to_string(), "山田太郎".to_string(), "鈴木花子".to_string(), 
    ];

    let n = vote.len();
    let mut candidate = "".to_string();

    let mut survivor = 0;

    for i in 0..n {
        if survivor == 0 {
            candidate = vote[i].clone();
            survivor = 1;
        } else if candidate == vote[i] {
            survivor += 1;
        } else {
            survivor -= 1;
        }
    }
    let mut count = 0;
    if survivor != 0 {
        for i in 0..n {
            if candidate == vote[i] {
                count += 1;
            }
        }
    }

    if count > (n / 2) {
        println!("{}が過半数を取りました。", candidate);
    } else {
        println!("過半数を取った人はいません。");
    }
}

### ソートによるカウント
ソートして真ん中の値のカウントをする。これも美しい。

fn main() {
    
    let mut vote = vec![
        "山田太郎".to_string(), "鈴木花子".to_string(), "佐藤三郎".to_string(), "鈴木花子".to_string(), 
        "鈴木花子".to_string(), "佐藤三郎".to_string(), "鈴木花子".to_string(), "佐藤三郎".to_string(), 
        "鈴木花子".to_string(), "鈴木花子".to_string(), "山田太郎".to_string(), "鈴木花子".to_string(), 
    ];

    let half = vote.len() / 2;

    vote.sort();

    if vote.clone().into_iter().filter(|n| *n == vote[half]).count() > half {
        println!("{}が過半数を取りました。", vote[half]);
    } else {
        println!("過半数を取った人はいません。");
    }
}

### hashmapで計算する

use std::collections::HashMap;

fn main() {
    
    let vote = [
        "山田太郎".to_string(), "鈴木花子".to_string(), "佐藤三郎".to_string(), "鈴木花子".to_string(), 
        "鈴木花子".to_string(), "佐藤三郎".to_string(), "鈴木花子".to_string(), "佐藤三郎".to_string(), 
        "鈴木花子".to_string(), "鈴木花子".to_string(), "山田太郎".to_string(), "鈴木花子".to_string(), 
    ];

    let mut blackboard: HashMap<String, i32> = HashMap::new();

    for v in vote.clone() {
        if !blackboard.contains_key(&v) {
            blackboard.insert(v.clone(),1);
        } else {
            blackboard.insert(v.clone(), 1 + blackboard[&v]);
        }
        println!("{:?}", blackboard);
    }

    let key_with_max_value =blackboard.iter().max_by_key(|entry | entry.1).unwrap();
    if *key_with_max_value.1 as usize > (vote.len() / 2) {
        println!("過半数を取得しました。{:?}", key_with_max_value);
    } else {
        println!("過半数を取った人はいません。");
    }
}

Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.24s
Running `target/debug/basic`
{“山田太郎”: 1}
{“山田太郎”: 1, “鈴木花子”: 1}
{“山田太郎”: 1, “鈴木花子”: 1, “佐藤三郎”: 1}
{“佐藤三郎”: 1, “山田太郎”: 1, “鈴木花子”: 2}
{“佐藤三郎”: 1, “山田太郎”: 1, “鈴木花子”: 3}
{“佐藤三郎”: 2, “山田太郎”: 1, “鈴木花子”: 3}
{“佐藤三郎”: 2, “山田太郎”: 1, “鈴木花子”: 4}
{“佐藤三郎”: 3, “山田太郎”: 1, “鈴木花子”: 4}
{“佐藤三郎”: 3, “山田太郎”: 1, “鈴木花子”: 5}
{“佐藤三郎”: 3, “山田太郎”: 1, “鈴木花子”: 6}
{“佐藤三郎”: 3, “山田太郎”: 2, “鈴木花子”: 6}
{“佐藤三郎”: 3, “山田太郎”: 2, “鈴木花子”: 7}
過半数を取得しました。(“鈴木花子”, 7)

これが感覚的には一番わかりやすい。でもアルゴリズム的にはボイヤーの方が優れているんだよなー

【Internet Computer】Rust ではじめてのキャニスター開発をやってみる

こちらの記事を参考にRustでDapps開発を行います。
https://smacon.dev/posts/hello-icp-rust/

### dfxのインストール
sh -ci “$(curl -fsSL https://sdk.dfinity.org/install.sh)”

dfxのインストールだが、mac m1チップのARMアーキテクチャではできない。
https://forum.dfinity.org/t/ubuntu-vm-on-macbook-m1-dfx-install-unknown-cpu-type-aarch64/21486

$ uname -m
aarch64

おいおいおい、いきなりつまづいてしまった。これのトラブルシューティングに丸一日かかりました。
どうしてもやりたかったので、結局、別の環境で作ることに。
# uname -m
x86_64

# dfxのプロジェクトファイル実行
# dfx new –type=rust rust_hello
# cd rust_hello
# dfx start –background
# rustup target add wasm32-unknown-unknown
# dfx deploy
Installed code for canister rust_hello_backend, with canister ID bkyz2-fmaaa-aaaaa-qaaaq-cai

# dfx canister call bkyz2-fmaaa-aaaaa-qaaaq-cai greet ‘(“everyone”: text)’
(“Hello, everyone!”)

# dfx canister status bkyz2-fmaaa-aaaaa-qaaaq-cai
Canister status call result for bkyz2-fmaaa-aaaaa-qaaaq-cai.
Status: Running
Controllers: bnz7o-iuaaa-aaaaa-qaaaa-cai wbqzn-2jy52-icft2-erprw-b52eq-e4ixe-4yxqb-5neaf-miz3k-arx3m-gae
Memory allocation: 0 Bytes
Compute allocation: 0 %
Freezing threshold: 2_592_000 Seconds
Idle cycles burned per day: 1_257_808 Cycles
Memory Size: 1_600_075 Bytes
Balance: 3_061_352_105_201 Cycles
Reserved: 0 Cycles
Reserved cycles limit: 5_000_000_000_000 Cycles
Wasm memory limit: 3_221_225_472 Bytes
Wasm memory threshold: 0 Bytes
Module hash: 0xb60872271f3ca7c4ee2b18a54434c344e3095982685ce10f2c7217c08dfb1cf7
Number of queries: 0
Instructions spent in queries: 0
Total query request payload size: 0 Bytes
Total query response payload size: 0 Bytes
Log visibility: controllers

$ dfx stop

どうやら、「デプロイして、キャニスターを実行」という流れになるようだ。
私の理解では、このキャニスターがスマートコントラクト。
そして、FrontはデフォルトでReactが用意されている。

なるほど、