2017/05/22

[本]小さなチーム、大きな仕事 働き方の新しいスタンダード (ハヤカワ文庫NF)

まず最初に
見直す
先に進む
進展
生産性
競合相手
進化
プロモーション
人を雇う
ダメージ・コントロール
文化

via:目次


2017/05/21

[Google App Engine][Python]Amazon Product Advertising APIのitemlookupを追加

先日、Google App EngineのPythonを使ってAmazon Product Advertising APIをclass化してみたのですが、新規にitemlookupを追加しました。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright 2007 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

from urlparse import urlparse
from google.appengine.api import urlfetch
from lxml import etree

import os
import urllib
import datetime
import hashlib
import base64
import logging
import hmac
import xmltodict

class AmazonProductAdvertisingAPI(object):
  def __init__(self,access_key_id,secret_access_key,associate_tag = None,locale = "US"):

    self._access_key_id = access_key_id
    self._secret_access_key = secret_access_key
    self._associate_tag = associate_tag

    self._validate_certificate = True
    _environ = os.environ
    _http_host = _environ["HTTP_HOST"]
    if _http_host.find("localhost") > -1:
      self._validate_certificate = False

    _wsgi_url_scheme = _environ["wsgi.url_scheme"]
    self._endpoint = {
      "JP":_wsgi_url_scheme + "://webservices.amazon.co.jp/onca/xml",
      "US":_wsgi_url_scheme + "://webservices.amazon.com/onca/xml",
      "FR":_wsgi_url_scheme + "://webservices.amazon.fr/onca/xml",
      "IN":_wsgi_url_scheme + "://webservices.amazon.in/onca/xml",
      "IT":_wsgi_url_scheme + "://webservices.amazon.it/onca/xml",
      "UK":_wsgi_url_scheme + "://webservices.amazon.co.uk/onca/xml"
    }

    if self._endpoint.has_key(locale):
      self._product_advertising_url = self._endpoint[locale]
    else:
      self._product_advertising_url = self._endpoint["US"]

    self._service = "AWSECommerceService"

    #self._method = ""
    #self._prms = {}

  #タイムスタンプを設定
  def _timestamp(self,prms):

    prms["Timestamp"] = datetime.datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ")

  #URLエンコード
  def _urlencode_rfc3986(self,s):
    _s = urllib.quote(s)
    _s = _s.replace("%7E", "~")
    return _s

  #アクセストークン用の文字列作成
  def _canonical_string(self,prms):

    _keys = sorted(prms)
    _canonical_string = ""
    for _k in _keys:
      _canonical_string = _canonical_string + "&" + self._urlencode_rfc3986(_k) + "=" + self._urlencode_rfc3986(prms[_k])
    else:
      _canonical_string = _canonical_string[1:]

    return _canonical_string

  #問い合わせURLの作成
  def _request_url(self,prms,method):

    _url_info = urlparse(self._product_advertising_url)

    _canonical_string = self._canonical_string(prms)

    _signature = method + "\n" + _url_info.hostname + "\n" + _url_info.path +"\n" + _canonical_string
    _signature = hmac.new(self._secret_access_key, _signature, hashlib.sha256).digest()
    _signature = base64.b64encode(_signature)

    _url = self._product_advertising_url + "?" + _canonical_string + "&Signature=" + self._urlencode_rfc3986(_signature)

    return _url

  #itemsearch
  def itemsearch(self,searchindex=None,browsenode=None,availability="Available",responsegroup="Small",itempage=1,keywords=None,minimumprice=None,maximumprice=None,isremoveadult=True,sort=None):

    _prms = {}

    _prms["Service"] = self._service
    _prms["AWSAccessKeyId"] = self._access_key_id

    if self._associate_tag is not None:
      _prms["AssociateTag"] = self._associate_tag

    _prms["Operation"] = "ItemSearch"
    self._timestamp(_prms)
    
    if availability == "":
      return None

    if availability is None:
      return None

    _prms["Availability"] = availability

    if searchindex == "":
      return None

    if searchindex is None:
      return None

    _prms["SearchIndex"] = searchindex

    if responsegroup is None:
      return None

    if responsegroup == "":
      return None

    _prms["ResponseGroup"] = responsegroup

    if browsenode is not None:
      _prms["BrowseNode"] = browsenode

    if itempage is not None:
      _prms["ItemPage"] = str(itempage)

    if keywords is not None:
      _prms["Keywords"] = keywords

    if maximumprice is not None:
      _prms["MaximumPrice"] = str(maximumprice)

    if minimumprice is not None:
      _prms["MinimumPrice"] = str(minimumprice)

    if sort is not None:
      _prms["Sort"] = sort

    _url = self._request_url(_prms,"GET")

    try:
      _result = urlfetch.fetch(_url,validate_certificate = self._validate_certificate)
      _status_code = _result.status_code
      #logging.info(_status_code)


      if _status_code == 200:

        _content = _result.content
        _root = etree.fromstring(_content)
        _ns = _root.xpath('namespace-uri(.)')
        _namespace = {"ns":_ns}
        _errorTags = _root.findall('.//ns:Error',namespaces=_namespace)
        #logging.info(_content)
        if len(_errorTags) > 0:
          _errorTag = _errorTags[0]
          _code  = _errorTag.findall('.//ns:Code',namespaces=_namespace)
          _message = _errorTag.findall('.//ns:Message',namespaces=_namespace)
          logging.error(u"code: " + _code[0].text + " message: " + _message[0].text)
        else:
          return _root
      else:
        logging.error(u"status code: " + str(_status_code) + " content:" + _content)
        
    except urlfetch.DeadlineExceededError:
      logging.error(u"urlfetch DeadlineExceededError")

    except urlfetch.ResponseTooLargeError, response:
      logging.error(u"urlfetch ResponseTooLargeError" + response)

    except urlfetch.InternalTransientError:
      logging.error(u"urlfetch InternalTransientError")

    except urlfetch.InvalidURLError:
      logging.error(u"urlfetch InvalidURLError")

    except urlfetch.Error:
      logging.error(u"urlfetch Error")

    return None

  #itemlookup
  def itemlookup(self,asin,responsegroup="Small"):
    _prms = {}

    _prms["Service"] = self._service
    _prms["AWSAccessKeyId"] = self._access_key_id

    if self._associate_tag is not None:
      _prms["AssociateTag"] = self._associate_tag

    _prms["Operation"] = "ItemLookup"
    self._timestamp(_prms)

    if asin is None:
      return None

    if asin == "":
      return None

    _prms["ItemId"] = asin

    if responsegroup is None:
      return None

    if responsegroup == "":
      return None

    _prms["ResponseGroup"] = responsegroup

    _url = self._request_url(_prms,"GET")

    try:
      _result = urlfetch.fetch(_url,validate_certificate = self._validate_certificate)
      _status_code = _result.status_code
      #logging.info(_status_code)


      if _status_code == 200:

        _content = _result.content
        _root = etree.fromstring(_content)
        _ns = _root.xpath('namespace-uri(.)')
        _namespace = {"ns":_ns}
        _errorTags = _root.findall('.//ns:Error',namespaces=_namespace)
        #logging.info(_content)
        if len(_errorTags) > 0:
          _errorTag = _errorTags[0]
          _code  = _errorTag.findall('.//ns:Code',namespaces=_namespace)
          _message = _errorTag.findall('.//ns:Message',namespaces=_namespace)
          logging.error(u"code: " + _code[0].text + " message: " + _message[0].text)
        else:
          return _root
      else:
        logging.error(u"status code: " + str(_status_code) + " content:" + _content)
        
    except urlfetch.DeadlineExceededError:
      logging.error(u"urlfetch DeadlineExceededError")

    except urlfetch.ResponseTooLargeError, response:
      logging.error(u"urlfetch ResponseTooLargeError" + response)

    except urlfetch.InternalTransientError:
      logging.error(u"urlfetch InternalTransientError")

    except urlfetch.InvalidURLError:
      logging.error(u"urlfetch InvalidURLError")

    except urlfetch.Error:
      logging.error(u"urlfetch Error")

    return None

2017/05/20

2017/05/19

[Google App Engine][Python]ItemPageの上限

Product Advertising APIのItemPageの上限はどうやら10までみたい

泣きそう。

2017/05/18

[Google App Engine][Python]xmltodict

lxml.etreeを使ってxmlを毎回、毎回、パースするのがめんどくさいので、xmlをjsonライクに変換してくれるライブラリーってないのかなーっと思って探していたら、どうやら
xmltodict
を発見して、以下のようにコードを組んだらjsonライクなdictonaryに変換されました。

import xmltodict
import json
from lxml import etree

_xml = etree.tostring("")
_json = json.dumps(xmltodict.parse(_xml))

2017/05/17

[css]画像の縦横比を維持して表示する方法

画像の縦横比を維持しながら表示する場合、確か、7、8年前(ってどんだけだよ)ぐらいだとJavaScriptで画像の計算を行って表示させていたような気がしたんですが、

【図解】CSSだけで画像の縦横比を維持したサムネイルを表示する

を見たら、なんとCSSだけでいけるじゃーないっすか!

確かに記載されている通りに実装したら縦横比が維持されたわけですが、改めて記事を読んで思ったのは、CSS3が誕生したことによって、かつ、androidのOS 4以上の場合は、
できることがかなり広がっているのではないだろうか?ということである。

もう少し調べたらJSを使っていた処理もCSSで完結しそうなコードっていっぱいありそうな気がしてきた。

2017/05/16

[mac]iMovieのストレージを減らす

macのストレージを確認したらmovieフォルダがものすごい容量になっていた。

で、特に「iMovie ライブラリ.imovielibrary」というファイルの容量が大きかったのですが、
Macのストレージを食い潰す「iMovieのTempファイル」を削減する方法 (2016年1月版)
で確認したところ、iMovieで作ったらライブラリが動画込みでこのファイルに格納されるらしく、不要なライブラリを削除するために、
iMovieを立ち上げて以前作ったライブラリを削除したらものすごい容量が減りました。

2017/05/15

[Python][Google App Engine]Google Cloud Storageにファイルを読み込む

昨日のエントリーではGoogle App EngineからGoogle Cloud Storageにファイルをアップロードする方法について書いたので、そのファイルをreadする方法を書きたいと思います。

import os
import cloudstorage
from google.appengine.api import app_identity

bucket_name = os.environ.get(DEFAULT_BUCKET_NAME,app_identity.get_default_gcs_bucket_name())
filename = '/' + bucket_name + '/' + "hoge"
gcs_file = cloudstorage.open(filename)
contents = gcs_file.read()
gcs_file.close()
確かにreadできたー。

2017/05/14

[Python][Google App Engine]Google Cloud Storageにファイルをアップロード

どうやらGoogle App EngineからGoogle Cloud Storageに5GBまでファイル保存が可能なようなので、アップロードの仕方を調査しました。

import os
import cloudstorage
from google.appengine.api import app_identity

bucket_name = os.environ.get(DEFAULT_BUCKET_NAME,app_identity.get_default_gcs_bucket_name())
write_retry_params = cloudstorage.RetryParams(backoff_factor=1)
filename = '/' + bucket_name + '/' + "hoge"
gcs_file = cloudstorage.open(
  filename,
  mode='w',
  content_type='text/plain',
  options={'x-goog-acl':'private'},
  retry_params=write_retry_params
)
gcs_file.write("foo bar")
gcs_file.close()
で実行したら確かにファイルが保存された超うれしい!

2017/05/13

[Twitter]短縮URLの長さ

POSTリクエストでstatuses/updateを行う時、一緒にURLが混じっている場合は、URLの文字数をいくつで見積もればいいのだろうか?

twitterのAPIで確認すると、「help/configuration」に問い合わせをして

short_url_length

short_url_length_https
を参照すればいいみたいです。