ラベル Python の投稿を表示しています。 すべての投稿を表示
ラベル Python の投稿を表示しています。 すべての投稿を表示

2021年10月3日日曜日

【moto】motoでtransact_write_itemsをモックする。【Python】

motoでtransact_write_itemsをモックする。

moto と transact_write_items で検索すると、motoを拡張してモックする方法が上位に表示される。詳しく調べていないが、moto 2.2.4 では拡張せずともモックできるので、サンプルを提示したい。

この記事ではテストコードは一部だけ記載するので、完全なものは https://github.com/tomohikoseven/moto_transact_write_items を参照ください。

実装コード

実装したコードは、ClickCountというテーブルにItemをトランザクションで追加・更新(アトミックカウンタ)するものです。DynamoDBをご存知の方には、簡単なものかと思います。

簡単に説明すると、以下の実装コードはハッシュキー:month は文字列 YYYYMM を、レンジキー(ソートキー):date は文字列 YYYYMMDD を期待して、1トランザクションで2つのItemに対し、clickCountをカウントUPするものになります。

トランザクションの1つ目は日付の末尾が00となっており、月間のclickCountをカウントUPします。今回の記事では気にすることではないです。


(index.py)
import boto3


client = boto3.client('dynamodb',  region_name='ap-northeast-1')
def lambda_handler( month:str, date:str ):
  try:
    response = client.transact_write_items(
      TransactItems=[
        {
          'Update' : {
            'Key' : {
              'hKey': { 'S': month },
              'rKey': { 'S': month + '00' }
            },
            'TableName':'ClickCount',
            'UpdateExpression': 'ADD clickCount :increment',
            'ExpressionAttributeValues' : {
              ':increment': { 'N': '1' }
            }
          }
        },
        {
          'Update' : {
            'Key' : {
              'hKey': { 'S': month },
              'rKey': { 'S': date }
            },
            'TableName':'ClickCount',
            'UpdateExpression': 'ADD clickCount :increment',
            'ExpressionAttributeValues' : {
              ':increment': { 'N': '1' }
            }
          }
        }
      ]
    )
  except Exception as e :
    print("===")
    print(e)
    raise e

  return 200

テストコード

pytestでテストコードを実行します。

motoでモックするテストコードを記述したことがある方は難しいことはないです。mock_dynamodb2をimportして、アノテーション@mock_dynamodb2をテストケースに付けてやればよいです。


(test_index.py)

# 本当のテストは1ケースだけではないので、ここでは余分なライブラリがインポートされています。
from importlib import import_module
import boto3
from moto import mock_dynamodb2
from unittest import mock
from botocore.exceptions import ClientError
import pytest


@mock_dynamodb2
def test_success(set_dynamodb_Count):
  # mock dynamodb
  table = set_dynamodb_Count()

  month = '202101'
  date = '20210101'
  from src.index import lambda_handler
  res = lambda_handler( month, date )

  item = table.get_item(Key={'hKey':month, 'rKey': date})
  assert res == 200
  assert item['Item']['clickCount'] == 1

conftest.pyに set_dynamodb_Count を記載しています。テストにはこちらも必要になります。test_index.pyと同一フォルダに置いてください。


(conftest.py)
import pytest
import boto3

@pytest.fixture
def set_dynamodb_Count():
  def definition():
    dynamodb = boto3.resource('dynamodb', region_name='ap-northeast-1')
    dynamodb.create_table(
      TableName='ClickCount',
      KeySchema=[
          {
              'AttributeName': 'hKey',
              'KeyType': 'HASH'
          },
          {
              'AttributeName': 'rKey',
              'KeyType': 'RANGE'
          }
      ],
      AttributeDefinitions=[
          {
              'AttributeName': 'hKey',
              'AttributeType': 'S'
          },
          {
              'AttributeName': 'rKey',
              'AttributeType': 'S'
          }
      ],
    )

    return dynamodb.Table('ClickCount')
  return definition

2020年9月1日火曜日

Chrome拡張機能を自動でインストールする【Python】【Selenium】【win32com】

 調べても調べても検索に引っ掛からなかった、Chrome拡張機能を自動でインストールする方法がわかったので、ここに記載する。Teratailありがとう。

必要なもの

  • Python
  • Selenium
  • chromedriver_binary
  • win32com

ソース

 「楽天検索」の拡張機能をChromeにインストールするコードを示す。ソースがはみ出してしまっているのはすみません。

import time
from time import sleep

# selenium
from selenium import webdriver
from selenium.webdriver.common.by import By
# ないと起動しない
import chromedriver_binary
import selenium.webdriver.support.expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait
import ctypes
import win32com.client

def getOptions():
    options = webdriver.ChromeOptions()
    options.add_argument( '--user-data-dir=' + 'UserData' )
    options.add_argument( '--profile-directory='+ 'Default' )
    return options

def installRakutenSearch():
    # 初回起動、楽天検索拡張機能をインストール
    driver = webdriver.Chrome( options=getOptions() )
    driver.get(r'https://chrome.google.com/webstore/detail/%E6%A5%BD%E5%A4%A9%E3%82%A6%E3%82%A7%E3%83%96%E6%A4%9C%E7%B4%A2/iihkglbebihpaflfihhkfmpabjgdpnol?hl=ja')

    WebDriverWait(driver, 15).until( EC.element_to_be_clickable( (By.XPATH, '//div[@class="g-c-Hf"]') ) )
    driver.find_element_by_xpath( '//div[@class="g-c-Hf"]' ).click() # Chromeに追加 ボタンクリック

    html = driver.page_source

    handle = ctypes.windll.user32.FindWindowW( "Chrome_WidgetWin_1",0)
    ctypes.windll.user32.SetForegroundWindow(handle)

    Shell = win32com.client.Dispatch("WScript.Shell")
    # うまくいかないときは、range(2)からrange(3)へ変更。調整してください。
    for i in range(2):
        Shell.SendKeys("{TAB}") 
        sleep (1)
    Shell.SendKeys("~") 
    time.sleep(5)
    driver.close()
    driver.quit()

if __name__ == '__main__':
    installRakutenSearch()

2020年8月18日火曜日

【Python】一括で svg を png に変換する。

Azure のアイコンが svg で用意されており、パワポで読み込めなかった。

 仕事の資料を作成するにあたって、Azureのアイコンを使いたかった。しかし、公式には svg のアイコンしかない。そのため、プログラム作ってpng画像を作ることにした。

Azure icon png(200 x 200) ここにpng変換後のアイコンを共有しておく。

以下にpng変換時のソースを貼っておく。

svg → png 変換ソース

 本プログラムは一括でsvg -> png 変換を行う。公式のsvgのzipファイルを解凍し、「AI + Machine Learning」フォルダがあるところと同一フォルダで本プログラムを動作させる。

python 3.7.8で動作確認済み。事前準備として、ライブラリの cairosvg をインストールする必要がある。

pip install cairosvg

 本体プログラムは以下。

import os.path
import cairosvg
import os

def main():
    for folder in os.listdir('.'):
        if os.path.isdir( folder ):
            for file in os.listdir( folder ):
                name = file.split('.svg')[0]
                url = folder + r'\{0}.svg'.format(name)
                write_to = folder + r'\{0}.png'.format(name)
                cairosvg.svg2png( url=url
                                , write_to=write_to
                                , output_width=200
                                , output_height=200 )  # svg -> png 本体
                print(file)

if __name__ == "__main__":
    main()

 svg が不要なら、以下のプログラムを実行すると削除できる。

import os.path
import cairosvg
import os

def main():
    for folder in os.listdir('.'):
        if os.path.isdir( folder ):
            for file in os.listdir( folder ):
                if '.svg' in file:
                    rfile = folder + r'\{0}'.format( file )
                    os.remove( rfile )
                    print(file)

if __name__ == "__main__":
    main()

2020年4月16日木曜日

【Python】演算子 in は数値や文字列の混合配列でも評価できる。

 Pythonのin 演算子を検索すると、だいたい数値や文字列のみを要素とする配列と比較している例が目立つが、数値と文字列を混合した配列でも比較することが可能である。

ドキュメントを見てみる

 Pythonのドキュメントを見てみよう。
6.10.2. 所属検査演算

ここには以下のような記述がある。

演算子 in および not in は所属関係を調べます。 x in s の評価は、 x が s の要素であれば True となり、そうでなければ False となります。

ドキュメント上では、配列要素が1種類のときに限定している記述はない。

試してみる

Python 3.7.3 (default, Apr  3 2019, 05:39:12)
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> 3 in [ 4, 'abc', 'cdf' ]
False
>>> 4 in [ 4, 'abc', 'cdf' ]
True
>>> [3,4] in [ [1,2], [3,4] ]
True
>>> [3,4] in [ [1,2], [3,5] ]
False
>>> 'abc' in [ 4, 'abc', 'cdf' ]
True

2019年11月4日月曜日

[Python]奇数の値のみ前後にダッシュ(-)を付ける。

問題

与えられた(引数)数値の中で、奇数の前後にダッシュ(-)マークを付けて文字列を返す関数を記述せよ。 ただし、開始および終了部分には付けないでください。

  • dashatize(274) -> '2-7-4'
  • dashatize(6815) -> '68-1-5'
  • dashatize(None) -> 'None'
  • dashatize(-1) -> '1'
  • dashatize(-28369) -> '28-3-6-9'

解答

def dashatize(num):
    try:
        return ''.join(['-'+i+'-' if int(i)%2 else i for i in str(abs(num))]).replace('--','-').strip('-')
    except:
        return 'None'

2019年10月29日火曜日

[Python]ある条件下のファイル名を抽出する。

問題

以下に示すファイル名の一部を抽出する関数を記述せよ。

  • 長い数字で表される日付で始まると仮定します。
  • アンダースコアが続きます。
  • その後、拡張子を持つファイル名があります。
  • 最後に常に追加の拡張子が付けられます。

Inputs:

1231231223123131_FILE_NAME.EXTENSION.OTHEREXTENSION

1_This_is_an_otherExample.mpg.OTHEREXTENSIONadasdassdassds34

1231231223123131_myFile.tar.gz2

Outputs:

FILE_NAME.EXTENSION

This_is_an_otherExample.mpg

myFile.tar

解答

class FileNameExtractor:
    @staticmethod
    def extract_file_name(fname):
        return fname.split('_', 1)[1].rsplit('.', 1)[0]

2019年10月28日月曜日

[Python]与えられた正の整数を1桁になるまで各桁を乗算した回数を返す関数を記述せよ。

問題

正の整数が与えられたとき、その与えられた値の各桁の数字を乗算し、1桁になるまでの回数を返す関数を記述せよ。

 persistence(39) => 3  # Because 3*9 = 27, 2*7 = 14, 1*4=4
                       # and 4 has only one digit.

 persistence(999) => 4 # Because 9*9*9 = 729, 7*2*9 = 126,
                       # 1*2*6 = 12, and finally 1*2 = 2.

 persistence(4) => 0   # Because 4 is already a one-digit number.

解答

def persistence(n):
    from functools import reduce
    cnt = 0
    while n >= 10 :
        cnt = cnt + 1
        n = reduce( (lambda x,y: int(x) * int(y)), list(str(n)) )
    return cnt

2019年10月24日木曜日

[Python]キャメルケースをケバブケースに変換する。

問題

キャメルケースをケバブケースに変換する関数を記述せよ。

ただし、関数の戻り値(文字列)は小文字のみ含むものとする。

  • kebabize('camelsHaveThreeHumps') // camels-have-three-humps
  • kebabize('camelsHave3Humps') // camels-have-humps
  • kebabize('myCamelCasedString') // my-camel-cased-string
  • kebabize('myCamelHas3Humps') // my-camel-has-humps
  • kebabize('SOS') // s-o-s
  • kebabize('42') // ''(空文字)

解答

def kebabize(s):
    return ''.join(c if c.islower() else '-' + c.lower() for c in s if c.isalpha()).strip('-')

2019年10月22日火曜日

[Python]文字列の各文字をアルファベット順の位置に変換する。

問題

文字列を入力として、それぞれの文字をアルファベット順の位置に変換せよ。

ただし、文字列内のアルファベット以外の文字は無視すること。

"a" = 1, "b" = 2, など。

alphabet_position("The sunset sets at twelve o' clock.") 
# => "20 8 5 19 21 14 19 5 20 19 5 20 19 1 20 20 23 5 12 22 5 15 3 12 15 3 11"

解答

def alphabet_position(text):
    return ' '.join(str(ord(c) - 96) for c in text.lower() if c.isalpha())

[Python]配列内に奇数回現れるint型整数を見つける。

問題

配列を入力として、奇数回現れるint型の整数を見つけ、戻す関数を記述せよ。

ただし、奇数回現れる整数は必ず1個である。

解答

def find_it(seq):
   for i in seq:
       if seq.count(i)%2!=0:
           return i

2019年10月21日月曜日

[Python]ハッシュを要素とする配列から'Bart, Lisa & Maggie'のように名前を結合した文字列を返す関数。

問題

入力:名前を含むハッシュの配列 戻り:カンマ区切りの名前文字列を返す。ただし、最後の2名の名前はアンパサンドで区切ります。

namelist([ {'name': 'Bart'}, {'name': 'Lisa'}, {'name': 'Maggie'} ])
# returns 'Bart, Lisa & Maggie'

namelist([ {'name': 'Bart'}, {'name': 'Lisa'} ])
# returns 'Bart & Lisa'

namelist([ {'name': 'Bart'} ])
# returns 'Bart'

namelist([])
# returns ''

解答

def namelist(names)
  return ' & '.join([ h['name'] for h in names ] ).replace(' &', ',', len(names)-2)

2019年10月19日土曜日

[Python]秒数から、経過時間を時分秒(HH:MM:SS)形式で出力する。

問題

0以上の整数(秒数)を入力として、人が認識可能な形式HH:MM:SSをフォーマットとする時刻に変換する関数を記述せよ。

  • HH:時、2桁でパディングする。00-99を範囲とする。
  • MM:分、2桁でパディングする。00-59を範囲とする。
  • SS:秒、2桁でパディングする。00-59を範囲とする。

入力の最大値は、359999 (99:59:59)

入力と出力例は以下の通り。

  • make_readable(0) -> "00:00:00"
  • make_readable(5) -> "00:00:05"
  • make_readable(60) -> "00:01:00"
  • make_readable(86399) -> "23:59:59"
  • make_readable(359999) -> "99:59:59"

解答

def make_readable(seconds):
    return '{:02}:{:02}:{:02}'.format(int(seconds / 3600), int(seconds / 60 % 60), seconds % 60 ) 

[Python]10進数の正の整数を2進数表示した後のビット数を数える。

問題

0以上の整数を入力とし、バイナリビットに変換したときの1の数を返す関数を記述せよ。

例: 入力:1234 のバイナリビットは10011010010。このとき、関数の戻り値は5。

解答

def countBits(n):
    return bin(n).count("1")

2017年10月22日日曜日

ラズパイ上に構築したDjangoアプリに外部からアクセスする方法

目的

 ラズパイ上でDjangoの勉強をするために環境を構築し、初歩から勉強を始めようとしたところ、外部PCからアクセスする方法を調べる必要があり、わかったため、その方法を以下に記す。

環境

  • RaspberryPi2:stretch
  • Python:3.5.3
  • Django:1.11.5

必要な設定

  1. python manage.py runserver 0.0.0.0:8000 で起動する。
  2. プロジェクト名/settings.py 内のALLOWED_HOSTSにアクセスURLを記述する。

具体例

 PCからラズパイ上のDjangoアプリアクセスURL:raspberrypi.local:8000/polls

 settings.py内に以下のような記述をする。

ALLOWED_HOSTS = ['raspberrypi.local']

2017年9月18日月曜日

【Python】【OpenCV】OpenCVがImport できない場合の対処。

目的

 Python×OpenCVを使って画像処理をしようとしたときに、「import cv2」でエラーとなった。そのときの対処を備忘録として残す。

環境

  • Windows10 64bit
  • Python 3.6
  • Numpy 1.13
  • OpenCV 3.3

対処:Visual C++ 再頒布可能パッケージをインストール

参考:https://stackoverflow.com/questions/33200025/cant-import-cv2-dll-load-failed 上記サイトに記載があるが、つまり、Visual C++ 2015 redistribution をインストールすると、エラーがでなくなる。
Visual C++ 2015 redistribution

2011年11月5日土曜日

NetBeans でpython入門したら、出力が文字化けした。


OS:Windows Vista
開発環境:NetBeans 6.7.1
python:2.6.6

NetBeans(総合開発環境)を使い、pythonを入門していた。

(10日でおぼえる Python 入門教室: 穂苅 実紀夫, 寺田 学, 中西 直樹, 堀田 直孝, 永井 孝: 本 : http://goo.gl/TMLKk

上記の本の通り、IDLE(こっちも総合開発環境)だかなんだかを使って、素直にコードを打ち込んでいればよかったものの、コードを補完したり、打ち間違えを表示したりしてくれないので、NetBeansを使っている。

NetBeansのpythonプラグインをインストールし、さて頑張るかと思いコードを打ち込んでいたが、出力が文字化けした。
(その前に日本語部分でコンパイルエラーがあった。コメント部分でさえ。今回はこの部分の対処は書いていない。)

その対処について以下記載する。
参考サイト:
Python でUTF-8, shift_jis, euc_jpなど日本語を使う方法 : http://osksn2.hep.sci.osaka-u.ac.jp/~taku/osx/python/encoding.html

手順としては以下。
1.プロジェクトプロパティーからファイル自体のエンコードを指定する。
2.ソースにプロパティーで指定したエンコード情報で出力するように設定する。
3.プログラムを動作させ、表示を確認する。

Windowsなので、Shift_JISでやりました。
UTF-8がよかったものの、どうしても文字化けしてしまうので、やめた。


1.プロジェクトプロパティーからファイル自体のエンコードを指定する。




2.ソースにプロパティーで指定したエンコード情報を書き込む。
以下をソースに挿入する。
--------- ここから ----------
# coding: Shift_JIS

import sys
import codecs

sys.stdout = codecs.getwriter('shift_jis')(sys.stdout)
--------- ここまで ----------

実際にはこんな風になる。

ソース一部:

# To change this template, choose Tools | Templates
# and open the template in the editor.
# coding: Shift_JIS

__author__="andre"
__date__ ="$2011/11/05 9:28:16$"
import sys
import codecs
import urllib
from xml.etree.ElementTree import ElementTree
from xml.parsers.expat import ExpatError
sys.stdout = codecs.getwriter('shift_jis')(sys.stdout)
XML_BASE_URL = "http://weather.livedoor.com/forecast/webservice/rest/v1"
CITY_MAPS = [('wakkanai', '1'),('tokyo', '63'),('osaka', '81'),('fukuoka', '110'),('naha', '136')]


-----------------------------------


3.プログラムを動作させ、表示を確認する。
メニューの「実行」から「ファイルを実行(F)」を選択する。
下の方に標準出力が出るので、日本語が出ていることが確認できる。