stylesheet

2024-10-06

パスキー用のQRコード読み取りは標準カメラアプリで

QRコードリーダーによっては、パスキー用のQRコードに対応していない場合があります。
FIDOスキームURL(FIDO:/…)の扱いがアプリによって異なるため、別のQRコードリーダーを試すと解決することがあります。

例えば、Microsoft Launcherではブラウザが起動してしまい失敗しましたが、カメラアプリを使用すると成功しました。Galaxyの場合、カメラアプリの設定で「QRコードをスキャン」をONにしておくと、QRコードを正しく処理できます。

「パスキーうざい。メルカリ、PCからログインできない」となった記念にメモ。

2024-10-03

Google Cloud Storage が「消しても消えない」機能を追加して課金を始める

Googleさん、Cloud Storageで削除ポリシーを勝手に追加したうえ課金を始めるって酷すぎるでしょ。
ファイル削除しても気付かれないように課金を続けるとか鬼畜。そこはオプトインにしてよ。もしかして「勝手に消す」機能がバグって怒られたの?

削除ポリシーの削除コマンド。バケット毎に必要。AppEngine関連の全てのバケットに設定されていた。

gcloud storage buckets update gs://my-bucket --clear-soft-delete

参照

2024-08-17

Flaskアプリケーションをサブディレクトリで公開

Flaskで作成したアプリケーションをサブディレクトリにマウントして公開する方法。 Flaskの機能ではないのでドキュメントを見ていてもなかなか見つけにくいが、WSGIミドルウェアを使用するものが正解。

毎回調べている気がするのでメモしておく。

設定内容

以下のように、http://localhost/consoleで公開する。

# nginx config

upstream console {
  server 127.0.0.1:8110;
}

server {
  ...

  location /console {
    proxy_pass http://console;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  }

  ...
}

このままではurl_for等のurl生成が壊れるので、 APPLICATION_ROOTを設定してやり、WSGIのミドルウェアを挟んでやるとうまく動作する。 環境変数を参照するようにすると、開発環境等にもコード修正なしで対応できる。

# app.py

import os
from flask import Flask, request, url_for, render_template
from werkzeug.serving import run_simple
from werkzeug.middleware.dispatcher import DispatcherMiddleware

application = Flask(__name__)

application_root = os.environ.get('FLASK_APPLICATION_ROOT', '')
application.config['APPLICATION_ROOT'] = application_root

application.wsgi_app = DispatcherMiddleware(run_simple, {application_root: application.wsgi_app})


@application.route('/')
def index():
    urls = (
        url_for('index', _external=True),
        url_for('index'),
        url_for('static', filename='css/style.css'))
    return '<br>'.join(urls)


if __name__ == "__main__":
    application.run(host=0.0.0.0)

今回は、WSGIコンテナにNGINX Unitを使用してみた。 インストール方法は割愛。

# config.json

{
    "listeners": {
        "*:8110": {
            "pass": "applications/console"
        }
    },
    "applications": {
        "console": {
            "type": "python",
            "user": "yamori",
            "group": "yamori",
            "working_directory": "/home/yamori/workspace/console/",
            "path": "/home/yamori/workspace/console/",
            "home": "/home/yamori/.anyenv/envs/pyenv/versions/console",
            "module": "app",
            "environment": {
                "FLASK_APPLICATION_ROOT": "/console"
            }
        }
    }
}

ソケットにputして反映させる。

$ sudo curl -X PUT --data-binary @config.json --unix-socket /run/control.unit.sock http://localhost/config/

以上で、webサーバー経由でのアクセスhttp://localhost/console、pythonファイルの直接実行時のhttp://localhost:5000どちらの場合でも正しいパスが生成される。

2024-06-27

WSL2の未使用領域を開放してディスクスペースを節約

WSL2の未使用領域を開放する手順をメモ。事前にいらないファイルを削除しておくと尚良し。

TL;DR

# wsl2-optimize-vhd.ps1
wsl --shutdown
$files = gci -Path $HOME/AppData/Local/Packages/*.vhdx -Recurse
foreach ($file in $files) {
    optimize-vhd -Path $file.FullName -Mode full
}

準備

vhdxファイルのパスを調べる。
$HOME/AppData/Local/Packages以下、ディストリビューションごとのディレクトリ、LocalState内にファイルが存在。

PS> gci -path $HOME/AppData/Local/Packages/*.vhdx -Recurse

作業の前に、WSLを停止。

PS> wsl --shutdown

optimize-vhd コマンドで縮小

optimize-vhdコマンドを利用できる場合は、以下のコマンドでOK。

PS> optimize-vhd -Path <path-to-vhdx> -Mode full

diskpart プログラムで縮小

diskpartプログラムを使う場合は以下。

PS> diskpart

DISKPART> select vdisk file=<path-to-vhdx>
DISKPART> attach vdisk readonly
DISKPART> compact vdisk
DISKPART> detach vdisk
DISKPART> exit

sparseモードを有効にしておく

『WSLの仮想ハードディスク(VHD)は、使用するにつれてサイズが大きくなりますが、この機能(sparseVhd)を有効にすると、自動的にサイズも小さくなります!』とのこと。

PS> wsl --manage <distro> --set-sparse true

2024-06-22

Riot.js v3系からv9系へのマイグレーションメモ

いわゆるレガシーコードの延命で、Riot3で書いたChrome拡張をRiot9へ移行した。公式でもRiot3からのマイグレーションは推奨されていないが、今回は外部要因で仕方なく。(Riot3はManifest V3で動作しない)

基本的なことはマイグレーションガイドに記載されている手順を参照するが、その他のガイドに記載されていない事柄をメモしておく。

each属性でオブジェクトのループができなくなった

以前のRiotで動作していた以下のコードはエラーとなる。

<my-component>
    <ul>
    <li each={ v,k in obj }>{k}: {v}</li>
    </ul>

    <script>
    export default {
        items: { mayer:46, frusciante:54 }
    }
    </script>
</my-component>

配列に変換するしかないようだ。

<my-component>
    <ul>
    <li each={ x in Object.entries(items) }>{x[0]}: {x[1]}</li>
    </ul>
    ...
</my-component>

タグとタグの間の空白文字や改行が完全に除去されるようになった

以前のRiotでは、通常のHTMLと同様に、連続するスペースや改行は1つにまとめて扱われていた。

<div>
    <span>Hello</span>
    <span>World</span>
</div>

上記のマークアップは、通常のHTMLと同様に以下のように表示される。

Hello World

一方、現行バージョンのRiotでは、スペースが付与されず、レイアウト乱れの要因に。

HelloWorld

意図してやっているようなので、&nbsp;を挟むか、スタイルで調整が必要。

Refsの置き換えはコンポーネントインスタンスの取得に使えない

refs属性廃止に伴い、$$$といった関数型ヘルパーが用意されたが、これらはDOM参照を返す。 コンポーネントインスタンスの取得には使えない。

<my-hello-button ref="b1"></my-hello-button>

<script>
export default {
    doSomething() {
        this.$('[ref="b1"]').sayHello()  // エラー
    }
}
</script>

「コンポーネントはpropsを介してのみ通信すべし」とのことらしい。 こういったバッドプラクティス的なコードも許容できるのが利点だったんだけどな…。

以上。

参照

2023-09-13

Grafana公式レポジトリの鍵が更新されたようだ

apt update失敗。サードパーティーレポジトリを追加していると稀に遭遇する公開鍵のエラー。

$ sudo apt update
ヒット:1 http://ftp.iij.ad.jp/pub/linux/ubuntu/archive jammy InRelease
ヒット:2 http://ftp.iij.ad.jp/pub/linux/ubuntu/archive jammy-updates InRelease
ヒット:3 http://ftp.iij.ad.jp/pub/linux/ubuntu/archive jammy-security InRelease
ヒット:4 https://download.docker.com/linux/ubuntu jammy InRelease
取得:5 https://apt.grafana.com stable InRelease [5,984 B]
ヒット:6 http://security.ubuntu.com/ubuntu jammy-security InRelease
エラー:5 https://apt.grafana.com stable InRelease
  公開鍵を利用できないため、以下の署名は検証できませんでした: NO_PUBKEY 963FA27710458545
パッケージリストを読み込んでいます... 完了
依存関係ツリーを作成しています... 完了
状態情報を読み取っています... 完了
アップグレードできるパッケージが 24 個あります。表示するには 'apt list --upgradable' を実行してください。
W: 署名照合中にエラーが発生しました。リポジトリは更新されず、過去のインデックスファイルが使われます。GPG エラー: https://apt.grafana.com stable InRelease: 公開鍵を利用できないため、以下の署名は検証できませんでした: NO_PUBKEY 963FA27710458545
W: https://apt.grafana.com/dists/stable/InRelease の取得に失敗しました  公開鍵を利用できないため、以下の署名は検証できませんでした: NO_PUBKEY 963FA27710458545
W: いくつかのインデックスファイルのダウンロードに失敗しました。これらは無視されるか、古いものが代わりに使われます。

今回はGrafanaのようだ。

こちらのブログによると、「秘密鍵をうっかり共有しちゃったので、鍵を新しくしたよ!」とのこと。
更新方法も書いてあったが、古い鍵の削除がうまくいかなかったので、ちゃんとやる。

apt-key listで鍵の一覧を表示。

$ apt-key list
Warning: apt-key is deprecated. Manage keyring files in trusted.gpg.d instead (see apt-key(8)).
/etc/apt/trusted.gpg
--------------------
pub   rsa2048 2017-01-24 [SC]
      4E40 DDF6 D76E 284A 4A67  80E4 8C8C 34C5 2409 8CB6
uid           [  不明  ] Grafana <info@grafana.com>
sub   rsa2048 2017-01-24 [E]

pub   rsa3072 2023-08-24 [SC] [有効期限: 2025-08-23]
      B53A E77B ADB6 30A6 8304  6005 963F A277 1045 8545
uid           [  不明  ] Grafana Labs <engineering@grafana.com>
sub   rsa3072 2023-08-24 [E] [有効期限: 2025-08-23]

/etc/apt/trusted.gpg.d/apt-fast-ubuntu-stable.gpg
-------------------------------------------------
pub   rsa1024 2012-06-11 [SC]
      A216 6B8D E8BD C336 7D19  01C1 1EE2 FF37 CA8D A16B
uid           [  不明  ] Launchpad PPA for apt-fast

/etc/apt/trusted.gpg.d/ubuntu-keyring-2012-cdimage.gpg
------------------------------------------------------
pub   rsa4096 2012-05-11 [SC]
      8439 38DF 228D 22F7 B374  2BC0 D94A A3F0 EFE2 1092
uid           [  不明  ] Ubuntu CD Image Automatic Signing Key (2012) <cdimage@ubuntu.com>

/etc/apt/trusted.gpg.d/ubuntu-keyring-2018-archive.gpg
------------------------------------------------------
pub   rsa4096 2018-09-17 [SC]
      F6EC B376 2474 EDA9 D21B  7022 8719 20D1 991B C93C
uid           [  不明  ] Ubuntu Archive Automatic Signing Key (2018) <ftpmaster@ubuntu.com>

apt-key delで指定するフィンガープリントは下8桁で良いらしい。
Grafana関連と思わしき鍵を削除。

$ sudo apt-key del 24098CB6
$ sudo apt-key del 10458545

新しい鍵をダウンロード。

$ sudo wget -q -O /usr/share/keyrings/grafana.key https://apt.grafana.com/gpg.key

以上。

2023-06-20

HyperSBI2をSolarized色に染める

HYPER SBI 2: テーマ選択できるのは良いのだけど、ブラックorホワイト、どっちのテーマもものすごく目が疲れるんですけど…。旧バージョン時代のブルーカラー復活を切に願い…。

我慢して使っていたけどもう限界。リソースを編集して無理やり背景色を書き換えることにする。

手順

stylesheet.rccの差し替えを行う。元に戻せるよう、事前にC:\Program Files (x86)\SBI SECURITIES\HYPERSBI2\stylesheet.rccのバックアップを取っておく。

curl -o RccExtended-v1.0.5-win32.zip -L  https://github.com/zedxxx/rccextended/releases/download/v1.0.5/RccExtended-v1.0.5-win32.zip
Expand-Archive RccExtended-v1.0.5-win32.zip
cd .\RccExtended-v1.0.5-win32\
cp 'C:\Program Files (x86)\SBI SECURITIES\HYPERSBI2\stylesheet.rcc' .
.\rcc.exe --reverse
Set-ItemProperty .\qresource\res\stylesheet.rcc\stylesheet\dark\common -Name IsReadOnly -Value $false
sed -i -e 's/#0C0C0D/#073642/g' -e 's/#1a1a1a/#002B36/g' .\qresource\res\stylesheet.rcc\stylesheet\dark\common
cd .\qresource\rcc\
cp ..\..\rcc.exe .
.\rcc-make.bat
sudo cp .\stylesheet.rcc 'C:\Program Files (x86)\SBI SECURITIES\HYPERSBI2\stylesheet.rcc'

以上。

リソースの書き換えには危険が伴います。また、アップデートに失敗したり、編集内容が元に戻ったりといったことが起こります。記載の内容を十分理解したうえ、自己責任で行いましょう。

概要

リソースのアンパック

HYPER SBI 2のインストール先はC:\Program Files (x86)\SBI SECURITIES\HYPERSBI2\
qtアプリケーションのようなので、rccextendedツールを使用してリソースをアンパックする。リソースファイルは拡張子が.rccのもの。 目当てのものはstylesheet.rccに含まれていた。config.rccにもテーマらしきものが含まれていたが、今回は使用しない。

wget https://github.com/zedxxx/rccextended/releases/download/v1.0.5/RccExtended-v1.0.5-win32.zip
Expand-Archive RccExtended-v1.0.5-win32.zip
cd .\RccExtended-v1.0.5-win32\
cp 'C:\Program Files (x86)\SBI SECURITIES\HYPERSBI2\stylesheet.rcc' .
.\rcc.exe --reverse

スタイルの書き換え

手っ取り早くsed.\qresource\res\stylesheet.rcc\stylesheet\dark\commonの色置換を行った。 なぜか読み取り専用属性がついていたので、外してから実行。

Set-ItemProperty .\qresource\res\stylesheet.rcc\stylesheet\dark\common -Name IsReadOnly -Value $false
sed -i -e 's/#0C0C0D/#073642/g' -e 's/#1a1a1a/#002B36/g' .\qresource\res\stylesheet.rcc\stylesheet\dark\common

リソースの再パック

.\qresource\rcc\rcc-make.batを実行してstylesheet.rccを生成。rcc.exeにパスが通っていることが前提のようなので、rcc.exeを同じ場所にコピーして実行した。

cd .\qresource\rcc\
cp ..\..\rcc.exe .
.\rcc-make.bat

stylesheet.rccの差し替え

書き換え済みのstylesheet.rccで上書き。UACの権限昇格が必要。

sudo cp .\stylesheet.rcc 'C:\Program Files (x86)\SBI SECURITIES\HYPERSBI2\stylesheet.rcc'

コマンドが見つからない?

sed,sudoscoopでインストールできるよ。PowerShellを使おう。

参照

2023-05-28

diffusers の使い方を調べてGoogle Colab ノートブックファイルを作った

概要: この記事では、Google Colabのフリー版におけるAUTOMATIC1111版WebUIの制限について取り上げ、その代替手段としてHugging Faceのdiffusersを使用する方法について調査します。

最近、Google Colabのフリー版での利用制限が行われ、AUTOMATIC1111版WebUIの使用が禁止されました。さらに悪いことに、この制限はRemoteUI全体に広がっているようです。
この制限によって、以前は簡単にアクセスできていたWebユーザーインターフェース(WebUI)を使用することができなくなりました。AUTOMATIC1111版WebUIは、使いやすく便利なユーザーインターフェースでしたが、これからは別の方法を探さなければなりませんでした。

代替の手段を模索する必要がありました。
その結果、UIを持たないHugging Faceのdiffusersを利用することに決めました。diffusersは、モデルの推論や処理を行うためのPythonライブラリであり、UIを必要としないため、制限を受けずに利用することができます。これは私たちのニーズに合致していると考え、積極的に取り入れることにしました。

diffusersの使い方について詳しく調査しました。Hugging Faceの公式ドキュメンテーションやコミュニティのフォーラムなど、さまざまな情報源を活用しました。

2023-05-23

オレオレ証明書に SAN (Subject Alternative Name) を追加。

TL;DR: /usr/share/ssl-cert/ssleay.cnf を編集して、make-ssl-cert -f generate-default-snakeoil するべし。

$ sudo cp /usr/share/ssl-cert/ssleay.cnf /usr/share/ssl-cert/ssleay.cnf.org
$ sudo jed /usr/share/ssl-cert/ssleay.cnf

...
[ v3_req ]
basicConstraints        = CA:FALSE
subjectAltName          = @SubjectAltName@, DNS:@HostName@.local, IP:192.168.1.5, IP:192.168.1.6

証明書の作成とwebサーバーの再起動。

$ sudo make-ssl-cert -f generate-default-snakeoil
$ sudo systemctl restart nginx

以上。

以下、結論にたどり着くまでの試行錯誤の記録。

2023-05-12

今はプログラマーにBardは必要ないかも

GoogleのChatGPT対抗AIBardが日本語対応とのことなので、プログラム生成を試してみた。 仕様書は、前回ChatGPTに生成させたドキュメントを使用する。

プログラムの実装

N.Y.

次のようなpythonプログラムを実装してください。

【タイトル】
マルチディスプレイ用の画像分割プログラム

【概要】
本プログラムは、1枚の画像をマルチディスプレイで使用するために複数の画像に分割するためのプログラムです。 分割数やディスプレイの縦横比を指定することができます。

【前提条件】

  • Python 3.6以上がインストールされていること。
  • 必要な外部ライブラリ(pillow)がインストールされていること。

【機能】

  • 画像の分割: 入力画像を指定された分割数に応じて複数の画像に分割します。
  • 出力: 分割された画像は指定された出力ディレクトリに保存されます。
  • ディスプレイの縦横比指定: マルチディスプレイの縦横比を指定することができます。デフォルト値は16:9です。

【入力】

  • input: 入力ファイルのglobパターンを指定します。1つまたは複数のファイルを指定することができます。
  • output: 分割された画像が保存されるディレクトリを指定します。デフォルト値は"output"です。
  • split: 入力画像を分割する数を指定します。デフォルト値は2です。
  • aspect: マルチディスプレイの縦横比を指定します。指定しない場合、デフォルト値は16:9です。

【出力】

  • 分割された画像が指定された出力ディレクトリに保存されます。

【エラー処理】

  • ファイルが存在しない場合、エラーが発生します。
  • ファイルが画像ファイルでない場合、エラーが発生します。
  • ファイルが開けない場合、エラーが発生します。
  • ディレクトリが存在しない場合、自動的に作成されます。

【ログ出力】

  • ログは標準出力に出力されます。ログレベルはINFOです。

【実行例】 次のように実行することができます。

$ python image_spliter.py input/*.jpg -o output -s 3 -a 4:3

【参考】

ChatGPT

マルチディスプレイ用の画像分割プログラムのPythonプログラムは次のとおりです。

import argparse
import os
from PIL import Image


def main():
    parser = argparse.ArgumentParser(description="Image splitter for multi-displays")
    parser.add_argument("input", type=str, help="Input image glob pattern")
    parser.add_argument("-o", "--output", type=str, default="output", help="Output directory")
    parser.add_argument("-s", "--split", type=int, default=2, help="Number of splits")
    parser.add_argument("-a", "--aspect", type=str, default="16:9", help="Display aspect ratio")
    args = parser.parse_args()

    if not os.path.exists(args.output):
        os.makedirs(args.output)

    for image_file in glob.glob(args.input):
        image = Image.open(image_file)
        width, height = image.size
        aspect_ratio = width / height

        if aspect_ratio != float(args.aspect):
            print("Warning: Image aspect ratio ({}) does not match display aspect ratio ({})".format(aspect_ratio, args.aspect))

        split_width = width // args.split
        split_height = height // args.split

        for i in range(args.split):
            for j in range(args.split):
                box = (i * split_width, j * split_height, (i + 1) * split_width, (j + 1) * split_height)
                image.crop(box).save(os.path.join(args.output, "{}-{}.png".format(i, j)))


if __name__ == "__main__":
    main()
コードは慎重に使用してください。詳細

このプログラムは、globを使用して入力画像を取得し、Pillowを使用して画像を分割します。分割された画像は指定された出力ディレクトリに保存されます。

2023-04-12

プログラマーには ChatGPT が必要だ

ChatGPT捗る。
これだけできると、非公開のプログラムコードも喰わせたくなる。
禁止されるのもわからないでもないな。

コードレビューの依頼

N.Y.

コードレビューをお願い

ChatGPT

もちろんです!コードを共有していただければ、レビューさせていただきます。

N.Y.
"""
マルチディスプレイ用の画像分割

python image_spliter.py -h

[requirements]
pillow
"""

import os
import glob
import argparse
import re
import logging
from PIL import Image, ImageFilter

logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO)


def split_image(name:str, output:str, split:int, aspect:str):
    img = Image.open(name)
    ax, ay = map(lambda x: int(x), aspect.split(':'))
    aspect_ratio  = ay / ax

    out_w = int(img.width / split)
    out_h = int(out_w * aspect_ratio)
    if out_h > img.height:
        out_w = int(out_h * aspect_ratio)
    
    x = int(img.width / 2 - out_w * split / 2)
    y = int(img.height / 2 - out_h / 2)
    folder, file_name = os.path.split(name)
    base_name, ext = os.path.splitext(file_name)

    logger.info(f'processing... {name}, {img.width}x{img.height} -> {out_w}x{out_h}')

    for n in range(split):
        p = os.path.join(output, f'{base_name}_{n}{ext}')
        cropped = img.crop((x, y, x + out_w, y + out_h))
        cropped.save(p, quality=95)
        logger.info(f'cropped: {p}')
        x += out_w


def aspect_type(value:str) -> str:
    if not re.match(r'^\d+:\d+$', value):
        raise argparse.ArgumentError('invalid value')
    return value


if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='マルチディスプレイ用の画像分割')
    parser.add_argument('input', help='入力ファイル globパターン')
    parser.add_argument('-o', '--output', default='output', help='出力ディレクトリ')
    parser.add_argument('-s', '--split', type=int, default=2, help='分割数')
    parser.add_argument('-a', '--aspect', default='16:9', help='ディスプレイの縦横比', type=aspect_type)
    args = parser.parse_args()

    os.makedirs(args.output, exist_ok=True)
    for s in glob.glob(args.input):
        split_image(s, args.output, args.split, args.aspect)

    logger.info('done')

ChatGPT

このコードは、指定された入力画像を複数の画像に分割し、それらを出力する Python スクリプトのようです。以下は、いくつかのコメントです。

  • コード全体の構造は良く整理されており、コードが読みやすくなっています。
  • それぞれの機能は、独自の関数に分割されており、単一の機能にフォーカスされています。
  • パラメータの解析には、Python の標準ライブラリである argparse が使用されています。
  • コマンドラインオプションのヘルプは、"-h" オプションで表示されます。
  • エラーの検査には、正規表現が使用されています。

最後に、いくつかの改善の提案です。

2023-04-11

「卵高騰 卵かけご飯に 涙こぼす」 by ちゃっとじぷと

Q:卵が値上がりしていることを、川柳で。
A:卵高騰 卵かけご飯に 涙こぼす
Q:その心は?
A:普段当たり前に食べていた卵が高騰し、手が届かなくなってしまったという悲しみや無念さを表現しています。食糧価格の高騰は、日々の暮らしに大きな影響を与えるため、胸が痛くなる思いがします。
Q:手が届かなくなってしまったなら、白ごはんのほうが良いのでは?
A:確かに、卵かけご飯が手が届かなくなってしまった場合は、白ごはんだけでもお腹を満たすことができますね。身近な食材であっても、ありがたさを感じることが大切です。また、栄養バランスを考えて、他の食材で補うことも大切です。
Q:川柳つくりなおして。
A:卵が手が届かなくなってしまったとき、身近な白ごはんに目を向け、感謝の気持ちを持つことが大切だという心情を表現しています。日常の中で当たり前に食べているものでも、高騰したり手に入らなくなると不便や悲しさを感じますが、そんなときこそ自分がもっているものに感謝し、謙虚な気持ちを持つことが大切だということを表しています。
Q:ありがとう。もう寝ます。
A:おやすみなさい。良い夢を見てくださいね。

ChatGPT、おっさんの話相手に最高やな。老害が捗るわ。
なんか、かわいそう。

(ChatGPT Mar 23 Version)

2022-10-06

ユニクロの月次売上を整形 9月度月次データ

ユニクロの国内月次売上推移が使いにくいので整形してみた。
正直、前年比じゃなく数値も公表して欲しいんだが、なにか問題があるんだろうか。

海外事業の分は公表されていないので、中国事業の月次を公表しているバロックジャパンを参照して雰囲気を感じてみる...。

コメントは控える。
今回作ったファイルはユニクロ月次売上.xlsxでダウンロード。

もう少しIRにも力を入れて欲しいなぁ...。

参照データ

作成ファイル