end0tknr's kipple - web写経開発

太宰府天満宮の狛犬って、妙にカワイイ

mysql v.8.0.16 を centos 7 にsrcから install

一体、何回目でしょうか? また、何度もこういったentryを記載しているのは、 mysqlにおけるversion毎の差異も原因かと思います。

https://dev.mysql.com/doc/refman/8.0/en/source-installation.html

dev.mysql.comに↑こちらのdocumentが公開されていますが、 私にはあまり役に立ちませんでした。

$ wget https://dev.mysql.com/get/Downloads/MySQL-8.0/mysql-8.0.16.tar.gz

# 当初、centos7にyum install cmake しましたが、
# mysql v.8.0.16 には古いようなので、以下の3行のように devtoolset-7 を利用。

$ sudo yum install centos-release-scl
$ sudo yum install devtoolset-7
$ scl enable devtoolset-7 bash

# mysqlの依存packageをinstall

$ sudo yum install openssl-devel
$ sudo yum install ncurses-devel


$ tar -xvf mysql-8.0.16.tar.gz
$ /usr/local/bin/cmake . \
   -DCMAKE_INSTALL_PREFIX=/usr/local/mysql \
   -DDEFAULT_CHARSET=utf8 \
   -DDEFAULT_COLLATION=utf8_general_ci \
   -DENABLED_LOCAL_INFILE=true \
   -DWITH_INNOBASE_STORAGE_ENGINE=1 \
   -DWITH_EXTRA_CHARSETS=all \
   -DWITH_READLINE=ON \
   -DDOWNLOAD_BOOST=ON \
   -DWITH_BOOST=/home/end0tknr/tmp/boost \
   -DFORCE_INSOURCE_BUILD=1 \
   -DWITH_SYSTEMD=ON
$ make
$ make test
$ sudo make install
$ sudo groupadd mysql
$ sudo useradd -r -g mysql mysql

https://end0tknr.hateblo.jp/entry/20160819/1471562683

my.cnf の内容は、以前のentryを参考にしています。

特に通常のinstallでは、rootでのログイン時に

$ /usr/local/mysql/bin/mysql -u root
ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: NO)

と怒られ、ログインできませんでしたので、「skip-grant-tables」を追加しています。

(rootの初期パスワードは、/var/log/mysql.log に表示されるらしいのですが、 私の環境では表示されませんでしたので)

$ sudo vi /etc/my.cnf

# For advice on how to change settings please see
# http://dev.mysql.com/doc/refman/5.6/en/server-configuration-defaults.html

[mysqld]
max_allowed_packet = 32M
innodb_log_file_size = 128MB
# Remove leading # and set to the amount of RAM for the most important data
# cache in MySQL. Start at 70% of total RAM for dedicated server, else 10%.
# innodb_buffer_pool_size = 128M

# Remove leading # to turn on a very important data integrity option: logging
# changes to the binary log between backups.
# log_bin

# These are commonly set, remove the # and set as required.
# basedir = .....
# datadir = .....
# port = .....
# server_id = .....
# socket = .....
basedir = /usr/local/mysql
datadir = /var/mysql_data

skip-grant-tables

default_password_lifetime=0
validate_password.policy=LOW

# Remove leading # to set options mainly useful for reporting servers.
# The server defaults are faster for transactions and fast SELECTs.
# Adjust sizes as needed, experiment to find the optimal values.
# join_buffer_size = 128M
# sort_buffer_size = 2M
# read_rnd_buffer_size = 2M 

sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES 
$ sudo mkdir /var/mysql_data
$ sudo chown mysql:mysql /var/mysql_data
$ sudo chmod 750 /var/mysql_data

$ sudo /usr/local/mysql/bin/mysqld --initialize --user=mysql

$ sudo /usr/local/mysql/bin/mysql_ssl_rsa_setup
$ sudo  /usr/local/mysql/bin/mysqld  --initialize-insecure
$ sudo  /usr/local/mysql/bin/mysql_secure_installation

自動起動

$ sudo cp /home/end0tknr/tmp/mysql-8.0.16/scripts/mysqld.service \
    /etc/systemd/system/
$ sudo systemctl enable mysqld.service
$ sudo systemctl start mysqld.service

再・MSXML2.XMLHTTPでvbaからweb apiを利用 (POST版)

https://end0tknr.hateblo.jp/entry/20081115/1226755041

↑こちらの古いエントリーの続き、今回はPOST版

Function CallApiCalc(Model)
    Set http = CreateObject("MSXML2.XMLHTTP")
    http.Open "POST", "https://house.app.lowenergy.jp/api/v1/eval", False
    http.setRequestHeader "Accept", "application/xml"
    http.setRequestHeader "Content-Type", "text/xml"
    http.send CVar("<request><model>" & Model & "</model><format>NewStandard</format></request>")
    
    MsgBox http.Status
    MsgBox http.StatusText
    
    
    With Sheet16
        .Range("G13") = http.ResponseXML.SelectSingleNode("/response/E_H").Text
        .Range("G14") = http.ResponseXML.SelectSingleNode("/response/E_C").Text
        .Range("G15") = http.ResponseXML.SelectSingleNode("/response/E_V").Text
        .Range("G16") = http.ResponseXML.SelectSingleNode("/response/E_W").Text
        .Range("G17") = http.ResponseXML.SelectSingleNode("/response/E_L").Text
        .Range("G18") = http.ResponseXML.SelectSingleNode("/response/E_M").Text
        .Range("G19") = http.ResponseXML.SelectSingleNode("/response/E_S").Text
        .Range("G20") = http.ResponseXML.SelectSingleNode("/response/E_T").Text
        .Range("G26") = http.ResponseXML.SelectSingleNode("/response/E_PV_gen").Text
        .Range("H13") = http.ResponseXML.SelectSingleNode("/response/E_SH").Text
        .Range("H14") = http.ResponseXML.SelectSingleNode("/response/E_SC").Text
        .Range("H15") = http.ResponseXML.SelectSingleNode("/response/E_SV").Text
        .Range("H16") = http.ResponseXML.SelectSingleNode("/response/E_SW").Text
        .Range("H17") = http.ResponseXML.SelectSingleNode("/response/E_SL").Text
        .Range("H18") = http.ResponseXML.SelectSingleNode("/response/E_SM").Text
        .Range("H20") = http.ResponseXML.SelectSingleNode("/response/E_ST").Text
    End With
    
    Set http = Nothing
    
End Function

yum の実行で、/usr/lib64/python2.7/site-packages/pycurl.so: undefined symbol: CRYPTO_num_locks

先日、curl-7.64.1.tar.bz2 をsrcから installしましたが、どうやら、yumの環境を壊したみたい。

$ sudo  yum -y install pandoc
[sudo] password for end0tknr: 
There was a problem importing one of the Python modules
required to run yum. The error leading to this problem was:

   /usr/lib64/python2.7/site-packages/pycurl.so: undefined symbol: CRYPTO_num_locks

Please install a package which provides this module, or
verify that the module is installed correctly.

It's possible that the above module doesn't match the
current version of Python, which is:
2.7.5 (default, Oct 30 2018, 23:45:53) 
[GCC 4.8.5 20150623 (Red Hat 4.8.5-36)]

If you cannot solve this problem yourself, please go to 
the yum faq at:
  http://yum.baseurl.org/wiki/Faq

以下の通り、lddコマンドで見ると、libcurl.so.4 のみ /usr/local/lib を参照しており、いかにも怪しい。

$ ldd /usr/lib64/python2.7/site-packages/pycurl.so
    linux-vdso.so.1 =>  (0x00007ffd5a3e6000)
    libcurl.so.4 => /usr/local/lib/libcurl.so.4 (0x00007fa8cec45000)
    libpython2.7.so.1.0 => /lib64/libpython2.7.so.1.0 (0x00007fa8ce879000)
    libpthread.so.0 => /lib64/libpthread.so.0 (0x00007fa8ce65d000)
    libc.so.6 => /lib64/libc.so.6 (0x00007fa8ce290000)
    libssl.so.1.1 => /usr/local/openssl_1_1_0/lib/libssl.so.1.1 (0x00007fa8ce021000)
    libcrypto.so.1.1 => /usr/local/openssl_1_1_0/lib/libcrypto.so.1.1 (0x00007fa8cdb97000)
    libz.so.1 => /lib64/libz.so.1 (0x00007fa8cd981000)
    libdl.so.2 => /lib64/libdl.so.2 (0x00007fa8cd77d000)
    libutil.so.1 => /lib64/libutil.so.1 (0x00007fa8cd57a000)
    libm.so.6 => /lib64/libm.so.6 (0x00007fa8cd278000)
    /lib64/ld-linux-x86-64.so.2 (0x00007fa8cf0ca000)

ldconfig -p でも重複していることが分かりましたので、 一方の libcurl.so.4 や libcurl.so からの ln -s としました。

$ ldconfig -p|grep curl
    libcurl.so.4 (libc6,x86-64) => /usr/local/lib/libcurl.so.4
    libcurl.so.4 (libc6,x86-64) => /lib64/libcurl.so.4
    libcurl.so (libc6,x86-64) => /usr/local/lib/libcurl.so
    libcurl.so (libc6,x86-64) => /lib64/libcurl.so

つまり、↓このような状態に ln -s としました

# pwd
/usr/local/lib
# ls -l libcurl*
-rw-r--r-- 1 root root 1011736 May  6 12:10 libcurl.a
-rwxr-xr-x 1 root root     991 May  6 12:10 libcurl.la
lrwxrwxrwx 1 root root      17 May 14 22:01 libcurl.so -> /lib64/libcurl.so
lrwxrwxrwx 1 root root      16 May  6 12:10 libcurl.so.20190515 -> libcurl.so.4.5.0
lrwxrwxrwx 1 root root      19 Aug 24 14:54 libcurl.so.4 -> /lib64/libcurl.so.4
lrwxrwxrwx 1 root root      16 May  6 12:10 libcurl.so.4.20190515 -> libcurl.so.4.5.0
-rwxr-xr-x 1 root root  539592 May  6 12:10 libcurl.so.4.5.0

更に以前に編集した /etc/ld.so.conf の内容が怪しいんでしょうね。

openssl や curlコマンドで、https ( ssl , tls )のプロトコル verやcipher対応状況を確認

次の通り

$ openssl s_client -tls1_2 -cipher AES128-GCM-SHA256 -connect www.yahoo.co.jp:443
$ curl -I --tlsv1.2 --ciphers AES128-GCM-SHA256 https://www.yahoo.co.jp/ 

また、プロトコル verやcipher 一覧は、次のコマンドで表示できます

$ /usr/local/openssl_1_1_0/bin/openssl ciphers -v

ただ、openssl で指定し、成功した cipher が、curl では失敗したケースがありました。 その原因は調べていません。

redmine v.4.0(+Rails 5.2)に対応したEasyGantt Free版を試す

redmineプラグインであるEasyGanttは人気プラグインの一つかと思います。

redmineは、ver.4.0でwebフレームワークにRails 5.2を採用し、 多くのプラグインが動作しなくなりましたが、 EasyGanttのredmine v.4.0(+Rails 5.2)に対応したようですので、お試し。

redmine ver.4.0のinstall

ver.3系と同様です。以前のエントリにも記載しています。

https://end0tknr.hateblo.jp/entry/20160717/1468755077

EasyGanttFree-4.x のinstall

基本的には、EasyGanttFree-4.x.zip を redmine/plugins/ 以下に解凍するだけ。

$ cd ~/local/redmine/
$ unzip EasyGanttFree-4.x.zip
$ mv easy_gantt/ plugins/

### unicornを起動しようとすると、以下のエラーとなる為、一旦、bundle install

$ bundle exec unicorn_rails -D --path / -c config/unicorn.rb -E production
Could not find gem 'redmine_extensions' in any of the gem sources listed in your Gemfile.
Run `bundle install` to install missing gems.

$ bundle install

$ bundle exec unicorn_rails -D --path / -c config/unicorn.rb -E production


### unicorn起動後に判明しましたが、yt.png が 404 errorとなっていた為、copy

$ cd /home/end0tknr/local/redmine/public/images
$ cp ../../plugins/easy_gantt/assets/images/yt.png .

redmine v.4.0 + EasyGantt の使い心地

EasyGanttの機能UPはありませんでしたが、これまで同様の使いやすさ。

redmine v.4.0では、標準のガントチャート画面で、右クリックからチケットの編集ができるようになり、 これがとても便利。

f:id:end0tknr:20190505083118p:plain

apache 2.4で、HTTP OPTIONS メソッド禁止は、httpd.confで「Require method HEAD GET POST」

ググると、

<Limit OPTIONS>
    Order deny,allow
    Deny from all
</Limit>

のような情報が多く見つかりますが、これはapache 2.2の記載方法。

apache 2.4では、httpd.confに対して

<Directory />
    Options FollowSymLinks
    AllowOverride None
    Require method HEAD GET POST
    Order deny,allow
    Deny from all
</Directory>

のように許可するmethodを列挙します (上記ではHEAD GET POSTを許可)

書籍「システム開発 受託契約の教科書」と、経産省が公開の「情報システム・モデル取引・契約 書」

契約書の内容チェックは、「法務担当にお任せ」な私にとって

  1. 書籍「システム開発 受託契約の教科書」
  2. 経産省が公開の「情報システム・モデル取引・契約 書」

の内容は非常に参考になります。

上記1では、システム開発契約の基礎を全体像(民法を含む六法との関連等)から説明されており、 更に契約書サンプル(テンプレート)のダウンロードも提供されている為、内容も具体的。

この書籍を読んだ上で、上記2を改めて読むと、更に理解が進みます。

1. 書籍「システム開発 受託契約の教科書」

www.amazon.co.jp

システム開発 受託契約の教科書(池田聡)|翔泳社の本

目次。

第1部 システム開発に関する契約の基礎知識
第1章 契約とは
第2章 契約書の目的
第3章 契約に関する基礎知識
第4章 契約書の体裁
第5章 これだけは知らないと危ない法律上のポイント

第2部 サンプル契約書の逐条解説
契約書例1 ソフトウエア開発基本契約書(多段階契約)
契約書例2 ソフトウエア開発契約書(請負一括型)
契約書例3 業務委託契約書(要件定義・準委任契約)
契約書例4 業務委託基本契約書
契約書例5 ソフトウエア開発基本契約書(アジャイル開発)
契約書例6 コンサルティング業務委託契約書
契約書例7 システム保守委託契約書
契約書例8 SES基本契約書
契約書例9 ソフトウエア使用許諾契約書
契約書例10 労働者派遣基本契約書

2. 経産省が公開の「情報システム・モデル取引・契約 書」

www.meti.go.jp

www.meti.go.jp

「情報システム・モデル取引・契約書(受託開発(一部企画を含む)、保守運用)<第一版>」の目次

1.総 論
(1) 経 緯                                                               1
(2) 目 的                                                               2
(3) モデル取引・契約書の全体像とポイント                                7
(4) モデル契約書の主要条項の論点整理                                    12
(5) 今後の検討課題及びモデル取引・契約書の活用について                  20
2.モデル契約プロセス
(1) モデル契約プロセス                                                  22
(2) フェーズの区切りと各々の概要・ポイント                              29
(3) マルチベンダ方式、分割発注に関する注意事項                          39
(4) ユーザとベンダの協力の重要性、役割分担                              42
(5) プロジェクトマネジメントの重要性                                    43
(6) 請負と準委任                                                        44
 (7)パッケージ活用、反復繰り返し型の開発、中小企業ユーザにおける活用の留意点 45
 (8)ハードウェア等調達契約の留意点                                      51
3.モデル契約書・逐条解説
(1) ソフトウェア開発委託基本モデル契約書                                53
(2) ソフトウェア開発委託基本モデル契約書ドキュメントモデル              116
※ドキュメントモデル【参考文書 1】~【参考文書 13】
(3) 仮発注合意書                                                        131
(4) 情報システム保守運用委託基本モデル契約書                            134
(5) 個別契約書・仕様書サンプル                                          152
(別紙1)信頼性向上・取引可視化のための「モデル取引・契約書」の全体像    167
(別紙2)提案依頼書(RFP)の詳細                                       168
(別紙 3)セキュリティ要求仕様書サンプル                                178
(別紙 4)提案書(プロポーザル)の詳細                                  196
(別紙5)委員名簿                                                      201

「情報システム・モデル取引・契約書~情報システム・モデル取引・契約書~(パッケージ、SaaS/ASP活用、保守・運用)<追補版>」の目次

総論                                                                    3
経緯                                                                    3
目的                                                                    5
モデル取引・契約書追補版の全体像とポイント                              11
モデル取引・契約書追補版の主要条項の論点整理                            17
今後の検討課題及びモデル取引・契約書追補版の活用について                19
モデル取引・契約プロセス                                                21
概要                                                                    21
モデル契約プロセスの全体構成                                            22
共通フレーム2007とモデル契約の関係                                      31
モデル契約書・逐条解説                                                  41
パッケージソフトウェア利用コンピュータシステム構築委託契約書            41
重要事項説明書                                                          55
ドキュメントモデル                                                      65
業務関連サンプルドキュメント                                            65
チェックリスト                                                          65

python + selenium + firefox を使用して、google mapで道路距離を算出 - RPAもどき

以下の様になると思います

#!/usr/local/bin/python
# -*- coding: utf-8 -*-
import getopt
import os
import sys
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import traceback
import time


def main():
    # browser起動
    browser = init_browser()

    # google mapで道路距離を計測
    get_road_distance_by_gmap(browser)
    
    # 終了処理
    browser.quit()

    
def get_road_distance_by_gmap(browser):

    # google mapを表示
    browser.get("https://www.google.co.jp/maps/dir////")

    # 経路探索 (車)モードへ
    drive_mode_icon = browser.find_element_by_class_name("directions-drive-icon")
    drive_mode_icon.click()

    # スタート地点
    from_address_div = browser.find_element_by_id("directions-searchbox-0")
    from_address_input = from_address_div.find_element_by_tag_name("input")
    from_address_input.send_keys("東京都渋谷区神南二丁目2番1号")
    time.sleep(1)

    # ゴール地点
    to_address_div = browser.find_element_by_id("directions-searchbox-1")
    to_address_input = to_address_div.find_element_by_tag_name("input")
    to_address_input.send_keys("さいたま市大宮区錦町")
    time.sleep(1)

    # 一旦検索することで、オプション欄表示
    search_btn = to_address_div.find_element_by_class_name("searchbox-searchbutton")
    search_btn.click()

    # 検索オプション設定 (高速道路を使用しない 等)
    directions_options_btn = browser.find_element_by_class_name("section-directions-options-link")
    directions_options_btn.click()

    
    avoid_chkboxes = browser.find_elements_by_class_name("kd-checkbox-label")
    avoid_chkboxes[0].click()

    distance_units = browser.find_elements_by_class_name("kd-radio-label")
    distance_units[2].click()

    distance_divs = browser.find_elements_by_class_name("section-directions-trip-distance")
    print( "ROAD DISTANCE:", distance_divs[0].text )


def init_browser():
    profile = webdriver.FirefoxProfile()

    # 様々なoptionを指定していましたが、以前のようなfile downloadを行わない為
    # implicitly_wait(10) 以外、コメントアウトしました

    # 0:desktop, 1:sys規定のfolder, 2:user def folder
    # profile.set_preference("browser.download.folderList",2)
    # profile.set_preference("browser.download.dir", os.getcwd())
    # profile.set_preference("browser.download.manager.showWhenStarting",False)

    # chrome driverでは、download.directory_upgrade=Trueで上書き保存できましたが
    # firefox では、上書き方法不明なので、コメントアウト
    # profile.set_preference("browser.download.directory_upgrade",True)

    # image/png の場合、強制的にdownload (=画面表示しない)
    # profile.set_preference("browser.helperApps.neverAsk.saveToDisk","image/png")

    browser = webdriver.Firefox(firefox_profile=profile)
    #要素がロードされるまでの待ち時間を10秒に設定
    browser.implicitly_wait(10) # seconds
    return browser


if __name__ == '__main__':
    main()

appiumでexcelファイルを開き、セルに値を入力 - RPAもどき

手順としては、以下。

  1. WinAppDriverUiRecorder で対象となるセルのXPATHを求める
  2. find_element_by_xpath()でセルをappiumから接続
  3. そのセルをクリックすることで、選択
  4. send_keys()で値を入力

ただ、値の入力程度で、これだけの手間を要するのであれば、 openpyxl や xlrd の方がお手軽が印象です。

xlrd for python で excel (xlsx) を読む - end0tknr's kipple - 新web写経開発

# -*- coding: utf-8 -*-
from appium import webdriver
from selenium.webdriver.common.keys import Keys
import pyautogui
import win32gui
import time

def main():
    ## appiumで、アプリ起動
    desired_caps = {}
    desired_caps["app"] = r"c:\Users\end0t\Downloads\ないしょ_DEV_MACRO.xlsm"

    driver = webdriver.Remote(
        command_executor='http://127.0.0.1:4723',
        desired_capabilities= desired_caps)

    ## win32guiで、対象のアプリを探索
    time.sleep(2)
    app_handle = win32gui.FindWindow(None, "ないしょ_DEV_MACRO.xlsm  \[保護ビュー\] - Excel")
    print("APP HANDLE 0:",app_handle)
    
    ## pyautoguiで、ショートカット(ALT→F→I→E)を送信し、保護ビューを解除
    if app_handle > 0 :
        pyautogui.press('alt')
        time.sleep(1)
        pyautogui.press('F')
        time.sleep(1)
        pyautogui.press('I')
        time.sleep(1)
        pyautogui.press('E')

    time.sleep(2)

    ## pyautoguiで、ショートカット(CTRL+I)を送信し、マクロ実行
    ## (CTRL+Iのショートカットは、EXCEL上でALT+F8により設定画面表示)
    app_handle = win32gui.FindWindow(None, "最適テリトリー_DEV_MACRO.xlsm - Excel")
    print("APP HANDLE 1:",app_handle)

    if app_handle > 0 :
        pyautogui.hotkey('ctrl','i')


    ## とあるセルに値を入力
    ## (CTRL+Iのショートカットは、EXCEL上でALT+F8により設定画面表示)
    xpath_str = \
        "".join(["/Pane[@Name=\"デスクトップ 1\"][@ClassName=\"#32769\"]",
                 "/Window[@Name=\"ないしょ_DEV_MACRO.xlsm - Excel\"][@ClassName=\"XLMAIN\"]",
                 "/Pane[@ClassName=\"XLDESK\"]",
                 "/Tab[@Name=\"ないしょ_DEV_MACRO.xlsm\"][@ClassName=\"EXCEL7\"]",
                 "/TabItem[@Name=\"シート 数_旬別\"]",
                 "/DataGrid[@Name=\"グリッド\"]/DataItem[@Name=\"B1\"]"])
    try:
        xls_cell = driver.find_element_by_xpath(xpath_str)
        xls_cell.click()
        xls_cell.send_keys("HOGE FOO")
        time.sleep(5)
    except Exception as e:
        pass  ## version check画面が単に見つからない場合、無視して進みます

        
    ## 終了
    driver.quit()


if __name__ == '__main__':
    main()

pyautogui から、ショートカットキーでexcel vbaマクロを実行- RPAもどき

appium + pyautogui + win32gui for python で、excelの保護ビューを解除する - end0tknr's kipple - 新web写経開発

上記エントリの続きとして、excel vbaマクロを実行。

当初、excelシートに配置された実行ボタンをクリックすることで、 マクロを実行しようとしましたが、appium や pywinauto で、ボタンを探索できなかった為、 excelマクロに対し、ショートカット実行(今回の場合、CTRL+I)を設定し、実行。

# -*- coding: utf-8 -*-
from appium import webdriver
from selenium.webdriver.common.keys import Keys
import pyautogui
import win32gui
import time

def main():
    ## appiumで、アプリ起動
    desired_caps = {}
    desired_caps["app"] = r"c:\Users\end0t\Downloads\ないしょ_DEV_MACRO.xlsm"

    driver = webdriver.Remote(
        command_executor='http://127.0.0.1:4723',
        desired_capabilities= desired_caps)

    ## win32guiで、対象のアプリを探索
    time.sleep(2)
    app_handle = win32gui.FindWindow(None, "ないしょ_DEV_MACRO.xlsm  \[保護ビュー\] - Excel")
    print("APP HANDLE 0:",app_handle)
    
    ## pyautoguiで、ショートカット(ALT→F→I→E)を送信し、保護ビューを解除
    if app_handle > 0 :
        pyautogui.press('alt')
        time.sleep(1)
        pyautogui.press('F')
        time.sleep(1)
        pyautogui.press('I')
        time.sleep(1)
        pyautogui.press('E')

    time.sleep(2)

    ## pyautoguiで、ショートカット(CTRL+I)を送信し、マクロ実行
    ## (CTRL+Iのショートカットは、EXCEL上でALT+F8により設定画面表示)
    app_handle = win32gui.FindWindow(None, "最適テリトリー_DEV_MACRO.xlsm - Excel")
    print("APP HANDLE 1:",app_handle)

    if app_handle > 0 :
        pyautogui.hotkey('ctrl','i')


    ## とあるセルに値を入力
    ## (CTRL+Iのショートカットは、EXCEL上でALT+F8により設定画面表示)
    xpath_str = \
        "".join(["/Pane[@Name=\"デスクトップ 1\"][@ClassName=\"#32769\"]",
                 "/Window[@Name=\"ないしょ_DEV_MACRO.xlsm - Excel\"][@ClassName=\"XLMAIN\"]",
                 "/Pane[@ClassName=\"XLDESK\"]",
                 "/Tab[@Name=\"ないしょ_DEV_MACRO.xlsm\"][@ClassName=\"EXCEL7\"]",
                 "/TabItem[@Name=\"シート 数_旬別\"]",
                 "/DataGrid[@Name=\"グリッド\"]/DataItem[@Name=\"B1\"]"])
    try:
        xls_cell = driver.find_element_by_xpath(xpath_str)
        xls_cell.click()
        xls_cell.send_keys("HOGE FOO")
        time.sleep(5)
    except Exception as e:
        pass  ## version check画面が単に見つからない場合、無視して進みます

        
    ## 終了
    driver.quit()


if __name__ == '__main__':
    main()

マクロ設定画面は、ALT + F8 で起動。

参考

f:id:end0tknr:20190503054505p:plain

appium + pyautogui + win32gui for python で、excelの保護ビューを解除 - RPAもどき

f:id:end0tknr:20190502034149p:plain

appium + windows app driver で、ダウンロードされたexcelファイルに対する 自動操作を行おうとしましたが、「保護ビュー」がある為、操作できない。

WinAppDriver UI Recorderで「編集を有効にする」ボタンのXPATHを調べ、 これをクリックしようとしても、できない。

appium + windows app driver では不可能と判断し、pyautogui + win32gui の力も借りることにしました。

で、以下が、そのpython script。

# -*- coding: utf-8 -*-
from appium import webdriver
from selenium.webdriver.common.keys import Keys
import pyautogui
import win32gui
import time

def main():
    ## appiumで、アプリ起動
    desired_caps = {}
    desired_caps["app"] = r"c:\Users\end0t\Downloads\ないしょ_DEV_MACRO_ORG.xlsm"

    driver = webdriver.Remote(
        command_executor='http://127.0.0.1:4723',
        desired_capabilities= desired_caps)

    ## win32guiで、対象のアプリを探索
    time.sleep(2)
    app_handle = win32gui.FindWindow(None, "ないしょ_DEV_MACRO_ORG.xlsm  [保護ビュー] - Excel")
    print("APP HANDLE:",app_handle)
    
    ## pyautoguiで、ショートカット(ALT→F→I→E)を送信し、保護ビューを解除
    if app_handle > 0 :
        pyautogui.press('alt')
        time.sleep(1)
        pyautogui.press('F')
        time.sleep(1)
        pyautogui.press('I')
        time.sleep(1)
        pyautogui.press('E')
        
    
    ## 終了
    driver.quit()


if __name__ == '__main__':
    main()

jQueryで右クリックによるコンテキスト メニュー表示 - jQuery-chromeContext ( javascript )

イベントは jQuery.on() で設定することで、後から追加されたelementにも対応できますし、 本体である jquery.chromeContext.js は、数10行の小規模で、後から改造するにしても楽ですので。

jQuery-chromeContext で十分かと思います。

http://travishorn.github.io/jquery-chromeContext/

以下、jquery-chromeContext のデモにあるsrc

html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    
    <title>chromeContext Menu</title>
    
    <link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/normalize/2.1.0/normalize.css">
    <link rel="stylesheet" href="css/demo.css">
    <link rel="stylesheet" href="../lib/css/chromeContext.css">
  </head>
  <body>
    <div id="one" class="box">one</div>
    <div id="two" class="box">two</div>
    
    <script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
    <script src="../lib/js/jquery.chromeContext.js"></script>
    <script src="js/demo.js"></script>
  </body>
</html>

jquery.chromeContext.js (本体)

(function ($) {
  var cctxId = 0;
  
  $.fn.chromeContext = function (options) {
    var trigger = this;
    var menu = $('<div class="cctx"/>');
    var l = options.items.length;
    var i;
    
    for (i = 0; i < l; i++) {
      var item = options.items[i];
      var el = $('<div/>');
      
      if (item.separator) {
        el.addClass('cctx-separator');
      } else {
        el.addClass('cctx-item');
        el.text(item.title);
        el.on('click', item.onclick);
      }
      
      menu.append(el);
    }
    
    menu.attr('data-cctxId', cctxId);
    
    $('body').append(menu);
    
    this
      .attr('data-cctxId', cctxId)
      .on('contextmenu', function (e) {
        var menu = $('.cctx[data-cctxId="'+ $(this).attr('data-cctxId') +'"]');
        
        e.preventDefault();
                
        menu
          .css('top', e.clientY)
          .css('left', e.clientX)
          .show();
      })
      .parents()
        .on('mouseup', function () {
          $('.cctx:visible').hide();
        });
    
    cctxId++;
    return this;
  };
}( jQuery ));

demo.js

$(function(){
  $('#one').chromeContext({
    items: [
      { title: 'Hello',
        onclick: function () { console.log('hello.'); } },
      { separator: true },
      { title: 'World',
        onclick: function () { console.log('world.'); } }
    ]
  });
  
  $('#two').chromeContext({
    items: [
      { title: 'Item 1',
        onclick: function () { console.log('one.'); } },
      { title: 'Item 2',
        onclick: function () { console.log('two.'); } },
      { title: 'Item 3',
        onclick: function () { console.log('three.'); } },
      { title: 'Item 4',
        onclick: function () { console.log('four.'); } },
      { title: 'Item 5',
        onclick: function () { console.log('five.'); } },
      { title: 'Item 6',
        onclick: function () { console.log('six.'); }
      }
    ]
  });
});

css

body {
  padding: 50px;
}

.box {
  width: 200px;
  height: 200px;
  float: left;
  margin: 20px;
  padding: 5px 10px;
  font-size: 30px;
  font-weight: bold;
  color: #FFF;
  text-shadow: 1px 1px 2px #555;
}

#one { background-color: #FF8080; }
#two { background-color: #84FF84; }