ツール・シミュレータ

ツール・シミュレータ

ツール・シミュレータ等のプログラミングをネタとしてブログ。

最近sveltekit,react+nextを勉強してシミュレータを作りたかったが、諸事情によりあきらめ。。

仕方なくSveltekit(static-adapter)+fastapi(embeddable)で作ろうと思う。

python embeddableを使うのはポータブルにするため。

※embeddableはpipの設定済みの前提です。

 

 

  ディレクトリ構成

sveltekit構築前の構成は以下にしました。

 

sppj

┗py   ※この下にpython.exeがあるイメージ

 

  fastapiインストール

以下のコマンドで実行。

 

sppj\py> .\python.exe -m pip install fastapi
Collecting fastapi
  Downloading fastapi-0.111.0-py3-none-any.whl.metadata (25 kB)
   ・・・以下省略・・・

 

  sveltekitインストール

以下コマンドで実行。

 

 

sppj> npm create svelte@latest svlk
create-svelte version 6.3.3
T  Welcome to SvelteKit!
|
o  Which Svelte app template?
|  Skeleton project
|
o  Add type checking with TypeScript?
|  Yes, using TypeScript syntax
|
o  Select additional options (use arrow keys/space bar)
|  none
|
—  Your project is ready!
Install more integrations with:
  npx svelte-add
Next steps:
  1: cd svlk
  2: npm install
  3: git init && git add -A && git commit -m "Initial commit" (optional)
  4: npm run dev -- --open
To close the dev server, hit Ctrl-C
Stuck? Visit us at https://svelte.dev/chat
sppj> cd .\svlk\
sppj\svlk> npm install
  ・・・以下省略・・・

 

  static-adapterの設定

以下でインストールします。

 

sppj\svlk> npm install @sveltejs/adapter-static
added 1 package, and audited 97 packages in 5s
9 packages are looking for funding
  run `npm fund` for details
found 0 vulnerabilities

 

svelte.config.jsを以下に修正。

import adapter from '@sveltejs/adapter-static';

 

/** @type {import('@sveltejs/kit').Config} */

const config = {

    kit: {

        adapter: adapter({

            pages: '../py/server_app/static',

            assets: '../py/server_app/static',

            fallback: 'index.html',

            precompress: false,

            strict: true

        })

    }

};

 

export default config;

 

  fastapi側のソース

svelte.config.jsの設定で"../py/server_app/static"に出力するようにしたので、ディレクトリ作りましょう。

その後、sppj/py/server_app/main.pyを作る。

 

 

from fastapi import FastAPI

from fastapi.staticfiles import StaticFiles

 

app = FastAPI()

 

app.mount("/",StaticFiles(directory="static",html=True),name="static")

 

 

 

 

  svelte build

 

 

sppj\svlk> npm run build

> svlk@0.0.1 build
> vite build

vite v5.3.3 building SSR bundle for production...
✓ 79 modules transformed.
vite v5.3.3 building for production...
✓ 61 modules transformed.
.svelte-kit/output/client/_app/version.json                             0.03 kB │ gzip:  0.05 kB
.svelte-kit/output/client/.vite/manifest.json                           2.25 kB │ gzip:  0.44 kB
.svelte-kit/output/client/_app/immutable/entry/start.DrsbX861.js        0.07 kB │ gzip:  0.08 kB
.svelte-kit/output/client/_app/immutable/nodes/0.V07vbrzA.js            0.60 kB │ gzip:  0.39 kB
.svelte-kit/output/client/_app/immutable/nodes/2.DYBzjjm4.js            0.69 kB │ gzip:  0.45 kB
.svelte-kit/output/client/_app/immutable/nodes/1.Ds1SO08V.js            1.02 kB │ gzip:  0.59 kB
.svelte-kit/output/client/_app/immutable/chunks/scheduler.BvLojk_z.js   2.16 kB │ gzip:  1.02 kB
.svelte-kit/output/client/_app/immutable/chunks/index.DFTQtrJW.js       5.43 kB │ gzip:  2.30 kB
.svelte-kit/output/client/_app/immutable/entry/app.n2g8uvqo.js          6.02 kB │ gzip:  2.45 kB
.svelte-kit/output/client/_app/immutable/chunks/entry.gTD_-arJ.js      27.42 kB │ gzip: 10.83 kB
✓ built in 1.26s
.svelte-kit/output/server/.vite/manifest.json                  1.64 kB
.svelte-kit/output/server/entries/fallbacks/layout.svelte.js   0.24 kB
.svelte-kit/output/server/internal.js                          0.31 kB
.svelte-kit/output/server/entries/pages/_page.svelte.js        0.37 kB
.svelte-kit/output/server/entries/fallbacks/error.svelte.js    1.18 kB
.svelte-kit/output/server/chunks/ssr.js                        3.34 kB
.svelte-kit/output/server/chunks/exports.js                    5.94 kB
.svelte-kit/output/server/chunks/internal.js                   6.00 kB
.svelte-kit/output/server/index.js                            93.12 kB
✓ built in 3.46s

Run npm run preview to preview your production build locally.

> Using @sveltejs/adapter-static
  Wrote site to "../py/server_app/static"
  ✔ done

 

 

 

過去にソースコード類似調査等でよく使いました。

textをグレップしてExcelにできます。

 

 

greptoexcel.py 

 

import sys

import os.path

import re

 

import openpyxl

from apl.io.ioutil import *

from apl.io.simple_config import *

from typing import Dict,List

from apl.excollections.elist import Enumerable as Elist

from openpyxl.utils import get_column_letter

 

CFG_KEY_SUMMARY_ONLY="SUMMARY_ONLY"

CFG_KEY_ENCODING="ENCODING"

CFG_KEY_ROOT_DIR="ROOT_DIR"

CFG_KEY_FILE_FILTER="FILE_FILTER"

CFG_KEY_OUTPUT_DIR="OUTPUT_DIR"

CFG_KEY_REG_PREFIX="REG"

 

CFG_VAL_SUM_ONLY = "1"

CFG_VAL_SUM_NOT_ONLY = "0"

 

SAVE_FILE_NAME = "result.xlsx"

SUMMARY_SHEET_NAME = "#SUMMARY#"

 

HIT_VALUE = "〇"

 

MAX_SHEET_NAME_LEN = 26 #シート数1000(4桁),シート名30桁 → 26桁

 

def check_cfg_key(cfg:Dict[str,str]) -> None:

    keys = [CFG_KEY_SUMMARY_ONLY,CFG_KEY_ENCODING,CFG_KEY_FILE_FILTER,CFG_KEY_OUTPUT_DIR,CFG_KEY_ROOT_DIR]

    for key in keys:

        if key not in cfg:

            raise Exception("keyが存在しません:" + key)

        if len(cfg[key]) == 0:

            raise Exception("値が設定されてません:" + key)

    dir_exists_with_exp(cfg[CFG_KEY_OUTPUT_DIR])

    dir_exists_with_exp(cfg[CFG_KEY_ROOT_DIR])

    if cfg[CFG_KEY_SUMMARY_ONLY] not in [CFG_VAL_SUM_NOT_ONLY,CFG_VAL_SUM_ONLY]:

        raise Exception("値が不正:" + CFG_KEY_SUMMARY_ONLY)

 

def check_reg_key(cfg:Dict[str,str]) -> List[str]:

    uselist = []

    for i in range(1,6):

        key = CFG_KEY_REG_PREFIX + str(i).zfill(2)

        if (key in cfg) and (len(cfg[key]) != 0):

            uselist.append(key)

    if len(uselist) == 0:

        raise Exception("有効な定義なし:" + CFG_KEY_REG_PREFIX)

    return uselist

 

def create_sheet_name(wb:openpyxl.Workbook,fullpath:str) -> str:

    sheetname = os.path.basename(fullpath)[0:MAX_SHEET_NAME_LEN]

    if sheetname not in wb.sheetnames:

        return sheetname

    for i in range(1,1000):

        if sheetname + str(i) not in wb.sheetnames:

            return sheetname + str(i)

    return "dummey"

 

def get_hit_value(f:bool) -> str:

    if f:

        return HIT_VALUE

    else:

        return ""

 

def get_hyperlink(f:bool,file:str,sn:str,r:int,c:int) -> str:

    if f:

        #print("=HYPERLINK(\"#" + sn + "!" + get_column_letter(c) + str(r) + "\",\"" + HIT_VALUE + "\")")

        #return "=HYPERLINK(#" + sn + "!" + get_column_letter(c) + str(r) + ",\"" + HIT_VALUE + "\")"

        #return f"{file}#{sn}!" + get_column_letter(c) + str(r)

        return f"#{sn}!" + get_column_letter(c) + str(r)

    else:

        return ""

 

def create_excel(cfg:Dict[str,str],fs:List[str],keylist:List[str]):

    wb = openpyxl.Workbook()

    wb.worksheets[0].title = SUMMARY_SHEET_NAME

    sws = wb.worksheets[0]

 

    # Write Summary Header

    crnt_sum_row = 1

    sws.cell(row=crnt_sum_row,column=1).value = "file"

    sws.cell(row=crnt_sum_row,column=2).value = "sheet"

    for i in range(0,len(keylist)):

        sws.cell(row=crnt_sum_row,column=3 + i).value = cfg[keylist[i]]

    crnt_sum_row += 1

 

    # File Write

    for fc,f in enumerate(fs):

        print(str(fc + 1) + "/" + str(len(fs)) + ":" + f)

        line_count = 1

        with open(f,mode="r",encoding=cfg[CFG_KEY_ENCODING]) as fp:

            iws = None if cfg[CFG_KEY_SUMMARY_ONLY] == CFG_VAL_SUM_ONLY else wb.create_sheet(title=create_sheet_name(wb,f))

            # iws header

            if iws != None:

                for c,key in enumerate(keylist):

                    iws.cell(row=line_count,column=c + 1).value = cfg[key]

                iws.cell(row=line_count,column=len(keylist) + 1).value = "line"

                line_count += 1

           

            # file line

            for line in fp:

                line = line.rstrip('\r\n')

                results = []

                for key in keylist:

                    if re.search(cfg[key],line):

                        results.append(True)

                    else:

                        results.append(False)

               

                # summary write

                if Elist(results).any(lambda x:x):

                    sws.cell(row=crnt_sum_row,column=1).value = f

                    if iws != None:

                        sws.cell(row=crnt_sum_row,column=2).hyperlink = get_hyperlink(True,SAVE_FILE_NAME,iws.title,1,1)

                        sws.cell(row=crnt_sum_row,column=2).value = iws.title

                    for i,r in enumerate(results):

                        if r:

                            if iws != None:

                                sws.cell(row=crnt_sum_row,column=3 + i).hyperlink = get_hyperlink(r,SAVE_FILE_NAME,iws.title,line_count,1 + i)

                            sws.cell(row=crnt_sum_row,column=3 + i).value = get_hit_value(r)

                        sws.cell(row=crnt_sum_row,column=3 + len(results)).value = line

                    crnt_sum_row += 1

 

                # iws write

                if iws != None:

                    for i,r in enumerate(results):

                        iws.cell(row=line_count,column=i + 1).value = get_hit_value(r)

                    iws.cell(row=line_count,column=len(results) + 1).value = line

                line_count += 1

 

    # Save

    wb.save(os.path.join(cfg[CFG_KEY_OUTPUT_DIR],SAVE_FILE_NAME))


 

if __name__ == '__main__':

    if len(sys.argv) != 2:

        raise Exception("引数不正:python greptoexcel.py config_file")

    file_exists_with_exp(sys.argv[1])

 

    cfg = create_config(sys.argv[1],"utf-8")

    check_cfg_key(cfg)

    keylist = check_reg_key(cfg)

 

    fs = find_file_recur(cfg[CFG_KEY_ROOT_DIR],PathFilter.regex(cfg[CFG_KEY_FILE_FILTER]))

    create_excel(cfg,fs,keylist)

 

 

 

 

grep.cfg 

 

SUMMARY_ONLY=0

ENCODING=utf-8

ROOT_DIR=

FILE_FILTER=^.*\.vue$

OUTPUT_DIR=C:\mysoft\py\apl\tools\output

REG01=in

REG02=out

REG03=if

REG04=

 

 

 

過去にたくさんDiffをとる機会があり、Winmergeのcuiを使うものだけラッピングしたライブラリを作った。

 

 

winapp.winmerge.py 

 

from ..io.ioutil import file_exists_with_exp,dir_exists_with_exp

import subprocess

from pathlib import Path

 

class Winmerge:

    def __init__(self,exe_path) -> None:

        file_exists_with_exp(exe_path)

        self.exe_path = exe_path

   

    def do_report(self,left_path:str,right_path,report_path:str) -> int:

        file_exists_with_exp(left_path)

        file_exists_with_exp(right_path)

        dir_exists_with_exp(Path(report_path).parent)

        p = subprocess.Popen([f"{self.exe_path}","-noninteractive",f"{left_path}",f"{right_path}","/or",f"{report_path}"])

        return p.wait()